This project has retired. For details please refer to its Attic page.
AbstractBrowserBindingService xref

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.client.bindings.spi.browser;
20  
21  import java.io.InputStream;
22  import java.io.InputStreamReader;
23  import java.util.ArrayList;
24  import java.util.LinkedHashMap;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession;
29  import org.apache.chemistry.opencmis.client.bindings.spi.LinkAccess;
30  import org.apache.chemistry.opencmis.client.bindings.spi.http.HttpUtils;
31  import org.apache.chemistry.opencmis.commons.SessionParameter;
32  import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
33  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
34  import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
35  import org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException;
36  import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
37  import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
38  import org.apache.chemistry.opencmis.commons.exceptions.CmisFilterNotValidException;
39  import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
40  import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException;
41  import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
42  import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
43  import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException;
44  import org.apache.chemistry.opencmis.commons.exceptions.CmisProxyAuthenticationException;
45  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
46  import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
47  import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
48  import org.apache.chemistry.opencmis.commons.exceptions.CmisUnauthorizedException;
49  import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
50  import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException;
51  import org.apache.chemistry.opencmis.commons.impl.Constants;
52  import org.apache.chemistry.opencmis.commons.impl.JSONConstants;
53  import org.apache.chemistry.opencmis.commons.impl.JSONConverter;
54  import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
55  import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoBrowserBindingImpl;
56  import org.apache.chemistry.opencmis.commons.impl.json.JSONObject;
57  import org.apache.chemistry.opencmis.commons.impl.json.parser.ContainerFactory;
58  import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParser;
59  
60  /**
61   * Base class for all Browser Binding client services.
62   */
63  public abstract class AbstractBrowserBindingService implements LinkAccess {
64  
65      protected static final ContainerFactory SIMPLE_CONTAINER_FACTORY = new ContainerFactory() {
66          public Map<String, Object> createObjectContainer() {
67              return new LinkedHashMap<String, Object>();
68          }
69  
70          public List<Object> creatArrayContainer() {
71              return new ArrayList<Object>();
72          }
73      };
74  
75      private BindingSession session;
76  
77      /**
78       * Sets the current session.
79       */
80      protected void setSession(BindingSession session) {
81          this.session = session;
82      }
83  
84      /**
85       * Gets the current session.
86       */
87      protected BindingSession getSession() {
88          return session;
89      }
90  
91      /**
92       * Returns the service URL of this session.
93       */
94      protected String getServiceUrl() {
95          Object url = session.get(SessionParameter.BROWSER_URL);
96          if (url instanceof String) {
97              return (String) url;
98          }
99  
100         return null;
101     }
102 
103     protected UrlBuilder getRepositoryUrl(String repositoryId, String selector) {
104         UrlBuilder result = getRepositoryUrlCache().getRepositoryUrl(repositoryId, selector);
105 
106         if (result == null) {
107             getRepositoriesInternal(repositoryId);
108             result = getRepositoryUrlCache().getRepositoryUrl(repositoryId, selector);
109         }
110 
111         if (result == null) {
112             throw new CmisObjectNotFoundException("Unknown repository!");
113         }
114 
115         return result;
116     }
117 
118     protected UrlBuilder getRepositoryUrl(String repositoryId) {
119         UrlBuilder result = getRepositoryUrlCache().getRepositoryUrl(repositoryId);
120 
121         if (result == null) {
122             getRepositoriesInternal(repositoryId);
123             result = getRepositoryUrlCache().getRepositoryUrl(repositoryId);
124         }
125 
126         if (result == null) {
127             throw new CmisObjectNotFoundException("Unknown repository!");
128         }
129 
130         return result;
131     }
132 
133     protected UrlBuilder getObjectUrl(String repositoryId, String objectId, String selector) {
134         UrlBuilder result = getRepositoryUrlCache().getObjectUrl(repositoryId, objectId, selector);
135 
136         if (result == null) {
137             getRepositoriesInternal(repositoryId);
138             result = getRepositoryUrlCache().getObjectUrl(repositoryId, objectId, selector);
139         }
140 
141         if (result == null) {
142             throw new CmisObjectNotFoundException("Unknown repository!");
143         }
144 
145         return result;
146     }
147 
148     protected UrlBuilder getObjectUrl(String repositoryId, String objectId) {
149         UrlBuilder result = getRepositoryUrlCache().getObjectUrl(repositoryId, objectId);
150 
151         if (result == null) {
152             getRepositoriesInternal(repositoryId);
153             result = getRepositoryUrlCache().getObjectUrl(repositoryId, objectId);
154         }
155 
156         if (result == null) {
157             throw new CmisObjectNotFoundException("Unknown repository!");
158         }
159 
160         return result;
161     }
162 
163     protected UrlBuilder getPathUrl(String repositoryId, String objectId, String selector) {
164         UrlBuilder result = getRepositoryUrlCache().getPathUrl(repositoryId, objectId, selector);
165 
166         if (result == null) {
167             getRepositoriesInternal(repositoryId);
168             result = getRepositoryUrlCache().getPathUrl(repositoryId, objectId, selector);
169         }
170 
171         if (result == null) {
172             throw new CmisObjectNotFoundException("Unknown repository!");
173         }
174 
175         return result;
176     }
177 
178     // ---- exceptions ----
179 
180     /**
181      * Converts an error message or a HTTP status code into an Exception.
182      */
183     protected CmisBaseException convertStatusCode(int code, String message, String errorContent, Throwable t) {
184         Object obj = null;
185         try {
186             JSONParser parser = new JSONParser();
187             obj = parser.parse(errorContent);
188         } catch (Exception pe) {
189         }
190 
191         if (obj instanceof JSONObject) {
192             JSONObject json = (JSONObject) obj;
193             Object jsonError = json.get(JSONConstants.ERROR_EXCEPTION);
194             if (jsonError instanceof String) {
195                 Object jsonMessage = json.get(JSONConstants.ERROR_MESSAGE);
196                 if (jsonMessage != null) {
197                     message = jsonMessage.toString();
198                 }
199 
200                 if (CmisConstraintException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
201                     return new CmisConstraintException(message, errorContent, t);
202                 } else if (CmisContentAlreadyExistsException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
203                     return new CmisContentAlreadyExistsException(message, errorContent, t);
204                 } else if (CmisFilterNotValidException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
205                     return new CmisFilterNotValidException(message, errorContent, t);
206                 } else if (CmisInvalidArgumentException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
207                     return new CmisInvalidArgumentException(message, errorContent, t);
208                 } else if (CmisNameConstraintViolationException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
209                     return new CmisNameConstraintViolationException(message, errorContent, t);
210                 } else if (CmisNotSupportedException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
211                     return new CmisNotSupportedException(message, errorContent, t);
212                 } else if (CmisObjectNotFoundException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
213                     return new CmisObjectNotFoundException(message, errorContent, t);
214                 } else if (CmisPermissionDeniedException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
215                     return new CmisPermissionDeniedException(message, errorContent, t);
216                 } else if (CmisStorageException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
217                     return new CmisStorageException(message, errorContent, t);
218                 } else if (CmisStreamNotSupportedException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
219                     return new CmisStreamNotSupportedException(message, errorContent, t);
220                 } else if (CmisUpdateConflictException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
221                     return new CmisUpdateConflictException(message, errorContent, t);
222                 } else if (CmisVersioningException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
223                     return new CmisVersioningException(message, errorContent, t);
224                 }
225             }
226         }
227 
228         // fall back to status code
229         switch (code) {
230         case 400:
231             return new CmisInvalidArgumentException(message, errorContent, t);
232         case 401:
233             return new CmisUnauthorizedException(message, errorContent, t);
234         case 403:
235             return new CmisPermissionDeniedException(message, errorContent, t);
236         case 404:
237             return new CmisObjectNotFoundException(message, errorContent, t);
238         case 405:
239             return new CmisNotSupportedException(message, errorContent, t);
240         case 407:
241             return new CmisProxyAuthenticationException(message, errorContent, t);
242         case 409:
243             return new CmisConstraintException(message, errorContent, t);
244         default:
245             return new CmisRuntimeException(message, errorContent, t);
246         }
247     }
248 
249     // ---- helpers ----
250 
251     /**
252      * Parses an object from an input stream.
253      */
254     @SuppressWarnings("unchecked")
255     protected Map<String, Object> parseObject(InputStream stream, String charset) {
256         Object obj = parse(stream, charset, SIMPLE_CONTAINER_FACTORY);
257 
258         if (obj instanceof Map) {
259             return (Map<String, Object>) obj;
260         }
261 
262         throw new CmisConnectionException("Unexpected object!");
263     }
264 
265     /**
266      * Parses an array from an input stream.
267      */
268     @SuppressWarnings("unchecked")
269     protected List<Object> parseArray(InputStream stream, String charset) {
270         Object obj = parse(stream, charset, SIMPLE_CONTAINER_FACTORY);
271 
272         if (obj instanceof List) {
273             return (List<Object>) obj;
274         }
275 
276         throw new CmisConnectionException("Unexpected object!");
277     }
278 
279     /**
280      * Parses an input stream.
281      */
282     protected Object parse(InputStream stream, String charset, ContainerFactory containerFactory) {
283 
284         InputStreamReader reader = null;
285 
286         Object obj = null;
287         try {
288             reader = new InputStreamReader(stream, charset);
289             JSONParser parser = new JSONParser();
290             obj = parser.parse(reader, containerFactory);
291         } catch (Exception e) {
292             throw new CmisConnectionException("Parsing exception!", e);
293         } finally {
294             try {
295                 char[] buffer = new char[4096];
296                 while (reader.read(buffer) > -1) {
297                 }
298             } catch (Exception e) {
299             }
300             try {
301                 if (reader == null) {
302                     stream.close();
303                 } else {
304                     reader.close();
305                 }
306             } catch (Exception e) {
307             }
308         }
309 
310         return obj;
311     }
312 
313     /**
314      * Performs a GET on an URL, checks the response code and returns the
315      * result.
316      */
317     protected HttpUtils.Response read(UrlBuilder url) {
318         // make the call
319         HttpUtils.Response resp = HttpUtils.invokeGET(url, session);
320 
321         // check response code
322         if (resp.getResponseCode() != 200) {
323             throw convertStatusCode(resp.getResponseCode(), resp.getResponseMessage(), resp.getErrorContent(), null);
324         }
325 
326         return resp;
327     }
328 
329     /**
330      * Performs a POST on an URL, checks the response code and returns the
331      * result.
332      */
333     protected HttpUtils.Response post(UrlBuilder url, String contentType, HttpUtils.Output writer) {
334         // make the call
335         HttpUtils.Response resp = HttpUtils.invokePOST(url, contentType, writer, session);
336 
337         // check response code
338         if (resp.getResponseCode() != 200 && resp.getResponseCode() != 201) {
339             throw convertStatusCode(resp.getResponseCode(), resp.getResponseMessage(), resp.getErrorContent(), null);
340         }
341 
342         return resp;
343     }
344 
345     /**
346      * Performs a POST on an URL, checks the response code and returns the
347      * result.
348      */
349     protected void postAndConsume(UrlBuilder url, String contentType, HttpUtils.Output writer) {
350         HttpUtils.Response resp = post(url, contentType, writer);
351 
352         InputStream stream = resp.getStream();
353         try {
354             byte[] buffer = new byte[4096];
355             while (stream.read(buffer) > -1) {
356             }
357         } catch (Exception e) {
358             // ignore
359         } finally {
360             try {
361                 stream.close();
362             } catch (Exception e) {
363             }
364         }
365     }
366 
367     // ---- URL ----
368 
369     /**
370      * Returns the repository URL cache or creates a new cache if it doesn't
371      * exist.
372      */
373     protected RepositoryUrlCache getRepositoryUrlCache() {
374         RepositoryUrlCache repositoryUrlCache = (RepositoryUrlCache) getSession().get(
375                 SpiSessionParameter.REPOSITORY_URL_CACHE);
376         if (repositoryUrlCache == null) {
377             repositoryUrlCache = new RepositoryUrlCache();
378             getSession().put(SpiSessionParameter.REPOSITORY_URL_CACHE, repositoryUrlCache);
379         }
380 
381         return repositoryUrlCache;
382     }
383 
384     /**
385      * Retrieves the the repository info objects.
386      */
387     protected List<RepositoryInfo> getRepositoriesInternal(String repositoryId) {
388 
389         UrlBuilder url = null;
390 
391         if (repositoryId == null) {
392             // no repository id provided -> get all
393             url = new UrlBuilder(getServiceUrl());
394         } else {
395             // use URL of the specified repository
396             url = getRepositoryUrlCache().getRepositoryUrl(repositoryId, Constants.SELECTOR_REPOSITORY_INFO);
397             if (url == null) {
398                 // repository infos haven't been fetched yet -> get them all
399                 url = new UrlBuilder(getServiceUrl());
400             }
401         }
402 
403         // read and parse
404         HttpUtils.Response resp = read(url);
405         Map<String, Object> json = parseObject(resp.getStream(), resp.getCharset());
406 
407         List<RepositoryInfo> repInfos = new ArrayList<RepositoryInfo>();
408 
409         for (Object jri : json.values()) {
410             if (jri instanceof Map) {
411                 @SuppressWarnings("unchecked")
412                 RepositoryInfo ri = JSONConverter.convertRepositoryInfo((Map<String, Object>) jri);
413                 String id = ri.getId();
414 
415                 if (ri instanceof RepositoryInfoBrowserBindingImpl) {
416                     String repositoryUrl = ((RepositoryInfoBrowserBindingImpl) ri).getRepositoryUrl();
417                     String rootUrl = ((RepositoryInfoBrowserBindingImpl) ri).getRootUrl();
418 
419                     if (id == null || repositoryUrl == null || rootUrl == null) {
420                         throw new CmisConnectionException("Found invalid Repository Info! (id: " + id + ")");
421                     }
422 
423                     getRepositoryUrlCache().addRepository(id, repositoryUrl, rootUrl);
424                 }
425 
426                 if (repositoryId == null || repositoryId.equals(id)) {
427                     repInfos.add(ri);
428                 }
429             } else {
430                 throw new CmisConnectionException("Found invalid Repository Info!");
431             }
432         }
433 
434         return repInfos;
435     }
436 
437     /**
438      * Retrieves a type definition.
439      */
440     protected TypeDefinition getTypeDefinitionInternal(String repositoryId, String typeId) {
441         // build URL
442         UrlBuilder url = getRepositoryUrl(repositoryId, Constants.SELECTOR_TYPE_DEFINITION);
443         url.addParameter(Constants.PARAM_TYPE_ID, typeId);
444 
445         // read and parse
446         HttpUtils.Response resp = read(url);
447         Map<String, Object> json = parseObject(resp.getStream(), resp.getCharset());
448 
449         return JSONConverter.convertTypeDefinition(json);
450     }
451 
452     // ---- LinkAccess interface ----
453 
454     public String loadLink(String repositoryId, String objectId, String rel, String type) {
455         // AtomPub specific -> return null
456         return null;
457     }
458 
459     public String loadContentLink(String repositoryId, String documentId) {
460         UrlBuilder result = getRepositoryUrlCache().getObjectUrl(repositoryId, documentId, Constants.SELECTOR_CONTENT);
461         return result == null ? null : result.toString();
462     }
463 }