ParsysConfigManagerImpl.java

  1. /*
  2.  * #%L
  3.  * wcm.io
  4.  * %%
  5.  * Copyright (C) 2014 wcm.io
  6.  * %%
  7.  * Licensed under the Apache License, Version 2.0 (the "License");
  8.  * you may not use this file except in compliance with the License.
  9.  * You may obtain a copy of the License at
  10.  *
  11.  *      http://www.apache.org/licenses/LICENSE-2.0
  12.  *
  13.  * Unless required by applicable law or agreed to in writing, software
  14.  * distributed under the License is distributed on an "AS IS" BASIS,
  15.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16.  * See the License for the specific language governing permissions and
  17.  * limitations under the License.
  18.  * #L%
  19.  */
  20. package io.wcm.wcm.parsys.componentinfo.impl;

  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.Collections;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.regex.Pattern;

  27. import org.apache.commons.collections4.IterableUtils;
  28. import org.apache.commons.lang3.StringUtils;
  29. import org.apache.sling.api.resource.Resource;
  30. import org.apache.sling.api.resource.ResourceResolver;
  31. import org.apache.sling.commons.osgi.Order;
  32. import org.apache.sling.commons.osgi.RankedServices;
  33. import org.jetbrains.annotations.NotNull;
  34. import org.osgi.service.component.annotations.Component;
  35. import org.osgi.service.component.annotations.Reference;
  36. import org.osgi.service.component.annotations.ReferenceCardinality;
  37. import org.osgi.service.component.annotations.ReferencePolicy;

  38. import io.wcm.sling.commons.resource.ResourceType;
  39. import io.wcm.wcm.parsys.componentinfo.ParsysConfig;
  40. import io.wcm.wcm.parsys.componentinfo.ParsysConfigManager;

  41. /**
  42.  * Collects paragraph system configurations from repository and OSGi configuration.
  43.  * Apply super resource type based inheritance to both configuration types.
  44.  */
  45. @Component(service = ParsysConfigManager.class, immediate = true, reference = {
  46.     @Reference(service = ParsysConfig.class, name = "parsysConfig",
  47.         bind = "bindParsysConfig", unbind = "unbindParsysConfig",
  48.         cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC)
  49. })
  50. public final class ParsysConfigManagerImpl implements ParsysConfigManager {

  51.   private final RankedServices<ParsysConfig> osgiParsysConfigs = new RankedServices<>(Order.ASCENDING);

  52.   @Override
  53.   public @NotNull Iterable<ParsysConfig> getParsysConfigs(@NotNull String pageComponentPath, @NotNull ResourceResolver resolver) {
  54.     Resource pageComponentResource = resolver.getResource(pageComponentPath);
  55.     if (pageComponentResource != null) {
  56.       return Collections.unmodifiableCollection(getParsysConfigsWithInheritance(pageComponentResource, resolver));
  57.     }
  58.     else {
  59.       return Collections.emptyList();
  60.     }
  61.   }

  62.   @Override
  63.   public @NotNull Iterable<ParsysConfig> getParsysConfigs(@NotNull final String pageComponentPath, @NotNull final String relativePath,
  64.       @NotNull final ResourceResolver resolver) {
  65.     Iterable<ParsysConfig> configs = getParsysConfigs(pageComponentPath, resolver);
  66.     return IterableUtils.filteredIterable(configs, parsysConfig -> {
  67.         // sanity check
  68.         if (parsysConfig == null || parsysConfig.getPathPattern() == null) {
  69.           return false;
  70.         }
  71.         Pattern pathPattern = parsysConfig.getPathPattern();
  72.         if (pathPattern == null) {
  73.           return false;
  74.         }
  75.         return pathPattern.matcher(relativePath).matches();
  76.     });
  77.   }

  78.   private Collection<ParsysConfig> getParsysConfigs(Resource pageComponentResource) {
  79.     List<ParsysConfig> configs = new ArrayList<>();

  80.     // get first jcr parsys configurations for this page component
  81.     ResourceParsysConfigProvider resourceParsysConfigProvider = new ResourceParsysConfigProvider(pageComponentResource);
  82.     configs.addAll(resourceParsysConfigProvider.getPathDefs());

  83.     // add osgi parsys configurations
  84.     for (ParsysConfig osgiParsysConfig : osgiParsysConfigs) {
  85.       if (ResourceType.equals(pageComponentResource.getPath(), osgiParsysConfig.getPageComponentPath(), pageComponentResource.getResourceResolver())) {
  86.         configs.add(osgiParsysConfig);
  87.       }
  88.     }

  89.     return configs;
  90.   }

  91.   @SuppressWarnings("null")
  92.   private Collection<ParsysConfig> getParsysConfigsWithInheritance(Resource pageComponentResource, ResourceResolver resolver) {
  93.     List<ParsysConfig> configs = new ArrayList<>();

  94.     // get path definitions from this page component
  95.     configs.addAll(getParsysConfigs(pageComponentResource));

  96.     // add path definitions from for super page components
  97.     String resourceSuperType = pageComponentResource.getResourceSuperType();
  98.     if (StringUtils.isNotEmpty(resourceSuperType)) {
  99.       Resource superResource = resolver.getResource(resourceSuperType);
  100.       if (superResource != null) {
  101.         Collection<ParsysConfig> configsFromSupertype = getParsysConfigsWithInheritance(superResource, resolver);
  102.         List<ParsysConfig> inheritedConfigs = new ArrayList<>();
  103.         for (ParsysConfig configFromSupertype : configsFromSupertype) {
  104.           if (existingPathParentConfigAllowsInheritance(configFromSupertype, configs)) {
  105.             inheritedConfigs.add(configFromSupertype);
  106.           }
  107.         }
  108.         configs.addAll(inheritedConfigs);
  109.       }
  110.     }

  111.     return configs;
  112.   }

  113.   private boolean existingPathParentConfigAllowsInheritance(ParsysConfig item, List<ParsysConfig> existingItems) {
  114.     for (ParsysConfig existingItem : existingItems) {
  115.       if (matchesPathParent(item, existingItem) && !existingItem.isInherit()) {
  116.         return false;
  117.       }
  118.     }
  119.     return true;
  120.   }

  121.   private boolean matchesPathParent(ParsysConfig item1, ParsysConfig item2) {
  122.     Pattern pattern1 = item1.getPathPattern();
  123.     Pattern pattern2 = item2.getPathPattern();
  124.     String pathPattern1 = pattern1 != null ? pattern1.pattern() : "";
  125.     String pathPattern2 = pattern2 != null ? pattern2.pattern() : "";
  126.     return pathPattern1.equals(pathPattern2)
  127.         && item1.getParentAncestorLevel() == item2.getParentAncestorLevel()
  128.         && item1.getAllowedParents().equals(item2.getAllowedParents());
  129.   }

  130.   void bindParsysConfig(ParsysConfig service, Map<String, Object> props) {
  131.     osgiParsysConfigs.bind(service, props);
  132.   }

  133.   void unbindParsysConfig(ParsysConfig service, Map<String, Object> props) {
  134.     osgiParsysConfigs.unbind(service, props);
  135.   }

  136. }