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));
+                }
+            }
+        }
+
+    }
+
+}

Reply via email to