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 package org.collectionspace.services.common;
26 import java.util.HashMap;
27 import java.util.List;
30 import javax.ws.rs.GET;
31 import javax.ws.rs.Path;
32 import javax.ws.rs.Produces;
33 import javax.ws.rs.core.Context;
34 import javax.ws.rs.core.MultivaluedMap;
35 import javax.ws.rs.core.Response;
36 import javax.ws.rs.core.UriInfo;
38 import org.collectionspace.services.client.CollectionSpaceClient;
39 import org.collectionspace.services.common.CSWebApplicationException;
40 import org.collectionspace.services.common.api.Tools;
41 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
42 import org.collectionspace.services.common.context.MultipartServiceContext;
43 import org.collectionspace.services.common.context.ServiceContext;
44 import org.collectionspace.services.common.context.ServiceContextProperties;
45 import org.collectionspace.services.common.document.BadRequestException;
46 import org.collectionspace.services.common.document.DocumentException;
47 import org.collectionspace.services.common.document.DocumentHandler;
48 import org.collectionspace.services.common.document.DocumentNotFoundException;
49 import org.collectionspace.services.common.document.TransactionException;
50 import org.collectionspace.services.common.repository.RepositoryClient;
51 import org.collectionspace.services.common.repository.RepositoryClientFactory;
52 import org.collectionspace.services.common.security.UnauthorizedException;
53 import org.collectionspace.services.common.storage.StorageClient;
54 import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
55 import org.collectionspace.services.config.service.ServiceBindingType;
56 import org.collectionspace.services.description.ServiceDescription;
57 import org.jboss.resteasy.spi.HttpRequest;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
62 * The Class AbstractCollectionSpaceResourceImpl.
64 * @param <IT> the generic type
65 * @param <OT> the generic type
67 public abstract class AbstractCollectionSpaceResourceImpl<IT, OT>
68 implements CollectionSpaceResource<IT, OT> {
70 protected final Logger logger = LoggerFactory.getLogger(this.getClass());
72 protected final ServiceContext<IT, OT> NULL_CONTEXT = null;
73 // Fields for default client factory and client
74 /** The repository client factory. */
75 private RepositoryClientFactory<IT, OT> repositoryClientFactory;
77 /** The repository client. */
78 private RepositoryClient<IT, OT> repositoryClient;
80 /** The storage client. */
81 private StorageClient storageClient;
89 protected static String extractId(Response res) {
90 MultivaluedMap<String, Object> mvm = res.getMetadata();
91 String uri = (String) ((List<Object>) mvm.get("Location")).get(0);
92 String[] segments = uri.split("/");
93 String id = segments[segments.length - 1];
98 * Instantiates a new abstract collection space resource.
100 public AbstractCollectionSpaceResourceImpl() {
101 repositoryClientFactory = (RepositoryClientFactory<IT, OT>) RepositoryClientFactory.getInstance();
105 * @see org.collectionspace.services.common.CollectionSpaceResource#getServiceName()
108 abstract public String getServiceName();
112 * @see org.collectionspace.services.common.CollectionSpaceResource#getRepositoryClient(org.collectionspace.services.common.context.ServiceContext)
115 synchronized public RepositoryClient<IT, OT> getRepositoryClient(ServiceContext<IT, OT> ctx) {
116 if (repositoryClient != null){
117 return repositoryClient;
119 repositoryClient = repositoryClientFactory.getClient(ctx.getRepositoryClientName());
120 return repositoryClient;
124 * @see org.collectionspace.services.common.CollectionSpaceResource#getStorageClient(org.collectionspace.services.common.context.ServiceContext)
127 synchronized public StorageClient getStorageClient(ServiceContext<IT, OT> ctx) {
128 if(storageClient != null) {
129 return storageClient;
131 storageClient = new JpaStorageClientImpl();
132 return storageClient;
136 * @see org.collectionspace.services.common.CollectionSpaceResource#createDocumentHandler(org.collectionspace.services.common.context.ServiceContext)
139 public DocumentHandler createDocumentHandler(ServiceContext<IT, OT> ctx) throws Exception {
140 DocumentHandler docHandler = createDocumentHandler(ctx, ctx.getInput());
145 * Creates the document handler.
148 * @param commonPart the common part
150 * @return the document handler
152 * @throws Exception the exception
154 public DocumentHandler createDocumentHandler(ServiceContext<IT, OT> ctx,
155 Object commonPart) throws Exception {
156 DocumentHandler docHandler = ctx.getDocumentHandler();
157 docHandler.setCommonPart(commonPart);
162 * Creates the service context.
164 * @return the service context< i t, o t>
166 * @throws Exception the exception
168 protected ServiceContext<IT, OT> createServiceContext() throws Exception {
169 ServiceContext<IT, OT> ctx = createServiceContext(this.getServiceName(),
170 (IT)null, //inputType
171 null, // The resource map
172 (UriInfo)null, // The query params
173 this.getCommonPartClass());
178 * Creates the service context.
180 * @param serviceName the service name
182 * @return the service context< i t, o t>
184 * @throws Exception the exception
186 public ServiceContext<IT, OT> createServiceContext(String serviceName) throws Exception {
187 ServiceContext<IT, OT> ctx = createServiceContext(
189 (IT)null, // The input part
190 null, // The resource map
191 (UriInfo)null, // The queryParams
192 (Class<?>)null /*input type's Class*/);
196 protected ServiceContext<IT, OT> createServiceContext(String serviceName, UriInfo ui) throws Exception {
197 ServiceContext<IT, OT> ctx = createServiceContext(
199 (IT)null, // The input part
200 null, // The resource map
201 (UriInfo)null, // The queryParams
202 (Class<?>)null /*input type's Class*/);
208 * Creates the service context.
210 * @param serviceName the service name
211 * @param input the input
213 * @return the service context< i t, o t>
215 * @throws Exception the exception
217 protected ServiceContext<IT, OT> createServiceContext(String serviceName,
218 IT input) throws Exception {
219 ServiceContext<IT, OT> ctx = createServiceContext(serviceName,
221 null, // The resource map
222 (UriInfo)null, /*queryParams*/
223 (Class<?>)null /*input type's Class*/);
227 protected ServiceContext<IT, OT> createServiceContext(String serviceName,
229 UriInfo uriInfo) throws Exception {
230 ServiceContext<IT, OT> ctx = createServiceContext(serviceName,
232 null, // The resource map
233 uriInfo, /*queryParams*/
234 (Class<?>)null /*input type's Class*/);
238 protected ServiceContext<IT, OT> createServiceContext(UriInfo uriInfo) throws Exception {
239 ServiceContext<IT, OT> ctx = createServiceContext(
242 (Class<?>)null /*input type's Class*/);
247 * Creates the service context.
249 * @param input the input
251 * @return the service context< i t, o t>
253 * @throws Exception the exception
255 protected ServiceContext<IT, OT> createServiceContext(IT input) throws Exception {
256 ServiceContext<IT, OT> ctx = createServiceContext(
258 (Class<?>)null /*input type's Class*/);
262 protected ServiceContext<IT, OT> createServiceContext(IT input, UriInfo uriInfo) throws Exception {
263 ServiceContext<IT, OT> ctx = createServiceContext(
266 null ); // The class param/argument
271 * Creates the service context.
273 * @param input the input
274 * @param theClass the the class
276 * @return the service context
278 * @throws Exception the exception
280 protected ServiceContext<IT, OT> createServiceContext(IT input, Class<?> theClass) throws Exception {
281 ServiceContext<IT, OT> ctx = createServiceContext(
283 (UriInfo)null, //queryParams,
288 protected ServiceContext<IT, OT> createServiceContext(IT input, Class<?> theClass, UriInfo uriInfo) throws Exception {
289 ServiceContext<IT, OT> ctx = createServiceContext(
296 protected ServiceContext<IT, OT> createServiceContext(
298 ResourceMap resourceMap,
299 UriInfo uriInfo) throws Exception {
300 ServiceContext<IT, OT> ctx = createServiceContext(
302 null, // The input object
305 null /* the class of the input type */);
309 protected ServiceContext<IT, OT> createServiceContext(
311 ResourceMap resourceMap,
312 UriInfo uriInfo) throws Exception {
313 ServiceContext<IT, OT> ctx = createServiceContext(
314 this.getServiceName(),
318 null /* the class of the input type */);
322 protected ServiceContext<IT, OT> createServiceContext(
325 ResourceMap resourceMap,
326 UriInfo uriInfo) throws Exception {
327 ServiceContext<IT, OT> ctx = createServiceContext(
332 null /* the class of the input type */);
337 * Creates the service context.
339 * @param input the input
340 * @param queryParams the query params
341 * @param theClass the the class
343 * @return the service context< i t, o t>
345 * @throws Exception the exception
347 private ServiceContext<IT, OT> createServiceContext(
350 Class<?> theClass) throws Exception {
351 return createServiceContext(this.getServiceName(),
353 null, // The resource map
359 * Creates the service context.
361 * @param serviceName the service name
362 * @param input the input
363 * @param queryParams the query params
364 * @param theClass the the class
366 * @return the service context< i t, o t>
368 * @throws Exception the exception
370 private ServiceContext<IT, OT> createServiceContext(
373 ResourceMap resourceMap,
375 Class<?> theClass) throws Exception {
376 ServiceContext<IT, OT> ctx = getServiceContextFactory().createServiceContext(
381 theClass != null ? theClass.getPackage().getName() : null,
382 theClass != null ? theClass.getName() : null);
383 if (theClass != null) {
384 ctx.setProperty(ServiceContextProperties.ENTITY_CLASS, theClass);
391 * Gets the version string.
393 * @return the version string
395 abstract protected String getVersionString();
400 * @return the version
404 @Produces("application/xml")
405 public Version getVersion() {
406 Version result = new Version();
408 result.setVersionString(getVersionString());
414 * Get the service description
417 @Path(CollectionSpaceClient.SERVICE_DESCRIPTION_PATH)
418 public ServiceDescription getDescription(@Context UriInfo uriInfo) {
419 ServiceDescription result = null;
421 ServiceContext ctx = null;
423 ctx = createServiceContext(uriInfo);
424 result = getDescription(ctx);
425 } catch (Exception e) {
426 String errMsg = String.format("Request to get service description information for the '%s' service failed.",
427 this.getServiceContextFactory());
428 throw bigReThrow(e, errMsg);
435 * Each resource can override this method if they need to.
440 public ServiceDescription getDescription(ServiceContext ctx) {
441 ServiceDescription result = new ServiceDescription();
443 result.setDocumentType(getDocType(ctx.getTenantId()));
448 public void checkResult(Object resultToCheck, String csid, String serviceMessage) throws CSWebApplicationException {
449 if (resultToCheck == null) {
450 Response response = Response.status(Response.Status.NOT_FOUND).entity(
451 serviceMessage + "csid=" + csid
452 + ": was not found.").type(
453 "text/plain").build();
454 throw new CSWebApplicationException(response);
458 protected void ensureCSID(String csid, String crudType) throws CSWebApplicationException {
459 ensureCSID(csid, crudType, "csid");
462 protected void ensureCSID(String csid, String crudType, String whichCsid) throws CSWebApplicationException {
463 if (logger.isDebugEnabled()) {
464 logger.debug(crudType + " for " + getClass().getName() + " with csid=" + csid);
466 if (csid == null || "".equals(csid)) {
467 logger.error(crudType + " for " + getClass().getName() + " missing csid!");
468 Response response = Response.status(Response.Status.BAD_REQUEST).entity(crudType + " failed on " + getClass().getName() + ' '+whichCsid+'=' + csid).type("text/plain").build();
469 throw new CSWebApplicationException(response);
473 protected CSWebApplicationException bigReThrow(Exception e, String serviceMsg) throws CSWebApplicationException {
474 return bigReThrow(e, serviceMsg, "");
477 protected CSWebApplicationException bigReThrow(Exception e, String serviceMsg, String csid) throws CSWebApplicationException {
478 boolean logException = true;
479 CSWebApplicationException result = null;
481 String detail = Tools.errorToString(e, true);
482 String detailNoTrace = Tools.errorToString(e, true, 3);
484 if (e instanceof UnauthorizedException) {
485 response = Response.status(Response.Status.UNAUTHORIZED).entity(serviceMsg + e.getMessage()).type("text/plain").build();
486 result = new CSWebApplicationException(e, response);
488 } else if (e instanceof DocumentNotFoundException) {
490 // Don't log this error unless we're in 'trace' mode
492 logException = false;
493 response = Response.status(Response.Status.NOT_FOUND).entity(serviceMsg + " on " + getClass().getName() + " csid=" + csid).type("text/plain").build();
494 result = new CSWebApplicationException(e, response);
496 } else if (e instanceof TransactionException) {
497 int code = ((TransactionException) e).getErrorCode();
498 response = Response.status(code).entity(e.getMessage()).type("text/plain").build();
499 result = new CSWebApplicationException(e, response);
501 } else if (e instanceof BadRequestException) {
502 int code = ((BadRequestException) e).getErrorCode();
504 code = Response.Status.BAD_REQUEST.getStatusCode();
507 response = Response.status(code).entity(serviceMsg + e.getMessage()).type("text/plain").build();
508 // return new WebApplicationException(e, code);
509 result = new CSWebApplicationException(e, response);
511 } else if (e instanceof DocumentException) {
512 int code = ((DocumentException) e).getErrorCode();
514 code = Response.Status.BAD_REQUEST.getStatusCode();
517 response = Response.status(code).entity(serviceMsg + e.getMessage()).type("text/plain").build();
518 // return new WebApplicationException(e, code);
519 result = new CSWebApplicationException(e, response);
521 } else if (e instanceof CSWebApplicationException) {
522 // subresource may have already thrown this exception
523 // so just pass it on
524 result = (CSWebApplicationException) e;
526 } else { // e is now instanceof Exception
527 response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(serviceMsg + " detail: " + detailNoTrace).type("text/plain").build();
528 result = new CSWebApplicationException(e, response);
531 // Some exceptions like DocumentNotFoundException won't be logged unless we're in 'trace' mode
533 boolean traceEnabled = logger.isTraceEnabled();
534 if (logException == true || traceEnabled == true) {
535 if (traceEnabled == true) {
536 logger.error(getClass().getName() + " detail: " + detail, e);
538 logger.error(getClass().getName() + " detail: " + detailNoTrace);
546 public boolean allowAnonymousAccess(HttpRequest request,
547 Class<?> resourceClass) {
552 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
553 * for the current resource, for all tenants
555 * @return a map of URI templates for the current resource, for all tenants
557 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
558 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
559 new HashMap<UriTemplateRegistryKey,StoredValuesUriTemplate>();
560 List<String> tenantIds = getTenantBindingsReader().getTenantIds();
561 for (String tenantId : tenantIds) {
562 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getDocType(tenantId), UriTemplateFactory.RESOURCE));
564 return uriRegistryEntriesMap;
568 * Returns a resource's document type.
574 public String getDocType(String tenantId) {
575 return getDocType(tenantId, getServiceName());
579 * Returns the document type associated with a specified service, within a specified tenant.
581 * @param tenantId a tenant ID
582 * @param serviceName a service name
583 * @return the Nuxeo document type associated with that service and tenant.
585 // FIXME: This method may properly belong in a different services package or class.
586 // Also, we need to check for any existing methods that may duplicate this one.
587 protected String getDocType(String tenantId, String serviceName) {
589 if (Tools.isBlank(tenantId)) {
592 ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, serviceName);
596 docType = sb.getObject().getName(); // Reads the Document Type from tenant bindings configuration
601 * Returns a UriRegistry entry: a map of tenant-qualified URI templates
602 * for the current resource, for a specified tenants
604 * @return a map of URI templates for the current resource, for a specified tenant
607 public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries(String tenantId,
608 String docType, UriTemplateFactory.UriTemplateType type) {
609 Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
610 new HashMap<UriTemplateRegistryKey,StoredValuesUriTemplate>();
611 UriTemplateRegistryKey key;
612 if (Tools.isBlank(tenantId) || Tools.isBlank(docType)) {
613 return uriRegistryEntriesMap;
615 key = new UriTemplateRegistryKey();
616 key.setTenantId(tenantId);
617 key.setDocType(docType);
618 uriRegistryEntriesMap.put(key, getUriTemplate(type));
619 return uriRegistryEntriesMap;
623 * Returns a URI template of the appropriate type, populated with the
624 * current service name as one of its stored values.
626 * @param type a URI template type
627 * @return a URI template of the appropriate type.
630 public StoredValuesUriTemplate getUriTemplate(UriTemplateFactory.UriTemplateType type) {
631 Map<String,String> storedValuesMap = new HashMap<String,String>();
632 storedValuesMap.put(UriTemplateFactory.SERVICENAME_VAR, getServiceName());
633 StoredValuesUriTemplate template =
634 UriTemplateFactory.getURITemplate(type, storedValuesMap);
639 * Returns a reader for reading values from tenant bindings configuration
641 * @return a tenant bindings configuration reader
644 public TenantBindingConfigReaderImpl getTenantBindingsReader() {
645 return ServiceMain.getInstance().getTenantBindingConfigReader();