Repository: jclouds-karaf Updated Branches: refs/heads/fix-script-engine 5e0fcbef1 -> 6c0c7e6e8
Move OSGi script engine helpers to their own package Also change log level of exception handlers for script engine creation to ERROR Project: http://git-wip-us.apache.org/repos/asf/jclouds-karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/jclouds-karaf/commit/4014f4cb Tree: http://git-wip-us.apache.org/repos/asf/jclouds-karaf/tree/4014f4cb Diff: http://git-wip-us.apache.org/repos/asf/jclouds-karaf/diff/4014f4cb Branch: refs/heads/fix-script-engine Commit: 4014f4cbc9003d69b3ebcfd170a66cc2a49a2c8e Parents: 5e0fcbe Author: Andrew Phillips <[email protected]> Authored: Mon Sep 12 00:50:24 2016 +0100 Committer: Andrew Phillips <[email protected]> Committed: Mon Sep 12 00:50:28 2016 +0100 ---------------------------------------------------------------------- .../table/internal/OSGiScriptEngine.java | 95 ------ .../table/internal/OSGiScriptEngineFactory.java | 98 ------ .../table/internal/OSGiScriptEngineManager.java | 296 ------------------- .../table/internal/osgi/OSGiScriptEngine.java | 95 ++++++ .../internal/osgi/OSGiScriptEngineFactory.java | 98 ++++++ .../internal/osgi/OSGiScriptEngineManager.java | 296 +++++++++++++++++++ 6 files changed, 489 insertions(+), 489 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/jclouds-karaf/blob/4014f4cb/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngine.java ---------------------------------------------------------------------- diff --git a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngine.java b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngine.java deleted file mode 100644 index d3415c3..0000000 --- a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngine.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2005 The Apache Software Foundation - * - * Licensed 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.jclouds.karaf.commands.table.internal; - -import java.io.Reader; - -import javax.script.Bindings; -import javax.script.ScriptContext; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineFactory; -import javax.script.ScriptException; - -// modified version of http://svn.apache.org/repos/asf/felix/trunk/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngine.java -public class OSGiScriptEngine implements ScriptEngine { - private ScriptEngine engine; - private OSGiScriptEngineFactory factory; - - public OSGiScriptEngine(ScriptEngine engine, OSGiScriptEngineFactory factory) { - this.engine = engine; - this.factory = factory; - } - - public Bindings createBindings() { - return engine.createBindings(); - } - - public Object eval(Reader reader, Bindings n) throws ScriptException { - return engine.eval(reader, n); - } - - public Object eval(Reader reader, ScriptContext context) - throws ScriptException { - return engine.eval(reader, context); - } - - public Object eval(Reader reader) throws ScriptException { - return engine.eval(reader); - } - - public Object eval(String script, Bindings n) throws ScriptException { - return engine.eval(script, n); - } - - public Object eval(String script, ScriptContext context) - throws ScriptException { - return engine.eval(script, context); - } - - public Object eval(String script) throws ScriptException { - return engine.eval(script); - } - - public Object get(String key) { - return engine.get(key); - } - - public Bindings getBindings(int scope) { - return engine.getBindings(scope); - } - - public ScriptContext getContext() { - return engine.getContext(); - } - - public ScriptEngineFactory getFactory() { - return factory; - } - - public void put(String key, Object value) { - engine.put(key, value); - } - - public void setBindings(Bindings bindings, int scope) { - engine.setBindings(bindings, scope); - } - - public void setContext(ScriptContext context) { - engine.setContext(context); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jclouds-karaf/blob/4014f4cb/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngineFactory.java ---------------------------------------------------------------------- diff --git a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngineFactory.java b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngineFactory.java deleted file mode 100644 index 1a8d318..0000000 --- a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngineFactory.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2005 The Apache Software Foundation - * - * Licensed 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.jclouds.karaf.commands.table.internal; - -import java.util.List; - -import javax.script.ScriptEngine; -import javax.script.ScriptEngineFactory; - -/** - * This is a wrapper class for the ScriptEngineFactory class that deals with - * context class loader issues It is necessary because engines (at least ruby) - * use the context classloader to find their resources (i.e., their "native" - * classes) - */ - -// modified version of http://svn.apache.org/repos/asf/felix/trunk/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineFactory.java -public class OSGiScriptEngineFactory implements ScriptEngineFactory { - private ScriptEngineFactory factory; - private ClassLoader contextClassLoader; - - public OSGiScriptEngineFactory(ScriptEngineFactory factory, - ClassLoader contextClassLoader) { - this.factory = factory; - this.contextClassLoader = contextClassLoader; - } - - public String getEngineName() { - return factory.getEngineName(); - } - - public String getEngineVersion() { - return factory.getEngineVersion(); - } - - public List<String> getExtensions() { - return factory.getExtensions(); - } - - public String getLanguageName() { - return factory.getLanguageName(); - } - - public String getLanguageVersion() { - return factory.getLanguageVersion(); - } - - public String getMethodCallSyntax(String obj, String m, String... args) { - return factory.getMethodCallSyntax(obj, m, args); - } - - public List<String> getMimeTypes() { - return factory.getMimeTypes(); - } - - public List<String> getNames() { - return factory.getNames(); - } - - public String getOutputStatement(String toDisplay) { - return factory.getOutputStatement(toDisplay); - } - - public Object getParameter(String key) { - return factory.getParameter(key); - } - - public String getProgram(String... statements) { - return factory.getProgram(statements); - } - - public ScriptEngine getScriptEngine() { - ScriptEngine engine = null; - if (contextClassLoader != null) { - ClassLoader old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread().setContextClassLoader(contextClassLoader); - engine = factory.getScriptEngine(); - Thread.currentThread().setContextClassLoader(old); - } else - engine = factory.getScriptEngine(); - return engine; - } - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jclouds-karaf/blob/4014f4cb/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngineManager.java ---------------------------------------------------------------------- diff --git a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngineManager.java b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngineManager.java deleted file mode 100644 index 95668ae..0000000 --- a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/OSGiScriptEngineManager.java +++ /dev/null @@ -1,296 +0,0 @@ -/* - * Copyright 2005 The Apache Software Foundation - * - * Licensed 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.jclouds.karaf.commands.table.internal; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import javax.script.Bindings; -import javax.script.ScriptEngine; -import javax.script.ScriptEngineFactory; -import javax.script.ScriptEngineManager; -import javax.script.SimpleBindings; - -import org.osgi.framework.Bundle; -import org.osgi.framework.BundleContext; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class acts as a delegate for all the available ScriptEngineManagers. - * Unluckily, the standard did not define it as an interface, so we need to - * extend it to allow polymorphism. However, no calls to super are used. It - * wraps all available ScriptEngineManagers in the OSGi ServicePlatform into a - * merged ScriptEngineManager. - * - * Internally, what this class does is creating ScriptEngineManagers for each - * bundle that contains a ScriptEngineFactory and includes a - * META-INF/services/javax.script.ScriptEngineFactory file. It assumes that the - * file contains a list of @link ScriptEngineFactory classes. For each bundle, - * it creates a ScriptEngineManager, then merges them. @link ScriptEngineFactory - * objects are wrapped into @link OSGiScriptEngineFactory objects to deal with - * problems of context class loader: Those scripting engines that rely on the - * ContextClassloader for finding resources need to use this wrapper and the @link - * OSGiScriptFactory. Mainly, jruby does. - * - * Note that even if no context classloader issues arose, it would still be - * needed to search manually for the factories and either use them directly - * (losing the mimeType/extension/shortName mechanisms for finding engines or - * manually registering them) or still use this class, which would be smarter. - * In the latter case, it would only be needed to remove the hack that - * temporarily sets the context classloader to the appropriate, bundle-related, - * class loader. - * - * Caveats: - * <ul> - * <li> - * All factories are wrapped with an {@link OSGiScriptEngineFactory}. As Engines - * are not wrapped, calls like <code> - * ScriptEngineManager osgiManager=new OSGiScriptEngineManager(context);<br> - * ScriptEngine engine=osgiManager.getEngineByName("ruby"); - * ScriptEngineFactory factory=engine.getFactory() //this does not return the OSGiFactory wrapper - * factory.getScriptEngine(); //this might fail, as it does not use OSGiScriptEngineFactory wrapper - * </code> - * might result in unexpected errors. Future versions may wrap the ScriptEngine - * with a OSGiScriptEngine to solve this issue, but for the moment it is not - * needed.</li> - */ - -// modified version of http://svn.apache.org/repos/asf/felix/trunk/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineManager.java -public class OSGiScriptEngineManager extends ScriptEngineManager { - private static final Logger LOGGER = LoggerFactory.getLogger(OSGiScriptEngineManager.class); - - private Bindings bindings; - private Map<ScriptEngineManager, ClassLoader> classLoaders; - private BundleContext context; - - public OSGiScriptEngineManager(BundleContext context) { - this.context = context; - bindings = new SimpleBindings(); - this.classLoaders = findManagers(context); - } - - /** - * This method is the only one that is visible and not part of the - * ScriptEngineManager class. Its purpose is to find new managers that - * weren't available before, but keeping the globalScope bindings set. If you - * want to clean the bindings you can either get a fresh instance of - * OSGiScriptManager or setting up a new bindings object. This can be done - * with: <code> - * ScriptEngineManager manager=new OSGiScriptEngineManager(context); - * (...) //do stuff - * osgiManager=(OSGiScriptEngineManager)manager; //cast to ease reading - * osgiManager.reloadManagers(); - * - * manager.setBindings(new OSGiBindings()); //or you can use your own bindings implementation - * </code> - */ - public void reloadManagers() { - this.classLoaders = findManagers(context); - } - - public Object get(String key) { - return bindings.get(key); - } - - public Bindings getBindings() { - return bindings; - } - - public ScriptEngine getEngineByExtension(String extension) { - // TODO this is a hack to deal with context class loader issues - ScriptEngine engine = null; - for (ScriptEngineManager manager : classLoaders.keySet()) { - ClassLoader old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread() - .setContextClassLoader(classLoaders.get(manager)); - engine = manager.getEngineByExtension(extension); - Thread.currentThread().setContextClassLoader(old); - if (engine != null) - break; - } - return engine; - } - - public ScriptEngine getEngineByMimeType(String mimeType) { - // TODO this is a hack to deal with context class loader issues - ScriptEngine engine = null; - for (ScriptEngineManager manager : classLoaders.keySet()) { - ClassLoader old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread() - .setContextClassLoader(classLoaders.get(manager)); - engine = manager.getEngineByMimeType(mimeType); - Thread.currentThread().setContextClassLoader(old); - if (engine != null) - break; - } - return engine; - } - - public ScriptEngine getEngineByName(String shortName) { - // TODO this is a hack to deal with context class loader issues - for (ScriptEngineManager manager : classLoaders.keySet()) { - ClassLoader old = Thread.currentThread().getContextClassLoader(); - Thread.currentThread() - .setContextClassLoader(classLoaders.get(manager)); - ScriptEngine engine = manager.getEngineByName(shortName); - Thread.currentThread().setContextClassLoader(old); - if (engine != null) { - return new OSGiScriptEngine(engine, new OSGiScriptEngineFactory( - engine.getFactory(), classLoaders.get(manager))); - } - } - return null; - } - - public List<ScriptEngineFactory> getEngineFactories() { - List<ScriptEngineFactory> osgiFactories = new ArrayList<ScriptEngineFactory>(); - for (ScriptEngineManager engineManager : classLoaders.keySet()) { - for (ScriptEngineFactory factory : engineManager.getEngineFactories()) { - osgiFactories.add(new OSGiScriptEngineFactory(factory, classLoaders - .get(engineManager))); - } - } - return osgiFactories; - } - - public void put(String key, Object value) { - bindings.put(key, value); - } - - public void registerEngineExtension(String extension, - ScriptEngineFactory factory) { - for (ScriptEngineManager engineManager : classLoaders.keySet()) - engineManager.registerEngineExtension(extension, factory); - } - - public void registerEngineMimeType(String type, ScriptEngineFactory factory) { - for (ScriptEngineManager engineManager : classLoaders.keySet()) - engineManager.registerEngineMimeType(type, factory); - } - - public void registerEngineName(String name, ScriptEngineFactory factory) { - for (ScriptEngineManager engineManager : classLoaders.keySet()) - engineManager.registerEngineName(name, factory); - } - - /** - * Follows the same behavior of @link - * javax.script.ScriptEngineManager#setBindings(Bindings) This means that the - * same bindings are applied to all the underlying managers. - * - * @param bindings - */ - public void setBindings(Bindings bindings) { - this.bindings = bindings; - for (ScriptEngineManager manager : classLoaders.keySet()) { - manager.setBindings(bindings); - } - } - - private Map<ScriptEngineManager, ClassLoader> findManagers( - BundleContext context) { - Map<ScriptEngineManager, ClassLoader> managers = new HashMap<ScriptEngineManager, ClassLoader>(); - for (String factoryName : findFactoryCandidates(context)) { - // we do not really need the class, but we need the classloader - ClassLoader factoryLoader = tryGetClassLoader(factoryName); - if (factoryLoader == null) { - continue; - } - ScriptEngineManager manager = tryCreateScriptEngineManager( - factoryName, factoryLoader); - if (manager != null) { - manager.setBindings(bindings); - managers.put(manager, factoryLoader); - } - } - return managers; - } - - private static ClassLoader tryGetClassLoader(String className) { - try { - return Class.forName(className).getClassLoader(); - } catch (ClassNotFoundException exception) { - LOGGER.warn("Tried to load class {} but failed due to: {}", - className, exception.getMessage()); - return null; - } - } - - private static ScriptEngineManager tryCreateScriptEngineManager( - String factoryName, ClassLoader factoryLoader) { - try { - return new ScriptEngineManager(factoryLoader); - } catch (Exception exception) { - LOGGER.warn("Found ScriptEngineFactory candidate {} but failed to create a factory due to: {}", - factoryName, exception.getMessage()); - return null; - } - } - - /** - * Iterates through all bundles to get the available @link - * ScriptEngineFactory classes - * - * @return the names of the available ScriptEngineFactory classes - * @throws IOException - */ - private List<String> findFactoryCandidates(BundleContext context) { - Bundle[] bundles = context.getBundles(); - List<String> factoryCandidates = new ArrayList<String>(); - for (Bundle bundle : bundles) { - if (bundle == null) { - continue; - } - - if (bundle.getSymbolicName().equals("system.bundle")) { - continue; - } - Enumeration<URL> urls = bundle.findEntries("META-INF/services", - "javax.script.ScriptEngineFactory", false); - if (urls == null) { - continue; - } - - while (urls.hasMoreElements()) { - URL u = (URL) urls.nextElement(); - try { - BufferedReader reader = new BufferedReader( - new InputStreamReader(u.openStream())); - String line; - while ((line = reader.readLine()) != null) { - line = line.trim(); - // ignore comments and empty lines - if (!line.isEmpty() && !line.startsWith("#")) { - factoryCandidates.add(line); - } - } - } catch (IOException ignored) { - } - } - } - return factoryCandidates; - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jclouds-karaf/blob/4014f4cb/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngine.java ---------------------------------------------------------------------- diff --git a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngine.java b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngine.java new file mode 100644 index 0000000..8f7f7a2 --- /dev/null +++ b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngine.java @@ -0,0 +1,95 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed 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.jclouds.karaf.commands.table.internal.osgi; + +import java.io.Reader; + +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptException; + +// modified version of http://svn.apache.org/repos/asf/felix/trunk/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngine.java +public class OSGiScriptEngine implements ScriptEngine { + private ScriptEngine engine; + private OSGiScriptEngineFactory factory; + + public OSGiScriptEngine(ScriptEngine engine, OSGiScriptEngineFactory factory) { + this.engine = engine; + this.factory = factory; + } + + public Bindings createBindings() { + return engine.createBindings(); + } + + public Object eval(Reader reader, Bindings n) throws ScriptException { + return engine.eval(reader, n); + } + + public Object eval(Reader reader, ScriptContext context) + throws ScriptException { + return engine.eval(reader, context); + } + + public Object eval(Reader reader) throws ScriptException { + return engine.eval(reader); + } + + public Object eval(String script, Bindings n) throws ScriptException { + return engine.eval(script, n); + } + + public Object eval(String script, ScriptContext context) + throws ScriptException { + return engine.eval(script, context); + } + + public Object eval(String script) throws ScriptException { + return engine.eval(script); + } + + public Object get(String key) { + return engine.get(key); + } + + public Bindings getBindings(int scope) { + return engine.getBindings(scope); + } + + public ScriptContext getContext() { + return engine.getContext(); + } + + public ScriptEngineFactory getFactory() { + return factory; + } + + public void put(String key, Object value) { + engine.put(key, value); + } + + public void setBindings(Bindings bindings, int scope) { + engine.setBindings(bindings, scope); + } + + public void setContext(ScriptContext context) { + engine.setContext(context); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jclouds-karaf/blob/4014f4cb/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngineFactory.java ---------------------------------------------------------------------- diff --git a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngineFactory.java b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngineFactory.java new file mode 100644 index 0000000..08d80c4 --- /dev/null +++ b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngineFactory.java @@ -0,0 +1,98 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed 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.jclouds.karaf.commands.table.internal.osgi; + +import java.util.List; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; + +/** + * This is a wrapper class for the ScriptEngineFactory class that deals with + * context class loader issues It is necessary because engines (at least ruby) + * use the context classloader to find their resources (i.e., their "native" + * classes) + */ + +// modified version of http://svn.apache.org/repos/asf/felix/trunk/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineFactory.java +public class OSGiScriptEngineFactory implements ScriptEngineFactory { + private ScriptEngineFactory factory; + private ClassLoader contextClassLoader; + + public OSGiScriptEngineFactory(ScriptEngineFactory factory, + ClassLoader contextClassLoader) { + this.factory = factory; + this.contextClassLoader = contextClassLoader; + } + + public String getEngineName() { + return factory.getEngineName(); + } + + public String getEngineVersion() { + return factory.getEngineVersion(); + } + + public List<String> getExtensions() { + return factory.getExtensions(); + } + + public String getLanguageName() { + return factory.getLanguageName(); + } + + public String getLanguageVersion() { + return factory.getLanguageVersion(); + } + + public String getMethodCallSyntax(String obj, String m, String... args) { + return factory.getMethodCallSyntax(obj, m, args); + } + + public List<String> getMimeTypes() { + return factory.getMimeTypes(); + } + + public List<String> getNames() { + return factory.getNames(); + } + + public String getOutputStatement(String toDisplay) { + return factory.getOutputStatement(toDisplay); + } + + public Object getParameter(String key) { + return factory.getParameter(key); + } + + public String getProgram(String... statements) { + return factory.getProgram(statements); + } + + public ScriptEngine getScriptEngine() { + ScriptEngine engine = null; + if (contextClassLoader != null) { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(contextClassLoader); + engine = factory.getScriptEngine(); + Thread.currentThread().setContextClassLoader(old); + } else + engine = factory.getScriptEngine(); + return engine; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/jclouds-karaf/blob/4014f4cb/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngineManager.java ---------------------------------------------------------------------- diff --git a/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngineManager.java b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngineManager.java new file mode 100644 index 0000000..0190ecd --- /dev/null +++ b/commands/src/main/java/org/jclouds/karaf/commands/table/internal/osgi/OSGiScriptEngineManager.java @@ -0,0 +1,296 @@ +/* + * Copyright 2005 The Apache Software Foundation + * + * Licensed 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.jclouds.karaf.commands.table.internal.osgi; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.script.Bindings; +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import javax.script.ScriptEngineManager; +import javax.script.SimpleBindings; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class acts as a delegate for all the available ScriptEngineManagers. + * Unluckily, the standard did not define it as an interface, so we need to + * extend it to allow polymorphism. However, no calls to super are used. It + * wraps all available ScriptEngineManagers in the OSGi ServicePlatform into a + * merged ScriptEngineManager. + * + * Internally, what this class does is creating ScriptEngineManagers for each + * bundle that contains a ScriptEngineFactory and includes a + * META-INF/services/javax.script.ScriptEngineFactory file. It assumes that the + * file contains a list of @link ScriptEngineFactory classes. For each bundle, + * it creates a ScriptEngineManager, then merges them. @link ScriptEngineFactory + * objects are wrapped into @link OSGiScriptEngineFactory objects to deal with + * problems of context class loader: Those scripting engines that rely on the + * ContextClassloader for finding resources need to use this wrapper and the @link + * OSGiScriptFactory. Mainly, jruby does. + * + * Note that even if no context classloader issues arose, it would still be + * needed to search manually for the factories and either use them directly + * (losing the mimeType/extension/shortName mechanisms for finding engines or + * manually registering them) or still use this class, which would be smarter. + * In the latter case, it would only be needed to remove the hack that + * temporarily sets the context classloader to the appropriate, bundle-related, + * class loader. + * + * Caveats: + * <ul> + * <li> + * All factories are wrapped with an {@link OSGiScriptEngineFactory}. As Engines + * are not wrapped, calls like <code> + * ScriptEngineManager osgiManager=new OSGiScriptEngineManager(context);<br> + * ScriptEngine engine=osgiManager.getEngineByName("ruby"); + * ScriptEngineFactory factory=engine.getFactory() //this does not return the OSGiFactory wrapper + * factory.getScriptEngine(); //this might fail, as it does not use OSGiScriptEngineFactory wrapper + * </code> + * might result in unexpected errors. Future versions may wrap the ScriptEngine + * with a OSGiScriptEngine to solve this issue, but for the moment it is not + * needed.</li> + */ + +// modified version of http://svn.apache.org/repos/asf/felix/trunk/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineManager.java +public class OSGiScriptEngineManager extends ScriptEngineManager { + private static final Logger LOGGER = LoggerFactory.getLogger(OSGiScriptEngineManager.class); + + private Bindings bindings; + private Map<ScriptEngineManager, ClassLoader> classLoaders; + private BundleContext context; + + public OSGiScriptEngineManager(BundleContext context) { + this.context = context; + bindings = new SimpleBindings(); + this.classLoaders = findManagers(context); + } + + /** + * This method is the only one that is visible and not part of the + * ScriptEngineManager class. Its purpose is to find new managers that + * weren't available before, but keeping the globalScope bindings set. If you + * want to clean the bindings you can either get a fresh instance of + * OSGiScriptManager or setting up a new bindings object. This can be done + * with: <code> + * ScriptEngineManager manager=new OSGiScriptEngineManager(context); + * (...) //do stuff + * osgiManager=(OSGiScriptEngineManager)manager; //cast to ease reading + * osgiManager.reloadManagers(); + * + * manager.setBindings(new OSGiBindings()); //or you can use your own bindings implementation + * </code> + */ + public void reloadManagers() { + this.classLoaders = findManagers(context); + } + + public Object get(String key) { + return bindings.get(key); + } + + public Bindings getBindings() { + return bindings; + } + + public ScriptEngine getEngineByExtension(String extension) { + // TODO this is a hack to deal with context class loader issues + ScriptEngine engine = null; + for (ScriptEngineManager manager : classLoaders.keySet()) { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread() + .setContextClassLoader(classLoaders.get(manager)); + engine = manager.getEngineByExtension(extension); + Thread.currentThread().setContextClassLoader(old); + if (engine != null) + break; + } + return engine; + } + + public ScriptEngine getEngineByMimeType(String mimeType) { + // TODO this is a hack to deal with context class loader issues + ScriptEngine engine = null; + for (ScriptEngineManager manager : classLoaders.keySet()) { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread() + .setContextClassLoader(classLoaders.get(manager)); + engine = manager.getEngineByMimeType(mimeType); + Thread.currentThread().setContextClassLoader(old); + if (engine != null) + break; + } + return engine; + } + + public ScriptEngine getEngineByName(String shortName) { + // TODO this is a hack to deal with context class loader issues + for (ScriptEngineManager manager : classLoaders.keySet()) { + ClassLoader old = Thread.currentThread().getContextClassLoader(); + Thread.currentThread() + .setContextClassLoader(classLoaders.get(manager)); + ScriptEngine engine = manager.getEngineByName(shortName); + Thread.currentThread().setContextClassLoader(old); + if (engine != null) { + return new OSGiScriptEngine(engine, new OSGiScriptEngineFactory( + engine.getFactory(), classLoaders.get(manager))); + } + } + return null; + } + + public List<ScriptEngineFactory> getEngineFactories() { + List<ScriptEngineFactory> osgiFactories = new ArrayList<ScriptEngineFactory>(); + for (ScriptEngineManager engineManager : classLoaders.keySet()) { + for (ScriptEngineFactory factory : engineManager.getEngineFactories()) { + osgiFactories.add(new OSGiScriptEngineFactory(factory, classLoaders + .get(engineManager))); + } + } + return osgiFactories; + } + + public void put(String key, Object value) { + bindings.put(key, value); + } + + public void registerEngineExtension(String extension, + ScriptEngineFactory factory) { + for (ScriptEngineManager engineManager : classLoaders.keySet()) + engineManager.registerEngineExtension(extension, factory); + } + + public void registerEngineMimeType(String type, ScriptEngineFactory factory) { + for (ScriptEngineManager engineManager : classLoaders.keySet()) + engineManager.registerEngineMimeType(type, factory); + } + + public void registerEngineName(String name, ScriptEngineFactory factory) { + for (ScriptEngineManager engineManager : classLoaders.keySet()) + engineManager.registerEngineName(name, factory); + } + + /** + * Follows the same behavior of @link + * javax.script.ScriptEngineManager#setBindings(Bindings) This means that the + * same bindings are applied to all the underlying managers. + * + * @param bindings + */ + public void setBindings(Bindings bindings) { + this.bindings = bindings; + for (ScriptEngineManager manager : classLoaders.keySet()) { + manager.setBindings(bindings); + } + } + + private Map<ScriptEngineManager, ClassLoader> findManagers( + BundleContext context) { + Map<ScriptEngineManager, ClassLoader> managers = new HashMap<ScriptEngineManager, ClassLoader>(); + for (String factoryName : findFactoryCandidates(context)) { + // we do not really need the class, but we need the classloader + ClassLoader factoryLoader = tryGetClassLoader(factoryName); + if (factoryLoader == null) { + continue; + } + ScriptEngineManager manager = tryCreateScriptEngineManager( + factoryName, factoryLoader); + if (manager != null) { + manager.setBindings(bindings); + managers.put(manager, factoryLoader); + } + } + return managers; + } + + private static ClassLoader tryGetClassLoader(String className) { + try { + return Class.forName(className).getClassLoader(); + } catch (ClassNotFoundException exception) { + LOGGER.error("Tried to load class {} but failed due to: {}", + className, exception.getMessage()); + return null; + } + } + + private static ScriptEngineManager tryCreateScriptEngineManager( + String factoryName, ClassLoader factoryLoader) { + try { + return new ScriptEngineManager(factoryLoader); + } catch (Exception exception) { + LOGGER.error("Found ScriptEngineFactory candidate {} but failed to create a factory due to: {}", + factoryName, exception.getMessage()); + return null; + } + } + + /** + * Iterates through all bundles to get the available @link + * ScriptEngineFactory classes + * + * @return the names of the available ScriptEngineFactory classes + * @throws IOException + */ + private List<String> findFactoryCandidates(BundleContext context) { + Bundle[] bundles = context.getBundles(); + List<String> factoryCandidates = new ArrayList<String>(); + for (Bundle bundle : bundles) { + if (bundle == null) { + continue; + } + + if (bundle.getSymbolicName().equals("system.bundle")) { + continue; + } + Enumeration<URL> urls = bundle.findEntries("META-INF/services", + "javax.script.ScriptEngineFactory", false); + if (urls == null) { + continue; + } + + while (urls.hasMoreElements()) { + URL u = (URL) urls.nextElement(); + try { + BufferedReader reader = new BufferedReader( + new InputStreamReader(u.openStream())); + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + // ignore comments and empty lines + if (!line.isEmpty() && !line.startsWith("#")) { + factoryCandidates.add(line); + } + } + } catch (IOException ignored) { + } + } + } + return factoryCandidates; + } +} \ No newline at end of file
