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