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:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
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.
24 package org.collectionspace.services.common.context;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
31 import javax.ws.rs.core.MultivaluedMap;
32 import javax.ws.rs.core.UriInfo;
34 import org.collectionspace.authentication.spi.AuthNContext;
35 import org.collectionspace.services.client.IClientQueryParams;
36 import org.collectionspace.services.client.IQueryManager;
37 import org.collectionspace.services.client.workflow.WorkflowClient;
38 import org.collectionspace.services.common.ServiceMain;
39 import org.collectionspace.services.common.authorization_mgt.AuthorizationCommon;
40 import org.collectionspace.services.common.config.PropertyItemUtils;
41 import org.collectionspace.services.common.config.ServiceConfigUtils;
42 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
43 import org.collectionspace.services.common.document.DocumentHandler;
44 import org.collectionspace.services.common.document.DocumentFilter;
45 import org.collectionspace.services.common.document.ValidatorHandler;
46 import org.collectionspace.services.common.security.SecurityContext;
47 import org.collectionspace.services.common.security.SecurityContextImpl;
48 import org.collectionspace.services.common.security.UnauthorizedException;
49 import org.collectionspace.services.config.ClientType;
50 import org.collectionspace.services.config.service.ObjectPartType;
51 import org.collectionspace.services.config.service.ServiceBindingType;
52 import org.collectionspace.services.config.tenant.RepositoryDomainType;
53 import org.collectionspace.services.config.tenant.TenantBindingType;
54 import org.collectionspace.services.config.types.PropertyItemType;
55 import org.collectionspace.services.config.types.PropertyType;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
60 * AbstractServiceContext
62 * $LastChangedRevision: $
77 public abstract class AbstractServiceContextImpl<IT, OT>
78 implements ServiceContext<IT, OT> {
81 final Logger logger = LoggerFactory.getLogger(AbstractServiceContextImpl.class);
83 /** The properties. */
84 Map<String, Object> properties = new HashMap<String, Object>();
85 /** The object part map. */
86 Map<String, ObjectPartType> objectPartMap = new HashMap<String, ObjectPartType>();
87 /** The service binding. */
88 protected ServiceBindingType serviceBinding;
89 /** The tenant binding. */
90 private TenantBindingType tenantBinding;
91 /** repository domain used by the service */
92 private RepositoryDomainType repositoryDomain;
93 /** The override document type. */
94 private String overrideDocumentType = null;
95 /** The val handlers. */
96 private List<ValidatorHandler<IT, OT>> valHandlers = null;
97 /** The doc handler. */
98 private DocumentHandler docHandler = null;
99 /** security context */
100 private SecurityContext securityContext;
101 /** The sessions JAX-RS URI information */
102 private UriInfo uriInfo;
103 /** The current repository session */
104 private Object currentRepositorySession;
105 /** A reference count for the current repository session */
106 private int currentRepoSesssionRefCount = 0;
109 * Instantiates a new abstract service context impl.
111 private AbstractServiceContextImpl() {
112 // private constructor for singleton pattern
114 // request query params
115 /** The query params. */
116 private MultivaluedMap<String, String> queryParams;
119 * Instantiates a new abstract service context impl.
121 * @param serviceName the service name
123 * @throws UnauthorizedException the unauthorized exception
125 protected AbstractServiceContextImpl(String serviceName, UriInfo uriInfo) throws UnauthorizedException {
127 //establish security context
128 securityContext = new SecurityContextImpl(uriInfo);
129 //make sure tenant context exists
130 checkTenantContext();
132 String tenantId = securityContext.getCurrentTenantId();
133 if (AuthorizationCommon.ALL_TENANTS_MANAGER_TENANT_ID.equals(tenantId) ||
134 AuthNContext.ANONYMOUS_TENANT_ID.equals(tenantId)) {
135 // Tenant Manager has no tenant binding, so don't bother...
136 tenantBinding = null;
137 serviceBinding = null;
138 repositoryDomain = null;
140 //retrieve service bindings
141 TenantBindingConfigReaderImpl tReader =
142 ServiceMain.getInstance().getTenantBindingConfigReader();
143 tenantBinding = tReader.getTenantBinding(tenantId);
144 if (tenantBinding == null) {
145 String msg = "No tenant binding found for tenantId=" + tenantId
146 + " while processing request for service= " + serviceName;
148 throw new IllegalStateException(msg);
150 serviceBinding = tReader.getServiceBinding(tenantId, serviceName);
151 if (serviceBinding == null) {
152 String msg = "No service binding found while processing request for "
153 + serviceName + " for tenant id=" + getTenantId()
154 + " name=" + getTenantName();
156 throw new IllegalStateException(msg);
158 if (logger.isDebugEnabled()) {
159 logger.debug("tenantId=" + tenantId
160 + " service binding=" + serviceBinding.getName());
162 repositoryDomain = tReader.getRepositoryDomain(tenantId, serviceName);
163 if (repositoryDomain != null) {
164 if (logger.isDebugEnabled()) {
165 logger.debug("tenantId=" + tenantId
166 + " repository doamin=" + repositoryDomain.getName());
172 public int getTimeoutParam(UriInfo ui) {
173 int result = DEFAULT_TX_TIMEOUT;
175 MultivaluedMap<String, String> queryParams = (ui == null) ? null : ui.getQueryParameters();
176 if (queryParams != null) {
177 String timeoutString = queryParams.getFirst(IClientQueryParams.IMPORT_TIMEOUT_PARAM);
178 if (timeoutString != null)
180 result = Integer.parseInt(timeoutString);
181 } catch (NumberFormatException e) {
182 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.",
191 public int getTimeoutSecs() {
192 UriInfo uriInfo = this.getUriInfo();
193 return this.getTimeoutParam(uriInfo);
197 * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel()
200 public String getCommonPartLabel() {
201 return getCommonPartLabel(getServiceName());
205 * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel(java.lang.String)
207 public String getCommonPartLabel(String schemaName) {
208 return schemaName.toLowerCase() + PART_LABEL_SEPARATOR + PART_COMMON_LABEL;
212 * @see org.collectionspace.services.common.context.ServiceContext#getPartsMetadata()
215 public Map<String, ObjectPartType> getPartsMetadata() {
216 if (objectPartMap.size() != 0) {
217 return objectPartMap;
219 ServiceBindingUtils.getPartsMetadata(getServiceBinding(), objectPartMap);
220 return objectPartMap;
224 * Gets the properties for part.
226 * @param partLabel the part label
228 * @return the properties for part
230 public List<PropertyItemType> getPropertiesForPart(String partLabel) {
231 Map<String, ObjectPartType> partMap = getPartsMetadata();
232 ObjectPartType part = partMap.get(partLabel);
234 throw new RuntimeException("No such part found: " + partLabel);
236 List<PropertyType> propNodeList = part.getProperties();
237 return propNodeList.isEmpty() ? null : propNodeList.get(0).getItem();
241 * @param partLabel The name of the scehma part to search in
242 * @param propName The name of the property (or properties) to find
243 * @param qualified Whether the returned values should be qualified with the
244 * partLabel. This is when the property values are schema field references.
245 * @return List of property values for the matched property on the named schema part.
247 public List<String> getPropertyValuesForPart(String partLabel, String propName, boolean qualified) {
248 List<PropertyItemType> allProps = getPropertiesForPart(partLabel);
249 return PropertyItemUtils.getPropertyValuesByName(allProps, propName,
250 (qualified ? (partLabel + ":") : null));
254 * @param propName The name of the property (or properties) to find
255 * @param qualified Whether the returned values should be qualified with the
256 * partLabel. This is when the property values are schema field references.
257 * @return List of property values for the matched property on any schema part.
259 public List<String> getAllPartsPropertyValues(String propName, boolean qualified) {
260 return ServiceBindingUtils.getAllPartsPropertyValues(getServiceBinding(), propName, qualified);
264 * @see org.collectionspace.services.common.context.ServiceContext#getServiceBindingPropertyValue(java.lang.String)
266 public String getServiceBindingPropertyValue(String propName) {
267 return ServiceBindingUtils.getPropertyValue(getServiceBinding(), propName);
271 * Gets the common part properties.
273 * @return the common part properties
275 public List<PropertyItemType> getCommonPartProperties() {
276 return getPropertiesForPart(getCommonPartLabel());
280 * @param propName The name of the property (or properties) to find
281 * @param qualified Whether the returned values should be qualified with the
282 * partLabel. This is when the property values are schema field references.
283 * @return List of property values for the matched property on the common schema part.
285 public List<String> getCommonPartPropertyValues(String propName, boolean qualified) {
286 return getPropertyValuesForPart(getCommonPartLabel(), propName, qualified);
290 * @see org.collectionspace.services.common.context.ServiceContext#getQualifiedServiceName()
293 public String getQualifiedServiceName() {
294 return TenantBindingConfigReaderImpl.getTenantQualifiedServiceName(getTenantId(), getServiceName());
298 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientName()
301 public String getRepositoryClientName() {
302 if (repositoryDomain == null) {
305 return repositoryDomain.getRepositoryClient();
309 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientType()
312 public ClientType getRepositoryClientType() {
313 //assumption: there is only one repository client configured
314 return ServiceMain.getInstance().getClientType();
318 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
321 public String getRepositoryDomainName() {
322 if (repositoryDomain == null) {
325 return repositoryDomain.getName();
329 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
332 public String getRepositoryDomainStorageName() {
333 if (repositoryDomain == null) {
336 return repositoryDomain.getStorageName();
340 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceId()
343 public String getRepositoryWorkspaceId() {
344 return ServiceMain.getInstance().getWorkspaceId(getTenantId(), getServiceName());
348 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceName()
351 public String getRepositoryWorkspaceName() {
352 //service name is workspace name by convention
353 return serviceBinding.getName();
357 * @see org.collectionspace.services.common.context.ServiceContext#getServiceBinding()
360 public ServiceBindingType getServiceBinding() {
361 return serviceBinding;
365 * @see org.collectionspace.services.common.context.ServiceContext#getServiceName()
368 public String getServiceName() {
369 return serviceBinding.getName();
373 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentType()
376 public String getDocumentType() {
377 // If they have not overridden the setting, use the type of the service
379 return (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
383 public String getTenantQualifiedDoctype(String docType) {
384 // If they have not overridden the setting, use the type of the service
386 String result = ServiceBindingUtils.getTenantQualifiedDocType(this.getTenantId(), docType);
392 public String getTenantQualifiedDoctype() {
393 String docType = (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
394 return getTenantQualifiedDoctype(docType);
398 * @see org.collectionspace.services.common.context.ServiceContext#setDocumentType(java.lang.String)
401 public void setDocumentType(String docType) {
402 overrideDocumentType = docType;
406 * @see org.collectionspace.services.common.context.ServiceContext#getSecurityContext()
409 public SecurityContext getSecurityContext() {
410 return securityContext;
414 * @see org.collectionspace.services.common.context.ServiceContext#getUserId()
417 public String getUserId() {
418 return securityContext.getUserId();
422 * @see org.collectionspace.services.common.context.ServiceContext#getTenantId()
425 public String getTenantId() {
426 return securityContext.getCurrentTenantId();
430 * @see org.collectionspace.services.common.context.ServiceContext#getTenantName()
433 public String getTenantName() {
434 return securityContext.getCurrentTenantName();
438 * @see org.collectionspace.services.common.context.ServiceContext#getInput()
441 public abstract IT getInput();
444 * @see org.collectionspace.services.common.context.ServiceContext#setInput(java.lang.Object)
447 public abstract void setInput(IT input);
450 * @see org.collectionspace.services.common.context.ServiceContext#getOutput()
453 public abstract OT getOutput();
456 * @see org.collectionspace.services.common.context.ServiceContext#setOutput(java.lang.Object)
459 public abstract void setOutput(OT output);
462 * @see org.collectionspace.services.common.context.ServiceContext#getProperties()
465 public Map<String, Object> getProperties() {
470 * @see org.collectionspace.services.common.context.ServiceContext#setProperties(java.util.Map)
473 public void setProperties(Map<String, Object> props) {
474 properties.putAll(props);
478 * @see org.collectionspace.services.common.context.ServiceContext#getProperty(java.lang.String)
480 public Object getProperty(String name) {
481 return properties.get(name);
485 * @see org.collectionspace.services.common.context.ServiceContext#setProperty(java.lang.String, java.lang.Object)
487 public void setProperty(String name, Object o) {
488 properties.put(name, o);
492 * checkTenantContext makss sure tenant context exists
496 * @throws UnauthorizedException the unauthorized exception
498 private void checkTenantContext() throws UnauthorizedException {
500 String tenantId = securityContext.getCurrentTenantId();
501 if (tenantId == null) {
502 String msg = "Could not find tenant context";
504 throw new UnauthorizedException(msg);
508 private static String buildWorkflowWhereClause(MultivaluedMap<String, String> queryParams) {
509 String result = null;
511 String includeDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
512 if (includeDeleted != null && includeDeleted.equalsIgnoreCase(Boolean.FALSE.toString())) {
513 result = "ecm:currentLifeCycleState <> 'deleted'";
520 * Creates the document handler instance.
522 * @return the document handler
524 * @throws Exception the exception
526 private DocumentHandler createDocumentHandlerInstance() throws Exception {
527 docHandler = ServiceConfigUtils.createDocumentHandlerInstance(tenantBinding, serviceBinding);
529 // Create a default document filter
531 docHandler.setServiceContext(this);
532 DocumentFilter docFilter = docHandler.createDocumentFilter();
534 // If the context was created with query parameters,
535 // reflect the values of those parameters in the document filter
536 // to specify sort ordering, pagination, etc.
538 if (this.getQueryParams() != null) {
539 docFilter.setSortOrder(this.getQueryParams());
540 docFilter.setPagination(this.getQueryParams());
541 String workflowWhereClause = buildWorkflowWhereClause(queryParams);
542 if (workflowWhereClause != null) {
543 docFilter.appendWhereClause(workflowWhereClause, IQueryManager.SEARCH_QUALIFIER_AND);
547 docHandler.setDocumentFilter(docFilter);
553 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHandler()
556 public DocumentHandler getDocumentHandler() throws Exception {
557 DocumentHandler result = docHandler;
558 // create a new instance if one does not yet exist
559 if (result == null) {
560 result = createDocumentHandlerInstance();
566 public void setDocumentHandler(DocumentHandler handler) throws Exception {
567 if (handler != null) {
568 docHandler = handler;
573 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHanlder(javax.ws.rs.core.MultivaluedMap)
576 public DocumentHandler getDocumentHandler(MultivaluedMap<String, String> queryParams) throws Exception {
577 DocumentHandler result = getDocumentHandler();
578 DocumentFilter documentFilter = result.getDocumentFilter(); //to see results in debugger variables view
579 documentFilter.setPagination(queryParams);
584 * If this element is set in the service binding then use it otherwise
585 * assume that asserts are NOT disabled.
587 private boolean disableValidationAsserts() {
589 Boolean disableAsserts = getServiceBinding().isDisableAsserts();
590 result = (disableAsserts != null) ? disableAsserts : false;
595 * @see org.collectionspace.services.common.context.ServiceContext#getValidatorHandlers()
598 public List<ValidatorHandler<IT, OT>> getValidatorHandlers() throws Exception {
599 if (valHandlers != null) {
602 List<String> handlerClazzes = getServiceBinding().getValidatorHandler();
603 List<ValidatorHandler<IT, OT>> handlers = new ArrayList<ValidatorHandler<IT, OT>>(handlerClazzes.size());
604 ClassLoader tccl = Thread.currentThread().getContextClassLoader();
605 for (String clazz : handlerClazzes) {
606 clazz = clazz.trim();
608 Class<?> c = tccl.loadClass(clazz);
609 if (disableValidationAsserts() == false) {
610 // enable validation assertions
611 tccl.setClassAssertionStatus(clazz, true);
613 if (ValidatorHandler.class.isAssignableFrom(c)) {
614 handlers.add((ValidatorHandler) c.newInstance());
616 } catch (ClassNotFoundException e) {
617 String msg = String.format("Missing document validation handler: '%s'.", clazz);
619 logger.trace(msg, e);
622 valHandlers = handlers;
627 public void addValidatorHandler(ValidatorHandler<IT, OT> validator) throws Exception {
628 if (valHandlers == null) {
629 valHandlers = new ArrayList<ValidatorHandler<IT, OT>>();
631 valHandlers.add(validator);
635 * @see java.lang.Object#toString()
638 public String toString() {
639 StringBuilder msg = new StringBuilder();
640 msg.append("AbstractServiceContext [");
641 msg.append("service name=" + serviceBinding.getName() + " ");
642 msg.append("service version=" + serviceBinding.getVersion() + " ");
643 msg.append("tenant id=" + tenantBinding.getId() + " ");
644 msg.append("tenant name=" + tenantBinding.getName() + " ");
645 msg.append(tenantBinding.getDisplayName() + " ");
646 if (repositoryDomain != null) {
647 msg.append("tenant repository domain=" + repositoryDomain.getName());
649 for (Map.Entry<String, Object> entry : properties.entrySet()) {
650 msg.append("property name=" + entry.getKey() + " value=" + entry.getValue().toString());
653 return msg.toString();
657 * @see org.collectionspace.services.common.context.ServiceContext#getQueryParams()
660 public MultivaluedMap<String, String> getQueryParams() {
662 if (queryParams == null){
663 if (this.uriInfo != null){
664 queryParams = this.uriInfo.getQueryParameters();
667 if (queryParams == null){
668 queryParams = new org.jboss.resteasy.specimpl.MultivaluedMapImpl<String,String>();
670 return this.queryParams;
674 public MultivaluedMap<String, String> getQueryParamsPtr() {
675 return this.queryParams;
679 * @see org.collectionspace.services.common.context.ServiceContext#setQueryParams(javax.ws.rs.core.MultivaluedMap)
682 public void setQueryParams(MultivaluedMap<String, String> theQueryParams) {
683 this.queryParams = theQueryParams;
687 public void setUriInfo(UriInfo ui){
692 public UriInfo getUriInfo() {
697 * We expect the 'currentRepositorySession' member to be set only once per instance. Also, we expect only one open repository session
698 * per HTTP request. We'll log an error if we see more than one attempt to set a service context's current repo session.
700 * @see org.collectionspace.services.common.context.ServiceContext#setCurrentRepositorySession(java.lang.Object)
703 public void setCurrentRepositorySession(Object repoSession) throws Exception {
704 if (repoSession == null) {
705 String errMsg = "Setting a service context's repository session to null is not allowed.";
706 logger.error(errMsg);
707 throw new Exception(errMsg);
708 } else if (currentRepositorySession != null && currentRepositorySession != repoSession) {
709 String errMsg = "The current service context's repository session was replaced. This may cause unexpected behavior and/or data loss.";
710 logger.error(errMsg);
711 throw new Exception(errMsg);
714 currentRepositorySession = repoSession;
715 this.currentRepoSesssionRefCount++;
719 public void clearCurrentRepositorySession() {
720 if (this.currentRepoSesssionRefCount > 0) {
721 currentRepoSesssionRefCount--;
724 if (currentRepoSesssionRefCount == 0) {
725 this.currentRepositorySession = null;
730 public Object getCurrentRepositorySession() {
731 // TODO Auto-generated method stub
732 return currentRepositorySession;
736 public RepositoryDomainType getRepositoryDomain() {
737 return repositoryDomain;
741 public void setRepositoryDomain(RepositoryDomainType repositoryDomain) {
742 this.repositoryDomain = repositoryDomain;