Some refactoring to decouple shell from engine logic and to wrap the javax.script classes so that they work in OSGi
git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@450629 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/mishell/src/main/java/org/apache/felix/mishell/Activator.java b/mishell/src/main/java/org/apache/felix/mishell/Activator.java
index a879a69..1376f9a 100644
--- a/mishell/src/main/java/org/apache/felix/mishell/Activator.java
+++ b/mishell/src/main/java/org/apache/felix/mishell/Activator.java
@@ -1,5 +1,22 @@
+/*
+ * 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.apache.felix.mishell;
+import org.apache.felix.mishell.console.Console;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
@@ -8,16 +25,17 @@
private Console console;
public void start(BundleContext context) throws Exception {
-
//NOTE: new javax.script.EngineManager() uses context class loader
//We've changed that to use EngineManager(ClassLoader loader)
//Alternatively, we could instantiate it in a separate thread with proper context class loader set.
- console = new Console("javascript", null);
+ //TODO: not sure wether resources are correctly freed. A running script will probably keep running, for example, if it has got threads
+ //but that would hang the console, which is quite primitive yet. (it would need a 'run [script] &' idiom)
+ console = new Console(new JMXEngineContext("javascript", new OSGiScriptEngineManager(context)));
Thread t=new Thread(console);
//At least JRuby engine (maybe others too) uses context class loaders internally
//So this is to prevent problems with that: context cl=console class loader which in turn
//loads the manager and therefore engine factory
- t.setContextClassLoader(this.getClass().getClassLoader());
+// t.setContextClassLoader(this.getClass().getClassLoader());
t.setName("ConsoleThread");
t.start();
diff --git a/mishell/src/main/java/org/apache/felix/mishell/Command.java b/mishell/src/main/java/org/apache/felix/mishell/Command.java
deleted file mode 100644
index 648428e..0000000
--- a/mishell/src/main/java/org/apache/felix/mishell/Command.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package org.apache.felix.mishell;
-
-import java.io.PrintStream;
-
-public interface Command {
- public void executeCommand(String cmd, PrintStream out) throws Exception;
- public String getName();
- public String getHelp();
-}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/CommandNotFoundException.java b/mishell/src/main/java/org/apache/felix/mishell/CommandNotFoundException.java
deleted file mode 100644
index 9da9126..0000000
--- a/mishell/src/main/java/org/apache/felix/mishell/CommandNotFoundException.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package org.apache.felix.mishell;
-
-public class CommandNotFoundException extends Exception {
-
-}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/Commander.java b/mishell/src/main/java/org/apache/felix/mishell/Commander.java
deleted file mode 100644
index 5be7cb3..0000000
--- a/mishell/src/main/java/org/apache/felix/mishell/Commander.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.apache.felix.mishell;
-
-import java.io.PrintStream;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.Map.Entry;
-import java.util.logging.Logger;
-
-import javax.script.ScriptContext;
-
-
-public class Commander extends HashSet<Command> implements Command{
- private Logger log=Logger.getLogger(this.getClass().getName());
- public void executeCommand(String cmd, PrintStream out) throws Exception {
- String[] parsedCmd=cmd.split(" ");
- if (parsedCmd.length==0)throw new CommandNotFoundException();
- for (Command c: this) {
- if (c.getName().equals(parsedCmd[0])){
- log.finest("executing: "+c.getName());
- c.executeCommand(cmd, out);
- return;
- }
- }
- throw new CommandNotFoundException();
-
- }
- public String getName() {
- return "commander";
- }
- public Commander() {
- }
- public String getHelp() {
- return "mishell commander";
- }
-}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/EngineNotFoundException.java b/mishell/src/main/java/org/apache/felix/mishell/EngineNotFoundException.java
index 3e8b05b..a470916 100644
--- a/mishell/src/main/java/org/apache/felix/mishell/EngineNotFoundException.java
+++ b/mishell/src/main/java/org/apache/felix/mishell/EngineNotFoundException.java
@@ -1,3 +1,19 @@
+/*
+ * 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.apache.felix.mishell;
public class EngineNotFoundException extends Exception {
diff --git a/mishell/src/main/java/org/apache/felix/mishell/JMXEngineContext.java b/mishell/src/main/java/org/apache/felix/mishell/JMXEngineContext.java
index 462f7c4..4036a37 100644
--- a/mishell/src/main/java/org/apache/felix/mishell/JMXEngineContext.java
+++ b/mishell/src/main/java/org/apache/felix/mishell/JMXEngineContext.java
@@ -1,54 +1,71 @@
+/*
+ * 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.apache.felix.mishell;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.MalformedURLException;
-import java.util.ArrayList;
-import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.management.InstanceNotFoundException;
-import javax.management.MBeanServerConnection;
-import javax.management.MBeanServerInvocationHandler;
-import javax.management.MalformedObjectNameException;
-import javax.management.Notification;
-import javax.management.NotificationListener;
-import javax.management.ObjectName;
-import javax.management.remote.JMXConnector;
-import javax.management.remote.JMXServiceURL;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
-import javax.script.ScriptEngineManager;
-import org.apache.felix.jmxintrospector.MBeanProxyManager;
-
-//import org.apache.felix.jmood.core.CoreControllerMBean;
-//import org.apache.felix.jmood.core.FrameworkMBean;
-//import org.apache.felix.jmood.utils.ObjectNames;
-
+/**
+ * This class is in charge of most of the interesting stuff. Basically, it keeps the current engine instance being used
+ * and creates the OSGiScriptEngineManager 'decorator' that handles the fact that engines can be installed in
+ * separate bundles and then the mechanism for finding engines used by the javax.script API does not work.
+ *
+ */
public class JMXEngineContext {
private ScriptEngine engine;
- private ScriptEngineManager engineManager;
-
+ private OSGiScriptEngineManager engineManager;
+ private String language;
Logger log=Logger.getLogger(JMXEngineContext.class.getName());
Level l=Level.INFO;
-
- public JMXEngineContext(String language)throws EngineNotFoundException{
- engineManager=new ScriptEngineManager(this.getClass().getClassLoader());
- log.log(l, "Available script engines are:");
- for (ScriptEngineFactory sef : engineManager.getEngineFactories()) {
- log.log(l, sef.getEngineName());
- }
- engine=engineManager.getEngineByName(language);
- if (engine==null) throw new EngineNotFoundException(language);
- JMoodProxyManager manager=new JMoodProxyManager();
- String managerName=getVarName("manager");
- engine.put(managerName, manager);
+ public JMXEngineContext(String language) throws EngineNotFoundException{
+ this(language, null);
}
+ public JMXEngineContext(String lang, OSGiScriptEngineManager engineManager)throws EngineNotFoundException{
+ this.engineManager=engineManager;
+ if (log.isLoggable(l)){
+ StringBuffer msg=new StringBuffer("Available script engines are:");
+ log.log(l, "Available script engines are: \n");
+ for (ScriptEngineFactory sef : engineManager.getEngineFactories()) {
+ msg.append(sef.getEngineName()+"\n");
+ }
+ log.log(l, msg.toString());
+ }
+ JMoodProxyManager manager=new JMoodProxyManager();
+ engine=engineManager.getEngineByName(lang);
+ this.language=engine.getFactory().getLanguageName();
+ String managerName=getVarName("manager");
+ engineManager.put(managerName, manager);
+ for(String key: engineManager.getBindings().keySet()){
+ engine.put(getVarName(key), engineManager.get(key));
+ }
+
+ if (engine==null) throw new EngineNotFoundException(language);
+ }
+ /**
+ * This method is used because prior versions of jrbuy binding needed global variables
+ * to be called '$'+name. Last version already do that automatically, so this is not needed anymore
+ * @param name
+ * @return
+ */
+ //TODO: remove this method when we update to latest version of jruby-engine
private String getVarName(String name){
if (engine.getFactory().getEngineName().equals("jruby")) name="$"+name;
return name;
@@ -60,14 +77,37 @@
public void setEngine(ScriptEngine engine) {
this.engine = engine;
}
- public ScriptEngineManager getEngineManager() {
+ public OSGiScriptEngineManager getEngineManager() {
return engineManager;
}
- public void setEngineManager(ScriptEngineManager engineManager) {
+ public void setEngineManager(OSGiScriptEngineManager engineManager) {
this.engineManager = engineManager;
}
+ /**
+ * This methods changes the engine language. It reloads the ScriptEngineManagers to ensure
+ * that any new engines (for example new bundles or updated ones) are discovered.
+ * @param name
+ * @throws EngineNotFoundException
+ */
+ public void setLanguage(String name) throws EngineNotFoundException{
+ OSGiScriptEngineManager manager=(OSGiScriptEngineManager)engineManager;
+ manager.reloadManagers();
+ ScriptEngine newEngine=manager.getEngineByName(name);
+ if(newEngine!=null) {
+ engine=newEngine;
+ language=engine.getFactory().getLanguageName();
+ for(String key: engineManager.getBindings().keySet()){
+ System.out.println(key);
+ engine.put(getVarName(key), engineManager.get(key));
+ }
+
+ }
+ else throw new EngineNotFoundException("name");
}
-
+ public String getLanguage(){
+ return language;
+ }
+}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/JMoodProxyManager.java b/mishell/src/main/java/org/apache/felix/mishell/JMoodProxyManager.java
index 73a545f..67aa5a2 100644
--- a/mishell/src/main/java/org/apache/felix/mishell/JMoodProxyManager.java
+++ b/mishell/src/main/java/org/apache/felix/mishell/JMoodProxyManager.java
@@ -1,3 +1,21 @@
+/*
+ * 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.felix.mishell;
import java.util.List;
@@ -7,6 +25,12 @@
import org.apache.felix.jmxintrospector.MBean;
import org.apache.felix.jmxintrospector.MBeanProxyManager;
+/**
+ * This class extends the MBeanProxyManager to include methods that are aware that they are connecting
+ * to an MBeanServer that has JMood mbeans. This is the most natural place to add some utility methods
+ * for script engines, since scripts will have a JMoodProxyManager named 'manager' as the entry point.
+ *
+ */
public class JMoodProxyManager extends MBeanProxyManager {
public List<Object> getControllers(){
return findAll("type=controller");
diff --git a/mishell/src/main/java/org/apache/felix/mishell/Main.java b/mishell/src/main/java/org/apache/felix/mishell/Main.java
deleted file mode 100644
index 177278a..0000000
--- a/mishell/src/main/java/org/apache/felix/mishell/Main.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.apache.felix.mishell;
-
-import java.io.InputStream;
-import java.util.logging.Level;
-import java.util.logging.LogManager;
-import java.util.logging.Logger;
-
-public class Main {
-// private static final String log_props="target/classes/logging.properties";
- private static final String log_props="logging.properties";
-
- public static void main(String[] args) throws Exception{
- Logger.getLogger(Console.class.getCanonicalName()).setLevel(Level.FINEST);
- InputStream is=Main.class.getResourceAsStream(log_props);
- LogManager.getLogManager().readConfiguration(is);
- is.close();
- String language=args.length==0?null:args[0];
- String scriptPath=args.length<2?null:args[1];
- Console m = new Console(language, scriptPath);
- Thread t=new Thread(m);
- t.setName("ConsoleThread");
- t.start();
- }
-}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngine.java b/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngine.java
new file mode 100644
index 0000000..127d56d
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngine.java
@@ -0,0 +1,77 @@
+/*
+ * 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.apache.felix.mishell;
+
+import java.io.Reader;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptException;
+
+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);
+ }
+
+}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineFactory.java b/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineFactory.java
new file mode 100644
index 0000000..843f826
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineFactory.java
@@ -0,0 +1,82 @@
+/*
+ * 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.apache.felix.mishell;
+
+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)
+ *
+ */
+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;
+ }
+
+
+}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineManager.java b/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineManager.java
new file mode 100644
index 0000000..84b3073
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/OSGiScriptEngineManager.java
@@ -0,0 +1,237 @@
+/*
+ * 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.apache.felix.mishell;
+
+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;
+
+/**
+ * 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>
+ *
+ */
+public class OSGiScriptEngineManager extends ScriptEngineManager{
+ 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>();
+ try {
+ for(String factoryName: findFactoryCandidates(context)){
+ //We do not really need the class, but we need the classloader
+ ClassLoader factoryLoader=Class.forName(factoryName).getClassLoader();
+ ScriptEngineManager manager=new ScriptEngineManager(factoryLoader);
+ manager.setBindings(bindings);
+ managers.put(manager, factoryLoader);
+ }
+ return managers;
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException(cnfe);
+ }
+ }
+ /**
+ * 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) throws IOException{
+ Bundle[] bundles = context.getBundles();
+ List<String> factoryCandidates = new ArrayList<String>();
+ for (Bundle bundle : bundles) {
+ System.out.println(bundle.getSymbolicName());
+ if(bundle.getSymbolicName().equals("system.bundle")) continue;
+ Enumeration urls = bundle.findEntries("META-INF/services",
+ "javax.script.ScriptEngineFactory", false);
+ if (urls == null)
+ continue;
+ while (urls.hasMoreElements()) {
+ URL u = (URL) urls.nextElement();
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(u.openStream()));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ factoryCandidates.add(line.trim());
+ }
+ }
+ }
+ return factoryCandidates;
+ }
+}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/console/Command.java b/mishell/src/main/java/org/apache/felix/mishell/console/Command.java
new file mode 100644
index 0000000..4e78a2f
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/console/Command.java
@@ -0,0 +1,25 @@
+/*
+ * 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.apache.felix.mishell.console;
+
+import java.io.PrintStream;
+
+public interface Command {
+ public void executeCommand(String cmd, PrintStream out) throws Exception;
+ public String getName();
+ public String getHelp();
+}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/console/CommandNotFoundException.java b/mishell/src/main/java/org/apache/felix/mishell/console/CommandNotFoundException.java
new file mode 100644
index 0000000..6e1ec23
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/console/CommandNotFoundException.java
@@ -0,0 +1,21 @@
+/*
+ * 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.apache.felix.mishell.console;
+
+public class CommandNotFoundException extends Exception {
+
+}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/console/Commander.java b/mishell/src/main/java/org/apache/felix/mishell/console/Commander.java
new file mode 100644
index 0000000..79fa215
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/console/Commander.java
@@ -0,0 +1,52 @@
+/*
+ * 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.apache.felix.mishell.console;
+
+import java.io.PrintStream;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.logging.Logger;
+
+import javax.script.ScriptContext;
+
+
+
+public class Commander extends HashSet<Command> implements Command{
+ private Logger log=Logger.getLogger(this.getClass().getName());
+ public void executeCommand(String cmd, PrintStream out) throws Exception {
+ String[] parsedCmd=cmd.split(" ");
+ if (parsedCmd.length==0)throw new CommandNotFoundException();
+ for (Command c: this) {
+ if (c.getName().equals(parsedCmd[0])){
+ log.finest("executing: "+c.getName());
+ c.executeCommand(cmd, out);
+ return;
+ }
+ }
+ throw new CommandNotFoundException();
+
+ }
+ public String getName() {
+ return "commander";
+ }
+ public Commander() {
+ }
+ public String getHelp() {
+ return "mishell commander";
+ }
+}
diff --git a/mishell/src/main/java/org/apache/felix/mishell/Console.java b/mishell/src/main/java/org/apache/felix/mishell/console/Console.java
similarity index 65%
rename from mishell/src/main/java/org/apache/felix/mishell/Console.java
rename to mishell/src/main/java/org/apache/felix/mishell/console/Console.java
index 01d9665..72c48f0 100644
--- a/mishell/src/main/java/org/apache/felix/mishell/Console.java
+++ b/mishell/src/main/java/org/apache/felix/mishell/console/Console.java
@@ -1,4 +1,20 @@
-package org.apache.felix.mishell;
+/*
+ * 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.apache.felix.mishell.console;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
@@ -18,12 +34,13 @@
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
-import jline.ConsoleReader;
-import jline.ConsoleReaderInputStream;
+import org.apache.felix.mishell.EngineNotFoundException;
+import org.apache.felix.mishell.JMXEngineContext;
+
public class Console implements Runnable{
public static final String DEFAULT_LANGUAGE = "javascript";
-Logger log = Logger.getLogger(this.getClass().getCanonicalName());
+ Logger log = Logger.getLogger(this.getClass().getCanonicalName());
Level l=Level.FINEST;
private String language=DEFAULT_LANGUAGE;
private String prompt;
@@ -32,70 +49,41 @@
private Commander commander;
private boolean stop = false;
private JMXEngineContext engineContext;
- private String scriptPath;
-
- public Console(String language, String scriptPath) throws IOException{
- if (language != null) this.language=language;
- prompt="mishell."+language+"$ ";
- /*
- * Not used for the moment. It does not work inside Eclipse, and presents
- problems from the command line
- */
- //useJline();
+ public Console(JMXEngineContext engineContext) throws IOException{
+ this.engineContext=engineContext;
+ prompt="mishell."+engineContext.getLanguage()+"$ ";
in=new BufferedReader(new InputStreamReader(System.in));
out=System.out;
commander= new Commander();
- this.scriptPath=scriptPath;
addBuiltInCmds();
stop=false;
}
- private void useJline() throws IOException{
- ConsoleReader cr=new ConsoleReader();
- ConsoleReaderInputStream.setIn(cr);
-
- }
- private void initLanguage() throws Exception{
- initLanguage(null);
- }
- private void initLanguage(String name) throws Exception{
- if(name!=null) {
- engineContext = new JMXEngineContext(name);
- }
- else {
- engineContext=new JMXEngineContext(language);
- }
+ private void setLanguage(String name)throws EngineNotFoundException{
+ engineContext.setLanguage(name);
language=engineContext.getEngine().getFactory().getLanguageName();
prompt="mishell."+language+"$ ";
+
}
public void run() {
try {
- initLanguage();
- if (scriptPath==null)
- runConsole();
- else engineContext.getEngine().eval(new FileReader(scriptPath));
+ out.println("Welcome to Apache Mishell!!");
+ out.println("For getting help type 'help' ");
+ out.print(prompt);
+ while (!stop) {
+ try {
+ String cmd = in.readLine();
+ executeCommand(cmd);
+ out.print(prompt);
+ out.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
} catch (Exception e) {
e.printStackTrace();
}
}
-
- private void runConsole() throws Exception {
- out.println("Welcome to Apache Mishell!!");
- out.println("For getting help type 'help' ");
- out.print(prompt);
- while (!stop) {
- try {
- String cmd = in.readLine();
- executeCommand(cmd);
- out.print(prompt);
- out.flush();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
-
public void stop() {
stop = true;
}
@@ -115,6 +103,17 @@
e.printStackTrace();
}
}
+ public void addCommand(Command cmd){
+ commander.add(cmd);
+ }
+ /**
+ * This method is needed for non-trivial commands that could eventually be added, as
+ * they will need to use the engineContext to do useful things
+ * @return
+ */
+ public JMXEngineContext getEngineContext(){
+ return engineContext;
+ }
private void addBuiltInCmds(){
commander.add(new Command(){
public void executeCommand(String cmd, PrintStream out) throws Exception {
@@ -145,14 +144,16 @@
});
commander.add(new Command(){
public void executeCommand(String cmd, PrintStream out) throws Exception {
- String[] args=cmd.split(" ");//TODO implement scape seqs
- if(args.length>1)initLanguage(args[1]);
- else for (ScriptEngineFactory factory: engineContext.getEngineManager().getEngineFactories()) {
- out.print(factory.getLanguageName()+"; version "+factory.getLanguageVersion());
- out.print("; AKA: ");
- for(String alias: factory.getNames()) out.print(alias+" ");
- out.print("\n");
-
+ String[] args=cmd.split(" ");//TODO implement scape seqs, that is, if path contains spaces, for example.
+ if(args.length>1){
+ setLanguage(args[1]);
+ } else{
+ for (ScriptEngineFactory factory: engineContext.getEngineManager().getEngineFactories()) {
+ out.print(factory.getLanguageName()+"; version "+factory.getLanguageVersion());
+ out.print("; AKA: ");
+ for(String alias: factory.getNames()) out.print(alias+" ");
+ out.print("\n");
+ }
}
}
public String getName() {