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.io.IOException;
24  import java.io.InputStream;
25  import java.io.OutputStream;
26  import java.math.BigInteger;
27  import java.util.ArrayList;
28  import java.util.EnumSet;
29  import java.util.HashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.Set;
33  
34  import org.apache.chemistry.opencmis.client.api.CmisObject;
35  import org.apache.chemistry.opencmis.client.api.Document;
36  import org.apache.chemistry.opencmis.client.api.DocumentType;
37  import org.apache.chemistry.opencmis.client.api.ObjectFactory;
38  import org.apache.chemistry.opencmis.client.api.ObjectId;
39  import org.apache.chemistry.opencmis.client.api.ObjectType;
40  import org.apache.chemistry.opencmis.client.api.OperationContext;
41  import org.apache.chemistry.opencmis.client.api.Policy;
42  import org.apache.chemistry.opencmis.client.api.Property;
43  import org.apache.chemistry.opencmis.client.bindings.spi.LinkAccess;
44  import org.apache.chemistry.opencmis.client.runtime.util.AppendOutputStream;
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.ContentStreamHash;
49  import org.apache.chemistry.opencmis.commons.data.ObjectData;
50  import org.apache.chemistry.opencmis.commons.data.PartialContentStream;
51  import org.apache.chemistry.opencmis.commons.enums.CmisVersion;
52  import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
53  import org.apache.chemistry.opencmis.commons.enums.Updatability;
54  import org.apache.chemistry.opencmis.commons.enums.VersioningState;
55  import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
56  import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
57  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamHashImpl;
58  import org.apache.chemistry.opencmis.commons.spi.Holder;
59  
60  public class DocumentImpl extends AbstractFilableCmisObject implements Document {
61  
62      private static final long serialVersionUID = 1L;
63  
64      /**
65       * Constructor.
66       */
67      public DocumentImpl(SessionImpl session, ObjectType objectType, ObjectData objectData, OperationContext context) {
68          initialize(session, objectType, objectData, context);
69      }
70  
71      @Override
72      public DocumentType getDocumentType() {
73          ObjectType objectType = super.getType();
74          if (objectType instanceof DocumentType) {
75              return (DocumentType) objectType;
76          } else {
77              throw new ClassCastException("Object type is not a document type.");
78          }
79      }
80  
81      @Override
82      public boolean isVersionable() {
83          return Boolean.TRUE.equals(getDocumentType().isVersionable());
84      }
85  
86      @Override
87      public Boolean isVersionSeriesPrivateWorkingCopy() {
88          if (Boolean.FALSE.equals(getDocumentType().isVersionable())) {
89              return false;
90          }
91  
92          Boolean isCheckedOut = isVersionSeriesCheckedOut();
93          if (Boolean.FALSE.equals(isCheckedOut)) {
94              return false;
95          }
96  
97          Boolean isPWC = isPrivateWorkingCopy();
98          if (isPWC != null) {
99              return isPWC;
100         }
101 
102         String vsCoId = getVersionSeriesCheckedOutId();
103         if (vsCoId == null) {
104             // we don't know ...
105             return null;
106         }
107 
108         return vsCoId.equals(getId());
109     }
110 
111     // properties
112 
113     @Override
114     public String getCheckinComment() {
115         return getPropertyValue(PropertyIds.CHECKIN_COMMENT);
116     }
117 
118     @Override
119     public String getVersionLabel() {
120         return getPropertyValue(PropertyIds.VERSION_LABEL);
121     }
122 
123     @Override
124     public String getVersionSeriesId() {
125         return getPropertyValue(PropertyIds.VERSION_SERIES_ID);
126     }
127 
128     @Override
129     public String getVersionSeriesCheckedOutId() {
130         return getPropertyValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_ID);
131     }
132 
133     @Override
134     public String getVersionSeriesCheckedOutBy() {
135         return getPropertyValue(PropertyIds.VERSION_SERIES_CHECKED_OUT_BY);
136     }
137 
138     @Override
139     public Boolean isImmutable() {
140         return getPropertyValue(PropertyIds.IS_IMMUTABLE);
141     }
142 
143     @Override
144     public Boolean isLatestMajorVersion() {
145         return getPropertyValue(PropertyIds.IS_LATEST_MAJOR_VERSION);
146     }
147 
148     @Override
149     public Boolean isLatestVersion() {
150         return getPropertyValue(PropertyIds.IS_LATEST_VERSION);
151     }
152 
153     @Override
154     public Boolean isMajorVersion() {
155         return getPropertyValue(PropertyIds.IS_MAJOR_VERSION);
156     }
157 
158     @Override
159     public Boolean isPrivateWorkingCopy() {
160         return getPropertyValue(PropertyIds.IS_PRIVATE_WORKING_COPY);
161     }
162 
163     @Override
164     public Boolean isVersionSeriesCheckedOut() {
165         return getPropertyValue(PropertyIds.IS_VERSION_SERIES_CHECKED_OUT);
166     }
167 
168     @Override
169     public long getContentStreamLength() {
170         BigInteger bigInt = getPropertyValue(PropertyIds.CONTENT_STREAM_LENGTH);
171         return bigInt == null ? (long) -1 : bigInt.longValue();
172     }
173 
174     @Override
175     public String getContentStreamMimeType() {
176         return getPropertyValue(PropertyIds.CONTENT_STREAM_MIME_TYPE);
177     }
178 
179     @Override
180     public String getContentStreamFileName() {
181         return getPropertyValue(PropertyIds.CONTENT_STREAM_FILE_NAME);
182     }
183 
184     @Override
185     public String getContentStreamId() {
186         return getPropertyValue(PropertyIds.CONTENT_STREAM_ID);
187     }
188 
189     @Override
190     public List<ContentStreamHash> getContentStreamHashes() {
191         List<String> hashes = getPropertyValue(PropertyIds.CONTENT_STREAM_HASH);
192         if (isNullOrEmpty(hashes)) {
193             return null;
194         }
195 
196         List<ContentStreamHash> result = new ArrayList<ContentStreamHash>(hashes.size());
197         for (String hash : hashes) {
198             result.add(new ContentStreamHashImpl(hash));
199         }
200 
201         return result;
202     }
203 
204     @Override
205     public String getLatestAccessibleStateId() {
206         return getPropertyValue(PropertyIds.LATEST_ACCESSIBLE_STATE_ID);
207     }
208 
209     // operations
210 
211     @Override
212     public Document copy(ObjectId targetFolderId, Map<String, ?> properties, VersioningState versioningState,
213             List<Policy> policies, List<Ace> addAces, List<Ace> removeAces, OperationContext context) {
214 
215         ObjectId newId = null;
216         try {
217             newId = getSession().createDocumentFromSource(this, properties, targetFolderId, versioningState, policies,
218                     addAces, removeAces);
219         } catch (CmisNotSupportedException nse) {
220             newId = copyViaClient(targetFolderId, properties, versioningState, policies, addAces, removeAces);
221         }
222 
223         // if no context is provided the object will not be fetched
224         if (context == null || newId == null) {
225             return null;
226         }
227         // get the new object
228         CmisObject object = getSession().getObject(newId, context);
229         if (!(object instanceof Document)) {
230             throw new CmisRuntimeException("Newly created object is not a document! New id: " + newId);
231         }
232 
233         return (Document) object;
234     }
235 
236     @Override
237     public Document copy(ObjectId targetFolderId) {
238         return copy(targetFolderId, null, null, null, null, null, getSession().getDefaultContext());
239     }
240 
241     /**
242      * Copies the document manually. The content is streamed from the repository
243      * and back.
244      */
245     protected ObjectId copyViaClient(ObjectId targetFolderId, Map<String, ?> properties,
246             VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces) {
247         Map<String, Object> newProperties = new HashMap<String, Object>();
248 
249         OperationContext allPropsContext = getSession().createOperationContext();
250         allPropsContext.setFilterString("*");
251         allPropsContext.setIncludeAcls(false);
252         allPropsContext.setIncludeAllowableActions(false);
253         allPropsContext.setIncludePathSegments(false);
254         allPropsContext.setIncludePolicies(false);
255         allPropsContext.setIncludeRelationships(IncludeRelationships.NONE);
256         allPropsContext.setRenditionFilterString("cmis:none");
257 
258         Document allPropsDoc = (Document) getSession().getObject(this, allPropsContext);
259 
260         for (Property<?> prop : allPropsDoc.getProperties()) {
261             if (prop.getDefinition().getUpdatability() == Updatability.READWRITE
262                     || prop.getDefinition().getUpdatability() == Updatability.ONCREATE) {
263                 newProperties.put(prop.getId(), prop.getValue());
264             }
265         }
266 
267         if (properties != null) {
268             newProperties.putAll(properties);
269         }
270 
271         ContentStream contentStream = allPropsDoc.getContentStream();
272         try {
273             return getSession().createDocument(newProperties, targetFolderId, contentStream, versioningState, policies,
274                     addAces, removeAces);
275         } finally {
276             if (contentStream != null) {
277                 InputStream stream = contentStream.getStream();
278                 if (stream != null) {
279                     try {
280                         stream.close();
281                     } catch (IOException ioe) {
282                         throw new CmisRuntimeException("Cannot close source stream!", ioe);
283                     }
284                 }
285             }
286         }
287     }
288 
289     @Override
290     public void deleteAllVersions() {
291         delete(true);
292     }
293 
294     // versioning
295 
296     @Override
297     public ObjectId checkOut() {
298         String newObjectId = null;
299 
300         readLock();
301         try {
302             String objectId = getObjectId();
303             Holder<String> objectIdHolder = new Holder<String>(objectId);
304 
305             getBinding().getVersioningService().checkOut(getRepositoryId(), objectIdHolder, null, null);
306             newObjectId = objectIdHolder.getValue();
307         } finally {
308             readUnlock();
309         }
310 
311         // remove original version from cache, the path and a few versioning
312         // properties are not valid anymore
313         getSession().removeObjectFromCache(this);
314 
315         if (newObjectId == null) {
316             return null;
317         }
318 
319         return getSession().createObjectId(newObjectId);
320     }
321 
322     @Override
323     public void cancelCheckOut() {
324         String objectId = getObjectId();
325 
326         getBinding().getVersioningService().cancelCheckOut(getRepositoryId(), objectId, null);
327 
328         // remove PWC from cache, it doesn't exist anymore
329         getSession().removeObjectFromCache(this);
330     }
331 
332     @Override
333     public ObjectId checkIn(boolean major, Map<String, ?> properties, ContentStream contentStream,
334             String checkinComment, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces) {
335         String newObjectId = null;
336 
337         readLock();
338         try {
339             Holder<String> objectIdHolder = new Holder<String>(getObjectId());
340 
341             ObjectFactory of = getObjectFactory();
342 
343             Set<Updatability> updatebility = EnumSet.noneOf(Updatability.class);
344             updatebility.add(Updatability.READWRITE);
345             updatebility.add(Updatability.WHENCHECKEDOUT);
346 
347             getBinding().getVersioningService().checkIn(getRepositoryId(), objectIdHolder, major,
348                     of.convertProperties(properties, getType(), getSecondaryTypes(), updatebility),
349                     of.convertContentStream(contentStream), checkinComment, of.convertPolicies(policies),
350                     of.convertAces(addAces), of.convertAces(removeAces), null);
351 
352             newObjectId = objectIdHolder.getValue();
353         } finally {
354             readUnlock();
355         }
356 
357         // remove PWC from cache, it doesn't exist anymore
358         getSession().removeObjectFromCache(this);
359 
360         if (newObjectId == null) {
361             return null;
362         }
363 
364         return getSession().createObjectId(newObjectId);
365     }
366 
367     @Override
368     public List<Document> getAllVersions() {
369         return getAllVersions(getSession().getDefaultContext());
370     }
371 
372     @Override
373     public List<Document> getAllVersions(OperationContext context) {
374         String objectId;
375         String versionSeriesId;
376 
377         readLock();
378         try {
379             objectId = getObjectId();
380             versionSeriesId = getVersionSeriesId();
381         } finally {
382             readUnlock();
383         }
384 
385         List<ObjectData> versions = getBinding().getVersioningService().getAllVersions(getRepositoryId(), objectId,
386                 versionSeriesId, context.getFilterString(), context.isIncludeAllowableActions(), null);
387 
388         ObjectFactory objectFactory = getSession().getObjectFactory();
389 
390         List<Document> result = new ArrayList<Document>();
391         if (versions != null) {
392             for (ObjectData objectData : versions) {
393                 CmisObject doc = objectFactory.convertObject(objectData, context);
394                 if (!(doc instanceof Document)) {
395                     // should not happen...
396                     continue;
397                 }
398 
399                 result.add((Document) doc);
400             }
401         }
402 
403         return result;
404 
405     }
406 
407     @Override
408     public Document getObjectOfLatestVersion(boolean major) {
409         return getObjectOfLatestVersion(major, getSession().getDefaultContext());
410     }
411 
412     @Override
413     public Document getObjectOfLatestVersion(boolean major, OperationContext context) {
414         return getSession().getLatestDocumentVersion(this, major, context);
415     }
416 
417     // content operations
418 
419     @Override
420     public ContentStream getContentStream() {
421         return getContentStream(null, null, null);
422     }
423 
424     @Override
425     public ContentStream getContentStream(BigInteger offset, BigInteger length) {
426         return getContentStream(null, offset, length);
427     }
428 
429     @Override
430     public ContentStream getContentStream(String streamId) {
431         return getContentStream(streamId, null, null);
432     }
433 
434     @Override
435     public ContentStream getContentStream(String streamId, BigInteger offset, BigInteger length) {
436         // get the stream
437         ContentStream contentStream = getSession().getContentStream(this, streamId, offset, length);
438 
439         if (contentStream == null) {
440             return null;
441         }
442 
443         // the AtomPub binding doesn't return a file name
444         // -> get the file name from properties, if present
445         String filename = contentStream.getFileName();
446         if (filename == null) {
447             filename = getContentStreamFileName();
448         }
449 
450         long lengthLong = (contentStream.getBigLength() == null ? -1 : contentStream.getBigLength().longValue());
451 
452         // convert and return stream object
453         return getSession().getObjectFactory().createContentStream(filename, lengthLong, contentStream.getMimeType(),
454                 contentStream.getStream(), contentStream instanceof PartialContentStream);
455     }
456 
457     @Override
458     public String getContentUrl() {
459         return getContentUrl(null);
460     }
461 
462     @Override
463     public String getContentUrl(String streamId) {
464         if (getBinding().getObjectService() instanceof LinkAccess) {
465             LinkAccess linkAccess = (LinkAccess) getBinding().getObjectService();
466 
467             if (streamId == null) {
468                 return linkAccess.loadContentLink(getRepositoryId(), getId());
469             } else {
470                 return linkAccess.loadRenditionContentLink(getRepositoryId(), getId(), streamId);
471             }
472         }
473 
474         return null;
475     }
476 
477     @Override
478     public Document setContentStream(ContentStream contentStream, boolean overwrite) {
479         ObjectId objectId = setContentStream(contentStream, overwrite, true);
480         if (objectId == null) {
481             return null;
482         }
483 
484         if (!getObjectId().equals(objectId.getId())) {
485             return (Document) getSession().getObject(objectId, getCreationContext());
486         }
487 
488         return this;
489     }
490 
491     @Override
492     public ObjectId setContentStream(ContentStream contentStream, boolean overwrite, boolean refresh) {
493         String newObjectId = null;
494 
495         readLock();
496         try {
497             Holder<String> objectIdHolder = new Holder<String>(getObjectId());
498             Holder<String> changeTokenHolder = new Holder<String>((String) getPropertyValue(PropertyIds.CHANGE_TOKEN));
499 
500             getBinding().getObjectService().setContentStream(getRepositoryId(), objectIdHolder, overwrite,
501                     changeTokenHolder, getObjectFactory().convertContentStream(contentStream), null);
502 
503             newObjectId = objectIdHolder.getValue();
504         } finally {
505             readUnlock();
506         }
507 
508         if (refresh) {
509             refresh();
510         }
511 
512         if (newObjectId == null) {
513             return null;
514         }
515 
516         return getSession().createObjectId(newObjectId);
517     }
518 
519     @Override
520     public Document appendContentStream(ContentStream contentStream, boolean isLastChunk) {
521         if (getSession().getRepositoryInfo().getCmisVersion() == CmisVersion.CMIS_1_0) {
522             throw new CmisNotSupportedException("This method is not supported for CMIS 1.0 repositories.");
523         }
524 
525         ObjectId objectId = appendContentStream(contentStream, isLastChunk, true);
526         if (objectId == null) {
527             return null;
528         }
529 
530         if (!getObjectId().equals(objectId.getId())) {
531             return (Document) getSession().getObject(objectId, getCreationContext());
532         }
533 
534         return this;
535     }
536 
537     @Override
538     public ObjectId appendContentStream(ContentStream contentStream, boolean isLastChunk, boolean refresh) {
539         if (getSession().getRepositoryInfo().getCmisVersion() == CmisVersion.CMIS_1_0) {
540             throw new CmisNotSupportedException("This method is not supported for CMIS 1.0 repositories.");
541         }
542 
543         String newObjectId = null;
544 
545         readLock();
546         try {
547             Holder<String> objectIdHolder = new Holder<String>(getObjectId());
548             Holder<String> changeTokenHolder = new Holder<String>((String) getPropertyValue(PropertyIds.CHANGE_TOKEN));
549 
550             getBinding().getObjectService().appendContentStream(getRepositoryId(), objectIdHolder, changeTokenHolder,
551                     getObjectFactory().convertContentStream(contentStream), isLastChunk, null);
552 
553             newObjectId = objectIdHolder.getValue();
554         } finally {
555             readUnlock();
556         }
557 
558         if (refresh) {
559             refresh();
560         }
561 
562         if (newObjectId == null) {
563             return null;
564         }
565 
566         return getSession().createObjectId(newObjectId);
567     }
568 
569     @Override
570     public Document deleteContentStream() {
571         ObjectId objectId = deleteContentStream(true);
572         if (objectId == null) {
573             return null;
574         }
575 
576         if (!getObjectId().equals(objectId.getId())) {
577             return (Document) getSession().getObject(objectId, getCreationContext());
578         }
579 
580         return this;
581     }
582 
583     @Override
584     public ObjectId deleteContentStream(boolean refresh) {
585         String newObjectId = null;
586 
587         readLock();
588         try {
589             Holder<String> objectIdHolder = new Holder<String>(getObjectId());
590             Holder<String> changeTokenHolder = new Holder<String>((String) getPropertyValue(PropertyIds.CHANGE_TOKEN));
591 
592             getBinding().getObjectService().deleteContentStream(getRepositoryId(), objectIdHolder, changeTokenHolder,
593                     null);
594 
595             newObjectId = objectIdHolder.getValue();
596         } finally {
597             readUnlock();
598         }
599 
600         if (refresh) {
601             refresh();
602         }
603 
604         if (newObjectId == null) {
605             return null;
606         }
607 
608         return getSession().createObjectId(newObjectId);
609     }
610 
611     @Override
612     public OutputStream createOverwriteOutputStream(String filename, String mimeType) {
613         if (getSession().getRepositoryInfo().getCmisVersion() == CmisVersion.CMIS_1_0) {
614             throw new CmisNotSupportedException("This method is not supported for CMIS 1.0 repositories.");
615         }
616 
617         return new AppendOutputStream(getSession(), this, true, filename, mimeType);
618     }
619 
620     @Override
621     public OutputStream createOverwriteOutputStream(String filename, String mimeType, int bufferSize) {
622         if (getSession().getRepositoryInfo().getCmisVersion() == CmisVersion.CMIS_1_0) {
623             throw new CmisNotSupportedException("This method is not supported for CMIS 1.0 repositories.");
624         }
625 
626         return new AppendOutputStream(getSession(), this, true, filename, mimeType, bufferSize);
627     }
628 
629     @Override
630     public OutputStream createAppendOutputStream() {
631         if (getSession().getRepositoryInfo().getCmisVersion() == CmisVersion.CMIS_1_0) {
632             throw new CmisNotSupportedException("This method is not supported for CMIS 1.0 repositories.");
633         }
634 
635         return new AppendOutputStream(getSession(), this, false, null, null);
636     }
637 
638     @Override
639     public OutputStream createAppendOutputStream(int bufferSize) {
640         if (getSession().getRepositoryInfo().getCmisVersion() == CmisVersion.CMIS_1_0) {
641             throw new CmisNotSupportedException("This method is not supported for CMIS 1.0 repositories.");
642         }
643 
644         return new AppendOutputStream(getSession(), this, false, null, null, bufferSize);
645     }
646 
647     @Override
648     public ObjectId checkIn(boolean major, Map<String, ?> properties, ContentStream contentStream,
649             String checkinComment) {
650         return this.checkIn(major, properties, contentStream, checkinComment, null, null, null);
651     }
652 }