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;
51 import org.collectionspace.services.config.tenant.RepositoryDomainType;
53 import org.nuxeo.common.utils.IdUtils;
54 import org.nuxeo.ecm.core.api.ClientException;
55 import org.nuxeo.ecm.core.api.DocumentModel;
56 import org.nuxeo.ecm.core.api.DocumentModelList;
57 import org.nuxeo.ecm.core.api.IterableQueryResult;
58 import org.nuxeo.ecm.core.api.VersioningOption;
59 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
60 import org.nuxeo.ecm.core.api.DocumentRef;
61 import org.nuxeo.ecm.core.api.IdRef;
62 import org.nuxeo.ecm.core.api.PathRef;
63 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
64 import org.nuxeo.runtime.transaction.TransactionRuntimeException;
67 // CSPACE-5036 - How to make CMISQL queries from Nuxeo
69 import org.apache.chemistry.opencmis.commons.server.CallContext;
70 import org.apache.chemistry.opencmis.server.impl.CallContextImpl;
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(RepositoryDomainType repoDomain) throws Exception {
1198 return NuxeoConnectorEmbedded.getInstance().retrieveWorkspaceIds(repoDomain);
1202 public String createDomain(RepositoryDomainType repositoryDomain) throws Exception {
1203 RepositoryInstance repoSession = null;
1204 String domainId = null;
1207 // Open a connection to the domain's repo/db
1209 String repoName = repositoryDomain.getRepositoryName();
1210 repoSession = getRepositorySession(repoName); // domainName=storageName=repoName=databaseName
1212 // First create the top-level domain directory
1214 String domainName = repositoryDomain.getStorageName();
1215 DocumentRef parentDocRef = new PathRef("/");
1216 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
1217 DocumentModel domainDoc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1218 domainName, NUXEO_CORE_TYPE_DOMAIN);
1219 domainDoc.setPropertyValue("dc:title", domainName);
1220 domainDoc.setPropertyValue("dc:description", "A CollectionSpace domain "
1222 domainDoc = repoSession.createDocument(domainDoc);
1223 domainId = domainDoc.getId();
1226 // Next, create a "Workspaces" root directory to contain the workspace folders for the individual service documents
1228 DocumentModel workspacesRoot = repoSession.createDocumentModel(domainDoc.getPathAsString(),
1229 NuxeoUtils.Workspaces, NUXEO_CORE_TYPE_WORKSPACEROOT);
1230 workspacesRoot.setPropertyValue("dc:title", NuxeoUtils.Workspaces);
1231 workspacesRoot.setPropertyValue("dc:description", "A CollectionSpace workspaces directory for "
1232 + domainDoc.getPathAsString());
1233 workspacesRoot = repoSession.createDocument(workspacesRoot);
1234 String workspacesRootId = workspacesRoot.getId();
1237 if (logger.isDebugEnabled()) {
1238 logger.debug("Created tenant domain name=" + domainName
1239 + " id=" + domainId + " " +
1240 NuxeoUtils.Workspaces + " id=" + workspacesRootId);
1241 logger.debug("Path to Domain: "+domainDoc.getPathAsString());
1242 logger.debug("Path to Workspaces root: "+workspacesRoot.getPathAsString());
1244 } catch (Exception e) {
1245 if (logger.isDebugEnabled()) {
1246 logger.debug("Could not create tenant domain name=" + repositoryDomain.getStorageName() + " caught exception ", e);
1250 if (repoSession != null) {
1251 releaseRepositorySession(null, repoSession);
1259 public String getDomainId(RepositoryDomainType repositoryDomain) throws Exception {
1260 String domainId = null;
1261 RepositoryInstance repoSession = null;
1263 String repoName = repositoryDomain.getRepositoryName();
1264 String domainStorageName = repositoryDomain.getStorageName();
1265 if (domainStorageName != null && !domainStorageName.isEmpty()) {
1267 repoSession = getRepositorySession(repoName);
1268 DocumentRef docRef = new PathRef("/" + domainStorageName);
1269 DocumentModel domain = repoSession.getDocument(docRef);
1270 domainId = domain.getId();
1271 } catch (Exception e) {
1272 if (logger.isTraceEnabled()) {
1273 logger.trace("Caught exception ", e); // The document doesn't exist, this let's us know we need to create it
1275 //there is no way to identify if document does not exist due to
1276 //lack of typed exception for getDocument method
1279 if (repoSession != null) {
1280 releaseRepositorySession(null, repoSession);
1289 * Returns the workspaces root directory for a given domain.
1291 private DocumentModel getWorkspacesRoot(RepositoryInstance repoSession,
1292 String domainName) throws Exception {
1293 DocumentModel result = null;
1295 String domainPath = "/" + domainName;
1296 DocumentRef parentDocRef = new PathRef(domainPath);
1297 DocumentModelList domainChildrenList = repoSession.getChildren(
1299 Iterator<DocumentModel> witer = domainChildrenList.iterator();
1300 while (witer.hasNext()) {
1301 DocumentModel childNode = witer.next();
1302 if (NuxeoUtils.Workspaces.equalsIgnoreCase(childNode.getName())) {
1304 logger.trace("Found workspaces directory at: " + result.getPathAsString());
1309 if (result == null) {
1310 throw new ClientException("Could not find workspace root directory in: "
1318 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
1321 public String createWorkspace(RepositoryDomainType repositoryDomain, String workspaceName) throws Exception {
1322 RepositoryInstance repoSession = null;
1323 String workspaceId = null;
1325 String repoName = repositoryDomain.getRepositoryName();
1326 repoSession = getRepositorySession(repoName);
1328 String domainStorageName = repositoryDomain.getStorageName();
1329 DocumentModel parentDoc = getWorkspacesRoot(repoSession, domainStorageName);
1330 if (logger.isTraceEnabled()) {
1331 for (String facet : parentDoc.getFacets()) {
1332 logger.trace("Facet: " + facet);
1336 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
1337 workspaceName, NuxeoUtils.WORKSPACE_DOCUMENT_TYPE);
1338 doc.setPropertyValue("dc:title", workspaceName);
1339 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
1341 doc = repoSession.createDocument(doc);
1342 workspaceId = doc.getId();
1344 if (logger.isDebugEnabled()) {
1345 logger.debug("Created workspace name=" + workspaceName
1346 + " id=" + workspaceId);
1348 } catch (Exception e) {
1349 if (logger.isDebugEnabled()) {
1350 logger.debug("createWorkspace caught exception ", e);
1354 if (repoSession != null) {
1355 releaseRepositorySession(null, repoSession);
1362 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
1366 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
1367 String workspaceId = null;
1369 RepositoryInstance repoSession = null;
1371 repoSession = getRepositorySession((ServiceContext)null);
1372 DocumentRef docRef = new PathRef(
1374 + "/" + NuxeoUtils.Workspaces
1375 + "/" + workspaceName);
1376 DocumentModel workspace = repoSession.getDocument(docRef);
1377 workspaceId = workspace.getId();
1378 } catch (DocumentException de) {
1380 } catch (Exception e) {
1381 if (logger.isDebugEnabled()) {
1382 logger.debug("Caught exception ", e);
1384 throw new DocumentException(e);
1386 if (repoSession != null) {
1387 releaseRepositorySession(null, repoSession);
1394 public RepositoryInstance getRepositorySession(ServiceContext ctx) throws Exception {
1395 return getRepositorySession(ctx, ctx.getRepositoryName());
1398 public RepositoryInstance getRepositorySession(String repoName) throws Exception {
1399 return getRepositorySession(null, repoName);
1403 * Gets the repository session. - Package access only. If the 'ctx' param is null then the repo name must be non-mull and vice-versa
1405 * @return the repository session
1406 * @throws Exception the exception
1408 public RepositoryInstance getRepositorySession(ServiceContext ctx, String repoName) throws Exception {
1409 RepositoryInstance repoSession = null;
1411 Profiler profiler = new Profiler("getRepositorySession():", 2);
1414 // To get a connection to the Nuxeo repo, we need either a valid ServiceContext instance or a repository name
1417 repoName = ctx.getRepositoryName(); // Notice we are overriding the passed in 'repoName' since we have a valid service context passed in to us
1418 repoSession = (RepositoryInstance)ctx.getCurrentRepositorySession(); // Look to see if one exists in the context before creating one
1419 } else if (repoName == null || repoName.trim().isEmpty()) {
1420 String errMsg = String.format("We can't get a connection to the Nuxeo repo because the service context passed in was null and no repository name was passed in either.");
1421 logger.error(errMsg);
1422 throw new Exception(errMsg);
1425 // If we couldn't find a repoSession from the service context (or the context was null) then we need to create a new one using
1426 // just the repo name
1428 if (repoSession == null) {
1429 NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1430 repoSession = client.openRepository(repoName);
1432 if (logger.isDebugEnabled() == true) {
1433 logger.warn("Reusing the current context's repository session.");
1437 if (logger.isTraceEnabled()) {
1438 logger.trace("Testing call to getRepository() repository root: " + repoSession.getRootDocument());
1444 ctx.setCurrentRepositorySession(repoSession); // For reusing, save the repository session in the current service context
1451 * Release repository session. - Package access only.
1453 * @param repoSession the repo session
1455 public void releaseRepositorySession(ServiceContext ctx, RepositoryInstance repoSession) throws TransactionException {
1457 NuxeoClientEmbedded client = NuxeoConnectorEmbedded.getInstance().getClient();
1460 ctx.clearCurrentRepositorySession(); //clear the current context of the now closed repo session
1461 if (ctx.getCurrentRepositorySession() == null) {
1462 client.releaseRepository(repoSession); //release the repo session if the service context's ref count is zeo.
1465 client.releaseRepository(repoSession); //repo session was acquired without a service context
1467 } catch (TransactionRuntimeException tre) {
1468 TransactionException te = new TransactionException(tre);
1469 logger.error(te.getMessage(), tre); // Log the standard transaction exception message, plus an exception-specific stack trace
1471 } catch (Exception e) {
1472 logger.error("Could not close the repository session", e);
1473 // no need to throw this service specific exception
1478 public void doWorkflowTransition(ServiceContext ctx, String id,
1479 DocumentHandler handler, TransitionDef transitionDef)
1480 throws BadRequestException, DocumentNotFoundException,
1482 // 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