001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Florent Guillaume
011 */
012package org.nuxeo.ecm.core.opencmis.impl.client;
013
014import java.math.BigInteger;
015import java.util.ArrayList;
016import java.util.Collection;
017import java.util.List;
018import java.util.Locale;
019import java.util.Map;
020import java.util.Set;
021
022import org.apache.chemistry.opencmis.client.api.ChangeEvent;
023import org.apache.chemistry.opencmis.client.api.ChangeEvents;
024import org.apache.chemistry.opencmis.client.api.CmisObject;
025import org.apache.chemistry.opencmis.client.api.Document;
026import org.apache.chemistry.opencmis.client.api.Folder;
027import org.apache.chemistry.opencmis.client.api.ItemIterable;
028import org.apache.chemistry.opencmis.client.api.ObjectId;
029import org.apache.chemistry.opencmis.client.api.ObjectType;
030import org.apache.chemistry.opencmis.client.api.OperationContext;
031import org.apache.chemistry.opencmis.client.api.Policy;
032import org.apache.chemistry.opencmis.client.api.QueryResult;
033import org.apache.chemistry.opencmis.client.api.QueryStatement;
034import org.apache.chemistry.opencmis.client.api.Relationship;
035import org.apache.chemistry.opencmis.client.api.Session;
036import org.apache.chemistry.opencmis.client.api.Tree;
037import org.apache.chemistry.opencmis.client.runtime.ObjectIdImpl;
038import org.apache.chemistry.opencmis.client.runtime.OperationContextImpl;
039import org.apache.chemistry.opencmis.client.runtime.QueryResultImpl;
040import org.apache.chemistry.opencmis.client.runtime.QueryStatementImpl;
041import org.apache.chemistry.opencmis.client.runtime.util.AbstractPageFetcher;
042import org.apache.chemistry.opencmis.client.runtime.util.CollectionIterable;
043import org.apache.chemistry.opencmis.commons.PropertyIds;
044import org.apache.chemistry.opencmis.commons.data.Ace;
045import org.apache.chemistry.opencmis.commons.data.Acl;
046import org.apache.chemistry.opencmis.commons.data.BulkUpdateObjectIdAndChangeToken;
047import org.apache.chemistry.opencmis.commons.data.ContentStream;
048import org.apache.chemistry.opencmis.commons.data.ObjectData;
049import org.apache.chemistry.opencmis.commons.data.ObjectList;
050import org.apache.chemistry.opencmis.commons.data.Properties;
051import org.apache.chemistry.opencmis.commons.data.PropertyData;
052import org.apache.chemistry.opencmis.commons.data.RepositoryInfo;
053import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition;
054import org.apache.chemistry.opencmis.commons.enums.AclPropagation;
055import org.apache.chemistry.opencmis.commons.enums.BindingType;
056import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships;
057import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection;
058import org.apache.chemistry.opencmis.commons.enums.VersioningState;
059import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException;
060import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException;
061import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException;
062import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl;
063import org.apache.chemistry.opencmis.commons.impl.dataobjects.BulkUpdateObjectIdAndChangeTokenImpl;
064import org.apache.chemistry.opencmis.commons.server.CallContext;
065import org.apache.chemistry.opencmis.commons.server.CmisService;
066import org.nuxeo.ecm.core.api.CoreSession;
067import org.nuxeo.ecm.core.api.DocumentModel;
068import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoObjectData;
069
070/**
071 * Nuxeo Persistent Session, having a direct connection to a Nuxeo {@link CoreSession}.
072 */
073public class NuxeoSession implements Session {
074
075    private static final long serialVersionUID = 1L;
076
077    public static final OperationContext DEFAULT_CONTEXT = new OperationContextImpl(null, false, true, false,
078            IncludeRelationships.NONE, null, true, null, true, 10);
079
080    private final CoreSession coreSession;
081
082    private final String repositoryId;
083
084    protected final NuxeoObjectFactory objectFactory;
085
086    private final CmisService service;
087
088    private final NuxeoBinding binding;
089
090    private OperationContext defaultContext = DEFAULT_CONTEXT;
091
092    public NuxeoSession(NuxeoBinding binding, CallContext context) {
093        this.coreSession = binding.getCoreSession();
094        repositoryId = context.getRepositoryId();
095        objectFactory = new NuxeoObjectFactory(this);
096        service = binding.service;
097        this.binding = binding;
098    }
099
100    @Override
101    public NuxeoObjectFactory getObjectFactory() {
102        return objectFactory;
103    }
104
105    @Override
106    public NuxeoBinding getBinding() {
107        return binding;
108    }
109
110    public CmisService getService() {
111        return service;
112    }
113
114    protected CoreSession getCoreSession() {
115        return coreSession;
116    }
117
118    @Override
119    public void clear() {
120    }
121
122    public void save() {
123        coreSession.save();
124    }
125
126    @Override
127    public void setDefaultContext(OperationContext defaultContext) {
128        this.defaultContext = defaultContext;
129    }
130
131    @Override
132    public OperationContext getDefaultContext() {
133        return defaultContext;
134    }
135
136    protected String getRepositoryId() {
137        return coreSession.getRepositoryName();
138    }
139
140    @Override
141    public ObjectId createObjectId(String id) {
142        return new ObjectIdImpl(id);
143    }
144
145    @Override
146    public ObjectId createDocument(Map<String, ?> properties, ObjectId folderId, ContentStream contentStream,
147            VersioningState versioningState) {
148        return createDocument(properties, folderId, contentStream, versioningState, null, null, null);
149    }
150
151    /** Converts from an untyped map to a {@link Properties} object. */
152    protected Properties convertProperties(Map<String, ?> properties) {
153        if (properties == null) {
154            return null;
155        }
156        // find type
157        String typeId = (String) properties.get(PropertyIds.OBJECT_TYPE_ID);
158        if (typeId == null) {
159            throw new IllegalArgumentException("Missing type");
160        }
161        ObjectType type = getTypeDefinition(typeId);
162        if (type == null) {
163            throw new IllegalArgumentException("Unknown type: " + typeId);
164        }
165        return objectFactory.convertProperties(properties, type, null, null);
166    }
167
168    @Override
169    public ObjectId createDocument(Map<String, ?> properties, ObjectId folderId, ContentStream contentStream,
170            VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces) {
171        String id = service.createDocument(repositoryId, convertProperties(properties), folderId == null ? null
172                : folderId.getId(), contentStream, versioningState, objectFactory.convertPolicies(policies),
173                objectFactory.convertAces(addAces), objectFactory.convertAces(removeAces), null);
174        return createObjectId(id);
175    }
176
177    @Override
178    public ObjectId createFolder(Map<String, ?> properties, ObjectId folderId) {
179        return createFolder(properties, folderId, null, null, null);
180    }
181
182    @Override
183    public ObjectId createFolder(Map<String, ?> properties, ObjectId folderId, List<Policy> policies,
184            List<Ace> addAces, List<Ace> removeAces) {
185        String id = service.createFolder(repositoryId, convertProperties(properties), folderId == null ? null
186                : folderId.getId(), objectFactory.convertPolicies(policies), objectFactory.convertAces(addAces),
187                objectFactory.convertAces(removeAces), null);
188        return createObjectId(id);
189    }
190
191    @Override
192    public OperationContext createOperationContext() {
193        return new OperationContextImpl();
194    }
195
196    @Override
197    public OperationContext createOperationContext(Set<String> filter, boolean includeAcls,
198            boolean includeAllowableActions, boolean includePolicies, IncludeRelationships includeRelationships,
199            Set<String> renditionFilter, boolean includePathSegments, String orderBy, boolean cacheEnabled,
200            int maxItemsPerPage) {
201        // TODO Auto-generated method stub
202        throw new UnsupportedOperationException();
203    }
204
205    @Override
206    public ObjectId createPolicy(Map<String, ?> properties, ObjectId folderId) {
207        return createPolicy(properties, folderId, null, null, null);
208    }
209
210    @Override
211    public ObjectId createPolicy(Map<String, ?> properties, ObjectId folderId, List<Policy> policies,
212            List<Ace> addAces, List<Ace> removeAces) {
213        // TODO Auto-generated method stub
214        throw new UnsupportedOperationException();
215    }
216
217    @Override
218    public ObjectId createRelationship(Map<String, ?> properties) {
219        return createRelationship(properties, null, null, null);
220    }
221
222    @Override
223    public ObjectId createRelationship(Map<String, ?> properties, List<Policy> policies, List<Ace> addAces,
224            List<Ace> removeAces) {
225        String id = service.createRelationship(repositoryId, convertProperties(properties),
226                objectFactory.convertPolicies(policies), objectFactory.convertAces(addAces),
227                objectFactory.convertAces(removeAces), null);
228        return createObjectId(id);
229    }
230
231    @Override
232    public ObjectId createItem(Map<String, ?> properties, ObjectId folderId, List<Policy> policies, List<Ace> addAces,
233            List<Ace> removeAces) {
234        throw new CmisNotSupportedException();
235    }
236
237    @Override
238    public ObjectId createItem(Map<String, ?> properties, ObjectId folderId) {
239        throw new CmisNotSupportedException();
240    }
241
242    @Override
243    public ObjectId createDocumentFromSource(ObjectId source, Map<String, ?> properties, ObjectId folderId,
244            VersioningState versioningState) {
245        return createDocumentFromSource(source, properties, folderId, versioningState, null, null, null);
246    }
247
248    @Override
249    public ObjectId createDocumentFromSource(ObjectId source, Map<String, ?> properties, ObjectId folderId,
250            VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces) {
251        // TODO Auto-generated method stub
252        throw new UnsupportedOperationException();
253    }
254
255    @Override
256    public ItemIterable<Document> getCheckedOutDocs() {
257        // TODO Auto-generated method stub
258        throw new UnsupportedOperationException();
259    }
260
261    @Override
262    public ItemIterable<Document> getCheckedOutDocs(OperationContext context) {
263        // TODO Auto-generated method stub
264        throw new UnsupportedOperationException();
265    }
266
267    @Override
268    public ChangeEvents getContentChanges(String changeLogToken, boolean includeProperties, long maxNumItems) {
269        return getContentChanges(changeLogToken, includeProperties, maxNumItems, getDefaultContext());
270    }
271
272    @Override
273    public ChangeEvents getContentChanges(String changeLogToken, boolean includeProperties, long maxNumItems,
274            OperationContext context) {
275        // TODO Auto-generated method stub
276        throw new UnsupportedOperationException();
277    }
278
279    @Override
280    public ItemIterable<ChangeEvent> getContentChanges(String changeLogToken, boolean includeProperties) {
281        return getContentChanges(changeLogToken, includeProperties, getDefaultContext());
282    };
283
284    @Override
285    public ItemIterable<ChangeEvent> getContentChanges(String changeLogToken, boolean includeProperties,
286            OperationContext context) {
287        // TODO Auto-generated method stub
288        throw new UnsupportedOperationException();
289    };
290
291    @Override
292    public Locale getLocale() {
293        // TODO Auto-generated method stub
294        throw new UnsupportedOperationException();
295    }
296
297    @Override
298    public CmisObject getObject(ObjectId objectId) {
299        return getObject(objectId, getDefaultContext());
300    }
301
302    @Override
303    public CmisObject getObject(String objectId) {
304        return getObject(objectId, getDefaultContext());
305    }
306
307    /** Gets a CMIS object given a Nuxeo {@link DocumentModel}. */
308    public CmisObject getObject(DocumentModel doc, OperationContext context) {
309        ObjectData data = new NuxeoObjectData(service, doc, context);
310        return objectFactory.convertObject(data, context);
311    }
312
313    @Override
314    public CmisObject getObject(ObjectId objectId, OperationContext context) {
315        if (objectId == null) {
316            throw new CmisInvalidArgumentException("Missing object ID");
317        }
318        return getObject(objectId.getId(), context);
319    }
320
321    @Override
322    public CmisObject getObject(String objectId, OperationContext context) {
323        if (objectId == null) {
324            throw new CmisInvalidArgumentException("Missing object ID");
325        }
326        if (context == null) {
327            throw new CmisInvalidArgumentException("Missing operation context");
328        }
329        ObjectData data = service.getObject(repositoryId, objectId, context.getFilterString(),
330                Boolean.valueOf(context.isIncludeAllowableActions()), context.getIncludeRelationships(),
331                context.getRenditionFilterString(), Boolean.valueOf(context.isIncludePolicies()),
332                Boolean.valueOf(context.isIncludeAcls()), null);
333        return objectFactory.convertObject(data, context);
334    }
335
336    @Override
337    public CmisObject getObjectByPath(String path) {
338        return getObjectByPath(path, getDefaultContext());
339    }
340
341    @Override
342    public CmisObject getObjectByPath(String path, OperationContext context) {
343        if (path == null || !path.startsWith("/")) {
344            throw new CmisInvalidArgumentException("Invalid path: " + path);
345        }
346        if (context == null) {
347            throw new CmisInvalidArgumentException("Missing operation context");
348        }
349        ObjectData data = service.getObjectByPath(repositoryId, path, context.getFilterString(),
350                Boolean.valueOf(context.isIncludeAllowableActions()), context.getIncludeRelationships(),
351                context.getRenditionFilterString(), Boolean.valueOf(context.isIncludePolicies()),
352                Boolean.valueOf(context.isIncludeAcls()), null);
353        return getObjectFactory().convertObject(data, context);
354    }
355
356    @Override
357    public RepositoryInfo getRepositoryInfo() {
358        return service.getRepositoryInfo(repositoryId, null);
359    }
360
361    @Override
362    public Folder getRootFolder() {
363        return getRootFolder(getDefaultContext());
364    }
365
366    @Override
367    public Folder getRootFolder(OperationContext context) {
368        String id = getRepositoryInfo().getRootFolderId();
369        CmisObject folder = getObject(createObjectId(id), context);
370        if (!(folder instanceof Folder)) {
371            throw new CmisRuntimeException("Root object is not a Folder but: " + folder.getClass().getName());
372        }
373        return (Folder) folder;
374    }
375
376    @Override
377    public ItemIterable<ObjectType> getTypeChildren(String typeId, boolean includePropertyDefinitions) {
378        // TODO Auto-generated method stub
379        throw new UnsupportedOperationException();
380    }
381
382    @Override
383    public ObjectType getTypeDefinition(String typeId) {
384        TypeDefinition typeDefinition = service.getTypeDefinition(repositoryId, typeId, null);
385        return objectFactory.convertTypeDefinition(typeDefinition);
386    }
387
388    @Override
389    public ObjectType getTypeDefinition(String typeId, boolean useCache) {
390        return getTypeDefinition(typeId);
391    }
392
393    @Override
394    public List<Tree<ObjectType>> getTypeDescendants(String typeId, int depth, boolean includePropertyDefinitions) {
395        // TODO Auto-generated method stub
396        throw new UnsupportedOperationException();
397    }
398
399    @Override
400    public ItemIterable<QueryResult> query(String statement, boolean searchAllVersions) {
401        // TODO Auto-generated method stub
402        throw new UnsupportedOperationException();
403    }
404
405    @Override
406    public ItemIterable<QueryResult> query(final String statement, final boolean searchAllVersions,
407            final OperationContext context) {
408        AbstractPageFetcher<QueryResult> pageFetcher = new AbstractPageFetcher<QueryResult>(
409                context.getMaxItemsPerPage()) {
410            @Override
411            protected Page<QueryResult> fetchPage(long skipCount) {
412                ObjectList results = service.query(repositoryId, statement, Boolean.valueOf(searchAllVersions),
413                        Boolean.valueOf(context.isIncludeAllowableActions()), context.getIncludeRelationships(),
414                        context.getRenditionFilterString(), BigInteger.valueOf(maxNumItems),
415                        BigInteger.valueOf(skipCount), null);
416                // convert objects
417                List<QueryResult> page = new ArrayList<QueryResult>();
418                if (results.getObjects() != null) {
419                    for (ObjectData data : results.getObjects()) {
420                        page.add(new QueryResultImpl(NuxeoSession.this, data));
421                    }
422                }
423                return new Page<QueryResult>(page, results.getNumItems(), results.hasMoreItems());
424            }
425        };
426        return new CollectionIterable<QueryResult>(pageFetcher);
427    }
428
429    @Override
430    public ItemIterable<CmisObject> queryObjects(String typeId, String where, boolean searchAllVersions,
431            OperationContext context) {
432        // TODO Auto-generated method stub
433        throw new UnsupportedOperationException();
434    }
435
436    @Override
437    public QueryStatement createQueryStatement(String statement) {
438        return new QueryStatementImpl(this, statement);
439    }
440
441    @Override
442    public QueryStatement createQueryStatement(Collection<String> selectPropertyIds, Map<String, String> fromTypes,
443            String whereClause, List<String> orderByPropertyIds) {
444        return new QueryStatementImpl(this, selectPropertyIds, fromTypes, whereClause, orderByPropertyIds);
445    }
446
447    @Override
448    public ItemIterable<Relationship> getRelationships(final ObjectId objectId,
449            final boolean includeSubRelationshipTypes, final RelationshipDirection relationshipDirection,
450            final ObjectType type, final OperationContext context) {
451        final String typeId = type == null ? null : type.getId();
452        AbstractPageFetcher<Relationship> pageFetcher = new AbstractPageFetcher<Relationship>(
453                context.getMaxItemsPerPage()) {
454            @Override
455            protected Page<Relationship> fetchPage(long skipCount) {
456                ObjectList relations = service.getObjectRelationships(repositoryId, objectId.getId(),
457                        Boolean.valueOf(includeSubRelationshipTypes), relationshipDirection, typeId, null, null,
458                        BigInteger.valueOf(maxNumItems), BigInteger.valueOf(skipCount), null);
459                // convert objects
460                List<Relationship> page = new ArrayList<Relationship>();
461                if (relations.getObjects() != null) {
462                    for (ObjectData data : relations.getObjects()) {
463                        CmisObject ob;
464                        if (data instanceof NuxeoObjectData) {
465                            ob = objectFactory.convertObject(data, context);
466                        } else {
467                            ob = getObject(data.getId(), context);
468                        }
469                        if (!(ob instanceof Relationship)) {
470                            // should not happen...
471                            continue;
472                        }
473                        page.add((Relationship) ob);
474                    }
475                }
476                return new Page<Relationship>(page, relations.getNumItems(), relations.hasMoreItems());
477            }
478        };
479        return new CollectionIterable<Relationship>(pageFetcher);
480    }
481
482    @Override
483    public Acl getAcl(ObjectId objectId, boolean onlyBasicPermissions) {
484        return service.getAcl(repositoryId, objectId.getId(), Boolean.valueOf(onlyBasicPermissions), null);
485    }
486
487    @Override
488    public Acl setAcl(ObjectId objectId, List<Ace> aces) {
489        return service.applyAcl(repositoryId, objectId.getId(), new AccessControlListImpl(aces), null);
490    }
491
492    @Override
493    public Acl applyAcl(ObjectId objectId, List<Ace> addAces, List<Ace> removeAces, AclPropagation aclPropagation) {
494        return service.applyAcl(repositoryId, objectId.getId(), new AccessControlListImpl(addAces),
495                new AccessControlListImpl(removeAces), aclPropagation, null);
496    }
497
498    @Override
499    public void applyPolicy(ObjectId objectId, ObjectId... policyIds) {
500        throw new CmisNotSupportedException();
501    }
502
503    @Override
504    public void removePolicy(ObjectId objectId, ObjectId... policyIds) {
505        throw new CmisNotSupportedException();
506    }
507
508    @Override
509    public void removeObjectFromCache(ObjectId objectId) {
510    }
511
512    @Override
513    public void removeObjectFromCache(String objectId) {
514    }
515
516    @Override
517    public void delete(ObjectId objectId) {
518        delete(objectId, true);
519    }
520
521    @Override
522    public void delete(ObjectId objectId, boolean allVersions) {
523        service.deleteObject(repositoryId, objectId.getId(), Boolean.valueOf(allVersions), null);
524    }
525
526    @Override
527    public ContentStream getContentStream(ObjectId docId) {
528        return getContentStream(docId, null, null, null);
529    }
530
531    @Override
532    public ContentStream getContentStream(ObjectId docId, String streamId, BigInteger offset, BigInteger length) {
533        if (docId == null) {
534            throw new CmisInvalidArgumentException("Missing object ID");
535        }
536        return service.getContentStream(repositoryId, docId.getId(), streamId, offset, length, null);
537    }
538
539    @Override
540    public ObjectType createType(TypeDefinition type) {
541        throw new CmisNotSupportedException();
542    }
543
544    @Override
545    public ObjectType updateType(TypeDefinition type) {
546        throw new CmisNotSupportedException();
547    }
548
549    @Override
550    public void deleteType(String typeId) {
551        throw new CmisNotSupportedException();
552    }
553
554    @Override
555    public List<BulkUpdateObjectIdAndChangeToken> bulkUpdateProperties(List<CmisObject> objects,
556            Map<String, ?> properties, List<String> addSecondaryTypeIds, List<String> removeSecondaryTypeIds) {
557        List<BulkUpdateObjectIdAndChangeToken> idts = new ArrayList<BulkUpdateObjectIdAndChangeToken>(objects.size());
558        for (CmisObject object : objects) {
559            idts.add(new BulkUpdateObjectIdAndChangeTokenImpl(object.getId(), object.getChangeToken()));
560        }
561        return service.bulkUpdateProperties(repositoryId, idts, convertProperties(properties), addSecondaryTypeIds,
562                removeSecondaryTypeIds, null);
563    }
564
565    @Override
566    public Document getLatestDocumentVersion(ObjectId objectId) {
567        return getLatestDocumentVersion(objectId, false, getDefaultContext());
568    }
569
570    @Override
571    public Document getLatestDocumentVersion(String objectId, OperationContext context) {
572        if (objectId == null) {
573            throw new IllegalArgumentException("Object ID must be set!");
574        }
575        return getLatestDocumentVersion(createObjectId(objectId), false, context);
576    }
577
578    @Override
579    public Document getLatestDocumentVersion(String objectId, boolean major, OperationContext context) {
580        if (objectId == null) {
581            throw new IllegalArgumentException("Object ID must be set!");
582        }
583        return getLatestDocumentVersion(createObjectId(objectId), major, context);
584    }
585
586    @Override
587    public Document getLatestDocumentVersion(String objectId) {
588        if (objectId == null) {
589            throw new IllegalArgumentException("Object ID must be set!");
590        }
591        return getLatestDocumentVersion(createObjectId(objectId), false, getDefaultContext());
592    }
593
594    @Override
595    public Document getLatestDocumentVersion(ObjectId objectId, OperationContext context) {
596        return getLatestDocumentVersion(objectId, false, context);
597    }
598
599    @Override
600    /**
601     * @See org.apache.chemistry.opencmis.client.runtime.SessionImpl
602     */
603    public Document getLatestDocumentVersion(ObjectId objectId, boolean major, OperationContext context) {
604        if (objectId == null || objectId.getId() == null) {
605            throw new IllegalArgumentException("Object ID must be set!");
606        }
607
608        if (context == null) {
609            throw new IllegalArgumentException("Operation context must be set!");
610        }
611
612        CmisObject result = null;
613
614        String versionSeriesId = null;
615
616        // first attempt: if we got a Document object, try getting the version
617        // series ID from it
618        if (objectId instanceof Document) {
619            versionSeriesId = ((Document) objectId).getVersionSeriesId();
620        }
621
622        // third attempt (Web Services only): get the version series ID from the
623        // repository
624        // (the AtomPub and Browser binding don't need the version series ID ->
625        // avoid roundtrip)
626        if (versionSeriesId == null) {
627            BindingType bindingType = getBinding().getBindingType();
628            if (bindingType == BindingType.WEBSERVICES || bindingType == BindingType.CUSTOM) {
629
630                // get the document to find the version series ID
631                ObjectData sourceObjectData = binding.getObjectService().getObject(getRepositoryId(), objectId.getId(),
632                        PropertyIds.OBJECT_ID + "," + PropertyIds.VERSION_SERIES_ID, false, IncludeRelationships.NONE,
633                        "cmis:none", false, false, null);
634
635                if (sourceObjectData.getProperties() != null
636                        && sourceObjectData.getProperties().getProperties() != null) {
637                    PropertyData<?> verionsSeriesIdProp = sourceObjectData.getProperties().getProperties().get(
638                            PropertyIds.VERSION_SERIES_ID);
639                    if (verionsSeriesIdProp != null && verionsSeriesIdProp.getFirstValue() instanceof String) {
640                        versionSeriesId = (String) verionsSeriesIdProp.getFirstValue();
641                    }
642                }
643
644                // the Web Services binding needs the version series ID -> fail
645                if (versionSeriesId == null) {
646                    throw new IllegalArgumentException("Object is not a document or not versionable!");
647                }
648            }
649        }
650
651        // get the object
652        ObjectData objectData = binding.getVersioningService().getObjectOfLatestVersion(getRepositoryId(),
653                objectId.getId(), versionSeriesId, major, context.getFilterString(),
654                context.isIncludeAllowableActions(), context.getIncludeRelationships(),
655                context.getRenditionFilterString(), context.isIncludePolicies(), context.isIncludeAcls(), null);
656
657        result = getObjectFactory().convertObject(objectData, context);
658
659        // check result
660        if (!(result instanceof Document)) {
661            throw new IllegalArgumentException("Latest version is not a document!");
662        }
663
664        return (Document) result;
665    }
666
667    @Override
668    public String getLatestChangeLogToken() {
669        return getBinding().getRepositoryService().getRepositoryInfo(getRepositoryId(), null).getLatestChangeLogToken();
670    }
671
672}