]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
9b66ad51ef3f2a688ebf54ae54cab4a73662b053
[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.ArrayList;
21 import java.util.Hashtable;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.UUID;
25
26 import javax.ws.rs.WebApplicationException;
27 import javax.ws.rs.core.MultivaluedMap;
28
29 import org.collectionspace.services.client.PoxPayloadIn;
30 import org.collectionspace.services.client.PoxPayloadOut;
31 import org.collectionspace.services.client.workflow.WorkflowClient;
32 import org.collectionspace.services.common.context.ServiceContext;
33 import org.collectionspace.services.common.query.QueryContext;
34 import org.collectionspace.services.common.repository.RepositoryClient;
35 import org.collectionspace.services.common.profile.Profiler;
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             // force limit to 1, and ignore totalSize
475             QueryContext queryContext = new QueryContext(ctx, whereClause);
476             String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
477             if (logger.isDebugEnabled()) {
478                 logger.debug("findDocs() NXQL: "+query);
479             }
480             docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
481             wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
482         } catch (IllegalArgumentException iae) {
483             throw iae;
484         } catch (Exception e) {
485             if (logger.isDebugEnabled()) {
486                 logger.debug("Caught exception ", e);
487             }
488             throw new DocumentException(e);
489         }
490                 
491         return wrapDoc;
492     }
493     
494     /**
495      * Find a list of documentModels from the Nuxeo repository
496      * @param docTypes a list of DocType names to match
497      * @param  whereClause where the clause to qualify on
498      * @return
499      */
500     @Override
501     public DocumentWrapper<DocumentModelList> findDocs(
502             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
503             List<String> docTypes,
504             String whereClause,
505             int pageSize, int pageNum, boolean computeTotal)
506             throws DocumentNotFoundException, DocumentException {
507         RepositoryInstance repoSession = null;
508         DocumentWrapper<DocumentModelList> wrapDoc = null;
509
510         try {
511             repoSession = getRepositorySession();
512             wrapDoc = findDocs(ctx, repoSession, docTypes, whereClause,
513                         pageSize, pageNum, computeTotal);
514         } catch (IllegalArgumentException iae) {
515             throw iae;
516         } catch (Exception e) {
517             if (logger.isDebugEnabled()) {
518                 logger.debug("Caught exception ", e);
519             }
520             throw new DocumentException(e);
521         } finally {
522             if (repoSession != null) {
523                 releaseRepositorySession(repoSession);
524             }
525         }
526         
527         if (logger.isWarnEnabled() == true) {
528                 logger.warn("Returned DocumentModelList instance was created with a repository session that is now closed.");
529         }
530         
531         return wrapDoc;
532     }
533
534     /* (non-Javadoc)
535      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
536      */
537     @Override
538     public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
539             throws DocumentNotFoundException, DocumentException {
540         if (handler == null) {
541             throw new IllegalArgumentException(
542                     "RepositoryJavaClient.getAll: handler is missing");
543         }
544
545         RepositoryInstance repoSession = null;
546         try {
547             handler.prepare(Action.GET_ALL);
548             repoSession = getRepositorySession();
549             DocumentModelList docModelList = new DocumentModelListImpl();
550             //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
551             for (String csid : csidList) {
552                 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
553                 DocumentModel docModel = repoSession.getDocument(docRef);
554                 docModelList.add(docModel);
555             }
556
557             //set reposession to handle the document
558             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
559             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
560             handler.handle(Action.GET_ALL, wrapDoc);
561             handler.complete(Action.GET_ALL, wrapDoc);
562         } catch (DocumentException de) {
563             throw de;
564         } catch (Exception e) {
565             if (logger.isDebugEnabled()) {
566                 logger.debug("Caught exception ", e);
567             }
568             throw new DocumentException(e);
569         } finally {
570             if (repoSession != null) {
571                 releaseRepositorySession(repoSession);
572             }
573         }
574     }
575
576     /**
577      * getAll get all documents for an entity entity service from the Nuxeo
578      * repository
579      *
580      * @param ctx service context under which this method is invoked
581      * @param handler
582      *            should be used by the caller to provide and transform the
583      *            document
584      * @throws DocumentException
585      */
586     @Override
587     public void getAll(ServiceContext ctx, DocumentHandler handler)
588             throws DocumentNotFoundException, DocumentException {
589         if (handler == null) {
590             throw new IllegalArgumentException(
591                     "RepositoryJavaClient.getAll: handler is missing");
592         }
593         String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
594         if (nuxeoWspaceId == null) {
595             throw new DocumentNotFoundException(
596                     "Unable to find workspace for service "
597                     + ctx.getServiceName()
598                     + " check if the workspace exists in the Nuxeo repository.");
599         }
600         
601         RepositoryInstance repoSession = null;
602         try {
603             handler.prepare(Action.GET_ALL);
604             repoSession = getRepositorySession();
605             DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
606             DocumentModelList docList = repoSession.getChildren(wsDocRef);
607             //set reposession to handle the document
608             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
609             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
610             handler.handle(Action.GET_ALL, wrapDoc);
611             handler.complete(Action.GET_ALL, wrapDoc);
612         } catch (DocumentException de) {
613             throw de;
614         } catch (Exception e) {
615             if (logger.isDebugEnabled()) {
616                 logger.debug("Caught exception ", e);
617             }
618             throw new DocumentException(e);
619         } finally {
620             if (repoSession != null) {
621                 releaseRepositorySession(repoSession);
622             }
623         }
624     }
625     
626     private boolean isClauseEmpty(String theString) {
627         boolean result = true;
628         if (theString != null && !theString.isEmpty()) {
629                 result = false;
630         }
631         return result;
632     }
633     
634     public DocumentWrapper<DocumentModel> getDocFromCsid(
635                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
636                 RepositoryInstance repoSession,
637                 String csid)
638             throws Exception {
639         DocumentWrapper<DocumentModel> result = null;
640
641         result = new DocumentWrapperImpl(NuxeoUtils.getDocFromCsid(ctx, repoSession, csid));
642         
643         return result;
644     }    
645
646     /*
647      * A method to find a CollectionSpace document (of any type) given just a service context and
648      * its CSID.  A search across *all* service workspaces (within a given tenant context) is performed to find
649      * the document
650      * 
651      * This query searches Nuxeo's Hierarchy table where our CSIDs are stored in the "name" column.
652      */
653     @Override
654     public DocumentWrapper<DocumentModel> getDocFromCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
655                 String csid)
656             throws Exception {
657         DocumentWrapper<DocumentModel> result = null;
658         RepositoryInstance repoSession = null;
659         try {
660                 repoSession = getRepositorySession();
661                 result = getDocFromCsid(ctx, repoSession, csid);
662         } finally {
663             if (repoSession != null) {
664                 releaseRepositorySession(repoSession);
665             }
666         }
667         
668         if (logger.isWarnEnabled() == true) {
669                 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
670         }
671         
672         return result;
673     }
674
675     /**
676      * find doc and return CSID from the Nuxeo repository
677      * @param ctx service context under which this method is invoked
678      * @param whereClause where NXQL where clause to get the document
679      * @throws DocumentException
680      */
681     @Override
682     public String getDocURI(DocumentWrapper<DocumentModel> wrappedDoc) throws ClientException {
683         DocumentModel docModel = wrappedDoc.getWrappedObject();
684         String uri = (String)docModel.getProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
685                                 DocumentModelHandler.COLLECTIONSPACE_CORE_URI);
686         return uri;
687     }
688
689
690     /**
691      * getFiltered get all documents for an entity service from the Document repository,
692      * given filter parameters specified by the handler. 
693      * @param ctx service context under which this method is invoked
694      * @param handler should be used by the caller to provide and transform the document
695      * @throws DocumentNotFoundException if workspace not found
696      * @throws DocumentException
697      */
698     @Override
699     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
700             throws DocumentNotFoundException, DocumentException {
701
702         DocumentFilter filter = handler.getDocumentFilter();
703         String oldOrderBy = filter.getOrderByClause();
704         if (isClauseEmpty(oldOrderBy) == true){
705             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?)
706         }
707         QueryContext queryContext = new QueryContext(ctx, handler);
708
709         RepositoryInstance repoSession = null;
710         try {
711             handler.prepare(Action.GET_ALL);
712             repoSession = getRepositorySession();
713             DocumentModelList docList = null;
714             String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
715
716             if (logger.isDebugEnabled()) {
717                 logger.debug("Executing NXQL query: " + query.toString());
718             }
719
720             // If we have limit and/or offset, then pass true to get totalSize
721             // in returned DocumentModelList.
722                 Profiler profiler = new Profiler(this, 2);
723                 profiler.log("Executing NXQL query: " + query.toString());
724                 profiler.start();
725             if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
726                 docList = repoSession.query(query, null,
727                         queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
728             } else {
729                 docList = repoSession.query(query);
730             }
731             profiler.stop();
732
733             //set repoSession to handle the document
734             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
735             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
736             handler.handle(Action.GET_ALL, wrapDoc);
737             handler.complete(Action.GET_ALL, wrapDoc);
738         } catch (DocumentException de) {
739             throw de;
740         } catch (Exception e) {
741             if (logger.isDebugEnabled()) {
742                 logger.debug("Caught exception ", e);
743             }
744             throw new DocumentException(e);
745         } finally {
746             if (repoSession != null) {
747                 releaseRepositorySession(repoSession);
748             }
749         }
750     }
751
752     private String logException(Exception e, String msg) {
753         String result = null;
754         
755         String exceptionMessage = e.getMessage();
756         exceptionMessage = exceptionMessage != null ? exceptionMessage : "<No details provided>";
757         result = msg = msg + ". Caught exception:" + exceptionMessage;
758         
759         if (logger.isTraceEnabled() == true) {
760                 logger.error(msg, e);
761         } else {
762                 logger.error(msg);
763         }
764         
765         return result;
766     }
767     
768     /**
769      * update given document in the Nuxeo repository
770      *
771      * @param ctx service context under which this method is invoked
772      * @param id
773      *            of the document
774      * @param handler
775      *            should be used by the caller to provide and transform the
776      *            document
777      * @throws DocumentException
778      */
779     @Override
780     public void update(ServiceContext ctx, String csid, DocumentHandler handler)
781             throws BadRequestException, DocumentNotFoundException,
782             DocumentException {
783         if (handler == null) {
784             throw new IllegalArgumentException(
785                     "RepositoryJavaClient.update: document handler is missing.");
786         }
787         
788         RepositoryInstance repoSession = null;
789         try {
790             handler.prepare(Action.UPDATE);
791             repoSession = getRepositorySession();
792             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
793             DocumentModel doc = null;
794             try {
795                 doc = repoSession.getDocument(docRef);
796             } catch (ClientException ce) {
797                 String msg = logException(ce, "Could not find document to update with CSID=" + csid);
798                 throw new DocumentNotFoundException(msg, ce);
799             }
800             //
801             // Set reposession to handle the document
802             //
803             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
804             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
805             handler.handle(Action.UPDATE, wrapDoc);
806             repoSession.saveDocument(doc);
807             repoSession.save();
808             handler.complete(Action.UPDATE, wrapDoc);
809         } catch (BadRequestException bre) {
810             throw bre;
811         } catch (DocumentException de) {
812             throw de;
813         } catch (WebApplicationException wae){
814             throw wae;
815         } catch (Exception e) {
816             if (logger.isDebugEnabled()) {
817                 logger.debug("Caught exception ", e);
818             }
819             throw new DocumentException(e);
820         } finally {
821             if (repoSession != null) {
822                 releaseRepositorySession(repoSession);
823             }
824         }
825     }
826     
827     /**
828      * Save a documentModel to the Nuxeo repository.
829      * @param ctx service context under which this method is invoked
830      * @param docModel the document to save
831      * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
832      * @throws DocumentException
833      */
834     public void saveDocWithoutHandlerProcessing(
835             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
836             RepositoryInstance repoSession,
837             DocumentModel docModel,
838             boolean fSaveSession)
839             throws ClientException, DocumentException {
840
841         try {
842             repoSession.saveDocument(docModel);
843             if (fSaveSession) {
844                 repoSession.save();
845             }
846         } catch (ClientException ce) {
847             throw ce;
848         } catch (Exception e) {
849             if (logger.isDebugEnabled()) {
850                 logger.debug("Caught exception ", e);
851             }
852             throw new DocumentException(e);
853         }
854     }
855
856
857     /**
858      * Save a list of documentModels to the Nuxeo repository.
859      * 
860      * @param ctx service context under which this method is invoked
861      * @param docModel the document to save
862      * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
863      * @throws DocumentException
864      */
865     public void saveDocListWithoutHandlerProcessing(
866             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
867             RepositoryInstance repoSession,
868             DocumentModelList docList, 
869             boolean fSaveSession)
870             throws ClientException, DocumentException {
871         try {
872             repoSession = getRepositorySession();
873             DocumentModel[] docModelArray = new DocumentModel[docList.size()];
874             repoSession.saveDocuments(docList.toArray(docModelArray));
875             if (fSaveSession) {
876                 repoSession.save();
877             }
878         } catch (ClientException ce) {
879             throw ce;
880         } catch (Exception e) {
881             logger.error("Caught exception ", e);
882             throw new DocumentException(e);
883         }
884     }
885
886     /**
887      * delete a document from the Nuxeo repository
888      * @param ctx service context under which this method is invoked
889      * @param id
890      *            of the document
891      * @throws DocumentException
892      */
893     @Override
894     public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
895             DocumentException {
896
897         if (logger.isDebugEnabled()) {
898             logger.debug("Deleting document with CSID=" + id);
899         }
900         RepositoryInstance repoSession = null;
901         try {
902             repoSession = getRepositorySession();
903             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
904             try {
905                 repoSession.removeDocument(docRef);
906             } catch (ClientException ce) {
907                 String msg = logException(ce, "Could not find document to delete with CSID=" + id);
908                 throw new DocumentNotFoundException(msg, ce);
909             }
910             repoSession.save();
911         } catch (DocumentException de) {
912             throw de;
913         } catch (Exception e) {
914             if (logger.isDebugEnabled()) {
915                 logger.debug("Caught exception ", e);
916             }
917             throw new DocumentException(e);
918         } finally {
919             if (repoSession != null) {
920                 releaseRepositorySession(repoSession);
921             }
922         }
923     }
924
925     /* (non-Javadoc)
926      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
927      */
928     @Override
929     public void delete(ServiceContext ctx, String id, DocumentHandler handler)
930             throws DocumentNotFoundException, DocumentException {
931         throw new UnsupportedOperationException();
932     }
933
934     @Override
935     public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
936         return NuxeoConnectorEmbedded.getInstance().retrieveWorkspaceIds(domainName);
937     }
938
939     @Override
940     public String createDomain(String domainName) throws Exception {
941         RepositoryInstance repoSession = null;
942         String domainId = null;
943         try {
944                 //
945                 // First create the top-level domain directory
946                 //
947             repoSession = getRepositorySession();
948             DocumentRef parentDocRef = new PathRef("/");
949             DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
950             DocumentModel domainDoc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
951                     domainName, NUXEO_CORE_TYPE_DOMAIN);
952             domainDoc.setPropertyValue("dc:title", domainName);
953             domainDoc.setPropertyValue("dc:description", "A CollectionSpace domain "
954                     + domainName);
955             domainDoc = repoSession.createDocument(domainDoc);
956             domainId = domainDoc.getId();
957             repoSession.save();
958             //
959             // Next, create a "Workspaces" root directory to contain the workspace folders for the individual service documents
960             //
961             DocumentModel workspacesRoot = repoSession.createDocumentModel(domainDoc.getPathAsString(),
962                         NuxeoUtils.Workspaces, NUXEO_CORE_TYPE_WORKSPACEROOT);
963             workspacesRoot.setPropertyValue("dc:title", NuxeoUtils.Workspaces);
964             workspacesRoot.setPropertyValue("dc:description", "A CollectionSpace workspaces directory for "
965                     + domainDoc.getPathAsString());
966             workspacesRoot = repoSession.createDocument(workspacesRoot);
967             String workspacesRootId = workspacesRoot.getId();
968             repoSession.save();
969             
970             if (logger.isDebugEnabled()) {
971                 logger.debug("Created tenant domain name=" + domainName
972                         + " id=" + domainId + " " +
973                         NuxeoUtils.Workspaces + " id=" + workspacesRootId);
974                 logger.debug("Path to Domain: "+domainDoc.getPathAsString());
975                 logger.debug("Path to Workspaces root: "+workspacesRoot.getPathAsString());
976             }
977         } catch (Exception e) {
978             if (logger.isDebugEnabled()) {
979                 logger.debug("Could not create tenant domain name=" + domainName + " caught exception ", e);
980             }
981             throw e;
982         } finally {
983             if (repoSession != null) {
984                 releaseRepositorySession(repoSession);
985             }
986         }
987         
988         return domainId;
989     }
990
991     @Override
992     public String getDomainId(String domainName) throws Exception {
993         String domainId = null;
994         RepositoryInstance repoSession = null;
995         
996         if (domainName != null && !domainName.isEmpty()) {
997                 try {
998                     repoSession = getRepositorySession();
999                     DocumentRef docRef = new PathRef(
1000                             "/" + domainName);
1001                     DocumentModel domain = repoSession.getDocument(docRef);
1002                     domainId = domain.getId();
1003                 } catch (Exception e) {
1004                     if (logger.isTraceEnabled()) {
1005                         logger.trace("Caught exception ", e);
1006                     }
1007                     //there is no way to identify if document does not exist due to
1008                     //lack of typed exception for getDocument method
1009                     return null;
1010                 } finally {
1011                     if (repoSession != null) {
1012                         releaseRepositorySession(repoSession);
1013                     }
1014                 }
1015         }
1016         
1017         return domainId;
1018     }
1019
1020     /*
1021          * Returns the workspaces root directory for a given domain.
1022          */
1023         private DocumentModel getWorkspacesRoot(RepositoryInstance repoSession,
1024                         String domainName) throws Exception {
1025                 DocumentModel result = null;
1026                 
1027                 String domainPath = "/" + domainName;
1028                 DocumentRef parentDocRef = new PathRef(domainPath);
1029                 DocumentModelList domainChildrenList = repoSession.getChildren(
1030                                 parentDocRef);
1031                 Iterator<DocumentModel> witer = domainChildrenList.iterator();
1032                 while (witer.hasNext()) {
1033                         DocumentModel childNode = witer.next();
1034                         if (NuxeoUtils.Workspaces.equalsIgnoreCase(childNode.getName())) {
1035                                 result = childNode;
1036                                 logger.trace("Found workspaces directory at: " + result.getPathAsString());
1037                                 break;
1038                         }
1039                 }
1040                 
1041                 if (result == null) {
1042                         throw new ClientException("Could not find workspace root directory in: "
1043                                         + domainPath);
1044                 }
1045
1046                 return result;
1047         }
1048     
1049     /* (non-Javadoc)
1050      * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
1051      */
1052     @Override
1053     public String createWorkspace(String domainName, String workspaceName) throws Exception {
1054         RepositoryInstance repoSession = null;
1055         String workspaceId = null;
1056         try {
1057             repoSession = getRepositorySession();
1058             DocumentModel parentDoc = getWorkspacesRoot(repoSession, domainName);            
1059             DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1060                     workspaceName, NuxeoUtils.WORKSPACE_DOCUMENT_TYPE);
1061             doc.setPropertyValue("dc:title", workspaceName);
1062             doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
1063                     + workspaceName);
1064             doc = repoSession.createDocument(doc);
1065             workspaceId = doc.getId();
1066             repoSession.save();
1067             if (logger.isDebugEnabled()) {
1068                 logger.debug("Created workspace name=" + workspaceName
1069                         + " id=" + workspaceId);
1070             }
1071         } catch (Exception e) {
1072             if (logger.isDebugEnabled()) {
1073                 logger.debug("createWorkspace caught exception ", e);
1074             }
1075             throw e;
1076         } finally {
1077             if (repoSession != null) {
1078                 releaseRepositorySession(repoSession);
1079             }
1080         }
1081         return workspaceId;
1082     }
1083
1084     /* (non-Javadoc)
1085      * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
1086      */
1087     @Override
1088     public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
1089         String workspaceId = null;
1090         
1091         RepositoryInstance repoSession = null;
1092         try {
1093             repoSession = getRepositorySession();
1094             DocumentRef docRef = new PathRef(
1095                     "/" + tenantDomain
1096                     + "/" + NuxeoUtils.Workspaces
1097                     + "/" + workspaceName);
1098             DocumentModel workspace = repoSession.getDocument(docRef);
1099             workspaceId = workspace.getId();
1100         } catch (DocumentException de) {
1101             throw de;
1102         } catch (Exception e) {
1103             if (logger.isDebugEnabled()) {
1104                 logger.debug("Caught exception ", e);
1105             }
1106             throw new DocumentException(e);
1107         } finally {
1108             if (repoSession != null) {
1109                 releaseRepositorySession(repoSession);
1110             }
1111         }
1112         
1113         return workspaceId;
1114     }
1115
1116
1117     /**
1118      * Gets the repository session. - Package access only.
1119      *
1120      * @return the repository session
1121      * @throws Exception the exception
1122      */
1123     public RepositoryInstance getRepositorySession() throws Exception {
1124         // FIXME: is it possible to reuse repository session?
1125         // Authentication failures happen while trying to reuse the session
1126         Profiler profiler = new Profiler("getRepositorySession():", 2);
1127         profiler.start();
1128         
1129         NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1130         RepositoryInstance repoSession = client.openRepository();
1131         if (logger.isTraceEnabled()) {
1132             logger.trace("Testing call to getRepository() repository root: " + repoSession.getRootDocument());
1133         }
1134         
1135         profiler.stop();
1136         return repoSession;
1137     }
1138
1139     /**
1140      * Release repository session. - Package access only.
1141      *
1142      * @param repoSession the repo session
1143      */
1144     public void releaseRepositorySession(RepositoryInstance repoSession) {
1145         try {
1146             NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1147             // release session
1148             client.releaseRepository(repoSession);
1149         } catch (Exception e) {
1150             logger.error("Could not close the repository session", e);
1151             // no need to throw this service specific exception
1152         }
1153     }
1154
1155 }