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.client.PoxPayloadIn;
28 import org.collectionspace.services.client.PoxPayloadOut;
29 import org.collectionspace.services.common.context.ServiceContext;
30 import org.collectionspace.services.common.datetime.GregorianCalendarDateTimeUtils;
31 import org.collectionspace.services.common.query.IQueryManager;
32 import org.collectionspace.services.common.query.QueryContext;
33 import org.collectionspace.services.common.repository.RepositoryClient;
34 import org.collectionspace.services.common.profile.Profiler;
35 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
37 import org.collectionspace.services.common.document.BadRequestException;
38 import org.collectionspace.services.common.document.DocumentException;
39 import org.collectionspace.services.common.document.DocumentFilter;
40 import org.collectionspace.services.common.document.DocumentHandler;
41 import org.collectionspace.services.common.document.DocumentNotFoundException;
42 import org.collectionspace.services.common.document.DocumentHandler.Action;
43 import org.collectionspace.services.common.document.DocumentWrapper;
44 import org.collectionspace.services.common.document.DocumentWrapperImpl;
46 import org.jboss.resteasy.plugins.providers.multipart.MultipartInput;
47 import org.jboss.resteasy.plugins.providers.multipart.MultipartOutput;
49 import org.nuxeo.common.utils.IdUtils;
50 import org.nuxeo.ecm.core.api.ClientException;
51 import org.nuxeo.ecm.core.api.DocumentModel;
52 import org.nuxeo.ecm.core.api.DocumentModelList;
53 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
54 import org.nuxeo.ecm.core.api.DocumentRef;
55 import org.nuxeo.ecm.core.api.IdRef;
56 import org.nuxeo.ecm.core.api.PathRef;
57 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
58 import org.nuxeo.ecm.core.client.NuxeoClient;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
65 * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
68 * $LastChangedRevision: $ $LastChangedDate: $
70 public class RepositoryJavaClientImpl implements RepositoryClient {
73 private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
74 // private final Logger profilerLogger = LoggerFactory.getLogger("remperf");
75 // private String foo = Profiler.createLogger();
77 // Regular expressions pattern for identifying valid ORDER BY clauses.
78 // FIXME: Currently supports only USASCII word characters in field names.
79 final String ORDER_BY_CLAUSE_REGEX = "\\w+(_\\w+)?:\\w+( ASC| DESC)?(, \\w+(_\\w+)?:\\w+( ASC| DESC)?)*";
82 * Instantiates a new repository java client impl.
84 public RepositoryJavaClientImpl() {
90 * Sets the collection space core values.
93 * @param documentModel the document model
94 * @throws ClientException the client exception
96 private void setCollectionSpaceCoreValues(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
97 DocumentModel documentModel,
98 Action action) throws ClientException {
100 // Add the tenant ID value to the new entity
102 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
103 DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID,
106 String now = GregorianCalendarDateTimeUtils.timestampUTC();
110 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
111 DocumentModelHandler.COLLECTIONSPACE_CORE_CREATED_AT,
113 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
114 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
118 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
119 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
128 * create document in the Nuxeo repository
130 * @param ctx service context under which this method is invoked
132 * should be used by the caller to provide and transform the
134 * @return id in repository of the newly created document
135 * @throws DocumentException
138 public String create(ServiceContext ctx,
139 DocumentHandler handler) throws BadRequestException,
142 if (ctx.getDocumentType() == null) {
143 throw new IllegalArgumentException(
144 "RepositoryJavaClient.create: docType is missing");
146 if (handler == null) {
147 throw new IllegalArgumentException(
148 "RepositoryJavaClient.create: handler is missing");
150 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
151 if (nuxeoWspaceId == null) {
152 throw new DocumentNotFoundException(
153 "Unable to find workspace for service " + ctx.getServiceName()
154 + " check if the workspace exists in the Nuxeo repository");
156 RepositoryInstance repoSession = null;
158 handler.prepare(Action.CREATE);
159 repoSession = getRepositorySession();
160 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
161 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
162 String wspacePath = wspaceDoc.getPathAsString();
163 //give our own ID so PathRef could be constructed later on
164 String id = IdUtils.generateId(UUID.randomUUID().toString());
165 // create document model
166 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
167 ctx.getDocumentType());
168 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
169 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
170 handler.handle(Action.CREATE, wrapDoc);
171 // create document with documentmodel
172 setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
173 doc = repoSession.createDocument(doc);
175 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
176 // and assume the handler has the state it needs (doc fragments).
177 handler.complete(Action.CREATE, wrapDoc);
179 } catch (BadRequestException bre) {
181 } catch (Exception e) {
182 if (logger.isDebugEnabled()) {
183 logger.debug("Caught exception ", e);
185 throw new DocumentException(e);
187 if (repoSession != null) {
188 releaseRepositorySession(repoSession);
195 * get document from the Nuxeo repository
196 * @param ctx service context under which this method is invoked
198 * of the document to retrieve
200 * should be used by the caller to provide and transform the
202 * @throws DocumentException
205 public void get(ServiceContext ctx, String id, DocumentHandler handler)
206 throws DocumentNotFoundException, DocumentException {
208 if (handler == null) {
209 throw new IllegalArgumentException(
210 "RepositoryJavaClient.get: handler is missing");
212 RepositoryInstance repoSession = null;
215 handler.prepare(Action.GET);
216 repoSession = getRepositorySession();
217 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
218 DocumentModel doc = null;
220 doc = repoSession.getDocument(docRef);
221 } catch (ClientException ce) {
222 String msg = "could not find document with id=" + id;
223 logger.error(msg, ce);
224 throw new DocumentNotFoundException(msg, ce);
226 //set reposession to handle the document
227 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
228 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
229 handler.handle(Action.GET, wrapDoc);
230 handler.complete(Action.GET, wrapDoc);
231 } catch (IllegalArgumentException iae) {
233 } catch (DocumentException de) {
235 } catch (Exception e) {
236 if (logger.isDebugEnabled()) {
237 logger.debug("Caught exception ", e);
239 throw new DocumentException(e);
241 if (repoSession != null) {
242 releaseRepositorySession(repoSession);
248 * get document from the Nuxeo repository, using the docFilter params.
249 * @param ctx service context under which this method is invoked
251 * should be used by the caller to provide and transform the
252 * document. Handler must have a docFilter set to return a single item.
253 * @throws DocumentException
256 public void get(ServiceContext ctx, DocumentHandler handler)
257 throws DocumentNotFoundException, DocumentException {
258 QueryContext queryContext = new QueryContext(ctx, handler);
259 RepositoryInstance repoSession = null;
262 handler.prepare(Action.GET);
263 repoSession = getRepositorySession();
265 DocumentModelList docList = null;
266 // force limit to 1, and ignore totalSize
267 String query = buildNXQLQuery(queryContext);
268 docList = repoSession.query(query, null, 1, 0, false);
269 if (docList.size() != 1) {
270 throw new DocumentNotFoundException("No document found matching filter params.");
272 DocumentModel doc = docList.get(0);
274 if (logger.isDebugEnabled()) {
275 logger.debug("Executed NXQL query: " + query);
278 //set reposession to handle the document
279 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
280 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
281 handler.handle(Action.GET, wrapDoc);
282 handler.complete(Action.GET, wrapDoc);
283 } catch (IllegalArgumentException iae) {
285 } catch (DocumentException de) {
287 } catch (Exception e) {
288 if (logger.isDebugEnabled()) {
289 logger.debug("Caught exception ", e);
291 throw new DocumentException(e);
293 if (repoSession != null) {
294 releaseRepositorySession(repoSession);
300 * get wrapped documentModel from the Nuxeo repository
301 * @param ctx service context under which this method is invoked
303 * of the document to retrieve
304 * @throws DocumentException
307 public DocumentWrapper<DocumentModel> getDoc(
308 ServiceContext ctx, String id)
309 throws DocumentNotFoundException, DocumentException {
310 RepositoryInstance repoSession = null;
311 DocumentWrapper<DocumentModel> wrapDoc = null;
314 repoSession = getRepositorySession();
315 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
316 DocumentModel doc = null;
318 doc = repoSession.getDocument(docRef);
319 } catch (ClientException ce) {
320 String msg = "could not find document with id=" + id;
321 logger.error(msg, ce);
322 throw new DocumentNotFoundException(msg, ce);
324 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
325 } catch (IllegalArgumentException iae) {
327 } catch (DocumentException de) {
329 } catch (Exception e) {
330 if (logger.isDebugEnabled()) {
331 logger.debug("Caught exception ", e);
333 throw new DocumentException(e);
335 if (repoSession != null) {
336 releaseRepositorySession(repoSession);
343 * find wrapped documentModel from the Nuxeo repository
344 * @param ctx service context under which this method is invoked
345 * @param whereClause where NXQL where clause to get the document
346 * @throws DocumentException
349 public DocumentWrapper<DocumentModel> findDoc(
350 ServiceContext ctx, String whereClause)
351 throws DocumentNotFoundException, DocumentException {
352 RepositoryInstance repoSession = null;
353 DocumentWrapper<DocumentModel> wrapDoc = null;
356 QueryContext queryContext = new QueryContext(ctx, whereClause);
357 repoSession = getRepositorySession();
358 DocumentModelList docList = null;
359 // force limit to 1, and ignore totalSize
360 String query = buildNXQLQuery(queryContext);
361 docList = repoSession.query(query,
366 if (docList.size() != 1) {
367 if (logger.isDebugEnabled()) {
368 logger.debug("findDoc: Query found: " + docList.size() + " items.");
369 logger.debug(" Query: " + query);
371 throw new DocumentNotFoundException("No document found matching filter params.");
373 DocumentModel doc = docList.get(0);
374 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
375 } catch (IllegalArgumentException iae) {
377 } catch (DocumentException de) {
379 } catch (Exception e) {
380 if (logger.isDebugEnabled()) {
381 logger.debug("Caught exception ", e);
383 throw new DocumentException(e);
385 if (repoSession != null) {
386 releaseRepositorySession(repoSession);
393 * find doc and return CSID from the Nuxeo repository
394 * @param ctx service context under which this method is invoked
395 * @param whereClause where NXQL where clause to get the document
396 * @throws DocumentException
399 public String findDocCSID(
400 ServiceContext ctx, String whereClause)
401 throws DocumentNotFoundException, DocumentException {
404 DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
405 DocumentModel docModel = wrapDoc.getWrappedObject();
406 csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
407 } catch (DocumentNotFoundException dnfe) {
409 } catch (IllegalArgumentException iae) {
411 } catch (DocumentException de) {
413 } catch (Exception e) {
414 if (logger.isDebugEnabled()) {
415 logger.debug("Caught exception ", e);
417 throw new DocumentException(e);
423 * Find a list of documentModels from the Nuxeo repository
424 * @param docTypes a list of DocType names to match
425 * @param whereClause where the clause to qualify on
429 public DocumentWrapper<DocumentModelList> findDocs(
431 List<String> docTypes,
433 int pageSize, int pageNum, boolean computeTotal)
434 throws DocumentNotFoundException, DocumentException {
435 RepositoryInstance repoSession = null;
436 DocumentWrapper<DocumentModelList> wrapDoc = null;
439 if (docTypes == null || docTypes.size() < 1) {
440 throw new DocumentNotFoundException(
441 "findDocs must specify at least one DocumentType.");
443 repoSession = getRepositorySession();
444 DocumentModelList docList = null;
445 // force limit to 1, and ignore totalSize
446 QueryContext queryContext = new QueryContext(ctx, whereClause);
447 String query = buildNXQLQuery(docTypes, queryContext);
448 docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
449 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
450 } catch (IllegalArgumentException iae) {
452 } catch (Exception e) {
453 if (logger.isDebugEnabled()) {
454 logger.debug("Caught exception ", e);
456 throw new DocumentException(e);
458 if (repoSession != null) {
459 releaseRepositorySession(repoSession);
466 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
469 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
470 throws DocumentNotFoundException, DocumentException {
471 if (handler == null) {
472 throw new IllegalArgumentException(
473 "RepositoryJavaClient.getAll: handler is missing");
476 RepositoryInstance repoSession = null;
479 handler.prepare(Action.GET_ALL);
480 repoSession = getRepositorySession();
481 DocumentModelList docModelList = new DocumentModelListImpl();
482 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
483 for (String csid : csidList) {
484 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
485 DocumentModel docModel = repoSession.getDocument(docRef);
486 docModelList.add(docModel);
489 //set reposession to handle the document
490 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
491 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
492 handler.handle(Action.GET_ALL, wrapDoc);
493 handler.complete(Action.GET_ALL, wrapDoc);
494 } catch (DocumentException de) {
496 } catch (Exception e) {
497 if (logger.isDebugEnabled()) {
498 logger.debug("Caught exception ", e);
500 throw new DocumentException(e);
502 if (repoSession != null) {
503 releaseRepositorySession(repoSession);
509 * getAll get all documents for an entity entity service from the Nuxeo
512 * @param ctx service context under which this method is invoked
514 * should be used by the caller to provide and transform the
516 * @throws DocumentException
519 public void getAll(ServiceContext ctx, DocumentHandler handler)
520 throws DocumentNotFoundException, DocumentException {
521 if (handler == null) {
522 throw new IllegalArgumentException(
523 "RepositoryJavaClient.getAll: handler is missing");
525 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
526 if (nuxeoWspaceId == null) {
527 throw new DocumentNotFoundException(
528 "Unable to find workspace for service "
529 + ctx.getServiceName()
530 + " check if the workspace exists in the Nuxeo repository");
532 RepositoryInstance repoSession = null;
535 handler.prepare(Action.GET_ALL);
536 repoSession = getRepositorySession();
537 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
538 DocumentModelList docList = repoSession.getChildren(wsDocRef);
539 //set reposession to handle the document
540 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
541 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
542 handler.handle(Action.GET_ALL, wrapDoc);
543 handler.complete(Action.GET_ALL, wrapDoc);
544 } catch (DocumentException de) {
546 } catch (Exception e) {
547 if (logger.isDebugEnabled()) {
548 logger.debug("Caught exception ", e);
550 throw new DocumentException(e);
552 if (repoSession != null) {
553 releaseRepositorySession(repoSession);
559 * getFiltered get all documents for an entity service from the Document repository,
560 * given filter parameters specified by the handler.
561 * @param ctx service context under which this method is invoked
562 * @param handler should be used by the caller to provide and transform the document
563 * @throws DocumentNotFoundException if workspace not found
564 * @throws DocumentException
567 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
568 throws DocumentNotFoundException, DocumentException {
570 DocumentFilter filter = handler.getDocumentFilter();
571 String oldOrderBy = filter.getOrderByClause();
572 if (oldOrderBy!=null && oldOrderBy.isEmpty()){
573 filter.setOrderByClause("collectionspace_core:updatedAt DESC"); //per http://issues.collectionspace.org/browse/CSPACE-705
575 QueryContext queryContext = new QueryContext(ctx, handler);
576 RepositoryInstance repoSession = null;
578 handler.prepare(Action.GET_ALL);
579 repoSession = getRepositorySession();
580 DocumentModelList docList = null;
581 String query = buildNXQLQuery(queryContext);
583 if (logger.isDebugEnabled()) {
584 logger.debug("Executing NXQL query: " + query.toString());
587 // If we have limit and/or offset, then pass true to get totalSize
588 // in returned DocumentModelList.
589 Profiler profiler = new Profiler(this, 2);
590 profiler.log("Executing NXQL query: " + query.toString());
592 if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
593 docList = repoSession.query(query, null,
594 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
596 docList = repoSession.query(query);
600 //set repoSession to handle the document
601 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
602 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
603 handler.handle(Action.GET_ALL, wrapDoc);
604 handler.complete(Action.GET_ALL, wrapDoc);
605 } catch (DocumentException de) {
607 } catch (Exception e) {
608 if (logger.isDebugEnabled()) {
609 logger.debug("Caught exception ", e);
611 throw new DocumentException(e);
613 if (repoSession != null) {
614 releaseRepositorySession(repoSession);
620 * update given document in the Nuxeo repository
622 * @param ctx service context under which this method is invoked
626 * should be used by the caller to provide and transform the
628 * @throws DocumentException
631 public void update(ServiceContext ctx, String id, DocumentHandler handler)
632 throws BadRequestException, DocumentNotFoundException,
634 if (handler == null) {
635 throw new IllegalArgumentException(
636 "RepositoryJavaClient.update: handler is missing");
638 RepositoryInstance repoSession = null;
640 handler.prepare(Action.UPDATE);
641 repoSession = getRepositorySession();
642 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
643 DocumentModel doc = null;
645 doc = repoSession.getDocument(docRef);
646 } catch (ClientException ce) {
647 String msg = "Could not find document to update with id=" + id;
648 logger.error(msg, ce);
649 throw new DocumentNotFoundException(msg, ce);
651 //set reposession to handle the document
652 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
653 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
654 handler.handle(Action.UPDATE, wrapDoc);
655 setCollectionSpaceCoreValues(ctx, doc, Action.UPDATE);
656 repoSession.saveDocument(doc);
658 handler.complete(Action.UPDATE, wrapDoc);
659 } catch (BadRequestException bre) {
661 } catch (DocumentException de) {
663 } catch (Exception e) {
664 if (logger.isDebugEnabled()) {
665 logger.debug("Caught exception ", e);
667 throw new DocumentException(e);
669 if (repoSession != null) {
670 releaseRepositorySession(repoSession);
676 * delete a document from the Nuxeo repository
677 * @param ctx service context under which this method is invoked
680 * @throws DocumentException
683 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
686 if (logger.isDebugEnabled()) {
687 logger.debug("deleting document with id=" + id);
689 RepositoryInstance repoSession = null;
691 repoSession = getRepositorySession();
692 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
694 repoSession.removeDocument(docRef);
695 } catch (ClientException ce) {
696 String msg = "could not find document to delete with id=" + id;
697 logger.error(msg, ce);
698 throw new DocumentNotFoundException(msg, ce);
701 } catch (DocumentException de) {
703 } catch (Exception e) {
704 if (logger.isDebugEnabled()) {
705 logger.debug("Caught exception ", e);
707 throw new DocumentException(e);
709 if (repoSession != null) {
710 releaseRepositorySession(repoSession);
716 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
719 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
720 throws DocumentNotFoundException, DocumentException {
721 throw new UnsupportedOperationException();
725 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
726 return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
730 public String createDomain(String domainName) throws Exception {
731 RepositoryInstance repoSession = null;
732 String domainId = null;
734 repoSession = getRepositorySession();
735 DocumentRef parentDocRef = new PathRef("/");
736 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
737 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
738 domainName, "Domain");
739 doc.setPropertyValue("dc:title", domainName);
740 doc.setPropertyValue("dc:description", "A CollectionSpace domain "
742 doc = repoSession.createDocument(doc);
743 domainId = doc.getId();
745 if (logger.isDebugEnabled()) {
746 logger.debug("created tenant domain name=" + domainName
747 + " id=" + domainId);
749 } catch (Exception e) {
750 if (logger.isDebugEnabled()) {
751 logger.debug("createTenantSpace caught exception ", e);
755 if (repoSession != null) {
756 releaseRepositorySession(repoSession);
763 public String getDomainId(String domainName) throws Exception {
764 String domainId = null;
765 RepositoryInstance repoSession = null;
767 repoSession = getRepositorySession();
768 DocumentRef docRef = new PathRef(
770 DocumentModel domain = repoSession.getDocument(docRef);
771 domainId = domain.getId();
772 } catch (Exception e) {
773 if (logger.isDebugEnabled()) {
774 logger.debug("Caught exception ", e);
776 //there is no way to identify if document does not exist due to
777 //lack of typed exception for getDocument method
780 if (repoSession != null) {
781 releaseRepositorySession(repoSession);
788 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
791 public String createWorkspace(String domainName, String workspaceName) throws Exception {
792 RepositoryInstance repoSession = null;
793 String workspaceId = null;
795 repoSession = getRepositorySession();
796 DocumentRef parentDocRef = new PathRef(
798 + "/" + "workspaces");
799 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
800 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
801 workspaceName, "Workspace");
802 doc.setPropertyValue("dc:title", workspaceName);
803 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
805 doc = repoSession.createDocument(doc);
806 workspaceId = doc.getId();
808 if (logger.isDebugEnabled()) {
809 logger.debug("created workspace name=" + workspaceName
810 + " id=" + workspaceId);
812 } catch (Exception e) {
813 if (logger.isDebugEnabled()) {
814 logger.debug("createWorkspace caught exception ", e);
818 if (repoSession != null) {
819 releaseRepositorySession(repoSession);
826 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
829 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
830 String workspaceId = null;
831 RepositoryInstance repoSession = null;
833 repoSession = getRepositorySession();
834 DocumentRef docRef = new PathRef(
837 + "/" + workspaceName);
838 DocumentModel workspace = repoSession.getDocument(docRef);
839 workspaceId = workspace.getId();
840 } catch (DocumentException de) {
842 } catch (Exception e) {
843 if (logger.isDebugEnabled()) {
844 logger.debug("Caught exception ", e);
846 throw new DocumentException(e);
848 if (repoSession != null) {
849 releaseRepositorySession(repoSession);
856 * Append a WHERE clause to the NXQL query.
858 * @param query The NXQL query to which the WHERE clause will be appended.
859 * @param queryContext The query context, which provides the WHERE clause to append.
861 private final void appendNXQLWhere(StringBuilder query, QueryContext queryContext) {
863 // Restrict search to a specific Nuxeo domain
864 // TODO This is a slow method for tenant-filter
865 // We should make this a property that is indexed.
867 // query.append(" WHERE ecm:path STARTSWITH '/" + queryContext.domain + "'");
870 // Restrict search to the current tenant ID. Is the domain path filter (above) still needed?
872 query.append(/*IQueryManager.SEARCH_QUALIFIER_AND +*/ " WHERE " + DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA + ":"
873 + DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID
874 + " = " + queryContext.getTenantId());
876 // Finally, append the incoming where clause
878 String whereClause = queryContext.getWhereClause();
879 if (whereClause != null && ! whereClause.trim().isEmpty()) {
880 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
881 // into SQL, we need to parenthesize our 'where' clause
882 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "(" + whereClause + ")");
885 // Please lookup this use in Nuxeo support and document here
887 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "ecm:isProxy = 0");
891 * Append an ORDER BY clause to the NXQL query.
893 * @param query the NXQL query to which the ORDER BY clause will be appended.
894 * @param queryContext the query context, which provides the ORDER BY clause to append.
896 * @throws DocumentException if the supplied value of the orderBy clause is not valid.
899 private final void appendNXQLOrderBy(StringBuilder query, QueryContext queryContext)
901 String orderByClause = queryContext.getOrderByClause();
902 if (orderByClause != null && ! orderByClause.trim().isEmpty()) {
903 if (isValidOrderByClause(orderByClause)) {
904 query.append(" ORDER BY ");
905 query.append(orderByClause);
907 throw new DocumentException("Invalid format in sort request '" + orderByClause
908 + "': must be schema_name:fieldName followed by optional sort order (' ASC' or ' DESC').");
914 * Identifies whether the ORDER BY clause is valid.
916 * @param orderByClause the ORDER BY clause.
918 * @return true if the ORDER BY clause is valid;
919 * false if it is not.
921 private final boolean isValidOrderByClause(String orderByClause) {
922 boolean isValidClause = false;
924 Pattern orderByPattern = Pattern.compile(ORDER_BY_CLAUSE_REGEX);
925 Matcher orderByMatcher = orderByPattern.matcher(orderByClause);
926 if (orderByMatcher.matches()) {
927 isValidClause = true;
929 } catch (PatternSyntaxException pe) {
930 logger.warn("ORDER BY clause regex pattern '" + ORDER_BY_CLAUSE_REGEX
931 + "' could not be compiled: " + pe.getMessage());
932 // If reached, method will return a value of false.
934 return isValidClause;
939 * Builds an NXQL SELECT query for a single document type.
941 * @param queryContext The query context
942 * @return an NXQL query
943 * @throws Exception if supplied values in the query are invalid.
945 private final String buildNXQLQuery(QueryContext queryContext) throws Exception {
946 StringBuilder query = new StringBuilder("SELECT * FROM ");
947 query.append(queryContext.getDocType());
948 appendNXQLWhere(query, queryContext);
949 appendNXQLOrderBy(query, queryContext);
950 return query.toString();
954 * Builds an NXQL SELECT query across multiple document types.
956 * @param docTypes a list of document types to be queried
957 * @param queryContext the query context
958 * @return an NXQL query
960 private final String buildNXQLQuery(List<String> docTypes, QueryContext queryContext) {
961 StringBuilder query = new StringBuilder("SELECT * FROM ");
962 boolean fFirst = true;
963 for (String docType : docTypes) {
969 query.append(docType);
971 appendNXQLWhere(query, queryContext);
972 // FIXME add 'order by' clause here, if appropriate
973 return query.toString();
977 * Gets the repository session.
979 * @return the repository session
980 * @throws Exception the exception
982 private RepositoryInstance getRepositorySession() throws Exception {
983 // FIXME: is it possible to reuse repository session?
984 // Authentication failures happen while trying to reuse the session
985 Profiler profiler = new Profiler("getRepositorySession():", 2);
987 NuxeoClient client = NuxeoConnector.getInstance().getClient();
988 RepositoryInstance repoSession = client.openRepository();
989 if (logger.isTraceEnabled()) {
990 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
997 * Release repository session.
999 * @param repoSession the repo session
1001 private void releaseRepositorySession(RepositoryInstance repoSession) {
1003 NuxeoClient client = NuxeoConnector.getInstance().getClient();
1005 client.releaseRepository(repoSession);
1006 } catch (Exception e) {
1007 logger.error("Could not close the repository session", e);
1008 // no need to throw this service specific exception