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