You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2010/10/21 15:42:25 UTC

svn commit: r1025996 - in /karaf/trunk: assembly/src/main/filtered-resources/ features/core/src/main/java/org/apache/karaf/features/internal/ features/core/src/test/java/org/apache/karaf/features/

Author: gnodet
Date: Thu Oct 21 13:42:25 2010
New Revision: 1025996

URL: http://svn.apache.org/viewvc?rev=1025996&view=rev
Log:
[KARAF-251] Allow the use of version ranges on dependant features

Modified:
    karaf/trunk/assembly/src/main/filtered-resources/features.xml
    karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
    karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java

Modified: karaf/trunk/assembly/src/main/filtered-resources/features.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/assembly/src/main/filtered-resources/features.xml?rev=1025996&r1=1025995&r2=1025996&view=diff
==============================================================================
--- karaf/trunk/assembly/src/main/filtered-resources/features.xml (original)
+++ karaf/trunk/assembly/src/main/filtered-resources/features.xml Thu Oct 21 13:42:25 2010
@@ -17,8 +17,16 @@
       limitations under the License.
 -->
 <features name="karaf-${project.version}">
+    <feature name="spring" version="${spring2.version}">
+        <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/${aopalliance.bundle.version}</bundle>
+        <bundle>mvn:org.springframework/spring-core/${spring2.version}</bundle>
+        <bundle>mvn:org.springframework/spring-beans/${spring2.version}</bundle>
+        <bundle>mvn:org.springframework/spring-aop/${spring2.version}</bundle>
+        <bundle>mvn:org.springframework/spring-context/${spring2.version}</bundle>
+        <bundle>mvn:org.springframework/spring-context-support/${spring2.version}</bundle>
+    </feature>
     <feature name="spring" version="${spring.version}">
-        <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/${aopalliance.bundle.version}</bundle>
+        <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.aopalliance/${aopalliance.bundle.version}</bundle>
         <bundle>mvn:org.springframework/spring-core/${spring.version}</bundle>
         <bundle>mvn:org.springframework/spring-asm/${spring.version}</bundle>
         <bundle>mvn:org.springframework/spring-expression/${spring.version}</bundle>
@@ -28,7 +36,7 @@
         <bundle>mvn:org.springframework/spring-context-support/${spring.version}</bundle>
     </feature>
     <feature name="spring-dm" version="${spring.osgi.version}">
-        <feature version="${spring.version}">spring</feature>
+        <feature version="[2.5.6,4)">spring</feature>
         <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.cglib/${cglib.bundle.version}</bundle>
         <bundle>mvn:org.springframework.osgi/spring-osgi-io/${spring.osgi.version}</bundle>
         <bundle>mvn:org.springframework.osgi/spring-osgi-core/${spring.osgi.version}</bundle>
@@ -91,8 +99,8 @@
             sshRealm=karaf
             hostKey=${karaf.base}/etc/host.key
         </config>
-        <bundle>mvn:org.apache.mina/mina-core/${mina.version}</bundle>
-        <bundle>mvn:org.apache.sshd/sshd-core/${sshd.version}</bundle>
+        <bundle dependency='true'>mvn:org.apache.mina/mina-core/${mina.version}</bundle>
+        <bundle dependency='true'>mvn:org.apache.sshd/sshd-core/${sshd.version}</bundle>
         <bundle>mvn:org.apache.karaf.shell/org.apache.karaf.shell.ssh/${project.version}</bundle>
     </feature>
     <feature name="management" version="${project.version}">
@@ -100,10 +108,10 @@
         <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx/${aries.jmx.version}</bundle>
         <bundle>mvn:org.apache.aries.jmx/org.apache.aries.jmx.blueprint/${aries.jmx.version}</bundle>
     </feature>
-    <feature name="jasypt-encryption" version="${project.version}">
-        <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.commons-codec/${commons-codec.bundle.version}</bundle>
-        <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.commons-lang/${commons-lang.bundle.version}</bundle>
-        <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jasypt/${jasypt.bundle.version}</bundle>
+    <feature name="jasypt-encryption" version="${project.version}" resolver="(obr)">
+        <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.commons-codec/${commons-codec.bundle.version}</bundle>
+        <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.commons-lang/${commons-lang.bundle.version}</bundle>
+        <bundle dependency='true'>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.jasypt/${jasypt.bundle.version}</bundle>
         <bundle>mvn:org.apache.karaf.jaas/org.apache.karaf.jaas.jasypt/${project.version}</bundle>
     </feature>
 </features>

Modified: karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
URL: http://svn.apache.org/viewvc/karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java?rev=1025996&r1=1025995&r2=1025996&view=diff
==============================================================================
--- karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java (original)
+++ karaf/trunk/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java Thu Oct 21 13:42:25 2010
@@ -33,6 +33,7 @@ import java.util.jar.Manifest;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.apache.felix.utils.version.VersionTable;
 import org.apache.karaf.features.BundleInfo;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeatureEvent;
@@ -378,12 +379,37 @@ public class FeaturesServiceImpl impleme
 
     protected void doInstallFeature(InstallationState state, Feature feature) throws Exception {
         for (Feature dependency : feature.getDependencies()) {
-            Feature f = getFeature(dependency.getName(), dependency.getVersion());
-            if (f == null) {
+            VersionRange range = FeatureImpl.DEFAULT_VERSION.equals(dependency.getVersion())
+                        ? VersionRange.ANY_VERSION : new VersionRange(dependency.getVersion(), true, true);
+            Feature fi = null;
+            for (Feature f : installed.keySet()) {
+                if (f.getName().equals(dependency.getName())) {
+                    Version v = VersionTable.getVersion(f.getVersion());
+                    if (range.contains(v)) {
+                        if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) {
+                            fi = f;
+                        }
+                    }
+                }
+            }
+            if (fi == null) {
+                Map<String, Feature> avail = getFeatures().get(dependency.getName());
+                if (avail != null) {
+                    for (Feature f : avail.values()) {
+                        Version v = VersionTable.getVersion(f.getVersion());
+                        if (range.contains(v)) {
+                            if (fi == null || VersionTable.getVersion(fi.getVersion()).compareTo(v) < 0) {
+                                fi = f;
+                            }
+                        }
+                    }
+                }
+            }
+            if (fi == null) {
                 throw new Exception("No feature named '" + dependency.getName()
                         + "' with version '" + dependency.getVersion() + "' available");
             }
-        	doInstallFeature(state, f);
+            doInstallFeature(state, fi);
         }
         for (String config : feature.getConfigurations().keySet()) {
             Dictionary<String,String> props = new Hashtable<String, String>(feature.getConfigurations().get(config));

Modified: karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java
URL: http://svn.apache.org/viewvc/karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java?rev=1025996&r1=1025995&r2=1025996&view=diff
==============================================================================
--- karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java (original)
+++ karaf/trunk/features/core/src/test/java/org/apache/karaf/features/FeaturesServiceTest.java Thu Oct 21 13:42:25 2010
@@ -43,6 +43,7 @@ import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.service.log.LogService;
 import org.osgi.service.packageadmin.PackageAdmin;
+import org.slf4j.Logger;
 
 import static org.easymock.EasyMock.*;
 
@@ -338,9 +339,9 @@ public class FeaturesServiceTest extends
     }    
 
 
-    // Tests install of a Repository that includes a feature 
-    // with a feature dependency  
-    // The dependant feature is in the same repository 
+    // Tests install of a Repository that includes a feature
+    // with a feature dependency
+    // The dependant feature is in the same repository
     // Tests uninstall of features
     public void testInstallFeatureWithDependantFeatures() throws Exception {
 
@@ -362,7 +363,7 @@ public class FeaturesServiceTest extends
         URI uri = tmp.toURI();
 
         BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
-        Bundle installedBundle = EasyMock.createMock(Bundle.class);        
+        Bundle installedBundle = EasyMock.createMock(Bundle.class);
 
         // Installs feature f1 with dependency on f2
         // so will install f2 first
@@ -375,7 +376,7 @@ public class FeaturesServiceTest extends
         expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
         expect(installedBundle.getHeaders()).andReturn(new Hashtable());
         installedBundle.start();
-        
+
         // Then installs f1
         expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
         expect(bundleContext.installBundle(isA(String.class),
@@ -386,7 +387,7 @@ public class FeaturesServiceTest extends
         expect(bundleContext.getBundle(1234L)).andReturn(installedBundle);
         expect(installedBundle.getHeaders()).andReturn(new Hashtable()).anyTimes();
         installedBundle.start();
-        
+
         // uninstalls first feature name = f1, version = 0.1
         expect(bundleContext.getBundle(1234)).andReturn(installedBundle);
         installedBundle.uninstall();
@@ -401,14 +402,201 @@ public class FeaturesServiceTest extends
 
         FeaturesServiceImpl svc = new FeaturesServiceImpl();
         svc.setBundleContext(bundleContext);
-        svc.addRepository(uri);    
+        svc.addRepository(uri);
 
         svc.installFeature("f1", "0.1");
-                
+
         // Uninstall repository
         svc.uninstallFeature("f1", "0.1");
         svc.uninstallFeature("f2", "0.1");
-        
+
+    }
+
+    // Tests install of a Repository that includes a feature with a feature dependency
+    public void testInstallFeatureWithDependantFeaturesAndVersionWithoutPreinstall() throws Exception {
+
+        String name = getJarUrl(Bundle.class);
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\" version=\"0.1\">");
+        pw.println("    <feature version=\"0.1\">f2</feature>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+
+        BundleContext bundleContext = prepareBundleContextForInstallUninstall();
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setBundleContext(bundleContext);
+        svc.addRepository(uri);
+
+        svc.installFeature("f1", "0.1");
+
+        // Uninstall repository
+        svc.uninstallFeature("f1", "0.1");
+        svc.uninstallFeature("f2", "0.1");
+    }
+
+    // Tests install of a Repository that includes a feature with a feature dependency
+    public void testInstallFeatureWithDependantFeaturesAndNoVersionWithoutPreinstall() throws Exception {
+
+        String name = getJarUrl(Bundle.class);
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\" version=\"0.1\">");
+        pw.println("    <feature>f2</feature>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+
+        BundleContext bundleContext = prepareBundleContextForInstallUninstall();
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setBundleContext(bundleContext);
+        svc.addRepository(uri);
+
+        svc.installFeature("f1", "0.1");
+
+        // Uninstall repository
+        svc.uninstallFeature("f1", "0.1");
+        svc.uninstallFeature("f2", "0.2");
+    }
+
+    public void testInstallFeatureWithDependantFeaturesAndRangeWithoutPreinstall() throws Exception {
+
+        String name = getJarUrl(Bundle.class);
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\" version=\"0.1\">");
+        pw.println("    <feature version=\"[0.1,0.3)\">f2</feature>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+
+        BundleContext bundleContext = prepareBundleContextForInstallUninstall();
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setBundleContext(bundleContext);
+        svc.addRepository(uri);
+
+        svc.installFeature("f1", "0.1");
+
+        // Uninstall repository
+        svc.uninstallFeature("f1", "0.1");
+        svc.uninstallFeature("f2", "0.2");
+    }
+
+    public void testInstallFeatureWithDependantFeaturesAndRangeWithPreinstall() throws Exception {
+
+        String name = getJarUrl(Bundle.class);
+
+        File tmp = File.createTempFile("smx", ".feature");
+        PrintWriter pw = new PrintWriter(new FileWriter(tmp));
+        pw.println("<features>");
+        pw.println("  <feature name=\"f1\" version=\"0.1\">");
+        pw.println("    <feature version=\"[0.1,0.3)\">f2</feature>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.1\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("  <feature name=\"f2\" version=\"0.2\">");
+        pw.println("    <bundle>" + name + "</bundle>");
+        pw.println("  </feature>");
+        pw.println("</features>");
+        pw.close();
+
+        URI uri = tmp.toURI();
+
+        BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+        Bundle installedBundle = EasyMock.createMock(Bundle.class);
+
+        // Installs feature f1 with dependency on f2
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(12345L).anyTimes();
+        expect(bundleContext.getBundle(12345L)).andReturn(installedBundle).anyTimes();
+        expect(installedBundle.getHeaders()).andReturn(new Hashtable()).anyTimes();
+        installedBundle.start();
+
+        expect(bundleContext.getBundles()).andReturn(new Bundle[] { installedBundle });
+        expect(installedBundle.getSymbolicName()).andReturn(name).anyTimes();
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        installedBundle.start();
+
+        // uninstalls first feature name = f2, version = 0.1
+        installedBundle.uninstall();
+
+        expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
+
+        replay(bundleContext, installedBundle);
+
+        FeaturesServiceImpl svc = new FeaturesServiceImpl();
+        svc.setBundleContext(bundleContext);
+        svc.addRepository(uri);
+
+        svc.installFeature("f2", "0.1");
+        svc.installFeature("f1", "0.1");
+
+        // Uninstall repository
+        svc.uninstallFeature("f1", "0.1");
+        svc.uninstallFeature("f2", "0.1");
+    }
+
+    private BundleContext prepareBundleContextForInstallUninstall() throws Exception {
+        BundleContext bundleContext = EasyMock.createMock(BundleContext.class);
+        Bundle installedBundle = EasyMock.createMock(Bundle.class);
+
+        // Installs feature f1 with dependency on f2
+        expect(bundleContext.getBundles()).andReturn(new Bundle[0]);
+        expect(bundleContext.installBundle(isA(String.class),
+                                           isA(InputStream.class))).andReturn(installedBundle);
+        expect(installedBundle.getBundleId()).andReturn(12345L);
+        expect(installedBundle.getBundleId()).andReturn(12345L);
+        expect(installedBundle.getBundleId()).andReturn(12345L);
+        expect(bundleContext.getBundle(12345L)).andReturn(installedBundle);
+        expect(installedBundle.getHeaders()).andReturn(new Hashtable());
+        installedBundle.start();
+
+        // uninstalls first feature name = f2, version = 0.1
+        expect(bundleContext.getBundle(12345)).andReturn(installedBundle);
+        installedBundle.uninstall();
+
+        expect(bundleContext.getDataFile(EasyMock.<String>anyObject())).andReturn(dataFile).anyTimes();
+
+        replay(bundleContext, installedBundle);
+        return bundleContext;
     }
 
     public void testInstallBatchFeatureWithContinueOnFailureNoClean() throws Exception {