This is an automated email from the ASF dual-hosted git repository.
enorman pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-scripting-core.git
The following commit(s) were added to refs/heads/master by this push:
new 3d910d1 SLING-10155 add configuration to include or exclude specific
ScriptEngineFactory (#6)
3d910d1 is described below
commit 3d910d1624c940b390e9163c24b6ffaa24287262
Author: Eric Norman <[email protected]>
AuthorDate: Wed Mar 10 11:08:52 2021 -0800
SLING-10155 add configuration to include or exclude specific
ScriptEngineFactory (#6)
SLING-10155 add configuration to include or exclude specific
ScriptEngineFactory
---
.../core/impl/jsr223/SlingScriptEngineManager.java | 150 +++++++++++++--
.../scripting/core/it/ExcludeScriptEngineIT.java | 202 +++++++++++++++++++++
2 files changed, 337 insertions(+), 15 deletions(-)
diff --git
a/src/main/java/org/apache/sling/scripting/core/impl/jsr223/SlingScriptEngineManager.java
b/src/main/java/org/apache/sling/scripting/core/impl/jsr223/SlingScriptEngineManager.java
index 76961f5..ca7a223 100644
---
a/src/main/java/org/apache/sling/scripting/core/impl/jsr223/SlingScriptEngineManager.java
+++
b/src/main/java/org/apache/sling/scripting/core/impl/jsr223/SlingScriptEngineManager.java
@@ -30,6 +30,7 @@ import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.script.ScriptEngine;
@@ -54,6 +55,9 @@ import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
+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;
@@ -72,8 +76,21 @@ import org.slf4j.LoggerFactory;
policy = ReferencePolicy.DYNAMIC
)
)
+@Designate(ocd=SlingScriptEngineManager.Config.class)
public class SlingScriptEngineManager extends ScriptEngineManager implements
BundleListener {
+ @ObjectClassDefinition(name ="Apache Sling Script Engine Manager",
+ description="Configures options for the Script Engine Manager")
+ public @interface Config {
+
+ @AttributeDefinition(name = "Includes", description = "A script engine
with a short name that matches any of these expressions is included")
+ String[] includes() default {".*"};
+
+ @AttributeDefinition(name = "Excludes", description = "A script engine
with a short name that matches any of these (optional) expressions is NOT
included, even if it was accepted by the 'Includes' configuration")
+ String[] excludes();
+
+ }
+
private ScriptEngineManager internalManager;
private final Set<Bundle> engineSpiBundles = new HashSet<>();
@@ -99,11 +116,18 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
private final Logger logger =
LoggerFactory.getLogger(SlingScriptEngineManager.class);
+ private Set<Pattern> includePatterns = Collections.emptySet();
+ private Set<Pattern> excludePatterns = Collections.emptySet();
+
@Override
public ScriptEngine getEngineByName(String shortName) {
readWriteLock.readLock().lock();
try {
- return internalManager.getEngineByName(shortName);
+ SortableScriptEngineFactory ssef = factories.stream()
+ .filter(factory -> factory.getNames().contains(shortName))
+ .findFirst()
+ .orElse(null);
+ return ssef == null ? null : ssef.getScriptEngine();
} finally {
readWriteLock.readLock().unlock();
}
@@ -113,7 +137,11 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
readWriteLock.readLock().lock();
try {
return factories.stream()
- .filter(factory -> factory.getEngineName().contains(shortName))
+ // first, check exact match of short names
+ .filter(factory -> factory.getNames().contains(shortName) ||
+ // then, check contains match of long name
+ // for backward compatibility
+ factory.getEngineName().contains(shortName))
.map(factory -> factory.getDelegate().getScriptEngine())
.collect(Collectors.toList());
} finally {
@@ -125,7 +153,11 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
public ScriptEngine getEngineByExtension(String extension) {
readWriteLock.readLock().lock();
try {
- return internalManager.getEngineByExtension(extension);
+ SortableScriptEngineFactory ssef = factories.stream()
+ .filter(factory ->
factory.getExtensions().contains(extension))
+ .findFirst()
+ .orElse(null);
+ return ssef == null ? null : ssef.getScriptEngine();
} finally {
readWriteLock.readLock().unlock();
}
@@ -147,7 +179,11 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
public ScriptEngine getEngineByMimeType(String mimeType) {
readWriteLock.readLock().lock();
try {
- return internalManager.getEngineByMimeType(mimeType);
+ SortableScriptEngineFactory ssef = factories.stream()
+ .filter(factory ->
factory.getMimeTypes().contains(mimeType))
+ .findFirst()
+ .orElse(null);
+ return ssef == null ? null : ssef.getScriptEngine();
} finally {
readWriteLock.readLock().unlock();
}
@@ -237,17 +273,91 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
}
@Activate
- private void activate(final BundleContext bundleContext) {
+ private void activate(final Config config, final BundleContext
bundleContext) {
+ String[] includes = config.includes();
+ if (includes == null) {
+ this.includePatterns = Collections.emptySet();
+ } else {
+ this.includePatterns = new HashSet<>();
+ for (String pattern : includes) {
+ if (!pattern.isEmpty()) {
+ Pattern p = Pattern.compile(pattern);
+ includePatterns.add(p);
+ }
+ }
+ }
+
+ String[] excludes = config.excludes();
+ if (excludes == null) {
+ this.excludePatterns = Collections.emptySet();
+ } else {
+ this.excludePatterns = new HashSet<>();
+ for (String pattern : excludes) {
+ if (!pattern.isEmpty()) {
+ Pattern p = Pattern.compile(pattern);
+ excludePatterns.add(p);
+ }
+ }
+ }
+
this.bundleContext = bundleContext;
bundleContext.addBundleListener(this);
updateFactories();
}
+ /**
+ * Check if the given factory matches any of the include/excludes patterns
+ *
+ * @param sef the factory to check
+ * @return true if included, false otherwise
+ */
+ private boolean isIncluded(ScriptEngineFactory sef) {
+ boolean include = false;
+
+ if (!this.includePatterns.isEmpty()) {
+ List<String> names = sef.getNames();
+ for (String name : names) {
+ for (Pattern p : this.includePatterns) {
+ if (p.matcher(name).matches()) {
+ include = true;
+ if (logger.isDebugEnabled()) {
+ logger.debug("ScriptEngineFactory \"{}\" matches
the include pattern \"{}\" for name \"{}\"", sef.getEngineName(), p.pattern(),
name);
+ }
+ break; // found a match so stop looking further
+ }
+ }
+ if (include) {
+ break; // break out of the outer loop too
+ }
+ }
+ }
+
+ if (include && !this.excludePatterns.isEmpty()) {
+ List<String> names = sef.getNames();
+ for (String name : names) {
+ for (Pattern p : this.excludePatterns) {
+ if (p.matcher(name).matches()) {
+ include = false;
+ if (logger.isDebugEnabled()) {
+ logger.debug("ScriptEngineFactory \"{}\" matches
the exclude pattern \"{}\" for name \"{}\" so it is not included",
sef.getEngineName(), p.pattern(), name);
+ }
+ break; // found a match so stop looking further
+ }
+ }
+ if (!include) {
+ break; // break out of the outer loop too
+ }
+ }
+ }
+ return include;
+ }
+
@Deactivate
private void deactivate(final BundleContext bundleContext) {
bundleContext.removeBundleListener(this);
}
+ @SuppressWarnings("unused")
private void bindScriptEngineFactory(final
ServiceReference<ScriptEngineFactory> serviceReference, final
ScriptEngineFactory factory) {
synchronized (this.serviceReferences) {
serviceReferences.add(serviceReference);
@@ -256,6 +366,7 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
postEvent(SlingScriptConstants.TOPIC_SCRIPT_ENGINE_FACTORY_ADDED,
factory);
}
+ @SuppressWarnings("unused")
private void unbindScriptEngineFactory(final
ServiceReference<ScriptEngineFactory> serviceReference, final
ScriptEngineFactory factory) {
synchronized (this.serviceReferences) {
serviceReferences.remove(serviceReference);
@@ -267,6 +378,7 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
postEvent(SlingScriptConstants.TOPIC_SCRIPT_ENGINE_FACTORY_REMOVED,
factory);
}
+ @SuppressWarnings("unused")
private void updatedScriptEngineFactory(final
ServiceReference<ScriptEngineFactory> serviceReference, final
ScriptEngineFactory factory) {
updateFactories();
postEvent(SlingScriptConstants.TOPIC_SCRIPT_ENGINE_FACTORY_UPDATED,
factory);
@@ -277,12 +389,16 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
try {
internalManager = getInternalScriptEngineManager();
factories.clear();
+
long fakeBundleIdCounter = Long.MIN_VALUE;
// first add the platform factories
for (final ScriptEngineFactory factory :
internalManager.getEngineFactories()) {
- final SortableScriptEngineFactory sortableScriptEngineFactory
= new SortableScriptEngineFactory(factory, fakeBundleIdCounter++, 0, null);
- factories.add(sortableScriptEngineFactory);
+ if (isIncluded(factory)) {
+ final SortableScriptEngineFactory
sortableScriptEngineFactory = new SortableScriptEngineFactory(factory,
fakeBundleIdCounter++, 0, null);
+ factories.add(sortableScriptEngineFactory);
+ }
}
+
// then factories from SPI Bundles
final ClassLoader loader =
Thread.currentThread().getContextClassLoader();
try {
@@ -291,8 +407,10 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
try {
final ScriptEngineManager manager = new
ScriptEngineManager(bundle.adapt(BundleWiring.class).getClassLoader());
for (final ScriptEngineFactory factory :
manager.getEngineFactories()) {
- final SortableScriptEngineFactory
sortableScriptEngineFactory = new SortableScriptEngineFactory(factory,
bundle.getBundleId(), 0, null);
- factories.add(sortableScriptEngineFactory);
+ if (isIncluded(factory)) {
+ final SortableScriptEngineFactory
sortableScriptEngineFactory = new SortableScriptEngineFactory(factory,
bundle.getBundleId(), 0, null);
+ factories.add(sortableScriptEngineFactory);
+ }
}
} catch (Exception ex) {
logger.error("Unable to process bundle " +
bundle.getSymbolicName(), ex);
@@ -306,12 +424,14 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
if (bundleContext != null) {
for (final ServiceReference<ScriptEngineFactory>
serviceReference : serviceReferences) {
final ScriptEngineFactory scriptEngineFactory =
bundleContext.getService(serviceReference);
- final Map<String, Object> factoryProperties = new
HashMap<>(serviceReference.getPropertyKeys().length);
- for (final String key :
serviceReference.getPropertyKeys()) {
- factoryProperties.put(key,
serviceReference.getProperty(key));
+ if (isIncluded(scriptEngineFactory)) {
+ final Map<String, Object> factoryProperties = new
HashMap<>(serviceReference.getPropertyKeys().length);
+ for (final String key :
serviceReference.getPropertyKeys()) {
+ factoryProperties.put(key,
serviceReference.getProperty(key));
+ }
+ final SortableScriptEngineFactory
sortableScriptEngineFactory = new
SortableScriptEngineFactory(scriptEngineFactory,
serviceReference.getBundle().getBundleId(),
PropertiesUtil.toInteger(serviceReference.getProperty(Constants.SERVICE_RANKING),
0), factoryProperties);
+ factories.add(sortableScriptEngineFactory);
}
- final SortableScriptEngineFactory
sortableScriptEngineFactory = new
SortableScriptEngineFactory(scriptEngineFactory,
serviceReference.getBundle().getBundleId(),
PropertiesUtil.toInteger(serviceReference.getProperty(Constants.SERVICE_RANKING),
0), factoryProperties);
- factories.add(sortableScriptEngineFactory);
}
}
// register the associations at the end, so that the priority
sorting is taken into consideration
@@ -319,7 +439,7 @@ public class SlingScriptEngineManager extends
ScriptEngineManager implements Bun
registerAssociations(factory);
}
if (eventAdmin != null) {
- eventAdmin.postEvent(new
Event(EVENT_TOPIC_SCRIPT_MANAGER_UPDATED, Collections.EMPTY_MAP));
+ eventAdmin.postEvent(new
Event(EVENT_TOPIC_SCRIPT_MANAGER_UPDATED, Collections.emptyMap()));
}
} finally {
readWriteLock.writeLock().unlock();
diff --git
a/src/test/java/org/apache/sling/scripting/core/it/ExcludeScriptEngineIT.java
b/src/test/java/org/apache/sling/scripting/core/it/ExcludeScriptEngineIT.java
new file mode 100644
index 0000000..1c59395
--- /dev/null
+++
b/src/test/java/org/apache/sling/scripting/core/it/ExcludeScriptEngineIT.java
@@ -0,0 +1,202 @@
+/*
+ * 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.streamBundle;
+import static org.ops4j.pax.exam.cm.ConfigurationAdminOptions.newConfiguration;
+import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
+
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+
+import org.apache.sling.scripting.core.impl.jsr223.SlingScriptEngineManager;
+import org.junit.Test;
+import org.junit.experimental.runners.Enclosed;
+import org.junit.runner.RunWith;
+import org.ops4j.pax.exam.Configuration;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.junit.PaxExam;
+import org.ops4j.pax.tinybundles.core.TinyBundle;
+import org.ops4j.pax.tinybundles.core.TinyBundles;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ * Verify that loading specific ScriptEngineFactories is affected
+ * by the "excludes" value in the configuration.
+ */
+@RunWith(Enclosed.class)
+public class ExcludeScriptEngineIT {
+
+ private ExcludeScriptEngineIT() {
+ // private constructor to hide the implicit public one
+ }
+
+ /**
+ * Base class to share the common parts of LoadedIT and NotLoadedIT
+ * tests
+ */
+ abstract static class BaseIT extends ScriptingCoreTestSupport {
+
+ @Inject
+ protected SlingScriptEngineManager scriptEngineManager;
+
+ @Configuration
+ public Option[] configuration() {
+ return options(
+ baseConfiguration(),
+ scriptingCoreFragmentBundle(),
+
newConfiguration("org.apache.sling.scripting.core.impl.jsr223.SlingScriptEngineManager")
+ .put("excludes", excludesPatterns())
+ .asOption()
+ );
+ }
+
+ protected abstract String[] excludesPatterns();
+
+ /**
+ * Add a fragment to make the
org.apache.sling.scripting.core.impl.jsr223 package
+ * accessible so the SlingScriptEngineManager reference can be located
+ */
+ private Option scriptingCoreFragmentBundle() {
+ TinyBundle bundle = TinyBundles.bundle()
+ .set(Constants.FRAGMENT_HOST,
"org.apache.sling.scripting.core")
+ .set(Constants.BUNDLE_MANIFESTVERSION, "2")
+ .set(Constants.BUNDLE_SYMBOLICNAME,
"org.apache.sling.scripting.core.fragment")
+ .set(Constants.EXPORT_PACKAGE,
"org.apache.sling.scripting.core.impl.jsr223");
+
+ return streamBundle(
+ bundle.build(withBnd())
+ ).noStart();
+ }
+
+ /**
+ * Locate the nashorn script engine to verify it can be excluded
+ */
+ protected ScriptEngineFactory getNashornScriptEngineFactory() {
+ ScriptEngineFactory factory = null;
+
+ final ScriptEngineManager internalScriptEngineManager;
+ final ClassLoader loader =
Thread.currentThread().getContextClassLoader();
+ try {
+ Thread.currentThread().setContextClassLoader(null);
+ internalScriptEngineManager = new
ScriptEngineManager(ClassLoader.getSystemClassLoader());
+ }
+ finally {
+ Thread.currentThread().setContextClassLoader(loader);
+ }
+
+ List<ScriptEngineFactory> engineFactories =
internalScriptEngineManager.getEngineFactories();
+ for (ScriptEngineFactory scriptEngineFactory : engineFactories) {
+ if (scriptEngineFactory.getNames().contains("nashorn")) {
+ factory = scriptEngineFactory;
+ break;
+ }
+ }
+ return factory;
+ }
+
+ }
+
+ /**
+ * Verify that when exclude pattern in the SlingScriptEngineManager
+ * configuration doesn't match that the script engine is accessible
+ */
+ @RunWith(PaxExam.class)
+ public static class NotExcludedIT extends BaseIT {
+
+
+ @Override
+ protected String[] excludesPatterns() {
+ return new String[0];
+ }
+
+ @Test
+ public void testScriptEngineFactoryPresent() throws
InvalidSyntaxException {
+ ScriptEngineFactory factory = getNashornScriptEngineFactory();
+ assertNotNull("Expected nashorn ScriptEngine to exist", factory);
+
+ if (factory != null) {
+ // for backward compatibility, the "long name" also matches
with the #getEnginesByName call
+ assertFalse("Expecting ScriptEngineFactory engines to exist: "
+ factory.getEngineName(),
scriptEngineManager.getEnginesByName(factory.getEngineName()).isEmpty());
+
+ for (final String engineName : factory.getNames()) {
+ assertFalse("Expecting ScriptEngineFactory engines to
exist: " + engineName,
scriptEngineManager.getEnginesByName(engineName).isEmpty());
+ assertNotNull("Expecting ScriptEngineFactory engine to
exist: " + engineName, scriptEngineManager.getEngineByName(engineName));
+ }
+ for (final String extension : factory.getExtensions()) {
+ assertFalse("Expecting ScriptEngineFactory engines to
exist: " + extension,
scriptEngineManager.getEnginesByExtension(extension).isEmpty());
+ assertNotNull("Expecting ScriptEngineFactory engine to
exist: " + extension, scriptEngineManager.getEngineByExtension(extension));
+ }
+ for (final String mimetype : factory.getMimeTypes()) {
+ assertFalse("Expecting ScriptEngineFactory engines to
exist: " + mimetype,
scriptEngineManager.getEnginesByMimeType(mimetype).isEmpty());
+ assertNotNull("Expecting ScriptEngineFactory engine to
exist: " + mimetype, scriptEngineManager.getEngineByMimeType(mimetype));
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Verify that when exclude pattern in the SlingScriptEngineManager
+ * configuration does match that the script engine is accessible
+ */
+ @RunWith(PaxExam.class)
+ public static class ExcludedIT extends BaseIT {
+
+ @Override
+ protected String[] excludesPatterns() {
+ return new String [] {"fakeNashorn", "nashorn"};
+ }
+
+ @Test
+ public void testScriptEngineFactoryNotPresent() throws
InvalidSyntaxException {
+ ScriptEngineFactory factory = getNashornScriptEngineFactory();
+ assertNotNull("Expected nashorn ScriptEngine to exist", factory);
+
+ if (factory != null) {
+ // for backward compatibility, the "long name" also matches
with the #getEnginesByName call
+ assertTrue("Expecting ScriptEngineFactory engines to not
exist: " + factory.getEngineName(),
scriptEngineManager.getEnginesByName(factory.getEngineName()).isEmpty());
+
+ for (final String engineName : factory.getNames()) {
+ assertTrue("Expecting ScriptEngineFactory engines to not
exist: " + engineName,
scriptEngineManager.getEnginesByName(engineName).isEmpty());
+ assertNull("Expecting ScriptEngineFactory engine to not
exist: " + engineName, scriptEngineManager.getEngineByName(engineName));
+ }
+ for (final String extension : factory.getExtensions()) {
+ assertTrue("Expecting ScriptEngineFactory engines to not
exist: " + extension,
scriptEngineManager.getEnginesByExtension(extension).isEmpty());
+ assertNull("Expecting ScriptEngineFactory engine to not
exist: " + extension, scriptEngineManager.getEngineByExtension(extension));
+ }
+ for (final String mimetype : factory.getMimeTypes()) {
+ assertTrue("Expecting ScriptEngineFactory engines to not
exist: " + mimetype,
scriptEngineManager.getEnginesByMimeType(mimetype).isEmpty());
+ assertNull("Expecting ScriptEngineFactory engine to not
exist: " + mimetype, scriptEngineManager.getEngineByMimeType(mimetype));
+ }
+ }
+ }
+
+ }
+
+}