]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
0972066ea0705f9e61c90d32af7ba9256400b953
[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 package org.collectionspace.services.nuxeo.client.java;
19
20 import java.util.Hashtable;
21 import java.util.List;
22 import java.util.UUID;
23
24 import javax.ws.rs.WebApplicationException;
25 import javax.ws.rs.core.MultivaluedMap;
26
27 import org.collectionspace.services.client.PoxPayloadIn;
28 import org.collectionspace.services.client.PoxPayloadOut;
29 import org.collectionspace.services.client.workflow.WorkflowClient;
30 import org.collectionspace.services.common.context.ServiceContext;
31 import org.collectionspace.services.common.query.QueryContext;
32 import org.collectionspace.services.common.repository.RepositoryClient;
33 import org.collectionspace.services.common.profile.Profiler;
34 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
35
36 import org.collectionspace.services.common.document.BadRequestException;
37 import org.collectionspace.services.common.document.DocumentException;
38 import org.collectionspace.services.common.document.DocumentFilter;
39 import org.collectionspace.services.common.document.DocumentHandler;
40 import org.collectionspace.services.common.document.DocumentNotFoundException;
41 import org.collectionspace.services.common.document.DocumentHandler.Action;
42 import org.collectionspace.services.common.document.DocumentWrapper;
43 import org.collectionspace.services.common.document.DocumentWrapperImpl;
44
45 import org.nuxeo.common.utils.IdUtils;
46 import org.nuxeo.ecm.core.api.ClientException;
47 import org.nuxeo.ecm.core.api.DocumentModel;
48 import org.nuxeo.ecm.core.api.DocumentModelList;
49 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
50 import org.nuxeo.ecm.core.api.DocumentRef;
51 import org.nuxeo.ecm.core.api.IdRef;
52 import org.nuxeo.ecm.core.api.PathRef;
53 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
54
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57
58 /**
59  * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
60  * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
61  * with the client.
62  * 
63  * $LastChangedRevision: $ $LastChangedDate: $
64  */
65 public class RepositoryJavaClientImpl implements RepositoryClient<PoxPayloadIn, PoxPayloadOut> {
66
67     /** The logger. */
68     private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
69 //    private final Logger profilerLogger = LoggerFactory.getLogger("remperf");
70 //    private String foo = Profiler.createLogger();
71
72     public static final String NUXEO_CORE_TYPE_DOMAIN = "Domain";
73     public static final String NUXEO_CORE_TYPE_WORKSPACEROOT = "WorkspaceRoot";
74     
75     /**
76      * Instantiates a new repository java client impl.
77      */
78     public RepositoryJavaClientImpl() {
79         //Empty constructor
80         
81     }
82
83     public void assertWorkflowState(ServiceContext ctx,
84                 DocumentModel docModel) throws DocumentNotFoundException, ClientException {
85         MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
86         if (queryParams != null) {
87                 //
88                 // Look for the workflow "delete" query param and see if we need to assert that the
89                 // docModel is in a non-deleted workflow state.
90                 //
91                 String currentState = docModel.getCurrentLifeCycleState();
92                 String includeDeletedStr = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
93                 boolean includeDeleted = includeDeletedStr == null ? true : Boolean.parseBoolean(includeDeletedStr);
94                 if (includeDeleted == false) {
95                         //
96                         // We don't wanted soft-deleted object, so throw an exception if this one is soft-deleted.
97                         //
98                         if (currentState.equalsIgnoreCase(WorkflowClient.WORKFLOWSTATE_DELETED)) {
99                                 String msg = "The GET assertion that docModel not be in 'deleted' workflow state failed.";
100                                 logger.debug(msg);
101                                 throw new DocumentNotFoundException(msg);
102                         }
103                 }
104         }
105     }
106     
107     /**
108      * create document in the Nuxeo repository
109      *
110      * @param ctx service context under which this method is invoked
111      * @param handler
112      *            should be used by the caller to provide and transform the
113      *            document
114      * @return id in repository of the newly created document
115      * @throws DocumentException
116      */
117     @Override
118     public String create(ServiceContext ctx,
119             DocumentHandler handler) throws BadRequestException,
120             DocumentException {
121
122         String docType = NuxeoUtils.getTenantQualifiedDocType(ctx); //ctx.getDocumentType();
123         if (docType == null) {
124             throw new IllegalArgumentException(
125                     "RepositoryJavaClient.create: docType is missing");
126         }
127         
128         if (handler == null) {
129             throw new IllegalArgumentException(
130                     "RepositoryJavaClient.create: handler is missing");
131         }
132         String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
133         if (nuxeoWspaceId == null) {
134             throw new DocumentNotFoundException(
135                     "Unable to find workspace for service " + ctx.getServiceName()
136                     + " check if the workspace exists in the Nuxeo repository");
137         }
138         
139         RepositoryInstance repoSession = null;
140         try {
141             handler.prepare(Action.CREATE);
142             repoSession = getRepositorySession();
143             DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
144             DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
145             String wspacePath = wspaceDoc.getPathAsString();
146             //give our own ID so PathRef could be constructed later on
147             String id = IdUtils.generateId(UUID.randomUUID().toString());
148             // create document model
149             DocumentModel doc = repoSession.createDocumentModel(wspacePath, id, docType);
150             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
151             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
152             handler.handle(Action.CREATE, wrapDoc);
153             // create document with documentmodel
154             doc = repoSession.createDocument(doc);
155             repoSession.save();
156 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
157 // and assume the handler has the state it needs (doc fragments). 
158             handler.complete(Action.CREATE, wrapDoc);
159             return id;
160         } catch (BadRequestException bre) {
161             throw bre;
162         } catch (Exception e) {
163                 logger.error("Caught exception ", e);
164             throw new DocumentException(e);
165         } finally {
166             if (repoSession != null) {
167                 releaseRepositorySession(repoSession);
168             }
169         }
170
171     }
172
173     /**
174      * get document from the Nuxeo repository
175      * @param ctx service context under which this method is invoked
176      * @param id
177      *            of the document to retrieve
178      * @param handler
179      *            should be used by the caller to provide and transform the
180      *            document
181      * @throws DocumentException
182      */
183     @Override
184     public void get(ServiceContext ctx, String id, DocumentHandler handler)
185             throws DocumentNotFoundException, DocumentException {
186
187         if (handler == null) {
188             throw new IllegalArgumentException(
189                     "RepositoryJavaClient.get: handler is missing");
190         }
191         
192         RepositoryInstance repoSession = null;
193         try {
194             handler.prepare(Action.GET);
195             repoSession = getRepositorySession();
196             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
197             DocumentModel docModel = null;
198             try {
199                 docModel = repoSession.getDocument(docRef);
200                 assertWorkflowState(ctx, docModel);
201             } catch (ClientException ce) {
202                 String msg = logException(ce, "Could not find document with CSID=" + id);
203                 throw new DocumentNotFoundException(msg, ce);
204             }
205             //
206             // Set repository session to handle the document
207             //
208             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
209             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(docModel);
210             handler.handle(Action.GET, wrapDoc);
211             handler.complete(Action.GET, wrapDoc);
212         } catch (IllegalArgumentException iae) {
213             throw iae;
214         } catch (DocumentException de) {
215             throw de;
216         } catch (Exception e) {
217             if (logger.isDebugEnabled()) {
218                 logger.debug("Caught exception ", e);
219             }
220             throw new DocumentException(e);
221         } finally {
222             if (repoSession != null) {
223                 releaseRepositorySession(repoSession);
224             }
225         }
226     }
227
228     /**
229      * get document from the Nuxeo repository, using the docFilter params.
230      * @param ctx service context under which this method is invoked
231      * @param handler
232      *            should be used by the caller to provide and transform the
233      *            document. Handler must have a docFilter set to return a single item.
234      * @throws DocumentException
235      */
236     @Override
237     public void get(ServiceContext ctx, DocumentHandler handler)
238             throws DocumentNotFoundException, DocumentException {
239         QueryContext queryContext = new QueryContext(ctx, handler);
240         RepositoryInstance repoSession = null;
241
242         try {
243             handler.prepare(Action.GET);
244             repoSession = getRepositorySession();
245
246             DocumentModelList docList = null;
247             // force limit to 1, and ignore totalSize
248             String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
249             docList = repoSession.query(query, null, 1, 0, false);
250             if (docList.size() != 1) {
251                 throw new DocumentNotFoundException("No document found matching filter params: " + query);
252             }
253             DocumentModel doc = docList.get(0);
254
255             if (logger.isDebugEnabled()) {
256                 logger.debug("Executed NXQL query: " + query);
257             }
258
259             //set reposession to handle the document
260             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
261             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
262             handler.handle(Action.GET, wrapDoc);
263             handler.complete(Action.GET, wrapDoc);
264         } catch (IllegalArgumentException iae) {
265             throw iae;
266         } catch (DocumentException de) {
267             throw de;
268         } catch (Exception e) {
269             if (logger.isDebugEnabled()) {
270                 logger.debug("Caught exception ", e);
271             }
272             throw new DocumentException(e);
273         } finally {
274             if (repoSession != null) {
275                 releaseRepositorySession(repoSession);
276             }
277         }
278     }
279     
280     public DocumentWrapper<DocumentModel> getDoc(
281                 RepositoryInstance repoSession,
282             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
283             String csid) throws DocumentNotFoundException, DocumentException {
284         DocumentWrapper<DocumentModel> wrapDoc = null;
285
286         try {
287             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
288             DocumentModel doc = null;
289             try {
290                 doc = repoSession.getDocument(docRef);
291             } catch (ClientException ce) {
292                 String msg = logException(ce, "Could not find document with CSID=" + csid);
293                 throw new DocumentNotFoundException(msg, ce);
294             }
295             wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
296         } catch (IllegalArgumentException iae) {
297             throw iae;
298         } catch (DocumentException de) {
299             throw de;
300         }
301
302         return wrapDoc;
303     }
304     
305     /**
306      * Get wrapped documentModel from the Nuxeo repository.  The search is restricted to the workspace
307      * of the current context.
308      * 
309      * @param ctx service context under which this method is invoked
310      * @param id
311      *            of the document to retrieve
312      * @throws DocumentException
313      */
314     @Override
315     public DocumentWrapper<DocumentModel> getDoc(
316             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
317             String csid) throws DocumentNotFoundException, DocumentException {
318         RepositoryInstance repoSession = null;
319         DocumentWrapper<DocumentModel> wrapDoc = null;
320
321         try {
322                 // Open a new repository session
323             repoSession = getRepositorySession();
324             wrapDoc = getDoc(repoSession, ctx, csid);
325         } catch (IllegalArgumentException iae) {
326             throw iae;
327         } catch (DocumentException de) {
328             throw de;
329         } catch (Exception e) {
330             if (logger.isDebugEnabled()) {
331                 logger.debug("Caught exception ", e);
332             }
333             throw new DocumentException(e);
334         } finally {
335             if (repoSession != null) {
336                 releaseRepositorySession(repoSession);
337             }
338         }
339         
340         if (logger.isWarnEnabled() == true) {
341                 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
342         }
343         return wrapDoc;
344     }
345
346     public DocumentWrapper<DocumentModel> findDoc(
347             RepositoryInstance repoSession,
348             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
349             String whereClause)
350             throws DocumentNotFoundException, DocumentException {
351         DocumentWrapper<DocumentModel> wrapDoc = null;
352
353         try {
354             QueryContext queryContext = new QueryContext(ctx, whereClause);
355             DocumentModelList docList = null;
356             // force limit to 1, and ignore totalSize
357             String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
358             docList = repoSession.query(query,
359                     null, //Filter
360                     1, //limit
361                     0, //offset
362                     false); //countTotal
363             if (docList.size() != 1) {
364                 if (logger.isDebugEnabled()) {
365                     logger.debug("findDoc: Query found: " + docList.size() + " items.");
366                     logger.debug(" Query: " + query);
367                 }
368                 throw new DocumentNotFoundException("No document found matching filter params: " + query);
369             }
370             DocumentModel doc = docList.get(0);
371             wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
372         } catch (IllegalArgumentException iae) {
373             throw iae;
374         } catch (DocumentException de) {
375             throw de;
376         } catch (Exception e) {
377             if (logger.isDebugEnabled()) {
378                 logger.debug("Caught exception ", e);
379             }
380             throw new DocumentException(e);
381         }
382         
383         return wrapDoc;
384     }
385     
386     /**
387      * find wrapped documentModel from the Nuxeo repository
388      * @param ctx service context under which this method is invoked
389      * @param whereClause where NXQL where clause to get the document
390      * @throws DocumentException
391      */
392     @Override
393     public DocumentWrapper<DocumentModel> findDoc(
394             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
395             String whereClause)
396                         throws DocumentNotFoundException, DocumentException {
397         RepositoryInstance repoSession = null;
398         DocumentWrapper<DocumentModel> wrapDoc = null;
399
400         try {
401             repoSession = getRepositorySession();
402             wrapDoc = findDoc(repoSession, ctx, whereClause);
403         } catch (Exception e) {
404                         throw new DocumentException("Unable to create a Nuxeo repository session.", e);
405                 } finally {
406             if (repoSession != null) {
407                 releaseRepositorySession(repoSession);
408             }
409         }
410         
411         if (logger.isWarnEnabled() == true) {
412                 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
413         }
414         
415         return wrapDoc;
416     }
417
418     /**
419      * find doc and return CSID from the Nuxeo repository
420      * @param ctx service context under which this method is invoked
421      * @param whereClause where NXQL where clause to get the document
422      * @throws DocumentException
423      */
424     @Override
425     public String findDocCSID(RepositoryInstance repoSession, 
426             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String whereClause)
427             throws DocumentNotFoundException, DocumentException {
428         String csid = null;
429         boolean releaseSession = false;
430         try {
431                 if(repoSession== null) {
432                         repoSession = this.getRepositorySession();
433                         releaseSession = true;
434                 }
435             DocumentWrapper<DocumentModel> wrapDoc = findDoc(repoSession, ctx, whereClause);
436             DocumentModel docModel = wrapDoc.getWrappedObject();
437             csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
438         } catch (DocumentNotFoundException dnfe) {
439             throw dnfe;
440         } catch (IllegalArgumentException iae) {
441             throw iae;
442         } catch (DocumentException de) {
443             throw de;
444         } catch (Exception e) {
445             if (logger.isDebugEnabled()) {
446                 logger.debug("Caught exception ", e);
447             }
448             throw new DocumentException(e);
449         } finally {
450                 if(releaseSession && (repoSession != null)) {
451                         this.releaseRepositorySession(repoSession);
452                 }
453         }
454         return csid;
455     }
456
457     public DocumentWrapper<DocumentModelList> findDocs(
458             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
459             RepositoryInstance repoSession,
460             List<String> docTypes,
461             String whereClause,
462             int pageSize, int pageNum, boolean computeTotal)
463                         throws DocumentNotFoundException, DocumentException {
464         DocumentWrapper<DocumentModelList> wrapDoc = null;
465
466         try {
467             if (docTypes == null || docTypes.size() < 1) {
468                 throw new DocumentNotFoundException(
469                         "The findDocs() method must specify at least one DocumentType.");
470             }
471             DocumentModelList docList = null;
472             // force limit to 1, and ignore totalSize
473             QueryContext queryContext = new QueryContext(ctx, whereClause);
474             String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
475             if (logger.isDebugEnabled()) {
476                 logger.debug("findDocs() NXQL: "+query);
477             }
478             docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
479             wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
480         } catch (IllegalArgumentException iae) {
481             throw iae;
482         } catch (Exception e) {
483             if (logger.isDebugEnabled()) {
484                 logger.debug("Caught exception ", e);
485             }
486             throw new DocumentException(e);
487         }
488                 
489         return wrapDoc;
490     }
491     
492     /**
493      * Find a list of documentModels from the Nuxeo repository
494      * @param docTypes a list of DocType names to match
495      * @param  whereClause where the clause to qualify on
496      * @return
497      */
498     @Override
499     public DocumentWrapper<DocumentModelList> findDocs(
500             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
501             List<String> docTypes,
502             String whereClause,
503             int pageSize, int pageNum, boolean computeTotal)
504             throws DocumentNotFoundException, DocumentException {
505         RepositoryInstance repoSession = null;
506         DocumentWrapper<DocumentModelList> wrapDoc = null;
507
508         try {
509             repoSession = getRepositorySession();
510             wrapDoc = findDocs(ctx, repoSession, docTypes, whereClause,
511                         pageSize, pageNum, computeTotal);
512         } catch (IllegalArgumentException iae) {
513             throw iae;
514         } catch (Exception e) {
515             if (logger.isDebugEnabled()) {
516                 logger.debug("Caught exception ", e);
517             }
518             throw new DocumentException(e);
519         } finally {
520             if (repoSession != null) {
521                 releaseRepositorySession(repoSession);
522             }
523         }
524         
525         if (logger.isWarnEnabled() == true) {
526                 logger.warn("Returned DocumentModelList instance was created with a repository session that is now closed.");
527         }
528         
529         return wrapDoc;
530     }
531
532     /* (non-Javadoc)
533      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
534      */
535     @Override
536     public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
537             throws DocumentNotFoundException, DocumentException {
538         if (handler == null) {
539             throw new IllegalArgumentException(
540                     "RepositoryJavaClient.getAll: handler is missing");
541         }
542
543         RepositoryInstance repoSession = null;
544         try {
545             handler.prepare(Action.GET_ALL);
546             repoSession = getRepositorySession();
547             DocumentModelList docModelList = new DocumentModelListImpl();
548             //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
549             for (String csid : csidList) {
550                 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
551                 DocumentModel docModel = repoSession.getDocument(docRef);
552                 docModelList.add(docModel);
553             }
554
555             //set reposession to handle the document
556             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
557             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
558             handler.handle(Action.GET_ALL, wrapDoc);
559             handler.complete(Action.GET_ALL, wrapDoc);
560         } catch (DocumentException de) {
561             throw de;
562         } catch (Exception e) {
563             if (logger.isDebugEnabled()) {
564                 logger.debug("Caught exception ", e);
565             }
566             throw new DocumentException(e);
567         } finally {
568             if (repoSession != null) {
569                 releaseRepositorySession(repoSession);
570             }
571         }
572     }
573
574     /**
575      * getAll get all documents for an entity entity service from the Nuxeo
576      * repository
577      *
578      * @param ctx service context under which this method is invoked
579      * @param handler
580      *            should be used by the caller to provide and transform the
581      *            document
582      * @throws DocumentException
583      */
584     @Override
585     public void getAll(ServiceContext ctx, DocumentHandler handler)
586             throws DocumentNotFoundException, DocumentException {
587         if (handler == null) {
588             throw new IllegalArgumentException(
589                     "RepositoryJavaClient.getAll: handler is missing");
590         }
591         String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
592         if (nuxeoWspaceId == null) {
593             throw new DocumentNotFoundException(
594                     "Unable to find workspace for service "
595                     + ctx.getServiceName()
596                     + " check if the workspace exists in the Nuxeo repository.");
597         }
598         
599         RepositoryInstance repoSession = null;
600         try {
601             handler.prepare(Action.GET_ALL);
602             repoSession = getRepositorySession();
603             DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
604             DocumentModelList docList = repoSession.getChildren(wsDocRef);
605             //set reposession to handle the document
606             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
607             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
608             handler.handle(Action.GET_ALL, wrapDoc);
609             handler.complete(Action.GET_ALL, wrapDoc);
610         } catch (DocumentException de) {
611             throw de;
612         } catch (Exception e) {
613             if (logger.isDebugEnabled()) {
614                 logger.debug("Caught exception ", e);
615             }
616             throw new DocumentException(e);
617         } finally {
618             if (repoSession != null) {
619                 releaseRepositorySession(repoSession);
620             }
621         }
622     }
623     
624     private boolean isClauseEmpty(String theString) {
625         boolean result = true;
626         if (theString != null && !theString.isEmpty()) {
627                 result = false;
628         }
629         return result;
630     }
631     
632     public DocumentWrapper<DocumentModel> getDocFromCsid(
633                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
634                 RepositoryInstance repoSession,
635                 String csid)
636             throws Exception {
637         DocumentWrapper<DocumentModel> result = null;
638
639         result = new DocumentWrapperImpl(NuxeoUtils.getDocFromCsid(ctx, repoSession, csid));
640         
641         return result;
642     }    
643
644     /*
645      * A method to find a CollectionSpace document (of any type) given just a service context and
646      * its CSID.  A search across *all* service workspaces (within a given tenant context) is performed to find
647      * the document
648      * 
649      * This query searches Nuxeo's Hierarchy table where our CSIDs are stored in the "name" column.
650      */
651     @Override
652     public DocumentWrapper<DocumentModel> getDocFromCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
653                 String csid)
654             throws Exception {
655         DocumentWrapper<DocumentModel> result = null;
656         RepositoryInstance repoSession = null;
657         try {
658                 repoSession = getRepositorySession();
659                 result = getDocFromCsid(ctx, repoSession, csid);
660         } finally {
661             if (repoSession != null) {
662                 releaseRepositorySession(repoSession);
663             }
664         }
665         
666         if (logger.isWarnEnabled() == true) {
667                 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
668         }
669         
670         return result;
671     }
672
673     /**
674      * find doc and return CSID from the Nuxeo repository
675      * @param ctx service context under which this method is invoked
676      * @param whereClause where NXQL where clause to get the document
677      * @throws DocumentException
678      */
679     @Override
680     public String getDocURI(DocumentWrapper<DocumentModel> wrappedDoc) throws ClientException {
681         DocumentModel docModel = wrappedDoc.getWrappedObject();
682         String uri = (String)docModel.getProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
683                                 DocumentModelHandler.COLLECTIONSPACE_CORE_URI);
684         return uri;
685     }
686
687
688     /**
689      * getFiltered get all documents for an entity service from the Document repository,
690      * given filter parameters specified by the handler. 
691      * @param ctx service context under which this method is invoked
692      * @param handler should be used by the caller to provide and transform the document
693      * @throws DocumentNotFoundException if workspace not found
694      * @throws DocumentException
695      */
696     @Override
697     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
698             throws DocumentNotFoundException, DocumentException {
699
700         DocumentFilter filter = handler.getDocumentFilter();
701         String oldOrderBy = filter.getOrderByClause();
702         if (isClauseEmpty(oldOrderBy) == true){
703             filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED);  //per http://issues.collectionspace.org/browse/CSPACE-705 (Doesn't this conflict with what happens with the QueryContext instance that we create below?)
704         }
705         QueryContext queryContext = new QueryContext(ctx, handler);
706
707         RepositoryInstance repoSession = null;
708         try {
709             handler.prepare(Action.GET_ALL);
710             repoSession = getRepositorySession();
711             DocumentModelList docList = null;
712             String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
713
714             if (logger.isDebugEnabled()) {
715                 logger.debug("Executing NXQL query: " + query.toString());
716             }
717
718             // If we have limit and/or offset, then pass true to get totalSize
719             // in returned DocumentModelList.
720                 Profiler profiler = new Profiler(this, 2);
721                 profiler.log("Executing NXQL query: " + query.toString());
722                 profiler.start();
723             if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
724                 docList = repoSession.query(query, null,
725                         queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
726             } else {
727                 docList = repoSession.query(query);
728             }
729             profiler.stop();
730
731             //set repoSession to handle the document
732             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
733             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
734             handler.handle(Action.GET_ALL, wrapDoc);
735             handler.complete(Action.GET_ALL, wrapDoc);
736         } catch (DocumentException de) {
737             throw de;
738         } catch (Exception e) {
739             if (logger.isDebugEnabled()) {
740                 logger.debug("Caught exception ", e);
741             }
742             throw new DocumentException(e);
743         } finally {
744             if (repoSession != null) {
745                 releaseRepositorySession(repoSession);
746             }
747         }
748     }
749
750     private String logException(Exception e, String msg) {
751         String result = null;
752         
753         String exceptionMessage = e.getMessage();
754         exceptionMessage = exceptionMessage != null ? exceptionMessage : "<No details provided>";
755         result = msg = msg + ". Caught exception:" + exceptionMessage;
756         
757         if (logger.isTraceEnabled() == true) {
758                 logger.error(msg, e);
759         } else {
760                 logger.error(msg);
761         }
762         
763         return result;
764     }
765     
766     /**
767      * update given document in the Nuxeo repository
768      *
769      * @param ctx service context under which this method is invoked
770      * @param id
771      *            of the document
772      * @param handler
773      *            should be used by the caller to provide and transform the
774      *            document
775      * @throws DocumentException
776      */
777     @Override
778     public void update(ServiceContext ctx, String csid, DocumentHandler handler)
779             throws BadRequestException, DocumentNotFoundException,
780             DocumentException {
781         if (handler == null) {
782             throw new IllegalArgumentException(
783                     "RepositoryJavaClient.update: document handler is missing.");
784         }
785         
786         RepositoryInstance repoSession = null;
787         try {
788             handler.prepare(Action.UPDATE);
789             repoSession = getRepositorySession();
790             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
791             DocumentModel doc = null;
792             try {
793                 doc = repoSession.getDocument(docRef);
794             } catch (ClientException ce) {
795                 String msg = logException(ce, "Could not find document to update with CSID=" + csid);
796                 throw new DocumentNotFoundException(msg, ce);
797             }
798             //
799             // Set reposession to handle the document
800             //
801             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
802             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
803             handler.handle(Action.UPDATE, wrapDoc);
804             repoSession.saveDocument(doc);
805             repoSession.save();
806             handler.complete(Action.UPDATE, wrapDoc);
807         } catch (BadRequestException bre) {
808             throw bre;
809         } catch (DocumentException de) {
810             throw de;
811         } catch (WebApplicationException wae){
812             throw wae;
813         } catch (Exception e) {
814             if (logger.isDebugEnabled()) {
815                 logger.debug("Caught exception ", e);
816             }
817             throw new DocumentException(e);
818         } finally {
819             if (repoSession != null) {
820                 releaseRepositorySession(repoSession);
821             }
822         }
823     }
824     
825     /**
826      * Save a documentModel to the Nuxeo repository.
827      * @param ctx service context under which this method is invoked
828      * @param docModel the document to save
829      * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
830      * @throws DocumentException
831      */
832     public void saveDocWithoutHandlerProcessing(
833             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
834             RepositoryInstance repoSession,
835             DocumentModel docModel,
836             boolean fSaveSession)
837             throws ClientException, DocumentException {
838
839         try {
840             repoSession.saveDocument(docModel);
841             if (fSaveSession) {
842                 repoSession.save();
843             }
844         } catch (ClientException ce) {
845             throw ce;
846         } catch (Exception e) {
847             if (logger.isDebugEnabled()) {
848                 logger.debug("Caught exception ", e);
849             }
850             throw new DocumentException(e);
851         }
852     }
853
854
855     /**
856      * Save a list of documentModels to the Nuxeo repository.
857      * 
858      * @param ctx service context under which this method is invoked
859      * @param docModel the document to save
860      * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
861      * @throws DocumentException
862      */
863     public void saveDocListWithoutHandlerProcessing(
864             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
865             RepositoryInstance repoSession,
866             DocumentModelList docList, 
867             boolean fSaveSession)
868             throws ClientException, DocumentException {
869         try {
870             repoSession = getRepositorySession();
871             DocumentModel[] docModelArray = new DocumentModel[docList.size()];
872             repoSession.saveDocuments(docList.toArray(docModelArray));
873             if (fSaveSession) {
874                 repoSession.save();
875             }
876         } catch (ClientException ce) {
877             throw ce;
878         } catch (Exception e) {
879             logger.error("Caught exception ", e);
880             throw new DocumentException(e);
881         }
882     }
883
884     /**
885      * delete a document from the Nuxeo repository
886      * @param ctx service context under which this method is invoked
887      * @param id
888      *            of the document
889      * @throws DocumentException
890      */
891     @Override
892     public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
893             DocumentException {
894
895         if (logger.isDebugEnabled()) {
896             logger.debug("Deleting document with CSID=" + id);
897         }
898         RepositoryInstance repoSession = null;
899         try {
900             repoSession = getRepositorySession();
901             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
902             try {
903                 repoSession.removeDocument(docRef);
904             } catch (ClientException ce) {
905                 String msg = logException(ce, "Could not find document to delete with CSID=" + id);
906                 throw new DocumentNotFoundException(msg, ce);
907             }
908             repoSession.save();
909         } catch (DocumentException de) {
910             throw de;
911         } catch (Exception e) {
912             if (logger.isDebugEnabled()) {
913                 logger.debug("Caught exception ", e);
914             }
915             throw new DocumentException(e);
916         } finally {
917             if (repoSession != null) {
918                 releaseRepositorySession(repoSession);
919             }
920         }
921     }
922
923     /* (non-Javadoc)
924      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
925      */
926     @Override
927     public void delete(ServiceContext ctx, String id, DocumentHandler handler)
928             throws DocumentNotFoundException, DocumentException {
929         throw new UnsupportedOperationException();
930     }
931
932     @Override
933     public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
934         return NuxeoConnectorEmbedded.getInstance().retrieveWorkspaceIds(domainName);
935     }
936
937     @Override
938     public String createDomain(String domainName) throws Exception {
939         RepositoryInstance repoSession = null;
940         String domainId = null;
941         try {
942                 //
943                 // First create the top-level domain directory
944                 //
945             repoSession = getRepositorySession();
946             DocumentRef parentDocRef = new PathRef("/");
947             DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
948             DocumentModel domainDoc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
949                     domainName, NUXEO_CORE_TYPE_DOMAIN);
950             domainDoc.setPropertyValue("dc:title", domainName);
951             domainDoc.setPropertyValue("dc:description", "A CollectionSpace domain "
952                     + domainName);
953             domainDoc = repoSession.createDocument(domainDoc);
954             domainId = domainDoc.getId();
955             repoSession.save();
956             //
957             // Next, create a "Workspaces" root directory to contain the workspace folders for the individual service documents
958             //
959             DocumentModel workspacesRoot = repoSession.createDocumentModel(domainDoc.getPathAsString(),
960                         NuxeoUtils.Workspaces, NUXEO_CORE_TYPE_WORKSPACEROOT);
961             workspacesRoot.setPropertyValue("dc:title", NuxeoUtils.Workspaces);
962             workspacesRoot.setPropertyValue("dc:description", "A CollectionSpace workspaces directory for "
963                     + domainDoc.getPathAsString());
964             workspacesRoot = repoSession.createDocument(workspacesRoot);
965             String workspacesRootId = workspacesRoot.getId();
966             repoSession.save();
967             
968             if (logger.isDebugEnabled()) {
969                 logger.debug("Created tenant domain name=" + domainName
970                         + " id=" + domainId + " " +
971                         NuxeoUtils.Workspaces + " id=" + workspacesRootId);
972                 logger.debug("Path to Domain: "+domainDoc.getPathAsString());
973                 logger.debug("Path to Workspaces root: "+workspacesRoot.getPathAsString());
974             }
975         } catch (Exception e) {
976             if (logger.isDebugEnabled()) {
977                 logger.debug("Could not create tenant domain name=" + domainName + " caught exception ", e);
978             }
979             throw e;
980         } finally {
981             if (repoSession != null) {
982                 releaseRepositorySession(repoSession);
983             }
984         }
985         
986         return domainId;
987     }
988
989     @Override
990     public String getDomainId(String domainName) throws Exception {
991         String domainId = null;
992         RepositoryInstance repoSession = null;
993         
994         if (domainName != null && !domainName.isEmpty()) {
995                 try {
996                     repoSession = getRepositorySession();
997                     DocumentRef docRef = new PathRef(
998                             "/" + domainName);
999                     DocumentModel domain = repoSession.getDocument(docRef);
1000                     domainId = domain.getId();
1001                 } catch (Exception e) {
1002                     if (logger.isTraceEnabled()) {
1003                         logger.trace("Caught exception ", e);
1004                     }
1005                     //there is no way to identify if document does not exist due to
1006                     //lack of typed exception for getDocument method
1007                     return null;
1008                 } finally {
1009                     if (repoSession != null) {
1010                         releaseRepositorySession(repoSession);
1011                     }
1012                 }
1013         }
1014         
1015         return domainId;
1016     }
1017
1018     /* (non-Javadoc)
1019      * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
1020      */
1021     @Override
1022     public String createWorkspace(String domainName, String workspaceName) throws Exception {
1023         RepositoryInstance repoSession = null;
1024         String workspaceId = null;
1025         try {
1026             repoSession = getRepositorySession();
1027             String parentDocRefPath = "/" + domainName + "/" + NuxeoUtils.Workspaces;
1028             DocumentRef parentDocRef = new PathRef(
1029                     "/" + domainName
1030                     + "/" + NuxeoUtils.Workspaces);
1031             if (logger.isDebugEnabled()) {
1032                 logger.debug("Fetching "+NuxeoUtils.Workspaces+", path=" + parentDocRefPath
1033                         + " docRef:" + parentDocRef.toString());
1034             }
1035             DocumentModel parentDoc = null;
1036             try {
1037                 parentDoc = repoSession.getDocument(parentDocRef);
1038             } catch(ClientException ce) {
1039                 logger.error("Failed to get "+NuxeoUtils.Workspaces+", path=" + parentDocRefPath
1040                         + " docRef:" + parentDocRef.toString());
1041                 // try again for debugging
1042                 parentDoc = repoSession.getDocument(parentDocRef);
1043                 //throw ce;
1044             }
1045             DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1046                     workspaceName, "Workspace");
1047             doc.setPropertyValue("dc:title", workspaceName);
1048             doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
1049                     + workspaceName);
1050             doc = repoSession.createDocument(doc);
1051             workspaceId = doc.getId();
1052             repoSession.save();
1053             if (logger.isDebugEnabled()) {
1054                 logger.debug("created workspace name=" + workspaceName
1055                         + " id=" + workspaceId);
1056             }
1057         } catch (Exception e) {
1058             if (logger.isDebugEnabled()) {
1059                 logger.debug("createWorkspace caught exception ", e);
1060             }
1061             throw e;
1062         } finally {
1063             if (repoSession != null) {
1064                 releaseRepositorySession(repoSession);
1065             }
1066         }
1067         return workspaceId;
1068     }
1069
1070     /* (non-Javadoc)
1071      * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
1072      */
1073     @Override
1074     public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
1075         String workspaceId = null;
1076         
1077         RepositoryInstance repoSession = null;
1078         try {
1079             repoSession = getRepositorySession();
1080             DocumentRef docRef = new PathRef(
1081                     "/" + tenantDomain
1082                     + "/" + NuxeoUtils.Workspaces
1083                     + "/" + workspaceName);
1084             DocumentModel workspace = repoSession.getDocument(docRef);
1085             workspaceId = workspace.getId();
1086         } catch (DocumentException de) {
1087             throw de;
1088         } catch (Exception e) {
1089             if (logger.isDebugEnabled()) {
1090                 logger.debug("Caught exception ", e);
1091             }
1092             throw new DocumentException(e);
1093         } finally {
1094             if (repoSession != null) {
1095                 releaseRepositorySession(repoSession);
1096             }
1097         }
1098         
1099         return workspaceId;
1100     }
1101
1102
1103     /**
1104      * Gets the repository session. - Package access only.
1105      *
1106      * @return the repository session
1107      * @throws Exception the exception
1108      */
1109     public RepositoryInstance getRepositorySession() throws Exception {
1110         // FIXME: is it possible to reuse repository session?
1111         // Authentication failures happen while trying to reuse the session
1112         Profiler profiler = new Profiler("getRepositorySession():", 2);
1113         profiler.start();
1114         
1115         NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1116         RepositoryInstance repoSession = client.openRepository();
1117         if (logger.isTraceEnabled()) {
1118             logger.trace("Testing call to getRepository() repository root: " + repoSession.getRootDocument());
1119         }
1120         
1121         profiler.stop();
1122         return repoSession;
1123     }
1124
1125     /**
1126      * Release repository session. - Package access only.
1127      *
1128      * @param repoSession the repo session
1129      */
1130     public void releaseRepositorySession(RepositoryInstance repoSession) {
1131         try {
1132             NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1133             // release session
1134             client.releaseRepository(repoSession);
1135         } catch (Exception e) {
1136             logger.error("Could not close the repository session", e);
1137             // no need to throw this service specific exception
1138         }
1139     }
1140
1141 }