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