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.bindings.spi.atompub;
20  
21  import java.math.BigInteger;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession;
26  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomBase;
27  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomElement;
28  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomEntry;
29  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomFeed;
30  import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomLink;
31  import org.apache.chemistry.opencmis.client.bindings.spi.http.Response;
32  import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
33  import org.apache.chemistry.opencmis.commons.data.ObjectData;
34  import org.apache.chemistry.opencmis.commons.data.ObjectInFolderContainer;
35  import org.apache.chemistry.opencmis.commons.data.ObjectInFolderData;
36  import org.apache.chemistry.opencmis.commons.data.ObjectInFolderList;
37  import org.apache.chemistry.opencmis.commons.data.ObjectList;
38  import org.apache.chemistry.opencmis.commons.data.ObjectParentData;
39  import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
40  import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
41  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
42  import org.apache.chemistry.opencmis.commons.impl.Constants;
43  import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
44  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderContainerImpl;
45  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderDataImpl;
46  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectInFolderListImpl;
47  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectListImpl;
48  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectParentDataImpl;
49  import org.apache.chemistry.opencmis.commons.spi.NavigationService;
50  
51  /**
52   * Navigation Service AtomPub client.
53   */
54  public class NavigationServiceImpl extends AbstractAtomPubService implements NavigationService {
55  
56      /**
57       * Constructor.
58       */
59      public NavigationServiceImpl(BindingSession session) {
60          setSession(session);
61      }
62  
63      @Override
64      public ObjectInFolderList getChildren(String repositoryId, String folderId, String filter, String orderBy,
65              Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
66              Boolean includePathSegment, BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
67          ObjectInFolderListImpl result = new ObjectInFolderListImpl();
68  
69          // find the link
70          String link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN);
71  
72          if (link == null) {
73              throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_CHILDREN);
74          }
75  
76          UrlBuilder url = new UrlBuilder(link);
77          url.addParameter(Constants.PARAM_FILTER, filter);
78          url.addParameter(Constants.PARAM_ORDER_BY, orderBy);
79          url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions);
80          url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships);
81          url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter);
82          url.addParameter(Constants.PARAM_PATH_SEGMENT, includePathSegment);
83          url.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
84          url.addParameter(Constants.PARAM_SKIP_COUNT, skipCount);
85  
86          // read and parse
87          Response resp = read(url);
88          AtomFeed feed = parse(resp.getStream(), AtomFeed.class);
89  
90          // handle top level
91          for (AtomElement element : feed.getElements()) {
92              if (element.getObject() instanceof AtomLink) {
93                  if (isNextLink(element)) {
94                      result.setHasMoreItems(Boolean.TRUE);
95                  }
96              } else if (isInt(NAME_NUM_ITEMS, element)) {
97                  result.setNumItems((BigInteger) element.getObject());
98              }
99          }
100 
101         // get the children
102         if (!feed.getEntries().isEmpty()) {
103             result.setObjects(new ArrayList<ObjectInFolderData>(feed.getEntries().size()));
104 
105             for (AtomEntry entry : feed.getEntries()) {
106                 ObjectInFolderDataImpl child = null;
107                 String pathSegment = null;
108 
109                 lockLinks();
110                 try {
111                     // clean up cache
112                     removeLinks(repositoryId, entry.getId());
113 
114                     // walk through the entry
115                     for (AtomElement element : entry.getElements()) {
116                         if (element.getObject() instanceof AtomLink) {
117                             addLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
118                         } else if (isStr(NAME_PATH_SEGMENT, element)) {
119                             pathSegment = (String) element.getObject();
120                         } else if (element.getObject() instanceof ObjectData) {
121                             child = new ObjectInFolderDataImpl();
122                             child.setObject((ObjectData) element.getObject());
123                         }
124                     }
125                 } finally {
126                     unlockLinks();
127                 }
128 
129                 if (child != null) {
130                     child.setPathSegment(pathSegment);
131                     result.getObjects().add(child);
132                 }
133             }
134         }
135 
136         return result;
137     }
138 
139     @Override
140     public List<ObjectInFolderContainer> getDescendants(String repositoryId, String folderId, BigInteger depth,
141             String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
142             String renditionFilter, Boolean includePathSegment, ExtensionsData extension) {
143         List<ObjectInFolderContainer> result = new ArrayList<ObjectInFolderContainer>();
144 
145         // find the link
146         String link = loadLink(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_DESCENDANTS);
147 
148         if (link == null) {
149             throwLinkException(repositoryId, folderId, Constants.REL_DOWN, Constants.MEDIATYPE_DESCENDANTS);
150         }
151 
152         UrlBuilder url = new UrlBuilder(link);
153         url.addParameter(Constants.PARAM_DEPTH, depth);
154         url.addParameter(Constants.PARAM_FILTER, filter);
155         url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions);
156         url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships);
157         url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter);
158         url.addParameter(Constants.PARAM_PATH_SEGMENT, includePathSegment);
159 
160         // read and parse
161         Response resp = read(url);
162         AtomFeed feed = parse(resp.getStream(), AtomFeed.class);
163 
164         // process tree
165         addDescendantsLevel(repositoryId, feed, result);
166 
167         return result;
168     }
169 
170     @Override
171     public ObjectData getFolderParent(String repositoryId, String folderId, String filter, ExtensionsData extension) {
172         ObjectData result = null;
173 
174         // find the link
175         String link = loadLink(repositoryId, folderId, Constants.REL_UP, Constants.MEDIATYPE_ENTRY);
176 
177         if (link == null) {
178             throwLinkException(repositoryId, folderId, Constants.REL_UP, Constants.MEDIATYPE_ENTRY);
179         }
180 
181         UrlBuilder url = new UrlBuilder(link);
182         url.addParameter(Constants.PARAM_FILTER, filter);
183 
184         // read
185         Response resp = read(url);
186 
187         AtomBase base = parse(resp.getStream(), AtomBase.class);
188 
189         // get the entry
190         AtomEntry entry = null;
191         if (base instanceof AtomFeed) {
192             AtomFeed feed = (AtomFeed) base;
193             if (feed.getEntries().isEmpty()) {
194                 throw new CmisRuntimeException("Parent feed is empty!");
195             }
196             entry = feed.getEntries().get(0);
197         } else if (base instanceof AtomEntry) {
198             entry = (AtomEntry) base;
199         } else {
200             throw new CmisRuntimeException("Unexpected document!");
201         }
202 
203         lockLinks();
204         try {
205             // clean up cache
206             removeLinks(repositoryId, entry.getId());
207 
208             // walk through the entry
209             for (AtomElement element : entry.getElements()) {
210                 if (element.getObject() instanceof AtomLink) {
211                     addLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
212                 } else if (element.getObject() instanceof ObjectData) {
213                     result = (ObjectData) element.getObject();
214                 }
215             }
216         } finally {
217             unlockLinks();
218         }
219 
220         return result;
221     }
222 
223     @Override
224     public List<ObjectInFolderContainer> getFolderTree(String repositoryId, String folderId, BigInteger depth,
225             String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
226             String renditionFilter, Boolean includePathSegment, ExtensionsData extension) {
227         List<ObjectInFolderContainer> result = new ArrayList<ObjectInFolderContainer>();
228 
229         // find the link
230         String link = loadLink(repositoryId, folderId, Constants.REL_FOLDERTREE, Constants.MEDIATYPE_DESCENDANTS);
231 
232         if (link == null) {
233             throwLinkException(repositoryId, folderId, Constants.REL_FOLDERTREE, Constants.MEDIATYPE_DESCENDANTS);
234         }
235 
236         UrlBuilder url = new UrlBuilder(link);
237         url.addParameter(Constants.PARAM_DEPTH, depth);
238         url.addParameter(Constants.PARAM_FILTER, filter);
239         url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions);
240         url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships);
241         url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter);
242         url.addParameter(Constants.PARAM_PATH_SEGMENT, includePathSegment);
243 
244         // read and parse
245         Response resp = read(url);
246         AtomFeed feed = parse(resp.getStream(), AtomFeed.class);
247 
248         // process tree
249         addDescendantsLevel(repositoryId, feed, result);
250 
251         return result;
252     }
253 
254     @Override
255     public List<ObjectParentData> getObjectParents(String repositoryId, String objectId, String filter,
256             Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
257             Boolean includeRelativePathSegment, ExtensionsData extension) {
258         List<ObjectParentData> result = new ArrayList<ObjectParentData>();
259 
260         // find the link
261         String link = loadLink(repositoryId, objectId, Constants.REL_UP, Constants.MEDIATYPE_FEED);
262 
263         if (link == null) {
264             // root and unfiled objects have no UP link
265             return result;
266         }
267 
268         UrlBuilder url = new UrlBuilder(link);
269         url.addParameter(Constants.PARAM_FILTER, filter);
270         url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions);
271         url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships);
272         url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter);
273         url.addParameter(Constants.PARAM_RELATIVE_PATH_SEGMENT, includeRelativePathSegment);
274 
275         // read and parse
276         Response resp = read(url);
277 
278         AtomBase base = parse(resp.getStream(), AtomBase.class);
279 
280         if (base instanceof AtomFeed) {
281             // it's a feed
282             AtomFeed feed = (AtomFeed) base;
283 
284             // walk through the feed
285             for (AtomEntry entry : feed.getEntries()) {
286                 ObjectParentDataImpl objectParent = processParentEntry(entry, repositoryId);
287 
288                 if (objectParent != null) {
289                     result.add(objectParent);
290                 }
291             }
292         } else if (base instanceof AtomEntry) {
293             // it's an entry
294             AtomEntry entry = (AtomEntry) base;
295 
296             ObjectParentDataImpl objectParent = processParentEntry(entry, repositoryId);
297 
298             if (objectParent != null) {
299                 result.add(objectParent);
300             }
301         }
302 
303         return result;
304     }
305 
306     private ObjectParentDataImpl processParentEntry(AtomEntry entry, String repositoryId) {
307         ObjectParentDataImpl result = null;
308         String relativePathSegment = null;
309 
310         lockLinks();
311         try {
312             // clean up cache
313             removeLinks(repositoryId, entry.getId());
314 
315             // walk through the entry
316             for (AtomElement element : entry.getElements()) {
317                 if (element.getObject() instanceof AtomLink) {
318                     addLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
319                 } else if (element.getObject() instanceof ObjectData) {
320                     result = new ObjectParentDataImpl((ObjectData) element.getObject());
321                 } else if (is(NAME_RELATIVE_PATH_SEGMENT, element)) {
322                     relativePathSegment = (String) element.getObject();
323                 }
324             }
325         } finally {
326             unlockLinks();
327         }
328 
329         if (result != null) {
330             result.setRelativePathSegment(relativePathSegment);
331         }
332 
333         return result;
334     }
335 
336     @Override
337     public ObjectList getCheckedOutDocs(String repositoryId, String folderId, String filter, String orderBy,
338             Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter,
339             BigInteger maxItems, BigInteger skipCount, ExtensionsData extension) {
340         ObjectListImpl result = new ObjectListImpl();
341 
342         // find the link
343         String link = loadCollection(repositoryId, Constants.COLLECTION_CHECKEDOUT);
344 
345         if (link == null) {
346             throw new CmisObjectNotFoundException("Unknown repository or checkedout collection not supported!");
347         }
348 
349         UrlBuilder url = new UrlBuilder(link);
350         url.addParameter(Constants.PARAM_FOLDER_ID, folderId);
351         url.addParameter(Constants.PARAM_FILTER, filter);
352         url.addParameter(Constants.PARAM_ORDER_BY, orderBy);
353         url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions);
354         url.addParameter(Constants.PARAM_RELATIONSHIPS, includeRelationships);
355         url.addParameter(Constants.PARAM_RENDITION_FILTER, renditionFilter);
356         url.addParameter(Constants.PARAM_MAX_ITEMS, maxItems);
357         url.addParameter(Constants.PARAM_SKIP_COUNT, skipCount);
358 
359         // read and parse
360         Response resp = read(url);
361         AtomFeed feed = parse(resp.getStream(), AtomFeed.class);
362 
363         // handle top level
364         for (AtomElement element : feed.getElements()) {
365             if (element.getObject() instanceof AtomLink) {
366                 if (isNextLink(element)) {
367                     result.setHasMoreItems(Boolean.TRUE);
368                 }
369             } else if (isInt(NAME_NUM_ITEMS, element)) {
370                 result.setNumItems((BigInteger) element.getObject());
371             }
372         }
373 
374         // get the documents
375         if (!feed.getEntries().isEmpty()) {
376             result.setObjects(new ArrayList<ObjectData>(feed.getEntries().size()));
377 
378             for (AtomEntry entry : feed.getEntries()) {
379                 ObjectData child = null;
380 
381                 lockLinks();
382                 try {
383                     // clean up cache
384                     removeLinks(repositoryId, entry.getId());
385 
386                     // walk through the entry
387                     for (AtomElement element : entry.getElements()) {
388                         if (element.getObject() instanceof AtomLink) {
389                             addLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
390                         } else if (element.getObject() instanceof ObjectData) {
391                             child = (ObjectData) element.getObject();
392                         }
393                     }
394                 } finally {
395                     unlockLinks();
396                 }
397 
398                 if (child != null) {
399                     result.getObjects().add(child);
400                 }
401             }
402         }
403 
404         return result;
405     }
406 
407     // ---- internal ----
408 
409     /**
410      * Adds descendants level recursively.
411      */
412     private void addDescendantsLevel(String repositoryId, AtomFeed feed, List<ObjectInFolderContainer> containerList) {
413         if (feed == null || feed.getEntries().isEmpty()) {
414             return;
415         }
416 
417         // walk through the feed
418         for (AtomEntry entry : feed.getEntries()) {
419             ObjectInFolderDataImpl objectInFolder = null;
420             String pathSegment = null;
421             List<ObjectInFolderContainer> childContainerList = new ArrayList<ObjectInFolderContainer>();
422 
423             lockLinks();
424             try {
425                 // clean up cache
426                 removeLinks(repositoryId, entry.getId());
427 
428                 // walk through the entry
429                 for (AtomElement element : entry.getElements()) {
430                     if (element.getObject() instanceof AtomLink) {
431                         addLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
432                     } else if (element.getObject() instanceof ObjectData) {
433                         objectInFolder = new ObjectInFolderDataImpl((ObjectData) element.getObject());
434                     } else if (is(NAME_PATH_SEGMENT, element)) {
435                         pathSegment = (String) element.getObject();
436                     } else if (element.getObject() instanceof AtomFeed) {
437                         addDescendantsLevel(repositoryId, (AtomFeed) element.getObject(), childContainerList);
438                     }
439                 }
440             } finally {
441                 unlockLinks();
442             }
443 
444             if (objectInFolder != null) {
445                 objectInFolder.setPathSegment(pathSegment);
446                 ObjectInFolderContainerImpl childContainer = new ObjectInFolderContainerImpl(objectInFolder);
447                 childContainer.setChildren(childContainerList);
448                 containerList.add(childContainer);
449             }
450         }
451     }
452 }