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