This project has retired. For details please refer to its Attic page.
ObjectService 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  package org.apache.chemistry.opencmis.server.impl.atompub;
20  
21  import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_CONTENT;
22  import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.RESOURCE_ENTRY;
23  import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.compileBaseUrl;
24  import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.compileUrl;
25  import static org.apache.chemistry.opencmis.server.impl.atompub.AtomPubUtils.writeObjectEntry;
26  import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getBooleanParameter;
27  import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getEnumParameter;
28  import static org.apache.chemistry.opencmis.server.shared.HttpUtils.getStringParameter;
29  
30  import java.io.BufferedInputStream;
31  import java.io.BufferedOutputStream;
32  import java.io.InputStream;
33  import java.io.OutputStream;
34  import java.io.PrintWriter;
35  import java.math.BigInteger;
36  import java.util.Map;
37  
38  import javax.servlet.http.HttpServletRequest;
39  import javax.servlet.http.HttpServletResponse;
40  
41  import org.apache.chemistry.opencmis.commons.PropertyIds;
42  import org.apache.chemistry.opencmis.commons.data.AllowableActions;
43  import org.apache.chemistry.opencmis.commons.data.ContentStream;
44  import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData;
45  import org.apache.chemistry.opencmis.commons.data.ObjectData;
46  import org.apache.chemistry.opencmis.commons.data.Properties;
47  import org.apache.chemistry.opencmis.commons.data.PropertyData;
48  import org.apache.chemistry.opencmis.commons.data.PropertyString;
49  import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
50  import org.apache.chemistry.opencmis.commons.enums.UnfileObject;
51  import org.apache.chemistry.opencmis.commons.enums.VersioningState;
52  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
53  import org.apache.chemistry.opencmis.commons.impl.Constants;
54  import org.apache.chemistry.opencmis.commons.impl.MimeHelper;
55  import org.apache.chemistry.opencmis.commons.impl.ReturnVersion;
56  import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
57  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
58  import org.apache.chemistry.opencmis.commons.server.CallContext;
59  import org.apache.chemistry.opencmis.commons.server.CmisService;
60  import org.apache.chemistry.opencmis.commons.server.ObjectInfo;
61  import org.apache.chemistry.opencmis.commons.spi.Holder;
62  
63  /**
64   * Object Service operations.
65   */
66  public final class ObjectService {
67  
68      private static final int BUFFER_SIZE = 64 * 1024;
69  
70      private ObjectService() {
71      }
72  
73      /**
74       * Create.
75       */
76      public static void create(CallContext context, CmisService service, String repositoryId,
77              HttpServletRequest request, HttpServletResponse response) throws Exception {
78          // get parameters
79          String folderId = getStringParameter(request, Constants.PARAM_ID);
80          String sourceFolderId = getStringParameter(request, Constants.PARAM_SOURCE_FOLDER_ID);
81          VersioningState versioningState = getEnumParameter(request, Constants.PARAM_VERSIONIG_STATE,
82                  VersioningState.class);
83  
84          AtomEntryParser parser = new AtomEntryParser(context.getTempDirectory(), context.getMemoryThreshold());
85          parser.setIgnoreAtomContentSrc(true); // needed for some clients
86          parser.parse(request.getInputStream());
87  
88          String objectId = parser.getId();
89  
90          // execute
91          String newObjectId = null;
92  
93          if (objectId == null) {
94              // create
95              newObjectId = service.create(repositoryId, parser.getProperties(), folderId, parser.getContentStream(),
96                      versioningState, parser.getPolicyIds(), null);
97          } else {
98              if ((sourceFolderId == null) || (sourceFolderId.trim().length() == 0)) {
99                  // addObjectToFolder
100                 service.addObjectToFolder(repositoryId, objectId, folderId, null, null);
101                 newObjectId = objectId;
102             } else {
103                 // move
104                 Holder<String> objectIdHolder = new Holder<String>(objectId);
105                 service.moveObject(repositoryId, objectIdHolder, folderId, sourceFolderId, null);
106                 newObjectId = objectIdHolder.getValue();
107             }
108         }
109 
110         ObjectInfo objectInfo = service.getObjectInfo(repositoryId, newObjectId);
111         if (objectInfo == null) {
112             throw new CmisRuntimeException("Object Info is missing!");
113         }
114 
115         ObjectData object = objectInfo.getObject();
116         if (object == null) {
117             throw new CmisRuntimeException("Object is null!");
118         }
119 
120         // set headers
121         UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
122 
123         response.setStatus(HttpServletResponse.SC_CREATED);
124         response.setContentType(Constants.MEDIATYPE_ENTRY);
125         response.setHeader("Location", compileUrl(baseUrl, RESOURCE_ENTRY, newObjectId));
126 
127         // write XML
128         AtomEntry entry = new AtomEntry();
129         entry.startDocument(response.getOutputStream());
130         writeObjectEntry(service, entry, object, null, repositoryId, null, null, baseUrl, true);
131         entry.endDocument();
132     }
133 
134     /**
135      * Create relationship.
136      */
137     public static void createRelationship(CallContext context, CmisService service, String repositoryId,
138             HttpServletRequest request, HttpServletResponse response) throws Exception {
139         // get parameters
140         AtomEntryParser parser = new AtomEntryParser(request.getInputStream(), context.getTempDirectory(),
141                 context.getMemoryThreshold());
142 
143         // execute
144         String newObjectId = service.createRelationship(repositoryId, parser.getProperties(), parser.getPolicyIds(),
145                 null, null, null);
146 
147         ObjectInfo objectInfo = service.getObjectInfo(repositoryId, newObjectId);
148         if (objectInfo == null) {
149             throw new CmisRuntimeException("Object Info is missing!");
150         }
151 
152         ObjectData object = objectInfo.getObject();
153         if (object == null) {
154             throw new CmisRuntimeException("Object is null!");
155         }
156 
157         // set headers
158         UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
159 
160         response.setStatus(HttpServletResponse.SC_CREATED);
161         response.setContentType(Constants.MEDIATYPE_ENTRY);
162         response.setHeader("Location", compileUrl(baseUrl, RESOURCE_ENTRY, newObjectId));
163 
164         // write XML
165         AtomEntry entry = new AtomEntry();
166         entry.startDocument(response.getOutputStream());
167         writeObjectEntry(service, entry, object, null, repositoryId, null, null, baseUrl, true);
168         entry.endDocument();
169     }
170 
171     /**
172      * Delete object.
173      */
174     public static void deleteObject(CallContext context, CmisService service, String repositoryId,
175             HttpServletRequest request, HttpServletResponse response) {
176         // get parameters
177         String objectId = getStringParameter(request, Constants.PARAM_ID);
178         Boolean allVersions = getBooleanParameter(request, Constants.PARAM_ALL_VERSIONS);
179 
180         // execute
181         service.deleteObjectOrCancelCheckOut(repositoryId, objectId, allVersions, null);
182 
183         // set headers
184         response.setStatus(HttpServletResponse.SC_NO_CONTENT);
185     }
186 
187     /**
188      * Delete content stream.
189      */
190     public static void deleteContentStream(CallContext context, CmisService service, String repositoryId,
191             HttpServletRequest request, HttpServletResponse response) {
192         // get parameters
193         String objectId = getStringParameter(request, Constants.PARAM_ID);
194         String changeToken = getStringParameter(request, Constants.PARAM_CHANGE_TOKEN);
195 
196         // execute
197         service.deleteContentStream(repositoryId, new Holder<String>(objectId), changeToken == null ? null
198                 : new Holder<String>(changeToken), null);
199 
200         // set headers
201         response.setStatus(HttpServletResponse.SC_NO_CONTENT);
202     }
203 
204     /**
205      * Set content stream.
206      */
207     public static void setContentStream(CallContext context, CmisService service, String repositoryId,
208             HttpServletRequest request, HttpServletResponse response) throws Exception {
209         // get parameters
210         String objectId = getStringParameter(request, Constants.PARAM_ID);
211         String changeToken = getStringParameter(request, Constants.PARAM_CHANGE_TOKEN);
212         Boolean overwriteFlag = getBooleanParameter(request, Constants.PARAM_OVERWRITE_FLAG);
213 
214         ContentStreamImpl contentStream = new ContentStreamImpl();
215         contentStream.setStream(request.getInputStream());
216         contentStream.setMimeType(request.getHeader("Content-Type"));
217         String lengthStr = request.getHeader("Content-Length");
218         if (lengthStr != null) {
219             try {
220                 contentStream.setLength(new BigInteger(lengthStr));
221             } catch (NumberFormatException e) {
222             }
223         }
224         String contentDisposition = request.getHeader(MimeHelper.CONTENT_DISPOSITION);
225         if (contentDisposition != null) {
226             contentStream.setFileName(MimeHelper.decodeContentDispositionFilename(contentDisposition));
227         }
228 
229         // execute
230         Holder<String> objectIdHolder = new Holder<String>(objectId);
231         service.setContentStream(repositoryId, objectIdHolder, overwriteFlag, changeToken == null ? null
232                 : new Holder<String>(changeToken), contentStream, null);
233 
234         // set headers
235         String newObjectId = (objectIdHolder.getValue() == null ? objectId : objectIdHolder.getValue());
236         String location = compileUrl(compileBaseUrl(request, repositoryId), RESOURCE_CONTENT, newObjectId);
237 
238         response.setStatus(HttpServletResponse.SC_CREATED);
239         response.setHeader("Content-Location", location);
240         response.setHeader("Location", location);
241     }
242 
243     /**
244      * Delete tree.
245      */
246     public static void deleteTree(CallContext context, CmisService service, String repositoryId,
247             HttpServletRequest request, HttpServletResponse response) throws Exception {
248         // get parameters
249         String folderId = getStringParameter(request, Constants.PARAM_ID);
250         Boolean allVersions = getBooleanParameter(request, Constants.PARAM_ALL_VERSIONS);
251         UnfileObject unfileObjects = getEnumParameter(request, Constants.PARAM_UNFILE_OBJECTS, UnfileObject.class);
252         Boolean continueOnFailure = getBooleanParameter(request, Constants.PARAM_CONTINUE_ON_FAILURE);
253 
254         // execute
255         FailedToDeleteData ftd = service.deleteTree(repositoryId, folderId, allVersions, unfileObjects,
256                 continueOnFailure, null);
257 
258         if ((ftd != null) && (ftd.getIds() != null) && (ftd.getIds().size() > 0)) {
259             // print ids that could not be deleted
260             response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
261             response.setContentType("text/plain");
262 
263             PrintWriter pw = response.getWriter();
264 
265             pw.println("Failed to delete the following objects:");
266             for (String id : ftd.getIds()) {
267                 pw.println(id);
268             }
269 
270             pw.flush();
271 
272             return;
273         }
274 
275         // set headers
276         response.setStatus(HttpServletResponse.SC_NO_CONTENT);
277     }
278 
279     /**
280      * getObject.
281      */
282     public static void getObject(CallContext context, CmisService service, String repositoryId,
283             HttpServletRequest request, HttpServletResponse response) throws Exception {
284         // get parameters
285         String objectId = getStringParameter(request, Constants.PARAM_ID);
286         ReturnVersion returnVersion = getEnumParameter(request, Constants.PARAM_RETURN_VERSION, ReturnVersion.class);
287         String filter = getStringParameter(request, Constants.PARAM_FILTER);
288         Boolean includeAllowableActions = getBooleanParameter(request, Constants.PARAM_ALLOWABLE_ACTIONS);
289         IncludeRelationships includeRelationships = getEnumParameter(request, Constants.PARAM_RELATIONSHIPS,
290                 IncludeRelationships.class);
291         String renditionFilter = getStringParameter(request, Constants.PARAM_RENDITION_FILTER);
292         Boolean includePolicyIds = getBooleanParameter(request, Constants.PARAM_POLICY_IDS);
293         Boolean includeAcl = getBooleanParameter(request, Constants.PARAM_ACL);
294 
295         // execute
296         ObjectData object = null;
297 
298         if ((returnVersion == ReturnVersion.LATEST) || (returnVersion == ReturnVersion.LASTESTMAJOR)) {
299             object = service.getObjectOfLatestVersion(repositoryId, objectId, null,
300                     returnVersion == ReturnVersion.LASTESTMAJOR, filter, includeAllowableActions, includeRelationships,
301                     renditionFilter, includePolicyIds, includeAcl, null);
302         } else {
303             object = service.getObject(repositoryId, objectId, filter, includeAllowableActions, includeRelationships,
304                     renditionFilter, includePolicyIds, includeAcl, null);
305         }
306 
307         if (object == null) {
308             throw new CmisRuntimeException("Object is null!");
309         }
310 
311         ObjectInfo objectInfo = service.getObjectInfo(repositoryId, objectId);
312         if (objectInfo == null) {
313             throw new CmisRuntimeException("Object Info is missing!");
314         }
315 
316         // set headers
317         response.setStatus(HttpServletResponse.SC_OK);
318         response.setContentType(Constants.MEDIATYPE_ENTRY);
319 
320         // write XML
321         UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
322 
323         AtomEntry entry = new AtomEntry();
324         entry.startDocument(response.getOutputStream());
325         writeObjectEntry(service, entry, object, null, repositoryId, null, null, baseUrl, true);
326         entry.endDocument();
327     }
328 
329     /**
330      * objectByPath URI template.
331      */
332     public static void getObjectByPath(CallContext context, CmisService service, String repositoryId,
333             HttpServletRequest request, HttpServletResponse response) throws Exception {
334         // get parameters
335         String path = getStringParameter(request, Constants.PARAM_PATH);
336         String filter = getStringParameter(request, Constants.PARAM_FILTER);
337         Boolean includeAllowableActions = getBooleanParameter(request, Constants.PARAM_ALLOWABLE_ACTIONS);
338         IncludeRelationships includeRelationships = getEnumParameter(request, Constants.PARAM_RELATIONSHIPS,
339                 IncludeRelationships.class);
340         String renditionFilter = getStringParameter(request, Constants.PARAM_RENDITION_FILTER);
341         Boolean includePolicyIds = getBooleanParameter(request, Constants.PARAM_POLICY_IDS);
342         Boolean includeAcl = getBooleanParameter(request, Constants.PARAM_ACL);
343 
344         // execute
345         ObjectData object = service.getObjectByPath(repositoryId, path, filter, includeAllowableActions,
346                 includeRelationships, renditionFilter, includePolicyIds, includeAcl, null);
347 
348         if (object == null) {
349             throw new CmisRuntimeException("Object is null!");
350         }
351 
352         ObjectInfo objectInfo = service.getObjectInfo(repositoryId, object.getId());
353         if (objectInfo == null) {
354             throw new CmisRuntimeException("Object Info is missing!");
355         }
356 
357         // set headers
358         response.setStatus(HttpServletResponse.SC_OK);
359         response.setContentType(Constants.MEDIATYPE_ENTRY);
360 
361         // write XML
362         UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
363 
364         AtomEntry entry = new AtomEntry();
365         entry.startDocument(response.getOutputStream());
366         writeObjectEntry(service, entry, object, null, repositoryId, null, null, baseUrl, true);
367         entry.endDocument();
368     }
369 
370     /**
371      * Allowable Actions.
372      */
373     public static void getAllowableActions(CallContext context, CmisService service, String repositoryId,
374             HttpServletRequest request, HttpServletResponse response) throws Exception {
375         // get parameters
376         String objectId = getStringParameter(request, Constants.PARAM_ID);
377 
378         // execute
379         AllowableActions allowableActions = service.getAllowableActions(repositoryId, objectId, null);
380 
381         if (allowableActions == null) {
382             throw new CmisRuntimeException("Allowable Actions is null!");
383         }
384 
385         // set headers
386         response.setStatus(HttpServletResponse.SC_OK);
387         response.setContentType(Constants.MEDIATYPE_ALLOWABLEACTION);
388 
389         // write XML
390         AllowableActionsDocument allowableActionsDocument = new AllowableActionsDocument();
391         allowableActionsDocument.writeAllowableActions(allowableActions, response.getOutputStream());
392     }
393 
394     /**
395      * getContentStream.
396      */
397     public static void getContentStream(CallContext context, CmisService service, String repositoryId,
398             HttpServletRequest request, HttpServletResponse response) throws Exception {
399         // get parameters
400         String objectId = getStringParameter(request, Constants.PARAM_ID);
401         String streamId = getStringParameter(request, Constants.PARAM_STREAM_ID);
402 
403         BigInteger offset = context.getOffset();
404         BigInteger length = context.getLength();
405 
406         // execute
407         ContentStream content = service.getContentStream(repositoryId, objectId, streamId, offset, length, null);
408 
409         if ((content == null) || (content.getStream() == null)) {
410             throw new CmisRuntimeException("Content stream is null!");
411         }
412 
413         String contentType = content.getMimeType();
414         if (contentType == null) {
415             contentType = Constants.MEDIATYPE_OCTETSTREAM;
416         }
417 
418         // set headers
419         if ((offset == null) && (length == null)) {
420             response.setStatus(HttpServletResponse.SC_OK);
421         } else {
422             response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
423         }
424         response.setContentType(contentType);
425 
426         // send content
427         InputStream in = new BufferedInputStream(content.getStream(), BUFFER_SIZE);
428         OutputStream out = new BufferedOutputStream(response.getOutputStream());
429 
430         byte[] buffer = new byte[BUFFER_SIZE];
431         int b;
432         while ((b = in.read(buffer)) > -1) {
433             out.write(buffer, 0, b);
434         }
435 
436         in.close();
437         out.flush();
438     }
439 
440     /**
441      * UpdateProperties.
442      */
443     public static void updateProperties(CallContext context, CmisService service, String repositoryId,
444             HttpServletRequest request, HttpServletResponse response) throws Exception {
445         // get parameters
446         String objectId = getStringParameter(request, Constants.PARAM_ID);
447         Boolean checkin = getBooleanParameter(request, Constants.PARAM_CHECK_IN);
448         String checkinComment = getStringParameter(request, Constants.PARAM_CHECKIN_COMMENT);
449         Boolean major = getBooleanParameter(request, Constants.PARAM_MAJOR);
450 
451         AtomEntryParser parser = new AtomEntryParser(request.getInputStream(), context.getTempDirectory(),
452                 context.getMemoryThreshold());
453 
454         // execute
455         Holder<String> objectIdHolder = new Holder<String>(objectId);
456 
457         if ((checkin != null) && (checkin.booleanValue())) {
458             service.checkIn(repositoryId, objectIdHolder, major, parser.getProperties(), parser.getContentStream(),
459                     checkinComment, parser.getPolicyIds(), null, null, null);
460         } else {
461             String changeToken = extractChangeToken(parser.getProperties());
462 
463             service.updateProperties(repositoryId, objectIdHolder, changeToken == null ? null : new Holder<String>(
464                     changeToken), parser.getProperties(), null);
465         }
466 
467         ObjectInfo objectInfo = service.getObjectInfo(repositoryId, objectIdHolder.getValue());
468         if (objectInfo == null) {
469             throw new CmisRuntimeException("Object Info is missing!");
470         }
471 
472         ObjectData object = objectInfo.getObject();
473         if (object == null) {
474             throw new CmisRuntimeException("Object is null!");
475         }
476 
477         // set headers
478         UrlBuilder baseUrl = compileBaseUrl(request, repositoryId);
479         String location = compileUrl(baseUrl, RESOURCE_ENTRY, objectIdHolder.getValue());
480 
481         response.setStatus(HttpServletResponse.SC_CREATED);
482         response.setContentType(Constants.MEDIATYPE_ENTRY);
483         response.setHeader("Content-Location", location);
484         response.setHeader("Location", location);
485 
486         // write XML
487         AtomEntry entry = new AtomEntry();
488         entry.startDocument(response.getOutputStream());
489         writeObjectEntry(service, entry, object, null, repositoryId, null, null, baseUrl, true);
490         entry.endDocument();
491     }
492 
493     /**
494      * Gets the change token from a property set.
495      */
496     private static String extractChangeToken(Properties properties) {
497         if (properties == null) {
498             return null;
499         }
500 
501         Map<String, PropertyData<?>> propertiesMap = properties.getProperties();
502         if (propertiesMap == null) {
503             return null;
504         }
505 
506         PropertyData<?> changeLogProperty = propertiesMap.get(PropertyIds.CHANGE_TOKEN);
507         if (!(changeLogProperty instanceof PropertyString)) {
508             return null;
509         }
510 
511         return ((PropertyString) changeLogProperty).getFirstValue();
512     }
513 }