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 * Unless required by applicable law or agreed to in writing, software
19 * distributed under the License is distributed on an "AS IS" BASIS,
20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 * See the License for the specific language governing permissions and
22 * limitations under the License.
24 package org.collectionspace.services.nuxeo.client.rest;
26 import java.io.ByteArrayInputStream;
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.io.OutputStream;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.Iterator;
33 import org.collectionspace.services.common.repository.DocumentException;
34 import org.collectionspace.services.common.repository.DocumentHandler;
35 import org.collectionspace.services.common.repository.RepositoryClient;
37 import org.restlet.data.Request;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
42 import java.util.List;
45 import org.collectionspace.services.common.ServiceConfig;
46 import org.collectionspace.services.common.RepositoryClientConfigType;
47 import org.collectionspace.services.common.ServiceMain;
48 import org.collectionspace.services.common.config.TenantBindingConfigReader;
49 import org.collectionspace.services.common.context.ServiceContext;
50 import org.collectionspace.services.common.repository.BadRequestException;
51 import org.collectionspace.services.common.repository.DocumentNotFoundException;
52 import org.collectionspace.services.common.repository.DocumentHandler.Action;
53 import org.dom4j.Document;
54 import org.dom4j.Element;
55 import org.dom4j.io.SAXReader;
56 import org.dom4j.tree.DefaultDocument;
57 import org.restlet.data.MediaType;
58 import org.restlet.data.Method;
59 import org.restlet.data.Response;
60 import org.restlet.data.Status;
61 import org.restlet.resource.OutputRepresentation;
62 import org.restlet.resource.Representation;
65 * RepositoryRESTClient is used to perform CRUD operations on documents
66 * in Nuxeo repository using Nuxeo RESTful APIs. It uses @see DocumentHandler
67 * as IOHandler with the client.
71 * $LastChangedRevision: $
74 public class RepositoryRESTClient implements RepositoryClient {
76 private final Logger logger = LoggerFactory.getLogger(RepositoryRESTClient.class);
77 private NuxeoRESTClient nuxeoRestClient;
79 public RepositoryRESTClient() {
83 public String create(ServiceContext ctx, DocumentHandler handler) throws BadRequestException, DocumentException {
85 if(handler.getDocumentType() == null){
86 throw new IllegalArgumentException("RepositoryRESTClient.create: docType is missing");
89 throw new IllegalArgumentException("RepositoryRESTClient.create: handler is missing");
91 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
92 if(nuxeoWspaceId == null){
93 throw new DocumentNotFoundException("Unable to find workspace for service " + ctx.getServiceName() +
94 " check if the mapping exists in service-config.xml or " +
95 " the the mapped workspace exists in the Nuxeo repository");
98 RepresentationHandler repHandler = (RepresentationHandler) handler;
99 repHandler.prepare(Action.CREATE);
100 List<String> pathParams = new ArrayList<String>();
101 pathParams.add("default");
102 pathParams.add(nuxeoWspaceId);
103 pathParams.add("createDocument");
104 if(repHandler.getPathParams().size() > 0){
105 pathParams.addAll(repHandler.getPathParams());
107 Map<String, String> queryParams = new HashMap<String, String>();
108 queryParams.put("docType", handler.getDocumentType());
109 // a default title for the Dublin Core schema
110 queryParams.put("dublincore:title", "CollectionSpace-" + handler.getDocumentType());
111 if(repHandler.getQueryParams().size() > 0){
112 queryParams.putAll(repHandler.getQueryParams());
115 String completeURL = getNuxeoRestClient().buildUrl(pathParams, queryParams);
116 if(logger.isDebugEnabled()){
117 logger.debug("create using url=" + completeURL);
119 Request request = buildRequest(Method.POST, completeURL);
121 //write out create stream, this is not needed as queryParams
122 //contain the document
123 final InputStream in = new ByteArrayInputStream(new byte[0]);
124 request.setEntity(new OutputRepresentation(
125 MediaType.MULTIPART_FORM_DATA) {
128 public void write(OutputStream outputStream) throws IOException {
129 byte[] buffer = new byte[1024 * 64];
131 while((read = in.read(buffer)) != -1){
132 outputStream.write(buffer, 0, read);
136 //following call to handler.handle is not needed as queryparams
137 //contains the real data
138 RepresentationWrapper wrapDoc = new RepresentationWrapper(new DefaultDocument());
139 repHandler.handle(Action.CREATE, wrapDoc);
141 //Nuxeo does not set 201 SUCCESS_CREATED on successful creation
142 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
143 //handle is not needed on create as queryparams have all data
144 repHandler.complete(Action.CREATE, wrapDoc);
145 return extractId(document);
148 if(logger.isDebugEnabled()){
149 logger.debug("Caught exception ", e);
151 throw new DocumentException(e);
157 public void get(ServiceContext ctx, String id, DocumentHandler handler) throws DocumentNotFoundException, DocumentException {
160 throw new IllegalArgumentException("RepositoryRESTClient.get: handler is missing");
164 RepresentationHandler repHandler = (RepresentationHandler) handler;
165 repHandler.prepare(Action.GET);
166 ArrayList pathParams = new ArrayList();
167 pathParams.add("default");
169 pathParams.add("export");
170 if(repHandler.getPathParams().size() > 0){
171 pathParams.addAll(repHandler.getPathParams());
173 HashMap<String, String> queryParams = new HashMap<String, String>();
174 queryParams.put("format", "XML");
175 if(repHandler.getQueryParams().size() > 0){
176 queryParams.putAll(repHandler.getQueryParams());
178 String completeURL = getNuxeoRestClient().buildUrl(pathParams, queryParams);
179 if(logger.isDebugEnabled()){
180 logger.debug("get using url=" + completeURL);
182 Request request = buildRequest(Method.GET, completeURL);
183 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
184 RepresentationWrapper wrapDoc = new RepresentationWrapper(document);
185 repHandler.handle(Action.GET, wrapDoc);
186 repHandler.complete(Action.GET, wrapDoc);
188 if(logger.isDebugEnabled()){
189 logger.debug("Caught exception ", e);
191 throw new DocumentException(e);
197 public void getAll(ServiceContext ctx, DocumentHandler handler) throws DocumentNotFoundException, DocumentException {
199 throw new IllegalArgumentException("RepositoryRESTClient.getAll: handler is missing");
201 String nuxeoWspaceId = ctx.getRepositoryWorkspaceId();
202 if(nuxeoWspaceId == null){
203 throw new DocumentNotFoundException("Unable to find workspace for service " + ctx.getServiceName() +
204 " check if the mapping exists in service-config.xml or " +
205 " the the mapped workspace exists in the Nuxeo repository");
208 RepresentationHandler repHandler = (RepresentationHandler) handler;
209 repHandler.prepare(Action.GET_ALL);
210 ArrayList pathParams = new ArrayList();
211 pathParams.add("default");
212 pathParams.add(nuxeoWspaceId);
213 pathParams.add("browse");
214 if(repHandler.getPathParams().size() > 0){
215 pathParams.addAll(repHandler.getPathParams());
217 String completeURL = getNuxeoRestClient().buildUrl(pathParams, repHandler.getQueryParams());
218 if(logger.isDebugEnabled()){
219 logger.debug("getAll using url=" + completeURL);
221 Request request = buildRequest(Method.GET, completeURL);
222 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
223 RepresentationWrapper wrapDoc = new RepresentationWrapper(document);
224 repHandler.handle(Action.GET_ALL, wrapDoc);
225 repHandler.complete(Action.GET_ALL, wrapDoc);
227 if(logger.isDebugEnabled()){
228 logger.debug("Caught exception ", e);
230 throw new DocumentException(e);
236 public void update(ServiceContext ctx, String id, DocumentHandler handler) throws BadRequestException, DocumentNotFoundException, DocumentException {
238 throw new IllegalArgumentException("RepositoryRESTClient.update: handler is missing");
242 RepresentationHandler repHandler = (RepresentationHandler) handler;
243 repHandler.prepare(Action.UPDATE);
244 List<String> pathParams = new ArrayList<String>();
245 pathParams.add("default");
247 pathParams.add("updateDocumentRestlet");
248 if(repHandler.getPathParams().size() > 0){
249 pathParams.addAll(repHandler.getPathParams());
251 String completeURL = getNuxeoRestClient().buildUrl(pathParams, repHandler.getQueryParams());
252 if(logger.isDebugEnabled()){
253 logger.debug("update using url=" + completeURL);
255 //repHandler.handle is not needed as queryParams contain all the data
256 RepresentationWrapper wrapDoc = new RepresentationWrapper(new DefaultDocument());
257 repHandler.handle(Action.UPDATE, wrapDoc);
258 Request request = buildRequest(Method.PUT, completeURL);
259 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
260 repHandler.complete(Action.UPDATE, wrapDoc);
262 if(logger.isDebugEnabled()){
263 logger.debug("Caught exception ", e);
265 throw new DocumentException(e);
271 public void delete(ServiceContext ctx, String id) throws DocumentNotFoundException, DocumentException {
273 if(logger.isDebugEnabled()){
274 logger.debug("deleting document with id=" + id);
278 List<String> pathParams = new ArrayList<String>();
279 pathParams.add("default");
281 pathParams.add("deleteDocumentRestlet");
283 Map<String, String> queryParams = new HashMap<String, String>();
284 String completeURL = getNuxeoRestClient().buildUrl(pathParams, queryParams);
285 if(logger.isDebugEnabled()){
286 logger.debug("delete using url=" + completeURL);
288 Request request = buildRequest(Method.DELETE, completeURL);
289 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
290 //FIXME error handling?
291 // Document document = service.deleteCollectionObject(csid);
292 // Element root = document.getRootElement();
293 // for(Iterator i = root.elementIterator(); i.hasNext();){
294 // Element element = (Element) i.next();
295 // if("docRef".equals(element.getName())){
296 // String status = (String) element.getData();
297 // verbose("deleteCollectionObjectt response: " + status);
302 if(logger.isDebugEnabled()){
303 logger.debug("Caught exception ", e);
305 throw new DocumentException(e);
311 public String createWorkspace(String tenantDomain, String workspaceName) throws Exception {
312 throw new UnsupportedOperationException();
316 public String getWorkspaceId(String tenantDomain, String workspaceName) throws Exception {
317 throw new UnsupportedOperationException();
321 * buildRequest build HTTP request given parameters
326 private Request buildRequest(Method method, String completeURL) {
327 Request request = new Request(method, completeURL);
328 getNuxeoRestClient().setupAuth(request);
329 getNuxeoRestClient().setupCookies(request);
334 * executeRequest execute given HTTP request
340 private Document executeRequest(Request request, String completeURL, Status expected) throws Exception {
342 Response res = getNuxeoRestClient().getRestClient().handle(request);
343 Status status = res.getStatus();
344 if(status.getCode() != expected.getCode()){
345 logger.error("failed to execute request=" + request.getMethod() +
346 " with error status=" + status +
347 " url=" + completeURL +
348 " response=" + res.toString());
349 throw new DocumentException(status.getCode(), status.getDescription());
351 Representation rep = res.getEntity();
354 return retrieveResponse(rep);
358 * retrieveResponse retrieves DOM document from Restlet Represeantion
363 private Document retrieveResponse(Representation rep) throws Exception {
364 SAXReader reader = new SAXReader();
365 Document document = reader.read(rep.getStream());
370 * extractId extract document id from response
374 private String extractId(Document document) {
376 Element root = document.getRootElement();
377 for(Iterator i = root.elementIterator(); i.hasNext();){
378 Element element = (Element) i.next();
379 if("docRef".equals(element.getName())){
380 csid = (String) element.getData();
387 private NuxeoRESTClient getNuxeoRestClient() {
388 if(nuxeoRestClient == null){
389 ServiceConfig sconfig = ServiceMain.getInstance().getServiceConfig();
390 //assumption: there is only one client and that also is rest
391 RepositoryClientConfigType repConfig = sconfig.getRepositoryClient();
392 String protocol = "http";
393 if(repConfig.getProtocol() != null && !"".equals(repConfig.getProtocol())){
394 protocol = repConfig.getProtocol();
396 NuxeoRESTClient tempClient = new NuxeoRESTClient(protocol,
397 repConfig.getHost(), "" + repConfig.getPort());
399 tempClient.setBasicAuthentication(repConfig.getUser(), repConfig.getPassword());
401 nuxeoRestClient = tempClient;
404 return nuxeoRestClient;