View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.chemistry.opencmis.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.Collections;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.apache.chemistry.opencmis.client.api.CmisObject;
30  import org.apache.chemistry.opencmis.client.api.Document;
31  import org.apache.chemistry.opencmis.client.api.FileableCmisObject;
32  import org.apache.chemistry.opencmis.client.api.Folder;
33  import org.apache.chemistry.opencmis.client.api.FolderType;
34  import org.apache.chemistry.opencmis.client.api.Item;
35  import org.apache.chemistry.opencmis.client.api.ItemIterable;
36  import org.apache.chemistry.opencmis.client.api.ObjectFactory;
37  import org.apache.chemistry.opencmis.client.api.ObjectId;
38  import org.apache.chemistry.opencmis.client.api.ObjectType;
39  import org.apache.chemistry.opencmis.client.api.OperationContext;
40  import org.apache.chemistry.opencmis.client.api.Policy;
41  import org.apache.chemistry.opencmis.client.api.Tree;
42  import org.apache.chemistry.opencmis.client.runtime.util.AbstractPageFetcher;
43  import org.apache.chemistry.opencmis.client.runtime.util.CollectionIterable;
44  import org.apache.chemistry.opencmis.client.runtime.util.TreeImpl;
45  import org.apache.chemistry.opencmis.commons.PropertyIds;
46  import org.apache.chemistry.opencmis.commons.data.Ace;
47  import org.apache.chemistry.opencmis.commons.data.ContentStream;
48  import org.apache.chemistry.opencmis.commons.data.ObjectData;
49  import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer;
50  import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData;
51  import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
52  import org.apache.chemistry.opencmis.commons.data.ObjectList;
53  import org.apache.chemistry.opencmis.commons.data.PropertyData;
54  import org.apache.chemistry.opencmis.commons.data.PropertyId;
55  import org.apache.chemistry.opencmis.commons.data.PropertyString;
56  import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
57  import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
58  import org.apache.chemistry.opencmis.commons.enums.VersioningState;
59  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
60  import org.apache.chemistry.opencmis.commons.spi.NavigationService;
61  
62  public class FolderImpl extends AbstractFilableCmisObject implements Folder {
63  
64      private static final long serialVersionUID = 1L;
65  
66      /**
67       * Constructor.
68       */
69      public FolderImpl(SessionImpl session, ObjectType objectType, ObjectData objectData, OperationContext context) {
70          initialize(session, objectType, objectData, context);
71      }
72  
73      @Override
74      public FolderType getFolderType() {
75          ObjectType objectType = super.getType();
76          if (objectType instanceof FolderType) {
77              return (FolderType) objectType;
78          } else {
79              throw new ClassCastException("Object type is not a folder type.");
80          }
81      }
82  
83      @Override
84      public Document createDocument(Map<String, ?> properties, ContentStream contentStream,
85              VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces,
86              OperationContext context) {
87  
88          ObjectId newId = getSession().createDocument(properties, this, contentStream, versioningState, policies,
89                  addAces, removeAces);
90  
91          // if no context is provided the object will not be fetched
92          if (context == null || newId == null) {
93              return null;
94          }
95  
96          // get the new object
97          CmisObject object = getSession().getObject(newId, context);
98          if (!(object instanceof Document)) {
99              throw new CmisRuntimeException("Newly created object is not a document! New id: " + newId);
100         }
101 
102         return (Document) object;
103     }
104 
105     @Override
106     public Document createDocumentFromSource(ObjectId source, Map<String, ?> properties,
107             VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces,
108             OperationContext context) {
109 
110         ObjectId newId = getSession().createDocumentFromSource(source, properties, this, versioningState, policies,
111                 addAces, removeAces);
112 
113         // if no context is provided the object will not be fetched
114         if (context == null || newId == null) {
115             return null;
116         }
117 
118         // get the new object
119         CmisObject object = getSession().getObject(newId, context);
120         if (!(object instanceof Document)) {
121             throw new CmisRuntimeException("Newly created object is not a document! New id: " + newId);
122         }
123 
124         return (Document) object;
125     }
126 
127     @Override
128     public Folder createFolder(Map<String, ?> properties, List<Policy> policies, List<Ace> addAces,
129             List<Ace> removeAces, OperationContext context) {
130 
131         ObjectId newId = getSession().createFolder(properties, this, policies, addAces, removeAces);
132 
133         // if no context is provided the object will not be fetched
134         if (context == null || newId == null) {
135             return null;
136         }
137 
138         // get the new object
139         CmisObject object = getSession().getObject(newId, context);
140         if (!(object instanceof Folder)) {
141             throw new CmisRuntimeException("Newly created object is not a folder! New id: " + newId);
142         }
143 
144         return (Folder) object;
145     }
146 
147     @Override
148     public Policy createPolicy(Map<String, ?> properties, List<Policy> policies, List<Ace> addAces,
149             List<Ace> removeAces, OperationContext context) {
150 
151         ObjectId newId = getSession().createPolicy(properties, this, policies, addAces, removeAces);
152 
153         // if no context is provided the object will not be fetched
154         if (context == null || newId == null) {
155             return null;
156         }
157 
158         // get the new object
159         CmisObject object = getSession().getObject(newId, context);
160         if (!(object instanceof Policy)) {
161             throw new CmisRuntimeException("Newly created object is not a policy! New id: " + newId);
162         }
163 
164         return (Policy) object;
165     }
166 
167     @Override
168     public Item createItem(Map<String, ?> properties, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces,
169             OperationContext context) {
170 
171         ObjectId newId = getSession().createItem(properties, this, policies, addAces, removeAces);
172 
173         // if no context is provided the object will not be fetched
174         if (context == null || newId == null) {
175             return null;
176         }
177 
178         // get the new object
179         CmisObject object = getSession().getObject(newId, context);
180         if (!(object instanceof Item)) {
181             throw new CmisRuntimeException("Newly created object is not an item! New id: " + newId);
182         }
183 
184         return (Item) object;
185     }
186 
187     @Override
188     public List<String> deleteTree(boolean allVersions, UnfileObject unfile, boolean continueOnFailure) {
189         return getSession().deleteTree(this, allVersions, unfile, continueOnFailure);
190     }
191 
192     @Override
193     public String getParentId() {
194         return getPropertyValue(PropertyIds.PARENT_ID);
195     }
196 
197     @Override
198     public List<ObjectType> getAllowedChildObjectTypes() {
199         List<ObjectType> result = new ArrayList<ObjectType>();
200 
201         readLock();
202         try {
203             List<String> otids = getPropertyValue(PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS);
204             if (otids == null) {
205                 return result;
206             }
207 
208             for (String otid : otids) {
209                 result.add(getSession().getTypeDefinition(otid));
210             }
211         } finally {
212             readUnlock();
213         }
214 
215         return result;
216     }
217 
218     @Override
219     public ItemIterable<Document> getCheckedOutDocs() {
220         return getCheckedOutDocs(getSession().getDefaultContext());
221     }
222 
223     @Override
224     public ItemIterable<Document> getCheckedOutDocs(OperationContext context) {
225         final String objectId = getObjectId();
226         final NavigationService navigationService = getBinding().getNavigationService();
227         final ObjectFactory objectFactory = getSession().getObjectFactory();
228         final OperationContext ctxt = new OperationContextImpl(context);
229 
230         return new CollectionIterable<Document>(new AbstractPageFetcher<Document>(ctxt.getMaxItemsPerPage()) {
231 
232             @Override
233             protected AbstractPageFetcher.Page<Document> fetchPage(long skipCount) {
234 
235                 // get checked out documents for this folder
236                 ObjectList checkedOutDocs = navigationService.getCheckedOutDocs(getRepositoryId(), objectId,
237                         ctxt.getFilterString(), ctxt.getOrderBy(), ctxt.isIncludeAllowableActions(),
238                         ctxt.getIncludeRelationships(), ctxt.getRenditionFilterString(),
239                         BigInteger.valueOf(this.maxNumItems), BigInteger.valueOf(skipCount), null);
240 
241                 // convert objects
242                 List<Document> page = new ArrayList<Document>();
243                 if (checkedOutDocs.getObjects() != null) {
244                     for (ObjectData objectData : checkedOutDocs.getObjects()) {
245                         CmisObject doc = objectFactory.convertObject(objectData, ctxt);
246                         if (!(doc instanceof Document)) {
247                             // should not happen...
248                             continue;
249                         }
250 
251                         page.add((Document) doc);
252                     }
253                 }
254 
255                 return new AbstractPageFetcher.Page<Document>(page, checkedOutDocs.getNumItems(),
256                         checkedOutDocs.hasMoreItems());
257             }
258         });
259     }
260 
261     @Override
262     public ItemIterable<CmisObject> getChildren() {
263         return getChildren(getSession().getDefaultContext());
264     }
265 
266     @Override
267     public ItemIterable<CmisObject> getChildren(OperationContext context) {
268         final String objectId = getObjectId();
269         final NavigationService navigationService = getBinding().getNavigationService();
270         final ObjectFactory objectFactory = getSession().getObjectFactory();
271         final OperationContext ctxt = new OperationContextImpl(context);
272 
273         return new CollectionIterable<CmisObject>(new AbstractPageFetcher<CmisObject>(ctxt.getMaxItemsPerPage()) {
274 
275             @Override
276             protected AbstractPageFetcher.Page<CmisObject> fetchPage(long skipCount) {
277 
278                 // get the children
279                 ObjectInFolderList children = navigationService.getChildren(getRepositoryId(), objectId,
280                         ctxt.getFilterString(), ctxt.getOrderBy(), ctxt.isIncludeAllowableActions(),
281                         ctxt.getIncludeRelationships(), ctxt.getRenditionFilterString(), ctxt.isIncludePathSegments(),
282                         BigInteger.valueOf(this.maxNumItems), BigInteger.valueOf(skipCount), null);
283 
284                 // convert objects
285                 List<CmisObject> page = new ArrayList<CmisObject>();
286                 List<ObjectInFolderData> childObjects = children.getObjects();
287                 if (childObjects != null) {
288                     for (ObjectInFolderData objectData : childObjects) {
289                         if (objectData.getObject() != null) {
290                             page.add(objectFactory.convertObject(objectData.getObject(), ctxt));
291                         }
292                     }
293                 }
294 
295                 return new AbstractPageFetcher.Page<CmisObject>(page, children.getNumItems(), children.hasMoreItems());
296             }
297         });
298     }
299 
300     @Override
301     public List<Tree<FileableCmisObject>> getDescendants(int depth) {
302         return getDescendants(depth, getSession().getDefaultContext());
303     }
304 
305     @Override
306     public List<Tree<FileableCmisObject>> getDescendants(int depth, OperationContext context) {
307         String objectId = getObjectId();
308 
309         // get the descendants
310         List<ObjectInFolderContainer> providerContainerList = getBinding().getNavigationService().getDescendants(
311                 getRepositoryId(), objectId, BigInteger.valueOf(depth), context.getFilterString(),
312                 context.isIncludeAllowableActions(), context.getIncludeRelationships(),
313                 context.getRenditionFilterString(), context.isIncludePathSegments(), null);
314 
315         return convertBindingContainer(providerContainerList, context);
316     }
317 
318     @Override
319     public List<Tree<FileableCmisObject>> getFolderTree(int depth) {
320         return getFolderTree(depth, getSession().getDefaultContext());
321     }
322 
323     @Override
324     public List<Tree<FileableCmisObject>> getFolderTree(int depth, OperationContext context) {
325         String objectId = getObjectId();
326 
327         // get the folder tree
328         List<ObjectInFolderContainer> providerContainerList = getBinding().getNavigationService().getFolderTree(
329                 getRepositoryId(), objectId, BigInteger.valueOf(depth), context.getFilterString(),
330                 context.isIncludeAllowableActions(), context.getIncludeRelationships(),
331                 context.getRenditionFilterString(), context.isIncludePathSegments(), null);
332 
333         return convertBindingContainer(providerContainerList, context);
334     }
335 
336     /**
337      * Converts a binding container into an API container.
338      */
339     private List<Tree<FileableCmisObject>> convertBindingContainer(List<ObjectInFolderContainer> bindingContainerList,
340             OperationContext context) {
341         if (bindingContainerList == null) {
342             return null;
343         }
344 
345         ObjectFactory of = getSession().getObjectFactory();
346 
347         List<Tree<FileableCmisObject>> result = new ArrayList<Tree<FileableCmisObject>>();
348         for (ObjectInFolderContainer oifc : bindingContainerList) {
349             if (oifc.getObject() == null || oifc.getObject().getObject() == null) {
350                 // shouldn't happen ...
351                 continue;
352             }
353 
354             // convert the object
355             CmisObject object = of.convertObject(oifc.getObject().getObject(), context);
356             if (!(object instanceof FileableCmisObject)) {
357                 // the repository must not return objects that are not fileable,
358                 // but you never know...
359                 continue;
360             }
361 
362             // convert the children
363             List<Tree<FileableCmisObject>> children = convertBindingContainer(oifc.getChildren(), context);
364 
365             // add both to current container
366             result.add(new TreeImpl<FileableCmisObject>((FileableCmisObject) object, children));
367         }
368 
369         return result;
370     }
371 
372     @Override
373     public boolean isRootFolder() {
374         String objectId = getObjectId();
375         String rootFolderId = getSession().getRepositoryInfo().getRootFolderId();
376 
377         return objectId.equals(rootFolderId);
378     }
379 
380     @Override
381     public Folder getFolderParent() {
382         if (isRootFolder()) {
383             return null;
384         }
385 
386         List<Folder> parents = getParents(getSession().getDefaultContext());
387         if (isNullOrEmpty(parents)) {
388             return null;
389         }
390 
391         return parents.get(0);
392     }
393 
394     @Override
395     public String getPath() {
396         String path;
397 
398         readLock();
399         try {
400             // get the path property
401             path = getPropertyValue(PropertyIds.PATH);
402 
403             // if the path property isn't set, get it
404             if (path == null) {
405                 String objectId = getObjectId();
406                 ObjectData objectData = getBinding().getObjectService().getObject(getRepositoryId(), objectId,
407                         getPropertyQueryName(PropertyIds.PATH), false, IncludeRelationships.NONE, "cmis:none", false,
408                         false, null);
409 
410                 if (objectData.getProperties() != null && objectData.getProperties().getProperties() != null) {
411                     PropertyData<?> pathProperty = objectData.getProperties().getProperties().get(PropertyIds.PATH);
412 
413                     if (pathProperty instanceof PropertyString) {
414                         path = ((PropertyString) pathProperty).getFirstValue();
415                     }
416                 }
417             }
418         } finally {
419             readUnlock();
420         }
421 
422         // we still don't know the path ... it's not a CMIS compliant repository
423         if (path == null) {
424             throw new CmisRuntimeException("Repository didn't return " + PropertyIds.PATH + "!");
425         }
426 
427         return path;
428     }
429 
430     @Override
431     public List<Folder> getParents(OperationContext context) {
432         if (isRootFolder()) {
433             return Collections.emptyList();
434         }
435 
436         String objectId = getObjectId();
437 
438         ObjectData bindingParent = getBinding().getNavigationService().getFolderParent(getRepositoryId(), objectId,
439                 getPropertyQueryName(PropertyIds.OBJECT_ID), null);
440 
441         if (bindingParent.getProperties() == null) {
442             // should not happen...
443             throw new CmisRuntimeException("Repository sent invalid data!");
444         }
445 
446         // get id property
447         PropertyData<?> idProperty = bindingParent.getProperties().getProperties().get(PropertyIds.OBJECT_ID);
448         if (!(idProperty instanceof PropertyId) && !(idProperty instanceof PropertyString)) {
449             // the repository sent an object without a valid object id...
450             throw new CmisRuntimeException("Repository sent invalid data! No object id!");
451         }
452 
453         // fetch the object and make sure it is a folder
454         CmisObject parentFolder = getSession().getObject((String) idProperty.getFirstValue(), context);
455         if (!(parentFolder instanceof Folder)) {
456             // the repository sent an object that is not a folder...
457             throw new CmisRuntimeException("Repository sent invalid data! Object is not a folder!");
458         }
459 
460         return Collections.singletonList((Folder) parentFolder);
461     }
462 
463     @Override
464     public List<String> getPaths() {
465         return Collections.singletonList(getPath());
466     }
467 
468     @Override
469     public Document createDocument(Map<String, ?> properties, ContentStream contentStream,
470             VersioningState versioningState) {
471         return this.createDocument(properties, contentStream, versioningState, null, null, null, getSession()
472                 .getDefaultContext());
473     }
474 
475     @Override
476     public Document createDocumentFromSource(ObjectId source, Map<String, ?> properties, VersioningState versioningState) {
477         return this.createDocumentFromSource(source, properties, versioningState, null, null, null, getSession()
478                 .getDefaultContext());
479     }
480 
481     @Override
482     public Folder createFolder(Map<String, ?> properties) {
483         return this.createFolder(properties, null, null, null, getSession().getDefaultContext());
484     }
485 
486     @Override
487     public Policy createPolicy(Map<String, ?> properties) {
488         return this.createPolicy(properties, null, null, null, getSession().getDefaultContext());
489     }
490 
491     @Override
492     public Item createItem(Map<String, ?> properties) {
493         return this.createItem(properties, null, null, null, getSession().getDefaultContext());
494     }
495 }