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