]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
74284e5a597e4144bcdab6ec85c146a70ad4cd81
[tmp/jakarta-migration.git] /
1 /**
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:
5
6  *  http://www.collectionspace.org
7  *  http://wiki.collectionspace.org
8
9  *  Copyright 2009 University of California at Berkeley
10
11  *  Licensed under the Educational Community License (ECL), Version 2.0.
12  *  You may not use this file except in compliance with this License.
13
14  *  You may obtain a copy of the ECL 2.0 License at
15
16  *  https://source.collectionspace.org/collection-space/LICENSE.txt
17
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.
23  */
24 package org.collectionspace.services.common;
25
26 import java.math.BigInteger;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.ws.rs.GET;
32 import javax.ws.rs.Path;
33 import javax.ws.rs.Produces;
34 import javax.ws.rs.core.CacheControl;
35 import javax.ws.rs.core.Context;
36 import javax.ws.rs.core.MultivaluedMap;
37 import javax.ws.rs.core.Response;
38 import javax.ws.rs.core.UriInfo;
39
40 import org.collectionspace.services.client.CollectionSpaceClient;
41 import org.collectionspace.services.common.CSWebApplicationException;
42 import org.collectionspace.services.common.api.Tools;
43 import org.collectionspace.services.common.config.ServiceConfigUtils;
44 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
45 import org.collectionspace.services.common.context.ServiceContext;
46 import org.collectionspace.services.common.context.ServiceContextProperties;
47 import org.collectionspace.services.common.document.BadRequestException;
48 import org.collectionspace.services.common.document.DocumentException;
49 import org.collectionspace.services.common.document.DocumentHandler;
50 import org.collectionspace.services.common.document.DocumentNotFoundException;
51 import org.collectionspace.services.common.document.TransactionException;
52 import org.collectionspace.services.common.repository.RepositoryClient;
53 import org.collectionspace.services.common.repository.RepositoryClientFactory;
54 import org.collectionspace.services.common.security.UnauthorizedException;
55 import org.collectionspace.services.common.storage.StorageClient;
56 import org.collectionspace.services.common.storage.jpa.JpaStorageClientImpl;
57 import org.collectionspace.services.config.service.ServiceBindingType;
58 import org.collectionspace.services.config.service.DocHandlerParams.Params;
59 import org.collectionspace.services.description.ServiceDescription;
60
61 import org.jboss.resteasy.spi.HttpRequest;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 /**
66  * The Class AbstractCollectionSpaceResourceImpl.
67  *
68  * @param <IT> the generic type
69  * @param <OT> the generic type
70  */
71 public abstract class AbstractCollectionSpaceResourceImpl<IT, OT>
72         implements CollectionSpaceResource<IT, OT> {
73
74     protected final Logger logger = LoggerFactory.getLogger(this.getClass());
75
76     protected final ServiceContext<IT, OT> NULL_CONTEXT = null;
77     // Fields for default client factory and client
78     /** The repository client factory. */
79     private RepositoryClientFactory<IT, OT> repositoryClientFactory;
80     
81     /** The repository client. */
82     private RepositoryClient<IT, OT> repositoryClient;
83     
84     /** The storage client. */
85     private StorageClient storageClient;
86     
87     /**
88      * Extract id.
89      *
90      * @param res the res
91      * @return the string
92      */
93     protected static String extractId(Response res) {
94         MultivaluedMap<String, Object> mvm = res.getMetadata();
95         String uri = (String) ((List<Object>) mvm.get("Location")).get(0);
96         String[] segments = uri.split("/");
97         String id = segments[segments.length - 1];
98         return id;
99     }
100             
101     /**
102      * Instantiates a new abstract collection space resource.
103      */
104     public AbstractCollectionSpaceResourceImpl() {
105         repositoryClientFactory = (RepositoryClientFactory<IT, OT>) RepositoryClientFactory.getInstance();
106     }
107
108     /* (non-Javadoc)
109      * @see org.collectionspace.services.common.CollectionSpaceResource#getServiceName()
110      */
111     @Override
112     abstract public String getServiceName();
113
114
115     /* (non-Javadoc)
116      * @see org.collectionspace.services.common.CollectionSpaceResource#getRepositoryClient(org.collectionspace.services.common.context.ServiceContext)
117      */
118     @Override
119     synchronized public RepositoryClient<IT, OT> getRepositoryClient(ServiceContext<IT, OT> ctx) {
120         if (repositoryClient != null){
121             return repositoryClient;
122         }
123         repositoryClient = repositoryClientFactory.getClient(ctx.getRepositoryClientName());
124         return repositoryClient;
125     }
126
127     /* (non-Javadoc)
128      * @see org.collectionspace.services.common.CollectionSpaceResource#getStorageClient(org.collectionspace.services.common.context.ServiceContext)
129      */
130     @Override
131     synchronized public StorageClient getStorageClient(ServiceContext<IT, OT> ctx) {
132         if(storageClient != null) {
133             return storageClient;
134         }
135         storageClient = new JpaStorageClientImpl();
136         return storageClient;
137     }
138     
139     /* (non-Javadoc)
140      * @see org.collectionspace.services.common.CollectionSpaceResource#createDocumentHandler(org.collectionspace.services.common.context.ServiceContext)
141      */
142     @Override
143     public DocumentHandler createDocumentHandler(ServiceContext<IT, OT> ctx) throws Exception {
144         DocumentHandler docHandler = createDocumentHandler(ctx, ctx.getInput());
145         return docHandler;
146     }
147     
148     /**
149      * Creates the document handler.
150      * 
151      * @param ctx the ctx
152      * @param commonPart the common part
153      * 
154      * @return the document handler
155      * 
156      * @throws Exception the exception
157      */
158     public DocumentHandler createDocumentHandler(ServiceContext<IT, OT> ctx,
159                 Object commonPart) throws Exception {
160         DocumentHandler docHandler = ctx.getDocumentHandler();
161         docHandler.setCommonPart(commonPart);
162         return docHandler;
163     }    
164     
165     /**
166      * Creates the service context.
167      * 
168      * @return the service context< i t, o t>
169      * 
170      * @throws Exception the exception
171      */
172     protected ServiceContext<IT, OT> createServiceContext() throws Exception {          
173         ServiceContext<IT, OT> ctx = createServiceContext(this.getServiceName(),
174                         (IT)null, //inputType
175                         null, // The resource map
176                         (UriInfo)null, // The query params
177                         this.getCommonPartClass());
178         return ctx;
179     }    
180     
181     /**
182      * Creates the service context.
183      * 
184      * @param serviceName the service name
185      * 
186      * @return the service context< i t, o t>
187      * 
188      * @throws Exception the exception
189      */
190     public ServiceContext<IT, OT> createServiceContext(String serviceName) throws Exception {           
191         ServiceContext<IT, OT> ctx = createServiceContext(
192                         serviceName,
193                         (IT)null, // The input part
194                         null, // The resource map
195                         (UriInfo)null, // The queryParams
196                         (Class<?>)null  /*input type's Class*/);
197         return ctx;
198     }
199     
200     protected ServiceContext<IT, OT> createServiceContext(String serviceName, UriInfo ui) throws Exception {            
201         ServiceContext<IT, OT> ctx = createServiceContext(
202                         serviceName,
203                         (IT)null, // The input part
204                         null, // The resource map
205                         (UriInfo)null, // The queryParams
206                         (Class<?>)null  /*input type's Class*/);
207         ctx.setUriInfo(ui);
208         return ctx;
209     }    
210     
211     /**
212      * Creates the service context.
213      * 
214      * @param serviceName the service name
215      * @param input the input
216      * 
217      * @return the service context< i t, o t>
218      * 
219      * @throws Exception the exception
220      */
221     protected ServiceContext<IT, OT> createServiceContext(String serviceName,
222                 IT input) throws Exception {            
223         ServiceContext<IT, OT> ctx = createServiceContext(serviceName,
224                         input,
225                         null, // The resource map
226                         (UriInfo)null, /*queryParams*/
227                         (Class<?>)null  /*input type's Class*/);
228         return ctx;
229     }
230     
231     protected ServiceContext<IT, OT> createServiceContext(String serviceName,
232                 IT input,
233                 UriInfo uriInfo) throws Exception {     
234         ServiceContext<IT, OT> ctx = createServiceContext(serviceName,
235                         input,
236                         null, // The resource map
237                         uriInfo, /*queryParams*/
238                         (Class<?>)null  /*input type's Class*/);
239         return ctx;
240     }
241     
242     protected ServiceContext<IT, OT> createServiceContext(UriInfo uriInfo) throws Exception {
243         ServiceContext<IT, OT> ctx = createServiceContext(
244                         (IT)null, /*input*/
245                         uriInfo,
246                         (Class<?>)null  /*input type's Class*/);
247         return ctx;
248     }
249
250     /**
251      * Creates the service context.
252      * 
253      * @param input the input
254      * 
255      * @return the service context< i t, o t>
256      * 
257      * @throws Exception the exception
258      */
259     protected ServiceContext<IT, OT> createServiceContext(IT input) throws Exception {          
260         ServiceContext<IT, OT> ctx = createServiceContext(
261                         input,
262                         (Class<?>)null /*input type's Class*/);
263         return ctx;
264     }
265     
266     protected ServiceContext<IT, OT> createServiceContext(IT input, UriInfo uriInfo) throws Exception {         
267         ServiceContext<IT, OT> ctx = createServiceContext(
268                         input,
269                         uriInfo,
270                         null ); // The class param/argument
271         return ctx;
272     }    
273     
274     /**
275      * Creates the service context.
276      * 
277      * @param input the input
278      * @param theClass the the class
279      * 
280      * @return the service context
281      * 
282      * @throws Exception the exception
283      */
284     protected ServiceContext<IT, OT> createServiceContext(IT input, Class<?> theClass) throws Exception {       
285         ServiceContext<IT, OT> ctx = createServiceContext(
286                         input,
287                         (UriInfo)null, //queryParams,
288                         theClass);
289         return ctx;
290     }
291     
292     protected ServiceContext<IT, OT> createServiceContext(IT input, Class<?> theClass, UriInfo uriInfo) throws Exception {      
293         ServiceContext<IT, OT> ctx = createServiceContext(
294                         input,
295                         uriInfo,
296                         theClass);
297         return ctx;
298     }
299     
300     protected ServiceContext<IT, OT> createServiceContext(
301                 String serviceName,
302                 ResourceMap resourceMap,
303                 UriInfo uriInfo) throws Exception {
304         ServiceContext<IT, OT> ctx = createServiceContext(
305                         serviceName,
306                         null, // The input object
307                         resourceMap,
308                         uriInfo,
309                         null /* the class of the input type */);
310         return ctx;
311     }
312         
313     protected ServiceContext<IT, OT> createServiceContext(
314                 IT input,
315                 ResourceMap resourceMap,
316                 UriInfo uriInfo) throws Exception {
317         ServiceContext<IT, OT> ctx = createServiceContext(
318                         this.getServiceName(),
319                         input,
320                         resourceMap,
321                         uriInfo,
322                         null /* the class of the input type */);
323         return ctx;
324     }
325     
326     protected ServiceContext<IT, OT> createServiceContext(
327                 String serviceName,
328                 IT input,
329                 ResourceMap resourceMap,
330                 UriInfo uriInfo) throws Exception {
331         ServiceContext<IT, OT> ctx = createServiceContext(
332                         serviceName,
333                         input,
334                         resourceMap,
335                         uriInfo,
336                         null /* the class of the input type */);
337         return ctx;
338     }
339         
340     /**
341      * Creates the service context.
342      * 
343      * @param input the input
344      * @param queryParams the query params
345      * @param theClass the the class
346      * 
347      * @return the service context< i t, o t>
348      * 
349      * @throws Exception the exception
350      */
351     private ServiceContext<IT, OT> createServiceContext(
352                 IT input,
353                 UriInfo uriInfo,
354                 Class<?> theClass) throws Exception {
355         return createServiceContext(this.getServiceName(),
356                         input,
357                         null, // The resource map
358                         uriInfo,
359                         theClass);
360     }
361
362     /**
363      * Creates the service context.
364      * 
365      * @param serviceName the service name
366      * @param input the input
367      * @param queryParams the query params
368      * @param theClass the the class
369      * 
370      * @return the service context< i t, o t>
371      * 
372      * @throws Exception the exception
373      */
374     private ServiceContext<IT, OT> createServiceContext(
375                 String serviceName,
376                 IT input,
377                 ResourceMap resourceMap,
378                 UriInfo uriInfo,
379                 Class<?> theClass) throws Exception {
380         ServiceContext<IT, OT> ctx = getServiceContextFactory().createServiceContext(
381                         serviceName,
382                         input,
383                         resourceMap,
384                         uriInfo,
385                         theClass != null ? theClass.getPackage().getName() : null,
386                         theClass != null ? theClass.getName() : null);
387         if (theClass != null) {
388             ctx.setProperty(ServiceContextProperties.ENTITY_CLASS, theClass);
389         }
390         
391         return ctx;
392     }
393         
394     /**
395      * Gets the version string.
396      * 
397      * @return the version string
398      */
399     abstract protected String getVersionString();
400     
401     /**
402      * Gets the version.
403      * 
404      * @return the version
405      */
406     @GET
407     @Path("/version")    
408     @Produces("application/xml")
409     public Version getVersion() {
410         Version result = new Version();
411         
412         result.setVersionString(getVersionString());
413         
414         return result;
415     }
416     
417     /*
418      * Get the service description
419      */
420     @GET
421     @Path(CollectionSpaceClient.SERVICE_DESCRIPTION_PATH)
422     public ServiceDescription getDescription(@Context UriInfo uriInfo) {
423         ServiceDescription result = null;
424
425         ServiceContext<IT, OT>  ctx = null;
426         try {
427             ctx = createServiceContext(uriInfo);
428             result = getDescription(ctx);
429         } catch (Exception e) {
430                 String errMsg = String.format("Request to get service description information for the '%s' service failed.",
431                                 this.getServiceContextFactory());
432             throw bigReThrow(e, errMsg);
433         }
434         
435         return result;
436     }
437     
438     /**
439      * Each resource can override this method if they need to.
440      * 
441      * @param ctx
442      * @return
443      */
444     public ServiceDescription getDescription(ServiceContext<IT, OT> ctx) {
445         ServiceDescription result = new ServiceDescription();
446         
447         result.setDocumentType(getDocType(ctx.getTenantId()));
448         
449         return result;
450     }    
451
452     public void checkResult(Object resultToCheck, String csid, String serviceMessage) throws CSWebApplicationException {
453         if (resultToCheck == null) {
454             Response response = Response.status(Response.Status.NOT_FOUND).entity(
455                     serviceMessage + "csid=" + csid
456                     + ": was not found.").type(
457                     "text/plain").build();
458             throw new CSWebApplicationException(response);
459         }
460     }
461
462     protected void ensureCSID(String csid, String crudType) throws CSWebApplicationException {
463         ensureCSID(csid, crudType, "csid");
464     }
465
466     protected void ensureCSID(String csid, String crudType, String whichCsid) throws CSWebApplicationException {
467            if (logger.isDebugEnabled()) {
468                logger.debug(crudType + " for " + getClass().getName() + " with csid=" + csid);
469            }
470            if (csid == null || "".equals(csid)) {
471                logger.error(crudType + " for " + getClass().getName() + " missing csid!");
472                Response response = Response.status(Response.Status.BAD_REQUEST).entity(crudType + " failed on " + getClass().getName() + ' '+whichCsid+'=' + csid).type("text/plain").build();
473                throw new CSWebApplicationException(response);
474            }
475        }
476
477     protected CSWebApplicationException bigReThrow(Exception e, String serviceMsg) throws CSWebApplicationException {
478         return bigReThrow(e, serviceMsg, "");
479     }
480
481     protected CSWebApplicationException bigReThrow(Exception e, String serviceMsg, String csid) throws CSWebApplicationException {
482         boolean logException = true;
483         CSWebApplicationException result = null;
484         Response response;
485         String detail = Tools.errorToString(e, true);
486         String detailNoTrace = Tools.errorToString(e, true, 3);
487         
488         if (e instanceof UnauthorizedException) {
489             response = Response.status(Response.Status.UNAUTHORIZED).entity(serviceMsg + e.getMessage()).type("text/plain").build();
490             result = new CSWebApplicationException(e, response);
491
492         } else if (e instanceof DocumentNotFoundException) {
493                 //
494                 // Don't log this error unless we're in 'trace' mode
495                 //
496                 logException = false;
497             response = Response.status(Response.Status.NOT_FOUND).entity(serviceMsg + " on " + getClass().getName() + " csid=" + csid).type("text/plain").build();
498             result = new CSWebApplicationException(e, response);
499             
500         } else if (e instanceof TransactionException) {
501             int code = ((TransactionException) e).getErrorCode();
502             response = Response.status(code).entity(e.getMessage()).type("text/plain").build();
503             result = new CSWebApplicationException(e, response);
504
505         } else if (e instanceof BadRequestException) {
506             int code = ((BadRequestException) e).getErrorCode();
507             if (code == 0) {
508                 code = Response.Status.BAD_REQUEST.getStatusCode();
509             }
510             // CSPACE-1110
511             response = Response.status(code).entity(serviceMsg + e.getMessage()).type("text/plain").build();
512             // return new WebApplicationException(e, code);
513             result = new CSWebApplicationException(e, response);
514
515         } else if (e instanceof DocumentException) {
516             int code = ((DocumentException) e).getErrorCode();
517             if (code == 0){
518                code = Response.Status.BAD_REQUEST.getStatusCode();
519             }
520             // CSPACE-1110
521             response = Response.status(code).entity(serviceMsg + e.getMessage()).type("text/plain").build();
522             // return new WebApplicationException(e, code);
523             result = new CSWebApplicationException(e, response);
524            
525         } else if (e instanceof CSWebApplicationException) {
526             // subresource may have already thrown this exception
527             // so just pass it on
528             result = (CSWebApplicationException) e;
529
530         } else { // e is now instanceof Exception
531             response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(serviceMsg + " detail: " + detailNoTrace).type("text/plain").build();
532             result = new CSWebApplicationException(e, response);
533         }
534         //
535         // Some exceptions like DocumentNotFoundException won't be logged unless we're in 'trace' mode
536         //
537         boolean traceEnabled = logger.isTraceEnabled();
538         if (logException == true || traceEnabled == true) {
539                 if (traceEnabled == true) {
540                         logger.error(getClass().getName() + " detail: " + detail, e);
541                 } else {
542                         logger.error(getClass().getName() + " detail: " + detailNoTrace);
543                 }
544         }
545         
546         return result;
547     }
548     
549         @Override
550         public boolean allowAnonymousAccess(HttpRequest request,
551                         Class<?> resourceClass) {
552                 return false;
553         }
554         
555     /**
556      * Returns a UriRegistry entry: a map of tenant-qualified URI templates
557      * for the current resource, for all tenants
558      * 
559      * @return a map of URI templates for the current resource, for all tenants
560      */
561     public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries() {
562         Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
563                 new HashMap<UriTemplateRegistryKey,StoredValuesUriTemplate>();
564         List<String> tenantIds = getTenantBindingsReader().getTenantIds();
565         for (String tenantId : tenantIds) {
566                 uriRegistryEntriesMap.putAll(getUriRegistryEntries(tenantId, getDocType(tenantId), UriTemplateFactory.RESOURCE));
567         }
568         return uriRegistryEntriesMap;
569     }
570     
571     /**
572      * Returns a resource's document type.
573      * 
574      * @param tenantId
575      * @return
576      */
577     @Override
578     public String getDocType(String tenantId) {
579         return getDocType(tenantId, getServiceName());
580     }
581
582     /**
583      * Returns the document type associated with a specified service, within a specified tenant.
584      * 
585      * @param tenantId a tenant ID
586      * @param serviceName a service name
587      * @return the Nuxeo document type associated with that service and tenant.
588      */
589     // FIXME: This method may properly belong in a different services package or class.
590     // Also, we need to check for any existing methods that may duplicate this one.
591     protected String getDocType(String tenantId, String serviceName) {
592         String docType = "";
593         if (Tools.isBlank(tenantId)) {
594             return docType;
595         }
596         ServiceBindingType sb = getTenantBindingsReader().getServiceBinding(tenantId, serviceName);
597         if (sb == null) {
598             return docType;
599         }
600         docType = sb.getObject().getName(); // Reads the Document Type from tenant bindings configuration
601         return docType;
602     }
603
604         /**
605      * Returns a UriRegistry entry: a map of tenant-qualified URI templates
606      * for the current resource, for a specified tenants
607      * 
608      * @return a map of URI templates for the current resource, for a specified tenant
609      */
610     @Override
611     public Map<UriTemplateRegistryKey,StoredValuesUriTemplate> getUriRegistryEntries(String tenantId,
612             String docType, UriTemplateFactory.UriTemplateType type) {
613         Map<UriTemplateRegistryKey,StoredValuesUriTemplate> uriRegistryEntriesMap =
614                 new HashMap<UriTemplateRegistryKey,StoredValuesUriTemplate>();
615         UriTemplateRegistryKey key;
616         if (Tools.isBlank(tenantId) || Tools.isBlank(docType)) {
617             return uriRegistryEntriesMap;
618         }
619         key = new UriTemplateRegistryKey();
620         key.setTenantId(tenantId);
621         key.setDocType(docType); 
622         uriRegistryEntriesMap.put(key, getUriTemplate(type));
623         return uriRegistryEntriesMap;
624     }
625     
626     /**
627      * Returns a URI template of the appropriate type, populated with the
628      * current service name as one of its stored values.
629      *      * 
630      * @param type a URI template type
631      * @return a URI template of the appropriate type.
632      */
633     @Override
634     public StoredValuesUriTemplate getUriTemplate(UriTemplateFactory.UriTemplateType type) {
635         Map<String,String> storedValuesMap = new HashMap<String,String>();
636         storedValuesMap.put(UriTemplateFactory.SERVICENAME_VAR, getServiceName());
637         StoredValuesUriTemplate template =
638                 UriTemplateFactory.getURITemplate(type, storedValuesMap);
639         return template;
640     }
641
642     /**
643      * Returns a reader for reading values from tenant bindings configuration
644      * 
645      * @return a tenant bindings configuration reader
646      */
647     @Override
648     public TenantBindingConfigReaderImpl getTenantBindingsReader() {
649         return ServiceMain.getInstance().getTenantBindingConfigReader();
650     }
651     
652     /**
653      * Get max cache age for HTTP responses from the tenant's service bindings.
654      * 
655      * @param ctx
656      * @return
657      */
658     protected int getCacheMaxAge(ServiceContext<IT, OT> ctx) {
659         BigInteger result = null;
660         
661         try {
662                         Params docHandlerParams = ServiceConfigUtils.getDocHandlerParams(ctx.getTenantId(), ctx.getServiceName());
663                         if (docHandlerParams.getCacheMaxAge() != null) {
664                                 result = docHandlerParams.getCacheMaxAge();
665                         }
666                 } catch (DocumentException e) {
667                         logger.debug("Failed to retrieve cache-age-max from service bindings.", e);
668                 }
669
670         return result != null ? result.intValue() : 0;
671     }
672     
673     protected Response.ResponseBuilder setCacheControl(ServiceContext<IT, OT> ctx, Response.ResponseBuilder responseBuilder) {
674         int cacheMaxAge = getCacheMaxAge(ctx);
675         
676         if (cacheMaxAge > 0) {
677                 CacheControl cacheControl = new CacheControl();
678                         cacheControl.setMaxAge(getCacheMaxAge(ctx));
679                         responseBuilder.cacheControl(cacheControl);
680                 logger.debug(String.format("Cache-max-age for service '%s' is set to '%d' in the service bindings for tenant ID='%s'.",
681                                 ctx.getServiceName(), cacheMaxAge, ctx.getTenantId()));
682         }
683         
684         return responseBuilder;
685     }   
686 }