]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
e51105cdf5ec884c271652d76d89dba7533ec945
[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 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
230 // and assume the handler has the state it needs (doc fragments). 
231             handler.complete(Action.CREATE, wrapDoc);
232             return id;
233         } catch (BadRequestException bre) {
234             throw bre;
235         } catch (Exception e) {
236             if (logger.isDebugEnabled()) {
237                 logger.debug("Caught exception ", e);
238             }
239             throw new DocumentException(e);
240         } finally {
241             if (repoSession != null) {
242                 releaseRepositorySession(repoSession);
243             }
244         }
245
246     }
247
248     /**
249      * get document from the Nuxeo repository
250      * @param ctx service context under which this method is invoked
251      * @param id
252      *            of the document to retrieve
253      * @param handler
254      *            should be used by the caller to provide and transform the
255      *            document
256      * @throws DocumentException
257      */
258     @Override
259     public void get(ServiceContext ctx, String id, DocumentHandler handler)
260             throws DocumentNotFoundException, DocumentException {
261
262         if (handler == null) {
263             throw new IllegalArgumentException(
264                     "RepositoryJavaClient.get: handler is missing");
265         }
266         RepositoryInstance repoSession = null;
267
268         try {
269             handler.prepare(Action.GET);
270             repoSession = getRepositorySession();
271             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
272             DocumentModel doc = null;
273             try {
274                 doc = repoSession.getDocument(docRef);
275             } catch (ClientException ce) {
276                 String msg = "could not find document with id=" + id;
277                 logger.error(msg, ce);
278                 throw new DocumentNotFoundException(msg, ce);
279             }
280             //set reposession to handle the document
281             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
282             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
283             handler.handle(Action.GET, wrapDoc);
284             handler.complete(Action.GET, wrapDoc);
285         } catch (IllegalArgumentException iae) {
286             throw iae;
287         } catch (DocumentException de) {
288             throw de;
289         } catch (Exception e) {
290             if (logger.isDebugEnabled()) {
291                 logger.debug("Caught exception ", e);
292             }
293             throw new DocumentException(e);
294         } finally {
295             if (repoSession != null) {
296                 releaseRepositorySession(repoSession);
297             }
298         }
299     }
300
301     /**
302      * get document from the Nuxeo repository, using the docFilter params.
303      * @param ctx service context under which this method is invoked
304      * @param handler
305      *            should be used by the caller to provide and transform the
306      *            document. Handler must have a docFilter set to return a single item.
307      * @throws DocumentException
308      */
309     @Override
310     public void get(ServiceContext ctx, DocumentHandler handler)
311             throws DocumentNotFoundException, DocumentException {
312         QueryContext queryContext = new QueryContext(ctx, handler);
313         RepositoryInstance repoSession = null;
314
315         try {
316             handler.prepare(Action.GET);
317             repoSession = getRepositorySession();
318
319             DocumentModelList docList = null;
320             // force limit to 1, and ignore totalSize
321             String query = buildNXQLQuery(queryContext);
322             docList = repoSession.query(query, null, 1, 0, false);
323             if (docList.size() != 1) {
324                 throw new DocumentNotFoundException("No document found matching filter params.");
325             }
326             DocumentModel doc = docList.get(0);
327
328             if (logger.isDebugEnabled()) {
329                 logger.debug("Executed NXQL query: " + query);
330             }
331
332             //set reposession to handle the document
333             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
334             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
335             handler.handle(Action.GET, wrapDoc);
336             handler.complete(Action.GET, wrapDoc);
337         } catch (IllegalArgumentException iae) {
338             throw iae;
339         } catch (DocumentException de) {
340             throw de;
341         } catch (Exception e) {
342             if (logger.isDebugEnabled()) {
343                 logger.debug("Caught exception ", e);
344             }
345             throw new DocumentException(e);
346         } finally {
347             if (repoSession != null) {
348                 releaseRepositorySession(repoSession);
349             }
350         }
351     }
352
353     /**
354      * get wrapped documentModel from the Nuxeo repository
355      * @param ctx service context under which this method is invoked
356      * @param id
357      *            of the document to retrieve
358      * @throws DocumentException
359      */
360     @Override
361     public DocumentWrapper<DocumentModel> getDoc(
362             ServiceContext ctx, String id)
363             throws DocumentNotFoundException, DocumentException {
364         RepositoryInstance repoSession = null;
365         DocumentWrapper<DocumentModel> wrapDoc = null;
366
367         try {
368             repoSession = getRepositorySession();
369             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
370             DocumentModel doc = null;
371             try {
372                 doc = repoSession.getDocument(docRef);
373             } catch (ClientException ce) {
374                 String msg = "could not find document with id=" + id;
375                 logger.error(msg, ce);
376                 throw new DocumentNotFoundException(msg, ce);
377             }
378             wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
379         } catch (IllegalArgumentException iae) {
380             throw iae;
381         } catch (DocumentException de) {
382             throw de;
383         } catch (Exception e) {
384             if (logger.isDebugEnabled()) {
385                 logger.debug("Caught exception ", e);
386             }
387             throw new DocumentException(e);
388         } finally {
389             if (repoSession != null) {
390                 releaseRepositorySession(repoSession);
391             }
392         }
393         return wrapDoc;
394     }
395
396     /**
397      * find wrapped documentModel from the Nuxeo repository
398      * @param ctx service context under which this method is invoked
399      * @param where NXQL where clause to get the document
400      * @throws DocumentException
401      */
402     @Override
403     public DocumentWrapper<DocumentModel> findDoc(
404             ServiceContext ctx, String whereClause)
405             throws DocumentNotFoundException, DocumentException {
406         RepositoryInstance repoSession = null;
407         DocumentWrapper<DocumentModel> wrapDoc = null;
408
409         try {
410             QueryContext queryContext = new QueryContext(ctx, whereClause);
411             repoSession = getRepositorySession();
412             DocumentModelList docList = null;
413             // force limit to 1, and ignore totalSize
414             String query = buildNXQLQuery(queryContext);
415             docList = repoSession.query(query,
416                     null, //Filter
417                     1, //limit
418                     0, //offset
419                     false); //countTotal
420             if (docList.size() != 1) {
421                 if (logger.isDebugEnabled()) {
422                     logger.debug("findDoc: Query found: " + docList.size() + " items.");
423                     logger.debug(" Query: " + query);
424                 }
425                 throw new DocumentNotFoundException("No document found matching filter params.");
426             }
427             DocumentModel doc = docList.get(0);
428             wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
429         } catch (IllegalArgumentException iae) {
430             throw iae;
431         } catch (DocumentException de) {
432             throw de;
433         } catch (Exception e) {
434             if (logger.isDebugEnabled()) {
435                 logger.debug("Caught exception ", e);
436             }
437             throw new DocumentException(e);
438         } finally {
439             if (repoSession != null) {
440                 releaseRepositorySession(repoSession);
441             }
442         }
443         return wrapDoc;
444     }
445
446     /**
447      * find doc and return CSID from the Nuxeo repository
448      * @param ctx service context under which this method is invoked
449      * @param where NXQL where clause to get the document
450      * @throws DocumentException
451      */
452     @Override
453     public String findDocCSID(
454             ServiceContext ctx, String whereClause)
455             throws DocumentNotFoundException, DocumentException {
456         String csid = null;
457         try {
458             DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
459             DocumentModel docModel = wrapDoc.getWrappedObject();
460             csid = NuxeoUtils.extractId(docModel.getPathAsString());
461         } catch (DocumentNotFoundException dnfe) {
462             throw dnfe;
463         } catch (IllegalArgumentException iae) {
464             throw iae;
465         } catch (DocumentException de) {
466             throw de;
467         } catch (Exception e) {
468             if (logger.isDebugEnabled()) {
469                 logger.debug("Caught exception ", e);
470             }
471             throw new DocumentException(e);
472         }
473         return csid;
474     }
475
476     /**
477      * Find a list of documentModels from the Nuxeo repository
478      * @param docTypes a list of DocType names to match
479      * @param where the clause to qualify on
480      * @param domain the domain for the associated services
481      * @return
482      */
483     @Override
484     public DocumentWrapper<DocumentModelList> findDocs(
485             ServiceContext ctx,
486             List<String> docTypes,
487             String whereClause,
488             int pageSize, int pageNum, boolean computeTotal)
489             throws DocumentNotFoundException, DocumentException {
490         RepositoryInstance repoSession = null;
491         DocumentWrapper<DocumentModelList> wrapDoc = null;
492
493         try {
494             if (docTypes == null || docTypes.size() < 1) {
495                 throw new DocumentNotFoundException(
496                         "findDocs must specify at least one DocumentType.");
497             }
498             repoSession = getRepositorySession();
499             DocumentModelList docList = null;
500             // force limit to 1, and ignore totalSize
501             QueryContext queryContext = new QueryContext(ctx, whereClause);
502             String query = buildNXQLQuery(docTypes, queryContext);
503             docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
504             wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
505         } catch (IllegalArgumentException iae) {
506             throw iae;
507         } catch (Exception e) {
508             if (logger.isDebugEnabled()) {
509                 logger.debug("Caught exception ", e);
510             }
511             throw new DocumentException(e);
512         } finally {
513             if (repoSession != null) {
514                 releaseRepositorySession(repoSession);
515             }
516         }
517         return wrapDoc;
518     }
519
520     /* (non-Javadoc)
521      * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
522      */
523     @Override
524     public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
525             throws DocumentNotFoundException, DocumentException {
526         if (handler == null) {
527             throw new IllegalArgumentException(
528                     "RepositoryJavaClient.getAll: handler is missing");
529         }
530
531         RepositoryInstance repoSession = null;
532
533         try {
534             handler.prepare(Action.GET_ALL);
535             repoSession = getRepositorySession();
536             DocumentModelList docModelList = new DocumentModelListImpl();
537             //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
538             for (String csid : csidList) {
539                 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
540                 DocumentModel docModel = repoSession.getDocument(docRef);
541                 docModelList.add(docModel);
542             }
543
544             //set reposession to handle the document
545             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
546             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
547             handler.handle(Action.GET_ALL, wrapDoc);
548             handler.complete(Action.GET_ALL, wrapDoc);
549         } catch (DocumentException de) {
550             throw de;
551         } catch (Exception e) {
552             if (logger.isDebugEnabled()) {
553                 logger.debug("Caught exception ", e);
554             }
555             throw new DocumentException(e);
556         } finally {
557             if (repoSession != null) {
558                 releaseRepositorySession(repoSession);
559             }
560         }
561     }
562
563     /**
564      * getAll get all documents for an entity entity service from the Nuxeo
565      * repository
566      *
567      * @param ctx service context under which this method is invoked
568      * @param handler
569      *            should be used by the caller to provide and transform the
570      *            document
571      * @throws DocumentException
572      */
573     @Override
574     public void getAll(ServiceContext ctx, DocumentHandler handler)
575             throws DocumentNotFoundException, DocumentException {
576         if (handler == null) {
577             throw new IllegalArgumentException(
578                     "RepositoryJavaClient.getAll: handler is missing");
579         }
580         String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
581         if (nuxeoWspaceId == null) {
582             throw new DocumentNotFoundException(
583                     "Unable to find workspace for service "
584                     + ctx.getServiceName()
585                     + " check if the workspace exists in the Nuxeo repository");
586         }
587         RepositoryInstance repoSession = null;
588
589         try {
590             handler.prepare(Action.GET_ALL);
591             repoSession = getRepositorySession();
592             DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
593             DocumentModelList docList = repoSession.getChildren(wsDocRef);
594             //set reposession to handle the document
595             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
596             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
597             handler.handle(Action.GET_ALL, wrapDoc);
598             handler.complete(Action.GET_ALL, wrapDoc);
599         } catch (DocumentException de) {
600             throw de;
601         } catch (Exception e) {
602             if (logger.isDebugEnabled()) {
603                 logger.debug("Caught exception ", e);
604             }
605             throw new DocumentException(e);
606         } finally {
607             if (repoSession != null) {
608                 releaseRepositorySession(repoSession);
609             }
610         }
611     }
612
613     /**
614      * getFiltered get all documents for an entity service from the Document repository,
615      * given filter parameters specified by the handler. 
616      * @param ctx service context under which this method is invoked
617      * @param handler should be used by the caller to provide and transform the document
618      * @throws DocumentNotFoundException if workspace not found
619      * @throws DocumentException
620      */
621     @Override
622     public void getFiltered(ServiceContext ctx, DocumentHandler handler)
623             throws DocumentNotFoundException, DocumentException {
624         QueryContext queryContext = new QueryContext(ctx, handler);
625
626         RepositoryInstance repoSession = null;
627         try {
628             handler.prepare(Action.GET_ALL);
629             repoSession = getRepositorySession();
630             DocumentModelList docList = null;
631             String query = buildNXQLQuery(queryContext);
632
633             if (logger.isDebugEnabled()) {
634                 logger.debug("Executing NXQL query: " + query.toString());
635             }
636
637             // If we have limit and/or offset, then pass true to get totalSize
638             // in returned DocumentModelList.
639             if ((queryContext.docFilter.getOffset() > 0) || (queryContext.docFilter.getPageSize() > 0)) {
640                 docList = repoSession.query(query, null,
641                         queryContext.docFilter.getPageSize(), queryContext.docFilter.getOffset(), true);
642             } else {
643                 docList = repoSession.query(query);
644             }
645
646             //set repoSession to handle the document
647             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
648             DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
649             handler.handle(Action.GET_ALL, wrapDoc);
650             handler.complete(Action.GET_ALL, wrapDoc);
651         } catch (DocumentException de) {
652             throw de;
653         } catch (Exception e) {
654             if (logger.isDebugEnabled()) {
655                 logger.debug("Caught exception ", e);
656             }
657             throw new DocumentException(e);
658         } finally {
659             if (repoSession != null) {
660                 releaseRepositorySession(repoSession);
661             }
662         }
663     }
664
665     /**
666      * update given document in the Nuxeo repository
667      *
668      * @param ctx service context under which this method is invoked
669      * @param id
670      *            of the document
671      * @param handler
672      *            should be used by the caller to provide and transform the
673      *            document
674      * @throws DocumentException
675      */
676     @Override
677     public void update(ServiceContext ctx, String id, DocumentHandler handler)
678             throws BadRequestException, DocumentNotFoundException,
679             DocumentException {
680         if (handler == null) {
681             throw new IllegalArgumentException(
682                     "RepositoryJavaClient.update: handler is missing");
683         }
684         RepositoryInstance repoSession = null;
685         try {
686             handler.prepare(Action.UPDATE);
687             repoSession = getRepositorySession();
688             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
689             DocumentModel doc = null;
690             try {
691                 doc = repoSession.getDocument(docRef);
692             } catch (ClientException ce) {
693                 String msg = "Could not find document to update with id=" + id;
694                 logger.error(msg, ce);
695                 throw new DocumentNotFoundException(msg, ce);
696             }
697             //set reposession to handle the document
698             ((DocumentModelHandler) handler).setRepositorySession(repoSession);
699             DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
700             handler.handle(Action.UPDATE, wrapDoc);
701             setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
702             repoSession.saveDocument(doc);
703             repoSession.save();
704             handler.complete(Action.UPDATE, wrapDoc);
705         } catch (BadRequestException bre) {
706             throw bre;
707         } catch (DocumentException de) {
708             throw de;
709         } catch (Exception e) {
710             if (logger.isDebugEnabled()) {
711                 logger.debug("Caught exception ", e);
712             }
713             throw new DocumentException(e);
714         } finally {
715             if (repoSession != null) {
716                 releaseRepositorySession(repoSession);
717             }
718         }
719     }
720
721     /**
722      * delete a document from the Nuxeo repository
723      * @param ctx service context under which this method is invoked
724      * @param id
725      *            of the document
726      * @throws DocumentException
727      */
728     @Override
729     public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
730             DocumentException {
731
732         if (logger.isDebugEnabled()) {
733             logger.debug("deleting document with id=" + id);
734         }
735         RepositoryInstance repoSession = null;
736         try {
737             repoSession = getRepositorySession();
738             DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
739             try {
740                 repoSession.removeDocument(docRef);
741             } catch (ClientException ce) {
742                 String msg = "could not find document to delete with id=" + id;
743                 logger.error(msg, ce);
744                 throw new DocumentNotFoundException(msg, ce);
745             }
746             repoSession.save();
747         } catch (DocumentException de) {
748             throw de;
749         } catch (Exception e) {
750             if (logger.isDebugEnabled()) {
751                 logger.debug("Caught exception ", e);
752             }
753             throw new DocumentException(e);
754         } finally {
755             if (repoSession != null) {
756                 releaseRepositorySession(repoSession);
757             }
758         }
759     }
760
761     /* (non-Javadoc)
762      * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
763      */
764     @Override
765     public void delete(ServiceContext ctx, String id, DocumentHandler handler)
766             throws DocumentNotFoundException, DocumentException {
767         throw new UnsupportedOperationException();
768     }
769
770     @Override
771     public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
772         return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
773     }
774
775     @Override
776     public String createDomain(String domainName) throws Exception {
777         RepositoryInstance repoSession = null;
778         String domainId = null;
779         try {
780             repoSession = getRepositorySession();
781             DocumentRef parentDocRef = new PathRef("/");
782             DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
783             DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
784                     domainName, "Domain");
785             doc.setPropertyValue("dc:title", domainName);
786             doc.setPropertyValue("dc:description", "A CollectionSpace domain "
787                     + domainName);
788             doc = repoSession.createDocument(doc);
789             domainId = doc.getId();
790             repoSession.save();
791             if (logger.isDebugEnabled()) {
792                 logger.debug("created tenant domain name=" + domainName
793                         + " id=" + domainId);
794             }
795         } catch (Exception e) {
796             if (logger.isDebugEnabled()) {
797                 logger.debug("createTenantSpace caught exception ", e);
798             }
799             throw e;
800         } finally {
801             if (repoSession != null) {
802                 releaseRepositorySession(repoSession);
803             }
804         }
805         return domainId;
806     }
807
808     @Override
809     public String getDomainId(String domainName) throws Exception {
810         String domainId = null;
811         RepositoryInstance repoSession = null;
812         try {
813             repoSession = getRepositorySession();
814             DocumentRef docRef = new PathRef(
815                     "/" + domainName);
816             DocumentModel domain = repoSession.getDocument(docRef);
817             domainId = domain.getId();
818         } catch (Exception e) {
819             if (logger.isDebugEnabled()) {
820                 logger.debug("Caught exception ", e);
821             }
822             //there is no way to identify if document does not exist due to
823             //lack of typed exception for getDocument method
824             return null;
825         } finally {
826             if (repoSession != null) {
827                 releaseRepositorySession(repoSession);
828             }
829         }
830         return domainId;
831     }
832
833     /* (non-Javadoc)
834      * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
835      */
836     @Override
837     public String createWorkspace(String domainName, String workspaceName) throws Exception {
838         RepositoryInstance repoSession = null;
839         String workspaceId = null;
840         try {
841             repoSession = getRepositorySession();
842             DocumentRef parentDocRef = new PathRef(
843                     "/" + domainName
844                     + "/" + "workspaces");
845             DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
846             DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
847                     workspaceName, "Workspace");
848             doc.setPropertyValue("dc:title", workspaceName);
849             doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
850                     + workspaceName);
851             doc = repoSession.createDocument(doc);
852             workspaceId = doc.getId();
853             repoSession.save();
854             if (logger.isDebugEnabled()) {
855                 logger.debug("created workspace name=" + workspaceName
856                         + " id=" + workspaceId);
857             }
858         } catch (Exception e) {
859             if (logger.isDebugEnabled()) {
860                 logger.debug("createWorkspace caught exception ", e);
861             }
862             throw e;
863         } finally {
864             if (repoSession != null) {
865                 releaseRepositorySession(repoSession);
866             }
867         }
868         return workspaceId;
869     }
870
871     /* (non-Javadoc)
872      * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
873      */
874     @Override
875     public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
876         String workspaceId = null;
877         RepositoryInstance repoSession = null;
878         try {
879             repoSession = getRepositorySession();
880             DocumentRef docRef = new PathRef(
881                     "/" + tenantDomain
882                     + "/" + "workspaces"
883                     + "/" + workspaceName);
884             DocumentModel workspace = repoSession.getDocument(docRef);
885             workspaceId = workspace.getId();
886         } catch (DocumentException de) {
887             throw de;
888         } catch (Exception e) {
889             if (logger.isDebugEnabled()) {
890                 logger.debug("Caught exception ", e);
891             }
892             throw new DocumentException(e);
893         } finally {
894             if (repoSession != null) {
895                 releaseRepositorySession(repoSession);
896             }
897         }
898         return workspaceId;
899     }
900
901     /**
902      * Append nxql where.
903      *
904      * @param query the query
905      * @param where the where
906      * @param domain the domain
907      */
908     private final void appendNXQLWhere(StringBuilder query, QueryContext queryContext) {
909         //
910         // Restrict search to a specific Nuxeo domain
911         // TODO This is a slow method for tenant-filter
912         // We should make this a property that is indexed.
913         //
914         query.append(" WHERE ecm:path STARTSWITH '/" + queryContext.domain + "'");
915
916         //
917         // Restrict search to the current tenant ID.  Is the domain path filter (above) still needed?
918         //
919         query.append(IQueryManager.SEARCH_QUALIFIER_AND + DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA + ":"
920                 + DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID
921                 + " = " + queryContext.tenantId);
922         //
923         // Finally, append the incoming where clause
924         //
925         String whereClause = queryContext.whereClause;
926         if (whereClause != null && whereClause.length() > 0) {
927             // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
928             // into SQL, we need to parenthesize our 'where' clause
929             query.append(IQueryManager.SEARCH_QUALIFIER_AND + "(" + whereClause + ")");
930         }
931         //
932         // Please lookup this use in Nuxeo support and document here
933         //
934         query.append(IQueryManager.SEARCH_QUALIFIER_AND + "ecm:isProxy = 0");
935     }
936
937     /**
938      * Builds the nxql query.
939      *
940      * @param docType the doc type
941      * @param where the where
942      * @param domain the domain
943      * @param tenantId the tenant id
944      * @return the string
945      */
946     private final String buildNXQLQuery(QueryContext queryContext) {
947         StringBuilder query = new StringBuilder("SELECT * FROM ");
948         query.append(queryContext.docType);
949         appendNXQLWhere(query, queryContext);
950         return query.toString();
951     }
952
953     /**
954      * Builds the nxql query.
955      *
956      * @param docTypes the doc types
957      * @param where the where
958      * @param domain the domain
959      * @return the string
960      */
961     private final String buildNXQLQuery(List<String> docTypes, QueryContext queryContext) {
962         StringBuilder query = new StringBuilder("SELECT * FROM ");
963         boolean fFirst = true;
964         for (String docType : docTypes) {
965             if (fFirst) {
966                 fFirst = false;
967             } else {
968                 query.append(",");
969             }
970             query.append(docType);
971         }
972         appendNXQLWhere(query, queryContext);
973         return query.toString();
974     }
975
976     /**
977      * Gets the repository session.
978      *
979      * @return the repository session
980      * @throws Exception the exception
981      */
982     private RepositoryInstance getRepositorySession() throws Exception {
983         // FIXME: is it possible to reuse repository session?
984         // Authentication failures happen while trying to reuse the session
985         NuxeoClient client = NuxeoConnector.getInstance().getClient();
986         RepositoryInstance repoSession = client.openRepository();
987         if (logger.isDebugEnabled()) {
988             logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
989         }
990         return repoSession;
991     }
992
993     /**
994      * Release repository session.
995      *
996      * @param repoSession the repo session
997      */
998     private void releaseRepositorySession(RepositoryInstance repoSession) {
999         try {
1000             NuxeoClient client = NuxeoConnector.getInstance().getClient();
1001             // release session
1002             client.releaseRepository(repoSession);
1003         } catch (Exception e) {
1004             logger.error("Could not close the repository session", e);
1005             // no need to throw this service specific exception
1006         }
1007     }
1008 }