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 static org.apache.chemistry.opencmis.commons.impl.Constants.RENDITION_NONE; 022 023import java.math.BigInteger; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.HashMap; 028import java.util.List; 029import java.util.Locale; 030import java.util.Map; 031import java.util.Set; 032 033import org.apache.chemistry.opencmis.client.api.ChangeEvent; 034import org.apache.chemistry.opencmis.client.api.ChangeEvents; 035import org.apache.chemistry.opencmis.client.api.CmisObject; 036import org.apache.chemistry.opencmis.client.api.Document; 037import org.apache.chemistry.opencmis.client.api.Folder; 038import org.apache.chemistry.opencmis.client.api.ItemIterable; 039import org.apache.chemistry.opencmis.client.api.ObjectId; 040import org.apache.chemistry.opencmis.client.api.ObjectType; 041import org.apache.chemistry.opencmis.client.api.OperationContext; 042import org.apache.chemistry.opencmis.client.api.Policy; 043import org.apache.chemistry.opencmis.client.api.QueryResult; 044import org.apache.chemistry.opencmis.client.api.QueryStatement; 045import org.apache.chemistry.opencmis.client.api.Relationship; 046import org.apache.chemistry.opencmis.client.api.Session; 047import org.apache.chemistry.opencmis.client.api.Tree; 048import org.apache.chemistry.opencmis.client.runtime.ObjectIdImpl; 049import org.apache.chemistry.opencmis.client.runtime.OperationContextImpl; 050import org.apache.chemistry.opencmis.client.runtime.QueryResultImpl; 051import org.apache.chemistry.opencmis.client.runtime.QueryStatementImpl; 052import org.apache.chemistry.opencmis.client.runtime.util.AbstractPageFetcher; 053import org.apache.chemistry.opencmis.client.runtime.util.CollectionIterable; 054import org.apache.chemistry.opencmis.commons.PropertyIds; 055import org.apache.chemistry.opencmis.commons.data.Ace; 056import org.apache.chemistry.opencmis.commons.data.Acl; 057import org.apache.chemistry.opencmis.commons.data.BulkUpdateObjectIdAndChangeToken; 058import org.apache.chemistry.opencmis.commons.data.ContentStream; 059import org.apache.chemistry.opencmis.commons.data.FailedToDeleteData; 060import org.apache.chemistry.opencmis.commons.data.ObjectData; 061import org.apache.chemistry.opencmis.commons.data.ObjectList; 062import org.apache.chemistry.opencmis.commons.data.Properties; 063import org.apache.chemistry.opencmis.commons.data.RepositoryInfo; 064import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; 065import org.apache.chemistry.opencmis.commons.enums.AclPropagation; 066import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; 067import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; 068import org.apache.chemistry.opencmis.commons.enums.RelationshipDirection; 069import org.apache.chemistry.opencmis.commons.enums.UnfileObject; 070import org.apache.chemistry.opencmis.commons.enums.VersioningState; 071import org.apache.chemistry.opencmis.commons.exceptions.CmisConstraintException; 072import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; 073import org.apache.chemistry.opencmis.commons.exceptions.CmisNotSupportedException; 074import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; 075import org.apache.chemistry.opencmis.commons.exceptions.CmisRuntimeException; 076import org.apache.chemistry.opencmis.commons.impl.dataobjects.AccessControlListImpl; 077import org.apache.chemistry.opencmis.commons.impl.dataobjects.BulkUpdateObjectIdAndChangeTokenImpl; 078import org.apache.chemistry.opencmis.commons.server.CallContext; 079import org.apache.chemistry.opencmis.commons.server.CmisService; 080import org.apache.commons.lang3.StringUtils; 081import org.nuxeo.ecm.core.api.CoreSession; 082import org.nuxeo.ecm.core.api.DocumentModel; 083import org.nuxeo.ecm.core.opencmis.impl.server.NuxeoObjectData; 084 085/** 086 * Nuxeo Persistent Session, having a direct connection to a Nuxeo {@link CoreSession}. 087 */ 088public class NuxeoSession implements Session { 089 090 private static final long serialVersionUID = 1L; 091 092 public static final OperationContext DEFAULT_CONTEXT = new OperationContextImpl(null, false, true, false, 093 IncludeRelationships.NONE, null, true, null, true, 10); 094 095 private final CoreSession coreSession; 096 097 private final String repositoryId; 098 099 protected final NuxeoObjectFactory objectFactory; 100 101 private final CmisService service; 102 103 private final NuxeoBinding binding; 104 105 private OperationContext defaultContext = DEFAULT_CONTEXT; 106 107 public NuxeoSession(NuxeoBinding binding, CallContext context) { 108 this.coreSession = binding.getCoreSession(); 109 repositoryId = context.getRepositoryId(); 110 objectFactory = new NuxeoObjectFactory(this); 111 service = binding.service; 112 this.binding = binding; 113 } 114 115 @Override 116 public NuxeoObjectFactory getObjectFactory() { 117 return objectFactory; 118 } 119 120 @Override 121 public NuxeoBinding getBinding() { 122 return binding; 123 } 124 125 public CmisService getService() { 126 return service; 127 } 128 129 protected CoreSession getCoreSession() { 130 return coreSession; 131 } 132 133 @Override 134 public void clear() { 135 } 136 137 public void save() { 138 coreSession.save(); 139 } 140 141 @Override 142 public void setDefaultContext(OperationContext defaultContext) { 143 this.defaultContext = defaultContext; 144 } 145 146 @Override 147 public OperationContext getDefaultContext() { 148 return defaultContext; 149 } 150 151 @Override 152 public Map<String, String> getSessionParameters() { 153 return Collections.emptyMap(); 154 } 155 156 protected String getRepositoryId() { 157 return coreSession.getRepositoryName(); 158 } 159 160 @Override 161 public ObjectId createObjectId(String id) { 162 return new ObjectIdImpl(id); 163 } 164 165 @Override 166 public ObjectId createDocument(Map<String, ?> properties, ObjectId folderId, ContentStream contentStream, 167 VersioningState versioningState) { 168 return createDocument(properties, folderId, contentStream, versioningState, null, null, null); 169 } 170 171 /** Converts from an untyped map to a {@link Properties} object. */ 172 protected Properties convertProperties(Map<String, ?> properties) { 173 if (properties == null) { 174 return null; 175 } 176 // find type 177 String typeId = (String) properties.get(PropertyIds.OBJECT_TYPE_ID); 178 if (typeId == null) { 179 throw new IllegalArgumentException("Missing type"); 180 } 181 ObjectType type = getTypeDefinition(typeId); 182 if (type == null) { 183 throw new IllegalArgumentException("Unknown type: " + typeId); 184 } 185 return objectFactory.convertProperties(properties, type, null, null); 186 } 187 188 @Override 189 public ObjectId createDocument(Map<String, ?> properties, ObjectId folderId, ContentStream contentStream, 190 VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces) { 191 String id = service.createDocument(repositoryId, convertProperties(properties), folderId == null ? null 192 : folderId.getId(), contentStream, versioningState, objectFactory.convertPolicies(policies), 193 objectFactory.convertAces(addAces), objectFactory.convertAces(removeAces), null); 194 return createObjectId(id); 195 } 196 197 @Override 198 public ObjectId createFolder(Map<String, ?> properties, ObjectId folderId) { 199 return createFolder(properties, folderId, null, null, null); 200 } 201 202 @Override 203 public ObjectId createFolder(Map<String, ?> properties, ObjectId folderId, List<Policy> policies, 204 List<Ace> addAces, List<Ace> removeAces) { 205 String id = service.createFolder(repositoryId, convertProperties(properties), folderId == null ? null 206 : folderId.getId(), objectFactory.convertPolicies(policies), objectFactory.convertAces(addAces), 207 objectFactory.convertAces(removeAces), null); 208 return createObjectId(id); 209 } 210 211 @Override 212 public OperationContext createOperationContext() { 213 return new OperationContextImpl(); 214 } 215 216 @Override 217 public OperationContext createOperationContext(Set<String> filter, boolean includeAcls, 218 boolean includeAllowableActions, boolean includePolicies, IncludeRelationships includeRelationships, 219 Set<String> renditionFilter, boolean includePathSegments, String orderBy, boolean cacheEnabled, 220 int maxItemsPerPage) { 221 // TODO Auto-generated method stub 222 throw new UnsupportedOperationException(); 223 } 224 225 @Override 226 public ObjectId createPolicy(Map<String, ?> properties, ObjectId folderId) { 227 return createPolicy(properties, folderId, null, null, null); 228 } 229 230 @Override 231 public ObjectId createPolicy(Map<String, ?> properties, ObjectId folderId, List<Policy> policies, 232 List<Ace> addAces, List<Ace> removeAces) { 233 // TODO Auto-generated method stub 234 throw new UnsupportedOperationException(); 235 } 236 237 @Override 238 public ObjectId createRelationship(Map<String, ?> properties) { 239 return createRelationship(properties, null, null, null); 240 } 241 242 @Override 243 public ObjectId createRelationship(Map<String, ?> properties, List<Policy> policies, List<Ace> addAces, 244 List<Ace> removeAces) { 245 String id = service.createRelationship(repositoryId, convertProperties(properties), 246 objectFactory.convertPolicies(policies), objectFactory.convertAces(addAces), 247 objectFactory.convertAces(removeAces), null); 248 return createObjectId(id); 249 } 250 251 @Override 252 public ObjectId createItem(Map<String, ?> properties, ObjectId folderId, List<Policy> policies, List<Ace> addAces, 253 List<Ace> removeAces) { 254 throw new CmisNotSupportedException(); 255 } 256 257 @Override 258 public ObjectId createItem(Map<String, ?> properties, ObjectId folderId) { 259 throw new CmisNotSupportedException(); 260 } 261 262 @Override 263 public ObjectId createPath(String newPath, Map<String, ?> properties) { 264 return createPath(null, newPath, properties); 265 } 266 267 @Override 268 public ObjectId createPath(ObjectId startFolderId, String newPath, Map<String, ?> properties) { 269 return createPath(startFolderId, newPath, properties, null, null, null); 270 } 271 272 @Override 273 public ObjectId createPath(String newPath, String typeId) { 274 return createPath(null, newPath, typeId); 275 } 276 277 @Override 278 public ObjectId createPath(ObjectId startFolderId, String newPath, String typeId) { 279 Map<String, Object> properties = Collections.singletonMap(PropertyIds.OBJECT_TYPE_ID, typeId); 280 return createPath(startFolderId, newPath, properties, null, null, null); 281 } 282 283 @Override 284 public ObjectId createPath(ObjectId startFolderId, String newPath, Map<String, ?> properties, List<Policy> policies, 285 List<Ace> addAces, List<Ace> removeAces) { 286 checkPath(newPath); 287 if (newPath.length() == 1) { 288 throw new CmisInvalidArgumentException("Cannot create root folder"); 289 } 290 if (newPath.endsWith("/")) { 291 throw new CmisInvalidArgumentException("Path cannot end with a slash"); 292 } 293 if (properties == null || properties.isEmpty()) { 294 throw new CmisInvalidArgumentException("Properties must not be empty"); 295 } 296 if (!(properties.get(PropertyIds.OBJECT_TYPE_ID) instanceof String)) { 297 throw new CmisInvalidArgumentException("Property cmis:objectTypeId not set or invalid"); 298 } 299 StringBuilder nextPath = new StringBuilder(); 300 String[] segments; 301 ObjectId lastFolderId = null; 302 boolean create = false; 303 304 // check start folder 305 if (startFolderId != null && startFolderId.getId() != null) { 306 if (startFolderId instanceof Folder) { 307 Folder startFolder = (Folder) startFolderId; 308 if (!startFolder.isRootFolder()) { 309 nextPath.append(startFolder.getPath()); 310 lastFolderId = startFolder; 311 } 312 } else { 313 String filter = PropertyIds.OBJECT_ID + ',' + PropertyIds.BASE_TYPE_ID + ',' + PropertyIds.PATH; 314 ObjectData startFolderData = service.getObject(repositoryId, startFolderId.getId(), filter, 315 Boolean.FALSE, IncludeRelationships.NONE, RENDITION_NONE, Boolean.FALSE, Boolean.FALSE, null); 316 if (startFolderData.getBaseTypeId() != BaseTypeId.CMIS_FOLDER) { 317 throw new CmisInvalidArgumentException("Start folder is not a folder"); 318 } 319 if (startFolderData.getProperties() == null || startFolderData.getProperties().getProperties() == null 320 || startFolderData.getProperties().getProperties().get(PropertyIds.PATH) == null) { 321 throw new CmisInvalidArgumentException("Start folder has no path property"); 322 } 323 String startPath = (String) startFolderData.getProperties() 324 .getProperties() 325 .get(PropertyIds.PATH) 326 .getFirstValue(); 327 if (!getRepositoryInfo().getRootFolderId().equals(startFolderData.getId())) { 328 nextPath.append(startPath); 329 lastFolderId = startFolderId; 330 } 331 } 332 if (!newPath.startsWith(nextPath.toString())) { 333 throw new CmisInvalidArgumentException("Start folder in not in the path"); 334 } 335 segments = newPath.substring(nextPath.length()).split("/"); 336 } else { 337 segments = newPath.split("/"); 338 } 339 340 // create folders 341 for (int i = 1; i < segments.length; i++) { 342 String segment = segments[i]; 343 if (create) { 344 lastFolderId = createFolder(propertiesWithName(properties, segment), lastFolderId, policies, addAces, 345 removeAces); 346 } else { 347 try { 348 nextPath.append('/'); 349 nextPath.append(segment); 350 String filter = PropertyIds.OBJECT_ID + ',' + PropertyIds.BASE_TYPE_ID; 351 ObjectData folderData = service.getObjectByPath(repositoryId, nextPath.toString(), filter, 352 Boolean.FALSE, IncludeRelationships.NONE, RENDITION_NONE, Boolean.FALSE, Boolean.FALSE, 353 null); 354 if (folderData.getBaseTypeId() != BaseTypeId.CMIS_FOLDER) { 355 throw new CmisConstraintException("Cannot create folder " + segment 356 + " because there is already an object with this name which is not a folder"); 357 } 358 lastFolderId = new ObjectIdImpl(folderData.getId()); 359 } catch (CmisObjectNotFoundException e) { 360 if (lastFolderId == null) { 361 lastFolderId = new ObjectIdImpl(getRepositoryInfo().getRootFolderId()); 362 } 363 lastFolderId = createFolder(propertiesWithName(properties, segment), lastFolderId, policies, 364 addAces, removeAces); 365 create = true; 366 } 367 } 368 } 369 return lastFolderId; 370 } 371 372 protected Map<String, Object> propertiesWithName(Map<String, ?> properties, String name) { 373 Map<String, Object> newProperties = new HashMap<>(properties); 374 newProperties.put(PropertyIds.NAME, name); 375 return newProperties; 376 } 377 378 @Override 379 public ObjectId createDocumentFromSource(ObjectId source, Map<String, ?> properties, ObjectId folderId, 380 VersioningState versioningState) { 381 return createDocumentFromSource(source, properties, folderId, versioningState, null, null, null); 382 } 383 384 @Override 385 public ObjectId createDocumentFromSource(ObjectId source, Map<String, ?> properties, ObjectId folderId, 386 VersioningState versioningState, List<Policy> policies, List<Ace> addAces, List<Ace> removeAces) { 387 // TODO Auto-generated method stub 388 throw new UnsupportedOperationException(); 389 } 390 391 @Override 392 public ItemIterable<Document> getCheckedOutDocs() { 393 // TODO Auto-generated method stub 394 throw new UnsupportedOperationException(); 395 } 396 397 @Override 398 public ItemIterable<Document> getCheckedOutDocs(OperationContext context) { 399 // TODO Auto-generated method stub 400 throw new UnsupportedOperationException(); 401 } 402 403 @Override 404 public ChangeEvents getContentChanges(String changeLogToken, boolean includeProperties, long maxNumItems) { 405 return getContentChanges(changeLogToken, includeProperties, maxNumItems, getDefaultContext()); 406 } 407 408 @Override 409 public ChangeEvents getContentChanges(String changeLogToken, boolean includeProperties, long maxNumItems, 410 OperationContext context) { 411 // TODO Auto-generated method stub 412 throw new UnsupportedOperationException(); 413 } 414 415 @Override 416 public ItemIterable<ChangeEvent> getContentChanges(String changeLogToken, boolean includeProperties) { 417 return getContentChanges(changeLogToken, includeProperties, getDefaultContext()); 418 }; 419 420 @Override 421 public ItemIterable<ChangeEvent> getContentChanges(String changeLogToken, boolean includeProperties, 422 OperationContext context) { 423 // TODO Auto-generated method stub 424 throw new UnsupportedOperationException(); 425 }; 426 427 @Override 428 public Locale getLocale() { 429 // TODO Auto-generated method stub 430 throw new UnsupportedOperationException(); 431 } 432 433 @Override 434 public boolean exists(ObjectId objectId) { 435 return exists(objectId.getId()); 436 } 437 438 @Override 439 public boolean exists(String objectId) { 440 try { 441 service.getObject(repositoryId, objectId, PropertyIds.OBJECT_ID, Boolean.FALSE, IncludeRelationships.NONE, 442 RENDITION_NONE, Boolean.FALSE, Boolean.FALSE, null); 443 return true; 444 } catch (CmisObjectNotFoundException e) { 445 return false; 446 } 447 } 448 449 @Override 450 public boolean existsPath(String parentPath, String name) { 451 if (parentPath == null || !parentPath.startsWith("/")) { 452 throw new CmisInvalidArgumentException("Invalid parent path: " + parentPath); 453 } 454 if (StringUtils.isEmpty(name)) { 455 throw new CmisInvalidArgumentException("Invalid empty name: " + name); 456 } 457 StringBuilder path = new StringBuilder(parentPath); 458 if (!parentPath.endsWith("/")) { 459 path.append('/'); 460 } 461 path.append(name); 462 return existsPath(path.toString()); 463 } 464 465 @Override 466 public boolean existsPath(String path) { 467 try { 468 service.getObjectByPath(repositoryId, path, PropertyIds.OBJECT_ID, Boolean.FALSE, IncludeRelationships.NONE, 469 RENDITION_NONE, Boolean.FALSE, Boolean.FALSE, null); 470 return true; 471 } catch (CmisObjectNotFoundException e) { 472 return false; 473 } 474 } 475 476 @Override 477 public CmisObject getObject(ObjectId objectId) { 478 return getObject(objectId, getDefaultContext()); 479 } 480 481 @Override 482 public CmisObject getObject(String objectId) { 483 return getObject(objectId, getDefaultContext()); 484 } 485 486 /** Gets a CMIS object given a Nuxeo {@link DocumentModel}. */ 487 public CmisObject getObject(DocumentModel doc, OperationContext context) { 488 ObjectData data = new NuxeoObjectData(service, doc, context); 489 return objectFactory.convertObject(data, context); 490 } 491 492 @Override 493 public CmisObject getObject(ObjectId objectId, OperationContext context) { 494 if (objectId == null) { 495 throw new CmisInvalidArgumentException("Missing object ID"); 496 } 497 return getObject(objectId.getId(), context); 498 } 499 500 @Override 501 public CmisObject getObject(String objectId, OperationContext context) { 502 if (objectId == null) { 503 throw new CmisInvalidArgumentException("Missing object ID"); 504 } 505 if (context == null) { 506 throw new CmisInvalidArgumentException("Missing operation context"); 507 } 508 ObjectData data = service.getObject(repositoryId, objectId, context.getFilterString(), 509 Boolean.valueOf(context.isIncludeAllowableActions()), context.getIncludeRelationships(), 510 context.getRenditionFilterString(), Boolean.valueOf(context.isIncludePolicies()), 511 Boolean.valueOf(context.isIncludeAcls()), null); 512 return objectFactory.convertObject(data, context); 513 } 514 515 @Override 516 public CmisObject getObjectByPath(String path) { 517 return getObjectByPath(path, getDefaultContext()); 518 } 519 520 @Override 521 public CmisObject getObjectByPath(String parentPath, String name) { 522 return getObjectByPath(parentPath, name, getDefaultContext()); 523 } 524 525 @Override 526 public CmisObject getObjectByPath(String parentPath, String name, OperationContext context) { 527 if (parentPath == null || !parentPath.startsWith("/")) { 528 throw new CmisInvalidArgumentException("Invalid parent path: " + parentPath); 529 } 530 if (StringUtils.isEmpty(name)) { 531 throw new CmisInvalidArgumentException("Invalid empty name: " + name); 532 } 533 StringBuilder path = new StringBuilder(parentPath); 534 if (!parentPath.endsWith("/")) { 535 path.append('/'); 536 } 537 path.append(name); 538 return getObjectByPath(path.toString(), context); 539 } 540 541 @Override 542 public CmisObject getObjectByPath(String path, OperationContext context) { 543 if (path == null || !path.startsWith("/")) { 544 throw new CmisInvalidArgumentException("Invalid path: " + path); 545 } 546 if (context == null) { 547 throw new CmisInvalidArgumentException("Missing operation context"); 548 } 549 ObjectData data = service.getObjectByPath(repositoryId, path, context.getFilterString(), 550 Boolean.valueOf(context.isIncludeAllowableActions()), context.getIncludeRelationships(), 551 context.getRenditionFilterString(), Boolean.valueOf(context.isIncludePolicies()), 552 Boolean.valueOf(context.isIncludeAcls()), null); 553 return getObjectFactory().convertObject(data, context); 554 } 555 556 protected String getObjectIdByPath(String path) { 557 return service.getObjectByPath(repositoryId, path, PropertyIds.OBJECT_ID, Boolean.FALSE, 558 IncludeRelationships.NONE, RENDITION_NONE, Boolean.FALSE, Boolean.FALSE, null).getId(); 559 } 560 561 @Override 562 public RepositoryInfo getRepositoryInfo() { 563 return service.getRepositoryInfo(repositoryId, null); 564 } 565 566 @Override 567 public Folder getRootFolder() { 568 return getRootFolder(getDefaultContext()); 569 } 570 571 @Override 572 public Folder getRootFolder(OperationContext context) { 573 String id = getRepositoryInfo().getRootFolderId(); 574 CmisObject folder = getObject(createObjectId(id), context); 575 if (!(folder instanceof Folder)) { 576 throw new CmisRuntimeException("Root object is not a Folder but: " + folder.getClass().getName()); 577 } 578 return (Folder) folder; 579 } 580 581 @Override 582 public ItemIterable<ObjectType> getTypeChildren(String typeId, boolean includePropertyDefinitions) { 583 // TODO Auto-generated method stub 584 throw new UnsupportedOperationException(); 585 } 586 587 @Override 588 public ObjectType getTypeDefinition(String typeId) { 589 TypeDefinition typeDefinition = service.getTypeDefinition(repositoryId, typeId, null); 590 return objectFactory.convertTypeDefinition(typeDefinition); 591 } 592 593 @Override 594 public ObjectType getTypeDefinition(String typeId, boolean useCache) { 595 return getTypeDefinition(typeId); 596 } 597 598 @Override 599 public List<Tree<ObjectType>> getTypeDescendants(String typeId, int depth, boolean includePropertyDefinitions) { 600 // TODO Auto-generated method stub 601 throw new UnsupportedOperationException(); 602 } 603 604 @Override 605 public ItemIterable<QueryResult> query(String statement, boolean searchAllVersions) { 606 // TODO Auto-generated method stub 607 throw new UnsupportedOperationException(); 608 } 609 610 @Override 611 public ItemIterable<QueryResult> query(final String statement, final boolean searchAllVersions, 612 final OperationContext context) { 613 AbstractPageFetcher<QueryResult> pageFetcher = new AbstractPageFetcher<QueryResult>( 614 context.getMaxItemsPerPage()) { 615 @Override 616 protected Page<QueryResult> fetchPage(long skipCount) { 617 ObjectList results = service.query(repositoryId, statement, Boolean.valueOf(searchAllVersions), 618 Boolean.valueOf(context.isIncludeAllowableActions()), context.getIncludeRelationships(), 619 context.getRenditionFilterString(), BigInteger.valueOf(maxNumItems), 620 BigInteger.valueOf(skipCount), null); 621 // convert objects 622 List<QueryResult> page = new ArrayList<QueryResult>(); 623 if (results.getObjects() != null) { 624 for (ObjectData data : results.getObjects()) { 625 page.add(new QueryResultImpl(NuxeoSession.this, data)); 626 } 627 } 628 return new Page<QueryResult>(page, results.getNumItems(), results.hasMoreItems()); 629 } 630 }; 631 return new CollectionIterable<QueryResult>(pageFetcher); 632 } 633 634 @Override 635 public ItemIterable<CmisObject> queryObjects(String typeId, String where, boolean searchAllVersions, 636 OperationContext context) { 637 // TODO Auto-generated method stub 638 throw new UnsupportedOperationException(); 639 } 640 641 @Override 642 public QueryStatement createQueryStatement(String statement) { 643 return new QueryStatementImpl(this, statement); 644 } 645 646 @Override 647 public QueryStatement createQueryStatement(Collection<String> selectPropertyIds, Map<String, String> fromTypes, 648 String whereClause, List<String> orderByPropertyIds) { 649 return new QueryStatementImpl(this, selectPropertyIds, fromTypes, whereClause, orderByPropertyIds); 650 } 651 652 @Override 653 public ItemIterable<Relationship> getRelationships(final ObjectId objectId, 654 final boolean includeSubRelationshipTypes, final RelationshipDirection relationshipDirection, 655 final ObjectType type, final OperationContext context) { 656 final String typeId = type == null ? null : type.getId(); 657 AbstractPageFetcher<Relationship> pageFetcher = new AbstractPageFetcher<Relationship>( 658 context.getMaxItemsPerPage()) { 659 @Override 660 protected Page<Relationship> fetchPage(long skipCount) { 661 ObjectList relations = service.getObjectRelationships(repositoryId, objectId.getId(), 662 Boolean.valueOf(includeSubRelationshipTypes), relationshipDirection, typeId, null, null, 663 BigInteger.valueOf(maxNumItems), BigInteger.valueOf(skipCount), null); 664 // convert objects 665 List<Relationship> page = new ArrayList<Relationship>(); 666 if (relations.getObjects() != null) { 667 for (ObjectData data : relations.getObjects()) { 668 CmisObject ob; 669 if (data instanceof NuxeoObjectData) { 670 ob = objectFactory.convertObject(data, context); 671 } else { 672 ob = getObject(data.getId(), context); 673 } 674 if (!(ob instanceof Relationship)) { 675 // should not happen... 676 continue; 677 } 678 page.add((Relationship) ob); 679 } 680 } 681 return new Page<Relationship>(page, relations.getNumItems(), relations.hasMoreItems()); 682 } 683 }; 684 return new CollectionIterable<Relationship>(pageFetcher); 685 } 686 687 @Override 688 public Acl getAcl(ObjectId objectId, boolean onlyBasicPermissions) { 689 return service.getAcl(repositoryId, objectId.getId(), Boolean.valueOf(onlyBasicPermissions), null); 690 } 691 692 @Override 693 public Acl setAcl(ObjectId objectId, List<Ace> aces) { 694 return service.applyAcl(repositoryId, objectId.getId(), new AccessControlListImpl(aces), null); 695 } 696 697 @Override 698 public Acl applyAcl(ObjectId objectId, List<Ace> addAces, List<Ace> removeAces, AclPropagation aclPropagation) { 699 return service.applyAcl(repositoryId, objectId.getId(), new AccessControlListImpl(addAces), 700 new AccessControlListImpl(removeAces), aclPropagation, null); 701 } 702 703 @Override 704 public void applyPolicy(ObjectId objectId, ObjectId... policyIds) { 705 throw new CmisNotSupportedException(); 706 } 707 708 @Override 709 public void removePolicy(ObjectId objectId, ObjectId... policyIds) { 710 throw new CmisNotSupportedException(); 711 } 712 713 @Override 714 public void removeObjectFromCache(ObjectId objectId) { 715 } 716 717 @Override 718 public void removeObjectFromCache(String objectId) { 719 } 720 721 @Override 722 public void delete(ObjectId objectId) { 723 delete(objectId, true); 724 } 725 726 @Override 727 public void delete(ObjectId objectId, boolean allVersions) { 728 service.deleteObject(repositoryId, objectId.getId(), Boolean.valueOf(allVersions), null); 729 } 730 731 @Override 732 public void deleteByPath(String path) { 733 deleteByPath(path, true); 734 } 735 736 @Override 737 public void deleteByPath(String parentPath, String name) { 738 deleteByPath(buildPath(parentPath, name), true); 739 } 740 741 @Override 742 public void deleteByPath(String path, boolean allVersions) { 743 checkPath(path); 744 delete(new ObjectIdImpl(getObjectIdByPath(path)), allVersions); 745 } 746 747 @Override 748 public List<String> deleteTree(ObjectId folderId, boolean allVersions, UnfileObject unfile, 749 boolean continueOnFailure) { 750 FailedToDeleteData res = service.deleteTree(repositoryId, folderId.getId(), Boolean.valueOf(allVersions), 751 unfile, Boolean.valueOf(continueOnFailure), null); 752 return res.getIds(); 753 } 754 755 @Override 756 public List<String> deleteTreebyPath(String parentPath, String name, boolean allVersions, UnfileObject unfile, 757 boolean continueOnFailure) { 758 return deleteTreebyPath(buildPath(parentPath, name), allVersions, unfile, continueOnFailure); 759 } 760 761 @Override 762 public List<String> deleteTreebyPath(String path, boolean allVersions, UnfileObject unfile, 763 boolean continueOnFailure) { 764 checkPath(path); 765 return deleteTree(new ObjectIdImpl(getObjectIdByPath(path)), allVersions, unfile, continueOnFailure); 766 } 767 768 /** Checks that the path is valid. */ 769 protected final void checkPath(String path) { 770 if (StringUtils.isEmpty(path)) { 771 throw new CmisInvalidArgumentException("Missing path"); 772 } 773 if (!path.startsWith("/")) { 774 throw new CmisInvalidArgumentException("Path must start with a slash"); 775 } 776 } 777 778 /** Checks that the parent path and name are valid, and builds a full path from them. */ 779 protected String buildPath(String parentPath, String name) { 780 checkPath(parentPath); 781 if (StringUtils.isEmpty(name)) { 782 throw new CmisInvalidArgumentException("Missing name"); 783 } 784 if (name.startsWith("/")) { 785 throw new CmisInvalidArgumentException("Name must not start with a slash"); 786 } 787 StringBuilder path = new StringBuilder(parentPath.length() + name.length() + 1); 788 path.append(parentPath); 789 if (!parentPath.endsWith("/")) { 790 path.append('/'); 791 } 792 path.append(name); 793 return path.toString(); 794 } 795 796 @Override 797 public ContentStream getContentStream(ObjectId docId) { 798 return getContentStream(docId, null, null, null); 799 } 800 801 @Override 802 public ContentStream getContentStream(ObjectId docId, String streamId, BigInteger offset, BigInteger length) { 803 if (docId == null) { 804 throw new CmisInvalidArgumentException("Missing object ID"); 805 } 806 return service.getContentStream(repositoryId, docId.getId(), streamId, offset, length, null); 807 } 808 809 @Override 810 public ContentStream getContentStreamByPath(String path) { 811 return getContentStreamByPath(path, null, null, null); 812 } 813 814 @Override 815 public ContentStream getContentStreamByPath(String path, String streamId, BigInteger offset, BigInteger length) { 816 checkPath(path); 817 return service.getContentStream(repositoryId, getObjectIdByPath(path), streamId, offset, length, null); 818 } 819 820 @Override 821 public ObjectType createType(TypeDefinition type) { 822 throw new CmisNotSupportedException(); 823 } 824 825 @Override 826 public ObjectType updateType(TypeDefinition type) { 827 throw new CmisNotSupportedException(); 828 } 829 830 @Override 831 public void deleteType(String typeId) { 832 throw new CmisNotSupportedException(); 833 } 834 835 @Override 836 public List<BulkUpdateObjectIdAndChangeToken> bulkUpdateProperties(List<CmisObject> objects, 837 Map<String, ?> properties, List<String> addSecondaryTypeIds, List<String> removeSecondaryTypeIds) { 838 List<BulkUpdateObjectIdAndChangeToken> idts = new ArrayList<BulkUpdateObjectIdAndChangeToken>(objects.size()); 839 for (CmisObject object : objects) { 840 idts.add(new BulkUpdateObjectIdAndChangeTokenImpl(object.getId(), object.getChangeToken())); 841 } 842 return service.bulkUpdateProperties(repositoryId, idts, convertProperties(properties), addSecondaryTypeIds, 843 removeSecondaryTypeIds, null); 844 } 845 846 @Override 847 public Document getLatestDocumentVersion(ObjectId objectId) { 848 return getLatestDocumentVersion(objectId, false, getDefaultContext()); 849 } 850 851 @Override 852 public Document getLatestDocumentVersion(String objectId, OperationContext context) { 853 if (objectId == null) { 854 throw new IllegalArgumentException("Object ID must be set!"); 855 } 856 return getLatestDocumentVersion(createObjectId(objectId), false, context); 857 } 858 859 @Override 860 public Document getLatestDocumentVersion(String objectId, boolean major, OperationContext context) { 861 if (objectId == null) { 862 throw new IllegalArgumentException("Object ID must be set!"); 863 } 864 return getLatestDocumentVersion(createObjectId(objectId), major, context); 865 } 866 867 @Override 868 public Document getLatestDocumentVersion(String objectId) { 869 if (objectId == null) { 870 throw new IllegalArgumentException("Object ID must be set!"); 871 } 872 return getLatestDocumentVersion(createObjectId(objectId), false, getDefaultContext()); 873 } 874 875 @Override 876 public Document getLatestDocumentVersion(ObjectId objectId, OperationContext context) { 877 return getLatestDocumentVersion(objectId, false, context); 878 } 879 880 @Override 881 /** 882 * @See org.apache.chemistry.opencmis.client.runtime.SessionImpl 883 */ 884 public Document getLatestDocumentVersion(ObjectId objectId, boolean major, OperationContext context) { 885 if (objectId == null || objectId.getId() == null) { 886 throw new IllegalArgumentException("Object ID must be set!"); 887 } 888 889 if (context == null) { 890 throw new IllegalArgumentException("Operation context must be set!"); 891 } 892 893 CmisObject result = null; 894 895 String versionSeriesId = null; 896 897 // first attempt: if we got a Document object, try getting the version 898 // series ID from it 899 if (objectId instanceof Document) { 900 versionSeriesId = ((Document) objectId).getVersionSeriesId(); 901 } 902 903 // (the AtomPub and Browser binding don't need the version series ID -> 904 // avoid roundtrip) 905 906 // get the object 907 ObjectData objectData = binding.getVersioningService().getObjectOfLatestVersion(getRepositoryId(), 908 objectId.getId(), versionSeriesId, major, context.getFilterString(), 909 context.isIncludeAllowableActions(), context.getIncludeRelationships(), 910 context.getRenditionFilterString(), context.isIncludePolicies(), context.isIncludeAcls(), null); 911 912 result = getObjectFactory().convertObject(objectData, context); 913 914 // check result 915 if (!(result instanceof Document)) { 916 throw new IllegalArgumentException("Latest version is not a document!"); 917 } 918 919 return (Document) result; 920 } 921 922 @Override 923 public String getLatestChangeLogToken() { 924 return getBinding().getRepositoryService().getRepositoryInfo(getRepositoryId(), null).getLatestChangeLogToken(); 925 } 926 927}