001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.configuration; 029 030import org.opencms.ade.configuration.CmsADEConfigData.DetailInfo; 031import org.opencms.ade.configuration.CmsADEConfigDataInternal.ConfigReference; 032import org.opencms.ade.configuration.CmsADEConfigDataInternal.ConfigReferenceInstance; 033import org.opencms.ade.configuration.CmsADEConfigDataInternal.ConfigReferenceMeta; 034import org.opencms.ade.configuration.plugins.CmsSitePlugin; 035import org.opencms.ade.detailpage.CmsDetailPageInfo; 036import org.opencms.file.CmsObject; 037import org.opencms.file.CmsResource; 038import org.opencms.file.CmsResourceFilter; 039import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 040import org.opencms.main.CmsException; 041import org.opencms.main.CmsLog; 042import org.opencms.util.CmsStringUtil; 043import org.opencms.util.CmsUUID; 044 045import java.util.ArrayList; 046import java.util.Collections; 047import java.util.HashMap; 048import java.util.HashSet; 049import java.util.List; 050import java.util.Map; 051import java.util.Set; 052import java.util.concurrent.TimeUnit; 053import java.util.stream.Collectors; 054 055import org.apache.commons.logging.Log; 056 057import com.google.common.base.Supplier; 058import com.google.common.base.Suppliers; 059import com.google.common.cache.CacheBuilder; 060import com.google.common.collect.Lists; 061import com.google.common.collect.Maps; 062 063/** 064 * An immutable object which represents the complete ADE configuration (sitemap and module configurations) 065 * at a certain instant in time.<p> 066 */ 067public class CmsADEConfigCacheState { 068 069 /** The logger instance for this class. */ 070 private static final Log LOG = CmsLog.getLog(CmsADEConfigCacheState.class); 071 072 /** The CMS context used for VFS operations. */ 073 private CmsObject m_cms; 074 075 /** Cache for detail page lists. */ 076 private Map<String, List<String>> m_detailPageCache; 077 078 /** Memoized supplier for the cached detail page ids. */ 079 private Supplier<Set<CmsUUID>> m_detailPageIdCache; 080 081 /** Cached detail page types. */ 082 private volatile Set<String> m_detailPageTypes; 083 084 /** The available element views. */ 085 private Map<CmsUUID, CmsElementView> m_elementViews; 086 087 /** The cached content types for folders. */ 088 private Map<String, String> m_folderTypes = new HashMap<String, String>(); 089 090 /** The merged configuration from all the modules. */ 091 private CmsADEConfigDataInternal m_moduleConfiguration; 092 093 /** The list of module configurations. */ 094 private List<CmsADEConfigDataInternal> m_moduleConfigurations; 095 096 /** The map of sitemap configurations by structure id. */ 097 private Map<CmsUUID, CmsADEConfigDataInternal> m_siteConfigurations = new HashMap<CmsUUID, CmsADEConfigDataInternal>(); 098 099 /** The configurations from the sitemap / VFS. */ 100 private Map<String, CmsADEConfigDataInternal> m_siteConfigurationsByPath = new HashMap<String, CmsADEConfigDataInternal>(); 101 102 /** The sitemap attribute editor configurations. */ 103 private Map<CmsUUID, CmsSitemapAttributeEditorConfiguration> m_sitemapAttributeEditorConfigurations; 104 105 /** Site plugins. */ 106 private Map<CmsUUID, CmsSitePlugin> m_sitePlugins; 107 108 /** Cached list of subsites to be included in the site selector. */ 109 private volatile List<String> m_subsitesForSiteSelector; 110 111 /** Cached set of names of content types anywhere in the configuration. */ 112 private volatile Set<String> m_contentTypes; 113 114 /** 115 * Creates a new configuration cache state.<p> 116 * 117 * @param cms the CMS context to use 118 * @param siteConfigurations the map of sitemap configuration beans by structure id 119 * @param moduleConfigs the complete list of module configurations 120 * @param elementViews the available element views 121 * @param sitePlugins the map of sitemap plugins 122 * @param attributeEditorConfigurations the attribute editor configurations 123 */ 124 public CmsADEConfigCacheState( 125 CmsObject cms, 126 Map<CmsUUID, CmsADEConfigDataInternal> siteConfigurations, 127 List<CmsADEConfigDataInternal> moduleConfigs, 128 Map<CmsUUID, CmsElementView> elementViews, 129 Map<CmsUUID, CmsSitePlugin> sitePlugins, 130 Map<CmsUUID, CmsSitemapAttributeEditorConfiguration> attributeEditorConfigurations) { 131 132 m_cms = cms; 133 m_siteConfigurations = siteConfigurations; 134 m_moduleConfigurations = moduleConfigs; 135 m_elementViews = elementViews; 136 m_sitePlugins = sitePlugins; 137 m_sitemapAttributeEditorConfigurations = attributeEditorConfigurations; 138 for (CmsADEConfigDataInternal data : siteConfigurations.values()) { 139 if (data.getBasePath() != null) { 140 // In theory, the base path should never be null 141 m_siteConfigurationsByPath.put(data.getBasePath(), data); 142 } else { 143 LOG.info("Empty base path for sitemap configuration: " + data.getResource().getRootPath()); 144 } 145 } 146 m_moduleConfiguration = mergeConfigurations(moduleConfigs); 147 try { 148 m_folderTypes = computeFolderTypes(); 149 } catch (Exception e) { 150 m_folderTypes = Maps.newHashMap(); 151 LOG.error(e.getLocalizedMessage(), e); 152 } 153 CacheBuilder<?, ?> detailPageCacheBuilder = CacheBuilder.newBuilder().concurrencyLevel(8).expireAfterWrite( 154 30, 155 TimeUnit.SECONDS); 156 m_detailPageCache = (Map<String, List<String>>)(detailPageCacheBuilder.build().asMap()); 157 m_detailPageIdCache = Suppliers.memoize(this::collectDetailPageIds); 158 159 } 160 161 /** 162 * Creates an empty ADE configuration cache state.<p> 163 * 164 * @param cms the CMS context 165 * @return the empty configuration cache state 166 */ 167 public static CmsADEConfigCacheState emptyState(CmsObject cms) { 168 169 return new CmsADEConfigCacheState( 170 cms, 171 Collections.<CmsUUID, CmsADEConfigDataInternal> emptyMap(), 172 Collections.<CmsADEConfigDataInternal> emptyList(), 173 Collections.<CmsUUID, CmsElementView> emptyMap(), 174 Collections.emptyMap(), 175 Collections.emptyMap()); 176 } 177 178 /** 179 * Computes the map from folder paths to content types for this ADE configuration state.<p> 180 * 181 * @return the map of content types by folder root paths 182 * 183 * @throws CmsException if something goes wrong 184 */ 185 public Map<String, String> computeFolderTypes() throws CmsException { 186 187 Map<String, String> folderTypes = Maps.newHashMap(); 188 // do this first, since folder types from modules should be overwritten by folder types from sitemaps 189 if (m_moduleConfiguration != null) { 190 folderTypes.putAll(wrap(m_moduleConfiguration).getFolderTypes()); 191 } 192 193 List<CmsADEConfigDataInternal> configDataObjects = new ArrayList<CmsADEConfigDataInternal>( 194 m_siteConfigurationsByPath.values()); 195 for (CmsADEConfigDataInternal configData : configDataObjects) { 196 folderTypes.putAll(wrap(configData).getFolderTypes()); 197 } 198 return folderTypes; 199 } 200 201 /** 202 * Creates a new object which represents the changed configuration state given some updates, without 203 * changing the current configuration state (this object instance). 204 * 205 * @param sitemapUpdates a map containing changed sitemap configurations indexed by structure id (the map values are null if the corresponding sitemap configuration is not valid or could not be found) 206 * @param moduleUpdates the list of *all* module configurations, or null if no module configuration update is needed 207 * @param elementViewUpdates the updated element views, or null if no update needed 208 * @param sitePluginUpdates the new map of site plugins, or null if no update needed 209 * @param attributeEditorConfigurations the sitemap attribute editor configurations 210 * 211 * @return the new configuration state 212 */ 213 public CmsADEConfigCacheState createUpdatedCopy( 214 Map<CmsUUID, CmsADEConfigDataInternal> sitemapUpdates, 215 List<CmsADEConfigDataInternal> moduleUpdates, 216 Map<CmsUUID, CmsElementView> elementViewUpdates, 217 Map<CmsUUID, CmsSitePlugin> sitePluginUpdates, 218 Map<CmsUUID, CmsSitemapAttributeEditorConfiguration> attributeEditorConfigurations) { 219 220 Map<CmsUUID, CmsADEConfigDataInternal> newSitemapConfigs = Maps.newHashMap(m_siteConfigurations); 221 if (sitemapUpdates != null) { 222 for (Map.Entry<CmsUUID, CmsADEConfigDataInternal> entry : sitemapUpdates.entrySet()) { 223 CmsUUID key = entry.getKey(); 224 CmsADEConfigDataInternal value = entry.getValue(); 225 if (value != null) { 226 newSitemapConfigs.put(key, value); 227 } else { 228 newSitemapConfigs.remove(key); 229 } 230 } 231 } 232 List<CmsADEConfigDataInternal> newModuleConfigs = m_moduleConfigurations; 233 if (moduleUpdates != null) { 234 newModuleConfigs = moduleUpdates; 235 } 236 Map<CmsUUID, CmsElementView> newElementViews = m_elementViews; 237 if (elementViewUpdates != null) { 238 newElementViews = elementViewUpdates; 239 } 240 241 Map<CmsUUID, CmsSitePlugin> newSitePlugins = m_sitePlugins; 242 if (sitePluginUpdates != null) { 243 newSitePlugins = sitePluginUpdates; 244 } 245 246 Map<CmsUUID, CmsSitemapAttributeEditorConfiguration> newAttributeEditorConfigs = m_sitemapAttributeEditorConfigurations; 247 if (attributeEditorConfigurations != null) { 248 newAttributeEditorConfigs = attributeEditorConfigurations; 249 } 250 251 return new CmsADEConfigCacheState( 252 m_cms, 253 newSitemapConfigs, 254 newModuleConfigs, 255 newElementViews, 256 newSitePlugins, 257 newAttributeEditorConfigs); 258 } 259 260 /** 261 * Gets the sitemap attribute editor configuration with the given id (or null, if there isn't one). 262 * 263 * @param id the structure id of an attribute editor configuration 264 * @return the attribute editor configuration for the id 265 */ 266 public CmsSitemapAttributeEditorConfiguration getAttributeEditorConfiguration(CmsUUID id) { 267 268 return m_sitemapAttributeEditorConfigurations.get(id); 269 } 270 271 /** 272 * Gets the set of content types configured anywhere in sitemap configurations. 273 * 274 * @return the set of content types 275 */ 276 public Set<String> getContentTypes() { 277 278 if (m_contentTypes == null) { 279 Set<String> contentTypes = new HashSet<>(); 280 for (CmsADEConfigDataInternal config : m_siteConfigurations.values()) { 281 for (CmsResourceTypeConfig typeConfig : config.getOwnResourceTypes()) { 282 contentTypes.add(typeConfig.getTypeName()); 283 } 284 } 285 for (CmsResourceTypeConfig typeConfig : m_moduleConfiguration.getOwnResourceTypes()) { 286 contentTypes.add(typeConfig.getTypeName()); 287 } 288 m_contentTypes = Collections.unmodifiableSet(contentTypes); 289 } 290 return m_contentTypes; 291 } 292 293 /** 294 * Gets the detail page information for everything.<p> 295 * 296 * @param cms the current CMS context 297 * @return the list containing all detail information 298 */ 299 public List<DetailInfo> getDetailInfosForSubsites(CmsObject cms) { 300 301 List<DetailInfo> result = Lists.newArrayList(); 302 for (CmsADEConfigDataInternal configData : m_siteConfigurationsByPath.values()) { 303 List<DetailInfo> infosForSubsite = wrap(configData).getDetailInfos(cms); 304 result.addAll(infosForSubsite); 305 } 306 return result; 307 } 308 309 /** 310 * Gets the set of type names for which detail pages are configured in any sitemap configuration.<p> 311 * 312 * @return the set of type names with configured detail pages 313 */ 314 public Set<String> getDetailPageTypes() { 315 316 if (m_detailPageTypes != null) { 317 return m_detailPageTypes; 318 } 319 Set<String> result = new HashSet<String>(); 320 for (CmsADEConfigDataInternal configData : m_siteConfigurationsByPath.values()) { 321 List<CmsDetailPageInfo> detailPageInfos = configData.getOwnDetailPages(); 322 for (CmsDetailPageInfo info : detailPageInfos) { 323 result.add(info.getType()); 324 } 325 } 326 m_detailPageTypes = result; 327 return result; 328 } 329 330 /** 331 * Returns the element views.<p> 332 * 333 * @return the element views 334 */ 335 public Map<CmsUUID, CmsElementView> getElementViews() { 336 337 return Collections.unmodifiableMap(m_elementViews); 338 } 339 340 /** 341 * Gets the map of folder types.<p> 342 * 343 * @return the map of folder types 344 */ 345 public Map<String, String> getFolderTypes() { 346 347 return Collections.unmodifiableMap(m_folderTypes); 348 } 349 350 /** 351 * Helper method to retrieve the parent folder type or <code>null</code> if none available.<p> 352 * 353 * @param rootPath the path of a resource 354 * @return the parent folder content type 355 */ 356 public String getParentFolderType(String rootPath) { 357 358 String parent = CmsResource.getParentFolder(rootPath); 359 if (parent == null) { 360 return null; 361 } 362 String type = m_folderTypes.get(parent); 363 // type may be null 364 return type; 365 } 366 367 /** 368 * Gets the raw detail page information, with no existence checks or path corrections. 369 * 370 * @return the detail page information 371 */ 372 public List<CmsDetailPageInfo> getRawDetailPages() { 373 374 List<CmsDetailPageInfo> result = new ArrayList<>(); 375 for (CmsADEConfigDataInternal config : m_siteConfigurationsByPath.values()) { 376 result.addAll(config.getOwnDetailPages()); 377 } 378 return result; 379 } 380 381 /** 382 * Returns the root paths to all configured sites and sub sites.<p> 383 * 384 * @return the root paths to all configured sites and sub sites 385 */ 386 public Set<String> getSiteConfigurationPaths() { 387 388 return m_siteConfigurationsByPath.keySet(); 389 } 390 391 /** 392 * The map of site plugins, by structure id. 393 * 394 * @return the map of site plugins 395 */ 396 public Map<CmsUUID, CmsSitePlugin> getSitePlugins() { 397 398 return m_sitePlugins; 399 } 400 401 /** 402 * Gets subsites to be included in the site selector. 403 * 404 * @return the list of root paths of subsites that should be included in the site selector 405 */ 406 public List<String> getSubsitesForSiteSelector() { 407 408 if (m_subsitesForSiteSelector == null) { 409 List<String> paths = m_siteConfigurations.values().stream().filter( 410 conf -> conf.isIncludeInSiteSelector()).map(conf -> conf.getBasePath()).collect(Collectors.toList()); 411 m_subsitesForSiteSelector = Collections.unmodifiableList(paths); 412 } 413 return m_subsitesForSiteSelector; 414 } 415 416 /** 417 * Looks up the sitemap configuration for a root path.<p> 418 * @param rootPath the root path for which to look up the configuration 419 * 420 * @return the sitemap configuration for the given root path 421 */ 422 public CmsADEConfigData lookupConfiguration(String rootPath) { 423 424 CmsADEConfigDataInternal internalSiteConfig = getSiteConfigData(rootPath); 425 CmsADEConfigData result; 426 if (internalSiteConfig == null) { 427 result = wrap(m_moduleConfiguration); 428 } else { 429 result = wrap(internalSiteConfig); 430 } 431 return result; 432 } 433 434 /** 435 * Gets all detail page info beans which are defined anywhere in the configuration.<p> 436 * 437 * @return the list of detail page info beans 438 */ 439 protected List<CmsDetailPageInfo> getAllDetailPages() { 440 441 List<CmsDetailPageInfo> result = new ArrayList<CmsDetailPageInfo>(); 442 for (CmsADEConfigDataInternal configData : m_siteConfigurationsByPath.values()) { 443 result.addAll(wrap(configData).getAllDetailPages(true)); 444 } 445 return result; 446 } 447 448 /** 449 * Gets the CMS context used for VFS operations.<p> 450 * 451 * @return the CMS context used for VFS operations 452 */ 453 protected CmsObject getCms() { 454 455 return m_cms; 456 } 457 458 /** 459 * Gets all the detail pages for a given type.<p> 460 * 461 * @param type the name of the type 462 * 463 * @return the detail pages for that type 464 */ 465 protected List<String> getDetailPages(String type) { 466 467 List<String> result = m_detailPageCache.get(type); 468 if (result == null) { 469 result = new ArrayList<>(); 470 for (CmsADEConfigDataInternal configData : m_siteConfigurationsByPath.values()) { 471 for (CmsDetailPageInfo pageInfo : wrap(configData).getDetailPagesForType(type)) { 472 result.add(pageInfo.getUri()); 473 } 474 } 475 m_detailPageCache.put(type, result); 476 } 477 return Collections.unmodifiableList(result); 478 } 479 480 /** 481 * Gets the merged module configuration.<p> 482 * @return the merged module configuration instance 483 */ 484 protected CmsADEConfigData getModuleConfiguration() { 485 486 return wrap(m_moduleConfiguration); 487 } 488 489 /** 490 * Helper method for getting the best matching sitemap configuration object for a given root path, ignoring the module 491 * configuration.<p> 492 * 493 * For example, if there are configurations available for the paths /a, /a/b/c, /a/b/x and /a/b/c/d/e, then 494 * the method will return the configuration object for /a/b/c when passed the path /a/b/c/d. 495 * 496 * If no configuration data is found for the path, null will be returned.<p> 497 * 498 * @param path a root path 499 * @return the configuration data for the given path, or null if none was found 500 */ 501 protected CmsADEConfigDataInternal getSiteConfigData(String path) { 502 503 if (path == null) { 504 return null; 505 } 506 List<String> prefixes = getSiteConfigPaths(path); 507 if (prefixes.size() == 0) { 508 return null; 509 } 510 // for any two prefixes of a string, one is a prefix of the other. so the alphabetically last 511 // prefix is the longest prefix of all. 512 return m_siteConfigurationsByPath.get(prefixes.get(prefixes.size() - 1)); 513 } 514 515 /** 516 * Finds the paths of sitemap configuration base paths above a given path.<p> 517 * 518 * @param path the path for which to find the base paths of all valid sitemap configurations 519 * 520 * @return the list of base paths 521 */ 522 protected List<String> getSiteConfigPaths(String path) { 523 524 String normalizedPath = CmsStringUtil.joinPaths("/", path, "/"); 525 List<String> prefixes = new ArrayList<String>(); 526 527 List<String> parents = new ArrayList<String>(); 528 String currentPath = normalizedPath; 529 while (currentPath != null) { 530 parents.add(currentPath); 531 currentPath = CmsResource.getParentFolder(currentPath); 532 } 533 534 for (String parent : parents) { 535 if (m_siteConfigurationsByPath.containsKey(parent)) { 536 prefixes.add(parent); 537 } 538 } 539 Collections.sort(prefixes); 540 return prefixes; 541 } 542 543 /** 544 * Checks whether the given resource is configured as a detail page.<p> 545 * 546 * @param cms the current CMS context 547 * @param resource the resource to test 548 * 549 * @return true if the resource is configured as a detail page 550 */ 551 protected boolean isDetailPage(CmsObject cms, CmsResource resource) { 552 553 if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 554 Set<CmsUUID> detailPageIds = m_detailPageIdCache.get(); 555 if (detailPageIds.contains(resource.getStructureId())) { 556 // page may have been created/replaced after the detail page id cache was generated, 557 // so we don't just return false if it doesn't contain the id. 558 // instead we check the parent folder too in the next step. 559 return true; 560 } 561 562 try { 563 CmsResource parent = getCms().readResource( 564 CmsResource.getParentFolder(resource.getRootPath()), 565 CmsResourceFilter.ALL); 566 return detailPageIds.contains(parent.getStructureId()); 567 568 } catch (Exception e) { 569 LOG.info(e.getLocalizedMessage(), e); 570 return false; 571 } 572 } else if (resource.isFolder()) { 573 return m_detailPageIdCache.get().contains(resource.getStructureId()); 574 } else { 575 return false; 576 } 577 } 578 579 /** 580 * Merges a list of multiple configuration objects into a single configuration object.<p> 581 * 582 * @param configurations the list of configuration objects.<p> 583 * 584 * @return the merged configuration object 585 */ 586 protected CmsADEConfigDataInternal mergeConfigurations(List<CmsADEConfigDataInternal> configurations) { 587 588 if (configurations.isEmpty()) { 589 return new CmsADEConfigDataInternal(null); 590 } 591 for (int i = 0; i < (configurations.size() - 1); i++) { 592 configurations.get(i + 1).mergeParent(configurations.get(i)); 593 } 594 CmsADEConfigDataInternal result = configurations.get(configurations.size() - 1); 595 result.processModuleOrdering(); 596 return result; 597 } 598 599 /** 600 * Internal method for collecting structure ids of all configured detail pages and their parent folders. 601 * 602 * @return the structure ids of configured detail pages and their parent folders 603 */ 604 private Set<CmsUUID> collectDetailPageIds() { 605 606 List<CmsDetailPageInfo> allDetailPages = new ArrayList<CmsDetailPageInfo>(); 607 // First collect all detail page infos 608 for (CmsADEConfigDataInternal configData : m_siteConfigurationsByPath.values()) { 609 List<CmsDetailPageInfo> detailPageInfos = configData.getOwnDetailPages(); 610 allDetailPages.addAll(detailPageInfos); 611 } 612 Set<CmsUUID> detailPageOrDetailPageFolderIds = new HashSet<>(); 613 for (CmsDetailPageInfo detailPageInfo : allDetailPages) { 614 try { 615 CmsResource detailPageRes = getCms().readResource(detailPageInfo.getId(), CmsResourceFilter.ALL); 616 detailPageOrDetailPageFolderIds.add(detailPageInfo.getId()); 617 if (detailPageRes.isFile()) { 618 CmsResource parent = getCms().readParentFolder(detailPageInfo.getId()); 619 detailPageOrDetailPageFolderIds.add(parent.getStructureId()); 620 } else { 621 CmsResource defaultfile = getCms().readDefaultFile("" + detailPageInfo.getId()); 622 if (defaultfile != null) { 623 detailPageOrDetailPageFolderIds.add(defaultfile.getStructureId()); 624 } 625 } 626 } catch (Exception e) { 627 LOG.info(e.getLocalizedMessage(), e); 628 } 629 } 630 return Collections.unmodifiableSet(detailPageOrDetailPageFolderIds); 631 } 632 633 /** 634 * For a given master configuration, lists all directly and indirectly referenced master configurations, in sitemap config inheritance order (i.e. referenced master configurations preceding the 635 * configurations from which they are referenced). 636 * 637 * @param result the list to append the results to 638 * @param current the configuration reference to start with 639 * @param seen the set of structure ids of sitemap configurations already visited 640 */ 641 private void fillMasterConfigurations( 642 List<ConfigReferenceInstance> result, 643 ConfigReferenceInstance current, 644 Set<CmsUUID> seen) { 645 646 CmsUUID currentId = current.getConfig().getResource().getStructureId(); 647 if (seen.contains(currentId)) { 648 LOG.warn("Loop in sitemap configuration references, target = " + current.getConfig().getBasePath()); 649 return; 650 } 651 seen.add(currentId); 652 // Recursively add the referenced master configurations before adding the current configuration in the end 653 for (ConfigReference configRef : current.getConfig().getMasterConfigs()) { 654 CmsADEConfigDataInternal config = m_siteConfigurations.get(configRef.getId()); 655 if (config != null) { 656 ConfigReferenceMeta combinedMeta = current.getMeta().combine(configRef.getMeta()); 657 ConfigReferenceInstance combinedRef = new ConfigReferenceInstance(config, combinedMeta); 658 fillMasterConfigurations(result, combinedRef, seen); 659 } else { 660 LOG.warn( 661 "Master configuration with id " 662 + configRef.getId() 663 + " not found, referenced by " 664 + current.getConfig().getResource().getRootPath()); 665 } 666 } 667 result.add(current); 668 seen.remove(currentId); 669 } 670 671 /** 672 * Wraps the internal config data into a bean which manages the lookup of inherited configurations.<p> 673 * 674 * @param data the config data to wrap 675 * 676 * @return the wrapper object 677 */ 678 private CmsADEConfigData wrap(CmsADEConfigDataInternal data) { 679 680 String path = data.getBasePath(); 681 List<ConfigReferenceInstance> configList = Lists.newArrayList(); 682 configList.add(new ConfigReferenceInstance(m_moduleConfiguration)); 683 if (path != null) { 684 List<String> siteConfigPaths = getSiteConfigPaths(path); 685 for (String siteConfigPath : siteConfigPaths) { 686 CmsADEConfigDataInternal currentConfig = m_siteConfigurationsByPath.get(siteConfigPath); 687 fillMasterConfigurations(configList, new ConfigReferenceInstance(currentConfig), new HashSet<>()); 688 } 689 } 690 return new CmsADEConfigData(data, this, new CmsADEConfigurationSequence(configList)); 691 } 692}