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() {