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