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.authentication.spi.AuthNContext;
34 import org.collectionspace.services.client.IQueryManager;
35 import org.collectionspace.services.client.workflow.WorkflowClient;
36 import org.collectionspace.services.common.ServiceMain;
37 import org.collectionspace.services.common.authorization_mgt.AuthorizationCommon;
38 import org.collectionspace.services.common.config.PropertyItemUtils;
39 import org.collectionspace.services.common.config.ServiceConfigUtils;
40 import org.collectionspace.services.common.config.TenantBindingConfigReaderImpl;
41 import org.collectionspace.services.common.document.DocumentHandler;
42 import org.collectionspace.services.common.document.DocumentFilter;
43 import org.collectionspace.services.common.document.ValidatorHandler;
44 import org.collectionspace.services.common.security.SecurityContext;
45 import org.collectionspace.services.common.security.SecurityContextImpl;
46 import org.collectionspace.services.common.security.UnauthorizedException;
47 import org.collectionspace.services.config.ClientType;
48 import org.collectionspace.services.config.service.ObjectPartType;
49 import org.collectionspace.services.config.service.ServiceBindingType;
50 import org.collectionspace.services.config.tenant.RepositoryDomainType;
51 import org.collectionspace.services.config.tenant.TenantBindingType;
52 import org.collectionspace.services.config.types.PropertyItemType;
53 import org.collectionspace.services.config.types.PropertyType;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
58 * AbstractServiceContext
60 * $LastChangedRevision: $
75 public abstract class AbstractServiceContextImpl<IT, OT>
76 implements ServiceContext<IT, OT> {
79 final Logger logger = LoggerFactory.getLogger(AbstractServiceContextImpl.class);
80 /** The properties. */
81 Map<String, Object> properties = new HashMap<String, Object>();
82 /** The object part map. */
83 Map<String, ObjectPartType> objectPartMap = new HashMap<String, ObjectPartType>();
84 /** The service binding. */
85 protected ServiceBindingType serviceBinding;
86 /** The tenant binding. */
87 private TenantBindingType tenantBinding;
88 /** repository domain used by the service */
89 private RepositoryDomainType repositoryDomain;
90 /** The override document type. */
91 private String overrideDocumentType = null;
92 /** The val handlers. */
93 private List<ValidatorHandler<IT, OT>> valHandlers = null;
94 /** The doc handler. */
95 private DocumentHandler docHandler = null;
96 /** security context */
97 private SecurityContext securityContext;
98 /** The sessions JAX-RS URI information */
99 private UriInfo uriInfo;
100 /** The current repository session */
101 private Object currentRepositorySession;
102 /** A reference count for the current repository session */
103 private int currentRepoSesssionRefCount = 0;
106 * Instantiates a new abstract service context impl.
108 private AbstractServiceContextImpl() {
109 // private constructor for singleton pattern
111 // request query params
112 /** The query params. */
113 private MultivaluedMap<String, String> queryParams;
116 * Instantiates a new abstract service context impl.
118 * @param serviceName the service name
120 * @throws UnauthorizedException the unauthorized exception
122 protected AbstractServiceContextImpl(String serviceName, UriInfo uriInfo) throws UnauthorizedException {
124 //establish security context
125 securityContext = new SecurityContextImpl(uriInfo);
126 //make sure tenant context exists
127 checkTenantContext();
129 String tenantId = securityContext.getCurrentTenantId();
130 if (AuthorizationCommon.ALL_TENANTS_MANAGER_TENANT_ID.equals(tenantId) ||
131 AuthNContext.ANONYMOUS_TENANT_ID.equals(tenantId)) {
132 // Tenant Manager has no tenant binding, so don't bother...
133 tenantBinding = null;
134 serviceBinding = null;
135 repositoryDomain = null;
137 //retrieve service bindings
138 TenantBindingConfigReaderImpl tReader =
139 ServiceMain.getInstance().getTenantBindingConfigReader();
140 tenantBinding = tReader.getTenantBinding(tenantId);
141 if (tenantBinding == null) {
142 String msg = "No tenant binding found for tenantId=" + tenantId
143 + " while processing request for service= " + serviceName;
145 throw new IllegalStateException(msg);
147 serviceBinding = tReader.getServiceBinding(tenantId, serviceName);
148 if (serviceBinding == null) {
149 String msg = "No service binding found while processing request for "
150 + serviceName + " for tenant id=" + getTenantId()
151 + " name=" + getTenantName();
153 throw new IllegalStateException(msg);
155 if (logger.isDebugEnabled()) {
156 logger.debug("tenantId=" + tenantId
157 + " service binding=" + serviceBinding.getName());
159 repositoryDomain = tReader.getRepositoryDomain(tenantId, serviceName);
160 if (repositoryDomain != null) {
161 if (logger.isDebugEnabled()) {
162 logger.debug("tenantId=" + tenantId
163 + " repository doamin=" + repositoryDomain.getName());
170 * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel()
173 public String getCommonPartLabel() {
174 return getCommonPartLabel(getServiceName());
178 * @see org.collectionspace.services.common.context.ServiceContext#getCommonPartLabel(java.lang.String)
180 public String getCommonPartLabel(String schemaName) {
181 return schemaName.toLowerCase() + PART_LABEL_SEPARATOR + PART_COMMON_LABEL;
185 * @see org.collectionspace.services.common.context.ServiceContext#getPartsMetadata()
188 public Map<String, ObjectPartType> getPartsMetadata() {
189 if (objectPartMap.size() != 0) {
190 return objectPartMap;
192 ServiceBindingUtils.getPartsMetadata(getServiceBinding(), objectPartMap);
193 return objectPartMap;
197 * Gets the properties for part.
199 * @param partLabel the part label
201 * @return the properties for part
203 public List<PropertyItemType> getPropertiesForPart(String partLabel) {
204 Map<String, ObjectPartType> partMap = getPartsMetadata();
205 ObjectPartType part = partMap.get(partLabel);
207 throw new RuntimeException("No such part found: " + partLabel);
209 List<PropertyType> propNodeList = part.getProperties();
210 return propNodeList.isEmpty() ? null : propNodeList.get(0).getItem();
214 * @param partLabel The name of the scehma part to search in
215 * @param propName The name of the property (or properties) to find
216 * @param qualified Whether the returned values should be qualified with the
217 * partLabel. This is when the property values are schema field references.
218 * @return List of property values for the matched property on the named schema part.
220 public List<String> getPropertyValuesForPart(String partLabel, String propName, boolean qualified) {
221 List<PropertyItemType> allProps = getPropertiesForPart(partLabel);
222 return PropertyItemUtils.getPropertyValuesByName(allProps, propName,
223 (qualified ? (partLabel + ":") : null));
227 * @param propName The name of the property (or properties) to find
228 * @param qualified Whether the returned values should be qualified with the
229 * partLabel. This is when the property values are schema field references.
230 * @return List of property values for the matched property on any schema part.
232 public List<String> getAllPartsPropertyValues(String propName, boolean qualified) {
233 return ServiceBindingUtils.getAllPartsPropertyValues(getServiceBinding(), propName, qualified);
237 * @see org.collectionspace.services.common.context.ServiceContext#getServiceBindingPropertyValue(java.lang.String)
239 public String getServiceBindingPropertyValue(String propName) {
240 return ServiceBindingUtils.getPropertyValue(getServiceBinding(), propName);
244 * Gets the common part properties.
246 * @return the common part properties
248 public List<PropertyItemType> getCommonPartProperties() {
249 return getPropertiesForPart(getCommonPartLabel());
253 * @param propName The name of the property (or properties) to find
254 * @param qualified Whether the returned values should be qualified with the
255 * partLabel. This is when the property values are schema field references.
256 * @return List of property values for the matched property on the common schema part.
258 public List<String> getCommonPartPropertyValues(String propName, boolean qualified) {
259 return getPropertyValuesForPart(getCommonPartLabel(), propName, qualified);
263 * @see org.collectionspace.services.common.context.ServiceContext#getQualifiedServiceName()
266 public String getQualifiedServiceName() {
267 return TenantBindingConfigReaderImpl.getTenantQualifiedServiceName(getTenantId(), getServiceName());
271 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientName()
274 public String getRepositoryClientName() {
275 if (repositoryDomain == null) {
278 return repositoryDomain.getRepositoryClient();
282 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryClientType()
285 public ClientType getRepositoryClientType() {
286 //assumption: there is only one repository client configured
287 return ServiceMain.getInstance().getClientType();
291 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
294 public String getRepositoryDomainName() {
295 if (repositoryDomain == null) {
298 return repositoryDomain.getName();
302 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryDomainName()
305 public String getRepositoryDomainStorageName() {
306 if (repositoryDomain == null) {
309 return repositoryDomain.getStorageName();
313 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceId()
316 public String getRepositoryWorkspaceId() {
317 return ServiceMain.getInstance().getWorkspaceId(getTenantId(), getServiceName());
321 * @see org.collectionspace.services.common.context.ServiceContext#getRepositoryWorkspaceName()
324 public String getRepositoryWorkspaceName() {
325 //service name is workspace name by convention
326 return serviceBinding.getName();
330 * @see org.collectionspace.services.common.context.ServiceContext#getServiceBinding()
333 public ServiceBindingType getServiceBinding() {
334 return serviceBinding;
338 * @see org.collectionspace.services.common.context.ServiceContext#getServiceName()
341 public String getServiceName() {
342 return serviceBinding.getName();
346 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentType()
349 public String getDocumentType() {
350 // If they have not overridden the setting, use the type of the service
352 return (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
356 public String getTenantQualifiedDoctype(String docType) {
357 // If they have not overridden the setting, use the type of the service
359 String result = ServiceBindingUtils.getTenantQualifiedDocType(this.getTenantId(), docType);
365 public String getTenantQualifiedDoctype() {
366 String docType = (overrideDocumentType != null) ? overrideDocumentType : serviceBinding.getObject().getName();
367 return getTenantQualifiedDoctype(docType);
371 * @see org.collectionspace.services.common.context.ServiceContext#setDocumentType(java.lang.String)
374 public void setDocumentType(String docType) {
375 overrideDocumentType = docType;
379 * @see org.collectionspace.services.common.context.ServiceContext#getSecurityContext()
382 public SecurityContext getSecurityContext() {
383 return securityContext;
387 * @see org.collectionspace.services.common.context.ServiceContext#getUserId()
390 public String getUserId() {
391 return securityContext.getUserId();
395 * @see org.collectionspace.services.common.context.ServiceContext#getTenantId()
398 public String getTenantId() {
399 return securityContext.getCurrentTenantId();
403 * @see org.collectionspace.services.common.context.ServiceContext#getTenantName()
406 public String getTenantName() {
407 return securityContext.getCurrentTenantName();
411 * @see org.collectionspace.services.common.context.ServiceContext#getInput()
414 public abstract IT getInput();
417 * @see org.collectionspace.services.common.context.ServiceContext#setInput(java.lang.Object)
420 public abstract void setInput(IT input);
423 * @see org.collectionspace.services.common.context.ServiceContext#getOutput()
426 public abstract OT getOutput();
429 * @see org.collectionspace.services.common.context.ServiceContext#setOutput(java.lang.Object)
432 public abstract void setOutput(OT output);
435 * @see org.collectionspace.services.common.context.ServiceContext#getProperties()
438 public Map<String, Object> getProperties() {
443 * @see org.collectionspace.services.common.context.ServiceContext#setProperties(java.util.Map)
446 public void setProperties(Map<String, Object> props) {
447 properties.putAll(props);
451 * @see org.collectionspace.services.common.context.ServiceContext#getProperty(java.lang.String)
453 public Object getProperty(String name) {
454 return properties.get(name);
458 * @see org.collectionspace.services.common.context.ServiceContext#setProperty(java.lang.String, java.lang.Object)
460 public void setProperty(String name, Object o) {
461 properties.put(name, o);
465 * checkTenantContext makss sure tenant context exists
469 * @throws UnauthorizedException the unauthorized exception
471 private void checkTenantContext() throws UnauthorizedException {
473 String tenantId = securityContext.getCurrentTenantId();
474 if (tenantId == null) {
475 String msg = "Could not find tenant context";
477 throw new UnauthorizedException(msg);
481 private static String buildWorkflowWhereClause(MultivaluedMap<String, String> queryParams) {
482 String result = null;
484 String includeDeleted = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
485 if (includeDeleted != null && includeDeleted.equalsIgnoreCase(Boolean.FALSE.toString())) {
486 result = "ecm:currentLifeCycleState <> 'deleted'";
493 * Creates the document handler instance.
495 * @return the document handler
497 * @throws Exception the exception
499 private DocumentHandler createDocumentHandlerInstance() throws Exception {
500 docHandler = ServiceConfigUtils.createDocumentHandlerInstance(tenantBinding, serviceBinding);
502 // Create a default document filter
504 docHandler.setServiceContext(this);
505 DocumentFilter docFilter = docHandler.createDocumentFilter();
507 // If the context was created with query parameters,
508 // reflect the values of those parameters in the document filter
509 // to specify sort ordering, pagination, etc.
511 if (this.getQueryParams() != null) {
512 docFilter.setSortOrder(this.getQueryParams());
513 docFilter.setPagination(this.getQueryParams());
514 String workflowWhereClause = buildWorkflowWhereClause(queryParams);
515 if (workflowWhereClause != null) {
516 docFilter.appendWhereClause(workflowWhereClause, IQueryManager.SEARCH_QUALIFIER_AND);
520 docHandler.setDocumentFilter(docFilter);
526 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHandler()
529 public DocumentHandler getDocumentHandler() throws Exception {
530 DocumentHandler result = docHandler;
531 // create a new instance if one does not yet exist
532 if (result == null) {
533 result = createDocumentHandlerInstance();
539 public void setDocumentHandler(DocumentHandler handler) throws Exception {
540 if (handler != null) {
541 docHandler = handler;
546 * @see org.collectionspace.services.common.context.ServiceContext#getDocumentHanlder(javax.ws.rs.core.MultivaluedMap)
549 public DocumentHandler getDocumentHandler(MultivaluedMap<String, String> queryParams) throws Exception {
550 DocumentHandler result = getDocumentHandler();
551 DocumentFilter documentFilter = result.getDocumentFilter(); //to see results in debugger variables view
552 documentFilter.setPagination(queryParams);
557 * If this element is set in the service binding then use it otherwise
558 * assume that asserts are NOT disabled.
560 private boolean disableValidationAsserts() {
562 Boolean disableAsserts = getServiceBinding().isDisableAsserts();
563 result = (disableAsserts != null) ? disableAsserts : false;
568 * @see org.collectionspace.services.common.context.ServiceContext#getValidatorHandlers()
571 public List<ValidatorHandler<IT, OT>> getValidatorHandlers() throws Exception {
572 if (valHandlers != null) {
575 List<String> handlerClazzes = getServiceBinding().getValidatorHandler();
576 List<ValidatorHandler<IT, OT>> handlers = new ArrayList<ValidatorHandler<IT, OT>>(handlerClazzes.size());
577 ClassLoader tccl = Thread.currentThread().getContextClassLoader();
578 for (String clazz : handlerClazzes) {
579 clazz = clazz.trim();
580 Class<?> c = tccl.loadClass(clazz);
581 if (disableValidationAsserts() == false) {
582 // enable validation assertions
583 tccl.setClassAssertionStatus(clazz, true);
585 if (ValidatorHandler.class.isAssignableFrom(c)) {
586 handlers.add((ValidatorHandler) c.newInstance());
589 valHandlers = handlers;
594 public void addValidatorHandler(ValidatorHandler<IT, OT> validator) throws Exception {
595 if (valHandlers == null) {
596 valHandlers = new ArrayList<ValidatorHandler<IT, OT>>();
598 valHandlers.add(validator);
602 * @see java.lang.Object#toString()
605 public String toString() {
606 StringBuilder msg = new StringBuilder();
607 msg.append("AbstractServiceContext [");
608 msg.append("service name=" + serviceBinding.getName() + " ");
609 msg.append("service version=" + serviceBinding.getVersion() + " ");
610 msg.append("tenant id=" + tenantBinding.getId() + " ");
611 msg.append("tenant name=" + tenantBinding.getName() + " ");
612 msg.append(tenantBinding.getDisplayName() + " ");
613 if (repositoryDomain != null) {
614 msg.append("tenant repository domain=" + repositoryDomain.getName());
616 for (Map.Entry<String, Object> entry : properties.entrySet()) {
617 msg.append("property name=" + entry.getKey() + " value=" + entry.getValue().toString());
620 return msg.toString();
624 * @see org.collectionspace.services.common.context.ServiceContext#getQueryParams()
627 public MultivaluedMap<String, String> getQueryParams() {
629 if (queryParams == null){
630 if (this.uriInfo != null){
631 queryParams = this.uriInfo.getQueryParameters();
634 if (queryParams == null){
635 queryParams = new org.jboss.resteasy.specimpl.MultivaluedMapImpl<String,String>();
637 return this.queryParams;
641 public MultivaluedMap<String, String> getQueryParamsPtr() {
642 return this.queryParams;
646 * @see org.collectionspace.services.common.context.ServiceContext#setQueryParams(javax.ws.rs.core.MultivaluedMap)
649 public void setQueryParams(MultivaluedMap<String, String> theQueryParams) {
650 this.queryParams = theQueryParams;
654 public void setUriInfo(UriInfo ui){
659 public UriInfo getUriInfo() {
664 * We expect the 'currentRepositorySession' member to be set only once per instance. Also, we expect only one open repository session
665 * per HTTP request. We'll log an error if we see more than one attempt to set a service context's current repo session.
667 * @see org.collectionspace.services.common.context.ServiceContext#setCurrentRepositorySession(java.lang.Object)
670 public void setCurrentRepositorySession(Object repoSession) throws Exception {
671 if (repoSession == null) {
672 String errMsg = "Setting a service context's repository session to null is not allowed.";
673 logger.error(errMsg);
674 throw new Exception(errMsg);
675 } else if (currentRepositorySession != null && currentRepositorySession != repoSession) {
676 String errMsg = "The current service context's repository session was replaced. This may cause unexpected behavior and/or data loss.";
677 logger.error(errMsg);
678 throw new Exception(errMsg);
681 currentRepositorySession = repoSession;
682 this.currentRepoSesssionRefCount++;
686 public void clearCurrentRepositorySession() {
687 if (this.currentRepoSesssionRefCount > 0) {
688 currentRepoSesssionRefCount--;
691 if (currentRepoSesssionRefCount == 0) {
692 this.currentRepositorySession = null;
697 public Object getCurrentRepositorySession() {
698 // TODO Auto-generated method stub
699 return currentRepositorySession;
703 public RepositoryDomainType getRepositoryDomain() {
704 return repositoryDomain;
708 public void setRepositoryDomain(RepositoryDomainType repositoryDomain) {
709 this.repositoryDomain = repositoryDomain;