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