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