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