Move ServiceMix Kernel trunk into Felix
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@768912 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/gshell/gshell-admin/pom.xml b/karaf/gshell/gshell-admin/pom.xml
new file mode 100644
index 0000000..0a6a2a4
--- /dev/null
+++ b/karaf/gshell/gshell-admin/pom.xml
@@ -0,0 +1,154 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.admin</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Admin</name>
+
+ <description>
+ Provides administration commands
+ </description>
+
+ <properties>
+ <gshell.osgi.import>
+ org.apache.servicemix.kernel.main.spi.*;resolution:=optional,
+ org.apache.geronimo.gshell*,
+ </gshell.osgi.import>
+ <gshell.osgi.export>
+ org.apache.servicemix.jpm
+ </gshell.osgi.export>
+ <gshell.osgi.private>
+ org.apache.servicemix.kernel.gshell.admin.*,
+ org.apache.servicemix.jpm.impl
+ </gshell.osgi.private>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${pom.basedir}/src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <!-- here the phase you need -->
+ <phase>compile</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/target/classes/org/apache/servicemix/kernel/gshell/admin/etc</outputDirectory>
+ <resources>
+ <resource>
+ <directory>../../${config.location}</directory>
+ <includes>
+ <include>config.properties</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>Main</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.gshell.admin.*;version=${project.version}
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>org.apache.servicemix.jpm.*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg
new file mode 100644
index 0000000..2e22aec
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.features.cfg
@@ -0,0 +1,28 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+# Comma separated list of features repositories to register by default
+#
+featuresRepositories=mvn:org.apache.servicemix.kernel/apache-servicemix-kernel/${version}/xml/features
+
+#
+# Comma separated list of features to install at startup
+#
+featuresBoot=
diff --git a/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties
new file mode 100644
index 0000000..f9689b1
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/admin/etc/startup.properties
@@ -0,0 +1,76 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+# This file allows you to control the start level of each bundle.
+#
+
+#
+# Startup core services like logging
+#
+org/ops4j/pax/url/pax-url-mvn/${pax.url.version}/pax-url-mvn-${pax.url.version}.jar=5
+org/ops4j/pax/url/pax-url-wrap/${pax.url.version}/pax-url-wrap-${pax.url.version}.jar=5
+org/apache/geronimo/specs/geronimo-servlet_2.5_spec/${geronimo.servlet.version}/geronimo-servlet_2.5_spec-${geronimo.servlet.version}.jar=10
+org/apache/servicemix/specs/org.apache.servicemix.specs.jaxp-api-1.4/${servicemix.specs.version}/org.apache.servicemix.specs.jaxp-api-1.4-${servicemix.specs.version}.jar=10
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.jaxp-ri/${jaxp.ri.version}/org.apache.servicemix.bundles.jaxp-ri-${jaxp.ri.version}.jar=10
+org/apache/felix/org.osgi.compendium/${felix.compendium.version}/org.osgi.compendium-${felix.compendium.version}.jar=10
+org/apache/felix/org.apache.felix.configadmin/${felix.configadmin.version}/org.apache.felix.configadmin-${felix.configadmin.version}.jar=10
+org/apache/geronimo/specs/geronimo-annotation_1.0_spec/${geronimo.annotation.version}/geronimo-annotation_1.0_spec-${geronimo.annotation.version}.jar=10
+org/apache/felix/org.apache.felix.prefs/${felix.prefs.version}/org.apache.felix.prefs-${felix.prefs.version}.jar=10
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.filemonitor/${pom.version}/org.apache.servicemix.kernel.filemonitor-${pom.version}.jar=15
+org/ops4j/pax/logging/pax-logging-api/${pax.logging.version}/pax-logging-api-${pax.logging.version}.jar=20
+org/ops4j/pax/logging/pax-logging-service/${pax.logging.version}/pax-logging-service-${pax.logging.version}.jar=20
+
+#
+# The rest of the services..
+#
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.jline/${jline.version}/org.apache.servicemix.bundles.jline-${jline.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.aopalliance/${aopalliance.version}/org.apache.servicemix.bundles.aopalliance-${aopalliance.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.cglib/${cglib.version}/org.apache.servicemix.bundles.cglib-${cglib.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.mina/${mina.version}/org.apache.servicemix.bundles.mina-${mina.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.oro/${oro.version}/org.apache.servicemix.bundles.oro-${oro.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-codec/${commons.codec.version}/org.apache.servicemix.bundles.commons-codec-${commons.codec.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-httpclient/${commons.httpclient.version}/org.apache.servicemix.bundles.commons-httpclient-${commons.httpclient.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-jexl/${commons.jexl.version}/org.apache.servicemix.bundles.commons-jexl-${commons.jexl.version}.jar=30
+org/apache/servicemix/bundles/org.apache.servicemix.bundles.commons-vfs/${commons.vfs.version}/org.apache.servicemix.bundles.commons-vfs-${commons.vfs.version}.jar=30
+org/springframework/spring-aop/${spring.version}/spring-aop-${spring.version}.jar=30
+org/springframework/spring-beans/${spring.version}/spring-beans-${spring.version}.jar=30
+org/springframework/spring-context/${spring.version}/spring-context-${spring.version}.jar=30
+org/springframework/spring-core/${spring.version}/spring-core-${spring.version}.jar=30
+org/springframework/osgi/spring-osgi-core/${spring.osgi.version}/spring-osgi-core-${spring.osgi.version}.jar=30
+org/springframework/osgi/spring-osgi-extender/${spring.osgi.version}/spring-osgi-extender-${spring.osgi.version}.jar=30
+org/springframework/osgi/spring-osgi-io/${spring.osgi.version}/spring-osgi-io-${spring.osgi.version}.jar=30
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.spring/${pom.version}/org.apache.servicemix.kernel.spring-${pom.version}.jar=30
+org/apache/servicemix/kernel/org.apache.servicemix.kernel.management/${pom.version}/org.apache.servicemix.kernel.management-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.admin/${pom.version}/org.apache.servicemix.kernel.gshell.admin-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.osgi/${pom.version}/org.apache.servicemix.kernel.gshell.osgi-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.features/${pom.version}/org.apache.servicemix.kernel.gshell.features-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.log/${pom.version}/org.apache.servicemix.kernel.gshell.log-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.config/${pom.version}/org.apache.servicemix.kernel.gshell.config-${pom.version}.jar=30
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.packages/${pom.version}/org.apache.servicemix.kernel.gshell.packages-${pom.version}.jar=30
+org/apache/servicemix/kernel/jaas/org.apache.servicemix.kernel.jaas.config/${pom.version}/org.apache.servicemix.kernel.jaas.config-${pom.version}.jar=30
+org/apache/servicemix/kernel/jaas/org.apache.servicemix.kernel.jaas.modules/${pom.version}/org.apache.servicemix.kernel.jaas.modules-${pom.version}.jar=30
+com/google/code/sshd/sshd/${sshd.version}/sshd-${sshd.version}.jar=30
+org/osgi/jmx/${osgi.jmx.version}/jmx-${osgi.jmx.version}.jar=30
+com/oracle/osgi/jmx-impl/${osgi.jmx.version}/jmx-impl-${osgi.jmx.version}.jar=30
+
+#
+# Start console last
+#
+org/apache/servicemix/kernel/gshell/org.apache.servicemix.kernel.gshell.core/${pom.version}/org.apache.servicemix.kernel.gshell.core-${pom.version}.jar=40
+
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java
new file mode 100644
index 0000000..eedb31d
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/Process.java
@@ -0,0 +1,47 @@
+/*
+ * 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.servicemix.jpm;
+
+import java.io.IOException;
+import java.io.Serializable;
+
+/**
+ * Interface representing a process
+ */
+public interface Process extends Serializable {
+
+ /**
+ * Retrieves the PID of the process
+ * @return the pid
+ */
+ int getPid();
+
+ /**
+ * Check if this process is still running
+ * @return <code>true</code> if the process is running
+ * @throws IOException if an error occurs
+ */
+ boolean isRunning() throws IOException;
+
+ /**
+ * Destroy the process.
+ *
+ * @throws IOException
+ */
+ void destroy() throws IOException;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java
new file mode 100644
index 0000000..6862222
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilder.java
@@ -0,0 +1,59 @@
+/*
+ * 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.servicemix.jpm;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Interface used to create new processes.
+ */
+public interface ProcessBuilder {
+
+ /**
+ * Specified the current directory to run the command from
+ *
+ * @param dir the directory to run the command from
+ * @return the ProcessBuilder instance
+ */
+ ProcessBuilder directory(File dir);
+
+ /**
+ * Set the command to execute
+ *
+ * @param command the command to execute
+ * @return the ProcessBuilder instance
+ */
+ ProcessBuilder command(String command);
+
+ /**
+ * Create and start the process
+ *
+ * @return the process that has been started
+ * @throws IOException if the process can not be created
+ */
+ Process start() throws IOException;
+
+ /**
+ * Attach to an existing process
+ *
+ * @return the process that has been attached
+ * @throws IOException if the process can not be attached to
+ */
+ Process attach(int pid) throws IOException;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java
new file mode 100644
index 0000000..433b615
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/ProcessBuilderFactory.java
@@ -0,0 +1,30 @@
+/*
+ * 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.servicemix.jpm;
+
+/**
+ * Factory for process builders.
+ */
+public abstract class ProcessBuilderFactory {
+
+ public static ProcessBuilderFactory newInstance() {
+ return new org.apache.servicemix.jpm.impl.ProcessBuilderFactoryImpl();
+ }
+
+ public abstract ProcessBuilder newBuilder();
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java
new file mode 100644
index 0000000..4db4657
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderFactoryImpl.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import org.apache.servicemix.jpm.ProcessBuilder;
+import org.apache.servicemix.jpm.ProcessBuilderFactory;
+
+public class ProcessBuilderFactoryImpl extends ProcessBuilderFactory {
+
+ public ProcessBuilder newBuilder() {
+ return new ProcessBuilderImpl();
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java
new file mode 100644
index 0000000..649da6b
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessBuilderImpl.java
@@ -0,0 +1,48 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.servicemix.jpm.Process;
+import org.apache.servicemix.jpm.ProcessBuilder;
+
+
+public class ProcessBuilderImpl implements ProcessBuilder {
+
+ private File dir;
+ private String command;
+
+ public ProcessBuilder directory(File dir) {
+ this.dir = dir;
+ return this;
+ }
+
+ public ProcessBuilder command(String command) {
+ this.command = command;
+ return this;
+ }
+
+ public Process start() throws IOException {
+ return ProcessImpl.create(dir, command);
+ }
+
+ public Process attach(int pid) throws IOException {
+ return ProcessImpl.attach(pid);
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java
new file mode 100644
index 0000000..71146fa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ProcessImpl.java
@@ -0,0 +1,150 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.servicemix.jpm.Process;
+
+public class ProcessImpl implements Process {
+
+ private int pid;
+ //private File input;
+ //private File output;
+ //private File error;
+
+ public ProcessImpl(int pid/*, File input, File output, File error*/) {
+ this.pid = pid;
+ //this.input = input;
+ //this.output = output;
+ //this.error = error;
+ }
+
+ public int getPid() {
+ return pid;
+ }
+
+ public boolean isRunning() throws IOException {
+ if (ScriptUtils.isWindows()) {
+ Map<String, String> props = new HashMap<String, String>();
+ props.put("${pid}", Integer.toString(pid));
+ int ret = ScriptUtils.execute("running", props);
+ return ret == 0;
+ } else {
+ try {
+ java.lang.Process process = new java.lang.ProcessBuilder("ps", "-p", Integer.toString(pid)).start();
+ BufferedReader r = new BufferedReader(new InputStreamReader(process.getInputStream()));
+ r.readLine(); // skip headers
+ String s = r.readLine();
+ boolean running = s != null && s.length() > 0;
+ int ret = process.waitFor();
+ return running;
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ }
+ }
+
+ public void destroy() throws IOException {
+ int ret;
+ if (ScriptUtils.isWindows()) {
+ Map<String, String> props = new HashMap<String, String>();
+ props.put("${pid}", Integer.toString(pid));
+ ret = ScriptUtils.execute("destroy", props);
+ } else {
+ ret = ScriptUtils.executeProcess(new java.lang.ProcessBuilder("kill", "-9", Integer.toString(pid)));
+ }
+ if (ret != 0) {
+ throw new IOException("Unable to destroy proces, it may be already terminated");
+ }
+ }
+
+ /*
+ public OutputStream getInputStream() throws FileNotFoundException {
+ return new FileOutputStream(input);
+ }
+
+ public InputStream getOutputStream() throws FileNotFoundException {
+ return new FileInputStream(output);
+ }
+
+ public InputStream getErrorStream() throws FileNotFoundException {
+ return new FileInputStream(error);
+ }
+ */
+
+ public int waitFor() throws InterruptedException {
+ return 0;
+ }
+
+ public int exitValue() {
+ return 0;
+ }
+
+ public static Process create(File dir, String command) throws IOException {
+ //File input = File.createTempFile("jpm.", ".input");
+ //File output = File.createTempFile("jpm.", ".output");
+ //File error = File.createTempFile("jpm.", ".error");
+ File pidFile = File.createTempFile("jpm.", ".pid");
+ try {
+ Map<String, String> props = new HashMap<String, String>();
+ //props.put("${in.file}", input.getCanonicalPath());
+ //props.put("${out.file}", output.getCanonicalPath());
+ //props.put("${err.file}", error.getCanonicalPath());
+ props.put("${pid.file}", pidFile.getCanonicalPath());
+ props.put("${dir}", dir != null ? dir.getCanonicalPath() : "");
+ if (ScriptUtils.isWindows()) {
+ command = command.replaceAll("\"", "\"\"");
+ }
+ props.put("${command}", command);
+ int ret = ScriptUtils.execute("start", props);
+ if (ret != 0) {
+ throw new IOException("Unable to create process (error code: " + ret + ")");
+ }
+ int pid = readPid(pidFile);
+ return new ProcessImpl(pid/*, input, output, error*/);
+ } finally {
+ pidFile.delete();
+ }
+ }
+
+ public static Process attach(int pid) throws IOException {
+ return new ProcessImpl(pid);
+ }
+
+ private static int readPid(File pidFile) throws IOException {
+ InputStream is = new FileInputStream(pidFile);
+ try {
+ BufferedReader r = new BufferedReader(new InputStreamReader(is));
+ String pidString = r.readLine();
+ return Integer.valueOf(pidString);
+ } finally {
+ try {
+ is.close();
+ } catch (IOException e) {}
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java
new file mode 100644
index 0000000..869c958
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/jpm/impl/ScriptUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.servicemix.jpm.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Map;
+import java.util.Scanner;
+
+public class ScriptUtils {
+
+ public static int execute(String name, Map<String, String> props) throws IOException {
+ File script = File.createTempFile("jpm.", ".script");
+ try {
+ if (isWindows()) {
+ String res = "windows/" + name + ".vbs";
+ ScriptUtils.copyFilteredResource(res, script, props);
+ return executeProcess(new java.lang.ProcessBuilder("cscript",
+ "/NOLOGO",
+ "//E:vbs",
+ script.getCanonicalPath()));
+ } else {
+ String res = "unix/" + name + ".sh";
+ ScriptUtils.copyFilteredResource(res, script, props);
+ return executeProcess(new java.lang.ProcessBuilder("/bin/sh",
+ script.getCanonicalPath()));
+ }
+ } finally {
+ script.delete();
+ }
+ }
+
+ public static int executeProcess(java.lang.ProcessBuilder builder) throws IOException {
+ try {
+ java.lang.Process process = builder.start();
+ return process.waitFor();
+ } catch (InterruptedException e) {
+ throw new InterruptedIOException();
+ }
+ }
+
+ public static void copyFilteredResource(String resource, File outFile, Map<String, String> props) throws IOException {
+ InputStream is = null;
+ try {
+ is = ScriptUtils.class.getResourceAsStream(resource);
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ line = filter(line, props);
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } finally {
+ safeClose(is);
+ }
+ }
+
+ private static void safeClose(InputStream is) throws IOException {
+ if (is == null) {
+ return;
+ }
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private static void safeClose(OutputStream is) throws IOException {
+ if (is == null) {
+ return;
+ }
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private static String filter(String line, Map<String, String> props) {
+ for (Map.Entry<String, String> i : props.entrySet()) {
+ int p1 = line.indexOf(i.getKey());
+ if( p1 >= 0 ) {
+ String l1 = line.substring(0, p1);
+ String l2 = line.substring(p1+i.getKey().length());
+ line = l1+i.getValue()+l2;
+ }
+ }
+ return line;
+ }
+
+ private static final boolean windows;
+
+ static {
+ windows = System.getProperty("os.name").toLowerCase().indexOf("windows") != -1;
+ }
+
+ public static boolean isWindows() {
+ return windows;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java
new file mode 100644
index 0000000..cbf7181
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminService.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.admin;
+
+public interface AdminService {
+
+ Instance createInstance(String name, int port, String location) throws Exception;
+
+ Instance[] getInstances();
+
+ Instance getInstance(String name);
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.java
new file mode 100644
index 0000000..8a68d2d
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/AdminServiceMBean.java
@@ -0,0 +1,37 @@
+/*
+ * 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.servicemix.kernel.gshell.admin;
+
+public interface AdminServiceMBean {
+
+ void createInstance(String name, int port, String location) throws Exception;
+
+ String[] getInstances();
+
+ int getPort(String name) throws Exception;
+
+ void changePort(String name, int port) throws Exception;
+
+ String getState(String name) throws Exception;
+
+ void start(String name, String javaOpts) throws Exception;
+
+ void stop(String name) throws Exception;
+
+ void destroy(String name) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.java
new file mode 100644
index 0000000..07612aa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/Instance.java
@@ -0,0 +1,43 @@
+/*
+ * 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.servicemix.kernel.gshell.admin;
+
+public interface Instance {
+
+ String STOPPED = "Stopped";
+ String STARTING = "Starting";
+ String STARTED = "Started";
+
+ String getName();
+
+ String getLocation();
+
+ int getPid();
+
+ int getPort() throws Exception;
+
+ void changePort(int port) throws Exception;
+
+ void start(String javaOpts) throws Exception;
+
+ void stop() throws Exception;
+
+ void destroy() throws Exception;
+
+ String getState() throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java
new file mode 100644
index 0000000..54283a8
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceImpl.java
@@ -0,0 +1,299 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.ServerSocket;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.beans.factory.InitializingBean;
+
+public class AdminServiceImpl implements AdminService, InitializingBean {
+
+ private static final Log LOGGER = LogFactory.getLog(AdminServiceImpl.class);
+
+ private PreferencesService preferences;
+
+ private Map<String, Instance> instances = new HashMap<String, Instance>();
+
+ private int defaultPortStart = 8101;
+
+ public PreferencesService getPreferences() {
+ return preferences;
+ }
+
+ public void setPreferences(PreferencesService preferences) {
+ this.preferences = preferences;
+ }
+
+ public synchronized void afterPropertiesSet() throws Exception {
+ try {
+ Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+ Preferences child = prefs.node("Instances");
+ int count = child.getInt("count", 0);
+ Map<String, Instance> newInstances = new HashMap<String, Instance>();
+ for (int i = 0; i < count; i++) {
+ String name = child.get("item." + i + ".name", null);
+ String loc = child.get("item." + i + ".loc", null);
+ int pid = child.getInt("item." + i + ".pid", 0);
+ if (name != null) {
+ InstanceImpl instance = new InstanceImpl(this, name, loc);
+ if (pid > 0) {
+ try {
+ instance.attach(pid);
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ newInstances.put(name, instance);
+ }
+ }
+ instances = newInstances;
+ } catch (Exception e) {
+ LOGGER.warn("Unable to reload ServiceMix instance list", e);
+ }
+ }
+
+ public synchronized Instance createInstance(String name, int port, String location) throws Exception {
+ if (instances.get(name) != null) {
+ throw new IllegalArgumentException("Instance '" + name + "' already exists");
+ }
+ File serviceMixBase = new File(location != null ? location : ("instances/" + name)).getCanonicalFile();
+ int sshPort = port;
+ if (sshPort <= 0) {
+ try {
+ Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+ sshPort = prefs.getInt("port", defaultPortStart + 1);
+ prefs.putInt("port", sshPort + 1);
+ prefs.flush();
+ prefs.sync();
+ } catch (Exception e) {
+ try {
+ ServerSocket ss = new ServerSocket(0);
+ sshPort = ss.getLocalPort();
+ ss.close();
+ } catch (Exception t) {
+ }
+ }
+ if (sshPort <= 0) {
+ sshPort = defaultPortStart;
+ }
+ }
+ println("Creating new instance on port " + sshPort + " at: @|bold " + serviceMixBase + "|");
+
+ mkdir(serviceMixBase, "bin");
+ mkdir(serviceMixBase, "etc");
+ mkdir(serviceMixBase, "system");
+ mkdir(serviceMixBase, "deploy");
+ mkdir(serviceMixBase, "data");
+
+ copyResourceToDir(serviceMixBase, "etc/config.properties", true);
+ copyResourceToDir(serviceMixBase, "etc/org.apache.servicemix.features.cfg", true);
+ copyResourceToDir(serviceMixBase, "etc/users.properties", true);
+ copyResourceToDir(serviceMixBase, "etc/org.ops4j.pax.logging.cfg", true);
+ copyResourceToDir(serviceMixBase, "etc/org.ops4j.pax.url.mvn.cfg", true);
+ copyResourceToDir(serviceMixBase, "etc/startup.properties", true);
+
+ HashMap<String, String> props = new HashMap<String, String>();
+ props.put("${servicemix.name}", name);
+ props.put("${servicemix.home}", System.getProperty("servicemix.home"));
+ props.put("${servicemix.base}", serviceMixBase.getPath());
+ props.put("${servicemix.sshPort}", Integer.toString(sshPort));
+ copyFilteredResourceToDir(serviceMixBase, "etc/system.properties", props);
+ copyFilteredResourceToDir(serviceMixBase, "etc/org.apache.servicemix.shell.cfg", props);
+ if( System.getProperty("os.name").startsWith("Win") ) {
+ copyFilteredResourceToDir(serviceMixBase, "bin/servicemix.bat", props);
+ } else {
+ copyFilteredResourceToDir(serviceMixBase, "bin/servicemix", props);
+ chmod(new File(serviceMixBase, "bin/servicemix"), "a+x");
+ }
+ Instance instance = new InstanceImpl(this, name, serviceMixBase.toString());
+ instances.put(name, instance);
+ saveState();
+ return instance;
+ }
+
+ public synchronized Instance[] getInstances() {
+ return instances.values().toArray(new Instance[0]);
+ }
+
+ public synchronized Instance getInstance(String name) {
+ return instances.get(name);
+ }
+
+ synchronized void forget(String name) {
+ instances.remove(name);
+ }
+
+ synchronized void saveState() throws IOException, BackingStoreException {
+ Preferences prefs = preferences.getUserPreferences("AdminServiceState");
+ Preferences child = prefs.node("Instances");
+ child.clear();
+ Instance[] data = getInstances();
+ child.putInt("count", data.length);
+ for (int i = 0; i < data.length; i++) {
+ child.put("item." + i + ".name", data[i].getName());
+ child.put("item." + i + ".loc", data[i].getLocation());
+ child.putInt("item." + i + ".pid", data[i].getPid());
+ }
+ prefs.flush();
+ prefs.sync();
+ }
+
+ private void copyResourceToDir(File target, String resource, boolean text) throws Exception {
+ File outFile = new File(target, resource);
+ if( !outFile.exists() ) {
+ println("Creating file: @|bold " + outFile.getPath() + "|");
+ InputStream is = getClass().getClassLoader().getResourceAsStream("/org/apache/servicemix/kernel/gshell/admin/" + resource);
+ try {
+ if( text ) {
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } else {
+ // Binary so just write it out the way it came in.
+ FileOutputStream out = new FileOutputStream(new File(target, resource));
+ try {
+ int c=0;
+ while((c=is.read())>=0) {
+ out.write(c);
+ }
+ } finally {
+ safeClose(out);
+ }
+ }
+ } finally {
+ safeClose(is);
+ }
+ }
+ }
+
+ private void println(String st) {
+ ShellContext ctx = ShellContextHolder.get(true);
+ if (ctx != null) {
+ ctx.getIo().out.println(st);
+ } else {
+ System.out.println(st);
+ }
+ }
+
+ private void copyFilteredResourceToDir(File target, String resource, HashMap<String, String> props) throws Exception {
+ File outFile = new File(target, resource);
+ if( !outFile.exists() ) {
+ println("Creating file: @|bold "+outFile.getPath()+"|");
+ InputStream is = getClass().getClassLoader().getResourceAsStream("/org/apache/servicemix/kernel/gshell/admin/" + resource);
+ try {
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ line = filter(line, props);
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } finally {
+ safeClose(is);
+ }
+ }
+ }
+
+ private void safeClose(InputStream is) throws IOException {
+ if (is == null) {
+ return;
+ }
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private void safeClose(OutputStream is) throws IOException {
+ if (is == null) {
+ return;
+ }
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private String filter(String line, HashMap<String, String> props) {
+ for (Map.Entry<String, String> i : props.entrySet()) {
+ int p1 = line.indexOf(i.getKey());
+ if( p1 >= 0 ) {
+ String l1 = line.substring(0, p1);
+ String l2 = line.substring(p1+i.getKey().length());
+ line = l1+i.getValue()+l2;
+ }
+ }
+ return line;
+ }
+
+ private void mkdir(File serviceMixBase, String path) {
+ File file = new File(serviceMixBase, path);
+ if( !file.exists() ) {
+ println("Creating dir: @|bold "+file.getPath()+"|");
+ file.mkdirs();
+ }
+ }
+
+ private int chmod(File serviceFile, String mode) throws Exception {
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command("chmod", mode, serviceFile.getCanonicalPath());
+ Process p = builder.start();
+
+ // gnodet: Fix SMX4KNL-46: cpu goes to 100% after running the 'admin create' command
+ // Not sure exactly what happens, but commenting the process io redirection seems
+ // to work around the problem.
+ //
+ //PumpStreamHandler handler = new PumpStreamHandler(io.inputStream, io.outputStream, io.errorStream);
+ //handler.attach(p);
+ //handler.start();
+ int status = p.waitFor();
+ //handler.stop();
+ return status;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.java
new file mode 100644
index 0000000..a53b663
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/AdminServiceMBeanImpl.java
@@ -0,0 +1,81 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.AdminServiceMBean;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+public class AdminServiceMBeanImpl implements AdminServiceMBean {
+
+ private AdminService adminService;
+
+ public AdminService getAdminService() {
+ return adminService;
+ }
+
+ public void setAdminService(AdminService adminService) {
+ this.adminService = adminService;
+ }
+
+ public void createInstance(String name, int port, String location) throws Exception {
+ adminService.createInstance(name, port, location);
+ }
+
+ public String[] getInstances() {
+ Instance[] instances = adminService.getInstances();
+ String[] names = new String[instances.length];
+ for (int i = 0; i < instances.length; i++) {
+ names[i] = instances[i].getName();
+ }
+ return names;
+ }
+
+ public int getPort(String name) throws Exception {
+ return getExistingInstance(name).getPort();
+ }
+
+ public void changePort(String name, int port) throws Exception {
+ getExistingInstance(name).changePort(port);
+ }
+
+ public String getState(String name) throws Exception {
+ return getExistingInstance(name).getState();
+ }
+
+ public void start(String name, String javaOpts) throws Exception {
+ getExistingInstance(name).start(javaOpts);
+ }
+
+ public void stop(String name) throws Exception {
+ getExistingInstance(name).stop();
+ }
+
+ public void destroy(String name) throws Exception {
+ getExistingInstance(name).destroy();
+ }
+
+
+ private Instance getExistingInstance(String name) {
+ Instance i = adminService.getInstance(name);
+ if (i == null) {
+ throw new IllegalArgumentException("Instance '" + name + "' does not exist");
+ }
+ return i;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java
new file mode 100644
index 0000000..3815ca5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/InstanceImpl.java
@@ -0,0 +1,220 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.jpm.Process;
+import org.apache.servicemix.jpm.ProcessBuilderFactory;
+import org.apache.servicemix.jpm.impl.ScriptUtils;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+public class InstanceImpl implements Instance {
+
+ private static final Log LOG = LogFactory.getLog(InstanceImpl.class);
+
+ private AdminServiceImpl service;
+ private String name;
+ private String location;
+ private Process process;
+
+ public InstanceImpl(AdminServiceImpl service, String name, String location) {
+ this.service = service;
+ this.name = name;
+ this.location = location;
+ }
+
+ public void attach(int pid) throws IOException {
+ checkProcess();
+ if (this.process != null) {
+ throw new IllegalStateException("Instance already started");
+ }
+ this.process = ProcessBuilderFactory.newInstance().newBuilder().attach(pid);
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public int getPid() {
+ checkProcess();
+ return this.process != null ? this.process.getPid() : 0;
+ }
+
+ public int getPort() throws Exception {
+ InputStream is = null;
+ try {
+ File f = new File(location, "etc/org.apache.servicemix.shell.cfg");
+ is = new FileInputStream(f);
+ Properties props = new Properties();
+ props.load(is);
+ String loc = props.getProperty("sshPort");
+ return Integer.parseInt(loc);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ public void changePort(int port) throws Exception {
+ checkProcess();
+ if (this.process != null) {
+ throw new IllegalStateException("Instance not stopped");
+ }
+ Properties props = new Properties();
+ File f = new File(location, "etc/org.apache.servicemix.shell.cfg");
+ InputStream is = new FileInputStream(f);
+ try {
+ props.load(is);
+ } finally {
+ is.close();
+ }
+ props.setProperty("sshPort", Integer.toString(port));
+ OutputStream os = new FileOutputStream(f);
+ try {
+ props.store(os, null);
+ } finally {
+ os.close();
+ }
+ }
+
+ public synchronized void start(String javaOpts) throws Exception {
+ checkProcess();
+ if (this.process != null) {
+ throw new IllegalStateException("Instance already started");
+ }
+ if (javaOpts == null) {
+ javaOpts = "-server -Xmx512M -Dcom.sun.management.jmxremote";
+ }
+ File libDir = new File(System.getProperty("servicemix.home"), "lib");
+ File[] jars = libDir.listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".jar");
+ }
+ });
+ StringBuilder classpath = new StringBuilder();
+ for (File jar : jars) {
+ if (classpath.length() > 0) {
+ classpath.append(System.getProperty("path.separator"));
+ }
+ classpath.append(jar.getCanonicalPath());
+ }
+ String command = new File(System.getProperty("java.home"), ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath()
+ + " " + javaOpts
+ + " -Dservicemix.home=\"" + System.getProperty("servicemix.home") + "\""
+ + " -Dservicemix.base=\"" + new File(location).getCanonicalPath() + "\""
+ + " -Dservicemix.startLocalConsole=false"
+ + " -Dservicemix.startRemoteShell=true"
+ + " -classpath " + classpath.toString()
+ + " org.apache.servicemix.kernel.main.Main";
+ LOG.debug("Starting instance with command: " + command);
+ this.process = ProcessBuilderFactory.newInstance().newBuilder()
+ .directory(new File(location))
+ .command(command)
+ .start();
+ this.service.saveState();
+ }
+
+ public synchronized void stop() throws Exception {
+ checkProcess();
+ if (this.process == null) {
+ throw new IllegalStateException("Instance not started");
+ }
+ this.process.destroy();
+ }
+
+ public synchronized void destroy() throws Exception {
+ checkProcess();
+ if (this.process != null) {
+ throw new IllegalStateException("Instance not stopped");
+ }
+ deleteFile(new File(location));
+ this.service.forget(name);
+ this.service.saveState();
+ }
+
+
+ public synchronized String getState() {
+ checkProcess();
+ if (this.process == null) {
+ return STOPPED;
+ } else {
+ try {
+ int port = getPort();
+ Socket s = new Socket("localhost", port);
+ s.close();
+ return STARTED;
+ } catch (Exception e) {
+ // ignore
+ }
+ return STARTING;
+ }
+ }
+
+ protected void checkProcess() {
+ if (this.process != null) {
+ try {
+ if (!this.process.isRunning()) {
+ this.process = null;
+ }
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ protected static boolean deleteFile(File fileToDelete) {
+ if (fileToDelete == null || !fileToDelete.exists()) {
+ return true;
+ }
+ boolean result = true;
+ if (fileToDelete.isDirectory()) {
+ File[] files = fileToDelete.listFiles();
+ if (files == null) {
+ result = false;
+ } else {
+ for (int i = 0; i < files.length; i++) {
+ File file = files[i];
+ if (file.getName().equals(".") || file.getName().equals("..")) {
+ continue;
+ }
+ if (file.isDirectory()) {
+ result &= deleteFile(file);
+ } else {
+ result &= file.delete();
+ }
+ }
+ }
+ }
+ result &= fileToDelete.delete();
+ return result;
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.java
new file mode 100644
index 0000000..9da97b4
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/AdminCommandSupport.java
@@ -0,0 +1,42 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+
+public abstract class AdminCommandSupport extends OsgiCommandSupport {
+
+ private AdminService adminService;
+
+ public AdminService getAdminService() {
+ return adminService;
+ }
+
+ public void setAdminService(AdminService adminService) {
+ this.adminService = adminService;
+ }
+
+ protected Instance getExistingInstance(String name) {
+ Instance i = adminService.getInstance(name);
+ if (i == null) {
+ throw new IllegalArgumentException("Instance '" + name + "' does not exist");
+ }
+ return i;
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.java
new file mode 100644
index 0000000..adbf2f6
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.java
@@ -0,0 +1,33 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+public class ChangePortCommand extends AdminCommandSupport {
+
+ @Argument(index=0, required=true, description="The instance name")
+ private String instance = null;
+
+ @Argument(index=1, required=true, description="The new port")
+ private int port = 0;
+
+ protected Object doExecute() throws Exception {
+ getExistingInstance(instance).changePort(port);
+ return Result.SUCCESS;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.java
new file mode 100644
index 0000000..244b9f8
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.java
@@ -0,0 +1,41 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+
+public class ConnectCommand extends AdminCommandSupport {
+
+ @Argument(index=0, required=true, description="The instance name")
+ private String instance = null;
+
+ @Option(name="-u", aliases={"--username"}, token="USERNAME", description="Remote user name")
+ private String username = "smx";
+
+ @Option(name="-p", aliases={"--password"}, token="PASSWORD", description="Remote user password")
+ private String password = "smx";
+
+ protected Object doExecute() throws Exception {
+ int port = getExistingInstance(instance).getPort();
+ ShellContextHolder.get().getShell().execute("ssh -l " + username + " -P " + password + " -p " + port + " localhost");
+ return Result.SUCCESS;
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.java
new file mode 100644
index 0000000..d13c381
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.java
@@ -0,0 +1,43 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+
+/**
+ * Creates a new servicemix instance
+ *
+ * @version $Rev: 679826 $ $Date: 2008-07-25 17:00:12 +0200 (Fri, 25 Jul 2008) $
+ */
+public class CreateCommand extends AdminCommandSupport
+{
+ @Option(name = "-p", aliases = { "--port"}, description = "Port number for remote shell connection")
+ private int port = 0;
+
+ @Option(name = "-l", aliases = { "--location"}, description = "Location of the new instance on the file system")
+ private String location;
+
+ @Argument(index=0, required=true, description="Name of the new ServiceMix instance")
+ private String instance = null;
+
+ protected Object doExecute() throws Exception {
+ getAdminService().createInstance(instance, port, location);
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.java
new file mode 100644
index 0000000..6084caa
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.java
@@ -0,0 +1,36 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+/**
+ * Creates a new servicemix instance
+ *
+ * @version $Rev: 679826 $ $Date: 2008-07-25 17:00:12 +0200 (Fri, 25 Jul 2008) $
+ */
+public class DestroyCommand extends AdminCommandSupport
+{
+ @Argument(index=0, required=true, description="The name of the ServiceMix instance to destroy")
+ private String instance = null;
+
+ protected Object doExecute() throws Exception {
+ getExistingInstance(instance).destroy();
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java
new file mode 100644
index 0000000..61f3afb
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.java
@@ -0,0 +1,68 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+
+/**
+ * List available instances
+ */
+public class ListCommand extends AdminCommandSupport {
+
+ @Option(name = "-l", aliases = { "--location" }, description = "Display instances location")
+ boolean location;
+
+ protected Object doExecute() throws Exception {
+ Instance[] instances = getAdminService().getInstances();
+ if (location) {
+ io.out.println(" Port State Pid Location");
+ } else {
+ io.out.println(" Port State Pid Name");
+ }
+ for (Instance instance : instances) {
+ StringBuilder sb = new StringBuilder();
+ sb.append('[');
+ String s = Integer.toString(instance.getPort());
+ for (int i = s.length(); i < 5; i++) {
+ sb.append(' ');
+ }
+ sb.append(s);
+ sb.append("] [");
+ String state = instance.getState();
+ while (state.length() < "starting".length()) {
+ state += " ";
+ }
+ sb.append(state);
+ sb.append("] [");
+ s = Integer.toString(instance.getPid());
+ for (int i = s.length(); i < 5; i++) {
+ sb.append(' ');
+ }
+ sb.append(s);
+ sb.append("] ");
+ if (location) {
+ sb.append(instance.getLocation());
+ } else {
+ sb.append(instance.getName());
+ }
+ io.out.println(sb.toString());
+ }
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java
new file mode 100644
index 0000000..77afa91
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.java
@@ -0,0 +1,34 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+
+public class StartCommand extends AdminCommandSupport {
+
+ @Option(name = "-o", aliases = { "--java-opts"}, description = "Java options when launching the instance")
+ private String javaOpts;
+
+ @Argument(index=0, required=true, description="The instance name")
+ private String instance = null;
+
+ protected Object doExecute() throws Exception {
+ getExistingInstance(instance).start(javaOpts);
+ return Result.SUCCESS;
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java
new file mode 100644
index 0000000..c449387
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.java
@@ -0,0 +1,30 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+
+public class StopCommand extends AdminCommandSupport {
+
+ @Argument(index=0, required=true, description="The instance name")
+ private String instance = null;
+
+ protected Object doExecute() throws Exception {
+ getExistingInstance(instance).stop();
+ return Result.SUCCESS;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java
new file mode 100644
index 0000000..cfcb95a
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/java/org/apache/servicemix/kernel/gshell/admin/internal/completers/InstanceCompleter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.servicemix.kernel.gshell.admin.internal.completers;
+
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.admin.AdminService;
+import org.apache.servicemix.kernel.gshell.admin.Instance;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import jline.Completor;
+
+/**
+ * {@link jline.Completor} for server instance names.
+ *
+ * Displays a list of configured server instances for the Admin commands.
+ *
+ */
+public class InstanceCompleter implements Completor {
+ private AdminService adminService;
+
+ public void setAdminService(AdminService adminService) {
+ this.adminService = adminService;
+ }
+
+ public int complete(String buffer, int cursor, List candidates) {
+ StringsCompleter delegate = new StringsCompleter();
+ for (Instance instance : adminService.getInstances()) {
+ delegate.getStrings().add(instance.getName());
+ }
+ return delegate.complete(buffer, cursor, candidates);
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml b/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml
new file mode 100644
index 0000000..5f05513
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/META-INF/spring/gshell-admin.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <bean id="adminCommandBundleSupport" scope="prototype">
+ <property name="adminService" ref="adminService" />
+ </bean>
+
+ <gshell:command-bundle>
+ <gshell:command name="admin/create">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.CreateCommand"
+ parent="adminCommandBundleSupport" />
+ </gshell:command>
+ <gshell:command name="admin/connect">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ConnectCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="admin/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ListCommand"
+ parent="adminCommandBundleSupport" />
+ </gshell:command>
+ <gshell:command name="admin/start">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.StartCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="admin/stop">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.StopCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="admin/destroy">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.DestroyCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="admin/change-port">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.admin.internal.commands.ChangePortCommand"
+ parent="adminCommandBundleSupport" />
+ <gshell:completers>
+ <ref bean="instanceCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <osgi:reference id="preferences" interface="org.osgi.service.prefs.PreferencesService" cardinality="0..1" />
+
+ <bean id="adminService" class="org.apache.servicemix.kernel.gshell.admin.internal.AdminServiceImpl">
+ <property name="preferences" ref="preferences" />
+ </bean>
+
+ <bean id="instanceCompleter" class="org.apache.servicemix.kernel.gshell.admin.internal.completers.InstanceCompleter">
+ <property name="adminService" ref="adminService" />
+ </bean>
+
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh
new file mode 100644
index 0000000..1d1d720
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/unix/start.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#exec 1>${out.file}
+#exec 2>${err.file}
+exec 1>/dev/null
+exec 2>/dev/null
+if [ "x${dir}" != "x" ]; then
+ cd ${dir}
+fi
+nohup ${command} &
+echo $! > ${pid.file}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs
new file mode 100644
index 0000000..abd60eb
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/destroy.vbs
@@ -0,0 +1,27 @@
+'===============================================================================
+'
+' 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.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process Where ProcessId = ${pid}")
+intRetVal = 1
+For Each objProcess in colProcessList
+ objProcess.Terminate()
+ intRetVal = 0
+Next
+WScript.Quit(intRetVal)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs
new file mode 100644
index 0000000..32c65c5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/running.vbs
@@ -0,0 +1,26 @@
+'===============================================================================
+'
+' 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.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set colProcessList = objWMIService.ExecQuery("Select * from Win32_Process Where ProcessId = ${pid}")
+intRetVal = 1
+For Each objProcess in colProcessList
+ intRetVal = 0
+Next
+WScript.Quit(intRetVal)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs
new file mode 100644
index 0000000..6004c86
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/jpm/impl/windows/start.vbs
@@ -0,0 +1,34 @@
+'===============================================================================
+'
+' 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.
+'
+'===============================================================================
+
+Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
+Set objConfig = objWMIService.Get("Win32_ProcessStartup").SpawnInstance_
+objConfig.ShowWindow = SW_HIDE
+objConfig.CreateFlags = 8
+If Len("${dir}") > 0 Then
+ intReturn = objWMIService.Get("Win32_Process").Create("${command}", "${dir}", objConfig, intProcessID)
+Else
+ intReturn = objWMIService.Get("Win32_Process").Create("${command}", Null, objConfig, intProcessID)
+End If
+If intReturn = 0 Then
+ Set objOutputFile = CreateObject("Scripting.fileSystemObject").CreateTextFile("${pid.file}", TRUE)
+ objOutputFile.WriteLine(intProcessID)
+ objOutputFile.Close
+End If
+WScript.Quit(intReturn)
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix
new file mode 100644
index 0000000..20a33e6
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix
@@ -0,0 +1,25 @@
+#!/bin/sh
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+SERVICEMIX_HOME=${servicemix.home}
+SERVICEMIX_BASE=${servicemix.base}
+
+export SERVICEMIX_BASE
+${SERVICEMIX_HOME}/bin/servicemix "$*"
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat
new file mode 100644
index 0000000..13c155a
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/bin/servicemix.bat
@@ -0,0 +1,25 @@
+@ECHO OFF
+REM =========================================================================
+REM
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements. See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License. You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+REM
+REM =========================================================================
+
+SET SERVICEMIX_BASE=${servicemix.base}
+SETLOCAL
+SET SERVICEMIX_HOME=${servicemix.home}
+
+%SERVICEMIX_HOME%\bin\servicemix.bat %*
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg
new file mode 100644
index 0000000..e6a8fd2
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.apache.servicemix.shell.cfg
@@ -0,0 +1,23 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+startLocalConsole=${servicemix.startLocalConsole}
+startRemoteShell=${servicemix.startRemoteShell}
+sshPort=${servicemix.sshPort}
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg
new file mode 100644
index 0000000..24ddea5
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.logging.cfg
@@ -0,0 +1,36 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, out, osgi:VmLogAppender
+
+# Logger infos
+log4j.logger.org.apache.geronimo.gshell.remote=WARN
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg
new file mode 100644
index 0000000..d0317cd
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/org.ops4j.pax.url.mvn.cfg
@@ -0,0 +1,73 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+#
+# If set to true, the following property will not allow any certificate to be used
+# when accessing maven repositories through SSL
+#
+#org.ops4j.pax.url.mvn.certificateCheck=
+
+#
+# Path to the local maven settings file.
+# The repositories defined in this file will be automatically added to the list
+# of default repositories if the 'org.ops4j.pax.url.mvn.repositories' property
+# below is not set.
+# The following locations are checked for the existence of the settings.xml file
+# * 1. looks for the specified url
+# * 2. if not found looks for ${user.home}/.m2/settings.xml
+# * 3. if not found looks for ${maven.home}/conf/settings.xml
+# * 4. if not found looks for ${M2_HOME}/conf/settings.xml
+#
+#org.ops4j.pax.url.mvn.settings=
+
+#
+# Path to the local maven repository which is used to avoid downloading
+# artifacts when they already exist locally.
+# The value of this property will be extracted from the settings.xml file
+# above, or defaulted to:
+# System.getProperty( "user.home" ) + "/.m2/repository"
+#
+#org.ops4j.pax.url.mvn.localRepository=
+
+#
+# Comma separated list of repositories scanned when resolving an artifact.
+# Those repositories will be checked before iterating through the
+ below list of repositories and even before the local repository
+# A repository url can be appended with zero or more of the following flags:
+# @snapshots : the repository contains snaphots
+# @noreleases : the repository does not contain any released artifacts
+#
+# The following property value will add the system folder as a repo.
+#
+org.ops4j.pax.url.mvn.defaultRepositories=file:${servicemix.home}/system@snapshots,file:${servicemix.base}/system@snapshots
+
+#
+# Comma separated list of repositories scanned when resolving an artifact.
+# The default list includes the following repositories:
+# http://repo1.maven.org/maven2
+# http://repository.ops4j.org/maven2
+# To add repositories to the default ones, prepend '+' to the list of repositories
+# to add.
+# A repository url can be appended with zero or more of the following flags:
+# @snapshots : the repository contains snaphots
+# @noreleases : the repository does not contain any released artifacts
+#
+# The following property value will add the system folders as a repo.
+#
+org.ops4j.pax.url.mvn.repositories=file:${servicemix.base}/system@snapshots,file:${user.home}/.m2/repository@snapshots,http://repo1.maven.org/maven2,http://people.apache.org/repo/m2-snapshot-repository@snapshots@noreleases,http://svn.apache.org/repos/asf/servicemix/m2-repo
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties
new file mode 100644
index 0000000..255295e
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/system.properties
@@ -0,0 +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.
+#
+################################################################################
+
+org.ops4j.pax.logging.DefaultServiceLog.level=ERROR
+servicemix.name=${servicemix.name}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties
new file mode 100644
index 0000000..0348fae
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/etc/users.properties
@@ -0,0 +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.
+#
+################################################################################
+
+#
+smx=smx,admin
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties
new file mode 100644
index 0000000..6a7edbf
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ChangePortCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Change the port of an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties
new file mode 100644
index 0000000..1c78a34
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ConnectCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Connect to an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties
new file mode 100644
index 0000000..532ada2
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/CreateCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Create a new instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties
new file mode 100644
index 0000000..cb0581c
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/DestroyCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Destroy an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties
new file mode 100644
index 0000000..c473471
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing instances.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties
new file mode 100644
index 0000000..dfb4046
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StartCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Start an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties
new file mode 100644
index 0000000..0cd3acd
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/main/resources/org/apache/servicemix/kernel/gshell/admin/internal/commands/StopCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Stop an existing instance.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java
new file mode 100644
index 0000000..1833584
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/MainTest.java
@@ -0,0 +1,24 @@
+/*
+ * 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.servicemix.jpm;
+
+public class MainTest {
+
+ public static void main(String[] args) throws Exception {
+ Thread.sleep(Long.parseLong(args[0]));
+ }
+}
diff --git a/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java
new file mode 100644
index 0000000..8328813
--- /dev/null
+++ b/karaf/gshell/gshell-admin/src/test/java/org/apache/servicemix/jpm/ProcessTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.servicemix.jpm;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.jpm.impl.ScriptUtils;
+
+public class ProcessTest extends TestCase {
+
+ public void testCreate() throws Exception {
+ String javaPath = new File(System.getProperty("java.home"), ScriptUtils.isWindows() ? "bin\\java.exe" : "bin/java").getCanonicalPath();
+ System.err.println(javaPath);
+ StringBuilder command = new StringBuilder();
+ command.append(javaPath);
+ command.append(" -Dprop=\"key\"");
+ command.append(" -classpath ");
+ String clRes = getClass().getName().replace('.', '/') + ".class";
+ String str = getClass().getClassLoader().getResource(clRes).toString();
+ str = str.substring("file:".length(), str.indexOf(clRes));
+ command.append(str);
+ command.append(" ");
+ command.append(MainTest.class.getName());
+ command.append(" ");
+ command.append(60000);
+ System.err.println("Executing: " + command.toString());
+
+ ProcessBuilder builder = ProcessBuilderFactory.newInstance().newBuilder();
+ Process p = builder.command(command.toString()).start();
+ assertNotNull(p);
+ System.err.println("Process: " + p.getPid());
+ assertNotNull(p.getPid());
+ Thread.currentThread().sleep(1000);
+ System.err.println("Running: " + p.isRunning());
+ assertTrue(p.isRunning());
+ System.err.println("Destroying");
+ p.destroy();
+ Thread.currentThread().sleep(1000);
+ System.err.println("Running: " + p.isRunning());
+ assertFalse(p.isRunning());
+ }
+
+ /*
+ * When the process creation fails, no error is reported by the script
+ *
+ public void testFailure() throws Exception {
+ ProcessBuilder builder = ProcessBuilderFactory.newInstance().newBuilder();
+ Process p = builder.command("ec").start();
+ fail("An exception should have been thrown");
+ }
+ */
+}
diff --git a/karaf/gshell/gshell-config/pom.xml b/karaf/gshell/gshell-config/pom.xml
new file mode 100644
index 0000000..8bab513
--- /dev/null
+++ b/karaf/gshell/gshell-config/pom.xml
@@ -0,0 +1,93 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.config</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell ConfigAdmin Commands</name>
+
+ <description>
+ Provides the ConfigAdmin GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.geronimo.specs</groupId>
+ <artifactId>geronimo-annotation_1.0_spec</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.gshell.config*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java
new file mode 100644
index 0000000..e31ccfb
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/CancelCommand.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class CancelCommand extends ConfigCommandSupport {
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ this.variables.parent().unset(PROPERTY_CONFIG_PID);
+ this.variables.parent().unset(PROPERTY_CONFIG_PROPS);
+ }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java
new file mode 100644
index 0000000..5a28289
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ConfigCommandSupport.java
@@ -0,0 +1,64 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Abstract class from which all commands related to the ConfigurationAdmin
+ * service should derive.
+ * This command retrieves a reference to the ConfigurationAdmin service before
+ * calling another method to actually process the command.
+ */
+public abstract class ConfigCommandSupport extends OsgiCommandSupport {
+
+ public static final String PROPERTY_CONFIG_PID = "ConfigCommand.PID";
+ public static final String PROPERTY_CONFIG_PROPS = "ConfigCommand.Props";
+
+ protected Object doExecute() throws Exception {
+ // Get config admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("ConfigurationAdmin service is unavailable.");
+ return null;
+ }
+ try {
+ ConfigurationAdmin admin = (ConfigurationAdmin) getBundleContext().getService(ref);
+ if (admin == null) {
+ io.out.println("ConfigAdmin service is unavailable.");
+ return null;
+ }
+
+ doExecute(admin);
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return null;
+ }
+
+ protected Dictionary getEditedProps() throws Exception {
+ return (Dictionary) this.variables.parent().get(PROPERTY_CONFIG_PROPS);
+ }
+
+ protected abstract void doExecute(ConfigurationAdmin admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.java
new file mode 100644
index 0000000..f041da3
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/EditCommand.java
@@ -0,0 +1,43 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class EditCommand extends ConfigCommandSupport {
+
+ @Argument(required = true, description = "PID of the configuration")
+ String pid;
+
+ @Option(name = "--force", description = "Force the edition of this config, even if another one was under edition")
+ boolean force;
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ String oldPid = (String) this.variables.get(PROPERTY_CONFIG_PID);
+ if (oldPid != null && !oldPid.equals(pid) && !force) {
+ io.err.println("Another config is being edited. Cancel / update first, or use the --force option");
+ return;
+ }
+ Dictionary props = admin.getConfiguration(pid).getProperties();
+ this.variables.parent().set(PROPERTY_CONFIG_PID, pid);
+ this.variables.parent().set(PROPERTY_CONFIG_PROPS, props);
+ }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.java
new file mode 100644
index 0000000..7657d6b
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/ListCommand.java
@@ -0,0 +1,50 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class ListCommand extends ConfigCommandSupport {
+
+ @Argument(required = false, description = "LDAP query")
+ String query;
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Configuration[] configs = admin.listConfigurations(query);
+ for (Configuration config : configs) {
+ io.out.println("----------------------------------------------------------------");
+ io.out.println("Pid: " + config.getPid());
+ if (config.getFactoryPid() != null) {
+ io.out.println("FactoryPid: " + config.getFactoryPid());
+ }
+ io.out.println("BundleLocation: " + config.getBundleLocation());
+ if (config.getProperties() != null) {
+ io.out.println("Properties:");
+ Dictionary props = config.getProperties();
+ for (Enumeration e = props.keys(); e.hasMoreElements();) {
+ Object key = e.nextElement();
+ io.out.println(" " + key + " = " + props.get(key));
+ }
+ }
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.java
new file mode 100644
index 0000000..8e87dc7
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropDelCommand.java
@@ -0,0 +1,38 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropDelCommand extends ConfigCommandSupport {
+
+ @Argument(index = 0, required = true, description = "the property to delete")
+ String prop;
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Dictionary props = getEditedProps();
+ if (props == null) {
+ System.err.println("No configuration is being edited. Run the edit command first");
+ } else {
+ props.remove(prop);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.java
new file mode 100644
index 0000000..2f7a446
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropListCommand.java
@@ -0,0 +1,38 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropListCommand extends ConfigCommandSupport {
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Dictionary props = getEditedProps();
+ if (props == null) {
+ System.err.println("No configuration is being edited. Run the edit command first");
+ } else {
+ for (Enumeration e = props.keys(); e.hasMoreElements();) {
+ Object key = e.nextElement();
+ io.out.println(" " + key + " = " + props.get(key));
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.java
new file mode 100644
index 0000000..a0c3112
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/PropSetCommand.java
@@ -0,0 +1,41 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class PropSetCommand extends ConfigCommandSupport {
+
+ @Argument(index = 0, required = true, description = "the property to set")
+ String prop;
+
+ @Argument(index = 1, required = true, description = "the value of the property")
+ String value;
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Dictionary props = getEditedProps();
+ if (props == null) {
+ System.err.println("No configuration is being edited. Run the edit command first");
+ } else {
+ props.put(prop, value);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.java
new file mode 100644
index 0000000..1c6683f
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/UpdateCommand.java
@@ -0,0 +1,38 @@
+/*
+ * 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.servicemix.kernel.gshell.config;
+
+import java.util.Dictionary;
+
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+public class UpdateCommand extends ConfigCommandSupport {
+
+ protected void doExecute(ConfigurationAdmin admin) throws Exception {
+ Dictionary props = getEditedProps();
+ if (props == null) {
+ System.err.println("No configuration is being edited. Run the edit command first");
+ } else {
+ String pid = (String) this.variables.parent().get(PROPERTY_CONFIG_PID);
+ Configuration cfg = admin.getConfiguration(pid, null);
+ cfg.update(props);
+ this.variables.parent().unset(PROPERTY_CONFIG_PID);
+ this.variables.parent().unset(PROPERTY_CONFIG_PROPS);
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.java
new file mode 100644
index 0000000..7b63351
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationCompleter.java
@@ -0,0 +1,86 @@
+/*
+ * 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.servicemix.kernel.gshell.config.completers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.annotation.PostConstruct;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.cm.ConfigurationEvent;
+import org.osgi.service.cm.ConfigurationListener;
+
+/**
+ * {@link jline.Completor} for Configuration Admin configurations.
+ *
+ * Displays a list of existing config admin configurations for completion.
+ *
+ */
+public class ConfigurationCompleter implements Completor, ConfigurationListener {
+
+ private final StringsCompleter delegate = new StringsCompleter();
+
+ private ConfigurationAdmin admin;
+
+ public void setAdmin(ConfigurationAdmin admin) {
+ this.admin = admin;
+ }
+
+ @PostConstruct
+ public void init() {
+ Configuration[] configs;
+ try {
+ configs = admin.listConfigurations(null);
+ } catch (Exception e) {
+ return;
+ }
+
+ Collection<String> pids = new ArrayList<String>();
+
+ for (Configuration config : configs) {
+ if (config.getFactoryPid() != null) {
+ pids.add(config.getFactoryPid());
+ } else {
+ pids.add(config.getPid());
+ }
+ }
+
+ delegate.getStrings().addAll(pids);
+
+ }
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+ public void configurationEvent(ConfigurationEvent configurationEvent) {
+ String pid = configurationEvent.getFactoryPid()!=null ? configurationEvent.getFactoryPid() : configurationEvent.getPid();
+ if (configurationEvent.getType() == ConfigurationEvent.CM_DELETED) {
+ delegate.getStrings().remove(pid);
+ } else if (configurationEvent.getType() == ConfigurationEvent.CM_UPDATED) {
+ delegate.getStrings().add(pid);
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.java b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.java
new file mode 100644
index 0000000..e21a8e3
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/java/org/apache/servicemix/kernel/gshell/config/completers/ConfigurationPropertyCompleter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.servicemix.kernel.gshell.config.completers;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.servicemix.kernel.gshell.config.ConfigCommandSupport;
+
+/**
+ * {@link jline.Completor} for Configuration Admin properties.
+ *
+ * Displays a list of existing properties based on the current configuration being edited.
+ *
+ */
+public class ConfigurationPropertyCompleter implements Completor {
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+ Variables vars = ShellContextHolder.get().getVariables();
+ if (vars.get(ConfigCommandSupport.PROPERTY_CONFIG_PID) == null) {
+ return -1;
+ }
+
+ Dictionary props = (Dictionary) vars.get(ConfigCommandSupport.PROPERTY_CONFIG_PROPS);
+ StringsCompleter delegate = new StringsCompleter();
+
+ for (Enumeration e = props.keys(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ delegate.getStrings().add(key);
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml b/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml
new file mode 100644
index 0000000..e5c9d6d
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/META-INF/spring/gshell-config.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="config/cancel">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.CancelCommand" />
+ </gshell:command>
+ <gshell:command name="config/edit">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.EditCommand"/>
+ <gshell:completers>
+ <ref bean="configCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="config/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.ListCommand" />
+ </gshell:command>
+ <gshell:command name="config/propdel">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropDelCommand" />
+ <gshell:completers>
+ <ref bean="configPropertyCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="config/proplist">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropListCommand" />
+ </gshell:command>
+ <gshell:command name="config/propset">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.PropSetCommand" />
+ <gshell:completers>
+ <ref bean="configPropertyCompleter" />
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="config/update">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.config.UpdateCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <bean id="configCompleter" class="org.apache.servicemix.kernel.gshell.config.completers.ConfigurationCompleter" init-method="init">
+ <property name="admin" ref="configAdmin"/>
+ </bean>
+
+ <bean id="configPropertyCompleter" class="org.apache.servicemix.kernel.gshell.config.completers.ConfigurationPropertyCompleter" />
+
+ <osgi:reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin" />
+
+ <osgi:service ref="configCompleter" interface="org.osgi.service.cm.ConfigurationListener" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties
new file mode 100644
index 0000000..1bc5226
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/CancelCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Change the changes to the configuration being edited.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties
new file mode 100644
index 0000000..ca2f8e6
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/EditCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Create or edit a configuration.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties
new file mode 100644
index 0000000..a5d2c31
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing configurations.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties
new file mode 100644
index 0000000..9e76af6
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropDelCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Delete a property from the edited configuration.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties
new file mode 100644
index 0000000..9dab240
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List properties from the edited configuration.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties
new file mode 100644
index 0000000..df4a2dc
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/PropSetCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Set a property on the edited configuration.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties
new file mode 100644
index 0000000..810f08f
--- /dev/null
+++ b/karaf/gshell/gshell-config/src/main/resources/org/apache/servicemix/kernel/gshell/config/UpdateCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Save and propagate changes from the configuration being edited.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/pom.xml b/karaf/gshell/gshell-core/pom.xml
new file mode 100644
index 0000000..19e9a27
--- /dev/null
+++ b/karaf/gshell/gshell-core/pom.xml
@@ -0,0 +1,480 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Core</name>
+
+ <description>
+ Provides the OSGi GShell integration
+ </description>
+
+ <properties>
+ <gshell.osgi.import>
+ org.springframework.aop,
+ org.springframework.aop.framework,
+ org.springframework.beans.factory.annotation,
+ org.springframework.context.annotation,
+ org.springframework.osgi.service.importer,
+ org.aopalliance.aop,
+ org.apache.commons.vfs.provider.temp,
+ org.apache.commons.vfs.provider.ram,
+ jline*,
+ org.apache.servicemix.kernel.jaas.config;resolution:=optional,
+ org.apache.servicemix.kernel.version;resolution:=optional,
+ org.apache.servicemix.kernel.main.spi;resolution:=optional;version="1.0.0",
+ org.codehaus.plexus*;resolution:=optional,
+ org.apache.sshd.server.keyprovider,
+ org.apache.sshd.server.jaas,
+ org.jsecurity*;resolution:=optional,
+ *
+ </gshell.osgi.import>
+ <!-- TODO: remove plexus util package -->
+ <gshell.osgi.export>
+ org.apache.geronimo.gshell*;version="1.0.0.alpha-2-SNAPSHOT";-split-package:=merge-first,
+ org.apache.servicemix.kernel.gshell.core*,
+ org.codehaus.plexus.interpolation*;-split-package:=merge-first,
+ org.codehaus.plexus.util;-split-package:=merge-first
+ </gshell.osgi.export>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.main</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.config</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.cglib</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-builtin</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>oro</groupId>
+ <artifactId>oro</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-vfs</groupId>
+ <artifactId>commons-vfs</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-file</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-network</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-shell</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-ssh</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.commands</groupId>
+ <artifactId>gshell-text</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.geronimo.gshell.wisdom</groupId>
+ <artifactId>gshell-wisdom-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-ivy</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.geronimo.gshell.support</groupId>
+ <artifactId>gshell-xstore</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>commons-jexl</groupId>
+ <artifactId>commons-jexl</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.plexus</groupId>
+ <artifactId>plexus-utils</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-context</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-aop</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jdk14</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-httpclient</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>commons-codec</groupId>
+ <artifactId>commons-codec</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-codec</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-jexl</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.commons-vfs</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.oro</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.mina</groupId>
+ <artifactId>mina-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.sshd</groupId>
+ <artifactId>sshd-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.jline</artifactId>
+ </dependency>
+
+ <!-- jsecurity is a dependency for the ssh commands -->
+ <dependency>
+ <groupId>org.jsecurity</groupId>
+ <artifactId>jsecurity</artifactId>
+ <version>0.9.0-RC2</version>
+ <exclusions>
+ <exclusion>
+ <groupId>commons-logging</groupId>
+ <artifactId>commons-logging</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${pom.basedir}/src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>Main</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Import-Package>${gshell.osgi.import}</Import-Package>
+ <Export-Package>${gshell.osgi.export}</Export-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ <unpackBundle>true</unpackBundle>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-shade-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>shade</goal>
+ </goals>
+ <configuration>
+ <artifactSet>
+ <includes>
+ <include>org.apache.geronimo.gshell:gshell-api</include>
+ <include>org.apache.geronimo.gshell:gshell-application</include>
+ <include>org.apache.geronimo.gshell:gshell-parser</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-builtin</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-file</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-network</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-shell</include>
+ <include>org.apache.geronimo.gshell.commands:gshell-text</include>
+ <include>org.apache.geronimo.gshell.support:gshell-ansi</include>
+ <include>org.apache.geronimo.gshell.support:gshell-artifact</include>
+ <include>org.apache.geronimo.gshell.support:gshell-chronos</include>
+ <include>org.apache.geronimo.gshell.support:gshell-clp</include>
+ <include>org.apache.geronimo.gshell.support:gshell-console</include>
+ <include>org.apache.geronimo.gshell.support:gshell-event</include>
+ <include>org.apache.geronimo.gshell.support:gshell-i18n</include>
+ <include>org.apache.geronimo.gshell.support:gshell-interpolation</include>
+ <include>org.apache.geronimo.gshell.support:gshell-io</include>
+ <include>org.apache.geronimo.gshell.support:gshell-security</include>
+ <include>org.apache.geronimo.gshell.support:gshell-spring</include>
+ <include>org.apache.geronimo.gshell.support:gshell-terminal</include>
+ <include>org.apache.geronimo.gshell.support:gshell-vfs</include>
+ <include>org.apache.geronimo.gshell.support:gshell-vfs-meta</include>
+ <include>org.apache.geronimo.gshell.support:gshell-yarn</include>
+ <include>org.apache.geronimo.gshell.wisdom:gshell-wisdom-core</include>
+ <include>org.apache.geronimo.gshell.wisdom:gshell-wisdom-bootstrap</include>
+ <include>org.codehaus.plexus:plexus-utils</include>
+ <include>org.codehaus.plexus:plexus-interpolation</include>
+ <include>${project.groupId}:${project.artifactId}</include>
+ </includes>
+ </artifactSet>
+ <filters>
+ <filter>
+ <artifact>org.apache.geronimo.gshell:gshell-api</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell:gshell-application</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell:gshell-parser</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-builtin</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-file</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-network</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-shell</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-ssh</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.commands:gshell-text</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-ansi</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-artifact</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-chronos</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-clp</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-console</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-event</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-i18n</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-interpolation</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-io</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-security</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-spring</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-terminal</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-vfs</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-vfs-meta</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.support:gshell-yarn</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.wisdom:gshell-wisdom-core</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.apache.geronimo.gshell.wisdom:gshell-wisdom-bootstrap</artifact>
+ <excludes>
+ <exclude>org/apache/geronimo/gshell/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.codehaus.plexus:plexus-utils</artifact>
+ <excludes>
+ <exclude>org/codehaus/plexus/**</exclude>
+ </excludes>
+ </filter>
+ <filter>
+ <artifact>org.codehaus.plexus:plexus-interpolation</artifact>
+ <excludes>
+ <exclude>org/codehaus/plexus/**</exclude>
+ </excludes>
+ </filter>
+ </filters>
+ <createSourcesJar>${createSourcesJar}</createSourcesJar>
+ <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
+ <createDependencyReducedPom>true</createDependencyReducedPom>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties b/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties
new file mode 100644
index 0000000..1fc0355
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/filtered-resources/org/apache/servicemix/kernel/gshell/core/servicemix-version.properties
@@ -0,0 +1,20 @@
+##
+## 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.
+##
+
+version=${pom.version}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java
new file mode 100644
index 0000000..203e340
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/shell/FindAction.java
@@ -0,0 +1,208 @@
+/*
+ * 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.geronimo.gshell.commands.shell;
+
+import java.net.URI;
+
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSelectInfo;
+import org.apache.commons.vfs.FileSelector;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileType;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.vfs.FileObjects;
+import org.apache.geronimo.gshell.vfs.selector.AggregateFileSelector;
+import org.apache.geronimo.gshell.vfs.support.VfsActionSupport;
+import org.apache.oro.text.GlobCompiler;
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.PatternCompiler;
+import org.apache.oro.text.regex.PatternMatcher;
+import org.apache.oro.text.regex.Perl5Matcher;
+
+/**
+ * Find files in a hierarchy.
+ *
+ * TODO: remove this file when gshell is upgraded
+ *
+ * @version $Rev: 722797 $ $Date: 2008-12-03 08:18:16 +0100 (Wed, 03 Dec 2008) $
+ */
+public class FindAction
+ extends VfsActionSupport
+{
+ private final AggregateFileSelector selector = new AggregateFileSelector();
+
+ @Option(name="-name")
+ private void addNameFilter(final String name) throws MalformedPatternException {
+ log.debug("Adding -name selector for: {}", name);
+ selector.getSelectors().add(new NameSelector(name));
+ }
+
+ @Option(name="-iname")
+ private void addiNameFilter(final String name) throws MalformedPatternException {
+ log.debug("Adding -iname selector for: {}", name);
+ selector.getSelectors().add(new NameSelector(name, true));
+ }
+
+ @Option(name="-type")
+ private void addTypeFilter(final Type type) {
+ log.debug("Adding -type selector for: {}", type);
+ selector.getSelectors().add(new TypeSelector(type));
+ }
+
+ @Argument(required=true)
+ private String path;
+
+ public Object execute(final CommandContext context) throws Exception {
+ assert context != null;
+ IO io = context.getIo();
+
+ FileObject root = resolveFile(context, path);
+
+ ensureFileExists(root);
+
+ find(context, root, selector);
+
+ FileObjects.close(root);
+
+ return CommandAction.Result.SUCCESS;
+ }
+
+ private void find(final CommandContext context, final FileObject file, final FileSelector selector) throws FileSystemException {
+ assert context != null;
+ assert file != null;
+ assert selector != null;
+
+ FileObject[] files = file.findFiles(selector);
+
+ if (files != null && files.length != 0) {
+ for (FileObject child : files) {
+ display(context, child, file);
+ }
+ }
+ }
+
+ private void display(final CommandContext context, final FileObject file, final FileObject root) throws FileSystemException {
+ assert context != null;
+ assert file != null;
+
+ String path;
+ try {
+ path = new URI(this.path).resolve(root.getURL().toURI().relativize(file.getURL().toURI())).toString();
+ } catch (Exception e) {
+ path = file.getName().getPath();
+ }
+ IO io = context.getIo();
+ io.info(path);
+ }
+
+ //
+ // Type & TypeSelector
+ //
+
+ private enum Type
+ {
+ F, // normal file
+ D, // directory
+ }
+
+ private class TypeSelector
+ implements FileSelector
+ {
+ private final Type type;
+
+ public TypeSelector(final Type type) {
+ assert type != null;
+
+ this.type = type;
+
+ log.trace("Type: {}", type);
+ }
+
+ public boolean includeFile(final FileSelectInfo selection) throws Exception {
+ assert selection != null;
+
+ FileType ftype = selection.getFile().getType();
+
+ switch (type) {
+ case D:
+ return ftype == FileType.FOLDER;
+
+ case F:
+ return ftype == FileType.FILE;
+
+ // TODO: Handle FileType.FILE_OR_FOLDER
+
+ default:
+ return false;
+ }
+ }
+
+ public boolean traverseDescendents(final FileSelectInfo selection) throws Exception {
+ return true;
+ }
+ }
+
+ //
+ // NameSelector
+ //
+
+ private class NameSelector
+ implements FileSelector
+ {
+ private final Pattern pattern;
+
+ private final PatternMatcher matcher;
+
+ public NameSelector(final String name, final boolean ignoreCase) throws MalformedPatternException {
+ assert name != null;
+
+ PatternCompiler compiler = new GlobCompiler();
+ int options;
+ if (ignoreCase) {
+ options = GlobCompiler.CASE_INSENSITIVE_MASK;
+ }
+ else {
+ options = GlobCompiler.DEFAULT_MASK;
+ }
+ this.pattern = compiler.compile(name, options);
+ this.matcher = new Perl5Matcher();
+
+ log.trace("Pattern: {}", pattern.getPattern());
+ }
+
+ public NameSelector(final String name) throws MalformedPatternException {
+ this(name, false);
+ }
+
+ public boolean includeFile(final FileSelectInfo selection) throws Exception {
+ assert selection != null;
+ return matcher.matches(selection.getFile().getName().getBaseName(), pattern);
+ }
+
+ public boolean traverseDescendents(final FileSelectInfo selection) throws Exception {
+ return true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java
new file mode 100644
index 0000000..d3bb6de
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/JSecurityPasswordAuthenticator.java
@@ -0,0 +1,84 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.server.PasswordAuthenticator;
+import org.jsecurity.SecurityUtils;
+import org.jsecurity.authc.AuthenticationException;
+import org.jsecurity.authc.UsernamePasswordToken;
+import org.jsecurity.mgt.SecurityManager;
+import org.jsecurity.subject.Subject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <a href="http://jsecurity.org">JSecurity</a> {@link PasswordAuthenticator}.
+ *
+ * @version $Rev: 722797 $ $Date: 2008-12-03 08:18:16 +0100 (Wed, 03 Dec 2008) $
+ */
+public class JSecurityPasswordAuthenticator
+ implements PasswordAuthenticator
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private final SecurityManager securityManager;
+
+ public JSecurityPasswordAuthenticator(final SecurityManager securityManager) {
+ // securityManager can be null
+ this.securityManager = securityManager;
+ }
+
+ public JSecurityPasswordAuthenticator() {
+ this(null);
+ }
+
+ public Object authenticate(final String username, final String password) {
+ assert username != null;
+ assert password != null;
+
+ log.debug("Authenticating: {}/{}", username, password);
+
+ Subject currentUser;
+
+ if (securityManager != null) {
+ currentUser = securityManager.getSubject();
+ }
+ else {
+ currentUser = SecurityUtils.getSubject();
+ }
+
+ if (currentUser.isAuthenticated()) {
+ log.debug("Logging out current user: {}", currentUser.getPrincipal());
+ currentUser.logout();
+ }
+
+ try {
+ UsernamePasswordToken token = new UsernamePasswordToken(username, password);
+ currentUser.login(token);
+ Object principal = currentUser.getPrincipal();
+ log.info("User [{}] logged in successfully", principal);
+ return principal;
+ }
+ catch (AuthenticationException e) {
+ log.error("Authentication failed: " + e, e);
+ return null;
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.java
new file mode 100644
index 0000000..9795f01
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/ShellFactoryImpl.java
@@ -0,0 +1,267 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.server.ShellFactory;
+import jline.Completor;
+import jline.History;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.commandline.CommandLineExecutor;
+import org.apache.geronimo.gshell.console.Console;
+import org.apache.geronimo.gshell.console.JLineConsole;
+import org.apache.geronimo.gshell.console.completer.AggregateCompleter;
+import org.apache.geronimo.gshell.io.Closer;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.notification.ExitNotification;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.geronimo.gshell.registry.CommandResolver;
+import org.apache.geronimo.gshell.application.Application;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * SSHD {@link ShellFactory} which provides access to GShell.
+ *
+ * @version $Rev: 731517 $ $Date: 2009-01-05 11:25:19 +0100 (Mon, 05 Jan 2009) $
+ */
+public class ShellFactoryImpl
+ implements ShellFactory
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private Application application;
+
+ private Console.Prompter prompter;
+
+ private CommandLineExecutor executor;
+
+ private History history;
+
+ private List<Completor> completers;
+
+ private Console.ErrorHandler errorHandler;
+
+ public Console.Prompter getPrompter() {
+ return prompter;
+ }
+
+ public void setPrompter(final Console.Prompter prompter) {
+ this.prompter = prompter;
+ }
+
+ public CommandLineExecutor getExecutor() {
+ return executor;
+ }
+
+ public void setExecutor(final CommandLineExecutor executor) {
+ this.executor = executor;
+ }
+
+ public History getHistory() {
+ return history;
+ }
+
+ public void setHistory(final History history) {
+ this.history = history;
+ }
+
+ public List<Completor> getCompleters() {
+ return completers;
+ }
+
+ public void setCompleters(final List<Completor> completers) {
+ this.completers = completers;
+ }
+
+ public Console.ErrorHandler getErrorHandler() {
+ return errorHandler;
+ }
+
+ public void setErrorHandler(final Console.ErrorHandler errorHandler) {
+ this.errorHandler = errorHandler;
+ }
+
+ public Application getApplication() {
+ return application;
+ }
+
+ public void setApplication(Application application) {
+ this.application = application;
+ }
+
+ public Shell createShell() {
+ return new ShellImpl();
+ }
+
+ public class ShellImpl
+ implements ShellFactory.Shell, org.apache.geronimo.gshell.shell.Shell, ShellContext, Runnable
+ {
+ private InputStream in;
+
+ private OutputStream out;
+
+ private OutputStream err;
+
+ private ExitCallback callback;
+
+ private IO io;
+
+ private Variables variables;
+
+ private boolean closed;
+
+ public void setInputStream(final InputStream in) {
+ this.in = in;
+ }
+
+ public void setOutputStream(final OutputStream out) {
+ this.out = out;
+ }
+
+ public void setErrorStream(final OutputStream err) {
+ this.err = err;
+ }
+
+ public void setExitCallback(ExitCallback callback) {
+ this.callback = callback;
+ }
+
+ public void start(final Map<String,String> env) throws IOException {
+ this.io = new IO(in, out, err, false);
+
+ // Create variables, inheriting the application ones
+ this.variables = new Variables(application.getVariables());
+ // Set up additional env
+ if (env != null) {
+ for (Map.Entry<String,String> entry : env.entrySet()) {
+ this.variables.set(entry.getKey(), entry.getValue());
+ }
+ }
+ this.variables.set("gshell.prompt", application.getModel().getBranding().getPrompt());
+ this.variables.set(CommandResolver.GROUP, "/");
+ this.variables.set("gshell.username", env.get("USER"));
+ this.variables.set("gshell.hostname", application.getLocalHost());
+ // HACK: Add history for the 'history' command, since its not part of the Shell intf it can't really access it
+ this.variables.set("gshell.internal.history", getHistory(), true);
+ new Thread(this).start();
+ }
+
+ public void destroy() {
+ close();
+ }
+
+ public ShellContext getContext() {
+ return this;
+ }
+
+ public Object execute(final String line) throws Exception {
+
+ return executor.execute(getContext(), line);
+ }
+
+ public Object execute(final String command, final Object[] args) throws Exception {
+ return executor.execute(getContext(), args);
+ }
+
+ public Object execute(final Object... args) throws Exception {
+ return executor.execute(getContext(), args);
+ }
+
+ public boolean isOpened() {
+ return !closed;
+ }
+
+ public void close() {
+ closed = true;
+ Closer.close(in, out, err);
+ callback.onExit(0);
+ }
+
+ public boolean isInteractive() {
+ return false;
+ }
+
+ public void run(final Object... args) throws Exception {
+ Console.Executor executor = new Console.Executor()
+ {
+ public Result execute(final String line) throws Exception {
+ assert line != null;
+ try {
+ ShellImpl.this.execute(line);
+ }
+ catch (ExitNotification n) {
+ return Result.STOP;
+ }
+ return Result.CONTINUE;
+ }
+ };
+
+ IO io = getContext().getIo();
+
+ // Setup the console runner
+ JLineConsole console = new JLineConsole(executor, io);
+ console.setPrompter(getPrompter());
+ console.setErrorHandler(getErrorHandler());
+ console.setHistory(getHistory());
+
+ if (completers != null) {
+ // Have to use aggregate here to get the completion list to update properly
+ console.addCompleter(new AggregateCompleter(completers));
+ }
+
+ console.run();
+ }
+
+ public org.apache.geronimo.gshell.shell.Shell getShell() {
+ return this;
+ }
+
+ public IO getIo() {
+ return io;
+ }
+
+ public Variables getVariables() {
+ return variables;
+ }
+
+ public void run() {
+ ShellContext ctx = ShellContextHolder.get(true);
+
+ try {
+ ShellContextHolder.set(getContext());
+ run(new Object[0]);
+ }
+ catch (Exception e) {
+ log.error("Unhandled failure: " + e, e);
+ }
+ finally {
+ ShellContextHolder.set(ctx);
+ close();
+ }
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java
new file mode 100644
index 0000000..cafc437
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshAction.java
@@ -0,0 +1,169 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.ClientChannel;
+import org.apache.sshd.ClientSession;
+import org.apache.sshd.SshClient;
+import org.apache.sshd.client.future.ConnectFuture;
+import org.apache.sshd.common.util.NoCloseInputStream;
+import org.apache.sshd.common.util.NoCloseOutputStream;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.i18n.MessageSource;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.io.PromptReader;
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Connect to a SSH server.
+ *
+ * @version $Rev: 721244 $ $Date: 2008-11-27 18:19:56 +0100 (Thu, 27 Nov 2008) $
+ */
+public class SshAction
+ implements CommandAction, BeanContainerAware
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Option(name="-l", aliases={"--username"})
+ private String username;
+
+ @Option(name="-P", aliases={"--password"})
+ private String password;
+
+ @Argument(required=true)
+ private String hostname;
+
+ @Option(name="-p", aliases={"--port"})
+ private int port = 22;
+
+ private BeanContainer container;
+
+ private ClientSession session;
+
+ public void setBeanContainer(final BeanContainer container) {
+ assert container != null;
+ this.container = container;
+ }
+
+ /**
+ * Helper to validate that prompted username or password is not null or empty.
+ */
+ private class UsernamePasswordValidator
+ implements PromptReader.Validator
+ {
+ private String type;
+
+ private int count = 0;
+
+ private int max = 3;
+
+ public UsernamePasswordValidator(final String type) {
+ assert type != null;
+
+ this.type = type;
+ }
+
+ public boolean isValid(final String value) {
+ count++;
+
+ if (value != null && value.trim().length() > 0) {
+ return true;
+ }
+
+ if (count >= max) {
+ throw new RuntimeException("Too many attempts; failed to prompt user for " + type + " after " + max + " tries");
+ }
+
+ return false;
+ }
+ }
+
+ public Object execute(final CommandContext context) throws Exception {
+ assert context != null;
+ IO io = context.getIo();
+ MessageSource messages = context.getCommand().getMessages();
+
+ //
+ // TODO: Parse hostname for <username>@<hostname>
+ //
+
+ io.info(messages.format("info.connecting", hostname, port));
+
+ // If the username/password was not configured via cli, then prompt the user for the values
+ if (username == null || password == null) {
+ PromptReader prompter = new PromptReader(io);
+ String text;
+
+ log.debug("Prompting user for credentials");
+
+ if (username == null) {
+ text = messages.getMessage("prompt.username");
+ username = prompter.readLine(text + ": ", new UsernamePasswordValidator(text));
+ }
+
+ if (password == null) {
+ text = messages.getMessage("prompt.password");
+ password = prompter.readPassword(text + ": ", new UsernamePasswordValidator(text));
+ }
+ }
+
+ // Create the client from prototype
+ SshClient client = container.getBean(SshClient.class);
+ log.debug("Created client: {}", client);
+ client.start();;
+
+ try {
+ ConnectFuture future = client.connect(hostname, port);
+ future.await();
+ session = future.getSession();
+ try {
+ io.info(messages.getMessage("info.connected"));
+
+ session.authPassword(username, password);
+ int ret = session.waitFor(ClientSession.WAIT_AUTH | ClientSession.CLOSED | ClientSession.AUTHED, 0);
+ if ((ret & ClientSession.AUTHED) == 0) {
+ io.err.println("Authentication failed");
+ return Result.FAILURE;
+ }
+
+ ClientChannel channel = session.createChannel("shell");
+ channel.setIn(new NoCloseInputStream(io.inputStream));
+ channel.setOut(new NoCloseOutputStream(io.outputStream));
+ channel.setErr(new NoCloseOutputStream(io.errorStream));
+ channel.open();
+ channel.waitFor(ClientChannel.CLOSED, 0);
+ } finally {
+ session.close(false);
+ }
+ } finally {
+ client.stop();
+ }
+
+ io.verbose(messages.getMessage("verbose.disconnected"));
+
+ return Result.SUCCESS;
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java
new file mode 100644
index 0000000..db1cfb0
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/ssh/SshServerAction.java
@@ -0,0 +1,84 @@
+/*
+ * 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.geronimo.gshell.commands.ssh;
+
+import org.apache.sshd.SshServer;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.i18n.MessageSource;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Start a SSH server.
+ *
+ * @version $Rev: 720411 $ $Date: 2008-11-25 05:32:43 +0100 (Tue, 25 Nov 2008) $
+ */
+public class SshServerAction
+ implements CommandAction, BeanContainerAware
+{
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ @Option(name="-p", aliases={ "--port" })
+ private int port=8101;
+
+ @Option(name="-b", aliases={ "--background"})
+ private boolean background = true;
+
+ private BeanContainer container;
+
+ public void setBeanContainer(final BeanContainer container) {
+ assert container != null;
+
+ this.container = container;
+ }
+
+ public Object execute(final CommandContext context) throws Exception {
+ assert context != null;
+ IO io = context.getIo();
+ MessageSource messages = context.getCommand().getMessages();
+
+ SshServer server = container.getBean("sshServer", SshServer.class);
+
+ log.debug("Created server: {}", server);
+
+ server.setPort(port);
+
+ server.start();
+
+ io.info(messages.format("info.listening", port));
+
+ if (!background) {
+ synchronized (this) {
+ log.debug("Waiting for server to shutdown");
+
+ wait();
+ }
+
+ server.stop();
+ }
+
+ return Result.SUCCESS;
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java
new file mode 100644
index 0000000..e81b247
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/commands/text/SortAction.java
@@ -0,0 +1,391 @@
+/*
+ * 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.geronimo.gshell.commands.text;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.vfs.FileObject;
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.Closer;
+import org.apache.geronimo.gshell.vfs.FileObjects;
+import org.apache.geronimo.gshell.vfs.support.VfsActionSupport;
+
+/**
+ * Sort lines of text
+ *
+ * TODO: remove this file when gshell is upgraded
+ *
+ */
+public class SortAction extends VfsActionSupport {
+
+ @Option(name = "-f")
+ private boolean caseInsensitive;
+
+ @Option(name = "-r")
+ private boolean reverse;
+
+ @Option(name = "-u")
+ private boolean unique;
+
+ @Option(name = "-t")
+ private String separator;
+
+ @Option(name = "-b")
+ private boolean ignoreBlanks;
+
+ @Option(name = "-k", argumentRequired = true, multiValued = true)
+ private List<String> sortFields;
+
+ @Option(name = "-n")
+ private boolean numeric;
+
+ @Argument(index = 0, required=false)
+ private String path;
+
+
+ public Object execute(CommandContext context) throws Exception {
+ assert context != null;
+
+ if (path != null) {
+ FileObject file = resolveFile(context, path);
+
+ try {
+ sort(context, file);
+ }
+ finally {
+ FileObjects.close(file);
+ }
+ }
+ else {
+ sort(context.getIo().inputStream, context.getIo().out);
+ }
+ return Result.SUCCESS;
+ }
+
+ protected void sort(final CommandContext context, final FileObject file) throws Exception {
+ assert context != null;
+ assert file != null;
+
+ ensureFileExists(file);
+ ensureFileHasContent(file);
+ ensureFileIsReadable(file);
+
+ BufferedInputStream input = new BufferedInputStream(file.getContent().getInputStream());
+ try {
+ sort(input, context.getIo().out);
+ }
+ finally {
+ Closer.close(input);
+ }
+ }
+
+ protected void sort(InputStream input, PrintWriter out) throws Exception {
+ BufferedReader r = new BufferedReader(new InputStreamReader(input));
+ List<String> strings = new ArrayList<String>();
+ for (String s = r.readLine(); s != null; s = r.readLine()) {
+ strings.add(s);
+ }
+ char sep = (separator == null || separator.length() == 0) ? '\0' : separator.charAt(0);
+ Collections.sort(strings, new SortComparator(caseInsensitive, reverse, ignoreBlanks, numeric, sep, sortFields));
+ String last = null;
+ for (String s : strings) {
+ if (last == null) {
+ last = s;
+ } else if (!unique || !s.equals(last)) {
+ out.println(s);
+ }
+ }
+ }
+
+ public static class SortComparator implements Comparator<String> {
+
+ private boolean caseInsensitive;
+ private boolean reverse;
+ private boolean ignoreBlanks;
+ private boolean numeric;
+ private char separator;
+ private List<Key> sortKeys;
+
+ private static Pattern fpPattern;
+ static {
+ final String Digits = "(\\p{Digit}+)";
+ final String HexDigits = "(\\p{XDigit}+)";
+ final String Exp = "[eE][+-]?" + Digits;
+ final String fpRegex = "([\\x00-\\x20]*[+-]?(NaN|Infinity|(((" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|(\\.(" + Digits + ")(" + Exp + ")?)|(((0[xX]" + HexDigits + "(\\.)?)|(0[xX]" + HexDigits + "?(\\.)" + HexDigits + "))[pP][+-]?" + Digits + "))" + "[fFdD]?))[\\x00-\\x20]*)(.*)";
+ fpPattern = Pattern.compile(fpRegex);
+ }
+
+ public SortComparator(boolean caseInsensitive,
+ boolean reverse,
+ boolean ignoreBlanks,
+ boolean numeric,
+ char separator,
+ List<String> sortFields) {
+ this.caseInsensitive = caseInsensitive;
+ this.reverse = reverse;
+ this.separator = separator;
+ this.ignoreBlanks = ignoreBlanks;
+ this.numeric = numeric;
+ if (sortFields == null || sortFields.size() == 0) {
+ sortFields = new ArrayList<String>();
+ sortFields.add("1");
+ }
+ sortKeys = new ArrayList<Key>();
+ for (String f : sortFields) {
+ sortKeys.add(new Key(f));
+ }
+ }
+
+ public int compare(String o1, String o2) {
+ int res = 0;
+
+ List<Integer> fi1 = getFieldIndexes(o1);
+ List<Integer> fi2 = getFieldIndexes(o2);
+ for (Key key : sortKeys) {
+ int[] k1 = getSortKey(o1, fi1, key);
+ int[] k2 = getSortKey(o2, fi2, key);
+ if (key.numeric) {
+ Double d1 = getDouble(o1, k1[0], k1[1]);
+ Double d2 = getDouble(o2, k2[0], k2[1]);
+ res = d1.compareTo(d2);
+ } else {
+ res = compareRegion(o1, k1[0], k1[1], o2, k2[0], k2[1], key.caseInsensitive);
+ }
+ if (res != 0) {
+ if (key.reverse) {
+ res = - res;
+ }
+ break;
+ }
+ }
+ return res;
+ }
+
+ protected Double getDouble(String s, int start, int end) {
+ Matcher m = fpPattern.matcher(s.substring(start, end));
+ m.find();
+ return new Double(s.substring(0, m.end(1)));
+ }
+
+ protected int compareRegion(String s1, int start1, int end1, String s2, int start2, int end2, boolean caseInsensitive) {
+ int n1 = end1, n2 = end2;
+ for (int i1 = start1, i2 = start2; i1 < end1 && i2 < n2; i1++, i2++) {
+ char c1 = s1.charAt(i1);
+ char c2 = s2.charAt(i2);
+ if (c1 != c2) {
+ if (caseInsensitive) {
+ c1 = Character.toUpperCase(c1);
+ c2 = Character.toUpperCase(c2);
+ if (c1 != c2) {
+ c1 = Character.toLowerCase(c1);
+ c2 = Character.toLowerCase(c2);
+ if (c1 != c2) {
+ return c1 - c2;
+ }
+ }
+ } else {
+ return c1 - c2;
+ }
+ }
+ }
+ return n1 - n2;
+ }
+
+ protected int[] getSortKey(String str, List<Integer> fields, Key key) {
+ int start;
+ int end;
+ if (key.startField * 2 < fields.size()) {
+ start = fields.get((key.startField - 1) * 2);
+ if (key.ignoreBlanksStart) {
+ while (start < fields.get((key.startField - 1) * 2 + 1) && Character.isWhitespace(str.charAt(start))) {
+ start++;
+ }
+ }
+ if (key.startChar > 0) {
+ start = Math.min(start + key.startChar - 1, fields.get((key.startField - 1) * 2 + 1));
+ }
+ } else {
+ start = 0;
+ }
+ if (key.endField > 0 && key.endField * 2 < fields.size()) {
+ end = fields.get((key.endField - 1) * 2);
+ if (key.ignoreBlanksEnd) {
+ while (end < fields.get((key.endField - 1) * 2 + 1) && Character.isWhitespace(str.charAt(end))) {
+ end++;
+ }
+ }
+ if (key.endChar > 0) {
+ end = Math.min(end + key.endChar - 1, fields.get((key.endField - 1) * 2 + 1));
+ }
+ } else {
+ end = str.length();
+ }
+ return new int[] { start, end };
+ }
+
+ protected List<Integer> getFieldIndexes(String o) {
+ List<Integer> fields = new ArrayList<Integer>();
+ if (o.length() > 0) {
+ if (separator == '\0') {
+ int i = 0;
+ fields.add(0);
+ for (int idx = 1; idx < o.length(); idx++) {
+ if (Character.isWhitespace(o.charAt(idx)) && !Character.isWhitespace(o.charAt(idx - 1))) {
+ fields.add(idx - 1);
+ fields.add(idx);
+ }
+ }
+ fields.add(o.length() - 1);
+ } else {
+ int last = -1;
+ for (int idx = o.indexOf(separator); idx >= 0; idx = o.indexOf(separator, idx + 1)) {
+ if (last >= 0) {
+ fields.add(last);
+ fields.add(idx - 1);
+ } else if (idx > 0) {
+ fields.add(0);
+ fields.add(idx - 1);
+ }
+ last = idx + 1;
+ }
+ if (last < o.length()) {
+ fields.add(last < 0 ? 0 : last);
+ fields.add(o.length() - 1);
+ }
+ }
+ }
+ return fields;
+ }
+
+ public class Key {
+ int startField;
+ int startChar;
+ int endField;
+ int endChar;
+ boolean ignoreBlanksStart;
+ boolean ignoreBlanksEnd;
+ boolean caseInsensitive;
+ boolean reverse;
+ boolean numeric;
+
+ public Key(String str) {
+ boolean modifiers = false;
+ boolean startPart = true;
+ boolean inField = true;
+ boolean inChar = false;
+ for (char c : str.toCharArray()) {
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (!inField && !inChar) {
+ throw new IllegalArgumentException("Bad field syntax: " + str);
+ }
+ if (startPart) {
+ if (inChar) {
+ startChar = startChar * 10 + (c - '0');
+ } else {
+ startField = startField * 10 + (c - '0');
+ }
+ } else {
+ if (inChar) {
+ endChar = endChar * 10 + (c - '0');
+ } else {
+ endField = endField * 10 + (c - '0');
+ }
+ }
+ break;
+ case '.':
+ if (!inField) {
+ throw new IllegalArgumentException("Bad field syntax: " + str);
+ }
+ inField = false;
+ inChar = true;
+ break;
+ case 'n':
+ inField = false;
+ inChar = false;
+ modifiers = true;
+ numeric = true;
+ break;
+ case 'f':
+ inField = false;
+ inChar = false;
+ modifiers = true;
+ caseInsensitive = true;
+ break;
+ case 'r':
+ inField = false;
+ inChar = false;
+ modifiers = true;
+ reverse = true;
+ break;
+ case 'b':
+ inField = false;
+ inChar = false;
+ modifiers = true;
+ if (startPart) {
+ ignoreBlanksStart = true;
+ } else {
+ ignoreBlanksEnd = true;
+ }
+ break;
+ case ',':
+ inField = true;
+ inChar = false;
+ startPart = false;
+ break;
+ default:
+ throw new IllegalArgumentException("Bad field syntax: " + str);
+ }
+ }
+ if (!modifiers) {
+ ignoreBlanksStart = ignoreBlanksEnd = SortComparator.this.ignoreBlanks;
+ reverse = SortComparator.this.reverse;
+ caseInsensitive = SortComparator.this.caseInsensitive;
+ numeric = SortComparator.this.numeric;
+ }
+ if (startField < 1) {
+ throw new IllegalArgumentException("Bad field syntax: " + str);
+ }
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java
new file mode 100644
index 0000000..7e71328
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/geronimo/gshell/vfs/provider/meta/MetaFileObject.java
@@ -0,0 +1,144 @@
+/*
+ * 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.geronimo.gshell.vfs.provider.meta;
+
+import org.apache.commons.vfs.FileContentInfoFactory;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.FileContent;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.provider.AbstractFileObject;
+import org.apache.commons.vfs.provider.DefaultFileContent;
+import org.apache.geronimo.gshell.vfs.provider.meta.data.MetaData;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.util.Map;
+
+/**
+ * Meta file object.
+ *
+ * TODO: remove this file when gshell version is upgraded
+ *
+ * @version $Rev: 706033 $ $Date: 2008-10-19 17:36:15 +0200 (Sun, 19 Oct 2008) $
+ */
+public class MetaFileObject
+ extends AbstractFileObject
+{
+ private final MetaFileSystem fileSystem;
+
+ private MetaData data;
+ private FileContent content;
+
+ public MetaFileObject(final FileName fileName, final MetaFileSystem fileSystem) {
+ super(fileName, fileSystem);
+
+ // Save for uncasted typed access
+ this.fileSystem = fileSystem;
+ }
+
+ public MetaData getData() {
+ if (data == null) {
+ throw new IllegalStateException("Meta data has not been attached");
+ }
+
+ return data;
+ }
+
+ @Override
+ protected FileType doGetType() throws Exception {
+ return getData().getType();
+ }
+
+ @Override
+ protected long doGetLastModifiedTime() throws Exception {
+ return getData().getLastModified();
+ }
+
+ @Override
+ protected boolean doIsReadable() throws Exception {
+ return data.getBuffer() != null;
+ }
+
+ @Override
+ protected boolean doIsWriteable() throws Exception {
+ return false;
+ }
+
+ @Override
+ protected FileContentInfoFactory getFileContentInfoFactory() {
+ return fileSystem.getFileContentInfoFactory();
+ }
+
+ @Override
+ protected long doGetContentSize() throws Exception {
+ byte[] bytes = data.getBuffer();
+ return bytes != null ? bytes.length : 0;
+ }
+
+ @Override
+ protected InputStream doGetInputStream() throws Exception {
+ byte[] bytes = data.getBuffer();
+ if (bytes != null) {
+ return new ByteArrayInputStream(bytes);
+ }
+
+ return null;
+ }
+
+ @Override
+ protected Map<String,Object> doGetAttributes() {
+ return getData().getAttributes();
+ }
+
+ @Override
+ protected void doSetAttribute(final String name, final Object value) {
+ getData().getAttributes().put(name, value);
+ }
+
+ protected void doRemoveAttribute(final String name) {
+ getData().getAttributes().remove(name);
+ }
+
+ @Override
+ protected String[] doListChildren() throws Exception {
+ return fileSystem.listChildren(getName());
+ }
+
+ @Override
+ protected void doAttach() throws Exception {
+ if (data == null) {
+ data = fileSystem.lookupData(this);
+ }
+ content = new DefaultFileContent(this, getFileContentInfoFactory());
+ }
+
+ @Override
+ protected void doDetach() throws Exception {
+ data = null;
+ content = null;
+ }
+
+ @Override
+ public FileContent getContent() throws FileSystemException {
+ super.getContent();
+ return content;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java
new file mode 100644
index 0000000..350fd63
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationImpl.java
@@ -0,0 +1,143 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.Properties;
+
+import org.apache.geronimo.gshell.application.Application;
+import org.apache.geronimo.gshell.application.ClassPath;
+import org.apache.geronimo.gshell.application.model.ApplicationModel;
+import org.apache.geronimo.gshell.artifact.Artifact;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.io.IO;
+import org.springframework.beans.factory.InitializingBean;
+
+public class ApplicationImpl implements Application, InitializingBean {
+
+ private static final String EMBEDDED_PROPS = "org/apache/servicemix/kernel/version/embedded.properties";
+ private static final String SERVICEMIX_VERSION ="org/apache/servicemix/kernel/gshell/core/servicemix-version.properties";
+ private static final String VERSION_PROPERTY = "version";
+
+ private String id;
+ private IO io;
+ private ApplicationModel model;
+ private Variables variables;
+ private InetAddress localHost;
+ private File homeDir;
+ private URL embeddedResource = null;
+
+ public ApplicationImpl() throws Exception {
+ this.localHost = InetAddress.getLocalHost();
+ this.homeDir = detectHomeDir();
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Properties props = new Properties();
+ props.load(getClass().getClassLoader().getResourceAsStream(SERVICEMIX_VERSION));
+ String kernelVersion = props.getProperty(VERSION_PROPERTY);
+ this.model.setVersion(kernelVersion);
+ ServiceMixBranding smxBranding = (ServiceMixBranding) this.model.getBranding();
+ smxBranding.setVersion(kernelVersion);
+
+ if (this.getClass().getClassLoader().getResource(EMBEDDED_PROPS) != null) {
+ embeddedResource = this.getClass().getClassLoader().getResource(EMBEDDED_PROPS);
+ smxBranding.setEmbeddedResource(embeddedResource);
+ }
+ }
+
+ public URL getEmbeddedResource() {
+ return embeddedResource;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public IO getIo() {
+ return io;
+ }
+
+ public void setIo(IO io) {
+ this.io = io;
+ }
+
+ public Variables getVariables() {
+ return variables;
+ }
+
+ public void setVariables(Variables variables) {
+ this.variables = variables;
+ }
+
+ public ApplicationModel getModel() {
+ return model;
+ }
+
+ public void setModel(ApplicationModel model) {
+ this.model = model;
+ }
+
+ public ClassPath getClassPath() {
+ throw new UnsupportedOperationException();
+ }
+
+ public File getHomeDir() {
+ if (homeDir == null) {
+ throw new IllegalStateException();
+ }
+ return homeDir;
+ }
+
+ public InetAddress getLocalHost() {
+ return localHost;
+ }
+
+ public String getUserName() {
+ return System.getProperty("user.name");
+ }
+
+ public Artifact getArtifact() {
+ return null;
+ }
+
+ private File detectHomeDir() {
+ String homePath = System.getProperty("user.home");
+ // And now lets resolve this sucker
+ File dir;
+ try {
+ dir = new File(homePath).getCanonicalFile();
+ }
+ catch (IOException e) {
+ throw new RuntimeException("Failed to resolve home directory: " + homePath, e);
+ }
+ // And some basic sanity too
+ if (!dir.exists() || !dir.isDirectory()) {
+ throw new RuntimeException("Home directory configured but is not a valid directory: " + dir);
+ }
+
+ return dir;
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.java
new file mode 100644
index 0000000..7465900
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ApplicationManagerImpl.java
@@ -0,0 +1,81 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.application.Application;
+import org.apache.geronimo.gshell.application.ApplicationConfiguration;
+import org.apache.geronimo.gshell.application.ApplicationManager;
+import org.apache.geronimo.gshell.event.EventPublisher;
+import org.apache.geronimo.gshell.io.SystemOutputHijacker;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.wisdom.application.ShellCreatedEvent;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+public class ApplicationManagerImpl implements ApplicationManager, ApplicationContextAware, InitializingBean, DisposableBean {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private EventPublisher eventPublisher;
+
+ private Application application;
+
+ private ApplicationContext applicationContext;
+
+ public ApplicationManagerImpl(EventPublisher eventPublisher, Application application) {
+ this.eventPublisher = eventPublisher;
+ this.application = application;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if (!SystemOutputHijacker.isInstalled()) {
+ SystemOutputHijacker.install();
+ }
+ //SystemOutputHijacker.register(application.getIo().outputStream, application.getIo().errorStream);
+ }
+
+ public void destroy() {
+ SystemOutputHijacker.uninstall();
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ public void configure(ApplicationConfiguration applicationConfiguration) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ public Application getApplication() {
+ return application;
+ }
+
+ public Shell create() throws Exception {
+ final Shell shell = (Shell) applicationContext.getBean("shell");
+
+ log.debug("Created shell instance: {}", shell);
+
+ eventPublisher.publish(new ShellCreatedEvent(shell));
+
+ return shell;
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.java
new file mode 100644
index 0000000..7deacb1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerAwareProcessor.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.apache.geronimo.gshell.spring.BeanContainerAware;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+public class BeanContainerAwareProcessor implements InitializingBean, BeanPostProcessor, ApplicationContextAware {
+
+ private ApplicationContext applicationContext;
+ private BeanContainer container;
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ this.container = new BeanContainerWrapper(applicationContext);
+ }
+
+ public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
+ assert bean != null;
+
+ if (bean instanceof BeanContainerAware) {
+ ((BeanContainerAware)bean).setBeanContainer(container);
+ }
+
+ return bean;
+ }
+
+ public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
+ return bean;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java
new file mode 100644
index 0000000..edc15d7
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/BeanContainerWrapper.java
@@ -0,0 +1,109 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.spring.BeanContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.NoSuchBeanDefinitionException;
+import org.springframework.context.ApplicationContext;
+
+public class BeanContainerWrapper implements BeanContainer {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private ApplicationContext context;
+
+ public BeanContainerWrapper(ApplicationContext context) {
+ this.context = context;
+ }
+
+ public BeanContainer getParent() {
+ return null;
+ }
+
+ public ClassLoader getClassLoader() {
+ return context.getClassLoader();
+ }
+
+ public void loadBeans(String[] strings) throws Exception {
+ throw new UnsupportedOperationException();
+ }
+
+ public <T> T getBean(Class<T> type) {
+ assert type != null;
+
+ log.trace("Getting bean of type: {}", type);
+
+ String[] names = context.getBeanNamesForType(type);
+
+ if (names.length == 0) {
+ throw new NoSuchBeanDefinitionException(type, "No bean defined for type: " + type);
+ }
+ if (names.length > 1) {
+ throw new NoSuchBeanDefinitionException(type, "No unique bean defined for type: " + type + ", found matches: " + Arrays.asList(names));
+ }
+
+ return getBean(names[0], type);
+ }
+
+ public <T> T getBean(String name, Class<T> requiredType) {
+ assert name != null;
+ assert requiredType != null;
+
+ log.trace("Getting bean named '{}' of type: {}", name, requiredType);
+
+ return (T) context.getBean(name, requiredType);
+ }
+
+ public <T> Map<String, T> getBeans(Class<T> type) {
+ assert type != null;
+
+ log.trace("Getting beans of type: {}", type);
+
+ return (Map<String,T>) context.getBeansOfType(type);
+ }
+
+ public String[] getBeanNames() {
+ log.trace("Getting bean names");
+
+ return context.getBeanDefinitionNames();
+ }
+
+ public String[] getBeanNames(Class type) {
+ assert type != null;
+
+ log.trace("Getting bean names of type: {}", type);
+
+ return context.getBeanNamesForType(type);
+ }
+
+ public BeanContainer createChild(Collection<URL> urls) {
+ throw new UnsupportedOperationException();
+ }
+
+ public BeanContainer createChild() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java
new file mode 100644
index 0000000..2111072
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/CommandBundle.java
@@ -0,0 +1,175 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.registry.AliasRegistry;
+import org.apache.geronimo.gshell.registry.CommandRegistry;
+import org.apache.geronimo.gshell.wisdom.command.LinkCommand;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class CommandBundle implements BundleContextAware, InitializingBean, DisposableBean, ApplicationContextAware {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private CommandRegistry commandRegistry;
+
+ private AliasRegistry aliasRegistry;
+
+ private BundleContext bundleContext;
+
+ private List<Command> commands;
+
+ private List<Link> links;
+
+ private List<Alias> aliases;
+
+ private ApplicationContext applicationContext;
+
+ private List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>();
+
+ public CommandBundle() {
+ }
+
+ public void setApplicationContext(ApplicationContext applicationContext) {
+ this.applicationContext = applicationContext;
+ }
+
+ public List<Command> getCommands() {
+ return commands;
+ }
+
+ public void setCommands(final List<Command> commands) {
+ assert commands != null;
+
+ this.commands = commands;
+ }
+
+ public List<Link> getLinks() {
+ return links;
+ }
+
+ public void setLinks(List<Link> links) {
+ assert links != null;
+
+ this.links = links;
+ }
+
+ public List<Alias> getAliases() {
+ return aliases;
+ }
+
+ public void setAliases(List<Alias> aliases) {
+ assert aliases != null;
+
+ this.aliases = aliases;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ log.debug("Initializing command bundle");
+ if (commandRegistry == null) {
+ String[] names = applicationContext.getBeanNamesForType(CommandRegistry.class);
+ if (names.length == 1) {
+ commandRegistry = (CommandRegistry) applicationContext.getBean(names[0], CommandRegistry.class);
+ }
+ }
+ if (aliasRegistry == null) {
+ String[] names = applicationContext.getBeanNamesForType(AliasRegistry.class);
+ if (names.length == 1) {
+ aliasRegistry = (AliasRegistry) applicationContext.getBean(names[0], AliasRegistry.class);
+ }
+ }
+ if (commandRegistry != null && aliasRegistry != null) {
+ log.debug("Command bundle is using the auto wired command/alias registry");
+ if (commands != null) {
+ for (Command command : commands) {
+ log.debug("Registering command: {}", command.getLocation());
+ commandRegistry.registerCommand(command);
+ }
+ }
+ if (links != null) {
+ for (Link link : links) {
+ log.debug("Registering link: {}", link.getName());
+ LinkCommand cmd = new LinkCommand(commandRegistry, link.getTarget());
+ cmd.setLocation(new CommandLocationImpl(link.getName()));
+ commandRegistry.registerCommand(cmd);
+ }
+ }
+ if (aliases != null) {
+ for (Alias alias : aliases) {
+ log.debug("Registering alias: {}", alias.getName());
+ aliasRegistry.registerAlias(alias.getName(), alias.getAlias());
+ }
+ }
+ } else if (bundleContext != null) {
+ log.debug("Command bundle is using the OSGi registry");
+ if (commands != null) {
+ for (Command command : commands) {
+ log.debug("Registering command: {}", command.getLocation());
+ Dictionary props = new Properties();
+ props.put(OsgiCommandRegistry.NAME, command.getLocation().getFullPath());
+ registrations.add(bundleContext.registerService(Command.class.getName(), command, props));
+ }
+ }
+ if (links != null) {
+ for (Link link : links) {
+ log.debug("Registering link: {}", link.getName());
+ registrations.add(bundleContext.registerService(Link.class.getName(), link, new Properties()));
+ }
+ }
+ if (aliases != null) {
+ for (Alias alias : aliases) {
+ log.debug("Registering alias: {}", alias.getName());
+ Dictionary props = new Properties();
+ registrations.add(bundleContext.registerService(Alias.class.getName(), alias, new Properties()));
+ }
+ }
+ } else {
+ throw new Exception("Command bundle should be wired to the command/alias registry or be used in an OSGi context");
+ }
+ }
+
+ public void destroy() {
+ log.debug("Destroying command bundle");
+ for (ServiceRegistration reg : registrations) {
+ reg.unregister();
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java
new file mode 100644
index 0000000..98c7193
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/LocalConsole.java
@@ -0,0 +1,172 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.geronimo.gshell.notification.ExitNotification;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.servicemix.kernel.main.spi.MainService;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.osgi.context.BundleContextAware;
+
+public class LocalConsole implements Runnable, BundleContextAware {
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private Shell shell;
+
+ private boolean createLocalShell;
+
+ private BundleContext bundleContext;
+
+ private MainService mainService;
+
+ private CountDownLatch frameworkStarted;
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public MainService getMainService() {
+ return mainService;
+ }
+
+ public void setMainService(MainService mainService) {
+ this.mainService = mainService;
+ }
+
+ public Shell getShell() {
+ return shell;
+ }
+
+ public void setShell(Shell shell) {
+ this.shell = shell;
+ }
+
+ public boolean isCreateLocalShell() {
+ return createLocalShell;
+ }
+
+ public void setCreateLocalShell(boolean createLocalShell) {
+ this.createLocalShell = createLocalShell;
+ }
+
+ public void init() {
+ shell.getContext().getVariables().set("gshell.username", "smx");
+ frameworkStarted = new CountDownLatch(1);
+ getBundleContext().addFrameworkListener(new FrameworkListener(){
+ public void frameworkEvent(FrameworkEvent event) {
+ log.debug("Got event: " + event.getType());
+ if( event.getType() == FrameworkEvent.STARTED ) {
+ frameworkStarted.countDown();
+ }
+ }
+ });
+ if (createLocalShell) {
+ new Thread(this, "localShell").start();
+ }
+ }
+
+ public void destroy() {
+ if (createLocalShell) {
+ shell.close();
+ }
+ }
+
+ public void run() {
+ try {
+ String[] args = mainService.getArgs();
+ // If a command was specified on the command line, then just execute that command.
+ if (args != null && args.length > 0) {
+ waitForFrameworkToStart();
+ log.info("Executing Shell with arguments: " + Arrays.toString(args));
+ StringBuilder sb = new StringBuilder();
+ for (String arg : args) {
+ sb.append(arg).append(" ");
+ }
+ Object value = shell.execute(sb.toString());
+ if (mainService != null) {
+ if (value instanceof Number) {
+ mainService.setExitCode(((Number) value).intValue());
+ } else {
+ mainService.setExitCode(value != null ? 1 : 0);
+ }
+ log.info("Exiting shell due to terminated command");
+ }
+ } else {
+ shell.run();
+ }
+ } catch (ExitNotification e) {
+ if (mainService != null) {
+ mainService.setExitCode(0);
+ }
+ log.info("Exiting shell due received exit notification");
+ } catch (Throwable e) {
+ if (mainService != null) {
+ mainService.setExitCode(-1);
+ }
+ log.error("Exiting shell due to caught exception " + e, e);
+ } finally {
+ try {
+ shell.close();
+ } catch (Throwable t) {}
+ asyncShutdown();
+ }
+ }
+
+ /**
+ * Blocks until the framework has finished starting. We do this so that any installed
+ * bundles for commands get fully registered.
+ *
+ * @throws InterruptedException
+ */
+ private void waitForFrameworkToStart() throws InterruptedException {
+ log.info("Waiting from framework to start.");
+ if (frameworkStarted.await(60, TimeUnit.SECONDS)) {
+ log.info("System completed startup.");
+ } else {
+ log.warn("System took too long startup... continuing");
+ }
+ }
+
+ private void asyncShutdown() {
+ new Thread() {
+ public void run() {
+ try {
+ getBundleContext().getBundle(0).stop();
+ } catch (BundleException e) {
+ log.info("Caught exception while shutting down framework: " + e, e);
+ }
+ }
+ }.start();
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.java
new file mode 100644
index 0000000..a09ce0b
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiAliasRegistry.java
@@ -0,0 +1,45 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Map;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.registry.AliasRegistry;
+
+public class OsgiAliasRegistry {
+
+ public static final String NAME = "name";
+ public static final String ALIAS = "alias";
+
+ private AliasRegistry aliasRegistry;
+
+ public OsgiAliasRegistry(AliasRegistry aliasRegistry) {
+ this.aliasRegistry = aliasRegistry;
+ }
+
+ public void register(final Alias alias, Map<String, ?> properties) throws Exception {
+ aliasRegistry.registerAlias(alias.getName(), alias.getAlias());
+ }
+
+ public void unregister(final Alias alias, Map<String, ?> properties) throws Exception {
+ aliasRegistry.removeAlias(alias.getName());
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.java
new file mode 100644
index 0000000..c6817e8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandRegistry.java
@@ -0,0 +1,58 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Map;
+
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.registry.CommandRegistry;
+import org.apache.geronimo.gshell.wisdom.command.LinkCommand;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+
+public class OsgiCommandRegistry {
+
+ public static final String NAME = "name";
+ public static final String TARGET = "target";
+
+ private CommandRegistry commandRegistry;
+
+ public OsgiCommandRegistry(CommandRegistry commandRegistry) {
+ this.commandRegistry = commandRegistry;
+ }
+
+ public void register(final Command command, Map<String, ?> properties) throws Exception {
+ commandRegistry.registerCommand(command);
+ }
+
+ public void unregister(final Command command, Map<String, ?> properties) throws Exception {
+ commandRegistry.removeCommand(command);
+ }
+
+ public void register(final Link link, Map<String, ?> properties) throws Exception {
+ LinkCommand cmd = new LinkCommand(commandRegistry, link.getTarget());
+ cmd.setLocation(new CommandLocationImpl(link.getName()));
+ commandRegistry.registerCommand(cmd);
+ }
+
+ public void unregister(final Link link, Map<String, ?> properties) throws Exception {
+ commandRegistry.removeCommand(commandRegistry.getCommand(link.getName()));
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.java
new file mode 100644
index 0000000..e13940a
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/OsgiCommandSupport.java
@@ -0,0 +1,96 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.io.IO;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.springframework.osgi.context.BundleContextAware;
+
+public abstract class OsgiCommandSupport implements CommandAction, BundleContextAware {
+
+ protected Log log = LogFactory.getLog(getClass());
+ protected BundleContext bundleContext;
+ protected CommandContext commandContext;
+ protected IO io;
+ protected Variables variables;
+ protected List<ServiceReference> usedReferences;
+
+ public Object execute(CommandContext commandContext) throws Exception {
+ this.commandContext = commandContext;
+ this.io = commandContext.getIo();
+ this.variables = commandContext.getVariables();
+ try {
+ return doExecute();
+ } finally {
+ ungetServices();
+ }
+ }
+
+ protected abstract Object doExecute() throws Exception;
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ protected <T> List<T> getAllServices(Class<T> clazz, String filter) throws Exception {
+ ServiceReference[] references = getBundleContext().getAllServiceReferences(clazz.getName(), filter);
+ if (references == null) {
+ return null;
+ }
+ List<T> services = new ArrayList<T>();
+ for (ServiceReference ref : references) {
+ T t = getService(clazz, ref);
+ services.add(t);
+ }
+ return services;
+ }
+
+ protected <T> T getService(Class<T> clazz, ServiceReference reference) {
+ T t = (T) getBundleContext().getService(reference);
+ if (t != null) {
+ if (usedReferences == null) {
+ usedReferences = new ArrayList<ServiceReference>();
+ }
+ usedReferences.add(reference);
+ }
+ return t;
+ }
+
+ protected void ungetServices() {
+ if (usedReferences != null) {
+ for (ServiceReference ref : usedReferences) {
+ getBundleContext().ungetService(ref);
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java
new file mode 100644
index 0000000..803b511
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ServiceMixBranding.java
@@ -0,0 +1,188 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import org.apache.geronimo.gshell.ansi.AnsiBuffer;
+import org.apache.geronimo.gshell.ansi.AnsiCode;
+import org.apache.geronimo.gshell.ansi.AnsiRenderWriter;
+import org.apache.geronimo.gshell.application.model.Branding;
+
+public class ServiceMixBranding extends Branding {
+
+ private String prompt;
+ private String[] banner;
+ private String displayName;
+ private String displayVersion;
+ private String displayLocation;
+ private String applicationName;
+ private String applicationVersion;
+ private String applicationLocation;
+
+ private String[] kernelBanner = {
+ " ____ _ __ __ _ ",
+ "/ ___| ___ _ ____ _(_) ___ ___| \\/ (_)_ __",
+ "\\___ \\ / _ \\ '__\\ \\ / / |/ __/ _ \\ |\\/| | \\ \\/ /",
+ " ___) | __/ | \\ V /| | (_| __/ | | | |> < ",
+ "|____/ \\___|_| \\_/ |_|\\___\\___|_| |_|_/_/\\_\\",
+ };
+
+ public ServiceMixBranding() {
+ banner = kernelBanner;
+ displayName = "ServiceMix Kernel";
+ displayLocation = "http://servicemix.apache.org/kernel/";
+ }
+
+ public void setEmbeddedResource(URL embeddedResource) {
+ Properties brandProps = loadPropertiesFile(embeddedResource);
+ String brandBanner = brandProps.getProperty("banner");
+ int i = 0;
+ char quot = '"';
+
+ StringTokenizer st = new StringTokenizer(brandBanner, ",");
+ banner = new String[st.countTokens()];
+
+ while (st.hasMoreTokens()) {
+ banner[i] = st.nextToken();
+ banner[i] = banner[i].substring(1, banner[i].lastIndexOf(quot));
+ i++;
+ }
+
+ applicationName = brandProps.getProperty("application.name");
+ applicationVersion = brandProps.getProperty("application.version");
+ applicationLocation = brandProps.getProperty("application.location");
+ }
+
+ public String getName() {
+ return "servicemix";
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+
+ public void setVersion(String version) {
+ displayVersion = version;
+ }
+
+ public String getVersion() {
+ return displayVersion;
+ }
+
+ public String getApplicationName() {
+ return applicationName;
+ }
+
+ public String getApplicationVersion() {
+ return applicationVersion;
+ }
+
+ public String getProgramName() {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getPrompt() {
+ return prompt;
+ }
+
+ public void setPrompt(String prompt) {
+ this.prompt = prompt;
+ }
+
+ public String getAboutMessage() {
+ StringWriter writer = new StringWriter();
+ PrintWriter out = new AnsiRenderWriter(writer);
+
+ out.println("For information about @|cyan " + displayName + "|, visit:");
+ out.println(" @|bold " + displayLocation + "| ");
+ out.flush();
+
+ if (applicationName != null && applicationVersion != null) {
+ out.println();
+ out.println(applicationName + " " + applicationVersion);
+ out.println();
+ if (applicationLocation != null) {
+ out.println("For information about @|cyan " + applicationName + "|, visit:");
+ out.println(" @|bold " + applicationLocation + "| ");
+ out.flush();
+ }
+ }
+
+ return writer.toString();
+ }
+
+ public String getWelcomeMessage() {
+ StringWriter writer = new StringWriter();
+ PrintWriter out = new AnsiRenderWriter(writer);
+
+ AnsiBuffer buff = new AnsiBuffer();
+
+ for (String line : banner) {
+ buff.attrib(line, AnsiCode.CYAN);
+ out.println(buff);
+ }
+
+ out.println();
+ out.println(" @|bold " + displayName + "| (" + displayVersion + ")");
+ if (applicationName != null && applicationVersion != null) {
+ out.println(" @|bold " + applicationName + "| (" + applicationVersion + ")");
+ }
+ out.println();
+ out.println("Type '@|bold help|' for more information.");
+ out.flush();
+
+ return writer.toString();
+ }
+
+ private static Properties loadPropertiesFile(URL brandPropURL) {
+ // Read the properties file.
+ Properties brandProps = new Properties();
+ InputStream is = null;
+ try {
+ is = brandPropURL.openConnection().getInputStream();
+ brandProps.load(is);
+ is.close();
+ }
+ catch (FileNotFoundException ex) {
+ // Ignore file not found.
+ }
+ catch (Exception ex) {
+ System.err.println(
+ "Error loading embedded properties from " + brandPropURL);
+ System.err.println("ServicemixBranding: " + ex);
+ try {
+ if (is != null) is.close();
+ }
+ catch (IOException ex2) {
+ // Nothing we can do.
+ }
+ return null;
+ }
+
+ return brandProps;
+ }
+
+}
+
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.java
new file mode 100644
index 0000000..ae86534
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/ShellWrapper.java
@@ -0,0 +1,86 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+
+public class ShellWrapper implements Shell {
+
+ private Shell delegate;
+
+ public ShellWrapper(Shell delegate) {
+ this.delegate = delegate;
+ }
+
+ public ShellContext getContext() {
+ return delegate.getContext();
+ }
+
+ public Object execute(String s) throws Exception {
+ ShellContext ctx = ShellContextHolder.get(true);
+ try {
+ ShellContextHolder.set(getContext());
+ return delegate.execute(s);
+ } finally {
+ ShellContextHolder.set(ctx);
+ }
+ }
+
+ public Object execute(String s, Object[] objects) throws Exception {
+ ShellContext ctx = ShellContextHolder.get(true);
+ try {
+ ShellContextHolder.set(getContext());
+ return delegate.execute(s, objects);
+ } finally {
+ ShellContextHolder.set(ctx);
+ }
+ }
+
+ public Object execute(Object... objects) throws Exception {
+ ShellContext ctx = ShellContextHolder.get(true);
+ try {
+ ShellContextHolder.set(getContext());
+ return delegate.execute(objects);
+ } finally {
+ ShellContextHolder.set(ctx);
+ }
+ }
+
+ public boolean isOpened() {
+ return delegate.isOpened();
+ }
+
+ public void close() {
+ delegate.close();
+ }
+
+ public boolean isInteractive() {
+ return delegate.isInteractive();
+ }
+
+ public void run(Object... objects) throws Exception {
+ ShellContext ctx = ShellContextHolder.get(true);
+ try {
+ ShellContextHolder.set(getContext());
+ delegate.run(objects);
+ } finally {
+ ShellContextHolder.set(ctx);
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java
new file mode 100644
index 0000000..1e52177
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/WorkAroundAliasCommand.java
@@ -0,0 +1,118 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.command.CommandResult;
+import org.apache.geronimo.gshell.command.Variables;
+import org.apache.geronimo.gshell.commandline.CommandLineExecutor;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.geronimo.gshell.shell.ShellContext;
+import org.apache.geronimo.gshell.shell.ShellContextHolder;
+import org.apache.geronimo.gshell.wisdom.command.AliasCommand;
+
+public class WorkAroundAliasCommand extends AliasCommand {
+
+ private final CommandLineExecutor executor;
+
+ public WorkAroundAliasCommand(CommandLineExecutor executor) {
+ super(executor);
+ this.executor = executor;
+ setAction(new AliasCommandAction());
+ }
+
+ @Override
+ protected void prepareAction(final ShellContext context, final Object[] args) {
+ // HACK: Reset state for proper appendArgs muck
+ assert context != null;
+ assert args != null;
+
+ setAction(new AliasCommandAction());
+ log.trace("Preparing action");
+
+ IO io = context.getIo();
+ CommandAction action = getAction();
+
+ // Setup the command action
+ try {
+ // Process command line options/arguments
+ processArguments(io, action, args);
+ }
+ catch (Exception e) {
+ // Abort if preparation caused a failure
+ throw new AbortExecutionNotification(new CommandResult.FailureResult(e));
+ }
+ }
+
+ private class AliasCommandAction
+ implements CommandAction
+ {
+ @Argument
+ private List<String> appendArgs = null;
+
+ public Object execute(final CommandContext context) throws Exception {
+ assert context != null;
+
+ StringBuilder buff = new StringBuilder();
+ buff.append(getAlias());
+
+ // If we have args to append, then do it
+ if (appendArgs != null && !appendArgs.isEmpty()) {
+ buff.append(" ");
+
+ // Append args quoted as they have already been processed by the parser
+ Iterator iter = appendArgs.iterator();
+ while (iter.hasNext()) {
+ //
+ // HACK: Using double quote instead of single quote for now as the parser's handling of single quote is broken
+ //
+
+ buff.append('"').append(iter.next()).append('"');
+ if (iter.hasNext()) {
+ buff.append(" ");
+ }
+ }
+ }
+
+ log.debug("Executing alias: {}", buff);
+
+ final Shell shell = ShellContextHolder.get().getShell();
+ ShellContext shellContext = new ShellContext() {
+ public Shell getShell() {
+ return shell;
+ }
+ public IO getIo() {
+ return context.getIo();
+ }
+ public Variables getVariables() {
+ return context.getVariables();
+ }
+ };
+ Object result = executor.execute(shellContext, buff.toString());
+
+ log.debug("Alias result: {}", result);
+
+ return result;
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.java
new file mode 100644
index 0000000..8f3b580
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.java
@@ -0,0 +1,177 @@
+/*
+ * 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.servicemix.kernel.gshell.core.commands;
+
+import java.lang.management.ClassLoadingMXBean;
+import java.lang.management.GarbageCollectorMXBean;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.RuntimeMXBean;
+import java.lang.management.ThreadMXBean;
+import java.lang.reflect.Method;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.NumberFormat;
+import java.util.Locale;
+
+import org.apache.geronimo.gshell.ansi.AnsiCode;
+import org.apache.geronimo.gshell.ansi.AnsiRenderer;
+import org.apache.geronimo.gshell.command.CommandAction;
+import org.apache.geronimo.gshell.command.CommandContext;
+import org.apache.geronimo.gshell.io.IO;
+import org.apache.servicemix.kernel.gshell.core.ServiceMixBranding;
+import org.codehaus.plexus.util.StringUtils;
+
+public class InfoAction implements CommandAction {
+
+ private ServiceMixBranding branding;
+
+ private IO io;
+
+ private AnsiRenderer renderer = new AnsiRenderer();
+ private NumberFormat fmtI = new DecimalFormat("###,###", new DecimalFormatSymbols(Locale.ENGLISH));
+ private NumberFormat fmtD = new DecimalFormat("###,##0.000", new DecimalFormatSymbols(Locale.ENGLISH));
+
+ public InfoAction(ServiceMixBranding branding) {
+ this.branding = branding;
+ }
+
+ public Object execute(CommandContext context) throws Exception {
+ int maxNameLen;
+
+ RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean();
+ OperatingSystemMXBean os = ManagementFactory.getOperatingSystemMXBean();
+ ThreadMXBean threads = ManagementFactory.getThreadMXBean();
+ MemoryMXBean mem = ManagementFactory.getMemoryMXBean();
+ ClassLoadingMXBean cl = ManagementFactory.getClassLoadingMXBean();
+ io = context.getIo();
+
+ //
+ // print ServiceMix informations
+ //
+ maxNameLen = 25;
+ io.out.println("ServiceMix");
+ printValue("ServiceMix home", maxNameLen, System.getProperty("servicemix.home"));
+ printValue("ServiceMix base", maxNameLen, System.getProperty("servicemix.base"));
+ printValue("ServiceMix Kernel version", maxNameLen, branding.getParent().getVersion());
+
+ if (branding.getApplicationName() != null && branding.getApplicationVersion() != null) {
+ printValue(branding.getApplicationName() + " version", maxNameLen, branding.getApplicationVersion());
+ }
+ io.out.println();
+
+ io.out.println("JVM");
+ printValue("Java Virtual Machine", maxNameLen, runtime.getVmName() + " version " + runtime.getVmVersion());
+ printValue("Vendor", maxNameLen, runtime.getVmVendor());
+ printValue("Uptime", maxNameLen, printDuration(runtime.getUptime()));
+ try {
+ printValue("Process CPU time", maxNameLen, printDuration(getSunOsValueAsLong(os, "getProcessCpuTime") / 1000000));
+ } catch (Throwable t) {}
+ printValue("Total compile time", maxNameLen, printDuration(ManagementFactory.getCompilationMXBean().getTotalCompilationTime()));
+
+ io.out.println("Threads");
+ printValue("Live threads", maxNameLen, Integer.toString(threads.getThreadCount()));
+ printValue("Daemon threads", maxNameLen, Integer.toString(threads.getDaemonThreadCount()));
+ printValue("Peak", maxNameLen, Integer.toString(threads.getPeakThreadCount()));
+ printValue("Total started", maxNameLen, Long.toString(threads.getTotalStartedThreadCount()));
+
+ io.out.println("Memory");
+ printValue("Current heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getUsed()));
+ printValue("Maximum heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getMax()));
+ printValue("Committed heap size", maxNameLen, printSizeInKb(mem.getHeapMemoryUsage().getCommitted()));
+ printValue("Pending objects", maxNameLen, Integer.toString(mem.getObjectPendingFinalizationCount()));
+ for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
+ String val = "Name = '" + gc.getName() + "', Collections = " + gc.getCollectionCount() + ", Time = " + printDuration(gc.getCollectionTime());
+ printValue("Garbage collector", maxNameLen, val);
+ }
+
+ io.out.println("Classes");
+ printValue("Current classes loaded", maxNameLen, printLong(cl.getLoadedClassCount()));
+ printValue("Total classes loaded", maxNameLen, printLong(cl.getTotalLoadedClassCount()));
+ printValue("Total classes unloaded", maxNameLen, printLong(cl.getUnloadedClassCount()));
+
+ io.out.println("Operating system");
+ printValue("Name", maxNameLen, os.getName() + " version " + os.getVersion());
+ printValue("Architecture", maxNameLen, os.getArch());
+ printValue("Processors", maxNameLen, Integer.toString(os.getAvailableProcessors()));
+ try {
+ printValue("Total physical memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getTotalPhysicalMemorySize")));
+ printValue("Free physical memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getFreePhysicalMemorySize")));
+ printValue("Committed virtual memory", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getCommittedVirtualMemorySize")));
+ printValue("Total swap space", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getTotalSwapSpaceSize")));
+ printValue("Free swap space", maxNameLen, printSizeInKb(getSunOsValueAsLong(os, "getFreeSwapSpaceSize")));
+ } catch (Throwable t) {}
+
+ return null;
+ }
+
+ private long getSunOsValueAsLong(OperatingSystemMXBean os, String name) throws Exception {
+ Method mth = os.getClass().getMethod(name);
+ return (Long) mth.invoke(os);
+ }
+
+ private String printLong(long i) {
+ return fmtI.format(i);
+ }
+
+ private String printSizeInKb(double size) {
+ return fmtI.format((long) (size / 1024)) + " kbytes";
+ }
+
+ private String printDuration(double uptime) {
+ uptime /= 1000;
+ if (uptime < 60) {
+ return fmtD.format(uptime) + " seconds";
+ }
+ uptime /= 60;
+ if (uptime < 60) {
+ long minutes = (long) uptime;
+ String s = fmtI.format(minutes) + (minutes > 1 ? " minutes" : " minute");
+ return s;
+ }
+ uptime /= 60;
+ if (uptime < 24) {
+ long hours = (long) uptime;
+ long minutes = (long) ((uptime - hours) * 60);
+ String s = fmtI.format(hours) + (hours > 1 ? " hours" : " hour");
+ if (minutes != 0) {
+ s += " " + fmtI.format(minutes) + (minutes > 1 ? " minutes" : "minute");
+ }
+ return s;
+ }
+ uptime /= 24;
+ long days = (long) uptime;
+ long hours = (long) ((uptime - days) * 60);
+ String s = fmtI.format(days) + (days > 1 ? " days" : " day");
+ if (hours != 0) {
+ s += " " + fmtI.format(hours) + (hours > 1 ? " hours" : "hour");
+ }
+ return s;
+ }
+
+ void printSysValue(String prop, int pad) {
+ printValue(prop, pad, System.getProperty(prop));
+ }
+
+ void printValue(String name, int pad, String value) {
+ io.out.println(" " + renderer.render(AnsiRenderer.encode(StringUtils.rightPad(name, pad), AnsiCode.BOLD)) + " " + value);
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java
new file mode 100644
index 0000000..45d9d7d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/CommandParser.java
@@ -0,0 +1,474 @@
+/*
+ * 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.servicemix.kernel.gshell.core.config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Element;
+
+import org.apache.geronimo.gshell.command.Alias;
+import org.apache.geronimo.gshell.command.Link;
+import org.apache.geronimo.gshell.wisdom.command.AliasImpl;
+import org.apache.geronimo.gshell.wisdom.command.ConfigurableCommandCompleter;
+import org.apache.geronimo.gshell.wisdom.command.LinkImpl;
+import org.apache.geronimo.gshell.wisdom.registry.CommandLocationImpl;
+import org.apache.servicemix.kernel.gshell.core.CommandBundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.RuntimeBeanReference;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.support.GenericBeanDefinition;
+import org.springframework.beans.factory.support.ManagedList;
+import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
+import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.util.StringUtils;
+import org.springframework.util.xml.DomUtils;
+
+public class CommandParser extends AbstractBeanDefinitionParser {
+
+ public static final String ID = ID_ATTRIBUTE;
+
+ public static final String DESCRIPTION = "description";
+
+ public static final String PLUGIN_TEMPLATE = "pluginTemplate";
+
+ public static final String ACTION = "action";
+
+ public static final String ACTION_ID = "actionId";
+
+ public static final String COMMAND_TEMPLATE_SUFFIX = "CommandTemplate";
+
+ public static final String COMMAND_BUNDLE = "command-bundle";
+
+ public static final String NAME = "name";
+
+ public static final String LOCATION = "location";
+
+ public static final String COMMANDS = "commands";
+
+ public static final String COMMAND = "command";
+
+ public static final String TYPE = "type";
+
+ public static final String DOCUMENTER = "documenter";
+
+ public static final String COMPLETER = "completer";
+
+ public static final String COMPLETERS = "completers";
+
+ public static final String BEAN = "bean";
+
+ public static final String REF = "ref";
+
+ public static final String NULL = "null";
+
+ public static final String MESSAGE_SOURCE = "message-source";
+
+ public static final String MESSAGES = "messages";
+
+ public static final String PROTOTYPE = "prototype";
+
+ public static final String ALIAS = "alias";
+
+ public static final String ALIASES = "aliases";
+
+ public static final String LINK = "link";
+
+ public static final String LINKS = "links";
+
+ public static final String TARGET = "target";
+
+ @Override
+ protected boolean shouldGenerateId() {
+ return true;
+ }
+
+ @Override
+ protected boolean shouldGenerateIdAsFallback() {
+ return true;
+ }
+
+ protected AbstractBeanDefinition parseInternal(final Element element, final ParserContext context) {
+ assert element != null;
+ assert context != null;
+
+ Builder builder = new Builder(context);
+ BeanDefinitionBuilder plugin = builder.parseCommandBundle(element);
+ return plugin.getBeanDefinition();
+ }
+
+ /**
+ * Helper to deal with command type.
+ */
+ private enum CommandType
+ {
+ STATELESS,
+ STATEFUL;
+
+ public static CommandType parse(final String text) {
+ assert text != null;
+
+ return valueOf(text.toUpperCase());
+ }
+
+ public String getTemplateName() {
+ return name().toLowerCase() + COMMAND_TEMPLATE_SUFFIX;
+ }
+
+ public void wire(final BeanDefinitionBuilder command, final BeanDefinitionHolder action) {
+ assert command != null;
+ assert action != null;
+
+ switch (this) {
+ case STATELESS:
+ command.addPropertyReference(ACTION, action.getBeanName());
+ break;
+
+ case STATEFUL:
+ command.addPropertyValue(ACTION_ID, action.getBeanName());
+ break;
+ }
+ }
+ }
+
+ /**
+ * Helper to build plugin related bean definitions.
+ */
+ private class Builder
+ {
+ private final Logger log = LoggerFactory.getLogger(getClass());
+
+ private ParserContext context;
+
+ public Builder(final ParserContext context) {
+ assert context != null;
+
+ this.context = context;
+ }
+
+ private String resolveId(final Element element, final BeanDefinition def) throws BeanDefinitionStoreException {
+ assert element != null;
+ assert def != null;
+
+ if (shouldGenerateId()) {
+ return context.getReaderContext().generateBeanName(def);
+ }
+
+ String id = element.getAttribute(ID_ATTRIBUTE);
+
+ if (!StringUtils.hasText(id) && shouldGenerateIdAsFallback()) {
+ id = context.getReaderContext().generateBeanName(def);
+ }
+
+ return id;
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private List<Element> getChildElements(final Element element, final String name) {
+ assert element != null;
+ assert name != null;
+
+ return DomUtils.getChildElementsByTagName(element, name);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private List<Element> getChildElements(final Element element, final String[] names) {
+ assert element != null;
+ assert names != null;
+
+ return DomUtils.getChildElementsByTagName(element, names);
+ }
+
+ @SuppressWarnings({"unchecked"})
+ private Element getChildElement(final Element element, final String name) {
+ assert element != null;
+ assert name != null;
+
+ List<Element> elements = DomUtils.getChildElementsByTagName(element, name);
+ if (elements != null && !elements.isEmpty()) {
+ return elements.get(0);
+ }
+ return null;
+ }
+
+ private BeanDefinitionParserDelegate createBeanDefinitionParserDelegate(final Element element) {
+ assert element != null;
+
+ BeanDefinitionParserDelegate parser = new BeanDefinitionParserDelegate(context.getReaderContext());
+ parser.initDefaults(element.getOwnerDocument().getDocumentElement());
+ return parser;
+ }
+
+ private BeanDefinitionHolder parseBeanDefinitionElement(final Element element) {
+ assert element != null;
+
+ BeanDefinitionParserDelegate parser = createBeanDefinitionParserDelegate(element);
+ return parser.parseBeanDefinitionElement(element);
+ }
+
+ private void parseAndApplyDescription(final Element element, final BeanDefinition def) {
+ assert element != null;
+ assert def != null;
+
+ Element desc = getChildElement(element, DESCRIPTION);
+ if (desc != null) {
+ if (def instanceof AbstractBeanDefinition) {
+ ((AbstractBeanDefinition)def).setDescription(desc.getTextContent());
+ }
+ }
+ }
+
+ private void parseAndApplyDescription(final Element element, final BeanDefinitionBuilder builder) {
+ assert element != null;
+ assert builder != null;
+
+ parseAndApplyDescription(element, builder.getRawBeanDefinition());
+ }
+
+ private BeanDefinitionHolder register(final BeanDefinitionHolder holder) {
+ assert holder != null;
+
+ registerBeanDefinition(holder, context.getRegistry());
+ return holder;
+ }
+
+ private BeanDefinitionHolder register(final BeanDefinition def, final String id) {
+ assert def != null;
+ assert id != null;
+
+ BeanDefinitionHolder holder = new BeanDefinitionHolder(def, id);
+ return register(holder);
+ }
+
+ //
+ // <gshell:command-bundle>
+ //
+
+ private BeanDefinitionBuilder parseCommandBundle(final Element element) {
+ assert element != null;
+
+ log.trace("Parse command bundle; element; {}", element);
+
+ BeanDefinitionBuilder bundle = BeanDefinitionBuilder.rootBeanDefinition(CommandBundle.class);
+ parseAndApplyDescription(element, bundle);
+
+ //
+ // TODO: Figure out how we can save the order of <gshell:command> and <gshell:link> so that 'help' displays them in the order they are defined
+ //
+
+ ManagedList commands = new ManagedList();
+ commands.addAll(parseCommands(element));
+ bundle.addPropertyValue(COMMANDS, commands);
+
+ ManagedList links = new ManagedList();
+ links.addAll(parseLinks(element));
+ bundle.addPropertyValue(LINKS, links);
+
+ ManagedList aliases = new ManagedList();
+ aliases.addAll(parseAliases(element));
+ bundle.addPropertyValue(ALIASES, aliases);
+
+ return bundle;
+ }
+
+ //
+ // <gshell:command>
+ //
+
+ private List<BeanDefinition> parseCommands(final Element element) {
+ assert element != null;
+
+ log.trace("Parse commands; element; {}", element);
+
+ List<BeanDefinition> commands = new ArrayList<BeanDefinition>();
+
+ List<Element> children = getChildElements(element, COMMAND);
+
+ for (Element child : children) {
+ BeanDefinitionBuilder command = parseCommand(child);
+ commands.add(command.getBeanDefinition());
+ }
+
+ return commands;
+ }
+
+ private BeanDefinitionBuilder parseCommand(final Element element) {
+ assert element != null;
+
+ log.trace("Parse command; element; {}", element);
+
+ CommandType type = CommandType.parse(element.getAttribute(TYPE));
+ BeanDefinitionBuilder command = BeanDefinitionBuilder.childBeanDefinition(type.getTemplateName());
+ parseAndApplyDescription(element, command);
+
+ Element child;
+
+ // Required children elements
+
+ String name = element.getAttribute(NAME);
+ BeanDefinition def = new GenericBeanDefinition();
+ def.setBeanClassName(CommandLocationImpl.class.getName());
+ def.getConstructorArgumentValues().addGenericArgumentValue(name);
+ command.addPropertyValue(LOCATION, def);
+
+ child = getChildElement(element, ACTION);
+ BeanDefinitionHolder action = parseCommandAction(child);
+ type.wire(command, action);
+
+ // Optional children elements
+
+ child = getChildElement(element, DOCUMENTER);
+ if (child != null) {
+ BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+ command.addPropertyValue(DOCUMENTER, holder.getBeanDefinition());
+ }
+
+ child = getChildElement(element, COMPLETER);
+ if (child != null) {
+ BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+ command.addPropertyValue(COMPLETER, holder.getBeanDefinition());
+ }
+
+ child = getChildElement(element, COMPLETERS);
+ if (child != null) {
+ BeanDefinitionBuilder completer = parseCommandCompleters(child);
+ command.addPropertyValue(COMPLETER, completer.getBeanDefinition());
+ }
+
+ child = getChildElement(element, MESSAGE_SOURCE);
+ if (child != null) {
+ BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+ command.addPropertyValue(MESSAGES, holder.getBeanDefinition());
+ }
+
+ //String id = resolveId(element, command.getBeanDefinition());
+ //BeanDefinitionHolder holder = register(command.getBeanDefinition(), id);
+
+ return command;
+ }
+
+ //
+ // <gshell:completers>
+ //
+
+ private BeanDefinitionBuilder parseCommandCompleters(final Element element) {
+ assert element != null;
+
+ BeanDefinitionBuilder completer = BeanDefinitionBuilder.rootBeanDefinition(ConfigurableCommandCompleter.class);
+
+ ManagedList completers = new ManagedList();
+
+ List<Element> children = getChildElements(element, new String[] {BEAN, REF, NULL});
+
+ for (Element child : children) {
+ if (DomUtils.nodeNameEquals(child, BEAN)) {
+ BeanDefinitionHolder holder = parseBeanDefinitionElement(child);
+ // noinspection unchecked
+ completers.add(holder.getBeanDefinition());
+ }
+ else if (DomUtils.nodeNameEquals(child, REF)) {
+ BeanDefinitionParserDelegate parser = createBeanDefinitionParserDelegate(child);
+ RuntimeBeanReference ref = (RuntimeBeanReference) parser.parsePropertySubElement(child, completer.getRawBeanDefinition());
+ // noinspection unchecked
+ completers.add(ref);
+ }
+ else if (DomUtils.nodeNameEquals(child, NULL)) {
+ // noinspection unchecked
+ completers.add(null);
+ }
+ }
+
+ completer.addConstructorArgValue(completers);
+
+ return completer;
+ }
+
+ //
+ // <gshell:action>
+ //
+
+ private BeanDefinitionHolder parseCommandAction(final Element element) {
+ assert element != null;
+
+ log.trace("Parse command action; element; {}", element);
+
+ // Construct the action
+ BeanDefinition action = parseBeanDefinitionElement(element).getBeanDefinition();
+
+ // All actions are configured as prototypes
+ action.setScope(PROTOTYPE);
+
+ // Generate id and register the bean
+ String id = resolveId(element, action);
+ return register(action, id);
+ }
+
+ //
+ // <gshell:link>
+ //
+
+ private List<Link> parseLinks(final Element element) {
+ assert element != null;
+
+ log.trace("Parse links; element; {}", element);
+
+ List<Link> links = new ArrayList<Link>();
+
+ List<Element> children = getChildElements(element, LINK);
+
+ for (Element child : children) {
+ String name = child.getAttribute(NAME);
+ String target = child.getAttribute(TARGET);
+
+ links.add(new LinkImpl(name, target));
+ }
+
+ return links;
+ }
+
+ //
+ // <gshell:alias>
+ //
+
+ private List<Alias> parseAliases(final Element element) {
+ assert element != null;
+
+ log.trace("Parse aliases; element; {}", element);
+
+ List<Alias> aliases = new ArrayList<Alias>();
+
+ List<Element> children = getChildElements(element, ALIAS);
+
+ for (Element child : children) {
+ String name = child.getAttribute(NAME);
+ String alias = child.getAttribute(ALIAS);
+
+ aliases.add(new AliasImpl(name, alias));
+ }
+
+ return aliases;
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java
new file mode 100644
index 0000000..3e464d3
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/config/NamespaceHandler.java
@@ -0,0 +1,29 @@
+/*
+ * 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.servicemix.kernel.gshell.core.config;
+
+import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+public class NamespaceHandler extends NamespaceHandlerSupport {
+
+ public void init() {
+ registerBeanDefinitionParser(CommandParser.COMMAND_BUNDLE, new CommandParser());
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java
new file mode 100644
index 0000000..34e2527
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/BogusPasswordAuthenticator.java
@@ -0,0 +1,29 @@
+/*
+ * 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.servicemix.kernel.gshell.core.sshd;
+
+import org.apache.sshd.server.PasswordAuthenticator;
+
+public class BogusPasswordAuthenticator implements PasswordAuthenticator {
+
+ public Object authenticate(String username, String password) {
+ return (username != null && username.equals(password)) ? username : null;
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.java
new file mode 100644
index 0000000..fbf9041
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/sshd/SshServerFactory.java
@@ -0,0 +1,58 @@
+/*
+ * 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.servicemix.kernel.gshell.core.sshd;
+
+import org.apache.sshd.SshServer;
+
+public class SshServerFactory {
+
+ private SshServer server;
+
+ private boolean start;
+
+ public SshServerFactory(SshServer server) {
+ this.server = server;
+ }
+
+ public boolean isStart() {
+ return start;
+ }
+
+ public void setStart(boolean start) {
+ this.start = start;
+ }
+
+ public void start() throws Exception {
+ if (start) {
+ try {
+ server.start();
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+ }
+
+ public void stop() throws Exception {
+ if (start) {
+ server.stop();
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.java
new file mode 100644
index 0000000..41a2bb8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileObject.java
@@ -0,0 +1,56 @@
+/*
+ * 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.servicemix.kernel.gshell.core.vfs.mvn;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.httpclient.URIException;
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.provider.URLFileName;
+import org.apache.commons.vfs.provider.url.UrlFileObject;
+
+public class MvnFileObject extends UrlFileObject {
+
+ public MvnFileObject(MvnFileSystem fs, FileName fileName) {
+ super(fs, fileName);
+ }
+
+ protected URL createURL(final FileName name) throws MalformedURLException, FileSystemException, URIException
+ {
+ String url;
+ if (name instanceof URLFileName)
+ {
+ URLFileName urlName = (URLFileName) getName();
+
+ // TODO: charset
+ url = urlName.getURIEncoded(null);
+ }
+ else
+ {
+ url = getName().getURI();
+ }
+ if (url.startsWith("mvn:///")) {
+ url = "mvn:" + url.substring("mvn:///".length());
+ }
+ return new URL(url);
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java
new file mode 100644
index 0000000..7d88cd6
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileProvider.java
@@ -0,0 +1,66 @@
+/*
+ * 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.servicemix.kernel.gshell.core.vfs.mvn;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystem;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.url.UrlFileProvider;
+
+public class MvnFileProvider extends UrlFileProvider {
+
+ /**
+ * Locates a file object, by absolute URI.
+ */
+ public synchronized FileObject findFile(final FileObject baseFile,
+ final String uri,
+ final FileSystemOptions fileSystemOptions)
+ throws FileSystemException
+ {
+ try
+ {
+ final URL url = new URL(uri);
+
+ URL rootUrl = new URL(url, "/");
+ final String key = this.getClass().getName() + rootUrl.toString();
+ FileSystem fs = findFileSystem(key, fileSystemOptions);
+ if (fs == null)
+ {
+ String extForm = rootUrl.toExternalForm();
+ final FileName rootName = getContext().parseURI(extForm);
+ // final FileName rootName =
+ // new BasicFileName(rootUrl, FileName.ROOT_PATH);
+ fs = new MvnFileSystem(rootName, fileSystemOptions);
+ addFileSystem(key, fs);
+ }
+ return fs.resolveFile(url.getPath());
+ }
+ catch (final MalformedURLException e)
+ {
+ throw new FileSystemException("vfs.provider.url/badly-formed-uri.error", uri, e);
+ }
+ }
+
+
+}
diff --git a/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.java b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.java
new file mode 100644
index 0000000..7583eb5
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/java/org/apache/servicemix/kernel/gshell/core/vfs/mvn/MvnFileSystem.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.core.vfs.mvn;
+
+import org.apache.commons.vfs.FileName;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemOptions;
+import org.apache.commons.vfs.provider.url.UrlFileSystem;
+
+public class MvnFileSystem extends UrlFileSystem {
+
+ protected MvnFileSystem(FileName fileName, FileSystemOptions fileSystemOptions) {
+ super(fileName, fileSystemOptions);
+ }
+
+ protected FileObject createFile(FileName fileName) {
+ return new MvnFileObject(this, fileName);
+ }
+}
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers
new file mode 100644
index 0000000..2315764
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.handlers
@@ -0,0 +1,24 @@
+##
+## 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.
+##
+
+##
+## $Rev: 697807 $ $Date: 2008-09-22 20:12:04 +0700 (Mon, 22 Sep 2008) $
+##
+
+http\://servicemix.apache.org/schema/servicemix-gshell=org.apache.servicemix.kernel.gshell.core.config.NamespaceHandler
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas
new file mode 100644
index 0000000..67eca68
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring.schemas
@@ -0,0 +1,24 @@
+##
+## 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.
+##
+
+##
+## $Rev: 697807 $ $Date: 2008-09-22 20:12:04 +0700 (Mon, 22 Sep 2008) $
+##
+
+http\://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd=org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml
new file mode 100644
index 0000000..dbcafd3
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-commands.xml
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <bean id="vfsCommandActionTemplate" abstract="true">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </bean>
+
+ <gshell:command-bundle>
+ <gshell:command name="about">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AboutAction">
+ <constructor-arg ref="application"/>
+ </gshell:action>
+ </gshell:command>
+
+ <gshell:command name="help">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HelpAction">
+ <constructor-arg ref="commandResolver"/>
+ </gshell:action>
+ <gshell:completers>
+ <bean class="org.apache.geronimo.gshell.console.completer.AggregateCompleter">
+ <constructor-arg>
+ <list>
+ <ref bean="aliasNameCompleter"/>
+ <ref bean="commandNameCompleter"/>
+ </list>
+ </constructor-arg>
+ </bean>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="exit">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ExitAction"/>
+ </gshell:command>
+
+ <gshell:link name="quit" target="exit"/>
+
+ <gshell:command name="echo">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.EchoAction"/>
+ </gshell:command>
+
+ <gshell:command name="clear" type="stateless">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ClearAction"/>
+ </gshell:command>
+
+ <gshell:command name="source">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SourceAction">
+ <constructor-arg ref="commandLineExecutor"/>
+ <constructor-arg ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="set">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SetAction"/>
+ </gshell:command>
+
+ <gshell:command name="unset">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnsetAction"/>
+ <gshell:completers>
+ <ref bean="variableNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="alias">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AliasAction">
+ <constructor-arg ref="aliasRegistry"/>
+ </gshell:action>
+ </gshell:command>
+
+ <gshell:command name="unalias">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnaliasAction">
+ <constructor-arg ref="aliasRegistry"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="aliasNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="history">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HistoryAction"/>
+ </gshell:command>
+
+ <gshell:command name="info">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.core.commands.InfoAction">
+ <constructor-arg ref="branding" />
+ </gshell:action>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="ssh">
+ <gshell:action class="org.apache.geronimo.gshell.commands.ssh.SshAction"/>
+ </gshell:command>
+
+ <gshell:command name="sshd">
+ <gshell:action class="org.apache.geronimo.gshell.commands.ssh.SshServerAction"/>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="exec">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.ExecuteAction"/>
+ </gshell:command>
+
+ <gshell:command name="java">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.JavaAction"/>
+ </gshell:command>
+
+ <gshell:command name="sleep">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.SleepAction"/>
+ </gshell:command>
+
+ <gshell:command name="date">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.DateAction"/>
+ </gshell:command>
+
+ <gshell:command name="edit">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.EditAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="find">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.FindAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="sort">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.SortAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="hostname">
+ <gshell:action class="org.apache.geronimo.gshell.commands.network.HostnameAction"/>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:link name="print" target="echo"/>
+
+ <gshell:command name="printf">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.PrintfAction"/>
+ </gshell:command>
+
+ <gshell:command name="cat">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.CatAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="grep">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.GrepAction"/>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="cd">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.ChangeDirectoryAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="pwd" type="stateless">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.CurrentDirectoryAction" parent="vfsCommandActionTemplate"/>
+ </gshell:command>
+
+ <gshell:command name="ls">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.ListDirectoryAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:link name="dir" target="ls"/>
+
+ <gshell:command name="cp">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.CopyAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:link name="copy" target="cp"/>
+
+ <gshell:command name="rm">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.RemoveAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:link name="del" target="rm"/>
+
+ <gshell:command name="fileinfo">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.FileInfoAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="touch">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.TouchAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml
new file mode 100644
index 0000000..a047339
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-local.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean id="localShellWrapped" parent="shell" init-method="init" destroy-method="close"/>
+
+ <bean id="localShell" class="org.apache.servicemix.kernel.gshell.core.ShellWrapper">
+ <constructor-arg ref="localShellWrapped" />
+ </bean>
+
+ <osgi:reference id="mainService" interface="org.apache.servicemix.kernel.main.spi.MainService"/>
+
+ <bean id="localConsole" class="org.apache.servicemix.kernel.gshell.core.LocalConsole">
+ <property name="createLocalShell" value="${servicemix.startLocalConsole}"/>
+ <property name="shell" ref="localShell"/>
+ <property name="mainService" ref="mainService" />
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml
new file mode 100644
index 0000000..b99626a
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-osgi.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ctx="http://www.springframework.org/schema/context"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean id="osgiCommandRegistry" class="org.apache.servicemix.kernel.gshell.core.OsgiCommandRegistry">
+ <constructor-arg ref="commandRegistry" />
+ </bean>
+
+ <bean id="osgiAliasRegistry" class="org.apache.servicemix.kernel.gshell.core.OsgiAliasRegistry">
+ <constructor-arg ref="aliasRegistry" />
+ </bean>
+
+ <osgi:list id="osgiCommands" interface="org.apache.geronimo.gshell.command.Command" cardinality="0..N">
+ <osgi:listener ref="osgiCommandRegistry" bind-method="register" unbind-method="unregister" />
+ </osgi:list>
+
+ <osgi:list id="osgiLinkCommands" interface="org.apache.geronimo.gshell.command.Link" cardinality="0..N">
+ <osgi:listener ref="osgiCommandRegistry" bind-method="register" unbind-method="unregister" />
+ </osgi:list>
+
+ <osgi:list id="osgiAliases" interface="org.apache.geronimo.gshell.command.Alias" cardinality="0..N">
+ <osgi:listener ref="osgiAliasRegistry" bind-method="register" unbind-method="unregister" />
+ </osgi:list>
+
+ <osgi:service ref="localShell" interface="org.apache.geronimo.gshell.shell.Shell">
+ </osgi:service>
+
+ <osgi:service ref="commandLineExecutor" interface="org.apache.geronimo.gshell.commandline.CommandLineExecutor">
+ </osgi:service>
+
+ <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.shell">
+ <prop key="sshPort">8101</prop>
+ <prop key="sshRealm">servicemix</prop>
+ <prop key="hostKey">${servicemix.base}/etc/host.key</prop>
+ </osgix:cm-properties>
+
+ <ctx:property-placeholder properties-ref="cmProps" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml
new file mode 100644
index 0000000..d68ee51
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-remote.xml
@@ -0,0 +1,91 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:jaas="http://servicemix.apache.org/jaas"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/jaas
+ http://servicemix.apache.org/schema/servicemix-jaas.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean name="sshClient" class="org.apache.sshd.SshClient" factory-method="setUpDefaultClient" init-method="start" destroy-method="stop">
+ </bean>
+
+ <bean name="sshServer" class="org.apache.sshd.SshServer" factory-method="setUpDefaultServer" scope="prototype">
+ <property name="port" value="${sshPort}" />
+ <property name="shellFactory">
+ <bean class="org.apache.geronimo.gshell.commands.ssh.ShellFactoryImpl">
+ <property name="application" ref="application" />
+ <property name="completers">
+ <list>
+ <ref bean="commandsCompleter"/>
+ <ref bean="aliasNameCompleter"/>
+ </list>
+ </property>
+ <property name="executor" ref="commandLineExecutor" />
+ <property name="prompter">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsolePrompterImpl">
+ <constructor-arg ref="application"/>
+ </bean>
+ </property>
+ <property name="errorHandler">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsoleErrorHandlerImpl" />
+ </property>
+ <property name="history">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.HistoryImpl">
+ <constructor-arg ref="application"/>
+ </bean>
+ </property>
+ </bean>
+ </property>
+ <property name="keyPairProvider" ref="keyPairProvider" />
+ <property name="passwordAuthenticator" ref="passwordAuthenticator" />
+ </bean>
+
+ <bean name="keyPairProvider" class="org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider">
+ <property name="path" value="${hostKey}" />
+ </bean>
+ <bean name="passwordAuthenticator" class="org.apache.sshd.server.jaas.JaasPasswordAuthenticator">
+ <property name="domain" value="${sshRealm}" />
+ </bean>
+
+ <bean id="sshServerFactory" class="org.apache.servicemix.kernel.gshell.core.sshd.SshServerFactory" init-method="start" destroy-method="stop">
+ <constructor-arg ref="sshServer" />
+ <property name="start" value="${servicemix.startRemoteShell}" />
+ </bean>
+
+ <!--
+ <jaas:config id="SshServer" rank="-1">
+ <jaas:module className="org.apache.geronimo.gshell.remote.server.auth.BogusLoginModule" flags="required" />
+ </jaas:config>
+ -->
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml
new file mode 100644
index 0000000..3992f54
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell-vfs.xml
@@ -0,0 +1,133 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean id="fileSystemAccess" class="org.apache.geronimo.gshell.vfs.FileSystemAccessImpl">
+ <constructor-arg ref="fileSystemManager"/>
+ </bean>
+
+ <bean id="fileObjectNameCompleter" class="org.apache.geronimo.gshell.vfs.FileObjectNameCompleter">
+ <constructor-arg ref="fileSystemAccess"/>
+ </bean>
+
+ <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
+ <property name="customEditors">
+ <map>
+ <entry key="org.apache.commons.vfs.CacheStrategy">
+ <bean class="org.apache.geronimo.gshell.vfs.config.CacheStrategyEditor"/>
+ </entry>
+ </map>
+ </property>
+ </bean>
+
+ <!--
+ TODO: Set tempDir File arg in constructor to set the temporary location, may need 2 replicators?
+ -->
+ <bean id="defaultFileReplicator" class="org.apache.commons.vfs.impl.DefaultFileReplicator"/>
+
+ <bean id="fileSystemManager" class="org.apache.geronimo.gshell.vfs.config.FileSystemManagerFactoryBean">
+ <property name="filesCache">
+ <bean class="org.apache.commons.vfs.cache.SoftRefFilesCache"/>
+ </property>
+
+ <property name="cacheStrategy" value="ON_RESOLVE"/>
+
+ <property name="fileReplicator">
+ <bean class="org.apache.commons.vfs.impl.PrivilegedFileReplicator">
+ <constructor-arg ref="defaultFileReplicator"/>
+ </bean>
+ </property>
+
+ <!--
+ TODO: Try and root the temporary store under ${gshell.home}/tmp or something
+ -->
+ <property name="temporaryFileStore">
+ <ref bean="defaultFileReplicator"/>
+ </property>
+
+ <property name="fileContentInfoFactory">
+ <bean class="org.apache.commons.vfs.impl.FileContentInfoFilenameFactory"/>
+ </property>
+
+ <property name="defaultProvider">
+ <bean class="org.apache.commons.vfs.provider.url.UrlFileProvider"/>
+ </property>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="tmp"/>
+ <property name="provider">
+ <bean class="org.apache.commons.vfs.provider.temp.TemporaryFileProvider"/>
+ </property>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="ram"/>
+ <property name="provider">
+ <bean class="org.apache.commons.vfs.provider.ram.RamFileProvider"/>
+ </property>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="file"/>
+ <property name="provider">
+ <bean class="org.apache.commons.vfs.provider.local.DefaultLocalFileProvider"/>
+ </property>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="mvn"/>
+ <property name="provider">
+ <bean class="org.apache.servicemix.kernel.gshell.core.vfs.mvn.MvnFileProvider"/>
+ </property>
+ </bean>
+
+ <bean id="metaDataRegistry" class="org.apache.geronimo.gshell.vfs.provider.meta.data.MetaDataRegistryImpl">
+ <constructor-arg ref="eventManager" />
+ </bean>
+
+ <bean id="metaFileProviderConfigurer" class="org.apache.geronimo.gshell.vfs.config.FileProviderConfigurer">
+ <property name="fileSystemManager" ref="fileSystemManager"/>
+ <property name="scheme" value="meta"/>
+ <property name="provider">
+ <bean class="org.apache.geronimo.gshell.vfs.provider.meta.MetaFileProvider">
+ <constructor-arg ref="metaDataRegistry"/>
+ </bean>
+ </property>
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml
new file mode 100644
index 0000000..8bc494e
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/META-INF/spring/gshell.xml
@@ -0,0 +1,155 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util" xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xsi:schemaLocation="http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <bean id="io" class="org.apache.geronimo.gshell.io.IO">
+ <property name="verbosity" value="DEBUG"/>
+ </bean>
+
+ <bean id="branding" class="org.apache.servicemix.kernel.gshell.core.ServiceMixBranding">
+ <property name="prompt" value="@|bold %{gshell.username}|@%{application.id}:@|bold %{gshell.group}|> " />
+ </bean>
+
+ <bean id="application" class="org.apache.servicemix.kernel.gshell.core.ApplicationImpl">
+ <property name="id" value="${servicemix.name}"/>
+ <property name="io" ref="io"/>
+ <property name="model">
+ <bean class="org.apache.geronimo.gshell.application.model.ApplicationModel">
+ <property name="branding" ref="branding"/>
+ </bean>
+ </property>
+ <property name="variables">
+ <bean class="org.apache.geronimo.gshell.command.Variables"/>
+ </property>
+ </bean>
+
+ <bean id="eventManager" class="org.apache.geronimo.gshell.event.EventManagerImpl"/>
+
+ <bean id="applicationManager" class="org.apache.servicemix.kernel.gshell.core.ApplicationManagerImpl">
+ <constructor-arg ref="eventManager" />
+ <constructor-arg ref="application" />
+ </bean>
+
+ <bean id="commandLineParser" class="org.apache.geronimo.gshell.parser.CommandLineParser"/>
+
+ <bean id="aliasRegistry" class="org.apache.geronimo.gshell.wisdom.registry.AliasRegistryImpl">
+ <constructor-arg ref="eventManager"/>
+ </bean>
+
+ <bean id="aliasMetaMapper" class="org.apache.geronimo.gshell.wisdom.registry.AliasMetaMapper">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="metaDataRegistry"/>
+ <constructor-arg ref="aliasRegistry"/>
+ </bean>
+
+ <bean id="commandRegistry" class="org.apache.geronimo.gshell.wisdom.registry.CommandRegistryImpl">
+ <constructor-arg ref="eventManager"/>
+ </bean>
+
+ <bean id="commandMetaMapper" class="org.apache.geronimo.gshell.wisdom.registry.CommandMetaMapper">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="metaDataRegistry"/>
+ <constructor-arg ref="commandRegistry"/>
+ </bean>
+
+ <bean id="groupDirResolver" class="org.apache.geronimo.gshell.wisdom.registry.GroupDirectoryResolver">
+ <constructor-arg ref="fileSystemAccess"/>
+ </bean>
+
+ <bean id="commandResolver" class="org.apache.geronimo.gshell.wisdom.registry.CommandResolverImpl">
+ <constructor-arg ref="fileSystemAccess"/>
+ <constructor-arg ref="groupDirResolver"/>
+ </bean>
+
+ <bean class="org.apache.servicemix.kernel.gshell.core.WorkAroundAliasCommand" scope="prototype">
+ <constructor-arg ref="commandLineExecutor"/>
+ </bean>
+
+ <bean class="org.apache.geronimo.gshell.wisdom.command.GroupCommand" scope="prototype"/>
+
+ <bean id="commandLineBuilder" class="org.apache.geronimo.gshell.wisdom.shell.CommandLineBuilderImpl">
+ <constructor-arg ref="commandLineParser"/>
+ </bean>
+
+ <bean id="commandLineExecutor" class="org.apache.geronimo.gshell.wisdom.shell.CommandLineExecutorImpl">
+ <constructor-arg ref="commandResolver"/>
+ <constructor-arg ref="commandLineBuilder"/>
+ </bean>
+
+ <bean id="shell" class="org.apache.geronimo.gshell.wisdom.shell.ShellImpl" scope="prototype" init-method="init" destroy-method="close">
+ <constructor-arg ref="application"/>
+ <constructor-arg ref="commandLineExecutor"/>
+
+ <property name="completers">
+ <list>
+ <ref bean="commandsCompleter"/>
+ <ref bean="aliasNameCompleter"/>
+ </list>
+ </property>
+ <property name="prompter">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsolePrompterImpl">
+ <constructor-arg ref="application"/>
+ </bean>
+ </property>
+ <property name="errorHandler">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.ConsoleErrorHandlerImpl" />
+ </property>
+ <property name="history">
+ <bean class="org.apache.geronimo.gshell.wisdom.shell.HistoryImpl">
+ <constructor-arg ref="application"/>
+ </bean>
+ </property>
+ </bean>
+
+ <bean id="commandNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.CommandNameCompleter"
+ lazy-init="true">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="commandRegistry"/>
+ </bean>
+
+ <bean id="aliasNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.AliasNameCompleter"
+ lazy-init="true">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="aliasRegistry"/>
+ </bean>
+
+ <bean id="commandsCompleter" class="org.apache.geronimo.gshell.wisdom.completer.CommandsCompleter" lazy-init="true">
+ <constructor-arg ref="eventManager"/>
+ <constructor-arg ref="commandRegistry"/>
+ </bean>
+
+ <bean id="variableNameCompleter" class="org.apache.geronimo.gshell.wisdom.completer.VariableNameCompleter" lazy-init="true">
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties b/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties
new file mode 100644
index 0000000..e4e787d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/geronimo/gshell/commands/text/SortAction.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Sort lines of text.
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml
new file mode 100644
index 0000000..8b91ee1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd">
+
+ <bean class="org.apache.servicemix.kernel.gshell.core.BeanContainerAwareProcessor" />
+
+ <bean id="statelessCommandTemplate" class="org.apache.geronimo.gshell.wisdom.command.StatelessCommand" abstract="true">
+ <property name="documenter">
+ <bean class="org.apache.geronimo.gshell.wisdom.command.MessageSourceCommandDocumenter"/>
+ </property>
+
+ <property name="messages">
+ <bean class="org.apache.geronimo.gshell.wisdom.command.CommandMessageSource"/>
+ </property>
+ </bean>
+
+ <bean id="statefulCommandTemplate" class="org.apache.geronimo.gshell.wisdom.command.StatefulCommand" abstract="true">
+ <property name="documenter">
+ <bean class="org.apache.geronimo.gshell.wisdom.command.MessageSourceCommandDocumenter"/>
+ </property>
+
+ <property name="messages">
+ <bean class="org.apache.geronimo.gshell.wisdom.command.CommandMessageSource"/>
+ </property>
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties
new file mode 100644
index 0000000..755f0c8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/commands/InfoAction.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display JVM informations about the current application.
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
new file mode 100644
index 0000000..36a1e1d
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/main/resources/org/apache/servicemix/kernel/gshell/core/servicemix-gshell.xsd
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+
+<!-- $Rev: 699828 $ $Date: 2008-09-28 16:35:27 +0200 (Sun, 28 Sep 2008) $ -->
+
+<xsd:schema xmlns="http://servicemix.apache.org/schema/servicemix-gshell"
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:beans="http://www.springframework.org/schema/beans"
+ targetNamespace="http://servicemix.apache.org/schema/servicemix-gshell"
+ elementFormDefault="qualified"
+ attributeFormDefault="unqualified">
+
+ <xsd:import namespace="http://www.springframework.org/schema/beans"/>
+
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the configuration elements for Apache ServiceMix Kernel commands support.
+ </xsd:documentation>
+ </xsd:annotation>
+
+ <xsd:element name="command-bundle">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command bundle.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="command"/>
+ <xsd:element ref="alias"/>
+ <xsd:element ref="link"/>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="command">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+ <!--
+ NOTE: Not using an xsd:choice here, as I can't seem to figure out how to get it to properly
+ validate the min/max of the containted elements. W/o the xsd:choice the validation
+ works, though have to define elements in order :-(
+ -->
+ <xsd:element ref="action" minOccurs="1" maxOccurs="1"/>
+ <xsd:element ref="documenter" minOccurs="0" maxOccurs="1"/>
+ <xsd:choice minOccurs="0" maxOccurs="1">
+ <xsd:element ref="completer"/>
+ <xsd:element ref="completers"/>
+ </xsd:choice>
+ <xsd:element ref="message-source" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="type" use="optional" default="stateful">
+ <xsd:annotation>
+ <xsd:documentation>
+ The command type.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="stateless"/>
+ <xsd:enumeration value="stateful"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="link">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a link command.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="target" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:group name="commandComponentElements">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the valid elements for command components. This is based on beans:beanElements,
+ stripping off the bits which are not valid in the command component context.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0"/>
+ <xsd:choice minOccurs="0" maxOccurs="unbounded">
+ <xsd:element ref="beans:meta"/>
+ <xsd:element ref="beans:constructor-arg"/>
+ <xsd:element ref="beans:property"/>
+ <xsd:element ref="beans:qualifier"/>
+ <xsd:element ref="beans:lookup-method"/>
+ <xsd:element ref="beans:replaced-method"/>
+ <!--
+ NOTE: This seems to cause schema validation problems... not really sure why
+ <xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+ -->
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:group>
+
+ <xsd:attributeGroup name="commandComponentAttributes">
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines the valid attributes for command components. This is based on beans:beanAttributes,
+ stripping off the bits which are not valid in the command component context.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:attribute name="class" type="xsd:string"/>
+ <xsd:attribute name="parent" type="xsd:string"/>
+ <xsd:attribute name="autowire" default="default">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="default"/>
+ <xsd:enumeration value="no"/>
+ <xsd:enumeration value="byName"/>
+ <xsd:enumeration value="byType"/>
+ <xsd:enumeration value="constructor"/>
+ <xsd:enumeration value="autodetect"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ <xsd:attribute name="dependency-check" default="default">
+ <xsd:simpleType>
+ <xsd:restriction base="xsd:NMTOKEN">
+ <xsd:enumeration value="default"/>
+ <xsd:enumeration value="none"/>
+ <xsd:enumeration value="simple"/>
+ <xsd:enumeration value="objects"/>
+ <xsd:enumeration value="all"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+ </xsd:attribute>
+ <xsd:attribute name="depends-on" type="xsd:string"/>
+ <xsd:attribute name="autowire-candidate" default="default" type="beans:defaultable-boolean"/>
+ <xsd:attribute name="primary" type="xsd:boolean"/>
+ <xsd:attribute name="init-method" type="xsd:string"/>
+ <xsd:attribute name="destroy-method" type="xsd:string"/>
+ <xsd:attribute name="factory-method" type="xsd:string"/>
+ <xsd:attribute name="factory-bean" type="xsd:string"/>
+ <xsd:anyAttribute namespace="##other" processContents="lax"/>
+ </xsd:attributeGroup>
+
+ <xsd:complexType name="commandComponent" abstract="true">
+ <xsd:annotation>
+ <xsd:documentation>
+ Support for command component elements, which are all basically just beans.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:group ref="commandComponentElements"/>
+ <xsd:attributeGroup ref="commandComponentAttributes"/>
+ </xsd:complexType>
+
+ <xsd:element name="action">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command action.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexContent>
+ <xsd:extension base="commandComponent"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="documenter">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command documenter.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexContent>
+ <xsd:extension base="commandComponent"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="completer">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command completer.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexContent>
+ <xsd:extension base="commandComponent"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="completers">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a configurable command completer with a set of completers.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:choice minOccurs="1" maxOccurs="unbounded">
+ <xsd:element ref="beans:bean"/>
+ <xsd:element ref="beans:ref"/>
+ <xsd:element ref="beans:null"/>
+ </xsd:choice>
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="message-source">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command message source.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:complexContent>
+ <xsd:extension base="commandComponent"/>
+ </xsd:complexContent>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:element name="alias">
+ <xsd:complexType>
+ <xsd:annotation>
+ <xsd:documentation>
+ Defines a command alias.
+ </xsd:documentation>
+ </xsd:annotation>
+ <xsd:sequence>
+ <xsd:element ref="beans:description" minOccurs="0" maxOccurs="1"/>
+ </xsd:sequence>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="alias" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+
+</xsd:schema>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java b/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java
new file mode 100644
index 0000000..90e405e
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/java/org/apache/geronimo/gshell/commands/text/SortTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.geronimo.gshell.commands.text;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Collections;
+
+import junit.framework.TestCase;
+
+/**
+ * TODO: remove this file when gshell is upgraded
+ */
+public class SortTest extends TestCase {
+
+ public void testFieldIndexesDefaultSep() {
+ SortAction.SortComparator comparator = new SortAction.SortComparator(false, false, false, false, '\0', null);
+ List<Integer> indexes = comparator.getFieldIndexes(" ad re t ");
+ assertTrue(Arrays.asList(0, 2, 3, 6, 7, 9, 10, 10).equals(indexes));
+ }
+
+ public void testFieldIndexesWithSep() {
+ SortAction.SortComparator comparator = new SortAction.SortComparator(false, false, false, false, '[', null);
+ List<Integer> indexes = comparator.getFieldIndexes("[ 10] [Active ] [ ] [ 8] OPS4J Pax Logging - Service (1.3.0)");
+ assertTrue(Arrays.asList(1, 6, 8, 20, 22, 30, 32, 73 ).equals(indexes));
+
+ indexes = comparator.getFieldIndexes(" ad re t ");
+ assertTrue(Arrays.asList(0, 10).equals(indexes));
+ }
+
+ public void testSort() {
+ String s0 = "0321 abcd ddcba a";
+ String s1 = " 57t bcad ddacb b";
+ String s2 = " 128 cab ddbac c";
+ List<String> strings = Arrays.asList(s0, s1, s2);
+
+ Collections.sort(strings, new SortAction.SortComparator(false, false, false, false, '\0', Arrays.asList("2")));
+ assertTrue(Arrays.asList(s0, s1, s2).equals(strings));
+
+ Collections.sort(strings, new SortAction.SortComparator(false, false, false, false, '\0', Arrays.asList("2.2b")));
+ assertTrue(Arrays.asList(s2, s0, s1).equals(strings));
+
+ Collections.sort(strings, new SortAction.SortComparator(false, false, false, true, '\0', null));
+ assertTrue(Arrays.asList(s1, s2, s0).equals(strings));
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java b/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java
new file mode 100644
index 0000000..0983b12
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/java/org/apache/servicemix/kernel/gshell/core/Test.java
@@ -0,0 +1,154 @@
+/*
+ * 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.servicemix.kernel.gshell.core;
+
+import junit.framework.TestCase;
+import org.apache.geronimo.gshell.application.ApplicationManager;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+public class Test extends TestCase {
+
+ public void test() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "META-INF/spring/gshell-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml" });
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ assertNotNull(shell);
+ shell.execute("help");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+ public void testBanner() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "META-INF/spring/gshell-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+ assertNotNull(smxBrandng.getWelcomeMessage());
+ System.out.println(smxBrandng.getWelcomeMessage());
+ assertNotNull(shell);
+ shell.execute("about");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+ public void testLs() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+ assertNotNull(smxBrandng.getWelcomeMessage());
+ System.out.println(smxBrandng.getWelcomeMessage());
+ assertNotNull(shell);
+ shell.execute("vfs/ls meta:/commands/");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+ public void testCommandGroups() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ ServiceMixBranding smxBrandng = (ServiceMixBranding)appMgr.getApplication().getModel().getBranding();
+ assertNotNull(smxBrandng.getWelcomeMessage());
+ System.out.println(smxBrandng.getWelcomeMessage());
+ assertNotNull(shell);
+
+ shell.execute("vfs");
+ shell.execute("help");
+ shell.execute("..");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+ public void testFileAccessCommands() throws Exception {
+ System.setProperty("startLocalConsole", "true");
+ System.setProperty("servicemix.name", "root");
+
+ ClassPathXmlApplicationContext context = null;
+ try {
+ context = new ClassPathXmlApplicationContext(
+ new String[] { "META-INF/spring/gshell.xml",
+ "META-INF/spring/gshell-vfs.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml",
+ "org/apache/servicemix/kernel/gshell/core/gshell-test.xml"});
+ ApplicationManager appMgr = (ApplicationManager) context.getBean("applicationManager");
+ assertNotNull(appMgr);
+ Shell shell = appMgr.create();
+ assertNotNull(shell);
+ shell.execute("optional/cat src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml");
+ shell.execute("optional/find src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml");
+ } finally {
+ if (context != null) {
+ context.destroy();
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-core/src/test/resources/log4j.properties b/karaf/gshell/gshell-core/src/test/resources/log4j.properties
new file mode 100644
index 0000000..8cc85f8
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/log4j.properties
@@ -0,0 +1,33 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, stdout
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml
new file mode 100644
index 0000000..25e4de1
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test-commands.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd"
+ default-autowire="no"
+ default-dependency-check="none"
+ default-init-method="init"
+ default-destroy-method="destroy">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <bean id="vfsCommandActionTemplate" abstract="true">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </bean>
+
+ <gshell:command-bundle>
+ <gshell:command name="about">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AboutAction">
+ <constructor-arg ref="application"/>
+ </gshell:action>
+ </gshell:command>
+
+ <gshell:command name="help">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HelpAction">
+ <constructor-arg ref="commandResolver"/>
+ </gshell:action>
+ <gshell:completers>
+ <bean class="org.apache.geronimo.gshell.console.completer.AggregateCompleter">
+ <constructor-arg>
+ <list>
+ <ref bean="aliasNameCompleter"/>
+ <ref bean="commandNameCompleter"/>
+ </list>
+ </constructor-arg>
+ </bean>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="exit">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ExitAction"/>
+ </gshell:command>
+
+ <gshell:link name="quit" target="exit"/>
+
+ <gshell:command name="echo">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.EchoAction"/>
+ </gshell:command>
+
+ <gshell:command name="clear" type="stateless">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.ClearAction"/>
+ </gshell:command>
+
+ <gshell:command name="source">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SourceAction">
+ <constructor-arg ref="commandLineExecutor"/>
+ <constructor-arg ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="set">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.SetAction"/>
+ </gshell:command>
+
+ <gshell:command name="unset">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnsetAction"/>
+ <gshell:completers>
+ <ref bean="variableNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="alias">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.AliasAction">
+ <constructor-arg ref="aliasRegistry"/>
+ </gshell:action>
+ </gshell:command>
+
+ <gshell:command name="unalias">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.UnaliasAction">
+ <constructor-arg ref="aliasRegistry"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="aliasNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="history">
+ <gshell:action class="org.apache.geronimo.gshell.commands.builtin.HistoryAction"/>
+ </gshell:command>
+
+ <gshell:command name="info">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.core.commands.InfoAction">
+ <constructor-arg ref="branding" />
+ </gshell:action>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="vfs/ls">
+ <gshell:action class="org.apache.geronimo.gshell.commands.file.ListDirectoryAction" parent="vfsCommandActionTemplate"/>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <gshell:command-bundle>
+ <gshell:command name="optional/cat">
+ <gshell:action class="org.apache.geronimo.gshell.commands.text.CatAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+
+ <gshell:command name="optional/find">
+ <gshell:action class="org.apache.geronimo.gshell.commands.shell.FindAction">
+ <property name="fileSystemAccess" ref="fileSystemAccess"/>
+ </gshell:action>
+ <gshell:completers>
+ <ref bean="fileObjectNameCompleter"/>
+ <null/>
+ </gshell:completers>
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml
new file mode 100644
index 0000000..127543f
--- /dev/null
+++ b/karaf/gshell/gshell-core/src/test/resources/org/apache/servicemix/kernel/gshell/core/gshell-test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/pom.xml b/karaf/gshell/gshell-features/pom.xml
new file mode 100644
index 0000000..f53d9a8
--- /dev/null
+++ b/karaf/gshell/gshell-features/pom.xml
@@ -0,0 +1,114 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.features</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Features</name>
+
+ <description>
+ Provides Features in GShell
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.bundlerepository</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>org.apache.servicemix.kernel.filemonitor</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.easymock</groupId>
+ <artifactId>easymock</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.gshell.features*;version=${project.version}
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.servicemix.kernel.gshell.core,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.springframework.beans.factory.config,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.java
new file mode 100644
index 0000000..45e8aa1
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Feature.java
@@ -0,0 +1,39 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A feature is a list of bundles associated identified by its name.
+ */
+public interface Feature {
+
+ String getId();
+
+ String getName();
+
+ String getVersion();
+
+ List<Feature> getDependencies();
+
+ List<String> getBundles();
+
+ Map<String, Map<String, String>> getConfigurations();
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.java
new file mode 100644
index 0000000..33e1f6e
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesRegistry.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+/**
+ * Main interface for a Feature Registry which tracks available and installed features.
+ * Tracks features and repositories.
+ */
+public interface FeaturesRegistry {
+ void register(Feature feature);
+
+ void unregister(Feature feature);
+
+ void registerInstalled(Feature feature);
+
+ void unregisterInstalled(Feature feature);
+
+ void register(Repository repository);
+
+ void unregister(Repository repository);
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.java
new file mode 100644
index 0000000..1226016
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/FeaturesService.java
@@ -0,0 +1,44 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+/**
+ * The service managing features repositories.
+ */
+public interface FeaturesService {
+
+ void addRepository(URI url) throws Exception;
+
+ void removeRepository(URI url);
+
+ Repository[] listRepositories();
+
+ void installFeature(String name) throws Exception;
+
+ void installFeature(String name, String version) throws Exception;
+
+ void uninstallFeature(String name) throws Exception;
+
+ void uninstallFeature(String name, String version) throws Exception;
+
+ String[] listFeatures() throws Exception;
+
+ String[] listInstalledFeatures();
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.java
new file mode 100644
index 0000000..2bfbc99
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/Repository.java
@@ -0,0 +1,32 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+/**
+ * A repository of features.
+ */
+public interface Repository {
+
+ URI getURI();
+
+ URI[] getRepositories() throws Exception;
+
+ Feature[] getFeatures() throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.java
new file mode 100644
index 0000000..213e1e5
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class AddUrlCommand extends FeaturesCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Repository URLs")
+ List<String> urls;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ for (String url : urls) {
+ admin.addRepository(new URI(url));
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.java
new file mode 100644
index 0000000..06e0636
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/FeaturesCommandSupport.java
@@ -0,0 +1,49 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.osgi.framework.ServiceReference;
+
+public abstract class FeaturesCommandSupport extends OsgiCommandSupport {
+
+ protected Object doExecute() throws Exception {
+ // Get repository admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(FeaturesService.class.getName());
+ if (ref == null) {
+ io.out.println("FeaturesService service is unavailable.");
+ return null;
+ }
+ try {
+ FeaturesService admin = (FeaturesService) getBundleContext().getService(ref);
+ if (admin == null) {
+ io.out.println("FeaturesService service is unavailable.");
+ return null;
+ }
+
+ doExecute(admin);
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return null;
+ }
+
+ protected abstract void doExecute(FeaturesService admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.java
new file mode 100644
index 0000000..a170f3c
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.java
@@ -0,0 +1,36 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class InstallFeatureCommand extends FeaturesCommandSupport {
+
+ @Argument(required = true, description = "The name of the feature")
+ String name;
+ @Argument(description = "The version of the feature", index = 1)
+ String version;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ if (version != null && version.length() > 0) {
+ admin.installFeature(name, version);
+ } else {
+ admin.installFeature(name);
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java
new file mode 100644
index 0000000..fba0475
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.java
@@ -0,0 +1,48 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class ListFeaturesCommand extends FeaturesCommandSupport {
+
+ @Option(name = "-i", aliases={"--installed"}, description="Display the list of installed features")
+ boolean installed;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ String[] features;
+ if (installed) {
+ features = admin.listInstalledFeatures();
+ } else {
+ // Print column headers.
+ io.out.println(" State Version Name");
+ features = admin.listFeatures();
+ }
+ if ((features != null) && (features.length > 0)) {
+ for (int i = 0; i < features.length; i++) {
+ io.out.println(features[i]);
+ }
+ } else {
+ if (installed) {
+ io.out.println("No features installed.");
+ } else {
+ io.out.println("No features available.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java
new file mode 100644
index 0000000..4c84e09
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.java
@@ -0,0 +1,34 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+
+public class ListUrlCommand extends FeaturesCommandSupport {
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ Repository[] repos = admin.listRepositories();
+ if ((repos != null) && (repos.length > 0)) {
+ for (int i = 0; i < repos.length; i++) {
+ io.out.println(repos[i].getURI());
+ }
+ } else {
+ io.out.println("No repository URLs are set.");
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.java
new file mode 100644
index 0000000..cd594b1
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.java
@@ -0,0 +1,45 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+
+public class RefreshUrlCommand extends FeaturesCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "Repository URLs (leave empty for all)")
+ List<String> urls;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ if (urls == null || urls.isEmpty()) {
+ urls = new ArrayList<String>();
+ for (Repository repo : admin.listRepositories()) {
+ urls.add(repo.getURI().toString());
+ }
+ }
+ for (String strUri : urls) {
+ URI uri = new URI(strUri);
+ admin.removeRepository(uri);
+ admin.addRepository(uri);
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.java
new file mode 100644
index 0000000..d7e6e01
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class RemoveUrlCommand extends FeaturesCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Repository URLs")
+ List<String> urls;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ for (String url : urls) {
+ admin.removeRepository(new URI(url));
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.java
new file mode 100644
index 0000000..522c721
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.java
@@ -0,0 +1,36 @@
+/*
+ * 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.servicemix.kernel.gshell.features.commands;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+
+public class UninstallFeatureCommand extends FeaturesCommandSupport {
+
+ @Argument(required = true, description = "The name of the feature")
+ String name;
+ @Argument(description = "The version of the feature", index = 1)
+ String version;
+
+ protected void doExecute(FeaturesService admin) throws Exception {
+ if (version != null && version.length() > 0) {
+ admin.uninstallFeature(name, version);
+ } else {
+ admin.uninstallFeature(name );
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.java
new file mode 100644
index 0000000..991ba59
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/AvailableFeatureCompleter.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicemix.kernel.gshell.features.completers;
+
+import java.util.Collection;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeature;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for available features.
+ *
+ * Displays a list of available features from installed repositories.
+ *
+ */
+public class AvailableFeatureCompleter implements Completor {
+
+ private ManagedFeaturesRegistry featuresRegistry;
+ private StringsCompleter delegate;
+
+ public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+ this.featuresRegistry = featuresRegistry;
+ }
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+
+ Collection<ManagedFeature> features = featuresRegistry.getAvailableFeatures().values();
+ delegate = new StringsCompleter();
+
+ for (ManagedFeature feature : features) {
+ delegate.getStrings().add(feature.getName());
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.java
new file mode 100644
index 0000000..461ca32
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/FeatureRepositoryCompleter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.servicemix.kernel.gshell.features.completers;
+
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for Feature Repository URLs.
+ *
+ * Displays a list of currently installed Feature repositories.
+ *
+ */
+
+public class FeatureRepositoryCompleter implements Completor {
+
+ private ManagedFeaturesRegistry featuresRegistry;
+
+ public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+ this.featuresRegistry = featuresRegistry;
+ }
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+ StringsCompleter delegate = new StringsCompleter(featuresRegistry.getRepositories().keySet());
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.java
new file mode 100644
index 0000000..6a121a6
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/completers/InstalledFeatureCompleter.java
@@ -0,0 +1,54 @@
+/*
+ * 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.servicemix.kernel.gshell.features.completers;
+
+import java.util.Collection;
+import java.util.List;
+
+import jline.Completor;
+import org.apache.geronimo.gshell.console.completer.StringsCompleter;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeature;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+
+/**
+ * {@link jline.Completor} for installed features.
+ *
+ * Displays a list of currently installed features.
+ *
+ */
+public class InstalledFeatureCompleter implements Completor {
+
+ private ManagedFeaturesRegistry featuresRegistry;
+ private StringsCompleter delegate;
+
+ public void setFeaturesRegistry(ManagedFeaturesRegistry featuresRegistry) {
+ this.featuresRegistry = featuresRegistry;
+ }
+
+ public int complete(final String buffer, final int cursor, final List candidates) {
+ Collection<ManagedFeature> features = featuresRegistry.getInstalledFeatures().values();
+ delegate = new StringsCompleter();
+
+ for (ManagedFeature feature : features) {
+ delegate.getStrings().add(feature.getName());
+ }
+
+ return delegate.complete(buffer, cursor, candidates);
+ }
+
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.java
new file mode 100644
index 0000000..e4a036b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureDeploymentListener.java
@@ -0,0 +1,267 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.ZipEntry;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.w3c.dom.Document;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.filemonitor.DeploymentListener;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.SynchronousBundleListener;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.osgi.context.BundleContextAware;
+
+/**
+ * A deployment listener able to hot deploy a feature descriptor
+ */
+public class FeatureDeploymentListener implements DeploymentListener, SynchronousBundleListener, BundleContextAware,
+ InitializingBean, DisposableBean {
+
+ public static final String FEATURE_PATH = "org.apache.servicemix.kernel.gshell.features";
+
+ private static final Log LOGGER = LogFactory.getLog(FeatureDeploymentListener.class);
+
+ private DocumentBuilderFactory dbf;
+ private FeaturesServiceImpl featuresService;
+ private BundleContext bundleContext;
+
+ public void setFeaturesService(FeaturesServiceImpl featuresService) {
+ this.featuresService = featuresService;
+ }
+
+ public FeaturesServiceImpl getFeaturesService() {
+ return featuresService;
+ }
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ bundleContext.addBundleListener(this);
+ for (Bundle bundle : bundleContext.getBundles()) {
+ bundleChanged(new BundleEvent(BundleEvent.INSTALLED, bundle));
+ }
+ }
+
+ public void destroy() throws Exception {
+ bundleContext.removeBundleListener(this);
+ }
+
+ public boolean canHandle(File artifact) {
+ try {
+ if (artifact.isFile() && artifact.getName().endsWith(".xml")) {
+ Document doc = parse(artifact);
+ String name = doc.getDocumentElement().getLocalName();
+ String uri = doc.getDocumentElement().getNamespaceURI();
+ if ("features".equals(name) && (uri == null || "".equals(uri))) {
+ return true;
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("Unable to parse deployed file " + artifact.getAbsolutePath(), e);
+ }
+ return false;
+ }
+
+ public File handle(File artifact, File tmpDir) {
+ // We can't really install the feature right now and just return nothing.
+ // We would not be aware of the fact that the bundle has been uninstalled
+ // and therefore require the feature to be uninstalled.
+ // So instead, create a fake bundle with the file inside, which will be listened by
+ // this deployer: installation / uninstallation of the feature will be done
+ // while the bundle is installed / uninstalled.
+ try {
+ File destFile = new File(tmpDir, artifact.getName() + ".jar");
+ OutputStream os = new BufferedOutputStream(new FileOutputStream(destFile));
+
+ String name = artifact.getCanonicalPath();
+ int idx = name.lastIndexOf('/');
+ if (idx >= 0) {
+ name = name.substring(idx + 1);
+ }
+ String[] str = extractNameVersionType(name);
+ // Create manifest
+ Manifest m = new Manifest();
+ m.getMainAttributes().putValue("Manifest-Version", "2");
+ m.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+ m.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, str[0]);
+ m.getMainAttributes().putValue(Constants.BUNDLE_VERSION, str[1]);
+ // Put content
+ JarOutputStream out = new JarOutputStream(os);
+ ZipEntry e = new ZipEntry(JarFile.MANIFEST_NAME);
+ out.putNextEntry(e);
+ m.write(out);
+ out.closeEntry();
+ e = new ZipEntry("META-INF/");
+ out.putNextEntry(e);
+ e = new ZipEntry("META-INF/" + FEATURE_PATH + "/");
+ out.putNextEntry(e);
+ out.closeEntry();
+ e = new ZipEntry("META-INF/" + FEATURE_PATH + "/" + name);
+ out.putNextEntry(e);
+ InputStream fis = new BufferedInputStream(new FileInputStream(artifact));
+ copyInputStream(fis, out);
+ fis.close();
+ out.closeEntry();
+ out.close();
+ os.close();
+ return destFile;
+ } catch (Exception e) {
+ LOGGER.error("Unable to build spring application bundle", e);
+ return null;
+ }
+ }
+
+ public void bundleChanged(BundleEvent bundleEvent) {
+ try {
+ Bundle bundle = bundleEvent.getBundle();
+ if (bundleEvent.getType() == BundleEvent.INSTALLED) {
+ Enumeration featuresUrlEnumeration = bundle.findEntries("/META-INF/" + FEATURE_PATH + "/", "*.xml", false);
+ while (featuresUrlEnumeration != null && featuresUrlEnumeration.hasMoreElements()) {
+ URL url = (URL) featuresUrlEnumeration.nextElement();
+ RepositoryImpl repo = featuresService.internalAddRepository(url.toURI());
+ for (Feature f : repo.getFeatures()) {
+ featuresService.installFeature(f.getName(), f.getVersion());
+ }
+ featuresService.internalRemoveRepository(url.toURI());
+ }
+ } else if (bundleEvent.getType() == BundleEvent.UNINSTALLED) {
+ Enumeration featuresUrlEnumeration = bundle.findEntries("/META-INF/" + FEATURE_PATH + "/", "*.xml", false);
+ while (featuresUrlEnumeration != null && featuresUrlEnumeration.hasMoreElements()) {
+ URL url = (URL) featuresUrlEnumeration.nextElement();
+ RepositoryImpl repo = featuresService.internalAddRepository(url.toURI());
+ for (Feature f : repo.getFeatures()) {
+ featuresService.uninstallFeature(f.getName(), f.getVersion());
+ }
+ featuresService.internalRemoveRepository(url.toURI());
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("Unable to install / uninstall feature", e);
+ }
+ }
+
+ protected Document parse(File artifact) throws Exception {
+ if (dbf == null) {
+ dbf = DocumentBuilderFactory.newInstance();
+ dbf.setNamespaceAware(true);
+ }
+ DocumentBuilder db = dbf.newDocumentBuilder();
+ return db.parse(artifact);
+ }
+
+ private static void copyInputStream(InputStream in, OutputStream out) throws IOException {
+ byte[] buffer = new byte[8192];
+ int len = in.read(buffer);
+ while (len >= 0) {
+ out.write(buffer, 0, len);
+ len = in.read(buffer);
+ }
+ }
+ private static final String DEFAULT_VERSION = "0.0.0";
+
+ private static final Pattern ARTIFACT_MATCHER = Pattern.compile("(.+)(?:-(\\d+)(?:\\.(\\d+)(?:\\.(\\d+))?)?(?:[^a-zA-Z0-9](.*))?)(?:\\.([^\\.]+))", Pattern.DOTALL);
+ private static final Pattern FUZZY_MODIFIDER = Pattern.compile("(?:\\d+[.-])*(.*)", Pattern.DOTALL);
+
+ public static String[] extractNameVersionType(String url) {
+ Matcher m = ARTIFACT_MATCHER.matcher(url);
+ if (!m.matches()) {
+ return new String[] { url, DEFAULT_VERSION };
+ }
+ else {
+ //System.err.println(m.groupCount());
+ //for (int i = 1; i <= m.groupCount(); i++) {
+ // System.err.println("Group " + i + ": " + m.group(i));
+ //}
+
+ StringBuffer v = new StringBuffer();
+ String d1 = m.group(1);
+ String d2 = m.group(2);
+ String d3 = m.group(3);
+ String d4 = m.group(4);
+ String d5 = m.group(5);
+ String d6 = m.group(6);
+ if (d2 != null) {
+ v.append(d2);
+ if (d3 != null) {
+ v.append('.');
+ v.append(d3);
+ if (d4 != null) {
+ v.append('.');
+ v.append(d4);
+ if (d5 != null) {
+ v.append(".");
+ cleanupModifier(v, d5);
+ }
+ } else if (d5 != null) {
+ v.append(".0.");
+ cleanupModifier(v, d5);
+ }
+ } else if (d5 != null) {
+ v.append(".0.0.");
+ cleanupModifier(v, d5);
+ }
+ }
+ return new String[] { d1, v.toString(), d6 };
+ }
+ }
+
+ private static void cleanupModifier(StringBuffer result, String modifier) {
+ Matcher m = FUZZY_MODIFIDER.matcher(modifier);
+ if (m.matches()) {
+ modifier = m.group(1);
+ }
+ for (int i = 0; i < modifier.length(); i++) {
+ char c = modifier.charAt(i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-') {
+ result.append(c);
+ }
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java
new file mode 100644
index 0000000..07806d3
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeatureImpl.java
@@ -0,0 +1,132 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * A feature
+ */
+@ManagedResource(description = "Managed Feature", currencyTimeLimit = 15)
+public class FeatureImpl implements Feature {
+
+ private String id;
+ private String name;
+ private String version;
+ private List<Feature> dependencies = new ArrayList<Feature>();
+ private List<String> bundles = new ArrayList<String>();
+ private Map<String, Map<String,String>> configs = new HashMap<String, Map<String,String>>();
+ public static String SPLIT_FOR_NAME_AND_VERSION = "_split_for_name_and_version_";
+ public static String DEFAULT_VERSION = "0.0.0";
+
+ public FeatureImpl(String name) {
+ this(name, DEFAULT_VERSION);
+ }
+
+ public FeatureImpl(String name, String version) {
+ this.name = name;
+ this.version = version;
+ this.id = name + "-" + version;
+ }
+
+ @ManagedAttribute(description = "Feature Unique ID")
+ public String getId() {
+ return id;
+ }
+
+ @ManagedAttribute(description = "Feature Name")
+ public String getName() {
+ return name;
+ }
+
+ @ManagedAttribute(description = "Feature Version")
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ @ManagedAttribute(description = "List of Dependencies")
+ public List<Feature> getDependencies() {
+ return dependencies;
+ }
+
+ public List<String> getBundles() {
+ return bundles;
+ }
+
+ public Map<String, Map<String, String>> getConfigurations() {
+ return configs;
+ }
+
+ public void addDependency(Feature dependency) {
+ dependencies.add(dependency);
+ }
+
+ public void addBundle(String bundle) {
+ bundles.add(bundle);
+ }
+
+ public void addConfig(String name, Map<String,String> properties) {
+ configs.put(name, properties);
+ }
+
+ public String toString() {
+ String ret = getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
+ return ret;
+ }
+
+ public static Feature valueOf(String str) {
+ if (str.indexOf(SPLIT_FOR_NAME_AND_VERSION) >= 0) {
+ String strName = str.substring(0, str.indexOf(SPLIT_FOR_NAME_AND_VERSION));
+ String strVersion = str.substring(str.indexOf(SPLIT_FOR_NAME_AND_VERSION)
+ + SPLIT_FOR_NAME_AND_VERSION.length(), str.length());
+ return new FeatureImpl(strName, strVersion);
+ } else {
+ return new FeatureImpl(str);
+ }
+
+
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ FeatureImpl feature = (FeatureImpl) o;
+
+ if (!name.equals(feature.name)) return false;
+ if (!version.equals(feature.version)) return false;
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result = name.hashCode();
+ result = 31 * result + version.hashCode();
+ return result;
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java
new file mode 100644
index 0000000..1c03515
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImpl.java
@@ -0,0 +1,599 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.Version;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.prefs.BackingStoreException;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.osgi.context.BundleContextAware;
+
+/**
+ * The Features service implementation.
+ * Adding a repository url will load the features contained in this repository and
+ * create dummy sub shells. When invoked, these commands will prompt the user for
+ * installing the needed bundles.
+ *
+ */
+public class FeaturesServiceImpl implements FeaturesService, BundleContextAware {
+
+ private static final String ALIAS_KEY = "_alias_factory_pid";
+
+ private static final Log LOGGER = LogFactory.getLog(FeaturesServiceImpl.class);
+
+ private BundleContext bundleContext;
+ private ConfigurationAdmin configAdmin;
+ private PreferencesService preferences;
+ private Set<URI> uris;
+ private Map<URI, RepositoryImpl> repositories = new HashMap<URI, RepositoryImpl>();
+ private Map<String, Map<String, Feature>> features;
+ private Map<Feature, Set<Long>> installed = new HashMap<Feature, Set<Long>>();
+ private String boot;
+ private boolean bootFeaturesInstalled;
+ private FeaturesRegistry featuresRegistry;
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public ConfigurationAdmin getConfigAdmin() {
+ return configAdmin;
+ }
+
+ public void setConfigAdmin(ConfigurationAdmin configAdmin) {
+ this.configAdmin = configAdmin;
+ }
+
+ public PreferencesService getPreferences() {
+ return preferences;
+ }
+
+ public void setPreferences(PreferencesService preferences) {
+ this.preferences = preferences;
+ }
+
+ public void setFeaturesServiceRegistry(FeaturesRegistry featuresRegistry) {
+ this.featuresRegistry = featuresRegistry;
+ }
+
+ public void setUrls(String uris) throws URISyntaxException {
+ String[] s = uris.split(",");
+ this.uris = new HashSet<URI>();
+ for (int i = 0; i < s.length; i++) {
+ this.uris.add(new URI(s[i]));
+ }
+ }
+
+ public void setBoot(String boot) {
+ this.boot = boot;
+ }
+
+ public void addRepository(URI uri) throws Exception {
+ if (!repositories.values().contains(uri)) {
+ internalAddRepository(uri);
+ saveState();
+ }
+ }
+
+ protected RepositoryImpl internalAddRepository(URI uri) throws Exception {
+ RepositoryImpl repo = new RepositoryImpl(uri);
+ repositories.put(uri, repo);
+ featuresRegistry.register(repo);
+ features = null;
+ return repo;
+ }
+
+ public void removeRepository(URI uri) {
+ if (repositories.containsKey(uri)) {
+ internalRemoveRepository(uri);
+ saveState();
+ }
+ }
+
+ public void internalRemoveRepository(URI uri) {
+ featuresRegistry.unregister(repositories.get(uri));
+ repositories.remove(uri);
+ features = null;
+ }
+
+ public Repository[] listRepositories() {
+ Collection<RepositoryImpl> repos = repositories.values();
+ return repos.toArray(new Repository[repos.size()]);
+ }
+
+ public void installAllFeatures(URI uri) throws Exception {
+ RepositoryImpl repo = internalAddRepository(uri);
+ for (Feature f : repo.getFeatures()) {
+ installFeature(f.getName(), f.getVersion());
+ }
+ internalRemoveRepository(uri);
+ }
+
+ public void uninstallAllFeatures(URI uri) throws Exception {
+ RepositoryImpl repo = internalAddRepository(uri);
+ for (Feature f : repo.getFeatures()) {
+ uninstallFeature(f.getName(), f.getVersion());
+ }
+ internalRemoveRepository(uri);
+ }
+
+ public void installFeature(String name) throws Exception {
+ installFeature(name, FeatureImpl.DEFAULT_VERSION);
+ }
+
+ public void installFeature(String name, String version) throws Exception {
+ Feature f = getFeature(name, version);
+ if (f == null) {
+ throw new Exception("No feature named '" + name
+ + "' with version '" + version + "' available");
+ }
+ for (Feature dependency : f.getDependencies()) {
+ installFeature(dependency.getName(), dependency.getVersion());
+ }
+ for (String config : f.getConfigurations().keySet()) {
+ Dictionary<String,String> props = new Hashtable<String, String>(f.getConfigurations().get(config));
+ String[] pid = parsePid(config);
+ if (pid[1] != null) {
+ props.put(ALIAS_KEY, pid[1]);
+ }
+ Configuration cfg = getConfiguration(configAdmin, pid[0], pid[1]);
+ if (cfg.getBundleLocation() != null) {
+ cfg.setBundleLocation(null);
+ }
+ cfg.update(props);
+ }
+ Set<Long> bundles = new HashSet<Long>();
+ for (String bundleLocation : f.getBundles()) {
+ Bundle b = installBundleIfNeeded(bundleLocation);
+ bundles.add(b.getBundleId());
+ }
+ for (long id : bundles) {
+ bundleContext.getBundle(id).start();
+ }
+
+ featuresRegistry.registerInstalled(f);
+ installed.put(f, bundles);
+ saveState();
+ }
+ protected Bundle installBundleIfNeeded(String bundleLocation) throws IOException, BundleException {
+ LOGGER.debug("Checking " + bundleLocation);
+ InputStream is = null;
+ try {
+ is = new BufferedInputStream(new URL(bundleLocation).openStream());
+ } catch (RuntimeException e) {
+ LOGGER.error(e.getMessage());
+ throw e;
+ }
+ try {
+ is.mark(256 * 1024);
+ JarInputStream jar = new JarInputStream(is);
+ Manifest m = jar.getManifest();
+ String sn = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+ String vStr = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+ Version v = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+ for (Bundle b : bundleContext.getBundles()) {
+ if (b.getSymbolicName() != null && b.getSymbolicName().equals(sn)) {
+ vStr = (String) b.getHeaders().get(Constants.BUNDLE_VERSION);
+ Version bv = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+ if (v.equals(bv)) {
+ LOGGER.debug(" found installed bundle: " + b);
+ return b;
+ }
+ }
+ }
+ try {
+ is.reset();
+ } catch (IOException e) {
+ is.close();
+ is = new BufferedInputStream(new URL(bundleLocation).openStream());
+ }
+ LOGGER.debug("Installing bundle " + bundleLocation);
+ return getBundleContext().installBundle(bundleLocation, is);
+ } finally {
+ is.close();
+ }
+ }
+
+ public void uninstallFeature(String name) throws Exception {
+ List<String> versions = new ArrayList<String>();
+ for (Feature f : installed.keySet()) {
+ if (name.equals(f.getName())) {
+ versions.add(f.getVersion());
+ }
+ }
+ if (versions.size() == 0) {
+ throw new Exception("Feature named '" + name + "' is not installed");
+ } else if (versions.size() > 1) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Feature named '" + name + "' has multiple versions installed (");
+ for (int i = 0; i < versions.size(); i++) {
+ if (i > 0) {
+ sb.append(", ");
+ }
+ sb.append(versions.get(i));
+ }
+ sb.append("). Please specify the version to uninstall.");
+ throw new Exception(sb.toString());
+ }
+ uninstallFeature(name, versions.get(0));
+ }
+
+ public void uninstallFeature(String name, String version) throws Exception {
+ Feature feature = getFeature(name, version);
+ if (feature == null || !installed.containsKey(feature)) {
+ throw new Exception("Feature named '" + name
+ + "' with version '" + version + "' is not installed");
+ }
+ // Grab all the bundles installed by this feature
+ // and remove all those who will still be in use.
+ // This gives this list of bundles to uninstall.
+ Set<Long> bundles = installed.remove(feature);
+ for (Set<Long> b : installed.values()) {
+ bundles.removeAll(b);
+ }
+ for (long bundleId : bundles) {
+ getBundleContext().getBundle(bundleId).uninstall();
+ }
+ featuresRegistry.unregisterInstalled(feature);
+ saveState();
+ }
+
+ public String[] listFeatures() throws Exception {
+ Collection<String> features = new ArrayList<String>();
+ for (Map<String, Feature> featureWithDifferentVersion : getFeatures()
+ .values()) {
+ for (Feature f : featureWithDifferentVersion.values()) {
+ String installStatus = installed.containsKey(f) ? "installed "
+ : "uninstalled";
+ String version = f.getVersion();
+ switch (version.length()) {
+ case 1:
+ version = " " + version;
+ case 2:
+ version = " " + version;
+ case 3:
+ version = " " + version;
+ case 4:
+ version = " " + version;
+ case 5:
+ version = " " + version;
+ case 6:
+ version = " " + version;
+ case 7:
+ version = " " + version;
+ }
+ features.add("[" + installStatus + "] " + " [" + version + "] "
+ + f.getName());
+ }
+ }
+ return features.toArray(new String[features.size()]);
+ }
+
+ public String[] listInstalledFeatures() {
+ List<String> result = new ArrayList<String>();
+ for (Feature feature : installed.keySet()) {
+ result.add(feature.getName());
+ }
+ return result.toArray(new String[result.size()]);
+ }
+
+ protected Feature getFeature(String name, String version) throws Exception {
+ Map<String, Feature> versions = getFeatures().get(name);
+ if (versions == null || versions.isEmpty()) {
+ return null;
+ } else {
+ Feature feature = versions.get(version);
+ if (feature == null && FeatureImpl.DEFAULT_VERSION.equals(version)) {
+ Version latest = new Version(cleanupVersion(version));
+ for (String available : versions.keySet()) {
+ Version availableVersion = new Version(cleanupVersion(available));
+ if (availableVersion.compareTo(latest) > 0) {
+ feature = versions.get(available);
+ latest = availableVersion;
+ }
+ }
+ }
+ return feature;
+ }
+ }
+
+ protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
+ if (features == null) {
+ //the outer map's key is feature name, the inner map's key is feature version
+ Map<String, Map<String, Feature>> map = new HashMap<String, Map<String, Feature>>();
+ // Two phase load:
+ // * first load dependent repositories
+ for (;;) {
+ boolean newRepo = false;
+ for (Repository repo : listRepositories()) {
+ for (URI uri : repo.getRepositories()) {
+ if (!repositories.keySet().contains(uri)) {
+ internalAddRepository(uri);
+ newRepo = true;
+ }
+ }
+ }
+ if (!newRepo) {
+ break;
+ }
+ }
+ // * then load all features
+ for (Repository repo : repositories.values()) {
+ for (Feature f : repo.getFeatures()) {
+ if (map.get(f.getName()) == null) {
+ Map<String, Feature> versionMap = new HashMap<String, Feature>();
+ versionMap.put(f.getVersion(), f);
+ map.put(f.getName(), versionMap);
+ } else {
+ map.get(f.getName()).put(f.getVersion(), f);
+ }
+ }
+ }
+ features = map;
+ }
+ return features;
+ }
+
+ public void start() throws Exception {
+ if (!loadState()) {
+ if (uris != null) {
+ for (URI uri : uris) {
+ internalAddRepository(uri);
+ }
+ }
+ saveState();
+ }
+ if (boot != null && !bootFeaturesInstalled) {
+ new Thread() {
+ public void run() {
+ String[] list = boot.split(",");
+ for (String f : list) {
+ if (f.length() > 0) {
+ try {
+ installFeature(f);
+ } catch (Exception e) {
+ LOGGER.error("Error installing boot feature " + f, e);
+ }
+ }
+ }
+ bootFeaturesInstalled = true;
+ saveState();
+ }
+ }.start();
+ }
+ }
+
+ public void stop() throws Exception {
+ uris = new HashSet<URI>(repositories.keySet());
+ while (!repositories.isEmpty()) {
+ internalRemoveRepository(repositories.keySet().iterator().next());
+ }
+ }
+
+ protected String[] parsePid(String pid) {
+ int n = pid.indexOf('-');
+ if (n > 0) {
+ String factoryPid = pid.substring(n + 1);
+ pid = pid.substring(0, n);
+ return new String[]{pid, factoryPid};
+ } else {
+ return new String[]{pid, null};
+ }
+ }
+
+ protected Configuration getConfiguration(ConfigurationAdmin configurationAdmin,
+ String pid, String factoryPid) throws IOException, InvalidSyntaxException {
+ if (factoryPid != null) {
+ Configuration[] configs = configurationAdmin.listConfigurations("(|(" + ALIAS_KEY + "=" + pid + ")(.alias_factory_pid=" + factoryPid + "))");
+ if (configs == null || configs.length == 0) {
+ return configurationAdmin.createFactoryConfiguration(pid, null);
+ } else {
+ return configs[0];
+ }
+ } else {
+ return configurationAdmin.getConfiguration(pid, null);
+ }
+ }
+
+ protected void saveState() {
+ try {
+ Preferences prefs = preferences.getUserPreferences("FeaturesServiceState");
+ saveSet(prefs.node("repositories"), repositories.keySet());
+ saveMap(prefs.node("features"), installed);
+ prefs.putBoolean("bootFeaturesInstalled", bootFeaturesInstalled);
+ prefs.flush();
+ } catch (Exception e) {
+ LOGGER.error("Error persisting FeaturesService state", e);
+ }
+ }
+
+ protected boolean loadState() {
+ try {
+ Preferences prefs = preferences.getUserPreferences("FeaturesServiceState");
+ if (prefs.nodeExists("repositories")) {
+ Set<URI> repositories = loadSet(prefs.node("repositories"));
+ for (URI repo : repositories) {
+ internalAddRepository(repo);
+ }
+ installed = loadMap(prefs.node("features"));
+ for (Feature f : installed.keySet()) {
+ featuresRegistry.registerInstalled(f);
+ }
+ bootFeaturesInstalled = prefs.getBoolean("bootFeaturesInstalled", false);
+ return true;
+ }
+ } catch (Exception e) {
+ LOGGER.error("Error loading FeaturesService state", e);
+ }
+ return false;
+ }
+
+ protected void saveSet(Preferences node, Set<URI> set) throws BackingStoreException {
+ List<URI> l = new ArrayList<URI>(set);
+ node.clear();
+ node.putInt("count", l.size());
+ for (int i = 0; i < l.size(); i++) {
+ node.put("item." + i, l.get(i).toString());
+ }
+ }
+
+ protected Set<URI> loadSet(Preferences node) {
+ Set<URI> l = new HashSet<URI>();
+ int count = node.getInt("count", 0);
+ for (int i = 0; i < count; i++) {
+ l.add(URI.create(node.get("item." + i, null)));
+ }
+ return l;
+ }
+
+ protected void saveMap(Preferences node, Map<Feature, Set<Long>> map) throws BackingStoreException {
+ node.clear();
+ for (Map.Entry<Feature, Set<Long>> entry : map.entrySet()) {
+ Feature key = entry.getKey();
+ String val = createValue(entry.getValue());
+ node.put(key.toString(), val);
+ }
+ }
+
+ protected Map<Feature, Set<Long>> loadMap(Preferences node) throws BackingStoreException {
+ Map<Feature, Set<Long>> map = new HashMap<Feature, Set<Long>>();
+ for (String key : node.keys()) {
+ String val = node.get(key, null);
+ Set<Long> set = readValue(val);
+ map.put(FeatureImpl.valueOf(key), set);
+ }
+ return map;
+ }
+
+ protected String createValue(Set<Long> set) {
+ StringBuilder sb = new StringBuilder();
+ for (long i : set) {
+ if (sb.length() > 0) {
+ sb.append(",");
+ }
+ sb.append(i);
+ }
+ return sb.toString();
+ }
+
+ protected Set<Long> readValue(String val) {
+ Set<Long> set = new HashSet<Long>();
+ for (String str : val.split(",")) {
+ set.add(Long.parseLong(str));
+ }
+ return set;
+ }
+
+ /**
+ * Clean up version parameters. Other builders use more fuzzy definitions of
+ * the version syntax. This method cleans up such a version to match an OSGi
+ * version.
+ *
+ * @param version
+ * @return
+ */
+ static Pattern fuzzyVersion = Pattern.compile("(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
+ Pattern.DOTALL);
+ static Pattern fuzzyModifier = Pattern.compile("(\\d+[.-])*(.*)",
+ Pattern.DOTALL);
+
+ static public String cleanupVersion(String version) {
+ Matcher m = fuzzyVersion.matcher(version);
+ if (m.matches()) {
+ StringBuffer result = new StringBuffer();
+ String d1 = m.group(1);
+ String d2 = m.group(3);
+ String d3 = m.group(5);
+ String qualifier = m.group(7);
+
+ if (d1 != null) {
+ result.append(d1);
+ if (d2 != null) {
+ result.append(".");
+ result.append(d2);
+ if (d3 != null) {
+ result.append(".");
+ result.append(d3);
+ if (qualifier != null) {
+ result.append(".");
+ cleanupModifier(result, qualifier);
+ }
+ } else if (qualifier != null) {
+ result.append(".0.");
+ cleanupModifier(result, qualifier);
+ }
+ } else if (qualifier != null) {
+ result.append(".0.0.");
+ cleanupModifier(result, qualifier);
+ }
+ return result.toString();
+ }
+ }
+ return version;
+ }
+
+ static void cleanupModifier(StringBuffer result, String modifier) {
+ Matcher m = fuzzyModifier.matcher(modifier);
+ if (m.matches())
+ modifier = m.group(2);
+
+ for (int i = 0; i < modifier.length(); i++) {
+ char c = modifier.charAt(i);
+ if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
+ || (c >= 'A' && c <= 'Z') || c == '_' || c == '-')
+ result.append(c);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java
new file mode 100644
index 0000000..485c3d4
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/internal/RepositoryImpl.java
@@ -0,0 +1,168 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+import org.xml.sax.SAXException;
+
+/**
+ * The repository implementation.
+ */
+@ManagedResource
+public class RepositoryImpl implements Repository {
+
+ private URI uri;
+ private List<Feature> features;
+ private List<URI> repositories;
+
+ public RepositoryImpl(URI uri) {
+ this.uri = uri;
+ }
+
+ @ManagedAttribute
+ public URI getURI() {
+ return uri;
+ }
+
+ @ManagedOperation
+ public URI[] getRepositories() throws Exception {
+ if (repositories == null) {
+ load();
+ }
+ return repositories.toArray(new URI[repositories.size()]);
+ }
+
+ @ManagedOperation(description = "List of Features provided by this repository")
+ public Feature[] getFeatures() throws Exception {
+ if (features == null) {
+ load();
+ }
+ return features.toArray(new Feature[features.size()]);
+ }
+
+ public void load() throws IOException {
+ try {
+ repositories = new ArrayList<URI>();
+ features = new ArrayList<Feature>();
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ Document doc = factory.newDocumentBuilder().parse(uri.toURL().openStream());
+ NodeList nodes = doc.getDocumentElement().getChildNodes();
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
+ if (!(node instanceof Element)) {
+ continue;
+ }
+ if ("repository".equals(node.getNodeName())) {
+ Element e = (Element) nodes.item(i);
+ repositories.add(new URI(e.getTextContent()));
+ } else if ("feature".equals(node.getNodeName())) {
+ Element e = (Element) nodes.item(i);
+ String name = e.getAttribute("name");
+ String version = e.getAttribute("version");
+ FeatureImpl f;
+ if (version != null && version.length() > 0) {
+ f = new FeatureImpl(name, version);
+ } else {
+ f = new FeatureImpl(name);
+ }
+
+ NodeList featureNodes = e.getElementsByTagName("feature");
+ for (int j = 0; j < featureNodes.getLength(); j++) {
+ Element b = (Element) featureNodes.item(j);
+ String dependencyFeatureVersion = b.getAttribute("version");
+ if (dependencyFeatureVersion != null && dependencyFeatureVersion.length() > 0) {
+ f.addDependency(new FeatureImpl(b.getTextContent(), dependencyFeatureVersion));
+ } else {
+ f.addDependency(new FeatureImpl(b.getTextContent()));
+ }
+ }
+ NodeList configNodes = e.getElementsByTagName("config");
+ for (int j = 0; j < configNodes.getLength(); j++) {
+ Element c = (Element) configNodes.item(j);
+ String cfgName = c.getAttribute("name");
+ String data = c.getTextContent();
+ Properties properties = new Properties();
+ properties.load(new ByteArrayInputStream(data.getBytes()));
+ interpolation(properties);
+ Map<String, String> hashtable = new Hashtable<String, String>();
+ for (Object key : properties.keySet()) {
+ String n = key.toString();
+ hashtable.put(n, properties.getProperty(n));
+ }
+ f.addConfig(cfgName, hashtable);
+ }
+ NodeList bundleNodes = e.getElementsByTagName("bundle");
+ for (int j = 0; j < bundleNodes.getLength(); j++) {
+ Element b = (Element) bundleNodes.item(j);
+ f.addBundle(b.getTextContent());
+ }
+ features.add(f);
+ }
+ }
+ } catch (SAXException e) {
+ throw (IOException) new IOException().initCause(e);
+ } catch (ParserConfigurationException e) {
+ throw (IOException) new IOException().initCause(e);
+ } catch (URISyntaxException e) {
+ throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+ } catch (IllegalArgumentException e) {
+ throw (IOException) new IOException(e.getMessage() + " : " + uri).initCause(e);
+ }
+ }
+
+ protected void interpolation(Properties properties) {
+ for (Enumeration e = properties.propertyNames(); e.hasMoreElements();) {
+ String key = (String)e.nextElement();
+ String val = properties.getProperty(key);
+ Matcher matcher = Pattern.compile("\\$\\{([^}]+)\\}").matcher(val);
+ while (matcher.find()) {
+ String rep = System.getProperty(matcher.group(1));
+ if (rep != null) {
+ val = val.replace(matcher.group(0), rep);
+ matcher.reset(val);
+ }
+ }
+ properties.put(key, val);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java
new file mode 100644
index 0000000..6bee5fc
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/DefaultNamingStrategy.java
@@ -0,0 +1,84 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+
+/**
+ * Naming strategy for JMX MBeans.
+ */
+public class DefaultNamingStrategy implements NamingStrategy {
+
+ private String jmxDomainName;
+
+ public String getJmxDomainName() {
+ return jmxDomainName;
+ }
+
+ public void setJmxDomainName(String jmxDomainName) {
+ this.jmxDomainName = jmxDomainName;
+ }
+
+ public ObjectName getObjectName(ManagedFeature feature) throws MalformedObjectNameException {
+ return getObjectName(feature, false);
+ }
+
+ public ObjectName getObjectName(ManagedFeature feature, boolean installed) throws MalformedObjectNameException {
+ StringBuffer sb = new StringBuffer();
+ sb.append(jmxDomainName).append(":Service=Features,");
+
+ if (installed) {
+ sb.append("Type=Installed,");
+ } else {
+ sb.append("Type=Available,");
+ }
+
+ sb.append("Name=").append(sanitize(feature.getName())).append(",")
+ .append("FeatureVersion=").append(sanitize(feature.getVersion()));
+
+ return new ObjectName(sb.toString());
+ }
+
+ public ObjectName getObjectName(ManagedRepository repository) throws MalformedObjectNameException {
+ return new ObjectName(jmxDomainName + ":" +
+ "Service=Features," +
+ "Type=Repositories," +
+ "Name=" + sanitize(repository.getUri().toString())); // + "," +
+ }
+
+ public ObjectName getObjectName(FeaturesRegistry featuresRegistry) throws MalformedObjectNameException {
+ return new ObjectName(jmxDomainName + ":" +
+ "Service=Features," +
+ "Name=FeaturesService");
+ }
+
+ private String sanitize(String in) {
+ String result = null;
+ if (in != null) {
+ result = in.replace(':', '_');
+ result = result.replace('/', '_');
+ result = result.replace('\\', '_');
+ result = result.replace('?', '_');
+ result = result.replace('=', '_');
+ result = result.replace(',', '_');
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java
new file mode 100644
index 0000000..1da589b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeature.java
@@ -0,0 +1,78 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * Managed Repository MBean
+ */
+@ManagedResource(description = "Feature")
+public class ManagedFeature {
+ private Feature feature;
+ private FeaturesService featuresService;
+ private String id;
+
+ public ManagedFeature(Feature feature, FeaturesService featuresService) {
+ this.feature = feature;
+ id = feature.getName() + "-" + feature.getVersion();
+ this.featuresService = featuresService;
+ }
+
+ @ManagedAttribute
+ public String getId() {
+ return id;
+ }
+
+ @ManagedAttribute
+ public String getName() {
+ return feature.getName();
+ }
+
+ @ManagedAttribute
+ public String getVersion() {
+ return feature.getVersion();
+ }
+
+ @ManagedAttribute
+ public List<Feature> getDependencies() {
+ return feature.getDependencies();
+ }
+
+ @ManagedAttribute
+ public List<String> getBundles() {
+ return feature.getBundles();
+ }
+
+ @ManagedOperation
+ public void uninstallFeature() throws Exception {
+ featuresService.uninstallFeature(feature.getName(), feature.getVersion());
+ }
+
+ @ManagedOperation
+ public void installFeature() throws Exception {
+ featuresService.installFeature(feature.getName(), feature.getVersion());
+ }
+
+}
+
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java
new file mode 100644
index 0000000..9fbdb46
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedFeaturesRegistry.java
@@ -0,0 +1,210 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import javax.management.MBeanServer;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+/**
+ * The FeaturesServiceRegistry maintains the managed Features and Repositories
+ * for JMX management.
+ */
+@ManagedResource(description = "Features Service Registry and Management")
+public class ManagedFeaturesRegistry implements InitializingBean, FeaturesRegistry {
+
+ private static final transient Log LOG = LogFactory.getLog(ManagedFeaturesRegistry.class);
+
+ private NamingStrategy namingStrategy;
+ private ManagementAgent managementAgent;
+ private Map<String, ManagedFeature> availableFeatures;
+ private Map<String, ManagedFeature> installedFeatures;
+ private Map<String, ManagedRepository> repositories;
+ private boolean mbeanServerInitialized;
+ private FeaturesService featuresService;
+
+ @ManagedOperation
+ public void installFeature(String name) throws Exception {
+ featuresService.installFeature(name);
+ }
+
+ @ManagedOperation
+ public void installFeature(String name, String version) throws Exception {
+ featuresService.installFeature(name, version);
+ }
+
+ @ManagedOperation
+ public void installRepository(String repositoryUri) throws Exception {
+ featuresService.addRepository(new URI(repositoryUri));
+ }
+
+ public ManagedFeaturesRegistry() {
+ availableFeatures = new ConcurrentHashMap<String, ManagedFeature>();
+ installedFeatures = new ConcurrentHashMap<String, ManagedFeature>();
+ repositories = new ConcurrentHashMap<String, ManagedRepository>();
+ }
+
+ public Map<String, ManagedFeature> getAvailableFeatures() {
+ return availableFeatures;
+ }
+
+ public Map<String, ManagedFeature> getInstalledFeatures() {
+ return installedFeatures;
+ }
+
+ public Map<String, ManagedRepository> getRepositories() {
+ return repositories;
+ }
+
+ public void setFeaturesService(FeaturesService featuresService) {
+ this.featuresService = featuresService;
+ }
+
+ public void setNamingStrategy(NamingStrategy namingStrategy) {
+ this.namingStrategy = namingStrategy;
+ }
+
+ public void setManagementAgent(ManagementAgent managementAgent) {
+ this.managementAgent = managementAgent;
+ }
+
+ public void register(Feature feature) {
+ try {
+ ManagedFeature mf = new ManagedFeature(feature, featuresService);
+ availableFeatures.put(feature.getId(), mf);
+ if ( mbeanServerInitialized ) {
+ managementAgent.register(mf, namingStrategy.getObjectName(mf));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to register managed feature: " + e, e);
+ }
+ }
+
+ public void unregister(Feature feature) {
+ try {
+ ManagedFeature mf = availableFeatures.remove(feature.getId());
+ if ( mbeanServerInitialized ) {
+ managementAgent.unregister(namingStrategy.getObjectName(mf));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to unregister managed feature: " + e, e);
+ }
+ }
+
+ public void registerInstalled(Feature feature) {
+ try {
+ ManagedFeature mf = new ManagedFeature(feature, featuresService);
+ installedFeatures.put(feature.getId(), mf);
+ if ( mbeanServerInitialized ) {
+ managementAgent.register(mf, namingStrategy.getObjectName(mf, true));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to register managed feature: " + e, e);
+ }
+ }
+
+ public void unregisterInstalled(Feature feature) {
+ try {
+ ManagedFeature mf = installedFeatures.remove(feature.getId());
+ if ( mbeanServerInitialized ) {
+ managementAgent.unregister(namingStrategy.getObjectName(mf, true));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to unregister managed feature: " + e, e);
+ }
+ }
+
+ public void register(Repository repository) {
+ try {
+ ManagedRepository mr = new ManagedRepository(repository, featuresService);
+ repositories.put(repository.getURI().toString(), mr);
+
+ for (Feature f : repository.getFeatures()) {
+ // TODO: Associate the feature with the Repo?
+ register(f);
+ }
+
+ if ( mbeanServerInitialized ) {
+ managementAgent.register(mr, namingStrategy.getObjectName(mr));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to register managed repository: " + e, e);
+ }
+ }
+
+ public void unregister(Repository repository) {
+ try {
+ ManagedRepository mr = repositories.remove(repository.getURI().toString());
+
+ for (Feature f : repository.getFeatures()) {
+ // TODO: Associate the feature with the Repo?
+ unregister(f);
+ }
+
+ if ( mbeanServerInitialized ) {
+ managementAgent.unregister(namingStrategy.getObjectName(mr));
+ }
+ } catch (Exception e) {
+ LOG.warn("Unable to unregister managed repository: " + e, e);
+ }
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ if (managementAgent == null) {
+ throw new IllegalArgumentException("managementAgent must not be null");
+ }
+ if (namingStrategy == null) {
+ throw new IllegalArgumentException("namingStrategy must not be null");
+ }
+ }
+
+ public void registerMBeanServer(MBeanServer mbeanServer, Map props ) throws Exception {
+ if (mbeanServer != null) {
+ mbeanServerInitialized = true;
+ }
+
+ managementAgent.register(this, namingStrategy.getObjectName(this));
+
+ for (ManagedRepository repository : repositories.values()) {
+ managementAgent.register(repository, namingStrategy.getObjectName(repository));
+ }
+
+ for (ManagedFeature feature : availableFeatures.values()) {
+ managementAgent.register(feature, namingStrategy.getObjectName(feature));
+ }
+
+ for (ManagedFeature feature : installedFeatures.values()) {
+ installedFeatures.put(feature.getId(), feature);
+ managementAgent.register(feature, namingStrategy.getObjectName(feature, true));
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.java
new file mode 100644
index 0000000..2d3ae17
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagedRepository.java
@@ -0,0 +1,57 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.net.URI;
+
+import org.apache.servicemix.kernel.gshell.features.Feature;
+import org.apache.servicemix.kernel.gshell.features.FeaturesService;
+import org.apache.servicemix.kernel.gshell.features.Repository;
+import org.springframework.jmx.export.annotation.ManagedAttribute;
+import org.springframework.jmx.export.annotation.ManagedOperation;
+import org.springframework.jmx.export.annotation.ManagedResource;
+
+@ManagedResource(description = "Features Repository")
+public class ManagedRepository {
+ private Repository repository;
+ private FeaturesService featuresService;
+
+ public ManagedRepository(Repository repository, FeaturesService featuresService) {
+ this.repository = repository;
+ this.featuresService = featuresService;
+ }
+
+ @ManagedAttribute
+ public URI getUri() {
+ return repository.getURI();
+ }
+
+ @ManagedAttribute
+ public URI[] getRepositories() throws Exception {
+ return repository.getRepositories();
+ }
+
+ @ManagedAttribute
+ public Feature[] getFeatures() throws Exception {
+ return repository.getFeatures();
+ }
+
+ @ManagedOperation
+ public void removeRepository() throws Exception {
+ featuresService.removeRepository(repository.getURI());
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java
new file mode 100644
index 0000000..611ec6b
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/ManagementAgent.java
@@ -0,0 +1,130 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import javax.management.modelmbean.InvalidTargetObjectTypeException;
+import javax.management.modelmbean.ModelMBeanInfo;
+import javax.management.modelmbean.RequiredModelMBean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
+import org.springframework.jmx.export.assembler.MetadataMBeanInfoAssembler;
+
+/**
+ * Management Agent that registers MBeans with JMX MBeanServer.
+ */
+public class ManagementAgent implements DisposableBean {
+
+ private static final transient Log LOG = LogFactory.getLog(ManagementAgent.class);
+
+ private MBeanServer mbeanServer;
+ private MetadataMBeanInfoAssembler assembler;
+ private Set<ObjectName> mbeans = new HashSet<ObjectName>();
+
+ public ManagementAgent() {
+ assembler = new MetadataMBeanInfoAssembler();
+ assembler.setAttributeSource(new AnnotationJmxAttributeSource());
+ }
+
+ public MBeanServer getMbeanServer() {
+ return mbeanServer;
+ }
+
+ public void setMbeanServer(MBeanServer mbeanServer) {
+ this.mbeanServer = mbeanServer;
+ }
+
+ public void destroy() throws Exception {
+ // Using the array to hold the busMBeans to avoid the
+ // CurrentModificationException
+ Object[] mBeans = mbeans.toArray();
+ int caught = 0;
+ for (Object name : mBeans) {
+ mbeans.remove((ObjectName)name);
+ try {
+ unregister((ObjectName)name);
+ } catch (JMException jmex) {
+ LOG.info("Exception unregistering MBean", jmex);
+ caught++;
+ }
+ }
+ if (caught > 0) {
+ LOG.warn("A number of " + caught
+ + " exceptions caught while unregistering MBeans during stop operation. "
+ + "See INFO log for details.");
+ }
+ }
+
+ public void register(Object obj, ObjectName name) throws JMException {
+ register(obj, name, false);
+ }
+
+ public void register(Object obj, ObjectName name, boolean forceRegistration) throws JMException {
+ try {
+ registerMBeanWithServer(obj, name, forceRegistration);
+ } catch (NotCompliantMBeanException e) {
+ // If this is not a "normal" MBean, then try to deploy it using JMX
+ // annotations
+ ModelMBeanInfo mbi = assembler.getMBeanInfo(obj, name.toString());
+ RequiredModelMBean mbean = (RequiredModelMBean) mbeanServer.instantiate(RequiredModelMBean.class.getName());
+ mbean.setModelMBeanInfo(mbi);
+ try {
+ mbean.setManagedResource(obj, "ObjectReference");
+ } catch (InvalidTargetObjectTypeException itotex) {
+ throw new JMException(itotex.getMessage());
+ }
+ registerMBeanWithServer(mbean, name, forceRegistration);
+ }
+ }
+
+ public synchronized void unregister(ObjectName name) throws JMException {
+ if (mbeans.contains(name)) {
+ //check if this bean already get removed in destory method
+ mbeanServer.unregisterMBean(name);
+ }
+ }
+
+ private void registerMBeanWithServer(Object obj, ObjectName name, boolean forceRegistration) throws JMException {
+ ObjectInstance instance = null;
+ try {
+ instance = mbeanServer.registerMBean(obj, name);
+ } catch (InstanceAlreadyExistsException e) {
+ if (forceRegistration) {
+ mbeanServer.unregisterMBean(name);
+ instance = mbeanServer.registerMBean(obj, name);
+ }
+ } catch (NotCompliantMBeanException e) {
+ throw e;
+ }
+
+ if (instance != null) {
+ mbeans.add(name);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.java b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.java
new file mode 100644
index 0000000..7a03399
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/java/org/apache/servicemix/kernel/gshell/features/management/NamingStrategy.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.features.management;
+
+import javax.management.MalformedObjectNameException;
+import javax.management.ObjectName;
+
+import org.apache.servicemix.kernel.gshell.features.FeaturesRegistry;
+
+public interface NamingStrategy {
+
+ ObjectName getObjectName(ManagedFeature feature) throws MalformedObjectNameException;
+
+ ObjectName getObjectName(ManagedFeature feature, boolean installed) throws MalformedObjectNameException;
+
+ ObjectName getObjectName(ManagedRepository component) throws MalformedObjectNameException;
+
+ String getJmxDomainName();
+
+ ObjectName getObjectName(FeaturesRegistry features) throws MalformedObjectNameException;
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml b/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml
new file mode 100644
index 0000000..d45b6be
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/META-INF/spring/gshell-features.xml
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ctx="http://www.springframework.org/schema/context"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <bean id="placeholderConfig" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
+ <property name="location" value="file:${servicemix.home}/etc/org.apache.servicemix.features.cfg"/>
+ </bean>
+
+ <gshell:command-bundle>
+ <gshell:command name="features/addUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.AddUrlCommand" />
+ </gshell:command>
+ <gshell:command name="features/listUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.ListUrlCommand" />
+ </gshell:command>
+ <gshell:command name="features/removeUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.RemoveUrlCommand" />
+ <gshell:completers>
+ <ref bean="removeUrlCompleter" />
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="features/refreshUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.RefreshUrlCommand" />
+ </gshell:command>
+ <gshell:command name="features/install">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.InstallFeatureCommand" />
+ <gshell:completers>
+ <ref bean="installFeatureCompleter" />
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="features/uninstall">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.UninstallFeatureCommand" />
+ <gshell:completers>
+ <ref bean="uninstallFeatureCompleter" />
+ </gshell:completers>
+ </gshell:command>
+ <gshell:command name="features/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.features.commands.ListFeaturesCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <bean id="featuresService" class="org.apache.servicemix.kernel.gshell.features.internal.FeaturesServiceImpl" init-method="start" destroy-method="stop">
+ <property name="urls" value="${featuresRepositories}" />
+ <property name="boot" value="${featuresBoot}" />
+ <property name="configAdmin" ref="configAdmin" />
+ <property name="preferences" ref="preferences" />
+ <property name="featuresServiceRegistry" ref="featureServiceRegistry" />
+ </bean>
+
+ <bean id="featureDeploymentListener" class="org.apache.servicemix.kernel.gshell.features.internal.FeatureDeploymentListener">
+ <property name="featuresService" ref="featuresService" />
+ </bean>
+
+ <bean id="namingStrategy" class="org.apache.servicemix.kernel.gshell.features.management.DefaultNamingStrategy">
+ <property name="jmxDomainName" value="org.apache.servicemix" />
+ </bean>
+
+ <bean id="managementAgent" class="org.apache.servicemix.kernel.gshell.features.management.ManagementAgent">
+ <property name="mbeanServer" ref="mbeanServer" />
+ </bean>
+
+ <bean id="featureServiceRegistry" class="org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry">
+ <property name="managementAgent" ref="managementAgent" />
+ <property name="namingStrategy" ref="namingStrategy" />
+ <property name="featuresService" ref="featuresService" />
+ </bean>
+
+ <osgi:reference id="configAdmin" interface="org.osgi.service.cm.ConfigurationAdmin" />
+
+ <osgi:reference id="preferences" interface="org.osgi.service.prefs.PreferencesService" cardinality="0..1" />
+
+ <osgi:service ref="featuresService" interface="org.apache.servicemix.kernel.gshell.features.FeaturesService" />
+
+ <osgi:service ref="featureDeploymentListener" interface="org.apache.servicemix.kernel.filemonitor.DeploymentListener" />
+
+ <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.features">
+ <prop key="featuresRepositories"></prop>
+ <prop key="featuresBoot"></prop>
+ </osgix:cm-properties>
+
+ <!-- <ctx:property-placeholder properties-ref="cmProps" /> -->
+
+ <osgi:reference id="mbeanServer"
+ interface="javax.management.MBeanServer"
+ cardinality="0..1" >
+ <osgi:listener ref="featureServiceRegistry" bind-method="registerMBeanServer" />
+ </osgi:reference>
+
+ <bean id="installFeatureCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.AvailableFeatureCompleter">
+ <property name="featuresRegistry" ref="featureServiceRegistry" />
+ </bean>
+
+ <bean id="uninstallFeatureCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.InstalledFeatureCompleter">
+ <property name="featuresRegistry" ref="featureServiceRegistry" />
+ </bean>
+
+ <bean id="removeUrlCompleter" class="org.apache.servicemix.kernel.gshell.features.completers.FeatureRepositoryCompleter">
+ <property name="featuresRegistry" ref="featureServiceRegistry" />
+ </bean>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties
new file mode 100644
index 0000000..cf92ffe
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/AddUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Add a list of repository URLs to the features service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties
new file mode 100644
index 0000000..97fcbe0
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/InstallFeatureCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install a feature.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties
new file mode 100644
index 0000000..4c807af
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListFeaturesCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List existing features.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties
new file mode 100644
index 0000000..a544da9
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/ListUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the repository URLs currently associated with the features service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties
new file mode 100644
index 0000000..12687fd
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RefreshUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Reload the repositories to obtain a fresh list of features.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties
new file mode 100644
index 0000000..34c2126
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/RemoveUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Remove a list of repository URLs from the features service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties
new file mode 100644
index 0000000..ab3348e
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/main/resources/org/apache/servicemix/kernel/gshell/features/commands/UninstallFeatureCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Uninstall a feature.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.java
new file mode 100644
index 0000000..97d6332
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeatureTest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+
+public class FeatureTest extends TestCase {
+
+ public void testValueOf() {
+ Feature feature = FeatureImpl.valueOf("name" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "version");
+ assertEquals(feature.getName(), "name");
+ assertEquals(feature.getVersion(), "version");
+ feature = FeatureImpl.valueOf("name");
+ assertEquals(feature.getName(), "name");
+ assertEquals(feature.getVersion(), FeatureImpl.DEFAULT_VERSION);
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java
new file mode 100644
index 0000000..5909722
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/FeaturesServiceTest.java
@@ -0,0 +1,700 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.URI;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+import org.apache.servicemix.kernel.gshell.features.internal.FeaturesServiceImpl;
+import org.apache.servicemix.kernel.gshell.features.management.ManagedFeaturesRegistry;
+import org.easymock.EasyMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.isA;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.reset;
+import static org.easymock.EasyMock.verify;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.prefs.Preferences;
+import org.osgi.service.prefs.PreferencesService;
+import org.springframework.context.ApplicationContext;
+
+public class FeaturesServiceTest extends TestCase {
+
+ public void testInstallFeature() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+ featuresRegistry.register(isA(Repository.class));
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+ svc.addRepository(uri);
+
+ Repository[] repositories = svc.listRepositories();
+ assertNotNull(repositories);
+ assertEquals(1, repositories.length);
+ assertNotNull(repositories[0]);
+ Feature[] features = repositories[0].getFeatures();
+ assertNotNull(features);
+ assertEquals(1, features.length);
+ assertNotNull(features[0]);
+ assertEquals("f1", features[0].getName());
+ assertNotNull(features[0].getDependencies());
+ assertEquals(0, features[0].getDependencies().size());
+ assertNotNull(features[0].getBundles());
+ assertEquals(1, features[0].getBundles().size());
+ assertEquals(name, features[0].getBundles().get(0));
+
+ verify(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ reset(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(12345L);
+ expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + FeatureImpl.DEFAULT_VERSION, "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle);
+
+ svc.installFeature("f1");
+
+ String[] installed = svc.listInstalledFeatures();
+ assertEquals(1, installed.length);
+ assertEquals("f1", installed[0]);
+ }
+
+ public void testUninstallFeature() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\" version=\"0.1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f1\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+ featuresRegistry.register(isA(Repository.class));
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+ svc.addRepository(uri);
+
+ verify(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ reset(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ // Installs f1 and 0.1
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(12345L);
+ expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs f1 and 0.2
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(123456L);
+ expect(bundleContext.getBundle(123456L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // UnInstalls f1 and 0.1
+ expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // UnInstalls f1 and 0.2
+ expect(bundleContext.getBundle(123456)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle);
+
+ try {
+ svc.uninstallFeature("f1");
+ fail("Uninstall should have failed as feature is not installed");
+ } catch (Exception e) {
+ // ok
+ }
+
+ svc.installFeature("f1", "0.1");
+ svc.installFeature("f1", "0.2");
+
+ try {
+ svc.uninstallFeature("f1");
+ fail("Uninstall should have failed as feature is installed in multiple versions");
+ } catch (Exception e) {
+ // ok
+ }
+
+ svc.uninstallFeature("f1", "0.1");
+ svc.uninstallFeature("f1");
+ }
+
+ // Tests Add and Remove Repository
+ public void testAddAndRemoveRepository() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\" version=\"0.1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f1\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f2\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ // loads the state
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ featuresRegistry.register(isA(Repository.class));
+
+ // SaveState for addRepository
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // SaveState for removeRepository
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 0);
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+
+ // Adds Repository
+ svc.addRepository(uri);
+
+ // Removes Repository
+ svc.removeRepository(uri);
+ }
+
+ // Tests installing all features in a repo and uninstalling
+ // all features in a repo
+ public void testInstallUninstallAllFeatures() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\" version=\"0.1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f1\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f2\" version=\"0.2\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ // loads the state
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs first feature name = f1, version = 0.1
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(12345L);
+ expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs second feature name = f1, version = 0.2
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(123456L);
+ expect(bundleContext.getBundle(123456L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs third feature name = f2, version = 0.2
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(1234567L);
+ expect(bundleContext.getBundle(1234567L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 0);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstallAllFeatures
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls first feature name = f1, version = 0.1
+ expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "1234567");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls third feature name = f2, version = 0.2
+ expect(bundleContext.getBundle(1234567)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.2", "123456");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls second feature name = f1, version = 0.2
+ expect(bundleContext.getBundle(123456)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 0);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+ svc.installAllFeatures(uri);
+
+ // Uninstalls features with versions.
+ svc.uninstallAllFeatures(uri);
+ }
+
+
+ // Tests install of a Repository that includes a feature
+ // with a feature dependency
+ // The dependant feature is in the same repository
+ // Tests uninstall of features
+ public void testInstallFeatureWithDependantFeatures() throws Exception {
+
+ String name = ApplicationContext.class.getName();
+ name = name.replace(".", "/") + ".class";
+ name = getClass().getClassLoader().getResource(name).toString();
+ name = name.substring("jar:".length(), name.indexOf('!'));
+
+ File tmp = File.createTempFile("smx", ".feature");
+ PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+ pw.println("<features>");
+ pw.println(" <feature name=\"f1\" version=\"0.1\">");
+ pw.println(" <feature version=\"0.1\">f2</feature>");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println(" <feature name=\"f2\" version=\"0.1\">");
+ pw.println(" <bundle>" + name + "</bundle>");
+ pw.println(" </feature>");
+ pw.println("</features>");
+ pw.close();
+
+ URI uri = tmp.toURI();
+
+ // loads the state
+ Preferences prefs = EasyMock.createMock(Preferences.class);
+ PreferencesService preferencesService = EasyMock.createMock(PreferencesService.class);
+ Preferences repositoriesNode = EasyMock.createMock(Preferences.class);
+ Preferences repositoriesAvailableNode = EasyMock.createMock(Preferences.class);
+ Preferences featuresNode = EasyMock.createMock(Preferences.class);
+ BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+ Bundle installedBundle = EasyMock.createMock(Bundle.class);
+ FeaturesRegistry featuresRegistry = EasyMock.createNiceMock(FeaturesRegistry.class);
+
+ // savestate from addRepository
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Installs feature f1 with dependency on f2
+ // so will install f2 first
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(12345L);
+ expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // Then installs f1
+ expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+ expect(bundleContext.installBundle(isA(String.class),
+ isA(InputStream.class))).andReturn(installedBundle);
+ expect(installedBundle.getBundleId()).andReturn(1234L);
+ expect(bundleContext.getBundle(1234L)).andReturn(installedBundle);
+ installedBundle.start();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ featuresNode.put("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "1234");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls first feature name = f1, version = 0.1
+ expect(bundleContext.getBundle(1234)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ featuresNode.put("f2" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + "0.1", "12345");
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ // uninstalls first feature name = f2, version = 0.1
+ expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+ installedBundle.uninstall();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 1);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ expect(preferencesService.getUserPreferences("FeaturesServiceState")).andStubReturn(prefs);
+ expect(prefs.node("repositories")).andReturn(repositoriesNode);
+ repositoriesNode.clear();
+ repositoriesNode.putInt("count", 0);
+ repositoriesNode.put("item.0", uri.toString());
+ expect(prefs.node("features")).andReturn(featuresNode);
+ featuresNode.clear();
+ prefs.putBoolean("bootFeaturesInstalled", false);
+ prefs.flush();
+
+ replay(preferencesService, prefs, repositoriesNode, featuresNode, bundleContext, installedBundle, featuresRegistry);
+
+ FeaturesServiceImpl svc = new FeaturesServiceImpl();
+ svc.setPreferences(preferencesService);
+ svc.setBundleContext(bundleContext);
+ svc.setFeaturesServiceRegistry(featuresRegistry);
+ svc.addRepository(uri);
+
+ svc.installFeature("f1", "0.1");
+
+ // Uninstall repository
+ svc.uninstallFeature("f1", "0.1");
+ svc.uninstallFeature("f2", "0.1");
+
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.java
new file mode 100644
index 0000000..e8cfffd
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/RepositoryTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.servicemix.kernel.gshell.features;
+
+import java.net.URI;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.internal.FeatureImpl;
+import org.apache.servicemix.kernel.gshell.features.internal.RepositoryImpl;
+
+
+public class RepositoryTest extends TestCase {
+
+ public void testLoad() throws Exception {
+ RepositoryImpl r = new RepositoryImpl(getClass().getResource("repo1.xml").toURI());
+ // Check repo
+ URI[] repos = r.getRepositories();
+ assertNotNull(repos);
+ assertEquals(1, repos.length);
+ assertEquals(URI.create("urn:r1"), repos[0]);
+ // Check features
+ Feature[] features = r.getFeatures();
+ assertNotNull(features);
+ assertEquals(2, features.length);
+ assertNotNull(features[0]);
+ assertEquals("f1", features[0].getName());
+ assertNotNull(features[0].getConfigurations());
+ assertEquals(1, features[0].getConfigurations().size());
+ assertNotNull(features[0].getConfigurations().get("c1"));
+ assertEquals(1, features[0].getConfigurations().get("c1").size());
+ assertEquals("v", features[0].getConfigurations().get("c1").get("k"));
+ assertNotNull(features[0].getDependencies());
+ assertEquals(0, features[0].getDependencies().size());
+ assertNotNull(features[0].getBundles());
+ assertEquals(2, features[0].getBundles().size());
+ assertEquals("b1", features[0].getBundles().get(0));
+ assertEquals("b2", features[0].getBundles().get(1));
+ assertNotNull(features[1]);
+ assertEquals("f2", features[1].getName());
+ assertNotNull(features[1].getConfigurations());
+ assertEquals(0, features[1].getConfigurations().size());
+ assertNotNull(features[1].getDependencies());
+ assertEquals(1, features[1].getDependencies().size());
+ assertEquals("f1" + FeatureImpl.SPLIT_FOR_NAME_AND_VERSION + FeatureImpl.DEFAULT_VERSION, features[1].getDependencies().get(0).toString());
+ assertNotNull(features[1].getBundles());
+ assertEquals(1, features[1].getBundles().size());
+ assertEquals("b3", features[1].getBundles().get(0));
+ }
+
+ public void testShowWrongUriInException() throws Exception {
+ String uri = "src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml";
+ RepositoryImpl r = new RepositoryImpl(new URI(uri));
+ try {
+ r.load();
+ } catch (Exception e) {
+ assertTrue(e.getMessage().contains(uri));
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.java b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.java
new file mode 100644
index 0000000..8ee87de
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/java/org/apache/servicemix/kernel/gshell/features/internal/FeaturesServiceImplTest.java
@@ -0,0 +1,73 @@
+/*
+ * 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.servicemix.kernel.gshell.features.internal;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.apache.servicemix.kernel.gshell.features.Feature;
+
+/**
+ * Test cases for {@link FeaturesServiceImpl}
+ */
+public class FeaturesServiceImplTest extends TestCase {
+
+ public void testGetFeature() throws Exception {
+ final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+ Map<String, Feature> versions = new HashMap<String, Feature>();
+ FeatureImpl feature = new FeatureImpl("transaction");
+ versions.put("1.0.0", feature);
+ features.put("transaction", versions);
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features;
+ };
+ };
+ assertNotNull(impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+ assertSame(feature, impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+ }
+
+ public void testGetFeatureNotAvailable() throws Exception {
+ final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+ Map<String, Feature> versions = new HashMap<String, Feature>();
+ versions.put("1.0.0", new FeatureImpl("transaction"));
+ features.put("transaction", versions);
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features;
+ };
+ };
+ assertNull(impl.getFeature("activemq", FeatureImpl.DEFAULT_VERSION));
+ }
+
+ public void testGetFeatureHighestAvailable() throws Exception {
+ final Map<String, Map<String, Feature>> features = new HashMap<String, Map<String,Feature>>();
+ Map<String, Feature> versions = new HashMap<String, Feature>();
+ versions.put("1.0.0", new FeatureImpl("transaction", "1.0.0"));
+ versions.put("2.0.0", new FeatureImpl("transaction", "2.0.0"));
+ features.put("transaction", versions);
+ final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+ protected Map<String,Map<String,Feature>> getFeatures() throws Exception {
+ return features;
+ };
+ };
+ assertNotNull(impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION));
+ assertSame("2.0.0", impl.getFeature("transaction", FeatureImpl.DEFAULT_VERSION).getVersion());
+ }
+
+}
diff --git a/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml b/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml
new file mode 100644
index 0000000..ea21df9
--- /dev/null
+++ b/karaf/gshell/gshell-features/src/test/resources/org/apache/servicemix/kernel/gshell/features/repo1.xml
@@ -0,0 +1,31 @@
+<!--
+
+ 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.
+-->
+<features>
+ <repository>urn:r1</repository>
+ <feature name="f1">
+ <config name="c1">
+ k=v
+ </config>
+ <bundle>b1</bundle>
+ <bundle>b2</bundle>
+ </feature>
+ <feature name="f2">
+ <feature>f1</feature>
+ <bundle>b3</bundle>
+ </feature>
+</features>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/pom.xml b/karaf/gshell/gshell-log/pom.xml
new file mode 100644
index 0000000..04ca7bd
--- /dev/null
+++ b/karaf/gshell/gshell-log/pom.xml
@@ -0,0 +1,95 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Log Commands</name>
+
+ <description>
+ Provides the OSGi Log commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ops4j.pax.logging</groupId>
+ <artifactId>pax-logging-service</artifactId>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.gshell.log*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ org.ops4j.pax.logging,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java
new file mode 100644
index 0000000..917e01c
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayException.java
@@ -0,0 +1,52 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+public class DisplayException extends OsgiCommandSupport {
+
+ protected LruList<PaxLoggingEvent> events;
+
+ public LruList<PaxLoggingEvent> getEvents() {
+ return events;
+ }
+
+ public void setEvents(LruList<PaxLoggingEvent> events) {
+ this.events = events;
+ }
+
+ protected Object doExecute() throws Exception {
+ PaxLoggingEvent throwableEvent = null;
+ Iterable<PaxLoggingEvent> le = events.getElements(Integer.MAX_VALUE);
+ for (PaxLoggingEvent event : le) {
+ if (event.getThrowableStrRep() != null) {
+ throwableEvent = event;
+ // Do not break, as we iterate from the oldest to the newest event
+ }
+ }
+ if (throwableEvent != null) {
+ for (String r : throwableEvent.getThrowableStrRep()) {
+ io.out.println(r);
+ }
+ io.out.println();
+ }
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java
new file mode 100644
index 0000000..3beff07
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/DisplayLog.java
@@ -0,0 +1,78 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.apache.servicemix.kernel.gshell.log.layout.PatternConverter;
+import org.apache.servicemix.kernel.gshell.log.layout.PatternParser;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * Displays the last log entries
+ */
+public class DisplayLog extends OsgiCommandSupport {
+
+ @Option(name = "-n", description="Number of entries to display")
+ protected int entries;
+
+ @Option(name = "-p", description="Output formatting pattern")
+ protected String overridenPattern;
+
+ protected String pattern;
+
+ protected LruList<PaxLoggingEvent> events;
+
+ public LruList<PaxLoggingEvent> getEvents() {
+ return events;
+ }
+
+ public void setEvents(LruList<PaxLoggingEvent> events) {
+ this.events = events;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ protected Object doExecute() throws Exception {
+ PatternConverter cnv = new PatternParser(overridenPattern != null ? overridenPattern : pattern).parse();
+
+ Iterable<PaxLoggingEvent> le = events.getElements(entries == 0 ? Integer.MAX_VALUE : entries);
+ StringBuffer sb = new StringBuffer();
+ for (PaxLoggingEvent event : le) {
+ sb.setLength(0);
+ for (PatternConverter pc = cnv; pc != null; pc = pc.next) {
+ pc.format(sb, event);
+ }
+ io.out.print(sb.toString());
+ if (event.getThrowableStrRep() != null) {
+ for (String r : event.getThrowableStrRep()) {
+ io.out.println(r);
+ }
+ }
+ }
+ io.out.println();
+
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.java
new file mode 100644
index 0000000..cf93797
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/GetLogLevel.java
@@ -0,0 +1,117 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Get the log level for a given logger
+ */
+public class GetLogLevel extends OsgiCommandSupport {
+
+ @Argument(required = false, description = "Logger name, ALL or ROOT (default)")
+ String logger;
+
+ static final String CONFIGURATION_PID = "org.ops4j.pax.logging";
+ static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+ static final String LOGGER_PREFIX = "log4j.logger.";
+ static final String ALL_LOGGER = "ALL";
+ static final String ROOT_LOGGER = "ROOT";
+
+ protected Object doExecute() throws Exception {
+ ConfigurationAdmin cfgAdmin = getConfigAdmin();
+ Configuration cfg = cfgAdmin.getConfiguration(CONFIGURATION_PID, null);
+ Dictionary props = cfg.getProperties();
+
+ if (ROOT_LOGGER.equalsIgnoreCase(this.logger)) {
+ this.logger = null;
+ }
+ if (ALL_LOGGER.equalsIgnoreCase(logger)) {
+ String root = getLevel((String) props.get(ROOT_LOGGER_PREFIX));
+ Map<String, String> loggers = new TreeMap<String, String>();
+ for (Enumeration e = props.keys(); e.hasMoreElements();) {
+ String prop = (String) e.nextElement();
+ if (prop.startsWith(LOGGER_PREFIX)) {
+ String val = getLevel((String) props.get(prop));
+ loggers.put(prop.substring(LOGGER_PREFIX.length()), val);
+ }
+ }
+ io.out.println("ROOT: " + root);
+ for (String logger : loggers.keySet()) {
+ io.out.println(logger + ": " + loggers.get(logger));
+ }
+ } else {
+ String logger = this.logger;
+ String val;
+ for (;;) {
+ String prop;
+ if (logger == null) {
+ prop = ROOT_LOGGER_PREFIX;
+ } else {
+ prop = LOGGER_PREFIX + logger;
+ }
+ val = (String) props.get(prop);
+ val = getLevel(val);
+ if (val != null || logger == null) {
+ break;
+ }
+ int idx = logger.lastIndexOf('.');
+ if (idx < 0) {
+ logger = null;
+ } else {
+ logger = logger.substring(0, idx);
+ }
+ }
+ String st = "Level: " + val;
+ if (logger != this.logger) {
+ st += " (inherited from " + (logger != null ? logger : "ROOT") + ")";
+ }
+ io.out.println(st);
+ }
+ return Result.SUCCESS;
+ }
+
+ protected String getLevel(String prop) {
+ if (prop == null) {
+ return null;
+ } else {
+ String val = prop.trim();
+ int idx = val.indexOf(",");
+ if (idx == 0) {
+ val = null;
+ } else if (idx > 0) {
+ val = val.substring(0, idx);
+ }
+ return val;
+ }
+ }
+
+ protected ConfigurationAdmin getConfigAdmin() {
+ ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+ return getService(ConfigurationAdmin.class, ref);
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java
new file mode 100644
index 0000000..a214a17
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/LruList.java
@@ -0,0 +1,99 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import java.util.Arrays;
+
+/**
+ * A list that only keep the last N elements added
+ */
+public class LruList<E> {
+
+ private E[] elements;
+ private transient int start = 0;
+ private transient int end = 0;
+ private transient boolean full = false;
+ private final int maxElements;
+
+ public LruList(int size) {
+ if (size <= 0) {
+ throw new IllegalArgumentException("The size must be greater than 0");
+ }
+ elements = (E[]) new Object[size];
+ maxElements = elements.length;
+ }
+
+ public int size() {
+ synchronized (elements) {
+ int size = 0;
+ if (end < start) {
+ size = maxElements - start + end;
+ } else if (end == start) {
+ size = (full ? maxElements : 0);
+ } else {
+ size = end - start;
+ }
+ return size;
+ }
+ }
+
+ public void add(E element) {
+ synchronized (elements) {
+ if (null == element) {
+ throw new NullPointerException("Attempted to add null object to buffer");
+ }
+ if (size() == maxElements) {
+ Object e = elements[start];
+ if (null != e) {
+ elements[start++] = null;
+ if (start >= maxElements) {
+ start = 0;
+ }
+ full = false;
+ }
+ }
+ elements[end++] = element;
+ if (end >= maxElements) {
+ end = 0;
+ }
+ if (end == start) {
+ full = true;
+ }
+ }
+ }
+
+ public Iterable<E> getElements() {
+ synchronized (elements) {
+ return getElements(size());
+ }
+ }
+
+ public Iterable<E> getElements(int nb) {
+ synchronized (elements) {
+ int s = size();
+ nb = Math.min(Math.max(0, nb), s);
+ E[] e = (E[]) new Object[nb];
+ for (int i = 0; i < nb; i++) {
+ e[i] = elements[(i + s - nb + start) % maxElements];
+ }
+ return Arrays.asList(e);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.java
new file mode 100644
index 0000000..7da0f7f
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/SetLogLevel.java
@@ -0,0 +1,119 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import java.util.Dictionary;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+
+/**
+ * Set the log level for a given logger
+ */
+public class SetLogLevel extends OsgiCommandSupport {
+
+ @Argument(index = 0, required = true, description = "Level (TRACE, DEBUG, INFO, WARN, ERROR or - to unset")
+ String level;
+
+ @Argument(index = 1, required = false, description = "Logger name or ROOT (default)")
+ String logger;
+
+ static final String CONFIGURATION_PID = "org.ops4j.pax.logging";
+ static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+ static final String LOGGER_PREFIX = "log4j.logger.";
+ static final String ROOT_LOGGER = "ROOT";
+
+ static final String TRACE = "TRACE";
+ static final String DEBUG = "DEBUG";
+ static final String INFO = "INFO";
+ static final String WARN = "WARN";
+ static final String ERROR = "ERROR";
+ static final String INHERITED = "-";
+
+ protected Object doExecute() throws Exception {
+ if (ROOT_LOGGER.equalsIgnoreCase(this.logger)) {
+ this.logger = null;
+ }
+ if (!TRACE.equals(level) &&
+ !DEBUG.equals(level) &&
+ !INFO.equals(level) &&
+ !WARN.equals(level) &&
+ !ERROR.equals(level) &&
+ !INHERITED.equals(level)) {
+ io.err.println("level must be set to TRACE, DEBUG, INFO, WARN or ERROR (or - to unset it)");
+ return Result.FAILURE;
+ }
+ if (INHERITED.equals(level) && logger == null) {
+ io.err.println("Can not unset the ROOT logger");
+ return Result.FAILURE;
+ }
+
+ ConfigurationAdmin cfgAdmin = getConfigAdmin();
+ Configuration cfg = cfgAdmin.getConfiguration(CONFIGURATION_PID, null);
+ Dictionary props = cfg.getProperties();
+
+ String logger = this.logger;
+ String val;
+ String prop;
+ if (logger == null) {
+ prop = ROOT_LOGGER_PREFIX;
+ } else {
+ prop = LOGGER_PREFIX + logger;
+ }
+ val = (String) props.get(prop);
+ if (INHERITED.equals(level)) {
+ if (val != null) {
+ val = val.trim();
+ int idx = val.indexOf(",");
+ if (idx > 0) {
+ val = val.substring(idx);
+ } else {
+ val = null;
+ }
+ }
+ } else {
+ if (val == null) {
+ val = level;
+ } else {
+ val = val.trim();
+ int idx = val.indexOf(",");
+ if (idx == 0) {
+ val = level + val;
+ } else if (idx > 0) {
+ val = level + val.substring(idx);
+ }
+ }
+ }
+ if (val == null) {
+ props.remove(prop);
+ } else {
+ props.put(prop, val);
+ }
+ cfg.update(props);
+
+ return Result.SUCCESS;
+ }
+
+ protected ConfigurationAdmin getConfigAdmin() {
+ ServiceReference ref = getBundleContext().getServiceReference(ConfigurationAdmin.class.getName());
+ return getService(ConfigurationAdmin.class, ref);
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.java
new file mode 100644
index 0000000..a1607b4
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/VmLogAppender.java
@@ -0,0 +1,45 @@
+/*
+ * 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.servicemix.kernel.gshell.log;
+
+import org.ops4j.pax.logging.spi.PaxAppender;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * A Pax Logging appender that keep a list of last events
+ */
+public class VmLogAppender implements PaxAppender {
+
+ protected LruList<PaxLoggingEvent> events;
+
+ public LruList<PaxLoggingEvent> getEvents() {
+ return events;
+ }
+
+ public void setEvents(LruList<PaxLoggingEvent> events) {
+ this.events = events;
+ }
+
+ public void doAppend(PaxLoggingEvent event) {
+ if (events != null) {
+ events.add(event);
+ }
+ }
+
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java
new file mode 100644
index 0000000..03b81ff
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/AbsoluteTimeDateFormat.java
@@ -0,0 +1,145 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+/**
+ Formats a {@link Date} in the format "HH:mm:ss,SSS" for example,
+ "15:49:37,459".
+
+ @author Ceki Gülcü
+ @author Andrew Vajoczki
+
+ @since 0.7.5
+*/
+public class AbsoluteTimeDateFormat extends DateFormat {
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.AbsoluteTimeDateFormat} in layouts. Current
+ value is <b>ABSOLUTE</b>. */
+ public final static String ABS_TIME_DATE_FORMAT = "ABSOLUTE";
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.DateTimeDateFormat} in layouts. Current
+ value is <b>DATE</b>.
+ */
+ public final static String DATE_AND_TIME_DATE_FORMAT = "DATE";
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.ISO8601DateFormat} in layouts. Current
+ value is <b>ISO8601</b>.
+ */
+ public final static String ISO8601_DATE_FORMAT = "ISO8601";
+
+ public
+ AbsoluteTimeDateFormat() {
+ setCalendar(Calendar.getInstance());
+ }
+
+ public
+ AbsoluteTimeDateFormat(TimeZone timeZone) {
+ setCalendar(Calendar.getInstance(timeZone));
+ }
+
+ private static long previousTime;
+ private static char[] previousTimeWithoutMillis = new char[9]; // "HH:mm:ss."
+
+ /**
+ Appends to <code>sbuf</code> the time in the format
+ "HH:mm:ss,SSS" for example, "15:49:37,459"
+
+ @param date the date to format
+ @param sbuf the string buffer to write to
+ @param fieldPosition remains untouched
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ long now = date.getTime();
+ int millis = (int)(now % 1000);
+
+ if ((now - millis) != previousTime) {
+ // We reach this point at most once per second
+ // across all threads instead of each time format()
+ // is called. This saves considerable CPU time.
+
+ calendar.setTime(date);
+
+ int start = sbuf.length();
+
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if(hour < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(hour);
+ sbuf.append(':');
+
+ int mins = calendar.get(Calendar.MINUTE);
+ if(mins < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(mins);
+ sbuf.append(':');
+
+ int secs = calendar.get(Calendar.SECOND);
+ if(secs < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(secs);
+ sbuf.append(',');
+
+ // store the time string for next time to avoid recomputation
+ sbuf.getChars(start, sbuf.length(), previousTimeWithoutMillis, 0);
+
+ previousTime = now - millis;
+ }
+ else {
+ sbuf.append(previousTimeWithoutMillis);
+ }
+
+
+
+ if(millis < 100)
+ sbuf.append('0');
+ if(millis < 10)
+ sbuf.append('0');
+
+ sbuf.append(millis);
+ return sbuf;
+ }
+
+ /**
+ This method does not do anything but return <code>null</code>.
+ */
+ public
+ Date parse(String s, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.java
new file mode 100644
index 0000000..0412ac8
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/DateTimeDateFormat.java
@@ -0,0 +1,86 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormatSymbols;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+/**
+ Formats a {@link Date} in the format "dd MMM yyyy HH:mm:ss,SSS" for example,
+ "06 Nov 1994 15:49:37,459".
+
+ @author Ceki Gülcü
+ @since 0.7.5
+*/
+public class DateTimeDateFormat extends AbsoluteTimeDateFormat {
+
+ String[] shortMonths;
+
+ public
+ DateTimeDateFormat() {
+ super();
+ shortMonths = new DateFormatSymbols().getShortMonths();
+ }
+
+ public
+ DateTimeDateFormat(TimeZone timeZone) {
+ this();
+ setCalendar(Calendar.getInstance(timeZone));
+ }
+
+ /**
+ Appends to <code>sbuf</code> the date in the format "dd MMM yyyy
+ HH:mm:ss,SSS" for example, "06 Nov 1994 08:49:37,459".
+
+ @param sbuf the string buffer to write to
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ calendar.setTime(date);
+
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if(day < 10)
+ sbuf.append('0');
+ sbuf.append(day);
+ sbuf.append(' ');
+ sbuf.append(shortMonths[calendar.get(Calendar.MONTH)]);
+ sbuf.append(' ');
+
+ int year = calendar.get(Calendar.YEAR);
+ sbuf.append(year);
+ sbuf.append(' ');
+
+ return super.format(date, sbuf, fieldPosition);
+ }
+
+ /**
+ This method does not do anything but return <code>null</code>.
+ */
+ public
+ Date parse(java.lang.String s, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java
new file mode 100644
index 0000000..b5d9dff
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/FormattingInfo.java
@@ -0,0 +1,47 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+
+/**
+ * Copied from log4j
+ */
+/**
+ FormattingInfo instances contain the information obtained when parsing
+ formatting modifiers in conversion modifiers.
+
+ @author <a href=mailto:jim_cakalic@na.biomerieux.com>Jim Cakalic</a>
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ */
+public class FormattingInfo {
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ void reset() {
+ min = -1;
+ max = 0x7FFFFFFF;
+ leftAlign = false;
+ }
+
+ void dump() {
+ //LogLog.debug("min="+min+", max="+max+", leftAlign="+leftAlign);
+ }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java
new file mode 100644
index 0000000..f19b8bd
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/ISO8601DateFormat.java
@@ -0,0 +1,155 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * Copied from log4j
+ */
+// Contributors: Arndt Schoenewald <arndt@ibm23093i821.mc.schoenewald.de>
+/**
+ Formats a {@link Date} in the format "yyyy-MM-dd HH:mm:ss,SSS" for example
+ "1999-11-27 15:49:37,459".
+
+ <p>Refer to the <a
+ href=http://www.cl.cam.ac.uk/~mgk25/iso-time.html>summary of the
+ International Standard Date and Time Notation</a> for more
+ information on this format.
+
+ @author Ceki Gülcü
+ @author Andrew Vajoczki
+
+ @since 0.7.5
+*/
+public class ISO8601DateFormat extends AbsoluteTimeDateFormat {
+
+ public
+ ISO8601DateFormat() {
+ }
+
+ public
+ ISO8601DateFormat(TimeZone timeZone) {
+ super(timeZone);
+ }
+
+ static private long lastTime;
+ static private char[] lastTimeString = new char[20];
+
+ /**
+ Appends a date in the format "YYYY-mm-dd HH:mm:ss,SSS"
+ to <code>sbuf</code>. For example: "1999-11-27 15:49:37,459".
+
+ @param sbuf the <code>StringBuffer</code> to write to
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ long now = date.getTime();
+ int millis = (int)(now % 1000);
+
+ if ((now - millis) != lastTime) {
+ // We reach this point at most once per second
+ // across all threads instead of each time format()
+ // is called. This saves considerable CPU time.
+
+ calendar.setTime(date);
+
+ int start = sbuf.length();
+
+ int year = calendar.get(Calendar.YEAR);
+ sbuf.append(year);
+
+ String month;
+ switch(calendar.get(Calendar.MONTH)) {
+ case Calendar.JANUARY: month = "-01-"; break;
+ case Calendar.FEBRUARY: month = "-02-"; break;
+ case Calendar.MARCH: month = "-03-"; break;
+ case Calendar.APRIL: month = "-04-"; break;
+ case Calendar.MAY: month = "-05-"; break;
+ case Calendar.JUNE: month = "-06-"; break;
+ case Calendar.JULY: month = "-07-"; break;
+ case Calendar.AUGUST: month = "-08-"; break;
+ case Calendar.SEPTEMBER: month = "-09-"; break;
+ case Calendar.OCTOBER: month = "-10-"; break;
+ case Calendar.NOVEMBER: month = "-11-"; break;
+ case Calendar.DECEMBER: month = "-12-"; break;
+ default: month = "-NA-"; break;
+ }
+ sbuf.append(month);
+
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if(day < 10)
+ sbuf.append('0');
+ sbuf.append(day);
+
+ sbuf.append(' ');
+
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if(hour < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(hour);
+ sbuf.append(':');
+
+ int mins = calendar.get(Calendar.MINUTE);
+ if(mins < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(mins);
+ sbuf.append(':');
+
+ int secs = calendar.get(Calendar.SECOND);
+ if(secs < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(secs);
+
+ sbuf.append(',');
+
+ // store the time string for next time to avoid recomputation
+ sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
+ lastTime = now - millis;
+ }
+ else {
+ sbuf.append(lastTimeString);
+ }
+
+
+ if (millis < 100)
+ sbuf.append('0');
+ if (millis < 10)
+ sbuf.append('0');
+
+ sbuf.append(millis);
+ return sbuf;
+ }
+
+ /**
+ This method does not do anything but return <code>null</code>.
+ */
+ public
+ Date parse(java.lang.String s, ParsePosition pos) {
+ return null;
+ }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java
new file mode 100644
index 0000000..1652d57
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternConverter.java
@@ -0,0 +1,110 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+
+ <p>PatternConverter is an abtract class that provides the
+ formatting functionality that derived classes need.
+
+ <p>Conversion specifiers in a conversion patterns are parsed to
+ individual PatternConverters. Each of which is responsible for
+ converting a logging event in a converter specific manner.
+
+ @author <a href="mailto:cakalijp@Maritz.com">James P. Cakalic</a>
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ */
+public abstract class PatternConverter {
+ public PatternConverter next;
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ protected
+ PatternConverter() { }
+
+ protected
+ PatternConverter(FormattingInfo fi) {
+ min = fi.min;
+ max = fi.max;
+ leftAlign = fi.leftAlign;
+ }
+
+ /**
+ Derived pattern converters must override this method in order to
+ convert conversion specifiers in the correct way.
+ */
+ abstract
+ protected
+ String convert(PaxLoggingEvent event);
+
+ /**
+ A template method for formatting in a converter specific way.
+ */
+ public
+ void format(StringBuffer sbuf, PaxLoggingEvent e) {
+ String s = convert(e);
+
+ if(s == null) {
+ if(0 < min)
+ spacePad(sbuf, min);
+ return;
+ }
+
+ int len = s.length();
+
+ if(len > max)
+ sbuf.append(s.substring(len-max));
+ else if(len < min) {
+ if(leftAlign) {
+ sbuf.append(s);
+ spacePad(sbuf, min-len);
+ }
+ else {
+ spacePad(sbuf, min-len);
+ sbuf.append(s);
+ }
+ }
+ else
+ sbuf.append(s);
+ }
+
+ static String[] SPACES = {" ", " ", " ", " ", //1,2,4,8 spaces
+ " ", // 16 spaces
+ " " }; // 32 spaces
+
+ /**
+ Fast space padding method.
+ */
+ public
+ void spacePad(StringBuffer sbuf, int length) {
+ while(length >= 32) {
+ sbuf.append(SPACES[5]);
+ length -= 32;
+ }
+
+ for(int i = 4; i >= 0; i--) {
+ if((length & (1<<i)) != 0) {
+ sbuf.append(SPACES[i]);
+ }
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java
new file mode 100644
index 0000000..4a74fd1
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/java/org/apache/servicemix/kernel/gshell/log/layout/PatternParser.java
@@ -0,0 +1,527 @@
+/*
+ * 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.servicemix.kernel.gshell.log.layout;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.ops4j.pax.logging.spi.PaxLocationInfo;
+import org.ops4j.pax.logging.spi.PaxLoggingEvent;
+
+/**
+ * Copied from log4j
+ */
+// Contributors: Nelson Minar <(nelson@monkey.org>
+// Igor E. Poteryaev <jah@mail.ru>
+// Reinhard Deschler <reinhard.deschler@web.de>
+/**
+ Most of the work of the {@link org.apache.log4j.PatternLayout} class
+ is delegated to the PatternParser class.
+
+ @author <a href=mailto:"cakalijp@Maritz.com">James P. Cakalic</a>
+ @author Ceki Gülcü
+ @author Anders Kristensen
+
+ @since 0.8.2
+*/
+public class PatternParser {
+
+ private static final String LINE_SEP = System.getProperty("line.separator");
+
+ private static final char ESCAPE_CHAR = '%';
+
+ private static final int LITERAL_STATE = 0;
+ private static final int CONVERTER_STATE = 1;
+ private static final int MINUS_STATE = 2;
+ private static final int DOT_STATE = 3;
+ private static final int MIN_STATE = 4;
+ private static final int MAX_STATE = 5;
+
+ static final int FULL_LOCATION_CONVERTER = 1000;
+ static final int METHOD_LOCATION_CONVERTER = 1001;
+ static final int CLASS_LOCATION_CONVERTER = 1002;
+ static final int LINE_LOCATION_CONVERTER = 1003;
+ static final int FILE_LOCATION_CONVERTER = 1004;
+
+ static final int RELATIVE_TIME_CONVERTER = 2000;
+ static final int THREAD_CONVERTER = 2001;
+ static final int LEVEL_CONVERTER = 2002;
+ static final int NDC_CONVERTER = 2003;
+ static final int MESSAGE_CONVERTER = 2004;
+
+ int state;
+ protected StringBuffer currentLiteral = new StringBuffer(32);
+ protected int patternLength;
+ protected int i;
+ PatternConverter head;
+ PatternConverter tail;
+ protected FormattingInfo formattingInfo = new FormattingInfo();
+ protected String pattern;
+
+ public
+ PatternParser(String pattern) {
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ state = LITERAL_STATE;
+ }
+
+ private
+ void addToList(PatternConverter pc) {
+ if(head == null) {
+ head = tail = pc;
+ } else {
+ tail.next = pc;
+ tail = pc;
+ }
+ }
+
+ protected
+ String extractOption() {
+ if((i < patternLength) && (pattern.charAt(i) == '{')) {
+ int end = pattern.indexOf('}', i);
+ if (end > i) {
+ String r = pattern.substring(i + 1, end);
+ i = end+1;
+ return r;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ The option is expected to be in decimal and positive. In case of
+ error, zero is returned. */
+ protected
+ int extractPrecisionOption() {
+ String opt = extractOption();
+ int r = 0;
+ if(opt != null) {
+ try {
+ r = Integer.parseInt(opt);
+ if(r <= 0) {
+ //LogLog.error("Precision option (" + opt + ") isn't a positive integer.");
+ r = 0;
+ }
+ }
+ catch (NumberFormatException e) {
+ //LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
+ }
+ }
+ return r;
+ }
+
+ public
+ PatternConverter parse() {
+ char c;
+ i = 0;
+ while(i < patternLength) {
+ c = pattern.charAt(i++);
+ switch(state) {
+ case LITERAL_STATE:
+ // In literal state, the last char is always a literal.
+ if(i == patternLength) {
+ currentLiteral.append(c);
+ continue;
+ }
+ if(c == ESCAPE_CHAR) {
+ // peek at the next char.
+ switch(pattern.charAt(i)) {
+ case ESCAPE_CHAR:
+ currentLiteral.append(c);
+ i++; // move pointer
+ break;
+ case 'n':
+ currentLiteral.append(LINE_SEP);
+ i++; // move pointer
+ break;
+ default:
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(
+ currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""
+ // +currentLiteral+"\".");
+ }
+ currentLiteral.setLength(0);
+ currentLiteral.append(c); // append %
+ state = CONVERTER_STATE;
+ formattingInfo.reset();
+ }
+ }
+ else {
+ currentLiteral.append(c);
+ }
+ break;
+ case CONVERTER_STATE:
+ currentLiteral.append(c);
+ switch(c) {
+ case '-':
+ formattingInfo.leftAlign = true;
+ break;
+ case '.':
+ state = DOT_STATE;
+ break;
+ default:
+ if(c >= '0' && c <= '9') {
+ formattingInfo.min = c - '0';
+ state = MIN_STATE;
+ }
+ else
+ finalizeConverter(c);
+ } // switch
+ break;
+ case MIN_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.min = formattingInfo.min*10 + (c - '0');
+ else if(c == '.')
+ state = DOT_STATE;
+ else {
+ finalizeConverter(c);
+ }
+ break;
+ case DOT_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9') {
+ formattingInfo.max = c - '0';
+ state = MAX_STATE;
+ }
+ else {
+ //LogLog.error("Error occured in position "+i+".\n Was expecting digit, instead got char \""+c+"\".");
+ state = LITERAL_STATE;
+ }
+ break;
+ case MAX_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.max = formattingInfo.max*10 + (c - '0');
+ else {
+ finalizeConverter(c);
+ state = LITERAL_STATE;
+ }
+ break;
+ } // switch
+ } // while
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+ }
+ return head;
+ }
+
+ protected
+ void finalizeConverter(char c) {
+ PatternConverter pc = null;
+ switch(c) {
+ case 'c':
+ pc = new CategoryPatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CATEGORY converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'C':
+ pc = new ClassNamePatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CLASS_NAME converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'd':
+ String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+ DateFormat df;
+ String dOpt = extractOption();
+ if(dOpt != null)
+ dateFormatStr = dOpt;
+
+ if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+ df = new ISO8601DateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+ df = new AbsoluteTimeDateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+ df = new DateTimeDateFormat();
+ else {
+ try {
+ df = new SimpleDateFormat(dateFormatStr);
+ }
+ catch (IllegalArgumentException e) {
+ //LogLog.error("Could not instantiate SimpleDateFormat with " + dateFormatStr, e);
+ df = new ISO8601DateFormat();
+ }
+ }
+ pc = new DatePatternConverter(formattingInfo, df);
+ //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'F':
+ pc = new LocationPatternConverter(formattingInfo,
+ FILE_LOCATION_CONVERTER);
+ //LogLog.debug("File name converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*case 'l':
+ pc = new LocationPatternConverter(formattingInfo,
+ FULL_LOCATION_CONVERTER);
+ //LogLog.debug("Location converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;*/
+ case 'L':
+ pc = new LocationPatternConverter(formattingInfo,
+ LINE_LOCATION_CONVERTER);
+ //LogLog.debug("LINE NUMBER converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'm':
+ pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+ //LogLog.debug("MESSAGE converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'M':
+ pc = new LocationPatternConverter(formattingInfo,
+ METHOD_LOCATION_CONVERTER);
+ //LogLog.debug("METHOD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'p':
+ pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+ //LogLog.debug("LEVEL converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'r':
+ pc = new BasicPatternConverter(formattingInfo,
+ RELATIVE_TIME_CONVERTER);
+ //LogLog.debug("RELATIVE time converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 't':
+ pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+ //LogLog.debug("THREAD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*case 'u':
+ if(i < patternLength) {
+ char cNext = pattern.charAt(i);
+ if(cNext >= '0' && cNext <= '9') {
+ pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
+ LogLog.debug("USER converter ["+cNext+"].");
+ formattingInfo.dump();
+ currentLiteral.setLength(0);
+ i++;
+ }
+ else
+ LogLog.error("Unexpected char" +cNext+" at position "+i);
+ }
+ break;*/
+ /*case 'x':
+ pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+ //LogLog.debug("NDC converter.");
+ currentLiteral.setLength(0);
+ break;
+ case 'X':
+ String xOpt = extractOption();
+ pc = new MDCPatternConverter(formattingInfo, xOpt);
+ currentLiteral.setLength(0);
+ break;*/
+ default:
+ //LogLog.error("Unexpected char [" +c+"] at position "+i+" in conversion patterrn.");
+ pc = new LiteralPatternConverter(currentLiteral.toString());
+ currentLiteral.setLength(0);
+ }
+
+ addConverter(pc);
+ }
+
+ protected
+ void addConverter(PatternConverter pc) {
+ currentLiteral.setLength(0);
+ // Add the pattern converter to the list.
+ addToList(pc);
+ // Next pattern is assumed to be a literal.
+ state = LITERAL_STATE;
+ // Reset formatting info
+ formattingInfo.reset();
+ }
+
+ // ---------------------------------------------------------------------
+ // PatternConverters
+ // ---------------------------------------------------------------------
+
+ private static class BasicPatternConverter extends PatternConverter {
+ int type;
+
+ BasicPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(PaxLoggingEvent event) {
+ switch(type) {
+ case RELATIVE_TIME_CONVERTER:
+ return (Long.toString(event.getTimeStamp() - LoggingEvent.getStartTime()));
+ case THREAD_CONVERTER:
+ return event.getThreadName();
+ case LEVEL_CONVERTER:
+ return event.getLevel().toString();
+ // case NDC_CONVERTER:
+ //return event.getNDC();
+ case MESSAGE_CONVERTER: {
+ return event.getRenderedMessage();
+ }
+ default: return null;
+ }
+ }
+ }
+
+ private static class LiteralPatternConverter extends PatternConverter {
+ private String literal;
+
+ LiteralPatternConverter(String value) {
+ literal = value;
+ }
+
+ public
+ final
+ void format(StringBuffer sbuf, LoggingEvent event) {
+ sbuf.append(literal);
+ }
+
+ public
+ String convert(PaxLoggingEvent event) {
+ return literal;
+ }
+ }
+
+ private static class DatePatternConverter extends PatternConverter {
+ private DateFormat df;
+ private Date date;
+
+ DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+ super(formattingInfo);
+ date = new Date();
+ this.df = df;
+ }
+
+ public
+ String convert(PaxLoggingEvent event) {
+ date.setTime(event.getTimeStamp());
+ String converted = null;
+ try {
+ converted = df.format(date);
+ }
+ catch (Exception ex) {
+ //LogLog.error("Error occured while converting date.", ex);
+ }
+ return converted;
+ }
+ }
+
+ private class LocationPatternConverter extends PatternConverter {
+ int type;
+
+ LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(PaxLoggingEvent event) {
+ PaxLocationInfo locationInfo = event.getLocationInformation();
+ switch(type) {
+ /*case FULL_LOCATION_CONVERTER:
+ return locationInfo.fullInfo;*/
+ case METHOD_LOCATION_CONVERTER:
+ return locationInfo.getMethodName();
+ case LINE_LOCATION_CONVERTER:
+ return locationInfo.getLineNumber();
+ case FILE_LOCATION_CONVERTER:
+ return locationInfo.getFileName();
+ default: return null;
+ }
+ }
+ }
+
+ private static abstract class NamedPatternConverter extends PatternConverter {
+ int precision;
+
+ NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo);
+ this.precision = precision;
+ }
+
+ abstract
+ String getFullyQualifiedName(PaxLoggingEvent event);
+
+ public
+ String convert(PaxLoggingEvent event) {
+ String n = getFullyQualifiedName(event);
+ if(precision <= 0)
+ return n;
+ else {
+ int len = n.length();
+
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = len -1 ;
+ for(int i = precision; i > 0; i--) {
+ end = n.lastIndexOf('.', end-1);
+ if(end == -1)
+ return n;
+ }
+ return n.substring(end+1, len);
+ }
+ }
+ }
+
+ private class ClassNamePatternConverter extends NamedPatternConverter {
+
+ ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(PaxLoggingEvent event) {
+ return event.getLocationInformation().getClassName();
+ }
+ }
+
+ private class CategoryPatternConverter extends NamedPatternConverter {
+
+ CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(PaxLoggingEvent event) {
+ return event.getLoggerName();
+ }
+ }
+}
+
diff --git a/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml b/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml
new file mode 100644
index 0000000..2055fe4
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/META-INF/spring/gshell-log.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:ctx="http://www.springframework.org/schema/context"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/context
+ http://www.springframework.org/schema/context/spring-context.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="log/display">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.log.DisplayLog">
+ <property name="events" ref="events" />
+ <property name="pattern" value="${pattern}" />
+ </gshell:action>
+ </gshell:command>
+ <gshell:link name="log/d" target="log/display" />
+ <gshell:command name="log/display-exception">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.log.DisplayException">
+ <property name="events" ref="events" />
+ </gshell:action>
+ </gshell:command>
+ <gshell:link name="log/de" target="log/display-exception" />
+ <gshell:command name="log/get">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.log.GetLogLevel" />
+ </gshell:command>
+ <gshell:command name="log/set">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.log.SetLogLevel" />
+ </gshell:command>
+
+ <gshell:alias name="ld" alias="log/d" />
+ <gshell:alias name="lde" alias="log/de" />
+ </gshell:command-bundle>
+
+ <bean id="vmLogAppender" class="org.apache.servicemix.kernel.gshell.log.VmLogAppender">
+ <property name="events" ref="events" />
+ </bean>
+
+ <bean id="events" class="org.apache.servicemix.kernel.gshell.log.LruList">
+ <constructor-arg value="${size}" />
+ </bean>
+
+ <osgi:service ref="vmLogAppender" interface="org.ops4j.pax.logging.spi.PaxAppender">
+ <osgi:service-properties>
+ <entry>
+ <key><util:constant static-field="org.ops4j.pax.logging.PaxLoggingService.APPENDER_NAME_PROPERTY"/></key>
+ <value>VmLogAppender</value>
+ </entry>
+ </osgi:service-properties>
+ </osgi:service>
+
+ <osgix:cm-properties id="cmProps" persistent-id="org.apache.servicemix.log">
+ <prop key="size">500</prop>
+ <prop key="pattern">%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n</prop>
+ </osgix:cm-properties>
+
+ <ctx:property-placeholder properties-ref="cmProps" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties
new file mode 100644
index 0000000..ff84091
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayException.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the last exception from the log.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties
new file mode 100644
index 0000000..d360b1a
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/DisplayLog.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display log entries.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties
new file mode 100644
index 0000000..94ee1d3
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/GetLogLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Show log level.
+
+command.manual=\
+ TODO: date manual
diff --git a/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties
new file mode 100644
index 0000000..511734b
--- /dev/null
+++ b/karaf/gshell/gshell-log/src/main/resources/org/apache/servicemix/kernel/gshell/log/SetLogLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Set log level.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/pom.xml b/karaf/gshell/gshell-obr/pom.xml
new file mode 100644
index 0000000..58cc9e8
--- /dev/null
+++ b/karaf/gshell/gshell-obr/pom.xml
@@ -0,0 +1,85 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.obr</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell OBR Commands</name>
+
+ <description>
+ Provides the OBR GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.bundlerepository</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.gshell.obr*;version=${project.version}</Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.java
new file mode 100644
index 0000000..d416674
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class AddUrlCommand extends ObrCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Repository URLs")
+ List<String> urls;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ for (String url : urls) {
+ admin.addRepository(new URL(url));
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.java
new file mode 100644
index 0000000..7bbd1dc
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/DeployCommand.java
@@ -0,0 +1,33 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class DeployCommand extends ObrCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "List of bundles")
+ protected List<String> bundles;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ doDeploy(admin, bundles, false);
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.java
new file mode 100644
index 0000000..344e592
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/FileUtil.java
@@ -0,0 +1,177 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+public class FileUtil
+{
+ public static void downloadSource(
+ PrintWriter out, PrintWriter err,
+ URL srcURL, String dirStr, boolean extract)
+ {
+ // Get the file name from the URL.
+ String fileName = (srcURL.getFile().lastIndexOf('/') > 0)
+ ? srcURL.getFile().substring(srcURL.getFile().lastIndexOf('/') + 1)
+ : srcURL.getFile();
+
+ try
+ {
+ out.println("Connecting...");
+
+ File dir = new File(dirStr);
+ if (!dir.exists())
+ {
+ err.println("Destination directory does not exist.");
+ }
+ File file = new File(dir, fileName);
+
+ OutputStream os = new FileOutputStream(file);
+ URLConnection conn = srcURL.openConnection();
+ int total = conn.getContentLength();
+ InputStream is = conn.getInputStream();
+
+ if (total > 0)
+ {
+ out.println("Downloading " + fileName
+ + " ( " + total + " bytes ).");
+ }
+ else
+ {
+ out.println("Downloading " + fileName + ".");
+ }
+ byte[] buffer = new byte[4096];
+ int count = 0;
+ for (int len = is.read(buffer); len > 0; len = is.read(buffer))
+ {
+ count += len;
+ os.write(buffer, 0, len);
+ }
+
+ os.close();
+ is.close();
+
+ if (extract)
+ {
+ is = new FileInputStream(file);
+ JarInputStream jis = new JarInputStream(is);
+ out.println("Extracting...");
+ unjar(jis, dir);
+ jis.close();
+ file.delete();
+ }
+ }
+ catch (Exception ex)
+ {
+ err.println(ex);
+ }
+ }
+
+ public static void unjar(JarInputStream jis, File dir)
+ throws IOException
+ {
+ // Reusable buffer.
+ byte[] buffer = new byte[4096];
+
+ // Loop through JAR entries.
+ for (JarEntry je = jis.getNextJarEntry();
+ je != null;
+ je = jis.getNextJarEntry())
+ {
+ if (je.getName().startsWith("/"))
+ {
+ throw new IOException("JAR resource cannot contain absolute paths.");
+ }
+
+ File target = new File(dir, je.getName());
+
+ // Check to see if the JAR entry is a directory.
+ if (je.isDirectory())
+ {
+ if (!target.exists())
+ {
+ if (!target.mkdirs())
+ {
+ throw new IOException("Unable to create target directory: "
+ + target);
+ }
+ }
+ // Just continue since directories do not have content to copy.
+ continue;
+ }
+
+ int lastIndex = je.getName().lastIndexOf('/');
+ String name = (lastIndex >= 0) ?
+ je.getName().substring(lastIndex + 1) : je.getName();
+ String destination = (lastIndex >= 0) ?
+ je.getName().substring(0, lastIndex) : "";
+
+ // JAR files use '/', so convert it to platform separator.
+ destination = destination.replace('/', File.separatorChar);
+ copy(jis, dir, name, destination, buffer);
+ }
+ }
+
+ public static void copy(
+ InputStream is, File dir, String destName, String destDir, byte[] buffer)
+ throws IOException
+ {
+ if (destDir == null)
+ {
+ destDir = "";
+ }
+
+ // Make sure the target directory exists and
+ // that is actually a directory.
+ File targetDir = new File(dir, destDir);
+ if (!targetDir.exists())
+ {
+ if (!targetDir.mkdirs())
+ {
+ throw new IOException("Unable to create target directory: "
+ + targetDir);
+ }
+ }
+ else if (!targetDir.isDirectory())
+ {
+ throw new IOException("Target is not a directory: "
+ + targetDir);
+ }
+
+ BufferedOutputStream bos = new BufferedOutputStream(
+ new FileOutputStream(new File(targetDir, destName)));
+ int count = 0;
+ while ((count = is.read(buffer)) > 0)
+ {
+ bos.write(buffer, 0, count);
+ }
+ bos.close();
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.java
new file mode 100644
index 0000000..50ede86
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/InfoCommand.java
@@ -0,0 +1,104 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Array;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.Capability;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Requirement;
+import org.osgi.service.obr.Resource;
+
+public class InfoCommand extends ObrCommandSupport {
+
+ @Argument(required = true, multiValued = true)
+ List<String> bundles;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ for (String bundle : bundles) {
+ String[] target = getTarget(bundle);
+ Resource[] resources = searchRepository(admin, target[0], target[1]);
+ if (resources == null)
+ {
+ io.err.println("Unknown bundle and/or version: "
+ + target[0]);
+ }
+ else
+ {
+ for (int resIdx = 0; resIdx < resources.length; resIdx++)
+ {
+ if (resIdx > 0)
+ {
+ io.out.println("");
+ }
+ printResource(io.out, resources[resIdx]);
+ }
+ }
+ }
+ }
+
+ private void printResource(PrintWriter out, Resource resource)
+ {
+ printUnderline(out, resource.getPresentationName().length());
+ out.println(resource.getPresentationName());
+ printUnderline(out, resource.getPresentationName().length());
+
+ Map map = resource.getProperties();
+ for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
+ {
+ Map.Entry entry = (Map.Entry) iter.next();
+ if (entry.getValue().getClass().isArray())
+ {
+ out.println(entry.getKey() + ":");
+ for (int j = 0; j < Array.getLength(entry.getValue()); j++)
+ {
+ out.println(" " + Array.get(entry.getValue(), j));
+ }
+ }
+ else
+ {
+ out.println(entry.getKey() + ": " + entry.getValue());
+ }
+ }
+
+ Requirement[] reqs = resource.getRequirements();
+ if ((reqs != null) && (reqs.length > 0))
+ {
+ out.println("Requires:");
+ for (int i = 0; i < reqs.length; i++)
+ {
+ out.println(" " + reqs[i].getFilter());
+ }
+ }
+
+ Capability[] caps = resource.getCapabilities();
+ if ((caps != null) && (caps.length > 0))
+ {
+ out.println("Capabilities:");
+ for (int i = 0; i < caps.length; i++)
+ {
+ out.println(" " + caps[i].getProperties());
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.java
new file mode 100644
index 0000000..37ff08f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListCommand.java
@@ -0,0 +1,85 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Version;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resource;
+
+public class ListCommand extends ObrCommandSupport {
+
+ @Argument(required = false, multiValued = true)
+ List<String> args;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ String substr = null;
+
+ if (args != null) {
+ for (String arg : args)
+ {
+ // Add a space in between tokens.
+ if (substr == null)
+ {
+ substr = "";
+ }
+ else
+ {
+ substr += " ";
+ }
+
+ substr += arg;
+ }
+ }
+
+ StringBuffer sb = new StringBuffer();
+ if ((substr == null) || (substr.length() == 0))
+ {
+ sb.append("(|(presentationname=*)(symbolicname=*))");
+ }
+ else
+ {
+ sb.append("(|(presentationname=*");
+ sb.append(substr);
+ sb.append("*)(symbolicname=*");
+ sb.append(substr);
+ sb.append("*))");
+ }
+ Resource[] resources = admin.discoverResources(sb.toString());
+ for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
+ {
+ String name = resources[resIdx].getPresentationName();
+ Version version = resources[resIdx].getVersion();
+ if (version != null)
+ {
+ io.out.println(name + " (" + version + ")");
+ }
+ else
+ {
+ io.out.println(name);
+ }
+ }
+
+ if (resources == null)
+ {
+ io.out.println("No matching bundles.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.java
new file mode 100644
index 0000000..ae8eaa2
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class ListUrlCommand extends ObrCommandSupport {
+
+ protected void doExecute(RepositoryAdmin admin) {
+ Repository[] repos = admin.listRepositories();
+ if ((repos != null) && (repos.length > 0)) {
+ for (int i = 0; i < repos.length; i++) {
+ io.out.println(repos[i].getURL());
+ }
+ } else {
+ io.out.println("No repository URLs are set.");
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java
new file mode 100644
index 0000000..dd6af1a
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/ObrCommandSupport.java
@@ -0,0 +1,221 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.Version;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Requirement;
+import org.osgi.service.obr.Resolver;
+import org.osgi.service.obr.Resource;
+
+public abstract class ObrCommandSupport extends OsgiCommandSupport {
+
+ protected static final char VERSION_DELIM = ',';
+
+ protected Object doExecute() throws Exception {
+ // Get repository admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(RepositoryAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("RepositoryAdmin service is unavailable.");
+ return null;
+ }
+ try {
+ RepositoryAdmin admin = (RepositoryAdmin) getBundleContext().getService(ref);
+ if (admin == null) {
+ io.out.println("RepositoryAdmin service is unavailable.");
+ return null;
+ }
+
+ doExecute(admin);
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return null;
+ }
+
+ protected abstract void doExecute(RepositoryAdmin admin) throws Exception;
+
+ protected Resource[] searchRepository(RepositoryAdmin admin, String targetId, String targetVersion)
+ {
+ // Try to see if the targetId is a bundle ID.
+ try
+ {
+ Bundle bundle = getBundleContext().getBundle(Long.parseLong(targetId));
+ targetId = bundle.getSymbolicName();
+ }
+ catch (NumberFormatException ex)
+ {
+ // It was not a number, so ignore.
+ }
+
+ // The targetId may be a bundle name or a bundle symbolic name,
+ // so create the appropriate LDAP query.
+ StringBuffer sb = new StringBuffer("(|(presentationname=");
+ sb.append(targetId);
+ sb.append(")(symbolicname=");
+ sb.append(targetId);
+ sb.append("))");
+ if (targetVersion != null)
+ {
+ sb.insert(0, "(&");
+ sb.append("(version=");
+ sb.append(targetVersion);
+ sb.append("))");
+ }
+ return admin.discoverResources(sb.toString());
+ }
+
+ public Resource selectNewestVersion(Resource[] resources)
+ {
+ int idx = -1;
+ Version v = null;
+ for (int i = 0; (resources != null) && (i < resources.length); i++)
+ {
+ if (i == 0)
+ {
+ idx = 0;
+ v = resources[i].getVersion();
+ }
+ else
+ {
+ Version vtmp = resources[i].getVersion();
+ if (vtmp.compareTo(v) > 0)
+ {
+ idx = i;
+ v = vtmp;
+ }
+ }
+ }
+ return (idx < 0) ? null : resources[idx];
+ }
+
+ protected String[] getTarget(String bundle) {
+ String[] target;
+ int idx = bundle.indexOf(VERSION_DELIM);
+ if (idx > 0) {
+ target = new String[] { bundle.substring(0, idx), bundle.substring(idx) };
+ }
+ else
+ {
+ target = new String[] { bundle, null };
+ }
+ return target;
+ }
+
+ protected void printUnderline(PrintWriter out, int length)
+ {
+ for (int i = 0; i < length; i++)
+ {
+ out.print('-');
+ }
+ out.println("");
+ }
+
+ protected void doDeploy(RepositoryAdmin admin, List<String> bundles, boolean start) throws Exception {
+ Resolver resolver = admin.resolver();
+ for (String bundle : bundles) {
+ String[] target = getTarget(bundle);
+ Resource resource = selectNewestVersion(searchRepository(admin, target[0], target[1]));
+ if (resource != null)
+ {
+ resolver.add(resource);
+ }
+ else
+ {
+ io.err.println("Unknown bundle - " + target[0]);
+ }
+ }
+ if ((resolver.getAddedResources() != null) &&
+ (resolver.getAddedResources().length > 0))
+ {
+ if (resolver.resolve())
+ {
+ io.out.println("Target resource(s):");
+ printUnderline(io.out, 19);
+ Resource[] resources = resolver.getAddedResources();
+ for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++)
+ {
+ io.out.println(" " + resources[resIdx].getPresentationName()
+ + " (" + resources[resIdx].getVersion() + ")");
+ }
+ resources = resolver.getRequiredResources();
+ if ((resources != null) && (resources.length > 0))
+ {
+ io.out.println("\nRequired resource(s):");
+ printUnderline(io.out, 21);
+ for (int resIdx = 0; resIdx < resources.length; resIdx++)
+ {
+ io.out.println(" " + resources[resIdx].getPresentationName()
+ + " (" + resources[resIdx].getVersion() + ")");
+ }
+ }
+ resources = resolver.getOptionalResources();
+ if ((resources != null) && (resources.length > 0))
+ {
+ io.out.println("\nOptional resource(s):");
+ printUnderline(io.out, 21);
+ for (int resIdx = 0; resIdx < resources.length; resIdx++)
+ {
+ io.out.println(" " + resources[resIdx].getPresentationName()
+ + " (" + resources[resIdx].getVersion() + ")");
+ }
+ }
+
+ try
+ {
+ io.out.print("\nDeploying...");
+ resolver.deploy(start);
+ io.out.println("done.");
+ }
+ catch (IllegalStateException ex)
+ {
+ io.err.println(ex);
+ }
+ }
+ else
+ {
+ Requirement[] reqs = resolver.getUnsatisfiedRequirements();
+ if ((reqs != null) && (reqs.length > 0))
+ {
+ io.out.println("Unsatisfied requirement(s):");
+ printUnderline(io.out, 27);
+ for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++)
+ {
+ io.out.println(" " + reqs[reqIdx].getFilter());
+ Resource[] resources = resolver.getResources(reqs[reqIdx]);
+ for (int resIdx = 0; resIdx < resources.length; resIdx++)
+ {
+ io.out.println(" " + resources[resIdx].getPresentationName());
+ }
+ }
+ }
+ else
+ {
+ io.out.println("Could not resolve targets.");
+ }
+ }
+ }
+
+ }
+}
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.java
new file mode 100644
index 0000000..056d094
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.java
@@ -0,0 +1,49 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.Repository;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class RefreshUrlCommand extends ObrCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "Repository URLs (leave empty for all)")
+ List<String> urls;
+
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ if (urls != null || urls.isEmpty()) {
+ for (String url : urls) {
+ admin.removeRepository(new URL(url));
+ admin.addRepository(new URL(url));
+ }
+ } else {
+ Repository[] repos = admin.listRepositories();
+ if ((repos != null) && (repos.length > 0)) {
+ for (int i = 0; i < repos.length; i++) {
+ admin.removeRepository(repos[i].getURL());
+ admin.addRepository(repos[i].getURL());
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.java
new file mode 100644
index 0000000..d122706
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.java
@@ -0,0 +1,35 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class RemoveUrlCommand extends ObrCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Repository URLs")
+ List<String> urls;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ for (String url : urls) {
+ admin.removeRepository(new URL(url));
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.java
new file mode 100644
index 0000000..f15db46
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/SourceCommand.java
@@ -0,0 +1,61 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import java.net.URL;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.service.obr.RepositoryAdmin;
+import org.osgi.service.obr.Resource;
+
+public class SourceCommand extends ObrCommandSupport {
+
+ @Option(name = "-x", description = "Extract")
+ boolean extract;
+
+ @Argument(required = true, index = 0, description = "Local directory")
+ String localDir;
+
+ @Argument(required = true, index = 1, multiValued = true, description = "List of bundles")
+ List<String> bundles;
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ for (String bundle : bundles) {
+ String[] target = getTarget(bundle);
+ Resource resource = selectNewestVersion(searchRepository(admin, target[0], target[1]));
+ if (resource == null)
+ {
+ io.err.println("Unknown bundle and/or version: " + target[0]);
+ }
+ else
+ {
+ URL srcURL = (URL) resource.getProperties().get(Resource.SOURCE_URL);
+ if (srcURL != null)
+ {
+ FileUtil.downloadSource(io.out, io.err, srcURL, localDir, extract);
+ }
+ else
+ {
+ io.err.println("Missing source URL: " + target[0]);
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java
new file mode 100644
index 0000000..1a34e26
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/java/org/apache/servicemix/kernel/gshell/obr/StartCommand.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.obr;
+
+import org.osgi.service.obr.RepositoryAdmin;
+
+public class StartCommand extends DeployCommand {
+
+ protected void doExecute(RepositoryAdmin admin) throws Exception {
+ doDeploy(admin, bundles, true);
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml b/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml
new file mode 100644
index 0000000..250051f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/META-INF/spring/gshell-obr.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:osgix="http://www.springframework.org/schema/osgi-compendium"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://www.springframework.org/schema/osgi-compendium
+ http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="obr/addUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.AddUrlCommand" />
+ </gshell:command>
+ <gshell:command name="obr/deploy">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.DeployCommand" />
+ </gshell:command>
+ <gshell:command name="obr/info">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.InfoCommand" />
+ </gshell:command>
+ <gshell:command name="obr/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.ListCommand" />
+ </gshell:command>
+ <gshell:command name="obr/listUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.ListUrlCommand" />
+ </gshell:command>
+ <gshell:command name="obr/removeUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.RemoveUrlCommand" />
+ </gshell:command>
+ <gshell:command name="obr/refreshUrl">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.RefreshUrlCommand" />
+ </gshell:command>
+ <gshell:command name="obr/source">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.SourceCommand" />
+ </gshell:command>
+ <gshell:command name="obr/start">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.obr.StartCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties
new file mode 100644
index 0000000..acf1131
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/AddUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Add a list of repository URLs to the OBR service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties
new file mode 100644
index 0000000..d518148
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/DeployCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Deploy a list of bundles using OBR
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties
new file mode 100644
index 0000000..23a2f59
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/InfoCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print informations about OBR bundles
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties
new file mode 100644
index 0000000..d73f3b7
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List OBR bundles
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties
new file mode 100644
index 0000000..40b9fc6
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/ListUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display the repository URLs currently associated with the OBR service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties
new file mode 100644
index 0000000..0a4553f
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RefreshUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Reload the repositories to obtain a fresh list of bundles.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties
new file mode 100644
index 0000000..259dfa1
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/RemoveUrlCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Remove a list of repository URLs from the OBR service.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties
new file mode 100644
index 0000000..76b6888
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/SourceCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Download the sources for an OBR bundle
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties
new file mode 100644
index 0000000..809ab4d
--- /dev/null
+++ b/karaf/gshell/gshell-obr/src/main/resources/org/apache/servicemix/kernel/gshell/obr/StartCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Deploy and start a list of bundles using OBR
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/pom.xml b/karaf/gshell/gshell-osgi/pom.xml
new file mode 100644
index 0000000..d232750
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/pom.xml
@@ -0,0 +1,87 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell OSGi Commands</name>
+
+ <description>
+ Provides the OSGi GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-extender</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.gshell.osgi*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java
new file mode 100644
index 0000000..5a3fd3f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleCommand.java
@@ -0,0 +1,79 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.startlevel.StartLevel;
+
+public abstract class BundleCommand extends OsgiCommandSupport {
+
+ @Argument(required = true, index = 0)
+ long id;
+
+ @Option(name = "--force")
+ boolean force;
+
+ protected Object doExecute() throws Exception {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle == null) {
+ io.out.println("Bundle " + id + " not found");
+ return Result.FAILURE;
+ }
+ if (!force) {
+ ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+ if (ref != null) {
+ StartLevel sl = getService(StartLevel.class, ref);
+ if (sl != null) {
+ int level = sl.getBundleStartLevel(bundle);
+ if (level < 50) {
+ for (;;) {
+ StringBuffer sb = new StringBuffer();
+ io.err.print("You are about to access a system bundle. Do you want to continue (yes/no): ");
+ io.err.flush();
+ for (;;) {
+ int c = io.in.read();
+ if (c < 0) {
+ return Result.FAILURE;
+ }
+ io.err.print((char) c);
+ if (c == '\r' || c == '\n') {
+ break;
+ }
+ sb.append((char) c);
+ }
+ String str = sb.toString();
+ if ("yes".equals(str)) {
+ break;
+ }
+ if ("no".equals(str)) {
+ return Result.FAILURE;
+ }
+ }
+ }
+ }
+ }
+ }
+ doExecute(bundle);
+ return Result.SUCCESS;
+ }
+
+ protected abstract void doExecute(Bundle bundle) throws Exception;
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java
new file mode 100644
index 0000000..cf64476
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.java
@@ -0,0 +1,74 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.startlevel.StartLevel;
+
+public class BundleLevel extends BundleCommand {
+
+ @Argument(required = false, index = 1)
+ Integer level;
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+ if (ref == null) {
+ io.out.println("StartLevel service is unavailable.");
+ return;
+ }
+ StartLevel sl = getService(StartLevel.class, ref);
+ if (sl == null) {
+ io.out.println("StartLevel service is unavailable.");
+ return;
+ }
+
+ if (level == null) {
+ io.out.println("Level " + sl.getBundleStartLevel(bundle));
+ }
+ else if ((level < 50) && sl.getBundleStartLevel(bundle) > 50){
+ for (;;) {
+ StringBuffer sb = new StringBuffer();
+ io.err.println("You are about to designate bundle as a system bundle. Do you want to continue (yes/no): ");
+ io.err.flush();
+ for (;;) {
+ int c = io.in.read();
+ if (c < 0) {
+ return;
+ }
+ io.err.println((char) c);
+ if (c == '\r' || c == '\n') {
+ break;
+ }
+ sb.append((char) c);
+ }
+ String str = sb.toString();
+ if ("yes".equals(str)) {
+ sl.setBundleStartLevel(bundle, level);
+ break;
+ } else if ("no".equals(str)) {
+ break;
+ }
+ }
+ } else {
+ sl.setBundleStartLevel(bundle, level);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java
new file mode 100644
index 0000000..7b937ee
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/BundlesCommand.java
@@ -0,0 +1,48 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+public abstract class BundlesCommand extends OsgiCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "Bundle IDs")
+ List<Long> ids;
+
+ protected Object doExecute() throws Exception {
+ List<Bundle> bundles = new ArrayList<Bundle>();
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle == null) {
+ io.err.println("Bundle ID" + id + " is invalid");
+ } else {
+ bundles.add(bundle);
+ }
+ }
+ }
+ doExecute(bundles);
+ return Result.SUCCESS;
+ }
+
+ protected abstract void doExecute(List<Bundle> bundles) throws Exception;
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java
new file mode 100644
index 0000000..0acf414
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Headers.java
@@ -0,0 +1,67 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+public class Headers extends OsgiCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "Bundles ids")
+ List<Long> ids;
+
+ protected Object doExecute() throws Exception {
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle != null) {
+ printHeaders(bundle);
+ }
+ else {
+ io.err.println("Bundle ID " + id + " is invalid.");
+ }
+ }
+ }
+ else {
+ Bundle[] bundles = getBundleContext().getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ printHeaders(bundles[i]);
+ }
+ }
+ return Result.SUCCESS;
+ }
+
+ protected void printHeaders(Bundle bundle) throws Exception {
+ String title = Util.getBundleName(bundle);
+ io.out.println("\n" + title);
+ io.out.println(Util.getUnderlineString(title));
+ Dictionary dict = bundle.getHeaders();
+ Enumeration keys = dict.keys();
+ while (keys.hasMoreElements())
+ {
+ Object k = (String) keys.nextElement();
+ Object v = dict.get(k);
+ io.out.println(k + " = " + Util.getValueString(v));
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.java
new file mode 100644
index 0000000..ad8d526
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.java
@@ -0,0 +1,80 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+
+public class InstallBundle extends OsgiCommandSupport {
+
+ @Argument(required = true, multiValued = true, description = "Bundle URLs")
+ List<String> urls;
+
+ @Option(name = "-s", aliases={"--start"}, description="Start the bundles after installation")
+ boolean start;
+
+ protected Object doExecute() throws Exception {
+ List<Bundle> bundles = new ArrayList<Bundle>();
+ StringBuffer sb = new StringBuffer();
+ for (String url : urls) {
+ Bundle bundle = install(url, io.out, io.err);
+ if (bundle != null) {
+ bundles.add(bundle);
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(bundle.getBundleId());
+ }
+ }
+ if (start) {
+ for (Bundle bundle : bundles) {
+ bundle.start();
+ }
+ }
+ if (sb.toString().indexOf(',') > 0) {
+ io.out.println("Bundle IDs: " + sb.toString());
+ } else if (sb.length() > 0) {
+ io.out.println("Bundle ID: " + sb.toString());
+ }
+ return Result.SUCCESS;
+ }
+
+ protected Bundle install(String location, PrintWriter out, PrintWriter err) {
+ try {
+ return getBundleContext().installBundle(location, null);
+ } catch (IllegalStateException ex) {
+ err.println(ex.toString());
+ } catch (BundleException ex) {
+ if (ex.getNestedException() != null) {
+ err.println(ex.getNestedException().toString());
+ } else {
+ err.println(ex.toString());
+ }
+ } catch (Exception ex) {
+ err.println(ex.toString());
+ }
+ return null;
+ }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java
new file mode 100644
index 0000000..00604f3
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListBundles.java
@@ -0,0 +1,203 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+import org.osgi.service.startlevel.StartLevel;
+
+public class ListBundles extends OsgiCommandSupport {
+
+ @Option(name = "-l", description = "Show locations")
+ boolean showLoc;
+
+ @Option(name = "-s", description = "Show symbolic name")
+ boolean showSymbolic;
+
+ @Option(name = "-u", description = "Show update")
+ boolean showUpdate;
+
+ private SpringApplicationListener springApplicationListener;
+
+ public SpringApplicationListener getSpringApplicationListener() {
+ return springApplicationListener;
+ }
+
+ public void setSpringApplicationListener(SpringApplicationListener springApplicationListener) {
+ this.springApplicationListener = springApplicationListener;
+ }
+
+ protected Object doExecute() throws Exception {
+ ServiceReference ref = getBundleContext().getServiceReference(StartLevel.class.getName());
+ StartLevel sl = null;
+ if (ref != null) {
+ sl = (StartLevel) getBundleContext().getService(ref);
+ }
+ if (sl == null) {
+ io.out.println("StartLevel service is unavailable.");
+ }
+
+ ServiceReference pkgref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+ PackageAdmin admin = null;
+ if (pkgref != null) {
+ admin = (PackageAdmin) getBundleContext().getService(pkgref);
+ if (admin == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ }
+ }
+
+ Bundle[] bundles = getBundleContext().getBundles();
+ if (bundles != null) {
+ // Display active start level.
+ if (sl != null) {
+ io.out.println("START LEVEL " + sl.getStartLevel());
+ }
+
+ // Print column headers.
+ String msg = " Name";
+ if (showLoc) {
+ msg = " Location";
+ }
+ else if (showSymbolic) {
+ msg = " Symbolic name";
+ }
+ else if (showUpdate) {
+ msg = " Update location";
+ }
+ String level = (sl == null) ? "" : " Level ";
+ io.out.println(" ID State Spring " + level + msg);
+ for (int i = 0; i < bundles.length; i++) {
+ // Get the bundle name or location.
+ String name = (String) bundles[i].getHeaders().get(Constants.BUNDLE_NAME);
+ // If there is no name, then default to symbolic name.
+ name = (name == null) ? bundles[i].getSymbolicName() : name;
+ // If there is no symbolic name, resort to location.
+ name = (name == null) ? bundles[i].getLocation() : name;
+
+ // Overwrite the default value is the user specifically
+ // requested to display one or the other.
+ if (showLoc) {
+ name = bundles[i].getLocation();
+ }
+ else if (showSymbolic) {
+ name = bundles[i].getSymbolicName();
+ name = (name == null) ? "<no symbolic name>" : name;
+ }
+ else if (showUpdate) {
+ name = (String) bundles[i].getHeaders().get(Constants.BUNDLE_UPDATELOCATION);
+ name = (name == null) ? bundles[i].getLocation() : name;
+ }
+ // Show bundle version if not showing location.
+ String version = (String) bundles[i].getHeaders().get(Constants.BUNDLE_VERSION);
+ name = (!showLoc && !showUpdate && (version != null)) ? name + " (" + version + ")" : name;
+ long l = bundles[i].getBundleId();
+ String id = String.valueOf(l);
+ if (sl == null) {
+ level = "1";
+ }
+ else {
+ level = String.valueOf(sl.getBundleStartLevel(bundles[i]));
+ }
+ while (level.length() < 5) {
+ level = " " + level;
+ }
+ while (id.length() < 4) {
+ id = " " + id;
+ }
+ io.out.println("[" + id + "] ["
+ + getStateString(bundles[i])
+ + "] [" + getSpringStateString(bundles[i])
+ + "] [" + level + "] " + name);
+
+ if (admin != null) {
+ Bundle[] fragments = admin.getFragments(bundles[i]);
+ Bundle[] hosts = admin.getHosts(bundles[i]);
+
+ if (fragments != null) {
+ io.out.print(" Fragments: ");
+ int ii = 0;
+ for (Bundle fragment : fragments) {
+ ii++;
+ io.out.print(fragment.getBundleId());
+ if ((fragments.length > 1) && ii < (fragments.length)) {
+ io.out.print(",");
+ }
+ }
+ io.out.println();
+ }
+
+ if (hosts != null) {
+ io.out.print(" Hosts: ");
+ int ii = 0;
+ for (Bundle host : hosts) {
+ ii++;
+ io.out.print(host.getBundleId());
+ if ((hosts.length > 1) && ii < (hosts.length)) {
+ io.out.print(",");
+ }
+ }
+ io.out.println();
+ }
+
+ }
+ }
+ }
+ else {
+ io.out.println("There are no installed bundles.");
+ }
+
+ getBundleContext().ungetService(ref);
+ getBundleContext().ungetService(pkgref);
+
+ return Result.SUCCESS;
+ }
+
+ public String getStateString(Bundle bundle)
+ {
+ int state = bundle.getState();
+ if (state == Bundle.ACTIVE) {
+ return "Active ";
+ } else if (state == Bundle.INSTALLED) {
+ return "Installed ";
+ } else if (state == Bundle.RESOLVED) {
+ return "Resolved ";
+ } else if (state == Bundle.STARTING) {
+ return "Starting ";
+ } else if (state == Bundle.STOPPING) {
+ return "Stopping ";
+ } else {
+ return "Unknown ";
+ }
+ }
+
+ public String getSpringStateString(Bundle bundle) {
+ SpringApplicationListener.SpringState state = springApplicationListener.getSpringState(bundle);
+ if (state == SpringApplicationListener.SpringState.Waiting) {
+ return "Waiting";
+ } else if (state == SpringApplicationListener.SpringState.Started) {
+ return "Started";
+ } else if (state == SpringApplicationListener.SpringState.Failed) {
+ return "Failed ";
+ } else {
+ return " ";
+ }
+ }
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java
new file mode 100644
index 0000000..3753e1a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ListServices.java
@@ -0,0 +1,174 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.command.Command;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+
+public class ListServices extends OsgiCommandSupport {
+
+ @Option(name = "-a", description = "Show all")
+ boolean showAll;
+
+ @Option(name = "-u", description = "Show in use")
+ boolean inUse;
+
+ @Argument(required = false, multiValued = true, description = "Bundles ids")
+ List<Long> ids;
+
+ protected Object doExecute() throws Exception {
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle != null) {
+ boolean headerPrinted = false;
+ boolean needSeparator = false;
+ ServiceReference[] refs = null;
+
+ // Get registered or in-use services.
+ if (inUse) {
+ refs = bundle.getServicesInUse();
+ } else {
+ refs = bundle.getRegisteredServices();
+ }
+
+ // Print properties for each service.
+ for (int refIdx = 0;
+ (refs != null) && (refIdx < refs.length);
+ refIdx++) {
+ String[] objectClass = (String[])
+ refs[refIdx].getProperty("objectClass");
+
+ // Determine if we need to print the service, depending
+ // on whether it is a command service or not.
+ boolean print = true;
+ for (int ocIdx = 0;
+ !showAll && (ocIdx < objectClass.length);
+ ocIdx++) {
+ if (objectClass[ocIdx].equals(Command.class.getName())) {
+ print = false;
+ }
+ }
+
+ // Print header if we have not already done so.
+ if (!headerPrinted) {
+ headerPrinted = true;
+ String title = Util.getBundleName(bundle);
+ title = (inUse)
+ ? title + " uses:"
+ : title + " provides:";
+ io.out.println("");
+ io.out.println(title);
+ io.out.println(Util.getUnderlineString(title));
+ }
+
+ if (showAll || print) {
+ // Print service separator if necessary.
+ if (needSeparator) {
+ io.out.println("----");
+ }
+
+ // Print service properties.
+ String[] keys = refs[refIdx].getPropertyKeys();
+ for (int keyIdx = 0;
+ (keys != null) && (keyIdx < keys.length);
+ keyIdx++) {
+ Object v = refs[refIdx].getProperty(keys[keyIdx]);
+ io.out.println(
+ keys[keyIdx] + " = " + Util.getValueString(v));
+ }
+
+ needSeparator = true;
+ }
+ }
+ } else {
+ io.err.println("Bundle ID " + id + " is invalid.");
+ }
+ }
+ }
+ else
+ {
+ Bundle[] bundles = getBundleContext().getBundles();
+ if (bundles != null)
+ {
+ // TODO: Sort list.
+ for (int bundleIdx = 0; bundleIdx < bundles.length; bundleIdx++)
+ {
+ boolean headerPrinted = false;
+ ServiceReference[] refs = null;
+
+ // Get registered or in-use services.
+ if (inUse)
+ {
+ refs = bundles[bundleIdx].getServicesInUse();
+ }
+ else
+ {
+ refs = bundles[bundleIdx].getRegisteredServices();
+ }
+
+ for (int refIdx = 0; (refs != null) && (refIdx < refs.length); refIdx++)
+ {
+ String[] objectClass = (String[])
+ refs[refIdx].getProperty("objectClass");
+
+ // Determine if we need to print the service, depending
+ // on whether it is a command service or not.
+ boolean print = true;
+ for (int ocIdx = 0;
+ !showAll && (ocIdx < objectClass.length);
+ ocIdx++)
+ {
+ if (objectClass[ocIdx].equals(Command.class.getName()))
+ {
+ print = false;
+ }
+ }
+
+ // Print the service if necessary.
+ if (showAll || print)
+ {
+ if (!headerPrinted)
+ {
+ headerPrinted = true;
+ String title = Util.getBundleName(bundles[bundleIdx]);
+ title = (inUse)
+ ? title + " uses:"
+ : title + " provides:";
+ io.out.println("\n" + title);
+ io.out.println(Util.getUnderlineString(title));
+ }
+ io.out.println(Util.getValueString(objectClass));
+ }
+ }
+ }
+ }
+ else
+ {
+ io.out.println("There are no registered services.");
+ }
+ }
+ return Result.SUCCESS;
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.java
new file mode 100644
index 0000000..e554b3c
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.java
@@ -0,0 +1,60 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class RefreshBundle extends OsgiCommandSupport {
+
+ @Argument(required = false)
+ Long id;
+
+ protected Object doExecute() throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return Result.FAILURE;
+ }
+ try {
+ PackageAdmin pa = (PackageAdmin) getBundleContext().getService(ref);
+ if (pa == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return Result.FAILURE;
+ }
+ if (id == null) {
+ pa.refreshPackages(null);
+ }
+ else {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle == null) {
+ io.out.println("Bundle " + id + " not found");
+ return Result.FAILURE;
+ }
+ pa.refreshPackages(new Bundle[] { bundle });
+ }
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return Result.SUCCESS;
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.java
new file mode 100644
index 0000000..607ef91
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.java
@@ -0,0 +1,45 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ResolveBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return;
+ }
+ try {
+ PackageAdmin pa = (PackageAdmin) getBundleContext().getService(ref);
+ if (pa == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return;
+ }
+ pa.resolveBundles(new Bundle[] { bundle });
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java
new file mode 100644
index 0000000..6793ecb
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.java
@@ -0,0 +1,28 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class RestartBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ bundle.stop();
+ bundle.start();
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.java
new file mode 100644
index 0000000..76e8081
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Shutdown.java
@@ -0,0 +1,41 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.Bundle;
+
+/**
+ * Command to shut down ServiceMix Kernel
+ */
+public class Shutdown extends OsgiCommandSupport {
+
+ protected Object doExecute() throws Exception {
+ new Thread() {
+ public void run() {
+ try {
+ Bundle bundle = getBundleContext().getBundle(0);
+ bundle.stop();
+ } catch (Exception e) {
+ log.error("Error when shutting down ServiceMix Kernel", e);
+ }
+ }
+ }.start();
+ return Result.SUCCESS;
+ }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java
new file mode 100644
index 0000000..240bd0a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/SpringApplicationListener.java
@@ -0,0 +1,102 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.springframework.beans.factory.DisposableBean;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.osgi.context.BundleContextAware;
+import org.springframework.osgi.context.event.OsgiBundleApplicationContextEvent;
+import org.springframework.osgi.context.event.OsgiBundleApplicationContextListener;
+import org.springframework.osgi.context.event.OsgiBundleContextFailedEvent;
+import org.springframework.osgi.context.event.OsgiBundleContextRefreshedEvent;
+import org.springframework.osgi.extender.event.BootstrappingDependencyEvent;
+import org.springframework.osgi.service.importer.event.OsgiServiceDependencyEvent;
+import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitStartingEvent;
+
+public class SpringApplicationListener implements OsgiBundleApplicationContextListener,
+ BundleListener, BundleContextAware,
+ InitializingBean, DisposableBean {
+
+ public static enum SpringState {
+ Unknown,
+ Waiting,
+ Started,
+ Failed,
+ }
+
+ private static final Log LOG = LogFactory.getLog(SpringApplicationListener.class);
+
+ private final Map<Long, SpringState> states;
+ private BundleContext bundleContext;
+
+ public SpringApplicationListener() {
+ this.states = new ConcurrentHashMap<Long, SpringState>();
+ }
+
+ public SpringState getSpringState(Bundle bundle) {
+ SpringState state = states.get(bundle.getBundleId());
+ if (state == null || bundle.getState() != Bundle.ACTIVE) {
+ state = SpringState.Unknown;
+ }
+ return state;
+ }
+
+ public void onOsgiApplicationEvent(OsgiBundleApplicationContextEvent event) {
+ SpringState state = null;
+ if (event instanceof BootstrappingDependencyEvent) {
+ OsgiServiceDependencyEvent de = ((BootstrappingDependencyEvent) event).getDependencyEvent();
+ if (de instanceof OsgiServiceDependencyWaitStartingEvent) {
+ state = SpringState.Waiting;
+ }
+ } else if (event instanceof OsgiBundleContextFailedEvent) {
+ state = SpringState.Failed;
+ } else if (event instanceof OsgiBundleContextRefreshedEvent) {
+ state = SpringState.Started;
+ }
+ if (state != null) {
+ LOG.debug("Spring app state changed to " + state + " for bundle " + event.getBundle().getBundleId());
+ states.put(event.getBundle().getBundleId(), state);
+ }
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ if (event.getType() == BundleEvent.UNINSTALLED) {
+ states.remove(event.getBundle().getBundleId());
+ }
+ }
+
+ public void setBundleContext(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ bundleContext.addBundleListener(this);
+ }
+
+ public void destroy() throws Exception {
+ bundleContext.removeBundleListener(this);
+ }
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java
new file mode 100644
index 0000000..6639802
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class StartBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ bundle.start();
+ }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.java
new file mode 100644
index 0000000..a04a688
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StartLevel.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+
+public class StartLevel extends OsgiCommandSupport {
+
+ @Argument(required = false, index = 0)
+ Integer level;
+
+ protected Object doExecute() throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(org.osgi.service.startlevel.StartLevel.class.getName());
+ if (ref == null) {
+ io.out.println("StartLevel service is unavailable.");
+ return null;
+ }
+ try {
+ org.osgi.service.startlevel.StartLevel sl = (org.osgi.service.startlevel.StartLevel) getBundleContext().getService(ref);
+ if (sl == null) {
+ io.out.println("StartLevel service is unavailable.");
+ return null;
+ }
+
+ if (level == null) {
+ io.out.println("Level " + sl.getStartLevel());
+ }
+ else {
+ sl.setStartLevel(level);
+ }
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return Result.SUCCESS;
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java
new file mode 100644
index 0000000..867a0a0
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/StopBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class StopBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ bundle.stop();
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java
new file mode 100644
index 0000000..f92cc9b
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.java
@@ -0,0 +1,27 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+
+public class UninstallBundle extends BundleCommand {
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ bundle.uninstall();
+ }
+
+}
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.java
new file mode 100644
index 0000000..1e02e6f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.java
@@ -0,0 +1,39 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+
+public class UpdateBundle extends BundleCommand {
+
+ @Argument(required = false, description = "Bundle location", index=1)
+ String location;
+
+ protected void doExecute(Bundle bundle) throws Exception {
+ if (location != null) {
+ InputStream is = new URL(location).openStream();
+ bundle.update(is);
+ } else {
+ bundle.update();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java
new file mode 100644
index 0000000..c8481d6
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/java/org/apache/servicemix/kernel/gshell/osgi/Util.java
@@ -0,0 +1,109 @@
+/*
+ * 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.servicemix.kernel.gshell.osgi;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+
+public class Util
+{
+ public static String getBundleName(Bundle bundle)
+ {
+ if (bundle != null)
+ {
+ String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+ return (name == null)
+ ? "Bundle " + Long.toString(bundle.getBundleId())
+ : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+ }
+ return "[STALE BUNDLE]";
+ }
+
+ private static StringBuffer m_sb = new StringBuffer();
+
+ public static String getUnderlineString(String s)
+ {
+ synchronized (m_sb)
+ {
+ m_sb.delete(0, m_sb.length());
+ for (int i = 0; i < s.length(); i++)
+ {
+ m_sb.append('-');
+ }
+ return m_sb.toString();
+ }
+ }
+
+ public static String getValueString(Object obj)
+ {
+ synchronized (m_sb)
+ {
+ if (obj instanceof String)
+ {
+ return (String) obj;
+ }
+ else if (obj instanceof String[])
+ {
+ String[] array = (String[]) obj;
+ m_sb.delete(0, m_sb.length());
+ for (int i = 0; i < array.length; i++)
+ {
+ if (i != 0)
+ {
+ m_sb.append(", ");
+ }
+ m_sb.append(array[i].toString());
+ }
+ return m_sb.toString();
+ }
+ else if (obj instanceof Boolean)
+ {
+ return ((Boolean) obj).toString();
+ }
+ else if (obj instanceof Long)
+ {
+ return ((Long) obj).toString();
+ }
+ else if (obj instanceof Integer)
+ {
+ return ((Integer) obj).toString();
+ }
+ else if (obj instanceof Short)
+ {
+ return ((Short) obj).toString();
+ }
+ else if (obj instanceof Double)
+ {
+ return ((Double) obj).toString();
+ }
+ else if (obj instanceof Float)
+ {
+ return ((Float) obj).toString();
+ }
+ else if (obj == null)
+ {
+ return "null";
+ }
+ else
+ {
+ return obj.toString();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml b/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml
new file mode 100644
index 0000000..7f06ebf
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/META-INF/spring/gshell-osgi.xml
@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="osgi/bundle-level">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.BundleLevel" />
+ </gshell:command>
+ <gshell:command name="osgi/headers">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.Headers" />
+ </gshell:command>
+ <gshell:command name="osgi/install">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.InstallBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/list">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ListBundles">
+ <property name="springApplicationListener" ref="springApplicationListener" />
+ </gshell:action>
+ </gshell:command>
+ <gshell:command name="osgi/ls">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ListServices" />
+ </gshell:command>
+ <gshell:command name="osgi/refresh">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.RefreshBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/update">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.UpdateBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/resolve">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.ResolveBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/restart">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.RestartBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/shutdown">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.Shutdown" />
+ </gshell:command>
+ <gshell:command name="osgi/start">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StartBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/start-level">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StartLevel" />
+ </gshell:command>
+ <gshell:command name="osgi/stop">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.StopBundle" />
+ </gshell:command>
+ <gshell:command name="osgi/uninstall">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.osgi.UninstallBundle" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+ <bean id="springApplicationListener" class="org.apache.servicemix.kernel.gshell.osgi.SpringApplicationListener" />
+
+ <osgi:service ref="springApplicationListener" interface="org.springframework.osgi.context.event.OsgiBundleApplicationContextListener" />
+
+</beans>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties
new file mode 100644
index 0000000..9a9bee9
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/BundleLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Get or set the start level of a given bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties
new file mode 100644
index 0000000..7d53e5f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Headers.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Display OSGi headers
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties
new file mode 100644
index 0000000..85858db
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/InstallBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties
new file mode 100644
index 0000000..204792a
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListBundles.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List bundles
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties
new file mode 100644
index 0000000..1f8ccf9
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ListServices.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=List services
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties
new file mode 100644
index 0000000..db60c15
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RefreshBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Refresh bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties
new file mode 100644
index 0000000..9f5de69
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/ResolveBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Resolve bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties
new file mode 100644
index 0000000..762aa40
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/RestartBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Restart bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties
new file mode 100644
index 0000000..940f5f6
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/Shutdown.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Shutdown OSGi
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties
new file mode 100644
index 0000000..7a6e7ae
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Start bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties
new file mode 100644
index 0000000..1b9761f
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StartLevel.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Get or set the start level
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties
new file mode 100644
index 0000000..40fc360
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/StopBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Stop bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties
new file mode 100644
index 0000000..36351ae
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UninstallBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Uninstall bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties
new file mode 100644
index 0000000..9b07581
--- /dev/null
+++ b/karaf/gshell/gshell-osgi/src/main/resources/org/apache/servicemix/kernel/gshell/osgi/UpdateBundle.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Update bundle
+
+command.manual=\
+ TODO: about manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/pom.xml b/karaf/gshell/gshell-packages/pom.xml
new file mode 100644
index 0000000..e127f98
--- /dev/null
+++ b/karaf/gshell/gshell-packages/pom.xml
@@ -0,0 +1,88 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.packages</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell PackageAdmin Commands</name>
+
+ <description>
+ Provides the PackageAdmin GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.servicemix.kernel.gshell.packages*;version=${project.version};-split-package:=merge-first
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.java
new file mode 100644
index 0000000..7bb15f1
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.java
@@ -0,0 +1,83 @@
+/*
+ * 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.servicemix.kernel.gshell.packages;
+
+import java.io.PrintWriter;
+import java.util.List;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.apache.geronimo.gshell.clp.Option;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ExportsCommand extends PackageCommandSupport {
+
+ @Option(name = "-i", aliases = { "--imports"}, description = "List bundles importing the packages")
+ boolean imports;
+
+ @Argument(required = false, multiValued = true, description = "bundle ids")
+ List<Long> ids;
+
+ protected void doExecute(PackageAdmin admin) throws Exception {
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle != null) {
+ printExports(io.out, bundle, admin.getExportedPackages(bundle));
+ } else {
+ io.err.println("Bundle ID " + id + " is invalid.");
+ }
+ }
+ }
+ else {
+ printExports(io.out, null, admin.getExportedPackages((Bundle) null));
+ }
+ }
+
+ protected void printExports(PrintWriter out, Bundle target, ExportedPackage[] exports) {
+ if ((exports != null) && (exports.length > 0)) {
+ for (int i = 0; i < exports.length; i++) {
+ Bundle bundle = exports[i].getExportingBundle();
+ out.print(getBundleName(bundle));
+ out.println(": " + exports[i]);
+ if (imports) {
+ Bundle[] bs = exports[i].getImportingBundles();
+ if (bs != null) {
+ for (Bundle b : bs) {
+ out.println("\t" + getBundleName(b));
+ }
+ }
+ }
+ }
+ } else {
+ out.println(getBundleName(target) + ": No active exported packages.");
+ }
+ }
+
+ public static String getBundleName(Bundle bundle) {
+ if (bundle != null) {
+ String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+ return (name == null)
+ ? "Bundle " + Long.toString(bundle.getBundleId())
+ : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+ }
+ return "[STALE BUNDLE]";
+ }
+
+}
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java
new file mode 100644
index 0000000..7d109cb
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.java
@@ -0,0 +1,93 @@
+/*
+ * 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.servicemix.kernel.gshell.packages;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.geronimo.gshell.clp.Argument;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.service.packageadmin.ExportedPackage;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+public class ImportsCommand extends PackageCommandSupport {
+
+ @Argument(required = false, multiValued = true, description = "bundle ids")
+ List<Long> ids;
+
+ protected void doExecute(PackageAdmin admin) throws Exception {
+ Map<Long, List<ExportedPackage>> packages = new HashMap<Long, List<ExportedPackage>>();
+ ExportedPackage[] exported = admin.getExportedPackages((Bundle) null);
+ for (ExportedPackage pkg : exported) {
+ Bundle[] bundles = pkg.getImportingBundles();
+ if (bundles != null) {
+ for (Bundle b : bundles) {
+ List<ExportedPackage> p = packages.get(b.getBundleId());
+ if (p == null) {
+ p = new ArrayList<ExportedPackage>();
+ packages.put(b.getBundleId(), p);
+ }
+ p.add(pkg);
+ }
+ }
+ }
+ if (ids != null && !ids.isEmpty()) {
+ for (long id : ids) {
+ Bundle bundle = getBundleContext().getBundle(id);
+ if (bundle != null) {
+ printImports(io.out, bundle, packages.get(bundle.getBundleId()));
+ } else {
+ io.err.println("Bundle ID " + id + " is invalid.");
+ }
+ }
+ }
+ else {
+ List<ExportedPackage> pkgs = new ArrayList<ExportedPackage>();
+ for (List<ExportedPackage> l : packages.values()) {
+ pkgs.addAll(l);
+ }
+ printImports(io.out, null, pkgs);
+ }
+ }
+
+ protected void printImports(PrintWriter out, Bundle target, List<ExportedPackage> imports) {
+ if ((imports != null) && (imports.size() > 0)) {
+ for (ExportedPackage p : imports) {
+ Bundle bundle = p.getExportingBundle();
+ out.print(getBundleName(bundle));
+ out.println(": " + p);
+ }
+ } else {
+ out.println(getBundleName(target) + ": No active imported packages.");
+ }
+ }
+
+ public static String getBundleName(Bundle bundle) {
+ if (bundle != null) {
+ String name = (String) bundle.getHeaders().get(Constants.BUNDLE_NAME);
+ return (name == null)
+ ? "Bundle " + Long.toString(bundle.getBundleId())
+ : name + " (" + Long.toString(bundle.getBundleId()) + ")";
+ }
+ return "[STALE BUNDLE]";
+ }
+
+}
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.java b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.java
new file mode 100644
index 0000000..ef99b94
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/java/org/apache/servicemix/kernel/gshell/packages/PackageCommandSupport.java
@@ -0,0 +1,55 @@
+/*
+ * 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.servicemix.kernel.gshell.packages;
+
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.packageadmin.PackageAdmin;
+
+/**
+ * Abstract class from which all commands related to the PackageAdmin
+ * service should derive.
+ * This command retrieves a reference to the PackageAdmin service before
+ * calling another method to actually process the command.
+ */
+public abstract class PackageCommandSupport extends OsgiCommandSupport {
+
+ protected Object doExecute() throws Exception {
+ // Get package admin service.
+ ServiceReference ref = getBundleContext().getServiceReference(PackageAdmin.class.getName());
+ if (ref == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return null;
+ }
+ try {
+ PackageAdmin admin = (PackageAdmin) getBundleContext().getService(ref);
+ if (admin == null) {
+ io.out.println("PackageAdmin service is unavailable.");
+ return null;
+ }
+
+ doExecute(admin);
+ }
+ finally {
+ getBundleContext().ungetService(ref);
+ }
+ return null;
+ }
+
+ protected abstract void doExecute(PackageAdmin admin) throws Exception;
+
+}
diff --git a/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml b/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml
new file mode 100644
index 0000000..9e3060d
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/META-INF/spring/gshell-packages.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="packages/exports">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.packages.ExportsCommand" />
+ </gshell:command>
+ <gshell:command name="packages/imports">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.packages.ImportsCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties
new file mode 100644
index 0000000..9487118
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ExportsCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print exported packages.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties
new file mode 100644
index 0000000..917d860
--- /dev/null
+++ b/karaf/gshell/gshell-packages/src/main/resources/org/apache/servicemix/kernel/gshell/packages/ImportsCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Print imported packages.
+
+command.manual=\
+ TODO: date manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/pom.xml b/karaf/gshell/gshell-wrapper/pom.xml
new file mode 100644
index 0000000..341e837
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/pom.xml
@@ -0,0 +1,115 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.wrapper</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell Service Wrapper</name>
+
+ <description>
+ Provides the Service Wrapper GShell integration
+ </description>
+
+ <properties>
+ <gshell.osgi.import>
+ org.apache.servicemix.kernel.main.spi.*;resolution:=optional,
+ org.apache.geronimo.gshell*,
+ </gshell.osgi.import>
+ <gshell.osgi.export>
+ </gshell.osgi.export>
+ <gshell.osgi.private>
+ org.apache.servicemix.kernel.gshell.wrapper.*,
+ </gshell.osgi.private>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ <resource>
+ <directory>${pom.basedir}/src/main/filtered-resources</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <configuration>
+ <mainClass>Main</mainClass>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.servicemix.kernel.gshell.wrapper.*;version=${project.version}
+ </Export-Package>
+ <Import-Package>
+ org.apache.geronimo.gshell.wisdom.command,
+ org.apache.geronimo.gshell.wisdom.registry,
+ org.apache.servicemix.kernel.gshell.core,
+ *
+ </Import-Package>
+ <Private-Package>!*</Private-Package>
+ <Spring-Context>*;publish-context:=false;create-asynchronously:=false</Spring-Context>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.java b/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.java
new file mode 100644
index 0000000..f58c3cc
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/java/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.java
@@ -0,0 +1,360 @@
+/*
+ * 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.servicemix.kernel.gshell.wrapper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+import org.apache.geronimo.gshell.clp.Option;
+import org.apache.geronimo.gshell.io.PumpStreamHandler;
+import org.apache.servicemix.kernel.gshell.core.OsgiCommandSupport;
+
+/**
+ * Installs this ServiceMix instance as a service in your operating systems.
+ *
+ * @version $Rev: 603634 $ $Date: 2007-12-12 16:07:16 +0100 (Wed, 12 Dec 2007) $
+ */
+public class InstallCommand extends OsgiCommandSupport
+{
+
+ @Option(name="-n", aliases={"--name"}, description="The service name that will be used when installing the service.")
+ private String name="servicemix";
+ @Option(name="-d", aliases={"--display"}, description="The display name of the service.")
+ private String displayName;
+ @Option(name="-D", aliases={"--description"}, description="The description of the service.")
+ private String description="";
+ @Option(name="-s", aliases={"--start-type"}, description="Mode in which the service is installed. AUTO_START or DEMAND_START")
+ private String startType="AUTO_START";
+
+ protected Object doExecute() throws Exception {
+
+ try {
+ String name = getName();
+ File base = new File(System.getProperty("servicemix.base"));
+ File bin = new File(base, "bin");
+ File etc = new File(base, "etc");
+ File lib = new File(base, "lib");
+
+ HashMap<String, String> props = new HashMap<String, String>();
+ props.put("${servicemix.home}", System.getProperty("servicemix.home"));
+ props.put("${servicemix.base}", base.getPath());
+ props.put("${name}", name);
+ props.put("${displayName}", getDisplayName());
+ props.put("${description}", getDescription());
+ props.put("${startType}", getStartType());
+
+ String os = System.getProperty("os.name", "Unknown");
+ File serviceFile=null;
+ if( os.startsWith("Win") ) {
+ mkdir(bin);
+ copyResourceTo(new File(bin, name+"-wrapper.exe"), "windows/servicemix-wrapper.exe", false);
+ serviceFile = new File(bin,name+"-service.bat");
+ copyFilteredResourceTo(serviceFile, "windows/servicemix-service.bat", props);
+ mkdir(lib);
+ copyResourceTo(new File(bin, "wrapper.dll"), "windows/wrapper.dll", false);
+ } else if( os.startsWith("Mac OS X") ) {
+ mkdir(bin);
+
+ File file = new File(bin, name+"-wrapper");
+ copyResourceTo(file, "macosx/servicemix-wrapper", false);
+ chmod(file, "a+x");
+
+ serviceFile = new File(bin,name+"-service");
+ copyFilteredResourceTo(serviceFile, "unix/servicemix-service", props);
+ chmod(serviceFile, "a+x");
+
+ mkdir(lib);
+ copyResourceTo(new File(lib, "libwrapper.jnilib"), "macosx/libwrapper.jnilib", false);
+
+ // TODO: figure out how to hook in the service that it starts up
+ // when the machine boots up.
+ } else if( os.startsWith("Linux") ) {
+ mkdir(bin);
+
+ File file = new File(bin, name+"-wrapper");
+ copyResourceTo(file, "linux/servicemix-wrapper", false);
+ chmod(file, "a+x");
+
+ serviceFile = new File(bin,name+"-service");
+ copyFilteredResourceTo(serviceFile, "unix/servicemix-service", props);
+ chmod(serviceFile, "a+x");
+
+ mkdir(lib);
+ copyResourceTo(new File(lib, "libwrapper.so"), "linux/libwrapper.so", false);
+
+ // TODO: figure out how to hook in the service that it starts up
+ // when the machine boots up.
+ } else {
+ io.out.println("Your operating system '"+os+"' is not currently supported.");
+ return 1;
+ }
+
+ // Install the wrapper jar to the lib directory..
+ mkdir(lib);
+ copyResourceTo(new File(lib, "servicemix-wrapper.jar"), "all/servicemix-wrapper.jar", false);
+ mkdir(etc);
+ File wrapperConf = new File(etc,name+"-wrapper.conf");
+ copyFilteredResourceTo(wrapperConf, "all/servicemix-wrapper.conf", props);
+
+ io.out.println("");
+ io.out.println("Setup complete. You may want to tweak the JVM properties in the wrapper configuration file: "+wrapperConf.getPath());
+ io.out.println("before installing and starting the service.");
+ io.out.println("");
+ if( os.startsWith("Win") ) {
+ io.out.println("");
+ io.out.println("To install the service, run: ");
+ io.out.println(" C:> "+serviceFile.getPath()+" install");
+ io.out.println("");
+ io.out.println("Once installed, to start the service run: ");
+ io.out.println(" C:> net start \""+name+"\"");
+ io.out.println("");
+ io.out.println("Once running, to stop the service run: ");
+ io.out.println(" C:> net stop \""+name+"\"");
+ io.out.println("");
+ io.out.println("Once stopped, to remove the installed the service run: ");
+ io.out.println(" C:> "+serviceFile.getPath()+" remove");
+ io.out.println("");
+ } else if( os.startsWith("Mac OS X") ) {
+ io.out.println("");
+ io.out.println("At this time it is not known how to get this service to start when the machine is rebooted.");
+ io.out.println("If you know how to install the following service script so that it gets started");
+ io.out.println("when OS X starts, please email dev@servicemix.apache.org and let us know how so");
+ io.out.println("we can update this message.");
+ io.out.println(" ");
+ io.out.println(" To start the service:");
+ io.out.println(" $ "+serviceFile.getPath()+" start");
+ io.out.println("");
+ io.out.println(" To stop the service:");
+ io.out.println(" $ "+serviceFile.getPath()+" stop");
+ io.out.println("");
+ } else if( os.startsWith("Linux") ) {
+ io.out.println("The way the service is installed depends upon your flavor of Linux.");
+
+ // TODO: figure out if we can detect the Linux flavor
+
+ io.out.println("");
+ io.out.println("@|cyan On Redhat/Fedora/CentOS Systems:|");
+ io.out.println(" To install the service:");
+ io.out.println(" $ ln -s "+serviceFile.getPath()+" /etc/init.d/");
+ io.out.println(" $ chkconfig "+serviceFile.getName()+" --add");
+ io.out.println("");
+ io.out.println(" To start the service when the machine is rebooted:");
+ io.out.println(" $ chkconfig "+serviceFile.getName()+" on");
+ io.out.println("");
+ io.out.println(" To disable starting the service when the machine is rebooted:");
+ io.out.println(" $ chkconfig "+serviceFile.getName()+" off");
+ io.out.println("");
+ io.out.println(" To start the service:");
+ io.out.println(" $ service "+serviceFile.getName()+" start");
+ io.out.println("");
+ io.out.println(" To stop the service:");
+ io.out.println(" $ service "+serviceFile.getName()+" stop");
+ io.out.println("");
+ io.out.println(" To uninstall the service :");
+ io.out.println(" $ chkconfig "+serviceFile.getName()+" --del");
+ io.out.println(" $ rm /etc/init.d/"+serviceFile.getName());
+
+ io.out.println("");
+ io.out.println("@|cyan On Ubuntu/Debian Systems:|");
+ io.out.println(" To install the service:");
+ io.out.println(" $ ln -s "+serviceFile.getPath()+" /etc/init.d/");
+ io.out.println("");
+ io.out.println(" To start the service when the machine is rebooted:");
+ io.out.println(" $ update-rc.d "+serviceFile.getName()+" defaults");
+ io.out.println("");
+ io.out.println(" To disable starting the service when the machine is rebooted:");
+ io.out.println(" $ update-rc.d -f "+serviceFile.getName()+" remove");
+ io.out.println("");
+ io.out.println(" To start the service:");
+ io.out.println(" $ /etc/init.d/"+serviceFile.getName()+" start");
+ io.out.println("");
+ io.out.println(" To stop the service:");
+ io.out.println(" $ /etc/init.d/"+serviceFile.getName()+" stop");
+ io.out.println("");
+ io.out.println(" To uninstall the service :");
+ io.out.println(" $ rm /etc/init.d/"+serviceFile.getName());
+
+ }
+
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+
+ return 0;
+ }
+
+ private int chmod(File serviceFile, String mode) throws Exception {
+ ProcessBuilder builder = new ProcessBuilder();
+ builder.command("chmod", mode, serviceFile.getCanonicalPath());
+ Process p = builder.start();
+
+ PumpStreamHandler handler = new PumpStreamHandler(io.inputStream, io.outputStream, io.errorStream);
+ handler.attach(p);
+ handler.start();
+ int status = p.waitFor();
+ handler.stop();
+ return status;
+ }
+
+ private void copyResourceTo(File outFile, String resource, boolean text) throws Exception {
+ if( !outFile.exists() ) {
+ io.out.println("Creating file: @|green "+outFile.getPath()+"|");
+ InputStream is = InstallCommand.class.getResourceAsStream(resource);
+ try {
+ if( text ) {
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ io.out.println("writing: "+line);
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } else {
+ // Binary so just write it out the way it came in.
+ FileOutputStream out = new FileOutputStream(outFile);
+ try {
+ int c=0;
+ while((c=is.read())>=0) {
+ out.write(c);
+ }
+ } finally {
+ safeClose(out);
+ }
+ }
+ } finally {
+ safeClose(is);
+ }
+ } else {
+ io.out.println("@|red File allready exists|. Move it out of the way if you want it re-created: "+outFile.getPath()+"");
+ }
+ }
+
+ private void copyFilteredResourceTo(File outFile, String resource, HashMap<String, String> props) throws Exception {
+ if( !outFile.exists() ) {
+ io.out.println("Creating file: @|green "+outFile.getPath()+"|");
+ InputStream is = InstallCommand.class.getResourceAsStream(resource);
+ try {
+ // Read it line at a time so that we can use the platform line ending when we write it out.
+ PrintStream out = new PrintStream(new FileOutputStream(outFile));
+ try {
+ Scanner scanner = new Scanner(is);
+ while (scanner.hasNextLine() ) {
+ String line = scanner.nextLine();
+ line = filter(line, props);
+ out.println(line);
+ }
+ } finally {
+ safeClose(out);
+ }
+ } finally {
+ safeClose(is);
+ }
+ } else {
+ io.out.println("@|red File allready exists|. Move it out of the way if you want it re-created: "+outFile.getPath()+"");
+ }
+ }
+
+ private void safeClose(InputStream is) throws IOException {
+ if( is==null)
+ return;
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private void safeClose(OutputStream is) throws IOException {
+ if( is==null)
+ return;
+ try {
+ is.close();
+ } catch (Throwable ignore) {
+ }
+ }
+
+ private String filter(String line, HashMap<String, String> props) {
+ for (Map.Entry<String, String> i : props.entrySet()) {
+ int p1 = line.indexOf(i.getKey());
+ if( p1 >= 0 ) {
+ String l1 = line.substring(0, p1);
+ String l2 = line.substring(p1+i.getKey().length());
+ line = l1+i.getValue()+l2;
+ }
+ }
+ return line;
+ }
+
+ private void mkdir(File file) {
+ if( !file.exists() ) {
+ io.out.println("Creating missing directory: @|green "+file.getPath()+"|");
+ file.mkdirs();
+ }
+ }
+
+ public String getName() {
+ if( name == null ) {
+ File base = new File(System.getProperty("servicemix.base"));
+ name = base.getName();
+ }
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getDisplayName() {
+ if( displayName == null ) {
+ displayName = getName();
+ }
+ return displayName;
+ }
+
+ public void setDisplayName(String displayName) {
+ this.displayName = displayName;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getStartType() {
+ return startType;
+ }
+
+ public void setStartType(String startType) {
+ this.startType = startType;
+ }
+}
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml b/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml
new file mode 100644
index 0000000..3eb5a60
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/META-INF/spring/gshell-wrapper.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+
+-->
+<beans xmlns="http://www.springframework.org/schema/beans"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns:osgi="http://www.springframework.org/schema/osgi"
+ xmlns:util="http://www.springframework.org/schema/util"
+ xmlns:gshell="http://servicemix.apache.org/schema/servicemix-gshell"
+ xsi:schemaLocation="
+ http://www.springframework.org/schema/beans
+ http://www.springframework.org/schema/beans/spring-beans.xsd
+ http://www.springframework.org/schema/util
+ http://www.springframework.org/schema/util/spring-util.xsd
+ http://www.springframework.org/schema/osgi
+ http://www.springframework.org/schema/osgi/spring-osgi.xsd
+ http://servicemix.apache.org/schema/servicemix-gshell
+ http://servicemix.apache.org/schema/servicemix-gshell/servicemix-gshell.xsd">
+
+ <import resource="classpath:org/apache/servicemix/kernel/gshell/core/commands.xml" />
+
+ <gshell:command-bundle>
+ <gshell:command name="wrapper/install">
+ <gshell:action class="org.apache.servicemix.kernel.gshell.wrapper.InstallCommand" />
+ </gshell:command>
+ </gshell:command-bundle>
+
+</beans>
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties
new file mode 100644
index 0000000..e5a5cb8
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/InstallCommand.properties
@@ -0,0 +1,27 @@
+##
+## 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.
+##
+
+##
+## $Rev: 703511 $ $Date: 2008-10-10 18:07:36 +0200 (Fri, 10 Oct 2008) $
+##
+
+command.description=Install ServiceMix Kernel as a system service in the OS.
+
+command.manual=\
+ TODO: install manual
\ No newline at end of file
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf
new file mode 100644
index 0000000..021872d
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.conf
@@ -0,0 +1,124 @@
+# ------------------------------------------------------------------------
+# 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.
+# ------------------------------------------------------------------------
+
+#********************************************************************
+# Wrapper Properties
+#********************************************************************
+set.default.SERVICEMIX_HOME=${servicemix.home}
+set.default.SERVICEMIX_BASE=${servicemix.base}
+
+# Java Application
+wrapper.working.dir=%SERVICEMIX_BASE%
+wrapper.java.command=java
+wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp
+wrapper.java.classpath.1=%SERVICEMIX_BASE%/lib/servicemix-wrapper.jar
+wrapper.java.classpath.2=%SERVICEMIX_HOME%/lib/servicemix.jar
+wrapper.java.classpath.3=%SERVICEMIX_HOME%/lib/servicemix-jaas-boot.jar
+wrapper.java.library.path.1=%SERVICEMIX_BASE%/lib/
+
+# Application Parameters. Add parameters as needed starting from 1
+wrapper.app.parameter.1=org.apache.servicemix.kernel.main.Main
+
+# JVM Parameters
+# note that n is the parameter number starting from 1.
+wrapper.java.additional.1=-Dservicemix.home=%SERVICEMIX_HOME%
+wrapper.java.additional.2=-Dservicemix.base=%SERVICEMIX_BASE%
+wrapper.java.additional.3=-Dcom.sun.management.jmxremote
+wrapper.java.additional.4=-Dservicemix.startLocalConsole=false
+wrapper.java.additional.5=-Dservicemix.startRemoteShell=true
+
+# Uncomment to enable jmx
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.port=1616
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.authenticate=false
+#wrapper.java.additional.n=-Dcom.sun.management.jmxremote.ssl=false
+
+# Uncomment to enable YourKit profiling
+#wrapper.java.additional.n=-Xrunyjpagent
+
+# Uncomment to enable remote debugging
+#wrapper.java.additional.n=-Xdebug -Xnoagent -Djava.compiler=NONE
+#wrapper.java.additional.n=-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005
+
+# Initial Java Heap Size (in MB)
+#wrapper.java.initmemory=3
+
+# Maximum Java Heap Size (in MB)
+wrapper.java.maxmemory=512
+
+
+#********************************************************************
+# Wrapper Logging Properties
+#********************************************************************
+# Format of output for the console. (See docs for formats)
+wrapper.console.format=PM
+
+# Log Level for console output. (See docs for log levels)
+wrapper.console.loglevel=INFO
+
+# Log file to use for wrapper output logging.
+wrapper.logfile=%SERVICEMIX_BASE%/data/log/wrapper.log
+
+# Format of output for the log file. (See docs for formats)
+wrapper.logfile.format=LPTM
+
+# Log Level for log file output. (See docs for log levels)
+wrapper.logfile.loglevel=INFO
+
+# Maximum size that the log file will be allowed to grow to before
+# the log is rolled. Size is specified in bytes. The default value
+# of 0, disables log rolling. May abbreviate with the 'k' (kb) or
+# 'm' (mb) suffix. For example: 10m = 10 megabytes.
+wrapper.logfile.maxsize=10m
+
+# Maximum number of rolled log files which will be allowed before old
+# files are deleted. The default value of 0 implies no limit.
+wrapper.logfile.maxfiles=5
+
+# Log Level for sys/event log output. (See docs for log levels)
+wrapper.syslog.loglevel=NONE
+
+#********************************************************************
+# Wrapper Windows Properties
+#********************************************************************
+# Title to use when running as a console
+wrapper.console.title=${name}
+
+#********************************************************************
+# Wrapper Windows NT/2000/XP Service Properties
+#********************************************************************
+# WARNING - Do not modify any of these properties when an application
+# using this configuration file has been installed as a service.
+# Please uninstall the service before modifying this section. The
+# service can then be reinstalled.
+
+# Name of the service
+wrapper.ntservice.name=${name}
+
+# Display name of the service
+wrapper.ntservice.displayname=${displayName}
+
+# Description of the service
+wrapper.ntservice.description=${description}
+
+# Service dependencies. Add dependencies as needed starting from 1
+wrapper.ntservice.dependency.1=
+
+# Mode in which the service is installed. AUTO_START or DEMAND_START
+wrapper.ntservice.starttype=${startType}
+
+# Allow the service to interact with the desktop.
+wrapper.ntservice.interactive=false
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar
new file mode 100755
index 0000000..c766405
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/all/servicemix-wrapper.jar
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so
new file mode 100644
index 0000000..df25ec6
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/libwrapper.so
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper
new file mode 100644
index 0000000..c58d4f7
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/linux/servicemix-wrapper
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib
new file mode 100644
index 0000000..ae5a14e
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/libwrapper.jnilib
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper
new file mode 100644
index 0000000..8c281b9
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/macosx/servicemix-wrapper
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service
new file mode 100755
index 0000000..d80c13b
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/unix/servicemix-service
@@ -0,0 +1,543 @@
+#! /bin/sh
+
+# ------------------------------------------------------------------------
+# 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.
+# ------------------------------------------------------------------------
+
+# Application
+APP_NAME="${name}"
+APP_LONG_NAME="${displayName}"
+
+# Wrapper
+WRAPPER_CMD="${servicemix.base}/bin/${APP_NAME}-wrapper"
+WRAPPER_CONF="${servicemix.base}/etc/${APP_NAME}-wrapper.conf"
+
+# Priority at which to run the wrapper. See "man nice" for valid priorities.
+# nice is only used if a priority is specified.
+PRIORITY=
+
+# Location of the pid file.
+PIDDIR="${servicemix.base}/data"
+
+# If uncommented, causes the Wrapper to be shutdown using an anchor file.
+# When launched with the 'start' command, it will also ignore all INT and
+# TERM signals.
+#IGNORE_SIGNALS=true
+
+# If specified, the Wrapper will be run as the specified user.
+# IMPORTANT - Make sure that the user has the required privileges to write
+# the PID file and wrapper.log files. Failure to be able to write the log
+# file will cause the Wrapper to exit without any way to write out an error
+# message.
+# NOTE - This will set the user which is used to run the Wrapper as well as
+# the JVM and is not useful in situations where a privileged resource or
+# port needs to be allocated prior to the user being changed.
+#RUN_AS_USER=
+
+# The following two lines are used by the chkconfig command. Change as is
+# appropriate for your application. They should remain commented.
+# chkconfig: 2345 20 80
+# description: ${displayName}
+
+# Do not modify anything beyond this point
+#-----------------------------------------------------------------------------
+
+# Get the fully qualified path to the script
+case $0 in
+ /*)
+ SCRIPT="$0"
+ ;;
+ *)
+ PWD=`pwd`
+ SCRIPT="$PWD/$0"
+ ;;
+esac
+
+# Resolve the true real path without any sym links.
+CHANGED=true
+while [ "X$CHANGED" != "X" ]
+do
+ # Change spaces to ":" so the tokens can be parsed.
+ SCRIPT=`echo $SCRIPT | sed -e 's; ;:;g'`
+ # Get the real path to this script, resolving any symbolic links
+ TOKENS=`echo $SCRIPT | sed -e 's;/; ;g'`
+ REALPATH=
+ for C in $TOKENS; do
+ REALPATH="$REALPATH/$C"
+ while [ -h "$REALPATH" ] ; do
+ LS="`ls -ld "$REALPATH"`"
+ LINK="`expr "$LS" : '.*-> \(.*\)$'`"
+ if expr "$LINK" : '/.*' > /dev/null; then
+ REALPATH="$LINK"
+ else
+ REALPATH="`dirname "$REALPATH"`""/$LINK"
+ fi
+ done
+ done
+ # Change ":" chars back to spaces.
+ REALPATH=`echo $REALPATH | sed -e 's;:; ;g'`
+
+ if [ "$REALPATH" = "$SCRIPT" ]
+ then
+ CHANGED=""
+ else
+ SCRIPT="$REALPATH"
+ fi
+done
+
+# Change the current directory to the location of the script
+cd "`dirname "$REALPATH"`"
+REALDIR=`pwd`
+
+# If the PIDDIR is relative, set its value relative to the full REALPATH to avoid problems if
+# the working directory is later changed.
+FIRST_CHAR=`echo $PIDDIR | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+ PIDDIR=$REALDIR/$PIDDIR
+fi
+# Same test for WRAPPER_CMD
+FIRST_CHAR=`echo $WRAPPER_CMD | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+ WRAPPER_CMD=$REALDIR/$WRAPPER_CMD
+fi
+# Same test for WRAPPER_CONF
+FIRST_CHAR=`echo $WRAPPER_CONF | cut -c1,1`
+if [ "$FIRST_CHAR" != "/" ]
+then
+ WRAPPER_CONF=$REALDIR/$WRAPPER_CONF
+fi
+
+# Process ID
+ANCHORFILE="$PIDDIR/$APP_NAME.anchor"
+PIDFILE="$PIDDIR/$APP_NAME.pid"
+LOCKDIR="/var/lock/subsys"
+LOCKFILE="$LOCKDIR/$APP_NAME"
+pid=""
+
+# Resolve the location of the 'ps' command
+PSEXE="/usr/bin/ps"
+if [ ! -x $PSEXE ]
+then
+ PSEXE="/bin/ps"
+ if [ ! -x $PSEXE ]
+ then
+ echo "Unable to locate 'ps'."
+ echo "Please report this message along with the location of the command on your system."
+ exit 1
+ fi
+fi
+
+# Resolve the os
+DIST_OS=`uname -s | tr [:upper:] [:lower:] | tr -d [:blank:]`
+case "$DIST_OS" in
+ 'sunos')
+ DIST_OS="solaris"
+ ;;
+ 'hp-ux' | 'hp-ux64')
+ DIST_OS="hpux"
+ ;;
+ 'darwin')
+ DIST_OS="macosx"
+ ;;
+ 'unix_sv')
+ DIST_OS="unixware"
+ ;;
+esac
+
+# Resolve the architecture
+DIST_ARCH=`uname -p | tr [:upper:] [:lower:] | tr -d [:blank:]`
+if [ "$DIST_ARCH" = "unknown" ]
+then
+ DIST_ARCH=`uname -m | tr [:upper:] [:lower:] | tr -d [:blank:]`
+fi
+case "$DIST_ARCH" in
+ 'amd64' | 'ia32' | 'ia64' | 'i386' | 'i486' | 'i586' | 'i686' | 'x86_64')
+ DIST_ARCH="x86"
+ ;;
+ 'ip27')
+ DIST_ARCH="mips"
+ ;;
+ 'power' | 'powerpc' | 'power_pc' | 'ppc64')
+ DIST_ARCH="ppc"
+ ;;
+ 'pa_risc' | 'pa-risc')
+ DIST_ARCH="parisc"
+ ;;
+ 'sun4u' | 'sparcv9')
+ DIST_ARCH="sparc"
+ ;;
+ '9000/800')
+ DIST_ARCH="parisc"
+ ;;
+esac
+
+# Decide on the wrapper binary to use.
+# If a 32-bit wrapper binary exists then it will work on 32 or 64 bit
+# platforms, if the 64-bit binary exists then the distribution most
+# likely wants to use long names. Otherwise, look for the default.
+# For macosx, we also want to look for universal binaries.
+WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+if [ -x $WRAPPER_TEST_CMD ]
+then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+else
+ if [ "$DIST_OS" = "macosx" ]
+ then
+ WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-universal-32"
+ if [ -x $WRAPPER_TEST_CMD ]
+ then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+ else
+ WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+ if [ -x $WRAPPER_TEST_CMD ]
+ then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+ else
+ WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-universal-64"
+ if [ -x $WRAPPER_TEST_CMD ]
+ then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+ else
+ if [ ! -x $WRAPPER_CMD ]
+ then
+ echo "Unable to locate any of the following binaries:"
+ echo " $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+ echo " $WRAPPER_CMD-$DIST_OS-universal-32"
+ echo " $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+ echo " $WRAPPER_CMD-$DIST_OS-universal-64"
+ echo " $WRAPPER_CMD"
+ exit 1
+ fi
+ fi
+ fi
+ fi
+ else
+ WRAPPER_TEST_CMD="$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+ if [ -x $WRAPPER_TEST_CMD ]
+ then
+ WRAPPER_CMD="$WRAPPER_TEST_CMD"
+ else
+ if [ ! -x $WRAPPER_CMD ]
+ then
+ echo "Unable to locate any of the following binaries:"
+ echo " $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32"
+ echo " $WRAPPER_CMD-$DIST_OS-$DIST_ARCH-64"
+ echo " $WRAPPER_CMD"
+ exit 1
+ fi
+ fi
+ fi
+fi
+
+# Build the nice clause
+if [ "X$PRIORITY" = "X" ]
+then
+ CMDNICE=""
+else
+ CMDNICE="nice -$PRIORITY"
+fi
+
+# Build the anchor file clause.
+if [ "X$IGNORE_SIGNALS" = "X" ]
+then
+ ANCHORPROP=
+ IGNOREPROP=
+else
+ ANCHORPROP=wrapper.anchorfile=$ANCHORFILE
+ IGNOREPROP=wrapper.ignore_signals=TRUE
+fi
+
+# Build the lock file clause. Only create a lock file if the lock directory exists on this platform.
+if [ -d $LOCKDIR ]
+then
+ LOCKPROP=wrapper.lockfile=$LOCKFILE
+else
+ LOCKPROP=
+fi
+
+checkUser() {
+ # Check the configured user. If necessary rerun this script as the desired user.
+ if [ "X$RUN_AS_USER" != "X" ]
+ then
+ # Resolve the location of the 'id' command
+ IDEXE="/usr/xpg4/bin/id"
+ if [ ! -x $IDEXE ]
+ then
+ IDEXE="/usr/bin/id"
+ if [ ! -x $IDEXE ]
+ then
+ echo "Unable to locate 'id'."
+ echo "Please report this message along with the location of the command on your system."
+ exit 1
+ fi
+ fi
+
+ if [ "`$IDEXE -u -n`" = "$RUN_AS_USER" ]
+ then
+ # Already running as the configured user. Avoid password prompts by not calling su.
+ RUN_AS_USER=""
+ fi
+ fi
+ if [ "X$RUN_AS_USER" != "X" ]
+ then
+ # If LOCKPROP and $RUN_AS_USER are defined then the new user will most likely not be
+ # able to create the lock file. The Wrapper will be able to update this file once it
+ # is created but will not be able to delete it on shutdown. If $2 is defined then
+ # the lock file should be created for the current command
+ if [ "X$LOCKPROP" != "X" ]
+ then
+ if [ "X$2" != "X" ]
+ then
+ # Resolve the primary group
+ RUN_AS_GROUP=`groups $RUN_AS_USER | awk '{print $3}' | tail -1`
+ if [ "X$RUN_AS_GROUP" = "X" ]
+ then
+ RUN_AS_GROUP=RUN_AS_USER
+ fi
+ touch $LOCKFILE
+ chown $RUN_AS_USER:$RUN_AS_GROUP $LOCKFILE
+ fi
+ fi
+
+ # Still want to change users, recurse. This means that the user will only be
+ # prompted for a password once.
+ su -m $RUN_AS_USER -c "$REALPATH $1"
+
+ # Now that we are the original user again, we may need to clean up the lock file.
+ if [ "X$LOCKPROP" != "X" ]
+ then
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ # Wrapper is not running so make sure the lock file is deleted.
+ if [ -f $LOCKFILE ]
+ then
+ rm $LOCKFILE
+ fi
+ fi
+ fi
+
+ exit 0
+ fi
+}
+
+getpid() {
+ if [ -f $PIDFILE ]
+ then
+ if [ -r $PIDFILE ]
+ then
+ pid=`cat $PIDFILE`
+ if [ "X$pid" != "X" ]
+ then
+ # It is possible that 'a' process with the pid exists but that it is not the
+ # correct process. This can happen in a number of cases, but the most
+ # common is during system startup after an unclean shutdown.
+ # The ps statement below looks for the specific wrapper command running as
+ # the pid. If it is not found then the pid file is considered to be stale.
+ pidtest=`$PSEXE -p $pid -o command | grep $WRAPPER_CMD | tail -1`
+ if [ "X$pidtest" = "X" ]
+ then
+ # This is a stale pid file.
+ rm -f $PIDFILE
+ echo "Removed stale pid file: $PIDFILE"
+ pid=""
+ fi
+ fi
+ else
+ echo "Cannot read $PIDFILE."
+ exit 1
+ fi
+ fi
+}
+
+testpid() {
+ pid=`$PSEXE -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1`
+ if [ "X$pid" = "X" ]
+ then
+ # Process is gone so remove the pid file.
+ rm -f $PIDFILE
+ pid=""
+ fi
+}
+
+console() {
+ echo "Running $APP_LONG_NAME..."
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ COMMAND_LINE="$CMDNICE $WRAPPER_CMD $WRAPPER_CONF wrapper.syslog.ident=$APP_NAME wrapper.pidfile=$PIDFILE $ANCHORPROP $LOCKPROP"
+ exec $COMMAND_LINE
+ else
+ echo "$APP_LONG_NAME is already running."
+ exit 1
+ fi
+}
+
+start() {
+ echo "Starting $APP_LONG_NAME..."
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ if [ ! -d ../../data ]; then
+ mkdir ../../data
+ fi
+ if [ ! -d ../../data/log ]; then
+ mkdir ../../data/log
+ fi
+ COMMAND_LINE="$CMDNICE $WRAPPER_CMD $WRAPPER_CONF wrapper.syslog.ident=$APP_NAME wrapper.pidfile=$PIDFILE wrapper.daemonize=TRUE $ANCHORPROP $IGNOREPROP $LOCKPROP"
+ exec $COMMAND_LINE
+ else
+ echo "$APP_LONG_NAME is already running."
+ exit 1
+ fi
+}
+
+stopit() {
+ echo "Stopping $APP_LONG_NAME..."
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ echo "$APP_LONG_NAME was not running."
+ else
+ if [ "X$IGNORE_SIGNALS" = "X" ]
+ then
+ # Running so try to stop it.
+ kill $pid
+ if [ $? -ne 0 ]
+ then
+ # An explanation for the failure should have been given
+ echo "Unable to stop $APP_LONG_NAME."
+ exit 1
+ fi
+ else
+ rm -f $ANCHORFILE
+ if [ -f $ANCHORFILE ]
+ then
+ # An explanation for the failure should have been given
+ echo "Unable to stop $APP_LONG_NAME."
+ exit 1
+ fi
+ fi
+
+ # We can not predict how long it will take for the wrapper to
+ # actually stop as it depends on settings in wrapper.conf.
+ # Loop until it does.
+ savepid=$pid
+ CNT=0
+ TOTCNT=0
+ while [ "X$pid" != "X" ]
+ do
+ # Show a waiting message every 5 seconds.
+ if [ "$CNT" -lt "5" ]
+ then
+ CNT=`expr $CNT + 1`
+ else
+ echo "Waiting for $APP_LONG_NAME to exit..."
+ CNT=0
+ fi
+ TOTCNT=`expr $TOTCNT + 1`
+
+ sleep 1
+
+ testpid
+ done
+
+ pid=$savepid
+ testpid
+ if [ "X$pid" != "X" ]
+ then
+ echo "Failed to stop $APP_LONG_NAME."
+ exit 1
+ else
+ echo "Stopped $APP_LONG_NAME."
+ fi
+ fi
+}
+
+status() {
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ echo "$APP_LONG_NAME is not running."
+ exit 1
+ else
+ echo "$APP_LONG_NAME is running ($pid)."
+ exit 0
+ fi
+}
+
+dump() {
+ echo "Dumping $APP_LONG_NAME..."
+ getpid
+ if [ "X$pid" = "X" ]
+ then
+ echo "$APP_LONG_NAME was not running."
+
+ else
+ kill -3 $pid
+
+ if [ $? -ne 0 ]
+ then
+ echo "Failed to dump $APP_LONG_NAME."
+ exit 1
+ else
+ echo "Dumped $APP_LONG_NAME."
+ fi
+ fi
+}
+
+case "$1" in
+
+ 'console')
+ checkUser $1 touchlock
+ console
+ ;;
+
+ 'start')
+ checkUser $1 touchlock
+ start
+ ;;
+
+ 'stop')
+ checkUser $1
+ stopit
+ ;;
+
+ 'restart')
+ checkUser $1 touchlock
+ stopit
+ start
+ ;;
+
+ 'status')
+ checkUser $1
+ status
+ ;;
+
+ 'dump')
+ checkUser $1
+ dump
+ ;;
+
+ *)
+ echo "Usage: $0 { console | start | stop | restart | status | dump }"
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat
new file mode 100644
index 0000000..df0160e
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-service.bat
@@ -0,0 +1,50 @@
+@echo off
+
+REM ------------------------------------------------------------------------
+REM Licensed to the Apache Software Foundation (ASF) under one or more
+REM contributor license agreements. See the NOTICE file distributed with
+REM this work for additional information regarding copyright ownership.
+REM The ASF licenses this file to You under the Apache License, Version 2.0
+REM (the "License"); you may not use this file except in compliance with
+REM the License. You may obtain a copy of the License at
+REM
+REM http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM Unless required by applicable law or agreed to in writing, software
+REM distributed under the License is distributed on an "AS IS" BASIS,
+REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM See the License for the specific language governing permissions and
+REM limitations under the License.
+REM ------------------------------------------------------------------------
+
+setlocal
+
+set APP_NAME=${name}
+set APP_LONG_NAME=${displayName}
+set APP_BASE=${servicemix.base}
+
+if ""%1"" == ""run"" goto doRun
+if ""%1"" == ""install"" goto doInstall
+if ""%1"" == ""remove"" goto doRemove
+
+echo Usage: servicemix-service ( commands ... )
+echo commands:
+echo run Start %APP_NAME% in the current console
+echo install Install %APP_NAME% as a Windows service
+echo remove Remove the %APP_NAME% Windows service
+goto end
+
+:doRun
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -c "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:doInstall
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -i "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:doRemove
+"%APP_BASE%\bin\%APP_NAME%-wrapper.exe" -r "%APP_BASE%\etc\%APP_NAME%-wrapper.conf"
+goto end
+
+:end
+if not "%PAUSE%" == "" pause
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe
new file mode 100644
index 0000000..a46a2ac
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/servicemix-wrapper.exe
Binary files differ
diff --git a/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll
new file mode 100644
index 0000000..37c4f33
--- /dev/null
+++ b/karaf/gshell/gshell-wrapper/src/main/resources/org/apache/servicemix/kernel/gshell/wrapper/windows/wrapper.dll
Binary files differ
diff --git a/karaf/gshell/itests/pom.xml b/karaf/gshell/itests/pom.xml
new file mode 100644
index 0000000..c684ceb
--- /dev/null
+++ b/karaf/gshell/itests/pom.xml
@@ -0,0 +1,153 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.itests</artifactId>
+ <packaging>jar</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell ITests</name>
+
+ <description>
+ Provides the OSGi GShell commands
+ </description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.core</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.log</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>org.apache.servicemix.kernel.gshell.osgi</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.jaas</groupId>
+ <artifactId>org.apache.servicemix.kernel.jaas.boot</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.kernel.testing</groupId>
+ <artifactId>org.apache.servicemix.kernel.testing.support</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.servicemix.bundles</groupId>
+ <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${pom.basedir}/target/test-classes/</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/test/filtered-resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- generate dependencies versions -->
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>generate-depends-file</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>ci-build-profile</id>
+ <activation>
+ <property>
+ <name>maven.repo.local</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <!-- when the local repo location has been specified, we need to pass on this information to PAX mvn url -->
+ <argLine>-Dorg.ops4j.pax.url.mvn.localRepository=${maven.repo.local}</argLine>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <activation>
+ <os>
+ <family>Windows</family>
+ </os>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/karaf/gshell/itests/src/test/filtered-resources/features.xml b/karaf/gshell/itests/src/test/filtered-resources/features.xml
new file mode 100644
index 0000000..8316b3f
--- /dev/null
+++ b/karaf/gshell/itests/src/test/filtered-resources/features.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+ 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.
+-->
+<features>
+ <feature name="wrapper" version="${version}">
+ <bundle>mvn:org.apache.servicemix.kernel.gshell/org.apache.servicemix.kernel.gshell.wrapper/${version}</bundle>
+ </feature>
+ <feature name="obr" version="${version}">
+ <bundle>mvn:org.apache.felix/org.apache.felix.bundlerepository/${felix.bundlerepository.version}</bundle>
+ <bundle>mvn:org.apache.servicemix.kernel.gshell/org.apache.servicemix.kernel.gshell.obr/${version}</bundle>
+ </feature>
+</features>
diff --git a/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.java b/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.java
new file mode 100644
index 0000000..6f90dc2
--- /dev/null
+++ b/karaf/gshell/itests/src/test/java/org/apache/servicemix/kernel/gshell/itests/CoreTest.java
@@ -0,0 +1,129 @@
+/*
+ * 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.servicemix.kernel.gshell.itests;
+
+import org.apache.geronimo.gshell.commandline.CommandLineExecutionFailed;
+import org.apache.geronimo.gshell.registry.NoSuchCommandException;
+import org.apache.geronimo.gshell.shell.Shell;
+import org.apache.servicemix.kernel.testing.support.AbstractIntegrationTest;
+import org.osgi.framework.Bundle;
+
+public class CoreTest extends AbstractIntegrationTest {
+
+ static {
+ System.setProperty("servicemix.startLocalConsole", "false");
+ System.setProperty("servicemix.startRemoteShell", "false");
+ }
+
+ @Override
+ protected String getManifestLocation() {
+ return "classpath:org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF";
+ }
+
+ @Override
+ protected String[] getTestBundlesNames() {
+ return new String[] {
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jline"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-httpclient"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-jexl"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.commons-vfs"),
+ getBundle("org.apache.mina", "mina-core"),
+ getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.oro"),
+ getBundle("org.apache.servicemix.kernel.jaas", "org.apache.servicemix.kernel.jaas.config"),
+ getBundle("org.apache.sshd", "sshd-core"),
+ getBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.core"),
+ getBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.osgi")
+ };
+ }
+
+ public void testHelp() throws Exception {
+ Shell shell = getOsgiService(Shell.class);
+ shell.execute("help");
+ }
+
+ public void testInstallCommand() throws Exception {
+ Shell shell = getOsgiService(Shell.class);
+
+ try {
+ shell.execute("log/display");
+ fail("command should not exist");
+ } catch (CommandLineExecutionFailed e) {
+ assertNotNull(e.getCause());
+ assertTrue(e.getCause() instanceof NoSuchCommandException);
+ }
+
+ Bundle b = installBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.log", null, "jar");
+
+ shell.execute("log/display");
+
+ b.uninstall();
+
+ try {
+ shell.execute("log/display");
+ fail("command should not exist");
+ } catch (CommandLineExecutionFailed e) {
+ assertNotNull(e.getCause());
+ assertTrue(e.getCause() instanceof NoSuchCommandException);
+ }
+ }
+
+ public void testCommandGroup() throws Exception {
+ Shell shell = getOsgiService(Shell.class);
+ shell.execute("osgi");
+ shell.execute("help");
+ shell.execute("..");
+ }
+
+ public void testInstallFeature() throws Exception {
+ Shell shell = getOsgiService(Shell.class);
+
+ try {
+ shell.execute("obr");
+ fail("command should not exist");
+ } catch (CommandLineExecutionFailed e) {
+ assertNotNull(e.getCause());
+ assertTrue(e.getCause() instanceof NoSuchCommandException);
+ }
+ try {
+ shell.execute("wrapper");
+ fail("command should not exist");
+ } catch (CommandLineExecutionFailed e) {
+ assertNotNull(e.getCause());
+ assertTrue(e.getCause() instanceof NoSuchCommandException);
+ }
+ String url = getClass().getClassLoader().getResource("features.xml").toString();
+ addFeatureRepo(url);
+ installFeature("obr");
+ installFeature("wrapper");
+ shell.execute("obr");
+ shell.execute("wrapper");
+ }
+
+ /**
+ * TODO: This test seems to fail, there must be a timing issue somewhere
+ *
+ public void testCommandGroupAfterInstall() throws Exception {
+ Bundle b = installBundle("org.apache.servicemix.kernel.gshell", "org.apache.servicemix.kernel.gshell.log", null, "jar");
+ Shell shell = getOsgiService(Shell.class);
+ shell.execute("log");
+ shell.execute("help");
+ shell.execute("..");
+ }
+ */
+
+
+}
diff --git a/karaf/gshell/itests/src/test/resources/log4j.properties b/karaf/gshell/itests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..8cc85f8
--- /dev/null
+++ b/karaf/gshell/itests/src/test/resources/log4j.properties
@@ -0,0 +1,33 @@
+################################################################################
+#
+# 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.
+#
+################################################################################
+
+# Root logger
+log4j.rootLogger=INFO, stdout
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=${servicemix.base}/data/log/servicemix.log
+log4j.appender.out.append=true
diff --git a/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF b/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF
new file mode 100644
index 0000000..df8a1a7
--- /dev/null
+++ b/karaf/gshell/itests/src/test/resources/org/apache/servicemix/kernel/gshell/itests/MANIFEST.MF
@@ -0,0 +1,30 @@
+Manifest-Version: 1.0
+License-00: .
+License-01: Licensed to the Apache Software Foundation (ASF) under one or more
+License-02: contributor license agreements. See the NOTICE file distributed with
+License-03: this work for additional information regarding copyright ownership.
+License-04: The ASF licenses this file to You under the Apache License, Version 2.0
+License-05: (the "License"); you may not use this file except in compliance with
+License-06: the License. You may obtain a copy of the License at
+License-07: .
+License-08: http://www.apache.org/licenses/LICENSE-2.0
+License-09: .
+License-10: Unless required by applicable law or agreed to in writing, software
+License-11: distributed under the License is distributed on an "AS IS" BASIS,
+License-12: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+License-13: See the License for the specific language governing permissions and
+License-14: limitations under the License.
+License-15: .
+Bundle-Name: org.apache.servicemix.testing.itests
+Bundle-SymbolicName: org.apache.servicemix.testing.itests
+Bundle-Vendor: Spring Framework
+Bundle-Activator: org.springframework.osgi.test.JUnitTestActivator
+Import-Package: junit.framework,
+ org.osgi.framework;specification-version="1.3.0",
+ org.apache.commons.logging,
+ org.springframework.core.io,
+ org.springframework.osgi.test,
+ org.apache.servicemix.kernel.testing.support,
+ org.apache.geronimo.gshell.commandline,
+ org.apache.geronimo.gshell.registry,
+ org.apache.geronimo.gshell.shell
diff --git a/karaf/gshell/pom.xml b/karaf/gshell/pom.xml
new file mode 100644
index 0000000..3c2d24f
--- /dev/null
+++ b/karaf/gshell/pom.xml
@@ -0,0 +1,49 @@
+<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">
+
+ <!--
+
+ 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.
+ -->
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.servicemix.kernel</groupId>
+ <artifactId>kernel</artifactId>
+ <version>1.2.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.servicemix.kernel.gshell</groupId>
+ <artifactId>gshell</artifactId>
+ <packaging>pom</packaging>
+ <version>1.2.0-SNAPSHOT</version>
+ <name>Apache ServiceMix Kernel :: GShell</name>
+
+ <modules>
+ <module>gshell-core</module>
+ <module>gshell-osgi</module>
+ <module>gshell-admin</module>
+ <module>gshell-features</module>
+ <module>gshell-obr</module>
+ <module>gshell-wrapper</module>
+ <module>gshell-log</module>
+ <module>gshell-config</module>
+ <module>gshell-packages</module>
+ <module>itests</module>
+ </modules>
+
+</project>
\ No newline at end of file
diff --git a/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties b/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties
new file mode 100644
index 0000000..7779a61
--- /dev/null
+++ b/karaf/gshell/src/test/configs/services/org.apache.servicemix.shell.properties
@@ -0,0 +1,20 @@
+#
+# 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.
+#
+#
+startLocalConsole=false
diff --git a/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties b/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties
new file mode 100644
index 0000000..23075bf
--- /dev/null
+++ b/karaf/gshell/src/test/configs/services/org.ops4j.pax.logging.properties
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+#
+log4j.rootLogger=DEBUG, out
+
+log4j.logger.org.springframework=DEBUG
+
+# CONSOLE appender not used by default
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d{ABSOLUTE} | %-5.5p | %-16.16t | %-32.32c{1} | %-32.32C %4L | %m%n
+log4j.appender.out.file=target/gshell.log
+log4j.appender.out.append=true