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.util.Hashtable;
21 import java.util.List;
22 import java.util.UUID;
23 import java.util.regex.Matcher;
24 import java.util.regex.Pattern;
25 import java.util.regex.PatternSyntaxException;
27 import org.collectionspace.services.common.context.ServiceContext;
28 import org.collectionspace.services.common.datetime.GregorianCalendarDateTimeUtils;
29 import org.collectionspace.services.common.query.IQueryManager;
30 import org.collectionspace.services.common.query.QueryContext;
31 import org.collectionspace.services.common.repository.RepositoryClient;
32 import org.collectionspace.services.common.profile.Profiler;
33 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
35 import org.collectionspace.services.common.document.BadRequestException;
36 import org.collectionspace.services.common.document.DocumentException;
37 import org.collectionspace.services.common.document.DocumentFilter;
38 import org.collectionspace.services.common.document.DocumentHandler;
39 import org.collectionspace.services.common.document.DocumentNotFoundException;
40 import org.collectionspace.services.common.document.DocumentHandler.Action;
41 import org.collectionspace.services.common.document.DocumentWrapper;
42 import org.collectionspace.services.common.document.DocumentWrapperImpl;
44 import org.jboss.resteasy.plugins.providers.multipart.MultipartInput;
45 import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput;
47 import org.nuxeo.common.utils.IdUtils;
48 import org.nuxeo.ecm.core.api.ClientException;
49 import org.nuxeo.ecm.core.api.DocumentModel;
50 import org.nuxeo.ecm.core.api.DocumentModelList;
51 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
52 import org.nuxeo.ecm.core.api.DocumentRef;
53 import org.nuxeo.ecm.core.api.IdRef;
54 import org.nuxeo.ecm.core.api.PathRef;
55 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
56 import org.nuxeo.ecm.core.client.NuxeoClient;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
62 * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
63 * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
66 * $LastChangedRevision: $ $LastChangedDate: $
68 public class RepositoryJavaClientImpl implements RepositoryClient {
71 private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
72 // private final Logger profilerLogger = LoggerFactory.getLogger("remperf");
73 // private String foo = Profiler.createLogger();
75 // Regular expressions pattern for identifying valid ORDER BY clauses.
76 // FIXME: Currently supports only USASCII word characters in field names.
77 final String ORDER_BY_CLAUSE_REGEX = "\\w+(_\\w+)?:\\w+( ASC| DESC)?(, \\w+(_\\w+)?:\\w+( ASC| DESC)?)*";
80 * Instantiates a new repository java client impl.
82 public RepositoryJavaClientImpl() {
88 * Sets the collection space core values.
91 * @param documentModel the document model
92 * @throws ClientException the client exception
94 private void setCollectionSpaceCoreValues(ServiceContext<MultipartInput, MultipartOutput> ctx,
95 DocumentModel documentModel,
96 Action action) throws ClientException {
98 // Add the tenant ID value to the new entity
100 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
101 DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID,
104 String now = GregorianCalendarDateTimeUtils.timestampUTC();
108 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
109 DocumentModelHandler.COLLECTIONSPACE_CORE_CREATED_AT,
111 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
112 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
116 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
117 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
126 * create document in the Nuxeo repository
128 * @param ctx service context under which this method is invoked
130 * should be used by the caller to provide and transform the
132 * @return id in repository of the newly created document
133 * @throws DocumentException
136 public String create(ServiceContext ctx,
137 DocumentHandler handler) throws BadRequestException,
140 if (ctx.getDocumentType() == null) {
141 throw new IllegalArgumentException(
142 "RepositoryJavaClient.create: docType is missing");
144 if (handler == null) {
145 throw new IllegalArgumentException(
146 "RepositoryJavaClient.create: handler is missing");
148 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
149 if (nuxeoWspaceId == null) {
150 throw new DocumentNotFoundException(
151 "Unable to find workspace for service " + ctx.getServiceName()
152 + " check if the workspace exists in the Nuxeo repository");
154 RepositoryInstance repoSession = null;
156 handler.prepare(Action.CREATE);
157 repoSession = getRepositorySession();
158 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
159 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
160 String wspacePath = wspaceDoc.getPathAsString();
161 //give our own ID so PathRef could be constructed later on
162 String id = IdUtils.generateId(UUID.randomUUID().toString());
163 // create document model
164 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
165 ctx.getDocumentType());
166 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
167 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
168 handler.handle(Action.CREATE, wrapDoc);
169 // create document with documentmodel
170 setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
171 doc = repoSession.createDocument(doc);
173 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
174 // and assume the handler has the state it needs (doc fragments).
175 handler.complete(Action.CREATE, wrapDoc);
177 } catch (BadRequestException bre) {
179 } catch (Exception e) {
180 if (logger.isDebugEnabled()) {
181 logger.debug("Caught exception ", e);
183 throw new DocumentException(e);
185 if (repoSession != null) {
186 releaseRepositorySession(repoSession);
193 * get document from the Nuxeo repository
194 * @param ctx service context under which this method is invoked
196 * of the document to retrieve
198 * should be used by the caller to provide and transform the
200 * @throws DocumentException
203 public void get(ServiceContext ctx, String id, DocumentHandler handler)
204 throws DocumentNotFoundException, DocumentException {
206 if (handler == null) {
207 throw new IllegalArgumentException(
208 "RepositoryJavaClient.get: handler is missing");
210 RepositoryInstance repoSession = null;
213 handler.prepare(Action.GET);
214 repoSession = getRepositorySession();
215 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
216 DocumentModel doc = null;
218 doc = repoSession.getDocument(docRef);
219 } catch (ClientException ce) {
220 String msg = "could not find document with id=" + id;
221 logger.error(msg, ce);
222 throw new DocumentNotFoundException(msg, ce);
224 //set reposession to handle the document
225 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
226 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
227 handler.handle(Action.GET, wrapDoc);
228 handler.complete(Action.GET, wrapDoc);
229 } catch (IllegalArgumentException iae) {
231 } catch (DocumentException de) {
233 } catch (Exception e) {
234 if (logger.isDebugEnabled()) {
235 logger.debug("Caught exception ", e);
237 throw new DocumentException(e);
239 if (repoSession != null) {
240 releaseRepositorySession(repoSession);
246 * get document from the Nuxeo repository, using the docFilter params.
247 * @param ctx service context under which this method is invoked
249 * should be used by the caller to provide and transform the
250 * document. Handler must have a docFilter set to return a single item.
251 * @throws DocumentException
254 public void get(ServiceContext ctx, DocumentHandler handler)
255 throws DocumentNotFoundException, DocumentException {
256 QueryContext queryContext = new QueryContext(ctx, handler);
257 RepositoryInstance repoSession = null;
260 handler.prepare(Action.GET);
261 repoSession = getRepositorySession();
263 DocumentModelList docList = null;
264 // force limit to 1, and ignore totalSize
265 String query = buildNXQLQuery(queryContext);
266 docList = repoSession.query(query, null, 1, 0, false);
267 if (docList.size() != 1) {
268 throw new DocumentNotFoundException("No document found matching filter params.");
270 DocumentModel doc = docList.get(0);
272 if (logger.isDebugEnabled()) {
273 logger.debug("Executed NXQL query: " + query);
276 //set reposession to handle the document
277 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
278 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
279 handler.handle(Action.GET, wrapDoc);
280 handler.complete(Action.GET, wrapDoc);
281 } catch (IllegalArgumentException iae) {
283 } catch (DocumentException de) {
285 } catch (Exception e) {
286 if (logger.isDebugEnabled()) {
287 logger.debug("Caught exception ", e);
289 throw new DocumentException(e);
291 if (repoSession != null) {
292 releaseRepositorySession(repoSession);
298 * get wrapped documentModel from the Nuxeo repository
299 * @param ctx service context under which this method is invoked
301 * of the document to retrieve
302 * @throws DocumentException
305 public DocumentWrapper<DocumentModel> getDoc(
306 ServiceContext ctx, String id)
307 throws DocumentNotFoundException, DocumentException {
308 RepositoryInstance repoSession = null;
309 DocumentWrapper<DocumentModel> wrapDoc = null;
312 repoSession = getRepositorySession();
313 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
314 DocumentModel doc = null;
316 doc = repoSession.getDocument(docRef);
317 } catch (ClientException ce) {
318 String msg = "could not find document with id=" + id;
319 logger.error(msg, ce);
320 throw new DocumentNotFoundException(msg, ce);
322 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
323 } catch (IllegalArgumentException iae) {
325 } catch (DocumentException de) {
327 } catch (Exception e) {
328 if (logger.isDebugEnabled()) {
329 logger.debug("Caught exception ", e);
331 throw new DocumentException(e);
333 if (repoSession != null) {
334 releaseRepositorySession(repoSession);
341 * find wrapped documentModel from the Nuxeo repository
342 * @param ctx service context under which this method is invoked
343 * @param whereClause where NXQL where clause to get the document
344 * @throws DocumentException
347 public DocumentWrapper<DocumentModel> findDoc(
348 ServiceContext ctx, String whereClause)
349 throws DocumentNotFoundException, DocumentException {
350 RepositoryInstance repoSession = null;
351 DocumentWrapper<DocumentModel> wrapDoc = null;
354 QueryContext queryContext = new QueryContext(ctx, whereClause);
355 repoSession = getRepositorySession();
356 DocumentModelList docList = null;
357 // force limit to 1, and ignore totalSize
358 String query = buildNXQLQuery(queryContext);
359 docList = repoSession.query(query,
364 if (docList.size() != 1) {
365 if (logger.isDebugEnabled()) {
366 logger.debug("findDoc: Query found: " + docList.size() + " items.");
367 logger.debug(" Query: " + query);
369 throw new DocumentNotFoundException("No document found matching filter params.");
371 DocumentModel doc = docList.get(0);
372 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
373 } catch (IllegalArgumentException iae) {
375 } catch (DocumentException de) {
377 } catch (Exception e) {
378 if (logger.isDebugEnabled()) {
379 logger.debug("Caught exception ", e);
381 throw new DocumentException(e);
383 if (repoSession != null) {
384 releaseRepositorySession(repoSession);
391 * find doc and return CSID from the Nuxeo repository
392 * @param ctx service context under which this method is invoked
393 * @param whereClause where NXQL where clause to get the document
394 * @throws DocumentException
397 public String findDocCSID(
398 ServiceContext ctx, String whereClause)
399 throws DocumentNotFoundException, DocumentException {
402 DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
403 DocumentModel docModel = wrapDoc.getWrappedObject();
404 csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
405 } catch (DocumentNotFoundException dnfe) {
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);
421 * Find a list of documentModels from the Nuxeo repository
422 * @param docTypes a list of DocType names to match
423 * @param whereClause where the clause to qualify on
427 public DocumentWrapper<DocumentModelList> findDocs(
429 List<String> docTypes,
431 int pageSize, int pageNum, boolean computeTotal)
432 throws DocumentNotFoundException, DocumentException {
433 RepositoryInstance repoSession = null;
434 DocumentWrapper<DocumentModelList> wrapDoc = null;
437 if (docTypes == null || docTypes.size() < 1) {
438 throw new DocumentNotFoundException(
439 "findDocs must specify at least one DocumentType.");
441 repoSession = getRepositorySession();
442 DocumentModelList docList = null;
443 // force limit to 1, and ignore totalSize
444 QueryContext queryContext = new QueryContext(ctx, whereClause);
445 String query = buildNXQLQuery(docTypes, queryContext);
446 docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
447 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
448 } catch (IllegalArgumentException iae) {
450 } catch (Exception e) {
451 if (logger.isDebugEnabled()) {
452 logger.debug("Caught exception ", e);
454 throw new DocumentException(e);
456 if (repoSession != null) {
457 releaseRepositorySession(repoSession);
464 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
467 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
468 throws DocumentNotFoundException, DocumentException {
469 if (handler == null) {
470 throw new IllegalArgumentException(
471 "RepositoryJavaClient.getAll: handler is missing");
474 RepositoryInstance repoSession = null;
477 handler.prepare(Action.GET_ALL);
478 repoSession = getRepositorySession();
479 DocumentModelList docModelList = new DocumentModelListImpl();
480 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
481 for (String csid : csidList) {
482 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
483 DocumentModel docModel = repoSession.getDocument(docRef);
484 docModelList.add(docModel);
487 //set reposession to handle the document
488 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
489 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
490 handler.handle(Action.GET_ALL, wrapDoc);
491 handler.complete(Action.GET_ALL, wrapDoc);
492 } catch (DocumentException de) {
494 } catch (Exception e) {
495 if (logger.isDebugEnabled()) {
496 logger.debug("Caught exception ", e);
498 throw new DocumentException(e);
500 if (repoSession != null) {
501 releaseRepositorySession(repoSession);
507 * getAll get all documents for an entity entity service from the Nuxeo
510 * @param ctx service context under which this method is invoked
512 * should be used by the caller to provide and transform the
514 * @throws DocumentException
517 public void getAll(ServiceContext ctx, DocumentHandler handler)
518 throws DocumentNotFoundException, DocumentException {
519 if (handler == null) {
520 throw new IllegalArgumentException(
521 "RepositoryJavaClient.getAll: handler is missing");
523 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
524 if (nuxeoWspaceId == null) {
525 throw new DocumentNotFoundException(
526 "Unable to find workspace for service "
527 + ctx.getServiceName()
528 + " check if the workspace exists in the Nuxeo repository");
530 RepositoryInstance repoSession = null;
533 handler.prepare(Action.GET_ALL);
534 repoSession = getRepositorySession();
535 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
536 DocumentModelList docList = repoSession.getChildren(wsDocRef);
537 //set reposession to handle the document
538 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
539 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
540 handler.handle(Action.GET_ALL, wrapDoc);
541 handler.complete(Action.GET_ALL, wrapDoc);
542 } catch (DocumentException de) {
544 } catch (Exception e) {
545 if (logger.isDebugEnabled()) {
546 logger.debug("Caught exception ", e);
548 throw new DocumentException(e);
550 if (repoSession != null) {
551 releaseRepositorySession(repoSession);
557 * getFiltered get all documents for an entity service from the Document repository,
558 * given filter parameters specified by the handler.
559 * @param ctx service context under which this method is invoked
560 * @param handler should be used by the caller to provide and transform the document
561 * @throws DocumentNotFoundException if workspace not found
562 * @throws DocumentException
565 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
566 throws DocumentNotFoundException, DocumentException {
568 DocumentFilter filter = handler.getDocumentFilter();
569 String oldOrderBy = filter.getOrderByClause();
570 if (oldOrderBy!=null && oldOrderBy.isEmpty()){
571 filter.setOrderByClause("collectionspace_core:updatedAt DESC"); //per http://issues.collectionspace.org/browse/CSPACE-705
573 QueryContext queryContext = new QueryContext(ctx, handler);
574 RepositoryInstance repoSession = null;
576 handler.prepare(Action.GET_ALL);
577 repoSession = getRepositorySession();
578 DocumentModelList docList = null;
579 String query = buildNXQLQuery(queryContext);
581 if (logger.isDebugEnabled()) {
582 logger.debug("Executing NXQL query: " + query.toString());
585 // If we have limit and/or offset, then pass true to get totalSize
586 // in returned DocumentModelList.
587 Profiler profiler = new Profiler(this, 2);
588 profiler.log("Executing NXQL query: " + query.toString());
590 if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
591 docList = repoSession.query(query, null,
592 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
594 docList = repoSession.query(query);
598 //set repoSession to handle the document
599 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
600 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
601 handler.handle(Action.GET_ALL, wrapDoc);
602 handler.complete(Action.GET_ALL, wrapDoc);
603 } catch (DocumentException de) {
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(repoSession);
618 * update given document in the Nuxeo repository
620 * @param ctx service context under which this method is invoked
624 * should be used by the caller to provide and transform the
626 * @throws DocumentException
629 public void update(ServiceContext ctx, String id, DocumentHandler handler)
630 throws BadRequestException, DocumentNotFoundException,
632 if (handler == null) {
633 throw new IllegalArgumentException(
634 "RepositoryJavaClient.update: handler is missing");
636 RepositoryInstance repoSession = null;
638 handler.prepare(Action.UPDATE);
639 repoSession = getRepositorySession();
640 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
641 DocumentModel doc = null;
643 doc = repoSession.getDocument(docRef);
644 } catch (ClientException ce) {
645 String msg = "Could not find document to update with id=" + id;
646 logger.error(msg, ce);
647 throw new DocumentNotFoundException(msg, ce);
649 //set reposession to handle the document
650 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
651 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
652 handler.handle(Action.UPDATE, wrapDoc);
653 setCollectionSpaceCoreValues(ctx, doc, Action.UPDATE);
654 repoSession.saveDocument(doc);
656 handler.complete(Action.UPDATE, wrapDoc);
657 } catch (BadRequestException bre) {
659 } catch (DocumentException de) {
661 } catch (Exception e) {
662 if (logger.isDebugEnabled()) {
663 logger.debug("Caught exception ", e);
665 throw new DocumentException(e);
667 if (repoSession != null) {
668 releaseRepositorySession(repoSession);
674 * delete a document from the Nuxeo repository
675 * @param ctx service context under which this method is invoked
678 * @throws DocumentException
681 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
684 if (logger.isDebugEnabled()) {
685 logger.debug("deleting document with id=" + id);
687 RepositoryInstance repoSession = null;
689 repoSession = getRepositorySession();
690 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
692 repoSession.removeDocument(docRef);
693 } catch (ClientException ce) {
694 String msg = "could not find document to delete with id=" + id;
695 logger.error(msg, ce);
696 throw new DocumentNotFoundException(msg, ce);
699 } catch (DocumentException de) {
701 } catch (Exception e) {
702 if (logger.isDebugEnabled()) {
703 logger.debug("Caught exception ", e);
705 throw new DocumentException(e);
707 if (repoSession != null) {
708 releaseRepositorySession(repoSession);
714 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
717 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
718 throws DocumentNotFoundException, DocumentException {
719 throw new UnsupportedOperationException();
723 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
724 return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
728 public String createDomain(String domainName) throws Exception {
729 RepositoryInstance repoSession = null;
730 String domainId = null;
732 repoSession = getRepositorySession();
733 DocumentRef parentDocRef = new PathRef("/");
734 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
735 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
736 domainName, "Domain");
737 doc.setPropertyValue("dc:title", domainName);
738 doc.setPropertyValue("dc:description", "A CollectionSpace domain "
740 doc = repoSession.createDocument(doc);
741 domainId = doc.getId();
743 if (logger.isDebugEnabled()) {
744 logger.debug("created tenant domain name=" + domainName
745 + " id=" + domainId);
747 } catch (Exception e) {
748 if (logger.isDebugEnabled()) {
749 logger.debug("createTenantSpace caught exception ", e);
753 if (repoSession != null) {
754 releaseRepositorySession(repoSession);
761 public String getDomainId(String domainName) throws Exception {
762 String domainId = null;
763 RepositoryInstance repoSession = null;
765 repoSession = getRepositorySession();
766 DocumentRef docRef = new PathRef(
768 DocumentModel domain = repoSession.getDocument(docRef);
769 domainId = domain.getId();
770 } catch (Exception e) {
771 if (logger.isDebugEnabled()) {
772 logger.debug("Caught exception ", e);
774 //there is no way to identify if document does not exist due to
775 //lack of typed exception for getDocument method
778 if (repoSession != null) {
779 releaseRepositorySession(repoSession);
786 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
789 public String createWorkspace(String domainName, String workspaceName) throws Exception {
790 RepositoryInstance repoSession = null;
791 String workspaceId = null;
793 repoSession = getRepositorySession();
794 DocumentRef parentDocRef = new PathRef(
796 + "/" + "workspaces");
797 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
798 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
799 workspaceName, "Workspace");
800 doc.setPropertyValue("dc:title", workspaceName);
801 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
803 doc = repoSession.createDocument(doc);
804 workspaceId = doc.getId();
806 if (logger.isDebugEnabled()) {
807 logger.debug("created workspace name=" + workspaceName
808 + " id=" + workspaceId);
810 } catch (Exception e) {
811 if (logger.isDebugEnabled()) {
812 logger.debug("createWorkspace caught exception ", e);
816 if (repoSession != null) {
817 releaseRepositorySession(repoSession);
824 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
827 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
828 String workspaceId = null;
829 RepositoryInstance repoSession = null;
831 repoSession = getRepositorySession();
832 DocumentRef docRef = new PathRef(
835 + "/" + workspaceName);
836 DocumentModel workspace = repoSession.getDocument(docRef);
837 workspaceId = workspace.getId();
838 } catch (DocumentException de) {
840 } catch (Exception e) {
841 if (logger.isDebugEnabled()) {
842 logger.debug("Caught exception ", e);
844 throw new DocumentException(e);
846 if (repoSession != null) {
847 releaseRepositorySession(repoSession);
854 * Append a WHERE clause to the NXQL query.
856 * @param query The NXQL query to which the WHERE clause will be appended.
857 * @param queryContext The query context, which provides the WHERE clause to append.
859 private final void appendNXQLWhere(StringBuilder query, QueryContext queryContext) {
861 // Restrict search to a specific Nuxeo domain
862 // TODO This is a slow method for tenant-filter
863 // We should make this a property that is indexed.
865 // query.append(" WHERE ecm:path STARTSWITH '/" + queryContext.domain + "'");
868 // Restrict search to the current tenant ID. Is the domain path filter (above) still needed?
870 query.append(/*IQueryManager.SEARCH_QUALIFIER_AND +*/ " WHERE " + DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA + ":"
871 + DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID
872 + " = " + queryContext.getTenantId());
874 // Finally, append the incoming where clause
876 String whereClause = queryContext.getWhereClause();
877 if (whereClause != null && ! whereClause.trim().isEmpty()) {
878 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
879 // into SQL, we need to parenthesize our 'where' clause
880 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "(" + whereClause + ")");
883 // Please lookup this use in Nuxeo support and document here
885 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "ecm:isProxy = 0");
889 * Append an ORDER BY clause to the NXQL query.
891 * @param query the NXQL query to which the ORDER BY clause will be appended.
892 * @param queryContext the query context, which provides the ORDER BY clause to append.
894 * @throws DocumentException if the supplied value of the orderBy clause is not valid.
897 private final void appendNXQLOrderBy(StringBuilder query, QueryContext queryContext)
899 String orderByClause = queryContext.getOrderByClause();
900 if (orderByClause != null && ! orderByClause.trim().isEmpty()) {
901 if (isValidOrderByClause(orderByClause)) {
902 query.append(" ORDER BY ");
903 query.append(orderByClause);
905 throw new DocumentException("Invalid format in sort request '" + orderByClause
906 + "': must be schema_name:fieldName followed by optional sort order (' ASC' or ' DESC').");
912 * Identifies whether the ORDER BY clause is valid.
914 * @param orderByClause the ORDER BY clause.
916 * @return true if the ORDER BY clause is valid;
917 * false if it is not.
919 private final boolean isValidOrderByClause(String orderByClause) {
920 boolean isValidClause = false;
922 Pattern orderByPattern = Pattern.compile(ORDER_BY_CLAUSE_REGEX);
923 Matcher orderByMatcher = orderByPattern.matcher(orderByClause);
924 if (orderByMatcher.matches()) {
925 isValidClause = true;
927 } catch (PatternSyntaxException pe) {
928 logger.warn("ORDER BY clause regex pattern '" + ORDER_BY_CLAUSE_REGEX
929 + "' could not be compiled: " + pe.getMessage());
930 // If reached, method will return a value of false.
932 return isValidClause;
937 * Builds an NXQL SELECT query for a single document type.
939 * @param queryContext The query context
940 * @return an NXQL query
941 * @throws Exception if supplied values in the query are invalid.
943 private final String buildNXQLQuery(QueryContext queryContext) throws Exception {
944 StringBuilder query = new StringBuilder("SELECT * FROM ");
945 query.append(queryContext.getDocType());
946 appendNXQLWhere(query, queryContext);
947 appendNXQLOrderBy(query, queryContext);
948 return query.toString();
952 * Builds an NXQL SELECT query across multiple document types.
954 * @param docTypes a list of document types to be queried
955 * @param queryContext the query context
956 * @return an NXQL query
958 private final String buildNXQLQuery(List<String> docTypes, QueryContext queryContext) {
959 StringBuilder query = new StringBuilder("SELECT * FROM ");
960 boolean fFirst = true;
961 for (String docType : docTypes) {
967 query.append(docType);
969 appendNXQLWhere(query, queryContext);
970 // FIXME add 'order by' clause here, if appropriate
971 return query.toString();
975 * Gets the repository session.
977 * @return the repository session
978 * @throws Exception the exception
980 private RepositoryInstance getRepositorySession() throws Exception {
981 // FIXME: is it possible to reuse repository session?
982 // Authentication failures happen while trying to reuse the session
983 Profiler profiler = new Profiler("getRepositorySession():", 2);
985 NuxeoClient client = NuxeoConnector.getInstance().getClient();
986 RepositoryInstance repoSession = client.openRepository();
987 if (logger.isTraceEnabled()) {
988 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
995 * Release repository session.
997 * @param repoSession the repo session
999 private void releaseRepositorySession(RepositoryInstance repoSession) {
1001 NuxeoClient client = NuxeoConnector.getInstance().getClient();
1003 client.releaseRepository(repoSession);
1004 } catch (Exception e) {
1005 logger.error("Could not close the repository session", e);
1006 // no need to throw this service specific exception