changeset 1307:75fe7ce6dea2

PR1591: Thermostat should have a way to specify "any bundle version" Reviewed-by: vanaltj Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-November/008670.html
author Omair Majid <omajid@redhat.com>
date Thu, 07 Nov 2013 15:27:27 -0500
parents ac26a7ee871d
children 41126b42f1e3
files config/src/main/java/com/redhat/thermostat/shared/config/Configuration.java launcher/src/main/java/com/redhat/thermostat/launcher/BundleManager.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleLoader.java launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleManagerImpl.java launcher/src/test/java/com/redhat/thermostat/launcher/internal/BundleManagerImplTest.java main/src/main/java/com/redhat/thermostat/main/Thermostat.java main/src/main/java/com/redhat/thermostat/main/impl/FrameworkProvider.java
diffstat 7 files changed, 189 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/config/src/main/java/com/redhat/thermostat/shared/config/Configuration.java	Thu Nov 07 14:55:48 2013 -0500
+++ b/config/src/main/java/com/redhat/thermostat/shared/config/Configuration.java	Thu Nov 07 15:27:27 2013 -0500
@@ -89,6 +89,7 @@
     private final UserDirectories userDirectories;
 
     private boolean printOsgiInfo = false;
+    private boolean ignoreVersions = false;
 
     public Configuration() throws InvalidConfigurationException {
         // allow this to be specified also as a property, especially for
@@ -234,6 +235,14 @@
         printOsgiInfo = newValue;
     }
 
+    public void setIgnoreVersions(boolean ignore) {
+        this.ignoreVersions  = ignore;
+    }
+
+    public boolean getIgnoreVersions() {
+        return ignoreVersions;
+    }
+
     private interface UserDirectories {
 
         public File getSystemRoot();
@@ -351,4 +360,5 @@
             return cacheDir;
         }
     }
+
 }
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/BundleManager.java	Thu Nov 07 14:55:48 2013 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/BundleManager.java	Thu Nov 07 15:27:27 2013 -0500
@@ -58,6 +58,17 @@
     public abstract void setPrintOSGiInfo(boolean printOSGiInfo);
 
     /**
+     * Indicates that versions in thermostat-specific config files (including
+     * thermostat-plugin.xml files) should be ignored and the latest version
+     * used.
+     * <p>
+     * This does not change OSGi's requirements; if OSGi bundles need specific
+     * versions and the latest version is not within the asked range, things
+     * will break.
+     */
+    public abstract void setIgnoreVersions(boolean ignore);
+
+    /**
      * Load and start bundles using the metadata about a bundle.
      */
     public abstract void loadBundlesByName(List<BundleInformation> bundles) throws BundleException, IOException;
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleLoader.java	Thu Nov 07 14:55:48 2013 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleLoader.java	Thu Nov 07 15:27:27 2013 -0500
@@ -61,6 +61,16 @@
         this.printOSGiInfo = printOSGiInfo;
     }
 
+    /**
+     * Install and start bundles in one step (solving any interdependencies).
+     *
+     * @param framework the {@link Framework} to use for installing and starting
+     * the bundles.
+     * @param bundleLocations a {@link List} of {@link String}s where each
+     * {@code String} is a URL of the bundle to load
+     * @return a {@link List} of {@link Bundle}s that were started.
+     * @throws BundleException
+     */
     public List<Bundle> installAndStartBundles(Framework framework,
             List<String> bundleLocations) throws BundleException {
         List<Bundle> bundles = new ArrayList<>();
--- a/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleManagerImpl.java	Thu Nov 07 14:55:48 2013 -0500
+++ b/launcher/src/main/java/com/redhat/thermostat/launcher/internal/BundleManagerImpl.java	Thu Nov 07 15:27:27 2013 -0500
@@ -61,6 +61,7 @@
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
 import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.Version;
 import org.osgi.framework.launch.Framework;
 
 import com.redhat.thermostat.common.utils.LoggingUtils;
@@ -143,6 +144,14 @@
         }
     }
 
+    /** For TESTS only: explicitly specify known bundles */
+    void setKnownBundles(Map<BundleInformation,Path> knownData) {
+        known.clear();
+        for (Entry<BundleInformation, Path> entry : knownData.entrySet()) {
+            known.put(entry.getKey(), entry.getValue());
+        }
+    }
+
     @Override
     public void setPrintOSGiInfo(boolean printOSGiInfo) {
         configuration.setPrintOSGiInfo(printOSGiInfo);
@@ -150,10 +159,22 @@
     }
 
     @Override
+    public void setIgnoreVersions(boolean ignore) {
+        configuration.setIgnoreVersions(ignore);
+    }
+
+    @Override
     public void loadBundlesByName(List<BundleInformation> bundles) throws BundleException, IOException {
         List<String> paths = new ArrayList<>();
         for (BundleInformation info : bundles) {
-            Path bundlePath = known.get(info);
+            Path bundlePath = null;
+
+            if (configuration.getIgnoreVersions()) {
+                bundlePath = findLatestVersion(info.getName());
+            } else {
+                bundlePath = known.get(info);
+            }
+
             if (bundlePath == null) {
                 logger.warning("no known bundle matching " + info.toString());
                 continue;
@@ -163,6 +184,30 @@
         loadBundlesByPath(paths);
     }
 
+    private Path findLatestVersion(String bundleSymbolicName) {
+        BundleInformation bestBundleInformation = null;
+        Version bestVersion = null;
+
+        Path bundlePath = null;
+
+        for (Entry<BundleInformation, Path> entry: known.entrySet()) {
+            if (bundleSymbolicName.equals(entry.getKey().getName())) {
+                Version version = Version.parseVersion(entry.getKey().getVersion());
+                if (bestVersion == null || version.compareTo(bestVersion) > 0) {
+                    bestVersion = version;
+                    bestBundleInformation = entry.getKey();
+                }
+            }
+        }
+        if (bestBundleInformation != null) {
+            bundlePath = known.get(bestBundleInformation);
+        }
+
+        logger.fine("Best match for " + bundleSymbolicName + " is " + bestBundleInformation);
+
+        return bundlePath;
+    }
+
     /* package private for testing only */
     void loadBundlesByPath(List<String> requiredBundles) throws BundleException, IOException {
         Framework framework = getFramework(this.getClass());
--- a/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BundleManagerImplTest.java	Thu Nov 07 14:55:48 2013 -0500
+++ b/launcher/src/test/java/com/redhat/thermostat/launcher/internal/BundleManagerImplTest.java	Thu Nov 07 15:27:27 2013 -0500
@@ -40,19 +40,25 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 import static org.powermock.api.mockito.PowerMockito.mockStatic;
 import static org.powermock.api.mockito.PowerMockito.whenNew;
 
+import java.io.File;
 import java.io.IOException;
 import java.lang.reflect.Method;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.nio.file.Paths;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 import org.junit.After;
 import org.junit.Before;
@@ -65,6 +71,7 @@
 import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
+import com.redhat.thermostat.launcher.BundleInformation;
 import com.redhat.thermostat.shared.config.Configuration;
 
 @RunWith(PowerMockRunner.class)
@@ -141,8 +148,9 @@
             }
         });
     }
+
     @Test
-    public void testInstallAndStartBundles() throws Exception {
+    public void verifyInstallAndStartBundles() throws Exception {
         Bundle theBundle = b2;
         when(theContext.getBundles()).thenReturn(new Bundle[] {});
         when(theBundle.getBundleContext()).thenReturn(theContext);
@@ -157,6 +165,95 @@
     }
 
     @Test
+    public void verifyLoadBundleByNameAndVersionWithBundleNotFound() throws Exception {
+        Bundle theBundle = b2;
+        when(theContext.getBundles()).thenReturn(new Bundle[] {});
+        when(theBundle.getBundleContext()).thenReturn(theContext);
+
+        mockStatic(FrameworkUtil.class);
+
+        when(FrameworkUtil.getBundle(any(Class.class))).thenReturn(theBundle);
+
+        BundleManagerImpl registry = new BundleManagerImpl(conf);
+        Map<BundleInformation, Path> bundleToPath = new HashMap<>();
+
+        registry.setKnownBundles(bundleToPath);
+
+        registry.loadBundlesByName(Arrays.asList(new BundleInformation("foo", "1.0")));
+
+        verify(loader).installAndStartBundles(theFramework, Arrays.<String>asList());
+    }
+
+    @Test
+    public void verifyLoadBundleByNameAndVersion() throws Exception {
+
+        Bundle theBundle = b2;
+        when(theContext.getBundles()).thenReturn(new Bundle[] {});
+        when(theBundle.getBundleContext()).thenReturn(theContext);
+
+        mockStatic(FrameworkUtil.class);
+
+        when(FrameworkUtil.getBundle(any(Class.class))).thenReturn(theBundle);
+
+        BundleManagerImpl registry = new BundleManagerImpl(conf);
+        Map<BundleInformation, Path> bundleToPath = new HashMap<>();
+        bundleToPath.put(new BundleInformation("foo", "1.0"), Paths.get(jar1Name));
+        registry.setKnownBundles(bundleToPath);
+
+        registry.loadBundlesByName(Arrays.asList(new BundleInformation("foo", "1.0")));
+
+        verify(loader).installAndStartBundles(theFramework, Arrays.asList(new File(jar1Name).toURI().toURL().toString()));
+    }
+
+    @Test
+    public void verifyLoadBundleByNameAndLatestVersion() throws Exception {
+
+        Bundle theBundle = b2;
+        when(theContext.getBundles()).thenReturn(new Bundle[] {});
+        when(theBundle.getBundleContext()).thenReturn(theContext);
+
+        mockStatic(FrameworkUtil.class);
+
+        when(FrameworkUtil.getBundle(any(Class.class))).thenReturn(theBundle);
+
+        when(conf.getIgnoreVersions()).thenReturn(true);
+
+        BundleManagerImpl registry = new BundleManagerImpl(conf);
+        Map<BundleInformation, Path> bundleToPath = new HashMap<>();
+        bundleToPath.put(new BundleInformation("foo", "1.0"), Paths.get(jar1Name));
+        bundleToPath.put(new BundleInformation("foo", "2.0"), Paths.get(jar2Name));
+
+        registry.setKnownBundles(bundleToPath);
+
+        registry.loadBundlesByName(Arrays.asList(new BundleInformation("foo", "3.0")));
+
+        verify(loader).installAndStartBundles(theFramework, Arrays.asList(new File(jar2Name).toURI().toURL().toString()));
+    }
+
+    @Test
+    public void verifyLoadBundleByNameAndLatestVersionWithoutSupportForLatestVersion() throws Exception {
+
+        Bundle theBundle = b2;
+        when(theContext.getBundles()).thenReturn(new Bundle[] {});
+        when(theBundle.getBundleContext()).thenReturn(theContext);
+
+        mockStatic(FrameworkUtil.class);
+
+        when(FrameworkUtil.getBundle(any(Class.class))).thenReturn(theBundle);
+
+        BundleManagerImpl registry = new BundleManagerImpl(conf);
+        Map<BundleInformation, Path> bundleToPath = new HashMap<>();
+        bundleToPath.put(new BundleInformation("foo", "1.0"), Paths.get(jar1Name));
+        bundleToPath.put(new BundleInformation("foo", "2.0"), Paths.get(jar2Name));
+
+        registry.setKnownBundles(bundleToPath);
+
+        registry.loadBundlesByName(Arrays.asList(new BundleInformation("foo", "3.0")));
+
+        verify(loader).installAndStartBundles(theFramework, new ArrayList<String>());
+    }
+
+    @Test
     public void verifyAlreadyLoadedBundlesNotReloaded() throws Exception {
 
         Bundle theBundle = b2;
@@ -170,7 +267,7 @@
 
         BundleManagerImpl registry = new BundleManagerImpl(conf);
         registry.loadBundlesByPath(bundleLocs);
-        verify(loader).installAndStartBundles(any(Framework.class), eq(Arrays.asList(jar3Name)));
+        verify(loader).installAndStartBundles(theFramework, Arrays.asList(jar3Name));
     }
 
     @Test
--- a/main/src/main/java/com/redhat/thermostat/main/Thermostat.java	Thu Nov 07 14:55:48 2013 -0500
+++ b/main/src/main/java/com/redhat/thermostat/main/Thermostat.java	Thu Nov 07 15:27:27 2013 -0500
@@ -57,10 +57,14 @@
         Iterator<String> iter = toProcess.iterator();
         while (iter.hasNext()) {
             String arg = iter.next();
-            if (("--print-osgi-info").equals(arg)) {
+            if ("--print-osgi-info".equals(arg)) {
                 config.setPrintOSGiInfo(true);
                 iter.remove();
             }
+            if ("--ignore-bundle-versions".equals(arg)) {
+                config.setIgnoreVersions(true);
+                iter.remove();
+            }
         }
 
         FrameworkProvider frameworkProvider = new FrameworkProvider(config);
--- a/main/src/main/java/com/redhat/thermostat/main/impl/FrameworkProvider.java	Thu Nov 07 14:55:48 2013 -0500
+++ b/main/src/main/java/com/redhat/thermostat/main/impl/FrameworkProvider.java	Thu Nov 07 15:27:27 2013 -0500
@@ -72,12 +72,14 @@
 
     private Configuration configuration;
     private boolean printOSGiInfo;
+    private boolean ignoreVersions;
     // The framework cache location; Must not be shared between apps!
     private Path osgiCacheStorage;
 
     public FrameworkProvider(Configuration config) {
         this.configuration = config;
         printOSGiInfo = config.getPrintOSGiInfo();
+        ignoreVersions = config.getIgnoreVersions();
     }
 
     // This is our ticket into OSGi land. Unfortunately, we to use a bit of reflection here.
@@ -89,6 +91,7 @@
             prepareFramework(framework);
             loadBootstrapBundles(framework);
             setLoaderVerbosity(framework);
+            setIgnoreBundleVersions(framework);
             runLauncher(framework, args);
         } catch (InterruptedException | BundleException | IOException e) {
             throw new RuntimeException("Could not start framework.", e);
@@ -227,6 +230,11 @@
         callVoidReflectedMethod(loader, "setPrintOSGiInfo", printOSGiInfo);
     }
 
+    private void setIgnoreBundleVersions(Framework framework) throws InterruptedException {
+        Object loader = getService(framework, BundleManager.class.getName());
+        callVoidReflectedMethod(loader, "setIgnoreVersions", ignoreVersions);
+    }
+
     private void runLauncher(Framework framework, String[] args) throws InterruptedException {
         Object launcher = getService(framework, Launcher.class.getName());
         callVoidReflectedMethod(launcher, "run", args, false);