[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; + } + +}