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 CSID to the DublinCore title so we can see the CSID in the default
104 documentModel.setProperty("dublincore",
106 documentModel.getName());
107 } catch (Exception x) {
108 if (logger.isWarnEnabled() == true) {
109 logger.warn("Could not set the Dublin Core 'title' field on document CSID:" +
110 documentModel.getName());
114 // Add the tenant ID value to the new entity
116 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
117 DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID,
120 String now = GregorianCalendarDateTimeUtils.timestampUTC();
124 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
125 DocumentModelHandler.COLLECTIONSPACE_CORE_CREATED_AT,
127 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
128 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
132 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
133 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
142 * create document in the Nuxeo repository
144 * @param ctx service context under which this method is invoked
146 * should be used by the caller to provide and transform the
148 * @return id in repository of the newly created document
149 * @throws DocumentException
152 public String create(ServiceContext ctx,
153 DocumentHandler handler) throws BadRequestException,
156 if (ctx.getDocumentType() == null) {
157 throw new IllegalArgumentException(
158 "RepositoryJavaClient.create: docType is missing");
160 if (handler == null) {
161 throw new IllegalArgumentException(
162 "RepositoryJavaClient.create: handler is missing");
164 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
165 if (nuxeoWspaceId == null) {
166 throw new DocumentNotFoundException(
167 "Unable to find workspace for service " + ctx.getServiceName()
168 + " check if the workspace exists in the Nuxeo repository");
170 RepositoryInstance repoSession = null;
172 handler.prepare(Action.CREATE);
173 repoSession = getRepositorySession();
174 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
175 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
176 String wspacePath = wspaceDoc.getPathAsString();
177 //give our own ID so PathRef could be constructed later on
178 String id = IdUtils.generateId(UUID.randomUUID().toString());
179 // create document model
180 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
181 ctx.getDocumentType());
182 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
183 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
184 handler.handle(Action.CREATE, wrapDoc);
185 // create document with documentmodel
186 setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
187 doc = repoSession.createDocument(doc);
189 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
190 // and assume the handler has the state it needs (doc fragments).
191 handler.complete(Action.CREATE, wrapDoc);
193 } catch (BadRequestException bre) {
195 } catch (Exception e) {
196 if (logger.isDebugEnabled()) {
197 logger.debug("Caught exception ", e);
199 throw new DocumentException(e);
201 if (repoSession != null) {
202 releaseRepositorySession(repoSession);
209 * get document from the Nuxeo repository
210 * @param ctx service context under which this method is invoked
212 * of the document to retrieve
214 * should be used by the caller to provide and transform the
216 * @throws DocumentException
219 public void get(ServiceContext ctx, String id, DocumentHandler handler)
220 throws DocumentNotFoundException, DocumentException {
222 if (handler == null) {
223 throw new IllegalArgumentException(
224 "RepositoryJavaClient.get: handler is missing");
226 RepositoryInstance repoSession = null;
229 handler.prepare(Action.GET);
230 repoSession = getRepositorySession();
231 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
232 DocumentModel doc = null;
234 doc = repoSession.getDocument(docRef);
235 } catch (ClientException ce) {
236 String msg = "could not find document with id=" + id;
237 logger.error(msg, ce);
238 throw new DocumentNotFoundException(msg, ce);
240 //set reposession to handle the document
241 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
242 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
243 handler.handle(Action.GET, wrapDoc);
244 handler.complete(Action.GET, wrapDoc);
245 } catch (IllegalArgumentException iae) {
247 } catch (DocumentException de) {
249 } catch (Exception e) {
250 if (logger.isDebugEnabled()) {
251 logger.debug("Caught exception ", e);
253 throw new DocumentException(e);
255 if (repoSession != null) {
256 releaseRepositorySession(repoSession);
262 * get document from the Nuxeo repository, using the docFilter params.
263 * @param ctx service context under which this method is invoked
265 * should be used by the caller to provide and transform the
266 * document. Handler must have a docFilter set to return a single item.
267 * @throws DocumentException
270 public void get(ServiceContext ctx, DocumentHandler handler)
271 throws DocumentNotFoundException, DocumentException {
272 QueryContext queryContext = new QueryContext(ctx, handler);
273 RepositoryInstance repoSession = null;
276 handler.prepare(Action.GET);
277 repoSession = getRepositorySession();
279 DocumentModelList docList = null;
280 // force limit to 1, and ignore totalSize
281 String query = buildNXQLQuery(queryContext);
282 docList = repoSession.query(query, null, 1, 0, false);
283 if (docList.size() != 1) {
284 throw new DocumentNotFoundException("No document found matching filter params.");
286 DocumentModel doc = docList.get(0);
288 if (logger.isDebugEnabled()) {
289 logger.debug("Executed NXQL query: " + query);
292 //set reposession to handle the document
293 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
294 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
295 handler.handle(Action.GET, wrapDoc);
296 handler.complete(Action.GET, wrapDoc);
297 } catch (IllegalArgumentException iae) {
299 } catch (DocumentException de) {
301 } catch (Exception e) {
302 if (logger.isDebugEnabled()) {
303 logger.debug("Caught exception ", e);
305 throw new DocumentException(e);
307 if (repoSession != null) {
308 releaseRepositorySession(repoSession);
314 * get wrapped documentModel from the Nuxeo repository
315 * @param ctx service context under which this method is invoked
317 * of the document to retrieve
318 * @throws DocumentException
321 public DocumentWrapper<DocumentModel> getDoc(
322 ServiceContext ctx, String id)
323 throws DocumentNotFoundException, DocumentException {
324 RepositoryInstance repoSession = null;
325 DocumentWrapper<DocumentModel> wrapDoc = null;
328 repoSession = getRepositorySession();
329 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
330 DocumentModel doc = null;
332 doc = repoSession.getDocument(docRef);
333 } catch (ClientException ce) {
334 String msg = "could not find document with id=" + id;
335 logger.error(msg, ce);
336 throw new DocumentNotFoundException(msg, ce);
338 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
339 } catch (IllegalArgumentException iae) {
341 } catch (DocumentException de) {
343 } catch (Exception e) {
344 if (logger.isDebugEnabled()) {
345 logger.debug("Caught exception ", e);
347 throw new DocumentException(e);
349 if (repoSession != null) {
350 releaseRepositorySession(repoSession);
357 * find wrapped documentModel from the Nuxeo repository
358 * @param ctx service context under which this method is invoked
359 * @param whereClause where NXQL where clause to get the document
360 * @throws DocumentException
363 public DocumentWrapper<DocumentModel> findDoc(
364 ServiceContext ctx, String whereClause)
365 throws DocumentNotFoundException, DocumentException {
366 RepositoryInstance repoSession = null;
367 DocumentWrapper<DocumentModel> wrapDoc = null;
370 QueryContext queryContext = new QueryContext(ctx, whereClause);
371 repoSession = getRepositorySession();
372 DocumentModelList docList = null;
373 // force limit to 1, and ignore totalSize
374 String query = buildNXQLQuery(queryContext);
375 docList = repoSession.query(query,
380 if (docList.size() != 1) {
381 if (logger.isDebugEnabled()) {
382 logger.debug("findDoc: Query found: " + docList.size() + " items.");
383 logger.debug(" Query: " + query);
385 throw new DocumentNotFoundException("No document found matching filter params.");
387 DocumentModel doc = docList.get(0);
388 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
389 } catch (IllegalArgumentException iae) {
391 } catch (DocumentException de) {
393 } catch (Exception e) {
394 if (logger.isDebugEnabled()) {
395 logger.debug("Caught exception ", e);
397 throw new DocumentException(e);
399 if (repoSession != null) {
400 releaseRepositorySession(repoSession);
407 * find doc and return CSID from the Nuxeo repository
408 * @param ctx service context under which this method is invoked
409 * @param whereClause where NXQL where clause to get the document
410 * @throws DocumentException
413 public String findDocCSID(
414 ServiceContext ctx, String whereClause)
415 throws DocumentNotFoundException, DocumentException {
418 DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
419 DocumentModel docModel = wrapDoc.getWrappedObject();
420 csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
421 } catch (DocumentNotFoundException dnfe) {
423 } catch (IllegalArgumentException iae) {
425 } catch (DocumentException de) {
427 } catch (Exception e) {
428 if (logger.isDebugEnabled()) {
429 logger.debug("Caught exception ", e);
431 throw new DocumentException(e);
437 * Find a list of documentModels from the Nuxeo repository
438 * @param docTypes a list of DocType names to match
439 * @param whereClause where the clause to qualify on
443 public DocumentWrapper<DocumentModelList> findDocs(
445 List<String> docTypes,
447 int pageSize, int pageNum, boolean computeTotal)
448 throws DocumentNotFoundException, DocumentException {
449 RepositoryInstance repoSession = null;
450 DocumentWrapper<DocumentModelList> wrapDoc = null;
453 if (docTypes == null || docTypes.size() < 1) {
454 throw new DocumentNotFoundException(
455 "findDocs must specify at least one DocumentType.");
457 repoSession = getRepositorySession();
458 DocumentModelList docList = null;
459 // force limit to 1, and ignore totalSize
460 QueryContext queryContext = new QueryContext(ctx, whereClause);
461 String query = buildNXQLQuery(docTypes, queryContext);
462 docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
463 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
464 } catch (IllegalArgumentException iae) {
466 } catch (Exception e) {
467 if (logger.isDebugEnabled()) {
468 logger.debug("Caught exception ", e);
470 throw new DocumentException(e);
472 if (repoSession != null) {
473 releaseRepositorySession(repoSession);
480 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
483 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
484 throws DocumentNotFoundException, DocumentException {
485 if (handler == null) {
486 throw new IllegalArgumentException(
487 "RepositoryJavaClient.getAll: handler is missing");
490 RepositoryInstance repoSession = null;
493 handler.prepare(Action.GET_ALL);
494 repoSession = getRepositorySession();
495 DocumentModelList docModelList = new DocumentModelListImpl();
496 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
497 for (String csid : csidList) {
498 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
499 DocumentModel docModel = repoSession.getDocument(docRef);
500 docModelList.add(docModel);
503 //set reposession to handle the document
504 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
505 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
506 handler.handle(Action.GET_ALL, wrapDoc);
507 handler.complete(Action.GET_ALL, wrapDoc);
508 } catch (DocumentException de) {
510 } catch (Exception e) {
511 if (logger.isDebugEnabled()) {
512 logger.debug("Caught exception ", e);
514 throw new DocumentException(e);
516 if (repoSession != null) {
517 releaseRepositorySession(repoSession);
523 * getAll get all documents for an entity entity service from the Nuxeo
526 * @param ctx service context under which this method is invoked
528 * should be used by the caller to provide and transform the
530 * @throws DocumentException
533 public void getAll(ServiceContext ctx, DocumentHandler handler)
534 throws DocumentNotFoundException, DocumentException {
535 if (handler == null) {
536 throw new IllegalArgumentException(
537 "RepositoryJavaClient.getAll: handler is missing");
539 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
540 if (nuxeoWspaceId == null) {
541 throw new DocumentNotFoundException(
542 "Unable to find workspace for service "
543 + ctx.getServiceName()
544 + " check if the workspace exists in the Nuxeo repository");
546 RepositoryInstance repoSession = null;
549 handler.prepare(Action.GET_ALL);
550 repoSession = getRepositorySession();
551 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
552 DocumentModelList docList = repoSession.getChildren(wsDocRef);
553 //set reposession to handle the document
554 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
555 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
556 handler.handle(Action.GET_ALL, wrapDoc);
557 handler.complete(Action.GET_ALL, wrapDoc);
558 } catch (DocumentException de) {
560 } catch (Exception e) {
561 if (logger.isDebugEnabled()) {
562 logger.debug("Caught exception ", e);
564 throw new DocumentException(e);
566 if (repoSession != null) {
567 releaseRepositorySession(repoSession);
572 private boolean isClauseEmpty(String theString) {
573 boolean result = true;
574 if (theString != null && !theString.isEmpty()) {
581 * getFiltered get all documents for an entity service from the Document repository,
582 * given filter parameters specified by the handler.
583 * @param ctx service context under which this method is invoked
584 * @param handler should be used by the caller to provide and transform the document
585 * @throws DocumentNotFoundException if workspace not found
586 * @throws DocumentException
589 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
590 throws DocumentNotFoundException, DocumentException {
592 DocumentFilter filter = handler.getDocumentFilter();
593 String oldOrderBy = filter.getOrderByClause();
594 if (isClauseEmpty(oldOrderBy) == true){
595 filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED); //per http://issues.collectionspace.org/browse/CSPACE-705
597 QueryContext queryContext = new QueryContext(ctx, handler);
598 RepositoryInstance repoSession = null;
600 handler.prepare(Action.GET_ALL);
601 repoSession = getRepositorySession();
602 DocumentModelList docList = null;
603 String query = buildNXQLQuery(queryContext);
605 if (logger.isDebugEnabled()) {
606 logger.debug("Executing NXQL query: " + query.toString());
609 // If we have limit and/or offset, then pass true to get totalSize
610 // in returned DocumentModelList.
611 Profiler profiler = new Profiler(this, 2);
612 profiler.log("Executing NXQL query: " + query.toString());
614 if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
615 docList = repoSession.query(query, null,
616 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
618 docList = repoSession.query(query);
622 //set repoSession to handle the document
623 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
624 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
625 handler.handle(Action.GET_ALL, wrapDoc);
626 handler.complete(Action.GET_ALL, wrapDoc);
627 } catch (DocumentException de) {
629 } catch (Exception e) {
630 if (logger.isDebugEnabled()) {
631 logger.debug("Caught exception ", e);
633 throw new DocumentException(e);
635 if (repoSession != null) {
636 releaseRepositorySession(repoSession);
642 * update given document in the Nuxeo repository
644 * @param ctx service context under which this method is invoked
648 * should be used by the caller to provide and transform the
650 * @throws DocumentException
653 public void update(ServiceContext ctx, String id, DocumentHandler handler)
654 throws BadRequestException, DocumentNotFoundException,
656 if (handler == null) {
657 throw new IllegalArgumentException(
658 "RepositoryJavaClient.update: handler is missing");
660 RepositoryInstance repoSession = null;
662 handler.prepare(Action.UPDATE);
663 repoSession = getRepositorySession();
664 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
665 DocumentModel doc = null;
667 doc = repoSession.getDocument(docRef);
668 } catch (ClientException ce) {
669 String msg = "Could not find document to update with id=" + id;
670 logger.error(msg, ce);
671 throw new DocumentNotFoundException(msg, ce);
673 //set reposession to handle the document
674 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
675 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
676 handler.handle(Action.UPDATE, wrapDoc);
677 setCollectionSpaceCoreValues(ctx, doc, Action.UPDATE);
678 repoSession.saveDocument(doc);
680 handler.complete(Action.UPDATE, wrapDoc);
681 } catch (BadRequestException bre) {
683 } catch (DocumentException de) {
685 } catch (Exception e) {
686 if (logger.isDebugEnabled()) {
687 logger.debug("Caught exception ", e);
689 throw new DocumentException(e);
691 if (repoSession != null) {
692 releaseRepositorySession(repoSession);
698 * delete a document from the Nuxeo repository
699 * @param ctx service context under which this method is invoked
702 * @throws DocumentException
705 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
708 if (logger.isDebugEnabled()) {
709 logger.debug("deleting document with id=" + id);
711 RepositoryInstance repoSession = null;
713 repoSession = getRepositorySession();
714 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
716 repoSession.removeDocument(docRef);
717 } catch (ClientException ce) {
718 String msg = "could not find document to delete with id=" + id;
719 logger.error(msg, ce);
720 throw new DocumentNotFoundException(msg, ce);
723 } catch (DocumentException de) {
725 } catch (Exception e) {
726 if (logger.isDebugEnabled()) {
727 logger.debug("Caught exception ", e);
729 throw new DocumentException(e);
731 if (repoSession != null) {
732 releaseRepositorySession(repoSession);
738 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
741 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
742 throws DocumentNotFoundException, DocumentException {
743 throw new UnsupportedOperationException();
747 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
748 return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
752 public String createDomain(String domainName) throws Exception {
753 RepositoryInstance repoSession = null;
754 String domainId = null;
756 repoSession = getRepositorySession();
757 DocumentRef parentDocRef = new PathRef("/");
758 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
759 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
760 domainName, "Domain");
761 doc.setPropertyValue("dc:title", domainName);
762 doc.setPropertyValue("dc:description", "A CollectionSpace domain "
764 doc = repoSession.createDocument(doc);
765 domainId = doc.getId();
767 if (logger.isDebugEnabled()) {
768 logger.debug("created tenant domain name=" + domainName
769 + " id=" + domainId);
771 } catch (Exception e) {
772 if (logger.isDebugEnabled()) {
773 logger.debug("createTenantSpace caught exception ", e);
777 if (repoSession != null) {
778 releaseRepositorySession(repoSession);
785 public String getDomainId(String domainName) throws Exception {
786 String domainId = null;
787 RepositoryInstance repoSession = null;
789 repoSession = getRepositorySession();
790 DocumentRef docRef = new PathRef(
792 DocumentModel domain = repoSession.getDocument(docRef);
793 domainId = domain.getId();
794 } catch (Exception e) {
795 if (logger.isDebugEnabled()) {
796 logger.debug("Caught exception ", e);
798 //there is no way to identify if document does not exist due to
799 //lack of typed exception for getDocument method
802 if (repoSession != null) {
803 releaseRepositorySession(repoSession);
810 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
813 public String createWorkspace(String domainName, String workspaceName) throws Exception {
814 RepositoryInstance repoSession = null;
815 String workspaceId = null;
817 repoSession = getRepositorySession();
818 DocumentRef parentDocRef = new PathRef(
820 + "/" + "workspaces");
821 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
822 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
823 workspaceName, "Workspace");
824 doc.setPropertyValue("dc:title", workspaceName);
825 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
827 doc = repoSession.createDocument(doc);
828 workspaceId = doc.getId();
830 if (logger.isDebugEnabled()) {
831 logger.debug("created workspace name=" + workspaceName
832 + " id=" + workspaceId);
834 } catch (Exception e) {
835 if (logger.isDebugEnabled()) {
836 logger.debug("createWorkspace caught exception ", e);
840 if (repoSession != null) {
841 releaseRepositorySession(repoSession);
848 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
851 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
852 String workspaceId = null;
853 RepositoryInstance repoSession = null;
855 repoSession = getRepositorySession();
856 DocumentRef docRef = new PathRef(
859 + "/" + workspaceName);
860 DocumentModel workspace = repoSession.getDocument(docRef);
861 workspaceId = workspace.getId();
862 } catch (DocumentException de) {
864 } catch (Exception e) {
865 if (logger.isDebugEnabled()) {
866 logger.debug("Caught exception ", e);
868 throw new DocumentException(e);
870 if (repoSession != null) {
871 releaseRepositorySession(repoSession);
878 * Append a WHERE clause to the NXQL query.
880 * @param query The NXQL query to which the WHERE clause will be appended.
881 * @param queryContext The query context, which provides the WHERE clause to append.
883 private final void appendNXQLWhere(StringBuilder query, QueryContext queryContext) {
885 // Restrict search to a specific Nuxeo domain
886 // TODO This is a slow method for tenant-filter
887 // We should make this a property that is indexed.
889 // query.append(" WHERE ecm:path STARTSWITH '/" + queryContext.domain + "'");
892 // Restrict search to the current tenant ID. Is the domain path filter (above) still needed?
894 query.append(/*IQueryManager.SEARCH_QUALIFIER_AND +*/ " WHERE " + DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA + ":"
895 + DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID
896 + " = " + queryContext.getTenantId());
898 // Finally, append the incoming where clause
900 String whereClause = queryContext.getWhereClause();
901 if (whereClause != null && ! whereClause.trim().isEmpty()) {
902 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
903 // into SQL, we need to parenthesize our 'where' clause
904 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "(" + whereClause + ")");
907 // Please lookup this use in Nuxeo support and document here
909 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "ecm:isProxy = 0");
913 * Append an ORDER BY clause to the NXQL query.
915 * @param query the NXQL query to which the ORDER BY clause will be appended.
916 * @param queryContext the query context, which provides the ORDER BY clause to append.
918 * @throws DocumentException if the supplied value of the orderBy clause is not valid.
921 private final void appendNXQLOrderBy(StringBuilder query, QueryContext queryContext)
923 String orderByClause = queryContext.getOrderByClause();
924 if (orderByClause != null && ! orderByClause.trim().isEmpty()) {
925 if (isValidOrderByClause(orderByClause)) {
926 query.append(" ORDER BY ");
927 query.append(orderByClause);
929 throw new DocumentException("Invalid format in sort request '" + orderByClause
930 + "': must be schema_name:fieldName followed by optional sort order (' ASC' or ' DESC').");
936 * Identifies whether the ORDER BY clause is valid.
938 * @param orderByClause the ORDER BY clause.
940 * @return true if the ORDER BY clause is valid;
941 * false if it is not.
943 private final boolean isValidOrderByClause(String orderByClause) {
944 boolean isValidClause = false;
946 Pattern orderByPattern = Pattern.compile(ORDER_BY_CLAUSE_REGEX);
947 Matcher orderByMatcher = orderByPattern.matcher(orderByClause);
948 if (orderByMatcher.matches()) {
949 isValidClause = true;
951 } catch (PatternSyntaxException pe) {
952 logger.warn("ORDER BY clause regex pattern '" + ORDER_BY_CLAUSE_REGEX
953 + "' could not be compiled: " + pe.getMessage());
954 // If reached, method will return a value of false.
956 return isValidClause;
961 * Builds an NXQL SELECT query for a single document type.
963 * @param queryContext The query context
964 * @return an NXQL query
965 * @throws Exception if supplied values in the query are invalid.
967 private final String buildNXQLQuery(QueryContext queryContext) throws Exception {
968 StringBuilder query = new StringBuilder("SELECT * FROM ");
969 query.append(queryContext.getDocType());
970 appendNXQLWhere(query, queryContext);
971 appendNXQLOrderBy(query, queryContext);
972 return query.toString();
976 * Builds an NXQL SELECT query across multiple document types.
978 * @param docTypes a list of document types to be queried
979 * @param queryContext the query context
980 * @return an NXQL query
982 private final String buildNXQLQuery(List<String> docTypes, QueryContext queryContext) {
983 StringBuilder query = new StringBuilder("SELECT * FROM ");
984 boolean fFirst = true;
985 for (String docType : docTypes) {
991 query.append(docType);
993 appendNXQLWhere(query, queryContext);
994 // FIXME add 'order by' clause here, if appropriate
995 return query.toString();
999 * Gets the repository session.
1001 * @return the repository session
1002 * @throws Exception the exception
1004 private RepositoryInstance getRepositorySession() throws Exception {
1005 // FIXME: is it possible to reuse repository session?
1006 // Authentication failures happen while trying to reuse the session
1007 Profiler profiler = new Profiler("getRepositorySession():", 2);
1009 NuxeoClient client = NuxeoConnector.getInstance().getClient();
1010 RepositoryInstance repoSession = client.openRepository();
1011 if (logger.isTraceEnabled()) {
1012 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
1019 * Release repository session.
1021 * @param repoSession the repo session
1023 private void releaseRepositorySession(RepositoryInstance repoSession) {
1025 NuxeoClient client = NuxeoConnector.getInstance().getClient();
1027 client.releaseRepository(repoSession);
1028 } catch (Exception e) {
1029 logger.error("Could not close the repository session", e);
1030 // no need to throw this service specific exception