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