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