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 public void get(ServiceContext ctx, List<String> csidList, DocumentHandler handler)
182 throws DocumentNotFoundException, DocumentException {
183 if (handler == null) {
184 throw new IllegalArgumentException(
185 "RepositoryJavaClient.getAll: handler is missing");
188 RepositoryInstance repoSession = null;
191 handler.prepare(Action.GET_ALL);
192 repoSession = getRepositorySession();
193 DocumentModelList docModelList = new DocumentModelListImpl();
194 //FIXME: Should be using NuxeoUtils.createPathRef for security reasons
195 for (String csid : csidList) {
196 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, csid);
197 DocumentModel docModel = repoSession.getDocument(docRef);
198 docModelList.add(docModel);
201 //set reposession to handle the document
202 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
203 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docModelList);
204 handler.handle(Action.GET_ALL, wrapDoc);
205 handler.complete(Action.GET_ALL, wrapDoc);
206 } catch (DocumentException de) {
208 } catch (Exception e) {
209 if (logger.isDebugEnabled()) {
210 logger.debug("Caught exception ", e);
212 throw new DocumentException(e);
214 if (repoSession != null) {
215 releaseRepositorySession(repoSession);
221 * getAll get all documents for an entity entity service from the Nuxeo
224 * @param ctx service context under which this method is invoked
226 * should be used by the caller to provide and transform the
228 * @throws DocumentException
231 public void getAll(ServiceContext ctx, DocumentHandler handler)
232 throws DocumentNotFoundException, DocumentException {
233 if (handler == null) {
234 throw new IllegalArgumentException(
235 "RepositoryJavaClient.getAll: handler is missing");
237 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
238 if (nuxeoWspaceId == null) {
239 throw new DocumentNotFoundException(
240 "Unable to find workspace for service "
241 + ctx.getServiceName()
242 + " check if the workspace exists in the Nuxeo repository");
244 RepositoryInstance repoSession = null;
247 handler.prepare(Action.GET_ALL);
248 repoSession = getRepositorySession();
249 DocumentRef wsDocRef = new IdRef(nuxeoWspaceId);
250 DocumentModelList docList = repoSession.getChildren(wsDocRef);
251 //set reposession to handle the document
252 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
253 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
254 handler.handle(Action.GET_ALL, wrapDoc);
255 handler.complete(Action.GET_ALL, wrapDoc);
256 } catch (DocumentException de) {
258 } catch (Exception e) {
259 if (logger.isDebugEnabled()) {
260 logger.debug("Caught exception ", e);
262 throw new DocumentException(e);
264 if (repoSession != null) {
265 releaseRepositorySession(repoSession);
271 * getFiltered get all documents for an entity service from the Document repository,
272 * given filter parameters specified by the handler.
273 * @param ctx service context under which this method is invoked
274 * @param handler should be used by the caller to provide and transform the document
275 * @throws DocumentNotFoundException if workspace not found
276 * @throws DocumentException
278 public void getFiltered(ServiceContext ctx, DocumentHandler handler)
279 throws DocumentNotFoundException, DocumentException {
280 if (handler == null) {
281 throw new IllegalArgumentException(
282 "RepositoryJavaClient.getFiltered: handler is missing");
284 DocumentFilter docFilter = handler.getDocumentFilter();
285 if (docFilter == null) {
286 throw new IllegalArgumentException(
287 "RepositoryJavaClient.getFiltered: handler has no Filter specified");
289 String docType = ctx.getDocumentType();
290 if (docType == null) {
291 throw new DocumentNotFoundException(
292 "Unable to find DocumentType for service " + ctx.getServiceName());
294 String domain = ctx.getRepositoryDomainName();
295 if (domain == null) {
296 throw new DocumentNotFoundException(
297 "Unable to find Domain for service " + ctx.getServiceName());
299 RepositoryInstance repoSession = null;
301 handler.prepare(Action.GET_ALL);
302 repoSession = getRepositorySession();
303 StringBuilder query = new StringBuilder("SELECT * FROM ");
304 query.append(docType);
305 String where = docFilter.getWhereClause();
306 // TODO This is a slow method for tenant-filter
307 // We should make this a property that is indexed.
308 query.append(" WHERE ecm:path STARTSWITH '/" + domain + "'");
309 if ((null != where) && (where.length() > 0)) {
310 // Due to an apparent bug/issue in how Nuxeo translates the NXQL query string
311 // into SQL, we need to parenthesize our 'where' clause
312 query.append(" AND " + "(" + where +")" + "AND ecm:isProxy = 0");
314 DocumentModelList docList = null;
315 // If we have limit and/or offset, then pass true to get totalSize
316 // in returned DocumentModelList.
317 if ((docFilter.getOffset() > 0) || (docFilter.getPageSize() > 0)) {
318 docList = repoSession.query(query.toString(), null,
319 docFilter.getPageSize(), docFilter.getOffset(), true);
321 docList = repoSession.query(query.toString());
324 if (logger.isDebugEnabled()) {
325 logger.debug("Executed NXQL query: " + query.toString());
328 //set repoSession to handle the document
329 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
330 DocumentWrapper<DocumentModelList> wrapDoc = new DocumentWrapperImpl<DocumentModelList>(docList);
331 handler.handle(Action.GET_ALL, wrapDoc);
332 handler.complete(Action.GET_ALL, wrapDoc);
333 } catch (DocumentException de) {
335 } catch (Exception e) {
336 if (logger.isDebugEnabled()) {
337 logger.debug("Caught exception ", e);
339 throw new DocumentException(e);
341 if (repoSession != null) {
342 releaseRepositorySession(repoSession);
348 * update given document in the Nuxeo repository
350 * @param ctx service context under which this method is invoked
354 * should be used by the caller to provide and transform the
356 * @throws DocumentException
359 public void update(ServiceContext ctx, String id, DocumentHandler handler)
360 throws BadRequestException, DocumentNotFoundException,
362 if (handler == null) {
363 throw new IllegalArgumentException(
364 "RepositoryJavaClient.update: handler is missing");
366 RepositoryInstance repoSession = null;
368 handler.prepare(Action.UPDATE);
369 repoSession = getRepositorySession();
370 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
371 DocumentModel doc = null;
373 doc = repoSession.getDocument(docRef);
374 } catch (ClientException ce) {
375 String msg = "Could not find document to update with id=" + id;
376 logger.error(msg, ce);
377 throw new DocumentNotFoundException(msg, ce);
379 //set reposession to handle the document
380 ((DocumentModelHandler) handler).setRepositorySession(repoSession);
381 DocumentWrapper<DocumentModel> wrapDoc = new DocumentWrapperImpl<DocumentModel>(doc);
382 handler.handle(Action.UPDATE, wrapDoc);
383 repoSession.saveDocument(doc);
385 handler.complete(Action.UPDATE, wrapDoc);
386 } catch (BadRequestException bre) {
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);
403 * delete a document from the Nuxeo repository
404 * @param ctx service context under which this method is invoked
407 * @throws DocumentException
410 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException,
413 if (logger.isDebugEnabled()) {
414 logger.debug("deleting document with id=" + id);
416 RepositoryInstance repoSession = null;
418 repoSession = getRepositorySession();
419 DocumentRef docRef = NuxeoUtils.createPathRef(ctx, id);
421 repoSession.removeDocument(docRef);
422 } catch (ClientException ce) {
423 String msg = "could not find document to delete with id=" + id;
424 logger.error(msg, ce);
425 throw new DocumentNotFoundException(msg, ce);
428 } catch (DocumentException de) {
430 } catch (Exception e) {
431 if (logger.isDebugEnabled()) {
432 logger.debug("Caught exception ", e);
434 throw new DocumentException(e);
436 if (repoSession != null) {
437 releaseRepositorySession(repoSession);
443 public String createWorkspace(String tenantDomain, String workspaceName) throws Exception {
444 RepositoryInstance repoSession = null;
445 String workspaceId = null;
447 repoSession = getRepositorySession();
448 DocumentRef docRef = new PathRef(
450 + "/" + "workspaces");
451 DocumentModel parent = repoSession.getDocument(docRef);
452 DocumentModel doc = repoSession.createDocumentModel(parent.getPathAsString(),
453 workspaceName, "Workspace");
454 doc.setPropertyValue("dc:title", workspaceName);
455 doc.setPropertyValue("dc:description", "A CollectionSpace workspace for "
457 doc = repoSession.createDocument(doc);
458 workspaceId = doc.getId();
460 if (logger.isDebugEnabled()) {
461 logger.debug("created workspace name=" + workspaceName
462 + " id=" + workspaceId);
464 } catch (Exception e) {
465 if (logger.isDebugEnabled()) {
466 logger.debug("createWorkspace caught exception ", e);
470 if (repoSession != null) {
471 releaseRepositorySession(repoSession);
478 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
479 String workspaceId = null;
480 RepositoryInstance repoSession = null;
482 repoSession = getRepositorySession();
483 DocumentRef docRef = new PathRef(
486 + "/" + workspaceName);
487 DocumentModel workspace = repoSession.getDocument(docRef);
488 workspaceId = workspace.getId();
489 } catch (DocumentException de) {
491 } catch (Exception e) {
492 if (logger.isDebugEnabled()) {
493 logger.debug("Caught exception ", e);
495 throw new DocumentException(e);
497 if (repoSession != null) {
498 releaseRepositorySession(repoSession);
504 private RepositoryInstance getRepositorySession() throws Exception {
505 // FIXME: is it possible to reuse repository session?
506 // Authentication failures happen while trying to reuse the session
507 NuxeoClient client = NuxeoConnector.getInstance().getClient();
508 RepositoryInstance repoSession = client.openRepository();
509 if (logger.isDebugEnabled()) {
510 logger.debug("getRepository() repository root: " + repoSession.getRootDocument());
515 private void releaseRepositorySession(RepositoryInstance repoSession) {
517 NuxeoClient client = NuxeoConnector.getInstance().getClient();
519 client.releaseRepository(repoSession);
520 } catch (Exception e) {
521 logger.error("Could not close the repository session", e);
522 // no need to throw this service specific exception