]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
5cdd32abe0a5e34a1f1b2f278bbdc421daf74fb2
[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.nuxeo.client.java;
25
26 import java.util.Collection;
27 import java.util.List;
28
29 import javax.ws.rs.core.MultivaluedMap;
30
31 import org.collectionspace.services.client.Profiler;
32 import org.collectionspace.services.client.CollectionSpaceClient;
33 import org.collectionspace.services.client.IQueryManager;
34 import org.collectionspace.services.client.IRelationsManager;
35 import org.collectionspace.services.client.PoxPayloadIn;
36 import org.collectionspace.services.client.PoxPayloadOut;
37 import org.collectionspace.services.common.api.GregorianCalendarDateTimeUtils;
38 import org.collectionspace.services.common.authorityref.AuthorityRefList;
39 import org.collectionspace.services.common.context.ServiceContext;
40 import org.collectionspace.services.common.document.AbstractMultipartDocumentHandlerImpl;
41 import org.collectionspace.services.common.document.DocumentFilter;
42 import org.collectionspace.services.common.document.DocumentWrapper;
43 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
44 import org.collectionspace.services.common.query.QueryContext;
45 import org.collectionspace.services.common.repository.RepositoryClient;
46 import org.collectionspace.services.common.repository.RepositoryClientFactory;
47 import org.collectionspace.services.common.vocabulary.RefNameServiceUtils.AuthRefConfigInfo;
48 import org.collectionspace.services.lifecycle.Lifecycle;
49 import org.collectionspace.services.lifecycle.State;
50 import org.collectionspace.services.lifecycle.StateList;
51 import org.collectionspace.services.lifecycle.TransitionDef;
52 import org.collectionspace.services.lifecycle.TransitionDefList;
53 import org.collectionspace.services.lifecycle.TransitionList;
54
55 import org.nuxeo.ecm.core.NXCore;
56 import org.nuxeo.ecm.core.api.ClientException;
57 import org.nuxeo.ecm.core.api.DocumentModel;
58 import org.nuxeo.ecm.core.api.DocumentModelList;
59 import org.nuxeo.ecm.core.api.model.PropertyException;
60 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
61 import org.nuxeo.ecm.core.lifecycle.LifeCycleService;
62
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66 /**
67  * DocumentModelHandler is a base abstract Nuxeo document handler
68  * using Nuxeo Java Remote APIs for CollectionSpace services
69  *
70  * $LastChangedRevision: $
71  * $LastChangedDate: $
72  */
73 public abstract class DocumentModelHandler<T, TL>
74         extends AbstractMultipartDocumentHandlerImpl<T, TL, DocumentModel, DocumentModelList> {
75
76     private final Logger logger = LoggerFactory.getLogger(DocumentModelHandler.class);
77     private RepositoryInstance repositorySession;
78
79     /*
80      * Map Nuxeo's life cycle object to our JAX-B based life cycle object
81      */
82     private Lifecycle createCollectionSpaceLifecycle(org.nuxeo.ecm.core.lifecycle.LifeCycle nuxeoLifecyle) {
83         Lifecycle result = null;
84         
85         if (nuxeoLifecyle != null) {
86                 //
87                 // Copy the life cycle's name
88                 result = new Lifecycle();
89                 result.setName(nuxeoLifecyle.getName());
90                 
91                 // We currently support only one initial state, so take the first one from Nuxeo
92                 Collection<String> initialStateNames = nuxeoLifecyle.getInitialStateNames();
93                 result.setDefaultInitial(initialStateNames.iterator().next());
94                 
95                 // Next, we copy the state and corresponding transition lists
96                 StateList stateList = new StateList();
97                 List<State> states = stateList.getState();
98                 Collection<org.nuxeo.ecm.core.lifecycle.LifeCycleState> nuxeoStates = nuxeoLifecyle.getStates();
99                 for (org.nuxeo.ecm.core.lifecycle.LifeCycleState nuxeoState : nuxeoStates) {
100                         State tempState = new State();
101                         tempState.setDescription(nuxeoState.getDescription());
102                         tempState.setInitial(nuxeoState.isInitial());
103                         tempState.setName(nuxeoState.getName());
104                         // Now get the list of transitions
105                         TransitionList transitionList = new TransitionList();
106                         List<String> transitions = transitionList.getTransition();
107                         Collection<String> nuxeoTransitions = nuxeoState.getAllowedStateTransitions();
108                         for (String nuxeoTransition : nuxeoTransitions) {
109                                 transitions.add(nuxeoTransition);
110                         }
111                         tempState.setTransitionList(transitionList);
112                         states.add(tempState);
113                 }
114                 result.setStateList(stateList);
115                 
116                 // Finally, we create the transition definitions
117                 TransitionDefList transitionDefList = new TransitionDefList();
118                 List<TransitionDef> transitionDefs = transitionDefList.getTransitionDef();
119                 Collection<org.nuxeo.ecm.core.lifecycle.LifeCycleTransition> nuxeoTransitionDefs = nuxeoLifecyle.getTransitions();
120                 for (org.nuxeo.ecm.core.lifecycle.LifeCycleTransition nuxeoTransitionDef : nuxeoTransitionDefs) {
121                         TransitionDef tempTransitionDef = new TransitionDef();
122                         tempTransitionDef.setDescription(nuxeoTransitionDef.getDescription());
123                         tempTransitionDef.setDestinationState(nuxeoTransitionDef.getDestinationStateName());
124                         tempTransitionDef.setName(nuxeoTransitionDef.getName());
125                         transitionDefs.add(tempTransitionDef);
126                 }
127                 result.setTransitionDefList(transitionDefList);
128         }
129         
130         return result;
131     }
132     
133     /*
134      * Returns the the life cycle definition of the related Nuxeo document type for this handler.
135      * (non-Javadoc)
136      * @see org.collectionspace.services.common.document.DocumentHandler#getLifecycle()
137      */
138     @Override
139     public Lifecycle getLifecycle() {
140         Lifecycle result = null;
141         
142         String docTypeName = null;
143         try {
144                 docTypeName = this.getServiceContext().getDocumentType();
145                 result = getLifecycle(docTypeName);
146         } catch (Exception e) {
147                 if (logger.isTraceEnabled() == true) {
148                         logger.trace("Could not retrieve lifecycle definition for Nuxeo doctype: " + docTypeName);
149                 }
150         }
151         
152         return result;
153     }
154     
155     /*
156      * Returns the the life cycle definition of the related Nuxeo document type for this handler.
157      * (non-Javadoc)
158      * @see org.collectionspace.services.common.document.DocumentHandler#getLifecycle(java.lang.String)
159      */
160     @Override
161     public Lifecycle getLifecycle(String docTypeName) {
162         org.nuxeo.ecm.core.lifecycle.LifeCycle nuxeoLifecyle;
163         Lifecycle result = null;
164         
165         try {
166                 LifeCycleService lifeCycleService = null;
167                         try {
168                                 lifeCycleService = NXCore.getLifeCycleService();
169                         } catch (Exception e) {
170                                 e.printStackTrace();
171                         }
172                         
173                 String lifeCycleName; 
174                 lifeCycleName = lifeCycleService.getLifeCycleNameFor(docTypeName);
175                 nuxeoLifecyle = lifeCycleService.getLifeCycleByName(lifeCycleName);
176                 
177                 result = createCollectionSpaceLifecycle(nuxeoLifecyle); 
178 //                      result = (Lifecycle)FileTools.getJaxbObjectFromFile(Lifecycle.class, "default-lifecycle.xml");
179                 } catch (Exception e) {
180                         // TODO Auto-generated catch block
181                         logger.error("Could not retreive life cycle information for Nuxeo doctype: " + docTypeName, e);
182                 }
183         
184         return result;
185     }
186     
187     /*
188      * We're using the "name" field of Nuxeo's DocumentModel to store
189      * the CSID.
190      */
191     public String getCsid(DocumentModel docModel) {
192         return NuxeoUtils.getCsid(docModel);
193     }
194
195     public String getUri(DocumentModel docModel) {
196         return getServiceContextPath()+getCsid(docModel);
197     }
198         
199     public RepositoryClient<PoxPayloadIn, PoxPayloadOut> getRepositoryClient(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx) {
200         RepositoryClient<PoxPayloadIn, PoxPayloadOut> repositoryClient = RepositoryClientFactory.getInstance().getClient(ctx.getRepositoryClientName());
201         return repositoryClient;
202     }
203
204     /**
205      * getRepositorySession returns Nuxeo Repository Session
206      * @return
207      */
208     public RepositoryInstance getRepositorySession() {
209         
210         return repositorySession;
211     }
212
213     /**
214      * setRepositorySession sets repository session
215      * @param repoSession
216      */
217     public void setRepositorySession(RepositoryInstance repoSession) {
218         this.repositorySession = repoSession;
219     }
220
221     @Override
222     public void handleCreate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
223         // TODO for sub-docs - check to see if the current service context is a multipart input, 
224         // OR a docfragment, and call a variant to fill the DocModel.
225         fillAllParts(wrapDoc, Action.CREATE);
226         handleCoreValues(wrapDoc, Action.CREATE);
227     }
228     
229     // TODO for sub-docs - Add completeCreate in which we look for set-aside doc fragments 
230     // and create the subitems. We will create service contexts with the doc fragments
231     // and then call 
232
233
234     @Override
235     public void handleUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
236         // TODO for sub-docs - check to see if the current service context is a multipart input, 
237         // OR a docfragment, and call a variant to fill the DocModel.
238         fillAllParts(wrapDoc, Action.UPDATE);
239         handleCoreValues(wrapDoc, Action.UPDATE);
240     }
241
242     @Override
243     public void handleGet(DocumentWrapper<DocumentModel> wrapDoc) throws Exception {
244         extractAllParts(wrapDoc);
245     }
246
247     @Override
248     public void handleGetAll(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception {
249         Profiler profiler = new Profiler(this, 2);
250         profiler.start();
251         setCommonPartList(extractCommonPartList(wrapDoc));
252         profiler.stop();
253     }
254
255     @Override
256     public abstract void completeUpdate(DocumentWrapper<DocumentModel> wrapDoc) throws Exception;
257
258     @Override
259     public abstract void extractAllParts(DocumentWrapper<DocumentModel> wrapDoc) throws Exception;
260
261     @Override
262     public abstract T extractCommonPart(DocumentWrapper<DocumentModel> wrapDoc) throws Exception;
263
264     @Override
265     public abstract void fillAllParts(DocumentWrapper<DocumentModel> wrapDoc, Action action) throws Exception;
266
267     @Override
268     public abstract void fillCommonPart(T obj, DocumentWrapper<DocumentModel> wrapDoc) throws Exception;
269
270     @Override
271     public abstract TL extractCommonPartList(DocumentWrapper<DocumentModelList> wrapDoc) throws Exception;
272
273     @Override
274     public abstract T getCommonPart();
275
276     @Override
277     public abstract void setCommonPart(T obj);
278
279     @Override
280     public abstract TL getCommonPartList();
281
282     @Override
283     public abstract void setCommonPartList(TL obj);
284     
285     @Override
286     public DocumentFilter createDocumentFilter() {
287         DocumentFilter filter = new NuxeoDocumentFilter(this.getServiceContext());
288         return filter;
289     }
290     
291     /**
292      * Gets the authority refs.
293      *
294      * @param docWrapper the doc wrapper
295      * @param authRefFields the auth ref fields
296      * @return the authority refs
297      * @throws PropertyException the property exception
298      */
299     abstract public AuthorityRefList getAuthorityRefs(String csid,
300                 List<AuthRefConfigInfo> authRefsInfo) throws PropertyException;    
301
302     private void handleCoreValues(DocumentWrapper<DocumentModel> docWrapper, 
303                 Action action)  throws ClientException {
304         DocumentModel documentModel = docWrapper.getWrappedObject();
305         String now = GregorianCalendarDateTimeUtils.timestampUTC();
306         ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx = getServiceContext();
307         String userId = ctx.getUserId();
308         if(action==Action.CREATE) {
309             //
310             // Add the tenant ID value to the new entity
311             //
312                 String tenantId = ctx.getTenantId();
313             documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
314                         CollectionSpaceClient.COLLECTIONSPACE_CORE_TENANTID, tenantId);
315             //
316             // Add the uri value to the new entity
317             //
318             documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
319                         CollectionSpaceClient.COLLECTIONSPACE_CORE_URI, getUri(documentModel));
320                 //
321                 // Add the CSID to the DublinCore title so we can see the CSID in the default
322                 // Nuxeo webapp.
323                 //
324                 try {
325                 documentModel.setProperty("dublincore", "title",
326                         documentModel.getName());
327                 } catch (Exception x) {
328                         if (logger.isWarnEnabled() == true) {
329                                 logger.warn("Could not set the Dublin Core 'title' field on document CSID:" +
330                                                 documentModel.getName());
331                         }
332                 }
333             documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
334                         CollectionSpaceClient.COLLECTIONSPACE_CORE_CREATED_AT, now);
335             documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
336                         CollectionSpaceClient.COLLECTIONSPACE_CORE_CREATED_BY, userId);
337         }
338         
339                 if (action == Action.CREATE || action == Action.UPDATE) {
340                         documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
341                                         CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_AT, now);
342                         documentModel.setProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
343                                         CollectionSpaceClient.COLLECTIONSPACE_CORE_UPDATED_BY, userId);
344                 }
345     }
346     
347     /*
348      * If we see the "rtSbj" query param then we need to perform a CMIS query.  Currently, we have only one
349      * CMIS query, but we could add more.  If we do, this method should look at the incoming request and corresponding
350      * query params to determine if we need to do a CMIS query
351      * (non-Javadoc)
352      * @see org.collectionspace.services.common.document.AbstractDocumentHandlerImpl#isCMISQuery()
353      */
354     public boolean isCMISQuery() {
355         boolean result = false;
356         
357         MultivaluedMap<String, String> queryParams = getServiceContext().getQueryParams();
358         //
359         // Look the query params to see if we need to make a CMSIS query.
360         //
361         String asSubjectCsid = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_TO_CSID_AS_SUBJECT);           
362         String asOjectCsid = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_TO_CSID_AS_OBJECT);      
363         String asEither = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_TO_CSID_AS_EITHER);         
364         if (asSubjectCsid != null || asOjectCsid != null || asEither != null) {
365                 result = true;
366         }
367         
368         return result;
369     }
370     
371         /**
372          * Creates the CMIS query from the service context. Each document handler is
373          * responsible for returning (can override) a valid CMIS query using the information in the
374          * current service context -which includes things like the query parameters,
375          * etc.
376          * 
377          * This method implementation supports three mutually exclusive cases. We will build a query
378          * that can find a document(s) 'A' in a relationship with another document
379          * 'B' where document 'B' has a CSID equal to the query param passed in and:
380          *              1. Document 'B' is the subject of the relationship
381          *              2. Document 'B' is the object of the relationship
382          *              3. Document 'B' is either the object or the subject of the relationship
383          */
384     @Override
385     public String getCMISQuery(QueryContext queryContext) {
386         String result = null;
387         
388         if (isCMISQuery() == true) {
389                 //
390                 // Build up the query arguments
391                 //
392                 String theOnClause = "";
393                 String theWhereClause = "";
394                 MultivaluedMap<String, String> queryParams = getServiceContext().getQueryParams();
395                 String asSubjectCsid = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_TO_CSID_AS_SUBJECT);
396                 String asObjectCsid = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_TO_CSID_AS_OBJECT);
397                 String asEitherCsid = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_TO_CSID_AS_EITHER);
398                 String matchObjDocTypes = (String)queryParams.getFirst(IQueryManager.SEARCH_RELATED_MATCH_OBJ_DOCTYPES);
399                 String selectDocType = (String)queryParams.getFirst(IQueryManager.SELECT_DOC_TYPE_FIELD);
400
401                 String docType = this.getServiceContext().getDocumentType();
402                 if (selectDocType != null && !selectDocType.isEmpty()) {  
403                         docType = selectDocType;
404                 }
405                 String selectFields = IQueryManager.CMIS_TARGET_CSID + ", "
406                                 + IQueryManager.CMIS_TARGET_TITLE + ", "
407                                 + IRelationsManager.CMIS_CSPACE_RELATIONS_TITLE + ", "
408                                 + IRelationsManager.CMIS_CSPACE_RELATIONS_OBJECT_ID + ", "
409                                 + IRelationsManager.CMIS_CSPACE_RELATIONS_SUBJECT_ID;
410                 String targetTable = docType + " " + IQueryManager.CMIS_TARGET_PREFIX;
411                 String relTable = IRelationsManager.DOC_TYPE + " " + IQueryManager.CMIS_RELATIONS_PREFIX;
412                 String relObjectCsidCol = IRelationsManager.CMIS_CSPACE_RELATIONS_OBJECT_ID;
413                 String relSubjectCsidCol = IRelationsManager.CMIS_CSPACE_RELATIONS_SUBJECT_ID;
414                 String targetCsidCol = IQueryManager.CMIS_TARGET_CSID;
415
416                 //
417                 // Create the "ON" and "WHERE" query clauses based on the params passed into the HTTP request.  
418                 //
419                 // First come, first serve -the first match determines the "ON" and "WHERE" query clauses.
420                 //
421                 if (asSubjectCsid != null && !asSubjectCsid.isEmpty()) {  
422                         // Since our query param is the "subject" value, join the tables where the CSID of the document is the other side (the "object") of the relationship.
423                         theOnClause = relObjectCsidCol + " = " + targetCsidCol;
424                         theWhereClause = relSubjectCsidCol + " = " + "'" + asSubjectCsid + "'";
425                 } else if (asObjectCsid != null && !asObjectCsid.isEmpty()) {
426                         // Since our query param is the "object" value, join the tables where the CSID of the document is the other side (the "subject") of the relationship.
427                         theOnClause = relSubjectCsidCol + " = " + targetCsidCol; 
428                         theWhereClause = relObjectCsidCol + " = " + "'" + asObjectCsid + "'";
429                 } else if (asEitherCsid != null && !asEitherCsid.isEmpty()) {
430                         theOnClause = relObjectCsidCol + " = " + targetCsidCol
431                                         + " OR " + relSubjectCsidCol + " = " + targetCsidCol;
432                         theWhereClause = relSubjectCsidCol + " = " + "'" + asEitherCsid + "'"
433                                         + " OR " + relObjectCsidCol + " = " + "'" + asEitherCsid + "'";
434                 } else {
435                         //Since the call to isCMISQuery() return true, we should never get here.
436                         logger.error("Attempt to make CMIS query failed because the HTTP request was missing valid query parameters.");
437                 }
438                 
439                 // Now consider a constraint on the object doc types (for search by service group)
440                 if (matchObjDocTypes != null && !matchObjDocTypes.isEmpty()) {  
441                         // Since our query param is the "subject" value, join the tables where the CSID of the document is the other side (the "object") of the relationship.
442                         theWhereClause += " AND (" + IRelationsManager.CMIS_CSPACE_RELATIONS_OBJECT_TYPE 
443                                                                 + " IN " + matchObjDocTypes + ")";
444                 }
445                 
446                 StringBuilder query = new StringBuilder();
447                 // assemble the query from the string arguments
448                 query.append("SELECT ");
449                 query.append(selectFields);
450                 query.append(" FROM " + targetTable + " JOIN " + relTable);
451                 query.append(" ON " + theOnClause);
452                 query.append(" WHERE " + theWhereClause);
453                 
454                 try {
455                                 NuxeoUtils.appendCMISOrderBy(query, queryContext);
456                         } catch (Exception e) {
457                                 logger.error("Could not append ORDER BY clause to CMIS query", e);
458                         }
459                 
460                 // An example:
461                 // SELECT D.cmis:name, D.dc:title, R.dc:title, R.relations_common:subjectCsid
462                 // FROM Dimension D JOIN Relation R
463                 // ON R.relations_common:objectCsid = D.cmis:name
464                 // WHERE R.relations_common:subjectCsid = '737527ec-a560-4776-99de'
465                 // ORDER BY D.collectionspace_core:updatedAt DESC
466                 
467                 result = query.toString();
468                 if (logger.isDebugEnabled() == true && result != null) {
469                         logger.debug("The CMIS query is: " + result);
470                 }
471         }
472         
473         return result;
474     }
475     
476 }