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.UUID;
21 import java.util.List;
23 import org.collectionspace.services.common.context.ServiceContext;
24 import org.collectionspace.services.common.document.BadRequestException;
25 import org.collectionspace.services.common.document.DocumentException;
26 import org.collectionspace.services.common.document.DocumentFilter;
27 import org.collectionspace.services.common.document.DocumentHandler;
28 import org.collectionspace.services.common.document.DocumentNotFoundException;
29 import org.collectionspace.services.common.repository.RepositoryClient;
30 import org.collectionspace.services.common.document.DocumentHandler.Action;
31 import org.collectionspace.services.common.document.DocumentWrapper;
32 import org.collectionspace.services.common.document.DocumentWrapperImpl;
33 import org.collectionspace.services.nuxeo.util.NuxeoUtils;
34 import org.nuxeo.common.utils.IdUtils;
35 import org.nuxeo.ecm.core.api.ClientException;
36 import org.nuxeo.ecm.core.api.DocumentModel;
37 import org.nuxeo.ecm.core.api.DocumentModelList;
38 import org.nuxeo.ecm.core.api.impl.DocumentModelListImpl;
39 import org.nuxeo.ecm.core.api.DocumentRef;
40 import org.nuxeo.ecm.core.api.IdRef;
41 import org.nuxeo.ecm.core.api.PathRef;
42 import org.nuxeo.ecm.core.api.repository.RepositoryInstance;
43 import org.nuxeo.ecm.core.client.NuxeoClient;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
48 * RepositoryJavaClient is used to perform CRUD operations on documents in Nuxeo
49 * repository using Remote Java APIs. It uses @see DocumentHandler as IOHandler
52 * $LastChangedRevision: $ $LastChangedDate: $
54 public class RepositoryJavaClientImpl implements RepositoryClient {
56 private final Logger logger = LoggerFactory.getLogger(RepositoryJavaClientImpl.class);
58 public RepositoryJavaClientImpl() {
62 * create document in the Nuxeo repository
64 * @param ctx service context under which this method is invoked
66 * of the document created
68 * should be used by the caller to provide and transform the
70 * @return id in repository of the newly created document
71 * @throws DocumentException
74 public String create(ServiceContext ctx,
75 DocumentHandler handler) throws BadRequestException,
78 if (ctx.getDocumentType() == null) {
79 throw new IllegalArgumentException(
80 "RepositoryJavaClient.create: docType is missing");
82 if (handler == null) {
83 throw new IllegalArgumentException(
84 "RepositoryJavaClient.create: handler is missing");
86 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
87 if (nuxeoWspaceId == null) {
88 throw new DocumentNotFoundException(
89 "Unable to find workspace for service " + ctx.getServiceName()
90 + " check if the workspace exists in the Nuxeo repository");
92 RepositoryInstance repoSession = null;
94 handler.prepare(Action.CREATE);
95 repoSession = getRepositorySession();
96 DocumentRef nuxeoWspace = new IdRef(nuxeoWspaceId);
97 DocumentModel wspaceDoc = repoSession.getDocument(nuxeoWspace);
98 String wspacePath = wspaceDoc.getPathAsString();
99 //give our own ID so PathRef could be constructed later on
100 String id = IdUtils.generateId(UUID.randomUUID().toString());
101 // create document model
102 DocumentModel doc = repoSession.createDocumentModel(wspacePath, id,
103 ctx.getDocumentType());
104 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
105 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
106 handler.handle(Action.CREATE, wrapDoc);
107 // create document with documentmodel
108 doc = repoSession.createDocument(doc);
110 handler.complete(Action.CREATE, wrapDoc);
112 } catch (BadRequestException bre) {
114 } catch (Exception e) {
115 if (logger.isDebugEnabled()) {
116 logger.debug("Caught exception ", e);
118 throw new DocumentException(e);
120 if (repoSession != null) {
121 releaseRepositorySession(repoSession);
128 * get document from the Nuxeo repository
129 * @param ctx service context under which this method is invoked
131 * of the document to retrieve
133 * should be used by the caller to provide and transform the
135 * @throws DocumentException
138 public void get(ServiceContext ctx, String id, DocumentHandler handler)
139 throws DocumentNotFoundException, DocumentException {
141 if (handler == null) {
142 throw new IllegalArgumentException(
143 "RepositoryJavaClient.get: handler is missing");
145 RepositoryInstance repoSession = null;
148 handler.prepare(Action.GET);
149 repoSession = getRepositorySession();
150 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
151 DocumentModel doc = null;
153 doc = repoSession.getDocument(docRef);
154 } catch (ClientException ce) {
155 String msg = "could not find document with id=" + id;
156 logger.error(msg, ce);
157 throw new DocumentNotFoundException(msg, ce);
159 //set reposession to handle the document
160 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
161 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
162 handler.handle(Action.GET, wrapDoc);
163 handler.complete(Action.GET, wrapDoc);
164 } catch (IllegalArgumentException iae) {
166 } catch (DocumentException de) {
168 } catch (Exception e) {
169 if (logger.isDebugEnabled()) {
170 logger.debug("Caught exception ", e);
172 throw new DocumentException(e);
174 if (repoSession != null) {
175 releaseRepositorySession(repoSession);
181 * get wrapped documentModel from the Nuxeo repository
182 * @param ctx service context under which this method is invoked
184 * of the document to retrieve
185 * @throws DocumentException
188 public DocumentWrapper<DocumentModel> getDoc(
189 ServiceContext ctx, String id)
190 throws DocumentNotFoundException, DocumentException {
191 RepositoryInstance repoSession = null;
192 DocumentWrapper<DocumentModel> wrapDoc = null;
195 repoSession = getRepositorySession();
196 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
197 DocumentModel doc = null;
199 doc = repoSession.getDocument(docRef);
200 } catch (ClientException ce) {
201 String msg = "could not find document with id=" + id;
202 logger.error(msg, ce);
203 throw new DocumentNotFoundException(msg, ce);
205 wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
206 } catch (IllegalArgumentException iae) {
208 } catch (DocumentException de) {
210 } catch (Exception e) {
211 if (logger.isDebugEnabled()) {
212 logger.debug("Caught exception ", e);
214 throw new DocumentException(e);
216 if (repoSession != null) {
217 releaseRepositorySession(repoSession);
224 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
225 throws DocumentNotFoundException, DocumentException {
226 if (handler == null) {
227 throw new IllegalArgumentException(
228 "RepositoryJavaClient.getAll: handler is missing");
231 RepositoryInstance repoSession = null;
234 handler.prepare(Action.GET_ALL);
235 repoSession = getRepositorySession();
236 DocumentModelList docModelList = new DocumentModelListImpl();
237 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
238 for (String csid : csidList) {
239 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
240 DocumentModel docModel = repoSession.getDocument(docRef);
241 docModelList.add(docModel);
244 //set reposession to handle the document
245 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
246 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
247 handler.handle(Action.GET_ALL, wrapDoc);
248 handler.complete(Action.GET_ALL, wrapDoc);
249 } catch (DocumentException de) {
251 } catch (Exception e) {
252 if (logger.isDebugEnabled()) {
253 logger.debug("Caught exception ", e);
255 throw new DocumentException(e);
257 if (repoSession != null) {
258 releaseRepositorySession(repoSession);
264 * getAll get all documents for an entity entity service from the Nuxeo
267 * @param ctx service context under which this method is invoked
269 * should be used by the caller to provide and transform the
271 * @throws DocumentException
274 public void getAll(ServiceContext ctx, DocumentHandler handler)
275 throws DocumentNotFoundException, DocumentException {
276 if (handler == null) {
277 throw new IllegalArgumentException(
278 "RepositoryJavaClient.getAll: handler is missing");
280 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
281 if (nuxeoWspaceId == null) {
282 throw new DocumentNotFoundException(
283 "Unable to find workspace for service "
284 + ctx.getServiceName()
285 + " check if the workspace exists in the Nuxeo repository");
287 RepositoryInstance repoSession = null;
290 handler.prepare(Action.GET_ALL);
291 repoSession = getRepositorySession();
292 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
293 DocumentModelList docList = repoSession.getChildren(wsDocRef);
294 //set reposession to handle the document
295 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
296 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
297 handler.handle(Action.GET_ALL, wrapDoc);
298 handler.complete(Action.GET_ALL, wrapDoc);
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 * getFiltered get all documents for an entity service from the Document repository,
315 * given filter parameters specified by the handler.
316 * @param ctx service context under which this method is invoked
317 * @param handler should be used by the caller to provide and transform the document
318 * @throws DocumentNotFoundException if workspace not found
319 * @throws DocumentException
321 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
322 throws DocumentNotFoundException, DocumentException {
323 if (handler == null) {
324 throw new IllegalArgumentException(
325 "RepositoryJavaClient.getFiltered: handler is missing");
327 DocumentFilter docFilter = handler.getDocumentFilter();
328 if (docFilter == null) {
329 throw new IllegalArgumentException(
330 "RepositoryJavaClient.getFiltered: handler has no Filter specified");
332 String docType = ctx.getDocumentType();
333 if (docType == null) {
334 throw new DocumentNotFoundException(
335 "Unable to find DocumentType for service " + ctx.getServiceName());
337 String domain = ctx.getRepositoryDomainName();
338 if (domain == null) {
339 throw new DocumentNotFoundException(
340 "Unable to find Domain for service " + ctx.getServiceName());
342 RepositoryInstance repoSession = null;
344 handler.prepare(Action.GET_ALL);
345 repoSession = getRepositorySession();
346 StringBuilder query = new StringBuilder("SELECT * FROM ");
347 query.append(docType);
348 String where = docFilter.getWhereClause();
349 // TODO This is a slow method for tenant-filter
350 // We should make this a property that is indexed.
351 query.append(" WHERE ecm:path STARTSWITH '/" + domain + "'");
352 if ((null != where) && (where.length() > 0)) {
353 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
354 // into SQL, we need to parenthesize our 'where' clause
355 query.append(" AND " + "(" + where +")" + "AND ecm:isProxy = 0");
357 DocumentModelList docList = null;
358 // If we have limit and/or offset, then pass true to get totalSize
359 // in returned DocumentModelList.
360 if ((docFilter.getOffset() > 0) || (docFilter.getPageSize() > 0)) {
361 docList = repoSession.query(query.toString(), null,
362 docFilter.getPageSize(), docFilter.getOffset(), true);
364 docList = repoSession.query(query.toString());
367 if (logger.isDebugEnabled()) {
368 logger.debug("Executed NXQL query: " + query.toString());
371 //set repoSession to handle the document
372 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
373 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
374 handler.handle(Action.GET_ALL, wrapDoc);
375 handler.complete(Action.GET_ALL, wrapDoc);
376 } catch (DocumentException de) {
378 } catch (Exception e) {
379 if (logger.isDebugEnabled()) {
380 logger.debug("Caught exception ", e);
382 throw new DocumentException(e);
384 if (repoSession != null) {
385 releaseRepositorySession(repoSession);
391 * update given document in the Nuxeo repository
393 * @param ctx service context under which this method is invoked
397 * should be used by the caller to provide and transform the
399 * @throws DocumentException
402 public void update(ServiceContext ctx, String id, DocumentHandler handler)
403 throws BadRequestException, DocumentNotFoundException,
405 if (handler == null) {
406 throw new IllegalArgumentException(
407 "RepositoryJavaClient.update: handler is missing");
409 RepositoryInstance repoSession = null;
411 handler.prepare(Action.UPDATE);
412 repoSession = getRepositorySession();
413 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
414 DocumentModel doc = null;
416 doc = repoSession.getDocument(docRef);
417 } catch (ClientException ce) {
418 String msg = "Could not find document to update with id=" + id;
419 logger.error(msg, ce);
420 throw new DocumentNotFoundException(msg, ce);
422 //set reposession to handle the document
423 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
424 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
425 handler.handle(Action.UPDATE, wrapDoc);
426 repoSession.saveDocument(doc);
428 handler.complete(Action.UPDATE, wrapDoc);
429 } catch (BadRequestException bre) {
431 } catch (DocumentException de) {
433 } catch (Exception e) {
434 if (logger.isDebugEnabled()) {
435 logger.debug("Caught exception ", e);
437 throw new DocumentException(e);
439 if (repoSession != null) {
440 releaseRepositorySession(repoSession);
446 * delete a document from the Nuxeo repository
447 * @param ctx service context under which this method is invoked
450 * @throws DocumentException
453 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
456 if (logger.isDebugEnabled()) {
457 logger.debug("deleting document with id=" + id);
459 RepositoryInstance repoSession = null;
461 repoSession = getRepositorySession();
462 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
464 repoSession.removeDocument(docRef);
465 } catch (ClientException ce) {
466 String msg = "could not find document to delete with id=" + id;
467 logger.error(msg, ce);
468 throw new DocumentNotFoundException(msg, ce);
471 } catch (DocumentException de) {
473 } catch (Exception e) {
474 if (logger.isDebugEnabled()) {
475 logger.debug("Caught exception ", e);
477 throw new DocumentException(e);
479 if (repoSession != null) {
480 releaseRepositorySession(repoSession);
486 public String createWorkspace(String tenantDomain, String workspaceName) throws Exception {
487 RepositoryInstance repoSession = null;
488 String workspaceId = null;
490 repoSession = getRepositorySession();
491 DocumentRef docRef = new PathRef(
493 + "/" + "workspaces");
494 DocumentModel parent = repoSession.getDocument(docRef);
495 DocumentModel doc = repoSession.createDocumentModel(parent.getPathAsString(),
496 workspaceName, "Workspace");
497 doc.setPropertyValue("dc:title", workspaceName);
498 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
500 doc = repoSession.createDocument(doc);
501 workspaceId = doc.getId();
503 if (logger.isDebugEnabled()) {
504 logger.debug("created workspace name=" + workspaceName
505 + " id=" + workspaceId);
507 } catch (Exception e) {
508 if (logger.isDebugEnabled()) {
509 logger.debug("createWorkspace caught exception ", e);
513 if (repoSession != null) {
514 releaseRepositorySession(repoSession);
521 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
522 String workspaceId = null;
523 RepositoryInstance repoSession = null;
525 repoSession = getRepositorySession();
526 DocumentRef docRef = new PathRef(
529 + "/" + workspaceName);
530 DocumentModel workspace = repoSession.getDocument(docRef);
531 workspaceId = workspace.getId();
532 } catch (DocumentException de) {
534 } catch (Exception e) {
535 if (logger.isDebugEnabled()) {
536 logger.debug("Caught exception ", e);
538 throw new DocumentException(e);
540 if (repoSession != null) {
541 releaseRepositorySession(repoSession);
547 private RepositoryInstance getRepositorySession() throws Exception {
548 // FIXME: is it possible to reuse repository session?
549 // Authentication failures happen while trying to reuse the session
550 NuxeoClient client = NuxeoConnector.getInstance().getClient();
551 RepositoryInstance repoSession = client.openRepository();
552 if (logger.isDebugEnabled()) {
553 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
558 private void releaseRepositorySession(RepositoryInstance repoSession) {
560 NuxeoClient client = NuxeoConnector.getInstance().getClient();
562 client.releaseRepository(repoSession);
563 } catch (Exception e) {
564 logger.error("Could not close the repository session", e);
565 // no need to throw this service specific exception