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/21 16:25:18 UTC

[sling-whiteboard] branch master updated: Write configurations associated with artifacts alongside these

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 19f026e  Write configurations associated with artifacts alongside these
19f026e is described below

commit 19f026e21396f95136785c620c8b8c341dc97150
Author: David Bosschaert <da...@gmail.com>
AuthorDate: Wed Mar 21 16:24:39 2018 +0000

    Write configurations associated with artifacts alongside these
---
 .../modelconverter/impl/ProvisioningToFeature.java | 73 +++++++++++++++-----
 .../modelconverter/impl/ModelConverterTest.java    | 78 ++++++++++++++++++++--
 .../feature/support/json/FeatureJSONWriter.java    |  7 +-
 .../sling/feature/support/json/JSONWriterBase.java | 29 ++++++--
 .../sling/feature/support/json/WriteOption.java    | 23 +++++++
 5 files changed, 180 insertions(+), 30 deletions(-)

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 a521780..5d0ac7c 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
@@ -30,6 +30,7 @@ import org.apache.sling.feature.support.ArtifactManagerConfig;
 import org.apache.sling.feature.support.FeatureUtil;
 import org.apache.sling.feature.support.json.ApplicationJSONWriter;
 import org.apache.sling.feature.support.json.FeatureJSONWriter;
+import org.apache.sling.feature.support.json.WriteOption;
 import org.apache.sling.provisioning.model.Artifact;
 import org.apache.sling.provisioning.model.ArtifactGroup;
 import org.apache.sling.provisioning.model.Configuration;
@@ -52,6 +53,7 @@ import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashSet;
@@ -67,7 +69,7 @@ public class ProvisioningToFeature {
     private static Logger LOGGER = LoggerFactory.getLogger(ProvisioningToFeature.class);
 
     public static void convert(File file, String output) {
-        Model model = createModel(Collections.singletonList(file), null, false);
+        Model model = createModel(Collections.singletonList(file), null, true, false);
         final List<org.apache.sling.feature.Feature> features = buildFeatures(model);
         if (features.size() != 1)
             throw new IllegalStateException("TODO");
@@ -77,7 +79,7 @@ public class ProvisioningToFeature {
 
     public static void convert(List<File> files,  String outputFile, String runModes, boolean createApp,
             boolean includeModelInfo, String propsFile) {
-        final Model model = createModel(files, runModes, includeModelInfo);
+        final Model model = createModel(files, runModes, false, includeModelInfo);
 
         if ( createApp ) {
             final Application app = buildApplication(model, propsFile);
@@ -99,27 +101,28 @@ public class ProvisioningToFeature {
      * @param includeModelInfo
      */
     private static Model createModel(final List<File> files,
-            final String runModes, boolean includeModelInfo) {
+            final String runModes, boolean allRunModes, boolean includeModelInfo) {
         LOGGER.info("Assembling model...");
+        ResolverOptions variableResolver = new ResolverOptions().variableResolver(new VariableResolver() {
+            @Override
+            public String resolve(final Feature feature, final String name) {
+                // Keep variables as-is in the model
+                return "${" + name + "}";
+            }
+        });
+
         Model model = null;
         for(final File initFile : files) {
             try {
-                model = processModel(model, initFile, includeModelInfo,
-                    new ResolverOptions().variableResolver(new VariableResolver() {
-                        @Override
-                        public String resolve(final Feature feature, final String name) {
-                            // Keep variables as-is in the model
-                            return "${" + name + "}";
-                        }
-                    })
-                );
+                model = processModel(model, initFile, includeModelInfo, variableResolver);
             } catch ( final IOException iae) {
                 LOGGER.error("Unable to read provisioning model {} : {}", initFile, iae.getMessage(), iae);
                 System.exit(1);
             }
         }
 
-        final Map<Traceable, String> errors = ModelUtility.validate(model);
+        final Model effectiveModel = ModelUtility.getEffectiveModel(model, variableResolver);
+        final Map<Traceable, String> errors = ModelUtility.validate(effectiveModel);
         if ( errors != null ) {
             LOGGER.error("Invalid assembled provisioning model.");
             for(final Map.Entry<Traceable, String> entry : errors.entrySet()) {
@@ -127,11 +130,24 @@ public class ProvisioningToFeature {
             }
             System.exit(1);
         }
-        final Set<String> modes = calculateRunModes(model, runModes);
+        final Set<String> modes;
+        if (allRunModes) {
+            modes = new HashSet<>();
+            for (Feature f : effectiveModel.getFeatures()) {
+                for (RunMode rm : f.getRunModes()) {
+                    String[] names = rm.getNames();
+                    if (names != null) {
+                        modes.addAll(Arrays.asList(names));
+                    }
+                }
+            }
+        } else {
+            modes = calculateRunModes(effectiveModel, runModes);
+        }
 
-        removeInactiveFeaturesAndRunModes(model, modes);
+        removeInactiveFeaturesAndRunModes(effectiveModel, modes);
 
-        return model;
+        return effectiveModel;
     }
 
     /**
@@ -420,6 +436,29 @@ public class ProvisioningToFeature {
                     final String key = keys.nextElement();
                     newCfg.getProperties().put(key, cfg.getProperties().get(key));
                 }
+
+                String[] runModeNames = runMode.getNames();
+                if (runModeNames != null) {
+                    // If this configuration is associated with a runmode other than null, attach it to a bundle
+                    // that has the same runmodes
+                    Artifact art = null;
+                    for (ArtifactGroup group : runMode.getArtifactGroups()) {
+                        if (art != null)
+                            break;
+
+                        for (Artifact artifact : group) {
+                            art = artifact;
+                            break;
+                        }
+                    }
+                    if (art == null) {
+                        throw new IllegalStateException("Should have at least one artifact in runmodes " +
+                                Arrays.toString(runModeNames) + " to attach configuration to");
+                    }
+
+                    newCfg.getProperties().put(org.apache.sling.feature.Configuration.PROP_ARTIFACT, art.toMvnUrl());
+                }
+
                 configurations.add(newCfg);
             }
 
@@ -491,7 +530,7 @@ public class ProvisioningToFeature {
 
         final File file = new File(out);
         try ( final FileWriter writer = new FileWriter(file)) {
-            FeatureJSONWriter.write(writer, f);
+            FeatureJSONWriter.write(writer, f, WriteOption.OLD_STYLE_FACTORY_CONFIGS);
         } catch ( final IOException ioe) {
             LOGGER.error("Unable to write feature to {} : {}", out, ioe.getMessage(), ioe);
             System.exit(1);
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
index bf35578..6facd22 100644
--- 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
@@ -93,6 +93,11 @@ public class ModelConverterTest {
         testConvertToProvisioningModel("/oak.json", "/oak.txt");
     }
 
+    @Test
+    public void testOakToFeature() throws Exception {
+        testConvertToFeature("/oak.txt", "/oak.json");
+    }
+
     public void testConvertToFeature(String originalProvModel, String expectedJSON) throws Exception {
         File inFile = new File(getClass().getResource(originalProvModel).toURI());
         File outFile = new File(tempDir.toFile(), expectedJSON + ".generated");
@@ -133,7 +138,7 @@ public class ModelConverterTest {
 
         assertFeatureKVMapEquals(expected.getVariables(), actual.getVariables());
         assertBundlesEqual(expected.getBundles(), actual.getBundles());
-        assertConfigurationsEqual(expected.getConfigurations(), actual.getConfigurations());
+        assertConfigurationsEqual(expected.getConfigurations(), actual.getConfigurations(), expected.getBundles(), actual.getBundles());
         assertFeatureKVMapEquals(expected.getFrameworkProperties(), actual.getFrameworkProperties());
 
         // Ignore caps and reqs, includes and extensions here since they cannot come from the prov model.
@@ -149,26 +154,86 @@ public class ModelConverterTest {
                 if (ac.getId().equals(ex.getId())) {
                     found = true;
                     assertFeatureKVMapEquals(ex.getMetadata(), ac.getMetadata());
+                    break;
                 }
             }
-            assertTrue(found);
+            assertTrue("Not found: " + ex, found);
         }
     }
 
-    private void assertConfigurationsEqual(Configurations expected, Configurations actual) {
+    private void assertConfigurationsEqual(Configurations expected, Configurations actual, Bundles exBundles, Bundles acBundles) {
         for (Iterator<org.apache.sling.feature.Configuration> it = expected.iterator(); it.hasNext(); ) {
             org.apache.sling.feature.Configuration ex = it.next();
 
             boolean found = false;
             for (Iterator<org.apache.sling.feature.Configuration> it2 = actual.iterator(); it2.hasNext(); ) {
                 org.apache.sling.feature.Configuration ac = it2.next();
-                if (ac.getPid().equals(ex.getPid())) {
+                if (ex.getPid() != null) {
+                    if (ex.getPid().equals(ac.getPid())) {
+                        found = true;
+                        assertConfigProps(ex, ac, exBundles, acBundles);
+                    }
+                } else {
+                    if (ex.getFactoryPid().equals(ac.getFactoryPid())) {
+                        found = true;
+                        assertConfigProps(ex, ac, exBundles, acBundles);
+                    }
+                }
+            }
+            assertTrue(found);
+        }
+    }
+
+    private void assertConfigProps(org.apache.sling.feature.Configuration expected, org.apache.sling.feature.Configuration actual, Bundles exBundles, Bundles acBundles) {
+        // If the configuration is associated with an artifact, it's considered equal
+        // if both artifacts have the same runmode (as the configuration is really
+        // associated with the runmode.
+        Object art = expected.getProperties().remove(org.apache.sling.feature.Configuration.PROP_ARTIFACT);
+        if (art instanceof String) {
+            String expectedArtifact = (String) art;
+            String actualArtifact = (String) actual.getProperties().remove(org.apache.sling.feature.Configuration.PROP_ARTIFACT);
+
+            String expectedRunmodes = null;
+            for(Iterator<org.apache.sling.feature.Artifact> it = exBundles.iterator(); it.hasNext(); ) {
+                org.apache.sling.feature.Artifact a = it.next();
+                if (a.getId().toMvnId().equals(expectedArtifact)) {
+                    expectedRunmodes = a.getMetadata().get("run-modes");
+                }
+            }
+
+            boolean found = false;
+            for(Iterator<org.apache.sling.feature.Artifact> it = acBundles.iterator(); it.hasNext(); ) {
+                org.apache.sling.feature.Artifact a = it.next();
+                if (a.getId().toMvnId().equals(actualArtifact)) {
                     found = true;
-                    assertEquals(ex.getProperties(), ac.getProperties());
+                    assertEquals(expectedRunmodes, a.getMetadata().get("run-modes"));
+                    break;
                 }
             }
             assertTrue(found);
         }
+
+        assertTrue("Configurations not equal: " + expected.getProperties() + " vs " + actual.getProperties(),
+                configPropsEqual(expected.getProperties(), actual.getProperties()));
+    }
+
+    private boolean configPropsEqual(Dictionary<String, Object> d1, Dictionary<String, Object> d2) {
+        if (d1.size() != d2.size()) {
+            return false;
+        }
+
+        for (Enumeration<String> e = d1.keys(); e.hasMoreElements(); ) {
+            String k = e.nextElement();
+            Object v = d1.get(k);
+            if (v instanceof Object[]) {
+                if (!Arrays.equals((Object[]) v, (Object[]) d2.get(k)))
+                    return false;
+            } else {
+                if (!v.equals(d2.get(k)))
+                    return false;
+            }
+        }
+        return true;
     }
 
     private void assertModelsEqual(Model expected, Model actual) {
@@ -279,6 +344,7 @@ public class ModelConverterTest {
         return m1.equals(m2);
     }
 
+    // TODO can this one go?
     private Map<String, Object> cfgMap(Dictionary<String, Object> properties) {
         Map<String, Object> m = new HashMap<>();
         for (Enumeration<String> e = properties.keys(); e.hasMoreElements(); ) {
@@ -292,7 +358,7 @@ public class ModelConverterTest {
 
                     String[] kv = line.trim().split("=");
                     if (kv.length >= 2) {
-                        String v = kv[1].trim().replaceAll("[" +Pattern.quote("[") + "]\\s+[\"]", "[\"");
+                        String v = kv[1].trim().replaceAll("[" + Pattern.quote("[") + "]\\s+[\"]", "[\"");
                         v = v.replaceAll("[\"][,]\\s*[]]","\"]");
                         m.put(kv[0].trim(), v.trim());
                     }
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
index 28f6f71..8cf5f5b 100644
--- a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
+++ b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/FeatureJSONWriter.java
@@ -44,6 +44,9 @@ import static org.apache.sling.feature.support.util.ManifestUtil.marshalDirectiv
  * Simple JSON writer for a feature
  */
 public class FeatureJSONWriter extends JSONWriterBase {
+    private FeatureJSONWriter(WriteOption ... options) {
+        super(options);
+    }
 
     /**
      * Writes the feature to the writer.
@@ -52,9 +55,9 @@ public class FeatureJSONWriter extends JSONWriterBase {
      * @param feature Feature
      * @throws IOException If writing fails
      */
-    public static void write(final Writer writer, final Feature feature)
+    public static void write(final Writer writer, final Feature feature, final WriteOption ... options)
     throws IOException {
-        final FeatureJSONWriter w = new FeatureJSONWriter();
+        final FeatureJSONWriter w = new FeatureJSONWriter(options);
         w.writeFeature(writer, feature);
     }
 
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java
index ba02dc0..2dc1438 100644
--- a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java
+++ b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/JSONWriterBase.java
@@ -26,6 +26,7 @@ import org.apache.sling.feature.KeyValueMap;
 
 import java.io.StringReader;
 import java.lang.reflect.Array;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.List;
 import java.util.Map;
@@ -36,11 +37,20 @@ import javax.json.JsonObject;
 import javax.json.JsonObjectBuilder;
 import javax.json.JsonStructure;
 
-
 /**
  * Common functionality for writing JSON
  */
 abstract class JSONWriterBase {
+    private final char factoryConfigSeparator;
+
+    protected JSONWriterBase(WriteOption ... opts) {
+        if (Arrays.asList(opts).contains(WriteOption.OLD_STYLE_FACTORY_CONFIGS)) {
+            factoryConfigSeparator = '-';
+        } else {
+            factoryConfigSeparator = '~';
+        }
+    }
+
     protected void writeBundles(final JsonObjectBuilder ob,
             final Bundles bundles,
             final Configurations allConfigs) {
@@ -51,9 +61,16 @@ abstract class JSONWriterBase {
             for(final Artifact artifact : bundles) {
                 final Configurations cfgs = new Configurations();
                 for(final Configuration cfg : allConfigs) {
-                    final String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT);
-                    if (  artifact.getId().toMvnId().equals(artifactProp) ) {
-                        cfgs.add(cfg);
+                    String artifactProp = (String)cfg.getProperties().get(Configuration.PROP_ARTIFACT);
+                    if (artifactProp != null) {
+                        if (artifactProp.startsWith("mvn:")) {
+                            // Change Maven URL to maven GAV syntax
+                            artifactProp = artifactProp.substring("mvn:".length());
+                            artifactProp = artifactProp.replace('/', ':');
+                        }
+                        if (artifact.getId().toMvnId().equals(artifactProp)) {
+                            cfgs.add(cfg);
+                        }
                     }
                 }
                 KeyValueMap md = artifact.getMetadata();
@@ -79,6 +96,8 @@ abstract class JSONWriterBase {
                         bundleObj.add(me.getKey(), me.getValue());
                     }
 
+                    writeConfigurations(bundleObj, cfgs);
+
                     bundleArray.add(bundleObj.build());
                 }
             }
@@ -109,7 +128,7 @@ abstract class JSONWriterBase {
         for(final Configuration cfg : cfgs) {
             final String key;
             if ( cfg.isFactoryConfiguration() ) {
-                key = cfg.getFactoryPid() + "~" + cfg.getName();
+                key = cfg.getFactoryPid() + factoryConfigSeparator + cfg.getName();
             } else {
                 key = cfg.getPid();
             }
diff --git a/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/WriteOption.java b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/WriteOption.java
new file mode 100644
index 0000000..01f0158
--- /dev/null
+++ b/featuremodel/feature-support/src/main/java/org/apache/sling/feature/support/json/WriteOption.java
@@ -0,0 +1,23 @@
+package org.apache.sling.feature.support.json;
+/*
+ * 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.
+ */
+public enum WriteOption {
+    /* Write factory configurations out in the old style with a '-' separator.
+     * If this option is not used the new style with the '~' separator is used.
+     */
+    OLD_STYLE_FACTORY_CONFIGS
+}

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