]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
9679a717754cc066af310527b48a8577332dd53d
[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     /* (non-Javadoc)
224      * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel()
225      */
226     @Override
227     public String getCommonPartLabel() {
228         return getCommonPartLabel(getServiceName());
229     }
230
231     /* (non-Javadoc)
232      * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel(java.lang.String)
233      */
234     public String getCommonPartLabel(String schemaName) {
235         return schemaName.toLowerCase() + PART_LABEL_SEPARATOR + PART_COMMON_LABEL;
236     }
237
238     /* (non-Javadoc)
239      * @see org.collectionspace.services.common.context.ServiceContext#getPartsMetadata()
240      */
241     @Override
242     public Map<String, ObjectPartType> getPartsMetadata() {
243         if (objectPartMap.size() != 0) {
244             return objectPartMap;
245         }
246         ServiceBindingUtils.getPartsMetadata(getServiceBinding(), objectPartMap);
247         return objectPartMap;
248     }
249
250     /**
251      * Gets the properties for part.
252      * 
253      * @param partLabel the part label
254      * 
255      * @return the properties for part
256      */
257     public List<PropertyItemType> getPropertiesForPart(String partLabel) {
258         Map<String, ObjectPartType> partMap = getPartsMetadata();
259         ObjectPartType part = partMap.get(partLabel);
260         if (part == null) {
261             throw new RuntimeException("No such part found: " + partLabel);
262         }
263         List<PropertyType> propNodeList = part.getProperties();
264         return propNodeList.isEmpty() ? null : propNodeList.get(0).getItem();
265     }
266
267     /**
268      * @param partLabel The name of the scehma part to search in
269      * @param propName The name of the property (or properties) to find
270      * @param qualified Whether the returned values should be qualified with the
271      *          partLabel. This is when the property values are schema field references.
272      * @return List of property values for the matched property on the named schema part.
273      */
274     public List<String> getPropertyValuesForPart(String partLabel, String propName, boolean qualified) {
275         List<PropertyItemType> allProps = getPropertiesForPart(partLabel);
276         return PropertyItemUtils.getPropertyValuesByName(allProps, propName,
277                 (qualified ? (partLabel + ":") : null));
278     }
279
280     /**
281      * @param propName The name of the property (or properties) to find
282      * @param qualified Whether the returned values should be qualified with the
283      *          partLabel. This is when the property values are schema field references.
284      * @return List of property values for the matched property on any schema part.
285      */
286     public List<String> getAllPartsPropertyValues(String propName, boolean qualified) {
287         return ServiceBindingUtils.getAllPartsPropertyValues(getServiceBinding(), propName, qualified);
288     }
289
290     /* (non-Javadoc)
291      * @see org.collectionspace.services.common.context.ServiceContext#getServiceBindingPropertyValue(java.lang.String)
292      */
293     public String getServiceBindingPropertyValue(String propName) {
294         return ServiceBindingUtils.getPropertyValue(getServiceBinding(), propName);
295     }
296
297     /**
298      * Gets the common part properties.
299      * 
300      * @return the common part properties
301      */
302     public List<PropertyItemType> getCommonPartProperties() {
303         return getPropertiesForPart(getCommonPartLabel());
304     }
305
306     /**
307      * @param propName The name of the property (or properties) to find
308      * @param qualified Whether the returned values should be qualified with the
309      *          partLabel. This is when the property values are schema field references.
310      * @return List of property values for the matched property on the common schema part.
311      */
312     public List<String> getCommonPartPropertyValues(String propName, boolean qualified) {
313         return getPropertyValuesForPart(getCommonPartLabel(), propName, qualified);
314     }
315
316     /* (non-Javadoc)
317      * @see org.collectionspace.services.common.context.ServiceContext#getQualifiedServiceName()
318      */
319     @Override
320     public String getQualifiedServiceName() {
321         return TenantBindingConfigReaderImpl.getTenantQualifiedServiceName(getTenantId(), getServiceName());
322     }
323
324     /* (non-Javadoc)
325      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientName()
326      */
327     @Override
328     public String getRepositoryClientName() {
329         if (repositoryDomain == null) {
330             return null;
331         }
332         return repositoryDomain.getRepositoryClient();
333     }
334
335     /* (non-Javadoc)
336      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientType()
337      */
338     @Override
339     public ClientType getRepositoryClientType() {
340         //assumption: there is only one repository client configured
341         return ServiceMain.getInstance().getClientType();
342     }
343
344     /* (non-Javadoc)
345      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
346      */
347     @Override
348     public String getRepositoryDomainName() {
349         if (repositoryDomain == null) {
350             return null;
351         }
352         return repositoryDomain.getName();
353     }
354
355     /* (non-Javadoc)
356      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
357      */
358     @Override
359     public String getRepositoryDomainStorageName() {
360         if (repositoryDomain == null) {
361             return null;
362         }
363         return repositoryDomain.getStorageName();
364     }
365
366     /* (non-Javadoc)
367      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceId()
368      */
369     @Override
370     public String getRepositoryWorkspaceId() {
371         return ServiceMain.getInstance().getWorkspaceId(getTenantId(), getServiceName());
372     }
373
374     /* (non-Javadoc)
375      * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceName()
376      */
377     @Override
378     public String getRepositoryWorkspaceName() {
379         //service name is workspace name by convention
380         return serviceBinding.getName();
381     }
382
383     /* (non-Javadoc)
384      * @see org.collectionspace.services.common.context.ServiceContext#getServiceBinding()
385      */
386     @Override
387     public ServiceBindingType getServiceBinding() {
388         return serviceBinding;
389     }
390
391     /* (non-Javadoc)
392      * @see org.collectionspace.services.common.context.ServiceContext#getServiceName()
393      */
394     @Override
395     public String getServiceName() {
396         return serviceBinding.getName();
397     }
398
399     /* (non-Javadoc)
400      * @see org.collectionspace.services.common.context.ServiceContext#getDocumentType()
401      */
402     @Override
403     public String getDocumentType() {
404         // If they have not overridden the setting, use the type of the service
405         // object.
406         return (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
407     }
408     
409     @Override
410     public String getTenantQualifiedDoctype(String docType) {
411         // If they have not overridden the setting, use the type of the service
412         // object.
413         String result = ServiceBindingUtils.getTenantQualifiedDocType(this.getTenantId(), docType);
414         
415         return result;
416     }
417     
418     @Override
419     public String getTenantQualifiedDoctype() {
420         String docType = (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
421         return getTenantQualifiedDoctype(docType);
422     }
423
424     /* (non-Javadoc)
425      * @see org.collectionspace.services.common.context.ServiceContext#setDocumentType(java.lang.String)
426      */
427     @Override
428     public void setDocumentType(String docType) {
429         overrideDocumentType = docType;
430     }
431
432     /* (non-Javadoc)
433      * @see org.collectionspace.services.common.context.ServiceContext#getSecurityContext()
434      */
435     @Override
436     public SecurityContext getSecurityContext() {
437         return securityContext;
438     }
439
440     /* (non-Javadoc)
441      * @see org.collectionspace.services.common.context.ServiceContext#getUserId()
442      */
443     @Override
444     public String getUserId() {
445         return securityContext.getUserId();
446     }
447
448     /* (non-Javadoc)
449      * @see org.collectionspace.services.common.context.ServiceContext#getTenantId()
450      */
451     @Override
452     public String getTenantId() {
453         return securityContext.getCurrentTenantId();
454     }
455
456     /* (non-Javadoc)
457      * @see org.collectionspace.services.common.context.ServiceContext#getTenantName()
458      */
459     @Override
460     public String getTenantName() {
461         return securityContext.getCurrentTenantName();
462     }
463
464     /* (non-Javadoc)
465      * @see org.collectionspace.services.common.context.ServiceContext#getInput()
466      */
467     @Override
468     public abstract IT getInput();
469
470     /* (non-Javadoc)
471      * @see org.collectionspace.services.common.context.ServiceContext#setInput(java.lang.Object)
472      */
473     @Override
474     public abstract void setInput(IT input);
475
476     /* (non-Javadoc)
477      * @see org.collectionspace.services.common.context.ServiceContext#getOutput()
478      */
479     @Override
480     public abstract OT getOutput();
481
482     /* (non-Javadoc)
483      * @see org.collectionspace.services.common.context.ServiceContext#setOutput(java.lang.Object)
484      */
485     @Override
486     public abstract void setOutput(OT output);
487
488     /* (non-Javadoc)
489      * @see org.collectionspace.services.common.context.ServiceContext#getProperties()
490      */
491     @Override
492     public Map<String, Object> getProperties() {
493         return properties;
494     }
495
496     /* (non-Javadoc)
497      * @see org.collectionspace.services.common.context.ServiceContext#setProperties(java.util.Map)
498      */
499     @Override
500     public void setProperties(Map<String, Object> props) {
501         properties.putAll(props);
502     }
503
504     /* (non-Javadoc)
505      * @see org.collectionspace.services.common.context.ServiceContext#getProperty(java.lang.String)
506      */
507     public Object getProperty(String name) {
508         return properties.get(name);
509     }
510
511     /* (non-Javadoc)
512      * @see org.collectionspace.services.common.context.ServiceContext#setProperty(java.lang.String, java.lang.Object)
513      */
514     public void setProperty(String name, Object o) {
515         properties.put(name, o);
516     }
517
518     /**
519      * checkTenantContext makss sure tenant context exists
520      *
521      * @return the string
522      *
523      * @throws UnauthorizedException the unauthorized exception
524      */
525     private void checkTenantContext() throws UnauthorizedException {
526
527         String tenantId = securityContext.getCurrentTenantId();
528         if (tenantId == null) {
529             String msg = "Could not find tenant context";
530             logger.error(msg);
531             throw new UnauthorizedException(msg);
532         }
533     }
534
535     /**
536      * Helps to filter for queries that either want to include or exclude documents in deleted workflow states.
537      * 
538      * @param queryParams
539      * @return
540      */
541     private static String buildWorkflowWhereClause(MultivaluedMap<String, String> queryParams) {
542         String result = null;
543         
544         String includeDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
545         String includeOnlyDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED);
546
547         if (includeDeleted != null && includeDeleted.equalsIgnoreCase(Boolean.FALSE.toString())) {      
548                 result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
549                                 WorkflowClient.WORKFLOWSTATE_DELETED, WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED, WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED);
550         } else if (includeOnlyDeleted != null && includeOnlyDeleted.equalsIgnoreCase(Boolean.TRUE.toString())) {
551                 result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
552                                 WorkflowClient.WORKFLOWSTATE_PROJECT, WorkflowClient.WORKFLOWSTATE_LOCKED, WorkflowClient.WORKFLOWSTATE_REPLICATED);
553         }
554         
555         return result;
556     }
557     
558     /**
559      * Creates the document handler instance.
560      * 
561      * @return the document handler
562      * 
563      * @throws Exception the exception
564      */
565     private DocumentHandler createDocumentHandlerInstance() throws Exception {
566         docHandler = ServiceConfigUtils.createDocumentHandlerInstance(tenantBinding, serviceBinding);
567         //
568         // Create a default document filter
569         //
570         docHandler.setServiceContext(this);
571         DocumentFilter docFilter = docHandler.createDocumentFilter();
572         //
573         // If the context was created with query parameters,
574         // reflect the values of those parameters in the document filter
575         // to specify sort ordering, pagination, etc.
576         //
577         MultivaluedMap<String, String> queryParameters = this.getQueryParams();
578         if (queryParameters != null) {
579           docFilter.setSortOrder(queryParameters);
580           docFilter.setPagination(queryParameters);
581           String workflowWhereClause = buildWorkflowWhereClause(queryParameters);
582           if (workflowWhereClause != null) {
583                   docFilter.appendWhereClause(workflowWhereClause, IQueryManager.SEARCH_QUALIFIER_AND);                 
584           }            
585
586         }
587         docHandler.setDocumentFilter(docFilter);
588
589         return docHandler;
590     }
591
592     /* (non-Javadoc)
593      * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHandler()
594      */
595     @Override
596     public DocumentHandler getDocumentHandler() throws Exception {
597         DocumentHandler result = docHandler;
598         // create a new instance if one does not yet exist
599         if (result == null) {
600             result = createDocumentHandlerInstance();
601         }
602         return result;
603     }
604
605     @Override
606     public void setDocumentHandler(DocumentHandler handler) throws Exception {
607         if (handler != null) {
608                 docHandler = handler;
609         }
610     }
611
612     /* (non-Javadoc)
613      * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHanlder(javax.ws.rs.core.MultivaluedMap)
614      */
615     @Override
616     public DocumentHandler getDocumentHandler(MultivaluedMap<String, String> queryParams) throws Exception {
617         DocumentHandler result = getDocumentHandler();
618         DocumentFilter documentFilter = result.getDocumentFilter(); //to see results in debugger variables view
619         documentFilter.setPagination(queryParams);
620         return result;
621     }
622     
623     /*
624      * If this element is set in the service binding then use it otherwise
625      * assume that asserts are NOT disabled.
626      */
627     private boolean disableValidationAsserts() {
628         boolean result;
629         Boolean disableAsserts = getServiceBinding().isDisableAsserts();
630         result = (disableAsserts != null) ? disableAsserts : false;
631         return result;
632     }
633     
634     /* (non-Javadoc)
635      * @see org.collectionspace.services.common.context.ServiceContext#getValidatorHandlers()
636      */
637     @Override
638     public List<ValidatorHandler<IT, OT>> getValidatorHandlers() throws Exception {
639         if (valHandlers != null) {
640             return valHandlers;
641         }
642         List<String> handlerClazzes = getServiceBinding().getValidatorHandler();
643         List<ValidatorHandler<IT, OT>> handlers = new ArrayList<ValidatorHandler<IT, OT>>(handlerClazzes.size());
644         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
645         for (String clazz : handlerClazzes) {
646             clazz = clazz.trim();
647             try {
648                     Class<?> c = tccl.loadClass(clazz);
649                     if (disableValidationAsserts() == false) {
650                         // enable validation assertions
651                         tccl.setClassAssertionStatus(clazz, true);
652                     }
653                     if (ValidatorHandler.class.isAssignableFrom(c)) {
654                         handlers.add((ValidatorHandler) c.newInstance());
655                     }
656             } catch (ClassNotFoundException e) {
657                 String msg = String.format("Missing document validation handler: '%s'.", clazz);
658                 logger.warn(msg);
659                 logger.trace(msg, e);
660             }
661         }
662         valHandlers = handlers;
663         return valHandlers;
664     }
665     
666     /**
667      * If one doesn't already exist, use the default properties filename to load a set of properties that
668      * will be used to create an HTTP client to a CollectionSpace instance.
669      */
670     @Override
671     public AuthorityClient getClient() throws Exception {
672         AuthorityClient result = authorityClient;
673
674         if (authorityClient == null) {
675                 result = authorityClient = getClient(CollectionSpaceClient.DEFAULT_CLIENT_PROPERTIES_FILENAME);
676         }
677         
678         return result;
679     }
680     
681     /*
682      * Use the properties filename passed in to load the URL and credentials that will be used
683      * to create a new HTTP client.
684      * 
685      * Never uses or resets the this.authorityClient member.  Always creates a new HTTP client using
686      * the loaded properties.
687      * 
688      * (non-Javadoc)
689      * @see org.collectionspace.services.common.context.ServiceContext#getClient(java.lang.String)
690      */
691         @Override
692     public AuthorityClient getClient(String clientPropertiesFilename) throws Exception {
693         AuthorityClient result = null;
694         
695         Properties inProperties = Tools.loadProperties(clientPropertiesFilename, true);
696         result = getClient(inProperties);
697         
698         return result;
699     }
700     
701     public AuthorityClient getClient(Properties inProperties) throws Exception {
702         AuthorityClient result = null;
703         
704         String authorityClientClazz = getServiceBinding().getClientHandler();
705         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
706         authorityClientClazz = authorityClientClazz.trim();
707         try {
708             Class<?> c = tccl.loadClass(authorityClientClazz);
709             if (AuthorityClient.class.isAssignableFrom(c)) {
710                 result = authorityClient = ((AuthorityClient) c.newInstance());
711                 result.setClientProperties(inProperties);
712             } else {
713                 logger.error(String.format("The service binding clientHandler class '%s' for '%s' service was not of type AuthorityClient.",
714                                 authorityClientClazz, this.getServiceName()));
715             }
716         } catch (ClassNotFoundException e) {
717                 String msg = String.format("Missing document validation handler: '%s'.", authorityClientClazz);
718                 logger.warn(msg);
719                 logger.trace(msg, e);
720         }
721         
722         return result;
723     }
724     
725     @Override
726     public AuthorityClient getClient(RemoteClientConfig remoteClientConfig) throws Exception {
727         AuthorityClient result = null;
728         
729         Properties properties = new Properties();
730         properties.setProperty(AuthorityClient.URL_PROPERTY, remoteClientConfig.getUrl());
731         properties.setProperty(AuthorityClient.USER_PROPERTY, remoteClientConfig.getUser());
732         properties.setProperty(AuthorityClient.PASSWORD_PROPERTY, remoteClientConfig.getPassword());
733         properties.setProperty(AuthorityClient.SSL_PROPERTY, remoteClientConfig.getSsl());
734         properties.setProperty(AuthorityClient.AUTH_PROPERTY, remoteClientConfig.getAuth());
735         //
736         // Optional values
737         String tenantId = remoteClientConfig.getTenantId();
738         if (tenantId != null) {
739                 properties.setProperty(AuthorityClient.TENANT_ID_PROPERTY, tenantId);
740         }
741         String tenantName = remoteClientConfig.getTenantName();
742         if (tenantName != null) {
743                 properties.setProperty(AuthorityClient.TENANT_NAME_PROPERTY, tenantName);
744         }
745         
746         result = getClient(properties);
747         
748         return result;
749     }
750     
751     @Override
752     public void addValidatorHandler(ValidatorHandler<IT, OT> validator) throws Exception {
753         if (valHandlers == null) {
754             valHandlers = new ArrayList<ValidatorHandler<IT, OT>>();
755         }
756         valHandlers.add(validator);
757     }
758
759     /* (non-Javadoc)
760      * @see java.lang.Object#toString()
761      */
762     @Override
763     public String toString() {
764         StringBuilder msg = new StringBuilder();
765         msg.append("AbstractServiceContext [");
766         msg.append("service name=" + serviceBinding.getName() + " ");
767         msg.append("service version=" + serviceBinding.getVersion() + " ");
768         msg.append("tenant id=" + tenantBinding.getId() + " ");
769         msg.append("tenant name=" + tenantBinding.getName() + " ");
770         msg.append(tenantBinding.getDisplayName() + " ");
771         if (repositoryDomain != null) {
772             msg.append("tenant repository domain=" + repositoryDomain.getName());
773         }
774         for (Map.Entry<String, Object> entry : properties.entrySet()) {
775             msg.append("property name=" + entry.getKey() + " value=" + entry.getValue().toString());
776         }
777         msg.append("]");
778         return msg.toString();
779     }
780
781     /* (non-Javadoc)
782      * @see org.collectionspace.services.common.context.ServiceContext#getQueryParams()
783      */
784     @Override
785     public MultivaluedMap<String, String> getQueryParams() {
786
787          if (queryParams == null){
788               if (this.uriInfo != null){
789                 queryParams = this.uriInfo.getQueryParameters();
790             }
791          }
792          if (queryParams == null){
793              queryParams = new org.jboss.resteasy.specimpl.MultivaluedMapImpl<String,String>();
794         }
795         return this.queryParams;
796     }
797
798     @Override
799      public MultivaluedMap<String, String> getQueryParamsPtr() {
800            return this.queryParams;
801     }
802
803     /* (non-Javadoc)
804      * @see org.collectionspace.services.common.context.ServiceContext#setQueryParams(javax.ws.rs.core.MultivaluedMap)
805      */
806     @Override
807     public void setQueryParams(MultivaluedMap<String, String> theQueryParams) {
808         this.queryParams = theQueryParams;
809     }
810
811     @Override
812     public void setUriInfo(UriInfo ui){
813         this.uriInfo = ui;
814     }
815
816         @Override
817         public UriInfo getUriInfo() {
818                 return this.uriInfo;
819         }
820         
821         /*
822          * We expect the 'currentRepositorySession' member to be set only once per instance.  Also, we expect only one open repository session
823          * per HTTP request.  We'll log an error if we see more than one attempt to set a service context's current repo session.
824          * (non-Javadoc)
825          * @see org.collectionspace.services.common.context.ServiceContext#setCurrentRepositorySession(java.lang.Object)
826          */
827         @Override
828         public void setCurrentRepositorySession(Object repoSession) throws Exception {
829                 if (repoSession == null) {
830                         String errMsg = "Setting a service context's repository session to null is not allowed.";
831                         logger.error(errMsg);
832                         throw new Exception(errMsg);
833                 } else if (currentRepositorySession != null && currentRepositorySession != repoSession) {
834                         String errMsg = "The current service context's repository session was replaced.  This may cause unexpected behavior and/or data loss.";
835                         logger.error(errMsg);
836                         throw new Exception(errMsg);
837                 }
838                 
839                 currentRepositorySession = repoSession;
840                 this.currentRepoSesssionRefCount++;
841         }
842         
843         @Override
844         public void clearCurrentRepositorySession() {
845                 if (this.currentRepoSesssionRefCount > 0) {
846                         currentRepoSesssionRefCount--;
847                 }
848                 
849                 if (currentRepoSesssionRefCount == 0) {
850                         this.currentRepositorySession = null;
851                 }
852         }
853         
854         @Override
855         public Object getCurrentRepositorySession() {
856                 // TODO Auto-generated method stub
857                 return currentRepositorySession;
858         }       
859
860         @Override       
861         public RepositoryDomainType getRepositoryDomain() {
862                 return repositoryDomain;
863         }
864
865         @Override       
866         public void setRepositoryDomain(RepositoryDomainType repositoryDomain) {
867                 this.repositoryDomain = repositoryDomain;
868         }
869 }