Repository: karaf
Updated Branches:
  refs/heads/master 70d14e6e1 -> 084f579bd


[KARAF-2911] Add simple commands for subsystems


Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/084f579b
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/084f579b
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/084f579b

Branch: refs/heads/master
Commit: 084f579bd1d417504fe7fcedf39eb1d1e987761b
Parents: 70d14e6
Author: Guillaume Nodet <[email protected]>
Authored: Tue Apr 15 10:17:46 2014 +0200
Committer: Guillaume Nodet <[email protected]>
Committed: Tue Apr 15 20:16:54 2014 +0200

----------------------------------------------------------------------
 .../enterprise/src/main/feature/feature.xml     |   7 +-
 pom.xml                                         |   3 +-
 services/coordinator/pom.xml                    |  11 +-
 .../main/java/org/eclipse/osgi/util/NLS.java    | 127 +++++++
 subsystem/NOTICE                                |  71 ++++
 subsystem/pom.xml                               | 120 +++++++
 .../karaf/subsystem/commands/InfoAction.java    | 337 +++++++++++++++++++
 .../karaf/subsystem/commands/InstallAction.java |  39 +++
 .../karaf/subsystem/commands/ListAction.java    |  56 +++
 .../karaf/subsystem/commands/StartAction.java   |  39 +++
 .../karaf/subsystem/commands/StopAction.java    |  39 +++
 .../subsystem/commands/SubsystemCompleter.java  |  38 +++
 .../subsystem/commands/SubsystemSupport.java    | 126 +++++++
 .../subsystem/commands/UninstallAction.java     |  39 +++
 14 files changed, 1037 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/assemblies/features/enterprise/src/main/feature/feature.xml
----------------------------------------------------------------------
diff --git a/assemblies/features/enterprise/src/main/feature/feature.xml 
b/assemblies/features/enterprise/src/main/feature/feature.xml
index 65ccf10..a0b8141 100644
--- a/assemblies/features/enterprise/src/main/feature/feature.xml
+++ b/assemblies/features/enterprise/src/main/feature/feature.xml
@@ -242,15 +242,12 @@
 
     <feature name="subsystems" description="Support for OSGi subsystems" 
version="${aries.subsystem.version}">
         <details>Support for Aries OSGi subsystems</details>
-        <!-- blueprint is required for application.modeller -->
-        <feature>aries-blueprint</feature>
+        <bundle dependency="true" 
start-level="30">mvn:org.apache.aries/org.apache.aries.util/${aries.util.version}</bundle>
         <bundle 
start-level="30">mvn:org.apache.felix/org.apache.felix.resolver/${felix.resolver.version}</bundle>
         <bundle 
start-level="30">mvn:org.apache.karaf.services/org.apache.karaf.services.coordinator/${project.version}</bundle>
         <bundle 
start-level="30">mvn:org.eclipse.equinox/org.eclipse.equinox.region/${equinox.region.version}</bundle>
-        <bundle 
start-level="30">mvn:org.apache.aries.application/org.apache.aries.application.api/${aries.application.api.version}</bundle>
-        <bundle 
start-level="30">mvn:org.apache.aries.application/org.apache.aries.application.utils/${aries.application.version}</bundle>
-        <bundle 
start-level="30">mvn:org.apache.aries.application/org.apache.aries.application.modeller/${aries.application.version}</bundle>
         <bundle 
start-level="30">mvn:org.apache.aries.subsystem/org.apache.aries.subsystem/${aries.subsystem.version}</bundle>
+        <bundle 
start-level="30">mvn:org.apache.karaf.subsystem/org.apache.karaf.subsystem.core/${project.version}</bundle>
     </feature>
 
 </features>

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index fc446f0..2ef129f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,6 +71,7 @@
         <module>archetypes</module>
         <module>itests</module>
         <module>services</module>
+        <module>subsystem</module>
     </modules>
 
     <scm>
@@ -201,7 +202,7 @@
         <aries.jndi.api.version>1.0.0</aries.jndi.api.version>
         <aries.proxy.version>1.0.2</aries.proxy.version>
         <aries.proxy.api.version>1.0.0</aries.proxy.api.version>
-        <aries.subsystem.version>1.0.0</aries.subsystem.version>
+        <aries.subsystem.version>1.0.1-SNAPSHOT</aries.subsystem.version>
         <aries.transaction.version>1.0.1</aries.transaction.version>
         
<aries.transaction.manager.version>1.1.0</aries.transaction.manager.version>
         
<aries.transaction.blueprint.version>1.0.1</aries.transaction.blueprint.version>

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/services/coordinator/pom.xml
----------------------------------------------------------------------
diff --git a/services/coordinator/pom.xml b/services/coordinator/pom.xml
index 413587e..6dbb674 100644
--- a/services/coordinator/pom.xml
+++ b/services/coordinator/pom.xml
@@ -59,14 +59,12 @@
                             org.osgi.service.coordinator;version="1.0.0"
                         </Export-Package>
                         <Private-Package>
-                            org.eclipse.equinox.coordinator
+                            org.eclipse.equinox.coordinator,
+                            org.eclipse.osgi.util
                         </Private-Package>
                         <Export-Service>
                             org.osgi.service.coordinator.Coordinator
                         </Export-Service>
-                        <Embed-Dependency>
-                            
org.eclipse.osgi;inline="org/eclipse/osgi/util/NLS*.class"
-                        </Embed-Dependency>
                     </instructions>
                 </configuration>
             </plugin>
@@ -85,11 +83,6 @@
             <version>1.1.0.v20120522-1841</version>
             <scope>provided</scope>
         </dependency>
-        <dependency>
-            <groupId>org.eclipse</groupId>
-            <artifactId>org.eclipse.osgi</artifactId>
-            <scope>provided</scope>
-        </dependency>
     </dependencies>
 
 </project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/services/coordinator/src/main/java/org/eclipse/osgi/util/NLS.java
----------------------------------------------------------------------
diff --git a/services/coordinator/src/main/java/org/eclipse/osgi/util/NLS.java 
b/services/coordinator/src/main/java/org/eclipse/osgi/util/NLS.java
new file mode 100644
index 0000000..7466346
--- /dev/null
+++ b/services/coordinator/src/main/java/org/eclipse/osgi/util/NLS.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.eclipse.osgi.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.util.Properties;
+
+
+public class NLS {
+
+    public static void initializeMessages(final String bundleName, final 
Class<?> clazz) {
+        String resource = bundleName.replace('.', '/') + ".properties";
+        final InputStream input = 
clazz.getClassLoader().getResourceAsStream(resource);
+        try {
+            Properties properties = new Properties();
+            properties.load(input);
+            for (String key : properties.stringPropertyNames()) {
+                String value = properties.getProperty(key);
+
+                try {
+                    Field field = clazz.getDeclaredField(key);
+                    field.setAccessible(true);
+                    field.set(null, value);
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+        } catch (IOException e) {
+            // ignore
+        } finally {
+            try {
+                input.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+
+    public static String bind(String message, Object binding) {
+        return bind(message, new Object[] { binding });
+    }
+
+    public static String bind(String message, Object binding1, Object 
binding2) {
+        return bind(message, new Object[] { binding1, binding2 });
+    }
+
+    public static String bind(String message, Object[] bindings) {
+        int length = message.length();
+        //estimate correct size of string buffer to avoid growth
+        StringBuilder buffer = new StringBuilder(message.length()
+                                    + (bindings != null ? bindings.length * 5 
: 0));
+        for (int i = 0; i < length; i++) {
+            char c = message.charAt(i);
+            switch (c) {
+                case '{' :
+                    int index = message.indexOf('}', i);
+                    // if we don't have a matching closing brace then...
+                    if (index == -1) {
+                        buffer.append(c);
+                        break;
+                    }
+                    i++;
+                    if (i >= length) {
+                        buffer.append(c);
+                        break;
+                    }
+                    // look for a substitution
+                    int number = -1;
+                    try {
+                        number = Integer.parseInt(message.substring(i, index));
+                    } catch (NumberFormatException e) {
+                        throw (IllegalArgumentException) new 
IllegalArgumentException().initCause(e);
+                    }
+                    if (bindings == null || number >= bindings.length || 
number < 0) {
+                        buffer.append("<missing argument>"); //$NON-NLS-1$
+                        i = index;
+                        break;
+                    }
+                    buffer.append(bindings[number]);
+                    i = index;
+                    break;
+                case '\'' :
+                    // if a single quote is the last char on the line then 
skip it
+                    int nextIndex = i + 1;
+                    if (nextIndex >= length) {
+                        buffer.append(c);
+                        break;
+                    }
+                    char next = message.charAt(nextIndex);
+                    // if the next char is another single quote then write out 
one
+                    if (next == '\'') {
+                        i++;
+                        buffer.append(c);
+                        break;
+                    }
+                    // otherwise we want to read until we get to the next 
single quote
+                    index = message.indexOf('\'', nextIndex);
+                    // if there are no more in the string, then skip it
+                    if (index == -1) {
+                        buffer.append(c);
+                        break;
+                    }
+                    // otherwise write out the chars inside the quotes
+                    buffer.append(message.substring(nextIndex, index));
+                    i = index;
+                    break;
+                default :
+                    buffer.append(c);
+            }
+        }
+        return buffer.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/NOTICE
----------------------------------------------------------------------
diff --git a/subsystem/NOTICE b/subsystem/NOTICE
new file mode 100644
index 0000000..b70f1f9
--- /dev/null
+++ b/subsystem/NOTICE
@@ -0,0 +1,71 @@
+Apache Karaf
+Copyright 2010-2014 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.
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2010).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+OW2 (http://www.ow2.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+OPS4J (http://www.ops4j.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Eclipse Foundation (http://www.eclipse.org/).
+Licensed under the EPL.
+
+This product includes software written by
+Antony Lesuisse.
+Licensed under Public Domain.
+
+
+II. Used Software
+
+This product uses software developed at
+FUSE Source (http://www.fusesource.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+AOP Alliance (http://aopalliance.sourceforge.net/).
+Licensed under the Public Domain.
+
+This product uses software developed at
+Tanuki Software (http://www.tanukisoftware.com/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+Jasypt (http://jasypt.sourceforge.net/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+JLine (http://jline.sourceforge.net).
+Licensed under the BSD License.
+
+This product uses software developed at
+SLF4J (http://www.slf4j.org/).
+Licensed under the MIT License.
+
+This product uses software developed at
+SpringSource (http://www.springsource.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+
+
+III. License Summary
+- Apache License 2.0
+- BSD License
+- EPL License
+- MIT License

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/pom.xml
----------------------------------------------------------------------
diff --git a/subsystem/pom.xml b/subsystem/pom.xml
new file mode 100644
index 0000000..6dd6579
--- /dev/null
+++ b/subsystem/pom.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/xsd/maven-4.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.karaf</groupId>
+        <artifactId>karaf</artifactId>
+        <version>4.0.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.karaf.subsystem</groupId>
+    <artifactId>org.apache.karaf.subsystem.core</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: Subsystem :: Core</name>
+    <description>This bundle provides support for Subsystems.</description>
+
+    <properties>
+        
<appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf</groupId>
+            <artifactId>org.apache.karaf.util</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf.features</groupId>
+            <artifactId>org.apache.karaf.features.core</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.aries.subsystem</groupId>
+            <artifactId>org.apache.aries.subsystem</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.core</artifactId>
+        </dependency>
+
+   </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*.info</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <!--
+                        <Bundle-Activator>
+                            org.apache.karaf.subsystem.internal.Activator
+                        </Bundle-Activator>
+                        -->
+                        <Private-Package>
+                            org.apache.karaf.subsystem.commands,
+                            org.apache.felix.utils.manifest
+                        </Private-Package>
+                        <Karaf-Commands>*</Karaf-Commands>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/InfoAction.java
----------------------------------------------------------------------
diff --git 
a/subsystem/src/main/java/org/apache/karaf/subsystem/commands/InfoAction.java 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/InfoAction.java
new file mode 100644
index 0000000..ad2cef2
--- /dev/null
+++ 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/InfoAction.java
@@ -0,0 +1,337 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.subsystem.commands;
+
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.felix.utils.manifest.Attribute;
+import org.apache.felix.utils.manifest.Clause;
+import org.apache.felix.utils.manifest.Directive;
+import org.apache.felix.utils.manifest.Parser;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.Terminal;
+import org.apache.karaf.shell.support.ShellUtil;
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemConstants;
+
+@Command(scope = "subsystem", name = "info", description = "Display 
information about subsystems")
+@Service
+public class InfoAction extends SubsystemSupport implements Action {
+
+    protected final static String SUBSYSTEM_PREFIX = "Subsystem-";
+    protected final static String PACKAGE_SUFFFIX = "-Package";
+    protected final static String SERVICE_SUFFIX = "-Service";
+    protected final static String CAPABILITY_SUFFIX = "-Capability";
+    protected final static String IMPORT_PACKAGES_ATTRIB = "Import-Package";
+    protected final static String REQUIRE_BUNDLE_ATTRIB = "Require-Bundle";
+
+    @Option(name = "--indent", description = "Indentation method")
+    int indent = -1;
+
+    @Argument(description = "Subsystem names or ids")
+    @Completion(SubsystemCompleter.class)
+    String id;
+
+    @Reference
+    Terminal terminal;
+
+    @Override
+    public Object execute() throws Exception {
+        for (Subsystem ss : getSubsystems(id)) {
+            printHeaders(ss);
+        }
+        return null;
+    }
+
+    protected void printHeaders(Subsystem subsystem) throws Exception {
+        String title = getSubsystemName(subsystem);
+        System.out.println("\n" + title);
+        System.out.println(ShellUtil.getUnderlineString(title));
+        if (indent == 0) {
+            Map<String, String> dict = subsystem.getSubsystemHeaders(null);
+            for (String k : dict.keySet()) {
+                Object v = dict.get(k);
+                System.out.println(k + " = " + ShellUtil.getValueString(v));
+            }
+        } else {
+            System.out.println(generateFormattedOutput(subsystem));
+        }
+    }
+
+    public static String getSubsystemName(Subsystem subsystem) {
+        String name = 
subsystem.getSubsystemHeaders(null).get(SubsystemConstants.SUBSYSTEM_NAME);
+        return (name == null)
+                ? "Subsystem " + Long.toString(subsystem.getSubsystemId())
+                : name + " (" + Long.toString(subsystem.getSubsystemId()) + 
")";
+    }
+
+    protected String generateFormattedOutput(Subsystem subsystem) {
+        StringBuilder output = new StringBuilder();
+        Map<String, Object> otherAttribs = new TreeMap<String, Object>();
+        Map<String, Object> subsystemAttribs = new TreeMap<String, Object>();
+        Map<String, Object> serviceAttribs = new TreeMap<String, Object>();
+        Map<String, Object> packagesAttribs = new TreeMap<String, Object>();
+        Map<String, String> dict = subsystem.getSubsystemHeaders(null);
+
+        // do an initial loop and separate the attributes in different groups
+        for (String k : dict.keySet()) {
+            Object v = dict.get(k);
+            if (k.startsWith(SUBSYSTEM_PREFIX)) {
+                // starts with Bundle-xxx
+                subsystemAttribs.put(k, v);
+            } else if (k.endsWith(SERVICE_SUFFIX) || 
k.endsWith(CAPABILITY_SUFFIX)) {
+                // ends with xxx-Service
+                serviceAttribs.put(k, v);
+            } else if (k.endsWith(PACKAGE_SUFFFIX)) {
+                // ends with xxx-Package
+                packagesAttribs.put(k, v);
+            } else if (k.endsWith(REQUIRE_BUNDLE_ATTRIB)) {
+                // require bundle statement
+                packagesAttribs.put(k, v);
+            } else {
+                // the remaining attribs
+                otherAttribs.put(k, v);
+            }
+        }
+
+        // we will display the formatted result like this:
+        // Bundle-Name (ID)
+        // -----------------------
+        // all other attributes
+        //
+        // all Subsystem attributes
+        //
+        // all Service attributes
+        //
+        // all Package attributes
+        Iterator<Map.Entry<String, Object>> it = 
otherAttribs.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, Object> e = it.next();
+            output.append(String.format("%s = %s\n", e.getKey(), 
ShellUtil.getValueString(e.getValue())));
+        }
+        if (otherAttribs.size() > 0) {
+            output.append('\n');
+        }
+
+        it = subsystemAttribs.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, Object> e = it.next();
+            output.append(e.getKey());
+            output.append(" = \n");
+            formatHeader(ShellUtil.getValueString(e.getValue()), null, output, 
indent);
+            output.append("\n");
+        }
+        if (subsystemAttribs.size() > 0) {
+            output.append('\n');
+        }
+
+        it = serviceAttribs.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, Object> e = it.next();
+            output.append(e.getKey());
+            output.append(" = \n");
+            formatHeader(ShellUtil.getValueString(e.getValue()), null, output, 
indent);
+            output.append("\n");
+        }
+        if (serviceAttribs.size() > 0) {
+            output.append('\n');
+        }
+
+        Map<String, ClauseFormatter> formatters = new HashMap<String, 
ClauseFormatter>();
+        /*
+        formatters.put(REQUIRE_BUNDLE_ATTRIB, new ClauseFormatter() {
+            public void pre(Clause clause, StringBuilder output) {
+                boolean isSatisfied = checkBundle(clause.getName(), 
clause.getAttribute("bundle-version"));
+                Ansi.ansi(output).fg(isSatisfied ? Ansi.Color.DEFAULT : 
Ansi.Color.RED).a("");
+            }
+            public void post(Clause clause, StringBuilder output) {
+                Ansi.ansi(output).reset().a("");
+            }
+        });
+        formatters.put(IMPORT_PACKAGES_ATTRIB, new ClauseFormatter() {
+            public void pre(Clause clause, StringBuilder output) {
+                boolean isSatisfied = checkPackage(clause.getName(), 
clause.getAttribute("version"));
+                boolean isOptional = 
"optional".equals(clause.getDirective("resolution"));
+                Ansi.ansi(output).fg(isSatisfied ? Ansi.Color.DEFAULT : 
Ansi.Color.RED)
+                        .a(isSatisfied || isOptional ? 
Ansi.Attribute.INTENSITY_BOLD_OFF : Ansi.Attribute.INTENSITY_BOLD)
+                        .a("");
+            }
+            public void post(Clause clause, StringBuilder output) {
+                Ansi.ansi(output).reset().a("");
+            }
+        });
+        */
+
+        it = packagesAttribs.entrySet().iterator();
+        while (it.hasNext()) {
+            Map.Entry<String, Object> e = it.next();
+            output.append(e.getKey());
+            output.append(" = \n");
+            formatHeader(ShellUtil.getValueString(e.getValue()), 
formatters.get(e.getKey()), output, indent);
+            output.append("\n");
+        }
+        if (packagesAttribs.size() > 0) {
+            output.append('\n');
+        }
+
+        return output.toString();
+    }
+
+    protected interface ClauseFormatter {
+        void pre(Clause clause, StringBuilder output);
+        void post(Clause clause, StringBuilder output);
+    }
+
+    protected void formatHeader(String header, ClauseFormatter formatter, 
StringBuilder builder, int indent) {
+        Clause[] clauses = Parser.parseHeader(header);
+        formatClauses(clauses, formatter, builder, indent);
+    }
+
+    protected void formatClauses(Clause[] clauses, ClauseFormatter formatter, 
StringBuilder builder, int indent) {
+        boolean first = true;
+        for (Clause clause : clauses) {
+            if (first) {
+                first = false;
+            } else {
+                builder.append(",\n");
+            }
+            formatClause(clause, formatter, builder, indent);
+        }
+    }
+
+    protected void formatClause(Clause clause, ClauseFormatter formatter, 
StringBuilder builder, int indent) {
+        builder.append("\t");
+        if (formatter != null) {
+            formatter.pre(clause, builder);
+        }
+        formatClause(clause, builder, indent);
+        if (formatter != null) {
+            formatter.post(clause, builder);
+        }
+    }
+
+    protected int getTermWidth() {
+        return terminal.getWidth();
+
+    }
+
+    protected void formatClause(Clause clause, StringBuilder builder, int 
indent) {
+        if (indent < 0) {
+            if (clause.toString().length() < getTermWidth() - 8) { // -8 for 
tabs
+                indent = 1;
+            } else {
+                indent = 3;
+            }
+        }
+        String name = clause.getName();
+        Directive[] directives = clause.getDirectives();
+        Attribute[] attributes = clause.getAttributes();
+        Arrays.sort(directives, new Comparator<Directive>() {
+            public int compare(Directive o1, Directive o2) {
+                return o1.getName().compareTo(o2.getName());
+            }
+        });
+        Arrays.sort(attributes, new Comparator<Attribute>() {
+            public int compare(Attribute o1, Attribute o2) {
+                return o1.getName().compareTo(o2.getName());
+            }
+        });
+        builder.append(name);
+        for (int i = 0; directives != null && i < directives.length; i++) {
+            builder.append(";");
+            if (indent > 1) {
+                builder.append("\n\t\t");
+            }
+            builder.append(directives[i].getName()).append(":=");
+            String v = directives[i].getValue();
+            if (v.contains(",")) {
+                if (indent > 2 && v.length() > 20) {
+                    v = v.replace(",", ",\n\t\t\t");
+                }
+                builder.append("\"").append(v).append("\"");
+            } else {
+                builder.append(v);
+            }
+        }
+        for (int i = 0; attributes != null && i < attributes.length; i++) {
+            builder.append(";");
+            if (indent > 1) {
+                builder.append("\n\t\t");
+            }
+            builder.append(attributes[i].getName()).append("=");
+            String v = attributes[i].getValue();
+            if (v.contains(",")) {
+                if (indent > 2 && v.length() > 20) {
+                    v = v.replace(",", ",\n\t\t\t");
+                }
+                builder.append("\"").append(v).append("\"");
+            } else {
+                builder.append(v);
+            }
+        }
+    }
+
+
+    /*
+    private boolean checkBundle(String bundleName, String version) {
+        VersionRange vr = VersionRange.parseVersionRange(version);
+        Bundle[] bundles = bundleContext.getBundles();
+        for (int i = 0; (bundles != null) && (i < bundles.length); i++) {
+            String sym = bundles[i].getSymbolicName();
+            if ((sym != null) && sym.equals(bundleName)) {
+                if (vr.contains(bundles[i].getVersion())) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean checkPackage(String packageName, String version) {
+        VersionRange range = VersionRange.parseVersionRange(version);
+        Bundle[] bundles = bundleContext.getBundles();
+        for (int i = 0; (bundles != null) && (i < bundles.length); i++) {
+            BundleWiring wiring = bundles[i].adapt(BundleWiring.class);
+            List<BundleCapability> caps = wiring != null ? 
wiring.getCapabilities(BundleRevision.PACKAGE_NAMESPACE) : null;
+            if (caps != null) {
+                for (BundleCapability cap : caps) {
+                    String n = getAttribute(cap, 
BundleRevision.PACKAGE_NAMESPACE);
+                    String v = getAttribute(cap, Constants.VERSION_ATTRIBUTE);
+                    if (packageName.equals(n) && 
range.contains(VersionTable.getVersion(v))) {
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    private String getAttribute(BundleCapability cap, String name)  {
+        Object obj = cap.getAttributes().get(name);
+        return obj != null ? obj.toString() : null;
+    }
+    */
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/InstallAction.java
----------------------------------------------------------------------
diff --git 
a/subsystem/src/main/java/org/apache/karaf/subsystem/commands/InstallAction.java
 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/InstallAction.java
new file mode 100644
index 0000000..a086d2c
--- /dev/null
+++ 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/InstallAction.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.subsystem.commands;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "subsystem", name = "install", description = "Install a new 
subsystem")
+@Service
+public class InstallAction extends SubsystemSupport implements Action {
+
+    @Argument(name = "Subsystem to install the new subsystem into")
+    @Completion(SubsystemCompleter.class)
+    String id;
+
+    @Argument(name = "New subsystem url", index = 1)
+    String location;
+
+    @Override
+    public Object execute() throws Exception {
+        getSubsystem(id).install(location);
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/ListAction.java
----------------------------------------------------------------------
diff --git 
a/subsystem/src/main/java/org/apache/karaf/subsystem/commands/ListAction.java 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/ListAction.java
new file mode 100644
index 0000000..458ff5b
--- /dev/null
+++ 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/ListAction.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.subsystem.commands;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.support.table.ShellTable;
+import org.osgi.service.subsystem.Subsystem;
+
+@Command(scope = "subsystem", name = "list", description = "List all 
subsystems")
+@Service
+public class ListAction extends SubsystemSupport implements Action {
+
+    @Override
+    public Object execute() throws Exception {
+        ShellTable table = new ShellTable();
+        table.column("ID").alignRight();
+        table.column("SymbolicName");
+        table.column("Version");
+        table.column("State");
+        table.column("Parents");
+        table.column("Children");
+
+        for (Subsystem ss : getSubsystems()) {
+            table.addRow().addContent(
+                    ss.getSubsystemId(),
+                    ss.getSymbolicName(),
+                    ss.getVersion(),
+                    ss.getState().toString(),
+                    getSubsytemIds(ss.getParents()),
+                    getSubsytemIds(ss.getChildren())
+            );
+        }
+        table.print(System.out);
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/StartAction.java
----------------------------------------------------------------------
diff --git 
a/subsystem/src/main/java/org/apache/karaf/subsystem/commands/StartAction.java 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/StartAction.java
new file mode 100644
index 0000000..1ce126e
--- /dev/null
+++ 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/StartAction.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.subsystem.commands;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.osgi.service.subsystem.Subsystem;
+
+@Command(scope = "subsystem", name = "start", description = "Start the 
specified subsystems")
+@Service
+public class StartAction extends SubsystemSupport implements Action {
+
+    @Argument(description = "Subsystem names or ids")
+    @Completion(SubsystemCompleter.class)
+    String id;
+
+    @Override
+    public Object execute() throws Exception {
+        for (Subsystem ss : getSubsystems(id)) {
+            ss.start();
+        }
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/StopAction.java
----------------------------------------------------------------------
diff --git 
a/subsystem/src/main/java/org/apache/karaf/subsystem/commands/StopAction.java 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/StopAction.java
new file mode 100644
index 0000000..5b2255c
--- /dev/null
+++ 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/StopAction.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.subsystem.commands;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.osgi.service.subsystem.Subsystem;
+
+@Command(scope = "subsystem", name = "stop", description = "Stop the specified 
subsystems")
+@Service
+public class StopAction extends SubsystemSupport implements Action {
+
+    @Argument(description = "Subsystem names or ids")
+    @Completion(SubsystemCompleter.class)
+    String id;
+
+    @Override
+    public Object execute() throws Exception {
+        for (Subsystem ss : getSubsystems(id)) {
+            ss.stop();
+        }
+        return null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/SubsystemCompleter.java
----------------------------------------------------------------------
diff --git 
a/subsystem/src/main/java/org/apache/karaf/subsystem/commands/SubsystemCompleter.java
 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/SubsystemCompleter.java
new file mode 100644
index 0000000..025b9f4
--- /dev/null
+++ 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/SubsystemCompleter.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.subsystem.commands;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+import org.osgi.service.subsystem.Subsystem;
+
+@Service
+public class SubsystemCompleter extends SubsystemSupport implements Completer {
+
+    @Override
+    public int complete(Session session, CommandLine commandLine, List<String> 
candidates) {
+        List<String> strings = new ArrayList<String>();
+        for (Subsystem ss : getSubsystems()) {
+            strings.add(Long.toString(ss.getSubsystemId()));
+            strings.add(ss.getSymbolicName() + "/" + ss.getVersion());
+        }
+        return new StringsCompleter(strings).complete(session, commandLine, 
candidates);
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/SubsystemSupport.java
----------------------------------------------------------------------
diff --git 
a/subsystem/src/main/java/org/apache/karaf/subsystem/commands/SubsystemSupport.java
 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/SubsystemSupport.java
new file mode 100644
index 0000000..e91461e
--- /dev/null
+++ 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/SubsystemSupport.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.subsystem.commands;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.osgi.service.subsystem.Subsystem;
+import org.osgi.service.subsystem.SubsystemConstants;
+
+public abstract class SubsystemSupport {
+
+    @Reference
+    Subsystem subsystem;
+
+    protected Subsystem getRoot() {
+        Subsystem ss = subsystem;
+        while (!ss.getParents().isEmpty()) {
+            ss = ss.getParents().iterator().next();
+        }
+        return ss;
+    }
+
+    protected Subsystem getSubsystem(String id) {
+        List<Subsystem> subsystems = getSubsystems(id);
+        int nb = subsystems.size();
+        if (nb == 0) {
+            throw new IllegalArgumentException("No subsystem matching " + id);
+        } else if (nb > 1) {
+            throw new IllegalArgumentException("Multiple subsystems matching " 
+ id);
+        } else {
+            return subsystems.get(0);
+        }
+    }
+
+    protected List<Subsystem> getSubsystems(String id) {
+        // Null id
+        if (id == null || id.isEmpty()) {
+            return getSubsystems();
+        }
+        List<Subsystem> subsystems = new ArrayList<Subsystem>();
+        // Try with the id
+        Pattern pattern = Pattern.compile("^\\d+$");
+        Matcher matcher = pattern.matcher(id);
+        if (matcher.find()) {
+            long lid = Long.parseLong(id);
+            for (Subsystem ss : getSubsystems()) {
+                if (ss.getSubsystemId() == lid) {
+                    subsystems.add(ss);
+                }
+            }
+            return subsystems;
+        }
+        // Try with an id range
+        pattern = Pattern.compile("^(\\d+)-(\\d+)$");
+        matcher = pattern.matcher(id);
+        if (matcher.find()) {
+            int index = id.indexOf('-');
+            long startId = Long.parseLong(id.substring(0, index));
+            long endId = Long.parseLong(id.substring(index + 1));
+            for (Subsystem ss : getSubsystems()) {
+                if (startId <= ss.getSubsystemId() && ss.getSubsystemId() <= 
endId) {
+                    subsystems.add(ss);
+                }
+            }
+            return subsystems;
+        }
+        int index = id.indexOf('/');
+        Pattern p1, p2;
+        if (index < 0) {
+            p1 = Pattern.compile(id);
+            p2 = null;
+        } else {
+            p1 = Pattern.compile(id.substring(0, index));
+            p2 = Pattern.compile(id.substring(index + 1));
+        }
+        for (Subsystem ss : getSubsystems()) {
+            if (p1.matcher(ss.getSymbolicName()).find() &&
+                    (p2 == null || 
p2.matcher(ss.getVersion().toString()).find())) {
+                subsystems.add(ss);
+            }
+        }
+        return subsystems;
+    }
+
+    protected List<Long> getSubsytemIds(Collection<Subsystem> subsystems) {
+        List<Long> ids = new ArrayList<Long>();
+        for (Subsystem ss : subsystems) {
+            ids.add(ss.getSubsystemId());
+        }
+        return ids;
+    }
+
+    protected List<Subsystem> getSubsystems() {
+        Map<Long, Subsystem> subsystems = new TreeMap<Long, Subsystem>();
+        doGetSubsystems(subsystems, getRoot());
+        return new ArrayList<Subsystem>(subsystems.values());
+    }
+
+    private void doGetSubsystems(Map<Long, Subsystem> subsystems, Subsystem 
subsystem) {
+        if (subsystems.put(subsystem.getSubsystemId(), subsystem) == null) {
+            for (Subsystem child : subsystem.getChildren()) {
+                doGetSubsystems(subsystems, child);
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/084f579b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/UninstallAction.java
----------------------------------------------------------------------
diff --git 
a/subsystem/src/main/java/org/apache/karaf/subsystem/commands/UninstallAction.java
 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/UninstallAction.java
new file mode 100644
index 0000000..93a924e
--- /dev/null
+++ 
b/subsystem/src/main/java/org/apache/karaf/subsystem/commands/UninstallAction.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.subsystem.commands;
+
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.osgi.service.subsystem.Subsystem;
+
+@Command(scope = "subsystem", name = "uninstall", description = "Uninstall the 
specified subsystems")
+@Service
+public class UninstallAction extends SubsystemSupport implements Action {
+
+    @Argument(description = "Subsystem names or ids")
+    @Completion(SubsystemCompleter.class)
+    String id;
+
+    @Override
+    public Object execute() throws Exception {
+        for (Subsystem ss : getSubsystems(id)) {
+            ss.uninstall();
+        }
+        return null;
+    }
+
+}

Reply via email to