]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
bf058c070de0c2199d1e44f6f1a33e78ee398f5b
[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.workflow.WorkflowClient;
35 import org.collectionspace.services.common.context.ServiceContext;
36 import org.collectionspace.services.common.query.QueryContext;
37 import org.collectionspace.services.common.repository.RepositoryClient;
38 import org.collectionspace.services.common.profile.Profiler;
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             int pageSize, int pageNum, boolean computeTotal)
479                         throws DocumentNotFoundException, DocumentException {
480         DocumentWrapper<DocumentModelList> wrapDoc = null;
481
482         try {
483             if (docTypes == null || docTypes.size() < 1) {
484                 throw new DocumentNotFoundException(
485                         "The findDocs() method must specify at least one DocumentType.");
486             }
487             DocumentModelList docList = null;
488             QueryContext queryContext = new QueryContext(ctx, whereClause);
489             String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
490             if (logger.isDebugEnabled()) {
491                 logger.debug("findDocs() NXQL: "+query);
492             }
493             docList = repoSession.query(query, null, pageSize, pageSize*pageNum, computeTotal);
494             wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
495         } catch (IllegalArgumentException iae) {
496             throw iae;
497         } catch (Exception e) {
498             if (logger.isDebugEnabled()) {
499                 logger.debug("Caught exception ", e);
500             }
501             throw new DocumentException(e);
502         }
503                 
504         return wrapDoc;
505     }
506     
507     protected static String buildInListForDocTypes(List<String> docTypes) {
508         StringBuilder sb = new StringBuilder();
509         sb.append("(");
510         boolean first = true;
511         for(String docType:docTypes) {
512                 if(first) {
513                         first = false;
514                 } else {
515                         sb.append(",");
516                 }
517                         sb.append("'");
518                 sb.append(docType);
519                         sb.append("'");
520         }
521         sb.append(")");
522         return sb.toString();
523     }
524     
525     public DocumentWrapper<DocumentModelList> findDocs(
526             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
527             DocumentHandler handler,
528             RepositoryInstance repoSession,
529             List<String> docTypes)
530                         throws DocumentNotFoundException, DocumentException {
531         DocumentWrapper<DocumentModelList> wrapDoc = null;
532
533         DocumentFilter filter = handler.getDocumentFilter();
534         String oldOrderBy = filter.getOrderByClause();
535         QueryContext queryContext = new QueryContext(ctx, handler);
536
537         try {
538             if (docTypes == null || docTypes.size() < 1) {
539                 throw new DocumentNotFoundException(
540                         "The findDocs() method must specify at least one DocumentType.");
541             }
542             DocumentModelList docList = null;
543                 if (handler.isCMISQuery() == true) {
544                         // We have to omit the ORDER BY until we can get the "Document" table bug addressed.
545                         // CF Nuxeo JIRA NXP-9562
546                         String inList = buildInListForDocTypes(docTypes);
547                         ctx.getQueryParams().add(IQueryManager.SEARCH_RELATED_MATCH_OBJ_DOCTYPES, inList);
548                         docList = getFilteredCMIS(repoSession, ctx, handler, queryContext);
549             } else {
550                 if (isClauseEmpty(oldOrderBy) == true){
551                     filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED);
552                 }
553                 String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
554                 if (logger.isDebugEnabled()) {
555                     logger.debug("findDocs() NXQL: "+query);
556                 }
557                 docList = repoSession.query(query, null, filter.getPageSize(), filter.getOffset(), true);
558             }
559             wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
560         } catch (IllegalArgumentException iae) {
561             throw iae;
562         } catch (Exception e) {
563             if (logger.isDebugEnabled()) {
564                 logger.debug("Caught exception ", e);
565             }
566             throw new DocumentException(e);
567         }
568                 
569         return wrapDoc;
570     }
571     
572
573     
574     /**
575      * Find a list of documentModels from the Nuxeo repository
576      * @param docTypes a list of DocType names to match
577      * @param  whereClause where the clause to qualify on
578      * @return
579      */
580     @Override
581     public DocumentWrapper<DocumentModelList> findDocs(
582             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
583             List<String> docTypes,
584             String whereClause,
585             int pageSize, int pageNum, boolean computeTotal)
586             throws DocumentNotFoundException, DocumentException {
587         RepositoryInstance repoSession = null;
588         DocumentWrapper<DocumentModelList> wrapDoc = null;
589
590         try {
591             repoSession = getRepositorySession(ctx);
592             wrapDoc = findDocs(ctx, repoSession, docTypes, whereClause,
593                         pageSize, pageNum, computeTotal);
594         } catch (IllegalArgumentException iae) {
595             throw iae;
596         } catch (Exception e) {
597             if (logger.isDebugEnabled()) {
598                 logger.debug("Caught exception ", e);
599             }
600             throw new DocumentException(e);
601         } finally {
602             if (repoSession != null) {
603                 releaseRepositorySession(ctx, repoSession);
604             }
605         }
606         
607         if (logger.isWarnEnabled() == true) {
608                 logger.warn("Returned DocumentModelList instance was created with a repository session that is now closed.");
609         }
610         
611         return wrapDoc;
612     }
613
614     /* (non-Javadoc)
615      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
616      */
617     @Override
618     public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
619             throws DocumentNotFoundException, DocumentException {
620         if (handler == null) {
621             throw new IllegalArgumentException(
622                     "RepositoryJavaClient.getAll: handler is missing");
623         }
624
625         RepositoryInstance repoSession = null;
626         try {
627             handler.prepare(Action.GET_ALL);
628             repoSession = getRepositorySession(ctx);
629             DocumentModelList docModelList = new DocumentModelListImpl();
630             //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
631             for (String csid : csidList) {
632                 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
633                 DocumentModel docModel = repoSession.getDocument(docRef);
634                 docModelList.add(docModel);
635             }
636
637             //set reposession to handle the document
638             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
639             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
640             handler.handle(Action.GET_ALL, wrapDoc);
641             handler.complete(Action.GET_ALL, wrapDoc);
642         } catch (DocumentException de) {
643             throw de;
644         } catch (Exception e) {
645             if (logger.isDebugEnabled()) {
646                 logger.debug("Caught exception ", e);
647             }
648             throw new DocumentException(e);
649         } finally {
650             if (repoSession != null) {
651                 releaseRepositorySession(ctx, repoSession);
652             }
653         }
654     }
655
656     /**
657      * getAll get all documents for an entity entity service from the Nuxeo
658      * repository
659      *
660      * @param ctx service context under which this method is invoked
661      * @param handler
662      *            should be used by the caller to provide and transform the
663      *            document
664      * @throws DocumentException
665      */
666     @Override
667     public void getAll(ServiceContext ctx, DocumentHandler handler)
668             throws DocumentNotFoundException, DocumentException {
669         if (handler == null) {
670             throw new IllegalArgumentException(
671                     "RepositoryJavaClient.getAll: handler is missing");
672         }
673         String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
674         if (nuxeoWspaceId == null) {
675             throw new DocumentNotFoundException(
676                     "Unable to find workspace for service "
677                     + ctx.getServiceName()
678                     + " check if the workspace exists in the Nuxeo repository.");
679         }
680         
681         RepositoryInstance repoSession = null;
682         try {
683             handler.prepare(Action.GET_ALL);
684             repoSession = getRepositorySession(ctx);
685             DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
686             DocumentModelList docList = repoSession.getChildren(wsDocRef);
687             //set reposession to handle the document
688             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
689             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
690             handler.handle(Action.GET_ALL, wrapDoc);
691             handler.complete(Action.GET_ALL, wrapDoc);
692         } catch (DocumentException de) {
693             throw de;
694         } catch (Exception e) {
695             if (logger.isDebugEnabled()) {
696                 logger.debug("Caught exception ", e);
697             }
698             throw new DocumentException(e);
699         } finally {
700             if (repoSession != null) {
701                 releaseRepositorySession(ctx, repoSession);
702             }
703         }
704     }
705     
706     private boolean isClauseEmpty(String theString) {
707         boolean result = true;
708         if (theString != null && !theString.isEmpty()) {
709                 result = false;
710         }
711         return result;
712     }
713     
714     public DocumentWrapper<DocumentModel> getDocFromCsid(
715                 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
716                 RepositoryInstance repoSession,
717                 String csid)
718             throws Exception {
719         DocumentWrapper<DocumentModel> result = null;
720
721         result = new DocumentWrapperImpl(NuxeoUtils.getDocFromCsid(ctx, repoSession, csid));
722         
723         return result;
724     }    
725
726     /*
727      * A method to find a CollectionSpace document (of any type) given just a service context and
728      * its CSID.  A search across *all* service workspaces (within a given tenant context) is performed to find
729      * the document
730      * 
731      * This query searches Nuxeo's Hierarchy table where our CSIDs are stored in the "name" column.
732      */
733     @Override
734     public DocumentWrapper<DocumentModel> getDocFromCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
735                 String csid)
736             throws Exception {
737         DocumentWrapper<DocumentModel> result = null;
738         RepositoryInstance repoSession = null;
739         try {
740                 repoSession = getRepositorySession(ctx);
741                 result = getDocFromCsid(ctx, repoSession, csid);
742         } finally {
743             if (repoSession != null) {
744                 releaseRepositorySession(ctx, repoSession);
745             }
746         }
747         
748         if (logger.isWarnEnabled() == true) {
749                 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
750         }
751         
752         return result;
753     }
754
755     /**
756      * find doc and return CSID from the Nuxeo repository
757      * @param ctx service context under which this method is invoked
758      * @param whereClause where NXQL where clause to get the document
759      * @throws DocumentException
760      */
761     @Override
762     public String getDocURI(DocumentWrapper<DocumentModel> wrappedDoc) throws ClientException {
763         DocumentModel docModel = wrappedDoc.getWrappedObject();
764         String uri = (String)docModel.getProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
765                         CollectionSpaceClient.COLLECTIONSPACE_CORE_URI);
766         return uri;
767     }
768
769     /*
770      * See CSPACE-5036 - How to make CMISQL queries from Nuxeo
771      */
772         private IterableQueryResult makeCMISQLQuery(RepositoryInstance repoSession, String query, QueryContext queryContext) {
773                 IterableQueryResult result = null;
774                 
775                 // the NuxeoRepository should be constructed only once, then cached
776                 // (its construction is expensive)
777                 try {
778                         NuxeoRepository repo = new NuxeoRepository(
779                                         repoSession.getRepositoryName(), repoSession
780                                                         .getRootDocument().getId());
781                         logger.debug("Repository ID:" + repo.getId() + " Root folder:"
782                                         + repo.getRootFolderId());
783
784                         CallContextImpl callContext = new CallContextImpl(
785                                         CallContext.BINDING_LOCAL, repo.getId(), false);
786                         callContext.put(CallContext.USERNAME, repoSession.getPrincipal()
787                                         .getName());
788                         NuxeoCmisService cmisService = new NuxeoCmisService(repo,
789                                         callContext, repoSession);
790
791                         result = repoSession.queryAndFetch(query, "CMISQL", cmisService);
792                 } catch (ClientException e) {
793                         // TODO Auto-generated catch block
794                         logger.error("Encounter trouble making the following CMIS query: " + query, e);
795                 }
796                 
797                 return result;
798         }
799      
800     /**
801      * getFiltered get all documents for an entity service from the Document repository,
802      * given filter parameters specified by the handler. 
803      * @param ctx service context under which this method is invoked
804      * @param handler should be used by the caller to provide and transform the document
805      * @throws DocumentNotFoundException if workspace not found
806      * @throws DocumentException
807      */
808     @Override
809     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
810             throws DocumentNotFoundException, DocumentException {
811
812         DocumentFilter filter = handler.getDocumentFilter();
813         String oldOrderBy = filter.getOrderByClause();
814         if (isClauseEmpty(oldOrderBy) == true){
815             filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED);
816         }
817         QueryContext queryContext = new QueryContext(ctx, handler);
818
819         RepositoryInstance repoSession = null;
820         try {
821             handler.prepare(Action.GET_ALL);
822             repoSession = getRepositorySession(ctx); //Need a refcount here for the repository session?
823             
824             DocumentModelList docList = null;
825             String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
826
827             if (logger.isDebugEnabled()) {
828                 logger.debug("Executing NXQL query: " + query.toString());
829             }
830
831             // If we have limit and/or offset, then pass true to get totalSize
832             // in returned DocumentModelList.
833                 Profiler profiler = new Profiler(this, 2);
834                 profiler.log("Executing NXQL query: " + query.toString());
835                 profiler.start();
836                 if (handler.isCMISQuery() == true) {
837                         docList = getFilteredCMIS(repoSession, ctx, handler, queryContext); //FIXME: REM - Need to deal with paging info in CMIS query
838                 } else if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
839                 docList = repoSession.query(query, null,
840                         queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
841             } else {
842                 docList = repoSession.query(query);
843             }
844             profiler.stop();
845
846             //set repoSession to handle the document
847             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
848             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
849             handler.handle(Action.GET_ALL, wrapDoc);
850             handler.complete(Action.GET_ALL, wrapDoc);
851         } catch (DocumentException de) {
852             throw de;
853         } catch (Exception e) {
854             if (logger.isDebugEnabled()) {
855                 logger.debug("Caught exception ", e);
856             }
857             throw new DocumentException(e);
858         } finally {
859             if (repoSession != null) {
860                 releaseRepositorySession(ctx, repoSession);
861             }
862         }
863     }
864
865     private DocumentModelList getFilteredCMIS(RepositoryInstance repoSession, ServiceContext ctx, DocumentHandler handler, QueryContext queryContext)
866             throws DocumentNotFoundException, DocumentException {
867
868         DocumentModelList result = new DocumentModelListImpl();
869         try {
870             String query = handler.getCMISQuery(queryContext);
871
872                 DocumentFilter docFilter = handler.getDocumentFilter();
873             int pageSize = docFilter.getPageSize();
874             int offset = docFilter.getOffset();
875             if (logger.isDebugEnabled()) {
876                 logger.debug("Executing CMIS query: " + query.toString()
877                                 + "with pageSize: "+pageSize+" at offset: "+offset);
878             }
879
880             // If we have limit and/or offset, then pass true to get totalSize
881             // in returned DocumentModelList.
882                 Profiler profiler = new Profiler(this, 2);
883                 profiler.log("Executing CMIS query: " + query.toString());
884                 profiler.start();
885                 //
886                 IterableQueryResult queryResult = makeCMISQLQuery(repoSession, query, queryContext);
887                 try {
888                 int totalSize = (int)queryResult.size();
889                 ((DocumentModelListImpl)result).setTotalSize(totalSize);
890                                 // Skip the rows before our offset
891                         if(offset>0) {
892                                 queryResult.skipTo(offset);
893                         }
894                         int nRows = 0;
895                         for (Map<String, Serializable> row : queryResult) {
896                                 logger.debug(""
897                                                 //                                      + " dc:title is: " + (String)row.get("dc:title")
898                                                 + " Hierarchy Table ID is:" + row.get(IQueryManager.CMIS_TARGET_NUXEO_ID)
899                                                 + " cmis:name is: " + row.get(IQueryManager.CMIS_TARGET_NAME)
900                                                 //                                      + " nuxeo:lifecycleState is: " + row.get("nuxeo:lifecycleState")
901                                                 );
902                                 String nuxeoId = (String) row.get(IQueryManager.CMIS_TARGET_NUXEO_ID);
903                                 DocumentModel docModel = NuxeoUtils.getDocumentModel(repoSession, nuxeoId);
904                                 result.add(docModel);
905                                 nRows++;
906                                 if(nRows >= pageSize) {
907                                         logger.debug("Got page full of items - quitting");
908                                         break;
909                                 }
910                         }
911                 } finally {
912                         queryResult.close();
913                 }
914                 //
915             profiler.stop();
916
917         } catch (Exception e) {
918             if (logger.isDebugEnabled()) {
919                 logger.debug("Caught exception ", e);
920             }
921             throw new DocumentException(e);
922         }
923         
924         //
925         // Since we're not supporting paging yet for CMIS queries, we need to perform
926         // a workaround for the paging information we return in our list of results
927         //
928         /*
929         if (result != null) {
930                 docFilter.setStartPage(0);
931                 if (totalSize > docFilter.getPageSize()) {
932                         docFilter.setPageSize(totalSize);
933                 ((DocumentModelListImpl)result).setTotalSize(totalSize);
934                 }
935         }
936         */
937         
938         return result;
939     }
940     
941     private String logException(Exception e, String msg) {
942         String result = null;
943         
944         String exceptionMessage = e.getMessage();
945         exceptionMessage = exceptionMessage != null ? exceptionMessage : "<No details provided>";
946         result = msg = msg + ". Caught exception:" + exceptionMessage;
947         
948         if (logger.isTraceEnabled() == true) {
949                 logger.error(msg, e);
950         } else {
951                 logger.error(msg);
952         }
953         
954         return result;
955     }
956     
957     /**
958      * update given document in the Nuxeo repository
959      *
960      * @param ctx service context under which this method is invoked
961      * @param id
962      *            of the document
963      * @param handler
964      *            should be used by the caller to provide and transform the
965      *            document
966      * @throws DocumentException
967      */
968     @Override
969     public void update(ServiceContext ctx, String csid, DocumentHandler handler)
970             throws BadRequestException, DocumentNotFoundException,
971             DocumentException {
972         if (handler == null) {
973             throw new IllegalArgumentException(
974                     "RepositoryJavaClient.update: document handler is missing.");
975         }
976         
977         RepositoryInstance repoSession = null;
978         try {
979             handler.prepare(Action.UPDATE);
980             repoSession = getRepositorySession(ctx);
981             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
982             DocumentModel doc = null;
983             try {
984                 doc = repoSession.getDocument(docRef);
985             } catch (ClientException ce) {
986                 String msg = logException(ce, "Could not find document to update with CSID=" + csid);
987                 throw new DocumentNotFoundException(msg, ce);
988             }
989             //
990             // Set reposession to handle the document
991             //
992             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
993             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
994             handler.handle(Action.UPDATE, wrapDoc);
995             repoSession.saveDocument(doc);
996             repoSession.save();
997             handler.complete(Action.UPDATE, wrapDoc);
998         } catch (BadRequestException bre) {
999             throw bre;
1000         } catch (DocumentException de) {
1001             throw de;
1002         } catch (WebApplicationException wae){
1003             throw wae;
1004         } catch (Exception e) {
1005             if (logger.isDebugEnabled()) {
1006                 logger.debug("Caught exception ", e);
1007             }
1008             throw new DocumentException(e);
1009         } finally {
1010             if (repoSession != null) {
1011                 releaseRepositorySession(ctx, repoSession);
1012             }
1013         }
1014     }
1015     
1016     /**
1017      * Save a documentModel to the Nuxeo repository.
1018      * @param ctx service context under which this method is invoked
1019      * @param docModel the document to save
1020      * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
1021      * @throws DocumentException
1022      */
1023     public void saveDocWithoutHandlerProcessing(
1024             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1025             RepositoryInstance repoSession,
1026             DocumentModel docModel,
1027             boolean fSaveSession)
1028             throws ClientException, DocumentException {
1029
1030         try {
1031             repoSession.saveDocument(docModel);
1032             if (fSaveSession) {
1033                 repoSession.save();
1034             }
1035         } catch (ClientException ce) {
1036             throw ce;
1037         } catch (Exception e) {
1038             if (logger.isDebugEnabled()) {
1039                 logger.debug("Caught exception ", e);
1040             }
1041             throw new DocumentException(e);
1042         }
1043     }
1044
1045
1046     /**
1047      * Save a list of documentModels to the Nuxeo repository.
1048      * 
1049      * @param ctx service context under which this method is invoked
1050      * @param docModel the document to save
1051      * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
1052      * @throws DocumentException
1053      */
1054     public void saveDocListWithoutHandlerProcessing(
1055             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1056             RepositoryInstance repoSession,
1057             DocumentModelList docList, 
1058             boolean fSaveSession)
1059             throws ClientException, DocumentException {
1060         try {
1061             repoSession = getRepositorySession(ctx);
1062             DocumentModel[] docModelArray = new DocumentModel[docList.size()];
1063             repoSession.saveDocuments(docList.toArray(docModelArray));
1064             if (fSaveSession) {
1065                 repoSession.save();
1066             }
1067         } catch (ClientException ce) {
1068             throw ce;
1069         } catch (Exception e) {
1070             logger.error("Caught exception ", e);
1071             throw new DocumentException(e);
1072         }
1073     }
1074
1075     /**
1076      * delete a document from the Nuxeo repository
1077      * @param ctx service context under which this method is invoked
1078      * @param id
1079      *            of the document
1080      * @throws DocumentException
1081      */
1082     @Override
1083     public void delete(ServiceContext ctx, String id, DocumentHandler handler) throws DocumentNotFoundException,
1084             DocumentException {
1085         if (ctx == null) {
1086             throw new IllegalArgumentException(
1087                     "delete(ctx, ix, handler): ctx is missing");
1088         }
1089         if (handler == null) {
1090             throw new IllegalArgumentException(
1091                     "delete(ctx, ix, handler): handler is missing");
1092         }
1093         if (logger.isDebugEnabled()) {
1094             logger.debug("Deleting document with CSID=" + id);
1095         }
1096         RepositoryInstance repoSession = null;
1097         try {
1098                 handler.prepare(Action.DELETE);
1099             repoSession = getRepositorySession(ctx);
1100             DocumentWrapper<DocumentModel> wrapDoc = null;
1101             try {
1102                 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
1103                     wrapDoc = new DocumentWrapperImpl<DocumentModel>(repoSession.getDocument(docRef));
1104                     ((DocumentModelHandler) handler).setRepositorySession(repoSession);
1105                     handler.handle(Action.DELETE, wrapDoc);
1106                 repoSession.removeDocument(docRef);
1107             } catch (ClientException ce) {
1108                 String msg = logException(ce, "Could not find document to delete with CSID=" + id);
1109                 throw new DocumentNotFoundException(msg, ce);
1110             }
1111             repoSession.save();
1112             handler.complete(Action.DELETE, wrapDoc);
1113         } catch (DocumentException de) {
1114             throw de;
1115         } catch (Exception e) {
1116             if (logger.isDebugEnabled()) {
1117                 logger.debug("Caught exception ", e);
1118             }
1119             throw new DocumentException(e);
1120         } finally {
1121             if (repoSession != null) {
1122                 releaseRepositorySession(ctx, repoSession);
1123             }
1124         }
1125     }
1126
1127     /* (non-Javadoc)
1128      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
1129      */
1130     @Override
1131     @Deprecated
1132     public void delete(@SuppressWarnings("rawtypes") ServiceContext ctx, String id)
1133             throws DocumentNotFoundException, DocumentException {
1134         throw new UnsupportedOperationException();
1135         // Use the other delete instead
1136     }
1137
1138     @Override
1139     public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
1140         return NuxeoConnectorEmbedded.getInstance().retrieveWorkspaceIds(domainName);
1141     }
1142
1143     @Override
1144     public String createDomain(String domainName) throws Exception {
1145         RepositoryInstance repoSession = null;
1146         String domainId = null;
1147         try {
1148                 //
1149                 // First create the top-level domain directory
1150                 //
1151             repoSession = getRepositorySession(null);
1152             DocumentRef parentDocRef = new PathRef("/");
1153             DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
1154             DocumentModel domainDoc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1155                     domainName, NUXEO_CORE_TYPE_DOMAIN);
1156             domainDoc.setPropertyValue("dc:title", domainName);
1157             domainDoc.setPropertyValue("dc:description", "A CollectionSpace domain "
1158                     + domainName);
1159             domainDoc = repoSession.createDocument(domainDoc);
1160             domainId = domainDoc.getId();
1161             repoSession.save();
1162             //
1163             // Next, create a "Workspaces" root directory to contain the workspace folders for the individual service documents
1164             //
1165             DocumentModel workspacesRoot = repoSession.createDocumentModel(domainDoc.getPathAsString(),
1166                         NuxeoUtils.Workspaces, NUXEO_CORE_TYPE_WORKSPACEROOT);
1167             workspacesRoot.setPropertyValue("dc:title", NuxeoUtils.Workspaces);
1168             workspacesRoot.setPropertyValue("dc:description", "A CollectionSpace workspaces directory for "
1169                     + domainDoc.getPathAsString());
1170             workspacesRoot = repoSession.createDocument(workspacesRoot);
1171             String workspacesRootId = workspacesRoot.getId();
1172             repoSession.save();
1173             
1174             if (logger.isDebugEnabled()) {
1175                 logger.debug("Created tenant domain name=" + domainName
1176                         + " id=" + domainId + " " +
1177                         NuxeoUtils.Workspaces + " id=" + workspacesRootId);
1178                 logger.debug("Path to Domain: "+domainDoc.getPathAsString());
1179                 logger.debug("Path to Workspaces root: "+workspacesRoot.getPathAsString());
1180             }
1181         } catch (Exception e) {
1182             if (logger.isDebugEnabled()) {
1183                 logger.debug("Could not create tenant domain name=" + domainName + " caught exception ", e);
1184             }
1185             throw e;
1186         } finally {
1187             if (repoSession != null) {
1188                 releaseRepositorySession(null, repoSession);
1189             }
1190         }
1191         
1192         return domainId;
1193     }
1194
1195     @Override
1196     public String getDomainId(String domainName) throws Exception {
1197         String domainId = null;
1198         RepositoryInstance repoSession = null;
1199         
1200         if (domainName != null && !domainName.isEmpty()) {
1201                 try {
1202                     repoSession = getRepositorySession(null);
1203                     DocumentRef docRef = new PathRef(
1204                             "/" + domainName);
1205                     DocumentModel domain = repoSession.getDocument(docRef);
1206                     domainId = domain.getId();
1207                 } catch (Exception e) {
1208                     if (logger.isTraceEnabled()) {
1209                         logger.trace("Caught exception ", e);
1210                     }
1211                     //there is no way to identify if document does not exist due to
1212                     //lack of typed exception for getDocument method
1213                     return null;
1214                 } finally {
1215                     if (repoSession != null) {
1216                         releaseRepositorySession(null, repoSession);
1217                     }
1218                 }
1219         }
1220         
1221         return domainId;
1222     }
1223
1224     /*
1225          * Returns the workspaces root directory for a given domain.
1226          */
1227         private DocumentModel getWorkspacesRoot(RepositoryInstance repoSession,
1228                         String domainName) throws Exception {
1229                 DocumentModel result = null;
1230                 
1231                 String domainPath = "/" + domainName;
1232                 DocumentRef parentDocRef = new PathRef(domainPath);
1233                 DocumentModelList domainChildrenList = repoSession.getChildren(
1234                                 parentDocRef);
1235                 Iterator<DocumentModel> witer = domainChildrenList.iterator();
1236                 while (witer.hasNext()) {
1237                         DocumentModel childNode = witer.next();
1238                         if (NuxeoUtils.Workspaces.equalsIgnoreCase(childNode.getName())) {
1239                                 result = childNode;
1240                                 logger.trace("Found workspaces directory at: " + result.getPathAsString());
1241                                 break;
1242                         }
1243                 }
1244                 
1245                 if (result == null) {
1246                         throw new ClientException("Could not find workspace root directory in: "
1247                                         + domainPath);
1248                 }
1249
1250                 return result;
1251         }
1252     
1253     /* (non-Javadoc)
1254      * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
1255      */
1256     @Override
1257     public String createWorkspace(String domainName, String workspaceName) throws Exception {
1258         RepositoryInstance repoSession = null;
1259         String workspaceId = null;
1260         try {
1261             repoSession = getRepositorySession(null);
1262             DocumentModel parentDoc = getWorkspacesRoot(repoSession, domainName);            
1263             DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1264                     workspaceName, NuxeoUtils.WORKSPACE_DOCUMENT_TYPE);
1265             doc.setPropertyValue("dc:title", workspaceName);
1266             doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
1267                     + workspaceName);
1268             doc = repoSession.createDocument(doc);
1269             workspaceId = doc.getId();
1270             repoSession.save();
1271             if (logger.isDebugEnabled()) {
1272                 logger.debug("Created workspace name=" + workspaceName
1273                         + " id=" + workspaceId);
1274             }
1275         } catch (Exception e) {
1276             if (logger.isDebugEnabled()) {
1277                 logger.debug("createWorkspace caught exception ", e);
1278             }
1279             throw e;
1280         } finally {
1281             if (repoSession != null) {
1282                 releaseRepositorySession(null, repoSession);
1283             }
1284         }
1285         return workspaceId;
1286     }
1287
1288     /* (non-Javadoc)
1289      * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
1290      */
1291     @Override
1292     public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
1293         String workspaceId = null;
1294         
1295         RepositoryInstance repoSession = null;
1296         try {
1297             repoSession = getRepositorySession(null);
1298             DocumentRef docRef = new PathRef(
1299                     "/" + tenantDomain
1300                     + "/" + NuxeoUtils.Workspaces
1301                     + "/" + workspaceName);
1302             DocumentModel workspace = repoSession.getDocument(docRef);
1303             workspaceId = workspace.getId();
1304         } catch (DocumentException de) {
1305             throw de;
1306         } catch (Exception e) {
1307             if (logger.isDebugEnabled()) {
1308                 logger.debug("Caught exception ", e);
1309             }
1310             throw new DocumentException(e);
1311         } finally {
1312             if (repoSession != null) {
1313                 releaseRepositorySession(null, repoSession);
1314             }
1315         }
1316         
1317         return workspaceId;
1318     }
1319
1320
1321     /**
1322      * Gets the repository session. - Package access only.
1323      *
1324      * @return the repository session
1325      * @throws Exception the exception
1326      */
1327     public RepositoryInstance getRepositorySession(ServiceContext ctx) throws Exception {
1328         RepositoryInstance repoSession = null;
1329         
1330         Profiler profiler = new Profiler("getRepositorySession():", 2);
1331         profiler.start();
1332                 
1333         if (ctx != null) {
1334                 repoSession = (RepositoryInstance)ctx.getCurrentRepositorySession();
1335             if (logger.isDebugEnabled() == true) {
1336                 if (repoSession != null) {
1337                         logger.warn("Reusing the current context's repository session.");
1338                 }
1339             }           
1340         }
1341         
1342         // If we couldn't find a repoSession from the service context then we need to create a new one
1343         if (repoSession == null) {
1344                 NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1345                 repoSession = client.openRepository();
1346         }
1347         
1348         if (logger.isTraceEnabled()) {
1349             logger.trace("Testing call to getRepository() repository root: " + repoSession.getRootDocument());
1350         }
1351         
1352         profiler.stop();
1353         
1354         if (ctx != null) {
1355                 ctx.setCurrentRepositorySession(repoSession); // For reusing, save the repository session in the current service context
1356         }
1357         
1358         return repoSession;
1359     }
1360
1361     /**
1362      * Release repository session. - Package access only.
1363      *
1364      * @param repoSession the repo session
1365      */
1366     public void releaseRepositorySession(ServiceContext ctx, RepositoryInstance repoSession) {
1367         try {
1368             NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1369             // release session
1370             if (ctx != null) {
1371                 ctx.clearCurrentRepositorySession(); //clear the current context of the now closed repo session
1372                 if (ctx.getCurrentRepositorySession() == null) {
1373                     client.releaseRepository(repoSession); //release the repo session if the service context's ref count is zeo.
1374                 }
1375             } else {
1376                 client.releaseRepository(repoSession); //repo session was acquired without a service context
1377             }
1378         } catch (Exception e) {
1379             logger.error("Could not close the repository session", e);
1380             // no need to throw this service specific exception
1381         }
1382     }
1383
1384         @Override
1385         public void doWorkflowTransition(ServiceContext ctx, String id,
1386                         DocumentHandler handler, TransitionDef transitionDef)
1387                         throws BadRequestException, DocumentNotFoundException,
1388                         DocumentException {
1389                 // 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
1390         }
1391
1392 }