Add support for archives
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1769388 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index 87fae4a..5a600d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -101,7 +101,7 @@
<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>
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/AbstractModelTask.java b/src/main/java/org/apache/sling/installer/factory/model/impl/AbstractModelTask.java
index 7fe3151..9f36b21 100644
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/AbstractModelTask.java
+++ b/src/main/java/org/apache/sling/installer/factory/model/impl/AbstractModelTask.java
@@ -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 @@
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();
+ }
}
}
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/InstallModelTask.java b/src/main/java/org/apache/sling/installer/factory/model/impl/InstallModelTask.java
index 0cf2fac..063aefa 100644
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/InstallModelTask.java
+++ b/src/main/java/org/apache/sling/installer/factory/model/impl/InstallModelTask.java
@@ -20,14 +20,17 @@
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.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 @@
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));
- // 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;
+ 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;
+ }
- } finally {
- if ( session != null ) {
- session.logout();
+ // 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();
+ }
}
}
- }
- 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 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);
+ }
+ }
+ }
- Map<Traceable, String> errors = collectArtifacts(model, files);
- if ( errors == null ) {
- final Result result = new Result();
- for(final ArtifactDescription desc : files) {
- if ( desc.artifactFile != null ) {
+ // 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;
+ }
+ }
+
+ 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 @@
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 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 @@
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 @@
@Override
public String getSortKey() {
- return "30-" + getModelName();
+ return "30-" + getResource().getAttribute(ModelTransformer.ATTR_FEATURE_NAME);
}
}
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/ModelTransformer.java b/src/main/java/org/apache/sling/installer/factory/model/impl/ModelTransformer.java
index a2c9fa2..e9bd83b 100644
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/ModelTransformer.java
+++ b/src/main/java/org/apache/sling/installer/factory/model/impl/ModelTransformer.java
@@ -18,27 +18,31 @@
*/
+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 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;
- }
+ if ( resource.getType().equals(InstallableResource.TYPE_FILE) && resource.getURL().endsWith(".mar") ) {
+ baseDir = this.bundleContext.getDataFile("");
+ try ( final InputStream is = resource.getInputStream() ) {
- 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.");
+ 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;
}
-
}
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/UninstallModelTask.java b/src/main/java/org/apache/sling/installer/factory/model/impl/UninstallModelTask.java
index 682c64b..ecce2bf 100644
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/UninstallModelTask.java
+++ b/src/main/java/org/apache/sling/installer/factory/model/impl/UninstallModelTask.java
@@ -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 @@
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 @@
@Override
public String getSortKey() {
- return "31-" + getModelName();
+ return "31-" + getResource().getAttribute(ModelTransformer.ATTR_FEATURE_NAME);
}
}