]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
25caaab683e6049ca304753149632e25b7155ac2
[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.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.config.PropertyItemUtils;
45 import org.collectionspace.services.common.config.ServiceConfigUtils;
46 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
47 import org.collectionspace.services.common.document.DocumentFilter;
48 import org.collectionspace.services.common.document.DocumentHandler;
49 import org.collectionspace.services.common.document.ValidatorHandler;
50 import org.collectionspace.services.common.security.SecurityContext;
51 import org.collectionspace.services.common.security.SecurityContextImpl;
52 import org.collectionspace.services.common.security.UnauthorizedException;
53 import org.collectionspace.services.config.ClientType;
54 import org.collectionspace.services.config.service.ObjectPartType;
55 import org.collectionspace.services.config.service.ServiceBindingType;
56 import org.collectionspace.services.config.tenant.RemoteClientConfig;
57 import org.collectionspace.services.config.tenant.RepositoryDomainType;
58 import org.collectionspace.services.config.tenant.TenantBindingType;
59 import org.collectionspace.services.config.types.PropertyItemType;
60 import org.collectionspace.services.config.types.PropertyType;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 /**
65  * AbstractServiceContext
66  *
67  * $LastChangedRevision: $
68  * $LastChangedDate: $
69  */
70 /**
71  * @author pschmitz
72  *
73  * @param <IT>
74  * @param <OT>
75  */
76 /**
77  * @author pschmitz
78  *
79  * @param <IT>
80  * @param <OT>
81  */
82 @SuppressWarnings("rawtypes")
83 public abstract class AbstractServiceContextImpl<IT, OT>
84         implements ServiceContext<IT, OT> {
85
86     /** The logger. */
87     final Logger logger = LoggerFactory.getLogger(AbstractServiceContextImpl.class);
88
89     /** The properties. */
90     Map<String, Object> properties = new HashMap<String, Object>();
91     /** The object part map. */
92     Map<String, ObjectPartType> objectPartMap = new HashMap<String, ObjectPartType>();
93     /** The service binding. */
94     protected ServiceBindingType serviceBinding;
95     /** The tenant binding. */
96     private TenantBindingType tenantBinding;
97     /** repository domain used by the service */
98     private RepositoryDomainType repositoryDomain;
99         /** The override document type. */
100     private String overrideDocumentType = null;
101     /** The val handlers. */
102     private List<ValidatorHandler<IT, OT>> valHandlers = null;
103     /** The authority client -use for shared authority server */
104     private AuthorityClient authorityClient = null;
105     /** The doc handler. */
106     private DocumentHandler docHandler = null;
107     /** security context */
108     private SecurityContext securityContext;
109     /** The sessions JAX-RS URI information */
110     private UriInfo uriInfo;
111     /** The JAX-RS request information */
112     private Request requestInfo;
113     /** The current repository session */
114     private Object currentRepositorySession;
115     /** A reference count for the current repository session */
116     private int currentRepoSesssionRefCount = 0;
117     /** Should the current transaction be rolled back when an exception is caught */
118     private boolean rollbackOnException = true;
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      * By default, we return *all* objects/records.
570      *
571      * @param queryParams
572      * @return
573      */
574         private static String buildWorkflowWhereClause(MultivaluedMap<String, String> queryParams) {
575                 String result = null;
576
577                 String includeDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_DELETED_QP);
578                 String includeOnlyDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_ONLY_DELETED_QP);  // if set to true, it doesn't matter what the value is for 'includeDeleted'
579
580                 if (includeOnlyDeleted != null) {
581                         if (Tools.isTrue(includeOnlyDeleted)) {
582                                 //
583                                 // A value of 'true' for 'includeOnlyDeleted' means we're looking *only* for soft-deleted records/documents.
584                                 //
585                                 result = String.format("(ecm:currentLifeCycleState = '%s' OR ecm:currentLifeCycleState = '%s' OR ecm:currentLifeCycleState = '%s')",
586                                                 WorkflowClient.WORKFLOWSTATE_DELETED,
587                                                 WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED,
588                                                 WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED);
589                         }
590                 } else if (includeDeleted != null && Tools.isFalse(includeDeleted)) {
591                         //
592                         // We can only get here if the 'includeOnlyDeleted' query param is missing altogether.
593                         // Ensure we don't return soft-deleted records
594                         //
595                         result = String.format("(ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s' AND ecm:currentLifeCycleState <> '%s')",
596                                         WorkflowClient.WORKFLOWSTATE_DELETED,
597                                         WorkflowClient.WORKFLOWSTATE_LOCKED_DELETED,
598                                         WorkflowClient.WORKFLOWSTATE_REPLICATED_DELETED);
599                 }
600
601                 return result;
602         }
603
604     /**
605      * Creates the document handler instance.
606      *
607      * @return the document handler
608      *
609      * @throws Exception the exception
610      */
611     private DocumentHandler createDocumentHandlerInstance() throws Exception {
612         docHandler = ServiceConfigUtils.createDocumentHandlerInstance(tenantBinding, serviceBinding);
613
614         //
615         // The docHandler for a Service can be null, but usually is not.
616         //
617         if (docHandler != null) {
618                 //
619                 // Create a default document filter
620                 //
621                 docHandler.setServiceContext(this);
622                 DocumentFilter docFilter = docHandler.createDocumentFilter();
623                 //
624                 // If the context was created with query parameters,
625                 // reflect the values of those parameters in the document filter
626                 // to specify sort ordering, pagination, etc.
627                 //
628                 MultivaluedMap<String, String> queryParameters = this.getQueryParams();
629                 if (queryParameters != null) {
630                   docFilter.setSortOrder(queryParameters);
631                   docFilter.setPagination(queryParameters);
632                   String workflowWhereClause = buildWorkflowWhereClause(queryParameters);
633                   if (workflowWhereClause != null) {
634                           docFilter.appendWhereClause(workflowWhereClause, IQueryManager.SEARCH_QUALIFIER_AND);
635                   }
636
637                 }
638                 docHandler.setDocumentFilter(docFilter);
639         }
640
641         return docHandler;
642     }
643
644     /* (non-Javadoc)
645      * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHandler()
646      */
647     @Override
648     public DocumentHandler getDocumentHandler() throws Exception {
649         DocumentHandler result = docHandler;
650         // create a new instance if one does not yet exist
651         if (result == null) {
652             result = createDocumentHandlerInstance();
653         }
654         return result;
655     }
656
657     @Override
658     public void setDocumentHandler(DocumentHandler handler) throws Exception {
659         if (handler != null) {
660                 docHandler = handler;
661         }
662     }
663
664     /* (non-Javadoc)
665      * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHanlder(javax.ws.rs.core.MultivaluedMap)
666      */
667     @Override
668     public DocumentHandler getDocumentHandler(MultivaluedMap<String, String> queryParams) throws Exception {
669         DocumentHandler result = getDocumentHandler();
670         DocumentFilter documentFilter = result.getDocumentFilter(); //to see results in debugger variables view
671         documentFilter.setPagination(queryParams);
672         return result;
673     }
674
675     /*
676      * If this element is set in the service binding then use it otherwise
677      * assume that asserts are NOT disabled.
678      */
679     private boolean disableValidationAsserts() {
680         boolean result;
681         Boolean disableAsserts = getServiceBinding().isDisableAsserts();
682         result = (disableAsserts != null) ? disableAsserts : false;
683         return result;
684     }
685
686     /* (non-Javadoc)
687      * @see org.collectionspace.services.common.context.ServiceContext#getValidatorHandlers()
688      */
689     @Override
690     public List<ValidatorHandler<IT, OT>> getValidatorHandlers() throws Exception {
691         if (valHandlers != null) {
692             return valHandlers;
693         }
694         List<String> handlerClazzes = getServiceBinding().getValidatorHandler();
695         List<ValidatorHandler<IT, OT>> handlers = new ArrayList<ValidatorHandler<IT, OT>>(handlerClazzes.size());
696         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
697         for (String clazz : handlerClazzes) {
698             clazz = clazz.trim();
699             try {
700                     Class<?> c = tccl.loadClass(clazz);
701                     if (disableValidationAsserts() == false) {
702                         // enable validation assertions
703                         tccl.setClassAssertionStatus(clazz, true);
704                     }
705                     if (ValidatorHandler.class.isAssignableFrom(c)) {
706                         handlers.add((ValidatorHandler) c.newInstance());
707                     }
708             } catch (ClassNotFoundException e) {
709                 String msg = String.format("Missing document validation handler: '%s'.", clazz);
710                 logger.warn(msg);
711                 logger.trace(msg, e);
712             }
713         }
714         valHandlers = handlers;
715         return valHandlers;
716     }
717
718     /**
719      * If one doesn't already exist, use the default properties filename to load a set of properties that
720      * will be used to create an HTTP client to a CollectionSpace instance.
721      */
722     @Override
723     public AuthorityClient getClient() throws Exception {
724         AuthorityClient result = authorityClient;
725
726         if (authorityClient == null) {
727                 result = authorityClient = getClient(CollectionSpaceClient.DEFAULT_CLIENT_PROPERTIES_FILENAME);
728         }
729
730         return result;
731     }
732
733     /*
734      * Use the properties filename passed in to load the URL and credentials that will be used
735      * to create a new HTTP client.
736      *
737      * Never uses or resets the this.authorityClient member.  Always creates a new HTTP client using
738      * the loaded properties.
739      *
740      * (non-Javadoc)
741      * @see org.collectionspace.services.common.context.ServiceContext#getClient(java.lang.String)
742      */
743         @Override
744     public AuthorityClient getClient(String clientPropertiesFilename) throws Exception {
745         AuthorityClient result = null;
746
747         Properties inProperties = Tools.loadProperties(clientPropertiesFilename, true);
748         result = getClient(inProperties);
749
750         return result;
751     }
752
753     public AuthorityClient getClient(Properties inProperties) throws Exception {
754         AuthorityClient result = null;
755
756         String authorityClientClazz = getServiceBinding().getClientHandler();
757         ClassLoader tccl = Thread.currentThread().getContextClassLoader();
758         authorityClientClazz = authorityClientClazz.trim();
759         try {
760             Class<?> c = tccl.loadClass(authorityClientClazz);
761             if (AuthorityClient.class.isAssignableFrom(c)) {
762                 result = authorityClient = ((AuthorityClient) c.newInstance());
763                 result.setClientProperties(inProperties);
764             } else {
765                 logger.error(String.format("The service binding clientHandler class '%s' for '%s' service was not of type AuthorityClient.",
766                                 authorityClientClazz, this.getServiceName()));
767             }
768         } catch (ClassNotFoundException e) {
769                 String msg = String.format("Missing document validation handler: '%s'.", authorityClientClazz);
770                 logger.warn(msg);
771                 logger.trace(msg, e);
772         }
773
774         return result;
775     }
776
777     @Override
778     public AuthorityClient getClient(RemoteClientConfig remoteClientConfig) throws Exception {
779         AuthorityClient result = null;
780
781         Properties properties = new Properties();
782         properties.setProperty(AuthorityClient.URL_PROPERTY, remoteClientConfig.getUrl());
783         properties.setProperty(AuthorityClient.USER_PROPERTY, remoteClientConfig.getUser());
784         properties.setProperty(AuthorityClient.PASSWORD_PROPERTY, remoteClientConfig.getPassword());
785         properties.setProperty(AuthorityClient.SSL_PROPERTY, remoteClientConfig.getSsl());
786         properties.setProperty(AuthorityClient.AUTH_PROPERTY, remoteClientConfig.getAuth());
787         //
788         // Optional values
789         String tenantId = remoteClientConfig.getTenantId();
790         if (tenantId != null) {
791                 properties.setProperty(AuthorityClient.TENANT_ID_PROPERTY, tenantId);
792         }
793         String tenantName = remoteClientConfig.getTenantName();
794         if (tenantName != null) {
795                 properties.setProperty(AuthorityClient.TENANT_NAME_PROPERTY, tenantName);
796         }
797
798         result = getClient(properties);
799
800         return result;
801     }
802
803     @Override
804     public void addValidatorHandler(ValidatorHandler<IT, OT> validator) throws Exception {
805         if (valHandlers == null) {
806             valHandlers = new ArrayList<ValidatorHandler<IT, OT>>();
807         }
808         valHandlers.add(validator);
809     }
810
811     /* (non-Javadoc)
812      * @see java.lang.Object#toString()
813      */
814     @Override
815     public String toString() {
816         StringBuilder msg = new StringBuilder();
817         msg.append("AbstractServiceContext [");
818         msg.append("service name=" + serviceBinding.getName() + " ");
819         msg.append("service version=" + serviceBinding.getVersion() + " ");
820         msg.append("tenant id=" + tenantBinding.getId() + " ");
821         msg.append("tenant name=" + tenantBinding.getName() + " ");
822         msg.append(tenantBinding.getDisplayName() + " ");
823         if (repositoryDomain != null) {
824             msg.append("tenant repository domain=" + repositoryDomain.getName());
825         }
826         for (Map.Entry<String, Object> entry : properties.entrySet()) {
827             msg.append("property name=" + entry.getKey() + " value=" + entry.getValue().toString());
828         }
829         msg.append("]");
830         return msg.toString();
831     }
832
833     /* (non-Javadoc)
834      * @see org.collectionspace.services.common.context.ServiceContext#getQueryParams()
835      *
836      * When we first created these services, the RESTEasy query parameters used to be a modifiable map.  That changed in a
837      * more recent version of RESTEasy, so we need to make a copy of the params into a modifiable map and return it instead.
838      */
839     @Override
840     public MultivaluedMap<String, String> getQueryParams() {
841
842          if (queryParams == null){
843               if (this.uriInfo != null){
844                 queryParams = this.uriInfo.getQueryParameters();
845             }
846          }
847          if (queryParams == null){
848              queryParams = new org.jboss.resteasy.specimpl.MultivaluedMapImpl<String,String>();
849         }
850         return this.queryParams;
851     }
852
853     @Override
854      public MultivaluedMap<String, String> getQueryParamsPtr() {
855            return this.queryParams;
856     }
857
858     /* (non-Javadoc)
859      * @see org.collectionspace.services.common.context.ServiceContext#setQueryParams(javax.ws.rs.core.MultivaluedMap)
860      */
861     @Override
862     public void setQueryParams(MultivaluedMap<String, String> theQueryParams) {
863         this.queryParams = theQueryParams;
864     }
865
866     @Override
867     public void setUriInfo(UriInfo ui){
868         this.uriInfo = ui;
869     }
870
871         @Override
872         public UriInfo getUriInfo() {
873                 return this.uriInfo;
874         }
875
876         @Override
877         public Request getRequestInfo() {
878                 return this.requestInfo;
879         }
880
881         @Override
882         public void setRequestInfo(Request requestInfo) {
883                 this.requestInfo = requestInfo;
884         }
885
886         /*
887          * We expect the 'currentRepositorySession' member to be set only once per instance.  Also, we expect only one open repository session
888          * per HTTP request.  We'll log an error if we see more than one attempt to set a service context's current repo session.
889          * (non-Javadoc)
890          * @see org.collectionspace.services.common.context.ServiceContext#setCurrentRepositorySession(java.lang.Object)
891          */
892         @Override
893         public void setCurrentRepositorySession(Object repoSession) throws Exception {
894                 if (repoSession == null) {
895                         String errMsg = "Setting a service context's repository session to null is not allowed.";
896                         logger.error(errMsg);
897                         throw new Exception(errMsg);
898                 } else if (currentRepositorySession != null && currentRepositorySession != repoSession) {
899                         String errMsg = "The current service context's repository session was replaced.  This may cause unexpected behavior and/or data loss.";
900                         logger.error(errMsg);
901                         throw new Exception(errMsg);
902                 }
903
904                 currentRepositorySession = repoSession;
905                 this.currentRepoSesssionRefCount++;
906         }
907
908         @Override
909         public void clearCurrentRepositorySession() {
910                 if (this.currentRepoSesssionRefCount > 0) {
911                         currentRepoSesssionRefCount--;
912                 }
913
914                 if (currentRepoSesssionRefCount == 0) {
915                         this.currentRepositorySession = null;
916                 }
917
918                 if (currentRepoSesssionRefCount < 0) {
919                         throw new RuntimeException("Attempted to clear/close a repository session that has already been cleared/closed.");
920                 }
921         }
922
923         @Override
924         public Object getCurrentRepositorySession() {
925                 // TODO Auto-generated method stub
926                 return currentRepositorySession;
927         }
928
929         @Override
930         public RepositoryDomainType getRepositoryDomain() {
931                 return repositoryDomain;
932         }
933
934         @Override
935         public void setRepositoryDomain(RepositoryDomainType repositoryDomain) {
936                 this.repositoryDomain = repositoryDomain;
937         }
938
939         /**
940          * Check for a query parameter that indicates if we should force a sync even if the revision numbers indicate otherwise.
941          * @return
942          */
943         @Override
944         public boolean shouldForceSync() {
945                 boolean forceSync = false;
946
947                 MultivaluedMap<String, String> queryParams = getQueryParams();
948                 String paramValue = queryParams.getFirst(IClientQueryParams.FORCE_SYCN);
949                 if (paramValue != null && paramValue.equalsIgnoreCase(Boolean.TRUE.toString())) { // Find our if the caller wants us to force refname updates
950                         forceSync = true;
951                 } else if (paramValue != null && paramValue.equals(Long.toString(1))) {
952                         forceSync = true;
953                 }
954
955                 return forceSync;
956     }
957
958     @Override
959     public void setRollbackOnException(boolean rollbackOnException) {
960         this.rollbackOnException = rollbackOnException;
961     }
962
963     @Override
964     public boolean isRollbackOnException() {
965         return this.rollbackOnException;
966     }
967 }