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

Last change on this file since 6963 was 6963, checked in by Twan Goosen, 8 years ago

merged 2.0 branch into trunk. Trunk now 2.1-SNAPSHOT

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