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.runtime;
20
21 import static org.apache.chemistry.opencmis.commons.impl.CollectionsHelper.isNullOrEmpty;
22
23 import java.math.BigInteger;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.Collections;
27 import java.util.EnumSet;
28 import java.util.HashMap;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Locale;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.concurrent.locks.ReentrantReadWriteLock;
35
36 import org.apache.chemistry.opencmis.client.api.ChangeEvent;
37 import org.apache.chemistry.opencmis.client.api.ChangeEvents;
38 import org.apache.chemistry.opencmis.client.api.CmisObject;
39 import org.apache.chemistry.opencmis.client.api.Document;
40 import org.apache.chemistry.opencmis.client.api.DocumentType;
41 import org.apache.chemistry.opencmis.client.api.Folder;
42 import org.apache.chemistry.opencmis.client.api.ItemIterable;
43 import org.apache.chemistry.opencmis.client.api.ObjectFactory;
44 import org.apache.chemistry.opencmis.client.api.ObjectId;
45 import org.apache.chemistry.opencmis.client.api.ObjectType;
46 import org.apache.chemistry.opencmis.client.api.OperationContext;
47 import org.apache.chemistry.opencmis.client.api.Policy;
48 import org.apache.chemistry.opencmis.client.api.QueryResult;
49 import org.apache.chemistry.opencmis.client.api.QueryStatement;
50 import org.apache.chemistry.opencmis.client.api.Relationship;
51 import org.apache.chemistry.opencmis.client.api.SecondaryType;
52 import org.apache.chemistry.opencmis.client.api.Session;
53 import org.apache.chemistry.opencmis.client.api.Tree;
54 import org.apache.chemistry.opencmis.client.bindings.cache.TypeDefinitionCache;
55 import org.apache.chemistry.opencmis.client.runtime.cache.Cache;
56 import org.apache.chemistry.opencmis.client.runtime.cache.CacheImpl;
57 import org.apache.chemistry.opencmis.client.runtime.repository.ObjectFactoryImpl;
58 import org.apache.chemistry.opencmis.client.runtime.util.AbstractPageFetcher;
59 import org.apache.chemistry.opencmis.client.runtime.util.CollectionIterable;
60 import org.apache.chemistry.opencmis.client.runtime.util.TreeImpl;
61 import org.apache.chemistry.opencmis.client.util.OperationContextUtils;
62 import org.apache.chemistry.opencmis.commons.PropertyIds;
63 import org.apache.chemistry.opencmis.commons.SessionParameter;
64 import org.apache.chemistry.opencmis.commons.SessionParameterDefaults;
65 import org.apache.chemistry.opencmis.commons.data.Ace;
66 import org.apache.chemistry.opencmis.commons.data.Acl;
67 import org.apache.chemistry.opencmis.commons.data.BulkUpdateObjectIdAndChangeToken;
68 import org.apache.chemistry.opencmis.commons.data.ContentStream;
69 import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
70 import org.apache.chemistry.opencmis.commons.data.ObjectData;
71 import org.apache.chemistry.opencmis.commons.data.ObjectList;
72 import org.apache.chemistry.opencmis.commons.data.PropertyData;
73 import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
74 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
75 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
76 import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList;
77 import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
78 import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
79 import org.apache.chemistry.opencmis.commons.enums.BindingType;
80 import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
81 import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
82 import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection;
83 import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
84 import org.apache.chemistry.opencmis.commons.enums.Updatability;
85 import org.apache.chemistry.opencmis.commons.enums.VersioningState;
86 import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
87 import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException;
88 import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
89 import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
90 import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
91 import org.apache.chemistry.opencmis.commons.impl.ClassLoaderUtil;
92 import org.apache.chemistry.opencmis.commons.impl.Constants;
93 import org.apache.chemistry.opencmis.commons.impl.dataobjects.BulkUpdateObjectIdAndChangeTokenImpl;
94 import org.apache.chemistry.opencmis.commons.spi.AclService;
95 import org.apache.chemistry.opencmis.commons.spi.AuthenticationProvider;
96 import org.apache.chemistry.opencmis.commons.spi.CmisBinding;
97 import org.apache.chemistry.opencmis.commons.spi.DiscoveryService;
98 import org.apache.chemistry.opencmis.commons.spi.ExtendedAclService;
99 import org.apache.chemistry.opencmis.commons.spi.ExtendedHolder;
100 import org.apache.chemistry.opencmis.commons.spi.ExtendedRepositoryService;
101 import org.apache.chemistry.opencmis.commons.spi.Holder;
102 import org.apache.chemistry.opencmis.commons.spi.NavigationService;
103 import org.apache.chemistry.opencmis.commons.spi.RelationshipService;
104 import org.apache.chemistry.opencmis.commons.spi.RepositoryService;
105
106
107
108
109 public class SessionImpl implements Session {
110
111 private static final OperationContext DEFAULT_CONTEXT = new OperationContextImpl(null, false, true, false,
112 IncludeRelationships.NONE, null, true, null, true, 100);
113
114 private static final Set<Updatability> CREATE_UPDATABILITY = EnumSet.noneOf(Updatability.class);
115 private static final Set<Updatability> CREATE_AND_CHECKOUT_UPDATABILITY = EnumSet.noneOf(Updatability.class);
116
117 static {
118 CREATE_UPDATABILITY.add(Updatability.ONCREATE);
119 CREATE_UPDATABILITY.add(Updatability.READWRITE);
120 CREATE_AND_CHECKOUT_UPDATABILITY.add(Updatability.ONCREATE);
121 CREATE_AND_CHECKOUT_UPDATABILITY.add(Updatability.READWRITE);
122 CREATE_AND_CHECKOUT_UPDATABILITY.add(Updatability.WHENCHECKEDOUT);
123 }
124
125
126 private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
127 private transient LinkedHashMap<String, ObjectType> objectTypeCache;
128
129
130
131
132 private OperationContext defaultContext = DEFAULT_CONTEXT;
133
134
135
136
137 private Map<String, String> parameters;
138
139
140
141
142 private CmisBinding binding;
143
144
145
146
147 private Locale locale;
148
149
150
151
152 private final ObjectFactory objectFactory;
153
154
155
156
157 private final AuthenticationProvider authenticationProvider;
158
159
160
161
162 private Cache cache;
163 private final boolean cachePathOmit;
164
165
166
167
168 private TypeDefinitionCache typeDefCache;
169
170
171
172
173 private RepositoryInfo repositoryInfo;
174
175
176
177
178 private static final long serialVersionUID = 1L;
179
180
181
182
183 public SessionImpl(Map<String, String> parameters, ObjectFactory objectFactory,
184 AuthenticationProvider authenticationProvider, Cache cache, TypeDefinitionCache typeDefCache) {
185 if (parameters == null) {
186 throw new IllegalArgumentException("No parameters provided!");
187 }
188
189 this.parameters = parameters;
190 this.locale = determineLocale(parameters);
191
192 this.objectFactory = objectFactory == null ? createObjectFactory() : objectFactory;
193 this.authenticationProvider = authenticationProvider;
194 this.cache = cache == null ? createCache() : cache;
195 this.typeDefCache = typeDefCache;
196
197 cachePathOmit = Boolean.parseBoolean(parameters.get(SessionParameter.CACHE_PATH_OMIT));
198 }
199
200 private Locale determineLocale(Map<String, String> parameters) {
201 Locale result;
202
203 String language = parameters.get(SessionParameter.LOCALE_ISO639_LANGUAGE);
204 String country = parameters.get(SessionParameter.LOCALE_ISO3166_COUNTRY);
205 String variant = parameters.get(SessionParameter.LOCALE_VARIANT);
206
207 if (variant != null) {
208
209 result = new Locale(language, country, variant);
210 } else if (country != null) {
211
212 result = new Locale(language, country);
213 } else if (language != null) {
214
215 result = new Locale(language);
216 } else {
217 result = Locale.getDefault();
218 }
219
220 return result;
221 }
222
223 private ObjectFactory createObjectFactory() {
224 try {
225 String classname = parameters.get(SessionParameter.OBJECT_FACTORY_CLASS);
226
227 Class<?> objectFactoryClass;
228 if (classname == null) {
229 objectFactoryClass = ObjectFactoryImpl.class;
230 } else {
231 objectFactoryClass = ClassLoaderUtil.loadClass(classname);
232 }
233
234 Object of = objectFactoryClass.newInstance();
235 if (!(of instanceof ObjectFactory)) {
236 throw new InstantiationException("Class does not implement ObjectFactory!");
237 }
238
239 ((ObjectFactory) of).initialize(this, parameters);
240
241 return (ObjectFactory) of;
242 } catch (Exception e) {
243 throw new IllegalArgumentException("Unable to create object factory: " + e, e);
244 }
245 }
246
247 private Cache createCache() {
248 try {
249 String classname = parameters.get(SessionParameter.CACHE_CLASS);
250
251 Class<?> cacheClass;
252 if (classname == null) {
253 cacheClass = CacheImpl.class;
254 } else {
255 cacheClass = ClassLoaderUtil.loadClass(classname);
256 }
257
258 Object of = cacheClass.newInstance();
259 if (!(of instanceof Cache)) {
260 throw new InstantiationException("Class does not implement Cache!");
261 }
262
263 ((Cache) of).initialize(this, parameters);
264
265 return (Cache) of;
266 } catch (Exception e) {
267 throw new IllegalArgumentException("Unable to create cache: " + e, e);
268 }
269 }
270
271 @Override
272 public Map<String, String> getSessionParameters() {
273 return Collections.unmodifiableMap(parameters);
274 }
275
276 @Override
277 public void clear() {
278 lock.writeLock().lock();
279 try {
280
281 cache = createCache();
282
283
284 objectTypeCache = null;
285
286
287 getBinding().clearAllCaches();
288 } finally {
289 lock.writeLock().unlock();
290 }
291 }
292
293 @Override
294 public ObjectFactory getObjectFactory() {
295 assert objectFactory != null;
296 return objectFactory;
297 }
298
299 @Override
300 public ItemIterable<Document> getCheckedOutDocs() {
301 return getCheckedOutDocs(getDefaultContext());
302 }
303
304 @Override
305 public ItemIterable<Document> getCheckedOutDocs(OperationContext context) {
306 checkContext(context);
307
308 final NavigationService navigationService = getBinding().getNavigationService();
309 final ObjectFactory of = getObjectFactory();
310 final OperationContext ctxt = new OperationContextImpl(context);
311
312 return new CollectionIterable<Document>(new AbstractPageFetcher<Document>(ctxt.getMaxItemsPerPage()) {
313
314 @Override
315 protected AbstractPageFetcher.Page<Document> fetchPage(long skipCount) {
316
317
318 ObjectList checkedOutDocs = navigationService.getCheckedOutDocs(getRepositoryId(), null,
319 ctxt.getFilterString(), ctxt.getOrderBy(), ctxt.isIncludeAllowableActions(),
320 ctxt.getIncludeRelationships(), ctxt.getRenditionFilterString(),
321 BigInteger.valueOf(this.maxNumItems), BigInteger.valueOf(skipCount), null);
322
323
324 List<Document> page = new ArrayList<Document>();
325 if (checkedOutDocs.getObjects() != null) {
326 for (ObjectData objectData : checkedOutDocs.getObjects()) {
327 CmisObject doc = of.convertObject(objectData, ctxt);
328 if (!(doc instanceof Document)) {
329
330 continue;
331 }
332
333 page.add((Document) doc);
334 }
335 }
336
337 return new AbstractPageFetcher.Page<Document>(page, checkedOutDocs.getNumItems(),
338 checkedOutDocs.hasMoreItems());
339 }
340 });
341 }
342
343 @Override
344 public ChangeEvents getContentChanges(String changeLogToken, boolean includeProperties, long maxNumItems) {
345 return getContentChanges(changeLogToken, includeProperties, maxNumItems, getDefaultContext());
346 }
347
348 @Override
349 public ChangeEvents getContentChanges(String changeLogToken, boolean includeProperties, long maxNumItems,
350 OperationContext context) {
351 checkContext(context);
352
353 Holder<String> changeLogTokenHolder = new Holder<String>(changeLogToken);
354 ObjectList objectList = null;
355
356 lock.readLock().lock();
357 try {
358 objectList = getBinding().getDiscoveryService().getContentChanges(getRepositoryInfo().getId(),
359 changeLogTokenHolder, includeProperties, context.getFilterString(), context.isIncludePolicies(),
360 context.isIncludeAcls(), BigInteger.valueOf(maxNumItems), null);
361 } finally {
362 lock.readLock().unlock();
363 }
364
365 return objectFactory.convertChangeEvents(changeLogTokenHolder.getValue(), objectList);
366 }
367
368 @Override
369 public ItemIterable<ChangeEvent> getContentChanges(String changeLogToken, final boolean includeProperties) {
370 return getContentChanges(changeLogToken, includeProperties, getDefaultContext());
371 }
372
373 @Override
374 public ItemIterable<ChangeEvent> getContentChanges(final String changeLogToken, final boolean includeProperties,
375 OperationContext context) {
376 checkContext(context);
377
378 final DiscoveryService discoveryService = getBinding().getDiscoveryService();
379 final ObjectFactory of = getObjectFactory();
380 final OperationContext ctxt = new OperationContextImpl(context);
381
382 return new CollectionIterable<ChangeEvent>(new AbstractPageFetcher<ChangeEvent>(Integer.MAX_VALUE) {
383
384 private String token = changeLogToken;
385 private String nextLink = null;
386 private boolean firstPage = true;
387
388 @Override
389 protected AbstractPageFetcher.Page<ChangeEvent> fetchPage(long skipCount) {
390 assert firstPage || token != null ? (nextLink == null) : true;
391
392
393 ExtendedHolder<String> changeLogTokenHolder = new ExtendedHolder<String>(token);
394 if (nextLink != null) {
395 changeLogTokenHolder.setExtraValue(Constants.REP_REL_CHANGES, nextLink);
396 }
397
398 ObjectList objectList = discoveryService.getContentChanges(getRepositoryInfo().getId(),
399 changeLogTokenHolder, includeProperties, ctxt.getFilterString(), ctxt.isIncludePolicies(),
400 ctxt.isIncludeAcls(), BigInteger.valueOf(this.maxNumItems), null);
401
402
403 List<ChangeEvent> page = new ArrayList<ChangeEvent>();
404 for (ObjectData objectData : objectList.getObjects()) {
405 page.add(of.convertChangeEvent(objectData));
406 }
407
408 if (!firstPage) {
409
410
411 page.remove(0);
412 }
413 firstPage = false;
414
415 if (changeLogTokenHolder.getValue() != null) {
416
417
418 token = changeLogTokenHolder.getValue();
419 } else {
420
421
422 token = null;
423 nextLink = (String) changeLogTokenHolder.getExtraValue(Constants.REP_REL_CHANGES);
424 }
425
426 return new AbstractPageFetcher.Page<ChangeEvent>(page, objectList.getNumItems(),
427 objectList.hasMoreItems()) {
428 };
429 }
430 }) {
431
432 @Override
433 public ItemIterable<ChangeEvent> skipTo(long position) {
434 throw new CmisNotSupportedException("Skipping not supported!");
435 }
436
437 @Override
438 public ItemIterable<ChangeEvent> getPage() {
439 throw new CmisNotSupportedException("Paging not supported!");
440 }
441
442 @Override
443 public ItemIterable<ChangeEvent> getPage(int maxNumItems) {
444 throw new CmisNotSupportedException("Paging not supported!");
445 }
446
447 };
448 }
449
450 @Override
451 public String getLatestChangeLogToken() {
452 return getBinding().getRepositoryService().getRepositoryInfo(getRepositoryId(), null).getLatestChangeLogToken();
453 }
454
455 @Override
456 public OperationContext getDefaultContext() {
457 lock.readLock().lock();
458 try {
459 return defaultContext;
460 } finally {
461 lock.readLock().unlock();
462 }
463 }
464
465 @Override
466 public void setDefaultContext(OperationContext context) {
467 lock.writeLock().lock();
468 try {
469 this.defaultContext = context == null ? DEFAULT_CONTEXT : context;
470 } finally {
471 lock.writeLock().unlock();
472 }
473 }
474
475 @Override
476 public OperationContext createOperationContext(Set<String> filter, boolean includeAcls,
477 boolean includeAllowableActions, boolean includePolicies, IncludeRelationships includeRelationships,
478 Set<String> renditionFilter, boolean includePathSegments, String orderBy, boolean cacheEnabled,
479 int maxItemsPerPage) {
480 return OperationContextUtils.createOperationContext(filter, includeAcls, includeAllowableActions,
481 includePolicies, includeRelationships, renditionFilter, includePathSegments, orderBy, cacheEnabled,
482 maxItemsPerPage);
483 }
484
485 @Override
486 public OperationContext createOperationContext() {
487 return OperationContextUtils.createOperationContext();
488 }
489
490 @Override
491 public ObjectId createObjectId(String id) {
492 return new ObjectIdImpl(id);
493 }
494
495 @Override
496 public Locale getLocale() {
497 return locale;
498 }
499
500 @Override
501 public CmisObject getObject(ObjectId objectId) {
502 return getObject(objectId, getDefaultContext());
503 }
504
505 @Override
506 public CmisObject getObject(ObjectId objectId, OperationContext context) {
507 checkObjectId(objectId);
508 return getObject(objectId.getId(), context);
509 }
510
511 @Override
512 public CmisObject getObject(String objectId) {
513 return getObject(objectId, getDefaultContext());
514 }
515
516 @Override
517 public CmisObject getObject(String objectId, OperationContext context) {
518 checkObjectId(objectId);
519 checkContext(context);
520
521 CmisObject result = null;
522
523
524 if (context.isCacheEnabled()) {
525 result = cache.getById(objectId, context.getCacheKey());
526 if (result != null) {
527 return result;
528 }
529 }
530
531
532 ObjectData objectData = binding.getObjectService().getObject(getRepositoryId(), objectId,
533 context.getFilterString(), context.isIncludeAllowableActions(), context.getIncludeRelationships(),
534 context.getRenditionFilterString(), context.isIncludePolicies(), context.isIncludeAcls(), null);
535
536 result = getObjectFactory().convertObject(objectData, context);
537
538
539 if (context.isCacheEnabled()) {
540 cache.put(result, context.getCacheKey());
541 }
542
543 return result;
544 }
545
546 @Override
547 public CmisObject getObjectByPath(String path) {
548 return getObjectByPath(path, getDefaultContext());
549 }
550
551 @Override
552 public CmisObject getObjectByPath(String path, OperationContext context) {
553 checkPath(path);
554 checkContext(context);
555
556 CmisObject result = null;
557
558
559 if (context.isCacheEnabled() && !cachePathOmit) {
560 result = cache.getByPath(path, context.getCacheKey());
561 if (result != null) {
562 return result;
563 }
564 }
565
566
567 ObjectData objectData = binding.getObjectService().getObjectByPath(getRepositoryId(), path,
568 context.getFilterString(), context.isIncludeAllowableActions(), context.getIncludeRelationships(),
569 context.getRenditionFilterString(), context.isIncludePolicies(), context.isIncludeAcls(), null);
570
571 result = getObjectFactory().convertObject(objectData, context);
572
573
574 if (context.isCacheEnabled()) {
575 cache.putPath(path, result, context.getCacheKey());
576 }
577
578 return result;
579 }
580
581 @Override
582 public CmisObject getObjectByPath(String parentPath, String name) {
583 return getObjectByPath(parentPath, name, getDefaultContext());
584 }
585
586 @Override
587 public CmisObject getObjectByPath(String parentPath, String name, OperationContext context) {
588 return getObjectByPath(buildPath(parentPath, name), context);
589 }
590
591 @Override
592 public Document getLatestDocumentVersion(ObjectId objectId) {
593 return getLatestDocumentVersion(objectId, false, getDefaultContext());
594 }
595
596 @Override
597 public Document getLatestDocumentVersion(String objectId, OperationContext context) {
598 checkDocumentId(objectId);
599 return getLatestDocumentVersion(createObjectId(objectId), false, context);
600 }
601
602 @Override
603 public Document getLatestDocumentVersion(String objectId, boolean major, OperationContext context) {
604 checkDocumentId(objectId);
605 return getLatestDocumentVersion(createObjectId(objectId), major, context);
606 }
607
608 @Override
609 public Document getLatestDocumentVersion(String objectId) {
610 checkDocumentId(objectId);
611 return getLatestDocumentVersion(createObjectId(objectId), false, getDefaultContext());
612 }
613
614 @Override
615 public Document getLatestDocumentVersion(ObjectId objectId, OperationContext context) {
616 return getLatestDocumentVersion(objectId, false, context);
617 }
618
619 @Override
620 public Document getLatestDocumentVersion(ObjectId objectId, boolean major, OperationContext context) {
621 checkDocumentId(objectId);
622 checkContext(context);
623
624 CmisObject result = null;
625
626 String versionSeriesId = null;
627
628
629
630 if (objectId instanceof Document) {
631 Document sourceDoc = (Document) objectId;
632
633 if (!sourceDoc.isVersionable()) {
634
635 return (Document) getObject(sourceDoc, context);
636 }
637
638 versionSeriesId = sourceDoc.getVersionSeriesId();
639 }
640
641
642
643 if (versionSeriesId == null) {
644 if (context.isCacheEnabled()) {
645 CmisObject sourceObj = cache.getById(objectId.getId(), context.getCacheKey());
646 if (sourceObj instanceof Document) {
647 Document sourceDoc = (Document) sourceObj;
648
649 if (!sourceDoc.isVersionable()) {
650
651 return (Document) getObject(sourceDoc, context);
652 }
653
654 versionSeriesId = sourceDoc.getVersionSeriesId();
655 }
656 }
657 }
658
659
660
661
662
663 if (versionSeriesId == null) {
664 BindingType bindingType = getBinding().getBindingType();
665 if (bindingType == BindingType.WEBSERVICES || bindingType == BindingType.CUSTOM) {
666
667
668 ObjectData sourceObjectData = binding.getObjectService().getObject(getRepositoryId(), objectId.getId(),
669 PropertyIds.OBJECT_ID + "," + PropertyIds.OBJECT_TYPE_ID + "," + PropertyIds.VERSION_SERIES_ID,
670 false, IncludeRelationships.NONE, "cmis:none", false, false, null);
671
672 String objectTypeId = null;
673
674 if (sourceObjectData.getProperties() != null
675 && sourceObjectData.getProperties().getProperties() != null) {
676
677 PropertyData<?> objectTypeIdProp = sourceObjectData.getProperties().getProperties()
678 .get(PropertyIds.OBJECT_TYPE_ID);
679 if (objectTypeIdProp != null && objectTypeIdProp.getFirstValue() instanceof String) {
680 objectTypeId = (String) objectTypeIdProp.getFirstValue();
681 }
682
683 PropertyData<?> verionsSeriesIdProp = sourceObjectData.getProperties().getProperties()
684 .get(PropertyIds.VERSION_SERIES_ID);
685 if (verionsSeriesIdProp != null && verionsSeriesIdProp.getFirstValue() instanceof String) {
686 versionSeriesId = (String) verionsSeriesIdProp.getFirstValue();
687 }
688 }
689
690
691 if (versionSeriesId == null) {
692
693 ObjectType type = getTypeDefinition(objectTypeId);
694 if (type instanceof DocumentType && Boolean.FALSE.equals(((DocumentType) type).isVersionable())) {
695
696
697 return (Document) getObject(objectId, context);
698 }
699
700 throw new IllegalArgumentException("Object is not a document or not versionable!");
701 }
702 }
703 }
704
705
706 ObjectData objectData = binding.getVersioningService().getObjectOfLatestVersion(getRepositoryId(),
707 objectId.getId(), versionSeriesId, major, context.getFilterString(),
708 context.isIncludeAllowableActions(), context.getIncludeRelationships(),
709 context.getRenditionFilterString(), context.isIncludePolicies(), context.isIncludeAcls(), null);
710
711 result = getObjectFactory().convertObject(objectData, context);
712
713
714 if (context.isCacheEnabled()) {
715 cache.put(result, context.getCacheKey());
716 }
717
718
719 if (!(result instanceof Document)) {
720 throw new IllegalArgumentException("Latest version is not a document!");
721 }
722
723 return (Document) result;
724 }
725
726 @Override
727 public boolean exists(ObjectId objectId) {
728 checkObjectId(objectId);
729 return exists(objectId.getId());
730 }
731
732 @Override
733 public boolean exists(String objectId) {
734 checkObjectId(objectId);
735
736 try {
737 binding.getObjectService().getObject(getRepositoryId(), objectId, "cmis:objectId", Boolean.FALSE,
738 IncludeRelationships.NONE, "cmis:none", Boolean.FALSE, Boolean.FALSE, null);
739 return true;
740 } catch (CmisObjectNotFoundException onf) {
741 removeObjectFromCache(objectId);
742 return false;
743 }
744 }
745
746 @Override
747 public boolean existsPath(String path) {
748 checkPath(path);
749
750 try {
751 String objectId = getObjectIdByPath(path);
752 String cacheObjectId = cache.getObjectIdByPath(path);
753
754 if (cacheObjectId != null && !cacheObjectId.equals(objectId)) {
755 cache.removePath(path);
756 }
757
758 return true;
759 } catch (CmisObjectNotFoundException onf) {
760 return false;
761 }
762 }
763
764 @Override
765 public boolean existsPath(String parentPath, String name) {
766 return existsPath(buildPath(parentPath, name));
767 }
768
769 @Override
770 public void removeObjectFromCache(ObjectId objectId) {
771 checkObjectId(objectId);
772 removeObjectFromCache(objectId.getId());
773 }
774
775 @Override
776 public void removeObjectFromCache(String objectId) {
777 cache.remove(objectId);
778 }
779
780 @Override
781 public RepositoryInfo getRepositoryInfo() {
782 lock.readLock().lock();
783 try {
784 return repositoryInfo;
785 } finally {
786 lock.readLock().unlock();
787 }
788 }
789
790 @Override
791 public Folder getRootFolder() {
792 return getRootFolder(getDefaultContext());
793 }
794
795 @Override
796 public Folder getRootFolder(OperationContext context) {
797 String rootFolderId = getRepositoryInfo().getRootFolderId();
798
799 CmisObject rootFolder = getObject(rootFolderId, context);
800 if (!(rootFolder instanceof Folder)) {
801 throw new CmisRuntimeException("Root folder object is not a folder!");
802 }
803
804 return (Folder) rootFolder;
805 }
806
807 @Override
808 public ItemIterable<ObjectType> getTypeChildren(final String typeId, final boolean includePropertyDefinitions) {
809 final RepositoryService repositoryService = getBinding().getRepositoryService();
810
811 return new CollectionIterable<ObjectType>(
812 new AbstractPageFetcher<ObjectType>(getDefaultContext().getMaxItemsPerPage()) {
813
814 @Override
815 protected AbstractPageFetcher.Page<ObjectType> fetchPage(long skipCount) {
816
817
818 TypeDefinitionList tdl = repositoryService.getTypeChildren(SessionImpl.this.getRepositoryId(),
819 typeId, includePropertyDefinitions, BigInteger.valueOf(this.maxNumItems),
820 BigInteger.valueOf(skipCount), null);
821
822
823 List<ObjectType> page = new ArrayList<ObjectType>(tdl.getList().size());
824 for (TypeDefinition typeDefinition : tdl.getList()) {
825 page.add(convertTypeDefinition(typeDefinition));
826 }
827
828 return new AbstractPageFetcher.Page<ObjectType>(page, tdl.getNumItems(), tdl.hasMoreItems()) {
829 };
830 }
831 });
832 }
833
834 @Override
835 public ObjectType getTypeDefinition(String typeId) {
836 TypeDefinition typeDefinition = getBinding().getRepositoryService().getTypeDefinition(getRepositoryId(), typeId,
837 null);
838
839 return convertAndCacheTypeDefinition(typeDefinition, true);
840 }
841
842 @Override
843 public ObjectType getTypeDefinition(String typeId, boolean useCache) {
844 RepositoryService service = getBinding().getRepositoryService();
845 if (!(service instanceof ExtendedRepositoryService)) {
846 throw new CmisRuntimeException(
847 "Internal error: Repository Service does not implement ExtendedRepositoryService!");
848 }
849
850 ExtendedRepositoryService extRepSrv = (ExtendedRepositoryService) service;
851 TypeDefinition typeDefinition = extRepSrv.getTypeDefinition(getRepositoryId(), typeId, null, useCache);
852
853 return convertAndCacheTypeDefinition(typeDefinition, useCache);
854 }
855
856 @Override
857 public List<Tree<ObjectType>> getTypeDescendants(String typeId, int depth, boolean includePropertyDefinitions) {
858 List<TypeDefinitionContainer> descendants = getBinding().getRepositoryService().getTypeDescendants(
859 getRepositoryId(), typeId, BigInteger.valueOf(depth), includePropertyDefinitions, null);
860
861 return convertTypeDescendants(descendants);
862 }
863
864
865
866
867
868 private List<Tree<ObjectType>> convertTypeDescendants(List<TypeDefinitionContainer> descendantsList) {
869 List<Tree<ObjectType>> result = new ArrayList<Tree<ObjectType>>();
870
871 for (TypeDefinitionContainer container : descendantsList) {
872 ObjectType objectType = convertTypeDefinition(container.getTypeDefinition());
873 List<Tree<ObjectType>> children = convertTypeDescendants(container.getChildren());
874
875 result.add(new TreeImpl<ObjectType>(objectType, children));
876 }
877
878 return result;
879 }
880
881 private ObjectType convertTypeDefinition(TypeDefinition typeDefinition) {
882 return objectFactory.convertTypeDefinition(typeDefinition);
883 }
884
885
886
887
888
889
890
891
892
893
894 private ObjectType convertAndCacheTypeDefinition(TypeDefinition typeDefinition, boolean useCache) {
895 ObjectType result = null;
896
897 lock.writeLock().lock();
898 try {
899 if (objectTypeCache == null) {
900 int cacheSize;
901 try {
902 cacheSize = Integer.valueOf(parameters.get(SessionParameter.CACHE_SIZE_TYPES));
903 if (cacheSize < 0) {
904 cacheSize = SessionParameterDefaults.CACHE_SIZE_TYPES;
905 }
906 } catch (NumberFormatException nfe) {
907 cacheSize = SessionParameterDefaults.CACHE_SIZE_TYPES;
908 }
909
910 final int maxEntries = cacheSize;
911
912 objectTypeCache = new LinkedHashMap<String, ObjectType>(maxEntries + 1, 0.70f, true) {
913 private static final long serialVersionUID = 1L;
914
915 @Override
916 public boolean removeEldestEntry(Map.Entry<String, ObjectType> eldest) {
917 return size() > maxEntries;
918 }
919 };
920 }
921
922 if (!useCache) {
923 result = objectFactory.convertTypeDefinition(typeDefinition);
924 objectTypeCache.put(result.getId(), result);
925 } else {
926 result = objectTypeCache.get(typeDefinition.getId());
927 if (result == null) {
928 result = objectFactory.convertTypeDefinition(typeDefinition);
929 objectTypeCache.put(result.getId(), result);
930 }
931 }
932
933 return result;
934 } finally {
935 lock.writeLock().unlock();
936 }
937 }
938
939
940
941
942 private void removeFromObjectTypeCache(String typeId) {
943 lock.writeLock().lock();
944 try {
945 if (objectTypeCache != null) {
946 objectTypeCache.remove(typeId);
947 }
948 } finally {
949 lock.writeLock().unlock();
950 }
951 }
952
953 @Override
954 public ObjectType createType(TypeDefinition type) {
955 checkCmisVersion();
956
957 TypeDefinition newType = getBinding().getRepositoryService().createType(getRepositoryId(), type, null);
958 return convertTypeDefinition(newType);
959 }
960
961 @Override
962 public ObjectType updateType(TypeDefinition type) {
963 checkCmisVersion();
964
965 TypeDefinition updatedType = getBinding().getRepositoryService().updateType(getRepositoryId(), type, null);
966
967 removeFromObjectTypeCache(updatedType.getId());
968
969 return convertTypeDefinition(updatedType);
970 }
971
972 @Override
973 public void deleteType(String typeId) {
974 checkCmisVersion();
975
976 getBinding().getRepositoryService().deleteType(getRepositoryId(), typeId, null);
977 removeFromObjectTypeCache(typeId);
978 }
979
980 @Override
981 public ItemIterable<QueryResult> query(final String statement, final boolean searchAllVersions) {
982 return query(statement, searchAllVersions, getDefaultContext());
983 }
984
985 @Override
986 public ItemIterable<QueryResult> query(final String statement, final boolean searchAllVersions,
987 OperationContext context) {
988 checkContext(context);
989
990 final DiscoveryService discoveryService = getBinding().getDiscoveryService();
991 final ObjectFactory of = getObjectFactory();
992 final OperationContext ctxt = new OperationContextImpl(context);
993
994 return new CollectionIterable<QueryResult>(new AbstractPageFetcher<QueryResult>(ctxt.getMaxItemsPerPage()) {
995
996 @Override
997 protected AbstractPageFetcher.Page<QueryResult> fetchPage(long skipCount) {
998
999
1000 ObjectList resultList = discoveryService.query(getRepositoryId(), statement, searchAllVersions,
1001 ctxt.isIncludeAllowableActions(), ctxt.getIncludeRelationships(),
1002 ctxt.getRenditionFilterString(), BigInteger.valueOf(this.maxNumItems),
1003 BigInteger.valueOf(skipCount), null);
1004
1005
1006 List<QueryResult> page = new ArrayList<QueryResult>();
1007 if (resultList.getObjects() != null) {
1008 for (ObjectData objectData : resultList.getObjects()) {
1009 if (objectData == null) {
1010 continue;
1011 }
1012
1013 page.add(of.convertQueryResult(objectData));
1014 }
1015 }
1016
1017 return new AbstractPageFetcher.Page<QueryResult>(page, resultList.getNumItems(),
1018 resultList.hasMoreItems());
1019 }
1020 });
1021 }
1022
1023 @Override
1024 public ItemIterable<CmisObject> queryObjects(String typeId, String where, final boolean searchAllVersions,
1025 OperationContext context) {
1026 if (typeId == null || typeId.trim().length() == 0) {
1027 throw new IllegalArgumentException("Type ID must be set!");
1028 }
1029
1030 checkContext(context);
1031
1032 final DiscoveryService discoveryService = getBinding().getDiscoveryService();
1033 final ObjectFactory of = getObjectFactory();
1034 final OperationContext ctxt = new OperationContextImpl(context);
1035 final StringBuilder statement = new StringBuilder(1024);
1036
1037 statement.append("SELECT ");
1038
1039 String select = ctxt.getFilterString();
1040 if (select == null) {
1041 statement.append('*');
1042 } else {
1043 statement.append(select);
1044 }
1045
1046 final ObjectType type = getTypeDefinition(typeId);
1047 statement.append(" FROM ");
1048 statement.append(type.getQueryName());
1049
1050 if (where != null && where.trim().length() > 0) {
1051 statement.append(" WHERE ");
1052 statement.append(where);
1053 }
1054
1055 String orderBy = ctxt.getOrderBy();
1056 if (orderBy != null && orderBy.trim().length() > 0) {
1057 statement.append(" ORDER BY ");
1058 statement.append(orderBy);
1059 }
1060
1061 return new CollectionIterable<CmisObject>(new AbstractPageFetcher<CmisObject>(ctxt.getMaxItemsPerPage()) {
1062
1063 @Override
1064 protected AbstractPageFetcher.Page<CmisObject> fetchPage(long skipCount) {
1065
1066
1067 ObjectList resultList = discoveryService.query(getRepositoryId(), statement.toString(),
1068 searchAllVersions, ctxt.isIncludeAllowableActions(), ctxt.getIncludeRelationships(),
1069 ctxt.getRenditionFilterString(), BigInteger.valueOf(this.maxNumItems),
1070 BigInteger.valueOf(skipCount), null);
1071
1072
1073 List<CmisObject> page = new ArrayList<CmisObject>();
1074 if (resultList.getObjects() != null) {
1075 for (ObjectData objectData : resultList.getObjects()) {
1076 if (objectData == null) {
1077 continue;
1078 }
1079
1080 page.add(of.convertObject(objectData, ctxt));
1081 }
1082 }
1083
1084 return new AbstractPageFetcher.Page<CmisObject>(page, resultList.getNumItems(),
1085 resultList.hasMoreItems());
1086 }
1087 });
1088 }
1089
1090 @Override
1091 public QueryStatement createQueryStatement(final String statement) {
1092 return new QueryStatementImpl(this, statement);
1093 }
1094
1095 @Override
1096 public QueryStatement createQueryStatement(final Collection<String> selectPropertyIds,
1097 final Map<String, String> fromTypes, final String whereClause, final List<String> orderByPropertyIds) {
1098 return new QueryStatementImpl(this, selectPropertyIds, fromTypes, whereClause, orderByPropertyIds);
1099 }
1100
1101
1102
1103
1104
1105
1106
1107
1108 public void connect() {
1109 lock.writeLock().lock();
1110 try {
1111 binding = CmisBindingHelper.createBinding(parameters, authenticationProvider, typeDefCache);
1112
1113
1114 String repositoryId = parameters.get(SessionParameter.REPOSITORY_ID);
1115 if (repositoryId == null) {
1116 throw new IllegalStateException("Repository ID is not set!");
1117 }
1118
1119 repositoryInfo = objectFactory
1120 .convertRepositoryInfo(getBinding().getRepositoryService().getRepositoryInfo(repositoryId, null));
1121 } finally {
1122 lock.writeLock().unlock();
1123 }
1124 }
1125
1126 @Override
1127 public CmisBinding getBinding() {
1128 lock.readLock().lock();
1129 try {
1130 return binding;
1131 } finally {
1132 lock.readLock().unlock();
1133 }
1134 }
1135
1136 public Cache getCache() {
1137 lock.readLock().lock();
1138 try {
1139 return cache;
1140 } finally {
1141 lock.readLock().unlock();
1142 }
1143 }
1144
1145
1146
1147
1148 public String getRepositoryId() {
1149 return getRepositoryInfo().getId();
1150 }
1151
1152
1153 @Override
1154 public ObjectId createDocument(Map<String, ?> properties, ObjectId folderId, ContentStream contentStream,
1155 VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces) {
1156 checkProperties(properties);
1157
1158 String newId = getBinding().getObjectService().createDocument(getRepositoryId(),
1159 objectFactory.convertProperties(properties, null, null, CREATE_AND_CHECKOUT_UPDATABILITY),
1160 (folderId == null ? null : folderId.getId()), objectFactory.convertContentStream(contentStream),
1161 versioningState, objectFactory.convertPolicies(policies), objectFactory.convertAces(addAces),
1162 objectFactory.convertAces(removeAces), null);
1163
1164 if (newId == null) {
1165 return null;
1166 }
1167
1168 return createObjectId(newId);
1169 }
1170
1171 @Override
1172 public ObjectId createDocumentFromSource(ObjectId source, Map<String, ?> properties, ObjectId folderId,
1173 VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces) {
1174 if (source == null || source.getId() == null) {
1175 throw new IllegalArgumentException("Source must be set!");
1176 }
1177
1178
1179 ObjectType type = null;
1180 List<SecondaryType> secondaryTypes = null;
1181 if (source instanceof CmisObject) {
1182 type = ((CmisObject) source).getType();
1183 secondaryTypes = ((CmisObject) source).getSecondaryTypes();
1184 } else {
1185 CmisObject sourceObj = getObject(source);
1186 type = sourceObj.getType();
1187 secondaryTypes = sourceObj.getSecondaryTypes();
1188 }
1189
1190 if (type.getBaseTypeId() != BaseTypeId.CMIS_DOCUMENT) {
1191 throw new IllegalArgumentException("Source object must be a document!");
1192 }
1193
1194 String newId = getBinding().getObjectService().createDocumentFromSource(getRepositoryId(), source.getId(),
1195 objectFactory.convertProperties(properties, type, secondaryTypes, CREATE_AND_CHECKOUT_UPDATABILITY),
1196 (folderId == null ? null : folderId.getId()), versioningState, objectFactory.convertPolicies(policies),
1197 objectFactory.convertAces(addAces), objectFactory.convertAces(removeAces), null);
1198
1199 if (newId == null) {
1200 return null;
1201 }
1202
1203 return createObjectId(newId);
1204 }
1205
1206 @Override
1207 public ObjectId createFolder(Map<String, ?> properties, ObjectId folderId, List<Policy> policies, List<Ace> addAces,
1208 List<Ace> removeAces) {
1209 checkFolderId(folderId);
1210 checkProperties(properties);
1211
1212 String newId = getBinding().getObjectService().createFolder(getRepositoryId(),
1213 objectFactory.convertProperties(properties, null, null, CREATE_UPDATABILITY), folderId.getId(),
1214 objectFactory.convertPolicies(policies), objectFactory.convertAces(addAces),
1215 objectFactory.convertAces(removeAces), null);
1216
1217 if (newId == null) {
1218 return null;
1219 }
1220
1221 return createObjectId(newId);
1222 }
1223
1224 @Override
1225 public ObjectId createPath(String newPath, String typeId) {
1226 return createPath(null, newPath, typeId);
1227 }
1228
1229 @Override
1230 public ObjectId createPath(ObjectId startFolderId, String newPath, String typeId) {
1231 Map<String, Object> properties = new HashMap<String, Object>();
1232 properties.put(PropertyIds.OBJECT_TYPE_ID, typeId);
1233
1234 return createPath(startFolderId, newPath, properties, null, null, null);
1235 }
1236
1237 @Override
1238 public ObjectId createPath(String newPath, Map<String, ?> properties) {
1239 return createPath(null, newPath, properties);
1240 }
1241
1242 @Override
1243 public ObjectId createPath(ObjectId startFolderId, String newPath, Map<String, ?> properties) {
1244 return createPath(startFolderId, newPath, properties, null, null, null);
1245 }
1246
1247 @Override
1248 public ObjectId createPath(ObjectId startFolderId, String newPath, Map<String, ?> properties, List<Policy> policies,
1249 List<Ace> addAces, List<Ace> removeAces) {
1250 checkPath(newPath);
1251 if (newPath.length() == 1) {
1252 throw new IllegalArgumentException("Cannot create root folder!");
1253 }
1254 if (newPath.charAt(newPath.length() - 1) == '/') {
1255 throw new IllegalArgumentException("Path cannot end with a '/'!");
1256 }
1257
1258 checkProperties(properties);
1259 if (!(properties.get(PropertyIds.OBJECT_TYPE_ID) instanceof String)) {
1260 throw new IllegalArgumentException("Property '" + PropertyIds.OBJECT_TYPE_ID + "' not set or invalid!");
1261 }
1262
1263 StringBuilder nextPath = new StringBuilder(newPath.length());
1264 String[] segements;
1265 ObjectId lastFolderId = null;
1266 boolean create = false;
1267
1268
1269 if (startFolderId != null && startFolderId.getId() != null) {
1270 if (startFolderId instanceof Folder) {
1271 Folder startFolder = (Folder) startFolderId;
1272 if (!startFolder.isRootFolder()) {
1273 nextPath.append(startFolder.getPath());
1274 lastFolderId = startFolder;
1275 }
1276 } else {
1277 ObjectData startFolderData = null;
1278 try {
1279 startFolderData = getBinding().getObjectService().getObject(getRepositoryId(),
1280 startFolderId.getId(), "cmis:objectId,cmis:baseTypeId,cmis:name,cmis:path", false,
1281 IncludeRelationships.NONE, "cmis:none", false, false, null);
1282 } catch (CmisBaseException cbe) {
1283 throw new IllegalArgumentException("Start folder does not exist or is not accessible!", cbe);
1284 }
1285
1286 if (startFolderData.getBaseTypeId() != BaseTypeId.CMIS_FOLDER) {
1287 throw new IllegalArgumentException("Start folder is not a folder!");
1288 }
1289
1290 if (startFolderData.getProperties() == null || startFolderData.getProperties().getProperties() == null
1291 || startFolderData.getProperties().getProperties().get(PropertyIds.PATH) == null) {
1292 throw new IllegalArgumentException("Start folder has no path property?!");
1293 }
1294
1295 Object startPath = startFolderData.getProperties().getProperties().get(PropertyIds.PATH)
1296 .getFirstValue();
1297 if (!(startPath instanceof String)) {
1298 throw new IllegalArgumentException("Start folder has an invalid path property?!");
1299 }
1300
1301 if (!repositoryInfo.getRootFolderId().equals(startFolderData.getId())) {
1302 nextPath.append(startPath);
1303 lastFolderId = startFolderId;
1304 }
1305 }
1306
1307 if (!newPath.startsWith(nextPath.toString())) {
1308 throw new IllegalArgumentException("Start folder in not in the path!");
1309 }
1310
1311 segements = newPath.substring(nextPath.length()).split("/");
1312 } else {
1313 segements = newPath.split("/");
1314 }
1315
1316
1317 for (int i = 1; i < segements.length; i++) {
1318 if (create) {
1319 lastFolderId = createFolder(buildCreatePathProperties(properties, segements[i]), lastFolderId, policies,
1320 addAces, removeAces);
1321 } else {
1322 try {
1323 nextPath.append('/');
1324 nextPath.append(segements[i]);
1325
1326 ObjectData folderData = getBinding().getObjectService().getObjectByPath(getRepositoryId(),
1327 nextPath.toString(), "cmis:objectId,cmis:baseTypeId,cmis:name", false,
1328 IncludeRelationships.NONE, "cmis:none", false, false, null);
1329 if (folderData.getBaseTypeId() != BaseTypeId.CMIS_FOLDER) {
1330 throw new CmisConstraintException("Cannot create a folder '" + segements[i]
1331 + "' because there is already an object with this name, which is not a folder!");
1332 }
1333
1334 lastFolderId = new ObjectIdImpl(folderData.getId());
1335 } catch (CmisObjectNotFoundException onfe) {
1336 if (lastFolderId == null) {
1337 lastFolderId = new ObjectIdImpl(repositoryInfo.getRootFolderId());
1338 }
1339
1340 lastFolderId = createFolder(buildCreatePathProperties(properties, segements[i]), lastFolderId,
1341 policies, addAces, removeAces);
1342 create = true;
1343 }
1344 }
1345 }
1346
1347 return lastFolderId;
1348 }
1349
1350 private Map<String, ?> buildCreatePathProperties(Map<String, ?> properties, String name) {
1351 Map<String, Object> newProperties = new HashMap<String, Object>(properties);
1352 newProperties.put(PropertyIds.NAME, name);
1353
1354 return newProperties;
1355 }
1356
1357 @Override
1358 public ObjectId createPolicy(Map<String, ?> properties, ObjectId folderId, List<Policy> policies, List<Ace> addAces,
1359 List<Ace> removeAces) {
1360 checkProperties(properties);
1361
1362 String newId = getBinding().getObjectService().createPolicy(getRepositoryId(),
1363 objectFactory.convertProperties(properties, null, null, CREATE_UPDATABILITY),
1364 (folderId == null ? null : folderId.getId()), objectFactory.convertPolicies(policies),
1365 objectFactory.convertAces(addAces), objectFactory.convertAces(removeAces), null);
1366
1367 if (newId == null) {
1368 return null;
1369 }
1370
1371 return createObjectId(newId);
1372 }
1373
1374 @Override
1375 public ObjectId createItem(Map<String, ?> properties, ObjectId folderId, List<Policy> policies, List<Ace> addAces,
1376 List<Ace> removeAces) {
1377 checkProperties(properties);
1378
1379 String newId = getBinding().getObjectService().createItem(getRepositoryId(),
1380 objectFactory.convertProperties(properties, null, null, CREATE_UPDATABILITY),
1381 (folderId == null ? null : folderId.getId()), objectFactory.convertPolicies(policies),
1382 objectFactory.convertAces(addAces), objectFactory.convertAces(removeAces), null);
1383
1384 if (newId == null) {
1385 return null;
1386 }
1387
1388 return createObjectId(newId);
1389 }
1390
1391 @Override
1392 public ObjectId createRelationship(Map<String, ?> properties, List<Policy> policies, List<Ace> addAces,
1393 List<Ace> removeAces) {
1394 checkProperties(properties);
1395
1396 String newId = getBinding().getObjectService().createRelationship(getRepositoryId(),
1397 objectFactory.convertProperties(properties, null, null, CREATE_UPDATABILITY),
1398 objectFactory.convertPolicies(policies), objectFactory.convertAces(addAces),
1399 objectFactory.convertAces(removeAces), null);
1400
1401 if (newId == null) {
1402 return null;
1403 }
1404
1405 return createObjectId(newId);
1406 }
1407
1408 @Override
1409 public ObjectId createDocument(Map<String, ?> properties, ObjectId folderId, ContentStream contentStream,
1410 VersioningState versioningState) {
1411 return createDocument(properties, folderId, contentStream, versioningState, null, null, null);
1412 }
1413
1414 @Override
1415 public ObjectId createDocumentFromSource(ObjectId source, Map<String, ?> properties, ObjectId folderId,
1416 VersioningState versioningState) {
1417 return createDocumentFromSource(source, properties, folderId, versioningState, null, null, null);
1418 }
1419
1420 @Override
1421 public ObjectId createFolder(Map<String, ?> properties, ObjectId folderId) {
1422 return createFolder(properties, folderId, null, null, null);
1423 }
1424
1425 @Override
1426 public ObjectId createPolicy(Map<String, ?> properties, ObjectId folderId) {
1427 return createPolicy(properties, folderId, null, null, null);
1428 }
1429
1430 @Override
1431 public ObjectId createItem(Map<String, ?> properties, ObjectId folderId) {
1432 return createItem(properties, folderId, null, null, null);
1433 }
1434
1435
1436 @Override
1437 public ObjectId createRelationship(Map<String, ?> properties) {
1438 return createRelationship(properties, null, null, null);
1439 }
1440
1441 @Override
1442 public ItemIterable<Relationship> getRelationships(ObjectId objectId, final boolean includeSubRelationshipTypes,
1443 final RelationshipDirection relationshipDirection, ObjectType type, OperationContext context) {
1444 checkObjectId(objectId);
1445 checkContext(context);
1446
1447 final String id = objectId.getId();
1448 final String typeId = type == null ? null : type.getId();
1449 final RelationshipService relationshipService = getBinding().getRelationshipService();
1450 final OperationContext ctxt = new OperationContextImpl(context);
1451
1452 return new CollectionIterable<Relationship>(new AbstractPageFetcher<Relationship>(ctxt.getMaxItemsPerPage()) {
1453
1454 @Override
1455 protected AbstractPageFetcher.Page<Relationship> fetchPage(long skipCount) {
1456
1457
1458 ObjectList relList = relationshipService.getObjectRelationships(getRepositoryId(), id,
1459 includeSubRelationshipTypes, relationshipDirection, typeId, ctxt.getFilterString(),
1460 ctxt.isIncludeAllowableActions(), BigInteger.valueOf(this.maxNumItems),
1461 BigInteger.valueOf(skipCount), null);
1462
1463
1464 List<Relationship> page = new ArrayList<Relationship>();
1465 if (relList.getObjects() != null) {
1466 for (ObjectData rod : relList.getObjects()) {
1467 CmisObject relationship = getObject(rod.getId(), ctxt);
1468 if (!(relationship instanceof Relationship)) {
1469 throw new CmisRuntimeException("Repository returned an object that is not a relationship!");
1470 }
1471
1472 page.add((Relationship) relationship);
1473 }
1474 }
1475
1476 return new AbstractPageFetcher.Page<Relationship>(page, relList.getNumItems(), relList.hasMoreItems());
1477 }
1478 });
1479 }
1480
1481
1482 @Override
1483 public List<BulkUpdateObjectIdAndChangeToken> bulkUpdateProperties(List<CmisObject> objects,
1484 Map<String, ?> properties, List<String> addSecondaryTypeIds, List<String> removeSecondaryTypeIds) {
1485 checkCmisVersion();
1486 checkProperties(properties);
1487
1488 ObjectType objectType = null;
1489 Map<String, SecondaryType> secondaryTypes = new HashMap<String, SecondaryType>();
1490
1491
1492 if (addSecondaryTypeIds != null) {
1493 for (String stid : addSecondaryTypeIds) {
1494 ObjectType secondaryType = getTypeDefinition(stid);
1495
1496 if (!(secondaryType instanceof SecondaryType)) {
1497 throw new IllegalArgumentException(
1498 "Secondary types contains a type that is not a secondary type: " + secondaryType.getId());
1499 }
1500
1501 secondaryTypes.put(secondaryType.getId(), (SecondaryType) secondaryType);
1502 }
1503 }
1504
1505
1506 List<BulkUpdateObjectIdAndChangeToken> objectIdsAndChangeTokens = new ArrayList<BulkUpdateObjectIdAndChangeToken>();
1507 for (CmisObject object : objects) {
1508 if (object == null) {
1509 continue;
1510 }
1511
1512 objectIdsAndChangeTokens
1513 .add(new BulkUpdateObjectIdAndChangeTokenImpl(object.getId(), object.getChangeToken()));
1514
1515 if (objectType == null) {
1516 objectType = object.getType();
1517 }
1518
1519 if (object.getSecondaryTypes() != null) {
1520 for (SecondaryType secondaryType : object.getSecondaryTypes()) {
1521 secondaryTypes.put(secondaryType.getId(), secondaryType);
1522 }
1523 }
1524 }
1525
1526 Set<Updatability> updatebility = EnumSet.noneOf(Updatability.class);
1527 updatebility.add(Updatability.READWRITE);
1528
1529 return getBinding().getObjectService().bulkUpdateProperties(getRepositoryId(), objectIdsAndChangeTokens,
1530 objectFactory.convertProperties(properties, objectType, secondaryTypes.values(), updatebility),
1531 addSecondaryTypeIds, removeSecondaryTypeIds, null);
1532 }
1533
1534
1535 @Override
1536 public void delete(ObjectId objectId) {
1537 delete(objectId, true);
1538 }
1539
1540 @Override
1541 public void delete(ObjectId objectId, boolean allVersions) {
1542 checkObjectId(objectId);
1543
1544 getBinding().getObjectService().deleteObject(getRepositoryId(), objectId.getId(), allVersions, null);
1545 removeObjectFromCache(objectId);
1546 }
1547
1548 @Override
1549 public void deleteByPath(String path) {
1550 deleteByPath(path, true);
1551 }
1552
1553 @Override
1554 public void deleteByPath(String parentPath, String name) {
1555 deleteByPath(buildPath(parentPath, name), true);
1556 }
1557
1558 @Override
1559 public void deleteByPath(String path, boolean allVersions) {
1560 checkPath(path);
1561
1562 delete(new ObjectIdImpl(getObjectIdByPath(path)), allVersions);
1563 }
1564
1565 @Override
1566 public List<String> deleteTree(ObjectId folderId, boolean allVersions, UnfileObject unfile,
1567 boolean continueOnFailure) {
1568 checkFolderId(folderId);
1569
1570 FailedToDeleteData failed = getBinding().getObjectService().deleteTree(getRepositoryId(), folderId.getId(),
1571 allVersions, unfile, continueOnFailure, null);
1572
1573 if (failed == null || isNullOrEmpty(failed.getIds())) {
1574 removeObjectFromCache(folderId);
1575 }
1576
1577 return (failed != null ? failed.getIds() : null);
1578 }
1579
1580 @Override
1581 public List<String> deleteTreebyPath(String parentPath, String name, boolean allVersions, UnfileObject unfile,
1582 boolean continueOnFailure) {
1583 return deleteTreebyPath(buildPath(parentPath, name), allVersions, unfile, continueOnFailure);
1584 }
1585
1586 @Override
1587 public List<String> deleteTreebyPath(String path, boolean allVersions, UnfileObject unfile,
1588 boolean continueOnFailure) {
1589 checkPath(path);
1590
1591 return deleteTree(new ObjectIdImpl(getObjectIdByPath(path)), allVersions, unfile, continueOnFailure);
1592 }
1593
1594
1595 @Override
1596 public ContentStream getContentStream(ObjectId docId) {
1597 return getContentStream(docId, null, null, null);
1598 }
1599
1600 @Override
1601 public ContentStream getContentStream(ObjectId docId, String streamId, BigInteger offset, BigInteger length) {
1602 checkDocumentId(docId);
1603
1604
1605 ContentStream contentStream = null;
1606 try {
1607 contentStream = getBinding().getObjectService().getContentStream(getRepositoryId(), docId.getId(), streamId,
1608 offset, length, null);
1609 } catch (CmisConstraintException e) {
1610
1611 return null;
1612 } catch (CmisObjectNotFoundException onfe) {
1613 removeObjectFromCache(docId.getId());
1614 throw onfe;
1615 }
1616
1617 return contentStream;
1618 }
1619
1620 @Override
1621 public ContentStream getContentStreamByPath(String path) {
1622 return getContentStreamByPath(path, null, null, null);
1623 }
1624
1625 @Override
1626 public ContentStream getContentStreamByPath(String path, String streamId, BigInteger offset, BigInteger length) {
1627 checkPath(path);
1628
1629
1630 boolean fromCache = true;
1631 String objectId = cache.getObjectIdByPath(path);
1632
1633
1634 if (objectId == null) {
1635 fromCache = false;
1636 objectId = getObjectIdByPath(path);
1637
1638
1639
1640
1641 }
1642
1643
1644 ContentStream contentStream = null;
1645 try {
1646 contentStream = getBinding().getObjectService().getContentStream(getRepositoryId(), objectId, streamId,
1647 offset, length, null);
1648 } catch (CmisConstraintException ce) {
1649
1650 return null;
1651 } catch (CmisObjectNotFoundException onfe) {
1652 if (fromCache) {
1653 removeObjectFromCache(objectId);
1654 cache.removePath(path);
1655 } else {
1656 throw onfe;
1657 }
1658 }
1659
1660 if (contentStream == null) {
1661
1662
1663
1664
1665 contentStream = getBinding().getObjectService().getContentStream(getRepositoryId(), getObjectIdByPath(path),
1666 streamId, offset, length, null);
1667 }
1668
1669 return contentStream;
1670 }
1671
1672
1673 @Override
1674 public Acl getAcl(ObjectId objectId, boolean onlyBasicPermissions) {
1675 checkObjectId(objectId);
1676 return getBinding().getAclService().getAcl(getRepositoryId(), objectId.getId(), onlyBasicPermissions, null);
1677 }
1678
1679 @Override
1680 public Acl applyAcl(ObjectId objectId, List<Ace> addAces, List<Ace> removeAces, AclPropagation aclPropagation) {
1681 checkObjectId(objectId);
1682
1683 ObjectFactory of = getObjectFactory();
1684
1685 return getBinding().getAclService().applyAcl(getRepositoryId(), objectId.getId(), of.convertAces(addAces),
1686 of.convertAces(removeAces), aclPropagation, null);
1687 }
1688
1689 @Override
1690 public Acl setAcl(ObjectId objectId, List<Ace> aces) {
1691 checkObjectId(objectId);
1692
1693 if (aces == null) {
1694 aces = Collections.emptyList();
1695 }
1696
1697 AclService aclService = getBinding().getAclService();
1698 if (!(aclService instanceof ExtendedAclService)) {
1699 throw new CmisNotSupportedException("setAcl() is not supported by the binding implementation.");
1700 }
1701
1702 ObjectFactory of = getObjectFactory();
1703
1704 return ((ExtendedAclService) aclService).setAcl(getRepositoryId(), objectId.getId(), of.convertAces(aces));
1705 }
1706
1707
1708 @Override
1709 public void applyPolicy(ObjectId objectId, ObjectId... policyIds) {
1710 checkObjectId(objectId);
1711
1712 if (policyIds == null || policyIds.length == 0) {
1713 throw new IllegalArgumentException("No Policies provided!");
1714 }
1715
1716 String[] ids = new String[policyIds.length];
1717 for (int i = 0; i < policyIds.length; i++) {
1718 if (policyIds[i] == null || policyIds[i].getId() == null) {
1719 throw new IllegalArgumentException("A Policy ID is not set!");
1720 }
1721
1722 ids[i] = policyIds[i].getId();
1723 }
1724
1725 for (String id : ids) {
1726 getBinding().getPolicyService().applyPolicy(getRepositoryId(), id, objectId.getId(), null);
1727 }
1728 }
1729
1730 @Override
1731 public void removePolicy(ObjectId objectId, ObjectId... policyIds) {
1732 checkObjectId(objectId);
1733
1734 if (policyIds == null || policyIds.length == 0) {
1735 throw new IllegalArgumentException("No Policies provided!");
1736 }
1737
1738 String[] ids = new String[policyIds.length];
1739 for (int i = 0; i < policyIds.length; i++) {
1740 if (policyIds[i] == null || policyIds[i].getId() == null) {
1741 throw new IllegalArgumentException("A Policy ID is not set!");
1742 }
1743
1744 ids[i] = policyIds[i].getId();
1745 }
1746
1747 for (String id : ids) {
1748 getBinding().getPolicyService().removePolicy(getRepositoryId(), id, objectId.getId(), null);
1749 }
1750 }
1751
1752
1753 protected String buildPath(String parentPath, String name) {
1754 if (parentPath == null || parentPath.length() < 1) {
1755 throw new IllegalArgumentException("Parent path must be set!");
1756 }
1757 if (parentPath.charAt(0) != '/') {
1758 throw new IllegalArgumentException("Parent path must start with a '/'!");
1759 }
1760 if (name == null || name.length() < 1) {
1761 throw new IllegalArgumentException("Name must be set!");
1762 }
1763
1764 StringBuilder path = new StringBuilder(parentPath.length() + name.length() + 2);
1765 path.append(parentPath);
1766 if (!parentPath.endsWith("/")) {
1767 path.append('/');
1768 }
1769 path.append(name);
1770
1771 return path.toString();
1772 }
1773
1774 protected String getObjectIdByPath(String path) {
1775 try {
1776 return getBinding().getObjectService().getObjectByPath(getRepositoryId(), path,
1777 "cmis:objectId,cmis:baseTypeId", false, IncludeRelationships.NONE, "cmis:none", false, false, null)
1778 .getId();
1779 } catch (CmisObjectNotFoundException onfe) {
1780 cache.removePath(path);
1781 throw onfe;
1782 }
1783 }
1784
1785 protected final void checkObjectId(ObjectId objectId) {
1786 if (objectId == null || objectId.getId() == null) {
1787 throw new IllegalArgumentException("Invalid object ID!");
1788 }
1789 }
1790
1791 protected final void checkObjectId(String objectId) {
1792 if (objectId == null) {
1793 throw new IllegalArgumentException("Invalid object ID!");
1794 }
1795 }
1796
1797 protected final void checkDocumentId(ObjectId docId) {
1798 if (docId == null || docId.getId() == null) {
1799 throw new IllegalArgumentException("Invalid document ID!");
1800 }
1801 }
1802
1803 protected final void checkDocumentId(String docId) {
1804 if (docId == null) {
1805 throw new IllegalArgumentException("Invalid document ID!");
1806 }
1807 }
1808
1809 protected final void checkFolderId(ObjectId folderId) {
1810 if (folderId == null || folderId.getId() == null) {
1811 throw new IllegalArgumentException("Invalid folder ID!");
1812 }
1813 }
1814
1815 protected final void checkPath(String path) {
1816 if (path == null || path.length() < 1) {
1817 throw new IllegalArgumentException("Invalid path!");
1818 }
1819 if (path.charAt(0) != '/') {
1820 throw new IllegalArgumentException("Path must start with a '/'!");
1821 }
1822 }
1823
1824 protected final void checkContext(OperationContext context) {
1825 if (context == null) {
1826 throw new IllegalArgumentException("Invalid Operation Context!");
1827 }
1828 }
1829
1830 protected final void checkProperties(Map<String, ?> properties) {
1831 if (isNullOrEmpty(properties)) {
1832 throw new IllegalArgumentException("Properties must not be empty!");
1833 }
1834 }
1835
1836 protected final void checkCmisVersion() {
1837 if (repositoryInfo.getCmisVersion() == CmisVersion.CMIS_1_0) {
1838 throw new CmisNotSupportedException("This method is not supported for CMIS 1.0 repositories.");
1839 }
1840 }
1841
1842
1843 @Override
1844 public String toString() {
1845 return "Session " + getBinding().getSessionId();
1846 }
1847 }