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 ordering on only one field.
77 // FIXME: Currently supports only USASCII word characters in field names.
78 final String ORDER_BY_CLAUSE_REGEX = "\\w+(_\\w+)?:\\w+( ASC| DESC)?";
81 * Instantiates a new repository java client impl.
83 public RepositoryJavaClientImpl() {
89 * Sets the collection space core values.
92 * @param documentModel the document model
93 * @throws ClientException the client exception
95 private void setCollectionSpaceCoreValues(ServiceContext<MultipartInput, MultipartOutput> ctx,
96 DocumentModel documentModel,
97 Action action) throws ClientException {
99 // Add the tenant ID value to the new entity
101 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
102 DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID,
105 String now = GregorianCalendarDateTimeUtils.timestampUTC();
109 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
110 DocumentModelHandler.COLLECTIONSPACE_CORE_CREATED_AT,
114 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
115 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
124 * create document in the Nuxeo repository
126 * @param ctx service context under which this method is invoked
128 * should be used by the caller to provide and transform the
130 * @return id in repository of the newly created document
131 * @throws DocumentException
134 public String create(ServiceContext ctx,
135 DocumentHandler handler) throws BadRequestException,
138 if (ctx.getDocumentType() == null) {
139 throw new IllegalArgumentException(
140 "RepositoryJavaClient.create: docType is missing");
142 if (handler == null) {
143 throw new IllegalArgumentException(
144 "RepositoryJavaClient.create: handler is missing");
146 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
147 if (nuxeoWspaceId == null) {
148 throw new DocumentNotFoundException(
149 "Unable to find workspace for service " + ctx.getServiceName()
150 + " check if the workspace exists in the Nuxeo repository");
152 RepositoryInstance repoSession = null;
154 handler.prepare(Action.CREATE);
155 repoSession = getRepositorySession();
156 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
157 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
158 String wspacePath = wspaceDoc.getPathAsString();
159 //give our own ID so PathRef could be constructed later on
160 String id = IdUtils.generateId(UUID.randomUUID().toString());
161 // create document model
162 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
163 ctx.getDocumentType());
164 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
165 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
166 handler.handle(Action.CREATE, wrapDoc);
167 // create document with documentmodel
168 setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
169 doc = repoSession.createDocument(doc);
171 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
172 // and assume the handler has the state it needs (doc fragments).
173 handler.complete(Action.CREATE, wrapDoc);
175 } catch (BadRequestException bre) {
177 } catch (Exception e) {
178 if (logger.isDebugEnabled()) {
179 logger.debug("Caught exception ", e);
181 throw new DocumentException(e);
183 if (repoSession != null) {
184 releaseRepositorySession(repoSession);
191 * get document from the Nuxeo repository
192 * @param ctx service context under which this method is invoked
194 * of the document to retrieve
196 * should be used by the caller to provide and transform the
198 * @throws DocumentException
201 public void get(ServiceContext ctx, String id, DocumentHandler handler)
202 throws DocumentNotFoundException, DocumentException {
204 if (handler == null) {
205 throw new IllegalArgumentException(
206 "RepositoryJavaClient.get: handler is missing");
208 RepositoryInstance repoSession = null;
211 handler.prepare(Action.GET);
212 repoSession = getRepositorySession();
213 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
214 DocumentModel doc = null;
216 doc = repoSession.getDocument(docRef);
217 } catch (ClientException ce) {
218 String msg = "could not find document with id=" + id;
219 logger.error(msg, ce);
220 throw new DocumentNotFoundException(msg, ce);
222 //set reposession to handle the document
223 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
224 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
225 handler.handle(Action.GET, wrapDoc);
226 handler.complete(Action.GET, wrapDoc);
227 } catch (IllegalArgumentException iae) {
229 } catch (DocumentException de) {
231 } catch (Exception e) {
232 if (logger.isDebugEnabled()) {
233 logger.debug("Caught exception ", e);
235 throw new DocumentException(e);
237 if (repoSession != null) {
238 releaseRepositorySession(repoSession);
244 * get document from the Nuxeo repository, using the docFilter params.
245 * @param ctx service context under which this method is invoked
247 * should be used by the caller to provide and transform the
248 * document. Handler must have a docFilter set to return a single item.
249 * @throws DocumentException
252 public void get(ServiceContext ctx, DocumentHandler handler)
253 throws DocumentNotFoundException, DocumentException {
254 QueryContext queryContext = new QueryContext(ctx, handler);
255 RepositoryInstance repoSession = null;
258 handler.prepare(Action.GET);
259 repoSession = getRepositorySession();
261 DocumentModelList docList = null;
262 // force limit to 1, and ignore totalSize
263 String query = buildNXQLQuery(queryContext);
264 docList = repoSession.query(query, null, 1, 0, false);
265 if (docList.size() != 1) {
266 throw new DocumentNotFoundException("No document found matching filter params.");
268 DocumentModel doc = docList.get(0);
270 if (logger.isDebugEnabled()) {
271 logger.debug("Executed NXQL query: " + query);
274 //set reposession to handle the document
275 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
276 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
277 handler.handle(Action.GET, wrapDoc);
278 handler.complete(Action.GET, wrapDoc);
279 } catch (IllegalArgumentException iae) {
281 } catch (DocumentException de) {
283 } catch (Exception e) {
284 if (logger.isDebugEnabled()) {
285 logger.debug("Caught exception ", e);
287 throw new DocumentException(e);
289 if (repoSession != null) {
290 releaseRepositorySession(repoSession);
296 * get wrapped documentModel from the Nuxeo repository
297 * @param ctx service context under which this method is invoked
299 * of the document to retrieve
300 * @throws DocumentException
303 public DocumentWrapper<DocumentModel> getDoc(
304 ServiceContext ctx, String id)
305 throws DocumentNotFoundException, DocumentException {
306 RepositoryInstance repoSession = null;
307 DocumentWrapper<DocumentModel> wrapDoc = null;
310 repoSession = getRepositorySession();
311 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
312 DocumentModel doc = null;
314 doc = repoSession.getDocument(docRef);
315 } catch (ClientException ce) {
316 String msg = "could not find document with id=" + id;
317 logger.error(msg, ce);
318 throw new DocumentNotFoundException(msg, ce);
320 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
321 } catch (IllegalArgumentException iae) {
323 } catch (DocumentException de) {
325 } catch (Exception e) {
326 if (logger.isDebugEnabled()) {
327 logger.debug("Caught exception ", e);
329 throw new DocumentException(e);
331 if (repoSession != null) {
332 releaseRepositorySession(repoSession);
339 * find wrapped documentModel from the Nuxeo repository
340 * @param ctx service context under which this method is invoked
341 * @param whereClause where NXQL where clause to get the document
342 * @throws DocumentException
345 public DocumentWrapper<DocumentModel> findDoc(
346 ServiceContext ctx, String whereClause)
347 throws DocumentNotFoundException, DocumentException {
348 RepositoryInstance repoSession = null;
349 DocumentWrapper<DocumentModel> wrapDoc = null;
352 QueryContext queryContext = new QueryContext(ctx, whereClause);
353 repoSession = getRepositorySession();
354 DocumentModelList docList = null;
355 // force limit to 1, and ignore totalSize
356 String query = buildNXQLQuery(queryContext);
357 docList = repoSession.query(query,
362 if (docList.size() != 1) {
363 if (logger.isDebugEnabled()) {
364 logger.debug("findDoc: Query found: " + docList.size() + " items.");
365 logger.debug(" Query: " + query);
367 throw new DocumentNotFoundException("No document found matching filter params.");
369 DocumentModel doc = docList.get(0);
370 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
371 } catch (IllegalArgumentException iae) {
373 } catch (DocumentException de) {
375 } catch (Exception e) {
376 if (logger.isDebugEnabled()) {
377 logger.debug("Caught exception ", e);
379 throw new DocumentException(e);
381 if (repoSession != null) {
382 releaseRepositorySession(repoSession);
389 * find doc and return CSID from the Nuxeo repository
390 * @param ctx service context under which this method is invoked
391 * @param whereClause where NXQL where clause to get the document
392 * @throws DocumentException
395 public String findDocCSID(
396 ServiceContext ctx, String whereClause)
397 throws DocumentNotFoundException, DocumentException {
400 DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
401 DocumentModel docModel = wrapDoc.getWrappedObject();
402 csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
403 } catch (DocumentNotFoundException dnfe) {
405 } catch (IllegalArgumentException iae) {
407 } catch (DocumentException de) {
409 } catch (Exception e) {
410 if (logger.isDebugEnabled()) {
411 logger.debug("Caught exception ", e);
413 throw new DocumentException(e);
419 * Find a list of documentModels from the Nuxeo repository
420 * @param docTypes a list of DocType names to match
421 * @param whereClause where the clause to qualify on
425 public DocumentWrapper<DocumentModelList> findDocs(
427 List<String> docTypes,
429 int pageSize, int pageNum, boolean computeTotal)
430 throws DocumentNotFoundException, DocumentException {
431 RepositoryInstance repoSession = null;
432 DocumentWrapper<DocumentModelList> wrapDoc = null;
435 if (docTypes == null || docTypes.size() < 1) {
436 throw new DocumentNotFoundException(
437 "findDocs must specify at least one DocumentType.");
439 repoSession = getRepositorySession();
440 DocumentModelList docList = null;
441 // force limit to 1, and ignore totalSize
442 QueryContext queryContext = new QueryContext(ctx, whereClause);
443 String query = buildNXQLQuery(docTypes, queryContext);
444 docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
445 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
446 } catch (IllegalArgumentException iae) {
448 } catch (Exception e) {
449 if (logger.isDebugEnabled()) {
450 logger.debug("Caught exception ", e);
452 throw new DocumentException(e);
454 if (repoSession != null) {
455 releaseRepositorySession(repoSession);
462 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
465 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
466 throws DocumentNotFoundException, DocumentException {
467 if (handler == null) {
468 throw new IllegalArgumentException(
469 "RepositoryJavaClient.getAll: handler is missing");
472 RepositoryInstance repoSession = null;
475 handler.prepare(Action.GET_ALL);
476 repoSession = getRepositorySession();
477 DocumentModelList docModelList = new DocumentModelListImpl();
478 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
479 for (String csid : csidList) {
480 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
481 DocumentModel docModel = repoSession.getDocument(docRef);
482 docModelList.add(docModel);
485 //set reposession to handle the document
486 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
487 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
488 handler.handle(Action.GET_ALL, wrapDoc);
489 handler.complete(Action.GET_ALL, wrapDoc);
490 } catch (DocumentException de) {
492 } catch (Exception e) {
493 if (logger.isDebugEnabled()) {
494 logger.debug("Caught exception ", e);
496 throw new DocumentException(e);
498 if (repoSession != null) {
499 releaseRepositorySession(repoSession);
505 * getAll get all documents for an entity entity service from the Nuxeo
508 * @param ctx service context under which this method is invoked
510 * should be used by the caller to provide and transform the
512 * @throws DocumentException
515 public void getAll(ServiceContext ctx, DocumentHandler handler)
516 throws DocumentNotFoundException, DocumentException {
517 if (handler == null) {
518 throw new IllegalArgumentException(
519 "RepositoryJavaClient.getAll: handler is missing");
521 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
522 if (nuxeoWspaceId == null) {
523 throw new DocumentNotFoundException(
524 "Unable to find workspace for service "
525 + ctx.getServiceName()
526 + " check if the workspace exists in the Nuxeo repository");
528 RepositoryInstance repoSession = null;
531 handler.prepare(Action.GET_ALL);
532 repoSession = getRepositorySession();
533 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
534 DocumentModelList docList = repoSession.getChildren(wsDocRef);
535 //set reposession to handle the document
536 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
537 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
538 handler.handle(Action.GET_ALL, wrapDoc);
539 handler.complete(Action.GET_ALL, wrapDoc);
540 } catch (DocumentException de) {
542 } catch (Exception e) {
543 if (logger.isDebugEnabled()) {
544 logger.debug("Caught exception ", e);
546 throw new DocumentException(e);
548 if (repoSession != null) {
549 releaseRepositorySession(repoSession);
555 * getFiltered get all documents for an entity service from the Document repository,
556 * given filter parameters specified by the handler.
557 * @param ctx service context under which this method is invoked
558 * @param handler should be used by the caller to provide and transform the document
559 * @throws DocumentNotFoundException if workspace not found
560 * @throws DocumentException
563 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
564 throws DocumentNotFoundException, DocumentException {
565 QueryContext queryContext = new QueryContext(ctx, handler);
567 RepositoryInstance repoSession = null;
569 handler.prepare(Action.GET_ALL);
570 repoSession = getRepositorySession();
571 DocumentModelList docList = null;
572 String query = buildNXQLQuery(queryContext);
574 if (logger.isDebugEnabled()) {
575 logger.debug("Executing NXQL query: " + query.toString());
578 // If we have limit and/or offset, then pass true to get totalSize
579 // in returned DocumentModelList.
580 Profiler profiler = new Profiler(this, 2);
581 profiler.log("Executing NXQL query: " + query.toString());
583 if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
584 docList = repoSession.query(query, null,
585 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
587 docList = repoSession.query(query);
591 //set repoSession to handle the document
592 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
593 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
594 handler.handle(Action.GET_ALL, wrapDoc);
595 handler.complete(Action.GET_ALL, wrapDoc);
596 } catch (DocumentException de) {
598 } catch (Exception e) {
599 if (logger.isDebugEnabled()) {
600 logger.debug("Caught exception ", e);
602 throw new DocumentException(e);
604 if (repoSession != null) {
605 releaseRepositorySession(repoSession);
611 * update given document in the Nuxeo repository
613 * @param ctx service context under which this method is invoked
617 * should be used by the caller to provide and transform the
619 * @throws DocumentException
622 public void update(ServiceContext ctx, String id, DocumentHandler handler)
623 throws BadRequestException, DocumentNotFoundException,
625 if (handler == null) {
626 throw new IllegalArgumentException(
627 "RepositoryJavaClient.update: handler is missing");
629 RepositoryInstance repoSession = null;
631 handler.prepare(Action.UPDATE);
632 repoSession = getRepositorySession();
633 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
634 DocumentModel doc = null;
636 doc = repoSession.getDocument(docRef);
637 } catch (ClientException ce) {
638 String msg = "Could not find document to update with id=" + id;
639 logger.error(msg, ce);
640 throw new DocumentNotFoundException(msg, ce);
642 //set reposession to handle the document
643 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
644 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
645 handler.handle(Action.UPDATE, wrapDoc);
646 setCollectionSpaceCoreValues(ctx, doc, Action.UPDATE);
647 repoSession.saveDocument(doc);
649 handler.complete(Action.UPDATE, wrapDoc);
650 } catch (BadRequestException bre) {
652 } catch (DocumentException de) {
654 } catch (Exception e) {
655 if (logger.isDebugEnabled()) {
656 logger.debug("Caught exception ", e);
658 throw new DocumentException(e);
660 if (repoSession != null) {
661 releaseRepositorySession(repoSession);
667 * delete a document from the Nuxeo repository
668 * @param ctx service context under which this method is invoked
671 * @throws DocumentException
674 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
677 if (logger.isDebugEnabled()) {
678 logger.debug("deleting document with id=" + id);
680 RepositoryInstance repoSession = null;
682 repoSession = getRepositorySession();
683 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
685 repoSession.removeDocument(docRef);
686 } catch (ClientException ce) {
687 String msg = "could not find document to delete with id=" + id;
688 logger.error(msg, ce);
689 throw new DocumentNotFoundException(msg, ce);
692 } catch (DocumentException de) {
694 } catch (Exception e) {
695 if (logger.isDebugEnabled()) {
696 logger.debug("Caught exception ", e);
698 throw new DocumentException(e);
700 if (repoSession != null) {
701 releaseRepositorySession(repoSession);
707 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
710 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
711 throws DocumentNotFoundException, DocumentException {
712 throw new UnsupportedOperationException();
716 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
717 return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
721 public String createDomain(String domainName) throws Exception {
722 RepositoryInstance repoSession = null;
723 String domainId = null;
725 repoSession = getRepositorySession();
726 DocumentRef parentDocRef = new PathRef("/");
727 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
728 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
729 domainName, "Domain");
730 doc.setPropertyValue("dc:title", domainName);
731 doc.setPropertyValue("dc:description", "A CollectionSpace domain "
733 doc = repoSession.createDocument(doc);
734 domainId = doc.getId();
736 if (logger.isDebugEnabled()) {
737 logger.debug("created tenant domain name=" + domainName
738 + " id=" + domainId);
740 } catch (Exception e) {
741 if (logger.isDebugEnabled()) {
742 logger.debug("createTenantSpace caught exception ", e);
746 if (repoSession != null) {
747 releaseRepositorySession(repoSession);
754 public String getDomainId(String domainName) throws Exception {
755 String domainId = null;
756 RepositoryInstance repoSession = null;
758 repoSession = getRepositorySession();
759 DocumentRef docRef = new PathRef(
761 DocumentModel domain = repoSession.getDocument(docRef);
762 domainId = domain.getId();
763 } catch (Exception e) {
764 if (logger.isDebugEnabled()) {
765 logger.debug("Caught exception ", e);
767 //there is no way to identify if document does not exist due to
768 //lack of typed exception for getDocument method
771 if (repoSession != null) {
772 releaseRepositorySession(repoSession);
779 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
782 public String createWorkspace(String domainName, String workspaceName) throws Exception {
783 RepositoryInstance repoSession = null;
784 String workspaceId = null;
786 repoSession = getRepositorySession();
787 DocumentRef parentDocRef = new PathRef(
789 + "/" + "workspaces");
790 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
791 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
792 workspaceName, "Workspace");
793 doc.setPropertyValue("dc:title", workspaceName);
794 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
796 doc = repoSession.createDocument(doc);
797 workspaceId = doc.getId();
799 if (logger.isDebugEnabled()) {
800 logger.debug("created workspace name=" + workspaceName
801 + " id=" + workspaceId);
803 } catch (Exception e) {
804 if (logger.isDebugEnabled()) {
805 logger.debug("createWorkspace caught exception ", e);
809 if (repoSession != null) {
810 releaseRepositorySession(repoSession);
817 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
820 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
821 String workspaceId = null;
822 RepositoryInstance repoSession = null;
824 repoSession = getRepositorySession();
825 DocumentRef docRef = new PathRef(
828 + "/" + workspaceName);
829 DocumentModel workspace = repoSession.getDocument(docRef);
830 workspaceId = workspace.getId();
831 } catch (DocumentException de) {
833 } catch (Exception e) {
834 if (logger.isDebugEnabled()) {
835 logger.debug("Caught exception ", e);
837 throw new DocumentException(e);
839 if (repoSession != null) {
840 releaseRepositorySession(repoSession);
847 * Append a WHERE clause to the NXQL query.
849 * @param query The NXQL query to which the WHERE clause will be appended.
850 * @param queryContext The query context, which provides the WHERE clause to append.
852 private final void appendNXQLWhere(StringBuilder query, QueryContext queryContext) {
854 // Restrict search to a specific Nuxeo domain
855 // TODO This is a slow method for tenant-filter
856 // We should make this a property that is indexed.
858 // query.append(" WHERE ecm:path STARTSWITH '/" + queryContext.domain + "'");
861 // Restrict search to the current tenant ID. Is the domain path filter (above) still needed?
863 query.append(/*IQueryManager.SEARCH_QUALIFIER_AND +*/ " WHERE " + DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA + ":"
864 + DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID
865 + " = " + queryContext.getTenantId());
867 // Finally, append the incoming where clause
869 String whereClause = queryContext.getWhereClause();
870 if (whereClause != null && ! whereClause.trim().isEmpty()) {
871 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
872 // into SQL, we need to parenthesize our 'where' clause
873 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "(" + whereClause + ")");
876 // Please lookup this use in Nuxeo support and document here
878 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "ecm:isProxy = 0");
882 * Append an ORDER BY clause to the NXQL query.
884 * @param query the NXQL query to which the ORDER BY clause will be appended.
885 * @param queryContext the query context, which provides the ORDER BY clause to append.
887 * @throws DocumentException if the supplied value of the orderBy clause is not valid.
890 private final void appendNXQLOrderBy(StringBuilder query, QueryContext queryContext)
892 String orderByClause = queryContext.getOrderByClause();
893 if (orderByClause != null && ! orderByClause.trim().isEmpty()) {
894 if (isValidOrderByClause(orderByClause)) {
895 query.append(" ORDER BY ");
896 query.append(orderByClause);
898 throw new DocumentException("Invalid format in sort request '" + orderByClause
899 + "': must be schema_name:fieldName followed by optional sort order (' ASC' or ' DESC').");
905 * Identifies whether the ORDER BY clause is valid.
907 * @param orderByClause the ORDER BY clause.
909 * @return true if the ORDER BY clause is valid;
910 * false if it is not.
912 private final boolean isValidOrderByClause(String orderByClause) {
913 boolean isValidClause = false;
915 Pattern orderByPattern = Pattern.compile(ORDER_BY_CLAUSE_REGEX);
916 Matcher orderByMatcher = orderByPattern.matcher(orderByClause);
917 if (orderByMatcher.matches()) {
918 isValidClause = true;
920 } catch (PatternSyntaxException pe) {
921 logger.warn("ORDER BY clause regex pattern '" + ORDER_BY_CLAUSE_REGEX
922 + "' could not be compiled: " + pe.getMessage());
923 // If reached, method will return a value of false.
925 return isValidClause;
930 * Builds an NXQL SELECT query for a single document type.
932 * @param queryContext The query context
933 * @return an NXQL query
934 * @throws Exception if supplied values in the query are invalid.
936 private final String buildNXQLQuery(QueryContext queryContext) throws Exception {
937 StringBuilder query = new StringBuilder("SELECT * FROM ");
938 query.append(queryContext.getDocType());
939 appendNXQLWhere(query, queryContext);
940 appendNXQLOrderBy(query, queryContext);
941 return query.toString();
945 * Builds an NXQL SELECT query across multiple document types.
947 * @param docTypes a list of document types to be queried
948 * @param queryContext the query context
949 * @return an NXQL query
951 private final String buildNXQLQuery(List<String> docTypes, QueryContext queryContext) {
952 StringBuilder query = new StringBuilder("SELECT * FROM ");
953 boolean fFirst = true;
954 for (String docType : docTypes) {
960 query.append(docType);
962 appendNXQLWhere(query, queryContext);
963 // FIXME add 'order by' clause here, if appropriate
964 return query.toString();
968 * Gets the repository session.
970 * @return the repository session
971 * @throws Exception the exception
973 private RepositoryInstance getRepositorySession() throws Exception {
974 // FIXME: is it possible to reuse repository session?
975 // Authentication failures happen while trying to reuse the session
976 Profiler profiler = new Profiler("getRepositorySession():", 2);
978 NuxeoClient client = NuxeoConnector.getInstance().getClient();
979 RepositoryInstance repoSession = client.openRepository();
980 if (logger.isTraceEnabled()) {
981 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
988 * Release repository session.
990 * @param repoSession the repo session
992 private void releaseRepositorySession(RepositoryInstance repoSession) {
994 NuxeoClient client = NuxeoConnector.getInstance().getClient();
996 client.releaseRepository(repoSession);
997 } catch (Exception e) {
998 logger.error("Could not close the repository session", e);
999 // no need to throw this service specific exception