001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.opencms.cmis; 021 022import org.opencms.file.CmsObject; 023import org.opencms.file.CmsPropertyDefinition; 024import org.opencms.main.CmsException; 025import org.opencms.main.CmsLog; 026import org.opencms.relations.CmsRelationType; 027 028import java.math.BigInteger; 029import java.util.ArrayList; 030import java.util.Collections; 031import java.util.HashMap; 032import java.util.List; 033import java.util.Map; 034 035import org.apache.chemistry.opencmis.commons.PropertyIds; 036import org.apache.chemistry.opencmis.commons.definitions.PropertyDefinition; 037import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; 038import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer; 039import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionList; 040import org.apache.chemistry.opencmis.commons.enums.BaseTypeId; 041import org.apache.chemistry.opencmis.commons.enums.Cardinality; 042import org.apache.chemistry.opencmis.commons.enums.ContentStreamAllowed; 043import org.apache.chemistry.opencmis.commons.enums.PropertyType; 044import org.apache.chemistry.opencmis.commons.enums.Updatability; 045import org.apache.chemistry.opencmis.commons.exceptions.CmisInvalidArgumentException; 046import org.apache.chemistry.opencmis.commons.exceptions.CmisObjectNotFoundException; 047import org.apache.chemistry.opencmis.commons.impl.WSConverter; 048import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractPropertyDefinition; 049import org.apache.chemistry.opencmis.commons.impl.dataobjects.AbstractTypeDefinition; 050import org.apache.chemistry.opencmis.commons.impl.dataobjects.DocumentTypeDefinitionImpl; 051import org.apache.chemistry.opencmis.commons.impl.dataobjects.FolderTypeDefinitionImpl; 052import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyBooleanDefinitionImpl; 053import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDateTimeDefinitionImpl; 054import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyDecimalDefinitionImpl; 055import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyHtmlDefinitionImpl; 056import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIdDefinitionImpl; 057import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyIntegerDefinitionImpl; 058import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyStringDefinitionImpl; 059import org.apache.chemistry.opencmis.commons.impl.dataobjects.PropertyUriDefinitionImpl; 060import org.apache.chemistry.opencmis.commons.impl.dataobjects.RelationshipTypeDefinitionImpl; 061import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionContainerImpl; 062import org.apache.chemistry.opencmis.commons.impl.dataobjects.TypeDefinitionListImpl; 063import org.apache.commons.logging.Log; 064 065/** 066 * This class keeps track of all the types which should be available for a {@link I_CmsCmisRepository}. 067 */ 068public class CmsCmisTypeManager { 069 070 /** CMIS type id for documents. */ 071 public static final String DOCUMENT_TYPE_ID = BaseTypeId.CMIS_DOCUMENT.value(); 072 073 /** CMIS type id for folders. */ 074 public static final String FOLDER_TYPE_ID = BaseTypeId.CMIS_FOLDER.value(); 075 076 /** Prefix for inherited properties. */ 077 public static final String INHERITED_PREFIX = "opencms-inherited:"; 078 079 /** CMIS type id for policies. */ 080 public static final String POLICY_TYPE_ID = BaseTypeId.CMIS_POLICY.value(); 081 082 /** The prefix used for normal OpenCms resource properties. */ 083 public static final String PROPERTY_PREFIX = "opencms:"; 084 085 /** Prefix for dynamic properties. */ 086 public static final String PROPERTY_PREFIX_DYNAMIC = "opencms-dynamic:"; 087 088 /** The prefix for special properties. */ 089 public static final String PROPERTY_PREFIX_SPECIAL = "opencms-special:"; 090 091 /** The name of the propery containing the resource type name. */ 092 public static final String PROPERTY_RESOURCE_TYPE = PROPERTY_PREFIX_SPECIAL + "resource-type"; 093 094 /** CMIS type id for relationships. */ 095 public static final String RELATIONSHIP_TYPE_ID = BaseTypeId.CMIS_RELATIONSHIP.value(); 096 097 /** Need to refresh property data after this time. */ 098 public static final long UPDATE_INTERVAL = 1000 * 60 * 5; 099 100 /** The logger instance for this class. */ 101 static final Log LOG = CmsLog.getLog(CmsCmisTypeManager.class); 102 103 /** The namespace used for properties. */ 104 private static final String NAMESPACE = "http://opencms.org/opencms-cmis"; 105 106 /** The admin CMS context. */ 107 private CmsObject m_adminCms; 108 109 /** The list of OpenCms property definitions. */ 110 private List<CmsPropertyDefinition> m_cmsPropertyDefinitions; 111 112 /** The last update time. */ 113 private long m_lastUpdate; 114 115 /** List of dynamic property providers. */ 116 private List<I_CmsPropertyProvider> m_propertyProviders = new ArrayList<I_CmsPropertyProvider>(); 117 118 /** The internal list of type definitions. */ 119 private List<TypeDefinitionContainer> m_typeList; 120 121 /** The internal map of type definitions. */ 122 private Map<String, TypeDefinitionContainerImpl> m_types; 123 124 /** 125 * Creates a new type manager instance.<p> 126 * 127 * @param adminCms a CMS context with admin privileges 128 * @param propertyProviders list which will be filled with property providers 129 * 130 * @throws CmsException if something goes wrong 131 */ 132 public CmsCmisTypeManager(CmsObject adminCms, List<I_CmsPropertyProvider> propertyProviders) 133 throws CmsException { 134 135 m_adminCms = adminCms; 136 m_propertyProviders = propertyProviders; 137 setup(); 138 } 139 140 /** 141 * Adds the base CMIS property definitions common to folders and documents.<p> 142 * 143 * @param type the type definition to which the property definitions should be added 144 */ 145 private static void addBasePropertyDefinitions(AbstractTypeDefinition type) { 146 147 type.addPropertyDefinition( 148 createPropDef( 149 PropertyIds.BASE_TYPE_ID, 150 "Base Type Id", 151 "Base Type Id", 152 PropertyType.ID, 153 Cardinality.SINGLE, 154 Updatability.READONLY, 155 false, 156 false)); 157 158 type.addPropertyDefinition( 159 createPropDef( 160 PropertyIds.OBJECT_ID, 161 "Object Id", 162 "Object Id", 163 PropertyType.ID, 164 Cardinality.SINGLE, 165 Updatability.READONLY, 166 false, 167 false)); 168 169 type.addPropertyDefinition( 170 createPropDef( 171 PropertyIds.OBJECT_TYPE_ID, 172 "Type Id", 173 "Type Id", 174 PropertyType.ID, 175 Cardinality.SINGLE, 176 Updatability.ONCREATE, 177 false, 178 true)); 179 180 type.addPropertyDefinition( 181 createPropDef( 182 PropertyIds.NAME, 183 "Name", 184 "Name", 185 PropertyType.STRING, 186 Cardinality.SINGLE, 187 Updatability.READWRITE, 188 false, 189 true)); 190 191 type.addPropertyDefinition( 192 queryableAndOrderable( 193 createPropDef( 194 PropertyIds.CREATED_BY, 195 "Created By", 196 "Created By", 197 PropertyType.STRING, 198 Cardinality.SINGLE, 199 Updatability.READONLY, 200 false, 201 false))); 202 203 type.addPropertyDefinition( 204 queryableAndOrderable( 205 createPropDef( 206 PropertyIds.CREATION_DATE, 207 "Creation Date", 208 "Creation Date", 209 PropertyType.DATETIME, 210 Cardinality.SINGLE, 211 Updatability.READONLY, 212 false, 213 false))); 214 215 type.addPropertyDefinition( 216 queryableAndOrderable( 217 createPropDef( 218 PropertyIds.LAST_MODIFIED_BY, 219 "Last Modified By", 220 "Last Modified By", 221 PropertyType.STRING, 222 Cardinality.SINGLE, 223 Updatability.READONLY, 224 false, 225 false))); 226 227 type.addPropertyDefinition( 228 queryableAndOrderable( 229 createPropDef( 230 PropertyIds.LAST_MODIFICATION_DATE, 231 "Last Modification Date", 232 "Last Modification Date", 233 PropertyType.DATETIME, 234 Cardinality.SINGLE, 235 Updatability.READONLY, 236 false, 237 false))); 238 239 type.addPropertyDefinition( 240 createPropDef( 241 PropertyIds.CHANGE_TOKEN, 242 "Change Token", 243 "Change Token", 244 PropertyType.STRING, 245 Cardinality.SINGLE, 246 Updatability.READONLY, 247 false, 248 false)); 249 250 } 251 252 /** 253 * Adds CMIS property definitions for documents.<p> 254 * 255 * @param type the document type 256 */ 257 private static void addDocumentPropertyDefinitions(DocumentTypeDefinitionImpl type) { 258 259 type.addPropertyDefinition( 260 createPropDef( 261 PropertyIds.IS_IMMUTABLE, 262 "Is Immutable", 263 "Is Immutable", 264 PropertyType.BOOLEAN, 265 Cardinality.SINGLE, 266 Updatability.READONLY, 267 false, 268 false)); 269 270 type.addPropertyDefinition( 271 createPropDef( 272 PropertyIds.IS_LATEST_VERSION, 273 "Is Latest Version", 274 "Is Latest Version", 275 PropertyType.BOOLEAN, 276 Cardinality.SINGLE, 277 Updatability.READONLY, 278 false, 279 false)); 280 281 type.addPropertyDefinition( 282 createPropDef( 283 PropertyIds.IS_MAJOR_VERSION, 284 "Is Major Version", 285 "Is Major Version", 286 PropertyType.BOOLEAN, 287 Cardinality.SINGLE, 288 Updatability.READONLY, 289 false, 290 false)); 291 292 type.addPropertyDefinition( 293 createPropDef( 294 PropertyIds.IS_LATEST_MAJOR_VERSION, 295 "Is Latest Major Version", 296 "Is Latest Major Version", 297 PropertyType.BOOLEAN, 298 Cardinality.SINGLE, 299 Updatability.READONLY, 300 false, 301 false)); 302 303 type.addPropertyDefinition( 304 createPropDef( 305 PropertyIds.VERSION_LABEL, 306 "Version Label", 307 "Version Label", 308 PropertyType.STRING, 309 Cardinality.SINGLE, 310 Updatability.READONLY, 311 false, 312 false)); 313 314 type.addPropertyDefinition( 315 createPropDef( 316 PropertyIds.VERSION_SERIES_ID, 317 "Version Series Id", 318 "Version Series Id", 319 PropertyType.ID, 320 Cardinality.SINGLE, 321 Updatability.READONLY, 322 false, 323 false)); 324 325 type.addPropertyDefinition( 326 createPropDef( 327 PropertyIds.IS_VERSION_SERIES_CHECKED_OUT, 328 "Is Verison Series Checked Out", 329 "Is Verison Series Checked Out", 330 PropertyType.BOOLEAN, 331 Cardinality.SINGLE, 332 Updatability.READONLY, 333 false, 334 false)); 335 336 type.addPropertyDefinition( 337 createPropDef( 338 PropertyIds.VERSION_SERIES_CHECKED_OUT_ID, 339 "Version Series Checked Out Id", 340 "Version Series Checked Out Id", 341 PropertyType.ID, 342 Cardinality.SINGLE, 343 Updatability.READONLY, 344 false, 345 false)); 346 347 type.addPropertyDefinition( 348 createPropDef( 349 PropertyIds.VERSION_SERIES_CHECKED_OUT_BY, 350 "Version Series Checked Out By", 351 "Version Series Checked Out By", 352 PropertyType.STRING, 353 Cardinality.SINGLE, 354 Updatability.READONLY, 355 false, 356 false)); 357 358 type.addPropertyDefinition( 359 createPropDef( 360 PropertyIds.CHECKIN_COMMENT, 361 "Checkin Comment", 362 "Checkin Comment", 363 PropertyType.STRING, 364 Cardinality.SINGLE, 365 Updatability.READONLY, 366 false, 367 false)); 368 369 type.addPropertyDefinition( 370 createPropDef( 371 PropertyIds.CONTENT_STREAM_LENGTH, 372 "Content Stream Length", 373 "Content Stream Length", 374 PropertyType.INTEGER, 375 Cardinality.SINGLE, 376 Updatability.READONLY, 377 false, 378 false)); 379 380 type.addPropertyDefinition( 381 createPropDef( 382 PropertyIds.CONTENT_STREAM_MIME_TYPE, 383 "MIME Type", 384 "MIME Type", 385 PropertyType.STRING, 386 Cardinality.SINGLE, 387 Updatability.READONLY, 388 false, 389 false)); 390 391 type.addPropertyDefinition( 392 createPropDef( 393 PropertyIds.CONTENT_STREAM_FILE_NAME, 394 "Filename", 395 "Filename", 396 PropertyType.STRING, 397 Cardinality.SINGLE, 398 Updatability.READONLY, 399 false, 400 false)); 401 402 type.addPropertyDefinition( 403 createPropDef( 404 PropertyIds.CONTENT_STREAM_ID, 405 "Content Stream Id", 406 "Content Stream Id", 407 PropertyType.ID, 408 Cardinality.SINGLE, 409 Updatability.READONLY, 410 false, 411 false)); 412 } 413 414 /** 415 * Adds folder specific CMIS property definitions.<p> 416 * 417 * @param type the folder type 418 */ 419 private static void addFolderPropertyDefinitions(FolderTypeDefinitionImpl type) { 420 421 type.addPropertyDefinition( 422 createPropDef( 423 PropertyIds.PARENT_ID, 424 "Parent Id", 425 "Parent Id", 426 PropertyType.ID, 427 Cardinality.SINGLE, 428 Updatability.READONLY, 429 false, 430 false)); 431 432 type.addPropertyDefinition( 433 createPropDef( 434 PropertyIds.ALLOWED_CHILD_OBJECT_TYPE_IDS, 435 "Allowed Child Object Type Ids", 436 "Allowed Child Object Type Ids", 437 PropertyType.ID, 438 Cardinality.MULTI, 439 Updatability.READONLY, 440 false, 441 false)); 442 443 type.addPropertyDefinition( 444 createPropDef( 445 PropertyIds.PATH, 446 "Path", 447 "Path", 448 PropertyType.STRING, 449 Cardinality.SINGLE, 450 Updatability.READONLY, 451 false, 452 false)); 453 } 454 455 /** 456 * Helper method to add the property definitions specific to relationship types.<p> 457 * 458 * @param type the type definition to which the property definitions should be added 459 */ 460 private static void addRelationPropertyDefinitions(RelationshipTypeDefinitionImpl type) { 461 462 type.addPropertyDefinition( 463 createPropDef( 464 PropertyIds.SOURCE_ID, 465 "Source", 466 "Source", 467 PropertyType.ID, 468 Cardinality.SINGLE, 469 Updatability.ONCREATE, 470 false, 471 true)); 472 473 type.addPropertyDefinition( 474 createPropDef( 475 PropertyIds.TARGET_ID, 476 "Target", 477 "Target", 478 PropertyType.ID, 479 Cardinality.SINGLE, 480 Updatability.ONCREATE, 481 false, 482 true)); 483 484 } 485 486 /** 487 * Copies a type definition.<p> 488 * 489 * @param type the type definition to copy 490 * 491 * @return the copied type definition 492 */ 493 private static TypeDefinition copyTypeDefintion(TypeDefinition type) { 494 495 return WSConverter.convert(WSConverter.convert(type)); 496 } 497 498 /** 499 * Creates a property definition.<p> 500 * 501 * @param id the property definition id 502 * @param displayName the property display name 503 * @param description the property description 504 * @param datatype the property type 505 * @param cardinality the property cardinality 506 * @param updateability the property updatability 507 * @param inherited the property inheritance status 508 * @param required true true if the property is required 509 * 510 * @return the property definition 511 */ 512 private static AbstractPropertyDefinition<?> createPropDef( 513 String id, 514 String displayName, 515 String description, 516 PropertyType datatype, 517 Cardinality cardinality, 518 Updatability updateability, 519 boolean inherited, 520 boolean required) { 521 522 AbstractPropertyDefinition<?> result = null; 523 524 switch (datatype) { 525 case BOOLEAN: 526 result = new PropertyBooleanDefinitionImpl(); 527 break; 528 case DATETIME: 529 result = new PropertyDateTimeDefinitionImpl(); 530 break; 531 case DECIMAL: 532 result = new PropertyDecimalDefinitionImpl(); 533 break; 534 case HTML: 535 result = new PropertyHtmlDefinitionImpl(); 536 break; 537 case ID: 538 result = new PropertyIdDefinitionImpl(); 539 break; 540 case INTEGER: 541 result = new PropertyIntegerDefinitionImpl(); 542 break; 543 case STRING: 544 result = new PropertyStringDefinitionImpl(); 545 break; 546 case URI: 547 result = new PropertyUriDefinitionImpl(); 548 break; 549 default: 550 throw new RuntimeException("Unknown datatype! Spec change?"); 551 } 552 553 result.setId(id); 554 result.setLocalName(id); 555 result.setDisplayName(displayName); 556 result.setDescription(description); 557 result.setPropertyType(datatype); 558 result.setCardinality(cardinality); 559 result.setUpdatability(updateability); 560 result.setIsInherited(Boolean.valueOf(inherited)); 561 result.setIsRequired(Boolean.valueOf(required)); 562 result.setIsQueryable(Boolean.FALSE); 563 result.setIsOrderable(Boolean.FALSE); 564 result.setQueryName(id); 565 return result; 566 } 567 568 /** 569 * Helper method to make a property definition queryable and orderable.<p> 570 * 571 * @param propDef the property definition 572 * 573 * @return the modified property definition 574 */ 575 private static AbstractPropertyDefinition<?> queryableAndOrderable(AbstractPropertyDefinition<?> propDef) { 576 577 propDef.setIsQueryable(Boolean.TRUE); 578 propDef.setIsOrderable(Boolean.TRUE); 579 return propDef; 580 } 581 582 /** 583 * Gets a list of names of OpenCms property definitions.<p> 584 * 585 * @return the list of OpenCms property names 586 */ 587 public List<String> getCmsPropertyNames() { 588 589 refresh(); 590 List<String> result = new ArrayList<String>(); 591 for (CmsPropertyDefinition propDef : m_cmsPropertyDefinitions) { 592 result.add(propDef.getName()); 593 } 594 return result; 595 } 596 597 /** 598 * Gets the property provider for a given key.<p> 599 * 600 * @param key the property nme 601 * 602 * @return the property provider for the given name, or null if there isn't any 603 */ 604 public I_CmsPropertyProvider getPropertyProvider(String key) { 605 606 if (key.startsWith(PROPERTY_PREFIX_DYNAMIC)) { 607 key = key.substring(PROPERTY_PREFIX_DYNAMIC.length()); 608 } 609 for (I_CmsPropertyProvider provider : m_propertyProviders) { 610 if (provider.getName().equals(key)) { 611 return provider; 612 } 613 } 614 return null; 615 } 616 617 /** 618 * Gets the list of all property providers.<p> 619 * 620 * @return the list of property providers 621 */ 622 public List<I_CmsPropertyProvider> getPropertyProviders() { 623 624 return Collections.unmodifiableList(m_propertyProviders); 625 } 626 627 /** 628 * Gets a type definition by id.<p> 629 * 630 * @param typeId the type id 631 * @return the type definition 632 */ 633 public TypeDefinition getType(String typeId) { 634 635 refresh(); 636 TypeDefinitionContainer tc = m_types.get(typeId); 637 if (tc == null) { 638 return null; 639 } 640 641 return tc.getTypeDefinition(); 642 } 643 644 /** 645 * Collects the children of a type.<p> 646 * 647 * @param typeId the id of the type 648 * @param includePropertyDefinitions true if the property definitions should be included 649 * @param maxItems the maximum number of items to return 650 * @param skipCount the number of items to skip 651 * 652 * @return the children of the type 653 */ 654 public TypeDefinitionList getTypeChildren( 655 656 String typeId, 657 boolean includePropertyDefinitions, 658 BigInteger maxItems, 659 BigInteger skipCount) { 660 661 refresh(); 662 TypeDefinitionListImpl result = new TypeDefinitionListImpl(new ArrayList<TypeDefinition>()); 663 664 int skip = (skipCount == null ? 0 : skipCount.intValue()); 665 if (skip < 0) { 666 skip = 0; 667 } 668 669 int max = (maxItems == null ? Integer.MAX_VALUE : maxItems.intValue()); 670 if (max < 1) { 671 return result; 672 } 673 674 if (typeId == null) { 675 if (skip < 1) { 676 result.getList().add(copyTypeDefintion(m_types.get(FOLDER_TYPE_ID).getTypeDefinition())); 677 max--; 678 } 679 if ((skip < 2) && (max > 0)) { 680 result.getList().add(copyTypeDefintion(m_types.get(DOCUMENT_TYPE_ID).getTypeDefinition())); 681 max--; 682 } 683 684 result.setHasMoreItems(Boolean.valueOf((result.getList().size() + skip) < 2)); 685 result.setNumItems(BigInteger.valueOf(2)); 686 } else { 687 TypeDefinitionContainer tc = m_types.get(typeId); 688 if ((tc == null) || (tc.getChildren() == null)) { 689 return result; 690 } 691 692 for (TypeDefinitionContainer child : tc.getChildren()) { 693 if (skip > 0) { 694 skip--; 695 continue; 696 } 697 698 result.getList().add(copyTypeDefintion(child.getTypeDefinition())); 699 700 max--; 701 if (max == 0) { 702 break; 703 } 704 } 705 706 result.setHasMoreItems(Boolean.valueOf((result.getList().size() + skip) < tc.getChildren().size())); 707 result.setNumItems(BigInteger.valueOf(tc.getChildren().size())); 708 } 709 710 if (!includePropertyDefinitions) { 711 for (TypeDefinition type : result.getList()) { 712 type.getPropertyDefinitions().clear(); 713 } 714 } 715 716 return result; 717 } 718 719 /** 720 * Gets the type definition for a given id in the given call context.<p> 721 * 722 * @param typeId the type id 723 * 724 * @return the matching type definition 725 */ 726 public TypeDefinition getTypeDefinition(String typeId) { 727 728 refresh(); 729 TypeDefinitionContainer tc = m_types.get(typeId); 730 if (tc == null) { 731 throw new CmisObjectNotFoundException("Type '" + typeId + "' is unknown!"); 732 } 733 734 return copyTypeDefintion(tc.getTypeDefinition()); 735 } 736 737 /** 738 * Gets the descendants of a type.<p> 739 * 740 * @param typeId the parent type id 741 * @param depth the depth up to which the descendant types should be collected 742 * @param includePropertyDefinitions true if the property definitions should be included 743 * 744 * @return the descendants of the type 745 */ 746 public List<TypeDefinitionContainer> getTypeDescendants( 747 748 String typeId, 749 BigInteger depth, 750 boolean includePropertyDefinitions) { 751 752 refresh(); 753 List<TypeDefinitionContainer> result = new ArrayList<TypeDefinitionContainer>(); 754 755 // check depth 756 int d = (depth == null ? -1 : depth.intValue()); 757 if (d == 0) { 758 throw new CmisInvalidArgumentException("Depth must not be 0!"); 759 } 760 761 if (typeId == null) { 762 result.add(getTypeDescendants(d, m_types.get(FOLDER_TYPE_ID), includePropertyDefinitions)); 763 result.add(getTypeDescendants(d, m_types.get(DOCUMENT_TYPE_ID), includePropertyDefinitions)); 764 result.add(getTypeDescendants(d, m_types.get(RELATIONSHIP_TYPE_ID), includePropertyDefinitions)); 765 } else { 766 TypeDefinitionContainer tc = m_types.get(typeId); 767 if (tc != null) { 768 result.add(getTypeDescendants(d, tc, includePropertyDefinitions)); 769 } 770 } 771 772 return result; 773 } 774 775 /** 776 * Creates the CMIS property definition for an OpenCms resource property definition.<p> 777 * 778 * @param cmsDef the OpenCms property definition 779 * 780 * @return the CMIS property definition 781 */ 782 PropertyDefinition<?> createOpenCmsPropertyDefinition(CmsPropertyDefinition cmsDef) { 783 784 return createPropDef( 785 PROPERTY_PREFIX + cmsDef.getName(), 786 cmsDef.getName(), 787 cmsDef.getName(), 788 PropertyType.STRING, 789 Cardinality.SINGLE, 790 Updatability.READWRITE, 791 false, 792 false); 793 } 794 795 /** 796 * Creates the base types. 797 * 798 * @throws CmsException if something goes wrong 799 */ 800 void setup() throws CmsException { 801 802 m_types = new HashMap<String, TypeDefinitionContainerImpl>(); 803 m_typeList = new ArrayList<TypeDefinitionContainer>(); 804 m_cmsPropertyDefinitions = m_adminCms.readAllPropertyDefinitions(); 805 806 // folder type 807 FolderTypeDefinitionImpl folderType = new FolderTypeDefinitionImpl(); 808 folderType.setBaseTypeId(BaseTypeId.CMIS_FOLDER); 809 folderType.setIsControllableAcl(Boolean.TRUE); 810 folderType.setIsControllablePolicy(Boolean.FALSE); 811 folderType.setIsCreatable(Boolean.TRUE); 812 folderType.setDescription("Folder"); 813 folderType.setDisplayName("Folder"); 814 folderType.setIsFileable(Boolean.TRUE); 815 folderType.setIsFulltextIndexed(Boolean.FALSE); 816 folderType.setIsIncludedInSupertypeQuery(Boolean.TRUE); 817 folderType.setLocalName("Folder"); 818 folderType.setLocalNamespace(NAMESPACE); 819 folderType.setIsQueryable(Boolean.TRUE); 820 folderType.setQueryName("cmis:folder"); 821 folderType.setId(FOLDER_TYPE_ID); 822 823 addBasePropertyDefinitions(folderType); 824 addFolderPropertyDefinitions(folderType); 825 addCmsPropertyDefinitions(folderType); 826 addProviderPropertyDefinitions(folderType); 827 828 addTypeInternal(folderType); 829 830 // document type 831 DocumentTypeDefinitionImpl documentType = new DocumentTypeDefinitionImpl(); 832 documentType.setBaseTypeId(BaseTypeId.CMIS_DOCUMENT); 833 documentType.setIsControllableAcl(Boolean.TRUE); 834 documentType.setIsControllablePolicy(Boolean.FALSE); 835 documentType.setIsCreatable(Boolean.TRUE); 836 documentType.setDescription("Document"); 837 documentType.setDisplayName("Document"); 838 documentType.setIsFileable(Boolean.TRUE); 839 documentType.setIsFulltextIndexed(Boolean.FALSE); 840 documentType.setIsIncludedInSupertypeQuery(Boolean.TRUE); 841 documentType.setLocalName("Document"); 842 documentType.setLocalNamespace(NAMESPACE); 843 documentType.setIsQueryable(Boolean.TRUE); 844 documentType.setQueryName("cmis:document"); 845 documentType.setId(DOCUMENT_TYPE_ID); 846 847 documentType.setIsVersionable(Boolean.FALSE); 848 documentType.setContentStreamAllowed(ContentStreamAllowed.REQUIRED); 849 850 addBasePropertyDefinitions(documentType); 851 addDocumentPropertyDefinitions(documentType); 852 addCmsPropertyDefinitions(documentType); 853 addProviderPropertyDefinitions(documentType); 854 855 addTypeInternal(documentType); 856 857 // relationship types 858 RelationshipTypeDefinitionImpl relationshipType = new RelationshipTypeDefinitionImpl(); 859 relationshipType.setBaseTypeId(BaseTypeId.CMIS_RELATIONSHIP); 860 relationshipType.setIsControllableAcl(Boolean.FALSE); 861 relationshipType.setIsControllablePolicy(Boolean.FALSE); 862 relationshipType.setIsCreatable(Boolean.FALSE); 863 relationshipType.setDescription("Relationship"); 864 relationshipType.setDisplayName("Relationship"); 865 relationshipType.setIsFileable(Boolean.FALSE); 866 relationshipType.setIsIncludedInSupertypeQuery(Boolean.TRUE); 867 relationshipType.setLocalName("Relationship"); 868 relationshipType.setLocalNamespace(NAMESPACE); 869 relationshipType.setIsQueryable(Boolean.FALSE); 870 relationshipType.setQueryName("cmis:relationship"); 871 relationshipType.setId(RELATIONSHIP_TYPE_ID); 872 List<String> typeList = new ArrayList<String>(); 873 typeList.add("cmis:document"); 874 typeList.add("cmis:folder"); 875 relationshipType.setAllowedSourceTypes(typeList); 876 relationshipType.setAllowedTargetTypes(typeList); 877 addBasePropertyDefinitions(relationshipType); 878 addRelationPropertyDefinitions(relationshipType); 879 addTypeInternal(relationshipType); 880 881 for (CmsRelationType relType : CmsRelationType.getAll()) { 882 createRelationshipType(relType); 883 } 884 m_lastUpdate = System.currentTimeMillis(); 885 } 886 887 /** 888 * Adds the CMIS property definitions corresponding to the OpenCms property definitions to a CMIS type definition.<p> 889 * 890 * @param type the type to which the property definitions should be added 891 */ 892 private void addCmsPropertyDefinitions(AbstractTypeDefinition type) { 893 894 for (CmsPropertyDefinition propDef : m_cmsPropertyDefinitions) { 895 type.addPropertyDefinition(createOpenCmsPropertyDefinition(propDef)); 896 type.addPropertyDefinition( 897 createPropDef( 898 INHERITED_PREFIX + propDef.getName(), 899 propDef.getName(), 900 propDef.getName(), 901 PropertyType.STRING, 902 Cardinality.SINGLE, 903 Updatability.READONLY, 904 false, 905 false)); 906 907 } 908 type.addPropertyDefinition( 909 createPropDef( 910 PROPERTY_RESOURCE_TYPE, 911 "Resource type", 912 "Resource type", 913 PropertyType.STRING, 914 Cardinality.SINGLE, 915 Updatability.ONCREATE, 916 false, 917 true)); 918 } 919 920 /** 921 * Helper method for adding property definitions for the dynamic properties.<p> 922 * 923 * @param type the type definition to which the properties should be added 924 */ 925 private void addProviderPropertyDefinitions(AbstractTypeDefinition type) { 926 927 for (I_CmsPropertyProvider provider : m_propertyProviders) { 928 type.addPropertyDefinition( 929 createPropDef( 930 PROPERTY_PREFIX_DYNAMIC + provider.getName(), 931 provider.getName(), 932 provider.getName(), 933 PropertyType.STRING, 934 Cardinality.SINGLE, 935 provider.isWritable() ? Updatability.READWRITE : Updatability.READONLY, 936 false, 937 false)); 938 } 939 } 940 941 /** 942 * Adds a type to collection with inheriting base type properties. 943 * 944 * @param type the type definition to add 945 * 946 * @return true if the type definition was added 947 */ 948 private boolean addType(TypeDefinition type) { 949 950 if (type == null) { 951 return false; 952 } 953 954 if (type.getBaseTypeId() == null) { 955 return false; 956 } 957 958 // find base type 959 TypeDefinition baseType = null; 960 if (type.getBaseTypeId() == BaseTypeId.CMIS_DOCUMENT) { 961 baseType = copyTypeDefintion(m_types.get(DOCUMENT_TYPE_ID).getTypeDefinition()); 962 } else if (type.getBaseTypeId() == BaseTypeId.CMIS_FOLDER) { 963 baseType = copyTypeDefintion(m_types.get(FOLDER_TYPE_ID).getTypeDefinition()); 964 } else if (type.getBaseTypeId() == BaseTypeId.CMIS_RELATIONSHIP) { 965 baseType = copyTypeDefintion(m_types.get(RELATIONSHIP_TYPE_ID).getTypeDefinition()); 966 } else if (type.getBaseTypeId() == BaseTypeId.CMIS_POLICY) { 967 baseType = copyTypeDefintion(m_types.get(POLICY_TYPE_ID).getTypeDefinition()); 968 } else { 969 return false; 970 } 971 972 AbstractTypeDefinition newType = (AbstractTypeDefinition)copyTypeDefintion(type); 973 974 // copy property definition 975 for (PropertyDefinition<?> propDef : baseType.getPropertyDefinitions().values()) { 976 ((AbstractPropertyDefinition<?>)propDef).setIsInherited(Boolean.TRUE); 977 newType.addPropertyDefinition(propDef); 978 } 979 980 // add it 981 addTypeInternal(newType); 982 return true; 983 } 984 985 /** 986 * Internal method which adds a new type, without adding any property definitions automatically.<p> 987 * 988 * @param type the type to add 989 */ 990 private void addTypeInternal(AbstractTypeDefinition type) { 991 992 if (type == null) { 993 return; 994 } 995 996 if (m_types.containsKey(type.getId())) { 997 // can't overwrite a type 998 return; 999 } 1000 1001 TypeDefinitionContainerImpl tc = new TypeDefinitionContainerImpl(); 1002 tc.setTypeDefinition(type); 1003 1004 // add to parent 1005 if (type.getParentTypeId() != null) { 1006 TypeDefinitionContainerImpl tdc = m_types.get(type.getParentTypeId()); 1007 if (tdc != null) { 1008 if (tdc.getChildren() == null) { 1009 tdc.setChildren(new ArrayList<TypeDefinitionContainer>()); 1010 } 1011 tdc.getChildren().add(tc); 1012 } 1013 } 1014 1015 m_types.put(type.getId(), tc); 1016 m_typeList.add(tc); 1017 } 1018 1019 /** 1020 * Creates a CMIS relationship subtype for a given OpenCms relation type.<p> 1021 * 1022 * @param relType the OpenCms relation type 1023 */ 1024 private void createRelationshipType(CmsRelationType relType) { 1025 1026 // relationship types 1027 RelationshipTypeDefinitionImpl relationshipType = new RelationshipTypeDefinitionImpl(); 1028 relationshipType.setBaseTypeId(BaseTypeId.CMIS_RELATIONSHIP); 1029 relationshipType.setParentTypeId(RELATIONSHIP_TYPE_ID); 1030 relationshipType.setIsControllableAcl(Boolean.FALSE); 1031 relationshipType.setIsControllablePolicy(Boolean.FALSE); 1032 relationshipType.setIsCreatable(Boolean.valueOf(!relType.isDefinedInContent())); 1033 relationshipType.setDescription(relType.getName()); 1034 relationshipType.setDisplayName(relType.getName()); 1035 relationshipType.setIsFileable(Boolean.FALSE); 1036 relationshipType.setIsIncludedInSupertypeQuery(Boolean.TRUE); 1037 relationshipType.setLocalName(relType.getName()); 1038 relationshipType.setLocalNamespace(NAMESPACE); 1039 relationshipType.setIsQueryable(Boolean.FALSE); 1040 String id = "opencms:" + relType.getName().toUpperCase(); 1041 relationshipType.setQueryName(id); 1042 relationshipType.setId(id); 1043 List<String> typeList = new ArrayList<String>(); 1044 typeList.add("cmis:document"); 1045 typeList.add("cmis:folder"); 1046 relationshipType.setAllowedSourceTypes(typeList); 1047 relationshipType.setAllowedTargetTypes(typeList); 1048 addType(relationshipType); 1049 } 1050 1051 /** 1052 * Collects the descendants of a type.<p> 1053 * 1054 * @param depth the depth up to which the descendants should be collected 1055 * @param tc the parent type 1056 * @param includePropertyDefinitions true if the property definitions should be included 1057 * 1058 * @return the descendants of the type 1059 */ 1060 private TypeDefinitionContainer getTypeDescendants( 1061 int depth, 1062 TypeDefinitionContainer tc, 1063 boolean includePropertyDefinitions) { 1064 1065 TypeDefinitionContainerImpl result = new TypeDefinitionContainerImpl(); 1066 1067 TypeDefinition type = copyTypeDefintion(tc.getTypeDefinition()); 1068 if (!includePropertyDefinitions) { 1069 type.getPropertyDefinitions().clear(); 1070 } 1071 1072 result.setTypeDefinition(type); 1073 1074 if (depth != 0) { 1075 if (tc.getChildren() != null) { 1076 result.setChildren(new ArrayList<TypeDefinitionContainer>()); 1077 for (TypeDefinitionContainer tdc : tc.getChildren()) { 1078 result.getChildren().add( 1079 getTypeDescendants(depth < 0 ? -1 : depth - 1, tdc, includePropertyDefinitions)); 1080 } 1081 } 1082 } 1083 1084 return result; 1085 } 1086 1087 /** 1088 * Refreshes the internal data if the last update was longer ago than the udpate interval.<p> 1089 */ 1090 private synchronized void refresh() { 1091 1092 try { 1093 long now = System.currentTimeMillis(); 1094 if ((now - m_lastUpdate) > UPDATE_INTERVAL) { 1095 setup(); 1096 } 1097 } catch (CmsException e) { 1098 LOG.error(e.getLocalizedMessage(), e); 1099 } 1100 } 1101 1102}