This project has retired. For details please refer to its Attic page.
CmisBrowserBindingServlet xref
View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.server.impl.browser;
20  
21  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_ADD_OBJECT_TO_FOLDER;
22  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_APPEND_CONTENT;
23  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_APPLY_ACL;
24  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_APPLY_POLICY;
25  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_BULK_UPDATE;
26  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CANCEL_CHECK_OUT;
27  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CHECK_IN;
28  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CHECK_OUT;
29  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CREATE_DOCUMENT;
30  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CREATE_DOCUMENT_FROM_SOURCE;
31  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CREATE_FOLDER;
32  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CREATE_ITEM;
33  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CREATE_POLICY;
34  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CREATE_RELATIONSHIP;
35  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_CREATE_TYPE;
36  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_DELETE;
37  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_DELETE_CONTENT;
38  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_DELETE_TREE;
39  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_DELETE_TYPE;
40  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_MOVE;
41  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_QUERY;
42  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_REMOVE_OBJECT_FROM_FOLDER;
43  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_REMOVE_POLICY;
44  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_SET_CONTENT;
45  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_UPDATE_PROPERTIES;
46  import static org.apache.chemistry.opencmis.commons.impl.Constants.CMISACTION_UPDATE_TYPE;
47  import static org.apache.chemistry.opencmis.commons.impl.Constants.PARAM_OBJECT_ID;
48  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_ACL;
49  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_ALLOWABLEACTIONS;
50  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_CHECKEDOUT;
51  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_CHILDREN;
52  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_CONTENT;
53  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_CONTENT_CHANGES;
54  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_DESCENDANTS;
55  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_FOLDER_TREE;
56  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_LAST_RESULT;
57  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_OBJECT;
58  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_PARENT;
59  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_PARENTS;
60  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_POLICIES;
61  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_PROPERTIES;
62  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_QUERY;
63  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_RELATIONSHIPS;
64  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_RENDITIONS;
65  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_REPOSITORY_INFO;
66  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_TYPE_CHILDREN;
67  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_TYPE_DEFINITION;
68  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_TYPE_DESCENDANTS;
69  import static org.apache.chemistry.opencmis.commons.impl.Constants.SELECTOR_VERSIONS;
70  import static org.apache.chemistry.opencmis.commons.impl.JSONConstants.ERROR_EXCEPTION;
71  import static org.apache.chemistry.opencmis.commons.impl.JSONConstants.ERROR_MESSAGE;
72  import static org.apache.chemistry.opencmis.commons.impl.JSONConstants.ERROR_STACKTRACE;
73  import static org.apache.chemistry.opencmis.server.shared.Dispatcher.METHOD_GET;
74  import static org.apache.chemistry.opencmis.server.shared.Dispatcher.METHOD_HEAD;
75  import static org.apache.chemistry.opencmis.server.shared.Dispatcher.METHOD_POST;
76  
77  import java.io.IOException;
78  import java.io.InputStream;
79  import java.io.PrintWriter;
80  import java.util.Map;
81  
82  import javax.servlet.ServletConfig;
83  import javax.servlet.ServletException;
84  import javax.servlet.http.HttpServletRequest;
85  import javax.servlet.http.HttpServletResponse;
86  
87  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
88  import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
89  import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
90  import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
91  import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
92  import org.apache.chemistry.opencmis.commons.exceptions.CmisFilterNotValidException;
93  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
94  import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException;
95  import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
96  import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
97  import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException;
98  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
99  import org.apache.chemistry.opencmis.commons.exceptions.CmisServiceUnavailableException;
100 import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
101 import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
102 import org.apache.chemistry.opencmis.commons.exceptions.CmisTooManyRequestsException;
103 import org.apache.chemistry.opencmis.commons.exceptions.CmisUnauthorizedException;
104 import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
105 import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException;
106 import org.apache.chemistry.opencmis.commons.impl.Constants;
107 import org.apache.chemistry.opencmis.commons.impl.IOUtils;
108 import org.apache.chemistry.opencmis.commons.impl.json.JSONObject;
109 import org.apache.chemistry.opencmis.commons.server.CallContext;
110 import org.apache.chemistry.opencmis.commons.server.CmisService;
111 import org.apache.chemistry.opencmis.server.impl.ServerVersion;
112 import org.apache.chemistry.opencmis.server.impl.browser.token.TokenHandler;
113 import org.apache.chemistry.opencmis.server.shared.AbstractCmisHttpServlet;
114 import org.apache.chemistry.opencmis.server.shared.Dispatcher;
115 import org.apache.chemistry.opencmis.server.shared.ExceptionHelper;
116 import org.apache.chemistry.opencmis.server.shared.HEADHttpServletRequestWrapper;
117 import org.apache.chemistry.opencmis.server.shared.HttpUtils;
118 import org.apache.chemistry.opencmis.server.shared.NoBodyHttpServletResponseWrapper;
119 import org.apache.chemistry.opencmis.server.shared.QueryStringHttpServletRequestWrapper;
120 import org.apache.chemistry.opencmis.server.shared.ServiceCall;
121 import org.apache.chemistry.opencmis.server.shared.TempStoreOutputStreamFactory;
122 import org.slf4j.Logger;
123 import org.slf4j.LoggerFactory;
124 
125 /**
126  * CMIS Browser binding servlet.
127  */
128 public class CmisBrowserBindingServlet extends AbstractCmisHttpServlet {
129 
130     private static final long serialVersionUID = 1L;
131 
132     private static final Logger LOG = LoggerFactory.getLogger(CmisBrowserBindingServlet.class);
133 
134     private final Dispatcher repositoryDispatcher = new Dispatcher(false);
135     private final Dispatcher rootDispatcher = new Dispatcher(false);
136     private static final ErrorServiceCall ERROR_SERTVICE_CALL = new ErrorServiceCall();
137 
138     public enum CallUrl {
139         SERVICE, REPOSITORY, ROOT
140     }
141 
142     @Override
143     public void init(ServletConfig config) throws ServletException {
144         super.init(config);
145 
146         // set the binding
147         setBinding(CallContext.BINDING_BROWSER);
148 
149         // set CMIS version -> can only be 1.1
150         setCmisVersion(CmisVersion.CMIS_1_1);
151 
152         // initialize repository resources
153         addRepositoryResource("", METHOD_GET, new RepositoryService.GetRepositories());
154         addRepositoryResource(SELECTOR_REPOSITORY_INFO, METHOD_GET, new RepositoryService.GetRepositoryInfo());
155         addRepositoryResource(SELECTOR_LAST_RESULT, METHOD_GET, new RepositoryService.GetLastResult());
156         addRepositoryResource(SELECTOR_TYPE_CHILDREN, METHOD_GET, new RepositoryService.GetTypeChildren());
157         addRepositoryResource(SELECTOR_TYPE_DESCENDANTS, METHOD_GET, new RepositoryService.GetTypeDescendants());
158         addRepositoryResource(SELECTOR_TYPE_DEFINITION, METHOD_GET, new RepositoryService.GetTypeDefinition());
159         addRepositoryResource(CMISACTION_CREATE_TYPE, METHOD_POST, new RepositoryService.CreateType());
160         addRepositoryResource(CMISACTION_UPDATE_TYPE, METHOD_POST, new RepositoryService.UpdateType());
161         addRepositoryResource(CMISACTION_DELETE_TYPE, METHOD_POST, new RepositoryService.DeleteType());
162         addRepositoryResource(SELECTOR_QUERY, METHOD_GET, new DiscoveryService.Query());
163         addRepositoryResource(SELECTOR_CHECKEDOUT, METHOD_GET, new NavigationService.GetCheckedOutDocs());
164         addRepositoryResource(SELECTOR_CONTENT_CHANGES, METHOD_GET, new DiscoveryService.GetContentChanges());
165 
166         addRepositoryResource(CMISACTION_QUERY, METHOD_POST, new DiscoveryService.Query());
167         addRepositoryResource(CMISACTION_CREATE_DOCUMENT, METHOD_POST, new ObjectService.CreateDocument());
168         addRepositoryResource(CMISACTION_CREATE_DOCUMENT_FROM_SOURCE, METHOD_POST,
169                 new ObjectService.CreateDocumentFromSource());
170         addRepositoryResource(CMISACTION_CREATE_POLICY, METHOD_POST, new ObjectService.CreatePolicy());
171         addRepositoryResource(CMISACTION_CREATE_ITEM, METHOD_POST, new ObjectService.CreateItem());
172         addRepositoryResource(CMISACTION_CREATE_RELATIONSHIP, METHOD_POST, new ObjectService.CreateRelationship());
173         addRepositoryResource(CMISACTION_BULK_UPDATE, METHOD_POST, new ObjectService.BulkUpdateProperties());
174 
175         // initialize root resources
176         addRootResource(SELECTOR_OBJECT, METHOD_GET, new ObjectService.GetObject());
177         addRootResource(SELECTOR_PROPERTIES, METHOD_GET, new ObjectService.GetProperties());
178         addRootResource(SELECTOR_ALLOWABLEACTIONS, METHOD_GET, new ObjectService.GetAllowableActions());
179         addRootResource(SELECTOR_RENDITIONS, METHOD_GET, new ObjectService.GetRenditions());
180         addRootResource(SELECTOR_CONTENT, METHOD_GET, new ObjectService.GetContentStream());
181         addRootResource(SELECTOR_CHILDREN, METHOD_GET, new NavigationService.GetChildren());
182         addRootResource(SELECTOR_DESCENDANTS, METHOD_GET, new NavigationService.GetDescendants());
183         addRootResource(SELECTOR_FOLDER_TREE, METHOD_GET, new NavigationService.GetFolderTree());
184         addRootResource(SELECTOR_PARENT, METHOD_GET, new NavigationService.GetFolderParent());
185         addRootResource(SELECTOR_PARENTS, METHOD_GET, new NavigationService.GetObjectParents());
186         addRootResource(SELECTOR_VERSIONS, METHOD_GET, new VersioningService.GetAllVersions());
187         addRootResource(SELECTOR_RELATIONSHIPS, METHOD_GET, new RelationshipService.GetObjectRelationships());
188         addRootResource(SELECTOR_CHECKEDOUT, METHOD_GET, new NavigationService.GetCheckedOutDocs());
189         addRootResource(SELECTOR_POLICIES, METHOD_GET, new PolicyService.GetAppliedPolicies());
190         addRootResource(SELECTOR_ACL, METHOD_GET, new AclService.GetACL());
191 
192         addRootResource(CMISACTION_CREATE_DOCUMENT, METHOD_POST, new ObjectService.CreateDocument());
193         addRootResource(CMISACTION_CREATE_DOCUMENT_FROM_SOURCE, METHOD_POST,
194                 new ObjectService.CreateDocumentFromSource());
195         addRootResource(CMISACTION_CREATE_FOLDER, METHOD_POST, new ObjectService.CreateFolder());
196         addRootResource(CMISACTION_CREATE_POLICY, METHOD_POST, new ObjectService.CreatePolicy());
197         addRootResource(CMISACTION_CREATE_ITEM, METHOD_POST, new ObjectService.CreateItem());
198         addRootResource(CMISACTION_UPDATE_PROPERTIES, METHOD_POST, new ObjectService.UpdateProperties());
199         addRootResource(CMISACTION_SET_CONTENT, METHOD_POST, new ObjectService.SetContentStream());
200         addRootResource(CMISACTION_APPEND_CONTENT, METHOD_POST, new ObjectService.AppendContentStream());
201         addRootResource(CMISACTION_DELETE_CONTENT, METHOD_POST, new ObjectService.DeleteContentStream());
202         addRootResource(CMISACTION_DELETE, METHOD_POST, new ObjectService.DeleteObject());
203         addRootResource(CMISACTION_DELETE_TREE, METHOD_POST, new ObjectService.DeleteTree());
204         addRootResource(CMISACTION_MOVE, METHOD_POST, new ObjectService.MoveObject());
205         addRootResource(CMISACTION_ADD_OBJECT_TO_FOLDER, METHOD_POST, new MultiFilingService.AddObjectToFolder());
206         addRootResource(CMISACTION_REMOVE_OBJECT_FROM_FOLDER, METHOD_POST,
207                 new MultiFilingService.RemoveObjectFromFolder());
208         addRootResource(CMISACTION_CHECK_OUT, METHOD_POST, new VersioningService.CheckOut());
209         addRootResource(CMISACTION_CANCEL_CHECK_OUT, METHOD_POST, new VersioningService.CancelCheckOut());
210         addRootResource(CMISACTION_CHECK_IN, METHOD_POST, new VersioningService.CheckIn());
211         addRootResource(CMISACTION_APPLY_POLICY, METHOD_POST, new PolicyService.ApplyPolicy());
212         addRootResource(CMISACTION_REMOVE_POLICY, METHOD_POST, new PolicyService.RemovePolicy());
213         addRootResource(CMISACTION_APPLY_ACL, METHOD_POST, new AclService.ApplyACL());
214 
215         // old OpenCMIS client send invalid selector, support them anyway
216         addRootResource("folder", METHOD_GET, new NavigationService.GetFolderTree());
217     }
218 
219     @Override
220     protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException,
221             IOException {
222         CallContext context = null;
223 
224         boolean flush = true;
225         try {
226             // CSRF token check
227             String method = request.getMethod();
228             if (!METHOD_GET.equals(method) && !METHOD_HEAD.equals(method)) {
229                 checkCsrfToken(request, response, false, false);
230             }
231 
232             // set default headers
233             response.addHeader("Cache-Control", "private, max-age=0");
234             response.addHeader("Server", ServerVersion.OPENCMIS_SERVER);
235 
236             // split path
237             String[] pathFragments = HttpUtils.splitPath(request);
238 
239             // create stream factory
240             TempStoreOutputStreamFactory streamFactoy = TempStoreOutputStreamFactory.newInstance(getServiceFactory(),
241                     pathFragments.length > 0 ? pathFragments[0] : null, request);
242 
243             // check HTTP method
244             if (METHOD_GET.equals(method)) {
245                 request = new QueryStringHttpServletRequestWrapper(request);
246             } else if (METHOD_POST.equals(method)) {
247                 request = new POSTHttpServletRequestWrapper(request, streamFactoy);
248             } else if (METHOD_HEAD.equals(method)) {
249                 request = new HEADHttpServletRequestWrapper(request);
250                 response = new NoBodyHttpServletResponseWrapper(response);
251             } else {
252                 throw new CmisNotSupportedException("Unsupported method");
253             }
254 
255             // invoke token handler, if necessary
256             if (request.getParameter("login") != null && getCallContextHandler() instanceof TokenHandler) {
257                 ((TokenHandler) getCallContextHandler()).service(getServletContext(), request, response);
258                 return;
259             }
260 
261             context = createContext(getServletContext(), request, response, streamFactoy);
262             dispatch(context, request, response, pathFragments);
263         } catch (Exception e) {
264             if (e instanceof CmisUnauthorizedException) {
265                 response.setHeader("WWW-Authenticate", "Basic realm=\"CMIS\", charset=\"UTF-8\"");
266                 response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization Required");
267             } else if (e instanceof CmisPermissionDeniedException) {
268                 if (context == null || context.getUsername() == null) {
269                     response.setHeader("WWW-Authenticate", "Basic realm=\"CMIS\", charset=\"UTF-8\"");
270                     response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Authorization Required");
271                 } else {
272                     printError(context, e, request, response);
273                 }
274             } else {
275                 // an IOException usually indicates that reading the request or
276                 // sending the response failed
277                 // flushing will probably fail and raise a new exception ->
278                 // avoid flushing
279                 flush = !(e instanceof IOException);
280 
281                 printError(context, e, request, response);
282             }
283         } catch (Throwable t) {
284             LOG.error(createLogMessage(t, request), t);
285 
286             try {
287                 response.resetBuffer();
288                 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
289                 response.setContentType(AbstractBrowserServiceCall.JSON_MIME_TYPE);
290                 response.setCharacterEncoding(IOUtils.UTF8);
291 
292                 PrintWriter pw = response.getWriter();
293                 pw.print("{\"exception\":\"runtime\",\"message\": \"An error occurred!\"}");
294                 pw.flush();
295             } catch (Exception te) {
296                 // we tried to send an error message but it failed.
297                 // there is nothing we can do...
298                 flush = false;
299             }
300 
301             throw t;
302         } finally {
303             // in any case close the content stream if one has been provided
304             if (request instanceof POSTHttpServletRequestWrapper) {
305                 InputStream stream = ((POSTHttpServletRequestWrapper) request).getStream();
306                 if (stream != null) {
307                     try {
308                         stream.close();
309                     } catch (IOException e) {
310                         LOG.error("Could not close POST stream: {}", e.toString(), e);
311                     }
312                 }
313             }
314 
315             // we are done.
316             if (flush) {
317                 try {
318                     response.flushBuffer();
319                 } catch (IOException ioe) {
320                     LOG.error("Could not flush resposne: {}", ioe.toString(), ioe);
321                 }
322             }
323         }
324     }
325 
326     // --------------------------------------------------------
327 
328     /**
329      * Registers a new repository resource.
330      */
331     protected void addRepositoryResource(String resource, String httpMethod, ServiceCall serviceCall) {
332         repositoryDispatcher.addResource(resource, httpMethod, serviceCall);
333     }
334 
335     /**
336      * Registers a new root resource.
337      */
338     protected void addRootResource(String resource, String httpMethod, ServiceCall serviceCall) {
339         rootDispatcher.addResource(resource, httpMethod, serviceCall);
340     }
341 
342     private void dispatch(CallContext context, HttpServletRequest request, HttpServletResponse response,
343             String[] pathFragments) throws Exception {
344         BrowserCallContextImpl browserContext = (BrowserCallContextImpl) context;
345         CmisService service = null;
346         try {
347             // get the service
348             service = getServiceFactory().getService(context);
349 
350             // analyze the path
351             if (pathFragments.length < 1) {
352                 // CSRF check
353                 checkCsrfToken(request, response, true, false);
354 
355                 // root -> repository infos
356                 repositoryDispatcher.dispatch("", METHOD_GET, context, service, null, request, response);
357                 return;
358             }
359 
360             // select dispatcher
361             CallUrl callUrl = null;
362             if (pathFragments.length == 1) {
363                 callUrl = CallUrl.REPOSITORY;
364             } else if (AbstractBrowserServiceCall.ROOT_PATH_FRAGMENT.equals(pathFragments[1])) {
365                 callUrl = CallUrl.ROOT;
366             }
367 
368             if (callUrl == null) {
369                 throw new CmisNotSupportedException("Unknown operation");
370             }
371 
372             String method = request.getMethod();
373             String repositoryId = pathFragments[0];
374             boolean callServiceFound = false;
375 
376             if (METHOD_GET.equals(method)) {
377                 String selector = HttpUtils.getStringParameter(request, Constants.PARAM_SELECTOR);
378                 String objectId = HttpUtils.getStringParameter(request, PARAM_OBJECT_ID);
379 
380                 // dispatch
381                 if (callUrl == CallUrl.REPOSITORY) {
382                     if (selector == null || selector.length() == 0) {
383                         throw new CmisNotSupportedException("No selector");
384                     }
385 
386                     // CSRF check
387                     checkCsrfToken(request, response, SELECTOR_REPOSITORY_INFO.equalsIgnoreCase(selector), false);
388 
389                     // dispatch
390                     browserContext.setCallDetails(service, objectId, null, null);
391                     callServiceFound = repositoryDispatcher.dispatch(selector, method, browserContext, service,
392                             repositoryId, request, response);
393                 } else if (callUrl == CallUrl.ROOT) {
394                     browserContext.setCallDetails(service, objectId, pathFragments, null);
395 
396                     // set default method if necessary
397                     if (selector == null) {
398                         try {
399                             BaseTypeId basetype = browserContext.getBaseTypeId();
400                             switch (basetype) {
401                             case CMIS_DOCUMENT:
402                                 selector = SELECTOR_CONTENT;
403                                 break;
404                             case CMIS_FOLDER:
405                                 selector = SELECTOR_CHILDREN;
406                                 break;
407                             default:
408                                 selector = SELECTOR_OBJECT;
409                                 break;
410                             }
411                         } catch (Exception e) {
412                             selector = SELECTOR_OBJECT;
413                         }
414                     }
415 
416                     // CSRF check
417                     checkCsrfToken(request, response, false, SELECTOR_CONTENT.equalsIgnoreCase(selector));
418 
419                     // dispatch
420                     callServiceFound = rootDispatcher.dispatch(selector, method, browserContext, service, repositoryId,
421                             request, response);
422                 }
423             } else if (METHOD_POST.equals(method)) {
424                 String cmisaction = HttpUtils.getStringParameter(request, Constants.CONTROL_CMISACTION);
425                 String objectId = HttpUtils.getStringParameter(request, Constants.CONTROL_OBJECT_ID);
426                 String token = HttpUtils.getStringParameter(request, Constants.CONTROL_TOKEN);
427 
428                 if (cmisaction == null || cmisaction.length() == 0) {
429                     throw new CmisNotSupportedException("Unknown action");
430                 }
431 
432                 // dispatch
433                 if (callUrl == CallUrl.REPOSITORY) {
434                     browserContext.setCallDetails(service, objectId, null, token);
435                     callServiceFound = repositoryDispatcher.dispatch(cmisaction, method, browserContext, service,
436                             repositoryId, request, response);
437                 } else if (callUrl == CallUrl.ROOT) {
438                     browserContext.setCallDetails(service, objectId, pathFragments, token);
439                     callServiceFound = rootDispatcher.dispatch(cmisaction, method, browserContext, service,
440                             repositoryId, request, response);
441                 }
442             }
443 
444             // if the dispatcher couldn't find a matching service call
445             // -> return an error message
446             if (!callServiceFound) {
447                 throw new CmisNotSupportedException("Unknown operation");
448             }
449         } finally {
450             if (service != null) {
451                 service.close();
452             }
453         }
454     }
455 
456     /**
457      * Translates an exception in an appropriate HTTP error code.
458      */
459     protected int getErrorCode(CmisBaseException ex) {
460         return ERROR_SERTVICE_CALL.getErrorCode(ex);
461     }
462 
463     /**
464      * Prints an error as JSON.
465      */
466     protected void printError(CallContext context, Exception ex, HttpServletRequest request,
467             HttpServletResponse response) {
468         ERROR_SERTVICE_CALL.printError(context, ex, request, response);
469     }
470 
471     static class ErrorServiceCall extends AbstractBrowserServiceCall {
472 
473         @Override
474         public void serve(CallContext context, CmisService service, String repositoryId, HttpServletRequest request,
475                 HttpServletResponse response) throws Exception {
476             // no implementation
477         }
478 
479         public int getErrorCode(CmisBaseException ex) {
480             if (ex instanceof CmisConstraintException) {
481                 return 409;
482             } else if (ex instanceof CmisContentAlreadyExistsException) {
483                 return 409;
484             } else if (ex instanceof CmisFilterNotValidException) {
485                 return 400;
486             } else if (ex instanceof CmisInvalidArgumentException) {
487                 return 400;
488             } else if (ex instanceof CmisNameConstraintViolationException) {
489                 return 409;
490             } else if (ex instanceof CmisNotSupportedException) {
491                 return 405;
492             } else if (ex instanceof CmisObjectNotFoundException) {
493                 return 404;
494             } else if (ex instanceof CmisPermissionDeniedException) {
495                 return 403;
496             } else if (ex instanceof CmisStorageException) {
497                 return 500;
498             } else if (ex instanceof CmisStreamNotSupportedException) {
499                 return 403;
500             } else if (ex instanceof CmisUpdateConflictException) {
501                 return 409;
502             } else if (ex instanceof CmisVersioningException) {
503                 return 409;
504             } else if (ex instanceof CmisTooManyRequestsException) {
505                 return 429;
506             } else if (ex instanceof CmisServiceUnavailableException) {
507                 return 503;
508             }
509 
510             return 500;
511         }
512 
513         public void printError(CallContext context, Exception ex, HttpServletRequest request,
514                 HttpServletResponse response) {
515             int statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
516             String exceptionName = CmisRuntimeException.EXCEPTION_NAME;
517 
518             if (ex instanceof CmisRuntimeException) {
519                 LOG.error(createLogMessage(ex, request), ex);
520                 statusCode = getErrorCode((CmisRuntimeException) ex);
521             } else if (ex instanceof CmisStorageException) {
522                 LOG.error(createLogMessage(ex, request), ex);
523                 statusCode = getErrorCode((CmisStorageException) ex);
524                 exceptionName = ((CmisStorageException) ex).getExceptionName();
525             } else if (ex instanceof CmisBaseException) {
526                 statusCode = getErrorCode((CmisBaseException) ex);
527                 exceptionName = ((CmisBaseException) ex).getExceptionName();
528 
529                 if (statusCode == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
530                     LOG.error(createLogMessage(ex, request), ex);
531                 }
532             } else if (ex instanceof IOException) {
533                 LOG.warn(createLogMessage(ex, request), ex);
534             } else {
535                 LOG.error(createLogMessage(ex, request), ex);
536             }
537 
538             if (response.isCommitted()) {
539                 LOG.warn("Failed to send error message to client. Response is already committed.", ex);
540                 return;
541             }
542 
543             String token = (context instanceof BrowserCallContextImpl ? ((BrowserCallContextImpl) context).getToken()
544                     : null);
545 
546             String message = ex.getMessage();
547             if (!(ex instanceof CmisBaseException)) {
548                 message = "An error occurred!";
549             }
550 
551             if (token == null) {
552                 response.resetBuffer();
553                 setStatus(request, response, statusCode);
554 
555                 JSONObject jsonResponse = new JSONObject();
556 
557                 jsonResponse.put(ERROR_EXCEPTION, exceptionName);
558                 jsonResponse.put(ERROR_MESSAGE, message);
559 
560                 String st = ExceptionHelper.getStacktraceAsString(ex);
561                 if (st != null) {
562                     jsonResponse.put(ERROR_STACKTRACE, st);
563                 }
564 
565                 if (ex instanceof CmisBaseException) {
566                     Map<String, String> additionalData = ((CmisBaseException) ex).getAdditionalData();
567                     if (additionalData != null && !additionalData.isEmpty()) {
568                         for (Map.Entry<String, String> e : additionalData.entrySet()) {
569                             if (ERROR_EXCEPTION.equalsIgnoreCase(e.getKey())
570                                     || ERROR_MESSAGE.equalsIgnoreCase(e.getKey())) {
571                                 continue;
572                             }
573                             jsonResponse.put(e.getKey(), e.getValue());
574                         }
575                     }
576                 }
577 
578                 try {
579                     writeJSON(jsonResponse, request, response);
580                 } catch (Exception e) {
581                     LOG.error(createLogMessage(ex, request), e);
582                     try {
583                         response.sendError(statusCode, message);
584                     } catch (Exception en) {
585                         // there is nothing else we can do
586                     }
587                 }
588             } else {
589                 setStatus(request, response, HttpServletResponse.SC_OK);
590                 response.setContentType(HTML_MIME_TYPE);
591                 response.setContentLength(0);
592 
593                 if (context != null) {
594                     setCookie(request, response, context.getRepositoryId(), token,
595                             createCookieValue(statusCode, null, exceptionName, message));
596                 }
597             }
598         }
599     }
600 }