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

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   * http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   *
19   * Contributors:
20   *     Florian Mueller
21   *     Florent Guillaume, Nuxeo
22   */
23  package org.apache.chemistry.opencmis.server.impl.atompub;
24  
25  import java.math.BigInteger;
26  import java.util.GregorianCalendar;
27  import java.util.List;
28  
29  import javax.servlet.http.HttpServletRequest;
30  import javax.xml.bind.JAXBException;
31  import javax.xml.stream.XMLStreamException;
32  
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.definitions.TypeDefinition;
36  import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer;
37  import org.apache.chemistry.opencmis.commons.enums.BaseTypeId;
38  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
39  import org.apache.chemistry.opencmis.commons.impl.Constants;
40  import org.apache.chemistry.opencmis.commons.impl.ReturnVersion;
41  import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
42  import org.apache.chemistry.opencmis.commons.server.CmisService;
43  import org.apache.chemistry.opencmis.commons.server.ObjectInfo;
44  import org.apache.chemistry.opencmis.commons.server.RenditionInfo;
45  
46  /**
47   * This class contains operations used by all services.
48   */
49  public final class AtomPubUtils {
50  
51      public static final String RESOURCE_CHILDREN = "children";
52      public static final String RESOURCE_DESCENDANTS = "descendants";
53      public static final String RESOURCE_FOLDERTREE = "foldertree";
54      public static final String RESOURCE_TYPE = "type";
55      public static final String RESOURCE_TYPES = "types";
56      public static final String RESOURCE_TYPESDESC = "typedesc";
57      public static final String RESOURCE_ENTRY = "entry";
58      public static final String RESOURCE_PARENTS = "parents";
59      public static final String RESOURCE_VERSIONS = "versions";
60      public static final String RESOURCE_ALLOWABLEACIONS = "allowableactions";
61      public static final String RESOURCE_ACL = "acl";
62      public static final String RESOURCE_POLICIES = "policies";
63      public static final String RESOURCE_RELATIONSHIPS = "relationships";
64      public static final String RESOURCE_OBJECTBYID = "id";
65      public static final String RESOURCE_OBJECTBYPATH = "path";
66      public static final String RESOURCE_QUERY = "query";
67      public static final String RESOURCE_CHECKEDOUT = "checkedout";
68      public static final String RESOURCE_UNFILED = "unfiled";
69      public static final String RESOURCE_CHANGES = "changes";
70      public static final String RESOURCE_CONTENT = "content";
71  
72      public static final BigInteger PAGE_SIZE = BigInteger.valueOf(100);
73  
74      public static final String TYPE_AUTHOR = "unknown";
75  
76      /**
77       * Private constructor.
78       */
79      private AtomPubUtils() {
80      }
81  
82      /**
83       * Compiles the base URL for links, collections and templates.
84       */
85      public static UrlBuilder compileBaseUrl(HttpServletRequest request, String repositoryId) {
86          UrlBuilder url = new UrlBuilder(request.getScheme(), request.getServerName(), request.getServerPort(), null);
87  
88          url.addPath(request.getContextPath());
89          url.addPath(request.getServletPath());
90  
91          if (repositoryId != null) {
92              url.addPathSegment(repositoryId);
93          }
94  
95          return url;
96      }
97  
98      /**
99       * Compiles a URL for links, collections and templates.
100      */
101     public static String compileUrl(UrlBuilder baseUrl, String resource, String id) {
102         return compileUrlBuilder(baseUrl, resource, id).toString();
103     }
104 
105     /**
106      * Compiles a URL for links, collections and templates.
107      */
108     public static UrlBuilder compileUrlBuilder(UrlBuilder baseUrl, String resource, String id) {
109         UrlBuilder url = new UrlBuilder(baseUrl);
110         url.addPathSegment(resource);
111 
112         if (id != null) {
113             url.addParameter("id", id);
114         }
115 
116         return url;
117     }
118 
119     // -------------------------------------------------------------------------
120     // --- entry builder ---
121     // -------------------------------------------------------------------------
122 
123     /**
124      * Writes the a object entry.
125      */
126     public static void writeObjectEntry(CmisService service, AtomEntry entry, ObjectData object,
127             List<ObjectInFolderContainer> children, String repositoryId, String pathSegment,
128             String relativePathSegment, UrlBuilder baseUrl, boolean isRoot) throws XMLStreamException, JAXBException {
129         if (object == null) {
130             throw new CmisRuntimeException("Object not set!");
131         }
132 
133         ObjectInfo info = service.getObjectInfo(repositoryId, object.getId());
134         if (info == null) {
135             throw new CmisRuntimeException("Object Info not found for: " + object.getId());
136         }
137 
138         // start
139         entry.startEntry(isRoot);
140 
141         // write object
142         String contentSrc = null;
143 
144         if (info.hasContent()) {
145             UrlBuilder contentSrcBuilder = compileUrlBuilder(baseUrl, RESOURCE_CONTENT, info.getId());
146             if (info.getFileName() != null) {
147                 contentSrcBuilder.addPathSegment(info.getFileName());
148             }
149 
150             contentSrc = contentSrcBuilder.toString();
151         }
152 
153         entry.writeObject(object, info, contentSrc, info.getContentType(), pathSegment, relativePathSegment);
154 
155         // write links
156         entry.writeServiceLink(baseUrl.toString(), repositoryId);
157 
158         entry.writeSelfLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()), info.getId());
159         entry.writeEnclosureLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()));
160         entry.writeEditLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getId()));
161         entry.writeDescribedByLink(compileUrl(baseUrl, RESOURCE_TYPE, info.getTypeId()));
162         entry.writeAllowableActionsLink(compileUrl(baseUrl, RESOURCE_ALLOWABLEACIONS, info.getId()));
163 
164         if (info.hasParent()) {
165             entry.writeUpLink(compileUrl(baseUrl, RESOURCE_PARENTS, info.getId()), Constants.MEDIATYPE_FEED);
166         }
167 
168         if (info.getBaseType() == BaseTypeId.CMIS_FOLDER) {
169             entry.writeDownLink(compileUrl(baseUrl, RESOURCE_CHILDREN, info.getId()), Constants.MEDIATYPE_FEED);
170 
171             if (info.supportsDescendants()) {
172                 entry.writeDownLink(compileUrl(baseUrl, RESOURCE_DESCENDANTS, info.getId()),
173                         Constants.MEDIATYPE_DESCENDANTS);
174             }
175 
176             if (info.supportsFolderTree()) {
177                 entry.writeFolderTreeLink(compileUrl(baseUrl, RESOURCE_FOLDERTREE, info.getId()));
178             }
179         }
180 
181         if (info.getVersionSeriesId() != null) {
182             UrlBuilder vsUrl = compileUrlBuilder(baseUrl, RESOURCE_VERSIONS, info.getId());
183             vsUrl.addParameter(Constants.PARAM_VERSION_SERIES_ID, info.getVersionSeriesId());
184             entry.writeVersionHistoryLink(vsUrl.toString());
185         }
186 
187         if (!info.isCurrentVersion()) {
188             UrlBuilder cvUrl = compileUrlBuilder(baseUrl, RESOURCE_ENTRY, info.getId());
189             cvUrl.addParameter(Constants.PARAM_RETURN_VERSION, ReturnVersion.LATEST);
190             entry.writeEditLink(cvUrl.toString());
191         }
192 
193         if (info.getBaseType() == BaseTypeId.CMIS_DOCUMENT) {
194             entry.writeEditMediaLink(compileUrl(baseUrl, RESOURCE_CONTENT, info.getId()), info.getContentType());
195         }
196 
197         if (info.getWorkingCopyId() != null) {
198             entry.writeWorkingCopyLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getWorkingCopyId()));
199         }
200 
201         if (info.getWorkingCopyOriginalId() != null) {
202             entry.writeViaLink(compileUrl(baseUrl, RESOURCE_ENTRY, info.getWorkingCopyOriginalId()));
203         }
204 
205         if (info.getRenditionInfos() != null) {
206             for (RenditionInfo ri : info.getRenditionInfos()) {
207                 UrlBuilder rurl = compileUrlBuilder(baseUrl, RESOURCE_CONTENT,
208                         info.getId());
209                 rurl.addParameter(Constants.PARAM_STREAM_ID, ri.getId());
210                 entry.writeAlternateLink(rurl.toString(), ri.getContenType(),
211                         ri.getKind(), ri.getTitle(), ri.getLength());
212             }
213         }
214 
215         if (info.hasAcl()) {
216             entry.writeAclLink(compileUrl(baseUrl, RESOURCE_ACL, info.getId()));
217         }
218 
219         if (info.supportsPolicies()) {
220             entry.writePoliciesLink(compileUrl(baseUrl, RESOURCE_POLICIES, info.getId()));
221         }
222 
223         if (info.supportsRelationships()) {
224             entry.writeRelationshipsLink(compileUrl(baseUrl, RESOURCE_RELATIONSHIPS, info.getId()));
225         }
226 
227         if (info.getRelationshipSourceIds() != null) {
228             for (String id : info.getRelationshipSourceIds()) {
229                 entry.writeRelationshipSourceLink(compileUrl(baseUrl, RESOURCE_ENTRY, id));
230             }
231         }
232 
233         if (info.getRelationshipTargetIds() != null) {
234             for (String id : info.getRelationshipTargetIds()) {
235                 entry.writeRelationshipTargetLink(compileUrl(baseUrl, RESOURCE_ENTRY, id));
236             }
237         }
238 
239         // write children
240         if ((children != null) && (children.size() > 0)) {
241             writeObjectChildren(service, entry, info, children, repositoryId, baseUrl);
242         }
243 
244         // we are done
245         entry.endEntry();
246     }
247 
248     /**
249      * Writes the a object entry in a content changes list.
250      *
251      * Content changes objects need special treatment because some of them could
252      * have been deleted and an object info cannot be generated.
253      */
254     public static void writeContentChangesObjectEntry(CmisService service, AtomEntry entry, ObjectData object,
255             List<ObjectInFolderContainer> children, String repositoryId, String pathSegment,
256             String relativePathSegment, UrlBuilder baseUrl, boolean isRoot) throws XMLStreamException, JAXBException {
257         if (object == null) {
258             throw new CmisRuntimeException("Object not set!");
259         }
260 
261         ObjectInfo info = null;
262         try {
263             info = service.getObjectInfo(repositoryId, object.getId());
264         } catch (Exception e) {
265             // ignore all exceptions
266         }
267 
268         if (info != null) {
269             writeObjectEntry(service, entry, object, children, repositoryId, pathSegment, relativePathSegment, baseUrl,
270                     isRoot);
271             return;
272         }
273 
274         // start delete object entry
275         entry.startEntry(isRoot);
276 
277         // write object
278         entry.writeDeletedObject(object);
279 
280         // write links
281         entry.writeServiceLink(baseUrl.toString(), repositoryId);
282 
283         // we are done
284         entry.endEntry();
285     }
286 
287     /**
288      * Writes an objects entry children feed.
289      */
290     public static void writeObjectChildren(CmisService service, AtomEntry entry, ObjectInfo folderInfo,
291             List<ObjectInFolderContainer> children, String repositoryId, UrlBuilder baseUrl) throws XMLStreamException,
292             JAXBException {
293 
294         // start
295         AtomFeed feed = new AtomFeed(entry.getWriter());
296         feed.startChildren();
297         feed.startFeed(false);
298 
299         // write basic Atom feed elements
300         feed.writeFeedElements(folderInfo.getId(), folderInfo.getCreatedBy(), folderInfo.getName(),
301                 folderInfo.getLastModificationDate(), null, null);
302 
303         // write links
304         feed.writeServiceLink(baseUrl.toString(), repositoryId);
305 
306         feed.writeSelfLink(compileUrl(baseUrl, RESOURCE_DESCENDANTS, folderInfo.getId()), null);
307 
308         feed.writeViaLink(compileUrl(baseUrl, RESOURCE_ENTRY, folderInfo.getId()));
309 
310         feed.writeDownLink(compileUrl(baseUrl, RESOURCE_CHILDREN, folderInfo.getId()), Constants.MEDIATYPE_FEED);
311 
312         feed.writeDownLink(compileUrl(baseUrl, RESOURCE_FOLDERTREE, folderInfo.getId()),
313                 Constants.MEDIATYPE_DESCENDANTS);
314 
315         feed.writeUpLink(compileUrl(baseUrl, RESOURCE_PARENTS, folderInfo.getId()), Constants.MEDIATYPE_FEED);
316 
317         for (ObjectInFolderContainer container : children) {
318             if ((container != null) && (container.getObject() != null)) {
319                 writeObjectEntry(service, entry, container.getObject().getObject(), container.getChildren(),
320                         repositoryId, container.getObject().getPathSegment(), null, baseUrl, false);
321             }
322         }
323 
324         // we are done
325         feed.endFeed();
326         feed.endChildren();
327     }
328 
329     /**
330      * Writes the a type entry.
331      */
332     public static void writeTypeEntry(AtomEntry entry, TypeDefinition type, List<TypeDefinitionContainer> children,
333             String repositoryId, UrlBuilder baseUrl, boolean isRoot) throws XMLStreamException, JAXBException {
334 
335         // start
336         entry.startEntry(isRoot);
337 
338         // write type
339         entry.writeType(type);
340 
341         // write links
342         entry.writeServiceLink(baseUrl.toString(), repositoryId);
343 
344         entry.writeSelfLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()), type.getId());
345         entry.writeEnclosureLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()));
346         if (type.getParentTypeId() != null) {
347             entry.writeUpLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getParentTypeId()), Constants.MEDIATYPE_ENTRY);
348         }
349         UrlBuilder downLink = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null);
350         downLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
351         entry.writeDownLink(downLink.toString(), Constants.MEDIATYPE_CHILDREN);
352         UrlBuilder downLink2 = compileUrlBuilder(baseUrl, RESOURCE_TYPESDESC, null);
353         downLink2.addParameter(Constants.PARAM_TYPE_ID, type.getId());
354         entry.writeDownLink(downLink2.toString(), Constants.MEDIATYPE_DESCENDANTS);
355         entry.writeDescribedByLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getBaseTypeId().value()));
356 
357         // write children
358         if ((children != null) && (children.size() > 0)) {
359             writeTypeChildren(entry, type, children, repositoryId, baseUrl);
360         }
361 
362         // we are done
363         entry.endEntry();
364     }
365 
366     /**
367      * Writes the a type entry children feed.
368      */
369     private static void writeTypeChildren(AtomEntry entry, TypeDefinition type, List<TypeDefinitionContainer> children,
370             String repositoryId, UrlBuilder baseUrl) throws XMLStreamException, JAXBException {
371 
372         // start
373         AtomFeed feed = new AtomFeed(entry.getWriter());
374         feed.startChildren();
375         feed.startFeed(false);
376 
377         // write basic Atom feed elements
378         feed.writeFeedElements(type.getId(), TYPE_AUTHOR, type.getDisplayName(), new GregorianCalendar(), null, null);
379 
380         feed.writeServiceLink(baseUrl.toString(), repositoryId);
381 
382         UrlBuilder selfLink = compileUrlBuilder(baseUrl, RESOURCE_TYPESDESC, null);
383         selfLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
384         feed.writeSelfLink(selfLink.toString(), type.getId());
385 
386         feed.writeViaLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getId()));
387 
388         UrlBuilder downLink = compileUrlBuilder(baseUrl, RESOURCE_TYPES, null);
389         downLink.addParameter(Constants.PARAM_TYPE_ID, type.getId());
390         feed.writeDownLink(downLink.toString(), Constants.MEDIATYPE_FEED);
391 
392         if (type.getParentTypeId() != null) {
393             feed.writeUpLink(compileUrl(baseUrl, RESOURCE_TYPE, type.getParentTypeId()), Constants.MEDIATYPE_ENTRY);
394         }
395 
396         // write tree
397         for (TypeDefinitionContainer container : children) {
398             if ((container != null) && (container.getTypeDefinition() != null)) {
399                 writeTypeEntry(entry, container.getTypeDefinition(), container.getChildren(), repositoryId, baseUrl,
400                         false);
401             }
402         }
403 
404         // we are done
405         feed.endFeed();
406         feed.endChildren();
407     }
408 }