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