2 * This document is a part of the source code and related artifacts
3 * for CollectionSpace, an open source collections management system
4 * for museums and related institutions:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
18 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
24 * $LastChangedRevision$
26 package org.collectionspace.services.collectionobject;
28 import java.io.InputStream;
30 import java.util.ArrayList;
31 import java.util.List;
33 import javax.servlet.http.HttpServletRequest;
34 import javax.ws.rs.Consumes;
35 import javax.ws.rs.DELETE;
36 import javax.ws.rs.GET;
37 import javax.ws.rs.POST;
38 import javax.ws.rs.PUT;
39 import javax.ws.rs.Path;
40 import javax.ws.rs.PathParam;
41 import javax.ws.rs.Produces;
42 import javax.ws.rs.QueryParam;
43 import javax.ws.rs.WebApplicationException;
44 import javax.ws.rs.core.Context;
45 import javax.ws.rs.core.Response;
46 import javax.ws.rs.core.UriBuilder;
47 import javax.ws.rs.core.UriInfo;
48 import javax.ws.rs.core.MultivaluedMap;
50 import org.collectionspace.services.common.imaging.nuxeo.NuxeoImageUtils;
51 import org.collectionspace.services.common.AbstractMultiPartCollectionSpaceResourceImpl;
52 import org.collectionspace.services.common.ServiceMain;
53 import org.collectionspace.services.common.authorityref.AuthorityRefList;
54 import org.collectionspace.services.common.blob.BlobInput;
55 import org.collectionspace.services.blob.BlobsCommon;
56 import org.collectionspace.services.common.context.ServiceContextFactory;
57 //import org.collectionspace.services.common.context.MultipartServiceContext;
58 import org.collectionspace.services.common.context.MultipartServiceContextFactory;
59 import org.collectionspace.services.common.context.MultipartServiceContextImpl;
60 import org.collectionspace.services.common.context.ServiceBindingUtils;
61 import org.collectionspace.services.common.context.ServiceContext;
62 import org.collectionspace.services.common.document.BadRequestException;
63 import org.collectionspace.services.common.document.DocumentFilter;
64 import org.collectionspace.services.common.document.DocumentHandler;
65 import org.collectionspace.services.common.document.DocumentNotFoundException;
66 import org.collectionspace.services.common.document.DocumentWrapper;
67 import org.collectionspace.services.common.query.IQueryManager;
68 import org.collectionspace.services.common.query.QueryManager;
69 import org.collectionspace.services.common.security.UnauthorizedException;
70 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils;
71 import org.collectionspace.services.intake.IntakeResource;
72 import org.collectionspace.services.intake.IntakesCommonList;
73 //import org.collectionspace.services.nuxeo.client.java.RemoteDocumentModelHandlerImpl;
74 import org.collectionspace.services.nuxeo.client.java.DocumentModelHandler;
75 import org.collectionspace.services.relation.NewRelationResource;
76 import org.collectionspace.services.relation.RelationsCommonList;
77 import org.collectionspace.services.relation.RelationshipType;
78 import org.collectionspace.services.common.profile.Profiler;
79 import org.collectionspace.services.common.FileUtils;
81 import org.jboss.resteasy.plugins.providers.multipart.MultipartInput;
82 import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput;
83 import org.jboss.resteasy.util.HttpResponseCodes;
85 //FIXME: There should be no direct dependency on Nuxeo in our resource classes.
86 import org.nuxeo.ecm.core.api.DocumentModel;
87 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
88 import org.nuxeo.ecm.core.api.ClientException;
90 import org.slf4j.Logger;
91 import org.slf4j.LoggerFactory;
95 * The Class CollectionObjectResource.
97 @Path("/collectionobjects")
98 @Consumes("multipart/mixed")
99 @Produces("multipart/mixed")
100 public class CollectionObjectResource
101 extends AbstractMultiPartCollectionSpaceResourceImpl {
103 /** The Constant serviceName. */
104 static final public String serviceName = "collectionobjects";
107 final Logger logger = LoggerFactory.getLogger(CollectionObjectResource.class);
110 * @see org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl#getVersionString()
113 public String getVersionString() {
114 /** The last change revision. */
115 final String lastChangeRevision = "$LastChangedRevision$";
116 return lastChangeRevision;
120 * @see org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl#getServiceName()
123 public String getServiceName() {
128 * @see org.collectionspace.services.common.CollectionSpaceResource#getCommonPartClass()
131 public Class<CollectionobjectsCommon> getCommonPartClass() {
132 return CollectionobjectsCommon.class;
136 * @see org.collectionspace.services.common.AbstractCollectionSpaceResourceImpl#createDocumentHandler(org.collectionspace.services.common.context.ServiceContext)
139 // public DocumentHandler createDocumentHandler(ServiceContext<MultipartInput, MultipartOutput> ctx) throws Exception {
140 // DocumentHandler docHandler = ctx.getDocumentHandler();
141 // if (ctx.getInput() != null) {
142 // Object obj = ((MultipartServiceContext) ctx).getInputPart(ctx.getCommonPartLabel(),
143 // CollectionobjectsCommon.class);
144 // if (obj != null) {
145 // docHandler.setCommonPart((CollectionobjectsCommon) obj);
148 // return docHandler;
152 * Creates the collection object.
154 * @param input the input
156 * @return the response
159 public Response createCollectionObject(MultipartInput input) {
161 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext(input); //
162 DocumentHandler handler = createDocumentHandler(ctx);
163 String csid = getRepositoryClient(ctx).create(ctx, handler);
164 UriBuilder path = UriBuilder.fromResource(CollectionObjectResource.class);
165 path.path("" + csid);
166 Response response = Response.created(path.build()).build();
168 } catch (BadRequestException bre) {
169 Response response = Response.status(
170 Response.Status.BAD_REQUEST).entity("Create failed reason " + bre.getErrorReason()).type("text/plain").build();
171 throw new WebApplicationException(response);
172 } catch (UnauthorizedException ue) {
173 Response response = Response.status(
174 Response.Status.UNAUTHORIZED).entity("Create failed reason " + ue.getErrorReason()).type("text/plain").build();
175 throw new WebApplicationException(response);
176 } catch (Exception e) {
177 if (logger.isDebugEnabled()) {
178 logger.debug("Caught exception in createCollectionObject", e);
180 Response response = Response.status(
181 Response.Status.INTERNAL_SERVER_ERROR).entity("Create failed").type("text/plain").build();
182 throw new WebApplicationException(response);
187 * Gets the collection object.
189 * @param csid the csid
191 * @return the collection object
195 public MultipartOutput getCollectionObject(
196 @PathParam("csid") String csid) {
197 if (logger.isDebugEnabled()) {
198 logger.debug("getCollectionObject with csid=" + csid);
200 if (csid == null || "".equals(csid)) {
201 logger.error("getCollectionObject: missing csid!");
202 Response response = Response.status(Response.Status.BAD_REQUEST).entity(
203 "get failed on CollectionObject csid=" + csid).type(
204 "text/plain").build();
205 throw new WebApplicationException(response);
207 MultipartOutput result = null;
209 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext();
210 DocumentHandler handler = createDocumentHandler(ctx);
211 getRepositoryClient(ctx).get(ctx, csid, handler);
212 result = (MultipartOutput) ctx.getOutput();
213 } catch (UnauthorizedException ue) {
214 Response response = Response.status(
215 Response.Status.UNAUTHORIZED).entity("Get failed reason " + ue.getErrorReason()).type("text/plain").build();
216 throw new WebApplicationException(response);
217 } catch (DocumentNotFoundException dnfe) {
218 if (logger.isDebugEnabled()) {
219 logger.debug("getCollectionObject", dnfe);
221 Response response = Response.status(Response.Status.NOT_FOUND).entity(
222 "Get failed on CollectionObject csid=" + csid).type(
223 "text/plain").build();
224 throw new WebApplicationException(response);
225 } catch (Exception e) {
226 if (logger.isDebugEnabled()) {
227 logger.debug("getCollectionObject", e);
229 Response response = Response.status(
230 Response.Status.INTERNAL_SERVER_ERROR).entity("Get failed").type("text/plain").build();
231 throw new WebApplicationException(response);
234 if (result == null) {
235 Response response = Response.status(Response.Status.NOT_FOUND).entity(
236 "Get failed, the requested CollectionObject CSID:" + csid + ": was not found.").type(
237 "text/plain").build();
238 throw new WebApplicationException(response);
244 * Gets the collection object list.
247 * @param keywords the keywords
249 * @return the collection object list
252 @Produces("application/xml")
253 public CollectionobjectsCommonList getCollectionObjectList(@Context UriInfo ui,
254 @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS_KW) String keywords) {
255 Profiler profiler = new Profiler("getCollectionObjectList():", 1);
257 CollectionobjectsCommonList result = null;
258 MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
259 if (keywords != null) {
260 result = searchCollectionObjects(queryParams, keywords);
262 result = getCollectionObjectList(queryParams);
269 * Gets the collection object list.
271 private CollectionobjectsCommonList getCollectionObjectList(MultivaluedMap<String, String> queryParams) {
272 CollectionobjectsCommonList collectionObjectList;
273 Profiler profiler = new Profiler(this, 1);
277 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext(queryParams);
278 DocumentHandler handler = createDocumentHandler(ctx);
279 getRepositoryClient(ctx).getFiltered(ctx, handler);
280 collectionObjectList = (CollectionobjectsCommonList) handler.getCommonPartList();
281 } catch (UnauthorizedException ue) {
282 Response response = Response.status(
283 Response.Status.UNAUTHORIZED).entity("Index failed reason " + ue.getErrorReason()).type("text/plain").build();
284 throw new WebApplicationException(response);
285 } catch (Exception e) {
286 if (logger.isDebugEnabled()) {
287 logger.debug("Caught exception in getCollectionObjectList", e);
289 Response response = Response.status(
290 Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed").type("text/plain").build();
291 throw new WebApplicationException(response);
295 return collectionObjectList;
299 * Update collection object.
301 * @param csid the csid
302 * @param theUpdate the the update
304 * @return the multipart output
308 public MultipartOutput updateCollectionObject(
309 @PathParam("csid") String csid,
310 MultipartInput theUpdate) {
311 if (logger.isDebugEnabled()) {
312 logger.debug("updateCollectionObject with csid=" + csid);
314 if (csid == null || "".equals(csid)) {
315 logger.error("updateCollectionObject: missing csid!");
316 Response response = Response.status(Response.Status.BAD_REQUEST).entity(
317 "update failed on CollectionObject csid=" + csid).type(
318 "text/plain").build();
319 throw new WebApplicationException(response);
321 MultipartOutput result = null;
323 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext(theUpdate);
324 DocumentHandler handler = createDocumentHandler(ctx);
325 getRepositoryClient(ctx).update(ctx, csid, handler);
326 result = (MultipartOutput) ctx.getOutput();
327 } catch (BadRequestException bre) {
328 Response response = Response.status(
329 Response.Status.BAD_REQUEST).entity("Update failed reason " + bre.getErrorReason()).type("text/plain").build();
330 throw new WebApplicationException(response);
331 } catch (UnauthorizedException ue) {
332 Response response = Response.status(
333 Response.Status.UNAUTHORIZED).entity("Update failed reason " + ue.getErrorReason()).type("text/plain").build();
334 throw new WebApplicationException(response);
335 } catch (DocumentNotFoundException dnfe) {
336 if (logger.isDebugEnabled()) {
337 logger.debug("caugth exception in updateCollectionObject", dnfe);
339 Response response = Response.status(Response.Status.NOT_FOUND).entity(
340 "Update failed on CollectionObject csid=" + csid).type(
341 "text/plain").build();
342 throw new WebApplicationException(response);
343 } catch (Exception e) {
344 Response response = Response.status(
345 Response.Status.INTERNAL_SERVER_ERROR).entity("Update failed").type("text/plain").build();
346 throw new WebApplicationException(response);
352 * Delete collection object.
354 * @param csid the csid
356 * @return the response
360 public Response deleteCollectionObject(@PathParam("csid") String csid) {
362 if (logger.isDebugEnabled()) {
363 logger.debug("deleteCollectionObject with csid=" + csid);
365 if (csid == null || "".equals(csid)) {
366 logger.error("deleteCollectionObject: missing csid!");
367 Response response = Response.status(Response.Status.BAD_REQUEST).entity(
368 "delete failed on CollectionObject csid=" + csid).type(
369 "text/plain").build();
370 throw new WebApplicationException(response);
373 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext();
374 getRepositoryClient(ctx).delete(ctx, csid);
375 return Response.status(HttpResponseCodes.SC_OK).build();
376 } catch (UnauthorizedException ue) {
377 Response response = Response.status(
378 Response.Status.UNAUTHORIZED).entity("Delete failed reason " + ue.getErrorReason()).type("text/plain").build();
379 throw new WebApplicationException(response);
380 } catch (DocumentNotFoundException dnfe) {
381 if (logger.isDebugEnabled()) {
382 logger.debug("caught exception in deleteCollectionObject", dnfe);
384 Response response = Response.status(Response.Status.NOT_FOUND).entity(
385 "Delete failed on CollectionObject csid=" + csid).type(
386 "text/plain").build();
387 throw new WebApplicationException(response);
388 } catch (Exception e) {
389 Response response = Response.status(
390 Response.Status.INTERNAL_SERVER_ERROR).entity("Delete failed").type("text/plain").build();
391 throw new WebApplicationException(response);
397 * Gets the intakes common list.
400 * @param csid the csid
402 * @return the intakes common list
405 @Path("{csid}/intakes")
406 @Produces("application/xml")
407 public IntakesCommonList getIntakesCommonList(@Context UriInfo ui,
408 @PathParam("csid") String csid) {
409 IntakesCommonList result = null;
410 MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
414 // Find all the intake-related relation records.
416 String subjectCsid = csid;
417 String predicate = RelationshipType.COLLECTIONOBJECT_INTAKE.value();
418 String objectCsid = null;
419 NewRelationResource relationResource = new NewRelationResource();
420 RelationsCommonList relationsCommonList = relationResource.getRelationList(queryParams,
422 null, /*subjectType*/
425 null /*objectType*/);
428 // Create an array of Intake csid's
430 List<RelationsCommonList.RelationListItem> relationsListItems = relationsCommonList.getRelationListItem();
431 List<String> intakeCsidList = new ArrayList<String>();
432 for (RelationsCommonList.RelationListItem relationsListItem : relationsListItems) {
433 intakeCsidList.add(relationsListItem.getObjectCsid());
437 // Get a response list for the Intake records from the Intake resource
439 IntakeResource intakeResource = new IntakeResource();
440 result = intakeResource.getIntakeList(intakeCsidList);
441 } catch (Exception e) {
442 if (logger.isDebugEnabled()) {
443 logger.debug("Caught exception in getIntakeList", e);
445 Response response = Response.status(
446 Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed").type("text/plain").build();
447 throw new WebApplicationException(response);
454 * Gets the authority refs.
456 * @param csid the csid
459 * @return the authority refs
462 @Path("{csid}/authorityrefs")
463 @Produces("application/xml")
464 public AuthorityRefList getAuthorityRefs(
465 @PathParam("csid") String csid,
466 @Context UriInfo ui) {
467 AuthorityRefList authRefList = null;
469 MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
470 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext(queryParams);
471 DocumentWrapper<DocumentModel> docWrapper =
472 getRepositoryClient(ctx).getDoc(ctx, csid);
473 DocumentModelHandler<MultipartInput, MultipartOutput> docHandler =
474 (DocumentModelHandler<MultipartInput, MultipartOutput>)createDocumentHandler(ctx);
475 List<String> authRefFields =
476 ((MultipartServiceContextImpl)ctx).getCommonPartPropertyValues(
477 ServiceBindingUtils.AUTH_REF_PROP, ServiceBindingUtils.QUALIFIED_PROP_NAMES);
478 authRefList = docHandler.getAuthorityRefs(docWrapper, authRefFields);
479 } catch (UnauthorizedException ue) {
480 Response response = Response.status(
481 Response.Status.UNAUTHORIZED).entity("Index failed reason " + ue.getErrorReason()).type("text/plain").build();
482 throw new WebApplicationException(response);
483 } catch (Exception e) {
484 if (logger.isDebugEnabled()) {
485 logger.debug("Caught exception in getAuthorityRefs", e);
487 Response response = Response.status(
488 Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed").type("text/plain").build();
489 throw new WebApplicationException(response);
497 * This is an intentionally empty method used for getting a rough time estimate
498 * of the overhead required for a client->server request/response cycle.
499 * @param ms - milliseconds to delay
501 * @return the response
504 @Path("/{ms}/roundtrip")
505 @Produces("application/xml")
506 public Response roundtrip(
507 @PathParam("ms") String ms) {
508 Response result = null;
510 Profiler profiler = new Profiler("roundtrip():", 1);
512 result = Response.status(HttpResponseCodes.SC_OK).build();
519 @Path("{csid}/getpicture/{blobId}")
520 @Produces({"image/jpeg", "image/png", "image/tiff"})
521 public InputStream getPicture(
522 @PathParam("csid") String csid,
523 @PathParam("blobId") String blobId) {
524 InputStream result = null;
525 RepositoryInstance repoSession = null;
527 repoSession = ServiceMain.getInstance().getNuxeoConnector().getRepositorySession();
528 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext();
529 result = NuxeoImageUtils.getPicture(ctx, repoSession, blobId, null);
530 } catch (Exception e) {
531 logger.error("Could not get image blob: " + blobId, e);
534 ServiceMain.getInstance().getNuxeoConnector().releaseRepositorySession(repoSession);
535 } catch (Exception e) {
536 logger.error("Could not release Nuxeo repository session", e);
540 if (result == null) {
541 Response response = Response.status(
542 Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed.").type("text/plain").build();
543 throw new WebApplicationException(response);
550 @Path("{csid}/getpicture/{blobId}/{derivativeTerm}")
551 @Produces({"image/jpeg", "image/png", "image/tiff"})
552 public InputStream getPicture(
553 @PathParam("csid") String csid,
554 @PathParam("blobId") String blobId,
555 @PathParam("derivativeTerm") String derivativeTerm) {
556 InputStream result = null;
557 RepositoryInstance repoSession = null;
559 repoSession = ServiceMain.getInstance().getNuxeoConnector().getRepositorySession();
560 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext();
561 result = NuxeoImageUtils.getPicture(ctx, repoSession, blobId, derivativeTerm);
562 } catch (Exception e) {
563 logger.error("Could not get image blob: " + blobId, e);
566 ServiceMain.getInstance().getNuxeoConnector().releaseRepositorySession(repoSession);
567 } catch (Exception e) {
568 logger.error("Could not release Nuxeo repository session", e);
572 if (result == null) {
573 Response response = Response.status(
574 Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed.").type("text/plain").build();
575 throw new WebApplicationException(response);
582 @Path("{csid}/postpicture")
583 @Consumes("multipart/form-data")
584 @Produces("application/xml")
585 public String createPictureDocument(@Context HttpServletRequest req,
586 @PathParam("csid") String csid,
587 @QueryParam("blobUri") String blobUri) {
588 String result = null;
590 RepositoryInstance repoSession = null;
592 repoSession = ServiceMain.getInstance().getNuxeoConnector().getRepositorySession();
593 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext();
594 File tmpFile = FileUtils.createTmpFile(req);
595 BlobInput blobInput = new BlobInput(tmpFile, blobUri);
596 BlobsCommon blobCommon = NuxeoImageUtils.createPicture(ctx, repoSession, blobInput);
597 if (blobCommon != null) {
598 result = blobCommon.getRepositoryId();
600 } catch (Exception e) {
601 logger.error("Could not create the new image file", e);
604 ServiceMain.getInstance().getNuxeoConnector().releaseRepositorySession(repoSession);
605 } catch (Exception e) {
606 logger.error("Could not release Nuxeo repository session", e);
610 if (result == null) {
611 Response response = Response.status(
612 Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed.").type("text/plain").build();
613 throw new WebApplicationException(response);
620 * This method is deprecated. Use kwSearchCollectionObjects() method instead.
621 * Keywords search collection objects.
624 * @param keywords the keywords
626 * @return the collectionobjects common list
630 @Produces("application/xml")
631 public CollectionobjectsCommonList keywordsSearchCollectionObjects(@Context UriInfo ui,
632 @QueryParam(IQueryManager.SEARCH_TYPE_KEYWORDS) String keywords) {
633 MultivaluedMap<String, String> queryParams = ui.getQueryParameters();
634 return searchCollectionObjects(queryParams, keywords);
638 * Search collection objects.
640 * @param keywords the keywords
642 * @return the collectionobjects common list
644 private CollectionobjectsCommonList searchCollectionObjects(
645 MultivaluedMap<String, String> queryParams,
647 CollectionobjectsCommonList collectionObjectList;
649 Profiler profiler = new Profiler("searchCollectionObjects():", 1);
651 ServiceContext<MultipartInput, MultipartOutput> ctx = createServiceContext(queryParams);
652 DocumentHandler handler = createDocumentHandler(ctx);
654 // perform a keyword search
655 if (keywords != null && !keywords.isEmpty()) {
656 String whereClause = QueryManager.createWhereClauseFromKeywords(keywords);
657 DocumentFilter documentFilter = handler.getDocumentFilter();
658 documentFilter.setWhereClause(whereClause);
659 if (logger.isDebugEnabled()) {
660 logger.debug("The WHERE clause is: " + documentFilter.getWhereClause());
664 getRepositoryClient(ctx).getFiltered(ctx, handler);
665 collectionObjectList = (CollectionobjectsCommonList) handler.getCommonPartList();
666 } catch (UnauthorizedException ue) {
667 Response response = Response.status(
668 Response.Status.UNAUTHORIZED).entity("Index failed reason " + ue.getErrorReason()).type("text/plain").build();
669 throw new WebApplicationException(response);
670 } catch (Exception e) {
671 if (logger.isDebugEnabled()) {
672 logger.debug("Caught exception in getCollectionObjectList", e);
674 Response response = Response.status(
675 Response.Status.INTERNAL_SERVER_ERROR).entity("Index failed").type("text/plain").build();
676 throw new WebApplicationException(response);
678 return collectionObjectList;