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