Author: gertv
Date: Thu Aug  9 15:15:55 2012
New Revision: 1371248

URL: http://svn.apache.org/viewvc?rev=1371248&view=rev
Log:
KARAF-1600: Avoid duplicate feature and bundle resolution when installing 
cascading features

Added:
    
karaf/branches/karaf-2.3.x/features/core/src/test/resources/org/apache/karaf/features/internal/
    
karaf/branches/karaf-2.3.x/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml
Modified:
    
karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
    
karaf/branches/karaf-2.3.x/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java

Modified: 
karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
URL: 
http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java?rev=1371248&r1=1371247&r2=1371248&view=diff
==============================================================================
--- 
karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
 (original)
+++ 
karaf/branches/karaf-2.3.x/features/core/src/main/java/org/apache/karaf/features/internal/FeaturesServiceImpl.java
 Thu Aug  9 15:15:55 2012
@@ -518,7 +518,11 @@ public class FeaturesServiceImpl impleme
                 throw new Exception("No feature named '" + dependency.getName()
                         + "' with version '" + dependency.getVersion() + "' 
available");
             }
-            doInstallFeature(state, fi, verbose);
+            if (state.features.containsKey(fi)) {
+                LOGGER.debug("Feature {} with version {} is already being 
installed", feature.getName(), feature.getVersion());
+            } else {
+                doInstallFeature(state, fi, verbose);
+            }
         }
         for (String config : feature.getConfigurations().keySet()) {
             Dictionary<String, String> props = new Hashtable<String, 
String>(feature.getConfigurations().get(config));
@@ -590,11 +594,11 @@ public class FeaturesServiceImpl impleme
     protected Set<Bundle> findBundlesToRefresh(InstallationState state) {
         Set<Bundle> bundles = new HashSet<Bundle>();
         bundles.addAll(findBundlesWithOptionalPackagesToRefresh(state));
-        bundles.addAll(findBundlesWithFramentsToRefresh(state));
+        bundles.addAll(findBundlesWithFragmentsToRefresh(state));
         return bundles;
     }
 
-    protected Set<Bundle> findBundlesWithFramentsToRefresh(InstallationState 
state) {
+    protected Set<Bundle> findBundlesWithFragmentsToRefresh(InstallationState 
state) {
         Set<Bundle> bundles = new HashSet<Bundle>();
         Set<Bundle> oldBundles = new HashSet<Bundle>(state.bundles);
         oldBundles.removeAll(state.installed);

Modified: 
karaf/branches/karaf-2.3.x/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java
URL: 
http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java?rev=1371248&r1=1371247&r2=1371248&view=diff
==============================================================================
--- 
karaf/branches/karaf-2.3.x/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java
 (original)
+++ 
karaf/branches/karaf-2.3.x/features/core/src/test/java/org/apache/karaf/features/internal/FeaturesServiceImplTest.java
 Thu Aug  9 15:15:55 2012
@@ -24,16 +24,22 @@ import java.net.URL;
 import java.net.URLClassLoader;
 import java.util.HashMap;
 import java.util.List;
+import java.util.LinkedList;
 import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
 import junit.framework.TestCase;
 
 import org.apache.felix.utils.manifest.Clause;
+import org.apache.karaf.features.BundleInfo;
 import org.apache.karaf.features.Feature;
 import org.easymock.EasyMock;
+import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
 import org.osgi.framework.FrameworkListener;
 
 /**
@@ -173,6 +179,56 @@ public class FeaturesServiceImplTest ext
         }
     }
 
+    /**
+     * This test ensures that every feature get installed only once, even if 
it appears multiple times in the list
+     * of transitive feature dependencies (KARAF-1600)
+     */
+    public void testNoDuplicateFeaturesInstallation() throws Exception {
+        final List<Feature> installed = new LinkedList<Feature>();
+
+        final FeaturesServiceImpl impl = new FeaturesServiceImpl() {
+            // override methods which refers to bundle context to avoid 
mocking everything
+            @Override
+            protected boolean loadState() {
+                return true;
+            }
+
+            @Override
+            protected void saveState() {
+
+            }
+
+            @Override
+            protected void doInstallFeature(InstallationState state, Feature 
feature, boolean verbose) throws Exception {
+                installed.add(feature);
+
+                super.doInstallFeature(state, feature, verbose);
+            }
+
+            @Override
+            protected Bundle installBundleIfNeeded(InstallationState state, 
BundleInfo bundleInfo, boolean verbose) throws IOException, BundleException {
+                // let's return a mock bundle and bundle id to keep the 
features service happy
+                Bundle bundle = createNiceMock(Bundle.class);
+                expect(bundle.getBundleId()).andReturn(10l).anyTimes();
+                replay(bundle);
+                return bundle;
+            }
+        };
+        impl.addRepository(getClass().getResource("repo2.xml").toURI());
+
+        try {
+            impl.installFeature("all");
+
+            // copying the features to a set to filter out the duplicates
+            Set<Feature> noduplicates = new HashSet<Feature>();
+            noduplicates.addAll(installed);
+
+            assertEquals("Every feature should only have been installed once", 
installed.size(), noduplicates.size());
+        } catch (Exception e) {
+            fail(String.format("Service should not throw any exceptions: %s", 
e));
+        }
+    }
+
     public void testGetOptionalImportsOnly() {
         FeaturesServiceImpl service = new FeaturesServiceImpl();
 

Added: 
karaf/branches/karaf-2.3.x/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml
URL: 
http://svn.apache.org/viewvc/karaf/branches/karaf-2.3.x/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml?rev=1371248&view=auto
==============================================================================
--- 
karaf/branches/karaf-2.3.x/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml
 (added)
+++ 
karaf/branches/karaf-2.3.x/features/core/src/test/resources/org/apache/karaf/features/internal/repo2.xml
 Thu Aug  9 15:15:55 2012
@@ -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.
+-->
+<features name="repo2">
+    <feature name="common">
+        <bundle>b1</bundle>
+    </feature>
+    <feature name="f1">
+        <feature>common</feature>
+        <bundle>b2</bundle>
+    </feature>
+    <feature name="f2">
+        <feature>common</feature>
+        <feature>f1</feature>
+        <bundle>b3</bundle>
+    </feature>
+    <feature name="f3">
+        <feature>f1</feature>
+        <feature>f2</feature>
+       <bundle>b4</bundle>
+    </feature>
+    <feature name="all">
+        <feature>f1</feature>
+        <feature>f2</feature>
+        <feature>f3</feature>
+    </feature>
+</features>


Reply via email to