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;
30 import javax.ws.rs.core.MultivaluedMap;
31 import javax.ws.rs.core.UriInfo;
33 import org.collectionspace.services.client.IQueryManager;
34 import org.collectionspace.services.client.workflow.WorkflowClient;
35 import org.collectionspace.services.common.ServiceMain;
36 import org.collectionspace.services.common.authorization_mgt.AuthorizationCommon;
37 import org.collectionspace.services.common.config.PropertyItemUtils;
38 import org.collectionspace.services.common.config.ServiceConfigUtils;
39 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
40 import org.collectionspace.services.common.document.DocumentHandler;
41 import org.collectionspace.services.common.document.DocumentFilter;
42 import org.collectionspace.services.common.document.ValidatorHandler;
43 import org.collectionspace.services.common.security.SecurityContext;
44 import org.collectionspace.services.common.security.SecurityContextImpl;
45 import org.collectionspace.services.common.security.UnauthorizedException;
46 import org.collectionspace.services.config.ClientType;
47 import org.collectionspace.services.config.service.ObjectPartType;
48 import org.collectionspace.services.config.service.ServiceBindingType;
49 import org.collectionspace.services.config.tenant.RepositoryDomainType;
50 import org.collectionspace.services.config.tenant.TenantBindingType;
51 import org.collectionspace.services.config.types.PropertyItemType;
52 import org.collectionspace.services.config.types.PropertyType;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
57 * AbstractServiceContext
59 * $LastChangedRevision: $
74 public abstract class AbstractServiceContextImpl<IT, OT>
75 implements ServiceContext<IT, OT> {
78 final Logger logger = LoggerFactory.getLogger(AbstractServiceContextImpl.class);
79 /** The properties. */
80 Map<String, Object> properties = new HashMap<String, Object>();
81 /** The object part map. */
82 Map<String, ObjectPartType> objectPartMap = new HashMap<String, ObjectPartType>();
83 /** The service binding. */
84 protected ServiceBindingType serviceBinding;
85 /** The tenant binding. */
86 private TenantBindingType tenantBinding;
87 /** repository domain used by the service */
88 private RepositoryDomainType repositoryDomain;
89 /** The override document type. */
90 private String overrideDocumentType = null;
91 /** The val handlers. */
92 private List<ValidatorHandler<IT, OT>> valHandlers = null;
93 /** The doc handler. */
94 private DocumentHandler docHandler = null;
95 /** security context */
96 private SecurityContext securityContext;
97 /** The sessions JAX-RS URI information */
98 private UriInfo uriInfo;
99 /** The current repository session */
100 private Object currentRepositorySession;
101 /** A reference count for the current repository session */
102 private int currentRepoSesssionRefCount = 0;
105 * Instantiates a new abstract service context impl.
107 private AbstractServiceContextImpl() {
108 // private constructor for singleton pattern
110 // request query params
111 /** The query params. */
112 private MultivaluedMap<String, String> queryParams;
115 * Instantiates a new abstract service context impl.
117 * @param serviceName the service name
119 * @throws UnauthorizedException the unauthorized exception
121 protected AbstractServiceContextImpl(String serviceName) throws UnauthorizedException {
123 //establish security context
124 securityContext = new SecurityContextImpl();
125 //make sure tenant context exists
126 checkTenantContext();
128 String tenantId = securityContext.getCurrentTenantId();
129 if(AuthorizationCommon.ALL_TENANTS_MANAGER_TENANT_ID.equals(tenantId)) {
130 // Tenant Manager has no tenant binding, so don't bother...
131 tenantBinding = null;
132 serviceBinding = null;
133 repositoryDomain = null;
135 //retrieve service bindings
136 TenantBindingConfigReaderImpl tReader =
137 ServiceMain.getInstance().getTenantBindingConfigReader();
138 tenantBinding = tReader.getTenantBinding(tenantId);
139 if (tenantBinding == null) {
140 String msg = "No tenant binding found for tenantId=" + tenantId
141 + " while processing request for service= " + serviceName;
143 throw new IllegalStateException(msg);
145 serviceBinding = tReader.getServiceBinding(tenantId, serviceName);
146 if (serviceBinding == null) {
147 String msg = "No service binding found while processing request for "
148 + serviceName + " for tenant id=" + getTenantId()
149 + " name=" + getTenantName();
151 throw new IllegalStateException(msg);
153 if (logger.isDebugEnabled()) {
154 logger.debug("tenantId=" + tenantId
155 + " service binding=" + serviceBinding.getName());
157 repositoryDomain = tReader.getRepositoryDomain(tenantId, serviceName);
158 if (repositoryDomain != null) {
159 if (logger.isDebugEnabled()) {
160 logger.debug("tenantId=" + tenantId
161 + " repository doamin=" + repositoryDomain.getName());
168 * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel()
171 public String getCommonPartLabel() {
172 return getCommonPartLabel(getServiceName());
176 * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel(java.lang.String)
178 public String getCommonPartLabel(String schemaName) {
179 return schemaName.toLowerCase() + PART_LABEL_SEPARATOR + PART_COMMON_LABEL;
183 * @see org.collectionspace.services.common.context.ServiceContext#getPartsMetadata()
186 public Map<String, ObjectPartType> getPartsMetadata() {
187 if (objectPartMap.size() != 0) {
188 return objectPartMap;
190 ServiceBindingUtils.getPartsMetadata(getServiceBinding(), objectPartMap);
191 return objectPartMap;
195 * Gets the properties for part.
197 * @param partLabel the part label
199 * @return the properties for part
201 public List<PropertyItemType> getPropertiesForPart(String partLabel) {
202 Map<String, ObjectPartType> partMap = getPartsMetadata();
203 ObjectPartType part = partMap.get(partLabel);
205 throw new RuntimeException("No such part found: " + partLabel);
207 List<PropertyType> propNodeList = part.getProperties();
208 return propNodeList.isEmpty() ? null : propNodeList.get(0).getItem();
212 * @param partLabel The name of the scehma part to search in
213 * @param propName The name of the property (or properties) to find
214 * @param qualified Whether the returned values should be qualified with the
215 * partLabel. This is when the property values are schema field references.
216 * @return List of property values for the matched property on the named schema part.
218 public List<String> getPropertyValuesForPart(String partLabel, String propName, boolean qualified) {
219 List<PropertyItemType> allProps = getPropertiesForPart(partLabel);
220 return PropertyItemUtils.getPropertyValuesByName(allProps, propName,
221 (qualified ? (partLabel + ":") : null));
225 * @param propName The name of the property (or properties) to find
226 * @param qualified Whether the returned values should be qualified with the
227 * partLabel. This is when the property values are schema field references.
228 * @return List of property values for the matched property on any schema part.
230 public List<String> getAllPartsPropertyValues(String propName, boolean qualified) {
231 return ServiceBindingUtils.getAllPartsPropertyValues(getServiceBinding(), propName, qualified);
235 * @see org.collectionspace.services.common.context.ServiceContext#getServiceBindingPropertyValue(java.lang.String)
237 public String getServiceBindingPropertyValue(String propName) {
238 return ServiceBindingUtils.getPropertyValue(getServiceBinding(), propName);
242 * Gets the common part properties.
244 * @return the common part properties
246 public List<PropertyItemType> getCommonPartProperties() {
247 return getPropertiesForPart(getCommonPartLabel());
251 * @param propName The name of the property (or properties) to find
252 * @param qualified Whether the returned values should be qualified with the
253 * partLabel. This is when the property values are schema field references.
254 * @return List of property values for the matched property on the common schema part.
256 public List<String> getCommonPartPropertyValues(String propName, boolean qualified) {
257 return getPropertyValuesForPart(getCommonPartLabel(), propName, qualified);
261 * @see org.collectionspace.services.common.context.ServiceContext#getQualifiedServiceName()
264 public String getQualifiedServiceName() {
265 return TenantBindingConfigReaderImpl.getTenantQualifiedServiceName(getTenantId(), getServiceName());
269 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientName()
272 public String getRepositoryClientName() {
273 if (repositoryDomain == null) {
276 return repositoryDomain.getRepositoryClient();
280 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientType()
283 public ClientType getRepositoryClientType() {
284 //assumption: there is only one repository client configured
285 return ServiceMain.getInstance().getClientType();
289 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
292 public String getRepositoryDomainName() {
293 if (repositoryDomain == null) {
296 return repositoryDomain.getName();
300 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
303 public String getRepositoryDomainStorageName() {
304 if (repositoryDomain == null) {
307 return repositoryDomain.getStorageName();
311 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceId()
314 public String getRepositoryWorkspaceId() {
315 return ServiceMain.getInstance().getWorkspaceId(getTenantId(), getServiceName());
319 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceName()
322 public String getRepositoryWorkspaceName() {
323 //service name is workspace name by convention
324 return serviceBinding.getName();
328 * @see org.collectionspace.services.common.context.ServiceContext#getServiceBinding()
331 public ServiceBindingType getServiceBinding() {
332 return serviceBinding;
336 * @see org.collectionspace.services.common.context.ServiceContext#getServiceName()
339 public String getServiceName() {
340 return serviceBinding.getName();
344 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentType()
347 public String getDocumentType() {
348 // If they have not overridden the setting, use the type of the service
350 return (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
354 public String getTenantQualifiedDoctype(String docType) {
355 // If they have not overridden the setting, use the type of the service
357 String result = ServiceBindingUtils.getTenantQualifiedDocType(this.getTenantId(), docType);
363 public String getTenantQualifiedDoctype() {
364 String docType = (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
365 return getTenantQualifiedDoctype(docType);
369 * @see org.collectionspace.services.common.context.ServiceContext#setDocumentType(java.lang.String)
372 public void setDocumentType(String docType) {
373 overrideDocumentType = docType;
377 * @see org.collectionspace.services.common.context.ServiceContext#getSecurityContext()
380 public SecurityContext getSecurityContext() {
381 return securityContext;
385 * @see org.collectionspace.services.common.context.ServiceContext#getUserId()
388 public String getUserId() {
389 return securityContext.getUserId();
393 * @see org.collectionspace.services.common.context.ServiceContext#getTenantId()
396 public String getTenantId() {
397 return securityContext.getCurrentTenantId();
401 * @see org.collectionspace.services.common.context.ServiceContext#getTenantName()
404 public String getTenantName() {
405 return securityContext.getCurrentTenantName();
409 * @see org.collectionspace.services.common.context.ServiceContext#getInput()
412 public abstract IT getInput();
415 * @see org.collectionspace.services.common.context.ServiceContext#setInput(java.lang.Object)
418 public abstract void setInput(IT input);
421 * @see org.collectionspace.services.common.context.ServiceContext#getOutput()
424 public abstract OT getOutput();
427 * @see org.collectionspace.services.common.context.ServiceContext#setOutput(java.lang.Object)
430 public abstract void setOutput(OT output);
433 * @see org.collectionspace.services.common.context.ServiceContext#getProperties()
436 public Map<String, Object> getProperties() {
441 * @see org.collectionspace.services.common.context.ServiceContext#setProperties(java.util.Map)
444 public void setProperties(Map<String, Object> props) {
445 properties.putAll(props);
449 * @see org.collectionspace.services.common.context.ServiceContext#getProperty(java.lang.String)
451 public Object getProperty(String name) {
452 return properties.get(name);
456 * @see org.collectionspace.services.common.context.ServiceContext#setProperty(java.lang.String, java.lang.Object)
458 public void setProperty(String name, Object o) {
459 properties.put(name, o);
463 * checkTenantContext makss sure tenant context exists
467 * @throws UnauthorizedException the unauthorized exception
469 private void checkTenantContext() throws UnauthorizedException {
471 String tenantId = securityContext.getCurrentTenantId();
472 if (tenantId == null) {
473 String msg = "Could not find tenant context";
475 throw new UnauthorizedException(msg);
479 private static String buildWorkflowWhereClause(MultivaluedMap<String, String> queryParams) {
480 String result = null;
482 String includeDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
483 if (includeDeleted != null && includeDeleted.equalsIgnoreCase(Boolean.FALSE.toString())) {
484 result = "ecm:currentLifeCycleState <> 'deleted'";
491 * Creates the document handler instance.
493 * @return the document handler
495 * @throws Exception the exception
497 private DocumentHandler createDocumentHandlerInstance() throws Exception {
498 docHandler = ServiceConfigUtils.createDocumentHandlerInstance(tenantBinding, serviceBinding);
500 // Create a default document filter
502 docHandler.setServiceContext(this);
503 DocumentFilter docFilter = docHandler.createDocumentFilter();
505 // If the context was created with query parameters,
506 // reflect the values of those parameters in the document filter
507 // to specify sort ordering, pagination, etc.
509 if (this.getQueryParams() != null) {
510 docFilter.setSortOrder(this.getQueryParams());
511 docFilter.setPagination(this.getQueryParams());
512 String workflowWhereClause = buildWorkflowWhereClause(queryParams);
513 if (workflowWhereClause != null) {
514 docFilter.appendWhereClause(workflowWhereClause, IQueryManager.SEARCH_QUALIFIER_AND);
518 docHandler.setDocumentFilter(docFilter);
524 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHandler()
527 public DocumentHandler getDocumentHandler() throws Exception {
528 DocumentHandler result = docHandler;
529 // create a new instance if one does not yet exist
530 if (result == null) {
531 result = createDocumentHandlerInstance();
537 public void setDocumentHandler(DocumentHandler handler) throws Exception {
538 if (handler != null) {
539 docHandler = handler;
544 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHanlder(javax.ws.rs.core.MultivaluedMap)
547 public DocumentHandler getDocumentHandler(MultivaluedMap<String, String> queryParams) throws Exception {
548 DocumentHandler result = getDocumentHandler();
549 DocumentFilter documentFilter = result.getDocumentFilter(); //to see results in debugger variables view
550 documentFilter.setPagination(queryParams);
555 * If this element is set in the service binding then use it otherwise
556 * assume that asserts are NOT disabled.
558 private boolean disableValidationAsserts() {
560 Boolean disableAsserts = getServiceBinding().isDisableAsserts();
561 result = (disableAsserts != null) ? disableAsserts : false;
566 * @see org.collectionspace.services.common.context.ServiceContext#getValidatorHandlers()
569 public List<ValidatorHandler<IT, OT>> getValidatorHandlers() throws Exception {
570 if (valHandlers != null) {
573 List<String> handlerClazzes = getServiceBinding().getValidatorHandler();
574 List<ValidatorHandler<IT, OT>> handlers = new ArrayList<ValidatorHandler<IT, OT>>(handlerClazzes.size());
575 ClassLoader tccl = Thread.currentThread().getContextClassLoader();
576 for (String clazz : handlerClazzes) {
577 clazz = clazz.trim();
578 Class<?> c = tccl.loadClass(clazz);
579 if (disableValidationAsserts() == false) {
580 // enable validation assertions
581 tccl.setClassAssertionStatus(clazz, true);
583 if (ValidatorHandler.class.isAssignableFrom(c)) {
584 handlers.add((ValidatorHandler) c.newInstance());
587 valHandlers = handlers;
592 public void addValidatorHandler(ValidatorHandler<IT, OT> validator) throws Exception {
593 if (valHandlers == null) {
594 valHandlers = new ArrayList<ValidatorHandler<IT, OT>>();
596 valHandlers.add(validator);
600 * @see java.lang.Object#toString()
603 public String toString() {
604 StringBuilder msg = new StringBuilder();
605 msg.append("AbstractServiceContext [");
606 msg.append("service name=" + serviceBinding.getName() + " ");
607 msg.append("service version=" + serviceBinding.getVersion() + " ");
608 msg.append("tenant id=" + tenantBinding.getId() + " ");
609 msg.append("tenant name=" + tenantBinding.getName() + " ");
610 msg.append(tenantBinding.getDisplayName() + " ");
611 if (repositoryDomain != null) {
612 msg.append("tenant repository domain=" + repositoryDomain.getName());
614 for (Map.Entry<String, Object> entry : properties.entrySet()) {
615 msg.append("property name=" + entry.getKey() + " value=" + entry.getValue().toString());
618 return msg.toString();
622 * @see org.collectionspace.services.common.context.ServiceContext#getQueryParams()
625 public MultivaluedMap<String, String> getQueryParams() {
627 if (queryParams == null){
628 if (this.uriInfo != null){
629 queryParams = this.uriInfo.getQueryParameters();
632 if (queryParams == null){
633 queryParams = new org.jboss.resteasy.specimpl.MultivaluedMapImpl<String,String>();
635 return this.queryParams;
639 public MultivaluedMap<String, String> getQueryParamsPtr() {
640 return this.queryParams;
644 * @see org.collectionspace.services.common.context.ServiceContext#setQueryParams(javax.ws.rs.core.MultivaluedMap)
647 public void setQueryParams(MultivaluedMap<String, String> theQueryParams) {
648 this.queryParams = theQueryParams;
652 public void setUriInfo(UriInfo ui){
657 public UriInfo getUriInfo() {
662 * We expect the 'currentRepositorySession' member to be set only once per instance. Also, we expect only one open repository session
663 * per HTTP request. We'll log an error if we see more than one attempt to set a service context's current repo session.
665 * @see org.collectionspace.services.common.context.ServiceContext#setCurrentRepositorySession(java.lang.Object)
668 public void setCurrentRepositorySession(Object repoSession) throws Exception {
669 if (repoSession == null) {
670 String errMsg = "Setting a service context's repository session to null is not allowed.";
671 logger.error(errMsg);
672 throw new Exception(errMsg);
673 } else if (currentRepositorySession != null && currentRepositorySession != repoSession) {
674 String errMsg = "The current service context's repository session was replaced. This may cause unexpected behavior and/or data loss.";
675 logger.error(errMsg);
676 throw new Exception(errMsg);
679 currentRepositorySession = repoSession;
680 this.currentRepoSesssionRefCount++;
684 public void clearCurrentRepositorySession() {
685 if (this.currentRepoSesssionRefCount > 0) {
686 currentRepoSesssionRefCount--;
689 if (currentRepoSesssionRefCount == 0) {
690 this.currentRepositorySession = null;
695 public Object getCurrentRepositorySession() {
696 // TODO Auto-generated method stub
697 return currentRepositorySession;
701 public RepositoryDomainType getRepositoryDomain() {
702 return repositoryDomain;
706 public void setRepositoryDomain(RepositoryDomainType repositoryDomain) {
707 this.repositoryDomain = repositoryDomain;