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