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.tck.tests.crud;
20  
21  import static org.apache.chemistry.opencmis.tck.CmisTestResultStatus.FAILURE;
22  import static org.apache.chemistry.opencmis.tck.CmisTestResultStatus.INFO;
23  import static org.apache.chemistry.opencmis.tck.CmisTestResultStatus.SKIPPED;
24  import static org.apache.chemistry.opencmis.tck.CmisTestResultStatus.WARNING;
25  
26  import java.io.ByteArrayInputStream;
27  import java.math.BigInteger;
28  import java.util.HashMap;
29  import java.util.List;
30  import java.util.Map;
31  
32  import org.apache.chemistry.opencmis.client.api.Document;
33  import org.apache.chemistry.opencmis.client.api.Folder;
34  import org.apache.chemistry.opencmis.client.api.ObjectId;
35  import org.apache.chemistry.opencmis.client.api.Session;
36  import org.apache.chemistry.opencmis.commons.PropertyIds;
37  import org.apache.chemistry.opencmis.commons.data.ContentStream;
38  import org.apache.chemistry.opencmis.commons.definitions.DocumentTypeDefinition;
39  import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition;
40  import org.apache.chemistry.opencmis.commons.enums.Action;
41  import org.apache.chemistry.opencmis.commons.enums.CapabilityContentStreamUpdates;
42  import org.apache.chemistry.opencmis.commons.enums.Updatability;
43  import org.apache.chemistry.opencmis.commons.exceptions.CmisUpdateConflictException;
44  import org.apache.chemistry.opencmis.commons.impl.IOUtils;
45  import org.apache.chemistry.opencmis.commons.impl.dataobjects.ContentStreamImpl;
46  import org.apache.chemistry.opencmis.tck.impl.AbstractSessionTest;
47  
48  public class ChangeTokenTest extends AbstractSessionTest {
49  
50      @Override
51      public void init(Map<String, String> parameters) {
52          super.init(parameters);
53          setName("Change Token Test");
54          setDescription("Creates a document and a folder and updates them with an outdated change token.");
55      }
56  
57      @Override
58      public void run(Session session) {
59          // create a test folder
60          Folder testFolder = createTestFolder(session);
61  
62          try {
63              // update document properties test
64              runUpdateDocumentTest(session, testFolder);
65  
66              // content update test
67              runContentTest(session, testFolder);
68  
69              // update folder properties test
70              runUpdateFolderTest(session, testFolder);
71  
72              // add child and update folder properties test
73              runAddChildFolderTest(session, testFolder);
74          } finally {
75              // delete the test folder
76              deleteTestFolder();
77          }
78      }
79  
80      private void runUpdateDocumentTest(Session session, Folder testFolder) {
81          Document doc = createDocument(session, testFolder, "update1.txt", "Hello World!");
82  
83          try {
84              if (doc.getChangeToken() == null) {
85                  addResult(createResult(SKIPPED,
86                          "Repository does not provide change tokens for documents. Test skipped!"));
87                  return;
88              }
89  
90              DocumentTypeDefinition type = (DocumentTypeDefinition) doc.getType();
91              PropertyDefinition<?> namePropDef = type.getPropertyDefinitions().get(PropertyIds.NAME);
92              if (namePropDef.getUpdatability() == Updatability.WHENCHECKEDOUT
93                      || !doc.getAllowableActions().getAllowableActions().contains(Action.CAN_UPDATE_PROPERTIES)) {
94                  addResult(createResult(SKIPPED, "Document name can't be changed. Test skipped!"));
95                  return;
96              }
97  
98              // the first update should succeed
99              Map<String, Object> properties2 = new HashMap<String, Object>();
100             properties2.put(PropertyIds.NAME, "update2.txt");
101             ObjectId newId = doc.updateProperties(properties2, false);
102 
103             if (!doc.getId().equals(newId.getId())) {
104                 // the repository created a new version
105                 // -> a change token test does not make sense
106                 addResult(createResult(INFO,
107                         "The repository created a new version. Change tokens are not relevant here."));
108             } else {
109                 try {
110                     Map<String, Object> properties3 = new HashMap<String, Object>();
111                     properties3.put(PropertyIds.NAME, "update3.txt");
112                     doc.updateProperties(properties3, false);
113 
114                     addResult(createResult(FAILURE, "Updating properties a second time with the same change token "
115                             + "should result in an UpdateConflict exception!"));
116                 } catch (CmisUpdateConflictException e) {
117                     // expected exception
118                 }
119             }
120         } finally {
121             deleteObject(doc);
122         }
123     }
124 
125     private void runContentTest(Session session, Folder testFolder) {
126         if (session.getRepositoryInfo().getCapabilities().getContentStreamUpdatesCapability() != CapabilityContentStreamUpdates.ANYTIME) {
127             addResult(createResult(SKIPPED, "Repository doesn't allow to replace content. Test skipped!"));
128             return;
129         }
130 
131         Document doc = createDocument(session, testFolder, "content1.txt", "Hello World!");
132 
133         try {
134             if (doc.getChangeToken() == null) {
135                 addResult(createResult(SKIPPED,
136                         "Repository does not provide change tokens for documents. Test skipped!"));
137                 return;
138             }
139 
140             if (!doc.getAllowableActions().getAllowableActions().contains(Action.CAN_SET_CONTENT_STREAM)) {
141                 addResult(createResult(SKIPPED, "Document content can't be changed. Test skipped!"));
142                 return;
143             }
144 
145             byte[] contentBytes = IOUtils.toUTF8Bytes("New content");
146             ContentStream contentStream = new ContentStreamImpl("content2.txt",
147                     BigInteger.valueOf(contentBytes.length), "text/plain", new ByteArrayInputStream(contentBytes));
148 
149             ObjectId newId = doc.setContentStream(contentStream, true, false);
150 
151             if (newId == null) {
152                 // the AtomPub binding does not return an id here
153                 // -> get the latest id from the version series
154                 if (Boolean.TRUE.equals(((DocumentTypeDefinition) doc.getType()).isVersionable())) {
155                     List<Document> versions = doc.getAllVersions();
156                     if (versions == null || versions.size() < 1) {
157                         addResult(createResult(FAILURE, "Repository returned an empty list of document versions!"));
158                     } else {
159                         // the latest document is at the top of the list
160                         newId = versions.get(0);
161                     }
162                 } else {
163                     // the document type is not versionable
164                     // -> the repository couldn't create a new version
165                     newId = doc;
166                 }
167             }
168 
169             if (newId != null) {
170                 if (!doc.getId().equals(newId.getId())) {
171                     // the repository created a new version
172                     // -> a change token test does not make sense
173                     addResult(createResult(INFO,
174                             "The repository created a new version. Change tokens are not relevant here."));
175                 } else {
176                     try {
177                         doc.setContentStream(contentStream, true, false);
178 
179                         addResult(createResult(FAILURE, "Updating content a second time with the same change token "
180                                 + "should result in an UpdateConflict exception!"));
181                     } catch (CmisUpdateConflictException uce) {
182                         // expected exception
183                     }
184                 }
185             }
186         } finally {
187             deleteObject(doc);
188         }
189     }
190 
191     private void runUpdateFolderTest(Session session, Folder testFolder) {
192         Folder folder = createFolder(session, testFolder, "folder1");
193 
194         try {
195             if (folder.getChangeToken() == null) {
196                 addResult(createResult(SKIPPED, "Repository does not provide change tokens for folders. Test skipped!"));
197                 return;
198             }
199 
200             if (!folder.getAllowableActions().getAllowableActions().contains(Action.CAN_UPDATE_PROPERTIES)) {
201                 addResult(createResult(SKIPPED, "Folder name can't be changed. Test skipped!"));
202                 return;
203             }
204 
205             // the first update should succeed
206             Map<String, Object> properties2 = new HashMap<String, Object>();
207             properties2.put(PropertyIds.NAME, "folder2");
208             folder.updateProperties(properties2, false);
209 
210             try {
211                 Map<String, Object> properties3 = new HashMap<String, Object>();
212                 properties3.put(PropertyIds.NAME, "folder3");
213                 folder.updateProperties(properties3, false);
214 
215                 addResult(createResult(FAILURE, "Updating properties a second time with the same change token "
216                         + "should result in an UpdateConflict exception!"));
217             } catch (CmisUpdateConflictException e) {
218                 // expected exception
219             }
220 
221         } finally {
222             deleteObject(folder);
223         }
224     }
225 
226     private void runAddChildFolderTest(Session session, Folder testFolder) {
227         Folder folder = createFolder(session, testFolder, "folder1");
228 
229         try {
230             if (folder.getChangeToken() == null) {
231                 addResult(createResult(SKIPPED, "Repository does not provide change tokens for folders. Test skipped!"));
232                 return;
233             }
234 
235             if (!folder.getAllowableActions().getAllowableActions().contains(Action.CAN_UPDATE_PROPERTIES)) {
236                 addResult(createResult(SKIPPED, "Folder name can't be changed. Test skipped!"));
237                 return;
238             }
239 
240             createDocument(session, folder, "doc1", "content");
241 
242             try {
243                 Map<String, Object> properties2 = new HashMap<String, Object>();
244                 properties2.put(PropertyIds.NAME, "folder2");
245                 folder.updateProperties(properties2, false);
246             } catch (CmisUpdateConflictException e) {
247                 addResult(createResult(WARNING, "Adding a child to a folder changes the change token of the folder. "
248                         + "CMIS clients might not expect that."));
249             }
250 
251         } finally {
252             deleteObject(folder);
253         }
254     }
255 }