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

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

Fixed issued arising after testing on localhost tomcat (not fully)

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