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