You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ol...@apache.org on 2019/09/15 14:12:27 UTC
[sling-org-apache-sling-scripting-core] 02/02: SLING-4330 Select
script engines in SlingScriptAdapterFactory via mapping
This is an automated email from the ASF dual-hosted git repository.
olli pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-core.git
commit 09e48f47691dbb1825e74c44f1f2bb6df9c9eb78
Author: Oliver Lietz <ol...@apache.org>
AuthorDate: Sun Sep 15 16:11:36 2019 +0200
SLING-4330 Select script engines in SlingScriptAdapterFactory via mapping
Add SlingScriptEnginePicker and use picker in SlingScriptAdapterFactory when more than one engine is available per script extension
---
.../core/impl/SlingScriptAdapterFactory.java | 9 +-
.../core/impl/SlingScriptEnginePicker.java | 128 ++++++++++++++++++++
.../sling/scripting/core/it/HtmlScriptingIT.java | 129 +++++++++++++++++++++
src/test/resources/apps/htl.json | 6 +
src/test/resources/apps/htl/page/page.html | 27 +++++
src/test/resources/apps/thymeleaf.json | 6 +
src/test/resources/apps/thymeleaf/page/page.html | 27 +++++
src/test/resources/content/scripting.json | 14 +++
8 files changed, 343 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/apache/sling/scripting/core/impl/SlingScriptAdapterFactory.java b/src/main/java/org/apache/sling/scripting/core/impl/SlingScriptAdapterFactory.java
index bb6ceff..4f64f5d 100644
--- a/src/main/java/org/apache/sling/scripting/core/impl/SlingScriptAdapterFactory.java
+++ b/src/main/java/org/apache/sling/scripting/core/impl/SlingScriptAdapterFactory.java
@@ -71,6 +71,9 @@ public class SlingScriptAdapterFactory implements AdapterFactory, MimeTypeProvid
@Reference
private SlingScriptEngineManager scriptEngineManager;
+ @Reference
+ private volatile SlingScriptEnginePicker scriptEnginePicker;
+
/**
* The BindingsValuesProviderTracker
*/
@@ -88,10 +91,10 @@ public class SlingScriptAdapterFactory implements AdapterFactory, MimeTypeProvid
final Resource resource = (Resource) adaptable;
final String path = resource.getPath();
- final String ext = path.substring(path.lastIndexOf('.') + 1);
+ final String extension = path.substring(path.lastIndexOf('.') + 1);
- final List<ScriptEngine> engines = scriptEngineManager.getEnginesByExtension(ext);
- final ScriptEngine engine = engines.size() > 0 ? engines.get(0) : null;
+ final List<ScriptEngine> engines = scriptEngineManager.getEnginesByExtension(extension);
+ final ScriptEngine engine = scriptEnginePicker.pickScriptEngine(engines, resource, extension);
if (engine != null) {
final Collection<BindingsValuesProvider> bindingsValuesProviders = bindingsValuesProviderTracker.getBindingsValuesProviders(engine.getFactory(), BINDINGS_CONTEXT);
// unchecked cast
diff --git a/src/main/java/org/apache/sling/scripting/core/impl/SlingScriptEnginePicker.java b/src/main/java/org/apache/sling/scripting/core/impl/SlingScriptEnginePicker.java
new file mode 100644
index 0000000..666189d
--- /dev/null
+++ b/src/main/java/org/apache/sling/scripting/core/impl/SlingScriptEnginePicker.java
@@ -0,0 +1,128 @@
+/*
+ * 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.scripting.core.impl;
+
+import java.util.List;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+
+import org.apache.sling.api.resource.Resource;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.framework.Constants;
+import org.osgi.service.component.annotations.Component;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(
+ service = {SlingScriptEnginePicker.class},
+ property = {
+ Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
+ Constants.SERVICE_DESCRIPTION + "=Apache Sling Scripting SlingScriptEnginePicker"
+ }
+)
+public class SlingScriptEnginePicker {
+
+ /**
+ * The property contains the required language mapping
+ *
+ * "sling:scripting": [
+ * "html=The HTL Templating Language:1.4"
+ * ]
+ * "sling:scripting": [
+ * "html=Thymeleaf:3.0"
+ * ]
+ */
+ private static String SLING_SCRIPTING = "sling:scripting";
+
+ private final Logger logger = LoggerFactory.getLogger(SlingScriptEnginePicker.class);
+
+ public SlingScriptEnginePicker() {
+ }
+
+ @Nullable ScriptEngine pickScriptEngine(@NotNull final List<ScriptEngine> scriptEngines, @NotNull Resource resource, @NotNull String extension) {
+ final String scriptingMapping = findScriptingMapping(resource, extension);
+ logger.debug("scripting mapping: {}", scriptingMapping);
+ if (scriptingMapping == null || scriptingMapping.isEmpty()) {
+ return scriptEngines.size() > 0 ? scriptEngines.get(0) : null;
+ }
+
+ final Conditions conditions = parseScriptingMapping(scriptingMapping);
+ logger.debug("scripting conditions: {}", conditions);
+ for (final ScriptEngine scriptEngine : scriptEngines) {
+ final ScriptEngineFactory scriptEngineFactory = scriptEngine.getFactory();
+ if (conditions.matches(scriptEngineFactory.getLanguageName())) {
+ logger.debug("script engine {} found for conditions {}", scriptEngineFactory.getEngineName(), conditions);
+ return scriptEngine;
+ }
+ }
+ return null;
+ }
+
+ private String findScriptingMapping(@NotNull final Resource resource, @NotNull final String extension) {
+ final String[] mappings = resource.getValueMap().get(SLING_SCRIPTING, String[].class);
+ if (mappings != null) {
+ final String start = String.format("%s=", extension);
+ for (final String mapping : mappings) {
+ if (mapping.startsWith(start)) {
+ return mapping.substring(start.length());
+ }
+ }
+ return resource.getParent() != null ? findScriptingMapping(resource.getParent(), extension) : null;
+ } else if (resource.getParent() != null) {
+ return findScriptingMapping(resource.getParent(), extension);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * TODO Take language version, engine name and engine version into account
+ */
+ private Conditions parseScriptingMapping(@NotNull final String scriptingMapping) {
+ final String[] values = scriptingMapping.split(":");
+ final String languageName = values[0];
+ final String languageVersion = values.length > 1 ? values[1] : null;
+ // values[2] -> engine name
+ // values[3] -> engine version
+ return new Conditions(languageName, languageVersion);
+ }
+
+ private class Conditions {
+
+ final String languageName;
+
+ final String languageVersion;
+
+ Conditions(@NotNull final String languageName, @Nullable final String languageVersion) {
+ this.languageName = languageName;
+ this.languageVersion = languageVersion;
+ }
+
+ boolean matches(final String languageName) {
+ return this.languageName.equalsIgnoreCase(languageName);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s, %s", languageName, languageVersion);
+ }
+
+ }
+
+}
diff --git a/src/test/java/org/apache/sling/scripting/core/it/HtmlScriptingIT.java b/src/test/java/org/apache/sling/scripting/core/it/HtmlScriptingIT.java
new file mode 100644
index 0000000..9714fb2
--- /dev/null
+++ b/src/test/java/org/apache/sling/scripting/core/it/HtmlScriptingIT.java
@@ -0,0 +1,129 @@
+/*
+ * 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.scripting.core.it;
+
+import java.io.IOException;
+
+import javax.inject.Inject;
+import javax.script.ScriptEngineFactory;
+
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.api.servlets.ServletResolver;
+import org.apache.sling.resource.presence.ResourcePresence;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.ProbeBuilder;
+import org.ops4j.pax.exam.TestProbeBuilder;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
+import org.ops4j.pax.exam.spi.reactors.PerClass;
+import org.ops4j.pax.exam.util.Filter;
+import org.osgi.service.http.HttpService;
+
+import static org.apache.sling.testing.paxexam.SlingOptions.slingQuickstartOakTar;
+import static org.apache.sling.testing.paxexam.SlingOptions.slingScriptingSightly;
+import static org.apache.sling.testing.paxexam.SlingOptions.slingScriptingThymeleaf;
+import static org.hamcrest.Matchers.is;
+import static org.junit.Assert.assertThat;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.factoryConfiguration;
+
+@RunWith(PaxExam.class)
+@ExamReactorStrategy(PerClass.class)
+public class HtmlScriptingIT extends ScriptingCoreTestSupport {
+
+ @Inject
+ protected ServletResolver servletResolver;
+
+ // SlingScriptAdapterFactory
+ @Inject
+ @Filter(value = "(adapters=org.apache.sling.api.scripting.SlingScript)")
+ protected AdapterFactory adapterFactory;
+
+ @Inject
+ protected HttpService httpService;
+
+ @Inject
+ @Filter(value = "(names=htl)")
+ protected ScriptEngineFactory htlScriptEngineFactory;
+
+ @Inject
+ @Filter(value = "(names=thymeleaf)")
+ protected ScriptEngineFactory thymeleafScriptEngineFactory;
+
+ @Inject
+ @Filter(value = "(path=/apps/htl)")
+ private ResourcePresence htl;
+
+ @Inject
+ @Filter(value = "(path=/apps/thymeleaf)")
+ private ResourcePresence thymeleaf;
+
+ @Inject
+ @Filter(value = "(path=/content/scripting)")
+ private ResourcePresence scripting;
+
+ @Configuration
+ public Option[] configuration() {
+ final String workingDirectory = workingDirectory();
+ return remove(options(
+ super.baseConfiguration(),
+ slingScriptingSightly(),
+ slingScriptingThymeleaf(),
+ slingQuickstartOakTar(workingDirectory, httpPort),
+ factoryConfiguration("org.apache.sling.resource.presence.internal.ResourcePresenter")
+ .put("path", "/apps/htl")
+ .asOption(),
+ factoryConfiguration("org.apache.sling.resource.presence.internal.ResourcePresenter")
+ .put("path", "/apps/thymeleaf")
+ .asOption(),
+ factoryConfiguration("org.apache.sling.resource.presence.internal.ResourcePresenter")
+ .put("path", "/content/scripting")
+ .asOption()
+ ), scriptingCore);
+ }
+
+ @ProbeBuilder
+ public TestProbeBuilder probeConfiguration(final TestProbeBuilder testProbeBuilder) {
+ testProbeBuilder.setHeader("Sling-Initial-Content", String.join(",",
+ "apps;path:=/apps;overwrite:=true;uninstall:=true",
+ "content;path:=/content;overwrite:=true;uninstall:=true"
+ ));
+ return testProbeBuilder;
+ }
+
+ @Test
+ public void testHtlTitle() throws IOException {
+ final String url = String.format("http://localhost:%s/scripting/htl.html", httpPort());
+ final Document document = Jsoup.connect(url).get();
+ assertThat(document.title(), is("Sling Scripting HTL"));
+ }
+
+ @Test
+ public void testThymeleafTitle() throws IOException {
+ final String url = String.format("http://localhost:%s/scripting/thymeleaf.html", httpPort());
+ final Document document = Jsoup.connect(url).get();
+ assertThat(document.title(), is("Sling Scripting Thymeleaf"));
+ }
+
+}
diff --git a/src/test/resources/apps/htl.json b/src/test/resources/apps/htl.json
new file mode 100644
index 0000000..4e0d286
--- /dev/null
+++ b/src/test/resources/apps/htl.json
@@ -0,0 +1,6 @@
+{
+ "sling:scripting": [
+ "html=The HTL Templating Language:1.4",
+ "js=ECMAScript:partial ECMAScript 2015 support"
+ ]
+}
diff --git a/src/test/resources/apps/htl/page/page.html b/src/test/resources/apps/htl/page/page.html
new file mode 100644
index 0000000..b3a05b3
--- /dev/null
+++ b/src/test/resources/apps/htl/page/page.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!--
+ 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.
+-->
+<html lang="en">
+<head>
+ <meta charset="UTF-8"/>
+ <title>${properties.title}</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/src/test/resources/apps/thymeleaf.json b/src/test/resources/apps/thymeleaf.json
new file mode 100644
index 0000000..cc55018
--- /dev/null
+++ b/src/test/resources/apps/thymeleaf.json
@@ -0,0 +1,6 @@
+{
+ "sling:scripting": [
+ "html=Thymeleaf:3.0",
+ "js=ECMAScript:partial ECMAScript 2015 support"
+ ]
+}
diff --git a/src/test/resources/apps/thymeleaf/page/page.html b/src/test/resources/apps/thymeleaf/page/page.html
new file mode 100644
index 0000000..48be9ac
--- /dev/null
+++ b/src/test/resources/apps/thymeleaf/page/page.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<!--
+ 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.
+-->
+<html lang="en">
+<head>
+ <meta charset="UTF-8"/>
+ <title data-th-text="${resource.getValueMap().get('title')}">HTML Title</title>
+</head>
+<body>
+</body>
+</html>
diff --git a/src/test/resources/content/scripting.json b/src/test/resources/content/scripting.json
new file mode 100644
index 0000000..41a2e98
--- /dev/null
+++ b/src/test/resources/content/scripting.json
@@ -0,0 +1,14 @@
+{
+ "jcr:primaryType": "nt:unstructured",
+ "title": "Apache Sling Scripting",
+ "htl": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType": "htl/page",
+ "title": "Sling Scripting HTL"
+ },
+ "thymeleaf": {
+ "jcr:primaryType": "nt:unstructured",
+ "sling:resourceType": "thymeleaf/page",
+ "title": "Sling Scripting Thymeleaf"
+ }
+}