[KARAF-4768] Add bundle update support

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

Branch: refs/heads/master
Commit: 725746548061e0bf9f3407d97f5788639611db90
Parents: 8cabd83
Author: Jean-Baptiste Onofré <jbono...@apache.org>
Authored: Fri Oct 14 17:07:06 2016 +0200
Committer: Jean-Baptiste Onofré <jbono...@apache.org>
Committed: Fri Oct 14 17:07:06 2016 +0200

----------------------------------------------------------------------
 bundle/pom.xml                                  |   3 +-
 .../karaf/cellar/bundle/BundleEventHandler.java |   2 +
 .../apache/karaf/cellar/bundle/BundleState.java |   2 +
 .../karaf/cellar/bundle/BundleSupport.java      |  44 ++++++-
 .../bundle/management/CellarBundleMBean.java    |  19 +++
 .../internal/CellarBundleMBeanImpl.java         |  62 ++++++++++
 .../cellar/bundle/shell/StartBundleCommand.java |   2 +-
 .../bundle/shell/UninstallBundleCommand.java    |   2 +-
 .../bundle/shell/UpdateBundleCommand.java       | 118 +++++++++++++++++++
 9 files changed, 245 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/pom.xml
----------------------------------------------------------------------
diff --git a/bundle/pom.xml b/bundle/pom.xml
index 640700c..e2fc6eb 100644
--- a/bundle/pom.xml
+++ b/bundle/pom.xml
@@ -98,7 +98,8 @@
                         <Private-Package>
                             org.apache.karaf.cellar.bundle.management.internal,
                             org.apache.karaf.cellar.bundle.internal.osgi,
-                            
org.apache.karaf.util.tracker;-split-package:=merge-first
+                            
org.apache.karaf.util.tracker;-split-package:=merge-first,
+                            
org.apache.karaf.util.bundles;-split-package:=merge-first
                         </Private-Package>
                     </instructions>
                 </configuration>

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java
index 2da1cca..109cad3 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java
@@ -123,6 +123,8 @@ public class BundleEventHandler extends BundleSupport 
implements EventHandler<Cl
                     } else {
                         LOGGER.warn("CELLAR BUNDLE: unable to find bundle 
located {} on node", event.getLocation());
                     }
+                } else if (event.getType() == BundleState.UPDATE) {
+                    updateBundle(event.getSymbolicName(), event.getVersion(), 
event.getLocation());
                 }
             } else
                 LOGGER.trace("CELLAR BUNDLE: bundle {} is marked BLOCKED 
INBOUND for cluster group {}", event.getSymbolicName(), 
event.getSourceGroup().getName());

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleState.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleState.java 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleState.java
index df6154a..a9c1519 100644
--- a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleState.java
+++ b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleState.java
@@ -20,6 +20,8 @@ import java.io.Serializable;
  */
 public class BundleState implements Serializable {
 
+    public static final int UPDATE = 555;
+
     private static final long serialVersionUID = 5933673686648413918L;
 
     private long id;

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSupport.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSupport.java 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSupport.java
index fd3f81f..242dc04 100644
--- a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSupport.java
+++ b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSupport.java
@@ -17,10 +17,14 @@ import org.apache.karaf.cellar.core.CellarSupport;
 import org.apache.karaf.features.BundleInfo;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeaturesService;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-
+import org.apache.karaf.util.bundles.BundleUtils;
+import org.osgi.framework.*;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -115,19 +119,47 @@ public class BundleSupport extends CellarSupport {
      *
      * @param symbolicName the bundle symbolic name.
      * @param version the bundle version.
+     * @param location the bundle update location.
      * @throws BundleException in case of update failure.
      */
-    public void updateBundle(String symbolicName, String version) throws 
BundleException {
+    public void updateBundle(String symbolicName, String version, String 
location) throws BundleException {
         Bundle[] bundles = getBundleContext().getBundles();
         if (bundles != null) {
             for (Bundle bundle : bundles) {
                 if (bundle.getSymbolicName().equals(symbolicName) && 
bundle.getHeaders().get("Bundle-Version").toString().equals(version)) {
-                    bundle.update();
+                    if (location != null) {
+                        try {
+                            update(bundle, new URL(location));
+                        } catch (Exception e) {
+                            throw new BundleException("Can't update bundle", 
e);
+                        }
+                    } else {
+                        String loc = 
bundle.getHeaders().get(org.osgi.framework.Constants.BUNDLE_UPDATELOCATION);
+                        if (loc != null && !loc.equals(bundle.getLocation())) {
+                            try {
+                                update(bundle, new URL(loc));
+                            } catch (Exception e) {
+                                throw new BundleException("Can't update 
bundle", e);
+                            }
+                        } else {
+                            bundle.update();
+                        }
+                    }
                 }
             }
         }
     }
 
+    private void update(Bundle bundle, URL location) throws IOException, 
BundleException {
+        try (InputStream is = location.openStream()) {
+            File file = BundleUtils.fixBundleWithUpdateLocation(is, 
location.toString());
+            try (FileInputStream fis = new FileInputStream(file)) {
+                bundle.update(fis);
+            }
+            file.delete();
+        }
+    }
+
     /**
      * Get the list of features where the bundle is belonging.
      *

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
index 58d791d..5704902 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
@@ -67,6 +67,25 @@ public interface CellarBundleMBean {
     void stop(String group, String id) throws Exception;
 
     /**
+     * Update a bundle in a cluster group.
+     *
+     * @param group the cluster group name.
+     * @param id the bundle id.
+     * @throws Exception in case of update failure.
+     */
+    void update(String group, String id) throws Exception;
+
+    /**
+     * Update a bundle in a cluster group using a given location.
+     *
+     * @param group the cluster group name.
+     * @param id the bundle id.
+     * @param location the update bundle location.
+     * @throws Exception in case of update failure.
+     */
+    void update(String group, String id, String location) throws Exception;
+
+    /**
      * Updating blocking policy for a given bundle pattern.
      *
      * @param group the cluster group name where to apply the blocking policy.

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
index 5bae2b0..908bc08 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
@@ -333,6 +333,68 @@ public class CellarBundleMBeanImpl extends StandardMBean 
implements CellarBundle
     }
 
     @Override
+    public void update(String groupName, String id, String updateLocation) 
throws Exception {
+        // check if the cluster group exists
+        Group group = groupManager.findGroupByName(groupName);
+        if (group == null) {
+            throw new IllegalArgumentException("Cluster group " + groupName + 
" doesn't exist");
+        }
+
+        // check if the producer is ON
+        if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
+            throw new IllegalStateException("Cluster event producer is OFF for 
this node");
+        }
+
+        CellarSupport support = new CellarSupport();
+        support.setClusterManager(this.clusterManager);
+        support.setGroupManager(this.groupManager);
+        support.setConfigurationAdmin(this.configurationAdmin);
+
+        // update the cluster group
+        ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
+        
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+        try {
+            Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
+
+            List<String> bundles = selector(id, gatherBundles(groupName));
+
+            for (String bundle : bundles) {
+                BundleState state = clusterBundles.get(bundle);
+                if (state == null) {
+                    continue;
+                }
+                String location = state.getLocation();
+                if (updateLocation != null) {
+                    location = updateLocation;
+                }
+
+                // check if the bundle location is allowed outbound
+                if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
+                    continue;
+                }
+
+                // update the cluster state
+                state.setLocation(location);
+                clusterBundles.put(bundle, state);
+
+                // broadcast the cluster event
+                String[] split = bundle.split("/");
+                ClusterBundleEvent event = new ClusterBundleEvent(split[0], 
split[1], location, BundleState.UPDATE);
+                event.setSourceGroup(group);
+                event.setSourceNode(clusterManager.getNode());
+                eventProducer.produce(event);
+            }
+        } finally {
+            Thread.currentThread().setContextClassLoader(originalClassLoader);
+        }
+    }
+
+    @Override
+    public void update(String groupName, String id) throws Exception {
+        update(groupName, id, null);
+    }
+
+    @Override
     public void block(String groupName, String bundlePattern, boolean 
whitelist, boolean blacklist, boolean in, boolean out) throws Exception {
         List<String> patterns = new ArrayList<String>();
 

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
index 059da85..d53dc06 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
@@ -64,7 +64,7 @@ public class StartBundleCommand extends BundleCommandSupport {
             for (String bundle : bundles) {
                 BundleState state = clusterBundles.get(bundle);
                 if (state == null) {
-                    System.err.println("Bundle " + state + " not found in 
cluster group " + groupName);
+                    System.err.println("Bundle " + bundle + " not found in 
cluster group " + groupName);
                 }
                 String location = state.getLocation();
 

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
index 8584a39..c45eab6 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
@@ -30,7 +30,7 @@ import org.osgi.framework.Bundle;
 import java.util.List;
 import java.util.Map;
 
-@Command(scope = "cluster", name = "bundle-uninstall", description = 
"Uninstall a bundle from a cluster group")
+@Command(scope = "cluster", name = "bundle-uninstall", description = 
"Uninstall bundles from a cluster group")
 @Service
 public class UninstallBundleCommand extends BundleCommandSupport {
 

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/72574654/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UpdateBundleCommand.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UpdateBundleCommand.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UpdateBundleCommand.java
new file mode 100644
index 0000000..a86183d
--- /dev/null
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UpdateBundleCommand.java
@@ -0,0 +1,118 @@
+/*
+ * 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.cellar.bundle.shell;
+
+import org.apache.karaf.cellar.bundle.BundleState;
+import org.apache.karaf.cellar.bundle.ClusterBundleEvent;
+import org.apache.karaf.cellar.bundle.Constants;
+import org.apache.karaf.cellar.core.CellarSupport;
+import org.apache.karaf.cellar.core.Configurations;
+import org.apache.karaf.cellar.core.Group;
+import org.apache.karaf.cellar.core.control.SwitchStatus;
+import org.apache.karaf.cellar.core.event.EventProducer;
+import org.apache.karaf.cellar.core.event.EventType;
+import org.apache.karaf.shell.api.action.Argument;
+import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+
+import java.util.List;
+import java.util.Map;
+
+@Command(scope = "cluster", name = "bundle-update", description = "Update a 
bundle in a cluster group")
+@Service
+public class UpdateBundleCommand extends BundleCommandSupport {
+
+    @Argument(index = 2, name = "location", description = "The update bundle 
location", required = false, multiValued = false)
+    String updateLocation;
+
+    @Reference
+    private EventProducer eventProducer;
+
+    @Override
+    public Object doExecute() throws Exception {
+        // check if the group exists
+        Group group = groupManager.findGroupByName(groupName);
+        if (group == null) {
+            System.err.println("Cluster group " + groupName + " doesn't 
exist");
+            return null;
+        }
+
+        // check if the producer is ON
+        if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
+            System.err.println("Cluster event producer is OFF");
+            return null;
+        }
+
+        ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
+        
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+
+        try {
+            // get cluster bundles
+            Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
+
+            List<String> bundles = selector(gatherBundles(true));
+
+            if (bundles.size() != 1) {
+                System.err.println("Update requires an unique bundle to 
update");
+                return null;
+            }
+
+            String bundle = bundles.get(0);
+            BundleState state = clusterBundles.get(bundle);
+            if (state == null) {
+                System.err.println("Bundle " + bundle + " not found in cluster 
group " + groupName);
+                return null;
+            }
+
+            String location = state.getLocation();
+            if (updateLocation != null) {
+                location = updateLocation;
+            }
+
+            // check if the bundle is allowed
+            CellarSupport support = new CellarSupport();
+            support.setClusterManager(this.clusterManager);
+            support.setGroupManager(this.groupManager);
+            support.setConfigurationAdmin(this.configurationAdmin);
+            if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
+                System.err.println("Bundle location " + location + " is 
blocked outbound for cluster group " + groupName);
+            }
+
+            // update cluster state
+            state.setLocation(updateLocation);
+            clusterBundles.put(bundle, state);
+
+            // broadcast the cluster event
+            String[] split = bundle.split("/");
+            ClusterBundleEvent event = new ClusterBundleEvent(split[0], 
split[1], location, BundleState.UPDATE);
+            event.setSourceGroup(group);
+            event.setSourceNode(clusterManager.getNode());
+            eventProducer.produce(event);
+        } finally {
+            Thread.currentThread().setContextClassLoader(originalClassLoader);
+        }
+
+        return null;
+    }
+
+    public EventProducer getEventProducer() {
+        return eventProducer;
+    }
+
+    public void setEventProducer(EventProducer eventProducer) {
+        this.eventProducer = eventProducer;
+    }
+
+}

Reply via email to