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