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