SLING-9098 : Support creation of feature archives
diff --git a/src/main/java/org/apache/sling/feature/io/archive/ArchiveReader.java b/src/main/java/org/apache/sling/feature/io/archive/ArchiveReader.java
index df00ced..2bb3bed 100644
--- a/src/main/java/org/apache/sling/feature/io/archive/ArchiveReader.java
+++ b/src/main/java/org/apache/sling/feature/io/archive/ArchiveReader.java
@@ -38,6 +38,7 @@
*/
public class ArchiveReader {
+ @FunctionalInterface
public interface ArtifactConsumer {
/**
@@ -55,16 +56,16 @@
* Read a feature 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 feature model
+ * @param in The input stream to read from.
+ * @param consumer The plugin consuming the binaries, if {@code null} only the
+ * feature models are read
+ * @return The feature models
* @throws IOException If anything goes wrong
*/
@SuppressWarnings("resource")
- public static Feature read(final InputStream in,
+ public static Set<Feature> read(final InputStream in,
final ArtifactConsumer consumer)
throws IOException {
- Feature feature = null;
-
final JarInputStream jis = new JarInputStream(in);
// check manifest
@@ -90,40 +91,44 @@
final Set<ArtifactId> artifacts = new HashSet<>();
// read contents
+ final Set<Feature> features = new HashSet<>();
+
JarEntry entry = null;
while ( ( entry = jis.getNextJarEntry() ) != null ) {
- if ( ArchiveWriter.MODEL_NAME.equals(entry.getName()) ) {
- feature = FeatureJSONReader.read(new InputStreamReader(jis, "UTF-8"), null);
+ if (!entry.isDirectory() && entry.getName().startsWith(ArchiveWriter.FEATURE_MODEL_PREFIX)) { // feature
+ features.add(FeatureJSONReader.read(new InputStreamReader(jis, "UTF-8"), entry.getName()));
} else if ( !entry.isDirectory() && entry.getName().startsWith(ArchiveWriter.ARTIFACTS_PREFIX) ) { // artifact
final ArtifactId id = ArtifactId
- .fromMvnUrl("mvn:" + entry.getName().substring(ArchiveWriter.ARTIFACTS_PREFIX.length()));
- consumer.consume(id, jis);
+ .fromMvnUrl("mvn:".concat(entry.getName().substring(ArchiveWriter.ARTIFACTS_PREFIX.length())));
+ if (consumer != null) {
+ consumer.consume(id, jis);
+ }
artifacts.add(id);
}
jis.closeEntry();
}
- if (feature == null) {
+ if (features.isEmpty()) {
throw new IOException("Not a feature model archive - feature file is missing.");
}
- // check whether all artifacts from the model are in the archive
-
- for (final Artifact a : feature.getBundles()) {
- if (!artifacts.contains(a.getId())) {
- throw new IOException("Artifact " + a.getId().toMvnId() + " is missing in archive");
+ // check whether all artifacts from the models are in the archive
+ for (final Feature feature : features) {
+ for (final Artifact a : feature.getBundles()) {
+ if (!artifacts.contains(a.getId())) {
+ throw new IOException("Artifact " + a.getId().toMvnId() + " is missing in archive");
+ }
}
- }
- for (final Extension e : feature.getExtensions()) {
- if (e.getType() == ExtensionType.ARTIFACTS) {
- for (final Artifact a : e.getArtifacts()) {
- if (!artifacts.contains(a.getId())) {
- throw new IOException("Artifact " + a.getId().toMvnId() + " is missing in archive");
+ for (final Extension e : feature.getExtensions()) {
+ if (e.getType() == ExtensionType.ARTIFACTS) {
+ for (final Artifact a : e.getArtifacts()) {
+ if (!artifacts.contains(a.getId())) {
+ throw new IOException("Artifact " + a.getId().toMvnId() + " is missing in archive");
+ }
}
}
}
}
-
- return feature;
+ return features;
}
}
diff --git a/src/main/java/org/apache/sling/feature/io/archive/ArchiveWriter.java b/src/main/java/org/apache/sling/feature/io/archive/ArchiveWriter.java
index 5e5cafd..6f48967 100644
--- a/src/main/java/org/apache/sling/feature/io/archive/ArchiveWriter.java
+++ b/src/main/java/org/apache/sling/feature/io/archive/ArchiveWriter.java
@@ -49,10 +49,10 @@
/** Current support version of the feature model archive. */
public static final int ARCHIVE_VERSION = 1;
- /** Model name. */
- public static final String MODEL_NAME = "features/feature.json";
+ /** The directory in the archive holding the features */
+ public static final String FEATURE_MODEL_PREFIX = "features/";
- /** Artifacts prefix. */
+ /** The directory in the archive holding the artifacts */
public static final String ARTIFACTS_PREFIX = "artifacts/";
/**
@@ -69,16 +69,15 @@
* complete} features.
*
* @param out The output stream to write to
- * @param feature The feature model to archive
* @param baseManifest Optional base manifest used for creating the manifest.
* @param provider The artifact provider
+ * @param features The features model to archive
* @return The jar output stream.
* @throws IOException If anything goes wrong
*/
public static JarOutputStream write(final OutputStream out,
- final Feature feature,
final Manifest baseManifest,
- final ArtifactProvider provider)
+ final ArtifactProvider provider, final Feature... features)
throws IOException {
// create manifest
final Manifest manifest = (baseManifest == null ? new Manifest() : new Manifest(baseManifest));
@@ -88,14 +87,16 @@
// create archive
final JarOutputStream jos = new JarOutputStream(out, manifest);
- // write model first with compression enabled
+ // write models first with compression enabled
jos.setLevel(Deflater.BEST_COMPRESSION);
- final JarEntry entry = new JarEntry(MODEL_NAME);
- jos.putNextEntry(entry);
- final Writer writer = new OutputStreamWriter(jos, "UTF-8");
- FeatureJSONWriter.write(writer, feature);
- writer.flush();
- jos.closeEntry();
+ for (final Feature feature : features) {
+ final JarEntry entry = new JarEntry(FEATURE_MODEL_PREFIX.concat(feature.getId().toMvnPath()));
+ jos.putNextEntry(entry);
+ final Writer writer = new OutputStreamWriter(jos, "UTF-8");
+ FeatureJSONWriter.write(writer, feature);
+ writer.flush();
+ jos.closeEntry();
+ }
// write artifacts with compression disabled
jos.setLevel(Deflater.NO_COMPRESSION);
@@ -103,14 +104,16 @@
final Set<ArtifactId> artifacts = new HashSet<>();
- for(final Artifact a : feature.getBundles() ) {
- writeArtifact(artifacts, provider, a, jos, buffer);
- }
+ for (final Feature feature : features) {
+ for (final Artifact a : feature.getBundles()) {
+ writeArtifact(artifacts, provider, a, jos, buffer);
+ }
- for (final Extension e : feature.getExtensions()) {
- if (e.getType() == ExtensionType.ARTIFACTS) {
- for (final Artifact a : e.getArtifacts()) {
- writeArtifact(artifacts, provider, a, jos, buffer);
+ for (final Extension e : feature.getExtensions()) {
+ if (e.getType() == ExtensionType.ARTIFACTS) {
+ for (final Artifact a : e.getArtifacts()) {
+ writeArtifact(artifacts, provider, a, jos, buffer);
+ }
}
}
}