Add support for archives
authorCarsten Ziegeler <cziegeler@apache.org>
Sat, 12 Nov 2016 15:13:35 +0000 (15:13 +0000)
committerCarsten Ziegeler <cziegeler@apache.org>
Sat, 12 Nov 2016 15:13:35 +0000 (15:13 +0000)
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1769388 13f79535-47bb-0310-9956-ffa450edef68

pom.xml
src/main/java/org/apache/sling/installer/factory/model/impl/AbstractModelTask.java
src/main/java/org/apache/sling/installer/factory/model/impl/InstallModelTask.java
src/main/java/org/apache/sling/installer/factory/model/impl/ModelTransformer.java
src/main/java/org/apache/sling/installer/factory/model/impl/UninstallModelTask.java

diff --git a/pom.xml b/pom.xml
index 87fae4a..5a600d6 100644 (file)
--- a/pom.xml
+++ b/pom.xml
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.provisioning.model</artifactId>
-            <version>1.7.0</version>
+            <version>1.7.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
     </dependencies>
index 7fe3151..9f36b21 100644 (file)
@@ -18,6 +18,7 @@
  */
 package org.apache.sling.installer.factory.model.impl;
 
+import java.io.File;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -73,9 +74,16 @@ public abstract class AbstractModelTask extends InstallTask {
         return service;
     }
 
-    protected String getModelName() {
-        final String url = this.getResource().getURL();
-        final int lastSlash = url.lastIndexOf('/');
-        return lastSlash == -1 ? url : url.substring(lastSlash + 1);
+    protected void deleteDirectory(final File dir) {
+        if ( dir.exists() ) {
+            for(final File f : dir.listFiles()) {
+                if ( f.isDirectory() ) {
+                    deleteDirectory(f);
+                } else {
+                    f.delete();
+                }
+            }
+            dir.delete();
+        }
     }
 }
index 0cf2fac..063aefa 100644 (file)
@@ -20,14 +20,17 @@ package org.apache.sling.installer.factory.model.impl;
 
 import java.io.File;
 import java.io.FileInputStream;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.Reader;
 import java.io.StringReader;
 import java.util.ArrayList;
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -52,6 +55,7 @@ import org.apache.sling.provisioning.model.ModelUtility;
 import org.apache.sling.provisioning.model.RunMode;
 import org.apache.sling.provisioning.model.Section;
 import org.apache.sling.provisioning.model.Traceable;
+import org.apache.sling.provisioning.model.io.ModelArchiveReader;
 import org.apache.sling.provisioning.model.io.ModelReader;
 import org.apache.sling.repoinit.parser.RepoInitParser;
 import org.apache.sling.repoinit.parser.RepoInitParsingException;
@@ -90,55 +94,68 @@ public class InstallModelTask extends AbstractModelTask {
         try {
             final TaskResource resource = this.getResource();
             final String modelTxt = (String) resource.getAttribute(ModelTransformer.ATTR_MODEL);
-            if ( modelTxt == null ) {
+            final Integer featureIndex = (Integer) resource.getAttribute(ModelTransformer.ATTR_FEATURE_INDEX);
+            final String name = (String) resource.getAttribute(ModelTransformer.ATTR_FEATURE_NAME);
+            if ( modelTxt == null || featureIndex == null || name == null ) {
                 ctx.log("Unable to install model resource {} : no model found", this.getResource());
                 this.getResourceGroup().setFinishState(ResourceState.IGNORED);
             } else {
-                final String name = this.getModelName();
-                final Result result = this.transform(name, modelTxt);
-                if ( result == null ) {
-                    ctx.log("Unable to install model resource {} : unable to create resources", this.getResource());
-                    this.getResourceGroup().setFinishState(ResourceState.IGNORED);
-                } else {
-                    // repo init first
-                    if ( result.repoinit != null ) {
-                        List<Operation> ops = null;
-                        try ( final Reader r = new StringReader(result.repoinit) ) {
-                            ops = this.repoInitParser.parse(r);
-                        } catch (final IOException | RepoInitParsingException e) {
-                            logger.error("Unable to parse repoinit block.", e);
-                            ctx.log("Unable to install model resource {} : unable parse repoinit block.", this.getResource());
-                            this.getResourceGroup().setFinishState(ResourceState.IGNORED);
-                            return;
-                        }
+                final String path = (String) resource.getAttribute(ModelTransformer.ATTR_BASE_PATH);
+                final File baseDir = (path == null ? null : new File(path));
+
+                boolean success = false;
+                try {
+                    final Result result = this.transform(name, modelTxt, featureIndex, resource, baseDir);
+                    if ( result == null ) {
+                        ctx.log("Unable to install model resource {} : unable to create resources", this.getResource());
+                        this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                    } else {
+                        // repo init first
+                        if ( result.repoinit != null ) {
+                            List<Operation> ops = null;
+                            try ( final Reader r = new StringReader(result.repoinit) ) {
+                                ops = this.repoInitParser.parse(r);
+                            } catch (final IOException | RepoInitParsingException e) {
+                                logger.error("Unable to parse repoinit block.", e);
+                                ctx.log("Unable to install model resource {} : unable parse repoinit block.", this.getResource());
+                                this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                                return;
+                            }
+
+                            // login admin is required for repo init
+                            Session session = null;
+                            try {
+                                session = this.repository.loginAdministrative(null);
+                                this.repoInitProcessor.apply(session, ops);
+                                session.save();
+                            } catch ( final RepositoryException re) {
+                                logger.error("Unable to process repoinit block.", re);
+                                ctx.log("Unable to install model resource {} : unable to process repoinit block.", this.getResource());
+                                this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                                return;
 
-                        // login admin is required for repo init
-                        Session session = null;
-                        try {
-                            session = this.repository.loginAdministrative(null);
-                            this.repoInitProcessor.apply(session, ops);
-                            session.save();
-                        } catch ( final RepositoryException re) {
-                            logger.error("Unable to process repoinit block.", re);
-                            ctx.log("Unable to install model resource {} : unable to process repoinit block.", this.getResource());
-                            this.getResourceGroup().setFinishState(ResourceState.IGNORED);
-                            return;
-
-                        } finally {
-                            if ( session != null ) {
-                                session.logout();
+                            } finally {
+                                if ( session != null ) {
+                                    session.logout();
+                                }
                             }
                         }
-                    }
-                    if ( !result.resources.isEmpty() ) {
-                        final OsgiInstaller installer = this.getService(OsgiInstaller.class);
-                        if ( installer != null ) {
-                            installer.registerResources("model-" + name, result.resources.toArray(new InstallableResource[result.resources.size()]));
-                            this.getResourceGroup().setFinishState(ResourceState.INSTALLED);
-                        } else {
-                            ctx.log("Unable to install model resource {} : unable to get OSGi installer", this.getResource());
-                            this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                        if ( !result.resources.isEmpty() ) {
+                            final OsgiInstaller installer = this.getService(OsgiInstaller.class);
+                            if ( installer != null ) {
+                                installer.registerResources("model-" + name, result.resources.toArray(new InstallableResource[result.resources.size()]));
+                            } else {
+                                ctx.log("Unable to install model resource {} : unable to get OSGi installer", this.getResource());
+                                this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                                return;
+                            }
                         }
+                        this.getResourceGroup().setFinishState(ResourceState.INSTALLED);
+                        success = true;
+                    }
+                } finally {
+                    if ( !success && baseDir != null ) {
+                        this.deleteDirectory(baseDir);
                     }
                 }
             }
@@ -152,17 +169,77 @@ public class InstallModelTask extends AbstractModelTask {
         public String repoinit;
     }
 
-    private Result transform(final String name, final String modelText) {
+    private Result transform(final String name,
+            final String modelText,
+            final int featureIndex,
+            final TaskResource rsrc,
+            final File baseDir) {
+        Model model = null;
         try ( final Reader reader = new StringReader(modelText)) {
-            final Model model = ModelUtility.getEffectiveModel(ModelReader.read(reader, name));
+           model = ModelUtility.getEffectiveModel(ModelReader.read(reader, name));
+        } catch ( final IOException ioe) {
+            logger.warn("Unable to read model file for feature " + name, ioe);
+        }
+        if ( model == null ) {
+            return null;
+        }
+        int index = 0;
+        final Iterator<Feature> iter = model.getFeatures().iterator();
+        while ( iter.hasNext() ) {
+            iter.next();
+            if ( index != featureIndex ) {
+                iter.remove();
+            }
+            index++;
+        }
 
-            final List<ArtifactDescription> files = new ArrayList<>();
+        if ( baseDir != null ) {
+            final List<Artifact> artifacts = new ArrayList<>();
+            final Feature feature = model.getFeatures().get(0);
+            for(final RunMode rm : feature.getRunModes()) {
+                for(final ArtifactGroup group : rm.getArtifactGroups()) {
+                    for(final Artifact a : group) {
+                        artifacts.add(a);
+                    }
+                }
+            }
+
+            // extract artifacts
+            final byte[] buffer = new byte[1024*1024*256];
+
+            try ( final InputStream is = rsrc.getInputStream() ) {
+                ModelArchiveReader.read(is, new ModelArchiveReader.ArtifactConsumer() {
+
+                    @Override
+                    public void consume(final Artifact artifact, final InputStream is) throws IOException {
+                        if ( artifacts.contains(artifact) ) {
+                            final File artifactFile = new File(baseDir, artifact.getRepositoryPath().replace('/', File.separatorChar));
+                            if ( !artifactFile.exists() ) {
+                                artifactFile.getParentFile().mkdirs();
+                                try (final OutputStream os = new FileOutputStream(artifactFile)) {
+                                    int l = 0;
+                                    while ( (l = is.read(buffer)) > 0 ) {
+                                        os.write(buffer, 0, l);
+                                    }
+                                }
+                            }
+                        }
+                    }
+                });
+            } catch ( final IOException ioe) {
+                logger.warn("Unable to extract artifacts from model " + name, ioe);
+                return null;
+            }
+        }
 
-            Map<Traceable, String> errors = collectArtifacts(model, files);
-            if ( errors == null ) {
-                final Result result = new Result();
-                for(final ArtifactDescription desc : files) {
-                    if ( desc.artifactFile != null ) {
+        final List<ArtifactDescription> files = new ArrayList<>();
+
+        Map<Traceable, String> errors = collectArtifacts(model, files, baseDir);
+        if ( errors == null ) {
+            final Result result = new Result();
+            for(final ArtifactDescription desc : files) {
+                if ( desc.artifactFile != null ) {
+                    try {
                         final InputStream is = new FileInputStream(desc.artifactFile);
                         final String digest = String.valueOf(desc.artifactFile.lastModified());
                         // handle start level
@@ -174,24 +251,26 @@ public class InstallModelTask extends AbstractModelTask {
 
                         result.resources.add(new InstallableResource("/" + desc.artifactFile.getName(), is, dict, digest,
                                                               InstallableResource.TYPE_FILE, null));
-                    } else if ( desc.cfg != null ) {
-                        final String id = (desc.cfg.getFactoryPid() != null ? desc.cfg.getFactoryPid() + "-" + desc.cfg.getPid() : desc.cfg.getPid());
-                        result.resources.add(new InstallableResource("/" + id + ".config",
-                                null,
-                                desc.cfg.getProperties(),
-                                null,
-                                InstallableResource.TYPE_CONFIG, null));
-
-                    } else if ( desc.section != null ) {
-                        result.repoinit = desc.section.getContents();
+                    } catch ( final IOException ioe ) {
+                        logger.warn("Unable to read artifact " + desc.artifactFile, ioe);
+                        return null;
                     }
+                } else if ( desc.cfg != null ) {
+                    final String id = (desc.cfg.getFactoryPid() != null ? desc.cfg.getFactoryPid() + "-" + desc.cfg.getPid() : desc.cfg.getPid());
+                    result.resources.add(new InstallableResource("/" + id + ".config",
+                            null,
+                            desc.cfg.getProperties(),
+                            null,
+                            InstallableResource.TYPE_CONFIG, null));
+
+                } else if ( desc.section != null ) {
+                    result.repoinit = desc.section.getContents();
                 }
-                return result;
             }
-            logger.warn("Errors during parsing model file {} : {}", name, errors.values());
-        } catch ( final IOException ioe) {
-            logger.warn("Unable to read potential model file " + name, ioe);
+            return result;
         }
+        logger.warn("Errors during parsing model file {} : {}", name, errors.values());
+
         return null;
     }
 
@@ -202,7 +281,9 @@ public class InstallModelTask extends AbstractModelTask {
         public Section section;
     }
 
-    private Map<Traceable, String> collectArtifacts(final Model effectiveModel, final List<ArtifactDescription> files) {
+    private Map<Traceable, String> collectArtifacts(final Model effectiveModel,
+            final List<ArtifactDescription> files,
+            final File baseDir) {
         final RepositoryAccess repo = new RepositoryAccess();
         final Map<Traceable, String> errors = new HashMap<>();
         for(final Feature f : effectiveModel.getFeatures()) {
@@ -221,7 +302,10 @@ public class InstallModelTask extends AbstractModelTask {
                 if ( mode.isActive(this.activeRunModes) ) {
                     for(final ArtifactGroup group : mode.getArtifactGroups()) {
                         for(final Artifact artifact : group) {
-                            final File file = repo.get(artifact);
+                            File file = (baseDir == null ? null : new File(baseDir, artifact.getRepositoryPath().replace('/', File.separatorChar)));
+                            if ( file == null || !file.exists() ) {
+                                file = repo.get(artifact);
+                            }
                             if ( file == null ) {
                                 errors.put(artifact, "Artifact " + artifact.toMvnUrl() + " not found.");
                             } else {
@@ -248,6 +332,6 @@ public class InstallModelTask extends AbstractModelTask {
 
     @Override
     public String getSortKey() {
-        return "30-" + getModelName();
+        return "30-" + getResource().getAttribute(ModelTransformer.ATTR_FEATURE_NAME);
     }
 }
index a2c9fa2..e9bd83b 100644 (file)
@@ -18,27 +18,31 @@ package org.apache.sling.installer.factory.model.impl;/*
  */
 
 
+import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringWriter;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 
 import org.apache.sling.installer.api.InstallableResource;
 import org.apache.sling.installer.api.tasks.RegisteredResource;
 import org.apache.sling.installer.api.tasks.ResourceTransformer;
 import org.apache.sling.installer.api.tasks.TransformationResult;
-import org.apache.sling.provisioning.model.Configuration;
+import org.apache.sling.provisioning.model.Artifact;
 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.Section;
 import org.apache.sling.provisioning.model.Traceable;
+import org.apache.sling.provisioning.model.io.ModelArchiveReader;
 import org.apache.sling.provisioning.model.io.ModelReader;
 import org.apache.sling.provisioning.model.io.ModelWriter;
+import org.osgi.framework.BundleContext;
 import org.osgi.framework.Version;
+import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -54,78 +58,98 @@ public class ModelTransformer implements ResourceTransformer {
 
     public static final String ATTR_MODEL = "model";
 
+    public static final String ATTR_FEATURE_INDEX = "feature";
+
+    public static final String ATTR_BASE_PATH = "path";
+
+    public static final String ATTR_FEATURE_NAME = "name";
+
     /** Logger. */
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
+    private BundleContext bundleContext;
+
+    @Activate
+    private void activate(final BundleContext bc) {
+        this.bundleContext = bc;
+    }
+
     @Override
     public TransformationResult[] transform(final RegisteredResource resource) {
+        Model model = null;
+        File baseDir = null;
         if ( resource.getType().equals(InstallableResource.TYPE_FILE) && resource.getURL().endsWith(".model") ) {
             try ( final Reader reader = new InputStreamReader(resource.getInputStream(), "UTF-8") ) {
-                final Model model = ModelReader.read(reader, resource.getURL());
-
-                Map<Traceable, String> errors = ModelUtility.validate(model);
-                if ( errors == null ) {
-                    try {
-                        final Model effectiveModel = ModelUtility.getEffectiveModel(model);
-
-                        errors = validate(effectiveModel);
-                        if ( errors == null ) {
-
-                            final Feature f = effectiveModel.getFeatures().get(0);
-                            final TransformationResult tr = new TransformationResult();
-                            tr.setId(f.getName());
-                            tr.setResourceType(TYPE_PROV_MODEL);
-                            tr.setVersion(new Version(f.getVersion()));
-
-                            try ( final StringWriter sw = new StringWriter()) {
-                                ModelWriter.write(sw, effectiveModel);
-                                tr.setAttributes(Collections.singletonMap(ATTR_MODEL, (Object)sw.toString()));
-                            }
-                            return new TransformationResult[] {tr};
-                        }
-                    } catch ( final IllegalArgumentException iae ) {
-                        errors = Collections.singletonMap((Traceable)model, iae.getMessage());
-                    }
-                }
-                if ( errors != null ) {
-                    logger.warn("Errors during parsing model at {} : {}", resource.getURL(), errors.values());
-                }
-
+                model = ModelReader.read(reader, resource.getURL());
             } catch ( final IOException ioe) {
                 logger.info("Unable to read model from " + resource.getURL(), ioe);
             }
         }
-        return null;
-    }
-
-    private Map<Traceable, String> validate(final Model effectiveModel) {
-        if ( effectiveModel.getFeatures().size() != 1 ) {
-            return Collections.singletonMap((Traceable)effectiveModel, "Model should only contain a single feature.");
-        }
-        final Feature feature = effectiveModel.getFeatures().get(0);
-        if ( feature.isSpecial() ) {
-            return Collections.singletonMap((Traceable)feature, "Feature must not be special.");
-        }
-        if ( feature.getVersion() == null ) {
-            return Collections.singletonMap((Traceable)feature, "Feature must have a version.");
-        }
-        for(final Section section : feature.getAdditionalSections()) {
-            if ( !"repoinit".equals(section.getName()) ) {
-                return Collections.singletonMap((Traceable)section, "Additional section not supported.");
+        if ( resource.getType().equals(InstallableResource.TYPE_FILE) && resource.getURL().endsWith(".mar") ) {
+            baseDir = this.bundleContext.getDataFile("");
+            try ( final InputStream is = resource.getInputStream() ) {
+
+                model = ModelArchiveReader.read(is, new ModelArchiveReader.ArtifactConsumer() {
+                    @Override
+                    public void consume(final Artifact artifact, final InputStream is) throws IOException {
+                        // nothing to do, install task does extraction
+                    }
+                });
+            } catch ( final IOException ioe) {
+                logger.info("Unable to read model from " + resource.getURL(), ioe);
             }
         }
-        for(final RunMode mode : feature.getRunModes()) {
-            if ( mode.isSpecial() ) {
-                return Collections.singletonMap((Traceable)mode, "RunMode must not be special.");
-            }
-            for(final Configuration cfg : mode.getConfigurations()) {
-                if ( cfg.isSpecial() ) {
-                    return Collections.singletonMap((Traceable)cfg, "Configuration must not be special.");
+        if ( model != null ) {
+            Map<Traceable, String> errors = ModelUtility.validate(model);
+            if ( errors == null ) {
+                try {
+                    final Model effectiveModel = ModelUtility.getEffectiveModel(model);
+
+                    errors = ModelUtility.validateIncludingVersion(effectiveModel);
+                    if ( errors == null ) {
+
+                        String modelTxt = null;
+                        try ( final StringWriter sw = new StringWriter()) {
+                            ModelWriter.write(sw, effectiveModel);
+                            modelTxt = sw.toString();
+                        } catch ( final IOException ioe) {
+                            logger.info("Unable to read model from " + resource.getURL(), ioe);
+                        }
+
+                        if ( modelTxt != null ) {
+                            final TransformationResult[] result = new TransformationResult[effectiveModel.getFeatures().size()];
+                            int index = 0;
+                            for(final Feature f : effectiveModel.getFeatures()) {
+
+                                final TransformationResult tr = new TransformationResult();
+                                tr.setResourceType(TYPE_PROV_MODEL);
+                                tr.setId(f.getName());
+                                tr.setVersion(new Version(f.getVersion()));
+
+                                final Map<String, Object> attributes = new HashMap<>();
+                                attributes.put(ATTR_MODEL, modelTxt);
+                                attributes.put(ATTR_FEATURE_INDEX, index);
+                                attributes.put(ATTR_FEATURE_NAME, f.getName() + "-" + f.getVersion());
+                                if ( baseDir != null ) {
+                                    final File dir = new File(baseDir, f.getName() + "-" + f.getVersion());
+                                    attributes.put(ATTR_BASE_PATH, dir.getAbsolutePath());
+                                }
+                                tr.setAttributes(attributes);
+
+                                result[index] = tr;
+                                index++;
+                            }
+                            return result;
+                        }
+                    }
+                } catch ( final IllegalArgumentException iae ) {
+                    errors = Collections.singletonMap((Traceable)model, iae.getMessage());
                 }
             }
+            if ( errors != null ) {
+                logger.warn("Errors during parsing model at {} : {}", resource.getURL(), errors.values());
+            }
         }
-
         return null;
     }
-
 }
index 682c64b..ecce2bf 100644 (file)
@@ -18,6 +18,8 @@
  */
 package org.apache.sling.installer.factory.model.impl;
 
+import java.io.File;
+
 import org.apache.sling.installer.api.OsgiInstaller;
 import org.apache.sling.installer.api.tasks.InstallationContext;
 import org.apache.sling.installer.api.tasks.ResourceState;
@@ -41,7 +43,12 @@ public class UninstallModelTask extends AbstractModelTask {
             if ( installer == null ) {
                 ctx.log("Unable to get OSGi Installer service!");
             } else {
-                installer.registerResources("model-" + getModelName(), null);
+                installer.registerResources("model-" + getResource().getAttribute(ModelTransformer.ATTR_FEATURE_NAME), null);
+                final String path = (String)this.getResource().getAttribute(ModelTransformer.ATTR_BASE_PATH);
+                if ( path != null ) {
+                    final File dir = new File(path);
+                    deleteDirectory(dir);
+                }
                 this.getResourceGroup().setFinishState(ResourceState.UNINSTALLED);
             }
         } finally {
@@ -51,6 +58,6 @@ public class UninstallModelTask extends AbstractModelTask {
 
     @Override
     public String getSortKey() {
-        return "31-" + getModelName();
+        return "31-" + getResource().getAttribute(ModelTransformer.ATTR_FEATURE_NAME);
     }
 }