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.query.IQueryManager;
29 import org.collectionspace.services.common.query.QueryContext;
30 import org.collectionspace.services.common.repository.RepositoryClient;
31 import org.collectionspace.services.common.profile.Profiler;
32 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
34 import org.collectionspace.services.common.document.BadRequestException;
35 import org.collectionspace.services.common.document.DocumentException;
36 import org.collectionspace.services.common.document.DocumentFilter;
37 import org.collectionspace.services.common.document.DocumentHandler;
38 import org.collectionspace.services.common.document.DocumentNotFoundException;
39 import org.collectionspace.services.common.document.DocumentHandler.Action;
40 import org.collectionspace.services.common.document.DocumentWrapper;
41 import org.collectionspace.services.common.document.DocumentWrapperImpl;
43 import org.jboss.resteasy.plugins.providers.multipart.MultipartInput;
44 import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput;
46 import org.nuxeo.common.utils.IdUtils;
47 import org.nuxeo.ecm.core.api.ClientException;
48 import org.nuxeo.ecm.core.api.DocumentModel;
49 import org.nuxeo.ecm.core.api.DocumentModelList;
50 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
51 import org.nuxeo.ecm.core.api.DocumentRef;
52 import org.nuxeo.ecm.core.api.IdRef;
53 import org.nuxeo.ecm.core.api.PathRef;
54 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
55 import org.nuxeo.ecm.core.client.NuxeoClient;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
61 * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
62 * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
65 * $LastChangedRevision: $ $LastChangedDate: $
67 public class RepositoryJavaClientImpl implements RepositoryClient {
70 private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
71 // private final Logger profilerLogger = LoggerFactory.getLogger("remperf");
72 // private String foo = Profiler.createLogger();
74 // Regular expressions pattern for identifying valid ORDER BY clauses.
75 // FIXME: Currently supports ordering on only one field.
76 // FIXME: Currently supports only USASCII word characters in field names.
77 final String ORDER_BY_CLAUSE_REGEX = "\\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,
105 //add creation date value
115 * create document in the Nuxeo repository
117 * @param ctx service context under which this method is invoked
119 * of the document created
121 * should be used by the caller to provide and transform the
123 * @return id in repository of the newly created document
124 * @throws DocumentException
127 public String create(ServiceContext ctx,
128 DocumentHandler handler) throws BadRequestException,
131 if (ctx.getDocumentType() == null) {
132 throw new IllegalArgumentException(
133 "RepositoryJavaClient.create: docType is missing");
135 if (handler == null) {
136 throw new IllegalArgumentException(
137 "RepositoryJavaClient.create: handler is missing");
139 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
140 if (nuxeoWspaceId == null) {
141 throw new DocumentNotFoundException(
142 "Unable to find workspace for service " + ctx.getServiceName()
143 + " check if the workspace exists in the Nuxeo repository");
145 RepositoryInstance repoSession = null;
147 handler.prepare(Action.CREATE);
148 repoSession = getRepositorySession();
149 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
150 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
151 String wspacePath = wspaceDoc.getPathAsString();
152 //give our own ID so PathRef could be constructed later on
153 String id = IdUtils.generateId(UUID.randomUUID().toString());
154 // create document model
155 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
156 ctx.getDocumentType());
157 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
158 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
159 handler.handle(Action.CREATE, wrapDoc);
160 // create document with documentmodel
161 setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
162 doc = repoSession.createDocument(doc);
164 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
165 // and assume the handler has the state it needs (doc fragments).
166 handler.complete(Action.CREATE, wrapDoc);
168 } catch (BadRequestException bre) {
170 } catch (Exception e) {
171 if (logger.isDebugEnabled()) {
172 logger.debug("Caught exception ", e);
174 throw new DocumentException(e);
176 if (repoSession != null) {
177 releaseRepositorySession(repoSession);
184 * get document from the Nuxeo repository
185 * @param ctx service context under which this method is invoked
187 * of the document to retrieve
189 * should be used by the caller to provide and transform the
191 * @throws DocumentException
194 public void get(ServiceContext ctx, String id, DocumentHandler handler)
195 throws DocumentNotFoundException, DocumentException {
197 if (handler == null) {
198 throw new IllegalArgumentException(
199 "RepositoryJavaClient.get: handler is missing");
201 RepositoryInstance repoSession = null;
204 handler.prepare(Action.GET);
205 repoSession = getRepositorySession();
206 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
207 DocumentModel doc = null;
209 doc = repoSession.getDocument(docRef);
210 } catch (ClientException ce) {
211 String msg = "could not find document with id=" + id;
212 logger.error(msg, ce);
213 throw new DocumentNotFoundException(msg, ce);
215 //set reposession to handle the document
216 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
217 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
218 handler.handle(Action.GET, wrapDoc);
219 handler.complete(Action.GET, wrapDoc);
220 } catch (IllegalArgumentException iae) {
222 } catch (DocumentException de) {
224 } catch (Exception e) {
225 if (logger.isDebugEnabled()) {
226 logger.debug("Caught exception ", e);
228 throw new DocumentException(e);
230 if (repoSession != null) {
231 releaseRepositorySession(repoSession);
237 * get document from the Nuxeo repository, using the docFilter params.
238 * @param ctx service context under which this method is invoked
240 * should be used by the caller to provide and transform the
241 * document. Handler must have a docFilter set to return a single item.
242 * @throws DocumentException
245 public void get(ServiceContext ctx, DocumentHandler handler)
246 throws DocumentNotFoundException, DocumentException {
247 QueryContext queryContext = new QueryContext(ctx, handler);
248 RepositoryInstance repoSession = null;
251 handler.prepare(Action.GET);
252 repoSession = getRepositorySession();
254 DocumentModelList docList = null;
255 // force limit to 1, and ignore totalSize
256 String query = buildNXQLQuery(queryContext);
257 docList = repoSession.query(query, null, 1, 0, false);
258 if (docList.size() != 1) {
259 throw new DocumentNotFoundException("No document found matching filter params.");
261 DocumentModel doc = docList.get(0);
263 if (logger.isDebugEnabled()) {
264 logger.debug("Executed NXQL query: " + query);
267 //set reposession to handle the document
268 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
269 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
270 handler.handle(Action.GET, wrapDoc);
271 handler.complete(Action.GET, wrapDoc);
272 } catch (IllegalArgumentException iae) {
274 } catch (DocumentException de) {
276 } catch (Exception e) {
277 if (logger.isDebugEnabled()) {
278 logger.debug("Caught exception ", e);
280 throw new DocumentException(e);
282 if (repoSession != null) {
283 releaseRepositorySession(repoSession);
289 * get wrapped documentModel from the Nuxeo repository
290 * @param ctx service context under which this method is invoked
292 * of the document to retrieve
293 * @throws DocumentException
296 public DocumentWrapper<DocumentModel> getDoc(
297 ServiceContext ctx, String id)
298 throws DocumentNotFoundException, DocumentException {
299 RepositoryInstance repoSession = null;
300 DocumentWrapper<DocumentModel> wrapDoc = null;
303 repoSession = getRepositorySession();
304 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
305 DocumentModel doc = null;
307 doc = repoSession.getDocument(docRef);
308 } catch (ClientException ce) {
309 String msg = "could not find document with id=" + id;
310 logger.error(msg, ce);
311 throw new DocumentNotFoundException(msg, ce);
313 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
314 } catch (IllegalArgumentException iae) {
316 } catch (DocumentException de) {
318 } catch (Exception e) {
319 if (logger.isDebugEnabled()) {
320 logger.debug("Caught exception ", e);
322 throw new DocumentException(e);
324 if (repoSession != null) {
325 releaseRepositorySession(repoSession);
332 * find wrapped documentModel from the Nuxeo repository
333 * @param ctx service context under which this method is invoked
334 * @param where NXQL where clause to get the document
335 * @throws DocumentException
338 public DocumentWrapper<DocumentModel> findDoc(
339 ServiceContext ctx, String whereClause)
340 throws DocumentNotFoundException, DocumentException {
341 RepositoryInstance repoSession = null;
342 DocumentWrapper<DocumentModel> wrapDoc = null;
345 QueryContext queryContext = new QueryContext(ctx, whereClause);
346 repoSession = getRepositorySession();
347 DocumentModelList docList = null;
348 // force limit to 1, and ignore totalSize
349 String query = buildNXQLQuery(queryContext);
350 docList = repoSession.query(query,
355 if (docList.size() != 1) {
356 if (logger.isDebugEnabled()) {
357 logger.debug("findDoc: Query found: " + docList.size() + " items.");
358 logger.debug(" Query: " + query);
360 throw new DocumentNotFoundException("No document found matching filter params.");
362 DocumentModel doc = docList.get(0);
363 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
364 } catch (IllegalArgumentException iae) {
366 } catch (DocumentException de) {
368 } catch (Exception e) {
369 if (logger.isDebugEnabled()) {
370 logger.debug("Caught exception ", e);
372 throw new DocumentException(e);
374 if (repoSession != null) {
375 releaseRepositorySession(repoSession);
382 * find doc and return CSID from the Nuxeo repository
383 * @param ctx service context under which this method is invoked
384 * @param where NXQL where clause to get the document
385 * @throws DocumentException
388 public String findDocCSID(
389 ServiceContext ctx, String whereClause)
390 throws DocumentNotFoundException, DocumentException {
393 DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
394 DocumentModel docModel = wrapDoc.getWrappedObject();
395 csid = NuxeoUtils.extractId(docModel.getPathAsString());
396 } catch (DocumentNotFoundException dnfe) {
398 } catch (IllegalArgumentException iae) {
400 } catch (DocumentException de) {
402 } catch (Exception e) {
403 if (logger.isDebugEnabled()) {
404 logger.debug("Caught exception ", e);
406 throw new DocumentException(e);
412 * Find a list of documentModels from the Nuxeo repository
413 * @param docTypes a list of DocType names to match
414 * @param where the clause to qualify on
415 * @param domain the domain for the associated services
419 public DocumentWrapper<DocumentModelList> findDocs(
421 List<String> docTypes,
423 int pageSize, int pageNum, boolean computeTotal)
424 throws DocumentNotFoundException, DocumentException {
425 RepositoryInstance repoSession = null;
426 DocumentWrapper<DocumentModelList> wrapDoc = null;
429 if (docTypes == null || docTypes.size() < 1) {
430 throw new DocumentNotFoundException(
431 "findDocs must specify at least one DocumentType.");
433 repoSession = getRepositorySession();
434 DocumentModelList docList = null;
435 // force limit to 1, and ignore totalSize
436 QueryContext queryContext = new QueryContext(ctx, whereClause);
437 String query = buildNXQLQuery(docTypes, queryContext);
438 docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
439 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
440 } catch (IllegalArgumentException iae) {
442 } catch (Exception e) {
443 if (logger.isDebugEnabled()) {
444 logger.debug("Caught exception ", e);
446 throw new DocumentException(e);
448 if (repoSession != null) {
449 releaseRepositorySession(repoSession);
456 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
459 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
460 throws DocumentNotFoundException, DocumentException {
461 if (handler == null) {
462 throw new IllegalArgumentException(
463 "RepositoryJavaClient.getAll: handler is missing");
466 RepositoryInstance repoSession = null;
469 handler.prepare(Action.GET_ALL);
470 repoSession = getRepositorySession();
471 DocumentModelList docModelList = new DocumentModelListImpl();
472 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
473 for (String csid : csidList) {
474 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
475 DocumentModel docModel = repoSession.getDocument(docRef);
476 docModelList.add(docModel);
479 //set reposession to handle the document
480 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
481 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
482 handler.handle(Action.GET_ALL, wrapDoc);
483 handler.complete(Action.GET_ALL, wrapDoc);
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 (repoSession != null) {
493 releaseRepositorySession(repoSession);
499 * getAll get all documents for an entity entity service from the Nuxeo
502 * @param ctx service context under which this method is invoked
504 * should be used by the caller to provide and transform the
506 * @throws DocumentException
509 public void getAll(ServiceContext ctx, DocumentHandler handler)
510 throws DocumentNotFoundException, DocumentException {
511 if (handler == null) {
512 throw new IllegalArgumentException(
513 "RepositoryJavaClient.getAll: handler is missing");
515 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
516 if (nuxeoWspaceId == null) {
517 throw new DocumentNotFoundException(
518 "Unable to find workspace for service "
519 + ctx.getServiceName()
520 + " check if the workspace exists in the Nuxeo repository");
522 RepositoryInstance repoSession = null;
525 handler.prepare(Action.GET_ALL);
526 repoSession = getRepositorySession();
527 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
528 DocumentModelList docList = repoSession.getChildren(wsDocRef);
529 //set reposession to handle the document
530 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
531 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
532 handler.handle(Action.GET_ALL, wrapDoc);
533 handler.complete(Action.GET_ALL, wrapDoc);
534 } catch (DocumentException de) {
536 } catch (Exception e) {
537 if (logger.isDebugEnabled()) {
538 logger.debug("Caught exception ", e);
540 throw new DocumentException(e);
542 if (repoSession != null) {
543 releaseRepositorySession(repoSession);
549 * getFiltered get all documents for an entity service from the Document repository,
550 * given filter parameters specified by the handler.
551 * @param ctx service context under which this method is invoked
552 * @param handler should be used by the caller to provide and transform the document
553 * @throws DocumentNotFoundException if workspace not found
554 * @throws DocumentException
557 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
558 throws DocumentNotFoundException, DocumentException {
559 QueryContext queryContext = new QueryContext(ctx, handler);
561 RepositoryInstance repoSession = null;
563 handler.prepare(Action.GET_ALL);
564 repoSession = getRepositorySession();
565 DocumentModelList docList = null;
566 String query = buildNXQLQuery(queryContext);
568 if (logger.isDebugEnabled()) {
569 logger.debug("Executing NXQL query: " + query.toString());
572 // If we have limit and/or offset, then pass true to get totalSize
573 // in returned DocumentModelList.
574 Profiler profiler = new Profiler(this, 2);
575 profiler.log("Executing NXQL query: " + query.toString());
577 if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
578 docList = repoSession.query(query, null,
579 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
581 docList = repoSession.query(query);
585 //set repoSession to handle the document
586 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
587 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
588 handler.handle(Action.GET_ALL, wrapDoc);
589 handler.complete(Action.GET_ALL, wrapDoc);
590 } catch (DocumentException de) {
592 } catch (Exception e) {
593 if (logger.isDebugEnabled()) {
594 logger.debug("Caught exception ", e);
596 throw new DocumentException(e);
598 if (repoSession != null) {
599 releaseRepositorySession(repoSession);
605 * update given document in the Nuxeo repository
607 * @param ctx service context under which this method is invoked
611 * should be used by the caller to provide and transform the
613 * @throws DocumentException
616 public void update(ServiceContext ctx, String id, DocumentHandler handler)
617 throws BadRequestException, DocumentNotFoundException,
619 if (handler == null) {
620 throw new IllegalArgumentException(
621 "RepositoryJavaClient.update: handler is missing");
623 RepositoryInstance repoSession = null;
625 handler.prepare(Action.UPDATE);
626 repoSession = getRepositorySession();
627 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
628 DocumentModel doc = null;
630 doc = repoSession.getDocument(docRef);
631 } catch (ClientException ce) {
632 String msg = "Could not find document to update with id=" + id;
633 logger.error(msg, ce);
634 throw new DocumentNotFoundException(msg, ce);
636 //set reposession to handle the document
637 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
638 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
639 handler.handle(Action.UPDATE, wrapDoc);
640 setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
641 repoSession.saveDocument(doc);
643 handler.complete(Action.UPDATE, wrapDoc);
644 } catch (BadRequestException bre) {
646 } catch (DocumentException de) {
648 } catch (Exception e) {
649 if (logger.isDebugEnabled()) {
650 logger.debug("Caught exception ", e);
652 throw new DocumentException(e);
654 if (repoSession != null) {
655 releaseRepositorySession(repoSession);
661 * delete a document from the Nuxeo repository
662 * @param ctx service context under which this method is invoked
665 * @throws DocumentException
668 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
671 if (logger.isDebugEnabled()) {
672 logger.debug("deleting document with id=" + id);
674 RepositoryInstance repoSession = null;
676 repoSession = getRepositorySession();
677 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
679 repoSession.removeDocument(docRef);
680 } catch (ClientException ce) {
681 String msg = "could not find document to delete with id=" + id;
682 logger.error(msg, ce);
683 throw new DocumentNotFoundException(msg, ce);
686 } catch (DocumentException de) {
688 } catch (Exception e) {
689 if (logger.isDebugEnabled()) {
690 logger.debug("Caught exception ", e);
692 throw new DocumentException(e);
694 if (repoSession != null) {
695 releaseRepositorySession(repoSession);
701 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
704 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
705 throws DocumentNotFoundException, DocumentException {
706 throw new UnsupportedOperationException();
710 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
711 return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
715 public String createDomain(String domainName) throws Exception {
716 RepositoryInstance repoSession = null;
717 String domainId = null;
719 repoSession = getRepositorySession();
720 DocumentRef parentDocRef = new PathRef("/");
721 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
722 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
723 domainName, "Domain");
724 doc.setPropertyValue("dc:title", domainName);
725 doc.setPropertyValue("dc:description", "A CollectionSpace domain "
727 doc = repoSession.createDocument(doc);
728 domainId = doc.getId();
730 if (logger.isDebugEnabled()) {
731 logger.debug("created tenant domain name=" + domainName
732 + " id=" + domainId);
734 } catch (Exception e) {
735 if (logger.isDebugEnabled()) {
736 logger.debug("createTenantSpace caught exception ", e);
740 if (repoSession != null) {
741 releaseRepositorySession(repoSession);
748 public String getDomainId(String domainName) throws Exception {
749 String domainId = null;
750 RepositoryInstance repoSession = null;
752 repoSession = getRepositorySession();
753 DocumentRef docRef = new PathRef(
755 DocumentModel domain = repoSession.getDocument(docRef);
756 domainId = domain.getId();
757 } catch (Exception e) {
758 if (logger.isDebugEnabled()) {
759 logger.debug("Caught exception ", e);
761 //there is no way to identify if document does not exist due to
762 //lack of typed exception for getDocument method
765 if (repoSession != null) {
766 releaseRepositorySession(repoSession);
773 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
776 public String createWorkspace(String domainName, String workspaceName) throws Exception {
777 RepositoryInstance repoSession = null;
778 String workspaceId = null;
780 repoSession = getRepositorySession();
781 DocumentRef parentDocRef = new PathRef(
783 + "/" + "workspaces");
784 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
785 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
786 workspaceName, "Workspace");
787 doc.setPropertyValue("dc:title", workspaceName);
788 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
790 doc = repoSession.createDocument(doc);
791 workspaceId = doc.getId();
793 if (logger.isDebugEnabled()) {
794 logger.debug("created workspace name=" + workspaceName
795 + " id=" + workspaceId);
797 } catch (Exception e) {
798 if (logger.isDebugEnabled()) {
799 logger.debug("createWorkspace caught exception ", e);
803 if (repoSession != null) {
804 releaseRepositorySession(repoSession);
811 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
814 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
815 String workspaceId = null;
816 RepositoryInstance repoSession = null;
818 repoSession = getRepositorySession();
819 DocumentRef docRef = new PathRef(
822 + "/" + workspaceName);
823 DocumentModel workspace = repoSession.getDocument(docRef);
824 workspaceId = workspace.getId();
825 } catch (DocumentException de) {
827 } catch (Exception e) {
828 if (logger.isDebugEnabled()) {
829 logger.debug("Caught exception ", e);
831 throw new DocumentException(e);
833 if (repoSession != null) {
834 releaseRepositorySession(repoSession);
841 * Append a WHERE clause to the NXQL query.
843 * @param query The NXQL query to which the WHERE clause will be appended.
844 * @param querycontext The query context, which provides the WHERE clause to append.
846 private final void appendNXQLWhere(StringBuilder query, QueryContext queryContext) {
848 // Restrict search to a specific Nuxeo domain
849 // TODO This is a slow method for tenant-filter
850 // We should make this a property that is indexed.
852 // query.append(" WHERE ecm:path STARTSWITH '/" + queryContext.domain + "'");
855 // Restrict search to the current tenant ID. Is the domain path filter (above) still needed?
857 query.append(/*IQueryManager.SEARCH_QUALIFIER_AND +*/ " WHERE " + DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA + ":"
858 + DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID
859 + " = " + queryContext.getTenantId());
861 // Finally, append the incoming where clause
863 String whereClause = queryContext.getWhereClause();
864 if (whereClause != null && ! whereClause.trim().isEmpty()) {
865 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
866 // into SQL, we need to parenthesize our 'where' clause
867 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "(" + whereClause + ")");
870 // Please lookup this use in Nuxeo support and document here
872 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "ecm:isProxy = 0");
876 * Append an ORDER BY clause to the NXQL query.
878 * @param query the NXQL query to which the ORDER BY clause will be appended.
879 * @param querycontext the query context, which provides the ORDER BY clause to append.
881 * @throws DocumentException if the supplied value of the orderBy clause is not valid.
884 private final void appendNXQLOrderBy(StringBuilder query, QueryContext queryContext)
886 String orderByClause = queryContext.getOrderByClause();
887 if (orderByClause != null && ! orderByClause.trim().isEmpty()) {
888 if (isValidOrderByClause(orderByClause)) {
889 query.append(" ORDER BY ");
890 query.append(orderByClause);
892 throw new DocumentException("Invalid format in sort request '" + orderByClause
893 + "': must be schema_name:fieldName followed by optional sort order (' ASC' or ' DESC').");
899 * Identifies whether the ORDER BY clause is valid.
901 * @param orderByClause the ORDER BY clause.
903 * @return true if the ORDER BY clause is valid;
904 * false if it is not.
906 private final boolean isValidOrderByClause(String orderByClause) {
907 boolean isValidClause = false;
909 Pattern orderByPattern = Pattern.compile(ORDER_BY_CLAUSE_REGEX);
910 Matcher orderByMatcher = orderByPattern.matcher(orderByClause);
911 if (orderByMatcher.matches()) {
912 isValidClause = true;
914 } catch (PatternSyntaxException pe) {
915 logger.warn("ORDER BY clause regex pattern '" + ORDER_BY_CLAUSE_REGEX
916 + "' could not be compiled: " + pe.getMessage());
917 // If reached, method will return a value of false.
919 return isValidClause;
924 * Builds an NXQL SELECT query for a single document type.
926 * @param queryContext The query context
927 * @return an NXQL query
928 * @throws Exception if supplied values in the query are invalid.
930 private final String buildNXQLQuery(QueryContext queryContext) throws Exception {
931 StringBuilder query = new StringBuilder("SELECT * FROM ");
932 query.append(queryContext.getDocType());
933 appendNXQLWhere(query, queryContext);
934 appendNXQLOrderBy(query, queryContext);
935 return query.toString();
939 * Builds an NXQL SELECT query across multiple document types.
941 * @param docTypes a list of document types to be queried
942 * @param queryContext the query context
943 * @return an NXQL query
945 private final String buildNXQLQuery(List<String> docTypes, QueryContext queryContext) {
946 StringBuilder query = new StringBuilder("SELECT * FROM ");
947 boolean fFirst = true;
948 for (String docType : docTypes) {
954 query.append(docType);
956 appendNXQLWhere(query, queryContext);
957 // FIXME add 'order by' clause here, if appropriate
958 return query.toString();
962 * Gets the repository session.
964 * @return the repository session
965 * @throws Exception the exception
967 private RepositoryInstance getRepositorySession() throws Exception {
968 // FIXME: is it possible to reuse repository session?
969 // Authentication failures happen while trying to reuse the session
970 Profiler profiler = new Profiler("getRepositorySession():", 2);
972 NuxeoClient client = NuxeoConnector.getInstance().getClient();
973 RepositoryInstance repoSession = client.openRepository();
974 if (logger.isTraceEnabled()) {
975 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
982 * Release repository session.
984 * @param repoSession the repo session
986 private void releaseRepositorySession(RepositoryInstance repoSession) {
988 NuxeoClient client = NuxeoConnector.getInstance().getClient();
990 client.releaseRepository(repoSession);
991 } catch (Exception e) {
992 logger.error("Could not close the repository session", e);
993 // no need to throw this service specific exception