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.IQueryManager;
28 import org.collectionspace.services.client.PoxPayloadIn;
29 import org.collectionspace.services.client.PoxPayloadOut;
30 import org.collectionspace.services.common.context.ServiceContext;
31 import org.collectionspace.services.common.datetime.GregorianCalendarDateTimeUtils;
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.nuxeo.common.utils.IdUtils;
47 import org.nuxeo.ecm.core.api.ClientException;
48 import org.nuxeo.ecm.core.api.DocumentModel;
49 import org.nuxeo.ecm.core.api.DocumentModelList;
50 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
51 import org.nuxeo.ecm.core.api.DocumentRef;
52 import org.nuxeo.ecm.core.api.IdRef;
53 import org.nuxeo.ecm.core.api.PathRef;
54 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
55 import org.nuxeo.ecm.core.client.NuxeoClient;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
61 * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
62 * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
65 * $LastChangedRevision: $ $LastChangedDate: $
67 public class RepositoryJavaClientImpl implements RepositoryClient {
70 private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
71 // private final Logger profilerLogger = LoggerFactory.getLogger("remperf");
72 // private String foo = Profiler.createLogger();
74 // Regular expressions pattern for identifying valid ORDER BY clauses.
75 // FIXME: Currently supports only USASCII word characters in field names.
76 final String ORDER_BY_CLAUSE_REGEX = "\\w+(_\\w+)?:\\w+( ASC| DESC)?(, \\w+(_\\w+)?:\\w+( ASC| DESC)?)*";
79 * Instantiates a new repository java client impl.
81 public RepositoryJavaClientImpl() {
87 * Sets the collection space core values.
90 * @param documentModel the document model
91 * @throws ClientException the client exception
93 private void setCollectionSpaceCoreValues(ServiceContext<PoxPayloadIn, PoxPayloadOut> ctx,
94 DocumentModel documentModel,
95 Action action) throws ClientException {
97 // Add the CSID to the DublinCore title so we can see the CSID in the default
101 documentModel.setProperty("dublincore",
103 documentModel.getName());
104 } catch (Exception x) {
105 if (logger.isWarnEnabled() == true) {
106 logger.warn("Could not set the Dublin Core 'title' field on document CSID:" +
107 documentModel.getName());
111 // Add the tenant ID value to the new entity
113 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
114 DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID,
117 String now = GregorianCalendarDateTimeUtils.timestampUTC();
121 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
122 DocumentModelHandler.COLLECTIONSPACE_CORE_CREATED_AT,
124 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
125 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
129 documentModel.setProperty(DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA,
130 DocumentModelHandler.COLLECTIONSPACE_CORE_UPDATED_AT,
139 * create document in the Nuxeo repository
141 * @param ctx service context under which this method is invoked
143 * should be used by the caller to provide and transform the
145 * @return id in repository of the newly created document
146 * @throws DocumentException
149 public String create(ServiceContext ctx,
150 DocumentHandler handler) throws BadRequestException,
153 if (ctx.getDocumentType() == null) {
154 throw new IllegalArgumentException(
155 "RepositoryJavaClient.create: docType is missing");
157 if (handler == null) {
158 throw new IllegalArgumentException(
159 "RepositoryJavaClient.create: handler is missing");
161 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
162 if (nuxeoWspaceId == null) {
163 throw new DocumentNotFoundException(
164 "Unable to find workspace for service " + ctx.getServiceName()
165 + " check if the workspace exists in the Nuxeo repository");
167 RepositoryInstance repoSession = null;
169 handler.prepare(Action.CREATE);
170 repoSession = getRepositorySession();
171 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
172 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
173 String wspacePath = wspaceDoc.getPathAsString();
174 //give our own ID so PathRef could be constructed later on
175 String id = IdUtils.generateId(UUID.randomUUID().toString());
176 // create document model
177 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
178 ctx.getDocumentType());
179 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
180 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
181 handler.handle(Action.CREATE, wrapDoc);
182 // create document with documentmodel
183 setCollectionSpaceCoreValues(ctx, doc, Action.CREATE);
184 doc = repoSession.createDocument(doc);
186 // TODO for sub-docs need to call into the handler to let it deal with subitems. Pass in the id,
187 // and assume the handler has the state it needs (doc fragments).
188 handler.complete(Action.CREATE, wrapDoc);
190 } catch (BadRequestException bre) {
192 } catch (Exception e) {
193 if (logger.isDebugEnabled()) {
194 logger.debug("Caught exception ", e);
196 throw new DocumentException(e);
198 if (repoSession != null) {
199 releaseRepositorySession(repoSession);
206 * get document from the Nuxeo repository
207 * @param ctx service context under which this method is invoked
209 * of the document to retrieve
211 * should be used by the caller to provide and transform the
213 * @throws DocumentException
216 public void get(ServiceContext ctx, String id, DocumentHandler handler)
217 throws DocumentNotFoundException, DocumentException {
219 if (handler == null) {
220 throw new IllegalArgumentException(
221 "RepositoryJavaClient.get: handler is missing");
223 RepositoryInstance repoSession = null;
226 handler.prepare(Action.GET);
227 repoSession = getRepositorySession();
228 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
229 DocumentModel doc = null;
231 doc = repoSession.getDocument(docRef);
232 } catch (ClientException ce) {
233 String msg = "could not find document with id=" + id;
234 logger.error(msg, ce);
235 throw new DocumentNotFoundException(msg, ce);
237 //set reposession to handle the document
238 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
239 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
240 handler.handle(Action.GET, wrapDoc);
241 handler.complete(Action.GET, wrapDoc);
242 } catch (IllegalArgumentException iae) {
244 } catch (DocumentException de) {
246 } catch (Exception e) {
247 if (logger.isDebugEnabled()) {
248 logger.debug("Caught exception ", e);
250 throw new DocumentException(e);
252 if (repoSession != null) {
253 releaseRepositorySession(repoSession);
259 * get document from the Nuxeo repository, using the docFilter params.
260 * @param ctx service context under which this method is invoked
262 * should be used by the caller to provide and transform the
263 * document. Handler must have a docFilter set to return a single item.
264 * @throws DocumentException
267 public void get(ServiceContext ctx, DocumentHandler handler)
268 throws DocumentNotFoundException, DocumentException {
269 QueryContext queryContext = new QueryContext(ctx, handler);
270 RepositoryInstance repoSession = null;
273 handler.prepare(Action.GET);
274 repoSession = getRepositorySession();
276 DocumentModelList docList = null;
277 // force limit to 1, and ignore totalSize
278 String query = buildNXQLQuery(queryContext);
279 docList = repoSession.query(query, null, 1, 0, false);
280 if (docList.size() != 1) {
281 throw new DocumentNotFoundException("No document found matching filter params.");
283 DocumentModel doc = docList.get(0);
285 if (logger.isDebugEnabled()) {
286 logger.debug("Executed NXQL query: " + query);
289 //set reposession to handle the document
290 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
291 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
292 handler.handle(Action.GET, wrapDoc);
293 handler.complete(Action.GET, wrapDoc);
294 } catch (IllegalArgumentException iae) {
296 } catch (DocumentException de) {
298 } catch (Exception e) {
299 if (logger.isDebugEnabled()) {
300 logger.debug("Caught exception ", e);
302 throw new DocumentException(e);
304 if (repoSession != null) {
305 releaseRepositorySession(repoSession);
311 * get wrapped documentModel from the Nuxeo repository
312 * @param ctx service context under which this method is invoked
314 * of the document to retrieve
315 * @throws DocumentException
318 public DocumentWrapper<DocumentModel> getDoc(
319 ServiceContext ctx, String id)
320 throws DocumentNotFoundException, DocumentException {
321 RepositoryInstance repoSession = null;
322 DocumentWrapper<DocumentModel> wrapDoc = null;
325 repoSession = getRepositorySession();
326 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
327 DocumentModel doc = null;
329 doc = repoSession.getDocument(docRef);
330 } catch (ClientException ce) {
331 String msg = "could not find document with id=" + id;
332 logger.error(msg, ce);
333 throw new DocumentNotFoundException(msg, ce);
335 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
336 } catch (IllegalArgumentException iae) {
338 } catch (DocumentException de) {
340 } catch (Exception e) {
341 if (logger.isDebugEnabled()) {
342 logger.debug("Caught exception ", e);
344 throw new DocumentException(e);
346 if (repoSession != null) {
347 releaseRepositorySession(repoSession);
354 * find wrapped documentModel from the Nuxeo repository
355 * @param ctx service context under which this method is invoked
356 * @param whereClause where NXQL where clause to get the document
357 * @throws DocumentException
360 public DocumentWrapper<DocumentModel> findDoc(
361 ServiceContext ctx, String whereClause)
362 throws DocumentNotFoundException, DocumentException {
363 RepositoryInstance repoSession = null;
364 DocumentWrapper<DocumentModel> wrapDoc = null;
367 QueryContext queryContext = new QueryContext(ctx, whereClause);
368 repoSession = getRepositorySession();
369 DocumentModelList docList = null;
370 // force limit to 1, and ignore totalSize
371 String query = buildNXQLQuery(queryContext);
372 docList = repoSession.query(query,
377 if (docList.size() != 1) {
378 if (logger.isDebugEnabled()) {
379 logger.debug("findDoc: Query found: " + docList.size() + " items.");
380 logger.debug(" Query: " + query);
382 throw new DocumentNotFoundException("No document found matching filter params.");
384 DocumentModel doc = docList.get(0);
385 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
386 } catch (IllegalArgumentException iae) {
388 } catch (DocumentException de) {
390 } catch (Exception e) {
391 if (logger.isDebugEnabled()) {
392 logger.debug("Caught exception ", e);
394 throw new DocumentException(e);
396 if (repoSession != null) {
397 releaseRepositorySession(repoSession);
404 * find doc and return CSID from the Nuxeo repository
405 * @param ctx service context under which this method is invoked
406 * @param whereClause where NXQL where clause to get the document
407 * @throws DocumentException
410 public String findDocCSID(
411 ServiceContext ctx, String whereClause)
412 throws DocumentNotFoundException, DocumentException {
415 DocumentWrapper<DocumentModel> wrapDoc = findDoc(ctx, whereClause);
416 DocumentModel docModel = wrapDoc.getWrappedObject();
417 csid = NuxeoUtils.getCsid(docModel);//NuxeoUtils.extractId(docModel.getPathAsString());
418 } catch (DocumentNotFoundException dnfe) {
420 } catch (IllegalArgumentException iae) {
422 } catch (DocumentException de) {
424 } catch (Exception e) {
425 if (logger.isDebugEnabled()) {
426 logger.debug("Caught exception ", e);
428 throw new DocumentException(e);
434 * Find a list of documentModels from the Nuxeo repository
435 * @param docTypes a list of DocType names to match
436 * @param whereClause where the clause to qualify on
440 public DocumentWrapper<DocumentModelList> findDocs(
442 List<String> docTypes,
444 int pageSize, int pageNum, boolean computeTotal)
445 throws DocumentNotFoundException, DocumentException {
446 RepositoryInstance repoSession = null;
447 DocumentWrapper<DocumentModelList> wrapDoc = null;
450 if (docTypes == null || docTypes.size() < 1) {
451 throw new DocumentNotFoundException(
452 "findDocs must specify at least one DocumentType.");
454 repoSession = getRepositorySession();
455 DocumentModelList docList = null;
456 // force limit to 1, and ignore totalSize
457 QueryContext queryContext = new QueryContext(ctx, whereClause);
458 String query = buildNXQLQuery(docTypes, queryContext);
459 docList = repoSession.query(query, null, pageSize, pageNum, computeTotal);
460 wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
461 } catch (IllegalArgumentException iae) {
463 } catch (Exception e) {
464 if (logger.isDebugEnabled()) {
465 logger.debug("Caught exception ", e);
467 throw new DocumentException(e);
469 if (repoSession != null) {
470 releaseRepositorySession(repoSession);
477 * @see org.collectionspace.services.common.storage.StorageClient#get(org.collectionspace.services.common.context.ServiceContext, java.util.List, org.collectionspace.services.common.document.DocumentHandler)
480 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
481 throws DocumentNotFoundException, DocumentException {
482 if (handler == null) {
483 throw new IllegalArgumentException(
484 "RepositoryJavaClient.getAll: handler is missing");
487 RepositoryInstance repoSession = null;
490 handler.prepare(Action.GET_ALL);
491 repoSession = getRepositorySession();
492 DocumentModelList docModelList = new DocumentModelListImpl();
493 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
494 for (String csid : csidList) {
495 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
496 DocumentModel docModel = repoSession.getDocument(docRef);
497 docModelList.add(docModel);
500 //set reposession to handle the document
501 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
502 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
503 handler.handle(Action.GET_ALL, wrapDoc);
504 handler.complete(Action.GET_ALL, wrapDoc);
505 } catch (DocumentException de) {
507 } catch (Exception e) {
508 if (logger.isDebugEnabled()) {
509 logger.debug("Caught exception ", e);
511 throw new DocumentException(e);
513 if (repoSession != null) {
514 releaseRepositorySession(repoSession);
520 * getAll get all documents for an entity entity service from the Nuxeo
523 * @param ctx service context under which this method is invoked
525 * should be used by the caller to provide and transform the
527 * @throws DocumentException
530 public void getAll(ServiceContext ctx, DocumentHandler handler)
531 throws DocumentNotFoundException, DocumentException {
532 if (handler == null) {
533 throw new IllegalArgumentException(
534 "RepositoryJavaClient.getAll: handler is missing");
536 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
537 if (nuxeoWspaceId == null) {
538 throw new DocumentNotFoundException(
539 "Unable to find workspace for service "
540 + ctx.getServiceName()
541 + " check if the workspace exists in the Nuxeo repository");
543 RepositoryInstance repoSession = null;
546 handler.prepare(Action.GET_ALL);
547 repoSession = getRepositorySession();
548 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
549 DocumentModelList docList = repoSession.getChildren(wsDocRef);
550 //set reposession to handle the document
551 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
552 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
553 handler.handle(Action.GET_ALL, wrapDoc);
554 handler.complete(Action.GET_ALL, wrapDoc);
555 } catch (DocumentException de) {
557 } catch (Exception e) {
558 if (logger.isDebugEnabled()) {
559 logger.debug("Caught exception ", e);
561 throw new DocumentException(e);
563 if (repoSession != null) {
564 releaseRepositorySession(repoSession);
569 private boolean isClauseEmpty(String theString) {
570 boolean result = true;
571 if (theString != null && !theString.isEmpty()) {
578 * getFiltered get all documents for an entity service from the Document repository,
579 * given filter parameters specified by the handler.
580 * @param ctx service context under which this method is invoked
581 * @param handler should be used by the caller to provide and transform the document
582 * @throws DocumentNotFoundException if workspace not found
583 * @throws DocumentException
586 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
587 throws DocumentNotFoundException, DocumentException {
589 DocumentFilter filter = handler.getDocumentFilter();
590 String oldOrderBy = filter.getOrderByClause();
591 if (isClauseEmpty(oldOrderBy) == true){
592 filter.setOrderByClause(DocumentFilter.ORDER_BY_LAST_UPDATED); //per http://issues.collectionspace.org/browse/CSPACE-705
594 QueryContext queryContext = new QueryContext(ctx, handler);
595 RepositoryInstance repoSession = null;
597 handler.prepare(Action.GET_ALL);
598 repoSession = getRepositorySession();
599 DocumentModelList docList = null;
600 String query = buildNXQLQuery(queryContext);
602 if (logger.isDebugEnabled()) {
603 logger.debug("Executing NXQL query: " + query.toString());
606 // If we have limit and/or offset, then pass true to get totalSize
607 // in returned DocumentModelList.
608 Profiler profiler = new Profiler(this, 2);
609 profiler.log("Executing NXQL query: " + query.toString());
611 if ((queryContext.getDocFilter().getOffset() > 0) || (queryContext.getDocFilter().getPageSize() > 0)) {
612 docList = repoSession.query(query, null,
613 queryContext.getDocFilter().getPageSize(), queryContext.getDocFilter().getOffset(), true);
615 docList = repoSession.query(query);
619 //set repoSession to handle the document
620 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
621 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
622 handler.handle(Action.GET_ALL, wrapDoc);
623 handler.complete(Action.GET_ALL, wrapDoc);
624 } catch (DocumentException de) {
626 } catch (Exception e) {
627 if (logger.isDebugEnabled()) {
628 logger.debug("Caught exception ", e);
630 throw new DocumentException(e);
632 if (repoSession != null) {
633 releaseRepositorySession(repoSession);
639 * update given document in the Nuxeo repository
641 * @param ctx service context under which this method is invoked
645 * should be used by the caller to provide and transform the
647 * @throws DocumentException
650 public void update(ServiceContext ctx, String id, DocumentHandler handler)
651 throws BadRequestException, DocumentNotFoundException,
653 if (handler == null) {
654 throw new IllegalArgumentException(
655 "RepositoryJavaClient.update: handler is missing");
657 RepositoryInstance repoSession = null;
659 handler.prepare(Action.UPDATE);
660 repoSession = getRepositorySession();
661 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
662 DocumentModel doc = null;
664 doc = repoSession.getDocument(docRef);
665 } catch (ClientException ce) {
666 String msg = "Could not find document to update with id=" + id;
667 logger.error(msg, ce);
668 throw new DocumentNotFoundException(msg, ce);
670 //set reposession to handle the document
671 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
672 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
673 handler.handle(Action.UPDATE, wrapDoc);
674 setCollectionSpaceCoreValues(ctx, doc, Action.UPDATE);
675 repoSession.saveDocument(doc);
677 handler.complete(Action.UPDATE, wrapDoc);
678 } catch (BadRequestException bre) {
680 } catch (DocumentException de) {
682 } catch (Exception e) {
683 if (logger.isDebugEnabled()) {
684 logger.debug("Caught exception ", e);
686 throw new DocumentException(e);
688 if (repoSession != null) {
689 releaseRepositorySession(repoSession);
695 * delete a document from the Nuxeo repository
696 * @param ctx service context under which this method is invoked
699 * @throws DocumentException
702 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
705 if (logger.isDebugEnabled()) {
706 logger.debug("deleting document with id=" + id);
708 RepositoryInstance repoSession = null;
710 repoSession = getRepositorySession();
711 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
713 repoSession.removeDocument(docRef);
714 } catch (ClientException ce) {
715 String msg = "could not find document to delete with id=" + id;
716 logger.error(msg, ce);
717 throw new DocumentNotFoundException(msg, ce);
720 } catch (DocumentException de) {
722 } catch (Exception e) {
723 if (logger.isDebugEnabled()) {
724 logger.debug("Caught exception ", e);
726 throw new DocumentException(e);
728 if (repoSession != null) {
729 releaseRepositorySession(repoSession);
735 * @see org.collectionspace.services.common.storage.StorageClient#delete(org.collectionspace.services.common.context.ServiceContext, java.lang.String, org.collectionspace.services.common.document.DocumentHandler)
738 public void delete(ServiceContext ctx, String id, DocumentHandler handler)
739 throws DocumentNotFoundException, DocumentException {
740 throw new UnsupportedOperationException();
744 public Hashtable<String, String> retrieveWorkspaceIds(String domainName) throws Exception {
745 return NuxeoConnector.getInstance().retrieveWorkspaceIds(domainName);
749 public String createDomain(String domainName) throws Exception {
750 RepositoryInstance repoSession = null;
751 String domainId = null;
753 repoSession = getRepositorySession();
754 DocumentRef parentDocRef = new PathRef("/");
755 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
756 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
757 domainName, "Domain");
758 doc.setPropertyValue("dc:title", domainName);
759 doc.setPropertyValue("dc:description", "A CollectionSpace domain "
761 doc = repoSession.createDocument(doc);
762 domainId = doc.getId();
764 if (logger.isDebugEnabled()) {
765 logger.debug("created tenant domain name=" + domainName
766 + " id=" + domainId);
768 } catch (Exception e) {
769 if (logger.isDebugEnabled()) {
770 logger.debug("createTenantSpace caught exception ", e);
774 if (repoSession != null) {
775 releaseRepositorySession(repoSession);
782 public String getDomainId(String domainName) throws Exception {
783 String domainId = null;
784 RepositoryInstance repoSession = null;
786 repoSession = getRepositorySession();
787 DocumentRef docRef = new PathRef(
789 DocumentModel domain = repoSession.getDocument(docRef);
790 domainId = domain.getId();
791 } catch (Exception e) {
792 if (logger.isDebugEnabled()) {
793 logger.debug("Caught exception ", e);
795 //there is no way to identify if document does not exist due to
796 //lack of typed exception for getDocument method
799 if (repoSession != null) {
800 releaseRepositorySession(repoSession);
807 * @see org.collectionspace.services.common.repository.RepositoryClient#createWorkspace(java.lang.String, java.lang.String)
810 public String createWorkspace(String domainName, String workspaceName) throws Exception {
811 RepositoryInstance repoSession = null;
812 String workspaceId = null;
814 repoSession = getRepositorySession();
815 DocumentRef parentDocRef = new PathRef(
817 + "/" + "workspaces");
818 DocumentModel parentDoc = repoSession.getDocument(parentDocRef);
819 DocumentModel doc = repoSession.createDocumentModel(parentDoc.getPathAsString(),
820 workspaceName, "Workspace");
821 doc.setPropertyValue("dc:title", workspaceName);
822 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
824 doc = repoSession.createDocument(doc);
825 workspaceId = doc.getId();
827 if (logger.isDebugEnabled()) {
828 logger.debug("created workspace name=" + workspaceName
829 + " id=" + workspaceId);
831 } catch (Exception e) {
832 if (logger.isDebugEnabled()) {
833 logger.debug("createWorkspace caught exception ", e);
837 if (repoSession != null) {
838 releaseRepositorySession(repoSession);
845 * @see org.collectionspace.services.common.repository.RepositoryClient#getWorkspaceId(java.lang.String, java.lang.String)
848 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
849 String workspaceId = null;
850 RepositoryInstance repoSession = null;
852 repoSession = getRepositorySession();
853 DocumentRef docRef = new PathRef(
856 + "/" + workspaceName);
857 DocumentModel workspace = repoSession.getDocument(docRef);
858 workspaceId = workspace.getId();
859 } catch (DocumentException de) {
861 } catch (Exception e) {
862 if (logger.isDebugEnabled()) {
863 logger.debug("Caught exception ", e);
865 throw new DocumentException(e);
867 if (repoSession != null) {
868 releaseRepositorySession(repoSession);
875 * Append a WHERE clause to the NXQL query.
877 * @param query The NXQL query to which the WHERE clause will be appended.
878 * @param queryContext The query context, which provides the WHERE clause to append.
880 private final void appendNXQLWhere(StringBuilder query, QueryContext queryContext) {
882 // Restrict search to a specific Nuxeo domain
883 // TODO This is a slow method for tenant-filter
884 // We should make this a property that is indexed.
886 // query.append(" WHERE ecm:path STARTSWITH '/" + queryContext.domain + "'");
889 // Restrict search to the current tenant ID. Is the domain path filter (above) still needed?
891 query.append(/*IQueryManager.SEARCH_QUALIFIER_AND +*/ " WHERE " + DocumentModelHandler.COLLECTIONSPACE_CORE_SCHEMA + ":"
892 + DocumentModelHandler.COLLECTIONSPACE_CORE_TENANTID
893 + " = " + queryContext.getTenantId());
895 // Finally, append the incoming where clause
897 String whereClause = queryContext.getWhereClause();
898 if (whereClause != null && ! whereClause.trim().isEmpty()) {
899 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
900 // into SQL, we need to parenthesize our 'where' clause
901 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "(" + whereClause + ")");
904 // Please lookup this use in Nuxeo support and document here
906 query.append(IQueryManager.SEARCH_QUALIFIER_AND + "ecm:isProxy = 0");
910 * Append an ORDER BY clause to the NXQL query.
912 * @param query the NXQL query to which the ORDER BY clause will be appended.
913 * @param queryContext the query context, which provides the ORDER BY clause to append.
915 * @throws DocumentException if the supplied value of the orderBy clause is not valid.
918 private final void appendNXQLOrderBy(StringBuilder query, QueryContext queryContext)
920 String orderByClause = queryContext.getOrderByClause();
921 if (orderByClause != null && ! orderByClause.trim().isEmpty()) {
922 if (isValidOrderByClause(orderByClause)) {
923 query.append(" ORDER BY ");
924 query.append(orderByClause);
926 throw new DocumentException("Invalid format in sort request '" + orderByClause
927 + "': must be schema_name:fieldName followed by optional sort order (' ASC' or ' DESC').");
933 * Identifies whether the ORDER BY clause is valid.
935 * @param orderByClause the ORDER BY clause.
937 * @return true if the ORDER BY clause is valid;
938 * false if it is not.
940 private final boolean isValidOrderByClause(String orderByClause) {
941 boolean isValidClause = false;
943 Pattern orderByPattern = Pattern.compile(ORDER_BY_CLAUSE_REGEX);
944 Matcher orderByMatcher = orderByPattern.matcher(orderByClause);
945 if (orderByMatcher.matches()) {
946 isValidClause = true;
948 } catch (PatternSyntaxException pe) {
949 logger.warn("ORDER BY clause regex pattern '" + ORDER_BY_CLAUSE_REGEX
950 + "' could not be compiled: " + pe.getMessage());
951 // If reached, method will return a value of false.
953 return isValidClause;
958 * Builds an NXQL SELECT query for a single document type.
960 * @param queryContext The query context
961 * @return an NXQL query
962 * @throws Exception if supplied values in the query are invalid.
964 private final String buildNXQLQuery(QueryContext queryContext) throws Exception {
965 StringBuilder query = new StringBuilder("SELECT * FROM ");
966 query.append(queryContext.getDocType());
967 appendNXQLWhere(query, queryContext);
968 appendNXQLOrderBy(query, queryContext);
969 return query.toString();
973 * Builds an NXQL SELECT query across multiple document types.
975 * @param docTypes a list of document types to be queried
976 * @param queryContext the query context
977 * @return an NXQL query
979 private final String buildNXQLQuery(List<String> docTypes, QueryContext queryContext) {
980 StringBuilder query = new StringBuilder("SELECT * FROM ");
981 boolean fFirst = true;
982 for (String docType : docTypes) {
988 query.append(docType);
990 appendNXQLWhere(query, queryContext);
991 // FIXME add 'order by' clause here, if appropriate
992 return query.toString();
996 * Gets the repository session.
998 * @return the repository session
999 * @throws Exception the exception
1001 private RepositoryInstance getRepositorySession() throws Exception {
1002 // FIXME: is it possible to reuse repository session?
1003 // Authentication failures happen while trying to reuse the session
1004 Profiler profiler = new Profiler("getRepositorySession():", 2);
1006 NuxeoClient client = NuxeoConnector.getInstance().getClient();
1007 RepositoryInstance repoSession = client.openRepository();
1008 if (logger.isTraceEnabled()) {
1009 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
1016 * Release repository session.
1018 * @param repoSession the repo session
1020 private void releaseRepositorySession(RepositoryInstance repoSession) {
1022 NuxeoClient client = NuxeoConnector.getInstance().getClient();
1024 client.releaseRepository(repoSession);
1025 } catch (Exception e) {
1026 logger.error("Could not close the repository session", e);
1027 // no need to throw this service specific exception