]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
20bcf75e204916b90d194a42468be5756ff1186a
[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.util.Hashtable;
21 import java.util.List;
22 import java.util.UUID;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 import java.util.regex.PatternSyntaxException;
26
27 import javax.ws.rs.WebApplicationException;
28 import javax.ws.rs.core.MultivaluedMap;
29
30 import org.collectionspace.services.client.IQueryManager;
31 import org.collectionspace.services.client.PoxPayloadIn;
32 import org.collectionspace.services.client.PoxPayloadOut;
33 import org.collectionspace.services.client.workflow.WorkflowClient;
34 import org.collectionspace.services.common.context.ServiceContext;
35 import org.collectionspace.services.common.datetime.GregorianCalendarDateTimeUtils;
36 import org.collectionspace.services.common.query.QueryContext;
37 import org.collectionspace.services.common.repository.RepositoryClient;
38 import org.collectionspace.services.common.workflow.service.nuxeo.WorkflowDocumentModelHandler;
39 import org.collectionspace.services.common.profile.Profiler;
40 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
41
42 import org.collectionspace.services.common.document.BadRequestException;
43 import org.collectionspace.services.common.document.DocumentException;
44 import org.collectionspace.services.common.document.DocumentFilter;
45 import org.collectionspace.services.common.document.DocumentHandler;
46 import org.collectionspace.services.common.document.DocumentNotFoundException;
47 import org.collectionspace.services.common.document.DocumentHandler.Action;
48 import org.collectionspace.services.common.document.DocumentWrapper;
49 import org.collectionspace.services.common.document.DocumentWrapperImpl;
50
51 import org.nuxeo.common.utils.IdUtils;
52 import org.nuxeo.ecm.core.api.ClientException;
53 import org.nuxeo.ecm.core.api.DocumentModel;
54 import org.nuxeo.ecm.core.api.DocumentModelList;
55 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
56 import org.nuxeo.ecm.core.api.DocumentRef;
57 import org.nuxeo.ecm.core.api.IdRef;
58 import org.nuxeo.ecm.core.api.PathRef;
59 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
60 import org.nuxeo.ecm.core.client.NuxeoClient;
61
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65 /**
66  * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
67  * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
68  * with the client.
69  * 
70  * $LastChangedRevision: $ $LastChangedDate: $
71  */
72 public class RepositoryJavaClientImpl implements RepositoryClient<PoxPayloadIn, PoxPayloadOut> {
73
74     /** The logger. */
75     private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
76 //    private final Logger profilerLogger = LoggerFactory.getLogger("remperf");
77 //    private String foo = Profiler.createLogger();
78
79     /**
80      * Instantiates a new repository java client impl.
81      */
82     public RepositoryJavaClientImpl() {
83         //Empty constructor
84         
85     }
86
87     public void assertWorkflowState(ServiceContext ctx,
88                 DocumentModel docModel) throws DocumentNotFoundException, ClientException {
89         MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
90         if (queryParams != null) {
91                 //
92                 // Look for the workflow "delete" query param and see if we need to assert that the
93                 // docModel is in a non-deleted workflow state.
94                 //
95                 String currentState = docModel.getCurrentLifeCycleState();
96                 String includeDeletedStr = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
97                 boolean includeDeleted = includeDeletedStr == null ? true : Boolean.parseBoolean(includeDeletedStr);
98                 if (includeDeleted == false) {
99                         //
100                         // We don't wanted soft-deleted object, so throw an exception if this one is soft-deleted.
101                         //
102                         if (currentState.equalsIgnoreCase(WorkflowClient.WORKFLOWSTATE_DELETED)) {
103                                 String msg = "GET assertion that docModel not be in 'deleted' workflow state failed.";
104                                 logger.debug(msg);
105                                 throw new DocumentNotFoundException(msg);
106                         }
107                 }
108         }
109     }
110     
111     /**
112      * create document in the Nuxeo repository
113      *
114      * @param ctx service context under which this method is invoked
115      * @param handler
116      *            should be used by the caller to provide and transform the
117      *            document
118      * @return id in repository of the newly created document
119      * @throws DocumentException
120      */
121     @Override
122     public String create(ServiceContext ctx,
123             DocumentHandler handler) throws BadRequestException,
124             DocumentException {
125
126         if (ctx.getDocumentType() == null) {
127             throw new IllegalArgumentException(
128                     "RepositoryJavaClient.create: docType is missing");
129         }
130         if (handler == null) {
131             throw new IllegalArgumentException(
132                     "RepositoryJavaClient.create: handler is missing");
133         }
134         String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
135         if (nuxeoWspaceId == null) {
136             throw new DocumentNotFoundException(
137                     "Unable to find workspace for service " + ctx.getServiceName()
138                     + " check if the workspace exists in the Nuxeo repository");
139         }
140         RepositoryInstance repoSession = null;
141         try {
142             handler.prepare(Action.CREATE);
143             repoSession = getRepositorySession();
144             DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
145             DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
146             String wspacePath = wspaceDoc.getPathAsString();
147             //give our own ID so PathRef could be constructed later on
148             String id = IdUtils.generateId(UUID.randomUUID().toString());
149             // create document model
150             DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
151                     ctx.getDocumentType());
152             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
153             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
154             handler.handle(Action.CREATE, wrapDoc);
155             // create document with documentmodel
156             doc = repoSession.createDocument(doc);
157             repoSession.save();
158 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
159 // and assume the handler has the state it needs (doc fragments). 
160             handler.complete(Action.CREATE, wrapDoc);
161             return id;
162         } catch (BadRequestException bre) {
163             throw bre;
164         } catch (Exception e) {
165             if (logger.isDebugEnabled()) {
166                 logger.debug("Caught exception ", e);
167             }
168             throw new DocumentException(e);
169         } finally {
170             if (repoSession != null) {
171                 releaseRepositorySession(repoSession);
172             }
173         }
174
175     }
176
177     /**
178      * get document from the Nuxeo repository
179      * @param ctx service context under which this method is invoked
180      * @param id
181      *            of the document to retrieve
182      * @param handler
183      *            should be used by the caller to provide and transform the
184      *            document
185      * @throws DocumentException
186      */
187     @Override
188     public void get(ServiceContext ctx, String id, DocumentHandler handler)
189             throws DocumentNotFoundException, DocumentException {
190
191         if (handler == null) {
192             throw new IllegalArgumentException(
193                     "RepositoryJavaClient.get: handler is missing");
194         }
195         RepositoryInstance repoSession = null;
196
197         try {
198             handler.prepare(Action.GET);
199             repoSession = getRepositorySession();
200             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
201             DocumentModel docModel = null;
202             try {
203                 docModel = repoSession.getDocument(docRef);
204                 assertWorkflowState(ctx, docModel);
205             } catch (ClientException ce) {
206                 String msg = "Could not find document with id=" + id;
207                 logger.error(msg, ce);
208                 throw new DocumentNotFoundException(msg, ce);
209             }
210             //set reposession to handle the document
211             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
212             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(docModel);
213             handler.handle(Action.GET, wrapDoc);
214             handler.complete(Action.GET, wrapDoc);
215         } catch (IllegalArgumentException iae) {
216             throw iae;
217         } catch (DocumentException de) {
218             throw de;
219         } catch (Exception e) {
220             if (logger.isDebugEnabled()) {
221                 logger.debug("Caught exception ", e);
222             }
223             throw new DocumentException(e);
224         } finally {
225             if (repoSession != null) {
226                 releaseRepositorySession(repoSession);
227             }
228         }
229     }
230
231     /**
232      * get document from the Nuxeo repository, using the docFilter params.
233      * @param ctx service context under which this method is invoked
234      * @param handler
235      *            should be used by the caller to provide and transform the
236      *            document. Handler must have a docFilter set to return a single item.
237      * @throws DocumentException
238      */
239     @Override
240     public void get(ServiceContext ctx, DocumentHandler handler)
241             throws DocumentNotFoundException, DocumentException {
242         QueryContext queryContext = new QueryContext(ctx, handler);
243         RepositoryInstance repoSession = null;
244
245         try {
246             handler.prepare(Action.GET);
247             repoSession = getRepositorySession();
248
249             DocumentModelList docList = null;
250             // force limit to 1, and ignore totalSize
251             String query = NuxeoUtils.buildNXQLQuery(queryContext);
252             docList = repoSession.query(query, null, 1, 0, false);
253             if (docList.size() != 1) {
254                 throw new DocumentNotFoundException("No document found matching filter params.");
255             }
256             DocumentModel doc = docList.get(0);
257
258             if (logger.isDebugEnabled()) {
259                 logger.debug("Executed NXQL query: " + query);
260             }
261
262             //set reposession to handle the document
263             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
264             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
265             handler.handle(Action.GET, wrapDoc);
266             handler.complete(Action.GET, wrapDoc);
267         } catch (IllegalArgumentException iae) {
268             throw iae;
269         } catch (DocumentException de) {
270             throw de;
271         } catch (Exception e) {
272             if (logger.isDebugEnabled()) {
273                 logger.debug("Caught exception ", e);
274             }
275             throw new DocumentException(e);
276         } finally {
277             if (repoSession != null) {
278                 releaseRepositorySession(repoSession);
279             }
280         }
281     }
282     
283     /**
284      * Get wrapped documentModel from the Nuxeo repository.  The search is restricted to the workspace
285      * of the current context.
286      * 
287      * @param ctx service context under which this method is invoked
288      * @param id
289      *            of the document to retrieve
290      * @throws DocumentException
291      */
292     @Override
293     public DocumentWrapper<DocumentModel> getDoc(
294             ServiceContext ctx, String csid)
295             throws DocumentNotFoundException, DocumentException {
296         RepositoryInstance repoSession = null;
297         DocumentWrapper<DocumentModel> wrapDoc = null;
298
299         try {
300             repoSession = getRepositorySession();
301             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
302             DocumentModel doc = null;
303             try {
304                 doc = repoSession.getDocument(docRef);
305             } catch (ClientException ce) {
306                 String msg = "could not find document with id=" + csid;
307                 logger.error(msg, ce);
308                 throw new DocumentNotFoundException(msg, ce);
309             }
310             wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
311         } catch (IllegalArgumentException iae) {
312             throw iae;
313         } catch (DocumentException de) {
314             throw de;
315         } catch (Exception e) {
316             if (logger.isDebugEnabled()) {
317                 logger.debug("Caught exception ", e);
318             }
319             throw new DocumentException(e);
320         } finally {
321             if (repoSession != null) {
322                 releaseRepositorySession(repoSession);
323             }
324         }
325         return wrapDoc;
326     }
327
328     /**
329      * find wrapped documentModel from the Nuxeo repository
330      * @param ctx service context under which this method is invoked
331      * @param whereClause where NXQL where clause to get the document
332      * @throws DocumentException
333      */
334     @Override
335     public DocumentWrapper<DocumentModel> findDoc(
336             ServiceContext ctx, String whereClause)
337             throws DocumentNotFoundException, DocumentException {
338         RepositoryInstance repoSession = null;
339         DocumentWrapper<DocumentModel> wrapDoc = null;
340
341         try {
342             QueryContext queryContext = new QueryContext(ctx, whereClause);
343             repoSession = getRepositorySession();
344             DocumentModelList docList = null;
345             // force limit to 1, and ignore totalSize
346             String query = NuxeoUtils.buildNXQLQuery(queryContext);
347             docList = repoSession.query(query,
348                     null, //Filter
349                     1, //limit
350                     0, //offset
351                     false); //countTotal
352             if (docList.size() != 1) {
353                 if (logger.isDebugEnabled()) {
354                     logger.debug("findDoc: Query found: " + docList.size() + " items.");
355                     logger.debug(" Query: " + query);
356                 }
357                 throw new DocumentNotFoundException("No document found matching filter params.");
358             }
359             DocumentModel doc = docList.get(0);
360             wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
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(repoSession);
373             }
374         }
375         return wrapDoc;
376     }
377
378     /**
379      * find doc and return CSID from the Nuxeo repository
380      * @param ctx service context under which this method is invoked
381      * @param whereClause where NXQL where clause to get the document
382      * @throws DocumentException
383      */
384     @Override
385     public String findDocCSID(
386             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String whereClause)
387             throws DocumentNotFoundException, DocumentException {
388         String csid = null;
389         try {
390             DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
391             DocumentModel docModel = wrapDoc.getWrappedObject();
392             csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
393         } catch (DocumentNotFoundException dnfe) {
394             throw dnfe;
395         } catch (IllegalArgumentException iae) {
396             throw iae;
397         } catch (DocumentException de) {
398             throw de;
399         } catch (Exception e) {
400             if (logger.isDebugEnabled()) {
401                 logger.debug("Caught exception ", e);
402             }
403             throw new DocumentException(e);
404         }
405         return csid;
406     }
407
408     /**
409      * Find a list of documentModels from the Nuxeo repository
410      * @param docTypes a list of DocType names to match
411      * @param  whereClause where the clause to qualify on
412      * @return
413      */
414     @Override
415     public DocumentWrapper<DocumentModelList> findDocs(
416             ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
417             List<String> docTypes,
418             String whereClause,
419             int pageSize, int pageNum, boolean computeTotal)
420             throws DocumentNotFoundException, DocumentException {
421         RepositoryInstance repoSession = null;
422         DocumentWrapper<DocumentModelList> wrapDoc = null;
423
424         try {
425             if (docTypes == null || docTypes.size() < 1) {
426                 throw new DocumentNotFoundException(
427                         "findDocs must specify at least one DocumentType.");
428             }
429             repoSession = getRepositorySession();
430             DocumentModelList docList = null;
431             // force limit to 1, and ignore totalSize
432             QueryContext queryContext = new QueryContext(ctx, whereClause);
433             String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
434             docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
435             wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
436         } catch (IllegalArgumentException iae) {
437             throw iae;
438         } catch (Exception e) {
439             if (logger.isDebugEnabled()) {
440                 logger.debug("Caught exception ", e);
441             }
442             throw new DocumentException(e);
443         } finally {
444             if (repoSession != null) {
445                 releaseRepositorySession(repoSession);
446             }
447         }
448         return wrapDoc;
449     }
450
451     /* (non-Javadoc)
452      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
453      */
454     @Override
455     public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
456             throws DocumentNotFoundException, DocumentException {
457         if (handler == null) {
458             throw new IllegalArgumentException(
459                     "RepositoryJavaClient.getAll: handler is missing");
460         }
461
462         RepositoryInstance repoSession = null;
463
464         try {
465             handler.prepare(Action.GET_ALL);
466             repoSession = getRepositorySession();
467             DocumentModelList docModelList = new DocumentModelListImpl();
468             //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
469             for (String csid : csidList) {
470                 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
471                 DocumentModel docModel = repoSession.getDocument(docRef);
472                 docModelList.add(docModel);
473             }
474
475             //set reposession to handle the document
476             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
477             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
478             handler.handle(Action.GET_ALL, wrapDoc);
479             handler.complete(Action.GET_ALL, wrapDoc);
480         } catch (DocumentException de) {
481             throw de;
482         } catch (Exception e) {
483             if (logger.isDebugEnabled()) {
484                 logger.debug("Caught exception ", e);
485             }
486             throw new DocumentException(e);
487         } finally {
488             if (repoSession != null) {
489                 releaseRepositorySession(repoSession);
490             }
491         }
492     }
493
494     /**
495      * getAll get all documents for an entity entity service from the Nuxeo
496      * repository
497      *
498      * @param ctx service context under which this method is invoked
499      * @param handler
500      *            should be used by the caller to provide and transform the
501      *            document
502      * @throws DocumentException
503      */
504     @Override
505     public void getAll(ServiceContext ctx, DocumentHandler handler)
506             throws DocumentNotFoundException, DocumentException {
507         if (handler == null) {
508             throw new IllegalArgumentException(
509                     "RepositoryJavaClient.getAll: handler is missing");
510         }
511         String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
512         if (nuxeoWspaceId == null) {
513             throw new DocumentNotFoundException(
514                     "Unable to find workspace for service "
515                     + ctx.getServiceName()
516                     + " check if the workspace exists in the Nuxeo repository");
517         }
518         RepositoryInstance repoSession = null;
519
520         try {
521             handler.prepare(Action.GET_ALL);
522             repoSession = getRepositorySession();
523             DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
524             DocumentModelList docList = repoSession.getChildren(wsDocRef);
525             //set reposession to handle the document
526             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
527             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
528             handler.handle(Action.GET_ALL, wrapDoc);
529             handler.complete(Action.GET_ALL, wrapDoc);
530         } catch (DocumentException de) {
531             throw de;
532         } catch (Exception e) {
533             if (logger.isDebugEnabled()) {
534                 logger.debug("Caught exception ", e);
535             }
536             throw new DocumentException(e);
537         } finally {
538             if (repoSession != null) {
539                 releaseRepositorySession(repoSession);
540             }
541         }
542     }
543     
544     private boolean isClauseEmpty(String theString) {
545         boolean result = true;
546         if (theString != null && !theString.isEmpty()) {
547                 result = false;
548         }
549         return result;
550     }
551
552     /*
553      * A method to find a CollectionSpace document (of any type) given just a service context and
554      * its CSID.  A search across *all* service workspaces (within a given tenant context) is performed to find
555      * the document
556      * 
557      * This query searches Nuxeo's Hierarchy table where our CSIDs are stored in the "name" column.
558      */
559     @Override
560     public DocumentWrapper<DocumentModel> getDocFromCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
561                 String csid)
562             throws Exception {
563         DocumentWrapper<DocumentModel> result = null;
564         RepositoryInstance repoSession = getRepositorySession();
565         try {
566                 result = new DocumentWrapperImpl(NuxeoUtils.getDocFromCsid(repoSession, ctx, csid));
567         } finally {
568             if (repoSession != null) {
569                 releaseRepositorySession(repoSession);
570             }
571         }
572         return result;
573     }
574
575     /**
576      * find doc and return CSID from the Nuxeo repository
577      * @param ctx service context under which this method is invoked
578      * @param whereClause where NXQL where clause to get the document
579      * @throws DocumentException
580      */
581     @Override
582     public String getDocURI(DocumentWrapper<DocumentModel> wrappedDoc) throws ClientException {
583         DocumentModel docModel = wrappedDoc.getWrappedObject();
584         String uri = (String)docModel.getProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
585                                 DocumentModelHandler.COLLECTIONSPACE_CORE_URI);
586         return uri;
587     }
588
589
590     /**
591      * getFiltered get all documents for an entity service from the Document repository,
592      * given filter parameters specified by the handler. 
593      * @param ctx service context under which this method is invoked
594      * @param handler should be used by the caller to provide and transform the document
595      * @throws DocumentNotFoundException if workspace not found
596      * @throws DocumentException
597      */
598     @Override
599     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
600             throws DocumentNotFoundException, DocumentException {
601
602         DocumentFilter filter = handler.getDocumentFilter();
603         String oldOrderBy = filter.getOrderByClause();
604         if (isClauseEmpty(oldOrderBy) == true){
605             filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED);  //per http://issues.collectionspace.org/browse/CSPACE-705
606         }
607         QueryContext queryContext = new QueryContext(ctx, handler);
608         RepositoryInstance repoSession = null;
609         try {
610             handler.prepare(Action.GET_ALL);
611             repoSession = getRepositorySession();
612             DocumentModelList docList = null;
613             String query = NuxeoUtils.buildNXQLQuery(queryContext);
614
615             if (logger.isDebugEnabled()) {
616                 logger.debug("Executing NXQL query: " + query.toString());
617             }
618
619             // If we have limit and/or offset, then pass true to get totalSize
620             // in returned DocumentModelList.
621                 Profiler profiler = new Profiler(this, 2);
622                 profiler.log("Executing NXQL query: " + query.toString());
623                 profiler.start();
624             if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
625                 docList = repoSession.query(query, null,
626                         queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
627             } else {
628                 docList = repoSession.query(query);
629             }
630             profiler.stop();
631
632             //set repoSession to handle the document
633             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
634             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
635             handler.handle(Action.GET_ALL, wrapDoc);
636             handler.complete(Action.GET_ALL, wrapDoc);
637         } catch (DocumentException de) {
638             throw de;
639         } catch (Exception e) {
640             if (logger.isDebugEnabled()) {
641                 logger.debug("Caught exception ", e);
642             }
643             throw new DocumentException(e);
644         } finally {
645             if (repoSession != null) {
646                 releaseRepositorySession(repoSession);
647             }
648         }
649     }
650
651     /**
652      * update given document in the Nuxeo repository
653      *
654      * @param ctx service context under which this method is invoked
655      * @param id
656      *            of the document
657      * @param handler
658      *            should be used by the caller to provide and transform the
659      *            document
660      * @throws DocumentException
661      */
662     @Override
663     public void update(ServiceContext ctx, String id, DocumentHandler handler)
664             throws BadRequestException, DocumentNotFoundException,
665             DocumentException {
666         if (handler == null) {
667             throw new IllegalArgumentException(
668                     "RepositoryJavaClient.update: handler is missing");
669         }
670         RepositoryInstance repoSession = null;
671         try {
672             handler.prepare(Action.UPDATE);
673             repoSession = getRepositorySession();
674             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
675             DocumentModel doc = null;
676             try {
677                 doc = repoSession.getDocument(docRef);
678             } catch (ClientException ce) {
679                 String msg = "Could not find document to update with id=" + id;
680                 logger.error(msg, ce);
681                 throw new DocumentNotFoundException(msg, ce);
682             }
683             //set reposession to handle the document
684             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
685             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
686             handler.handle(Action.UPDATE, wrapDoc);
687             repoSession.saveDocument(doc);
688             repoSession.save();
689             handler.complete(Action.UPDATE, wrapDoc);
690         } catch (BadRequestException bre) {
691             throw bre;
692         } catch (DocumentException de) {
693             throw de;
694         } catch (WebApplicationException wae){
695             throw wae;
696         } catch (Exception e) {
697             if (logger.isDebugEnabled()) {
698                 logger.debug("Caught exception ", e);
699             }
700             throw new DocumentException(e);
701         } finally {
702             if (repoSession != null) {
703                 releaseRepositorySession(repoSession);
704             }
705         }
706     }
707
708     /**
709      * delete a document from the Nuxeo repository
710      * @param ctx service context under which this method is invoked
711      * @param id
712      *            of the document
713      * @throws DocumentException
714      */
715     @Override
716     public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
717             DocumentException {
718
719         if (logger.isDebugEnabled()) {
720             logger.debug("deleting document with id=" + id);
721         }
722         RepositoryInstance repoSession = null;
723         try {
724             repoSession = getRepositorySession();
725             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
726             try {
727                 repoSession.removeDocument(docRef);
728             } catch (ClientException ce) {
729                 String msg = "could not find document to delete with id=" + id;
730                 logger.error(msg, ce);
731                 throw new DocumentNotFoundException(msg, ce);
732             }
733             repoSession.save();
734         } catch (DocumentException de) {
735             throw de;
736         } catch (Exception e) {
737             if (logger.isDebugEnabled()) {
738                 logger.debug("Caught exception ", e);
739             }
740             throw new DocumentException(e);
741         } finally {
742             if (repoSession != null) {
743                 releaseRepositorySession(repoSession);
744             }
745         }
746     }
747
748     /* (non-Javadoc)
749      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
750      */
751     @Override
752     public void delete(ServiceContext ctx, String id, DocumentHandler handler)
753             throws DocumentNotFoundException, DocumentException {
754         throw new UnsupportedOperationException();
755     }
756
757     @Override
758     public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
759         return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
760     }
761
762     @Override
763     public String createDomain(String domainName) throws Exception {
764         RepositoryInstance repoSession = null;
765         String domainId = null;
766         try {
767             repoSession = getRepositorySession();
768             DocumentRef parentDocRef = new PathRef("/");
769             DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
770             DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
771                     domainName, "Domain");
772             doc.setPropertyValue("dc:title", domainName);
773             doc.setPropertyValue("dc:description", "A CollectionSpace domain "
774                     + domainName);
775             doc = repoSession.createDocument(doc);
776             domainId = doc.getId();
777             repoSession.save();
778             if (logger.isDebugEnabled()) {
779                 logger.debug("created tenant domain name=" + domainName
780                         + " id=" + domainId);
781             }
782         } catch (Exception e) {
783             if (logger.isDebugEnabled()) {
784                 logger.debug("createTenantSpace caught exception ", e);
785             }
786             throw e;
787         } finally {
788             if (repoSession != null) {
789                 releaseRepositorySession(repoSession);
790             }
791         }
792         return domainId;
793     }
794
795     @Override
796     public String getDomainId(String domainName) throws Exception {
797         String domainId = null;
798         RepositoryInstance repoSession = null;
799         
800         if (domainName != null && !domainName.isEmpty()) {
801                 try {
802                     repoSession = getRepositorySession();
803                     DocumentRef docRef = new PathRef(
804                             "/" + domainName);
805                     DocumentModel domain = repoSession.getDocument(docRef);
806                     domainId = domain.getId();
807                 } catch (Exception e) {
808                     if (logger.isDebugEnabled()) {
809                         logger.debug("Caught exception ", e);
810                     }
811                     //there is no way to identify if document does not exist due to
812                     //lack of typed exception for getDocument method
813                     return null;
814                 } finally {
815                     if (repoSession != null) {
816                         releaseRepositorySession(repoSession);
817                     }
818                 }
819         }
820         
821         return domainId;
822     }
823
824     /* (non-Javadoc)
825      * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
826      */
827     @Override
828     public String createWorkspace(String domainName, String workspaceName) throws Exception {
829         RepositoryInstance repoSession = null;
830         String workspaceId = null;
831         try {
832             repoSession = getRepositorySession();
833             DocumentRef parentDocRef = new PathRef(
834                     "/" + domainName
835                     + "/" + "workspaces");
836             DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
837             DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
838                     workspaceName, "Workspace");
839             doc.setPropertyValue("dc:title", workspaceName);
840             doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
841                     + workspaceName);
842             doc = repoSession.createDocument(doc);
843             workspaceId = doc.getId();
844             repoSession.save();
845             if (logger.isDebugEnabled()) {
846                 logger.debug("created workspace name=" + workspaceName
847                         + " id=" + workspaceId);
848             }
849         } catch (Exception e) {
850             if (logger.isDebugEnabled()) {
851                 logger.debug("createWorkspace caught exception ", e);
852             }
853             throw e;
854         } finally {
855             if (repoSession != null) {
856                 releaseRepositorySession(repoSession);
857             }
858         }
859         return workspaceId;
860     }
861
862     /* (non-Javadoc)
863      * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
864      */
865     @Override
866     public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
867         String workspaceId = null;
868         RepositoryInstance repoSession = null;
869         try {
870             repoSession = getRepositorySession();
871             DocumentRef docRef = new PathRef(
872                     "/" + tenantDomain
873                     + "/" + "workspaces"
874                     + "/" + workspaceName);
875             DocumentModel workspace = repoSession.getDocument(docRef);
876             workspaceId = workspace.getId();
877         } catch (DocumentException de) {
878             throw de;
879         } catch (Exception e) {
880             if (logger.isDebugEnabled()) {
881                 logger.debug("Caught exception ", e);
882             }
883             throw new DocumentException(e);
884         } finally {
885             if (repoSession != null) {
886                 releaseRepositorySession(repoSession);
887             }
888         }
889         return workspaceId;
890     }
891
892
893     /**
894      * Gets the repository session.
895      *
896      * @return the repository session
897      * @throws Exception the exception
898      */
899     private RepositoryInstance getRepositorySession() throws Exception {
900         // FIXME: is it possible to reuse repository session?
901         // Authentication failures happen while trying to reuse the session
902         Profiler profiler = new Profiler("getRepositorySession():", 2);
903         profiler.start();
904         NuxeoClient client = NuxeoConnector.getInstance().getClient();
905         RepositoryInstance repoSession = client.openRepository();
906         if (logger.isTraceEnabled()) {
907             logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
908         }
909         profiler.stop();
910         return repoSession;
911     }
912
913     /**
914      * Release repository session.
915      *
916      * @param repoSession the repo session
917      */
918     private void releaseRepositorySession(RepositoryInstance repoSession) {
919         try {
920             NuxeoClient client = NuxeoConnector.getInstance().getClient();
921             // release session
922             client.releaseRepository(repoSession);
923         } catch (Exception e) {
924             logger.error("Could not close the repository session", e);
925             // no need to throw this service specific exception
926         }
927     }
928
929 }