You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2020/09/10 15:01:38 UTC

[sling-whiteboard] branch master updated: Rules and matchers

This is an automated email from the ASF dual-hosted git repository.

bdelacretaz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 5cf51af  Rules and matchers
5cf51af is described below

commit 5cf51af47c0e78c2f5880464ff61e872f5deba29
Author: Bertrand Delacretaz <bd...@apache.org>
AuthorDate: Thu Sep 10 16:59:48 2020 +0200

    Rules and matchers
---
 .../remotecontentapi/take5/ResourceProcessor.java  |  30 ++++
 .../remotecontentapi/take5/ResourceRules.java      |  65 +++++++++
 .../sling/remotecontentapi/take5/RulesList.java    | 162 +++++++++++++++++++++
 .../sling/remotecontentapi/take5/Take5Servlet.java |  89 +++++++++++
 .../sling/remotecontentapi/take5/UrlBuilder.java   |  49 +++++++
 .../resources/SLING-INF/initial-content/ROOT.json  |   2 +-
 6 files changed, 396 insertions(+), 1 deletion(-)

diff --git a/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/ResourceProcessor.java b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/ResourceProcessor.java
new file mode 100644
index 0000000..6de8c6e
--- /dev/null
+++ b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/ResourceProcessor.java
@@ -0,0 +1,30 @@
+/*
+ * 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.remotecontentapi.take5;
+
+import java.io.IOException;
+
+import javax.json.JsonObjectBuilder;
+
+import org.apache.sling.api.resource.Resource;
+
+interface ResourceProcessor {
+    void process(Resource r, JsonObjectBuilder b, UrlBuilder urlb) throws IOException;
+}
\ No newline at end of file
diff --git a/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/ResourceRules.java b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/ResourceRules.java
new file mode 100644
index 0000000..2669499
--- /dev/null
+++ b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/ResourceRules.java
@@ -0,0 +1,65 @@
+/*
+ * 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.remotecontentapi.take5;
+
+import java.util.function.Predicate;
+
+import org.apache.sling.api.resource.Resource;
+
+class ResourceRules {
+    final Predicate<Resource> matcher;
+    final ResourceProcessor navigationProcessor;
+    final ResourceProcessor contentProcessor;
+
+    private ResourceRules(Predicate<Resource> matcher, ResourceProcessor navigationProcessor, ResourceProcessor contentProcessor) {
+        this.matcher = matcher;
+        this.navigationProcessor = navigationProcessor;
+        this.contentProcessor = contentProcessor;      
+    }
+
+    static class Builder {
+        private final Predicate<Resource> matcher;
+        private ResourceProcessor navigationProcessor;
+        private ResourceProcessor contentProcessor;
+
+        Builder(Predicate<Resource> matcher) {
+            this.matcher = matcher;
+        }
+
+        Builder withNavigationProcessor(ResourceProcessor rp) {
+            navigationProcessor = rp;
+            return this;
+        }
+
+        Builder withContentProcessor(ResourceProcessor rp) {
+            contentProcessor = rp;
+            return this;
+        }
+
+        ResourceRules build() {
+            return new ResourceRules(matcher, navigationProcessor, contentProcessor);
+        }
+
+    }
+
+    static Builder builder(Predicate<Resource> matcher) {
+        return new Builder(matcher);
+    }
+}
\ No newline at end of file
diff --git a/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/RulesList.java b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/RulesList.java
new file mode 100644
index 0000000..a7d20f4
--- /dev/null
+++ b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/RulesList.java
@@ -0,0 +1,162 @@
+/*
+ * 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.remotecontentapi.take5;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import javax.json.Json;
+import javax.json.JsonObjectBuilder;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+
+class RulesList {
+    private final List<ResourceRules> rules = new ArrayList<>();
+
+    static class DefaultProcessor implements ResourceProcessor {
+        private final String name;
+
+        DefaultProcessor(String name) {
+            this.name = name;
+        }
+
+        @Override
+        public void process(Resource r, JsonObjectBuilder b, UrlBuilder urlb) throws IOException {
+            b.add(name, r.getPath());
+        }
+    }
+
+    static boolean matchAny(String in, String ... variants) {
+        if(in == null) {
+            return false;
+        }
+        for(String variant : variants) {
+            if(in.equals(variant)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static class DefaultNavProcessor implements ResourceProcessor {
+        @Override
+        public void process(Resource r, JsonObjectBuilder b, UrlBuilder urlb) throws IOException {
+            b.add("_id", r.getPath());
+            b.add("_url", urlb.pathToUrl(r.getPath()));
+            if(!r.getResourceType().isEmpty()) {
+                b.add("_resourceType", r.getResourceType());
+            }
+        }
+    }
+
+    static class WkndImageContentProcessor implements ResourceProcessor {
+        @Override
+        public void process(Resource r, JsonObjectBuilder b, UrlBuilder urlb) throws IOException {
+            final ValueMap vm = r.adaptTo(ValueMap.class);
+            if(vm != null) {
+                vm.entrySet().stream()
+                .filter(entry -> "fileReference".equals(entry.getKey()))
+                .forEach(entry -> b.add(entry.getKey(), String.valueOf(entry.getValue())));
+            }
+        }
+    }
+
+    static class DefaultContentProcessor implements ResourceProcessor {
+        private final RulesList rules;
+
+        DefaultContentProcessor(RulesList rules) {
+            this.rules = rules;
+        }
+
+        @Override
+        public void process(Resource r, JsonObjectBuilder b, UrlBuilder urlb) throws IOException {
+            final ValueMap vm = r.adaptTo(ValueMap.class);
+            if(vm != null) {
+                vm.entrySet().stream().forEach(entry -> b.add(entry.getKey(), String.valueOf(entry.getValue())));
+            }
+            for(Resource child : r.getChildren()) {
+                final JsonObjectBuilder childJson = Json.createObjectBuilder();
+                if(rules.applyRules(child, true, childJson, urlb)) {
+                    b.add(child.getName(), childJson);
+                }
+            }
+        }
+    }
+
+    static boolean isNodeType(Resource r, String ... nodeTypes) {
+        final ValueMap vm = r.adaptTo(ValueMap.class);
+        if(vm != null) {
+            return matchAny(vm.get("jcr:primaryType", String.class), nodeTypes);
+        }
+        return false;
+    }
+
+    RulesList() {
+        rules.add(
+            ResourceRules.builder(r -> isNodeType(r, "sling:Folder", "sling:OrderedFolder", "nt:folder"))
+            .withNavigationProcessor(new DefaultNavProcessor())
+            .withContentProcessor(new DefaultProcessor("FolderContent"))
+            .build()
+        );        
+        rules.add(
+            ResourceRules.builder(r -> "wknd/components/image".equals(r.getResourceType()))
+            .withNavigationProcessor(null)
+            .withContentProcessor(new WkndImageContentProcessor())
+            .build()
+        );
+        rules.add(
+            ResourceRules.builder(r -> isNodeType(r, "cq:Page"))
+            .withNavigationProcessor(new DefaultNavProcessor())
+            .withContentProcessor(new DefaultContentProcessor(this))
+            .build()
+        );
+        rules.add(
+            ResourceRules.builder(r -> "samples/article".equals(r.getResourceSuperType()))
+            .withNavigationProcessor(new DefaultNavProcessor())
+            .withContentProcessor(new DefaultContentProcessor(this))
+            .build()
+        );
+        rules.add(
+            ResourceRules.builder(r -> true)
+            .withContentProcessor(new DefaultContentProcessor(this))
+            .build()
+        );
+    }
+
+    boolean applyRules(Resource resource, boolean contentMode, JsonObjectBuilder json, UrlBuilder urlb) throws IOException {
+        final Optional<ResourceRules> activeRule = matchingRules(resource)
+            .filter(rule -> contentMode ? rule.contentProcessor != null : rule.navigationProcessor != null)
+            .findFirst();
+        if(activeRule.isPresent()) {
+            final ResourceProcessor p = contentMode ? activeRule.get().contentProcessor : activeRule.get().navigationProcessor;
+            p.process(resource, json, urlb);
+            return true;
+        }
+        return false;
+    }
+
+    private Stream<ResourceRules> matchingRules(Resource r) {
+        return rules.stream().filter(rule -> rule.matcher.test(r));
+    }
+}
\ No newline at end of file
diff --git a/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/Take5Servlet.java b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/Take5Servlet.java
new file mode 100644
index 0000000..b38f0f8
--- /dev/null
+++ b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/Take5Servlet.java
@@ -0,0 +1,89 @@
+/*
+ * 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.remotecontentapi.take5;
+
+import java.io.IOException;
+import java.util.Optional;
+
+import javax.json.Json;
+import javax.json.JsonObjectBuilder;
+import javax.servlet.Servlet;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
+import org.osgi.service.component.annotations.Component;
+
+/** This is "take 5" of this experiment, using a table of
+ *  content processors driven by Resource matchers
+ */
+@Component(service = Servlet.class,
+    property = {
+            "sling.servlet.resourceTypes=sling/servlet/default",
+            "sling.servlet.prefix:Integer=-1",
+
+            "sling.servlet.methods=GET",
+            "sling.servlet.methods=HEAD",
+            "sling.servlet.selectors=s:t5",
+            "sling.servlet.extension=json",
+    })
+public class Take5Servlet extends SlingSafeMethodsServlet {
+    private static final long serialVersionUID = 1L;
+    private final RulesList rules = new RulesList();
+
+    @Override
+    public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
+        final JsonObjectBuilder result = Json.createObjectBuilder();
+        final UrlBuilder urlb = new UrlBuilder(request);
+        final Resource resource = request.getResource();
+        
+        // Nav and metadata
+        final JsonObjectBuilder navigation = Json.createObjectBuilder();
+        navigation.add("self", urlb.pathToUrl(resource.getPath()));
+        if(resource.getParent() != null) {
+            navigation.add("parent", urlb.pathToUrl(resource.getParent().getPath()));
+        }
+        result.add("navigation", navigation);
+
+        final JsonObjectBuilder metadata = Json.createObjectBuilder();
+        metadata.add("_id", resource.getPath());
+        result.add("metatada", metadata);
+
+        // Apply the first rule that matches and has a non-null processor
+        final JsonObjectBuilder content = Json.createObjectBuilder();
+        rules.applyRules(resource, true, content, urlb);
+        result.add("content", content);
+
+        // And recurse into children
+        final JsonObjectBuilder children = Json.createObjectBuilder();
+        for(Resource child : resource.getChildren()) {
+            final JsonObjectBuilder childJson = Json.createObjectBuilder();
+            if(rules.applyRules(child, false, childJson, urlb)) {
+                children.add(child.getName(), childJson);
+            }
+        }
+        result.add("children", children);
+
+        response.setCharacterEncoding("UTF-8");
+        response.setContentType("application/json");
+        response.getWriter().write(result.build().toString());
+    }
+}
\ No newline at end of file
diff --git a/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/UrlBuilder.java b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/UrlBuilder.java
new file mode 100644
index 0000000..4a3744d
--- /dev/null
+++ b/remote-content-api/src/main/java/org/apache/sling/remotecontentapi/take5/UrlBuilder.java
@@ -0,0 +1,49 @@
+/*
+ * 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.remotecontentapi.take5;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+
+class UrlBuilder {
+    private SlingHttpServletRequest request;
+
+    UrlBuilder(SlingHttpServletRequest request) {
+        this.request = request;
+    }
+
+    public String pathToUrlNoExtension(String path) {
+        return String.format(
+            "%s://%s:%d%s",
+            request.getScheme(),
+            request.getServerName(),
+            request.getServerPort(),
+            path
+        );
+    }
+
+    public String pathToUrl(String path) {
+        return String.format(
+            "%s.%s.%s",
+            pathToUrlNoExtension(path),
+            request.getRequestPathInfo().getSelectorString(),
+            request.getRequestPathInfo().getExtension()
+        );
+    }
+}
\ No newline at end of file
diff --git a/remote-content-api/src/main/resources/SLING-INF/initial-content/ROOT.json b/remote-content-api/src/main/resources/SLING-INF/initial-content/ROOT.json
index a5d2b28..456502e 100644
--- a/remote-content-api/src/main/resources/SLING-INF/initial-content/ROOT.json
+++ b/remote-content-api/src/main/resources/SLING-INF/initial-content/ROOT.json
@@ -1,4 +1,4 @@
 {
     "sling:resourceType" : "sling:redirect",
-    "sling:target" : "/content.s:xyz.json"
+    "sling:target" : "/content.s:t5.json"
 }
\ No newline at end of file