source: ComponentRegistry/trunk/ComponentRegistry/src/main/java/clarin/cmdi/componentregistry/rest/ComponentRegistryRestService.java @ 5549

Last change on this file since 5549 was 5549, checked in by olhsha@mpi.nl, 10 years ago

Added group service. Tested via the tomcat on loclahots (test URI and postman), old unit tests are adjusted and work well. Todo: retest on localhost tomcat, look at run-time exceptions, add new unit tests, adjust front-end

File size: 75.6 KB
Line 
1package clarin.cmdi.componentregistry.rest;
2
3import clarin.cmdi.componentregistry.AllowedAttributetypesXML;
4import clarin.cmdi.componentregistry.AuthenticationFailException;
5import clarin.cmdi.componentregistry.ComponentRegistry;
6import clarin.cmdi.componentregistry.ComponentRegistryException;
7import clarin.cmdi.componentregistry.ComponentRegistryFactory;
8import clarin.cmdi.componentregistry.DeleteFailedException;
9import clarin.cmdi.componentregistry.ItemNotFoundException;
10import clarin.cmdi.componentregistry.MDMarshaller;
11import clarin.cmdi.componentregistry.RegistrySpace;
12import clarin.cmdi.componentregistry.UserCredentials;
13import clarin.cmdi.componentregistry.UserUnauthorizedException;
14import clarin.cmdi.componentregistry.components.CMDComponentSpec;
15import clarin.cmdi.componentregistry.components.CMDComponentType;
16import clarin.cmdi.componentregistry.impl.ComponentUtils;
17import clarin.cmdi.componentregistry.impl.database.GroupService;
18import clarin.cmdi.componentregistry.impl.database.ValidationException;
19import clarin.cmdi.componentregistry.model.BaseDescription;
20import clarin.cmdi.componentregistry.model.Comment;
21import clarin.cmdi.componentregistry.model.CommentResponse;
22import clarin.cmdi.componentregistry.model.ComponentDescription;
23import clarin.cmdi.componentregistry.model.Group;
24import clarin.cmdi.componentregistry.model.ProfileDescription;
25import clarin.cmdi.componentregistry.model.RegisterResponse;
26import clarin.cmdi.componentregistry.rss.Rss;
27import clarin.cmdi.componentregistry.rss.RssCreatorComments;
28import clarin.cmdi.componentregistry.rss.RssCreatorDescriptions;
29import com.google.common.collect.Lists;
30
31import com.sun.jersey.api.core.InjectParam;
32import com.sun.jersey.multipart.FormDataParam;
33
34import java.io.IOException;
35import java.io.InputStream;
36import java.io.OutputStream;
37import java.net.URI;
38import java.security.Principal;
39import java.text.ParseException;
40import java.util.ArrayList;
41import java.util.Arrays;
42import java.util.Collections;
43import java.util.Date;
44import java.util.List;
45
46import javax.servlet.ServletContext;
47import javax.servlet.http.HttpServletRequest;
48import javax.servlet.http.HttpServletResponse;
49import javax.ws.rs.Consumes;
50import javax.ws.rs.DELETE;
51import javax.ws.rs.DefaultValue;
52import javax.ws.rs.FormParam;
53import javax.ws.rs.GET;
54import javax.ws.rs.POST;
55import javax.ws.rs.Path;
56import javax.ws.rs.PathParam;
57import javax.ws.rs.Produces;
58import javax.ws.rs.QueryParam;
59import javax.ws.rs.WebApplicationException;
60import javax.ws.rs.core.Context;
61import javax.ws.rs.core.MediaType;
62import javax.ws.rs.core.Response;
63import javax.ws.rs.core.Response.Status;
64import javax.ws.rs.core.SecurityContext;
65import javax.ws.rs.core.StreamingOutput;
66import javax.ws.rs.core.UriInfo;
67import javax.xml.bind.JAXBException;
68
69import org.slf4j.Logger;
70import org.slf4j.LoggerFactory;
71import org.springframework.beans.factory.annotation.Autowired;
72import org.springframework.stereotype.Service;
73import org.springframework.transaction.annotation.Transactional;
74
75/**
76 * Handles CRUD operations on
77 * {@link ComponentDescription}, {@link ProfileDescription} and {@link Comment}s
78 *
79 * @author twago@mpi.nl
80 * @author olsha@mpi.nl
81 * @author george.georgovassilis@mpi.nl
82 *
83 */
84@Path("/registry")
85@Service
86@Transactional(rollbackFor = {Exception.class, ValidationException.class})
87public class ComponentRegistryRestService implements
88        IComponentRegistryRestService {
89
90    private final static Logger LOG = LoggerFactory
91            .getLogger(IComponentRegistryRestService.class);
92    @Context
93    private UriInfo uriInfo;
94    @Context
95    private SecurityContext security;
96    @Context
97    private HttpServletRequest request;
98    @Context
99    private HttpServletResponse response;
100    @Context
101    private ServletContext servletContext;
102    @InjectParam(value = "componentRegistryFactory")
103    private ComponentRegistryFactory componentRegistryFactory;
104    @InjectParam(value = "mdMarshaller")
105    private MDMarshaller marshaller;
106    @Autowired
107    private GroupService groupService;
108
109    private ComponentRegistry getBaseRegistry() throws AuthenticationFailException {
110        Principal userPrincipal = this.checkAndGetUserPrincipal();
111        UserCredentials userCredentials = this.getUserCredentials(userPrincipal);
112        return componentRegistryFactory.getBaseRegistry(userCredentials);
113    }
114
115    private ComponentRegistry getRegistry(RegistrySpace space, Number groupId) {
116        Principal userPrincipal = security.getUserPrincipal();
117        UserCredentials userCredentials = this.getUserCredentials(userPrincipal);
118        try {
119            return componentRegistryFactory.getComponentRegistry(space, null, userCredentials, groupId);
120        } catch (UserUnauthorizedException uuEx) {
121            LOG.warn("Unauthorized access to {} registry by user {}", space,
122                    userCredentials);
123            LOG.debug("Details for unauthorized access", uuEx);
124            throw new WebApplicationException(uuEx, Status.FORBIDDEN);
125        }
126    }
127
128    /**
129     *
130     * @return Principal of current request
131     * @throws IllegalArgumentException If no user principal found
132     */
133    private Principal checkAndGetUserPrincipal()
134            throws AuthenticationFailException {
135        Principal principal = security.getUserPrincipal();
136        if (principal == null) {
137            throw new AuthenticationFailException("No user principal found.");
138        }
139        return principal;
140    }
141
142    private UserCredentials getUserCredentials(Principal userPrincipal) {
143        UserCredentials userCredentials = null;
144        if (userPrincipal != null) {
145            userCredentials = new UserCredentials(userPrincipal);
146        }
147        return userCredentials;
148    }
149
150    private ComponentRegistry initialiseRegistry(String space, String groupId) throws AuthenticationFailException {
151        //checking credentials
152        RegistrySpace regSpace = RegistrySpace.valueOf(space.toUpperCase());
153        UserCredentials user = this.getUserCredentials(this.checkAndGetUserPrincipal());
154        // initializing the registry
155        Number groupIdNumber = null;
156        if (groupId != null && !groupId.isEmpty()) {
157            groupIdNumber = Integer.parseInt(groupId);
158        }
159
160        return this.getRegistry(regSpace, groupIdNumber);
161    }
162
163    @Override
164    @GET
165    @Path("/components")
166    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
167        MediaType.APPLICATION_JSON})
168    public List<ComponentDescription> getRegisteredComponents(
169            @QueryParam(REGISTRY_SPACE_PARAM) @DefaultValue("published") String registrySpace,
170            @QueryParam(GROUPID_PARAM) String groupId)
171            throws ComponentRegistryException, IOException {
172        long start = System.currentTimeMillis();
173
174        try {
175            ComponentRegistry cr = this.initialiseRegistry(registrySpace, groupId);
176            List<ComponentDescription> result = cr.getComponentDescriptions();
177            LOG.debug(
178                    "Releasing {} registered components into the world ({} millisecs)",
179                    result.size(), (System.currentTimeMillis() - start));
180            return result;
181        } catch (AuthenticationFailException e) {
182            response.sendError(Status.UNAUTHORIZED.getStatusCode(), e.toString());
183            return new ArrayList<ComponentDescription>();
184
185        } catch (UserUnauthorizedException e) {
186            response.sendError(Status.FORBIDDEN.getStatusCode(), e.toString());
187            return new ArrayList<ComponentDescription>();
188
189        }
190
191    }
192
193    @Override
194    @GET
195    @Path("/profiles")
196    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
197        MediaType.APPLICATION_JSON})
198    public List<ProfileDescription> getRegisteredProfiles(
199            @QueryParam(REGISTRY_SPACE_PARAM) @DefaultValue("published") String registrySpace,
200            @QueryParam(METADATA_EDITOR_PARAM) @DefaultValue("false") boolean metadataEditor,
201            @QueryParam(GROUPID_PARAM) String groupId)
202            throws ComponentRegistryException, IOException {
203
204        long start = System.currentTimeMillis();
205        try {
206            ComponentRegistry cr = this.initialiseRegistry(registrySpace, groupId);
207            List<ProfileDescription> result = (metadataEditor) ? cr.getProfileDescriptionsForMetadaEditor() : cr.getProfileDescriptions();
208            LOG.debug(
209                    "Releasing {} registered components into the world ({} millisecs)",
210                    result.size(), (System.currentTimeMillis() - start));
211            return result;
212        } catch (AuthenticationFailException e) {
213            response.sendError(Status.UNAUTHORIZED.getStatusCode(), e.toString());
214            return new ArrayList<ProfileDescription>();
215
216        } catch (UserUnauthorizedException e) {
217            response.sendError(Status.FORBIDDEN.getStatusCode(), e.toString());
218            return new ArrayList<ProfileDescription>();
219        }
220    }
221
222    @Override
223    @GET
224    @Path("/components/{componentId}")
225    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
226        MediaType.APPLICATION_JSON})
227    public Response getRegisteredComponent(
228            @PathParam("componentId") String componentId) throws IOException {
229        LOG.debug("Component with id: {} is requested.", componentId);
230        try {
231            CMDComponentSpec mdComponent = this.getBaseRegistry().getMDComponentAccessControlled(componentId);
232            return Response.ok(mdComponent).build();
233        } catch (ItemNotFoundException e) {
234            return Response.status(Status.NOT_FOUND).build();
235        } catch (ComponentRegistryException e1) {
236            return Response.serverError().status(Status.CONFLICT).build();
237        } catch (AuthenticationFailException e) {
238            return Response.serverError().status(Status.UNAUTHORIZED).build();
239        } catch (UserUnauthorizedException e) {
240            return Response.serverError().status(Status.FORBIDDEN).build();
241        }
242    }
243
244    @Override
245    @GET
246    @Path("/profiles/{profileId}")
247    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
248        MediaType.APPLICATION_JSON})
249    public Response getRegisteredProfile(
250            @PathParam("profileId") String profileId) throws IOException {
251        LOG.debug("Profile with id {} is requested.", profileId);
252        try {
253            CMDComponentSpec mdProfile = this.getBaseRegistry().getMDProfileAccessControled(profileId);
254            return Response.ok(mdProfile).build();
255        } catch (ItemNotFoundException e) {
256            return Response.status(Status.NOT_FOUND).build();
257        } catch (ComponentRegistryException e1) {
258            return Response.serverError().status(Status.CONFLICT).build();
259        } catch (UserUnauthorizedException e) {
260            return Response.serverError().status(Status.FORBIDDEN).build();
261        } catch (AuthenticationFailException e) {
262            return Response.serverError().status(Status.UNAUTHORIZED).build();
263        }
264    }
265
266    @Override
267    @GET
268    @Path("/components/{componentId}/{rawType}")
269    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
270    public Response getRegisteredComponentRawType(
271            @PathParam("componentId") final String componentId, @PathParam("rawType") String rawType) throws ComponentRegistryException {
272
273        LOG.debug("Component with id: {} and rawType: {} is requested.", componentId, rawType);
274        try {
275            final ComponentRegistry registry = this.getBaseRegistry();
276            try {
277                ComponentDescription desc = registry.getComponentDescriptionAccessControlled(componentId);
278                StreamingOutput result = null;
279                String fileName = desc.getName() + "." + rawType;
280                if ("xml".equalsIgnoreCase(rawType)) {
281                    result = new StreamingOutput() {
282                        @Override
283                        public void write(OutputStream output) throws IOException,
284                                WebApplicationException {
285                            try {
286                                try {
287                                    try {
288                                        registry.getMDComponentAsXml(componentId, output);
289                                    } catch (ItemNotFoundException e1) {
290                                        LOG.warn("Could not retrieve component {}",
291                                                componentId);
292                                        LOG.debug("Details", e1);
293                                        throw new WebApplicationException(e1, Response
294                                                .serverError()
295                                                .status(Status.INTERNAL_SERVER_ERROR)
296                                                .build());
297                                    }
298                                } catch (ComponentRegistryException e) {
299                                    LOG.warn("Could not retrieve component {}",
300                                            componentId);
301                                    LOG.debug("Details", e);
302                                    throw new WebApplicationException(e, Response
303                                            .serverError()
304                                            .status(Status.INTERNAL_SERVER_ERROR)
305                                            .build());
306                                }
307
308                            } catch (UserUnauthorizedException e2) {
309                                LOG.error(e2.toString());
310                            }
311                        }
312                    };
313                } else if ("xsd".equalsIgnoreCase(rawType)) {
314                    result = new StreamingOutput() {
315                        @Override
316                        public void write(OutputStream output) throws IOException,
317                                WebApplicationException {
318                            try {
319                                try {
320                                    try {
321                                        registry.getMDComponentAsXsd(componentId, output);
322                                    } catch (ItemNotFoundException e1) {
323                                        LOG.warn("Could not retrieve component {}",
324                                                componentId);
325                                        LOG.debug("Details", e1);
326                                        throw new WebApplicationException(e1, Response
327                                                .serverError()
328                                                .status(Status.INTERNAL_SERVER_ERROR)
329                                                .build());
330                                    }
331                                } catch (ComponentRegistryException e) {
332                                    LOG.warn("Could not retrieve component {}",
333                                            componentId);
334                                    LOG.debug("Details", e);
335                                    throw new WebApplicationException(e, Response
336                                            .serverError()
337                                            .status(Status.INTERNAL_SERVER_ERROR)
338                                            .build());
339                                }
340                            } catch (UserUnauthorizedException e2) {
341                                LOG.error(e2.toString());
342                            }
343
344                        }
345                    };
346                } else {
347                    throw new WebApplicationException(Response
348                            .serverError()
349                            .entity("unsupported rawType: " + rawType
350                            + " (only xml or xsd are supported)").build());
351                }
352                return createDownloadResponse(result, fileName);
353
354            } catch (UserUnauthorizedException e2) {
355                return Response.status(Status.FORBIDDEN).build();
356            }
357        } catch (ItemNotFoundException e3) {
358            return Response.status(Status.NOT_FOUND).build();
359        } catch (AuthenticationFailException e) {
360            return Response.serverError().status(Status.UNAUTHORIZED).build();
361        }
362    }
363
364    @Override
365    @GET
366    @Path("/components/usage/{componentId}")
367    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
368        MediaType.APPLICATION_JSON})
369    public List<BaseDescription> getComponentUsage(
370            @PathParam("componentId") String componentId) throws ComponentRegistryException, IOException {
371
372        final long start = System.currentTimeMillis();
373        try {
374            ComponentRegistry registry = this.getBaseRegistry();
375            List<ComponentDescription> components = registry.getUsageInComponents(componentId);
376            List<ProfileDescription> profiles = registry.getUsageInProfiles(componentId);
377
378            LOG.debug(
379                    "Found {} components and {} profiles that use component {} ({} millisecs)",
380                    components.size(), profiles.size(), componentId,
381                    (System.currentTimeMillis() - start));
382
383            List<BaseDescription> usages = new ArrayList<BaseDescription>(components.size() + profiles.size());
384            usages.addAll(components);
385            usages.addAll(profiles);
386
387            return usages;
388        } catch (ComponentRegistryException e) {
389            LOG.warn("Could not retrieve profile usage {}", componentId);
390            LOG.debug("Details", e);
391            return new ArrayList<BaseDescription>();
392        } catch (AuthenticationFailException e1) {
393            response.sendError(Status.UNAUTHORIZED.getStatusCode());
394            return new ArrayList<BaseDescription>();
395        }
396    }
397
398    @Override
399    @GET
400    @Path("/profiles/{profileId}/comments")
401    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
402        MediaType.APPLICATION_JSON})
403    public List<Comment> getCommentsFromProfile(
404            @PathParam("profileId") String profileId)
405            throws ComponentRegistryException, IOException {
406        long start = System.currentTimeMillis();
407        try {
408            List<Comment> comments = this.getBaseRegistry().getCommentsInProfile(profileId);
409            LOG.debug(
410                    "Releasing {} registered comments in profile into the world ({} millisecs)",
411                    comments.size(), (System.currentTimeMillis() - start));
412            return comments;
413        } catch (ItemNotFoundException e) {
414            response.sendError(Status.NOT_FOUND.getStatusCode());
415            return new ArrayList<Comment>();
416
417        } catch (UserUnauthorizedException e) {
418            response.sendError(Status.FORBIDDEN.getStatusCode());
419            return new ArrayList<Comment>();
420        } catch (AuthenticationFailException e1) {
421            response.sendError(Status.UNAUTHORIZED.getStatusCode());
422            return new ArrayList<Comment>();
423        }
424    }
425
426    @Override
427    @GET
428    @Path("/components/{componentId}/comments")
429    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
430        MediaType.APPLICATION_JSON})
431    public List<Comment> getCommentsFromComponent(
432            @PathParam("componentId") String componentId)
433            throws ComponentRegistryException, IOException {
434        long start = System.currentTimeMillis();
435        try {
436            List<Comment> comments = this.getBaseRegistry().getCommentsInComponent(componentId);
437            LOG.debug(
438                    "Releasing {} registered comments in Component into the world ({} millisecs)",
439                    comments.size(), (System.currentTimeMillis() - start));
440            return comments;
441        } catch (ItemNotFoundException e) {
442            response.sendError(Status.NOT_FOUND.getStatusCode());
443            return new ArrayList<Comment>();
444        } catch (UserUnauthorizedException e1) {
445            response.sendError(Status.FORBIDDEN.getStatusCode());
446            return new ArrayList<Comment>();
447        } catch (AuthenticationFailException e1) {
448            response.sendError(Status.UNAUTHORIZED.getStatusCode());
449            return new ArrayList<Comment>();
450        }
451    }
452
453    @Override
454    @GET
455    @Path("/profiles/{profileId}/comments/{commentId}")
456    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
457        MediaType.APPLICATION_JSON})
458    public Comment getSpecifiedCommentFromProfile(
459            @PathParam("profileId") String profileId,
460            @PathParam("commentId") String commentId)
461            throws ComponentRegistryException, IOException {
462
463        LOG.debug("Comments of profile with id {} are requested.", commentId);
464        try {
465
466            return this.getBaseRegistry().getSpecifiedCommentInProfile(profileId, commentId);
467        } catch (ItemNotFoundException e) {
468            response.sendError(Status.NOT_FOUND.getStatusCode());
469            return new Comment();
470        } catch (UserUnauthorizedException e1) {
471            response.sendError(Status.FORBIDDEN.getStatusCode());
472            return new Comment();
473        } catch (AuthenticationFailException e1) {
474            response.sendError(Status.UNAUTHORIZED.getStatusCode());
475            return new Comment();
476        }
477    }
478
479    @Override
480    @GET
481    @Path("/components/{componentId}/comments/{commentId}")
482    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
483        MediaType.APPLICATION_JSON})
484    public Comment getSpecifiedCommentFromComponent(
485            @PathParam("componentId") String componentId,
486            @PathParam("commentId") String commentId)
487            throws ComponentRegistryException, IOException {
488        LOG.debug("Comments of component with id {} are requested.", commentId);
489        try {
490
491            return this.getBaseRegistry().getSpecifiedCommentInComponent(componentId, commentId);
492        } catch (ItemNotFoundException e) {
493            response.sendError(Status.NOT_FOUND.getStatusCode());
494            return new Comment();
495        } catch (UserUnauthorizedException e1) {
496            response.sendError(Status.FORBIDDEN.getStatusCode());
497            return new Comment();
498        } catch (AuthenticationFailException e1) {
499            response.sendError(Status.UNAUTHORIZED.getStatusCode());
500            return new Comment();
501        }
502    }
503
504    /**
505     *
506     * Purely helper method for my front-end (FLEX) which only does post/get
507     * requests. The query param is checked and the "proper" method is called.
508     *
509     * @param profileId
510     * @param method
511     * @return
512     */
513    // TODO: test via POSTMAN
514    @Override
515    @POST
516    @Path("/profiles/{profileId}")
517    public Response manipulateRegisteredProfile(
518            @PathParam("profileId") String profileId,
519            @FormParam("method") String method) {
520        if ("delete".equalsIgnoreCase(method)) {
521            return this.deleteRegisteredProfile(profileId);
522        } else {
523            return Response.ok().build();
524        }
525    }
526
527    // TODO: test via POSTMAN
528    @Override
529    @POST
530    @Path("/profiles/{profileId}/comments/{commentId}")
531    public Response manipulateCommentFromProfile(
532            @PathParam("profileId") String profileId,
533            @PathParam("commentId") String commentId,
534            @FormParam("method") String method) {
535        if ("delete".equalsIgnoreCase(method)) {
536            return this.deleteCommentFromProfile(profileId, commentId);
537        } else {
538            return Response.ok().build();
539        }
540    }
541
542    // TODO: test via POSTMAN
543    @Override
544    @POST
545    @Path("/components/{componentId}/comments/{commentId}")
546    public Response manipulateCommentFromComponent(
547            @PathParam("componentId") String componentId,
548            @PathParam("commentId") String commentId,
549            @FormParam("method") String method) {
550        if ("delete".equalsIgnoreCase(method)) {
551            return this.deleteCommentFromComponent(componentId, commentId);
552        } else {
553            return Response.ok().build();
554        }
555    }
556
557    // TODO: test via POSTMAN
558    @Override
559    @POST
560    @Path("/profiles/{profileId}/publish")
561    @Consumes("multipart/form-data")
562    public Response publishRegisteredProfile(
563            @PathParam("profileId") String profileId,
564            @FormDataParam(DATA_FORM_FIELD) InputStream input,
565            @FormDataParam(NAME_FORM_FIELD) String name,
566            @FormDataParam(DESCRIPTION_FORM_FIELD) String description,
567            @FormDataParam(GROUP_FORM_FIELD) String group,
568            @FormDataParam(DOMAIN_FORM_FIELD) String domainName) {
569
570        try {
571            Principal principal = checkAndGetUserPrincipal();
572            ComponentRegistry registry = this.getBaseRegistry();
573            ProfileDescription desc = registry.getProfileDescriptionAccessControlled(profileId);
574            if (desc != null) {
575                if (desc.isPublic()) {
576                    return Response.status(Status.CONFLICT).entity("Cannot publish already published profile.")
577                            .build();
578                }
579                desc.setPublic(true);
580                this.updateDescription(desc, name, description, domainName, group);
581                return this.register(input, desc, new PublishAction(principal), registry);
582            } else {
583                LOG.error("Update of nonexistent profile {} failed.", profileId);
584                return Response
585                        .serverError()
586                        .entity("Invalid id, cannot update nonexistent profile")
587                        .build();
588            }
589
590        } catch (AuthenticationFailException e) {
591            return Response.serverError().status(Status.UNAUTHORIZED)
592                    .build();
593        } catch (ItemNotFoundException e1) {
594            return Response.status(Status.NOT_FOUND).entity(e1.getMessage()).build();
595        } catch (ComponentRegistryException e) {
596            LOG.warn("Could not retrieve profile {}", profileId);
597            LOG.debug("Details", e);
598            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
599                    .build();
600        } catch (UserUnauthorizedException ex) {
601            return Response.status(Status.FORBIDDEN).entity(ex.getMessage())
602                    .build();
603        }
604    }
605
606    @Override
607    @POST
608    @Path("/profiles/{profileId}/update")
609    @Consumes("multipart/form-data")
610    public Response updateRegisteredProfile(
611            @PathParam("profileId") String profileId,
612            @FormDataParam(DATA_FORM_FIELD) InputStream input,
613            @FormDataParam(NAME_FORM_FIELD) String name,
614            @FormDataParam(DESCRIPTION_FORM_FIELD) String description,
615            @FormDataParam(GROUP_FORM_FIELD) String group,
616            @FormDataParam(DOMAIN_FORM_FIELD) String domainName) {
617        try {
618            ComponentRegistry br = this.getBaseRegistry();
619            ProfileDescription desc = br.getProfileDescriptionAccessControlled(profileId);
620            if (desc != null) {
621                if (desc.isPublic()) {
622                    return Response.status(Status.CONFLICT).entity("Cannot update already published profile.")
623                            .build();
624
625                }
626                Number groupId;
627                RegistrySpace space;
628                List<Number> groupIds = br.getItemGroups(profileId);
629                if (groupIds == null || groupIds.isEmpty()) {
630                    groupId = null;
631                    space = RegistrySpace.PRIVATE;
632                } else {
633                    groupId = groupIds.get(0);
634                    space = RegistrySpace.GROUP;
635                }
636
637                updateDescription(desc, name, description, domainName, group);
638                ComponentRegistry cr = this.getRegistry(space, groupId);
639                return register(input, desc, new UpdateAction(), cr);
640            } else {
641                LOG.error("Update of nonexistent id (" + profileId
642                        + ") failed.");
643                return Response
644                        .serverError()
645                        .entity("Invalid id, cannot update nonexistent profile")
646                        .build();
647            }
648        } catch (ComponentRegistryException e) {
649            LOG.warn("Could not retrieve profile {}", profileId);
650            LOG.debug("Details", e);
651            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
652                    .build();
653
654        } catch (UserUnauthorizedException ex) {
655            return Response.status(Status.FORBIDDEN).entity(ex.getMessage())
656                    .build();
657
658        } catch (ItemNotFoundException ex2) {
659            return Response.status(Status.NOT_FOUND).entity(ex2.getMessage())
660                    .build();
661        } catch (AuthenticationFailException e1) {
662            return Response.status(Status.UNAUTHORIZED).entity(e1.getMessage())
663                    .build();
664        }
665
666    }
667
668    /**
669     *
670     * Purely helper method for my front-end (FLEX) which van only do post/get
671     * requests. The query param is checked and the "proper" method is called.
672     *
673     * @param componentId
674     * @param method
675     * @return
676     */
677    // twan: why do we need it?
678    // TODO: test via POSTMAN
679    @Override
680    @POST
681    @Path("/components/{componentId}")
682    public Response manipulateRegisteredComponent(
683            @PathParam("componentId") String componentId,
684            @FormParam("method") String method) {
685        if ("delete".equalsIgnoreCase(method)) {
686            return this.deleteRegisteredComponent(componentId);
687        } else {
688            return Response.ok("Nothing to do, not 'delete' method").build();
689        }
690    }
691
692    // TODO: test via POSTMAN
693    @Override
694    @POST
695    @Path("/components/{componentId}/publish")
696    @Consumes("multipart/form-data")
697    public Response publishRegisteredComponent(
698            @PathParam("componentId") String componentId,
699            @FormDataParam(DATA_FORM_FIELD) InputStream input,
700            @FormDataParam(NAME_FORM_FIELD) String name,
701            @FormDataParam(DESCRIPTION_FORM_FIELD) String description,
702            @FormDataParam(GROUP_FORM_FIELD) String group,
703            @FormDataParam(DOMAIN_FORM_FIELD) String domainName) {
704
705        try {
706            Principal principal = checkAndGetUserPrincipal();
707            ComponentRegistry registry = this.getBaseRegistry();
708            ComponentDescription desc = registry.getComponentDescriptionAccessControlled(componentId);
709            if (desc != null) {
710                if (desc.isPublic()) {
711                    return Response.status(Status.CONFLICT).entity("Cannot publish already published omponent.")
712                            .build();
713                }
714                desc.setPublic(true);
715                this.updateDescription(desc, name, description, domainName, group);
716                return this.register(input, desc, new PublishAction(principal), registry);
717            } else {
718                LOG.error("Update of nonexistent id (" + componentId
719                        + ") failed.");
720                return Response
721                        .serverError()
722                        .entity("Invalid id, cannot update nonexistent profile")
723                        .build();
724            }
725        } catch (AuthenticationFailException e) {
726            LOG.warn("Could not retrieve component {}", componentId);
727            LOG.debug("Details", e);
728            return Response.serverError().status(Status.UNAUTHORIZED)
729                    .build();
730        } catch (ComponentRegistryException e) {
731            LOG.warn("Could not retrieve component {}", componentId);
732            LOG.debug("Details", e);
733            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
734                    .build();
735        } catch (ItemNotFoundException e) {
736            LOG.warn("Could not retrieve component {}", componentId);
737            LOG.debug("Details", e);
738            return Response.serverError().status(Status.NOT_FOUND)
739                    .build();
740        } catch (UserUnauthorizedException ex) {
741            return Response.status(Status.FORBIDDEN).entity(ex.getMessage())
742                    .build();
743        }
744    }
745
746    @Override
747    @POST
748    @Path("/components/{componentId}/update")
749    @Consumes("multipart/form-data")
750    public Response updateRegisteredComponent(
751            @PathParam("componentId") String componentId,
752            @FormDataParam(DATA_FORM_FIELD) InputStream input,
753            @FormDataParam(NAME_FORM_FIELD) String name,
754            @FormDataParam(DESCRIPTION_FORM_FIELD) String description,
755            @FormDataParam(GROUP_FORM_FIELD) String group,
756            @FormDataParam(DOMAIN_FORM_FIELD) String domainName) {
757        try {
758            ComponentRegistry br = this.getBaseRegistry();
759            ComponentDescription desc =br.getComponentDescriptionAccessControlled(componentId);
760            if (desc != null) {
761                if (desc.isPublic()) {
762                    return Response.status(Status.CONFLICT).entity("Cannot update already published component.")
763                            .build();
764                }
765                Number groupId;
766                RegistrySpace space;
767                List<Number> groupIds = br.getItemGroups(componentId);
768                if (groupIds == null || groupIds.isEmpty()) {
769                    groupId = null;
770                    space = RegistrySpace.PRIVATE;
771                } else {
772                    groupId = groupIds.get(0);
773                    space = RegistrySpace.GROUP;
774                }
775
776                this.updateDescription(desc, name, description, domainName, group);
777                ComponentRegistry cr = this.getRegistry(space, groupId);
778                return this.register(input, desc, new UpdateAction(), cr);
779            } else {
780                LOG.error("Update of nonexistent id (" + componentId
781                        + ") failed.");
782                return Response
783                        .serverError()
784                        .entity("Invalid id, cannot update nonexistent component")
785                        .build();
786            }
787        } catch (ComponentRegistryException e) {
788            LOG.warn("Could not retrieve component {}", componentId);
789            LOG.debug("Details", e);
790            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
791                    .build();
792        } catch (UserUnauthorizedException ex) {
793            return Response.status(Status.FORBIDDEN).entity(ex.getMessage())
794                    .build();
795        } catch (ItemNotFoundException ex2) {
796            return Response.status(Status.FORBIDDEN).entity(ex2.getMessage())
797                    .build();
798        } catch (AuthenticationFailException e1) {
799            return Response.status(Status.UNAUTHORIZED).entity(e1.getMessage())
800                    .build();
801        }
802
803    }
804
805    private void updateDescription(BaseDescription desc, String name,
806            String description, String domainName, String group) {
807        desc.setName(name);
808        desc.setDescription(description);
809        desc.setDomainName(domainName);
810        desc.setGroupName(group);
811        desc.setRegistrationDate(new Date());
812    }
813
814    @Override
815    @DELETE
816    @Path("/components/{componentId}")
817    public Response deleteRegisteredComponent(
818            @PathParam("componentId") String componentId) {
819        try {
820            ComponentRegistry registry = this.getBaseRegistry();
821            LOG.debug("Component with id {} set for deletion.", componentId);
822            registry.deleteMDComponent(componentId, false);
823        } catch (DeleteFailedException e) {
824            LOG.info("Component with id {} deletion failed. Reason: {}",
825                    componentId, e.getMessage());
826            LOG.debug("Deletion failure details:", e);
827            return Response.status(Status.FORBIDDEN)
828                    .entity("" + e.getMessage()).build();
829        } catch (ComponentRegistryException e) {
830            LOG.warn("Component with id " + componentId + " deletion failed.",
831                    e);
832            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
833                    .build();
834        } catch (ItemNotFoundException e) {
835            LOG.warn("Component with id " + componentId + " is not found.",
836                    e);
837            return Response.serverError().status(Status.NOT_FOUND)
838                    .build();
839        } catch (IOException e) {
840            LOG.error("Component with id " + componentId + " deletion failed.",
841                    e);
842            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
843                    .build();
844        } catch (UserUnauthorizedException e) {
845            LOG.info("Component with id {} deletion failed: {}", componentId,
846                    e.getMessage());
847            LOG.debug("Deletion failure details:", e);
848            return Response.serverError().status(Status.FORBIDDEN)
849                    .entity("" + e.getMessage()).build();
850        } catch (AuthenticationFailException e1) {
851            return Response.status(Status.UNAUTHORIZED).entity(e1.getMessage())
852                    .build();
853        }
854
855        LOG.info("Component with id: {} deleted.", componentId);
856        return Response.ok().build();
857    }
858
859    @Override
860    @DELETE
861    @Path("/profiles/{profileId}")
862    public Response deleteRegisteredProfile(
863            @PathParam("profileId") String profileId) {
864        try {
865            LOG.debug("Profile with id: {} set for deletion.", profileId);
866            this.getBaseRegistry().deleteMDProfile(profileId);
867        } catch (DeleteFailedException e) {
868            LOG.info("Profile with id: {} deletion failed: {}", profileId,
869                    e.getMessage());
870            LOG.debug("Deletion failure details:", e);
871            return Response.serverError().status(Status.FORBIDDEN)
872                    .entity("" + e.getMessage()).build();
873        } catch (ComponentRegistryException e) {
874            LOG.warn("Could not retrieve component", e);
875            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
876                    .build();
877        } catch (ItemNotFoundException e) {
878            LOG.warn("Profile with id " + profileId + " is not found.",
879                    e);
880            return Response.serverError().status(Status.NOT_FOUND)
881                    .build();
882        } catch (IOException e) {
883            LOG.error("Profile with id: " + profileId + " deletion failed.", e);
884            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
885                    .build();
886        } catch (UserUnauthorizedException e) {
887            LOG.info("Profile with id: {} deletion failed: {}", profileId,
888                    e.getMessage());
889            LOG.debug("Deletion failure details:", e);
890            return Response.serverError().status(Status.FORBIDDEN)
891                    .entity("" + e.getMessage()).build();
892        } catch (AuthenticationFailException e1) {
893            return Response.status(Status.UNAUTHORIZED).entity(e1.getMessage())
894                    .build();
895        }
896
897        LOG.info("Profile with id: {} deleted.", profileId);
898        return Response.ok().build();
899    }
900
901    @Override
902    @DELETE
903    @Path("/profiles/{profileId}/comments/{commentId}")
904    public Response deleteCommentFromProfile(
905            @PathParam("profileId") String profileId,
906            @PathParam("commentId") String commentId) {
907        try {
908            final ComponentRegistry registry = this.getBaseRegistry();
909            try {
910                final Comment comment = registry.getSpecifiedCommentInProfile(profileId, commentId);
911                if (comment != null
912                        && profileId.equals(comment.getComponentId())) {
913                    LOG.debug("Comment with id: {} set for deletion.", commentId);
914                    registry.deleteComment(commentId);
915                } else {
916                    throw new ComponentRegistryException(
917                            "Comment not found for specified profile");
918                }
919            } catch (ItemNotFoundException e1) {
920                LOG.info("Comment with id: {} deletion failed: {}", commentId,
921                        e1.getMessage());
922                LOG.debug("Deletion failure details:", e1);
923                return Response.serverError().status(Status.NOT_FOUND)
924                        .entity("" + e1.getMessage()).build();
925            }
926
927        } catch (DeleteFailedException e) {
928            LOG.info("Comment with id: {} deletion failed: {}", commentId,
929                    e.getMessage());
930            LOG.debug("Deletion failure details:", e);
931            return Response.serverError().status(Status.FORBIDDEN)
932                    .entity("" + e.getMessage()).build();
933        } catch (ComponentRegistryException e) {
934            LOG.info("Could not retrieve component", e);
935            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
936                    .build();
937        } catch (IOException e) {
938            LOG.error("Comment with id: " + commentId + " deletion failed.", e);
939            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
940                    .build();
941        } catch (UserUnauthorizedException e) {
942            LOG.info("Comment with id: {} deletion failed: {}", commentId,
943                    e.getMessage());
944            LOG.debug("Deletion failure details:", e);
945            return Response.serverError().status(Status.FORBIDDEN)
946                    .entity("" + e.getMessage()).build();
947        } catch (AuthenticationFailException e1) {
948            return Response.status(Status.UNAUTHORIZED).entity(e1.getMessage())
949                    .build();
950        }
951
952        LOG.info("Comment with id: {} deleted.", commentId);
953        return Response.ok("Comment with id " + commentId + " deleted.").build();
954    }
955
956    @Override
957    @DELETE
958    @Path("/components/{componentId}/comments/{commentId}")
959    public Response deleteCommentFromComponent(
960            @PathParam("componentId") String componentId,
961            @PathParam("commentId") String commentId) {
962        try {
963            final ComponentRegistry registry = this.getBaseRegistry();
964            final Comment comment = registry.getSpecifiedCommentInComponent(componentId, commentId);
965            if (comment != null
966                    && componentId.equals(comment.getComponentId())) {
967                LOG.debug("Comment with id: {} set for deletion.", commentId);
968                registry.deleteComment(commentId);
969            } else {
970                throw new ComponentRegistryException(
971                        "Comment not found for specified component");
972            }
973        } catch (ItemNotFoundException e) {
974            LOG.info("Comment with id: {} deletion failed: {}", commentId,
975                    e.getMessage());
976            LOG.debug("Deletion failure details:", e);
977            return Response.serverError().status(Status.NOT_FOUND)
978                    .entity("" + e.getMessage()).build();
979        } catch (DeleteFailedException e) {
980            LOG.info("Comment with id: {} deletion failed: {}", commentId,
981                    e.getMessage());
982            LOG.debug("Deletion failure details:", e);
983            return Response.serverError().status(Status.FORBIDDEN)
984                    .entity("" + e.getMessage()).build();
985        } catch (ComponentRegistryException e) {
986            LOG.info("Could not retrieve component", e);
987            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
988                    .build();
989        } catch (IOException e) {
990            LOG.error("Comment with id: " + commentId + " deletion failed.", e);
991            return Response.serverError().status(Status.INTERNAL_SERVER_ERROR)
992                    .build();
993        } catch (UserUnauthorizedException e) {
994            LOG.info("Comment with id: {} deletion failed: {}", commentId,
995                    e.getMessage());
996            LOG.debug("Deletion failure details:", e);
997            return Response.serverError().status(Status.FORBIDDEN)
998                    .entity("" + e.getMessage()).build();
999        } catch (AuthenticationFailException e1) {
1000            return Response.status(Status.UNAUTHORIZED).entity(e1.getMessage())
1001                    .build();
1002        }
1003
1004        LOG.info("Comment with id: {} deleted.", commentId);
1005        return Response.ok("Comment with id " + commentId + " deleted.").build();
1006    }
1007
1008    @Override
1009    @GET
1010    @Path("/profiles/{profileId}/{rawType}")
1011    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML})
1012    public Response getRegisteredProfileRawType(
1013            @PathParam("profileId") final String profileId,
1014            @PathParam("rawType") String rawType) throws ComponentRegistryException {
1015
1016
1017        LOG.debug("Profile with id {} and rawType {} is requested.", profileId,
1018                rawType);
1019        try {
1020            final ComponentRegistry registry = this.getBaseRegistry();
1021
1022            ProfileDescription desc = registry.getProfileDescriptionAccessControlled(profileId);
1023            if (desc == null) {
1024                return Response.status(Status.NOT_FOUND).build();
1025            }
1026
1027            StreamingOutput result = null;
1028            String fileName = desc.getName() + "." + rawType;
1029            if ("xml".equalsIgnoreCase(rawType)) {
1030                result = new StreamingOutput() {
1031                    @Override
1032                    public void write(OutputStream output) throws IOException,
1033                            WebApplicationException {
1034                        try {
1035                            registry.getMDProfileAsXml(profileId, output);
1036                        } catch (Exception e) {
1037                            LOG.warn("Could not retrieve component {}",
1038                                    profileId);
1039                            LOG.debug("Details", e);
1040                            throw new WebApplicationException(e, Response
1041                                    .serverError()
1042                                    .status(Status.INTERNAL_SERVER_ERROR)
1043                                    .build());
1044                        }
1045                    }
1046                };
1047            } else if ("xsd".equalsIgnoreCase(rawType)) {
1048                result = new StreamingOutput() {
1049                    @Override
1050                    public void write(OutputStream output) throws IOException,
1051                            WebApplicationException {
1052                        try {
1053                            registry.getMDProfileAsXsd(profileId, output);
1054                        } catch (Exception e) {
1055                            LOG.warn("Could not retrieve component {}",
1056                                    profileId);
1057                            LOG.debug("Details", e);
1058                            throw new WebApplicationException(e, Response
1059                                    .serverError()
1060                                    .status(Status.INTERNAL_SERVER_ERROR)
1061                                    .build());
1062                        }
1063                    }
1064                };
1065            } else {
1066                throw new WebApplicationException(Response
1067                        .serverError()
1068                        .entity("unsupported rawType: " + rawType
1069                        + " (only xml or xsd are supported)").build());
1070            }
1071            return createDownloadResponse(result, fileName);
1072        } catch (UserUnauthorizedException ex) {
1073            return Response.status(Status.FORBIDDEN).build();
1074        } catch (ItemNotFoundException e) {
1075            return Response.serverError().status(Status.NOT_FOUND)
1076                    .entity("" + e.getMessage()).build();
1077        } catch (AuthenticationFailException e1) {
1078            return Response.status(Status.UNAUTHORIZED).entity(e1.getMessage())
1079                    .build();
1080        }
1081
1082    }
1083
1084    private Response createDownloadResponse(StreamingOutput result,
1085            String fileName) {
1086        // Making response so it triggers browsers native save as dialog.
1087        Response response = Response
1088                .ok()
1089                .type("application/x-download")
1090                .header("Content-Disposition",
1091                "attachment; filename=\"" + fileName + "\"")
1092                .entity(result).build();
1093        return response;
1094
1095    }
1096
1097    @Override
1098    @POST
1099    @Path("/profiles")
1100    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1101        MediaType.APPLICATION_JSON})
1102    @Consumes("multipart/form-data")
1103    public Response registerProfile(
1104            @FormDataParam(DATA_FORM_FIELD) InputStream input,
1105            @FormDataParam(NAME_FORM_FIELD) String name,
1106            @FormDataParam(DESCRIPTION_FORM_FIELD) String description,
1107            @FormDataParam(GROUP_FORM_FIELD) String group,
1108            @FormDataParam(DOMAIN_FORM_FIELD) String domainName) {
1109        try {
1110            Principal principal = checkAndGetUserPrincipal();
1111            UserCredentials userCredentials = getUserCredentials(principal);
1112            ProfileDescription desc = this.createNewProfileDescription();
1113            desc.setCreatorName(userCredentials.getDisplayName());
1114            desc.setUserId(userCredentials.getPrincipalName()); // Hash used to
1115            // be created
1116            // here, now Id
1117            // is
1118            // constructed
1119            // by impl
1120            desc.setName(name);
1121            desc.setDescription(description);
1122            desc.setGroupName(group);
1123            desc.setDomainName(domainName);
1124            desc.setPublic(false);
1125            LOG.debug("Trying to register Profile: {}", desc);
1126            ComponentRegistry cr = this.getRegistry(RegistrySpace.PRIVATE, null);
1127            return this.register(input, desc, new NewAction(), cr);
1128        } catch (AuthenticationFailException e) {
1129            LOG.debug("Details", e);
1130            return Response.serverError().status(Status.UNAUTHORIZED)
1131                    .build();
1132        } catch (UserUnauthorizedException ex) {
1133            return Response.status(Status.FORBIDDEN).entity(ex.getMessage())
1134                    .build();
1135        }
1136    }
1137
1138    @Override
1139    @POST
1140    @Path("/components")
1141    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1142        MediaType.APPLICATION_JSON})
1143    @Consumes("multipart/form-data")
1144    public Response registerComponent(
1145            @FormDataParam(DATA_FORM_FIELD) InputStream input,
1146            @FormDataParam(NAME_FORM_FIELD) String name,
1147            @FormDataParam(DESCRIPTION_FORM_FIELD) String description,
1148            @FormDataParam(GROUP_FORM_FIELD) String group,
1149            @FormDataParam(DOMAIN_FORM_FIELD) String domainName) {
1150        try {
1151            Principal principal = checkAndGetUserPrincipal();
1152            UserCredentials userCredentials = getUserCredentials(principal);
1153            ComponentDescription desc = createNewComponentDescription();
1154            desc.setCreatorName(userCredentials.getDisplayName());
1155            desc.setUserId(userCredentials.getPrincipalName()); // Hash used to
1156            // be created
1157            // here, now Id
1158            // is
1159            // constructed
1160            // by impl
1161            desc.setName(name);
1162            desc.setDescription(description);
1163            desc.setGroupName(group);
1164            desc.setDomainName(domainName);
1165            desc.setPublic(false);
1166            LOG.debug("Trying to register Component: {}", desc);
1167            ComponentRegistry cr = this.getRegistry(RegistrySpace.PRIVATE, null);
1168            return this.register(input, desc, new NewAction(), cr);
1169        } catch (AuthenticationFailException e) {
1170            LOG.debug("Details", e);
1171            return Response.serverError().status(Status.UNAUTHORIZED)
1172                    .build();
1173        } catch (UserUnauthorizedException ex) {
1174            return Response.status(Status.FORBIDDEN).entity(ex.getMessage())
1175                    .build();
1176        }
1177    }
1178
1179    @Override
1180    @POST
1181    @Path("/components/{componentId}/comments")
1182    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1183        MediaType.APPLICATION_JSON})
1184    @Consumes("multipart/form-data")
1185    public Response registerCommentInComponent(
1186            @FormDataParam(DATA_FORM_FIELD) InputStream input,
1187            @PathParam("componentId") String componentId) throws ComponentRegistryException {
1188        try {
1189            ComponentRegistry registry = this.getBaseRegistry();
1190            ComponentDescription description = registry.getComponentDescriptionAccessControlled(componentId);
1191
1192            LOG.debug("Trying to register comment to {}", componentId);
1193
1194            return this.registerComment(input, description, registry);
1195        } catch (AuthenticationFailException e) {
1196            LOG.debug("Details", e);
1197            return Response.serverError().status(Status.UNAUTHORIZED)
1198                    .build();
1199        } catch (UserUnauthorizedException ex) {
1200            return Response.status(Status.FORBIDDEN)
1201                    .build();
1202        } catch (ItemNotFoundException e) {
1203            return Response.serverError().status(Status.NOT_FOUND)
1204                    .entity("" + e.getMessage()).build();
1205        }
1206    }
1207
1208    // TODO test with the POSTMAN
1209    @Override
1210    @POST
1211    @Path("/profiles/{profileId}/comments")
1212    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1213        MediaType.APPLICATION_JSON})
1214    @Consumes("multipart/form-data")
1215    public Response registerCommentInProfile(
1216            @FormDataParam(DATA_FORM_FIELD) InputStream input,
1217            @PathParam("profileId") String profileId)
1218            throws ComponentRegistryException {
1219        try {
1220            ComponentRegistry registry = this.getBaseRegistry();
1221            ProfileDescription description = registry
1222                    .getProfileDescriptionAccessControlled(profileId);
1223
1224            LOG.debug("Trying to register comment to {}", profileId);
1225
1226            return this.registerComment(input, description, registry);
1227        } catch (AuthenticationFailException e) {
1228            LOG.debug("Details", e);
1229            return Response.serverError().status(Status.UNAUTHORIZED)
1230                    .build();
1231        } catch (UserUnauthorizedException ex) {
1232            return Response.status(Status.FORBIDDEN)
1233                    .build();
1234        } catch (ItemNotFoundException e1) {
1235            return Response.serverError().status(Status.NOT_FOUND)
1236                    .entity("" + e1.getMessage()).build();
1237        }
1238    }
1239
1240    @Override
1241    @GET
1242    @Path("/pingSession")
1243    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1244        MediaType.APPLICATION_JSON})
1245    public Response pingSession() {
1246        boolean stillActive = false;
1247        Principal userPrincipal = security.getUserPrincipal();
1248        if (LOG.isInfoEnabled()) {
1249            LOG.debug("ping by <{}>",
1250                    (userPrincipal == null ? "unauthorized user"
1251                    : userPrincipal.getName()));
1252        }
1253        if (request != null) {
1254            if (userPrincipal != null
1255                    && !ComponentRegistryFactory.ANONYMOUS_USER
1256                    .equals(userPrincipal.getName())) {
1257                stillActive = !((HttpServletRequest) request).getSession()
1258                        .isNew();
1259            }
1260        }
1261        return Response
1262                .ok()
1263                .entity(String.format("<session stillActive=\"%s\"/>",
1264                stillActive)).build();
1265    }
1266
1267    private Response register(InputStream input, BaseDescription desc, RegisterAction action, ComponentRegistry registry) throws UserUnauthorizedException {
1268        try {
1269
1270
1271            DescriptionValidator descriptionValidator = new DescriptionValidator(
1272                    desc);
1273            MDValidator validator = new MDValidator(input, desc, registry, marshaller);
1274            RegisterResponse response = new RegisterResponse();
1275            //obsolete. Make it setstatus
1276            response.setIsInUserSpace(!desc.isPublic());
1277            this.validate(response, descriptionValidator, validator);
1278            if (response.getErrors().isEmpty()) {
1279
1280                CMDComponentSpec spec = validator.getCMDComponentSpec();
1281
1282                // removing filename from spec before it gets extended.
1283                // recursion over all the components
1284                setFileNamesFromListToNull(Collections.singletonList(spec.getCMDComponent()));
1285
1286                try {
1287                    checkForRecursion(validator, registry, desc);
1288
1289                    // Add profile
1290                    int returnCode = action.execute(desc, spec, response,
1291                            registry);
1292                    if (returnCode == 0) {
1293                        response.setRegistered(true);
1294                        response.setDescription(desc);
1295                    } else {
1296                        response.setRegistered(false);
1297                        response.addError("Unable to register at this moment. Internal server error.");
1298                    }
1299                } catch (ComponentRegistryException ex) {
1300                    // Recursion detected
1301                    response.setRegistered(false);
1302                    response.addError("Error while expanding specification. "
1303                            + ex.getMessage());
1304                } catch (ItemNotFoundException ex) {
1305                    // Recursion detected
1306                    response.setRegistered(false);
1307                    response.addError("Error while expanding specification. "
1308                            + ex.getMessage());
1309                }
1310            } else {
1311                LOG.warn("Registration failed with validation errors: {}",
1312                        Arrays.toString(response.getErrors().toArray()));
1313                response.setRegistered(false);
1314            }
1315            LOG.info("Registered new {} {}", desc.isProfile() ? "profile"
1316                    : "component", desc);
1317            response.setIsProfile(desc.isProfile());
1318            return Response.ok(response).build();
1319        } finally {
1320            try {
1321                input.close();// either we read the input or there was an
1322                // exception, we need to close it.
1323            } catch (IOException e) {
1324                LOG.error("Error when closing inputstream: ", e);
1325            }
1326        }
1327    }
1328
1329    /**
1330     *
1331     * @param validator
1332     * @param registry
1333     * @param desc
1334     * @throws ComponentRegistryException if recursion is detected or something
1335     * goes wrong while trying to detect recursion
1336     */
1337    private void checkForRecursion(MDValidator validator,
1338            ComponentRegistry registry, BaseDescription desc)
1339            throws ComponentRegistryException {
1340        try {
1341            // Expand to check for recursion. Operate on copy so that original
1342            // does not get expanded.
1343            final CMDComponentSpec specCopy = validator
1344                    .getCopyOfCMDComponentSpec();
1345            // In case of recursion, the following will throw a
1346            // ComponentRegistryException
1347            registry.getExpander().expandNestedComponent(
1348                    Lists.newArrayList(specCopy.getCMDComponent()), desc.getId());
1349        } catch (JAXBException ex) {
1350            throw new ComponentRegistryException(
1351                    "Unmarshalling failed while preparing recursion detection",
1352                    ex);
1353        }
1354    }
1355
1356    private Response registerComment(InputStream input, BaseDescription description, ComponentRegistry registry) throws UserUnauthorizedException, AuthenticationFailException {
1357        try {
1358            CommentValidator validator = new CommentValidator(input, description, marshaller);
1359            CommentResponse responseLocal = new CommentResponse();
1360
1361            responseLocal.setIsInUserSpace(!description.isPublic());
1362            this.validateComment(responseLocal, validator);
1363            if (responseLocal.getErrors().isEmpty()) {
1364                Comment com = validator.getCommentSpec();
1365                // int returnCode = action.executeComment(com, response,
1366                // registry, principal.getName());
1367
1368                // If user name is left empty, fill it using the user's display
1369                // name
1370
1371                Principal principal = this.checkAndGetUserPrincipal();
1372                UserCredentials userCredentials = this.getUserCredentials(principal);
1373                if (null == com.getUserName() || "".equals(com.getUserName())) {
1374                    if (userCredentials != null) {
1375                        com.setUserName(userCredentials.getDisplayName());
1376                    } else {
1377                        com.setUserName(principal.getName());
1378                    }
1379                }
1380                try {
1381                    int returnCode = registry.registerComment(com,
1382                            principal.getName());
1383                    if (returnCode == 0) {
1384                        responseLocal.setRegistered(true);
1385                        responseLocal.setComment(com);
1386                    } else {
1387                        responseLocal.setRegistered(false);
1388                        responseLocal.addError("Unable to post at this moment. Internal server error.");
1389                    }
1390                    if (com.getComponentId() != null) {
1391                        LOG.info("Posted new comment on component {}",
1392                                com.getComponentId());
1393                    } else {
1394                        LOG.info("Posted new comment on profile {}",
1395                                com.getComponentId());
1396                    }
1397                } catch (ItemNotFoundException e) {
1398                    return Response.serverError().status(Status.NOT_FOUND)
1399                            .entity("" + e.getMessage()).build();
1400                }
1401            } else {
1402                LOG.warn(
1403                        "Posting of comment failed with validation errors: {}",
1404                        Arrays.toString(responseLocal.getErrors().toArray()));
1405                responseLocal.setRegistered(false);
1406            }
1407            return Response.ok(responseLocal).build();
1408        } catch (ComponentRegistryException ex) {
1409            LOG.error("Error while inserting comment: ", ex);
1410            return Response.serverError().entity(ex.getMessage()).build();
1411        } finally {
1412            try {
1413                input.close();// either we read the input or there was an
1414                // exception, we need to close it.
1415            } catch (IOException e) {
1416                LOG.error("Error when closing inputstream: ", e);
1417                return Response.serverError().build();
1418            }
1419        }
1420    }
1421
1422    private ComponentDescription createNewComponentDescription() {
1423        ComponentDescription desc = ComponentDescription.createNewDescription();
1424        desc.setHref(createXlink(desc.getId()));
1425        return desc;
1426    }
1427
1428    private ProfileDescription createNewProfileDescription() {
1429        ProfileDescription desc = ProfileDescription.createNewDescription();
1430        desc.setHref(createXlink(desc.getId()));
1431        return desc;
1432    }
1433
1434    private String createXlink(String id) {
1435        URI uri = uriInfo.getRequestUriBuilder().path(id).build();
1436        return uri.toString();
1437    }
1438
1439    /**
1440     *
1441     * @return The application's base URI as configured in the
1442     * {@link #APPLICATION_BASE_URL_PARAM} context parameter. If correctly
1443     * configured, it should look something like
1444     * "http://catalog.clarin.eu/ds/ComponentRegistry". <em>Be aware that this
1445     * can also be null if configured incorrectly!</em>
1446     *
1447     * @see #APPLICATION_BASE_URL_PARAM
1448     */
1449    private String getApplicationBaseURI() {
1450        return servletContext.getInitParameter(APPLICATION_BASE_URL_PARAM);
1451    }
1452
1453    private void validate(RegisterResponse response, Validator... validators) throws UserUnauthorizedException {
1454        for (Validator validator : validators) {
1455            if (!validator.validate()) {
1456                for (String error : validator.getErrorMessages()) {
1457                    response.addError(error);
1458                }
1459            }
1460        }
1461    }
1462
1463    private void validateComment(CommentResponse response,
1464            Validator... validators) throws UserUnauthorizedException {
1465        for (Validator validator : validators) {
1466            if (!validator.validate()) {
1467                for (String error : validator.getErrorMessages()) {
1468                    response.addError(error);
1469                }
1470            }
1471        }
1472    }
1473
1474    /**
1475     * @param componentRegistryFactory the componentRegistryFactory to set
1476     */
1477    @Override
1478    public void setComponentRegistryFactory(
1479            ComponentRegistryFactory componentRegistryFactory) {
1480        this.componentRegistryFactory = componentRegistryFactory;
1481    }
1482
1483    /**
1484     *
1485     * @param listofcomponents a list of components whose file-names and whose
1486     * childrens' filenames are to be set to null
1487     */
1488    @Override
1489    public void setFileNamesFromListToNull(
1490            List<CMDComponentType> listofcomponents) {
1491
1492        for (CMDComponentType currentcomponent : listofcomponents) {
1493            setFileNamesToNullCurrent(currentcomponent);
1494        }
1495
1496    }
1497
1498    /**
1499     *
1500     * @param currentcomponent a component whose file-name and whose children
1501     * filenames are to be set to null
1502     */
1503    protected void setFileNamesToNullCurrent(CMDComponentType currentcomponent) {
1504        currentcomponent.setFilename(null);
1505        setFileNamesFromListToNull(currentcomponent.getCMDComponent());
1506    }
1507
1508    /**
1509     *
1510     * @param isPublished if "true" then profiles and components from the user's
1511     * workspace, otherwise -- public
1512     * @param limit the number of items to be displayed
1513     * @return rss for the components in the database to which we are currently
1514     * connected
1515     * @throws ComponentRegistryException
1516     * @throws ParseException
1517     */
1518    @Override
1519    @GET
1520    @Path("/components/rss")
1521    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1522        MediaType.APPLICATION_JSON})
1523    public Rss getRssComponent(@QueryParam(GROUPID_PARAM) String groupId,
1524            @QueryParam(REGISTRY_SPACE_PARAM) @DefaultValue("published") String registrySpace,
1525            @QueryParam(NUMBER_OF_RSSITEMS) @DefaultValue("20") String limit)
1526            throws ComponentRegistryException, ParseException, IOException {
1527        List<ComponentDescription> components = null;
1528        try {
1529            ComponentRegistry cr = this.initialiseRegistry(registrySpace, groupId);
1530            components = cr.getComponentDescriptions();
1531        } catch (AuthenticationFailException e) {
1532            response.sendError(Status.UNAUTHORIZED.getStatusCode(), e.toString());
1533            return new Rss();
1534        } catch (UserUnauthorizedException e) {
1535            response.sendError(Status.FORBIDDEN.getStatusCode(), e.toString());
1536            return new Rss();
1537        }
1538        // obsolete, add group Id
1539        final RssCreatorDescriptions instance = new RssCreatorDescriptions(!registrySpace.equalsIgnoreCase("published"), getApplicationBaseURI(), "components",
1540                Integer.parseInt(limit), components,
1541                ComponentUtils.COMPARE_ON_DATE);
1542        final Rss rss = instance.getRss();
1543        LOG.debug("Releasing RSS of {} most recently registered components",
1544                limit);
1545        return rss;
1546    }
1547
1548    /**
1549     *
1550     * @param isPublished if "true" then profiles and components from the user's
1551     * workspace, otherwise -- public
1552     * @param limit the number of items to be displayed
1553     * @return rss for the profiles in the database to which we are currently
1554     * connected
1555     * @throws ComponentRegistryException
1556     * @throws ParseException
1557     */
1558    @Override
1559    @GET
1560    @Path("/profiles/rss")
1561    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1562        MediaType.APPLICATION_JSON})
1563    public Rss getRssProfile(@QueryParam(GROUPID_PARAM) String groupId,
1564            @QueryParam(REGISTRY_SPACE_PARAM) @DefaultValue("published") String registrySpace,
1565            @QueryParam(NUMBER_OF_RSSITEMS) @DefaultValue("20") String limit)
1566            throws ComponentRegistryException, ParseException, IOException {
1567        List<ProfileDescription> profiles = null;
1568        try {
1569            ComponentRegistry cr = this.initialiseRegistry(registrySpace, groupId);
1570            profiles = cr.getProfileDescriptions();
1571        } catch (AuthenticationFailException e) {
1572            response.sendError(Status.UNAUTHORIZED.getStatusCode(), e.toString());
1573            return new Rss();
1574        } catch (UserUnauthorizedException e) {
1575            response.sendError(Status.FORBIDDEN.getStatusCode(), e.toString());
1576            return new Rss();
1577        }
1578        final RssCreatorDescriptions instance = new RssCreatorDescriptions(
1579                !registrySpace.equalsIgnoreCase("published"), getApplicationBaseURI(), "profiles",
1580                Integer.parseInt(limit), profiles,
1581                ComponentUtils.COMPARE_ON_DATE);
1582        final Rss rss = instance.getRss();
1583        LOG.debug("Releasing RSS of {} most recently registered profiles",
1584                limit);
1585        return rss;
1586    }
1587
1588    /**
1589     *
1590     * @param profileId the Id of a profile whose comments are to be rss-ed
1591     * @param isPublished if "true" then profiles and components from the user's
1592     * workspace, otherwise -- public
1593     * @param limit the number of items to be displayed
1594     * @return rss of the comments for a chosen profile
1595     * @throws ComponentRegistryException
1596     * @throws IOException
1597     * @throws JAXBException
1598     * @throws ParseException
1599     */
1600    @Override
1601    @GET
1602    @Path("/profiles/{profileId}/comments/rss")
1603    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1604        MediaType.APPLICATION_JSON})
1605    public Rss getRssOfCommentsFromProfile(
1606            @PathParam("profileId") String profileId,
1607            @QueryParam(NUMBER_OF_RSSITEMS) @DefaultValue("20") String limit)
1608            throws ComponentRegistryException, IOException, JAXBException,
1609            ParseException {
1610        try {
1611
1612            ComponentRegistry cr = this.getBaseRegistry();
1613
1614            final List<Comment> comments = cr.getCommentsInProfile(profileId);
1615            final ProfileDescription pd = cr.getProfileDescriptionAccessControlled(profileId);
1616            final String profileName = pd.getName();
1617            boolean profileIsPrivate = !pd.isPublic();
1618            // obsolete, status must be involved, not boolean profileIsPrivate
1619            final RssCreatorComments instance = new RssCreatorComments(profileIsPrivate,
1620                    getApplicationBaseURI(), Integer.parseInt(limit), profileId,
1621                    profileName, "profile", comments, Comment.COMPARE_ON_DATE);
1622            final Rss rss = instance.getRss();
1623            LOG.debug("Releasing RSS of {} most recent post on profile {}", limit,
1624                    profileId);
1625            return rss;
1626        } catch (UserUnauthorizedException ex) {
1627            response.sendError(Status.FORBIDDEN.getStatusCode());
1628            return new Rss();
1629        } catch (ItemNotFoundException e) {
1630            response.sendError(Status.NOT_FOUND.getStatusCode());
1631            return new Rss();
1632        } catch (AuthenticationFailException e) {
1633            response.sendError(Status.UNAUTHORIZED.getStatusCode(), e.toString());
1634            return new Rss();
1635        }
1636    }
1637
1638    /**
1639     *
1640     * @param componentId the Id of a component whose comments are to be rss-ed
1641     * @param isPublished if "true" then profiles and components from the user's
1642     * workspace, otherwise -- public
1643     * @param limit the number of items to be displayed
1644     * @return rss of the comments for a chosen component
1645     * @throws ComponentRegistryException
1646     * @throws IOException
1647     * @throws JAXBException
1648     * @throws ParseException
1649     */
1650    @Override
1651    @GET
1652    @Path("/components/{componentId}/comments/rss")
1653    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1654        MediaType.APPLICATION_JSON})
1655    public Rss getRssOfCommentsFromComponent(
1656            @PathParam("componentId") String componentId,
1657            @QueryParam(NUMBER_OF_RSSITEMS)
1658            @DefaultValue("20") String limit)
1659            throws ComponentRegistryException, IOException, JAXBException,
1660            ParseException {
1661        try {
1662            ComponentRegistry cr = this.getBaseRegistry();
1663            final List<Comment> comments = cr.getCommentsInComponent(componentId);
1664            final ComponentDescription cd = cr.getComponentDescriptionAccessControlled(componentId);
1665            final String componentName = cd.getName();
1666            final boolean isPrivate = !cd.isPublic();
1667            //oboslete. status must be involved, not boolean isPrivate
1668            final RssCreatorComments instance = new RssCreatorComments(isPrivate,
1669                    getApplicationBaseURI(), Integer.parseInt(limit), componentId,
1670                    componentName, "component", comments, Comment.COMPARE_ON_DATE);
1671            final Rss rss = instance.getRss();
1672            LOG.debug("Releasing RSS of {} most recent post on component {}",
1673                    limit, componentId);
1674            return rss;
1675        } catch (UserUnauthorizedException e) {
1676            response.sendError(Status.FORBIDDEN.getStatusCode());
1677            return new Rss();
1678        } catch (ItemNotFoundException e) {
1679            response.sendError(Status.NOT_FOUND.getStatusCode());
1680            return new Rss();
1681        } catch (AuthenticationFailException e1) {
1682            response.sendError(Status.UNAUTHORIZED.getStatusCode());
1683            return new Rss();
1684        }
1685
1686    }
1687
1688    @Override
1689    @GET
1690    @Path("/AllowedTypes")
1691    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1692        MediaType.APPLICATION_JSON})
1693    public AllowedAttributetypesXML getAllowedAttributeTypes()
1694            throws ComponentRegistryException, IOException, JAXBException,
1695            ParseException {
1696        return (new AllowedAttributetypesXML());
1697    }
1698
1699    @Override
1700    @GET
1701    @Path("/groups/usermembership")
1702    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1703        MediaType.APPLICATION_JSON})
1704    public List<Group> getGroupsTheCurrentUserIsAMemberOf() {
1705        Principal principal = security.getUserPrincipal();
1706        if (principal == null) {
1707            return new ArrayList<Group>();
1708        }
1709        List<Group> groups = groupService.getGroupsOfWhichUserIsAMember(principal.getName());
1710        return groups;
1711    }
1712
1713    @Override
1714    @GET
1715    @Path("/items/{itemId}/groups")
1716    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1717        MediaType.APPLICATION_JSON})
1718    public List<Group> getGroupsTheItemIsAMemberOf(@PathParam("itemId") String itemId) {
1719        return groupService.getGroupsTheItemIsAMemberOf(itemId);
1720    }
1721
1722    @Override
1723    @POST
1724    @Path("/items/{itemId}/transferownership")
1725    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1726        MediaType.APPLICATION_JSON})
1727    public String transferItemOwnershipToGroup(@PathParam("itemId") String itemId,
1728            @QueryParam("groupId") long groupId) throws IOException {
1729        Principal principal = security.getUserPrincipal();
1730        try {
1731            groupService.transferItemOwnershipFromUserToGroupId(principal.getName(), groupId, itemId);
1732            return "No exceptions happen, the item shoul be transferred";
1733        } catch (UserUnauthorizedException e) {
1734            response.sendError(Status.FORBIDDEN.getStatusCode(), e.toString());
1735            return e.toString();
1736        }
1737    }
1738
1739    @Override
1740    @GET
1741    @Path("/items/{itemId}")
1742    @Produces({MediaType.TEXT_XML, MediaType.APPLICATION_XML,
1743        MediaType.APPLICATION_JSON})
1744    public BaseDescription getBaseDescription(@PathParam("itemId") String itemId) throws ComponentRegistryException, IOException {
1745        LOG.debug("Item with id: {} is requested.", itemId);
1746        try {
1747            ComponentRegistry cr = this.getBaseRegistry();
1748            try {
1749                BaseDescription description = cr.getComponentDescriptionAccessControlled(itemId);
1750                return description;
1751            } catch (UserUnauthorizedException ex1) {
1752                try {
1753                    BaseDescription description = cr.getProfileDescriptionAccessControlled(itemId);
1754                    return description;
1755                } catch (UserUnauthorizedException ex2) {
1756                    response.sendError(Status.FORBIDDEN.getStatusCode(), "User \'" + security.getUserPrincipal().getName() + "\' does not have access to the item with the given id or the item with the given id does not exist.");
1757                    return new BaseDescription();
1758                }
1759            }
1760        } catch (ItemNotFoundException e) {
1761            response.sendError(Status.NOT_FOUND.getStatusCode());
1762            return new BaseDescription();
1763        } catch (AuthenticationFailException e) {
1764            response.sendError(Status.UNAUTHORIZED.getStatusCode(), e.toString());
1765            return new BaseDescription();
1766        }
1767    }
1768}
Note: See TracBrowser for help on using the repository browser.