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