You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by da...@apache.org on 2018/03/19 15:15:45 UTC

[sling-whiteboard] branch master updated: Support for special feature names when converting to provisioning model.

This is an automated email from the ASF dual-hosted git repository.

davidb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 9daa1a7  Support for special feature names when converting to provisioning model.
9daa1a7 is described below

commit 9daa1a75c128ab38d5ae4fd4b99829f93d8fe71d
Author: David Bosschaert <da...@gmail.com>
AuthorDate: Mon Mar 19 15:09:52 2018 +0000

    Support for special feature names when converting to provisioning model.
    
    The special feature names must be specified in the variables section
    under the variable name 'provisioning.model.name'.
    This commit also contains the start of a document-based set of tests for
    testing between conversions of the feature model to the provisioning
    model and back.
---
 featuremodel/feature-modelconverter/pom.xml        |   9 +
 .../modelconverter/impl/FeatureToProvisioning.java |   9 +-
 .../modelconverter/impl/ProvisioningToFeature.java |   2 +-
 .../modelconverter/impl/ModelConverterTest.java    | 211 +++++++++++++++++++++
 .../src/test/resources/boot.json                   |  41 ++++
 .../src/test/resources/boot.txt                    |  26 +++
 .../feature/support/json/FeatureJSONReader.java    |  19 +-
 .../java/org/apache/sling/feature/Feature.java     |  19 +-
 8 files changed, 319 insertions(+), 17 deletions(-)

diff --git a/featuremodel/feature-modelconverter/pom.xml b/featuremodel/feature-modelconverter/pom.xml
index 2e0c607..8d19064 100644
--- a/featuremodel/feature-modelconverter/pom.xml
+++ b/featuremodel/feature-modelconverter/pom.xml
@@ -72,6 +72,15 @@
                     </archive>
                 </configuration>
             </plugin>
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>src/test/resources/**</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
         </plugins>
     </build>
     
diff --git a/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/FeatureToProvisioning.java b/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/FeatureToProvisioning.java
index 6679020..2325549 100644
--- a/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/FeatureToProvisioning.java
+++ b/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/FeatureToProvisioning.java
@@ -48,15 +48,14 @@ import java.util.Map;
 /** Converter that converts the feature model to the provisioning model.
  */
 public class FeatureToProvisioning {
-    private static Logger LOGGER = LoggerFactory.getLogger(FeatureToProvisioning.class);
+    private static final Logger LOGGER = LoggerFactory.getLogger(FeatureToProvisioning.class);
+    private static final String PROVISIONING_MODEL_NAME_VARIABLE = "provisioning.model.name";
 
     public static void convert(File file, String output, ArtifactManager am) throws IOException {
         org.apache.sling.feature.Feature feature = FeatureUtil.getFeature(file.getAbsolutePath(), am);
 
-        String featureName;
-        if (feature.getTitle() != null) {
-            featureName = feature.getTitle();
-        } else {
+        String featureName = feature.getVariables().get(PROVISIONING_MODEL_NAME_VARIABLE);
+        if (featureName == null) {
             featureName = feature.getId().getArtifactId();
         }
 
diff --git a/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/ProvisioningToFeature.java b/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/ProvisioningToFeature.java
index 57f13c6..860de6b 100644
--- a/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/ProvisioningToFeature.java
+++ b/featuremodel/feature-modelconverter/src/main/java/org/apache/sling/feature/modelconverter/impl/ProvisioningToFeature.java
@@ -199,7 +199,7 @@ public class ProvisioningToFeature {
     /**
      * Read the provisioning model
      */
-    private static Model readProvisioningModel(final File file)
+    static Model readProvisioningModel(final File file)
     throws IOException {
         try (final FileReader is = new FileReader(file)) {
             final Model m = ModelReader.read(is, file.getAbsolutePath());
diff --git a/featuremodel/feature-modelconverter/src/test/java/org/apache/sling/feature/modelconverter/impl/ModelConverterTest.java b/featuremodel/feature-modelconverter/src/test/java/org/apache/sling/feature/modelconverter/impl/ModelConverterTest.java
new file mode 100644
index 0000000..bba43c5
--- /dev/null
+++ b/featuremodel/feature-modelconverter/src/test/java/org/apache/sling/feature/modelconverter/impl/ModelConverterTest.java
@@ -0,0 +1,211 @@
+/*
+ * 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.modelconverter.impl;
+
+import org.apache.sling.feature.support.ArtifactManager;
+import org.apache.sling.feature.support.ArtifactManagerConfig;
+import org.apache.sling.provisioning.model.Artifact;
+import org.apache.sling.provisioning.model.ArtifactGroup;
+import org.apache.sling.provisioning.model.Configuration;
+import org.apache.sling.provisioning.model.Feature;
+import org.apache.sling.provisioning.model.KeyValueMap;
+import org.apache.sling.provisioning.model.Model;
+import org.apache.sling.provisioning.model.RunMode;
+import org.apache.sling.provisioning.model.Section;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class ModelConverterTest {
+    private Path tempDir;
+    private ArtifactManager artifactManager;
+
+    @Before
+    public void setup() throws Exception {
+        tempDir = Files.createTempDirectory(getClass().getSimpleName());
+        artifactManager = ArtifactManager.getArtifactManager(
+                new ArtifactManagerConfig());
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Delete the temp dir again
+        Files.walk(tempDir)
+            .sorted(Comparator.reverseOrder())
+            .map(Path::toFile)
+            .forEach(File::delete);
+    }
+
+    @Test
+    public void testBoot() throws Exception {
+        File inFile = new File(getClass().getResource("/boot.json").toURI());
+        File outFile = new File(tempDir.toFile(), "/boot.generated.txt");
+
+        FeatureToProvisioning.convert(inFile, outFile.getAbsolutePath(),
+                artifactManager);
+
+        File expectedFile = new File(getClass().getResource("/boot.txt").toURI());
+        Model expected = ProvisioningToFeature.readProvisioningModel(expectedFile);
+        Model actual = ProvisioningToFeature.readProvisioningModel(outFile);
+        assertModelsEqual(expected, actual);
+    }
+
+    private void assertModelsEqual(Model expected, Model actual) {
+        for (int i=0; i<expected.getFeatures().size(); i++) {
+            Feature expectedFeature = expected.getFeatures().get(i);
+            Feature actualFeature = actual.getFeatures().get(i);
+            assertFeaturesEqual(expectedFeature, actualFeature);
+        }
+    }
+
+    private void assertFeaturesEqual(Feature expected, Feature actual) {
+        assertEquals(expected.getName(), actual.getName());
+        assertEquals(expected.getVersion(), actual.getVersion());
+        assertEquals(expected.getType(), actual.getType());
+        assertRunModesEqual(expected.getRunModes(), actual.getRunModes());
+        assertKVMapEquals(expected.getVariables(), actual.getVariables());
+        assertSectionsEqual(expected.getAdditionalSections(), actual.getAdditionalSections());
+    }
+
+    private void assertRunModesEqual(List<RunMode> expected, List<RunMode> actual) {
+        assertEquals(expected.size(), actual.size());
+        for (RunMode rm : expected) {
+            boolean found = false;
+            for (RunMode arm : actual) {
+                if (runModesEqual(rm, arm)) {
+                    found = true;
+                    break;
+                }
+
+            }
+            if (!found) {
+                fail("Run Mode " + rm + " not found in actual list " + actual);
+            }
+        }
+    }
+
+    private boolean runModesEqual(RunMode rm1, RunMode rm2) {
+        if (rm1.getNames() == null) {
+            if (rm2.getNames() != null)
+                return false;
+        } else {
+            HashSet<String> names1 = new HashSet<>(Arrays.asList(rm1.getNames()));
+            HashSet<String> names2 = new HashSet<>(Arrays.asList(rm2.getNames()));
+
+            if (!names1.equals(names2))
+                return false;
+        }
+
+        List<ArtifactGroup> ag1 = rm1.getArtifactGroups();
+        List<ArtifactGroup> ag2 = rm2.getArtifactGroups();
+        if (ag1.size() != ag2.size())
+            return false;
+
+        for (ArtifactGroup g1 : ag1) {
+            boolean found = false;
+            for (ArtifactGroup g2 : ag2) {
+                if (artifactGroupsEquals(g1, g2)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found)
+                return false;
+        }
+
+        List<Configuration> configs1 = new ArrayList<>();
+        rm1.getConfigurations().iterator().forEachRemaining(configs1::add);
+        List<Configuration> configs2 = new ArrayList<>();
+        rm2.getConfigurations().iterator().forEachRemaining(configs2::add);
+        if (configs1.size() != configs2.size())
+            return false;
+
+        for (int i=0; i < configs1.size(); i++) {
+            Configuration cfg1 = configs1.get(i);
+            Configuration cfg2 = configs2.get(i);
+            if (!cfg1.getProperties().equals(cfg2.getProperties()))
+                return false;
+        }
+
+        Map<String, String> m1 = kvToMap(rm1.getSettings());
+        Map<String, String> m2 = kvToMap(rm2.getSettings());
+
+        return m1.equals(m2);
+    }
+
+    private Map<String, String> kvToMap(KeyValueMap<String> kvm) {
+        Map<String, String> m = new HashMap<>();
+
+        for (Map.Entry<String, String> entry : kvm) {
+            m.put(entry.getKey(), entry.getValue());
+        }
+
+        return m;
+    }
+
+    private boolean artifactGroupsEquals(ArtifactGroup g1, ArtifactGroup g2) {
+        if (g1.getStartLevel() != g2.getStartLevel())
+            return false;
+
+        List<Artifact> al1 = new ArrayList<>();
+        g1.iterator().forEachRemaining(al1::add);
+
+        List<Artifact> al2 = new ArrayList<>();
+        g2.iterator().forEachRemaining(al2::add);
+
+        for (int i=0; i < al1.size(); i++) {
+            Artifact a1 = al1.get(i);
+            Artifact a2 = al2.get(i);
+            if (a1.compareTo(a2) != 0)
+                return false;
+        }
+        return true;
+    }
+
+    private void assertKVMapEquals(KeyValueMap<String> expected, KeyValueMap<String> actual) {
+        assertEquals(expected.size(), actual.size());
+        for (Map.Entry<String, String> entry : expected) {
+            assertEquals(entry.getValue(), actual.get(entry.getKey()));
+        }
+    }
+
+    private void assertSectionsEqual(List<Section> expected, List<Section> actual) {
+        assertEquals(expected.size(), actual.size());
+
+        for (int i=0; i<expected.size(); i++) {
+            Section esec = expected.get(i);
+            Section asec = actual.get(i);
+            assertEquals(esec.getName(), asec.getName());
+            assertEquals(esec.getContents(), asec.getContents());
+            assertEquals(esec.getAttributes(), asec.getAttributes());
+        }
+    }
+}
diff --git a/featuremodel/feature-modelconverter/src/test/resources/boot.json b/featuremodel/feature-modelconverter/src/test/resources/boot.json
new file mode 100644
index 0000000..2a17a40
--- /dev/null
+++ b/featuremodel/feature-modelconverter/src/test/resources/boot.json
@@ -0,0 +1,41 @@
+{
+    "#": "The model version defaults to 1 if not specified",
+    "model-version": "1",
+    
+    "id": "org.apache.sling.simple/boot/1.0.0",
+    "variables": {
+        "slf4j.version": "1.7.25",
+        
+        "#": "The model name when transformed to the provisioning model",
+        "provisioning.model.name": ":boot"
+    },
+    "bundles": 
+        ["org.slf4j/slf4j-api/${slf4j.version}",
+        "org.apache.sling/org.apache.sling.commons.log/5.1.0",
+        "org.apache.sling/org.apache.sling.commons.logservice/1.0.6",
+        "org.slf4j/jcl-over-slf4j/${slf4j.version}",
+        "org.slf4j/log4j-over-slf4j/${slf4j.version}",
+        "org.apache.sling/org.apache.sling.settings/1.3.8",
+        "org.apache.sling/org.apache.sling.fragment.xml/1.0.2",
+        "org.apache.sling/org.apache.sling.fragment.transaction/1.0.0",
+        "org.apache.sling/org.apache.sling.javax.activation/0.1.0",
+        "org.apache.sling/org.apache.sling.fragment.ws/1.0.2",
+        "org.apache.sling/org.apache.sling.launchpad.installer/1.2.2",
+        "org.apache.sling/org.apache.sling.installer.core/3.8.12",
+        "org.apache.sling/org.apache.sling.installer.provider.file/1.1.0",
+        "org.apache.sling/org.apache.sling.installer.factory.configuration/1.1.2",
+        "org.apache.felix/org.apache.felix.configadmin/1.8.16",
+        "org.apache.felix/org.apache.felix.eventadmin/1.4.10",
+        "org.apache.aries/org.apache.aries.util/1.1.3",
+        "org.apache.geronimo.specs/geronimo-atinject_1.0_spec/1.0"],
+        
+    "framework-properties": {
+        "# oak_tar and oak_mongo run modes are mutually exclusive":
+        "# and cannot be changed after the first startup",
+    
+        "sling.run.mode.install.options": "oak_tar,oak_mongo",
+        "repository.home": "${sling.home}/repository",
+        "localIndexDir": "${sling.home}/repository/index",
+        "#": "${sling.home} needs to be provided at launch time"
+    }
+}
diff --git a/featuremodel/feature-modelconverter/src/test/resources/boot.txt b/featuremodel/feature-modelconverter/src/test/resources/boot.txt
new file mode 100644
index 0000000..b8d2e7f
--- /dev/null
+++ b/featuremodel/feature-modelconverter/src/test/resources/boot.txt
@@ -0,0 +1,26 @@
+[feature name=:boot]
+
+[settings]
+  localIndexDir=${sling.home}/repository/index
+  repository.home=${sling.home}/repository
+  sling.run.mode.install.options=oak_tar,oak_mongo
+
+[artifacts startLevel=20]
+  org.apache.aries/org.apache.aries.util/1.1.3
+  org.apache.felix/org.apache.felix.configadmin/1.8.16
+  org.apache.felix/org.apache.felix.eventadmin/1.4.10
+  org.apache.geronimo.specs/geronimo-atinject_1.0_spec/1.0
+  org.apache.sling/org.apache.sling.commons.log/5.1.0
+  org.apache.sling/org.apache.sling.commons.logservice/1.0.6
+  org.apache.sling/org.apache.sling.fragment.transaction/1.0.0
+  org.apache.sling/org.apache.sling.fragment.ws/1.0.2
+  org.apache.sling/org.apache.sling.fragment.xml/1.0.2
+  org.apache.sling/org.apache.sling.installer.core/3.8.12
+  org.apache.sling/org.apache.sling.installer.factory.configuration/1.1.2
+  org.apache.sling/org.apache.sling.installer.provider.file/1.1.0
+  org.apache.sling/org.apache.sling.javax.activation/0.1.0
+  org.apache.sling/org.apache.sling.launchpad.installer/1.2.2
+  org.apache.sling/org.apache.sling.settings/1.3.8
+  org.slf4j/jcl-over-slf4j/1.7.25
+  org.slf4j/log4j-over-slf4j/1.7.25
+  org.slf4j/slf4j-api/1.7.25
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
index d6f8bd7..2e30e38 100644
--- a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
+++ b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONReader.java
@@ -19,6 +19,7 @@ package org.apache.sling.feature.support.json;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.Include;
+import org.apache.sling.feature.KeyValueMap;
 import org.apache.sling.feature.OSGiCapability;
 import org.apache.sling.feature.OSGiRequirement;
 import org.osgi.resource.Capability;
@@ -143,8 +144,7 @@ public class FeatureJSONReader extends JSONReaderBase {
         this.feature.setVendor(getProperty(map, JSONConstants.FEATURE_VENDOR));
         this.feature.setLicense(getProperty(map, JSONConstants.FEATURE_LICENSE));
 
-        this.variables = this.readVariables(map);
-
+        this.readVariables(map, feature.getVariables());
         this.readBundles(map, feature.getBundles(), feature.getConfigurations());
         this.readFrameworkProperties(map, feature.getFrameworkProperties());
         this.readConfigurations(map, feature.getConfigurations());
@@ -213,8 +213,8 @@ public class FeatureJSONReader extends JSONReaderBase {
         return sb.toString();
     }
 
-    private Map<String, String> readVariables(Map<String, Object> map) throws IOException {
-        Map<String, String> varMap = new HashMap<>();
+    private void readVariables(Map<String, Object> map, KeyValueMap kvMap) throws IOException {
+        variables = new HashMap<>();
 
         if (map.containsKey(JSONConstants.FEATURE_VARIABLES)) {
             final Object variablesObj = map.get(JSONConstants.FEATURE_VARIABLES);
@@ -224,13 +224,16 @@ public class FeatureJSONReader extends JSONReaderBase {
             final Map<String, Object> vars = (Map<String, Object>) variablesObj;
             for (final Map.Entry<String, Object> entry : vars.entrySet()) {
                 checkType("variable value", entry.getValue(), String.class, Boolean.class, Number.class);
-                if (varMap.get(entry.getKey()) != null) {
-                    throw new IOException(this.exceptionPrefix + "Duplicate variable " + entry.getKey());
+
+                String key = entry.getKey();
+                if (kvMap.get(key) != null) {
+                    throw new IOException(this.exceptionPrefix + "Duplicate variable " + key);
                 }
-                varMap.put(entry.getKey(), "" + entry.getValue());
+                String value = "" + entry.getValue();
+                kvMap.put(key, value);
+                variables.put(key, value);
             }
         }
-        return varMap;
     }
 
     private void readIncludes(final Map<String, Object> map) throws IOException {
diff --git a/featuremodel/feature/src/main/java/org/apache/sling/feature/Feature.java b/featuremodel/feature/src/main/java/org/apache/sling/feature/Feature.java
index 874790d..b1ad406 100644
--- a/featuremodel/feature/src/main/java/org/apache/sling/feature/Feature.java
+++ b/featuremodel/feature/src/main/java/org/apache/sling/feature/Feature.java
@@ -16,13 +16,13 @@
  */
 package org.apache.sling.feature;
 
+import org.osgi.resource.Capability;
+import org.osgi.resource.Requirement;
+
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
 
-import org.osgi.resource.Capability;
-import org.osgi.resource.Requirement;
-
 /**
  * A feature consists of
  * <ul>
@@ -53,6 +53,8 @@ public class Feature implements Comparable<Feature> {
 
     private final Extensions extensions = new Extensions();
 
+    private final KeyValueMap variables = new KeyValueMap();
+
     /** The optional location. */
     private volatile String location;
 
@@ -204,6 +206,14 @@ public class Feature implements Comparable<Feature> {
     }
 
     /**
+     * Obtain the variables of the feature
+     * @return The variables
+     */
+    public KeyValueMap getVariables() {
+        return this.variables;
+    }
+
+    /**
      * Get the vendor
      * @return The vendor or {@code null}
      */
@@ -275,6 +285,9 @@ public class Feature implements Comparable<Feature> {
         result.setLicense(this.getLicense());
         result.setAssembled(this.isAssembled());
 
+        // variables
+        result.getVariables().putAll(this.getVariables());
+
         // bundles
         for(final Artifact b : this.getBundles()) {
             final Artifact c = new Artifact(b.getId());

-- 
To stop receiving notification emails like this one, please contact
davidb@apache.org.