FELIX-1766: Refactor and enable AdminServiceMBean
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@828802 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/admin/core/NOTICE b/karaf/admin/core/NOTICE
new file mode 100644
index 0000000..2f69501
--- /dev/null
+++ b/karaf/admin/core/NOTICE
@@ -0,0 +1,21 @@
+Apache Felix Karaf
+Copyright 2009 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+
+II. Used Software
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2009).
+Licensed under the Apache License 2.0.
+
+
+III. License Summary
+- Apache License 2.0
diff --git a/karaf/admin/core/pom.xml b/karaf/admin/core/pom.xml
new file mode 100644
index 0000000..86bff82
--- /dev/null
+++ b/karaf/admin/core/pom.xml
@@ -0,0 +1,227 @@
+<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.felix.karaf.admin</groupId>
+ <artifactId>admin</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.felix.karaf.admin</groupId>
+ <artifactId>org.apache.felix.karaf.admin.core</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.1.0-SNAPSHOT</version>
+ <name>Apache Felix Karaf :: Admin Core</name>
+
+ <properties>
+ <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
+ </properties>
+
+ <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.felix.karaf.shell</groupId>
+ <artifactId>org.apache.felix.karaf.shell.console</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.felix.karaf.shell</groupId>
+ <artifactId>org.apache.felix.karaf.shell.obr</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework.osgi</groupId>
+ <artifactId>spring-osgi-core</artifactId>
+ <scope>test</scope>
+ </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>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-jdk14</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <filters>
+ <filter>target/filter.txt</filter>
+ </filters>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>create-prop</id>
+ <phase>initialize</phase>
+ <configuration>
+ <tasks>
+ <taskdef resource="net/sf/antcontrib/antcontrib.properties" classpathref="maven.plugin.classpath" />
+ <property name="ant.regexp.regexpimpl" value="org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp" />
+ <property name="mv" value="${project.version}" />
+ <echo message="Maven version: ${mv}" />
+ <propertyregex property="ov.p1" input="${mv}" regexp="(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:[^a-zA-Z0-9](.*))?" replace="\1" defaultValue="0" />
+ <propertyregex property="ov.p2" input="${mv}" regexp="(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:[^a-zA-Z0-9](.*))?" replace=".\2" defaultValue=".0" />
+ <propertyregex property="ov.p3" input="${mv}" regexp="(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:[^a-zA-Z0-9](.*))?" replace=".\3" defaultValue=".0" />
+ <propertyregex property="ov.p4" input="${mv}" regexp="(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:[^a-zA-Z0-9](.*))?" replace=".\4" defaultValue="" />
+ <propertyregex property="ov.p1a" input="${ov.p1}" regexp="(.+)" replace="\1" defaultValue="0" />
+ <propertyregex property="ov.p2a" input="${ov.p2}" regexp="(\..+)" replace="\1" defaultValue=".0" />
+ <propertyregex property="ov.p3a" input="${ov.p3}" regexp="(\..+)" replace="\1" defaultValue=".0" />
+ <propertyregex property="ov.p4a" input="${ov.p4}" regexp="(\..+)" replace="\1" defaultValue="" />
+ <property name="ov" value="${ov.p1a}${ov.p2a}${ov.p3a}${ov.p4a}" />
+ <echo message="OSGi version: ${ov}" />
+ <mkdir dir="target" />
+ <echo message="karaf.osgi.version = ${ov}" file="target/filter.txt" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-config-properties</id>
+ <!-- here the phase you need -->
+ <phase>compile</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/target/classes/org/apache/felix/karaf/admin/etc</outputDirectory>
+ <resources>
+ <resource>
+ <directory>../../assembly/src/main/distribution/text/etc/</directory>
+ <includes>
+ <include>*.properties</include>
+ <include>*.cfg</include>
+ </includes>
+ <excludes>
+ <exclude>org.apache.felix.karaf.shell.cfg</exclude>
+ <exclude>org.ops4j.pax.url.mvn.cfg</exclude>
+ <exclude>system.properties</exclude>
+ </excludes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ <execution>
+ <id>copy-filtered</id>
+ <!-- here the phase you need -->
+ <phase>compile</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/target/classes/org/apache/felix/karaf/admin/etc</outputDirectory>
+ <resources>
+ <resource>
+ <directory>../../assembly/src/main/filtered-resources/etc</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>*.properties</include>
+ <include>*.cfg</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+ <Export-Package>
+ org.apache.felix.karaf.admin;version=${pom.version},
+ org.apache.felix.karaf.jpm;version=${pom.version}
+ </Export-Package>
+ <Import-Package>
+ !org.apache.felix.karaf.admin,
+ !org.apache.felix.karaf.jpm,
+ javax.management,
+ javax.management.loading,
+ org.osgi.service.command,
+ org.apache.felix.gogo.commands,
+ org.apache.felix.karaf.shell.console,
+ *
+ </Import-Package>
+ <Private-Package>
+ org.apache.felix.karaf.admin.internal,
+ org.apache.felix.karaf.jpm.impl
+ </Private-Package>
+ <_versionpolicy>${bnd.version.policy}</_versionpolicy>
+ </instructions>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <!-- this is not a unit test but an application used for testing -->
+ <exclude>**/MainTest.java</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/AdminService.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/AdminService.java
new file mode 100644
index 0000000..e8d1cb5
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/AdminService.java
@@ -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.
+ */
+package org.apache.felix.karaf.admin;
+
+public interface AdminService {
+
+ Instance createInstance(String name, InstanceSettings settings) throws Exception;
+
+ Instance[] getInstances();
+
+ Instance getInstance(String name);
+}
diff --git a/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/Instance.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/Instance.java
new file mode 100644
index 0000000..4f267c7
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/Instance.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.felix.karaf.admin;
+
+public interface Instance {
+
+ String STOPPED = "Stopped";
+ String STARTING = "Starting";
+ String STARTED = "Started";
+ String ERROR = "Error";
+
+ String getName();
+
+ String getLocation();
+
+ int getPid();
+
+ int getPort();
+
+ 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/admin/core/src/main/java/org/apache/felix/karaf/admin/InstanceSettings.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/InstanceSettings.java
new file mode 100644
index 0000000..832e24f
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/InstanceSettings.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.karaf.admin;
+
+import java.util.List;
+
+public class InstanceSettings {
+ private final int port;
+ private final String location;
+ private final List<String> featureURLs;
+ private final List<String> features;
+
+ public InstanceSettings(int port, String location, List<String> featureURLs, List<String> features) {
+ this.port = port;
+ this.location = location;
+ this.featureURLs = featureURLs;
+ this.features = features;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public List<String> getFeatureURLs() {
+ return featureURLs;
+ }
+
+ public List<String> getFeatures() {
+ return features;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof InstanceSettings)) {
+ return false;
+ }
+ InstanceSettings is = (InstanceSettings) o;
+ return is.port == port &&
+ (location == null ? is.location == null : location.equals(is.location)) &&
+ (featureURLs == null ? is.featureURLs == null : featureURLs.equals(is.featureURLs)) &&
+ (features == null ? is.features == null : features.equals(is.features));
+ }
+
+ @Override
+ public int hashCode() {
+ int rc = 17;
+ rc = 37 * port;
+ if (location != null) {
+ rc = 37 * location.hashCode();
+ }
+ if (featureURLs != null) {
+ rc = 37 * featureURLs.hashCode();
+ }
+ if (features != null) {
+ rc = 37 * features.hashCode();
+ }
+ return rc;
+ }
+
+
+}
diff --git a/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/internal/AdminServiceImpl.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/internal/AdminServiceImpl.java
new file mode 100644
index 0000000..df8d155
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/internal/AdminServiceImpl.java
@@ -0,0 +1,347 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.karaf.admin.internal;
+
+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.PrintStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Scanner;
+
+import org.apache.felix.karaf.admin.AdminService;
+import org.apache.felix.karaf.admin.Instance;
+import org.apache.felix.karaf.admin.InstanceSettings;
+import org.fusesource.jansi.Ansi;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class AdminServiceImpl implements AdminService {
+ public static final String STORAGE_FILE = "instance.properties";
+ private static final String FEATURES_CFG = "etc/org.apache.felix.karaf.features.cfg";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AdminServiceImpl.class);
+
+ private Map<String, Instance> instances = new HashMap<String, Instance>();
+
+ private int defaultPortStart = 8101;
+
+ private File storageLocation;
+
+ public File getStorageLocation() {
+ return storageLocation;
+ }
+
+ public void setStorageLocation(File storage) {
+ this.storageLocation = storage;
+ }
+
+ private Properties loadStorage(File location) throws IOException {
+ InputStream is = null;
+ try {
+ is = new FileInputStream(location);
+ Properties props = new Properties();
+ props.load(is);
+ return props;
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ private void saveStorage(Properties props, File location, String comment) throws IOException {
+ OutputStream os = null;
+ try {
+ os = new FileOutputStream(location);
+ props.store(os, comment);
+ } finally {
+ if (os != null) {
+ os.close();
+ }
+ }
+ }
+
+ public synchronized void init() throws Exception {
+ try {
+ File storageFile = new File(storageLocation, STORAGE_FILE);
+ if (!storageFile.isFile()) {
+ if (storageFile.exists()) {
+ LOGGER.error("Instances storage location should be a file: " + storageFile);
+ }
+ return;
+ }
+ Properties storage = loadStorage(storageFile);
+ int count = Integer.parseInt(storage.getProperty("count", "0"));
+ defaultPortStart = Integer.parseInt(storage.getProperty("port", Integer.toString(defaultPortStart)));
+ Map<String, Instance> newInstances = new HashMap<String, Instance>();
+ for (int i = 0; i < count; i++) {
+ String name = storage.getProperty("item." + i + ".name", null);
+ String loc = storage.getProperty("item." + i + ".loc", null);
+ int pid = Integer.parseInt(storage.getProperty("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 Karaf instance list", e);
+ }
+ }
+
+ public synchronized Instance createInstance(String name, InstanceSettings settings) throws Exception {
+ if (instances.get(name) != null) {
+ throw new IllegalArgumentException("Instance '" + name + "' already exists");
+ }
+ String loc = settings.getLocation() != null ? settings.getLocation() : name;
+ File karafBase = new File(loc);
+ if (!karafBase.isAbsolute()) {
+ karafBase = new File(storageLocation, loc);
+ }
+ int sshPort = settings.getPort();
+ if (sshPort <= 0) {
+ sshPort = ++defaultPortStart;
+ }
+ println(Ansi.ansi().a("Creating new instance on port ").a(sshPort).a(" at: ").a(Ansi.Attribute.INTENSITY_BOLD).a(karafBase).a(Ansi.Attribute.RESET).toString());
+
+ mkdir(karafBase, "bin");
+ mkdir(karafBase, "etc");
+ mkdir(karafBase, "system");
+ mkdir(karafBase, "deploy");
+ mkdir(karafBase, "data");
+
+ copyResourceToDir(karafBase, "etc/config.properties", true);
+ copyResourceToDir(karafBase, "etc/java.util.logging.properties", true);
+ copyResourceToDir(karafBase, "etc/org.apache.felix.karaf.log.cfg", true);
+ copyResourceToDir(karafBase, FEATURES_CFG, true);
+ copyResourceToDir(karafBase, "etc/org.apache.felix.karaf.management.cfg", true);
+ copyResourceToDir(karafBase, "etc/org.ops4j.pax.logging.cfg", true);
+ copyResourceToDir(karafBase, "etc/org.ops4j.pax.url.mvn.cfg", true);
+ copyResourceToDir(karafBase, "etc/startup.properties", true);
+ copyResourceToDir(karafBase, "etc/users.properties", true);
+
+ HashMap<String, String> props = new HashMap<String, String>();
+ props.put("${karaf.name}", name);
+ props.put("${karaf.home}", System.getProperty("karaf.home"));
+ props.put("${karaf.base}", karafBase.getPath());
+ props.put("${karaf.sshPort}", Integer.toString(sshPort));
+ copyFilteredResourceToDir(karafBase, "etc/system.properties", props);
+ copyFilteredResourceToDir(karafBase, "etc/org.apache.felix.karaf.shell.cfg", props);
+ if( System.getProperty("os.name").startsWith("Win") ) {
+ copyFilteredResourceToDir(karafBase, "bin/karaf.bat", props);
+ copyFilteredResourceToDir(karafBase, "bin/start.bat", props);
+ copyFilteredResourceToDir(karafBase, "bin/stop.bat", props);
+ } else {
+ copyFilteredResourceToDir(karafBase, "bin/karaf", props);
+ copyFilteredResourceToDir(karafBase, "bin/start", props);
+ copyFilteredResourceToDir(karafBase, "bin/stop", props);
+ chmod(new File(karafBase, "bin/karaf"), "a+x");
+ chmod(new File(karafBase, "bin/start"), "a+x");
+ chmod(new File(karafBase, "bin/stop"), "a+x");
+ }
+
+ handleFeatures(new File(karafBase, FEATURES_CFG), settings);
+
+ Instance instance = new InstanceImpl(this, name, karafBase.toString());
+ instances.put(name, instance);
+ saveState();
+ return instance;
+ }
+
+ void handleFeatures(File featuresCfg, InstanceSettings settings) throws IOException {
+ Properties p = loadStorage(featuresCfg);
+
+ appendToPropList(p, "featuresBoot", settings.getFeatures());
+ appendToPropList(p, "featuresRepositories", settings.getFeatureURLs());
+ saveStorage(p, featuresCfg, "Features Configuration");
+ }
+
+ private void appendToPropList(Properties p, String key, List<String> elements) {
+ if (elements == null) {
+ return;
+ }
+ StringBuilder sb = new StringBuilder(p.getProperty(key).trim());
+ for (String f : elements) {
+ if (sb.length() > 0) {
+ sb.append(',');
+ }
+ sb.append(f);
+ }
+ p.setProperty(key, sb.toString());
+ }
+
+ 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 {
+ Properties storage = new Properties();
+ Instance[] data = getInstances();
+ storage.setProperty("port", Integer.toString(defaultPortStart));
+ storage.setProperty("count", Integer.toString(data.length));
+ for (int i = 0; i < data.length; i++) {
+ storage.setProperty("item." + i + ".name", data[i].getName());
+ storage.setProperty("item." + i + ".loc", data[i].getLocation());
+ storage.setProperty("item." + i + ".pid", Integer.toString(data[i].getPid()));
+ }
+ saveStorage(storage, new File(storageLocation, STORAGE_FILE), "Admin Service storage");
+ }
+
+ private void copyResourceToDir(File target, String resource, boolean text) throws Exception {
+ File outFile = new File(target, resource);
+ if( !outFile.exists() ) {
+ println(Ansi.ansi().a("Creating file: ").a(Ansi.Attribute.INTENSITY_BOLD).a(outFile.getPath()).a(Ansi.Attribute.RESET).toString());
+ InputStream is = getClass().getClassLoader().getResourceAsStream("org/apache/felix/karaf/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) {
+ 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(Ansi.ansi().a("Creating file: ").a(Ansi.Attribute.INTENSITY_BOLD).a(outFile.getPath()).a(Ansi.Attribute.RESET).toString());
+ InputStream is = getClass().getClassLoader().getResourceAsStream("org/apache/felix/karaf/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 karafBase, String path) {
+ File file = new File(karafBase, path);
+ if( !file.exists() ) {
+ println(Ansi.ansi().a("Creating dir: ").a(Ansi.Attribute.INTENSITY_BOLD).a(file.getPath()).a(Ansi.Attribute.RESET).toString());
+ 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/admin/core/src/main/java/org/apache/felix/karaf/admin/internal/InstanceImpl.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/internal/InstanceImpl.java
new file mode 100644
index 0000000..40a76a5
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/admin/internal/InstanceImpl.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.felix.karaf.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.felix.karaf.admin.Instance;
+import org.apache.felix.karaf.jpm.Process;
+import org.apache.felix.karaf.jpm.ProcessBuilderFactory;
+import org.apache.felix.karaf.jpm.impl.ScriptUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class InstanceImpl implements Instance {
+
+ private static final Logger LOG = LoggerFactory.getLogger(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 boolean exists() {
+ return new File(location).isDirectory();
+ }
+
+ public int getPid() {
+ checkProcess();
+ return this.process != null ? this.process.getPid() : 0;
+ }
+
+ public int getPort() {
+ InputStream is = null;
+ try {
+ File f = new File(location, "etc/org.apache.felix.karaf.shell.cfg");
+ is = new FileInputStream(f);
+ Properties props = new Properties();
+ props.load(is);
+ String loc = props.getProperty("sshPort");
+ return Integer.parseInt(loc);
+ } catch (Exception e) {
+ return 0;
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (IOException e) {
+ // Ignore
+ }
+ }
+ }
+ }
+
+ 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.felix.karaf.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("karaf.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
+ + " -Djava.util.logging.config.file=\"" + new File(location, "etc/java.util.logging.properties").getCanonicalPath() + "\""
+ + " -Dkaraf.home=\"" + System.getProperty("karaf.home") + "\""
+ + " -Dkaraf.base=\"" + new File(location).getCanonicalPath() + "\""
+ + " -Dkaraf.startLocalConsole=false"
+ + " -Dkaraf.startRemoteShell=true"
+ + " -classpath " + classpath.toString()
+ + " org.apache.felix.karaf.main.Bootstrap";
+ LOG.debug("Starting instance " + name + " 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() {
+ int port = getPort();
+ if (!exists() || port <= 0) {
+ return ERROR;
+ }
+ checkProcess();
+ if (this.process == null) {
+ return STOPPED;
+ } else {
+ try {
+ 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/admin/core/src/main/java/org/apache/felix/karaf/jpm/Process.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/Process.java
new file mode 100644
index 0000000..0644f50
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/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.felix.karaf.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/admin/core/src/main/java/org/apache/felix/karaf/jpm/ProcessBuilder.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/ProcessBuilder.java
new file mode 100644
index 0000000..a81cf36
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/ProcessBuilder.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.felix.karaf.jpm;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.*;
+
+/**
+ * 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
+ */
+ org.apache.felix.karaf.jpm.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
+ */
+ org.apache.felix.karaf.jpm.Process attach(int pid) throws IOException;
+
+}
diff --git a/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/ProcessBuilderFactory.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/ProcessBuilderFactory.java
new file mode 100644
index 0000000..6c4980c
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/ProcessBuilderFactory.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.felix.karaf.jpm;
+
+import java.lang.*;
+
+import org.apache.felix.karaf.jpm.impl.ProcessBuilderFactoryImpl;
+
+/**
+ * Factory for process builders.
+ */
+public abstract class ProcessBuilderFactory {
+
+ public static ProcessBuilderFactory newInstance() {
+ return new ProcessBuilderFactoryImpl();
+ }
+
+ public abstract ProcessBuilder newBuilder();
+
+}
diff --git a/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/impl/ProcessBuilderFactoryImpl.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/impl/ProcessBuilderFactoryImpl.java
new file mode 100644
index 0000000..23ee364
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/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.felix.karaf.jpm.impl;
+
+import org.apache.felix.karaf.jpm.ProcessBuilder;
+import org.apache.felix.karaf.jpm.ProcessBuilderFactory;
+
+public class ProcessBuilderFactoryImpl extends ProcessBuilderFactory {
+
+ public ProcessBuilder newBuilder() {
+ return new ProcessBuilderImpl();
+ }
+}
diff --git a/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/impl/ProcessBuilderImpl.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/impl/ProcessBuilderImpl.java
new file mode 100644
index 0000000..c6b622e
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/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.felix.karaf.jpm.impl;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.felix.karaf.jpm.Process;
+import org.apache.felix.karaf.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/admin/core/src/main/java/org/apache/felix/karaf/jpm/impl/ProcessImpl.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/impl/ProcessImpl.java
new file mode 100644
index 0000000..27dec49
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/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.felix.karaf.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.felix.karaf.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/admin/core/src/main/java/org/apache/felix/karaf/jpm/impl/ScriptUtils.java b/karaf/admin/core/src/main/java/org/apache/felix/karaf/jpm/impl/ScriptUtils.java
new file mode 100644
index 0000000..0f29087
--- /dev/null
+++ b/karaf/admin/core/src/main/java/org/apache/felix/karaf/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.felix.karaf.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/admin/core/src/main/resources/OSGI-INF/blueprint/admin-core.xml b/karaf/admin/core/src/main/resources/OSGI-INF/blueprint/admin-core.xml
new file mode 100644
index 0000000..f68a106
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/OSGI-INF/blueprint/admin-core.xml
@@ -0,0 +1,33 @@
+<?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.
+
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+ xmlns:ext="http://geronimo.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0"
+ default-activation="lazy">
+
+ <bean id="adminService" class="org.apache.felix.karaf.admin.internal.AdminServiceImpl" init-method="init">
+ <property name="storageLocation" value="${karaf.home}/instances" />
+ </bean>
+
+ <service ref="adminService" interface="org.apache.felix.karaf.admin.AdminService" />
+
+ <!-- Allow the use of system properties -->
+ <ext:property-placeholder />
+
+</blueprint>
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/karaf b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/karaf
new file mode 100644
index 0000000..7a97f92
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/karaf
@@ -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.
+#
+# $Id: karaf 979 2005-11-30 22:50:55Z bsnyder $
+#
+
+KARAF_HOME=${karaf.home}
+KARAF_BASE=${karaf.base}
+
+export KARAF_BASE
+exec ${KARAF_HOME}/bin/karaf "$*"
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/karaf.bat b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/karaf.bat
new file mode 100644
index 0000000..b54a669
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/karaf.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 =========================================================================
+
+SETLOCAL
+SET KARAF_BASE=${karaf.base}
+SET KARAF_HOME=${karaf.home}
+
+%KARAF_HOME%\bin\karaf.bat %*
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/start b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/start
new file mode 100644
index 0000000..d4552b5
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/start
@@ -0,0 +1,24 @@
+#!/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.
+#
+################################################################################
+
+KARAF_HOME=${karaf.home}
+
+exec ${KARAF_HOME}/bin/admin start ${karaf.name} "$@"
+
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/start.bat b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/start.bat
new file mode 100644
index 0000000..2da0758
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/start.bat
@@ -0,0 +1,23 @@
+@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 KARAF_HOME=${karaf.home}
+
+%KARAF_HOME%\bin\admin.bat start ${karaf.name}
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/stop b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/stop
new file mode 100644
index 0000000..46f8b01
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/stop
@@ -0,0 +1,24 @@
+#!/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.
+#
+################################################################################
+
+KARAF_HOME=${karaf.home}
+
+exec ${KARAF_HOME}/bin/admin stop ${karaf.name} "$@"
+
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/stop.bat b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/stop.bat
new file mode 100644
index 0000000..fd6279e
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/bin/stop.bat
@@ -0,0 +1,23 @@
+@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 KARAF_HOME=${karaf.home}
+
+%KARAF_HOME%\bin\admin.bat stop ${karaf.name}
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/org.apache.felix.karaf.shell.cfg b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/org.apache.felix.karaf.shell.cfg
new file mode 100644
index 0000000..8b83a07
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/org.apache.felix.karaf.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=${karaf.startLocalConsole}
+startRemoteShell=${karaf.startRemoteShell}
+sshPort=${karaf.sshPort}
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/org.ops4j.pax.url.mvn.cfg b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/org.ops4j.pax.url.mvn.cfg
new file mode 100644
index 0000000..9ae4b8a
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/org.ops4j.pax.url.mvn.cfg
@@ -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.
+#
+################################################################################
+
+#
+# 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:${karaf.home}/system@snapshots, \
+ file:${karaf.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 folder as a repo.
+#
+org.ops4j.pax.url.mvn.repositories= \
+ http://repo1.maven.org/maven2, \
+ http://people.apache.org/repo/m2-snapshot-repository@snapshots@noreleases, \
+ http://repository.ops4j.org/maven2, \
+ http://svn.apache.org/repos/asf/servicemix/m2-repo, \
+ http://repository.springsource.com/maven/bundles/release, \
+ http://repository.springsource.com/maven/bundles/external
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/system.properties b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/system.properties
new file mode 100644
index 0000000..66bbaef
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/admin/etc/system.properties
@@ -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.
+#
+################################################################################
+
+org.ops4j.pax.logging.DefaultServiceLog.level=ERROR
+karaf.name=${karaf.name}
+karaf.default.repository=system
+xml.catalog.files=
diff --git a/karaf/admin/core/src/main/resources/org/apache/felix/karaf/jpm/impl/unix/start.sh b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/jpm/impl/unix/start.sh
new file mode 100644
index 0000000..1d1d720
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/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/admin/core/src/main/resources/org/apache/felix/karaf/jpm/impl/windows/destroy.vbs b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/jpm/impl/windows/destroy.vbs
new file mode 100644
index 0000000..abd60eb
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/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/admin/core/src/main/resources/org/apache/felix/karaf/jpm/impl/windows/running.vbs b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/jpm/impl/windows/running.vbs
new file mode 100644
index 0000000..32c65c5
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/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/admin/core/src/main/resources/org/apache/felix/karaf/jpm/impl/windows/start.vbs b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/jpm/impl/windows/start.vbs
new file mode 100644
index 0000000..6004c86
--- /dev/null
+++ b/karaf/admin/core/src/main/resources/org/apache/felix/karaf/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/admin/core/src/test/java/org/apache/felix/karaf/admin/InstanceSettingsTest.java b/karaf/admin/core/src/test/java/org/apache/felix/karaf/admin/InstanceSettingsTest.java
new file mode 100644
index 0000000..cce8c80
--- /dev/null
+++ b/karaf/admin/core/src/test/java/org/apache/felix/karaf/admin/InstanceSettingsTest.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.felix.karaf.admin;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.TestCase;
+import org.apache.felix.karaf.admin.InstanceSettings;
+import org.junit.Assert;
+
+public class InstanceSettingsTest extends TestCase {
+ public void testInstanceSettings() {
+ InstanceSettings is =
+ new InstanceSettings(1, null, Collections.<String>emptyList(), Arrays.asList("hi"));
+ assertEquals(1, is.getPort());
+ Assert.assertNull(is.getLocation());
+ assertEquals(Arrays.asList("hi"), is.getFeatures());
+ assertEquals(0, is.getFeatureURLs().size());
+ }
+
+ public void testEqualsHashCode() {
+ testEqualsHashCode(1, "top", Collections.<String>emptyList(), Arrays.asList("hi"));
+ testEqualsHashCode(0, null, null, null);
+ }
+
+ private void testEqualsHashCode(int port, String location, List<String> featureURLs, List<String> features) {
+ InstanceSettings is = new InstanceSettings(port, location, featureURLs, features);
+ InstanceSettings is2 = new InstanceSettings(port, location, featureURLs, features);
+ assertEquals(is, is2);
+ assertEquals(is.hashCode(), is2.hashCode());
+ }
+
+ public void testEqualsHashCode2() {
+ InstanceSettings is = new InstanceSettings(1, "top", Collections.<String>emptyList(), Arrays.asList("hi"));
+ Assert.assertFalse(is.equals(null));
+ Assert.assertFalse(is.equals(new Object()));
+ assertEquals(is, is);
+ }
+}
diff --git a/karaf/admin/core/src/test/java/org/apache/felix/karaf/admin/internal/AdminServiceImplTest.java b/karaf/admin/core/src/test/java/org/apache/felix/karaf/admin/internal/AdminServiceImplTest.java
new file mode 100644
index 0000000..02b9961
--- /dev/null
+++ b/karaf/admin/core/src/test/java/org/apache/felix/karaf/admin/internal/AdminServiceImplTest.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.felix.karaf.admin.internal;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.Properties;
+
+import junit.framework.TestCase;
+
+import org.apache.felix.karaf.admin.InstanceSettings;
+
+public class AdminServiceImplTest extends TestCase {
+ public void testHandleFeatures() throws Exception {
+ AdminServiceImpl as = new AdminServiceImpl();
+
+ File f = File.createTempFile(getName(), ".test");
+ try {
+ Properties p = new Properties();
+ p.put("featuresBoot", "abc,def ");
+ p.put("featuresRepositories", "somescheme://xyz");
+ OutputStream os = new FileOutputStream(f);
+ try {
+ p.store(os, "Test comment");
+ } finally {
+ os.close();
+ }
+
+ InstanceSettings s = new InstanceSettings(8122, null, null, Arrays.asList("test"));
+ as.handleFeatures(f, s);
+
+ Properties p2 = new Properties();
+ InputStream is = new FileInputStream(f);
+ try {
+ p2.load(is);
+ } finally {
+ is.close();
+ }
+ assertEquals(2, p2.size());
+ assertEquals("abc,def,test", p2.get("featuresBoot"));
+ assertEquals("somescheme://xyz", p2.get("featuresRepositories"));
+ } finally {
+ f.delete();
+ }
+ }
+}
diff --git a/karaf/admin/core/src/test/java/org/apache/felix/karaf/jpm/MainTest.java b/karaf/admin/core/src/test/java/org/apache/felix/karaf/jpm/MainTest.java
new file mode 100644
index 0000000..2d4c80a
--- /dev/null
+++ b/karaf/admin/core/src/test/java/org/apache/felix/karaf/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.felix.karaf.jpm;
+
+public class MainTest {
+
+ public static void main(String[] args) throws Exception {
+ Thread.sleep(Long.parseLong(args[0]));
+ }
+}
diff --git a/karaf/admin/core/src/test/java/org/apache/felix/karaf/jpm/ProcessTest.java b/karaf/admin/core/src/test/java/org/apache/felix/karaf/jpm/ProcessTest.java
new file mode 100644
index 0000000..8b6ebc4
--- /dev/null
+++ b/karaf/admin/core/src/test/java/org/apache/felix/karaf/jpm/ProcessTest.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.felix.karaf.jpm;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+import org.apache.felix.karaf.jpm.impl.ScriptUtils;
+import org.apache.felix.karaf.jpm.ProcessBuilder;
+
+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();
+ org.apache.felix.karaf.jpm.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");
+ }
+ */
+}