You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2016/11/12 15:09:18 UTC

svn commit: r1769384 - in /sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model: ModelUtility.java io/ModelArchiveReader.java io/ModelArchiveWriter.java io/package-info.java

Author: cziegeler
Date: Sat Nov 12 15:09:17 2016
New Revision: 1769384

URL: http://svn.apache.org/viewvc?rev=1769384&view=rev
Log:
SLING-6278 : Provide tooling to create an archive with the provisioning model and all artifacts

Added:
    sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveReader.java   (with props)
    sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveWriter.java   (with props)
Modified:
    sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
    sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/package-info.java

Modified: sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java?rev=1769384&r1=1769383&r2=1769384&view=diff
==============================================================================
--- sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java (original)
+++ sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/ModelUtility.java Sat Nov 12 15:09:17 2016
@@ -167,14 +167,14 @@ public abstract class ModelUtility {
         for(final Feature feature : model.getFeatures() ) {
             // validate feature
             if ( feature.getName() == null || feature.getName().isEmpty() ) {
-                errors.put(feature, "Name is required for a feature.");
+                addError(errors, feature, "Name is required for a feature.");
             }
             // version should be a valid version
             if ( feature.getVersion() != null ) {
                 try {
                     new Version(feature.getVersion());
                 } catch ( final IllegalArgumentException iae) {
-                    errors.put(feature, "Version is not a valid version: " + feature.getVersion());
+                    addError(errors, feature, "Version is not a valid version: " + feature.getVersion());
                 }
             }
             for(final RunMode runMode : feature.getRunModes()) {
@@ -193,12 +193,12 @@ public abstract class ModelUtility {
                                         hasSpecial = 2;
                                     } else {
                                         hasSpecial = 2;
-                                        errors.put(runMode, "Invalid modes " + Arrays.toString(rm));
+                                        addError(errors, runMode, "Invalid modes " + Arrays.toString(rm));
                                         break;
                                     }
                                 } else {
                                     hasSpecial++;
-                                    errors.put(runMode, "Invalid modes " + Arrays.toString(rm));
+                                    addError(errors, runMode, "Invalid modes " + Arrays.toString(rm));
                                     break;
                                 }
 
@@ -212,7 +212,7 @@ public abstract class ModelUtility {
 
                 for(final ArtifactGroup sl : runMode.getArtifactGroups()) {
                     if ( sl.getStartLevel() < 0 ) {
-                        errors.put(sl, "Invalid start level " + sl.getStartLevel());
+                        addError(errors, sl, "Invalid start level " + sl.getStartLevel());
                     }
                     for(final Artifact a : sl) {
                         String error = null;
@@ -229,7 +229,7 @@ public abstract class ModelUtility {
                             error = (error != null ? error + ", " : "") + "type missing";
                         }
                         if (error != null) {
-                            errors.put(a, error);
+                            addError(errors, a, error);
                         }
                     }
                 }
@@ -246,12 +246,12 @@ public abstract class ModelUtility {
                         error = (error != null ? error + ", " : "") + "configuration properties missing";
                     }
                     if (error != null) {
-                        errors.put(c, error);
+                        addError(errors, c, error);
                     }
                 }
             }
         }
-        if ( errors.size() == 0 ) {
+        if ( errors.isEmpty()) {
             return null;
         }
         return errors;
@@ -260,7 +260,7 @@ public abstract class ModelUtility {
     /**
      * Applies a set of variables to the given model.
      * All variables that are referenced anywhere within the model are detected and passed to the given variable resolver.
-     * The variable resolver may look up variables on it's own, or fallback to the variables already defined for the feature.
+     * The variable resolver may look up variables on it's own, or fall back to the variables already defined for the feature.
      * All resolved variable values are collected and put to the "variables" section of the resulting model.
      * @param model Original model
      * @param resolver Variable resolver
@@ -346,4 +346,38 @@ public abstract class ModelUtility {
         return versionUpdater.process(model);
     }
 
+    /**
+     * Validates the model and checks that each feature has a valid version.
+     *
+     * This method first calls {@link #validate(Model)} and then checks
+     * that each feature has a version.
+     *
+     * @param model The model to validate
+     * @return A map with errors or {@code null} if valid.
+     * @since 1.9
+     */
+    public static Map<Traceable, String> validateIncludingVersion(final Model model) {
+        Map<Traceable, String> errors = validate(model);
+        for(final Feature feature : model.getFeatures()) {
+            if ( feature.getVersion() == null ) {
+                if ( errors == null ) {
+                    errors = new HashMap<>();
+                }
+                addError(errors, feature, "Feature must have a version.");
+            }
+        }
+        return errors;
+    }
+
+    /**
+     * Add an error for the {@code Traceable} to the error map
+     * @param errors The map of errors
+     * @param object The traceable object
+     * @param error The error message
+     * @since 1.9
+     */
+    private static void addError(final Map<Traceable, String> errors, final Traceable object, final String error) {
+        String value = errors.get(object);
+        errors.put(object, (value == null ? error : value + " " + error));
+    }
 }

Added: sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveReader.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveReader.java?rev=1769384&view=auto
==============================================================================
--- sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveReader.java (added)
+++ sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveReader.java Sat Nov 12 15:09:17 2016
@@ -0,0 +1,103 @@
+/*
+ * 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.provisioning.model.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.sling.provisioning.model.Artifact;
+import org.apache.sling.provisioning.model.Model;
+import org.apache.sling.provisioning.model.ModelUtility;
+
+/**
+ * The model archive reader can be used to read an archive based on a model
+ * The archive contains the model file and all artifacts.
+ * @since 1.3
+ */
+public class ModelArchiveReader {
+
+    public interface ArtifactConsumer {
+
+        /**
+         * Consume the artifact from the archive
+         * The input stream must not be closed by the consumer.
+         * @param artifact The artifact
+         * @param is The input stream for the artifact
+         * @throws IOException If the artifact can't be consumed
+         */
+        void consume(Artifact artifact, final InputStream is) throws IOException;
+    }
+
+    /**
+     * Read a model archive.
+     * The input stream is not closed. It is up to the caller to close the input stream.
+     * @param in The input stream to read from.
+     * @return The model
+     * @throws IOException If anything goes wrong
+     */
+    @SuppressWarnings("resource")
+    public static Model read(final InputStream in,
+                             final ArtifactConsumer consumer)
+    throws IOException {
+        Model model = null;
+
+        final JarInputStream jis = new JarInputStream(in);
+
+        // check manifest
+        final Manifest manifest = jis.getManifest();
+        if ( manifest == null ) {
+            throw new IOException("Not a model archive - manifest is missing.");
+        }
+        // check manifest header
+        final String version = manifest.getMainAttributes().getValue(ModelArchiveWriter.MANIFEST_HEADER);
+        if ( version == null ) {
+            throw new IOException("Not a model archive - manifest header is missing.");
+        }
+        // validate manifest header
+        try {
+            final int number = Integer.valueOf(version);
+            if ( number < 1 || number > ModelArchiveWriter.ARCHIVE_VERSION ) {
+                throw new IOException("Not a model archive - invalid manifest header value: " + version);
+            }
+        } catch (final NumberFormatException nfe) {
+            throw new IOException("Not a model archive - invalid manifest header value: " + version);
+        }
+
+        // read contents
+        JarEntry entry = null;
+        while ( ( entry = jis.getNextJarEntry() ) != null ) {
+            if ( ModelArchiveWriter.MODEL_NAME.equals(entry.getName()) ) {
+                model = ModelUtility.getEffectiveModel(ModelReader.read(new InputStreamReader(jis, "UTF-8"), null));
+            } else if ( !entry.isDirectory() && entry.getName().startsWith(ModelArchiveWriter.ARTIFACTS_PREFIX) ) { // artifact
+                final Artifact artifact = Artifact.fromMvnUrl("mvn:" + entry.getName().substring(ModelArchiveWriter.ARTIFACTS_PREFIX.length()));
+                consumer.consume(artifact, jis);
+            }
+            jis.closeEntry();
+        }
+        if ( model == null ) {
+            throw new IOException("Not a model archive - model file is missing.");
+        }
+
+        // TODO - we could check whether all artifacts from the model are in the archive
+
+        return model;
+    }
+}

Propchange: sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveReader.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveReader.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveWriter.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveWriter.java?rev=1769384&view=auto
==============================================================================
--- sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveWriter.java (added)
+++ sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveWriter.java Sat Nov 12 15:09:17 2016
@@ -0,0 +1,137 @@
+/*
+ * 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.provisioning.model.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import org.apache.sling.provisioning.model.Artifact;
+import org.apache.sling.provisioning.model.ArtifactGroup;
+import org.apache.sling.provisioning.model.Feature;
+import org.apache.sling.provisioning.model.Model;
+import org.apache.sling.provisioning.model.ModelUtility;
+import org.apache.sling.provisioning.model.RunMode;
+import org.apache.sling.provisioning.model.Traceable;
+
+/**
+ * The model archive writer can be used to create an archive based on a model
+ * The archive contains the model file and all artifacts.
+ * @since 1.3
+ */
+public class ModelArchiveWriter {
+
+    /** The manifest header marking an archive as a model archive. */
+    public static final String MANIFEST_HEADER = "Model-Archive-Version";
+
+    /** Current support version of the model archive. */
+    public static final int ARCHIVE_VERSION = 1;
+
+    /** Default extension for model archives. */
+    public static final String DEFAULT_EXTENSION = "mar";
+
+    /** Model name. */
+    public static final String MODEL_NAME = "models/feature.model";
+
+    /** Artifacts prefix. */
+    public static final String ARTIFACTS_PREFIX = "artifacts/";
+
+    public interface ArtifactProvider {
+
+        /**
+         * Provide an input stream for the artifact.
+         * The input stream will be closed by the caller.
+         * @param artifact The artifact
+         * @return The input stream
+         * @throws IOException If the input stream can't be provided
+         */
+        InputStream getInputStream(Artifact artifact) throws IOException;
+    }
+
+    /**
+     * Create a model archive.
+     * The output stream will not be closed by this method. The caller
+     * must call {@link JarOutputStream#close()} or {@link JarOutputStream#finish()}
+     * on the return output stream. The caller can add additional files through
+     * the return stream.
+     *
+     * In order to create an archive for a model, each feature in the model must
+     * have a name and a version and the model must be valid, therefore {@link ModelUtility#validateIncludingVersion(Model)}
+     * is called first. If the model is invalid an {@code IOException} is thrown.
+     *
+     * @param out The output stream to write to
+     * @param model The model to write
+     * @param baseManifest Optional base manifest used for creating the manifest.
+     * @param provider The artifact provider
+     * @return The jar output stream.
+     * @throws IOException If anything goes wrong
+     */
+    public static JarOutputStream write(final OutputStream out,
+                             final Model model,
+                             final Manifest baseManifest,
+                             final ArtifactProvider provider)
+    throws IOException {
+        // check model
+        final Map<Traceable, String> errors = ModelUtility.validate(model);
+        if ( errors != null ) {
+            throw new IOException("Model is not valid: " + errors);
+        }
+
+        // create manifest
+        final Manifest manifest = (baseManifest == null ? new Manifest() : new Manifest(baseManifest));
+        manifest.getMainAttributes().putValue("Manifest-Version", "1.0");
+        manifest.getMainAttributes().putValue(MANIFEST_HEADER, String.valueOf(ARCHIVE_VERSION));
+
+        // create archive
+        final JarOutputStream jos = new JarOutputStream(out, manifest);
+
+        // write model first
+        final JarEntry entry = new JarEntry(MODEL_NAME);
+        jos.putNextEntry(entry);
+        final Writer writer = new OutputStreamWriter(jos, "UTF-8");
+        ModelWriter.write(writer, model);
+        writer.flush();
+        jos.closeEntry();
+
+        final byte[] buffer = new byte[1024*1024*256];
+        for(final Feature f : model.getFeatures() ) {
+            for(final RunMode rm : f.getRunModes()) {
+                for(final ArtifactGroup g : rm.getArtifactGroups()) {
+                    for(final Artifact a : g) {
+                        final JarEntry artifactEntry = new JarEntry(ARTIFACTS_PREFIX + a.getRepositoryPath());
+                        jos.putNextEntry(artifactEntry);
+
+                        try (final InputStream is = provider.getInputStream(a)) {
+                            int l = 0;
+                            while ( (l = is.read(buffer)) > 0 ) {
+                                jos.write(buffer, 0, l);
+                            }
+                        }
+                        jos.closeEntry();
+                    }
+                }
+            }
+        }
+        return jos;
+    }
+}

Propchange: sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveWriter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/ModelArchiveWriter.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Modified: sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/package-info.java?rev=1769384&r1=1769383&r2=1769384&view=diff
==============================================================================
--- sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/package-info.java (original)
+++ sling/trunk/tooling/support/provisioning-model/src/main/java/org/apache/sling/provisioning/model/io/package-info.java Sat Nov 12 15:09:17 2016
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@org.osgi.annotation.versioning.Version("1.2")
+@org.osgi.annotation.versioning.Version("1.3")
 package org.apache.sling.provisioning.model.io;