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.nuxeo.client.java;
26 import java.util.Collection;
27 import java.util.List;
29 import javax.ws.rs.core.MultivaluedMap;
31 import org.collectionspace.services.client.IQueryManager;
32 import org.collectionspace.services.client.PoxPayloadIn;
33 import org.collectionspace.services.client.PoxPayloadOut;
34 import org.collectionspace.services.client.RelationClient;
35 import org.collectionspace.services.common.authorityref.AuthorityRefList;
36 import org.collectionspace.services.common.context.ServiceContext;
37 import org.collectionspace.services.common.datetime.GregorianCalendarDateTimeUtils;
38 import org.collectionspace.services.common.document.AbstractMultipartDocumentHandlerImpl;
39 import org.collectionspace.services.common.document.DocumentFilter;
40 import org.collectionspace.services.common.document.DocumentWrapper;
41 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
42 import org.collectionspace.services.common.profile.Profiler;
43 import org.collectionspace.services.common.repository.RepositoryClient;
44 import org.collectionspace.services.common.repository.RepositoryClientFactory;
45 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthRefConfigInfo;
46 import org.collectionspace.services.lifecycle.Lifecycle;
47 import org.collectionspace.services.lifecycle.State;
48 import org.collectionspace.services.lifecycle.StateList;
49 import org.collectionspace.services.lifecycle.TransitionDef;
50 import org.collectionspace.services.lifecycle.TransitionDefList;
51 import org.collectionspace.services.lifecycle.TransitionList;
53 import org.nuxeo.ecm.core.NXCore;
54 import org.nuxeo.ecm.core.api.ClientException;
55 import org.nuxeo.ecm.core.api.DocumentModel;
56 import org.nuxeo.ecm.core.api.DocumentModelList;
57 import org.nuxeo.ecm.core.api.model.PropertyException;
58 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
59 import org.nuxeo.ecm.core.lifecycle.LifeCycleService;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
65 * DocumentModelHandler is a base abstract Nuxeo document handler
66 * using Nuxeo Java Remote APIs for CollectionSpace services
68 * $LastChangedRevision: $
71 public abstract class DocumentModelHandler<T, TL>
72 extends AbstractMultipartDocumentHandlerImpl<T, TL, DocumentModel, DocumentModelList> {
74 private final Logger logger = LoggerFactory.getLogger(DocumentModelHandler.class);
75 private RepositoryInstance repositorySession;
76 //key=schema, value=documentpart
78 public final static String COLLECTIONSPACE_CORE_SCHEMA = "collectionspace_core";
79 public final static String COLLECTIONSPACE_CORE_TENANTID = "tenantId";
80 public final static String COLLECTIONSPACE_CORE_URI = "uri";
81 public final static String COLLECTIONSPACE_CORE_CREATED_AT = "createdAt";
82 public final static String COLLECTIONSPACE_CORE_UPDATED_AT = "updatedAt";
83 public final static String COLLECTIONSPACE_CORE_CREATED_BY = "createdBy";
84 public final static String COLLECTIONSPACE_CORE_UPDATED_BY = "updatedBy";
85 public final static String COLLECTIONSPACE_CORE_WORKFLOWSTATE = "workflowState";
88 * Map Nuxeo's life cycle object to our JAX-B based life cycle object
90 private Lifecycle createCollectionSpaceLifecycle(org.nuxeo.ecm.core.lifecycle.LifeCycle nuxeoLifecyle) {
91 Lifecycle result = null;
93 if (nuxeoLifecyle != null) {
95 // Copy the life cycle's name
96 result = new Lifecycle();
97 result.setName(nuxeoLifecyle.getName());
99 // We currently support only one initial state, so take the first one from Nuxeo
100 Collection<String> initialStateNames = nuxeoLifecyle.getInitialStateNames();
101 result.setDefaultInitial(initialStateNames.iterator().next());
103 // Next, we copy the state and corresponding transition lists
104 StateList stateList = new StateList();
105 List<State> states = stateList.getState();
106 Collection<org.nuxeo.ecm.core.lifecycle.LifeCycleState> nuxeoStates = nuxeoLifecyle.getStates();
107 for (org.nuxeo.ecm.core.lifecycle.LifeCycleState nuxeoState : nuxeoStates) {
108 State tempState = new State();
109 tempState.setDescription(nuxeoState.getDescription());
110 tempState.setInitial(nuxeoState.isInitial());
111 tempState.setName(nuxeoState.getName());
112 // Now get the list of transitions
113 TransitionList transitionList = new TransitionList();
114 List<String> transitions = transitionList.getTransition();
115 Collection<String> nuxeoTransitions = nuxeoState.getAllowedStateTransitions();
116 for (String nuxeoTransition : nuxeoTransitions) {
117 transitions.add(nuxeoTransition);
119 tempState.setTransitionList(transitionList);
120 states.add(tempState);
122 result.setStateList(stateList);
124 // Finally, we create the transition definitions
125 TransitionDefList transitionDefList = new TransitionDefList();
126 List<TransitionDef> transitionDefs = transitionDefList.getTransitionDef();
127 Collection<org.nuxeo.ecm.core.lifecycle.LifeCycleTransition> nuxeoTransitionDefs = nuxeoLifecyle.getTransitions();
128 for (org.nuxeo.ecm.core.lifecycle.LifeCycleTransition nuxeoTransitionDef : nuxeoTransitionDefs) {
129 TransitionDef tempTransitionDef = new TransitionDef();
130 tempTransitionDef.setDescription(nuxeoTransitionDef.getDescription());
131 tempTransitionDef.setDestinationState(nuxeoTransitionDef.getDestinationStateName());
132 tempTransitionDef.setName(nuxeoTransitionDef.getName());
133 transitionDefs.add(tempTransitionDef);
135 result.setTransitionDefList(transitionDefList);
142 * Returns the the life cycle definition of the related Nuxeo document type for this handler.
144 * @see org.collectionspace.services.common.document.DocumentHandler#getLifecycle()
147 public Lifecycle getLifecycle() {
148 Lifecycle result = null;
150 String docTypeName = null;
152 docTypeName = this.getServiceContext().getDocumentType();
153 result = getLifecycle(docTypeName);
154 } catch (Exception e) {
155 if (logger.isTraceEnabled() == true) {
156 logger.trace("Could not retrieve lifecycle definition for Nuxeo doctype: " + docTypeName);
164 * Returns the the life cycle definition of the related Nuxeo document type for this handler.
166 * @see org.collectionspace.services.common.document.DocumentHandler#getLifecycle(java.lang.String)
169 public Lifecycle getLifecycle(String docTypeName) {
170 org.nuxeo.ecm.core.lifecycle.LifeCycle nuxeoLifecyle;
171 Lifecycle result = null;
174 LifeCycleService lifeCycleService = null;
176 lifeCycleService = NXCore.getLifeCycleService();
177 } catch (Exception e) {
181 String lifeCycleName;
182 lifeCycleName = lifeCycleService.getLifeCycleNameFor(docTypeName);
183 nuxeoLifecyle = lifeCycleService.getLifeCycleByName(lifeCycleName);
185 result = createCollectionSpaceLifecycle(nuxeoLifecyle);
186 // result = (Lifecycle)FileTools.getJaxbObjectFromFile(Lifecycle.class, "default-lifecycle.xml");
187 } catch (Exception e) {
188 // TODO Auto-generated catch block
189 logger.error("Could not retreive life cycle information for Nuxeo doctype: " + docTypeName, e);
196 * We're using the "name" field of Nuxeo's DocumentModel to store
199 public String getCsid(DocumentModel docModel) {
200 return NuxeoUtils.getCsid(docModel);
203 public String getUri(DocumentModel docModel) {
204 return getServiceContextPath()+getCsid(docModel);
207 public RepositoryClient<PoxPayloadIn, PoxPayloadOut> getRepositoryClient(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
208 RepositoryClient<PoxPayloadIn, PoxPayloadOut> repositoryClient = RepositoryClientFactory.getInstance().getClient(ctx.getRepositoryClientName());
209 return repositoryClient;
213 * getRepositorySession returns Nuxeo Repository Session
216 public RepositoryInstance getRepositorySession() {
218 return repositorySession;
222 * setRepositorySession sets repository session
225 public void setRepositorySession(RepositoryInstance repoSession) {
226 this.repositorySession = repoSession;
230 public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
231 // TODO for sub-docs - check to see if the current service context is a multipart input,
232 // OR a docfragment, and call a variant to fill the DocModel.
233 fillAllParts(wrapDoc, Action.CREATE);
234 handleCoreValues(wrapDoc, Action.CREATE);
237 // TODO for sub-docs - Add completeCreate in which we look for set-aside doc fragments
238 // and create the subitems. We will create service contexts with the doc fragments
243 public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
244 // TODO for sub-docs - check to see if the current service context is a multipart input,
245 // OR a docfragment, and call a variant to fill the DocModel.
246 fillAllParts(wrapDoc, Action.UPDATE);
247 handleCoreValues(wrapDoc, Action.UPDATE);
251 public void handleGet(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
252 extractAllParts(wrapDoc);
256 public void handleGetAll(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception {
257 Profiler profiler = new Profiler(this, 2);
259 setCommonPartList(extractCommonPartList(wrapDoc));
264 public abstract void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception;
267 public abstract void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception;
270 public abstract T extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc) throws Exception;
273 public abstract void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception;
276 public abstract void fillCommonPart(T obj, DocumentWrapper<DocumentModel> wrapDoc) throws Exception;
279 public abstract TL extractCommonPartList(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception;
282 public abstract T getCommonPart();
285 public abstract void setCommonPart(T obj);
288 public abstract TL getCommonPartList();
291 public abstract void setCommonPartList(TL obj);
294 public DocumentFilter createDocumentFilter() {
295 DocumentFilter filter = new NuxeoDocumentFilter(this.getServiceContext());
300 * Gets the authority refs.
302 * @param docWrapper the doc wrapper
303 * @param authRefFields the auth ref fields
304 * @return the authority refs
305 * @throws PropertyException the property exception
307 abstract public AuthorityRefList getAuthorityRefs(String csid,
308 List<AuthRefConfigInfo> authRefsInfo) throws PropertyException;
310 private void handleCoreValues(DocumentWrapper<DocumentModel> docWrapper,
311 Action action) throws ClientException {
312 DocumentModel documentModel = docWrapper.getWrappedObject();
313 String now = GregorianCalendarDateTimeUtils.timestampUTC();
314 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
315 String userId = ctx.getUserId();
316 if(action==Action.CREATE) {
318 // Add the tenant ID value to the new entity
320 String tenantId = ctx.getTenantId();
321 documentModel.setProperty(COLLECTIONSPACE_CORE_SCHEMA,
322 COLLECTIONSPACE_CORE_TENANTID, tenantId);
324 // Add the uri value to the new entity
326 documentModel.setProperty(COLLECTIONSPACE_CORE_SCHEMA,
327 COLLECTIONSPACE_CORE_URI, getUri(documentModel));
329 // Add the CSID to the DublinCore title so we can see the CSID in the default
333 documentModel.setProperty("dublincore", "title",
334 documentModel.getName());
335 } catch (Exception x) {
336 if (logger.isWarnEnabled() == true) {
337 logger.warn("Could not set the Dublin Core 'title' field on document CSID:" +
338 documentModel.getName());
341 documentModel.setProperty(COLLECTIONSPACE_CORE_SCHEMA,
342 COLLECTIONSPACE_CORE_CREATED_AT, now);
343 documentModel.setProperty(COLLECTIONSPACE_CORE_SCHEMA,
344 COLLECTIONSPACE_CORE_CREATED_BY, userId);
346 if(action==Action.CREATE || action==Action.UPDATE) {
347 documentModel.setProperty(COLLECTIONSPACE_CORE_SCHEMA,
348 COLLECTIONSPACE_CORE_UPDATED_AT, now);
349 documentModel.setProperty(COLLECTIONSPACE_CORE_SCHEMA,
350 COLLECTIONSPACE_CORE_UPDATED_BY, userId);
355 * If we see the "rtSbj" query param then we need to perform a CMIS query. Currently, we have only one
356 * CMIS query, but we could add more. If we do, this method should look at the incoming request and corresponding
357 * query params to determine if we need to do a CMIS query
359 * @see org.collectionspace.services.common.document.AbstractDocumentHandlerImpl#isCMISQuery()
361 public boolean isCMISQuery() {
362 boolean result = false;
364 MultivaluedMap<String, String> queryParams = getServiceContext().getQueryParams();
366 // Look the query params to see if we need to make a CMSIS query.
368 String subjectCsid = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_TO_CSID_SUBJECT);
369 if (subjectCsid != null) {
377 * Creates the CMIS query from the service context. Each document handler is responsible for returning a valid CMIS query using the
378 * information in the current service context -which includes things like the query parameters, etc.
381 public String getCMISQuery() {
382 String result = null;
384 if (isCMISQuery() == true) {
385 MultivaluedMap<String, String> queryParams = getServiceContext().getQueryParams();
386 String subjectCsid = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_TO_CSID_SUBJECT);
387 String docType = this.getServiceContext().getDocumentType();
389 String selectFields = IQueryManager.CMIS_TARGET_NAME + ", "
390 + IQueryManager.CMIS_TARGET_TITLE + ", "
391 + RelationClient.CMIS_CSPACE_RELATIONS_TITLE + ", "
392 + RelationClient.CMIS_CSPACE_RELATIONS_SUBJECT_ID;
393 String targetTable = docType + " " + IQueryManager.CMIS_TARGET_PREFIX;
394 String relationTable = RelationClient.SERVICE_DOC_TYPE + " " + IQueryManager.CMIS_RELATIONS_PREFIX;
395 String relationObjectCsid = RelationClient.CMIS_CSPACE_RELATIONS_OBJECT_ID;
396 String relationSubjectCsid = RelationClient.CMIS_CSPACE_RELATIONS_SUBJECT_ID;
397 String targetCsid = IQueryManager.CMIS_TARGET_CSID;
399 result = "SELECT " + selectFields
400 + " FROM " + targetTable + " JOIN " + relationTable
401 + " ON " + relationObjectCsid + " = " + targetCsid
402 + " WHERE " + relationSubjectCsid + " = " + "'" + subjectCsid + "'";
404 // SELECT D.cmis:name, D.dc:title, R.dc:title, R.relations_common:subjectCsid
405 // FROM Dimension D JOIN Relation R
406 // ON R.relations_common:objectCsid = D.cmis:name
407 // WHERE R.relations_common:subjectCsid = '737527ec-a560-4776-99de'
409 if (logger.isDebugEnabled() == true && result != null) {
410 logger.debug("The CMIS query for the Movement service is: " + result);