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;
51 import org.nuxeo.common.utils.IdUtils;
52 import org.nuxeo.ecm.core.api.ClientException;
53 import org.nuxeo.ecm.core.api.DocumentModel;
54 import org.nuxeo.ecm.core.api.DocumentModelList;
55 import org.nuxeo.ecm.core.api.IterableQueryResult;
56 import org.nuxeo.ecm.core.api.VersioningOption;
57 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
58 import org.nuxeo.ecm.core.api.DocumentRef;
59 import org.nuxeo.ecm.core.api.IdRef;
60 import org.nuxeo.ecm.core.api.PathRef;
61 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
64 // CSPACE-5036 - How to make CMISQL queries from Nuxeo
66 import org.apache.chemistry.opencmis.commons.server.CallContext;
67 import org.apache.chemistry.opencmis.server.impl.CallContextImpl;
68 import org.apache.chemistry.opencmis.server.support.query.CmisQlExtParser_CmisBaseGrammar.boolean_factor_return;
69 import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoCmisService;
70 import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoRepository;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
76 * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
77 * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
80 * $LastChangedRevision: $ $LastChangedDate: $
82 public class RepositoryJavaClientImpl implements RepositoryClient<PoxPayloadIn, PoxPayloadOut> {
85 private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
86 // private final Logger profilerLogger = LoggerFactory.getLogger("remperf");
87 // private String foo = Profiler.createLogger();
89 public static final String NUXEO_CORE_TYPE_DOMAIN = "Domain";
90 public static final String NUXEO_CORE_TYPE_WORKSPACEROOT = "WorkspaceRoot";
93 * Instantiates a new repository java client impl.
95 public RepositoryJavaClientImpl() {
100 public void assertWorkflowState(ServiceContext ctx,
101 DocumentModel docModel) throws DocumentNotFoundException, ClientException {
102 MultivaluedMap<String, String> queryParams = ctx.getQueryParams();
103 if (queryParams != null) {
105 // Look for the workflow "delete" query param and see if we need to assert that the
106 // docModel is in a non-deleted workflow state.
108 String currentState = docModel.getCurrentLifeCycleState();
109 String includeDeletedStr = queryParams.getFirst(WorkflowClient.WORKFLOW_QUERY_NONDELETED);
110 boolean includeDeleted = includeDeletedStr == null ? true : Boolean.parseBoolean(includeDeletedStr);
111 if (includeDeleted == false) {
113 // We don't wanted soft-deleted object, so throw an exception if this one is soft-deleted.
115 if (currentState.equalsIgnoreCase(WorkflowClient.WORKFLOWSTATE_DELETED)) {
116 String msg = "The GET assertion that docModel not be in 'deleted' workflow state failed.";
118 throw new DocumentNotFoundException(msg);
125 * create document in the Nuxeo repository
127 * @param ctx service context under which this method is invoked
129 * should be used by the caller to provide and transform the
131 * @return id in repository of the newly created document
132 * @throws DocumentException
135 public String create(ServiceContext ctx,
136 DocumentHandler handler) throws BadRequestException,
139 String docType = NuxeoUtils.getTenantQualifiedDocType(ctx); //ctx.getDocumentType();
140 if (docType == null) {
141 throw new IllegalArgumentException(
142 "RepositoryJavaClient.create: docType is missing");
145 if (handler == null) {
146 throw new IllegalArgumentException(
147 "RepositoryJavaClient.create: handler is missing");
149 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
150 if (nuxeoWspaceId == null) {
151 throw new DocumentNotFoundException(
152 "Unable to find workspace for service " + ctx.getServiceName()
153 + " check if the workspace exists in the Nuxeo repository");
156 RepositoryInstance repoSession = null;
158 handler.prepare(Action.CREATE);
159 repoSession = getRepositorySession(ctx);
160 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
161 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
162 String wspacePath = wspaceDoc.getPathAsString();
163 //give our own ID so PathRef could be constructed later on
164 String id = IdUtils.generateId(UUID.randomUUID().toString());
165 // create document model
166 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id, docType);
167 /* Check for a versioned document, and check In and Out before we proceed.
168 * This does not work as we do not have the uid schema on our docs.
169 if(((DocumentModelHandler) handler).supportsVersioning()) {
170 doc.setProperty("uid","major_version",1);
171 doc.setProperty("uid","minor_version",0);
174 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
175 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
176 handler.handle(Action.CREATE, wrapDoc);
177 // create document with documentmodel
178 doc = repoSession.createDocument(doc);
180 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
181 // and assume the handler has the state it needs (doc fragments).
182 handler.complete(Action.CREATE, wrapDoc);
184 } catch (BadRequestException bre) {
186 } catch (Exception e) {
187 logger.error("Caught exception ", e);
188 throw new DocumentException(e);
190 if (repoSession != null) {
191 releaseRepositorySession(ctx, repoSession);
198 * get document from the Nuxeo repository
199 * @param ctx service context under which this method is invoked
201 * of the document to retrieve
203 * should be used by the caller to provide and transform the
205 * @throws DocumentException
208 public void get(ServiceContext ctx, String id, DocumentHandler handler)
209 throws DocumentNotFoundException, DocumentException {
211 if (handler == null) {
212 throw new IllegalArgumentException(
213 "RepositoryJavaClient.get: handler is missing");
216 RepositoryInstance repoSession = null;
218 handler.prepare(Action.GET);
219 repoSession = getRepositorySession(ctx);
220 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
221 DocumentModel docModel = null;
223 docModel = repoSession.getDocument(docRef);
224 assertWorkflowState(ctx, docModel);
225 } catch (ClientException ce) {
226 String msg = logException(ce, "Could not find document with CSID=" + id);
227 throw new DocumentNotFoundException(msg, ce);
230 // Set repository session to handle the document
232 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
233 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(docModel);
234 handler.handle(Action.GET, wrapDoc);
235 handler.complete(Action.GET, wrapDoc);
236 } catch (IllegalArgumentException iae) {
238 } catch (DocumentException de) {
240 } catch (Exception e) {
241 if (logger.isDebugEnabled()) {
242 logger.debug("Caught exception ", e);
244 throw new DocumentException(e);
246 if (repoSession != null) {
247 releaseRepositorySession(ctx, repoSession);
253 * get document from the Nuxeo repository, using the docFilter params.
254 * @param ctx service context under which this method is invoked
256 * should be used by the caller to provide and transform the
257 * document. Handler must have a docFilter set to return a single item.
258 * @throws DocumentException
261 public void get(ServiceContext ctx, DocumentHandler handler)
262 throws DocumentNotFoundException, DocumentException {
263 QueryContext queryContext = new QueryContext(ctx, handler);
264 RepositoryInstance repoSession = null;
267 handler.prepare(Action.GET);
268 repoSession = getRepositorySession(ctx);
270 DocumentModelList docList = null;
271 // force limit to 1, and ignore totalSize
272 String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
273 docList = repoSession.query(query, null, 1, 0, false);
274 if (docList.size() != 1) {
275 throw new DocumentNotFoundException("No document found matching filter params: " + query);
277 DocumentModel doc = docList.get(0);
279 if (logger.isDebugEnabled()) {
280 logger.debug("Executed NXQL query: " + query);
283 //set reposession to handle the document
284 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
285 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
286 handler.handle(Action.GET, wrapDoc);
287 handler.complete(Action.GET, wrapDoc);
288 } catch (IllegalArgumentException iae) {
290 } catch (DocumentException de) {
292 } catch (Exception e) {
293 if (logger.isDebugEnabled()) {
294 logger.debug("Caught exception ", e);
296 throw new DocumentException(e);
298 if (repoSession != null) {
299 releaseRepositorySession(ctx, repoSession);
304 public DocumentWrapper<DocumentModel> getDoc(
305 RepositoryInstance repoSession,
306 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
307 String csid) throws DocumentNotFoundException, DocumentException {
308 DocumentWrapper<DocumentModel> wrapDoc = null;
311 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
312 DocumentModel doc = null;
314 doc = repoSession.getDocument(docRef);
315 } catch (ClientException ce) {
316 String msg = logException(ce, "Could not find document with CSID=" + csid);
317 throw new DocumentNotFoundException(msg, ce);
319 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
320 } catch (IllegalArgumentException iae) {
322 } catch (DocumentException de) {
330 * Get wrapped documentModel from the Nuxeo repository. The search is restricted to the workspace
331 * of the current context.
333 * @param ctx service context under which this method is invoked
335 * of the document to retrieve
336 * @throws DocumentException
339 public DocumentWrapper<DocumentModel> getDoc(
340 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
341 String csid) throws DocumentNotFoundException, DocumentException {
342 RepositoryInstance repoSession = null;
343 DocumentWrapper<DocumentModel> wrapDoc = null;
346 // Open a new repository session
347 repoSession = getRepositorySession(ctx);
348 wrapDoc = getDoc(repoSession, ctx, csid);
349 } catch (IllegalArgumentException iae) {
351 } catch (DocumentException de) {
353 } catch (Exception e) {
354 if (logger.isDebugEnabled()) {
355 logger.debug("Caught exception ", e);
357 throw new DocumentException(e);
359 if (repoSession != null) {
360 releaseRepositorySession(ctx, repoSession);
364 if (logger.isWarnEnabled() == true) {
365 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
370 public DocumentWrapper<DocumentModel> findDoc(
371 RepositoryInstance repoSession,
372 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
374 throws DocumentNotFoundException, DocumentException {
375 DocumentWrapper<DocumentModel> wrapDoc = null;
378 QueryContext queryContext = new QueryContext(ctx, whereClause);
379 DocumentModelList docList = null;
380 // force limit to 1, and ignore totalSize
381 String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
382 docList = repoSession.query(query,
387 if (docList.size() != 1) {
388 if (logger.isDebugEnabled()) {
389 logger.debug("findDoc: Query found: " + docList.size() + " items.");
390 logger.debug(" Query: " + query);
392 throw new DocumentNotFoundException("No document found matching filter params: " + query);
394 DocumentModel doc = docList.get(0);
395 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
396 } catch (IllegalArgumentException iae) {
398 } catch (DocumentException de) {
400 } catch (Exception e) {
401 if (logger.isDebugEnabled()) {
402 logger.debug("Caught exception ", e);
404 throw new DocumentException(e);
411 * find wrapped documentModel from the Nuxeo repository
412 * @param ctx service context under which this method is invoked
413 * @param whereClause where NXQL where clause to get the document
414 * @throws DocumentException
417 public DocumentWrapper<DocumentModel> findDoc(
418 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
420 throws DocumentNotFoundException, DocumentException {
421 RepositoryInstance repoSession = null;
422 DocumentWrapper<DocumentModel> wrapDoc = null;
425 repoSession = getRepositorySession(ctx);
426 wrapDoc = findDoc(repoSession, ctx, whereClause);
427 } catch (Exception e) {
428 throw new DocumentException("Unable to create a Nuxeo repository session.", e);
430 if (repoSession != null) {
431 releaseRepositorySession(ctx, repoSession);
435 if (logger.isWarnEnabled() == true) {
436 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
443 * find doc and return CSID from the Nuxeo repository
444 * @param ctx service context under which this method is invoked
445 * @param whereClause where NXQL where clause to get the document
446 * @throws DocumentException
449 public String findDocCSID(RepositoryInstance repoSession,
450 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx, String whereClause)
451 throws DocumentNotFoundException, DocumentException {
453 boolean releaseSession = false;
455 if (repoSession == null) {
456 repoSession = this.getRepositorySession(ctx);
457 releaseSession = true;
459 DocumentWrapper<DocumentModel> wrapDoc = findDoc(repoSession, ctx, whereClause);
460 DocumentModel docModel = wrapDoc.getWrappedObject();
461 csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
462 } catch (DocumentNotFoundException dnfe) {
464 } catch (IllegalArgumentException iae) {
466 } catch (DocumentException de) {
468 } catch (Exception e) {
469 if (logger.isDebugEnabled()) {
470 logger.debug("Caught exception ", e);
472 throw new DocumentException(e);
474 if(releaseSession && (repoSession != null)) {
475 this.releaseRepositorySession(ctx, repoSession);
481 public DocumentWrapper<DocumentModelList> findDocs(
482 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
483 RepositoryInstance repoSession,
484 List<String> docTypes,
486 String orderByClause,
489 boolean computeTotal)
490 throws DocumentNotFoundException, DocumentException {
491 DocumentWrapper<DocumentModelList> wrapDoc = null;
494 if (docTypes == null || docTypes.size() < 1) {
495 throw new DocumentNotFoundException(
496 "The findDocs() method must specify at least one DocumentType.");
498 DocumentModelList docList = null;
499 QueryContext queryContext = new QueryContext(ctx, whereClause, orderByClause);
500 String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
501 if (logger.isDebugEnabled()) {
502 logger.debug("findDocs() NXQL: "+query);
504 docList = repoSession.query(query, null, pageSize, pageSize*pageNum, computeTotal);
505 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
506 } catch (IllegalArgumentException iae) {
508 } catch (Exception e) {
509 if (logger.isDebugEnabled()) {
510 logger.debug("Caught exception ", e);
512 throw new DocumentException(e);
518 protected static String buildInListForDocTypes(List<String> docTypes) {
519 StringBuilder sb = new StringBuilder();
521 boolean first = true;
522 for(String docType:docTypes) {
533 return sb.toString();
536 public DocumentWrapper<DocumentModelList> findDocs(
537 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
538 DocumentHandler handler,
539 RepositoryInstance repoSession,
540 List<String> docTypes)
541 throws DocumentNotFoundException, DocumentException {
542 DocumentWrapper<DocumentModelList> wrapDoc = null;
544 DocumentFilter filter = handler.getDocumentFilter();
545 String oldOrderBy = filter.getOrderByClause();
546 if (isClauseEmpty(oldOrderBy) == true){
547 filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED);
549 QueryContext queryContext = new QueryContext(ctx, handler);
552 if (docTypes == null || docTypes.size() < 1) {
553 throw new DocumentNotFoundException(
554 "The findDocs() method must specify at least one DocumentType.");
556 DocumentModelList docList = null;
557 if (handler.isCMISQuery() == true) {
558 String inList = buildInListForDocTypes(docTypes);
559 ctx.getQueryParams().add(IQueryManager.SEARCH_RELATED_MATCH_OBJ_DOCTYPES, inList);
560 docList = getFilteredCMIS(repoSession, ctx, handler, queryContext);
562 String query = NuxeoUtils.buildNXQLQuery(docTypes, queryContext);
563 if (logger.isDebugEnabled()) {
564 logger.debug("findDocs() NXQL: "+query);
566 docList = repoSession.query(query, null, filter.getPageSize(), filter.getOffset(), true);
568 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
569 } catch (IllegalArgumentException iae) {
571 } catch (Exception e) {
572 if (logger.isDebugEnabled()) {
573 logger.debug("Caught exception ", e);
575 throw new DocumentException(e);
584 * Find a list of documentModels from the Nuxeo repository
585 * @param docTypes a list of DocType names to match
586 * @param whereClause where the clause to qualify on
590 public DocumentWrapper<DocumentModelList> findDocs(
591 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
592 List<String> docTypes,
594 int pageSize, int pageNum, boolean computeTotal)
595 throws DocumentNotFoundException, DocumentException {
596 RepositoryInstance repoSession = null;
597 DocumentWrapper<DocumentModelList> wrapDoc = null;
600 repoSession = getRepositorySession(ctx);
601 wrapDoc = findDocs(ctx, repoSession, docTypes, whereClause, null,
602 pageSize, pageNum, computeTotal);
603 } catch (IllegalArgumentException iae) {
605 } catch (Exception e) {
606 if (logger.isDebugEnabled()) {
607 logger.debug("Caught exception ", e);
609 throw new DocumentException(e);
611 if (repoSession != null) {
612 releaseRepositorySession(ctx, repoSession);
616 if (logger.isWarnEnabled() == true) {
617 logger.warn("Returned DocumentModelList instance was created with a repository session that is now closed.");
624 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
627 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
628 throws DocumentNotFoundException, DocumentException {
629 if (handler == null) {
630 throw new IllegalArgumentException(
631 "RepositoryJavaClient.getAll: handler is missing");
634 RepositoryInstance repoSession = null;
636 handler.prepare(Action.GET_ALL);
637 repoSession = getRepositorySession(ctx);
638 DocumentModelList docModelList = new DocumentModelListImpl();
639 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
640 for (String csid : csidList) {
641 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
642 DocumentModel docModel = repoSession.getDocument(docRef);
643 docModelList.add(docModel);
646 //set reposession to handle the document
647 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
648 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
649 handler.handle(Action.GET_ALL, wrapDoc);
650 handler.complete(Action.GET_ALL, wrapDoc);
651 } catch (DocumentException de) {
653 } catch (Exception e) {
654 if (logger.isDebugEnabled()) {
655 logger.debug("Caught exception ", e);
657 throw new DocumentException(e);
659 if (repoSession != null) {
660 releaseRepositorySession(ctx, repoSession);
666 * getAll get all documents for an entity entity service from the Nuxeo
669 * @param ctx service context under which this method is invoked
671 * should be used by the caller to provide and transform the
673 * @throws DocumentException
676 public void getAll(ServiceContext ctx, DocumentHandler handler)
677 throws DocumentNotFoundException, DocumentException {
678 if (handler == null) {
679 throw new IllegalArgumentException(
680 "RepositoryJavaClient.getAll: handler is missing");
682 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
683 if (nuxeoWspaceId == null) {
684 throw new DocumentNotFoundException(
685 "Unable to find workspace for service "
686 + ctx.getServiceName()
687 + " check if the workspace exists in the Nuxeo repository.");
690 RepositoryInstance repoSession = null;
692 handler.prepare(Action.GET_ALL);
693 repoSession = getRepositorySession(ctx);
694 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
695 DocumentModelList docList = repoSession.getChildren(wsDocRef);
696 //set reposession to handle the document
697 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
698 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
699 handler.handle(Action.GET_ALL, wrapDoc);
700 handler.complete(Action.GET_ALL, wrapDoc);
701 } catch (DocumentException de) {
703 } catch (Exception e) {
704 if (logger.isDebugEnabled()) {
705 logger.debug("Caught exception ", e);
707 throw new DocumentException(e);
709 if (repoSession != null) {
710 releaseRepositorySession(ctx, repoSession);
715 private boolean isClauseEmpty(String theString) {
716 boolean result = true;
717 if (theString != null && !theString.isEmpty()) {
723 public DocumentWrapper<DocumentModel> getDocFromCsid(
724 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
725 RepositoryInstance repoSession,
728 DocumentWrapper<DocumentModel> result = null;
730 result = new DocumentWrapperImpl(NuxeoUtils.getDocFromCsid(ctx, repoSession, csid));
736 * A method to find a CollectionSpace document (of any type) given just a service context and
737 * its CSID. A search across *all* service workspaces (within a given tenant context) is performed to find
740 * This query searches Nuxeo's Hierarchy table where our CSIDs are stored in the "name" column.
743 public DocumentWrapper<DocumentModel> getDocFromCsid(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
746 DocumentWrapper<DocumentModel> result = null;
747 RepositoryInstance repoSession = null;
749 repoSession = getRepositorySession(ctx);
750 result = getDocFromCsid(ctx, repoSession, csid);
752 if (repoSession != null) {
753 releaseRepositorySession(ctx, repoSession);
757 if (logger.isWarnEnabled() == true) {
758 logger.warn("Returned DocumentModel instance was created with a repository session that is now closed.");
765 * find doc and return CSID from the Nuxeo repository
766 * @param ctx service context under which this method is invoked
767 * @param whereClause where NXQL where clause to get the document
768 * @throws DocumentException
771 public String getDocURI(DocumentWrapper<DocumentModel> wrappedDoc) throws ClientException {
772 DocumentModel docModel = wrappedDoc.getWrappedObject();
773 String uri = (String)docModel.getProperty(CollectionSpaceClient.COLLECTIONSPACE_CORE_SCHEMA,
774 CollectionSpaceClient.COLLECTIONSPACE_CORE_URI);
779 * See CSPACE-5036 - How to make CMISQL queries from Nuxeo
781 private IterableQueryResult makeCMISQLQuery(RepositoryInstance repoSession, String query, QueryContext queryContext) {
782 IterableQueryResult result = null;
784 // the NuxeoRepository should be constructed only once, then cached
785 // (its construction is expensive)
787 NuxeoRepository repo = new NuxeoRepository(
788 repoSession.getRepositoryName(), repoSession
789 .getRootDocument().getId());
790 logger.debug("Repository ID:" + repo.getId() + " Root folder:"
791 + repo.getRootFolderId());
793 CallContextImpl callContext = new CallContextImpl(
794 CallContext.BINDING_LOCAL, repo.getId(), false);
795 callContext.put(CallContext.USERNAME, repoSession.getPrincipal()
797 NuxeoCmisService cmisService = new NuxeoCmisService(repo,
798 callContext, repoSession);
800 result = repoSession.queryAndFetch(query, "CMISQL", cmisService);
801 } catch (ClientException e) {
802 // TODO Auto-generated catch block
803 logger.error("Encounter trouble making the following CMIS query: " + query, e);
810 * getFiltered get all documents for an entity service from the Document repository,
811 * given filter parameters specified by the handler.
812 * @param ctx service context under which this method is invoked
813 * @param handler should be used by the caller to provide and transform the document
814 * @throws DocumentNotFoundException if workspace not found
815 * @throws DocumentException
818 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
819 throws DocumentNotFoundException, DocumentException {
821 DocumentFilter filter = handler.getDocumentFilter();
822 String oldOrderBy = filter.getOrderByClause();
823 if (isClauseEmpty(oldOrderBy) == true){
824 filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED);
826 QueryContext queryContext = new QueryContext(ctx, handler);
828 RepositoryInstance repoSession = null;
830 handler.prepare(Action.GET_ALL);
831 repoSession = getRepositorySession(ctx); //Keeps a refcount here for the repository session so you need to release this when finished
833 DocumentModelList docList = null;
834 String query = NuxeoUtils.buildNXQLQuery(ctx, queryContext);
836 if (logger.isDebugEnabled()) {
837 logger.debug("Executing NXQL query: " + query.toString());
840 // If we have limit and/or offset, then pass true to get totalSize
841 // in returned DocumentModelList.
842 Profiler profiler = new Profiler(this, 2);
843 profiler.log("Executing NXQL query: " + query.toString());
845 if (handler.isCMISQuery() == true) {
846 docList = getFilteredCMIS(repoSession, ctx, handler, queryContext); //FIXME: REM - Need to deal with paging info in CMIS query
847 } else if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
848 docList = repoSession.query(query, null,
849 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
851 docList = repoSession.query(query);
855 //set repoSession to handle the document
856 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
857 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
858 handler.handle(Action.GET_ALL, wrapDoc);
859 handler.complete(Action.GET_ALL, wrapDoc);
860 } catch (DocumentException de) {
862 } catch (Exception e) {
863 if (logger.isDebugEnabled()) {
864 logger.debug("Caught exception ", e);
866 throw new DocumentException(e);
868 if (repoSession != null) {
869 releaseRepositorySession(ctx, repoSession);
874 private DocumentModelList getFilteredCMIS(RepositoryInstance repoSession, ServiceContext ctx, DocumentHandler handler, QueryContext queryContext)
875 throws DocumentNotFoundException, DocumentException {
877 DocumentModelList result = new DocumentModelListImpl();
879 String query = handler.getCMISQuery(queryContext);
881 DocumentFilter docFilter = handler.getDocumentFilter();
882 int pageSize = docFilter.getPageSize();
883 int offset = docFilter.getOffset();
884 if (logger.isDebugEnabled()) {
885 logger.debug("Executing CMIS query: " + query.toString()
886 + "with pageSize: "+pageSize+" at offset: "+offset);
889 // If we have limit and/or offset, then pass true to get totalSize
890 // in returned DocumentModelList.
891 Profiler profiler = new Profiler(this, 2);
892 profiler.log("Executing CMIS query: " + query.toString());
895 IterableQueryResult queryResult = makeCMISQLQuery(repoSession, query, queryContext);
897 int totalSize = (int)queryResult.size();
898 ((DocumentModelListImpl)result).setTotalSize(totalSize);
899 // Skip the rows before our offset
901 queryResult.skipTo(offset);
904 for (Map<String, Serializable> row : queryResult) {
905 if (logger.isTraceEnabled()) {
906 logger.trace(" Hierarchy Table ID is:" + row.get(IQueryManager.CMIS_TARGET_NUXEO_ID)
907 + " nuxeo:pathSegment is: " + row.get(IQueryManager.CMIS_TARGET_NAME));
909 String nuxeoId = (String) row.get(IQueryManager.CMIS_TARGET_NUXEO_ID);
910 DocumentModel docModel = NuxeoUtils.getDocumentModel(repoSession, nuxeoId);
911 result.add(docModel);
913 if (nRows >= pageSize && pageSize != 0 ) { // A page size of zero means that they want all of them
914 logger.debug("Got page full of items - quitting");
924 } catch (Exception e) {
925 if (logger.isDebugEnabled()) {
926 logger.debug("Caught exception ", e);
928 throw new DocumentException(e);
932 // Since we're not supporting paging yet for CMIS queries, we need to perform
933 // a workaround for the paging information we return in our list of results
936 if (result != null) {
937 docFilter.setStartPage(0);
938 if (totalSize > docFilter.getPageSize()) {
939 docFilter.setPageSize(totalSize);
940 ((DocumentModelListImpl)result).setTotalSize(totalSize);
948 private String logException(Exception e, String msg) {
949 String result = null;
951 String exceptionMessage = e.getMessage();
952 exceptionMessage = exceptionMessage != null ? exceptionMessage : "<No details provided>";
953 result = msg = msg + ". Caught exception:" + exceptionMessage;
955 if (logger.isTraceEnabled() == true) {
956 logger.error(msg, e);
965 * update given document in the Nuxeo repository
967 * @param ctx service context under which this method is invoked
971 * should be used by the caller to provide and transform the
973 * @throws DocumentException
976 public void update(ServiceContext ctx, String csid, DocumentHandler handler)
977 throws BadRequestException, DocumentNotFoundException,
979 if (handler == null) {
980 throw new IllegalArgumentException(
981 "RepositoryJavaClient.update: document handler is missing.");
984 RepositoryInstance repoSession = null;
986 handler.prepare(Action.UPDATE);
987 repoSession = getRepositorySession(ctx);
988 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
989 DocumentModel doc = null;
991 doc = repoSession.getDocument(docRef);
992 } catch (ClientException ce) {
993 String msg = logException(ce, "Could not find document to update with CSID=" + csid);
994 throw new DocumentNotFoundException(msg, ce);
996 // Check for a versioned document, and check In and Out before we proceed.
997 if(((DocumentModelHandler) handler).supportsVersioning()) {
998 /* Once we advance to 5.5 or later, we can add this.
999 * See also https://jira.nuxeo.com/browse/NXP-8506
1000 if(!doc.isVersionable()) {
1001 throw new DocumentException("Configuration for: "
1002 +handler.getServiceContextPath()+" supports versioning, but Nuxeo config does not!");
1005 /* Force a version number - Not working. Apparently we need to configure the uid schema??
1006 if(doc.getProperty("uid","major_version") == null) {
1007 doc.setProperty("uid","major_version",1);
1009 if(doc.getProperty("uid","minor_version") == null) {
1010 doc.setProperty("uid","minor_version",0);
1013 doc.checkIn(VersioningOption.MINOR, null);
1018 // Set reposession to handle the document
1020 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
1021 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
1022 handler.handle(Action.UPDATE, wrapDoc);
1023 repoSession.saveDocument(doc);
1025 handler.complete(Action.UPDATE, wrapDoc);
1026 } catch (BadRequestException bre) {
1028 } catch (DocumentException de) {
1030 } catch (WebApplicationException wae){
1032 } catch (Exception e) {
1033 if (logger.isDebugEnabled()) {
1034 logger.debug("Caught exception ", e);
1036 throw new DocumentException(e);
1038 if (repoSession != null) {
1039 releaseRepositorySession(ctx, repoSession);
1045 * Save a documentModel to the Nuxeo repository.
1046 * @param ctx service context under which this method is invoked
1047 * @param docModel the document to save
1048 * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
1049 * @throws DocumentException
1051 public void saveDocWithoutHandlerProcessing(
1052 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1053 RepositoryInstance repoSession,
1054 DocumentModel docModel,
1055 boolean fSaveSession)
1056 throws ClientException, DocumentException {
1059 repoSession.saveDocument(docModel);
1063 } catch (ClientException ce) {
1065 } catch (Exception e) {
1066 if (logger.isDebugEnabled()) {
1067 logger.debug("Caught exception ", e);
1069 throw new DocumentException(e);
1075 * Save a list of documentModels to the Nuxeo repository.
1077 * @param ctx service context under which this method is invoked
1078 * @param docModel the document to save
1079 * @param fSaveSession if TRUE, will call CoreSession.save() to save accumulated changes.
1080 * @throws DocumentException
1082 public void saveDocListWithoutHandlerProcessing(
1083 ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
1084 RepositoryInstance repoSession,
1085 DocumentModelList docList,
1086 boolean fSaveSession)
1087 throws ClientException, DocumentException {
1089 DocumentModel[] docModelArray = new DocumentModel[docList.size()];
1090 repoSession.saveDocuments(docList.toArray(docModelArray));
1094 } catch (ClientException ce) {
1096 } catch (Exception e) {
1097 logger.error("Caught exception ", e);
1098 throw new DocumentException(e);
1103 * delete a document from the Nuxeo repository
1104 * @param ctx service context under which this method is invoked
1107 * @throws DocumentException
1110 public void delete(ServiceContext ctx, String id, DocumentHandler handler) throws DocumentNotFoundException,
1113 throw new IllegalArgumentException(
1114 "delete(ctx, ix, handler): ctx is missing");
1116 if (handler == null) {
1117 throw new IllegalArgumentException(
1118 "delete(ctx, ix, handler): handler is missing");
1120 if (logger.isDebugEnabled()) {
1121 logger.debug("Deleting document with CSID=" + id);
1123 RepositoryInstance repoSession = null;
1125 handler.prepare(Action.DELETE);
1126 repoSession = getRepositorySession(ctx);
1127 DocumentWrapper<DocumentModel> wrapDoc = null;
1129 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
1130 wrapDoc = new DocumentWrapperImpl<DocumentModel>(repoSession.getDocument(docRef));
1131 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
1132 handler.handle(Action.DELETE, wrapDoc);
1133 repoSession.removeDocument(docRef);
1134 } catch (ClientException ce) {
1135 String msg = logException(ce, "Could not find document to delete with CSID=" + id);
1136 throw new DocumentNotFoundException(msg, ce);
1139 handler.complete(Action.DELETE, wrapDoc);
1140 } catch (DocumentException de) {
1142 } catch (Exception e) {
1143 if (logger.isDebugEnabled()) {
1144 logger.debug("Caught exception ", e);
1146 throw new DocumentException(e);
1148 if (repoSession != null) {
1149 releaseRepositorySession(ctx, repoSession);
1155 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
1159 public void delete(@SuppressWarnings("rawtypes") ServiceContext ctx, String id)
1160 throws DocumentNotFoundException, DocumentException {
1161 throw new UnsupportedOperationException();
1162 // Use the other delete instead
1166 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
1167 return NuxeoConnectorEmbedded.getInstance().retrieveWorkspaceIds(domainName);
1171 public String createDomain(String domainName) throws Exception {
1172 RepositoryInstance repoSession = null;
1173 String domainId = null;
1176 // First create the top-level domain directory
1178 repoSession = getRepositorySession(null);
1179 DocumentRef parentDocRef = new PathRef("/");
1180 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
1181 DocumentModel domainDoc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1182 domainName, NUXEO_CORE_TYPE_DOMAIN);
1183 domainDoc.setPropertyValue("dc:title", domainName);
1184 domainDoc.setPropertyValue("dc:description", "A CollectionSpace domain "
1186 domainDoc = repoSession.createDocument(domainDoc);
1187 domainId = domainDoc.getId();
1190 // Next, create a "Workspaces" root directory to contain the workspace folders for the individual service documents
1192 DocumentModel workspacesRoot = repoSession.createDocumentModel(domainDoc.getPathAsString(),
1193 NuxeoUtils.Workspaces, NUXEO_CORE_TYPE_WORKSPACEROOT);
1194 workspacesRoot.setPropertyValue("dc:title", NuxeoUtils.Workspaces);
1195 workspacesRoot.setPropertyValue("dc:description", "A CollectionSpace workspaces directory for "
1196 + domainDoc.getPathAsString());
1197 workspacesRoot = repoSession.createDocument(workspacesRoot);
1198 String workspacesRootId = workspacesRoot.getId();
1201 if (logger.isDebugEnabled()) {
1202 logger.debug("Created tenant domain name=" + domainName
1203 + " id=" + domainId + " " +
1204 NuxeoUtils.Workspaces + " id=" + workspacesRootId);
1205 logger.debug("Path to Domain: "+domainDoc.getPathAsString());
1206 logger.debug("Path to Workspaces root: "+workspacesRoot.getPathAsString());
1208 } catch (Exception e) {
1209 if (logger.isDebugEnabled()) {
1210 logger.debug("Could not create tenant domain name=" + domainName + " caught exception ", e);
1214 if (repoSession != null) {
1215 releaseRepositorySession(null, repoSession);
1223 public String getDomainId(String domainName) throws Exception {
1224 String domainId = null;
1225 RepositoryInstance repoSession = null;
1227 if (domainName != null && !domainName.isEmpty()) {
1229 repoSession = getRepositorySession(null);
1230 DocumentRef docRef = new PathRef(
1232 DocumentModel domain = repoSession.getDocument(docRef);
1233 domainId = domain.getId();
1234 } catch (Exception e) {
1235 if (logger.isTraceEnabled()) {
1236 logger.trace("Caught exception ", e);
1238 //there is no way to identify if document does not exist due to
1239 //lack of typed exception for getDocument method
1242 if (repoSession != null) {
1243 releaseRepositorySession(null, repoSession);
1252 * Returns the workspaces root directory for a given domain.
1254 private DocumentModel getWorkspacesRoot(RepositoryInstance repoSession,
1255 String domainName) throws Exception {
1256 DocumentModel result = null;
1258 String domainPath = "/" + domainName;
1259 DocumentRef parentDocRef = new PathRef(domainPath);
1260 DocumentModelList domainChildrenList = repoSession.getChildren(
1262 Iterator<DocumentModel> witer = domainChildrenList.iterator();
1263 while (witer.hasNext()) {
1264 DocumentModel childNode = witer.next();
1265 if (NuxeoUtils.Workspaces.equalsIgnoreCase(childNode.getName())) {
1267 logger.trace("Found workspaces directory at: " + result.getPathAsString());
1272 if (result == null) {
1273 throw new ClientException("Could not find workspace root directory in: "
1281 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
1284 public String createWorkspace(String domainName, String workspaceName) throws Exception {
1285 RepositoryInstance repoSession = null;
1286 String workspaceId = null;
1288 repoSession = getRepositorySession(null);
1289 DocumentModel parentDoc = getWorkspacesRoot(repoSession, domainName);
1291 if (logger.isTraceEnabled()) {
1292 for (String facet : parentDoc.getFacets()) {
1293 logger.debug("Facet: " + facet);
1297 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1298 workspaceName, NuxeoUtils.WORKSPACE_DOCUMENT_TYPE);
1299 doc.setPropertyValue("dc:title", workspaceName);
1300 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
1302 doc = repoSession.createDocument(doc);
1303 workspaceId = doc.getId();
1305 if (logger.isDebugEnabled()) {
1306 logger.debug("Created workspace name=" + workspaceName
1307 + " id=" + workspaceId);
1309 } catch (Exception e) {
1310 if (logger.isDebugEnabled()) {
1311 logger.debug("createWorkspace caught exception ", e);
1315 if (repoSession != null) {
1316 releaseRepositorySession(null, repoSession);
1323 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
1326 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
1327 String workspaceId = null;
1329 RepositoryInstance repoSession = null;
1331 repoSession = getRepositorySession(null);
1332 DocumentRef docRef = new PathRef(
1334 + "/" + NuxeoUtils.Workspaces
1335 + "/" + workspaceName);
1336 DocumentModel workspace = repoSession.getDocument(docRef);
1337 workspaceId = workspace.getId();
1338 } catch (DocumentException de) {
1340 } catch (Exception e) {
1341 if (logger.isDebugEnabled()) {
1342 logger.debug("Caught exception ", e);
1344 throw new DocumentException(e);
1346 if (repoSession != null) {
1347 releaseRepositorySession(null, repoSession);
1356 * Gets the repository session. - Package access only.
1358 * @return the repository session
1359 * @throws Exception the exception
1361 public RepositoryInstance getRepositorySession(ServiceContext ctx) throws Exception {
1362 RepositoryInstance repoSession = null;
1364 Profiler profiler = new Profiler("getRepositorySession():", 2);
1368 repoSession = (RepositoryInstance)ctx.getCurrentRepositorySession();
1369 if (logger.isDebugEnabled() == true) {
1370 if (repoSession != null) {
1371 logger.warn("Reusing the current context's repository session.");
1376 // If we couldn't find a repoSession from the service context then we need to create a new one
1377 if (repoSession == null) {
1378 NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1379 repoSession = client.openRepository();
1382 if (logger.isTraceEnabled()) {
1383 logger.trace("Testing call to getRepository() repository root: " + repoSession.getRootDocument());
1389 ctx.setCurrentRepositorySession(repoSession); // For reusing, save the repository session in the current service context
1396 * Release repository session. - Package access only.
1398 * @param repoSession the repo session
1400 public void releaseRepositorySession(ServiceContext ctx, RepositoryInstance repoSession) {
1402 NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1405 ctx.clearCurrentRepositorySession(); //clear the current context of the now closed repo session
1406 if (ctx.getCurrentRepositorySession() == null) {
1407 client.releaseRepository(repoSession); //release the repo session if the service context's ref count is zeo.
1410 client.releaseRepository(repoSession); //repo session was acquired without a service context
1412 } catch (Exception e) {
1413 logger.error("Could not close the repository session", e);
1414 // no need to throw this service specific exception
1419 public void doWorkflowTransition(ServiceContext ctx, String id,
1420 DocumentHandler handler, TransitionDef transitionDef)
1421 throws BadRequestException, DocumentNotFoundException,
1423 // 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