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:
6 * http://www.collectionspace.org
7 * http://wiki.collectionspace.org
9 * Copyright 2009 University of California at Berkeley
11 * Licensed under the Educational Community License (ECL), Version 2.0.
12 * You may not use this file except in compliance with this License.
14 * You may obtain a copy of the ECL 2.0 License at
16 * https://source.collectionspace.org/collection-space/LICENSE.txt
18 package org.collectionspace.services.nuxeo.client.java;
20 import java.io.Serializable;
21 import java.util.Hashtable;
22 import java.util.Iterator;
23 import java.util.List;
25 import java.util.UUID;
27 import javax.ws.rs.WebApplicationException;
28 import javax.ws.rs.core.MultivaluedMap;
30 import org.collectionspace.services.client.CollectionSpaceClient;
31 import org.collectionspace.services.client.IQueryManager;
32 import org.collectionspace.services.client.PoxPayloadIn;
33 import org.collectionspace.services.client.PoxPayloadOut;
34 import org.collectionspace.services.client.Profiler;
35 import org.collectionspace.services.client.workflow.WorkflowClient;
36 import org.collectionspace.services.common.context.ServiceContext;
37 import org.collectionspace.services.common.query.QueryContext;
38 import org.collectionspace.services.common.repository.RepositoryClient;
39 import org.collectionspace.services.lifecycle.TransitionDef;
40 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
42 import org.collectionspace.services.common.document.BadRequestException;
43 import org.collectionspace.services.common.document.DocumentException;
44 import org.collectionspace.services.common.document.DocumentFilter;
45 import org.collectionspace.services.common.document.DocumentHandler;
46 import org.collectionspace.services.common.document.DocumentNotFoundException;
47 import org.collectionspace.services.common.document.DocumentHandler.Action;
48 import org.collectionspace.services.common.document.DocumentWrapper;
49 import org.collectionspace.services.common.document.DocumentWrapperImpl;
50 import org.collectionspace.services.common.document.TransactionException;
52 import org.nuxeo.common.utils.IdUtils;
53 import org.nuxeo.ecm.core.api.ClientException;
54 import org.nuxeo.ecm.core.api.DocumentModel;
55 import org.nuxeo.ecm.core.api.DocumentModelList;
56 import org.nuxeo.ecm.core.api.IterableQueryResult;
57 import org.nuxeo.ecm.core.api.VersioningOption;
58 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
59 import org.nuxeo.ecm.core.api.DocumentRef;
60 import org.nuxeo.ecm.core.api.IdRef;
61 import org.nuxeo.ecm.core.api.PathRef;
62 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
63 import org.nuxeo.runtime.transaction.TransactionRuntimeException;
66 // CSPACE-5036 - How to make CMISQL queries from Nuxeo
68 import org.apache.chemistry.opencmis.commons.server.CallContext;
69 import org.apache.chemistry.opencmis.server.impl.CallContextImpl;
70 import org.apache.chemistry.opencmis.server.support.query.CmisQlExtParser_CmisBaseGrammar.boolean_factor_return;
71 import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoCmisService;
72 import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoRepository;
74 import org.slf4j.Logger;
75 import org.slf4j.LoggerFactory;
78 * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
79 * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
82 * $LastChangedRevision: $ $LastChangedDate: $
84 public class RepositoryJavaClientImpl implements RepositoryClient<PoxPayloadIn, PoxPayloadOut> {
87 private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
88 // private final Logger profilerLogger = LoggerFactory.getLogger("remperf");
89 // private String foo = Profiler.createLogger();
91 public static final String NUXEO_CORE_TYPE_DOMAIN = "Domain";
92 public static final String NUXEO_CORE_TYPE_WORKSPACEROOT = "WorkspaceRoot";
95 * Instantiates a new repository java client impl.
97 public RepositoryJavaClientImpl() {
102 public void assertWorkflowState(ServiceContext ctx,
103 DocumentModel docModel) throws DocumentNotFoundException, ClientException {
104 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
105 if (queryParams != null) {
107 // Look for the workflow "delete" query param and see if we need to assert that the
108 // docModel is in a non-deleted workflow state.
110 String currentState = docModel.getCurrentLifeCycleState();
111 String includeDeletedStr = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
112 boolean includeDeleted = includeDeletedStr == null ? true : Boolean.parseBoolean(includeDeletedStr);
113 if (includeDeleted == false) {
115 // We don't wanted soft-deleted object, so throw an exception if this one is soft-deleted.
117 if (currentState.equalsIgnoreCase(WorkflowClient.WORKFLOWSTATE_DELETED)) {
118 String msg = "The GET assertion that docModel not be in 'deleted' workflow state failed.";
120 throw new DocumentNotFoundException(msg);
127 * create document in the Nuxeo repository
129 * @param ctx service context under which this method is invoked
131 * should be used by the caller to provide and transform the
133 * @return id in repository of the newly created document
134 * @throws BadRequestException
135 * @throws TransactionException
136 * @throws DocumentException
139 public String create(ServiceContext ctx,
140 DocumentHandler handler) throws BadRequestException,
141 TransactionException, DocumentException {
143 String docType = NuxeoUtils.getTenantQualifiedDocType(ctx); //ctx.getDocumentType();
144 if (docType == null) {
145 throw new IllegalArgumentException(
146 "RepositoryJavaClient.create: docType is missing");
149 if (handler == null) {
150 throw new IllegalArgumentException(
151 "RepositoryJavaClient.create: handler is missing");
153 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
154 if (nuxeoWspaceId == null) {
155 throw new DocumentNotFoundException(
156 "Unable to find workspace for service " + ctx.getServiceName()
157 + " check if the workspace exists in the Nuxeo repository");
160 RepositoryInstance repoSession = null;
162 handler.prepare(Action.CREATE);
163 repoSession = getRepositorySession(ctx);
164 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
165 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
166 String wspacePath = wspaceDoc.getPathAsString();
167 //give our own ID so PathRef could be constructed later on
168 String id = IdUtils.generateId(UUID.randomUUID().toString());
169 // create document model
170 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id, docType);
171 /* Check for a versioned document, and check In and Out before we proceed.
172 * This does not work as we do not have the uid schema on our docs.
173 if(((DocumentModelHandler) handler).supportsVersioning()) {
174 doc.setProperty("uid","major_version",1);
175 doc.setProperty("uid","minor_version",0);
178 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
179 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
180 handler.handle(Action.CREATE, wrapDoc);
181 // create document with documentmodel
182 doc = repoSession.createDocument(doc);
184 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
185 // and assume the handler has the state it needs (doc fragments).
186 handler.complete(Action.CREATE, wrapDoc);
188 } catch (BadRequestException bre) {
190 } catch (Exception e) {
191 logger.error("Caught exception ", e);
192 throw new DocumentException(e);
194 if (repoSession != null) {
195 releaseRepositorySession(ctx, repoSession);
202 * get document from the Nuxeo repository
203 * @param ctx service context under which this method is invoked
205 * of the document to retrieve
207 * should be used by the caller to provide and transform the
209 * @throws DocumentNotFoundException if the document cannot be found in the repository
210 * @throws TransactionException
211 * @throws DocumentException
214 public void get(ServiceContext ctx, String id, DocumentHandler handler)
215 throws DocumentNotFoundException, TransactionException, DocumentException {
217 if (handler == null) {
218 throw new IllegalArgumentException(
219 "RepositoryJavaClient.get: handler is missing");
222 RepositoryInstance repoSession = null;
224 handler.prepare(Action.GET);
225 repoSession = getRepositorySession(ctx);
226 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
227 DocumentModel docModel = null;
229 docModel = repoSession.getDocument(docRef);
230 assertWorkflowState(ctx, docModel);
231 } catch (ClientException ce) {
232 String msg = logException(ce, "Could not find document with CSID=" + id);
233 throw new DocumentNotFoundException(msg, ce);
236 // Set repository session to handle the document
238 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
239 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(docModel);
240 handler.handle(Action.GET, wrapDoc);
241 handler.complete(Action.GET, wrapDoc);
242 } catch (IllegalArgumentException iae) {
244 } catch (DocumentException de) {
246 } catch (Exception e) {
247 if (logger.isDebugEnabled()) {
248 logger.debug("Caught exception ", e);
250 throw new DocumentException(e);
252 if (repoSession != null) {
253 releaseRepositorySession(ctx, repoSession);
259 * get a document from the Nuxeo repository, using the docFilter params.
260 * @param ctx service context under which this method is invoked
262 * should be used by the caller to provide and transform the
263 * document. Handler must have a docFilter set to return a single item.
264 * @throws DocumentNotFoundException if the document cannot be found in the repository
265 * @throws TransactionException
266 * @throws DocumentException
269 public void get(ServiceContext ctx, DocumentHandler handler)
270 throws DocumentNotFoundException, TransactionException, DocumentException {
271 QueryContext queryContext = new QueryContext(ctx, handler);
272 RepositoryInstance repoSession = null;
275 handler.prepare(Action.GET);
276 repoSession = getRepositorySession(ctx);
278 DocumentModelList docList = null;
279 // force limit to 1, and ignore totalSize
280 String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
281 docList = repoSession.query(query, null, 1, 0, false);
282 if (docList.size() != 1) {
283 throw new DocumentNotFoundException("No document found matching filter params: " + query);
285 DocumentModel doc = docList.get(0);
287 if (logger.isDebugEnabled()) {
288 logger.debug("Executed NXQL query: " + query);
291 //set reposession to handle the document
292 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
293 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
294 handler.handle(Action.GET, wrapDoc);
295 handler.complete(Action.GET, wrapDoc);
296 } catch (IllegalArgumentException iae) {
298 } catch (DocumentException de) {
300 } catch (Exception e) {
301 if (logger.isDebugEnabled()) {
302 logger.debug("Caught exception ", e);
304 throw new DocumentException(e);
306 if (repoSession != null) {
307 releaseRepositorySession(ctx, repoSession);
312 public DocumentWrapper<DocumentModel> getDoc(
313 RepositoryInstance repoSession,
314 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
315 String csid) throws DocumentNotFoundException, DocumentException {
316 DocumentWrapper<DocumentModel> wrapDoc = null;
319 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
320 DocumentModel doc = null;
322 doc = repoSession.getDocument(docRef);
323 } catch (ClientException ce) {
324 String msg = logException(ce, "Could not find document with CSID=" + csid);
325 throw new DocumentNotFoundException(msg, ce);
327 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
328 } catch (IllegalArgumentException iae) {
330 } catch (DocumentException de) {
338 * Get wrapped documentModel from the Nuxeo repository. The search is restricted to the workspace
339 * of the current context.
341 * @param ctx service context under which this method is invoked
343 * of the document to retrieve
344 * @throws DocumentNotFoundException
345 * @throws TransactionException
346 * @throws DocumentException
347 * @return a wrapped documentModel
350 public DocumentWrapper<DocumentModel> getDoc(
351 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
352 String csid) throws DocumentNotFoundException, TransactionException, DocumentException {
353 RepositoryInstance repoSession = null;
354 DocumentWrapper<DocumentModel> wrapDoc = null;
357 // Open a new repository session
358 repoSession = getRepositorySession(ctx);
359 wrapDoc = getDoc(repoSession, ctx, csid);
360 } catch (IllegalArgumentException iae) {
362 } catch (DocumentException de) {
364 } catch (Exception e) {
365 if (logger.isDebugEnabled()) {
366 logger.debug("Caught exception ", e);
368 throw new DocumentException(e);
370 if (repoSession != null) {
371 releaseRepositorySession(ctx, repoSession);
375 if (logger.isWarnEnabled() == true) {
376 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
381 public DocumentWrapper<DocumentModel> findDoc(
382 RepositoryInstance repoSession,
383 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
385 throws DocumentNotFoundException, DocumentException {
386 DocumentWrapper<DocumentModel> wrapDoc = null;
389 QueryContext queryContext = new QueryContext(ctx, whereClause);
390 DocumentModelList docList = null;
391 // force limit to 1, and ignore totalSize
392 String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
393 docList = repoSession.query(query,
398 if (docList.size() != 1) {
399 if (logger.isDebugEnabled()) {
400 logger.debug("findDoc: Query found: " + docList.size() + " items.");
401 logger.debug(" Query: " + query);
403 throw new DocumentNotFoundException("No document found matching filter params: " + query);
405 DocumentModel doc = docList.get(0);
406 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
407 } catch (IllegalArgumentException iae) {
409 } catch (DocumentException de) {
411 } catch (Exception e) {
412 if (logger.isDebugEnabled()) {
413 logger.debug("Caught exception ", e);
415 throw new DocumentException(e);
422 * find wrapped documentModel from the Nuxeo repository
423 * @param ctx service context under which this method is invoked
424 * @param whereClause where NXQL where clause to get the document
425 * @throws DocumentNotFoundException
426 * @throws TransactionException
427 * @throws DocumentException
428 * @return a wrapped documentModel retrieved by the repository query
431 public DocumentWrapper<DocumentModel> findDoc(
432 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
434 throws DocumentNotFoundException, TransactionException, DocumentException {
435 RepositoryInstance repoSession = null;
436 DocumentWrapper<DocumentModel> wrapDoc = null;
439 repoSession = getRepositorySession(ctx);
440 wrapDoc = findDoc(repoSession, ctx, whereClause);
441 } catch (Exception e) {
442 throw new DocumentException("Unable to create a Nuxeo repository session.", e);
444 if (repoSession != null) {
445 releaseRepositorySession(ctx, repoSession);
449 if (logger.isWarnEnabled() == true) {
450 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
457 * find doc and return CSID from the Nuxeo repository
459 * @param ctx service context under which this method is invoked
460 * @param whereClause where NXQL where clause to get the document
461 * @throws DocumentNotFoundException
462 * @throws TransactionException
463 * @throws DocumentException
464 * @return the CollectionSpace ID (CSID) of the requested document
467 public String findDocCSID(RepositoryInstance repoSession,
468 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String whereClause)
469 throws DocumentNotFoundException, TransactionException, DocumentException {
471 boolean releaseSession = false;
473 if (repoSession == null) {
474 repoSession = this.getRepositorySession(ctx);
475 releaseSession = true;
477 DocumentWrapper<DocumentModel> wrapDoc = findDoc(repoSession, ctx, whereClause);
478 DocumentModel docModel = wrapDoc.getWrappedObject();
479 csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
480 } catch (DocumentNotFoundException dnfe) {
482 } catch (IllegalArgumentException iae) {
484 } catch (DocumentException de) {
486 } catch (Exception e) {
487 if (logger.isDebugEnabled()) {
488 logger.debug("Caught exception ", e);
490 throw new DocumentException(e);
492 if(releaseSession && (repoSession != null)) {
493 this.releaseRepositorySession(ctx, repoSession);
499 public DocumentWrapper<DocumentModelList> findDocs(
500 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
501 RepositoryInstance repoSession,
502 List<String> docTypes,
504 String orderByClause,
507 boolean computeTotal)
508 throws DocumentNotFoundException, DocumentException {
509 DocumentWrapper<DocumentModelList> wrapDoc = null;
512 if (docTypes == null || docTypes.size() < 1) {
513 throw new DocumentNotFoundException(
514 "The findDocs() method must specify at least one DocumentType.");
516 DocumentModelList docList = null;
517 QueryContext queryContext = new QueryContext(ctx, whereClause, orderByClause);
518 String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
519 if (logger.isDebugEnabled()) {
520 logger.debug("findDocs() NXQL: "+query);
522 docList = repoSession.query(query, null, pageSize, pageSize*pageNum, computeTotal);
523 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
524 } catch (IllegalArgumentException iae) {
526 } catch (Exception e) {
527 if (logger.isDebugEnabled()) {
528 logger.debug("Caught exception ", e);
530 throw new DocumentException(e);
536 protected static String buildInListForDocTypes(List<String> docTypes) {
537 StringBuilder sb = new StringBuilder();
539 boolean first = true;
540 for(String docType:docTypes) {
551 return sb.toString();
554 public DocumentWrapper<DocumentModelList> findDocs(
555 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
556 DocumentHandler handler,
557 RepositoryInstance repoSession,
558 List<String> docTypes)
559 throws DocumentNotFoundException, DocumentException {
560 DocumentWrapper<DocumentModelList> wrapDoc = null;
562 DocumentFilter filter = handler.getDocumentFilter();
563 String oldOrderBy = filter.getOrderByClause();
564 if (isClauseEmpty(oldOrderBy) == true){
565 filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED);
567 QueryContext queryContext = new QueryContext(ctx, handler);
570 if (docTypes == null || docTypes.size() < 1) {
571 throw new DocumentNotFoundException(
572 "The findDocs() method must specify at least one DocumentType.");
574 DocumentModelList docList = null;
575 if (handler.isCMISQuery() == true) {
576 String inList = buildInListForDocTypes(docTypes);
577 ctx.getQueryParams().add(IQueryManager.SEARCH_RELATED_MATCH_OBJ_DOCTYPES, inList);
578 docList = getFilteredCMIS(repoSession, ctx, handler, queryContext);
580 String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
581 if (logger.isDebugEnabled()) {
582 logger.debug("findDocs() NXQL: "+query);
584 docList = repoSession.query(query, null, filter.getPageSize(), filter.getOffset(), true);
586 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
587 } catch (IllegalArgumentException iae) {
589 } catch (Exception e) {
590 if (logger.isDebugEnabled()) {
591 logger.debug("Caught exception ", e);
593 throw new DocumentException(e);
602 * Find a list of documentModels from the Nuxeo repository
603 * @param docTypes a list of DocType names to match
604 * @param whereClause where the clause to qualify on
605 * @throws DocumentNotFoundException
606 * @throws TransactionException
607 * @throws DocumentException
608 * @return a list of documentModels
611 public DocumentWrapper<DocumentModelList> findDocs(
612 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
613 List<String> docTypes,
615 int pageSize, int pageNum, boolean computeTotal)
616 throws DocumentNotFoundException, TransactionException, DocumentException {
617 RepositoryInstance repoSession = null;
618 DocumentWrapper<DocumentModelList> wrapDoc = null;
621 repoSession = getRepositorySession(ctx);
622 wrapDoc = findDocs(ctx, repoSession, docTypes, whereClause, null,
623 pageSize, pageNum, computeTotal);
624 } catch (IllegalArgumentException iae) {
626 } catch (Exception e) {
627 if (logger.isDebugEnabled()) {
628 logger.debug("Caught exception ", e);
630 throw new DocumentException(e);
632 if (repoSession != null) {
633 releaseRepositorySession(ctx, repoSession);
637 if (logger.isWarnEnabled() == true) {
638 logger.warn("Returned DocumentModelList instance was created with a repository session that is now closed.");
645 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
648 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
649 throws DocumentNotFoundException, TransactionException, DocumentException {
650 if (handler == null) {
651 throw new IllegalArgumentException(
652 "RepositoryJavaClient.getAll: handler is missing");
655 RepositoryInstance repoSession = null;
657 handler.prepare(Action.GET_ALL);
658 repoSession = getRepositorySession(ctx);
659 DocumentModelList docModelList = new DocumentModelListImpl();
660 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
661 for (String csid : csidList) {
662 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
663 DocumentModel docModel = repoSession.getDocument(docRef);
664 docModelList.add(docModel);
667 //set reposession to handle the document
668 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
669 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
670 handler.handle(Action.GET_ALL, wrapDoc);
671 handler.complete(Action.GET_ALL, wrapDoc);
672 } catch (DocumentException de) {
674 } catch (Exception e) {
675 if (logger.isDebugEnabled()) {
676 logger.debug("Caught exception ", e);
678 throw new DocumentException(e);
680 if (repoSession != null) {
681 releaseRepositorySession(ctx, repoSession);
687 * getAll get all documents for an entity entity service from the Nuxeo
690 * @param ctx service context under which this method is invoked
692 * should be used by the caller to provide and transform the
694 * @throws DocumentNotFoundException
695 * @throws TransactionException
696 * @throws DocumentException
699 public void getAll(ServiceContext ctx, DocumentHandler handler)
700 throws DocumentNotFoundException, TransactionException, DocumentException {
701 if (handler == null) {
702 throw new IllegalArgumentException(
703 "RepositoryJavaClient.getAll: handler is missing");
705 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
706 if (nuxeoWspaceId == null) {
707 throw new DocumentNotFoundException(
708 "Unable to find workspace for service "
709 + ctx.getServiceName()
710 + " check if the workspace exists in the Nuxeo repository.");
713 RepositoryInstance repoSession = null;
715 handler.prepare(Action.GET_ALL);
716 repoSession = getRepositorySession(ctx);
717 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
718 DocumentModelList docList = repoSession.getChildren(wsDocRef);
719 //set reposession to handle the document
720 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
721 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
722 handler.handle(Action.GET_ALL, wrapDoc);
723 handler.complete(Action.GET_ALL, wrapDoc);
724 } catch (DocumentException de) {
726 } catch (Exception e) {
727 if (logger.isDebugEnabled()) {
728 logger.debug("Caught exception ", e);
730 throw new DocumentException(e);
732 if (repoSession != null) {
733 releaseRepositorySession(ctx, repoSession);
738 private boolean isClauseEmpty(String theString) {
739 boolean result = true;
740 if (theString != null && !theString.isEmpty()) {
746 public DocumentWrapper<DocumentModel> getDocFromCsid(
747 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
748 RepositoryInstance repoSession,
751 DocumentWrapper<DocumentModel> result = null;
753 result = new DocumentWrapperImpl(NuxeoUtils.getDocFromCsid(ctx, repoSession, csid));
759 * A method to find a CollectionSpace document (of any type) given just a service context and
760 * its CSID. A search across *all* service workspaces (within a given tenant context) is performed to find
763 * This query searches Nuxeo's Hierarchy table where our CSIDs are stored in the "name" column.
766 public DocumentWrapper<DocumentModel> getDocFromCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
769 DocumentWrapper<DocumentModel> result = null;
770 RepositoryInstance repoSession = null;
772 repoSession = getRepositorySession(ctx);
773 result = getDocFromCsid(ctx, repoSession, csid);
775 if (repoSession != null) {
776 releaseRepositorySession(ctx, repoSession);
780 if (logger.isWarnEnabled() == true) {
781 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
788 * Returns a URI value for a document in the Nuxeo repository
789 * @param wrappedDoc a wrapped documentModel
790 * @throws ClientException
791 * @return a document URI
794 public String getDocURI(DocumentWrapper<DocumentModel> wrappedDoc) throws ClientException {
795 DocumentModel docModel = wrappedDoc.getWrappedObject();
796 String uri = (String)docModel.getProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
797 CollectionSpaceClient.COLLECTIONSPACE_CORE_URI);
802 * See CSPACE-5036 - How to make CMISQL queries from Nuxeo
804 private IterableQueryResult makeCMISQLQuery(RepositoryInstance repoSession, String query, QueryContext queryContext) {
805 IterableQueryResult result = null;
807 // the NuxeoRepository should be constructed only once, then cached
808 // (its construction is expensive)
810 NuxeoRepository repo = new NuxeoRepository(
811 repoSession.getRepositoryName(), repoSession
812 .getRootDocument().getId());
813 logger.debug("Repository ID:" + repo.getId() + " Root folder:"
814 + repo.getRootFolderId());
816 CallContextImpl callContext = new CallContextImpl(
817 CallContext.BINDING_LOCAL, repo.getId(), false);
818 callContext.put(CallContext.USERNAME, repoSession.getPrincipal()
820 NuxeoCmisService cmisService = new NuxeoCmisService(repo,
821 callContext, repoSession);
823 result = repoSession.queryAndFetch(query, "CMISQL", cmisService);
824 } catch (ClientException e) {
825 // TODO Auto-generated catch block
826 logger.error("Encounter trouble making the following CMIS query: " + query, e);
833 * getFiltered get all documents for an entity service from the Document repository,
834 * given filter parameters specified by the handler.
835 * @param ctx service context under which this method is invoked
836 * @param handler should be used by the caller to provide and transform the document
837 * @throws DocumentNotFoundException if workspace not found
838 * @throws TransactionException
839 * @throws DocumentException
842 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
843 throws DocumentNotFoundException, TransactionException, DocumentException {
845 DocumentFilter filter = handler.getDocumentFilter();
846 String oldOrderBy = filter.getOrderByClause();
847 if (isClauseEmpty(oldOrderBy) == true){
848 filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED);
850 QueryContext queryContext = new QueryContext(ctx, handler);
852 RepositoryInstance repoSession = null;
854 handler.prepare(Action.GET_ALL);
855 repoSession = getRepositorySession(ctx); //Keeps a refcount here for the repository session so you need to release this when finished
857 DocumentModelList docList = null;
858 String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
860 if (logger.isDebugEnabled()) {
861 logger.debug("Executing NXQL query: " + query.toString());
864 // If we have limit and/or offset, then pass true to get totalSize
865 // in returned DocumentModelList.
866 Profiler profiler = new Profiler(this, 2);
867 profiler.log("Executing NXQL query: " + query.toString());
869 if (handler.isCMISQuery() == true) {
870 docList = getFilteredCMIS(repoSession, ctx, handler, queryContext); //FIXME: REM - Need to deal with paging info in CMIS query
871 } else if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
872 docList = repoSession.query(query, null,
873 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
875 docList = repoSession.query(query);
879 //set repoSession to handle the document
880 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
881 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
882 handler.handle(Action.GET_ALL, wrapDoc);
883 handler.complete(Action.GET_ALL, wrapDoc);
884 } catch (DocumentException de) {
886 } catch (Exception e) {
887 if (logger.isDebugEnabled()) {
888 logger.debug("Caught exception ", e);
890 throw new DocumentException(e);
892 if (repoSession != null) {
893 releaseRepositorySession(ctx, repoSession);
898 private DocumentModelList getFilteredCMIS(RepositoryInstance repoSession, ServiceContext ctx, DocumentHandler handler, QueryContext queryContext)
899 throws DocumentNotFoundException, DocumentException {
901 DocumentModelList result = new DocumentModelListImpl();
903 String query = handler.getCMISQuery(queryContext);
905 DocumentFilter docFilter = handler.getDocumentFilter();
906 int pageSize = docFilter.getPageSize();
907 int offset = docFilter.getOffset();
908 if (logger.isDebugEnabled()) {
909 logger.debug("Executing CMIS query: " + query.toString()
910 + "with pageSize: "+pageSize+" at offset: "+offset);
913 // If we have limit and/or offset, then pass true to get totalSize
914 // in returned DocumentModelList.
915 Profiler profiler = new Profiler(this, 2);
916 profiler.log("Executing CMIS query: " + query.toString());
919 IterableQueryResult queryResult = makeCMISQLQuery(repoSession, query, queryContext);
921 int totalSize = (int)queryResult.size();
922 ((DocumentModelListImpl)result).setTotalSize(totalSize);
923 // Skip the rows before our offset
925 queryResult.skipTo(offset);
928 for (Map<String, Serializable> row : queryResult) {
929 if (logger.isTraceEnabled()) {
930 logger.trace(" Hierarchy Table ID is:" + row.get(IQueryManager.CMIS_TARGET_NUXEO_ID)
931 + " nuxeo:pathSegment is: " + row.get(IQueryManager.CMIS_TARGET_NAME));
933 String nuxeoId = (String) row.get(IQueryManager.CMIS_TARGET_NUXEO_ID);
934 DocumentModel docModel = NuxeoUtils.getDocumentModel(repoSession, nuxeoId);
935 result.add(docModel);
937 if (nRows >= pageSize && pageSize != 0 ) { // A page size of zero means that they want all of them
938 logger.debug("Got page full of items - quitting");
948 } catch (Exception e) {
949 if (logger.isDebugEnabled()) {
950 logger.debug("Caught exception ", e);
952 throw new DocumentException(e);
956 // Since we're not supporting paging yet for CMIS queries, we need to perform
957 // a workaround for the paging information we return in our list of results
960 if (result != null) {
961 docFilter.setStartPage(0);
962 if (totalSize > docFilter.getPageSize()) {
963 docFilter.setPageSize(totalSize);
964 ((DocumentModelListImpl)result).setTotalSize(totalSize);
972 private String logException(Exception e, String msg) {
973 String result = null;
975 String exceptionMessage = e.getMessage();
976 exceptionMessage = exceptionMessage != null ? exceptionMessage : "<No details provided>";
977 result = msg = msg + ". Caught exception:" + exceptionMessage;
979 if (logger.isTraceEnabled() == true) {
980 logger.error(msg, e);
989 * update given document in the Nuxeo repository
991 * @param ctx service context under which this method is invoked
995 * should be used by the caller to provide and transform the
997 * @throws BadRequestException
998 * @throws DocumentNotFoundException
999 * @throws TransactionException if the transaction times out or otherwise cannot be successfully completed
1000 * @throws DocumentException
1003 public void update(ServiceContext ctx, String csid, DocumentHandler handler)
1004 throws BadRequestException, DocumentNotFoundException, TransactionException,
1006 if (handler == null) {
1007 throw new IllegalArgumentException(
1008 "RepositoryJavaClient.update: document handler is missing.");
1011 RepositoryInstance repoSession = null;
1013 handler.prepare(Action.UPDATE);
1014 repoSession = getRepositorySession(ctx);
1015 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
1016 DocumentModel doc = null;
1018 doc = repoSession.getDocument(docRef);
1019 } catch (ClientException ce) {
1020 String msg = logException(ce, "Could not find document to update with CSID=" + csid);
1021 throw new DocumentNotFoundException(msg, ce);
1023 // Check for a versioned document, and check In and Out before we proceed.
1024 if(((DocumentModelHandler) handler).supportsVersioning()) {
1025 /* Once we advance to 5.5 or later, we can add this.
1026 * See also https://jira.nuxeo.com/browse/NXP-8506
1027 if(!doc.isVersionable()) {
1028 throw new DocumentException("Configuration for: "
1029 +handler.getServiceContextPath()+" supports versioning, but Nuxeo config does not!");
1032 /* Force a version number - Not working. Apparently we need to configure the uid schema??
1033 if(doc.getProperty("uid","major_version") == null) {
1034 doc.setProperty("uid","major_version",1);
1036 if(doc.getProperty("uid","minor_version") == null) {
1037 doc.setProperty("uid","minor_version",0);
1040 doc.checkIn(VersioningOption.MINOR, null);
1045 // Set reposession to handle the document
1047 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
1048 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
1049 handler.handle(Action.UPDATE, wrapDoc);
1050 repoSession.saveDocument(doc);
1052 handler.complete(Action.UPDATE, wrapDoc);
1053 } catch (BadRequestException bre) {
1055 } catch (DocumentException de) {
1057 } catch (WebApplicationException wae){
1059 } catch (Exception e) {
1060 if (logger.isDebugEnabled()) {
1061 logger.debug("Caught exception ", e);
1063 throw new DocumentException(e);
1065 if (repoSession != null) {
1066 releaseRepositorySession(ctx, repoSession);
1072 * Save a documentModel to the Nuxeo repository.
1073 * @param ctx service context under which this method is invoked
1074 * @param repoSession
1075 * @param docModel the document to save
1076 * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
1077 * @throws ClientException
1078 * @throws DocumentException
1080 public void saveDocWithoutHandlerProcessing(
1081 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1082 RepositoryInstance repoSession,
1083 DocumentModel docModel,
1084 boolean fSaveSession)
1085 throws ClientException, DocumentException {
1088 repoSession.saveDocument(docModel);
1092 } catch (ClientException ce) {
1094 } catch (Exception e) {
1095 if (logger.isDebugEnabled()) {
1096 logger.debug("Caught exception ", e);
1098 throw new DocumentException(e);
1104 * Save a list of documentModels to the Nuxeo repository.
1106 * @param ctx service context under which this method is invoked
1107 * @param repoSession a repository session
1108 * @param docModelList a list of document models
1109 * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
1110 * @throws ClientException
1111 * @throws DocumentException
1113 public void saveDocListWithoutHandlerProcessing(
1114 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1115 RepositoryInstance repoSession,
1116 DocumentModelList docList,
1117 boolean fSaveSession)
1118 throws ClientException, DocumentException {
1120 DocumentModel[] docModelArray = new DocumentModel[docList.size()];
1121 repoSession.saveDocuments(docList.toArray(docModelArray));
1125 } catch (ClientException ce) {
1127 } catch (Exception e) {
1128 logger.error("Caught exception ", e);
1129 throw new DocumentException(e);
1134 * delete a document from the Nuxeo repository
1135 * @param ctx service context under which this method is invoked
1138 * @throws DocumentException
1141 public void delete(ServiceContext ctx, String id, DocumentHandler handler) throws DocumentNotFoundException,
1142 DocumentException, TransactionException {
1144 throw new IllegalArgumentException(
1145 "delete(ctx, ix, handler): ctx is missing");
1147 if (handler == null) {
1148 throw new IllegalArgumentException(
1149 "delete(ctx, ix, handler): handler is missing");
1151 if (logger.isDebugEnabled()) {
1152 logger.debug("Deleting document with CSID=" + id);
1154 RepositoryInstance repoSession = null;
1156 handler.prepare(Action.DELETE);
1157 repoSession = getRepositorySession(ctx);
1158 DocumentWrapper<DocumentModel> wrapDoc = null;
1160 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
1161 wrapDoc = new DocumentWrapperImpl<DocumentModel>(repoSession.getDocument(docRef));
1162 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
1163 handler.handle(Action.DELETE, wrapDoc);
1164 repoSession.removeDocument(docRef);
1165 } catch (ClientException ce) {
1166 String msg = logException(ce, "Could not find document to delete with CSID=" + id);
1167 throw new DocumentNotFoundException(msg, ce);
1170 handler.complete(Action.DELETE, wrapDoc);
1171 } catch (DocumentException de) {
1173 } catch (Exception e) {
1174 if (logger.isDebugEnabled()) {
1175 logger.debug("Caught exception ", e);
1177 throw new DocumentException(e);
1179 if (repoSession != null) {
1180 releaseRepositorySession(ctx, repoSession);
1186 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
1190 public void delete(@SuppressWarnings("rawtypes") ServiceContext ctx, String id)
1191 throws DocumentNotFoundException, DocumentException {
1192 throw new UnsupportedOperationException();
1193 // Use the other delete instead
1197 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
1198 return NuxeoConnectorEmbedded.getInstance().retrieveWorkspaceIds(domainName);
1202 public String createDomain(String domainName) throws Exception {
1203 RepositoryInstance repoSession = null;
1204 String domainId = null;
1207 // First create the top-level domain directory
1209 repoSession = getRepositorySession(null);
1210 DocumentRef parentDocRef = new PathRef("/");
1211 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
1212 DocumentModel domainDoc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1213 domainName, NUXEO_CORE_TYPE_DOMAIN);
1214 domainDoc.setPropertyValue("dc:title", domainName);
1215 domainDoc.setPropertyValue("dc:description", "A CollectionSpace domain "
1217 domainDoc = repoSession.createDocument(domainDoc);
1218 domainId = domainDoc.getId();
1221 // Next, create a "Workspaces" root directory to contain the workspace folders for the individual service documents
1223 DocumentModel workspacesRoot = repoSession.createDocumentModel(domainDoc.getPathAsString(),
1224 NuxeoUtils.Workspaces, NUXEO_CORE_TYPE_WORKSPACEROOT);
1225 workspacesRoot.setPropertyValue("dc:title", NuxeoUtils.Workspaces);
1226 workspacesRoot.setPropertyValue("dc:description", "A CollectionSpace workspaces directory for "
1227 + domainDoc.getPathAsString());
1228 workspacesRoot = repoSession.createDocument(workspacesRoot);
1229 String workspacesRootId = workspacesRoot.getId();
1232 if (logger.isDebugEnabled()) {
1233 logger.debug("Created tenant domain name=" + domainName
1234 + " id=" + domainId + " " +
1235 NuxeoUtils.Workspaces + " id=" + workspacesRootId);
1236 logger.debug("Path to Domain: "+domainDoc.getPathAsString());
1237 logger.debug("Path to Workspaces root: "+workspacesRoot.getPathAsString());
1239 } catch (Exception e) {
1240 if (logger.isDebugEnabled()) {
1241 logger.debug("Could not create tenant domain name=" + domainName + " caught exception ", e);
1245 if (repoSession != null) {
1246 releaseRepositorySession(null, repoSession);
1254 public String getDomainId(String domainName) throws Exception {
1255 String domainId = null;
1256 RepositoryInstance repoSession = null;
1258 if (domainName != null && !domainName.isEmpty()) {
1260 repoSession = getRepositorySession(null);
1261 DocumentRef docRef = new PathRef(
1263 DocumentModel domain = repoSession.getDocument(docRef);
1264 domainId = domain.getId();
1265 } catch (Exception e) {
1266 if (logger.isTraceEnabled()) {
1267 logger.trace("Caught exception ", e);
1269 //there is no way to identify if document does not exist due to
1270 //lack of typed exception for getDocument method
1273 if (repoSession != null) {
1274 releaseRepositorySession(null, repoSession);
1283 * Returns the workspaces root directory for a given domain.
1285 private DocumentModel getWorkspacesRoot(RepositoryInstance repoSession,
1286 String domainName) throws Exception {
1287 DocumentModel result = null;
1289 String domainPath = "/" + domainName;
1290 DocumentRef parentDocRef = new PathRef(domainPath);
1291 DocumentModelList domainChildrenList = repoSession.getChildren(
1293 Iterator<DocumentModel> witer = domainChildrenList.iterator();
1294 while (witer.hasNext()) {
1295 DocumentModel childNode = witer.next();
1296 if (NuxeoUtils.Workspaces.equalsIgnoreCase(childNode.getName())) {
1298 logger.trace("Found workspaces directory at: " + result.getPathAsString());
1303 if (result == null) {
1304 throw new ClientException("Could not find workspace root directory in: "
1312 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
1315 public String createWorkspace(String domainName, String workspaceName) throws Exception {
1316 RepositoryInstance repoSession = null;
1317 String workspaceId = null;
1319 repoSession = getRepositorySession(null);
1320 DocumentModel parentDoc = getWorkspacesRoot(repoSession, domainName);
1322 if (logger.isTraceEnabled()) {
1323 for (String facet : parentDoc.getFacets()) {
1324 logger.debug("Facet: " + facet);
1328 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1329 workspaceName, NuxeoUtils.WORKSPACE_DOCUMENT_TYPE);
1330 doc.setPropertyValue("dc:title", workspaceName);
1331 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
1333 doc = repoSession.createDocument(doc);
1334 workspaceId = doc.getId();
1336 if (logger.isDebugEnabled()) {
1337 logger.debug("Created workspace name=" + workspaceName
1338 + " id=" + workspaceId);
1340 } catch (Exception e) {
1341 if (logger.isDebugEnabled()) {
1342 logger.debug("createWorkspace caught exception ", e);
1346 if (repoSession != null) {
1347 releaseRepositorySession(null, repoSession);
1354 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
1357 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
1358 String workspaceId = null;
1360 RepositoryInstance repoSession = null;
1362 repoSession = getRepositorySession(null);
1363 DocumentRef docRef = new PathRef(
1365 + "/" + NuxeoUtils.Workspaces
1366 + "/" + workspaceName);
1367 DocumentModel workspace = repoSession.getDocument(docRef);
1368 workspaceId = workspace.getId();
1369 } catch (DocumentException de) {
1371 } catch (Exception e) {
1372 if (logger.isDebugEnabled()) {
1373 logger.debug("Caught exception ", e);
1375 throw new DocumentException(e);
1377 if (repoSession != null) {
1378 releaseRepositorySession(null, repoSession);
1387 * Gets the repository session. - Package access only.
1389 * @return the repository session
1390 * @throws Exception the exception
1392 public RepositoryInstance getRepositorySession(ServiceContext ctx) throws Exception {
1393 RepositoryInstance repoSession = null;
1395 Profiler profiler = new Profiler("getRepositorySession():", 2);
1399 repoSession = (RepositoryInstance)ctx.getCurrentRepositorySession();
1400 if (logger.isDebugEnabled() == true) {
1401 if (repoSession != null) {
1402 logger.warn("Reusing the current context's repository session.");
1407 // If we couldn't find a repoSession from the service context then we need to create a new one
1408 if (repoSession == null) {
1409 NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1410 repoSession = client.openRepository();
1413 if (logger.isTraceEnabled()) {
1414 logger.trace("Testing call to getRepository() repository root: " + repoSession.getRootDocument());
1420 ctx.setCurrentRepositorySession(repoSession); // For reusing, save the repository session in the current service context
1427 * Release repository session. - Package access only.
1429 * @param repoSession the repo session
1431 public void releaseRepositorySession(ServiceContext ctx, RepositoryInstance repoSession) throws TransactionException {
1433 NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1436 ctx.clearCurrentRepositorySession(); //clear the current context of the now closed repo session
1437 if (ctx.getCurrentRepositorySession() == null) {
1438 client.releaseRepository(repoSession); //release the repo session if the service context's ref count is zeo.
1441 client.releaseRepository(repoSession); //repo session was acquired without a service context
1443 } catch (TransactionRuntimeException tre) {
1444 TransactionException te = new TransactionException(tre);
1445 logger.error(te.getMessage(), tre); // Log the standard transaction exception message, plus an exception-specific stack trace
1447 } catch (Exception e) {
1448 logger.error("Could not close the repository session", e);
1449 // no need to throw this service specific exception
1454 public void doWorkflowTransition(ServiceContext ctx, String id,
1455 DocumentHandler handler, TransitionDef transitionDef)
1456 throws BadRequestException, DocumentNotFoundException,
1458 // This is a placeholder for when we change the StorageClient interface to treat workflow transitions as 1st class operations like 'get', 'create', 'update, 'delete', etc