1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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.HashMap;
25 import java.util.LinkedHashMap;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29
30 import org.apache.chemistry.opencmis.client.bindings.impl.CmisBindingsHelper;
31 import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession;
32 import org.apache.chemistry.opencmis.client.bindings.spi.LinkAccess;
33 import org.apache.chemistry.opencmis.client.bindings.spi.http.HttpInvoker;
34 import org.apache.chemistry.opencmis.client.bindings.spi.http.Output;
35 import org.apache.chemistry.opencmis.client.bindings.spi.http.Response;
36 import org.apache.chemistry.opencmis.commons.PropertyIds;
37 import org.apache.chemistry.opencmis.commons.SessionParameter;
38 import org.apache.chemistry.opencmis.commons.data.ObjectData;
39 import org.apache.chemistry.opencmis.commons.data.PropertyData;
40 import org.apache.chemistry.opencmis.commons.data.PropertyString;
41 import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
42 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
43 import org.apache.chemistry.opencmis.commons.enums.DateTimeFormat;
44 import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
45 import org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException;
46 import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
47 import org.apache.chemistry.opencmis.commons.exceptions.CmisContentAlreadyExistsException;
48 import org.apache.chemistry.opencmis.commons.exceptions.CmisFilterNotValidException;
49 import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
50 import org.apache.chemistry.opencmis.commons.exceptions.CmisNameConstraintViolationException;
51 import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
52 import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
53 import org.apache.chemistry.opencmis.commons.exceptions.CmisPermissionDeniedException;
54 import org.apache.chemistry.opencmis.commons.exceptions.CmisProxyAuthenticationException;
55 import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
56 import org.apache.chemistry.opencmis.commons.exceptions.CmisServiceUnavailableException;
57 import org.apache.chemistry.opencmis.commons.exceptions.CmisStorageException;
58 import org.apache.chemistry.opencmis.commons.exceptions.CmisStreamNotSupportedException;
59 import org.apache.chemistry.opencmis.commons.exceptions.CmisTooManyRequestsException;
60 import org.apache.chemistry.opencmis.commons.exceptions.CmisUnauthorizedException;
61 import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
62 import org.apache.chemistry.opencmis.commons.exceptions.CmisVersioningException;
63 import org.apache.chemistry.opencmis.commons.impl.Constants;
64 import org.apache.chemistry.opencmis.commons.impl.IOUtils;
65 import org.apache.chemistry.opencmis.commons.impl.JSONConstants;
66 import org.apache.chemistry.opencmis.commons.impl.JSONConverter;
67 import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
68 import org.apache.chemistry.opencmis.commons.impl.dataobjects.RepositoryInfoBrowserBindingImpl;
69 import org.apache.chemistry.opencmis.commons.impl.json.JSONObject;
70 import org.apache.chemistry.opencmis.commons.impl.json.parser.ContainerFactory;
71 import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParseException;
72 import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParser;
73 import org.apache.chemistry.opencmis.commons.spi.Holder;
74
75
76
77
78 public abstract class AbstractBrowserBindingService implements LinkAccess {
79
80 protected static final ContainerFactory SIMPLE_CONTAINER_FACTORY = new ContainerFactory() {
81 @Override
82 public Map<String, Object> createObjectContainer() {
83 return new LinkedHashMap<String, Object>();
84 }
85
86 @Override
87 public List<Object> creatArrayContainer() {
88 return new ArrayList<Object>();
89 }
90 };
91
92 private BindingSession session;
93 private boolean succint;
94 private DateTimeFormat dateTimeFormat;
95
96
97
98
99 protected void setSession(BindingSession session) {
100 this.session = session;
101
102 Object succintObj = session.get(SessionParameter.BROWSER_SUCCINCT);
103 this.succint = succintObj == null ? true : Boolean.parseBoolean(succintObj.toString());
104
105 Object dateTimeFormatObj = session.get(SessionParameter.BROWSER_DATETIME_FORMAT);
106 this.dateTimeFormat = dateTimeFormatObj == null ? DateTimeFormat.SIMPLE : DateTimeFormat
107 .fromValue(dateTimeFormatObj.toString().toLowerCase(Locale.ENGLISH));
108 }
109
110
111
112
113 protected BindingSession getSession() {
114 return session;
115 }
116
117
118
119
120 protected HttpInvoker getHttpInvoker() {
121 return CmisBindingsHelper.getHttpInvoker(session);
122 }
123
124
125
126
127 protected String getServiceUrl() {
128 Object url = session.get(SessionParameter.BROWSER_URL);
129 if (url instanceof String) {
130 return (String) url;
131 }
132
133 return null;
134 }
135
136 protected UrlBuilder getRepositoryUrl(String repositoryId, String selector) {
137 UrlBuilder result = getRepositoryUrlCache().getRepositoryUrl(repositoryId, selector);
138
139 if (result == null) {
140 getRepositoriesInternal(repositoryId);
141 result = getRepositoryUrlCache().getRepositoryUrl(repositoryId, selector);
142 }
143
144 if (result == null) {
145 throw new CmisObjectNotFoundException("Unknown repository!");
146 }
147
148 return result;
149 }
150
151 protected UrlBuilder getRepositoryUrl(String repositoryId) {
152 UrlBuilder result = getRepositoryUrlCache().getRepositoryUrl(repositoryId);
153
154 if (result == null) {
155 getRepositoriesInternal(repositoryId);
156 result = getRepositoryUrlCache().getRepositoryUrl(repositoryId);
157 }
158
159 if (result == null) {
160 throw new CmisObjectNotFoundException("Unknown repository!");
161 }
162
163 return result;
164 }
165
166 protected UrlBuilder getObjectUrl(String repositoryId, String objectId, String selector) {
167 UrlBuilder result = getRepositoryUrlCache().getObjectUrl(repositoryId, objectId, selector);
168
169 if (result == null) {
170 getRepositoriesInternal(repositoryId);
171 result = getRepositoryUrlCache().getObjectUrl(repositoryId, objectId, selector);
172 }
173
174 if (result == null) {
175 throw new CmisObjectNotFoundException("Unknown repository!");
176 }
177
178 return result;
179 }
180
181 protected UrlBuilder getObjectUrl(String repositoryId, String objectId) {
182 UrlBuilder result = getRepositoryUrlCache().getObjectUrl(repositoryId, objectId);
183
184 if (result == null) {
185 getRepositoriesInternal(repositoryId);
186 result = getRepositoryUrlCache().getObjectUrl(repositoryId, objectId);
187 }
188
189 if (result == null) {
190 throw new CmisObjectNotFoundException("Unknown repository!");
191 }
192
193 return result;
194 }
195
196 protected UrlBuilder getPathUrl(String repositoryId, String path, String selector) {
197 UrlBuilder result = getRepositoryUrlCache().getPathUrl(repositoryId, path, selector);
198
199 if (result == null) {
200 getRepositoriesInternal(repositoryId);
201 result = getRepositoryUrlCache().getPathUrl(repositoryId, path, selector);
202 }
203
204 if (result == null) {
205 throw new CmisObjectNotFoundException("Unknown repository!");
206 }
207
208 return result;
209 }
210
211 protected boolean getSuccinct() {
212 return succint;
213 }
214
215 protected String getSuccinctParameter() {
216 return succint ? "true" : null;
217 }
218
219 protected DateTimeFormat getDateTimeFormat() {
220 return dateTimeFormat;
221 }
222
223 protected String getDateTimeFormatParameter() {
224 return dateTimeFormat == null || dateTimeFormat == DateTimeFormat.SIMPLE ? null : dateTimeFormat.value();
225 }
226
227 protected void setChangeToken(Holder<String> changeToken, ObjectData obj) {
228 if (changeToken == null) {
229 return;
230 }
231
232 changeToken.setValue(null);
233
234 if (obj == null || obj.getProperties() == null || obj.getProperties().getProperties() == null) {
235 return;
236 }
237
238 PropertyData<?> ct = obj.getProperties().getProperties().get(PropertyIds.CHANGE_TOKEN);
239 if (ct instanceof PropertyString) {
240 changeToken.setValue(((PropertyString) ct).getFirstValue());
241 }
242 }
243
244
245
246
247
248
249 protected CmisBaseException convertStatusCode(int code, String message, String errorContent, Throwable t) {
250 Object obj = null;
251 try {
252 if (errorContent != null) {
253 JSONParser parser = new JSONParser();
254 obj = parser.parse(errorContent);
255 }
256 } catch (JSONParseException pe) {
257
258 }
259
260 if (obj instanceof JSONObject) {
261 JSONObject json = (JSONObject) obj;
262 Object jsonError = json.get(JSONConstants.ERROR_EXCEPTION);
263 if (jsonError instanceof String) {
264 Object jsonMessage = json.get(JSONConstants.ERROR_MESSAGE);
265 if (jsonMessage != null) {
266 message = jsonMessage.toString();
267 }
268
269 Map<String, String> additionalData = null;
270 for (Map.Entry<String, Object> e : json.entrySet()) {
271 if (JSONConstants.ERROR_EXCEPTION.equalsIgnoreCase(e.getKey())
272 || JSONConstants.ERROR_MESSAGE.equalsIgnoreCase(e.getKey())) {
273 continue;
274 }
275
276 if (additionalData == null) {
277 additionalData = new HashMap<String, String>();
278 }
279
280 additionalData.put(e.getKey(), e.getValue() == null ? null : e.getValue().toString());
281 }
282
283 if (CmisConstraintException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
284 return new CmisConstraintException(message, errorContent, additionalData, t);
285 } else if (CmisContentAlreadyExistsException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
286 return new CmisContentAlreadyExistsException(message, errorContent, additionalData, t);
287 } else if (CmisFilterNotValidException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
288 return new CmisFilterNotValidException(message, errorContent, additionalData, t);
289 } else if (CmisInvalidArgumentException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
290 return new CmisInvalidArgumentException(message, errorContent, additionalData, t);
291 } else if (CmisNameConstraintViolationException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
292 return new CmisNameConstraintViolationException(message, errorContent, additionalData, t);
293 } else if (CmisNotSupportedException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
294 return new CmisNotSupportedException(message, errorContent, additionalData, t);
295 } else if (CmisObjectNotFoundException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
296 return new CmisObjectNotFoundException(message, errorContent, additionalData, t);
297 } else if (CmisPermissionDeniedException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
298 return new CmisPermissionDeniedException(message, errorContent, additionalData, t);
299 } else if (CmisStorageException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
300 return new CmisStorageException(message, errorContent, additionalData, t);
301 } else if (CmisStreamNotSupportedException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
302 return new CmisStreamNotSupportedException(message, errorContent, additionalData, t);
303 } else if (CmisUpdateConflictException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
304 return new CmisUpdateConflictException(message, errorContent, additionalData, t);
305 } else if (CmisVersioningException.EXCEPTION_NAME.equalsIgnoreCase((String) jsonError)) {
306 return new CmisVersioningException(message, errorContent, additionalData, t);
307 } else if (code == 503) {
308 return new CmisServiceUnavailableException(message, errorContent, additionalData, t);
309 }
310 }
311 }
312
313
314 switch (code) {
315 case 301:
316 case 302:
317 case 303:
318 case 307:
319 return new CmisConnectionException("Redirects are not supported (HTTP status code " + code + "): "
320 + message, errorContent, t);
321 case 400:
322 return new CmisInvalidArgumentException(message, errorContent, t);
323 case 401:
324 return new CmisUnauthorizedException(message, errorContent, t);
325 case 403:
326 return new CmisPermissionDeniedException(message, errorContent, t);
327 case 404:
328 return new CmisObjectNotFoundException(message, errorContent, t);
329 case 405:
330 return new CmisNotSupportedException(message, errorContent, t);
331 case 407:
332 return new CmisProxyAuthenticationException(message, errorContent, t);
333 case 409:
334 return new CmisConstraintException(message, errorContent, t);
335 case 429:
336 return new CmisTooManyRequestsException(message, errorContent, t);
337 case 503:
338 return new CmisServiceUnavailableException(message, errorContent, t);
339 default:
340 return new CmisRuntimeException(message, errorContent, t);
341 }
342 }
343
344
345
346
347
348
349 @SuppressWarnings("unchecked")
350 protected Map<String, Object> parseObject(InputStream stream, String charset) {
351 Object obj = parse(stream, charset, SIMPLE_CONTAINER_FACTORY);
352
353 if (obj instanceof Map) {
354 return (Map<String, Object>) obj;
355 }
356
357 throw new CmisConnectionException("Unexpected object!");
358 }
359
360
361
362
363 @SuppressWarnings("unchecked")
364 protected List<Object> parseArray(InputStream stream, String charset) {
365 Object obj = parse(stream, charset, SIMPLE_CONTAINER_FACTORY);
366
367 if (obj instanceof List) {
368 return (List<Object>) obj;
369 }
370
371 throw new CmisConnectionException("Unexpected object!");
372 }
373
374
375
376
377 protected Object parse(InputStream stream, String charset, ContainerFactory containerFactory) {
378
379 InputStreamReader reader = null;
380
381 Object obj = null;
382 try {
383 reader = new InputStreamReader(stream, charset);
384 JSONParser parser = new JSONParser();
385 obj = parser.parse(reader, containerFactory);
386 } catch (JSONParseException e) {
387 throw new CmisConnectionException("Parsing exception: " + e.getMessage(), e);
388 } catch (Exception e) {
389 throw new CmisConnectionException("Parsing exception!", e);
390 } finally {
391 IOUtils.consumeAndClose(reader);
392 if (reader == null) {
393 IOUtils.closeQuietly(stream);
394 }
395 }
396
397 return obj;
398 }
399
400
401
402
403
404 protected Response read(UrlBuilder url) {
405
406 Response resp = getHttpInvoker().invokeGET(url, session);
407
408
409 if (resp.getResponseCode() != 200) {
410 throw convertStatusCode(resp.getResponseCode(), resp.getResponseMessage(), resp.getErrorContent(), null);
411 }
412
413 return resp;
414 }
415
416
417
418
419
420 protected Response post(UrlBuilder url, String contentType, Output writer) {
421
422 Response resp = getHttpInvoker().invokePOST(url, contentType, writer, session);
423
424
425 if (resp.getResponseCode() != 200 && resp.getResponseCode() != 201) {
426 throw convertStatusCode(resp.getResponseCode(), resp.getResponseMessage(), resp.getErrorContent(), null);
427 }
428
429 return resp;
430 }
431
432
433
434
435
436 protected void postAndConsume(UrlBuilder url, String contentType, Output writer) {
437 Response resp = post(url, contentType, writer);
438 IOUtils.consumeAndClose(resp.getStream());
439 }
440
441
442
443
444
445
446
447 protected RepositoryUrlCache getRepositoryUrlCache() {
448 RepositoryUrlCache repositoryUrlCache = (RepositoryUrlCache) getSession().get(
449 SpiSessionParameter.REPOSITORY_URL_CACHE);
450 if (repositoryUrlCache == null) {
451 repositoryUrlCache = new RepositoryUrlCache();
452 getSession().put(SpiSessionParameter.REPOSITORY_URL_CACHE, repositoryUrlCache);
453 }
454
455 return repositoryUrlCache;
456 }
457
458
459
460
461 protected List<RepositoryInfo> getRepositoriesInternal(String repositoryId) {
462
463 UrlBuilder url = null;
464
465 if (repositoryId == null) {
466
467 url = new UrlBuilder(getServiceUrl());
468 } else {
469
470 url = getRepositoryUrlCache().getRepositoryUrl(repositoryId, Constants.SELECTOR_REPOSITORY_INFO);
471 if (url == null) {
472
473 url = new UrlBuilder(getServiceUrl());
474 }
475 }
476
477
478 Response resp = read(url);
479 Map<String, Object> json = parseObject(resp.getStream(), resp.getCharset());
480
481 List<RepositoryInfo> repInfos = new ArrayList<RepositoryInfo>();
482
483 for (Object jri : json.values()) {
484 if (jri instanceof Map) {
485 @SuppressWarnings("unchecked")
486 RepositoryInfo ri = JSONConverter.convertRepositoryInfo((Map<String, Object>) jri);
487 String id = ri.getId();
488
489 if (ri instanceof RepositoryInfoBrowserBindingImpl) {
490 String repositoryUrl = ((RepositoryInfoBrowserBindingImpl) ri).getRepositoryUrl();
491 String rootUrl = ((RepositoryInfoBrowserBindingImpl) ri).getRootUrl();
492
493 if (id == null || repositoryUrl == null || rootUrl == null) {
494 throw new CmisConnectionException("Found invalid Repository Info! (id: " + id + ")");
495 }
496
497 getRepositoryUrlCache().addRepository(id, repositoryUrl, rootUrl);
498 }
499
500 repInfos.add(ri);
501 } else {
502 throw new CmisConnectionException("Found invalid Repository Info!");
503 }
504 }
505
506 return repInfos;
507 }
508
509
510
511
512 protected TypeDefinition getTypeDefinitionInternal(String repositoryId, String typeId) {
513
514 UrlBuilder url = getRepositoryUrl(repositoryId, Constants.SELECTOR_TYPE_DEFINITION);
515 url.addParameter(Constants.PARAM_TYPE_ID, typeId);
516
517
518 Response resp = read(url);
519 Map<String, Object> json = parseObject(resp.getStream(), resp.getCharset());
520
521 return JSONConverter.convertTypeDefinition(json);
522 }
523
524
525
526 @Override
527 public String loadLink(String repositoryId, String objectId, String rel, String type) {
528
529 return null;
530 }
531
532 @Override
533 public String loadContentLink(String repositoryId, String documentId) {
534 UrlBuilder result = getRepositoryUrlCache().getObjectUrl(repositoryId, documentId, Constants.SELECTOR_CONTENT);
535 return result == null ? null : result.toString();
536 }
537
538 @Override
539 public String loadRenditionContentLink(String repositoryId, String documentId, String streamId) {
540 UrlBuilder result = getRepositoryUrlCache().getObjectUrl(repositoryId, documentId, Constants.SELECTOR_CONTENT);
541 if (result != null) {
542 result.addParameter(Constants.PARAM_STREAM_ID, streamId);
543 return result.toString();
544 }
545
546 return null;
547 }
548 }