1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.chemistry.opencmis.client.bindings.spi.atompub;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.util.ArrayList;
24 import java.util.List;
25
26 import javax.xml.stream.XMLStreamException;
27
28 import org.apache.chemistry.opencmis.client.bindings.spi.BindingSession;
29 import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomElement;
30 import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomEntry;
31 import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomFeed;
32 import org.apache.chemistry.opencmis.client.bindings.spi.atompub.objects.AtomLink;
33 import org.apache.chemistry.opencmis.client.bindings.spi.http.Output;
34 import org.apache.chemistry.opencmis.client.bindings.spi.http.Response;
35 import org.apache.chemistry.opencmis.commons.PropertyIds;
36 import org.apache.chemistry.opencmis.commons.SessionParameter;
37 import org.apache.chemistry.opencmis.commons.data.Acl;
38 import org.apache.chemistry.opencmis.commons.data.ContentStream;
39 import org.apache.chemistry.opencmis.commons.data.ExtensionsData;
40 import org.apache.chemistry.opencmis.commons.data.ObjectData;
41 import org.apache.chemistry.opencmis.commons.data.Properties;
42 import org.apache.chemistry.opencmis.commons.data.PropertyData;
43 import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
44 import org.apache.chemistry.opencmis.commons.exceptions.CmisBaseException;
45 import org.apache.chemistry.opencmis.commons.exceptions.CmisConnectionException;
46 import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
47 import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException;
48 import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
49 import org.apache.chemistry.opencmis.commons.impl.Constants;
50 import org.apache.chemistry.opencmis.commons.impl.ReturnVersion;
51 import org.apache.chemistry.opencmis.commons.impl.UrlBuilder;
52 import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
53 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertiesImpl;
54 import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringImpl;
55 import org.apache.chemistry.opencmis.commons.spi.Holder;
56 import org.apache.chemistry.opencmis.commons.spi.VersioningService;
57
58
59
60
61 public class VersioningServiceImpl extends AbstractAtomPubService implements VersioningService {
62
63
64
65
66 public VersioningServiceImpl(BindingSession session) {
67 setSession(session);
68 }
69
70 @Override
71 public void checkOut(String repositoryId, Holder<String> objectId, ExtensionsData extension,
72 Holder<Boolean> contentCopied) {
73 if ((objectId == null) || (objectId.getValue() == null) || (objectId.getValue().length() == 0)) {
74 throw new CmisInvalidArgumentException("Object id must be set!");
75 }
76
77
78 String link = loadCollection(repositoryId, Constants.COLLECTION_CHECKEDOUT);
79
80 if (link == null) {
81 throw new CmisObjectNotFoundException("Unknown repository or checkedout collection not supported!");
82 }
83
84 UrlBuilder url = new UrlBuilder(link);
85
86
87 if (getSession().get(SessionParameter.INCLUDE_OBJECTID_URL_PARAM_ON_CHECKOUT, false)) {
88 url.addParameter("objectId", objectId.getValue());
89 }
90
91
92 final AtomEntryWriter entryWriter = new AtomEntryWriter(createIdObject(objectId.getValue()),
93 getCmisVersion(repositoryId));
94
95
96 Response resp = post(url, Constants.MEDIATYPE_ENTRY, new Output() {
97 @Override
98 public void write(OutputStream out) throws XMLStreamException, IOException {
99 entryWriter.write(out);
100 }
101 });
102
103
104 AtomEntry entry = parse(resp.getStream(), AtomEntry.class);
105
106 objectId.setValue(entry.getId());
107
108 lockLinks();
109 try {
110
111 removeLinks(repositoryId, entry.getId());
112
113
114 for (AtomElement element : entry.getElements()) {
115 if (element.getObject() instanceof AtomLink) {
116 addLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
117 }
118 }
119 } finally {
120 unlockLinks();
121 }
122
123 if (contentCopied != null) {
124 contentCopied.setValue(null);
125 }
126 }
127
128 @Override
129 public void cancelCheckOut(String repositoryId, String objectId, ExtensionsData extension) {
130
131 String link = loadLink(repositoryId, objectId, Constants.REL_SELF, Constants.MEDIATYPE_ENTRY);
132
133 if (link == null) {
134 throwLinkException(repositoryId, objectId, Constants.REL_SELF, Constants.MEDIATYPE_ENTRY);
135 }
136
137
138
139 String wcLink = getLink(repositoryId, objectId, Constants.REL_WORKINGCOPY, Constants.MEDIATYPE_ENTRY);
140 if (wcLink != null) {
141 link = wcLink;
142 }
143
144 delete(new UrlBuilder(link));
145 }
146
147 @Override
148 public void checkIn(String repositoryId, Holder<String> objectId, Boolean major, Properties properties,
149 ContentStream contentStream, String checkinComment, List<String> policies, Acl addAces, Acl removeAces,
150 ExtensionsData extension) {
151
152 if ((objectId == null) || (objectId.getValue() == null) || (objectId.getValue().length() == 0)) {
153 throw new CmisInvalidArgumentException("Object id must be set!");
154 }
155
156
157 String link = loadLink(repositoryId, objectId.getValue(), Constants.REL_SELF, Constants.MEDIATYPE_ENTRY);
158
159 if (link == null) {
160 throwLinkException(repositoryId, objectId.getValue(), Constants.REL_SELF, Constants.MEDIATYPE_ENTRY);
161 }
162
163
164
165 String wcLink = getLink(repositoryId, objectId.getValue(), Constants.REL_WORKINGCOPY, Constants.MEDIATYPE_ENTRY);
166 if (wcLink != null) {
167 link = wcLink;
168 }
169
170 UrlBuilder url = new UrlBuilder(link);
171 url.addParameter(Constants.PARAM_CHECKIN_COMMENT, checkinComment);
172 url.addParameter(Constants.PARAM_MAJOR, major);
173 url.addParameter(Constants.PARAM_CHECK_IN, "true");
174
175
176 if (getSession().get(SessionParameter.ADD_NAME_ON_CHECK_IN, false)) {
177 if (properties == null || properties.getPropertyList().isEmpty()) {
178 properties = new PropertiesImpl();
179
180 try {
181 String name = null;
182
183
184 ObjectData obj = getObjectInternal(repositoryId, IdentifierType.ID, objectId.getValue(),
185 ReturnVersion.THIS, "cmis:objectId,cmis:name", Boolean.FALSE, IncludeRelationships.NONE,
186 "cmis:none", Boolean.FALSE, Boolean.FALSE, null);
187
188 if (obj != null && obj.getProperties() != null && obj.getProperties().getProperties() != null
189 && obj.getProperties().getProperties().get(PropertyIds.NAME) != null) {
190 PropertyData<?> nameProp = obj.getProperties().getProperties().get(PropertyIds.NAME);
191 if (nameProp.getFirstValue() instanceof String) {
192 name = (String) nameProp.getFirstValue();
193 }
194 }
195
196 if (name == null) {
197 throw new CmisRuntimeException("Could not determine the name of the PWC!");
198 }
199
200
201
202
203 ((PropertiesImpl) properties).addProperty(new PropertyStringImpl(PropertyIds.NAME, name));
204 } catch (CmisBaseException e) {
205 throw new CmisRuntimeException("Could not determine the name of the PWC: " + e.toString(), e);
206 }
207 }
208 }
209
210
211 final AtomEntryWriter entryWriter = new AtomEntryWriter(createObject(properties, null, policies),
212 getCmisVersion(repositoryId), contentStream);
213
214
215 Response resp = put(url, Constants.MEDIATYPE_ENTRY, new Output() {
216 @Override
217 public void write(OutputStream out) throws XMLStreamException, IOException {
218 entryWriter.write(out);
219 }
220 });
221
222
223 AtomEntry entry = parse(resp.getStream(), AtomEntry.class);
224
225
226 if (entry.getId() == null) {
227 throw new CmisConnectionException("Received Atom entry is not a CMIS entry!");
228 }
229
230
231 objectId.setValue(entry.getId());
232
233 AccessControlListImpl originalAces = null;
234
235 lockLinks();
236 try {
237
238 removeLinks(repositoryId, entry.getId());
239
240
241 for (AtomElement element : entry.getElements()) {
242 if (element.getObject() instanceof AtomLink) {
243 addLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
244 } else if (element.getObject() instanceof ObjectData) {
245
246 ObjectData object = (ObjectData) element.getObject();
247 if (object.getAcl() != null) {
248 originalAces = new AccessControlListImpl(object.getAcl().getAces());
249 originalAces.setExact(object.isExactAcl());
250 }
251 }
252 }
253 } finally {
254 unlockLinks();
255 }
256
257
258 if ((originalAces != null) && (isAclMergeRequired(addAces, removeAces))) {
259
260 Acl newACL = mergeAcls(originalAces, addAces, removeAces);
261 if (newACL != null) {
262 updateAcl(repositoryId, entry.getId(), newACL, null);
263 }
264 }
265 }
266
267 @Override
268 public List<ObjectData> getAllVersions(String repositoryId, String objectId, String versionSeriesId, String filter,
269 Boolean includeAllowableActions, ExtensionsData extension) {
270 List<ObjectData> result = new ArrayList<ObjectData>();
271
272
273 String link = loadLink(repositoryId, objectId, Constants.REL_VERSIONHISTORY, Constants.MEDIATYPE_FEED);
274
275 if (link == null) {
276 throwLinkException(repositoryId, objectId, Constants.REL_VERSIONHISTORY, Constants.MEDIATYPE_FEED);
277 }
278
279 UrlBuilder url = new UrlBuilder(link);
280 url.addParameter(Constants.PARAM_FILTER, filter);
281 url.addParameter(Constants.PARAM_ALLOWABLE_ACTIONS, includeAllowableActions);
282
283
284 Response resp = read(url);
285 AtomFeed feed = parse(resp.getStream(), AtomFeed.class);
286
287
288 if (!feed.getEntries().isEmpty()) {
289 for (AtomEntry entry : feed.getEntries()) {
290 ObjectData version = null;
291
292 lockLinks();
293 try {
294
295 removeLinks(repositoryId, entry.getId());
296
297
298 for (AtomElement element : entry.getElements()) {
299 if (element.getObject() instanceof AtomLink) {
300 addLink(repositoryId, entry.getId(), (AtomLink) element.getObject());
301 } else if (element.getObject() instanceof ObjectData) {
302 version = (ObjectData) element.getObject();
303 }
304 }
305 } finally {
306 unlockLinks();
307 }
308
309 if (version != null) {
310 result.add(version);
311 }
312 }
313 }
314
315 return result;
316 }
317
318 @Override
319 public ObjectData getObjectOfLatestVersion(String repositoryId, String objectId, String versionSeriesId,
320 Boolean major, String filter, Boolean includeAllowableActions, IncludeRelationships includeRelationships,
321 String renditionFilter, Boolean includePolicyIds, Boolean includeACL, ExtensionsData extension) {
322
323 ReturnVersion returnVersion = ReturnVersion.LATEST;
324 if ((major != null) && (major.booleanValue())) {
325 returnVersion = ReturnVersion.LASTESTMAJOR;
326 }
327
328
329
330 if (getSession().get(SessionParameter.LATEST_VERSION_WITH_VERSION_SERIES_ID, false)) {
331 if (versionSeriesId != null) {
332 objectId = versionSeriesId;
333 } else {
334 ObjectData obj = getObjectInternal(repositoryId, IdentifierType.ID, objectId, null,
335 PropertyIds.OBJECT_ID + "," + PropertyIds.VERSION_SERIES_ID, Boolean.FALSE,
336 IncludeRelationships.NONE, "cmis:none", Boolean.FALSE, Boolean.FALSE, extension);
337
338 if (obj.getProperties() != null && obj.getProperties().getProperties() != null) {
339 PropertyData<?> versionSeriesProp = obj.getProperties().getProperties()
340 .get(PropertyIds.VERSION_SERIES_ID);
341 if (versionSeriesProp != null && versionSeriesProp.getFirstValue() instanceof String) {
342 objectId = (String) versionSeriesProp.getFirstValue();
343 }
344 }
345 }
346 }
347
348 return getObjectInternal(repositoryId, IdentifierType.ID, objectId, returnVersion, filter,
349 includeAllowableActions, includeRelationships, renditionFilter, includePolicyIds, includeACL, extension);
350 }
351
352 @Override
353 public Properties getPropertiesOfLatestVersion(String repositoryId, String objectId, String versionSeriesId,
354 Boolean major, String filter, ExtensionsData extension) {
355 return getObjectOfLatestVersion(repositoryId, objectId, versionSeriesId, major, filter, Boolean.FALSE,
356 IncludeRelationships.NONE, "cmis:none", Boolean.FALSE, Boolean.FALSE, extension).getProperties();
357 }
358 }