Author: cziegeler
Date: Fri Jul 28 14:27:20 2017
New Revision: 1803275

URL: http://svn.apache.org/viewvc?rev=1803275&view=rev
Log:
Add upgrades to application builder

Modified:
    sling/whiteboard/cziegeler/feature/readme.md
    
sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
    
sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureBuilder.java
    
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/g-a-1.json
    
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/include-1.json
    
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/result-1.json

Modified: sling/whiteboard/cziegeler/feature/readme.md
URL: 
http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature/readme.md?rev=1803275&r1=1803274&r2=1803275&view=diff
==============================================================================
--- sling/whiteboard/cziegeler/feature/readme.md (original)
+++ sling/whiteboard/cziegeler/feature/readme.md Fri Jul 28 14:27:20 2017
@@ -88,7 +88,7 @@ Once a feature is processed, included re
 
 * Includes are processed in the order they are defined in the model. The 
current feature (containing the includes) is used last which means the 
algorithm starts with the first included feature.
 * Removal instructions for an include are handled first
-* A clash of bundles or content packages is resolved by picking the one with 
the highest version
+* A clash of bundles or content packages is resolved by picking the latest 
version (not the highest!)
 * Configurations will be merged by default, later ones potentially overriding 
newer ones:
   * If the same property is declared in more than one feature, the last one 
wins - in case of an array value, this requires redeclaring all values (if they 
are meant to be kept)
   * Configurations can be bound to a bundle. When two features are merged, all 
cases can occur: both might be bound to the same bundle (symbolic name), both 
might not be bound, they might be bound to different bundles (symbolic name), 
or one might be bound and the other one might not. As configurations are 
handled as a set regardless of whether they are bound to a bundle or not, the 
information of the belonging bundle is handled like a property in the 
configuration. This means:

Modified: 
sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
URL: 
http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java?rev=1803275&r1=1803274&r2=1803275&view=diff
==============================================================================
--- 
sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
 (original)
+++ 
sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/ApplicationBuilder.java
 Fri Jul 28 14:27:20 2017
@@ -20,6 +20,7 @@ import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
@@ -44,6 +45,11 @@ public class ApplicationBuilder {
 
     /**
      * Assemble an application based on the provided feature Ids.
+     *
+     * Upgrade features are only applied if the provided feature list
+     * contains the feature to be upgraded. Otherwise the upgrade feature
+     * is ignored.
+     *
      * @param app The optional application to use as a base.
      * @param provider The provider to resolve features.
      * @param featureIds The feature ids
@@ -72,6 +78,11 @@ public class ApplicationBuilder {
 
     /**
      * Assemble an application based on the provided features.
+     *
+     * Upgrade features are only applied if the provided feature list
+     * contains the feature to be upgraded. Otherwise the upgrade feature
+     * is ignored.
+     *
      * @param app The optional application to use as a base.
      * @param provider The provider to resolve features.
      * @param features The features
@@ -90,18 +101,54 @@ public class ApplicationBuilder {
         if ( app == null ) {
             app = new Application();
         }
+
+        // detect upgrades and created sorted feature list
+        final Map<Feature, List<Feature>> upgrades = new HashMap<>();
         final List<Feature> sortedFeatureList = new ArrayList<>();
         for(final Feature f : features) {
-            app.getFeatureIds().add(f.getId());
-            sortedFeatureList.add(f);
+            if ( f.getUpgradeOf() != null ) {
+                for(final Feature i : features) {
+                    if ( i.getId().equals(f.getUpgradeOf()) ) {
+                        List<Feature> u = upgrades.get(i);
+                        if ( u == null ) {
+                            u = new ArrayList<>();
+                            upgrades.put(i, u);
+                        }
+                        u.add(f);
+                        app.getFeatureIds().add(f.getId());
+                        break;
+                    }
+                }
+            } else {
+                app.getFeatureIds().add(f.getId());
+                sortedFeatureList.add(f);
+            }
+        }
+
+        // process upgrades first
+        for(final Map.Entry<Feature, List<Feature>> entry : 
upgrades.entrySet()) {
+            final Feature assembled = FeatureBuilder.assemble(entry.getKey(),
+                    entry.getValue(),
+                    provider);
+            // update feature to assembled feature
+            sortedFeatureList.remove(entry.getKey());
+            sortedFeatureList.add(assembled);
         }
+
+        // sort
         Collections.sort(sortedFeatureList);
 
+        // assemble
         for(final Feature f : sortedFeatureList) {
             final Feature assembled = FeatureBuilder.assemble(f, new 
FeatureProvider() {
 
                 @Override
                 public Feature provide(final ArtifactId id) {
+                    for(final Feature f : upgrades.keySet()) {
+                        if ( f.getId().equals(id) ) {
+                            return f;
+                        }
+                    }
                     for(final Feature f : features) {
                         if ( f.getId().equals(id) ) {
                             return f;

Modified: 
sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureBuilder.java
URL: 
http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureBuilder.java?rev=1803275&r1=1803274&r2=1803275&view=diff
==============================================================================
--- 
sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureBuilder.java
 (original)
+++ 
sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureBuilder.java
 Fri Jul 28 14:27:20 2017
@@ -18,6 +18,7 @@ package org.apache.sling.feature.process
 
 import java.io.StringReader;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.List;
@@ -43,6 +44,7 @@ public class FeatureBuilder {
 
     /**
      * Assemble the full feature by processing all includes.
+     *
      * @param feature The feature to start
      * @param provider A provider providing the included features
      * @return The assembled feature.
@@ -50,15 +52,103 @@ public class FeatureBuilder {
      * @throws IllegalStateException If an included feature can't be provided
      */
     public static Feature assemble(final Feature feature, final 
FeatureProvider provider) {
+        if ( feature == null || provider == null ) {
+            throw new IllegalArgumentException("Feature and/or provider must 
not be null");
+        }
         return internalAssemble(new ArrayList<>(), feature, provider);
     }
 
-    private static Feature internalAssemble(final List<String> 
processedFeatures,
-            final Feature feature,
+    /**
+     * Assemble the final feature and apply upgrades
+     *
+     * If the list of upgrades contains upgrade features not intended for the
+     * provided feature, this is not considered an error situation. But the
+     * provided upgrade is ignored.
+     *
+     * @param feature The feature to start
+     * @param upgrades The list of upgrades. If this is {@code null} or empty, 
this method
+     *     behaves like {@link #assemble(Feature, FeatureProvider)}.
+     * @param provider A provider providing the included features
+     * @return The assembled feature.
+     * @throws IllegalArgumentException If feature or provider is {@code null}
+     * @throws IllegalStateException If an included feature can't be provided
+     */
+    public static Feature assemble(final Feature feature,
+            final List<Feature> upgrades,
             final FeatureProvider provider) {
         if ( feature == null || provider == null ) {
             throw new IllegalArgumentException("Feature and/or provider must 
not be null");
         }
+
+        // check upgrades
+        List<Feature> useUpdates = null;
+        if ( upgrades != null && !upgrades.isEmpty() ) {
+            useUpdates = new ArrayList<>();
+            for(final Feature uf : upgrades) {
+                if ( !feature.getId().equals(uf.getUpgradeOf()) ) {
+                    continue;
+                }
+                boolean found = false;
+                for(final Feature i : useUpdates) {
+                    if ( i.getId().isSame(uf.getId()) ) {
+                        if ( 
uf.getId().getOSGiVersion().compareTo(i.getId().getOSGiVersion()) > 0 ) {
+                            useUpdates.remove(i);
+                        } else {
+                            found = true;
+                        }
+                        break;
+                    }
+                }
+                if ( !found ) {
+                    // we add a copy as we manipulate the upgrade below
+                    useUpdates.add(uf.copy());
+                }
+            }
+            Collections.sort(useUpdates);
+            if ( useUpdates.isEmpty() ) {
+                useUpdates = null;
+            }
+        }
+
+        // assemble feature without upgrades
+        final Feature assembledFeature = internalAssemble(new ArrayList<>(), 
feature, provider);
+
+        // handle upgrades
+        if ( useUpdates != null ) {
+            for(final Feature uf : useUpdates) {
+                Include found = null;
+                for(final Include inc : uf.getIncludes() ) {
+                    if ( inc.getId().equals(assembledFeature.getId()) ) {
+                        found = inc;
+                        break;
+                    }
+                }
+                if ( found != null ) {
+                    uf.getIncludes().remove(found);
+
+                    // process include instructions
+                    include(assembledFeature, found);
+                }
+
+                // now assemble upgrade, but without considering the base
+                uf.setUpgradeOf(null);
+                assembledFeature.getUpgrades().add(uf.getId());
+                final Feature auf = assemble(uf, provider);
+
+                // merge
+                merge(assembledFeature, auf);
+            }
+        }
+
+        return assembledFeature;
+    }
+
+    private static Feature internalAssemble(final List<String> 
processedFeatures,
+            final Feature feature,
+            final FeatureProvider provider) {
+        if ( feature.isAssembled() ) {
+            return feature;
+        }
         if ( processedFeatures.contains(feature.getId().toMvnId()) ) {
             throw new IllegalStateException("Recursive inclusion of " + 
feature.getId().toMvnId() + " via " + processedFeatures);
         }

Modified: 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/g-a-1.json
URL: 
http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature/src/test/resources/features/process/g-a-1.json?rev=1803275&r1=1803274&r2=1803275&view=diff
==============================================================================
--- 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/g-a-1.json
 (original)
+++ 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/g-a-1.json
 Fri Jul 28 14:27:20 2017
@@ -8,7 +8,14 @@
     "bundles" : {
       "3" : [
             "org.apache.sling/foo-bar/4.5.6"
-           ]
+           ],
+      "5" : [
+            "group/testnewversion_low/2",
+            "group/testnewversion_high/2",
+            "group/testnewstartlevel/1",
+            "group/testnewstartlevelandversion/1"            
+          ]
+           
     },
     "configurations" : {
         "org.apache.sling.foo" : {

Modified: 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/include-1.json
URL: 
http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature/src/test/resources/features/process/include-1.json?rev=1803275&r1=1803274&r2=1803275&view=diff
==============================================================================
--- 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/include-1.json
 (original)
+++ 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/include-1.json
 Fri Jul 28 14:27:20 2017
@@ -59,7 +59,16 @@
           ],
       "2" : [
             "org.apache.sling/foo-xyz/1.2.3"
+          ],
+      "5" : [
+            "group/testnewversion_low/1",
+            "group/testnewversion_high/5"
+         ],
+      "10" : [
+            "group/testnewstartlevel/1",
+            "group/testnewstartlevelandversion/2"
           ]
+      
     },
     "configurations" : {
         "my.pid" : {

Modified: 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/result-1.json
URL: 
http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature/src/test/resources/features/process/result-1.json?rev=1803275&r1=1803274&r2=1803275&view=diff
==============================================================================
--- 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/result-1.json
 (original)
+++ 
sling/whiteboard/cziegeler/feature/src/test/resources/features/process/result-1.json
 Fri Jul 28 14:27:20 2017
@@ -50,7 +50,16 @@
           ],
       "3" : [
             "org.apache.sling/foo-bar/4.5.6"
-           ]
+           ],
+      "5" : [
+            "group/testnewversion_low/1",
+            "group/testnewversion_high/5"
+          ],
+      "10" : [
+            "group/testnewstartlevel/1",
+            "group/testnewstartlevelandversion/2"
+          ]
+           
     },
     "configurations" : {
         "my.pid" : {


Reply via email to