Initial commit of mishell. Mishell is a scripting environment and console for remote jmx management. 
See README.txt for details.

git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@442641 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/mishell/README.txt b/mishell/README.txt
new file mode 100644
index 0000000..9d65f94
--- /dev/null
+++ b/mishell/README.txt
@@ -0,0 +1,41 @@
+Mishell provides an interactive console

+that executes scripts in different scripting languages.

+Running mishell: 

+- Standalone: 

+Mishell can be run directly with the "java -jar ${artifactId}-${version}.jar" idiom or as an OSGi bundle. Remember that in both cases

+JRE 6 is needed. Mishell provides some built-in commands and interprets ruby, javascript or any other language that you

+configure. You can also load scripts with the load command. Type 'help' for available commands. 

+- Inside OSGi: 

+You can see an example of configuring Felix for launching both Jmood and mishell in the same OSGi platform in the FelixLauncher

+class in the src/test/java dir. Remember to change the paths to match your installation. 

+

+The initial object that is exported to the scripting engine is a JMoodProxyManager that extends 

+the general-purpose MBeanProxyManager (from the jmxintrospector project) to simplify working with JMood. 

+For example, when running on OSGi with JMood you can add the mbeans by typing:

+$manager.addLocalServer(nil) #Ruby

+or

+manager.addLocalServer(null) //Javascript

+And you issue commands like

+$manager.objects.each{|mbean| puts mbean.objectName} #this lists the objectnames for all mbeans known to the shell

+

+

+

+

+IMPORTANT: 

+Dependencies that need to be manually installed:

+1. It needs Java 6 to work (as it depends on javax.script API). 

+Once that API is stable and released standalone, it should also work in Java 5.

+2. It needs JMX introspector which in turn needs Javassist to be 

+manually installed in your local maven repo.

+3. It needs both the jruby-javax.script binding and Jruby 0.9 to be manually 

+installed in the M2_REPO. 

+	- The binding is available at https://scripting.dev.java.net/ and licensed

+	under the BSD license. Download the jsr223-engines.[zip|tar.gz]

+	and install the  jruby-engine.jar

+	- JRuby is available at http://dist.codehaus.org/jruby/ under a tri-license: CPL/LGPL/GPL

+	located at engines/jruby/build/jruby-engine.jar. JRuby implementation

+4. If you want to use any other language, you should: 

+	- Install the binding and the implementation in M2_REPO. Use Jruby as an example to do it. 

+	- Add the dependencies to the pom.

+	

+

diff --git a/mishell/pom.xml b/mishell/pom.xml
new file mode 100644
index 0000000..a4ff486
--- /dev/null
+++ b/mishell/pom.xml
@@ -0,0 +1,113 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

+<parent>

+    <groupId>org.apache.felix</groupId>

+    <artifactId>felix</artifactId>

+    <version>0.8.0-SNAPSHOT</version>

+  </parent>

+  <modelVersion>4.0.0</modelVersion>

+  <name>Management Interactive Shell</name>

+   <packaging>osgi-bundle</packaging>

+  <artifactId>${groupId}.mishell</artifactId>

+  <dependencies>

+    <dependency>

+      <groupId>junit</groupId>

+      <artifactId>junit</artifactId>

+      <version>3.8.1</version>

+      <scope>test</scope>

+    </dependency>

+    <dependency><!--BSD -->

+      <groupId>jline</groupId>

+      <artifactId>jline</artifactId>

+      <version>0.9.9</version>

+    </dependency>

+     <dependency><!--Currently needs manual installation-->

+       <groupId>org.jruby</groupId><!--trilicense CPL/GPL/LGPL -->

+       <artifactId>jruby</artifactId>

+       <version>0.9.0</version>

+     </dependency>

+     <dependency><!--Currently needs manual installation-->

+       <groupId>javax.script</groupId><!--BSD-->

+       <artifactId>jruby-engine</artifactId>

+       <version>SNAPSHOT</version>

+     </dependency>

+     <dependency>

+      <groupId>${pom.groupId}</groupId>

+      <artifactId>${groupId}.jmxintrospector</artifactId>

+      <version>${pom.version}</version>

+    </dependency>

+        <dependency>

+      <groupId>${pom.groupId}</groupId>

+      <artifactId>org.osgi.core</artifactId>

+      <version>${pom.version}</version>

+      <scope>provided</scope>

+    </dependency>

+        <dependency>

+      <groupId>${pom.groupId}</groupId>

+      <artifactId>org.apache.felix.framework</artifactId>

+      <version>${pom.version}</version>

+      <scope>provided</scope>

+    </dependency>

+    

+  </dependencies>

+  <build>

+    <resources>

+      <resource>

+        <directory>src/main/resources</directory>

+        <filtering>true</filtering>

+      </resource>

+    </resources>

+    <plugins>

+      <plugin>

+        <groupId>org.apache.felix.plugins</groupId>

+        <artifactId>maven-osgi-plugin</artifactId>

+        <version>${pom.version}</version>

+        <extensions>true</extensions>

+        <configuration>

+		    <manifestFile>target/classes/manifest.mf</manifestFile>

+		    <!--

+		    We're inlining a lot here. This is nasty for a bundle, nice for an executable jar.

+		    It should be simple to change afterwards

+		    -->

+    	    <inlinedArtifacts>

+        	    <inlinedArtifact>jruby</inlinedArtifact>

+        	    <inlinedArtifact>jruby-engine</inlinedArtifact>

+        	    <inlinedArtifact>org.apache.felix.jmxintrospector</inlinedArtifact>

+        	    <inlinedArtifact>jline</inlinedArtifact>

+          	</inlinedArtifacts>

+          <osgiManifest>

+            <bundleActivator>${artifactId}.Activator</bundleActivator>

+            <bundleName>${name}</bundleName>

+            <bundleDescription>Management Interactive Shell</bundleDescription>

+            <bundleSymbolicName>${artifactId}</bundleSymbolicName>

+            <dynamicImportPackage>

+            *

+            </dynamicImportPackage>

+            <importPackage>

+            <!--Note that we explicitly do not import jmood classes as they we use 

+            dynamically generated interfaces and proxies instead-->

+			org.osgi.framework, javax.management, javax.management.remote, javax.script

+			</importPackage>

+            

+          </osgiManifest>

+        </configuration>

+      </plugin>

+              <plugin>

+                <groupId>org.apache.maven.plugins</groupId>

+                <artifactId>maven-compiler-plugin</artifactId>

+                <configuration>

+                    <source>1.6</source><!--should fail if not java6-->

+                    <target>1.6</target>

+                </configuration>

+             </plugin>      

+    </plugins>

+  </build>

+  <reporting>

+    <plugins>

+      <plugin>

+        <groupId>org.apache.maven.plugins</groupId>

+        <artifactId>maven-project-info-reports-plugin</artifactId>

+      </plugin>

+    </plugins>

+  </reporting>

+</project>

diff --git a/mishell/src/main/java/org/apache/felix/mishell/Activator.java b/mishell/src/main/java/org/apache/felix/mishell/Activator.java
new file mode 100644
index 0000000..a879a69
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/Activator.java
@@ -0,0 +1,31 @@
+package org.apache.felix.mishell;

+

+import org.osgi.framework.BundleActivator;

+import org.osgi.framework.BundleContext;

+

+public class Activator implements BundleActivator {

+

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

+		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.setName("ConsoleThread");

+		t.start();

+

+	}

+

+	public void stop(BundleContext context) throws Exception {

+		console.stop();

+

+	}

+

+}

diff --git a/mishell/src/main/java/org/apache/felix/mishell/Command.java b/mishell/src/main/java/org/apache/felix/mishell/Command.java
new file mode 100644
index 0000000..648428e
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/Command.java
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 0000000..9da9126
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/CommandNotFoundException.java
@@ -0,0 +1,5 @@
+package org.apache.felix.mishell;

+

+public class CommandNotFoundException extends Exception {

+

+}

diff --git a/mishell/src/main/java/org/apache/felix/mishell/EngineNotFoundException.java b/mishell/src/main/java/org/apache/felix/mishell/EngineNotFoundException.java
new file mode 100644
index 0000000..3e8b05b
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/EngineNotFoundException.java
@@ -0,0 +1,9 @@
+package org.apache.felix.mishell;

+

+public class EngineNotFoundException extends Exception {

+

+	public EngineNotFoundException(String language) {

+		super(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
new file mode 100644
index 0000000..73a545f
--- /dev/null
+++ b/mishell/src/main/java/org/apache/felix/mishell/JMoodProxyManager.java
@@ -0,0 +1,27 @@
+package org.apache.felix.mishell;

+

+import java.util.List;

+

+import javax.management.MBeanServerConnection;

+

+import org.apache.felix.jmxintrospector.MBean;

+import org.apache.felix.jmxintrospector.MBeanProxyManager;

+

+public class JMoodProxyManager extends MBeanProxyManager {

+	public List<Object> getControllers(){

+		return findAll("type=controller");

+	}

+	public List<Object> getBundlesAt(Object controller){

+		MBeanServerConnection server=((MBean)controller).getMBeanServer();

+		return findAll("type=bundle", server);

+	}

+	public List<Object> getServicesAt(Object controller){

+		MBeanServerConnection server=((MBean)controller).getMBeanServer();

+		return findAll("type=service", server);

+	}

+	public List<Object> getPackagesAt(Object controller){

+		MBeanServerConnection server=((MBean)controller).getMBeanServer();

+		return findAll("type=package", server);

+	}

+	

+}

diff --git a/mishell/src/main/resources/logging.properties b/mishell/src/main/resources/logging.properties
new file mode 100644
index 0000000..65f94dd
--- /dev/null
+++ b/mishell/src/main/resources/logging.properties
@@ -0,0 +1,54 @@
+############################################################

+#  	Default Logging Configuration File

+#

+# You can use a different file by specifying a filename

+# with the java.util.logging.config.file system property.  

+# For example java -Djava.util.logging.config.file=myfile

+############################################################

+

+############################################################

+#  	Global properties

+############################################################

+

+# "handlers" specifies a comma separated list of log Handler 

+# classes.  These handlers will be installed during VM startup.

+# Note that these classes must be on the system classpath.

+# By default we only configure a ConsoleHandler, which will only

+# show messages at the INFO and above levels.

+handlers= java.util.logging.ConsoleHandler

+

+# To also add the FileHandler, use the following line instead.

+#handlers= java.util.logging.FileHandler, java.util.logging.ConsoleHandler

+

+# Default global logging level.

+# This specifies which kinds of events are logged across

+# all loggers.  For any given facility this global level

+# can be overriden by a facility specific level

+# Note that the ConsoleHandler also has a separate level

+# setting to limit messages printed to the console.

+.level= FINEST

+

+############################################################

+# Handler specific properties.

+# Describes specific configuration info for Handlers.

+############################################################

+

+# default file output is in user's home directory.

+java.util.logging.FileHandler.pattern = %h/java%u.log

+java.util.logging.FileHandler.limit = 50000

+java.util.logging.FileHandler.count = 1

+java.util.logging.FileHandler.formatter = java.util.logging.XMLFormatter

+

+# Limit the message that are printed on the console to INFO and above.

+java.util.logging.ConsoleHandler.level = FINE

+java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

+

+

+############################################################

+# Facility specific properties.

+# Provides extra control for each logger.

+############################################################

+

+# For example, set the com.xyz.foo logger to only log SEVERE

+# messages:

+com.xyz.foo.level = SEVERE

diff --git a/mishell/src/test/java/org/apache/felix/mishell/FelixLauncher.java b/mishell/src/test/java/org/apache/felix/mishell/FelixLauncher.java
new file mode 100644
index 0000000..d2a085f
--- /dev/null
+++ b/mishell/src/test/java/org/apache/felix/mishell/FelixLauncher.java
@@ -0,0 +1,157 @@
+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.

+ *

+ */

+

+import java.io.BufferedReader;

+import java.io.File;

+import java.io.InputStreamReader;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.logging.Logger;

+

+import javax.management.MBeanServerConnection;

+

+import org.apache.felix.framework.Felix;

+import org.apache.felix.framework.util.MutablePropertyResolver;

+import org.apache.felix.framework.util.MutablePropertyResolverImpl;

+import org.osgi.framework.Constants;

+public class FelixLauncher {

+

+    private MutablePropertyResolver props;

+	private Felix framework;

+	private List bundles;

+	private List packages;

+	private File cacheDir;

+	public FelixLauncher() {

+        super();

+    	cacheDir = new File("./cache");

+    	System.out.println(cacheDir.getAbsolutePath());

+		clearCache(cacheDir);

+    	cacheDir.mkdir();

+        

+        framework = new Felix();

+		Map m=new HashMap();

+		bundles=new ArrayList();

+		packages=new ArrayList();

+

+        props = new MutablePropertyResolverImpl(m);

+        props.put("felix.cache.profiledir", cacheDir.getAbsolutePath());

+

+    }

+	public void addBundle(String url){

+		if (!bundles.contains(url))

+		bundles.add(url);

+	}

+	public void addPackage(String packageName){

+		if(!packages.contains(packageName))

+		packages.add(packageName);

+	}

+	public void start(){

+		StringBuffer autostart=new StringBuffer();

+		for (int i=0; i<bundles.size(); i++){

+			String bundle=(String)bundles.get(i);

+			autostart.append(bundle).append(" ");

+		}

+		props.put("felix.auto.start.1", autostart.toString());

+		StringBuffer spkg=new StringBuffer((String)packages.get(0));

+		packages.remove(0);

+		for (int i=0; i<packages.size(); i++){

+			String pkg=(String)packages.get(i);

+			spkg.append(", "+pkg);

+		}

+		

+		

+        props.put(Constants.FRAMEWORK_SYSTEMPACKAGES, spkg.toString());

+

+        framework.start(props,null);

+	}

+	public void blockingStart() throws Exception{

+		this.start();

+        int to=0;

+        while(framework.getStatus()!=Felix.RUNNING_STATUS) {

+            Thread.sleep(10);

+            to++;

+            if(to>100) throw new Exception("timeout");

+        }

+

+	}

+	public void shutdown(){

+		framework.shutdown();

+		clearCache(cacheDir);

+	}

+

+	private void clearCache(File cacheDir) {

+		if(!isCache(cacheDir)){

+			System.out.println("not valid cache");

+			return;

+		}

+		File[] files=cacheDir.listFiles();

+		for(int i=0; i<files.length; i++){

+			recursiveDelete(files[i]);

+		}

+	}

+	private void recursiveDelete(File file){

+	   if(file.isDirectory()){

+			File[] files=file.listFiles();

+			for(int i=0; i<files.length; i++){

+				File f=files[i];	

+				recursiveDelete(f);

+		   }

+	   }

+	   file.delete();

+	}

+	private boolean isCache(File cacheDir){

+		if(!cacheDir.exists()||!cacheDir.isDirectory()) return false;

+		else{

+			String[] names=cacheDir.list();

+			for(int i=0;i<names.length;i++){

+				String name=names[i];

+				if(!name.startsWith("bundle")) return false;

+			}

+			return true;

+		}

+	}

+

+    /**

+     * @param args

+     */

+    public static void main(String[] args) throws Exception{

+    	FelixLauncher launcher=new FelixLauncher();

+        String jmood="file:../org.apache.felix.jmood_trunk/target/org.apache.felix.jmood-0.8.0-SNAPSHOT.jar";

+        String jmxintrospector="file:../org.apache.felix.mishell/target/org.apache.felix.mishell-0.8.0-SNAPSHOT.jar";

+    	launcher.addBundle(jmood);

+    	launcher.addBundle(jmxintrospector);

+        launcher.addPackage("org.osgi.framework");

+        launcher.addPackage("org.osgi.util.tracker");

+        launcher.addPackage("org.osgi.service.log");

+        launcher.addPackage("org.osgi.service.packageadmin");

+        launcher.addPackage("org.osgi.service.startlevel");

+        launcher.addPackage("org.osgi.service.permissionadmin");

+        launcher.addPackage("org.osgi.service.useradmin");

+        launcher.addPackage("org.osgi.service.cm");

+        launcher.addPackage("javax.management");

+        launcher.addPackage("javax.management.remote");

+        launcher.addPackage("javax.management.openmbean");  

+        launcher.addPackage("javax.script");

+    	launcher.start();

+    }

+	public Felix getFramework() {

+		return framework;

+	}

+}

diff --git a/mishell/src/test/java/org/apache/felix/mishell/TypeTest.java b/mishell/src/test/java/org/apache/felix/mishell/TypeTest.java
new file mode 100644
index 0000000..8e34067
--- /dev/null
+++ b/mishell/src/test/java/org/apache/felix/mishell/TypeTest.java
@@ -0,0 +1,19 @@
+package org.apache.felix.mishell;

+

+import org.objectweb.asm.Type;

+

+public class TypeTest {

+

+	/**

+	 * @param args

+	 */

+	public static void main(String[] args) {

+		StringBuffer s=new StringBuffer();

+		s.append("bolean type: ");

+		s.append(Type.BOOLEAN_TYPE.getDescriptor()+"\n");

+		s.append("String: "+Type.getType((new String()).getClass()));

+		System.out.println(s);

+

+	}

+

+}