Author: cziegeler Date: Fri Sep 1 15:02:41 2017 New Revision: 1806972 URL: http://svn.apache.org/viewvc?rev=1806972&view=rev Log: Add feature extension handler
Added: sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureExtensionHandler.java (with props) Modified: 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/BuilderUtil.java sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureBuilder.java 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=1806972&r1=1806971&r2=1806972&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 Sep 1 15:02:41 2017 @@ -16,16 +16,16 @@ */ package org.apache.sling.feature.process; -import org.apache.sling.feature.Application; -import org.apache.sling.feature.ArtifactId; -import org.apache.sling.feature.Feature; - import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.sling.feature.Application; +import org.apache.sling.feature.ArtifactId; +import org.apache.sling.feature.Feature; + /** * Build an application based on features. */ @@ -156,6 +156,6 @@ public class ApplicationBuilder { BuilderUtil.mergeBundles(target.getBundles(), source.getBundles(), BuilderUtil.ArtifactMerge.HIGHEST); BuilderUtil.mergeConfigurations(target.getConfigurations(), source.getConfigurations()); BuilderUtil.mergeFrameworkProperties(target.getFrameworkProperties(), source.getFrameworkProperties()); - BuilderUtil.mergeExtensions(target.getExtensions(), source.getExtensions(), BuilderUtil.ArtifactMerge.HIGHEST); + BuilderUtil.mergeExtensions(target, source, BuilderUtil.ArtifactMerge.HIGHEST); } } Modified: sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/BuilderUtil.java URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/BuilderUtil.java?rev=1806972&r1=1806971&r2=1806972&view=diff ============================================================================== --- sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/BuilderUtil.java (original) +++ sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/BuilderUtil.java Fri Sep 1 15:02:41 2017 @@ -16,27 +16,29 @@ */ package org.apache.sling.feature.process; +import java.io.StringReader; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; + +import javax.json.Json; +import javax.json.JsonArray; +import javax.json.JsonObject; +import javax.json.JsonStructure; +import javax.json.JsonValue; +import javax.json.JsonValue.ValueType; + +import org.apache.sling.feature.Application; import org.apache.sling.feature.Artifact; import org.apache.sling.feature.Bundles; import org.apache.sling.feature.Capability; import org.apache.sling.feature.Configuration; import org.apache.sling.feature.Configurations; import org.apache.sling.feature.Extension; -import org.apache.sling.feature.Extensions; +import org.apache.sling.feature.Feature; import org.apache.sling.feature.KeyValueMap; import org.apache.sling.feature.Requirement; -import javax.json.Json; -import javax.json.JsonArray; -import javax.json.JsonObject; -import javax.json.JsonStructure; -import javax.json.JsonValue; -import javax.json.JsonValue.ValueType; -import java.io.StringReader; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; - /** * Utility methods for the builders */ @@ -114,80 +116,118 @@ class BuilderUtil { } } - // extensions (add/merge) - static void mergeExtensions(final Extensions target, - final Extensions source, + // default merge for extensions + static void mergeExtensions(final Extension target, + final Extension source, final ArtifactMerge artifactMergeAlg) { - for(final Extension ext : source) { + switch ( target.getType() ) { + case TEXT : // simply append + target.setText(target.getText() + "\n" + source.getText()); + break; + case JSON : final JsonStructure struct1; + try ( final StringReader reader = new StringReader(target.getJSON()) ) { + struct1 = Json.createReader(reader).read(); + } + final JsonStructure struct2; + try ( final StringReader reader = new StringReader(source.getJSON()) ) { + struct2 = Json.createReader(reader).read(); + } + + if ( struct1.getValueType() != struct2.getValueType() ) { + throw new IllegalStateException("Found different JSON types for extension " + target.getName() + + " : " + struct1.getValueType() + " and " + struct2.getValueType()); + } + if ( struct1.getValueType() == ValueType.ARRAY ) { + // array is append + final JsonArray a1 = (JsonArray)struct1; + final JsonArray a2 = (JsonArray)struct2; + for(final JsonValue val : a2) { + a1.add(val); + } + } else { + // object is merge + merge((JsonObject)struct1, (JsonObject)struct2); + } + break; + + case ARTIFACTS : for(final Artifact a : source.getArtifacts()) { + // use artifactMergeAlg + boolean add = true; + for(final Artifact targetArtifact : target.getArtifacts()) { + if ( targetArtifact.getId().isSame(a.getId()) ) { + if ( artifactMergeAlg == ArtifactMerge.HIGHEST ) { + if ( targetArtifact.getId().getOSGiVersion().compareTo(a.getId().getOSGiVersion()) > 0 ) { + add = false; + } else { + target.getArtifacts().remove(targetArtifact); + } + } else { // latest + + target.getArtifacts().remove(targetArtifact); + } + break; + } + } + + if ( add ) { + target.getArtifacts().add(a); + } + + } + break; + } + } + + // extensions (add/merge) + static void mergeExtensions(final Feature target, + final Feature source, + final ArtifactMerge artifactMergeAlg, + final FeatureExtensionHandler... extensionMergers) { + for(final Extension ext : source.getExtensions()) { boolean found = false; - for(final Extension current : target) { + for(final Extension current : target.getExtensions()) { if ( current.getName().equals(ext.getName()) ) { found = true; if ( current.getType() != ext.getType() ) { throw new IllegalStateException("Found different types for extension " + current.getName() + " : " + current.getType() + " and " + ext.getType()); } - switch ( current.getType() ) { - case TEXT : // simply append - current.setText(current.getText() + "\n" + ext.getText()); - break; - case JSON : final JsonStructure struct1; - try ( final StringReader reader = new StringReader(current.getJSON()) ) { - struct1 = Json.createReader(reader).read(); - } - final JsonStructure struct2; - try ( final StringReader reader = new StringReader(ext.getJSON()) ) { - struct2 = Json.createReader(reader).read(); - } - - if ( struct1.getValueType() != struct2.getValueType() ) { - throw new IllegalStateException("Found different JSON types for extension " + current.getName() - + " : " + struct1.getValueType() + " and " + struct2.getValueType()); - } - if ( struct1.getValueType() == ValueType.ARRAY ) { - // array is append - final JsonArray a1 = (JsonArray)struct1; - final JsonArray a2 = (JsonArray)struct2; - for(final JsonValue val : a2) { - a1.add(val); - } - } else { - // object is merge - merge((JsonObject)struct1, (JsonObject)struct2); - } - break; - - case ARTIFACTS : for(final Artifact a : ext.getArtifacts()) { - // use artifactMergeAlg - boolean add = true; - for(final Artifact targetArtifact : current.getArtifacts()) { - if ( targetArtifact.getId().isSame(a.getId()) ) { - if ( artifactMergeAlg == ArtifactMerge.HIGHEST ) { - if ( targetArtifact.getId().getOSGiVersion().compareTo(a.getId().getOSGiVersion()) > 0 ) { - add = false; - } else { - current.getArtifacts().remove(targetArtifact); - } - } else { // latest - - current.getArtifacts().remove(targetArtifact); - } - break; - } - } - - if ( add ) { - current.getArtifacts().add(a); - } - - } - break; + if ( extensionMergers != null ) { + for(final FeatureExtensionHandler fem : extensionMergers) { + if ( fem.canMerge(current.getName()) ) { + fem.merge(target, source, current.getName()); + } + } + } else { + // default merge + mergeExtensions(current, ext, artifactMergeAlg); + } + } + } + if ( !found ) { + target.getExtensions().add(ext); + } + } + } + static void mergeExtensions(final Application target, + final Feature source, + final ArtifactMerge artifactMergeAlg) { + for(final Extension ext : source.getExtensions()) { + boolean found = false; + for(final Extension current : target.getExtensions()) { + if ( current.getName().equals(ext.getName()) ) { + found = true; + if ( current.getType() != ext.getType() ) { + throw new IllegalStateException("Found different types for extension " + current.getName() + + " : " + current.getType() + " and " + ext.getType()); } + // default merge + mergeExtensions(current, ext, artifactMergeAlg); } } if ( !found ) { - target.add(ext); + target.getExtensions().add(ext); } } } 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=1806972&r1=1806971&r2=1806972&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 Sep 1 15:02:41 2017 @@ -16,6 +16,12 @@ */ package org.apache.sling.feature.process; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + import org.apache.sling.feature.Artifact; import org.apache.sling.feature.ArtifactId; import org.apache.sling.feature.Configuration; @@ -23,12 +29,6 @@ import org.apache.sling.feature.Extensio import org.apache.sling.feature.Feature; import org.apache.sling.feature.Include; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - public class FeatureBuilder { /** @@ -36,15 +36,18 @@ public class FeatureBuilder { * * @param feature The feature to start * @param provider A provider providing the included features + * @param extensionMergers Optional feature mergers * @return The assembled feature. * @throws IllegalArgumentException If feature or provider is {@code null} - * @throws IllegalStateException If an included feature can't be provided + * @throws IllegalStateException If an included feature can't be provided or merged. */ - public static Feature assemble(final Feature feature, final FeatureProvider provider) { + public static Feature assemble(final Feature feature, + final FeatureProvider provider, + final FeatureExtensionHandler... extensionMergers) { if ( feature == null || provider == null ) { throw new IllegalArgumentException("Feature and/or provider must not be null"); } - return internalAssemble(new ArrayList<>(), feature, provider); + return internalAssemble(new ArrayList<>(), feature, provider, extensionMergers); } /** @@ -64,7 +67,8 @@ public class FeatureBuilder { */ public static Feature assemble(final Feature feature, final List<Feature> upgrades, - final FeatureProvider provider) { + final FeatureProvider provider, + final FeatureExtensionHandler... extensionMergers) { if ( feature == null || provider == null ) { throw new IllegalArgumentException("Feature and/or provider must not be null"); } @@ -100,7 +104,7 @@ public class FeatureBuilder { } // assemble feature without upgrades - final Feature assembledFeature = internalAssemble(new ArrayList<>(), feature, provider); + final Feature assembledFeature = internalAssemble(new ArrayList<>(), feature, provider, extensionMergers); // handle upgrades if ( useUpdates != null ) { @@ -125,7 +129,7 @@ public class FeatureBuilder { final Feature auf = assemble(uf, provider); // merge - merge(assembledFeature, auf); + merge(assembledFeature, auf, extensionMergers); } } @@ -134,7 +138,8 @@ public class FeatureBuilder { private static Feature internalAssemble(final List<String> processedFeatures, final Feature feature, - final FeatureProvider provider) { + final FeatureProvider provider, + final FeatureExtensionHandler... extensionMergers) { if ( feature.isAssembled() ) { return feature; } @@ -187,15 +192,15 @@ public class FeatureBuilder { if ( f == null ) { throw new IllegalStateException("Unable to find included feature " + i.getId()); } - final Feature af = internalAssemble(processedFeatures, f, provider); + final Feature af = internalAssemble(processedFeatures, f, provider, extensionMergers); // process include instructions include(af, i); // and now merge - merge(result, af); + merge(result, af, extensionMergers); } - merge(result, feature); + merge(result, feature, extensionMergers); } processedFeatures.remove(feature.getId().toMvnId()); @@ -203,13 +208,17 @@ public class FeatureBuilder { return result; } - private static void merge(final Feature target, final Feature source) { + private static void merge(final Feature target, final Feature source, + final FeatureExtensionHandler... extensionMergers) { BuilderUtil.mergeBundles(target.getBundles(), source.getBundles(), BuilderUtil.ArtifactMerge.LATEST); BuilderUtil.mergeConfigurations(target.getConfigurations(), source.getConfigurations()); BuilderUtil.mergeFrameworkProperties(target.getFrameworkProperties(), source.getFrameworkProperties()); BuilderUtil.mergeRequirements(target.getRequirements(), source.getRequirements()); BuilderUtil.mergeCapabilities(target.getCapabilities(), source.getCapabilities()); - BuilderUtil.mergeExtensions(target.getExtensions(), source.getExtensions(), BuilderUtil.ArtifactMerge.LATEST); + BuilderUtil.mergeExtensions(target, + source, + BuilderUtil.ArtifactMerge.LATEST, + extensionMergers); } private static void include(final Feature base, final Include i) { Added: sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureExtensionHandler.java URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureExtensionHandler.java?rev=1806972&view=auto ============================================================================== --- sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureExtensionHandler.java (added) +++ sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureExtensionHandler.java Fri Sep 1 15:02:41 2017 @@ -0,0 +1,51 @@ +/* + * 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. + */ +package org.apache.sling.feature.process; + +import org.apache.sling.feature.Feature; + +public interface FeatureExtensionHandler { + + /** + * Checks whether this merger can merge extensions with that name + * @param extensionName The extension name + * @return {@code true} if merger can handle this + */ + boolean canMerge(String extensionName); + + /** + * Merge the source extension into the target extension. + * + * The caller of this method already ensured that both + * extensions share the same name and type and that + * {@link #canMerge(String)} returned {@code true}. + * + * @param target The target feature + * @param source The source feature + * @param extensionName The extension name + * @throws IllegalStateException If the extensions can't be merged + */ + void merge(Feature target, Feature source, String extensionName); + + /** + * Post process the feature with respect to the extension + * @param feature The feature + * @param extensionName The extension name + * @throws IllegalStateException If post processing failed + */ + void postProcess(Feature feature, String extensionName); +} Propchange: sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureExtensionHandler.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/process/FeatureExtensionHandler.java ------------------------------------------------------------------------------ svn:keywords = author date id revision rev url