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