You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by an...@apache.org on 2021/03/29 04:27:26 UTC
[sling-whiteboard] 01/02: Removed the Setup and made the DDR
configurable through the source resources through primary type and property
This is an automated email from the ASF dual-hosted git repository.
andysch pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git
commit 26ea9fec4ce93cd501ba2b18c1781550618ae742
Author: Andreas Schaefer <sc...@me.com>
AuthorDate: Sat Mar 27 22:35:14 2021 -0700
Removed the Setup and made the DDR configurable through the source resources through primary type and property
---
org.apache.sling.ddr/README.md | 32 ++
.../ddr/api/DeclarativeDynamicResourceSetup.java | 23 --
org.apache.sling.ddr/core/pom.xml | 3 +
.../DeclarativeDynamicResourceManagerService.java | 179 +++++++---
.../DeclarativeDynamicResourceSetupService.java | 362 ---------------------
...g.jcr.repoinit.RepositoryInitializer-ddr.config | 2 -
.../main/resources/SLING-CONTENT/nodetypes/ddr.cnd | 23 ++
org.apache.sling.ddr/sample/Readme.md | 37 +++
org.apache.sling.ddr/sample/pom.xml | 1 +
.../SLING-CONTENT/apps/ddr-dynamic/components.json | 4 +-
.../resources/SLING-CONTENT/apps/ddr-sample.json | 4 +-
...iveDynamicResourceManagerService-ddrsample.json | 4 -
...ativeDynamicResourceSetupService-ddrsample.json | 16 -
...repoinit.RepositoryInitializer-ddrsample.config | 38 ---
.../apps/ddr-static/{container.json => text.json} | 0
.../{container/container.html => text/text.html} | 2 +-
.../resources/SLING-CONTENT/conf/ddr-sample.json | 4 +
.../SLING-CONTENT/conf/ddr-sample/settings.json | 23 ++
.../SLING-CONTENT/content/ddr-sample/text.json | 4 +
19 files changed, 262 insertions(+), 499 deletions(-)
diff --git a/org.apache.sling.ddr/README.md b/org.apache.sling.ddr/README.md
new file mode 100644
index 0000000..a1822eb
--- /dev/null
+++ b/org.apache.sling.ddr/README.md
@@ -0,0 +1,32 @@
+# Apache Sling Declarative Dynamic Resources
+
+## Introduction
+
+In Peregrine CMS we were facing the fact that each tenant had their own set of components that were
+just a redirection through their Sling Resource Super Type to a component provided by Peregrine. This
+was the Tenant could maintain their components but Peregrine could add new components w/o affecting
+or confusing the tenant.
+In addition, if Peregrine wants to make new components available we face the issue of a read-only
+JCR tree in /libs and /apps which would require a new deployment and restart of Peregrine.
+
+## Declarative Dynamic Resource
+
+Both of these issues can be solved by creating Synthetic Resources that appear in the desired target
+resource but its actual content is provided from another node.
+The Declarative Dynamic Resource (DDR) is composed of these components:
+
+* Declarative Dynamic Resource Manager: listens to DDRs becoming available and then creates the Synthetic Resources needed
+* Declarative Dynamic Resource Provider: a Resource Provider for any DDR source properly configured
+* Declarative Dynamic Resource: a Synthetic Resource that lives in a given node and points to its source for its properties
+* Declarative Dynamic Resource Listener: an interface that informs the implementor about newly created DDRs
+
+The DDR is using its own Service User **ddr-serviceuser** that is used to read and handle DDRs.
+
+Any source folder needs to set the **jcr:primaryType** to **slingddr:Folder** and set the property
+**slingddr:target** to the path of the target folder of its DDR. That path needs to be absolute and
+point to an existing resource. Keep in mind that this path points to the parent of a DDR as each node
+inside the source is creating a DDR with the path of the target as parent and the source name as
+name of the DDR. Keep in mind that **no resource with that name** can existing in the target folder
+otherwise it will be shadowed by the existing resource.
+
+Andreas Schaefer, 3/27/2021
\ No newline at end of file
diff --git a/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceSetup.java b/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceSetup.java
deleted file mode 100644
index dc44211..0000000
--- a/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceSetup.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.ddr.api;
-
-/**
- * Marker Interface for the Setup Service for Dynamic Source Instances
- */
-public interface DeclarativeDynamicResourceSetup {
-}
diff --git a/org.apache.sling.ddr/core/pom.xml b/org.apache.sling.ddr/core/pom.xml
index 550c5d8..738ab6c 100644
--- a/org.apache.sling.ddr/core/pom.xml
+++ b/org.apache.sling.ddr/core/pom.xml
@@ -52,6 +52,9 @@
Private-Package,
Include-Resource
</_removeheaders>
+ <Sling-Nodetypes>
+ SLING-CONTENT/nodetypes/ddr.cnd
+ </Sling-Nodetypes>
<Sling-Initial-Content>
SLING-CONTENT/apps/ddr;path:=/apps/ddr;overwrite:=true;overwriteProperties:=true
</Sling-Initial-Content>
diff --git a/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceManagerService.java b/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceManagerService.java
index 1f19d50..fb7b447 100644
--- a/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceManagerService.java
+++ b/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceManagerService.java
@@ -28,17 +28,20 @@ import org.apache.sling.serviceusermapping.ServiceUserMapped;
import org.osgi.framework.BundleContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicyOption;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.UnsupportedRepositoryOperationException;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+import javax.jcr.query.Query;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
@@ -49,23 +52,24 @@ import static org.apache.sling.ddr.api.Constants.JCR_PRIMARY_TYPE;
import static org.apache.sling.ddr.api.Constants.REP_POLICY;
@Component(
- service= DeclarativeDynamicResourceManager.class,
- immediate = true,
- configurationPolicy = ConfigurationPolicy.REQUIRE
+ service = { DeclarativeDynamicResourceManager.class, EventListener.class },
+ immediate = true
)
-@Designate(ocd = DeclarativeDynamicResourceManagerService.Configuration.class, factory = true)
public class DeclarativeDynamicResourceManagerService
- implements DeclarativeDynamicResourceManager
+ implements DeclarativeDynamicResourceManager, EventListener
{
- @ObjectClassDefinition(
- name = "Declarative Dynamic Component Resource Provider",
- description = "Configuration of the Dynamic Component Resource Provider")
- public @interface Configuration {
- @AttributeDefinition(
- name = "Dynamic Component Target Path",
- description="Path to the Folder where the Dynamic Components will added to dynamically")
- String dynamic_component_target_path();
- }
+ public static final int EVENT_TYPES =
+ Event.NODE_ADDED |
+ Event.NODE_REMOVED |
+ Event.NODE_MOVED |
+ Event.PROPERTY_ADDED |
+ Event.PROPERTY_CHANGED |
+ Event.PROPERTY_REMOVED;
+
+ public static final String DDR_NODE_TYPE = "slingddr:Folder";
+ public static final String DDR_TARGET_PROPERTY_NAME = "slingddr:target";
+ public static final String[] NODE_TYPES = new String[] { DDR_NODE_TYPE };
+ public static final String CONFIGURATION_ROOT_PATH = "/conf";
@Reference
private ResourceResolverFactory resourceResolverFactory;
@@ -81,14 +85,82 @@ public class DeclarativeDynamicResourceManagerService
private Map<String, DeclarativeDynamicResourceProvider> registeredServices = new HashMap<>();
private BundleContext bundleContext;
- private String dynamicTargetPath;
+// private String dynamicTargetPath;
@Activate
- private void activate(BundleContext bundleContext, Configuration configuration) {
- log.info("Activate Started, bundle context: '{}'", bundleContext);
+ private void activate(BundleContext bundleContext) {
this.bundleContext = bundleContext;
- dynamicTargetPath = configuration.dynamic_component_target_path();
- log.info("Dynamic Target Path: '{}'", dynamicTargetPath);
+ log.info("Activate Started, bundle context: '{}'", bundleContext);
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(
+ new HashMap<String, Object>() {{ put(ResourceResolverFactory.SUBSERVICE, DYNAMIC_COMPONENTS_SERVICE_USER); }}
+ )) {
+ // Register an Event Listener to get informed when
+ Session session = resourceResolver.adaptTo(Session.class);
+ if (session != null) {
+ log.info("Register Event Listener on Path: '{}'", CONFIGURATION_ROOT_PATH);
+ session.getWorkspace().getObservationManager().addEventListener(
+ this, EVENT_TYPES, CONFIGURATION_ROOT_PATH,
+ true, null, null, true
+ );
+ } else {
+ log.warn("Resource Resolver could not be adapted to Session");
+ }
+ // To make sure we get all we will query all existing nodes
+ Resource root = resourceResolver.getResource(CONFIGURATION_ROOT_PATH);
+ log.info("Manual Check for Existing Nodes in: '{}'", CONFIGURATION_ROOT_PATH);
+ if(root != null) {
+ Iterator<Resource> i = resourceResolver.findResources(
+ "SELECT * FROM [slingddr:Folder]",
+ Query.JCR_SQL2
+ );
+ while(i.hasNext()) {
+ Resource item = i.next();
+ log.info("Handle Found DDR Resource: '{}'", item);
+ handleDDRResource(item);
+ }
+ }
+ } catch (LoginException e) {
+ log.error("Failed to Activation Resource", e);
+ } catch (UnsupportedRepositoryOperationException e) {
+ log.error("Failed to Activation Resource", e);
+ } catch (RepositoryException e) {
+ log.error("Failed to Activation Resource", e);
+ }
+ }
+
+ private void handleDDRResource(Resource ddrSourceResource) {
+ if(ddrSourceResource != null) {
+ ValueMap properties = ddrSourceResource.getValueMap();
+ String ddrTargetPath = properties.get(DDR_TARGET_PROPERTY_NAME, String.class);
+ log.info("Found DDR Target Path: '{}'", ddrTargetPath);
+ if (ddrTargetPath != null) {
+ Resource ddrTargetResource = ddrSourceResource.getResourceResolver().getResource(ddrTargetPath);
+ if(ddrTargetResource != null) {
+ DeclarativeDynamicResourceProviderHandler service = new DeclarativeDynamicResourceProviderHandler();
+ log.info("Dynamic Target: '{}', Dynamic Provider: '{}'", ddrSourceResource, ddrSourceResource);
+ long id = service.registerService(bundleContext.getBundle(), ddrTargetPath, ddrSourceResource.getPath(), resourceResolverFactory);
+ log.info("After Registering Tenant RP: service: '{}', id: '{}'", service, id);
+ registeredServices.put(ddrTargetResource.getPath(), service);
+ Iterator<Resource> i = ddrSourceResource.listChildren();
+ while (i.hasNext()) {
+ Resource provided = i.next();
+ String componentName = provided.getName();
+ if (componentName.equals(REP_POLICY)) {
+ continue;
+ }
+ log.info("Provided Dynamic: '{}'", provided);
+ ValueMap childProperties = provided.getValueMap();
+ String primaryType = childProperties.get(JCR_PRIMARY_TYPE, String.class);
+ log.info("Dynamic Child Source: '{}', Primary Type: '{}'", componentName, primaryType);
+ if (componentName != null && !componentName.isEmpty() && dynamicComponentFilterNotifier != null) {
+ dynamicComponentFilterNotifier.addDeclarativeDynamicResource(
+ ddrTargetPath + '/' + componentName, provided
+ );
+ }
+ }
+ }
+ }
+ }
}
public void update(String dynamicProviderPath) {
@@ -96,32 +168,7 @@ public class DeclarativeDynamicResourceManagerService
new HashMap<String, Object>() {{ put(ResourceResolverFactory.SUBSERVICE, DYNAMIC_COMPONENTS_SERVICE_USER); }}
)) {
Resource dynamicProvider = resourceResolver.getResource(dynamicProviderPath);
- Resource dynamicTarget = resourceResolver.getResource(dynamicTargetPath);
- log.info("Dynamic Resource Provider: '{}', Target: '{}'", dynamicProvider, dynamicTarget);
- if(dynamicProvider != null) {
- DeclarativeDynamicResourceProviderHandler service = new DeclarativeDynamicResourceProviderHandler();
- log.info("Dynamic Target: '{}', Dynamic Provider: '{}'", dynamicTarget, dynamicProvider);
- long id = service.registerService(bundleContext.getBundle(), dynamicTargetPath, dynamicProviderPath, resourceResolverFactory);
- log.info("After Registering Tenant RP: service: '{}', id: '{}'", service, id);
- registeredServices.put(dynamicTarget.getPath(), service);
- Iterator<Resource> i = dynamicProvider.listChildren();
- while (i.hasNext()) {
- Resource provided = i.next();
- String componentName = provided.getName();
- if (componentName.equals(REP_POLICY)) {
- continue;
- }
- log.info("Provided Dynamic: '{}'", provided);
- ValueMap childProperties = provided.getValueMap();
- String primaryType = childProperties.get(JCR_PRIMARY_TYPE, String.class);
- log.info("Dynamic Child Source: '{}', Primary Type: '{}'", componentName, primaryType);
- if (componentName != null && !componentName.isEmpty() && dynamicComponentFilterNotifier != null) {
- dynamicComponentFilterNotifier.addDeclarativeDynamicResource(
- dynamicTargetPath + '/' + componentName, provided
- );
- }
- }
- }
+ handleDDRResource(dynamicProvider);
} catch (LoginException e) {
log.error("Was not able to obtain Service Resource Resolver", e);
}
@@ -135,5 +182,39 @@ public class DeclarativeDynamicResourceManagerService
log.info("After UnRegistering Tenant RP, service: '{}'", service);
}
}
+
+ @Override
+ public void onEvent(EventIterator events) {
+ try (ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(
+ new HashMap<String, Object>() {{ put(ResourceResolverFactory.SUBSERVICE, DYNAMIC_COMPONENTS_SERVICE_USER); }}
+ )) {
+ while (events.hasNext()) {
+ Event event = events.nextEvent();
+ String path = event.getPath();
+ switch (event.getType()) {
+ case Event.PROPERTY_ADDED:
+ case Event.PROPERTY_CHANGED:
+ int index = path.lastIndexOf('/');
+ if(index > 0) {
+ path = path.substring(0, index -1);
+ }
+ case Event.NODE_ADDED:
+ Resource source = resourceResolver.getResource(path);
+ if(source != null) {
+ handleDDRResource(source);
+ }
+ break;
+ case Event.NODE_MOVED:
+ //AS TODO: Handle later
+ break;
+ case Event.PROPERTY_REMOVED:
+ //AS TODO: Handle later
+// break;
+ }
+ }
+ } catch (LoginException | RepositoryException e) {
+ log.error("Failed to Handle Events", e);
+ }
+ }
}
diff --git a/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/setup/DeclarativeDynamicResourceSetupService.java b/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/setup/DeclarativeDynamicResourceSetupService.java
deleted file mode 100644
index eb132de..0000000
--- a/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/setup/DeclarativeDynamicResourceSetupService.java
+++ /dev/null
@@ -1,362 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with this
- * work for additional information regarding copyright ownership. The ASF
- * licenses this file to You under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package org.apache.sling.ddr.core.setup;
-
-import org.apache.sling.api.resource.LoginException;
-import org.apache.sling.api.resource.PersistenceException;
-import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceResolver;
-import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.ddr.api.DeclarativeDynamicResourceSetup;
-import org.apache.sling.ddr.api.DeclarativeDynamicResourceManager;
-import org.apache.sling.serviceusermapping.ServiceUserMapped;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.component.annotations.Reference;
-import org.osgi.service.component.annotations.ReferencePolicyOption;
-import org.osgi.service.metatype.annotations.AttributeDefinition;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.UnsupportedRepositoryOperationException;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.observation.EventListener;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import static org.apache.sling.ddr.api.Constants.CLOSING_MULTIPLE;
-import static org.apache.sling.ddr.api.Constants.COMPONENT_GROUP;
-import static org.apache.sling.ddr.api.Constants.DYNAMIC_COMPONENTS_SERVICE_USER;
-import static org.apache.sling.ddr.api.Constants.DYNAMIC_COMPONENT_FOLDER_NAME;
-import static org.apache.sling.ddr.api.Constants.EQUALS;
-import static org.apache.sling.ddr.api.Constants.JCR_PRIMARY_TYPE;
-import static org.apache.sling.ddr.api.Constants.JCR_TITLE;
-import static org.apache.sling.ddr.api.Constants.MULTI_SEPARATOR;
-import static org.apache.sling.ddr.api.Constants.NT_UNSTRUCTURED;
-import static org.apache.sling.ddr.api.Constants.OPENING_MULTIPLE;
-import static org.apache.sling.ddr.api.Constants.REFERENCE_PROPERTY_NAME;
-import static org.apache.sling.ddr.api.Constants.REP_POLICY;
-import static org.apache.sling.ddr.api.Constants.SLING_FOLDER;
-import static org.apache.sling.ddr.api.Constants.SLING_RESOURCE_SUPER_TYPE_PROPERTY;
-import static org.apache.sling.ddr.api.Constants.VERTICAL_LINE;
-
-@Component(
- service= { DeclarativeDynamicResourceSetup.class, EventListener.class },
- immediate = true
-)
-@Designate(ocd = DeclarativeDynamicResourceSetupService.Configuration.class, factory = true)
-public class DeclarativeDynamicResourceSetupService
- implements DeclarativeDynamicResourceSetup, EventListener
-{
-
- @ObjectClassDefinition(
- name = "Declarative Dynamic Resource Setup",
- description = "Configuration of the Setup for Declarative Dynamic Resource Provider")
- public @interface Configuration {
- @AttributeDefinition(
- name = "Dynamic Target Path",
- description = "The Location where the DDR are made available")
- String dynamic_component_target_path() default "/apps/wknd/components";
- @AttributeDefinition(
- name = "Dynamic Source Root Path",
- description = "The Location of the Source for the DDRs")
- String dynamic_component_root_path() default "/conf/wknd/settings";
- @AttributeDefinition(
- name = "Component Group of the Dynamic Components",
- description = "Component Group Name")
- String dynamic_component_group() default "WKND.Content";
- @AttributeDefinition(
- name = "Primary Group",
- description = "Component Primary Type")
- String dynamic_component_primary_type() default "cq:Component";
- @AttributeDefinition(
- name = "List of Dynamic Components",
- description = "Dynamic Component Definitions in format: <name>=<title>:<super resource type>")
- String[] dynamic_component_names() default "button=Button-default:core/wcm/components/button/v1/button";
- @AttributeDefinition(
- name = "Additional Properties for Dynamic Components",
- description = "Dynamic Component Additional Properties in format: <name>=<property name>|<property value>")
- String[] dynamic_component_additional_properties() default "";
- @AttributeDefinition(
- name = "References for Dynamic Components",
- description = "Dynamic Component Reference in format: <name>=<path>")
- String[] dynamic_component_refs() default "";
- }
-
- private final Logger log = LoggerFactory.getLogger(getClass());
-
- @Reference
- private ResourceResolverFactory resourceResolverFactory;
-
- @Reference
- DeclarativeDynamicResourceManager dynamicComponentResourceManager;
-
- // Make sure that the Service User Mapping is available before obtaining the Service Resource Resolver
- @Reference(policyOption= ReferencePolicyOption.GREEDY, target="(" + ServiceUserMapped.SUBSERVICENAME + EQUALS + DYNAMIC_COMPONENTS_SERVICE_USER + ")")
- private ServiceUserMapped serviceUserMapped;
-
- private BundleContext bundleContext;
- private String rootPath;
- private String targetRootPath;
-
- @Activate
- private void activate(BundleContext bundleContext, Configuration configuration) {
- log.info("Activate Started, bundle context: '{}'", bundleContext);
- this.bundleContext = bundleContext;
- rootPath = configuration.dynamic_component_root_path();
- targetRootPath = configuration.dynamic_component_target_path();
- final String group = configuration.dynamic_component_group();
- final String primaryType = configuration.dynamic_component_primary_type();
- try (ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(
- new HashMap<String, Object>() {{ put(ResourceResolverFactory.SUBSERVICE, DYNAMIC_COMPONENTS_SERVICE_USER); }}
- )) {
- Resource root = resourceResolver.getResource(rootPath);
- if(root == null) {
- throw new IllegalArgumentException("Root Path: '" + rootPath + "' does not exist");
- }
- Resource target = root.getChild(DYNAMIC_COMPONENT_FOLDER_NAME);
- log.info("Dynamic Folder looked up: '{}'", target);
- if(target == null) {
- target = resourceResolver.create(root, DYNAMIC_COMPONENT_FOLDER_NAME, new HashMap<String, Object>() {{
- put(JCR_PRIMARY_TYPE, SLING_FOLDER);
- }}
- );
- resourceResolver.commit();
- log.info("Dynamic Folder created: '{}'", target);
- } else {
- // Remove any existing children
- Iterator<Resource> i = target.listChildren();
- while(i.hasNext()) {
- Resource child = i.next();
- if(!child.getName().equals(REP_POLICY)) {
- log.info("Delete Existing Resource: '{}'", child);
- resourceResolver.delete(child);
- }
- }
- resourceResolver.commit();
- }
- Map<String, List<Property>> additionalProperties = new HashMap<>();
- for(String additionalProperty: configuration.dynamic_component_additional_properties()) {
- Property component = new Property(additionalProperty, "Dynamic Additional Property");
- if(!component.isComponent()) {
- throw new IllegalArgumentException("Addition Properties is not a component: '" + additionalProperty + "'");
- }
- addItemToListMap(additionalProperties, component);
- }
- Map<String, List<Property>> dynamicRefs = new HashMap<>();
- for(String ref: configuration.dynamic_component_refs()) {
- Property component = new Property(ref, "Dynamic Ref");
- if(!component.isComponent()) {
- throw new IllegalArgumentException("Dynamic Ref is not a component: '" + ref + "'");
- }
- addItemToListMap(dynamicRefs, component);
- }
- log.info("Dynamic Refs: '{}'", dynamicRefs);
- for (String dynamicComponentName : configuration.dynamic_component_names()) {
- final Property dynamicComponent = new Property(dynamicComponentName, "Dynamic Component");
- if(!dynamicComponent.isComponent()) {
- throw new IllegalArgumentException("Dynamic Configuration Name is invalid (split on = does not yield 2 tokens): " + dynamicComponentName);
- }
- Map<String, Object> props = new HashMap<String, Object>() {{
- put(COMPONENT_GROUP, group);
- put(JCR_PRIMARY_TYPE, primaryType);
- put(JCR_TITLE, dynamicComponent.getName());
- put(SLING_RESOURCE_SUPER_TYPE_PROPERTY, dynamicComponent.getValue());
- }};
- List<Property> propertyList = additionalProperties.get(dynamicComponent.getComponent());
- log.info("Component: '{}', property list: '{}'", dynamicComponent.getComponent(), propertyList);
- if(propertyList != null) {
- for (Property property : propertyList) {
- if(property.isSingle()) {
- props.put(property.getName(), property.getValue());
- } else {
- log.info("Add Property as Multi-Value: '{}'", property.getValues());
- props.put(property.getName(), property.getValues().toArray());
- }
- }
- }
- log.info("Props for to be created Node: '{}'", props);
- Resource newTarget = resourceResolver.create(target, dynamicComponent.getComponent(), props);
- log.info("Newly Created Target: '{}'", newTarget);
- // Add Dynamic Refs
- List<Property> refs = dynamicRefs.get(dynamicComponent.getComponent());
- if(refs != null) {
- for (final Property ref : refs) {
- Map<String, Object> refProps = new HashMap<String, Object>() {{
- put(JCR_PRIMARY_TYPE, NT_UNSTRUCTURED);
- put(JCR_TITLE, ref.getName());
- put(REFERENCE_PROPERTY_NAME, ref.getValue());
- }};
- Resource newRef = resourceResolver.create(
- newTarget, ref.getName(), refProps
- );
- }
- }
- }
- resourceResolver.commit();
- Resource componentResource = resourceResolver.getResource(targetRootPath);
- log.info("Target Root Path: '{}', resource: '{}'", targetRootPath, componentResource);
- if(componentResource == null) {
- Session session = resourceResolver.adaptTo(Session.class);
- if (session != null) {
- final int eventTypes = Event.NODE_ADDED;
- final boolean isDeep = false;
- final boolean noLocal = true;
- session.getWorkspace().getObservationManager().addEventListener(
- this, eventTypes, targetRootPath,
- isDeep, null, null, noLocal
- );
- } else {
- log.warn("Resource Resolver could not be adapted to Session");
- }
- } else {
- targetAvailable();
- }
- } catch (LoginException e) {
- log.error("2. Cannot Access Resource Resolver", e);
- } catch (PersistenceException e) {
- log.error("Failed to create Dynamic Component", e);
- } catch (UnsupportedRepositoryOperationException e) {
- log.error("Could not register Event Listener", e);
- } catch (RepositoryException e) {
- log.error("Could not get Observation Manager to register Event Listener", e);
- }
- }
-
- @Override
- public void onEvent(EventIterator events) {
- targetAvailable();
- }
-
- private void targetAvailable() {
- log.info("Target Available Called");
- try (ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(
- new HashMap<String, Object>() {{ put(ResourceResolverFactory.SUBSERVICE, DYNAMIC_COMPONENTS_SERVICE_USER); }}
- )) {
- Resource root = resourceResolver.getResource(rootPath);
- if (root == null) {
- throw new IllegalArgumentException("Root Path: '" + rootPath + "' does not exist");
- }
- Resource target = root.getChild(DYNAMIC_COMPONENT_FOLDER_NAME);
- log.info("Update the Dynamic Component Resource Manager with Provider Path: '{}'", target);
- dynamicComponentResourceManager.update(target.getPath());
- log.info("Update the Dynamic Component Resource Manager done");
-
- // Now test the setup
- Resource button1 = resourceResolver.getResource(targetRootPath + "/" + "button1");
- log.info("Dynamic Button 1: '{}'", button1);
- Resource container1 = resourceResolver.getResource(targetRootPath + "/" + "container1");
- log.info("Dynamic Container 1: '{}'", container1);
- if(container1 != null) {
- Iterator<Resource> i = resourceResolver.listChildren(container1.getParent());
- int index = 0;
- while (i.hasNext()) {
- log.info("{}. Entry: '{}'", index++, i.next());
- }
- }
- } catch (LoginException e) {
- log.error("2. Cannot Access Resource Resolver", e);
- }
- }
-
- private void addItemToListMap(Map<String, List<Property>> target, Property value) {
- String componentName = value.getComponent();
- List<Property> propertyList = target.get(componentName);
- if(propertyList == null) {
- propertyList = new ArrayList<>();
- target.put(componentName, propertyList);
- }
- propertyList.add(value);
- }
-
- private static class Property {
- private String component;
- private String name;
- private List<String> values = new ArrayList<>();
-
- public Property(String line, String messageTitle) {
- String[] split = line.split(EQUALS);
- if(split.length != 2) {
- throw new IllegalArgumentException(messageTitle + " is invalid (split on '" + EQUALS + "' does not yield 2 tokens): " + line);
- }
- String tempName = split[0];
- String tempValue = split[1];
- int index = tempValue.indexOf(VERTICAL_LINE);
- if( index > 0 && index < tempValue.length() - 1) {
- String[] splitTemp = tempValue.split("\\" + VERTICAL_LINE);
- this.component = tempName;
- this.name = splitTemp[0];
- tempValue = splitTemp[1];
- if(tempValue.charAt(0) == OPENING_MULTIPLE && tempValue.charAt(tempValue.length() - 1) == CLOSING_MULTIPLE) {
- tempValue = tempValue.substring(1, tempValue.length() - 1);
- splitTemp = tempValue.split(MULTI_SEPARATOR);
- values.addAll(Arrays.asList(splitTemp));
- } else {
- values.add(tempValue);
- }
- } else {
- this.name = tempName;
- if(tempValue.charAt(0) == OPENING_MULTIPLE && tempValue.charAt(tempValue.length() - 1) == CLOSING_MULTIPLE) {
- tempValue = tempValue.substring(1, tempValue.length() - 1);
- String[] splitTemp = tempValue.split(MULTI_SEPARATOR);
- values.addAll(Arrays.asList(splitTemp));
- } else {
- values.add(tempValue);
- }
- }
- }
-
- public boolean isComponent() { return component != null; }
- public boolean isEmpty() { return values.isEmpty(); }
- public boolean isSingle() { return values.size() == 1; }
-
- public String getComponent() {
- return component;
- }
-
- public String getName() {
- return name;
- }
-
- public String getValue() {
- return values.isEmpty() ? null : values.get(0);
- }
-
- public List<String> getValues() {
- return new ArrayList<>(values);
- }
-
- @Override
- public String toString() {
- return "Property{" +
- "component-name='" + component + '\'' +
- ", name='" + name + '\'' +
- ", values=" + values +
- '}';
- }
- }
-}
\ No newline at end of file
diff --git a/org.apache.sling.ddr/core/src/main/resources/SLING-CONTENT/apps/ddr/config/org.apache.sling.jcr.repoinit.RepositoryInitializer-ddr.config b/org.apache.sling.ddr/core/src/main/resources/SLING-CONTENT/apps/ddr/config/org.apache.sling.jcr.repoinit.RepositoryInitializer-ddr.config
index 95066c1..39a45b1 100644
--- a/org.apache.sling.ddr/core/src/main/resources/SLING-CONTENT/apps/ddr/config/org.apache.sling.jcr.repoinit.RepositoryInitializer-ddr.config
+++ b/org.apache.sling.ddr/core/src/main/resources/SLING-CONTENT/apps/ddr/config/org.apache.sling.jcr.repoinit.RepositoryInitializer-ddr.config
@@ -15,7 +15,6 @@
#
scripts=[\
"
-
# Create the Service User needed for Declarative Dynamic Resources
create service user ddr-serviceuser with path /home/users/system/dynamic
@@ -25,7 +24,6 @@ scripts=[\
set ACL for ddr-serviceuser
allow jcr:read on /
end
-
"\
]
diff --git a/org.apache.sling.ddr/core/src/main/resources/SLING-CONTENT/nodetypes/ddr.cnd b/org.apache.sling.ddr/core/src/main/resources/SLING-CONTENT/nodetypes/ddr.cnd
new file mode 100644
index 0000000..7d151d5
--- /dev/null
+++ b/org.apache.sling.ddr/core/src/main/resources/SLING-CONTENT/nodetypes/ddr.cnd
@@ -0,0 +1,23 @@
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+
+<slingddr='http://sling.apache.org/jcr/ddr/1.0'>
+
+[slingddr:Folder] > sling:Folder
+ - slingddr:target (string)
diff --git a/org.apache.sling.ddr/sample/Readme.md b/org.apache.sling.ddr/sample/Readme.md
new file mode 100644
index 0000000..fb66989
--- /dev/null
+++ b/org.apache.sling.ddr/sample/Readme.md
@@ -0,0 +1,37 @@
+# Declarative Dynamic Resource Sample
+
+This is a sample project to showcase the use of DDRs in Sling. It requires a Sling 12
+instance to be installed on.
+
+## Design
+
+These are the components of the Sample:
+
+* **/apps/ddr-dynamic/components**: the DDR target folder which is empty
+* **/apps/ddr-sample/config**: runmode configuration for this Sample
+* **/apps/ddr-static**: the base component **button** and **text**
+* **/conf/ddr-sample/settings/dynamic**: the source DDR components that points to the DDR target of **/apps/ddr-dynamic/components and contains an updated version of Button and Text
+* **/content/ddr-sample**: content that show the usage of enhanced Button and Text
+
+## What Happens
+
+After the DDR Sample is installed the DDR Manager will discover that */conf/ddr-sample/settings/dynamic*
+is a DDR source (by its primary type) and then read out the DDR target path. Then it will register that
+folder as DDR Source / Target pair with the DDR Provider. Whenever now a user requests a resource from that
+folder the DDR Provider will look it up and if:
+
+* it finds an existing resource in that folder -> return that resource
+* it finds a resource in the DDR source with that name -> return a Synthetic Resource that contains the properties of the DDR source with the same name
+
+## Review
+
+After the installation open Sling in a browser and then go to **composum** browser.
+Here go to **/apps/ddr-dynamic** and you will find two child nodes **button1** and **text1**.
+These resources are fully dynamic mean that they would go away if the DDR core is disabled.
+Try this by going to the System Console **Bundles** and stop the **org.apache.sling.ddr.core** bundle.
+Refershing the **/apps/ddr-dynamic** folder in composum will not display any child resources. Restart
+the bundle now again and make sure the child resources are there again.
+Now we want to see these components but open the content on **/ddr-sample/button.html**. This will
+show *Hello Button: * followed by the path of the resource.
+
+Andreas Schaefer: 3/27/2021
\ No newline at end of file
diff --git a/org.apache.sling.ddr/sample/pom.xml b/org.apache.sling.ddr/sample/pom.xml
index 3999060..4578bb1 100644
--- a/org.apache.sling.ddr/sample/pom.xml
+++ b/org.apache.sling.ddr/sample/pom.xml
@@ -57,6 +57,7 @@
SLING-CONTENT/apps/ddr-sample;path:=/apps/ddr-sample;overwrite:=true;overwriteProperties:=true,
SLING-CONTENT/apps/ddr-dynamic;path:=/apps/ddr-dynamic;overwrite:=true;overwriteProperties:=true,
SLING-CONTENT/apps/ddr-static;path:=/apps/ddr-static;overwrite:=true;overwriteProperties:=true,
+ SLING-CONTENT/conf/ddr-sample;path:=/conf/ddr-sample;overwrite:=true;overwriteProperties:=true,
SLING-CONTENT/content/ddr-sample;path:=/content/ddr-sample;overwrite:=true;overwriteProperties:=true
</Sling-Initial-Content>
</instructions>
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-dynamic/components.json b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-dynamic/components.json
index 3c7b6bf..41c83f6 100644
--- a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-dynamic/components.json
+++ b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-dynamic/components.json
@@ -1,5 +1,5 @@
{
+ "jcr:description": "Root Folder of the Declarative Dynamic Resources (Target)",
"jcr:primaryType": "sling:Folder",
- "jcr:title": "Declarative Dynamic Resource Folder",
- "jcr:description": "Root Folder of the Declarative Dynamic Resources (Target)"
+ "jcr:title": "Declarative Dynamic Resource Folder"
}
\ No newline at end of file
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample.json b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample.json
index 152abde..4cee809 100644
--- a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample.json
+++ b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample.json
@@ -1,5 +1,5 @@
{
"jcr:primaryType": "sling:Folder",
- "jcr:title": "Declarative Dynamic Resource Root",
- "jcr:description": "Declarative Dynamic Resource Apps Root folder"
+ "jcr:title": "Declarative Dynamic Sample Resource Root",
+ "jcr:description": "Declarative Dynamic Sample Resource Apps Root folder"
}
\ No newline at end of file
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.ddr.core.DeclarativeDynamicResourceManagerService-ddrsample.json b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.ddr.core.DeclarativeDynamicResourceManagerService-ddrsample.json
deleted file mode 100644
index 5d0b6c2..0000000
--- a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.ddr.core.DeclarativeDynamicResourceManagerService-ddrsample.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "jcr:primaryType": "sling:OsgiConfig",
- "dynamic.component.target.path": "/apps/ddr-dynamic/components"
-}
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.ddr.core.setup.DeclarativeDynamicResourceSetupService-ddrsample.json b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.ddr.core.setup.DeclarativeDynamicResourceSetupService-ddrsample.json
deleted file mode 100644
index 5dab22e..0000000
--- a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.ddr.core.setup.DeclarativeDynamicResourceSetupService-ddrsample.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "jcr:primaryType": "sling:OsgiConfig",
- "dynamic.component.root.path": "/conf/ddr-sample/settings",
- "dynamic.component.target.path": "/apps/ddr-dynamic/components",
- "dynamic.component.group": "Dynamic.Content",
- "dynamic.component.primary.type": "sling:Folder",
- "dynamic.component.names": [
- "button1=Button-1|ddr-static/button",
- "container1=Container-1|ddr-static/container"
- ],
- "dynamic.component.additional.properties": [
- "button1=jcr:description|Dynamic Test Button",
- "container1=cq:styleElements|{div;section;article;main;aside;header;footer}"
- ],
- "dynamic.component.refs": []
-}
\ No newline at end of file
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.jcr.repoinit.RepositoryInitializer-ddrsample.config b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.jcr.repoinit.RepositoryInitializer-ddrsample.config
deleted file mode 100644
index 8f79a87..0000000
--- a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-sample/config/org.apache.sling.jcr.repoinit.RepositoryInitializer-ddrsample.config
+++ /dev/null
@@ -1,38 +0,0 @@
-# Licensed to the Apache Software Foundation (ASF) under one or more
-# contributor license agreements. See the NOTICE file distributed with this
-# work for additional information regarding copyright ownership. The ASF
-# licenses this file to You under the Apache License, Version 2.0 (the
-# "License"); you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations under
-# the License.
-#
-scripts=[\
-"
-# This Repoinit Script is here to make sure that during an upgrade the necessary users
-# and permissions are available
-
-# Create the Folders for Dynamic Components
-
- create path /conf(sling:Folder)
- create path /conf/ddr-sample(sling:Folder)
- create path /conf/ddr-sample/settings(sling:Folder)
- create path /conf/ddr-sample/settings/dynamic(sling:Folder)
-
- create service user ddr-serviceuser with path /home/users/system/dynamic
-
-# Set Permission for Dynamic Components Service User
-
- set ACL for ddr-serviceuser
- allow jcr:all on /conf/ddr-sample/settings/dynamic
- end
-"\
-]
-
-references=[]
\ No newline at end of file
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/container.json b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/text.json
similarity index 100%
rename from org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/container.json
rename to org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/text.json
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/container/container.html b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/text/text.html
similarity index 97%
rename from org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/container/container.html
rename to org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/text/text.html
index 24f58ce..de65ea5 100644
--- a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/container/container.html
+++ b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/apps/ddr-static/text/text.html
@@ -16,4 +16,4 @@
specific language governing permissions and limitations
under the License.
-->
-<p>Container</p>
\ No newline at end of file
+<p>Hello Text</p>
\ No newline at end of file
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/conf/ddr-sample.json b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/conf/ddr-sample.json
new file mode 100644
index 0000000..7e04c89
--- /dev/null
+++ b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/conf/ddr-sample.json
@@ -0,0 +1,4 @@
+{
+ "jcr:primaryType": "sling:Folder",
+ "jcr:title": "DDR Sample Configuration Folder"
+}
\ No newline at end of file
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/conf/ddr-sample/settings.json b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/conf/ddr-sample/settings.json
new file mode 100644
index 0000000..d79a772
--- /dev/null
+++ b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/conf/ddr-sample/settings.json
@@ -0,0 +1,23 @@
+{
+ "jcr:primaryType": "sling:Folder",
+ "dynamic": {
+ "jcr:mixinTypes": [
+ "rep:AccessControllable"
+ ],
+ "jcr:primaryType": "slingddr:Folder",
+ "slingddr:target": "/apps/ddr-dynamic/components",
+ "button1": {
+ "componentGroup": "Dynamic.Content",
+ "jcr:description": "Dynamic Test Button",
+ "jcr:primaryType": "sling:Folder",
+ "jcr:title": "Button-1",
+ "sling:resourceSuperType": "ddr-static/button"
+ },
+ "text1": {
+ "componentGroup": "Dynamic.Content",
+ "jcr:primaryType": "sling:Folder",
+ "jcr:title": "Text-1",
+ "sling:resourceSuperType": "ddr-static/text"
+ }
+ }
+}
\ No newline at end of file
diff --git a/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/content/ddr-sample/text.json b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/content/ddr-sample/text.json
new file mode 100644
index 0000000..8bd5484
--- /dev/null
+++ b/org.apache.sling.ddr/sample/src/main/resources/SLING-CONTENT/content/ddr-sample/text.json
@@ -0,0 +1,4 @@
+{
+ "jcr:primaryType": "sling:Folder",
+ "sling:resourceSuperType": "ddr-dynamic/components/text1"
+}