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