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