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.ServiceConfig.NuxeoClientConfig;
47 import org.collectionspace.services.common.ServiceMain;
48 import org.collectionspace.services.common.repository.BadRequestException;
49 import org.collectionspace.services.common.repository.DocumentNotFoundException;
50 import org.collectionspace.services.common.repository.DocumentHandler.Action;
51 import org.dom4j.Document;
52 import org.dom4j.Element;
53 import org.dom4j.io.SAXReader;
54 import org.dom4j.tree.DefaultDocument;
55 import org.restlet.data.MediaType;
56 import org.restlet.data.Method;
57 import org.restlet.data.Response;
58 import org.restlet.data.Status;
59 import org.restlet.resource.OutputRepresentation;
60 import org.restlet.resource.Representation;
63 * RepositoryRESTClient is used to perform CRUD operations on documents
64 * in Nuxeo repository using Nuxeo RESTful APIs. It uses @see DocumentHandler
65 * as IOHandler with the client.
69 * $LastChangedRevision: $
72 public class RepositoryRESTClient implements RepositoryClient {
74 private final Logger logger = LoggerFactory.getLogger(RepositoryRESTClient.class);
75 private NuxeoRESTClient nuxeoRestClient;
77 public RepositoryRESTClient() {
81 public String create(String serviceName, DocumentHandler handler) throws BadRequestException, DocumentException {
82 if(serviceName == null){
83 throw new IllegalArgumentException("RepositoryRESTClient.create: serviceName is missing");
85 if(handler.getDocumentType() == null){
86 throw new IllegalArgumentException("RepositoryRESTClient.create: docType is missing");
89 throw new IllegalArgumentException("RepositoryRESTClient.create: handler is missing");
91 ServiceMain smain = ServiceMain.getInstance();
92 String nuxeoWspaceId = smain.getWorkspaceId(serviceName);
93 if(nuxeoWspaceId == null){
94 throw new DocumentNotFoundException("Unable to find workspace for service " + serviceName +
95 " check if the mapping exists in service-config.xml or " +
96 " the the mapped workspace exists in the Nuxeo repository");
99 RepresentationHandler repHandler = (RepresentationHandler) handler;
100 repHandler.prepare(Action.CREATE);
101 List<String> pathParams = new ArrayList<String>();
102 pathParams.add("default");
103 pathParams.add(nuxeoWspaceId);
104 pathParams.add("createDocument");
105 if(repHandler.getPathParams().size() > 0){
106 pathParams.addAll(repHandler.getPathParams());
108 Map<String, String> queryParams = new HashMap<String, String>();
109 queryParams.put("docType", handler.getDocumentType());
110 // a default title for the Dublin Core schema
111 queryParams.put("dublincore:title", "CollectionSpace-" + handler.getDocumentType());
112 if(repHandler.getQueryParams().size() > 0){
113 queryParams.putAll(repHandler.getQueryParams());
116 String completeURL = getNuxeoRestClient().buildUrl(pathParams, queryParams);
117 if(logger.isDebugEnabled()){
118 logger.debug("create using url=" + completeURL);
120 Request request = buildRequest(Method.POST, completeURL);
122 //write out create stream, this is not needed as queryParams
123 //contain the document
124 final InputStream in = new ByteArrayInputStream(new byte[0]);
125 request.setEntity(new OutputRepresentation(
126 MediaType.MULTIPART_FORM_DATA) {
129 public void write(OutputStream outputStream) throws IOException {
130 byte[] buffer = new byte[1024 * 64];
132 while((read = in.read(buffer)) != -1){
133 outputStream.write(buffer, 0, read);
137 //following call to handler.handle is not needed as queryparams
138 //contains the real data
139 RepresentationWrapper wrapDoc = new RepresentationWrapper(new DefaultDocument());
140 repHandler.handle(Action.CREATE, wrapDoc);
142 //Nuxeo does not set 201 SUCCESS_CREATED on successful creation
143 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
144 //handle is not needed on create as queryparams have all data
145 return extractId(document);
148 if(logger.isDebugEnabled()){
149 logger.debug("Caught exception ", e);
151 throw new DocumentException(e);
157 public void get(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);
187 if(logger.isDebugEnabled()){
188 logger.debug("Caught exception ", e);
190 throw new DocumentException(e);
196 public void getAll(String serviceName, DocumentHandler handler) throws DocumentNotFoundException, DocumentException {
197 if(serviceName == null){
198 throw new IllegalArgumentException("RepositoryRESTClient.getAll: serviceName is missing");
201 throw new IllegalArgumentException("RepositoryRESTClient.getAll: handler is missing");
203 ServiceMain smain = ServiceMain.getInstance();
204 String nuxeoWspaceId = smain.getWorkspaceId(serviceName);
205 if(nuxeoWspaceId == null){
206 throw new DocumentNotFoundException("Unable to find workspace for service " + serviceName +
207 " check if the mapping exists in service-config.xml or " +
208 " the the mapped workspace exists in the Nuxeo repository");
211 RepresentationHandler repHandler = (RepresentationHandler) handler;
212 repHandler.prepare(Action.GET_ALL);
213 ArrayList pathParams = new ArrayList();
214 pathParams.add("default");
215 pathParams.add(nuxeoWspaceId);
216 pathParams.add("browse");
217 if(repHandler.getPathParams().size() > 0){
218 pathParams.addAll(repHandler.getPathParams());
220 String completeURL = getNuxeoRestClient().buildUrl(pathParams, repHandler.getQueryParams());
221 if(logger.isDebugEnabled()){
222 logger.debug("getAll using url=" + completeURL);
224 Request request = buildRequest(Method.GET, completeURL);
225 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
226 RepresentationWrapper wrapDoc = new RepresentationWrapper(document);
227 repHandler.handle(Action.GET_ALL, wrapDoc);
229 if(logger.isDebugEnabled()){
230 logger.debug("Caught exception ", e);
232 throw new DocumentException(e);
238 public void update(String id, DocumentHandler handler) throws BadRequestException, DocumentNotFoundException, DocumentException {
240 throw new IllegalArgumentException("RepositoryRESTClient.update: handler is missing");
244 RepresentationHandler repHandler = (RepresentationHandler) handler;
245 repHandler.prepare(Action.UPDATE);
246 List<String> pathParams = new ArrayList<String>();
247 pathParams.add("default");
249 pathParams.add("updateDocumentRestlet");
250 if(repHandler.getPathParams().size() > 0){
251 pathParams.addAll(repHandler.getPathParams());
253 String completeURL = getNuxeoRestClient().buildUrl(pathParams, repHandler.getQueryParams());
254 if(logger.isDebugEnabled()){
255 logger.debug("update using url=" + completeURL);
257 //repHandler.handle is not needed as queryParams contain all the data
258 RepresentationWrapper wrapDoc = new RepresentationWrapper(new DefaultDocument());
259 repHandler.handle(Action.UPDATE, wrapDoc);
260 Request request = buildRequest(Method.PUT, completeURL);
261 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
264 if(logger.isDebugEnabled()){
265 logger.debug("Caught exception ", e);
267 throw new DocumentException(e);
273 public void delete(String id) throws DocumentNotFoundException, DocumentException {
275 if(logger.isDebugEnabled()){
276 logger.debug("deleting document with id=" + id);
280 List<String> pathParams = new ArrayList<String>();
281 pathParams.add("default");
283 pathParams.add("deleteDocumentRestlet");
285 Map<String, String> queryParams = new HashMap<String, String>();
286 String completeURL = getNuxeoRestClient().buildUrl(pathParams, queryParams);
287 if(logger.isDebugEnabled()){
288 logger.debug("delete using url=" + completeURL);
290 Request request = buildRequest(Method.DELETE, completeURL);
291 Document document = executeRequest(request, completeURL, Status.SUCCESS_OK);
292 //FIXME error handling?
293 // Document document = service.deleteCollectionObject(csid);
294 // Element root = document.getRootElement();
295 // for(Iterator i = root.elementIterator(); i.hasNext();){
296 // Element element = (Element) i.next();
297 // if("docRef".equals(element.getName())){
298 // String status = (String) element.getData();
299 // verbose("deleteCollectionObjectt response: " + status);
304 if(logger.isDebugEnabled()){
305 logger.debug("Caught exception ", e);
307 throw new DocumentException(e);
313 * buildRequest build HTTP request given parameters
318 private Request buildRequest(Method method, String completeURL) {
319 Request request = new Request(method, completeURL);
320 getNuxeoRestClient().setupAuth(request);
321 getNuxeoRestClient().setupCookies(request);
326 * executeRequest execute given HTTP request
332 private Document executeRequest(Request request, String completeURL, Status expected) throws Exception {
334 Response res = getNuxeoRestClient().getRestClient().handle(request);
335 Status status = res.getStatus();
336 if(status.getCode() != expected.getCode()){
337 logger.error("failed to execute request=" + request.getMethod() +
338 " with error status=" + status +
339 " url=" + completeURL +
340 " response=" + res.toString());
341 throw new DocumentException(status.getCode(), status.getDescription());
343 Representation rep = res.getEntity();
346 return retrieveResponse(rep);
350 * retrieveResponse retrieves DOM document from Restlet Represeantion
355 private Document retrieveResponse(Representation rep) throws Exception {
356 SAXReader reader = new SAXReader();
357 Document document = reader.read(rep.getStream());
362 * extractId extract document id from response
366 private String extractId(Document document) {
368 Element root = document.getRootElement();
369 for(Iterator i = root.elementIterator(); i.hasNext();){
370 Element element = (Element) i.next();
371 if("docRef".equals(element.getName())){
372 csid = (String) element.getData();
379 private NuxeoRESTClient getNuxeoRestClient() {
380 if(nuxeoRestClient == null){
381 ServiceConfig sconfig = ServiceMain.getInstance().getServiceConfig();
382 NuxeoClientConfig nxConfig = sconfig.getNuxeoClientConfig();
383 String protocol = "http";
384 if(nxConfig.getProtocol() != null && !"".equals(nxConfig.getProtocol())){
385 protocol = nxConfig.getProtocol();
387 NuxeoRESTClient tempClient = new NuxeoRESTClient(protocol,
388 nxConfig.getHost(), "" + nxConfig.getPort());
390 tempClient.setBasicAuthentication(nxConfig.getUser(), nxConfig.getPassword());
392 nuxeoRestClient = tempClient;
395 return nuxeoRestClient;