karaf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From gno...@apache.org
Subject [44/59] [abbrv] [KARAF-2852] Merge obr/core and obr/command
Date Thu, 10 Apr 2014 14:16:06 GMT
http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/core/src/main/java/org/apache/karaf/obr/core/internal/ObrMBeanImpl.java
----------------------------------------------------------------------
diff --git a/obr/core/src/main/java/org/apache/karaf/obr/core/internal/ObrMBeanImpl.java b/obr/core/src/main/java/org/apache/karaf/obr/core/internal/ObrMBeanImpl.java
deleted file mode 100644
index c182165..0000000
--- a/obr/core/src/main/java/org/apache/karaf/obr/core/internal/ObrMBeanImpl.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.obr.core.internal;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import javax.management.MBeanException;
-import javax.management.NotCompliantMBeanException;
-import javax.management.StandardMBean;
-import javax.management.openmbean.CompositeData;
-import javax.management.openmbean.CompositeDataSupport;
-import javax.management.openmbean.CompositeType;
-import javax.management.openmbean.OpenType;
-import javax.management.openmbean.SimpleType;
-import javax.management.openmbean.TabularData;
-import javax.management.openmbean.TabularDataSupport;
-import javax.management.openmbean.TabularType;
-
-import org.apache.felix.bundlerepository.Repository;
-import org.apache.felix.bundlerepository.RepositoryAdmin;
-import org.apache.felix.bundlerepository.Resolver;
-import org.apache.felix.bundlerepository.Resource;
-import org.apache.karaf.obr.core.ObrMBean;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.Version;
-
-/**
- * Implementation of the OBR MBean.
- */
-public class ObrMBeanImpl extends StandardMBean implements ObrMBean {
-
-    private static final char VERSION_DELIM = ',';
-
-    private BundleContext bundleContext;
-    private RepositoryAdmin repositoryAdmin;
-
-    public ObrMBeanImpl(BundleContext bundleContext, RepositoryAdmin repositoryAdmin) throws NotCompliantMBeanException {
-        super(ObrMBean.class);
-        this.bundleContext = bundleContext;
-        this.repositoryAdmin = repositoryAdmin;
-    }
-
-    public List<String> getUrls() {
-        Repository[] repositories = repositoryAdmin.listRepositories();
-        List<String> urls = new ArrayList<String>();
-        for (int i = 0; i < repositories.length; i++) {
-            urls.add(repositories[i].getURI());
-        }
-        return urls;
-    }
-
-    public TabularData getBundles() throws MBeanException {
-        try {
-            CompositeType bundleType = new CompositeType("OBR Resource", "Bundle available in the OBR",
-                    new String[]{"presentationname", "symbolicname", "version"},
-                    new String[]{"Presentation Name", "Symbolic Name", "Version"},
-                    new OpenType[]{SimpleType.STRING, SimpleType.STRING, SimpleType.STRING});
-            TabularType tableType = new TabularType("OBR Resources", "Table of all resources/bundles available in the OBR",
-                    bundleType, new String[]{"symbolicname", "version"});
-            TabularData table = new TabularDataSupport(tableType);
-
-            Resource[] resources = repositoryAdmin.discoverResources("(|(presentationname=*)(symbolicname=*))");
-            for (int i = 0; i < resources.length; i++) {
-                try {
-                    CompositeData data = new CompositeDataSupport(bundleType,
-                            new String[]{"presentationname", "symbolicname", "version"},
-                            new Object[]{resources[i].getPresentationName(), resources[i].getSymbolicName(), resources[i].getVersion().toString()});
-                    table.put(data);
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
-            }
-
-            return table;
-        } catch (Exception e) {
-            throw new MBeanException(null, e.getMessage());
-        }
-    }
-
-    public void addUrl(String url) throws MBeanException {
-        try {
-            repositoryAdmin.addRepository(url);
-        } catch (Exception e) {
-            throw new MBeanException(null, e.getMessage());
-        }
-    }
-
-    public void removeUrl(String url) {
-        repositoryAdmin.removeRepository(url);
-    }
-
-    public void refreshUrl(String url) throws MBeanException {
-        try {
-            repositoryAdmin.addRepository(url);
-        } catch (Exception e) {
-            throw new MBeanException(null, e.getMessage());
-        }
-    }
-
-    public void deployBundle(String bundle) throws MBeanException {
-        try {
-            deployBundle(bundle, false, false);
-        } catch (Exception e) {
-            throw new MBeanException(null, e.getMessage());
-        }
-    }
-
-    public void deployBundle(String bundle, boolean start, boolean deployOptional) throws MBeanException {
-        try {
-            Resolver resolver = repositoryAdmin.resolver();
-            String[] target = getTarget(bundle);
-            Resource resource = selectNewestVersion(searchRepository(repositoryAdmin, target[0], target[1]));
-            if (resource == null) {
-                throw new IllegalArgumentException("Unknown bundle " + target[0]);
-            }
-            resolver.add(resource);
-            if ((resolver.getAddedResources() != null) &&
-                    (resolver.getAddedResources().length > 0)) {
-                if (resolver.resolve(deployOptional ? 0 : Resolver.NO_OPTIONAL_RESOURCES)) {
-                    try {
-                        resolver.deploy(start ? Resolver.START : 0);
-                    } catch (IllegalStateException ex) {
-                        throw new IllegalStateException("Can't deploy using OBR", ex);
-                    }
-                }
-            }
-        } catch (Exception e) {
-            throw new MBeanException(null, e.getMessage());
-        }
-    }
-
-    private Resource[] searchRepository(RepositoryAdmin admin, String targetId, String targetVersion) throws InvalidSyntaxException {
-        // Try to see if the targetId is a bundle ID.
-        try {
-            Bundle bundle = bundleContext.getBundle(Long.parseLong(targetId));
-            targetId = bundle.getSymbolicName();
-        } catch (NumberFormatException ex) {
-            // It was not a number, so ignore.
-        }
-
-        // The targetId may be a bundle name or a bundle symbolic name,
-        // so create the appropriate LDAP query.
-        StringBuffer sb = new StringBuffer("(|(presentationname=");
-        sb.append(targetId);
-        sb.append(")(symbolicname=");
-        sb.append(targetId);
-        sb.append("))");
-        if (targetVersion != null) {
-            sb.insert(0, "(&");
-            sb.append("(version=");
-            sb.append(targetVersion);
-            sb.append("))");
-        }
-        return admin.discoverResources(sb.toString());
-    }
-
-    private Resource selectNewestVersion(Resource[] resources) {
-        int idx = -1;
-        Version v = null;
-        for (int i = 0; (resources != null) && (i < resources.length); i++) {
-            if (i == 0) {
-                idx = 0;
-                v = resources[i].getVersion();
-            } else {
-                Version vtmp = resources[i].getVersion();
-                if (vtmp.compareTo(v) > 0) {
-                    idx = i;
-                    v = vtmp;
-                }
-            }
-        }
-        return (idx < 0) ? null : resources[idx];
-    }
-
-    private String[] getTarget(String bundle) {
-        String[] target;
-        int idx = bundle.indexOf(VERSION_DELIM);
-        if (idx > 0) {
-            target = new String[]{bundle.substring(0, idx), bundle.substring(idx + 1)};
-        } else {
-            target = new String[]{bundle, null};
-        }
-        return target;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
----------------------------------------------------------------------
diff --git a/obr/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/obr/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
deleted file mode 100644
index 4fbe9fb..0000000
--- a/obr/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-   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.
--->
-<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
-    xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
-    xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">
-
-    <ext:property-placeholder />
-
-    <reference id="repositoryAdmin" interface="org.apache.felix.bundlerepository.RepositoryAdmin"/>
-
-    <bean id="obrMBean" class="org.apache.karaf.obr.core.internal.ObrMBeanImpl">
-        <argument ref="blueprintBundleContext"/>
-        <argument ref="repositoryAdmin"/>
-    </bean>
-
-    <service ref="obrMBean" auto-export="interfaces">
-        <service-properties>
-            <entry key="jmx.objectname" value="org.apache.karaf:type=obr,name=${karaf.name}"/>
-        </service-properties>
-    </service>
-
-</blueprint>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/core/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/obr/core/src/main/resources/OSGI-INF/bundle.info b/obr/core/src/main/resources/OSGI-INF/bundle.info
deleted file mode 100644
index e14433c..0000000
--- a/obr/core/src/main/resources/OSGI-INF/bundle.info
+++ /dev/null
@@ -1,25 +0,0 @@
-h1. Synopsis
-
-${project.name}
-
-${project.description}
-
-Maven URL:
-[mvn:${project.groupId}/${project.artifactId}/${project.version}]
-
-h1. Description
-
-The obr mbean management bundle exposes an OBR MBean that can be used with any JMX client (for instance JConsole).
-
-The OBR MBean allows quite the same actions that can be performed using obr:* commands:
-  * listUrls()
-  * addUrl(url)
-  * removeUrl(url)
-  * refreshUrl(url)
-  * list()
-  * deploy(bundle)
-  * deploy(bundle, start)
-
-h1. See also
-
-  * Monitoring and Administration using JMX - section of the Karaf User Guide

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/pom.xml
----------------------------------------------------------------------
diff --git a/obr/pom.xml b/obr/pom.xml
index 28fbcdd..e0498c7 100644
--- a/obr/pom.xml
+++ b/obr/pom.xml
@@ -29,13 +29,67 @@
     </parent>
 
     <groupId>org.apache.karaf.obr</groupId>
-    <artifactId>obr</artifactId>
-    <packaging>pom</packaging>
-    <name>Apache Karaf :: OBR</name>
-
-    <modules>
-        <module>core</module>
-        <module>command</module>
-    </modules>
+    <artifactId>org.apache.karaf.obr.core</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: OBR :: Core</name>
+    <description>OBR core services and MBeans</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.felix</groupId>
+            <artifactId>org.apache.felix.bundlerepository</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.core</artifactId>
+            <optional>true</optional>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>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>
+                        <Export-Package>
+                            org.apache.karaf.obr.core
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.karaf.obr.command,
+                            org.apache.karaf.obr.core.internal,
+                        </Private-Package>
+                        <Karaf-Commands>*</Karaf-Commands>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
 
 </project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/AddUrlCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/AddUrlCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/AddUrlCommand.java
new file mode 100644
index 0000000..5aaf13a
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/AddUrlCommand.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import java.util.List;
+
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "obr", name = "url-add", description = "Adds a list of repository URLs to the OBR service.")
+@Service
+public class AddUrlCommand extends ObrCommandSupport {
+
+    @Argument(index = 0, name = "urls", description = "Repository URLs to add to the OBR service separated by whitespaces", required = true, multiValued = true)
+    List<String> urls;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        for (String url : urls) {
+            admin.addRepository(url);
+        }
+        persistRepositoryList(admin);
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/DeployCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/DeployCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/DeployCommand.java
new file mode 100644
index 0000000..a852c70
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/DeployCommand.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.karaf.obr.command;
+
+import java.util.List;
+
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "obr", name = "deploy", description = "Deploys a list of bundles using OBR service.")
+@Service
+public class DeployCommand extends ObrCommandSupport {
+
+    @Argument(index = 0, name = "bundles", description = "List of bundle names to deploy (separated by whitespaces)", required = true, multiValued = true)
+    protected List<String> bundles;
+
+    @Option(name = "-s", aliases = { "--start" }, description = "Start the deployed bundles", required = false, multiValued = false)
+    protected boolean start = false;
+
+    @Option(name = "-d", aliases = { "--deployOptional" }, description = "Deploy optional bundles", required = false, multiValued = false)
+    protected boolean deployOptional = false;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        doDeploy(admin, bundles, start, deployOptional);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/FindCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/FindCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/FindCommand.java
new file mode 100644
index 0000000..fad967b
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/FindCommand.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import org.apache.felix.bundlerepository.Capability;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Requirement;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import java.io.PrintStream;
+import java.lang.reflect.Array;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+@Command(scope = "obr", name = "find", description = "Find OBR bundles for a given filter.")
+@Service
+public class FindCommand extends ObrCommandSupport {
+
+    @Argument(index = 0, name = "requirements", description = "Requirement", required = true, multiValued = true)
+    List<String> requirements;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        Resource[] resources = admin.discoverResources(parseRequirements(admin, requirements));
+        if (resources == null)
+        {
+            System.err.println("No matching resources.");
+        }
+        else
+        {
+            for (int resIdx = 0; resIdx < resources.length; resIdx++)
+            {
+                if (resIdx > 0)
+                {
+                    System.out.println("");
+                }
+                printResource(System.out, resources[resIdx]);
+            }
+        }
+    }
+
+    private void printResource(PrintStream out, Resource resource)
+    {
+        String name = resource.getPresentationName();
+        if (name == null) {
+            name = resource.getSymbolicName();
+        }
+
+        printUnderline(out, name.length());
+        out.println(name);
+        printUnderline(out, name    .length());
+
+        Map map = resource.getProperties();
+        for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) iter.next();
+            if (entry.getValue().getClass().isArray())
+            {
+                out.println(entry.getKey() + ":");
+                for (int j = 0; j < Array.getLength(entry.getValue()); j++)
+                {
+                    out.println("   " + Array.get(entry.getValue(), j));
+                }
+            }
+            else
+            {
+                out.println(entry.getKey() + ": " + entry.getValue());
+            }
+        }
+
+        Requirement[] reqs = resource.getRequirements();
+        if ((reqs != null) && (reqs.length > 0))
+        {
+            boolean hdr = false;
+            for (int i = 0; i < reqs.length; i++)
+            {
+                if (!reqs[i].isOptional())
+                {
+                    if (!hdr)
+                    {
+                        hdr = true;
+                        out.println("Requirements:");
+                    }
+                    out.println("   " + reqs[i].getName() + ":" + reqs[i].getFilter());
+                }
+            }
+            hdr = false;
+            for (int i = 0; i < reqs.length; i++)
+            {
+                if (reqs[i].isOptional())
+                {
+                    if (!hdr)
+                    {
+                        hdr = true;
+                        out.println("Optional Requirements:");
+                    }
+                    out.println("   " + reqs[i].getName() + ":" + reqs[i].getFilter());
+                }
+            }
+        }
+
+        Capability[] caps = resource.getCapabilities();
+        if ((caps != null) && (caps.length > 0))
+        {
+            out.println("Capabilities:");
+            for (int i = 0; i < caps.length; i++)
+            {
+                out.println("   " + caps[i].getName() + ":" + caps[i].getPropertiesAsMap());
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/InfoCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/InfoCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/InfoCommand.java
new file mode 100644
index 0000000..bacc61b
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/InfoCommand.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.obr.command;
+
+import java.io.PrintStream;
+import java.lang.reflect.Array;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.bundlerepository.Capability;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Requirement;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "obr", name = "info", description = "Prints information about OBR bundles.")
+@Service
+public class InfoCommand extends ObrCommandSupport {
+
+    @Argument(index = 0, name = "bundles", description = "Specify bundles to query for information (separated by whitespaces)", required = true, multiValued = true)
+    List<String> bundles;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        for (String bundle : bundles) {
+            String[] target = getTarget(bundle);
+            Resource[] resources = searchRepository(admin, target[0], target[1]);
+            if (resources == null)
+            {
+                System.err.println("Unknown bundle and/or version: "
+                    + target[0]);
+            }
+            else
+            {
+                for (int resIdx = 0; resIdx < resources.length; resIdx++)
+                {
+                    if (resIdx > 0)
+                    {
+                        System.out.println("");
+                    }
+                    printResource(System.out, resources[resIdx]);
+                }
+            }
+        }
+    }
+
+    private void printResource(PrintStream out, Resource resource)
+    {
+        printUnderline(out, resource.getPresentationName().length());
+        out.println(resource.getPresentationName());
+        printUnderline(out, resource.getPresentationName().length());
+
+        Map map = resource.getProperties();
+        for (Iterator iter = map.entrySet().iterator(); iter.hasNext(); )
+        {
+            Map.Entry entry = (Map.Entry) iter.next();
+            if (entry.getValue().getClass().isArray())
+            {
+                out.println(entry.getKey() + ":");
+                for (int j = 0; j < Array.getLength(entry.getValue()); j++)
+                {
+                    out.println("   " + Array.get(entry.getValue(), j));
+                }
+            }
+            else
+            {
+                out.println(entry.getKey() + ": " + entry.getValue());
+            }
+        }
+
+        Requirement[] reqs = resource.getRequirements();
+        if ((reqs != null) && (reqs.length > 0))
+        {
+            out.println("Requires:");
+            for (int i = 0; i < reqs.length; i++)
+            {
+                out.println("   " + reqs[i].getName() + ":" + reqs[i].getFilter());
+            }
+        }
+
+        Capability[] caps = resource.getCapabilities();
+        if ((caps != null) && (caps.length > 0))
+        {
+            out.println("Capabilities:");
+            for (int i = 0; i < caps.length; i++)
+            {
+                out.println("   " + caps[i].getName() + ":" + caps[i].getPropertiesAsMap());
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/ListCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/ListCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/ListCommand.java
new file mode 100644
index 0000000..45db82f
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/ListCommand.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import java.util.List;
+
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.support.table.ShellTable;
+
+@Command(scope = "obr", name = "list", description = "Lists OBR bundles, optionally providing the given packages.")
+@Service
+public class ListCommand extends ObrCommandSupport {
+
+    @Argument(index = 0, name = "packages", description = "A list of packages separated by whitespaces.", required = false, multiValued = true)
+    List<String> packages;
+
+    @Option(name = "--no-format", description = "Disable table rendered output", required = false, multiValued = false)
+    boolean noFormat;
+
+    @Override
+    void doExecute(RepositoryAdmin admin) throws Exception {
+        StringBuilder substr = new StringBuilder();
+
+        if (packages != null) {
+            for (String packageName : packages) {
+                substr.append(" ");
+                substr.append(packageName);
+            }
+        }
+
+        String query;
+        if ((substr == null) || (substr.length() == 0)) {
+            query = "(|(presentationname=*)(symbolicname=*))";
+        } else {
+            query = "(|(presentationname=*" + substr + "*)(symbolicname=*" + substr + "*))";
+        }
+        Resource[] resources = admin.discoverResources(query);
+        int maxPName = 4;
+        int maxSName = 13;
+        int maxVersion = 7;
+        for (Resource resource : resources) {
+            maxPName = Math.max(maxPName, emptyIfNull(resource.getPresentationName()).length());
+            maxSName = Math.max(maxSName, emptyIfNull(resource.getSymbolicName()).length());
+            maxVersion = Math.max(maxVersion, emptyIfNull(resource.getVersion()).length());
+        }
+
+        ShellTable table = new ShellTable();
+        table.column("Name");
+        table.column("Symbolic Name");
+        table.column("Version");
+        table.emptyTableText("No matching bundles");
+
+        for (Resource resource : resources) {
+            table.addRow().addContent(emptyIfNull(resource.getPresentationName()),
+                    emptyIfNull(resource.getSymbolicName()),
+                    emptyIfNull(resource.getVersion()));
+        }
+
+        table.print(System.out, !noFormat);
+    }
+
+    private String emptyIfNull(Object st) {
+        return st == null ? "" : st.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/ListUrlCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/ListUrlCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/ListUrlCommand.java
new file mode 100644
index 0000000..1c07236
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/ListUrlCommand.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import org.apache.felix.bundlerepository.Repository;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.support.table.ShellTable;
+
+@Command(scope = "obr", name = "url-list", description = "Displays the repository URLs currently associated with the OBR service.")
+@Service
+public class ListUrlCommand extends ObrCommandSupport {
+
+    @Option(name = "--no-format", description = "Disable table rendered output", required = false, multiValued = false)
+    boolean noFormat;
+
+    protected void doExecute(RepositoryAdmin admin) {
+
+        ShellTable table = new ShellTable();
+        table.column("Index");
+        table.column("OBR URL");
+        table.emptyTableText("No OBR repository URL");
+
+        Repository[] repos = admin.listRepositories();
+        if (repos != null) {
+            for (int i = 0; i < repos.length; i++) {
+                table.addRow().addContent(i, repos[i].getURI());
+            }
+        }
+
+        table.print(System.out, !noFormat);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/ObrCommandSupport.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/ObrCommandSupport.java b/obr/src/main/java/org/apache/karaf/obr/command/ObrCommandSupport.java
new file mode 100644
index 0000000..5f038b4
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/ObrCommandSupport.java
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.obr.command;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.util.List;
+
+import org.apache.felix.bundlerepository.Reason;
+import org.apache.felix.bundlerepository.Repository;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Requirement;
+import org.apache.felix.bundlerepository.Resolver;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.karaf.shell.api.action.Action;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.Version;
+
+public abstract class ObrCommandSupport implements Action {
+
+    protected static final char VERSION_DELIM = ',';
+
+    @Reference
+    private RepositoryAdmin repositoryAdmin;
+
+    @Reference
+    BundleContext bundleContext;
+
+    public void setRepositoryAdmin(RepositoryAdmin repositoryAdmin) {
+        this.repositoryAdmin = repositoryAdmin;
+    }
+
+    @Override
+    public Object execute() throws Exception {
+        doExecute(repositoryAdmin);
+        return null;
+    }
+
+    abstract void doExecute(RepositoryAdmin admin) throws Exception;
+
+    protected Resource[] searchRepository(RepositoryAdmin admin, String targetId, String targetVersion) throws InvalidSyntaxException {
+        // Try to see if the targetId is a bundle ID.
+        try {
+            Bundle bundle = bundleContext.getBundle(Long.parseLong(targetId));
+            targetId = bundle.getSymbolicName();
+        } catch (NumberFormatException ex) {
+            // It was not a number, so ignore.
+        }
+
+        // The targetId may be a bundle name or a bundle symbolic name,
+        // so create the appropriate LDAP query.
+        StringBuffer sb = new StringBuffer("(|(presentationname=");
+        sb.append(targetId);
+        sb.append(")(symbolicname=");
+        sb.append(targetId);
+        sb.append("))");
+        if (targetVersion != null) {
+            sb.insert(0, "(&");
+            sb.append("(version=");
+            sb.append(targetVersion);
+            sb.append("))");
+        }
+        return admin.discoverResources(sb.toString());
+    }
+
+    public Resource selectNewestVersion(Resource[] resources) {
+        int idx = -1;
+        Version v = null;
+        for (int i = 0; (resources != null) && (i < resources.length); i++) {
+            if (i == 0) {
+                idx = 0;
+                v = resources[i].getVersion();
+            } else {
+                Version vtmp = resources[i].getVersion();
+                if (vtmp.compareTo(v) > 0) {
+                    idx = i;
+                    v = vtmp;
+                }
+            }
+        }
+        return (idx < 0) ? null : resources[idx];
+    }
+
+    protected String[] getTarget(String bundle) {
+        String[] target;
+        int idx = bundle.indexOf(VERSION_DELIM);
+        if (idx > 0) {
+            target = new String[]{bundle.substring(0, idx), bundle.substring(idx + 1)};
+        } else {
+            target = new String[]{bundle, null};
+        }
+        return target;
+    }
+
+    protected void printUnderline(PrintStream out, int length) {
+        for (int i = 0; i < length; i++) {
+            out.print('-');
+        }
+        out.println("");
+    }
+
+    protected void doDeploy(RepositoryAdmin admin, List<String> bundles, boolean start, boolean deployOptional) throws Exception {
+        Resolver resolver = admin.resolver();
+        for (String bundle : bundles) {
+            String[] target = getTarget(bundle);
+            Resource resource = selectNewestVersion(searchRepository(admin, target[0], target[1]));
+            if (resource != null) {
+                resolver.add(resource);
+            } else {
+                System.err.println("Unknown bundle - " + target[0]);
+            }
+        }
+        if ((resolver.getAddedResources() != null) &&
+                (resolver.getAddedResources().length > 0)) {
+            if (resolver.resolve(deployOptional ? 0 : Resolver.NO_OPTIONAL_RESOURCES)) {
+                System.out.println("Target resource(s):");
+                printUnderline(System.out, 19);
+                Resource[] resources = resolver.getAddedResources();
+                for (int resIdx = 0; (resources != null) && (resIdx < resources.length); resIdx++) {
+                    System.out.println("   " + resources[resIdx].getPresentationName()
+                            + " (" + resources[resIdx].getVersion() + ")");
+                }
+                resources = resolver.getRequiredResources();
+                if ((resources != null) && (resources.length > 0)) {
+                    System.out.println("\nRequired resource(s):");
+                    printUnderline(System.out, 21);
+                    for (int resIdx = 0; resIdx < resources.length; resIdx++) {
+                        System.out.println("   " + resources[resIdx].getPresentationName()
+                                + " (" + resources[resIdx].getVersion() + ")");
+                    }
+                }
+                if (deployOptional) {
+                    resources = resolver.getOptionalResources();
+                    if ((resources != null) && (resources.length > 0)) {
+                        System.out.println("\nOptional resource(s):");
+                        printUnderline(System.out, 21);
+                        for (int resIdx = 0; resIdx < resources.length; resIdx++) {
+                            System.out.println("   " + resources[resIdx].getPresentationName() + " (" + resources[resIdx].getVersion() + ")");
+                        }
+                    }
+                }
+
+                try {
+                    System.out.print("\nDeploying...");
+                    resolver.deploy(start ? Resolver.START : 0);
+                    System.out.println("done.");
+                } catch (IllegalStateException ex) {
+                    System.err.println(ex);
+                }
+            } else {
+                Reason[] reqs = resolver.getUnsatisfiedRequirements();
+                if ((reqs != null) && (reqs.length > 0)) {
+                    System.out.println("Unsatisfied requirement(s):");
+                    printUnderline(System.out, 27);
+                    for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++) {
+                        System.out.println("   " + reqs[reqIdx].getRequirement().getFilter());
+                        System.out.println("      " + reqs[reqIdx].getResource().getPresentationName());
+                    }
+                } else {
+                    System.out.println("Could not resolve targets.");
+                }
+            }
+        }
+
+    }
+
+
+    protected Requirement parseRequirement(RepositoryAdmin admin, String req) throws InvalidSyntaxException {
+        int p = req.indexOf(':');
+        String name;
+        String filter;
+        if (p > 0) {
+            name = req.substring(0, p);
+            filter = req.substring(p + 1);
+        } else {
+            if (req.contains("package")) {
+                name = "package";
+            } else if (req.contains("service")) {
+                name = "service";
+            } else {
+                name = "bundle";
+            }
+            filter = req;
+        }
+        if (!filter.startsWith("(")) {
+            filter = "(" + filter + ")";
+        }
+        return admin.getHelper().requirement(name, filter);
+    }
+
+    protected Requirement[] parseRequirements(RepositoryAdmin admin, List<String> requirements) throws InvalidSyntaxException {
+        Requirement[] reqs = new Requirement[requirements.size()];
+        for (int i = 0; i < reqs.length; i++) {
+            reqs[i] = parseRequirement(admin, requirements.get(i));
+        }
+        return reqs;
+    }
+
+    public static final String REPOSITORY_URL_PROP = "obr.repository.url";
+
+    protected void persistRepositoryList(RepositoryAdmin admin) {
+        try {
+            StringBuilder sb = new StringBuilder();
+            for (Repository repo : admin.listRepositories()) {
+                if (sb.length() > 0) {
+                    sb.append(" ");
+                }
+                sb.append(repo.getURI());
+            }
+            File etc = new File(System.getProperty("karaf.etc"));
+            File sys = new File(etc, "config.properties");
+            File sysTmp = new File(etc, "config.properties.tmp");
+
+            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(sysTmp)));
+            boolean modified = false;
+            try {
+                if (sys.exists()) {
+                    BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(sys)));
+                    try {
+                        String line = reader.readLine();
+                        while (line != null) {
+                            if (line.matches("obr\\.repository\\.url[:= ].*")) {
+                                modified = true;
+                                line = "obr.repository.url = " + sb.toString();
+                            }
+                            writer.write(line);
+                            writer.newLine();
+                            line = reader.readLine();
+                        }
+                    } finally {
+                        reader.close();
+                    }
+                }
+                if (!modified) {
+                    writer.newLine();
+                    writer.write("# ");
+                    writer.newLine();
+                    writer.write("# OBR Repository list");
+                    writer.newLine();
+                    writer.write("# ");
+                    writer.newLine();
+                    writer.write("obr.repository.url = " + sb.toString());
+                    writer.newLine();
+                    writer.newLine();
+                }
+            } finally {
+                writer.close();
+            }
+
+            sys.delete();
+            sysTmp.renameTo(sys);
+
+        } catch (Exception e) {
+            System.err.println("Error while persisting repository list");
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/RefreshUrlCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/RefreshUrlCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/RefreshUrlCommand.java
new file mode 100644
index 0000000..20a65ed
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/RefreshUrlCommand.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import java.util.List;
+
+import org.apache.felix.bundlerepository.Repository;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "obr", name = "url-refresh", description = "Reloads the repositories to obtain a fresh list of bundles.")
+@Service
+public class RefreshUrlCommand extends ObrCommandSupport {
+
+    @Option(name = "-i", aliases = { "--index" }, description = "Use index to identify URL", required = false, multiValued = false)
+    boolean useIndex;
+
+    @Argument(index = 0, name = "ids", description = "Repository URLs (or indexes if you use -i) to refresh (leave empty for all)", required = false, multiValued = true)
+    List<String> ids;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+		if (ids != null && !ids.isEmpty()) {
+			for (String id : ids) {
+                if (useIndex) {
+                    Repository[] repos = admin.listRepositories();
+                    int index = Integer.parseInt(id);
+                    if (index >= 0 && index < repos.length) {
+                        admin.addRepository(repos[index].getURI());
+                    } else {
+                        System.err.println("Invalid index");
+                    }
+                } else {
+				    admin.addRepository(id);
+                }
+			}
+		} else {
+			Repository[] repos = admin.listRepositories();
+			if ((repos != null) && (repos.length > 0)) {
+				for (int i = 0; i < repos.length; i++) {
+					admin.addRepository(repos[i].getURI());
+				}
+			}
+		}
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/RemoveUrlCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/RemoveUrlCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/RemoveUrlCommand.java
new file mode 100644
index 0000000..2d41519
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/RemoveUrlCommand.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import java.util.List;
+
+import org.apache.felix.bundlerepository.Repository;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "obr", name = "url-remove", description = "Removes a list of repository URLs from the OBR service.")
+@Service
+public class RemoveUrlCommand extends ObrCommandSupport {
+
+    @Option(name = "-i", aliases = { "--index" }, description = "Use index to identify URL", required = false, multiValued = false)
+    boolean useIndex;
+
+    @Argument(index = 0, name = "ids", description = "Repository URLs (or indexes if you use -i) to remove from OBR service", required = true, multiValued = true)
+    List<String> ids;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        for (String id : ids) {
+            if (useIndex) {
+                Repository[] repos = admin.listRepositories();
+                int index = Integer.parseInt(id);
+                if (index >= 0 && index < repos.length) {
+                    admin.removeRepository(repos[index].getURI());
+                } else {
+                    System.err.println("Invalid index");
+                }
+            } else {
+                admin.removeRepository(id);
+            }
+        }
+        persistRepositoryList(admin);
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/ResolveCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/ResolveCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/ResolveCommand.java
new file mode 100644
index 0000000..ab1bd04
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/ResolveCommand.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.felix.bundlerepository.Reason;
+import org.apache.felix.bundlerepository.Repository;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Requirement;
+import org.apache.felix.bundlerepository.Resolver;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "obr", name = "resolve", description = "Shows the resolution output for a given set of requirements.")
+@Service
+public class ResolveCommand extends ObrCommandSupport {
+
+    @Option(name = "-w", aliases = "--why", description = "Display the reason of the inclusion of the resource")
+    boolean why;
+
+    @Option(name = "-l", aliases = "--no-local", description = "Ignore local resources during resolution")
+    boolean noLocal;
+
+    @Option(name = "--no-remote", description = "Ignore remote resources during resolution")
+    boolean noRemote;
+
+    @Option(name = "--deploy", description = "Deploy the selected bundles")
+    boolean deploy;
+
+    @Option(name = "--start", description = "Deploy and start the selected bundles")
+    boolean start;
+
+    @Option(name = "--optional", description = "Resolve optional dependencies")
+    boolean optional;
+
+    @Argument(index = 0, name = "requirements", description = "Requirements", required = true, multiValued = true)
+    List<String> requirements;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        List<Repository> repositories = new ArrayList<Repository>();
+        repositories.add(admin.getSystemRepository());
+        if (!noLocal) {
+            repositories.add(admin.getLocalRepository());
+        }
+        if (!noRemote) {
+            repositories.addAll(Arrays.asList(admin.listRepositories()));
+        }
+        Resolver resolver = admin.resolver(repositories.toArray(new Repository[repositories.size()]));
+        for (Requirement requirement : parseRequirements(admin, requirements)) {
+            resolver.add(requirement);
+        }
+        if (resolver.resolve(optional ? 0 : Resolver.NO_OPTIONAL_RESOURCES)) {
+            Resource[] resources;
+            resources = resolver.getRequiredResources();
+            if ((resources != null) && (resources.length > 0)) {
+                System.out.println("Required resource(s):");
+                printUnderline(System.out, 21);
+                for (int resIdx = 0; resIdx < resources.length; resIdx++) {
+                    System.out.println("   " + resources[resIdx].getPresentationName() + " (" + resources[resIdx].getVersion() + ")");
+                    if (why) {
+                        Reason[] req = resolver.getReason(resources[resIdx]);
+                        for (int reqIdx = 0; req != null && reqIdx < req.length; reqIdx++) {
+                            if (!req[reqIdx].getRequirement().isOptional()) {
+                                Resource r = req[reqIdx].getResource();
+                                if (r != null) {
+                                    System.out.println("      - " + r.getPresentationName() + " / " + req[reqIdx].getRequirement().getName() + ":" + req[reqIdx].getRequirement().getFilter());
+                                } else {
+                                    System.out.println("      - " + req[reqIdx].getRequirement().getName() + ":" + req[reqIdx].getRequirement().getFilter());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            resources = resolver.getOptionalResources();
+            if ((resources != null) && (resources.length > 0)) {
+                System.out.println();
+                System.out.println("Optional resource(s):");
+                printUnderline(System.out, 21);
+                for (int resIdx = 0; resIdx < resources.length; resIdx++) {
+                    System.out.println("   " + resources[resIdx].getPresentationName()
+                        + " (" + resources[resIdx].getVersion() + ")");
+                    if (why) {
+                        Reason[] req = resolver.getReason(resources[resIdx]);
+                        for (int reqIdx = 0; req != null && reqIdx < req.length; reqIdx++) {
+                            if (!req[reqIdx].getRequirement().isOptional()) {
+                                Resource r = req[reqIdx].getResource();
+                                if (r != null) {
+                                    System.out.println("      - " + r.getPresentationName() + " / " + req[reqIdx].getRequirement().getName() + ":" + req[reqIdx].getRequirement().getFilter());
+                                } else {
+                                    System.out.println("      - " + req[reqIdx].getRequirement().getName() + ":" + req[reqIdx].getRequirement().getFilter());
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            if (deploy || start) {
+                try
+                {
+                    System.out.print("\nDeploying...");
+                    resolver.deploy(start ? Resolver.START : 0);
+                    System.out.println("done.");
+                }
+                catch (IllegalStateException ex)
+                {
+                    System.err.println(ex);
+                }
+            }
+        } else {
+            Reason[] reqs = resolver.getUnsatisfiedRequirements();
+            if ((reqs != null) && (reqs.length > 0)) {
+                System.out.println("Unsatisfied requirement(s):");
+                printUnderline(System.out, 27);
+                for (int reqIdx = 0; reqIdx < reqs.length; reqIdx++) {
+                    System.out.println("   " + reqs[reqIdx].getRequirement().getName() + ":" + reqs[reqIdx].getRequirement().getFilter());
+                    System.out.println("      " +reqs[reqIdx].getResource().getPresentationName());
+                }
+            } else {
+                System.out.println("Could not resolve targets.");
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/SourceCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/SourceCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/SourceCommand.java
new file mode 100644
index 0000000..161ba82
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/SourceCommand.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import java.net.URI;
+import java.util.List;
+
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.karaf.obr.command.util.FileUtil;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+@Command(scope = "obr", name = "source", description = "Downloads the sources for an OBR bundle.")
+@Service
+public class SourceCommand extends ObrCommandSupport {
+
+    @Option(name = "-x", aliases = {}, description = "Extract the archive", required = false, multiValued = false)
+    boolean extract;
+
+    @Argument(index = 0, name = "folder", description = "Local folder for storing sources", required = true, multiValued = false)
+    String localDir;
+
+    @Argument(index = 1, name = "bundles", description = "List of bundles to download the sources for", required = true, multiValued = true)
+    List<String> bundles;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        for (String bundle : bundles) {
+            String[] target = getTarget(bundle);
+            Resource resource = selectNewestVersion(searchRepository(admin, target[0], target[1]));
+            if (resource == null)
+            {
+                System.err.println("Unknown bundle and/or version: " + target[0]);
+            }
+            else
+            {
+                URI srcURL = (URI) resource.getProperties().get(Resource.SOURCE_URI);
+                if (srcURL != null)
+                {
+                    FileUtil.downloadSource(System.out, System.err, srcURL.toURL(), localDir, extract);
+                }
+                else
+                {
+                    System.err.println("Missing source URL: " + target[0]);
+                }
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/StartCommand.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/StartCommand.java b/obr/src/main/java/org/apache/karaf/obr/command/StartCommand.java
new file mode 100644
index 0000000..0465bae
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/StartCommand.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.command;
+
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Option;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import java.util.List;
+
+@Command(scope = "obr", name = "start", description = "Deploys and starts a list of bundles using OBR.")
+@Service
+public class StartCommand extends ObrCommandSupport {
+
+    @Argument(index = 0, name = "bundles", description = "List of bundles to deploy (separated by whitespaces)", required = true, multiValued = true)
+    protected List<String> bundles;
+
+    @Option(name = "-d", aliases = { "--deployOptional" }, description = "Deploy optional bundles", required = false, multiValued = false)
+    protected boolean deployOptional = false;
+
+    protected void doExecute(RepositoryAdmin admin) throws Exception {
+        doDeploy(admin, bundles, true, deployOptional);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/command/util/FileUtil.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/command/util/FileUtil.java b/obr/src/main/java/org/apache/karaf/obr/command/util/FileUtil.java
new file mode 100644
index 0000000..26aa0bd
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/command/util/FileUtil.java
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.karaf.obr.command.util;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+public class FileUtil
+{
+    public static void downloadSource(
+        PrintStream out, PrintStream err,
+        URL srcURL, String dirStr, boolean extract)
+    {
+        // Get the file name from the URL.
+        String fileName = (srcURL.getFile().lastIndexOf('/') > 0)
+            ? srcURL.getFile().substring(srcURL.getFile().lastIndexOf('/') + 1)
+            : srcURL.getFile();
+
+        try
+        {
+            out.println("Connecting...");
+
+            File dir = new File(dirStr);
+            if (!dir.exists())
+            {
+                err.println("Destination directory does not exist.");
+            }
+            File file = new File(dir, fileName);
+
+            OutputStream os = new FileOutputStream(file);
+            URLConnection conn = srcURL.openConnection();
+            int total = conn.getContentLength();
+            InputStream is = conn.getInputStream();
+
+            if (total > 0)
+            {
+                out.println("Downloading " + fileName
+                    + " ( " + total + " bytes ).");
+            }
+            else
+            {
+                out.println("Downloading " + fileName + ".");
+            }
+            byte[] buffer = new byte[4096];
+            int count = 0;
+            for (int len = is.read(buffer); len > 0; len = is.read(buffer))
+            {
+                count += len;
+                os.write(buffer, 0, len);
+            }
+
+            os.close();
+            is.close();
+
+            if (extract)
+            {
+                is = new FileInputStream(file);
+                JarInputStream jis = new JarInputStream(is);
+                out.println("Extracting...");
+                unjar(jis, dir);
+                jis.close();
+                file.delete();
+            }
+        }
+        catch (Exception ex)
+        {
+            err.println(ex);
+        }
+    }
+
+    public static void unjar(JarInputStream jis, File dir)
+        throws IOException
+    {
+        // Reusable buffer.
+        byte[] buffer = new byte[4096];
+
+        // Loop through JAR entries.
+        for (JarEntry je = jis.getNextJarEntry();
+             je != null;
+             je = jis.getNextJarEntry())
+        {
+            if (je.getName().startsWith("/"))
+            {
+                throw new IOException("JAR resource cannot contain absolute paths.");
+            }
+
+            File target = new File(dir, je.getName());
+
+            // Check to see if the JAR entry is a directory.
+            if (je.isDirectory())
+            {
+                if (!target.exists())
+                {
+                    if (!target.mkdirs())
+                    {
+                        throw new IOException("Unable to create target directory: "
+                            + target);
+                    }
+                }
+                // Just continue since directories do not have content to copy.
+                continue;
+            }
+
+            int lastIndex = je.getName().lastIndexOf('/');
+            String name = (lastIndex >= 0) ?
+                je.getName().substring(lastIndex + 1) : je.getName();
+            String destination = (lastIndex >= 0) ?
+                je.getName().substring(0, lastIndex) : "";
+
+            // JAR files use '/', so convert it to platform separator.
+            destination = destination.replace('/', File.separatorChar);
+            copy(jis, dir, name, destination, buffer);
+        }
+    }
+
+    public static void copy(
+        InputStream is, File dir, String destName, String destDir, byte[] buffer)
+        throws IOException
+    {
+        if (destDir == null)
+        {
+            destDir = "";
+        }
+
+        // Make sure the target directory exists and
+        // that is actually a directory.
+        File targetDir = new File(dir, destDir);
+        if (!targetDir.exists())
+        {
+            if (!targetDir.mkdirs())
+            {
+                throw new IOException("Unable to create target directory: "
+                    + targetDir);
+            }
+        }
+        else if (!targetDir.isDirectory())
+        {
+            throw new IOException("Target is not a directory: "
+                + targetDir);
+        }
+
+        BufferedOutputStream bos = new BufferedOutputStream(
+            new FileOutputStream(new File(targetDir, destName)));
+        int count = 0;
+        while ((count = is.read(buffer)) > 0)
+        {
+            bos.write(buffer, 0, count);
+        }
+        bos.close();
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/core/ObrMBean.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/core/ObrMBean.java b/obr/src/main/java/org/apache/karaf/obr/core/ObrMBean.java
new file mode 100644
index 0000000..bf5ebcb
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/core/ObrMBean.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.core;
+
+import javax.management.MBeanException;
+import javax.management.openmbean.TabularData;
+import java.util.List;
+
+/**
+ * OBR MBean.
+ */
+public interface ObrMBean {
+
+    List<String> getUrls();
+    TabularData getBundles() throws MBeanException;
+
+    void addUrl(String url) throws MBeanException;
+    void removeUrl(String url);
+    void refreshUrl(String url) throws MBeanException;
+
+    void deployBundle(String bundle) throws MBeanException;
+    void deployBundle(String bundle, boolean start, boolean deployOptional) throws MBeanException;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/java/org/apache/karaf/obr/core/internal/ObrMBeanImpl.java
----------------------------------------------------------------------
diff --git a/obr/src/main/java/org/apache/karaf/obr/core/internal/ObrMBeanImpl.java b/obr/src/main/java/org/apache/karaf/obr/core/internal/ObrMBeanImpl.java
new file mode 100644
index 0000000..c182165
--- /dev/null
+++ b/obr/src/main/java/org/apache/karaf/obr/core/internal/ObrMBeanImpl.java
@@ -0,0 +1,203 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.karaf.obr.core.internal;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.management.MBeanException;
+import javax.management.NotCompliantMBeanException;
+import javax.management.StandardMBean;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.apache.felix.bundlerepository.Repository;
+import org.apache.felix.bundlerepository.RepositoryAdmin;
+import org.apache.felix.bundlerepository.Resolver;
+import org.apache.felix.bundlerepository.Resource;
+import org.apache.karaf.obr.core.ObrMBean;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.Version;
+
+/**
+ * Implementation of the OBR MBean.
+ */
+public class ObrMBeanImpl extends StandardMBean implements ObrMBean {
+
+    private static final char VERSION_DELIM = ',';
+
+    private BundleContext bundleContext;
+    private RepositoryAdmin repositoryAdmin;
+
+    public ObrMBeanImpl(BundleContext bundleContext, RepositoryAdmin repositoryAdmin) throws NotCompliantMBeanException {
+        super(ObrMBean.class);
+        this.bundleContext = bundleContext;
+        this.repositoryAdmin = repositoryAdmin;
+    }
+
+    public List<String> getUrls() {
+        Repository[] repositories = repositoryAdmin.listRepositories();
+        List<String> urls = new ArrayList<String>();
+        for (int i = 0; i < repositories.length; i++) {
+            urls.add(repositories[i].getURI());
+        }
+        return urls;
+    }
+
+    public TabularData getBundles() throws MBeanException {
+        try {
+            CompositeType bundleType = new CompositeType("OBR Resource", "Bundle available in the OBR",
+                    new String[]{"presentationname", "symbolicname", "version"},
+                    new String[]{"Presentation Name", "Symbolic Name", "Version"},
+                    new OpenType[]{SimpleType.STRING, SimpleType.STRING, SimpleType.STRING});
+            TabularType tableType = new TabularType("OBR Resources", "Table of all resources/bundles available in the OBR",
+                    bundleType, new String[]{"symbolicname", "version"});
+            TabularData table = new TabularDataSupport(tableType);
+
+            Resource[] resources = repositoryAdmin.discoverResources("(|(presentationname=*)(symbolicname=*))");
+            for (int i = 0; i < resources.length; i++) {
+                try {
+                    CompositeData data = new CompositeDataSupport(bundleType,
+                            new String[]{"presentationname", "symbolicname", "version"},
+                            new Object[]{resources[i].getPresentationName(), resources[i].getSymbolicName(), resources[i].getVersion().toString()});
+                    table.put(data);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+
+            return table;
+        } catch (Exception e) {
+            throw new MBeanException(null, e.getMessage());
+        }
+    }
+
+    public void addUrl(String url) throws MBeanException {
+        try {
+            repositoryAdmin.addRepository(url);
+        } catch (Exception e) {
+            throw new MBeanException(null, e.getMessage());
+        }
+    }
+
+    public void removeUrl(String url) {
+        repositoryAdmin.removeRepository(url);
+    }
+
+    public void refreshUrl(String url) throws MBeanException {
+        try {
+            repositoryAdmin.addRepository(url);
+        } catch (Exception e) {
+            throw new MBeanException(null, e.getMessage());
+        }
+    }
+
+    public void deployBundle(String bundle) throws MBeanException {
+        try {
+            deployBundle(bundle, false, false);
+        } catch (Exception e) {
+            throw new MBeanException(null, e.getMessage());
+        }
+    }
+
+    public void deployBundle(String bundle, boolean start, boolean deployOptional) throws MBeanException {
+        try {
+            Resolver resolver = repositoryAdmin.resolver();
+            String[] target = getTarget(bundle);
+            Resource resource = selectNewestVersion(searchRepository(repositoryAdmin, target[0], target[1]));
+            if (resource == null) {
+                throw new IllegalArgumentException("Unknown bundle " + target[0]);
+            }
+            resolver.add(resource);
+            if ((resolver.getAddedResources() != null) &&
+                    (resolver.getAddedResources().length > 0)) {
+                if (resolver.resolve(deployOptional ? 0 : Resolver.NO_OPTIONAL_RESOURCES)) {
+                    try {
+                        resolver.deploy(start ? Resolver.START : 0);
+                    } catch (IllegalStateException ex) {
+                        throw new IllegalStateException("Can't deploy using OBR", ex);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            throw new MBeanException(null, e.getMessage());
+        }
+    }
+
+    private Resource[] searchRepository(RepositoryAdmin admin, String targetId, String targetVersion) throws InvalidSyntaxException {
+        // Try to see if the targetId is a bundle ID.
+        try {
+            Bundle bundle = bundleContext.getBundle(Long.parseLong(targetId));
+            targetId = bundle.getSymbolicName();
+        } catch (NumberFormatException ex) {
+            // It was not a number, so ignore.
+        }
+
+        // The targetId may be a bundle name or a bundle symbolic name,
+        // so create the appropriate LDAP query.
+        StringBuffer sb = new StringBuffer("(|(presentationname=");
+        sb.append(targetId);
+        sb.append(")(symbolicname=");
+        sb.append(targetId);
+        sb.append("))");
+        if (targetVersion != null) {
+            sb.insert(0, "(&");
+            sb.append("(version=");
+            sb.append(targetVersion);
+            sb.append("))");
+        }
+        return admin.discoverResources(sb.toString());
+    }
+
+    private Resource selectNewestVersion(Resource[] resources) {
+        int idx = -1;
+        Version v = null;
+        for (int i = 0; (resources != null) && (i < resources.length); i++) {
+            if (i == 0) {
+                idx = 0;
+                v = resources[i].getVersion();
+            } else {
+                Version vtmp = resources[i].getVersion();
+                if (vtmp.compareTo(v) > 0) {
+                    idx = i;
+                    v = vtmp;
+                }
+            }
+        }
+        return (idx < 0) ? null : resources[idx];
+    }
+
+    private String[] getTarget(String bundle) {
+        String[] target;
+        int idx = bundle.indexOf(VERSION_DELIM);
+        if (idx > 0) {
+            target = new String[]{bundle.substring(0, idx), bundle.substring(idx + 1)};
+        } else {
+            target = new String[]{bundle, null};
+        }
+        return target;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/resources/OSGI-INF/blueprint/blueprint.xml
----------------------------------------------------------------------
diff --git a/obr/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/obr/src/main/resources/OSGI-INF/blueprint/blueprint.xml
new file mode 100644
index 0000000..4fbe9fb
--- /dev/null
+++ b/obr/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   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.
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+    xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
+    xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">
+
+    <ext:property-placeholder />
+
+    <reference id="repositoryAdmin" interface="org.apache.felix.bundlerepository.RepositoryAdmin"/>
+
+    <bean id="obrMBean" class="org.apache.karaf.obr.core.internal.ObrMBeanImpl">
+        <argument ref="blueprintBundleContext"/>
+        <argument ref="repositoryAdmin"/>
+    </bean>
+
+    <service ref="obrMBean" auto-export="interfaces">
+        <service-properties>
+            <entry key="jmx.objectname" value="org.apache.karaf:type=obr,name=${karaf.name}"/>
+        </service-properties>
+    </service>
+
+</blueprint>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/karaf/blob/b0a20b83/obr/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/obr/src/main/resources/OSGI-INF/bundle.info b/obr/src/main/resources/OSGI-INF/bundle.info
new file mode 100644
index 0000000..e14433c
--- /dev/null
+++ b/obr/src/main/resources/OSGI-INF/bundle.info
@@ -0,0 +1,25 @@
+h1. Synopsis
+
+${project.name}
+
+${project.description}
+
+Maven URL:
+[mvn:${project.groupId}/${project.artifactId}/${project.version}]
+
+h1. Description
+
+The obr mbean management bundle exposes an OBR MBean that can be used with any JMX client (for instance JConsole).
+
+The OBR MBean allows quite the same actions that can be performed using obr:* commands:
+  * listUrls()
+  * addUrl(url)
+  * removeUrl(url)
+  * refreshUrl(url)
+  * list()
+  * deploy(bundle)
+  * deploy(bundle, start)
+
+h1. See also
+
+  * Monitoring and Administration using JMX - section of the Karaf User Guide


Mime
View raw message