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