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,
112 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
113 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
117 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
118 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
127 * create document in the Nuxeo repository
129 * @param ctx service context under which this method is invoked
131 * should be used by the caller to provide and transform the
133 * @return id in repository of the newly created document
134 * @throws DocumentException
137 public String create(ServiceContext ctx,
138 DocumentHandler handler) throws BadRequestException,
141 if (ctx.getDocumentType() == null) {
142 throw new IllegalArgumentException(
143 "RepositoryJavaClient.create: docType is missing");
145 if (handler == null) {
146 throw new IllegalArgumentException(
147 "RepositoryJavaClient.create: handler is missing");
149 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
150 if (nuxeoWspaceId == null) {
151 throw new DocumentNotFoundException(
152 "Unable to find workspace for service " + ctx.getServiceName()
153 + " check if the workspace exists in the Nuxeo repository");
155 RepositoryInstance repoSession = null;
157 handler.prepare(Action.CREATE);
158 repoSession = getRepositorySession();
159 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
160 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
161 String wspacePath = wspaceDoc.getPathAsString();
162 //give our own ID so PathRef could be constructed later on
163 String id = IdUtils.generateId(UUID.randomUUID().toString());
164 // create document model
165 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
166 ctx.getDocumentType());
167 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
168 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
169 handler.handle(Action.CREATE, wrapDoc);
170 // create document with documentmodel
171 setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
172 doc = repoSession.createDocument(doc);
174 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
175 // and assume the handler has the state it needs (doc fragments).
176 handler.complete(Action.CREATE, wrapDoc);
178 } catch (BadRequestException bre) {
180 } catch (Exception e) {
181 if (logger.isDebugEnabled()) {
182 logger.debug("Caught exception ", e);
184 throw new DocumentException(e);
186 if (repoSession != null) {
187 releaseRepositorySession(repoSession);
194 * get document from the Nuxeo repository
195 * @param ctx service context under which this method is invoked
197 * of the document to retrieve
199 * should be used by the caller to provide and transform the
201 * @throws DocumentException
204 public void get(ServiceContext ctx, String id, DocumentHandler handler)
205 throws DocumentNotFoundException, DocumentException {
207 if (handler == null) {
208 throw new IllegalArgumentException(
209 "RepositoryJavaClient.get: handler is missing");
211 RepositoryInstance repoSession = null;
214 handler.prepare(Action.GET);
215 repoSession = getRepositorySession();
216 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
217 DocumentModel doc = null;
219 doc = repoSession.getDocument(docRef);
220 } catch (ClientException ce) {
221 String msg = "could not find document with id=" + id;
222 logger.error(msg, ce);
223 throw new DocumentNotFoundException(msg, ce);
225 //set reposession to handle the document
226 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
227 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
228 handler.handle(Action.GET, wrapDoc);
229 handler.complete(Action.GET, wrapDoc);
230 } catch (IllegalArgumentException iae) {
232 } catch (DocumentException de) {
234 } catch (Exception e) {
235 if (logger.isDebugEnabled()) {
236 logger.debug("Caught exception ", e);
238 throw new DocumentException(e);
240 if (repoSession != null) {
241 releaseRepositorySession(repoSession);
247 * get document from the Nuxeo repository, using the docFilter params.
248 * @param ctx service context under which this method is invoked
250 * should be used by the caller to provide and transform the
251 * document. Handler must have a docFilter set to return a single item.
252 * @throws DocumentException
255 public void get(ServiceContext ctx, DocumentHandler handler)
256 throws DocumentNotFoundException, DocumentException {
257 QueryContext queryContext = new QueryContext(ctx, handler);
258 RepositoryInstance repoSession = null;
261 handler.prepare(Action.GET);
262 repoSession = getRepositorySession();
264 DocumentModelList docList = null;
265 // force limit to 1, and ignore totalSize
266 String query = buildNXQLQuery(queryContext);
267 docList = repoSession.query(query, null, 1, 0, false);
268 if (docList.size() != 1) {
269 throw new DocumentNotFoundException("No document found matching filter params.");
271 DocumentModel doc = docList.get(0);
273 if (logger.isDebugEnabled()) {
274 logger.debug("Executed NXQL query: " + query);
277 //set reposession to handle the document
278 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
279 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
280 handler.handle(Action.GET, wrapDoc);
281 handler.complete(Action.GET, wrapDoc);
282 } catch (IllegalArgumentException iae) {
284 } catch (DocumentException de) {
286 } catch (Exception e) {
287 if (logger.isDebugEnabled()) {
288 logger.debug("Caught exception ", e);
290 throw new DocumentException(e);
292 if (repoSession != null) {
293 releaseRepositorySession(repoSession);
299 * get wrapped documentModel from the Nuxeo repository
300 * @param ctx service context under which this method is invoked
302 * of the document to retrieve
303 * @throws DocumentException
306 public DocumentWrapper<DocumentModel> getDoc(
307 ServiceContext ctx, String id)
308 throws DocumentNotFoundException, DocumentException {
309 RepositoryInstance repoSession = null;
310 DocumentWrapper<DocumentModel> wrapDoc = null;
313 repoSession = getRepositorySession();
314 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
315 DocumentModel doc = null;
317 doc = repoSession.getDocument(docRef);
318 } catch (ClientException ce) {
319 String msg = "could not find document with id=" + id;
320 logger.error(msg, ce);
321 throw new DocumentNotFoundException(msg, ce);
323 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
324 } catch (IllegalArgumentException iae) {
326 } catch (DocumentException de) {
328 } catch (Exception e) {
329 if (logger.isDebugEnabled()) {
330 logger.debug("Caught exception ", e);
332 throw new DocumentException(e);
334 if (repoSession != null) {
335 releaseRepositorySession(repoSession);
342 * find wrapped documentModel from the Nuxeo repository
343 * @param ctx service context under which this method is invoked
344 * @param whereClause where NXQL where clause to get the document
345 * @throws DocumentException
348 public DocumentWrapper<DocumentModel> findDoc(
349 ServiceContext ctx, String whereClause)
350 throws DocumentNotFoundException, DocumentException {
351 RepositoryInstance repoSession = null;
352 DocumentWrapper<DocumentModel> wrapDoc = null;
355 QueryContext queryContext = new QueryContext(ctx, whereClause);
356 repoSession = getRepositorySession();
357 DocumentModelList docList = null;
358 // force limit to 1, and ignore totalSize
359 String query = buildNXQLQuery(queryContext);
360 docList = repoSession.query(query,
365 if (docList.size() != 1) {
366 if (logger.isDebugEnabled()) {
367 logger.debug("findDoc: Query found: " + docList.size() + " items.");
368 logger.debug(" Query: " + query);
370 throw new DocumentNotFoundException("No document found matching filter params.");
372 DocumentModel doc = docList.get(0);
373 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
374 } catch (IllegalArgumentException iae) {
376 } catch (DocumentException de) {
378 } catch (Exception e) {
379 if (logger.isDebugEnabled()) {
380 logger.debug("Caught exception ", e);
382 throw new DocumentException(e);
384 if (repoSession != null) {
385 releaseRepositorySession(repoSession);
392 * find doc and return CSID from the Nuxeo repository
393 * @param ctx service context under which this method is invoked
394 * @param whereClause where NXQL where clause to get the document
395 * @throws DocumentException
398 public String findDocCSID(
399 ServiceContext ctx, String whereClause)
400 throws DocumentNotFoundException, DocumentException {
403 DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
404 DocumentModel docModel = wrapDoc.getWrappedObject();
405 csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
406 } catch (DocumentNotFoundException dnfe) {
408 } catch (IllegalArgumentException iae) {
410 } catch (DocumentException de) {
412 } catch (Exception e) {
413 if (logger.isDebugEnabled()) {
414 logger.debug("Caught exception ", e);
416 throw new DocumentException(e);
422 * Find a list of documentModels from the Nuxeo repository
423 * @param docTypes a list of DocType names to match
424 * @param whereClause where the clause to qualify on
428 public DocumentWrapper<DocumentModelList> findDocs(
430 List<String> docTypes,
432 int pageSize, int pageNum, boolean computeTotal)
433 throws DocumentNotFoundException, DocumentException {
434 RepositoryInstance repoSession = null;
435 DocumentWrapper<DocumentModelList> wrapDoc = null;
438 if (docTypes == null || docTypes.size() < 1) {
439 throw new DocumentNotFoundException(
440 "findDocs must specify at least one DocumentType.");
442 repoSession = getRepositorySession();
443 DocumentModelList docList = null;
444 // force limit to 1, and ignore totalSize
445 QueryContext queryContext = new QueryContext(ctx, whereClause);
446 String query = buildNXQLQuery(docTypes, queryContext);
447 docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
448 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
449 } catch (IllegalArgumentException iae) {
451 } catch (Exception e) {
452 if (logger.isDebugEnabled()) {
453 logger.debug("Caught exception ", e);
455 throw new DocumentException(e);
457 if (repoSession != null) {
458 releaseRepositorySession(repoSession);
465 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
468 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
469 throws DocumentNotFoundException, DocumentException {
470 if (handler == null) {
471 throw new IllegalArgumentException(
472 "RepositoryJavaClient.getAll: handler is missing");
475 RepositoryInstance repoSession = null;
478 handler.prepare(Action.GET_ALL);
479 repoSession = getRepositorySession();
480 DocumentModelList docModelList = new DocumentModelListImpl();
481 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
482 for (String csid : csidList) {
483 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
484 DocumentModel docModel = repoSession.getDocument(docRef);
485 docModelList.add(docModel);
488 //set reposession to handle the document
489 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
490 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
491 handler.handle(Action.GET_ALL, wrapDoc);
492 handler.complete(Action.GET_ALL, wrapDoc);
493 } catch (DocumentException de) {
495 } catch (Exception e) {
496 if (logger.isDebugEnabled()) {
497 logger.debug("Caught exception ", e);
499 throw new DocumentException(e);
501 if (repoSession != null) {
502 releaseRepositorySession(repoSession);
508 * getAll get all documents for an entity entity service from the Nuxeo
511 * @param ctx service context under which this method is invoked
513 * should be used by the caller to provide and transform the
515 * @throws DocumentException
518 public void getAll(ServiceContext ctx, DocumentHandler handler)
519 throws DocumentNotFoundException, DocumentException {
520 if (handler == null) {
521 throw new IllegalArgumentException(
522 "RepositoryJavaClient.getAll: handler is missing");
524 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
525 if (nuxeoWspaceId == null) {
526 throw new DocumentNotFoundException(
527 "Unable to find workspace for service "
528 + ctx.getServiceName()
529 + " check if the workspace exists in the Nuxeo repository");
531 RepositoryInstance repoSession = null;
534 handler.prepare(Action.GET_ALL);
535 repoSession = getRepositorySession();
536 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
537 DocumentModelList docList = repoSession.getChildren(wsDocRef);
538 //set reposession to handle the document
539 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
540 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
541 handler.handle(Action.GET_ALL, wrapDoc);
542 handler.complete(Action.GET_ALL, wrapDoc);
543 } catch (DocumentException de) {
545 } catch (Exception e) {
546 if (logger.isDebugEnabled()) {
547 logger.debug("Caught exception ", e);
549 throw new DocumentException(e);
551 if (repoSession != null) {
552 releaseRepositorySession(repoSession);
558 * getFiltered get all documents for an entity service from the Document repository,
559 * given filter parameters specified by the handler.
560 * @param ctx service context under which this method is invoked
561 * @param handler should be used by the caller to provide and transform the document
562 * @throws DocumentNotFoundException if workspace not found
563 * @throws DocumentException
566 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
567 throws DocumentNotFoundException, DocumentException {
569 DocumentFilter filter = handler.getDocumentFilter();
570 String oldOrderBy = filter.getOrderByClause();
571 if (oldOrderBy!=null && oldOrderBy.isEmpty()){
572 filter.setOrderByClause("collectionspace_core:updatedAt DESC"); //per http://issues.collectionspace.org/browse/CSPACE-705
574 QueryContext queryContext = new QueryContext(ctx, handler);
575 RepositoryInstance repoSession = null;
577 handler.prepare(Action.GET_ALL);
578 repoSession = getRepositorySession();
579 DocumentModelList docList = null;
580 String query = buildNXQLQuery(queryContext);
582 if (logger.isDebugEnabled()) {
583 logger.debug("Executing NXQL query: " + query.toString());
586 // If we have limit and/or offset, then pass true to get totalSize
587 // in returned DocumentModelList.
588 Profiler profiler = new Profiler(this, 2);
589 profiler.log("Executing NXQL query: " + query.toString());
591 if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
592 docList = repoSession.query(query, null,
593 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
595 docList = repoSession.query(query);
599 //set repoSession to handle the document
600 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
601 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
602 handler.handle(Action.GET_ALL, wrapDoc);
603 handler.complete(Action.GET_ALL, wrapDoc);
604 } catch (DocumentException de) {
606 } catch (Exception e) {
607 if (logger.isDebugEnabled()) {
608 logger.debug("Caught exception ", e);
610 throw new DocumentException(e);
612 if (repoSession != null) {
613 releaseRepositorySession(repoSession);
619 * update given document in the Nuxeo repository
621 * @param ctx service context under which this method is invoked
625 * should be used by the caller to provide and transform the
627 * @throws DocumentException
630 public void update(ServiceContext ctx, String id, DocumentHandler handler)
631 throws BadRequestException, DocumentNotFoundException,
633 if (handler == null) {
634 throw new IllegalArgumentException(
635 "RepositoryJavaClient.update: handler is missing");
637 RepositoryInstance repoSession = null;
639 handler.prepare(Action.UPDATE);
640 repoSession = getRepositorySession();
641 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
642 DocumentModel doc = null;
644 doc = repoSession.getDocument(docRef);
645 } catch (ClientException ce) {
646 String msg = "Could not find document to update with id=" + id;
647 logger.error(msg, ce);
648 throw new DocumentNotFoundException(msg, ce);
650 //set reposession to handle the document
651 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
652 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
653 handler.handle(Action.UPDATE, wrapDoc);
654 setCollectionSpaceCoreValues(ctx, doc, Action.UPDATE);
655 repoSession.saveDocument(doc);
657 handler.complete(Action.UPDATE, wrapDoc);
658 } catch (BadRequestException bre) {
660 } catch (DocumentException de) {
662 } catch (Exception e) {
663 if (logger.isDebugEnabled()) {
664 logger.debug("Caught exception ", e);
666 throw new DocumentException(e);
668 if (repoSession != null) {
669 releaseRepositorySession(repoSession);
675 * delete a document from the Nuxeo repository
676 * @param ctx service context under which this method is invoked
679 * @throws DocumentException
682 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
685 if (logger.isDebugEnabled()) {
686 logger.debug("deleting document with id=" + id);
688 RepositoryInstance repoSession = null;
690 repoSession = getRepositorySession();
691 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
693 repoSession.removeDocument(docRef);
694 } catch (ClientException ce) {
695 String msg = "could not find document to delete with id=" + id;
696 logger.error(msg, ce);
697 throw new DocumentNotFoundException(msg, ce);
700 } catch (DocumentException de) {
702 } catch (Exception e) {
703 if (logger.isDebugEnabled()) {
704 logger.debug("Caught exception ", e);
706 throw new DocumentException(e);
708 if (repoSession != null) {
709 releaseRepositorySession(repoSession);
715 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
718 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
719 throws DocumentNotFoundException, DocumentException {
720 throw new UnsupportedOperationException();
724 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
725 return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
729 public String createDomain(String domainName) throws Exception {
730 RepositoryInstance repoSession = null;
731 String domainId = null;
733 repoSession = getRepositorySession();
734 DocumentRef parentDocRef = new PathRef("/");
735 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
736 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
737 domainName, "Domain");
738 doc.setPropertyValue("dc:title", domainName);
739 doc.setPropertyValue("dc:description", "A CollectionSpace domain "
741 doc = repoSession.createDocument(doc);
742 domainId = doc.getId();
744 if (logger.isDebugEnabled()) {
745 logger.debug("created tenant domain name=" + domainName
746 + " id=" + domainId);
748 } catch (Exception e) {
749 if (logger.isDebugEnabled()) {
750 logger.debug("createTenantSpace caught exception ", e);
754 if (repoSession != null) {
755 releaseRepositorySession(repoSession);
762 public String getDomainId(String domainName) throws Exception {
763 String domainId = null;
764 RepositoryInstance repoSession = null;
766 repoSession = getRepositorySession();
767 DocumentRef docRef = new PathRef(
769 DocumentModel domain = repoSession.getDocument(docRef);
770 domainId = domain.getId();
771 } catch (Exception e) {
772 if (logger.isDebugEnabled()) {
773 logger.debug("Caught exception ", e);
775 //there is no way to identify if document does not exist due to
776 //lack of typed exception for getDocument method
779 if (repoSession != null) {
780 releaseRepositorySession(repoSession);
787 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
790 public String createWorkspace(String domainName, String workspaceName) throws Exception {
791 RepositoryInstance repoSession = null;
792 String workspaceId = null;
794 repoSession = getRepositorySession();
795 DocumentRef parentDocRef = new PathRef(
797 + "/" + "workspaces");
798 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
799 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
800 workspaceName, "Workspace");
801 doc.setPropertyValue("dc:title", workspaceName);
802 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
804 doc = repoSession.createDocument(doc);
805 workspaceId = doc.getId();
807 if (logger.isDebugEnabled()) {
808 logger.debug("created workspace name=" + workspaceName
809 + " id=" + workspaceId);
811 } catch (Exception e) {
812 if (logger.isDebugEnabled()) {
813 logger.debug("createWorkspace caught exception ", e);
817 if (repoSession != null) {
818 releaseRepositorySession(repoSession);
825 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
828 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
829 String workspaceId = null;
830 RepositoryInstance repoSession = null;
832 repoSession = getRepositorySession();
833 DocumentRef docRef = new PathRef(
836 + "/" + workspaceName);
837 DocumentModel workspace = repoSession.getDocument(docRef);
838 workspaceId = workspace.getId();
839 } catch (DocumentException de) {
841 } catch (Exception e) {
842 if (logger.isDebugEnabled()) {
843 logger.debug("Caught exception ", e);
845 throw new DocumentException(e);
847 if (repoSession != null) {
848 releaseRepositorySession(repoSession);
855 * Append a WHERE clause to the NXQL query.
857 * @param query The NXQL query to which the WHERE clause will be appended.
858 * @param queryContext The query context, which provides the WHERE clause to append.
860 private final void appendNXQLWhere(StringBuilder query, QueryContext queryContext) {
862 // Restrict search to a specific Nuxeo domain
863 // TODO This is a slow method for tenant-filter
864 // We should make this a property that is indexed.
866 // query.append(" WHERE ecm:path STARTSWITH '/" + queryContext.domain + "'");
869 // Restrict search to the current tenant ID. Is the domain path filter (above) still needed?
871 query.append(/*IQueryManager.SEARCH_QUALIFIER_AND +*/ " WHERE " + DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA + ":"
872 + DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID
873 + " = " + queryContext.getTenantId());
875 // Finally, append the incoming where clause
877 String whereClause = queryContext.getWhereClause();
878 if (whereClause != null && ! whereClause.trim().isEmpty()) {
879 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
880 // into SQL, we need to parenthesize our 'where' clause
881 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "(" + whereClause + ")");
884 // Please lookup this use in Nuxeo support and document here
886 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "ecm:isProxy = 0");
890 * Append an ORDER BY clause to the NXQL query.
892 * @param query the NXQL query to which the ORDER BY clause will be appended.
893 * @param queryContext the query context, which provides the ORDER BY clause to append.
895 * @throws DocumentException if the supplied value of the orderBy clause is not valid.
898 private final void appendNXQLOrderBy(StringBuilder query, QueryContext queryContext)
900 String orderByClause = queryContext.getOrderByClause();
901 if (orderByClause != null && ! orderByClause.trim().isEmpty()) {
902 if (isValidOrderByClause(orderByClause)) {
903 query.append(" ORDER BY ");
904 query.append(orderByClause);
906 throw new DocumentException("Invalid format in sort request '" + orderByClause
907 + "': must be schema_name:fieldName followed by optional sort order (' ASC' or ' DESC').");
913 * Identifies whether the ORDER BY clause is valid.
915 * @param orderByClause the ORDER BY clause.
917 * @return true if the ORDER BY clause is valid;
918 * false if it is not.
920 private final boolean isValidOrderByClause(String orderByClause) {
921 boolean isValidClause = false;
923 Pattern orderByPattern = Pattern.compile(ORDER_BY_CLAUSE_REGEX);
924 Matcher orderByMatcher = orderByPattern.matcher(orderByClause);
925 if (orderByMatcher.matches()) {
926 isValidClause = true;
928 } catch (PatternSyntaxException pe) {
929 logger.warn("ORDER BY clause regex pattern '" + ORDER_BY_CLAUSE_REGEX
930 + "' could not be compiled: " + pe.getMessage());
931 // If reached, method will return a value of false.
933 return isValidClause;
938 * Builds an NXQL SELECT query for a single document type.
940 * @param queryContext The query context
941 * @return an NXQL query
942 * @throws Exception if supplied values in the query are invalid.
944 private final String buildNXQLQuery(QueryContext queryContext) throws Exception {
945 StringBuilder query = new StringBuilder("SELECT * FROM ");
946 query.append(queryContext.getDocType());
947 appendNXQLWhere(query, queryContext);
948 appendNXQLOrderBy(query, queryContext);
949 return query.toString();
953 * Builds an NXQL SELECT query across multiple document types.
955 * @param docTypes a list of document types to be queried
956 * @param queryContext the query context
957 * @return an NXQL query
959 private final String buildNXQLQuery(List<String> docTypes, QueryContext queryContext) {
960 StringBuilder query = new StringBuilder("SELECT * FROM ");
961 boolean fFirst = true;
962 for (String docType : docTypes) {
968 query.append(docType);
970 appendNXQLWhere(query, queryContext);
971 // FIXME add 'order by' clause here, if appropriate
972 return query.toString();
976 * Gets the repository session.
978 * @return the repository session
979 * @throws Exception the exception
981 private RepositoryInstance getRepositorySession() throws Exception {
982 // FIXME: is it possible to reuse repository session?
983 // Authentication failures happen while trying to reuse the session
984 Profiler profiler = new Profiler("getRepositorySession():", 2);
986 NuxeoClient client = NuxeoConnector.getInstance().getClient();
987 RepositoryInstance repoSession = client.openRepository();
988 if (logger.isTraceEnabled()) {
989 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
996 * Release repository session.
998 * @param repoSession the repo session
1000 private void releaseRepositorySession(RepositoryInstance repoSession) {
1002 NuxeoClient client = NuxeoConnector.getInstance().getClient();
1004 client.releaseRepository(repoSession);
1005 } catch (Exception e) {
1006 logger.error("Could not close the repository session", e);
1007 // no need to throw this service specific exception