]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
24e37fbf6dfa3c84caa79220cedbbf1623d9ad49
[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.context;
25
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Properties;
31
32 import javax.ws.rs.core.MultivaluedMap;
33 import javax.ws.rs.core.Request;
34 import javax.ws.rs.core.UriInfo;
35
36 import org.collectionspace.authentication.spi.AuthNContext;
37 import org.collectionspace.services.client.AuthorityClient;
38 import org.collectionspace.services.client.CollectionSpaceClient;
39 import org.collectionspace.services.client.IClientQueryParams;
40 import org.collectionspace.services.client.IQueryManager;
41 import org.collectionspace.services.client.workflow.WorkflowClient;
42 import org.collectionspace.services.common.ServiceMain;
43 import org.collectionspace.services.common.api.Tools;
44 import org.collectionspace.services.common.authorization_mgt.AuthorizationCommon;
45 import org.collectionspace.services.common.config.PropertyItemUtils;
46 import org.collectionspace.services.common.config.ServiceConfigUtils;
47 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
48 import org.collectionspace.services.common.document.DocumentHandler;
49 import org.collectionspace.services.common.document.DocumentFilter;
50 import org.collectionspace.services.common.document.ValidatorHandler;
51 import org.collectionspace.services.common.security.SecurityContext;
52 import org.collectionspace.services.common.security.SecurityContextImpl;
53 import org.collectionspace.services.common.security.UnauthorizedException;
54 import org.collectionspace.services.config.ClientType;
55 import org.collectionspace.services.config.service.ObjectPartType;
56 import org.collectionspace.services.config.service.ServiceBindingType;
57 import org.collectionspace.services.config.tenant.RemoteClientConfig;
58 import org.collectionspace.services.config.tenant.RepositoryDomainType;
59 import org.collectionspace.services.config.tenant.TenantBindingType;
60 import org.collectionspace.services.config.types.PropertyItemType;
61 import org.collectionspace.services.config.types.PropertyType;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 /**
66  * AbstractServiceContext
67  *
68  * $LastChangedRevision: $
69  * $LastChangedDate: $
70  */
71 /**
72  * @author pschmitz
73  *
74  * @param <IT>
75  * @param <OT>
76  */
77 /**
78  * @author pschmitz
79  *
80  * @param <IT>
81  * @param <OT>
82  */
83 @SuppressWarnings("rawtypes")
84 public abstract class AbstractServiceContextImpl<IT, OT>
85         implements ServiceContext<IT, OT> {
86
87     /** The logger. */
88     final Logger logger = LoggerFactory.getLogger(AbstractServiceContextImpl.class);
89     
90     /** The properties. */
91     Map<String, Object> properties = new HashMap<String, Object>();
92     /** The object part map. */
93     Map<String, ObjectPartType> objectPartMap = new HashMap<String, ObjectPartType>();
94     /** The service binding. */
95     protected ServiceBindingType serviceBinding;
96     /** The tenant binding. */
97     private TenantBindingType tenantBinding;
98     /** repository domain used by the service */
99     private RepositoryDomainType repositoryDomain;
100         /** The override document type. */
101     private String overrideDocumentType = null;
102     /** The val handlers. */
103     private List<ValidatorHandler<IT, OT>> valHandlers = null;
104     /** The authority client -use for shared authority server */
105     private AuthorityClient authorityClient = null;
106     /** The doc handler. */
107     private DocumentHandler docHandler = null;
108     /** security context */
109     private SecurityContext securityContext;
110     /** The sessions JAX-RS URI information */
111     private UriInfo uriInfo;
112     /** The JAX-RS request information */
113     private Request requestInfo;
114     /** The current repository session */
115     private Object currentRepositorySession;
116     /** A reference count for the current repository session */
117     private int currentRepoSesssionRefCount = 0;
118         
119     /**
120      * Instantiates a new abstract service context impl.
121      */
122     private AbstractServiceContextImpl() {
123         // private constructor for singleton pattern
124     }
125     // request query params
126     /** The query params. */
127     private MultivaluedMap<String, String> queryParams;
128
129     /**
130      * Instantiates a new abstract service context impl.
131      * 
132      * @param serviceName the service name
133      * 
134      * @throws UnauthorizedException the unauthorized exception
135      */
136     protected AbstractServiceContextImpl(String serviceName, UriInfo uriInfo) throws UnauthorizedException {
137
138         //establish security context
139         securityContext = new SecurityContextImpl(uriInfo);
140         //make sure tenant context exists
141         checkTenantContext();
142
143         String tenantId = securityContext.getCurrentTenantId();
144         if (AuthorizationCommon.ALL_TENANTS_MANAGER_TENANT_ID.equals(tenantId) ||
145                         AuthNContext.ANONYMOUS_TENANT_ID.equals(tenantId)) {
146                 // Tenant Manager has no tenant binding, so don't bother...
147                 tenantBinding = null;
148                 serviceBinding = null;
149                 repositoryDomain = null;
150         } else {
151                 //retrieve service bindings
152                 TenantBindingConfigReaderImpl tReader =
153                         ServiceMain.getInstance().getTenantBindingConfigReader();
154                 tenantBinding = tReader.getTenantBinding(tenantId);
155                 if (tenantBinding == null) {
156                     String msg = "No tenant binding found for tenantId=" + tenantId
157                             + " while processing request for service= " + serviceName;
158                     logger.error(msg);
159                     throw new IllegalStateException(msg);
160                 }
161                 serviceBinding = tReader.getServiceBinding(tenantId, serviceName);
162                 if (serviceBinding == null) {
163                     String msg = "No service binding found while processing request for "
164                             + serviceName + " for tenant id=" + getTenantId()
165                             + " name=" + getTenantName();
166                     logger.error(msg);
167                     throw new IllegalStateException(msg);
168                 }
169                 if (logger.isDebugEnabled()) {
170                     logger.debug("tenantId=" + tenantId
171                             + " service binding=" + serviceBinding.getName());
172                 }
173                 repositoryDomain = tReader.getRepositoryDomain(tenantId, serviceName);
174                 if (repositoryDomain != null) {
175                     if (logger.isDebugEnabled()) {
176                         logger.debug("tenantId=" + tenantId
177                                 + " repository doamin=" + repositoryDomain.getName());
178                     }
179                 }
180         }
181     }
182     
183     public int getTimeoutParam(UriInfo ui) {
184                 int result = DEFAULT_TX_TIMEOUT;
185
186                 MultivaluedMap<String, String> queryParams = (ui == null) ? null : ui.getQueryParameters();
187                 if (queryParams != null) {
188                         String timeoutString = queryParams.getFirst(IClientQueryParams.IMPORT_TIMEOUT_PARAM);
189                         if (timeoutString == null) {
190                                 timeoutString = queryParams.getFirst(IClientQueryParams.IMPORT_TIMOUT_PARAM);
191                         }
192                         
193                         if (timeoutString != null) {
194                                 try {                                   
195                                         result = Integer.parseInt(timeoutString);
196                                 } catch (NumberFormatException e) {
197                                         logger.warn("Transaction timeout period parameter could not be parsed.  The characters in the parameter string must all be decimal digits.  The Import service will use the default timeout period instead.",
198                                                         e);
199                                 }
200                         }
201                 }
202
203                 return result;
204         }
205     
206     @Override
207     public int getTimeoutSecs() {
208         UriInfo uriInfo = this.getUriInfo();
209         return this.getTimeoutParam(uriInfo);
210     }
211
212     /**
213      * Returns TRUE unless the "recordUpdates" query param is set with a value of either "false", "FALSE", or "0"
214      * @return
215      */
216     @Override
217     public boolean shouldUpdateCoreValues() {
218                 boolean recordUpdates = true;
219                 
220                 MultivaluedMap<String, String> queryParams = getQueryParams();
221                 String paramValue = queryParams.getFirst(IClientQueryParams.UPDATE_CORE_VALUES);
222                 if (paramValue != null && paramValue.equalsIgnoreCase(Boolean.FALSE.toString())) { // Find our if the caller wants us to record updates
223                         recordUpdates = false;
224                 } else if (paramValue != null && paramValue.equals(Long.toString(0))) {
225                         recordUpdates = false;
226                 }
227                 
228                 return recordUpdates;
229     }
230     
231         /**
232          * Default value is 'FALSE'
233          * If this returns true, it means that the refname values in referencing objects (records that reference authority or vocabulary terms) will be updated
234          * regardless of their current value.  This is sometimes needed when refname values become stale for one of several reasons.
235          * @return
236          */
237     @Override
238     public boolean shouldForceUpdateRefnameReferences() {
239                 boolean forceUpdates = false;
240                 
241                 MultivaluedMap<String, String> queryParams = getQueryParams();
242                 String paramValue = queryParams.getFirst(IClientQueryParams.FORCE_REFNAME_UPDATES);
243                 if (paramValue != null && paramValue.equalsIgnoreCase(Boolean.TRUE.toString())) { // Find our if the caller wants us to force refname updates
244                         forceUpdates = true;
245                 } else if (paramValue != null && paramValue.equals(Long.toString(1))) {
246                         forceUpdates = true;
247                 }
248                 
249                 return forceUpdates;
250     }
251     
252     
253     /* (non-Javadoc)
254      * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel()
255      */
256     @Override
257     public String getCommonPartLabel() {
258         return getCommonPartLabel(getServiceName());
259     }
260
261     /* (non-Javadoc)
262      * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel(java.lang.String)
263      */
264     public String getCommonPartLabel(String schemaName) {
265         return schemaName.toLowerCase() + PART_LABEL_SEPARATOR + PART_COMMON_LABEL;
266     }
267
268     /* (non-Javadoc)
269      * @see org.collectionspace.services.common.context.ServiceContext#getPartsMetadata()
270      */
271     @Override
272     public Map<String, ObjectPartType> getPartsMetadata() {
273         if (objectPartMap.size() != 0) {
274             return objectPartMap;
275         }
276         ServiceBindingUtils.getPartsMetadata(getServiceBinding(), objectPartMap);
277         return objectPartMap;
278     }
279
280     /**
281      * Gets the properties for part.
282      * 
283      * @param partLabel the part label
284      * 
285      * @return the properties for part
286      */
287     public List<PropertyItemType> getPropertiesForPart(String partLabel) {
288         Map<String, ObjectPartType> partMap = getPartsMetadata();
289         ObjectPartType part = partMap.get(partLabel);
290         if (part == null) {
291             throw new RuntimeException("No such part found: " + partLabel);
292         }
293         List<PropertyType> propNodeList = part.getProperties();
294         return propNodeList.isEmpty() ? null : propNodeList.get(0).getItem();
295     }
296
297     /**
298      * @param partLabel The name of the scehma part to search in
299      * @param propName The name of the property (or properties) to find
300      * @param qualified Whether the returned values should be qualified with the
301      *          partLabel. This is when the property values are schema field references.
302      * @return List of property values for the matched property on the named schema part.
303      */
304     public List<String> getPropertyValuesForPart(String partLabel, String propName, boolean qualified) {
305         List<PropertyItemType> allProps = getPropertiesForPart(partLabel);
306         return PropertyItemUtils.getPropertyValuesByName(allProps, propName,
307                 (qualified ? (partLabel + ":") : null));
308     }
309
310     /**
311      * @param propName The name of the property (or properties) to find
312      * @param qualified Whether the returned values should be qualified with the
313      *          partLabel. This is when the property values are schema field references.
314      * @return List of property values for the matched property on any schema part.
315      */
316     public List<String> getAllPartsPropertyValues(String propName, boolean qualified) {
317         return ServiceBindingUtils.getAllPartsPropertyValues(getServiceBinding(), propName, qualified);
318     }
319
320     /* (non-Javadoc)
321      * @see org.collectionspace.services.common.context.ServiceContext#getServiceBindingPropertyValue(java.lang.String)
322      */
323     public String getServiceBindingPropertyValue(String propName) {
324         return ServiceBindingUtils.getPropertyValue(getServiceBinding(), propName);
325     }
326
327     /**
328      * Gets the common part properties.
329      * 
330      * @return the common part properties
331      */
332     public List<PropertyItemType> getCommonPartProperties() {
333         return getPropertiesForPart(getCommonPartLabel());
334     }
335
336     /**
337      * @param propName The name of the property (or properties) to find
338      * @param qualified Whether the returned values should be qualified with the
339      *          partLabel. This is when the property values are schema field references.
340      * @return List of property values for the matched property on the common schema part.
341      */
342     public List<String> getCommonPartPropertyValues(String propName, boolean qualified) {
343         return getPropertyValuesForPart(getCommonPartLabel(), propName, qualified);
344     }
345
346     /* (non-Javadoc)
347      * @see org.collectionspace.services.common.context.ServiceContext#getQualifiedServiceName()
348      */
349     @Override
350     public String getQualifiedServiceName() {
351         return TenantBindingConfigReaderImpl.getTenantQualifiedServiceName(getTenantId(), getServiceName());
352     }
353
354     /* (non-Javadoc)
355      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientName()
356      */
357     @Override
358     public String getRepositoryClientName() {
359         if (repositoryDomain == null) {
360             return null;
361         }
362         return repositoryDomain.getRepositoryClient();
363     }
364
365     /* (non-Javadoc)
366      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientType()
367      */
368     @Override
369     public ClientType getRepositoryClientType() {
370         //assumption: there is only one repository client configured
371         return ServiceMain.getInstance().getClientType();
372     }
373
374     /* (non-Javadoc)
375      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
376      */
377     @Override
378     public String getRepositoryDomainName() {
379         if (repositoryDomain == null) {
380             return null;
381         }
382         return repositoryDomain.getName();
383     }
384
385     /* (non-Javadoc)
386      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
387      */
388     @Override
389     public String getRepositoryDomainStorageName() {
390         if (repositoryDomain == null) {
391             return null;
392         }
393         return repositoryDomain.getStorageName();
394     }
395
396     /* (non-Javadoc)
397      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceId()
398      */
399     @Override
400     public String getRepositoryWorkspaceId() {
401         return ServiceMain.getInstance().getWorkspaceId(getTenantId(), getServiceName());
402     }
403
404     /* (non-Javadoc)
405      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceName()
406      */
407     @Override
408     public String getRepositoryWorkspaceName() {
409         //service name is workspace name by convention
410         return serviceBinding.getName();
411     }
412
413     /* (non-Javadoc)
414      * @see org.collectionspace.services.common.context.ServiceContext#getServiceBinding()
415      */
416     @Override
417     public ServiceBindingType getServiceBinding() {
418         return serviceBinding;
419     }
420
421     /* (non-Javadoc)
422      * @see org.collectionspace.services.common.context.ServiceContext#getServiceName()
423      */
424     @Override
425     public String getServiceName() {
426         return serviceBinding.getName();
427     }
428
429     /* (non-Javadoc)
430      * @see org.collectionspace.services.common.context.ServiceContext#getDocumentType()
431      */
432     @Override
433     public String getDocumentType() {
434         // If they have not overridden the setting, use the type of the service
435         // object.
436         return (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
437     }
438     
439     @Override
440     public String getTenantQualifiedDoctype(String docType) {
441         // If they have not overridden the setting, use the type of the service
442         // object.
443         String result = ServiceBindingUtils.getTenantQualifiedDocType(this.getTenantId(), docType);
444         
445         return result;
446     }
447     
448     @Override
449     public String getTenantQualifiedDoctype() {
450         String docType = (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
451         return getTenantQualifiedDoctype(docType);
452     }
453
454     /* (non-Javadoc)
455      * @see org.collectionspace.services.common.context.ServiceContext#setDocumentType(java.lang.String)
456      */
457     @Override
458     public void setDocumentType(String docType) {
459         overrideDocumentType = docType;
460     }
461
462     /* (non-Javadoc)
463      * @see org.collectionspace.services.common.context.ServiceContext#getSecurityContext()
464      */
465     @Override
466     public SecurityContext getSecurityContext() {
467         return securityContext;
468     }
469
470     /* (non-Javadoc)
471      * @see org.collectionspace.services.common.context.ServiceContext#getUserId()
472      */
473     @Override
474     public String getUserId() {
475         return securityContext.getUserId();
476     }
477
478     /* (non-Javadoc)
479      * @see org.collectionspace.services.common.context.ServiceContext#getTenantId()
480      */
481     @Override
482     public String getTenantId() {
483         return securityContext.getCurrentTenantId();
484     }
485
486     /* (non-Javadoc)
487      * @see org.collectionspace.services.common.context.ServiceContext#getTenantName()
488      */
489     @Override
490     public String getTenantName() {
491         return securityContext.getCurrentTenantName();
492     }
493
494     /* (non-Javadoc)
495      * @see org.collectionspace.services.common.context.ServiceContext#getInput()
496      */
497     @Override
498     public abstract IT getInput();
499
500     /* (non-Javadoc)
501      * @see org.collectionspace.services.common.context.ServiceContext#setInput(java.lang.Object)
502      */
503     @Override
504     public abstract void setInput(IT input);
505
506     /* (non-Javadoc)
507      * @see org.collectionspace.services.common.context.ServiceContext#getOutput()
508      */
509     @Override
510     public abstract OT getOutput();
511
512     /* (non-Javadoc)
513      * @see org.collectionspace.services.common.context.ServiceContext#setOutput(java.lang.Object)
514      */
515     @Override
516     public abstract void setOutput(OT output);
517
518     /* (non-Javadoc)
519      * @see org.collectionspace.services.common.context.ServiceContext#getProperties()
520      */
521     @Override
522     public Map<String, Object> getProperties() {
523         return properties;
524     }
525
526     /* (non-Javadoc)
527      * @see org.collectionspace.services.common.context.ServiceContext#setProperties(java.util.Map)
528      */
529     @Override
530     public void setProperties(Map<String, Object> props) {
531         properties.putAll(props);
532     }
533
534     /* (non-Javadoc)
535      * @see org.collectionspace.services.common.context.ServiceContext#getProperty(java.lang.String)
536      */
537     public Object getProperty(String name) {
538         return properties.get(name);
539     }
540
541     /* (non-Javadoc)
542      * @see org.collectionspace.services.common.context.ServiceContext#setProperty(java.lang.String, java.lang.Object)
543      */
544     public void setProperty(String name, Object o) {
545         properties.put(name, o);
546     }
547
548     /**
549      * checkTenantContext makss sure tenant context exists
550      *
551      * @return the string
552      *
553      * @throws UnauthorizedException the unauthorized exception
554      */
555     private void checkTenantContext() throws UnauthorizedException {
556
557         String tenantId = securityContext.getCurrentTenantId();
558         if (tenantId == null) {
559             String msg = "Could not find tenant context";
560             logger.error(msg);
561             throw new UnauthorizedException(msg);
562         }
563     }
564
565     /**
566      * Helps to filter for queries that either want to include or exclude documents in deleted workflow states.
567      * 
568      * @param queryParams
569      * @return
570      */
571     private static String buildWorkflowWhereClause(MultivaluedMap<String, String> queryParams) {
572         String result = null;
573         
574         String includeDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
575         String includeOnlyDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED);
576
577         if (includeDeleted != null && includeDeleted.equalsIgnoreCase(Boolean.FALSE.toString())) {      
578                 result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
579                                 WorkflowClient.WORKFLOWSTATE_DELETED, WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED, WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED);
580         } else if (includeOnlyDeleted != null && includeOnlyDeleted.equalsIgnoreCase(Boolean.TRUE.toString())) {
581                 result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
582                                 WorkflowClient.WORKFLOWSTATE_PROJECT, WorkflowClient.WORKFLOWSTATE_LOCKED, WorkflowClient.WORKFLOWSTATE_REPLICATED);
583         }
584         
585         return result;
586     }
587     
588     /**
589      * Creates the document handler instance.
590      * 
591      * @return the document handler
592      * 
593      * @throws Exception the exception
594      */
595     private DocumentHandler createDocumentHandlerInstance() throws Exception {
596         docHandler = ServiceConfigUtils.createDocumentHandlerInstance(tenantBinding, serviceBinding);
597         //
598         // Create a default document filter
599         //
600         docHandler.setServiceContext(this);
601         DocumentFilter docFilter = docHandler.createDocumentFilter();
602         //
603         // If the context was created with query parameters,
604         // reflect the values of those parameters in the document filter
605         // to specify sort ordering, pagination, etc.
606         //
607         MultivaluedMap<String, String> queryParameters = this.getQueryParams();
608         if (queryParameters != null) {
609           docFilter.setSortOrder(queryParameters);
610           docFilter.setPagination(queryParameters);
611           String workflowWhereClause = buildWorkflowWhereClause(queryParameters);
612           if (workflowWhereClause != null) {
613                   docFilter.appendWhereClause(workflowWhereClause, IQueryManager.SEARCH_QUALIFIER_AND);                 
614           }            
615
616         }
617         docHandler.setDocumentFilter(docFilter);
618
619         return docHandler;
620     }
621
622     /* (non-Javadoc)
623      * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHandler()
624      */
625     @Override
626     public DocumentHandler getDocumentHandler() throws Exception {
627         DocumentHandler result = docHandler;
628         // create a new instance if one does not yet exist
629         if (result == null) {
630             result = createDocumentHandlerInstance();
631         }
632         return result;
633     }
634
635     @Override
636     public void setDocumentHandler(DocumentHandler handler) throws Exception {
637         if (handler != null) {
638                 docHandler = handler;
639         }
640     }
641
642     /* (non-Javadoc)
643      * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHanlder(javax.ws.rs.core.MultivaluedMap)
644      */
645     @Override
646     public DocumentHandler getDocumentHandler(MultivaluedMap<String, String> queryParams) throws Exception {
647         DocumentHandler result = getDocumentHandler();
648         DocumentFilter documentFilter = result.getDocumentFilter(); //to see results in debugger variables view
649         documentFilter.setPagination(queryParams);
650         return result;
651     }
652     
653     /*
654      * If this element is set in the service binding then use it otherwise
655      * assume that asserts are NOT disabled.
656      */
657     private boolean disableValidationAsserts() {
658         boolean result;
659         Boolean disableAsserts = getServiceBinding().isDisableAsserts();
660         result = (disableAsserts != null) ? disableAsserts : false;
661         return result;
662     }
663     
664     /* (non-Javadoc)
665      * @see org.collectionspace.services.common.context.ServiceContext#getValidatorHandlers()
666      */
667     @Override
668     public List<ValidatorHandler<IT, OT>> getValidatorHandlers() throws Exception {
669         if (valHandlers != null) {
670             return valHandlers;
671         }
672         List<String> handlerClazzes = getServiceBinding().getValidatorHandler();
673         List<ValidatorHandler<IT, OT>> handlers = new ArrayList<ValidatorHandler<IT, OT>>(handlerClazzes.size());
674         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
675         for (String clazz : handlerClazzes) {
676             clazz = clazz.trim();
677             try {
678                     Class<?> c = tccl.loadClass(clazz);
679                     if (disableValidationAsserts() == false) {
680                         // enable validation assertions
681                         tccl.setClassAssertionStatus(clazz, true);
682                     }
683                     if (ValidatorHandler.class.isAssignableFrom(c)) {
684                         handlers.add((ValidatorHandler) c.newInstance());
685                     }
686             } catch (ClassNotFoundException e) {
687                 String msg = String.format("Missing document validation handler: '%s'.", clazz);
688                 logger.warn(msg);
689                 logger.trace(msg, e);
690             }
691         }
692         valHandlers = handlers;
693         return valHandlers;
694     }
695     
696     /**
697      * If one doesn't already exist, use the default properties filename to load a set of properties that
698      * will be used to create an HTTP client to a CollectionSpace instance.
699      */
700     @Override
701     public AuthorityClient getClient() throws Exception {
702         AuthorityClient result = authorityClient;
703
704         if (authorityClient == null) {
705                 result = authorityClient = getClient(CollectionSpaceClient.DEFAULT_CLIENT_PROPERTIES_FILENAME);
706         }
707         
708         return result;
709     }
710     
711     /*
712      * Use the properties filename passed in to load the URL and credentials that will be used
713      * to create a new HTTP client.
714      * 
715      * Never uses or resets the this.authorityClient member.  Always creates a new HTTP client using
716      * the loaded properties.
717      * 
718      * (non-Javadoc)
719      * @see org.collectionspace.services.common.context.ServiceContext#getClient(java.lang.String)
720      */
721         @Override
722     public AuthorityClient getClient(String clientPropertiesFilename) throws Exception {
723         AuthorityClient result = null;
724         
725         Properties inProperties = Tools.loadProperties(clientPropertiesFilename, true);
726         result = getClient(inProperties);
727         
728         return result;
729     }
730     
731     public AuthorityClient getClient(Properties inProperties) throws Exception {
732         AuthorityClient result = null;
733         
734         String authorityClientClazz = getServiceBinding().getClientHandler();
735         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
736         authorityClientClazz = authorityClientClazz.trim();
737         try {
738             Class<?> c = tccl.loadClass(authorityClientClazz);
739             if (AuthorityClient.class.isAssignableFrom(c)) {
740                 result = authorityClient = ((AuthorityClient) c.newInstance());
741                 result.setClientProperties(inProperties);
742             } else {
743                 logger.error(String.format("The service binding clientHandler class '%s' for '%s' service was not of type AuthorityClient.",
744                                 authorityClientClazz, this.getServiceName()));
745             }
746         } catch (ClassNotFoundException e) {
747                 String msg = String.format("Missing document validation handler: '%s'.", authorityClientClazz);
748                 logger.warn(msg);
749                 logger.trace(msg, e);
750         }
751         
752         return result;
753     }
754     
755     @Override
756     public AuthorityClient getClient(RemoteClientConfig remoteClientConfig) throws Exception {
757         AuthorityClient result = null;
758         
759         Properties properties = new Properties();
760         properties.setProperty(AuthorityClient.URL_PROPERTY, remoteClientConfig.getUrl());
761         properties.setProperty(AuthorityClient.USER_PROPERTY, remoteClientConfig.getUser());
762         properties.setProperty(AuthorityClient.PASSWORD_PROPERTY, remoteClientConfig.getPassword());
763         properties.setProperty(AuthorityClient.SSL_PROPERTY, remoteClientConfig.getSsl());
764         properties.setProperty(AuthorityClient.AUTH_PROPERTY, remoteClientConfig.getAuth());
765         //
766         // Optional values
767         String tenantId = remoteClientConfig.getTenantId();
768         if (tenantId != null) {
769                 properties.setProperty(AuthorityClient.TENANT_ID_PROPERTY, tenantId);
770         }
771         String tenantName = remoteClientConfig.getTenantName();
772         if (tenantName != null) {
773                 properties.setProperty(AuthorityClient.TENANT_NAME_PROPERTY, tenantName);
774         }
775         
776         result = getClient(properties);
777         
778         return result;
779     }
780     
781     @Override
782     public void addValidatorHandler(ValidatorHandler<IT, OT> validator) throws Exception {
783         if (valHandlers == null) {
784             valHandlers = new ArrayList<ValidatorHandler<IT, OT>>();
785         }
786         valHandlers.add(validator);
787     }
788
789     /* (non-Javadoc)
790      * @see java.lang.Object#toString()
791      */
792     @Override
793     public String toString() {
794         StringBuilder msg = new StringBuilder();
795         msg.append("AbstractServiceContext [");
796         msg.append("service name=" + serviceBinding.getName() + " ");
797         msg.append("service version=" + serviceBinding.getVersion() + " ");
798         msg.append("tenant id=" + tenantBinding.getId() + " ");
799         msg.append("tenant name=" + tenantBinding.getName() + " ");
800         msg.append(tenantBinding.getDisplayName() + " ");
801         if (repositoryDomain != null) {
802             msg.append("tenant repository domain=" + repositoryDomain.getName());
803         }
804         for (Map.Entry<String, Object> entry : properties.entrySet()) {
805             msg.append("property name=" + entry.getKey() + " value=" + entry.getValue().toString());
806         }
807         msg.append("]");
808         return msg.toString();
809     }
810
811     /* (non-Javadoc)
812      * @see org.collectionspace.services.common.context.ServiceContext#getQueryParams()
813      * 
814      * When we first created these services, the RESTEasy query parameters used to be a modifiable map.  That changed in a
815      * more recent version of RESTEasy, so we need to make a copy of the params into a modifiable map and return it instead.
816      */
817     @Override
818     public MultivaluedMap<String, String> getQueryParams() {
819
820          if (queryParams == null){
821               if (this.uriInfo != null){
822                 queryParams = this.uriInfo.getQueryParameters();
823             }
824          }
825          if (queryParams == null){
826              queryParams = new org.jboss.resteasy.specimpl.MultivaluedMapImpl<String,String>();
827         }
828         return this.queryParams;
829     }
830
831     @Override
832      public MultivaluedMap<String, String> getQueryParamsPtr() {
833            return this.queryParams;
834     }
835
836     /* (non-Javadoc)
837      * @see org.collectionspace.services.common.context.ServiceContext#setQueryParams(javax.ws.rs.core.MultivaluedMap)
838      */
839     @Override
840     public void setQueryParams(MultivaluedMap<String, String> theQueryParams) {
841         this.queryParams = theQueryParams;
842     }
843
844     @Override
845     public void setUriInfo(UriInfo ui){
846         this.uriInfo = ui;
847     }
848
849         @Override
850         public UriInfo getUriInfo() {
851                 return this.uriInfo;
852         }
853         
854         @Override
855         public Request getRequestInfo() {
856                 return this.requestInfo;
857         }
858         
859         @Override
860         public void setRequestInfo(Request requestInfo) {
861                 this.requestInfo = requestInfo;
862         }
863         
864         /*
865          * We expect the 'currentRepositorySession' member to be set only once per instance.  Also, we expect only one open repository session
866          * per HTTP request.  We'll log an error if we see more than one attempt to set a service context's current repo session.
867          * (non-Javadoc)
868          * @see org.collectionspace.services.common.context.ServiceContext#setCurrentRepositorySession(java.lang.Object)
869          */
870         @Override
871         public void setCurrentRepositorySession(Object repoSession) throws Exception {
872                 if (repoSession == null) {
873                         String errMsg = "Setting a service context's repository session to null is not allowed.";
874                         logger.error(errMsg);
875                         throw new Exception(errMsg);
876                 } else if (currentRepositorySession != null && currentRepositorySession != repoSession) {
877                         String errMsg = "The current service context's repository session was replaced.  This may cause unexpected behavior and/or data loss.";
878                         logger.error(errMsg);
879                         throw new Exception(errMsg);
880                 }
881                 
882                 currentRepositorySession = repoSession;
883                 this.currentRepoSesssionRefCount++;
884         }
885         
886         @Override
887         public void clearCurrentRepositorySession() {
888                 if (this.currentRepoSesssionRefCount > 0) {
889                         currentRepoSesssionRefCount--;
890                 }
891                 
892                 if (currentRepoSesssionRefCount == 0) {
893                         this.currentRepositorySession = null;
894                 }
895         }
896         
897         @Override
898         public Object getCurrentRepositorySession() {
899                 // TODO Auto-generated method stub
900                 return currentRepositorySession;
901         }       
902
903         @Override       
904         public RepositoryDomainType getRepositoryDomain() {
905                 return repositoryDomain;
906         }
907
908         @Override       
909         public void setRepositoryDomain(RepositoryDomainType repositoryDomain) {
910                 this.repositoryDomain = repositoryDomain;
911         }
912         
913         /**
914          * Check for a query parameter that indicates if we should force a sync even if the revision numbers indicate otherwise.
915          * @return
916          */
917         @Override
918         public boolean shouldForceSync() {
919                 boolean forceSync = false;
920                 
921                 MultivaluedMap<String, String> queryParams = getQueryParams();
922                 String paramValue = queryParams.getFirst(IClientQueryParams.FORCE_SYCN);
923                 if (paramValue != null && paramValue.equalsIgnoreCase(Boolean.TRUE.toString())) { // Find our if the caller wants us to force refname updates
924                         forceSync = true;
925                 } else if (paramValue != null && paramValue.equals(Long.toString(1))) {
926                         forceSync = true;
927                 }
928                 
929                 return forceSync;
930     }
931         
932 }