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 2021/09/12 14:01:42 UTC

[sling-org-apache-sling-feature-analyser] branch master updated: SLING-10805 : Add ContentPackageDescriptor to public API

This is an automated email from the ASF dual-hosted git repository.

cziegeler pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-analyser.git


The following commit(s) were added to refs/heads/master by this push:
     new 51a7c40  SLING-10805 : Add ContentPackageDescriptor to public API
51a7c40 is described below

commit 51a7c405113dbf026747bda8cadab55e65523770
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sun Sep 12 16:01:32 2021 +0200

    SLING-10805 : Add ContentPackageDescriptor to public API
---
 .../impl/CheckContentPackageForInstallables.java   |  21 +--
 .../impl/CheckContentPackagesDependencies.java     |   8 +-
 .../task/impl/CheckContentPackagesForPaths.java    |  11 +-
 .../sling/feature/scanner/ContainerDescriptor.java |  16 +++
 .../feature/scanner/ContentPackageDescriptor.java  |  97 +++++++++++++
 .../scanner/impl/ContentPackageDescriptor.java     | 121 ----------------
 .../scanner/impl/ContentPackageDescriptorImpl.java | 159 +++++++++++++++++++++
 .../scanner/impl/ContentPackageScanner.java        |  39 ++---
 .../impl/ContentPackagesExtensionScanner.java      |  10 +-
 .../apache/sling/feature/scanner/package-info.java |   2 +-
 .../task/impl/CheckApisJarsPropertiesTest.java     |  11 +-
 .../impl/CheckContentPackagesDependenciesTest.java |   4 +-
 .../CheckContentPackagesForInstallablesTest.java   |  14 +-
 .../impl/CheckContentPackagesForPathsTest.java     |  27 ++--
 .../scanner/impl/ContentPackageScannerTest.java    |  34 +++--
 15 files changed, 363 insertions(+), 211 deletions(-)

diff --git a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackageForInstallables.java b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackageForInstallables.java
index b319eba..03385fe 100644
--- a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackageForInstallables.java
+++ b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackageForInstallables.java
@@ -16,13 +16,9 @@
  */
 package org.apache.sling.feature.analyser.task.impl;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import org.apache.sling.feature.analyser.task.AnalyserTask;
 import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
-import org.apache.sling.feature.scanner.ArtifactDescriptor;
-import org.apache.sling.feature.scanner.impl.ContentPackageDescriptor;
+import org.apache.sling.feature.scanner.ContentPackageDescriptor;
 
 /**
  * This analyser checks for bundles and configurations in packages
@@ -46,20 +42,13 @@ public class CheckContentPackageForInstallables implements AnalyserTask {
             throws Exception {
         final boolean checkPcks = Boolean.parseBoolean(ctx.getConfiguration().getOrDefault(CFG_CHECK_PACKAGES, "false"));
 
-        final List<ContentPackageDescriptor> contentPackages = new ArrayList<>();
-        for (final ArtifactDescriptor d : ctx.getFeatureDescriptor().getArtifactDescriptors()) {
-            if (d instanceof ContentPackageDescriptor) {
-                contentPackages.add((ContentPackageDescriptor) d);
-            }
-        }
-
-        for (final ContentPackageDescriptor cp : contentPackages) {
+        for (final ContentPackageDescriptor cp : ctx.getFeatureDescriptor().getDescriptors(ContentPackageDescriptor.class)) {
             if (cp.getArtifactFile() ==  null) {
                 ctx.reportArtifactError(cp.getArtifact().getId(), "Content package " + cp.getName() + " is not resolved and can not be checked.");
                 continue;
             }
             if ( checkPcks && cp.isEmbeddedInContentPackage() ) {
-                ctx.reportArtifactError(cp.getContentPackage().getId(), "Content package " + cp.getContentPackage().getId() +
+                ctx.reportArtifactError(cp.getParentContentPackage().getId(), "Content package " + cp.getParentContentPackage().getId() +
                         " embedds content package " + cp.getName());
             }
             if (!cp.hasEmbeddedArtifacts() || cp.isEmbeddedInContentPackage()) {
@@ -67,8 +56,8 @@ public class CheckContentPackageForInstallables implements AnalyserTask {
             }
 
             ctx.reportArtifactError(cp.getArtifact().getId(), "Content package " + cp.getName() +
-                    " contains " + String.valueOf(cp.bundles.size()) + " bundles and "
-                    + String.valueOf(cp.configs.size()) + " configurations.");
+                    " contains " + String.valueOf(cp.getBundles().size()) + " bundles and "
+                    + String.valueOf(cp.getConfigurations().size()) + " configurations.");
 
         }
     }
diff --git a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesDependencies.java b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesDependencies.java
index c51de09..81d2911 100644
--- a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesDependencies.java
+++ b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesDependencies.java
@@ -35,6 +35,7 @@ import org.apache.sling.feature.analyser.task.AnalyserTask;
 import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
 import org.apache.sling.feature.io.IOUtils;
 import org.apache.sling.feature.scanner.ArtifactDescriptor;
+import org.apache.sling.feature.scanner.ContentPackageDescriptor;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -56,14 +57,9 @@ public class CheckContentPackagesDependencies implements AnalyserTask {
 
     @Override
     public void execute(AnalyserTaskContext ctx) throws Exception {
-        Set<ArtifactDescriptor> descriptors = ctx.getFeatureDescriptor().getArtifactDescriptors();
-        if (descriptors == null || descriptors.isEmpty()) {
-            return;
-        }
-
         Map<PackageId, Dependency[]> dependenciesMap = new HashMap<>();
 
-        for (ArtifactDescriptor descriptor : descriptors) {
+        for (ArtifactDescriptor descriptor : ctx.getFeatureDescriptor().getDescriptors(ContentPackageDescriptor.class)) {
             onDescriptor(ctx, descriptor, dependenciesMap);
         }
 
diff --git a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForPaths.java b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForPaths.java
index d874fe5..641772b 100644
--- a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForPaths.java
+++ b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForPaths.java
@@ -18,8 +18,7 @@ package org.apache.sling.feature.analyser.task.impl;
 
 import org.apache.sling.feature.analyser.task.AnalyserTask;
 import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
-import org.apache.sling.feature.scanner.ArtifactDescriptor;
-import org.apache.sling.feature.scanner.impl.ContentPackageDescriptor;
+import org.apache.sling.feature.scanner.ContentPackageDescriptor;
 
 /**
  * This analyser checks for content paths in packages
@@ -46,10 +45,8 @@ public class CheckContentPackagesForPaths implements AnalyserTask {
         final Rules rules = getRules(ctx);
 
         if (rules != null ) {
-            for (final ArtifactDescriptor d : ctx.getFeatureDescriptor().getArtifactDescriptors()) {
-                if (d instanceof ContentPackageDescriptor) {
-                    checkPackage(ctx, (ContentPackageDescriptor) d, rules);
-                }
+            for (final ContentPackageDescriptor d : ctx.getFeatureDescriptor().getDescriptors(ContentPackageDescriptor.class)) {
+                checkPackage(ctx, d, rules);
             }
         } else {
             ctx.reportError("Configuration for task " + getId() + " is missing.");
@@ -84,7 +81,7 @@ public class CheckContentPackagesForPaths implements AnalyserTask {
     }
 
     void checkPackage(final AnalyserTaskContext ctx, final ContentPackageDescriptor desc, final Rules rules) {
-        for(final String path : desc.paths) {
+        for(final String path : desc.getContentPaths()) {
             boolean isAllowed = rules.includes == null;
             int matchLength = 0;
             if ( !isAllowed ) {
diff --git a/src/main/java/org/apache/sling/feature/scanner/ContainerDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/ContainerDescriptor.java
index efebd09..3c1c72f 100644
--- a/src/main/java/org/apache/sling/feature/scanner/ContainerDescriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/ContainerDescriptor.java
@@ -67,6 +67,22 @@ public abstract class ContainerDescriptor extends Descriptor {
         return this.isLocked() ? Collections.unmodifiableSet(artifacts) : artifacts;
     }
 
+    /**
+     * Return a set of artifact descriptors of the given type
+     * @param type The descriptor type
+     * @return The set of artifact descriptors matching the type (might be empty)
+     * @since 2.3
+     */
+    public <T extends ArtifactDescriptor> Set<T> getDescriptors(final Class<T> type) {
+        final Set<T> result = new HashSet<>();
+        for(final ArtifactDescriptor desc : this.getArtifactDescriptors()) {
+            if ( type.isInstance(desc) ) {
+                result.add(type.cast(desc));
+            }
+        }
+        return result;
+    }
+
     @Override
     public void lock() {
         if ( this.isLocked() ) {
diff --git a/src/main/java/org/apache/sling/feature/scanner/ContentPackageDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/ContentPackageDescriptor.java
new file mode 100644
index 0000000..43f8021
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/scanner/ContentPackageDescriptor.java
@@ -0,0 +1,97 @@
+/*
+ * 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.feature.scanner;
+
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Configuration;
+
+/**
+ * Information about a content package.
+ * @since 2.3.0
+ */
+public abstract class ContentPackageDescriptor extends ArtifactDescriptor {
+
+    /**
+     * Constructor for the descriptor
+     * @param name The name
+     * @throws IllegalArgumentException if name is {@code null}
+     */
+    public ContentPackageDescriptor(final String name) {
+        super(name);
+    }
+
+    /**
+     * Get the content paths
+     * @return The list of content paths
+     */
+    public abstract List<String> getContentPaths();
+
+    /**
+     * Get the included bundles
+     * @return The list of bundles, might be empty
+     */
+    public abstract List<BundleDescriptor> getBundles();
+
+    /**
+     * Get the included configurations
+     * @return The list of configurations, might be empty
+     */
+    public abstract List<Configuration> getConfigurations();
+
+    /**
+     * Get the parent content package
+     * @return The parent content package or {@code null}
+     */
+    public abstract Artifact getParentContentPackage();
+
+    /**
+     * Get the parent content path
+     * @return The parent content path or {@code null}
+     */
+    public abstract String getParentContentPath();
+
+    /**
+     * Whether this artifact is embedded in a content package
+     * @return {@code true} if embedded.
+     */
+    public boolean isEmbeddedInContentPackage() {
+        return this.getParentContentPath() != null;
+    }
+
+    /**
+     * Check whether the package has embedded artifacts
+     * @return {@code true} if the package has embedded artifacts
+     */
+    public boolean hasEmbeddedArtifacts() {
+        return !this.getBundles().isEmpty() || !this.getConfigurations().isEmpty();
+    }
+
+    /**
+     * Get the package properties
+     * @return The package properties
+     */
+    public abstract Properties getPackageProperties();
+    
+    @Override
+    public String toString() {
+        return "ContentPackage [" + getName() + "]";
+    }
+}
+
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageDescriptor.java b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageDescriptor.java
deleted file mode 100644
index 13f1dde..0000000
--- a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageDescriptor.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * 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.feature.scanner.impl;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.jar.Manifest;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.scanner.BundleDescriptor;
-
-/**
- * Information about a content package.
- */
-public class ContentPackageDescriptor extends ArtifactDescriptorImpl {
-
-    /**
-     * The metadata added to bundles and configurations for the package they are in.
-     */
-    public static final String METADATA_PACKAGE = "content-package";
-
-    /**
-     * The metadata added to bundles and configurations for the path in the package
-     */
-    public static final String METADATA_PATH = "content-path";
-
-    /** Bundles in the content package. */
-    public final List<BundleDescriptor> bundles = new ArrayList<>();
-
-    /** Configurations in the content package. */
-    public final List<Configuration> configs = new ArrayList<>();
-
-    /** Paths in the content package. */
-    public final List<String> paths = new ArrayList<>();
-
-    /** Optional: the artifact of the content package. */
-    private Artifact contentPackage;
-
-    /** Optional: the path inside of the content package. */
-    private String contentPath;
-
-    /**
-     * Constructor for the descriptor
-     * @param name The name
-     * @param artifact The artifact
-     * @param url The url to the binary
-     * @param manifest The manifest (optional)
-     * @throws NullPointerException If artifact is {@code null}
-     */
-    public ContentPackageDescriptor(final String name,
-            final Artifact artifact,
-            final URL url,
-            final Manifest manifest) {
-        super(name, artifact, url, manifest);
-    }
-
-    /**
-     * Get the content package
-     * @return The content package or {@code null}
-     */
-    public Artifact getContentPackage() {
-        return contentPackage;
-    }
-
-    /**
-     * Get the content path
-     * @return The content path or {@code null}
-     */
-    public String getContentPath() {
-        return this.contentPath;
-    }
-
-    /**
-     * Whether this artifact is embedded in a content package
-     * @return {@code true} if embedded.
-     */
-    public boolean isEmbeddedInContentPackage() {
-        return this.contentPath != null;
-    }
-
-    /**
-     * Set the information about the content package containing this artifact
-     * @param artifact The package
-     * @param path The path inside the package
-     */
-    public void setContentPackageInfo(final Artifact artifact, final String path) {
-        checkLocked();
-        this.contentPackage = artifact;
-        this.contentPath = path;
-    }
-
-    /**
-     * Check whether the package has embedded artifacts
-     * @return {@code true} if the package has embedded artifacts
-     */
-    public boolean hasEmbeddedArtifacts() {
-        return !this.bundles.isEmpty() || !this.configs.isEmpty();
-    }
-
-    @Override
-    public String toString() {
-        return "ContentPackage [" + getName() + "]";
-    }
-}
-
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageDescriptorImpl.java b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageDescriptorImpl.java
new file mode 100644
index 0000000..c1dfa7e
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageDescriptorImpl.java
@@ -0,0 +1,159 @@
+/*
+ * 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.feature.scanner.impl;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.jar.Manifest;
+
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.Configuration;
+import org.apache.sling.feature.scanner.BundleDescriptor;
+import org.apache.sling.feature.scanner.ContentPackageDescriptor;
+
+/**
+ * Information about a content package.
+ */
+public class ContentPackageDescriptorImpl extends ContentPackageDescriptor {
+
+    /**
+     * The metadata added to bundles and configurations for the package they are in.
+     */
+    public static final String METADATA_PACKAGE = "content-package";
+
+    /**
+     * The metadata added to bundles and configurations for the path in the package
+     */
+    public static final String METADATA_PATH = "content-path";
+
+    /** Bundles in the content package. */
+    private final List<BundleDescriptor> bundles;
+
+    /** Configurations in the content package. */
+    private final List<Configuration> configs;
+
+    /** Paths in the content package. */
+    private final List<String> paths;
+
+    /** Optional: the artifact of the parent content package. */
+    private Artifact parentContentPackage;
+
+    /** Optional: the path inside of the parent content package. */
+    private String parentContentPath;
+
+    /** Manifest */
+    private final Manifest manifest;
+
+    /** The physical file for analyzing. */
+    private final URL artifactFile;
+    
+    /** The corresponding artifact from the feature. */
+    private final Artifact artifact;
+    
+    /** The package properties */
+    private final Properties packageProperties;
+
+    /**
+     * Constructor for the descriptor
+     * @param name The name
+     * @param artifact The artifact
+     * @param url The url to the binary
+     * @param manifest The manifest (optional)
+     * @param bundles Mutable list of contained bundles or {@code null}
+     * @param paths Mutable list of content paths or {@code null}
+     * @param configs Mutable list of configurations or {@code null}
+     * @param properties Package properties
+     * @throws NullPointerException If artifact is {@code null}
+     */
+    public ContentPackageDescriptorImpl(final String name,
+            final Artifact artifact,
+            final URL url,
+            final Manifest manifest,
+            final List<BundleDescriptor> bundles,
+            final List<String> paths,
+            final List<Configuration> configs,
+            final Properties packageProps) {
+        super(name);
+        this.bundles = bundles == null ? new ArrayList<>() : bundles;
+        this.paths = paths == null ? new ArrayList<>() : paths;
+        this.configs = configs == null ? new ArrayList<>() : configs;
+        this.artifact = artifact;
+        this.artifact.getId(); // throw NPE if artifact is null
+        this.artifactFile = url;
+        this.manifest = manifest;
+        this.packageProperties = packageProps;
+    }
+
+    @Override
+    public URL getArtifactFile() {
+        return this.artifactFile;
+    }
+
+    @Override
+    public Artifact getArtifact() {
+        return artifact;
+    }
+
+    @Override
+    public Manifest getManifest() {
+        return this.manifest;
+    }
+
+    @Override
+    public List<String> getContentPaths() {
+        return this.isLocked() ? Collections.unmodifiableList(this.paths) : this.paths;
+    }
+
+    @Override
+    public List<BundleDescriptor> getBundles() {
+        return this.isLocked() ? Collections.unmodifiableList(this.bundles) : this.bundles;
+    }
+
+    @Override
+    public List<Configuration> getConfigurations() {
+        return this.isLocked() ? Collections.unmodifiableList(this.configs) : this.configs;
+    }
+
+    @Override
+    public Artifact getParentContentPackage() {
+        return parentContentPackage;
+    }
+
+    @Override
+    public String getParentContentPath() {
+        return this.parentContentPath;
+    }
+
+    /**
+     * Set the information about the parent content package containing this artifact
+     * @param artifact The package
+     * @param path The path inside the package
+     */
+    public void setParentContentPackageInfo(final Artifact artifact, final String path) {
+        checkLocked();
+        this.parentContentPackage = artifact;
+        this.parentContentPath = path;
+    }
+
+    @Override
+    public Properties getPackageProperties() {
+        return this.packageProperties;
+    }
+}
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageScanner.java b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageScanner.java
index fff0c2a..bd4ef22 100644
--- a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageScanner.java
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageScanner.java
@@ -48,7 +48,11 @@ import org.slf4j.LoggerFactory;
  */
 public class ContentPackageScanner {
 
-    private static final Logger logger = LoggerFactory.getLogger(ContentPackageScanner.class);
+    private static final String FILE_PACKAGE_PROPS = "META-INF/vault/properties.xml";
+
+    private static final String FILE_MANIFEST = "META-INF/MANIFEST.MF";
+
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
     private final byte[] buffer = new byte[65536];
 
@@ -67,8 +71,8 @@ public class ContentPackageScanner {
      * @return A set of artifacts
      * @throws IOException If processing fails
      */
-    public Set<ContentPackageDescriptor> scan(final Artifact artifact, final URL url) throws IOException {
-        final Set<ContentPackageDescriptor> contentPackages = new HashSet<>();
+    public Set<ContentPackageDescriptorImpl> scan(final Artifact artifact, final URL url) throws IOException {
+        final Set<ContentPackageDescriptorImpl> contentPackages = new HashSet<>();
         if (url != null) {
             final String path = url.getPath();
             final int lastDotInUrl = path.lastIndexOf(".");
@@ -141,12 +145,12 @@ public class ContentPackageScanner {
         return fileType;
     }
 
-    private ContentPackageDescriptor extractContentPackage(final ContentPackageDescriptor parentPackage,
+    private ContentPackageDescriptorImpl extractContentPackage(final ContentPackageDescriptorImpl parentPackage,
             final String parentContentPath,
             final Artifact packageArtifact,
             final String name,
             final URL archiveUrl,
-            final Set<ContentPackageDescriptor> infos)
+            final Set<ContentPackageDescriptorImpl> infos)
     throws IOException {
         logger.debug("Analyzing Content Package {}", archiveUrl);
 
@@ -160,7 +164,8 @@ public class ContentPackageScanner {
             final List<String> contentPaths = new ArrayList<>();
             final List<BundleDescriptor> bundles = new ArrayList<>();
             final List<Configuration> configs = new ArrayList<>();
-    
+            final Properties packageProps = new Properties();
+
             try (final JarFile zipFile = IOUtils.getJarFileFromURL(archiveUrl, true, null)) {
                 Enumeration<? extends ZipEntry> entries = zipFile.entries();
                 while (entries.hasMoreElements()) {
@@ -209,9 +214,9 @@ public class ContentPackageScanner {
                                 bundle.setStartOrder(startLevel);
                                 final BundleDescriptor info = new BundleDescriptorImpl(bundle, newFile.toURI().toURL(),
                                         startLevel);
-                                bundle.getMetadata().put(ContentPackageDescriptor.METADATA_PACKAGE,
+                                bundle.getMetadata().put(ContentPackageDescriptorImpl.METADATA_PACKAGE,
                                         packageArtifact.getId().toMvnId());
-                                bundle.getMetadata().put(ContentPackageDescriptor.METADATA_PATH, contentPath);
+                                bundle.getMetadata().put(ContentPackageDescriptorImpl.METADATA_PATH, contentPath);
     
                                 bundles.add(info);
     
@@ -226,20 +231,22 @@ public class ContentPackageScanner {
                                 toProcess.add(newFile);
                             } 
                         }
-                    } else if ( entryName.equals("META-INF/MANIFEST.MF") ) {
+                    } else if ( FILE_MANIFEST.equals(entry.getName()) ) {
                         try ( final InputStream zis = zipFile.getInputStream(entry)) {
                             manifest = new Manifest(zis);
                         }
+                    } else if ( FILE_PACKAGE_PROPS.equals(entry.getName()) ) {
+                        try ( final InputStream zis = zipFile.getInputStream(entry)) {
+                            packageProps.loadFromXML(zis);
+                        }
                     }
                 }
 
-                final ContentPackageDescriptor desc = new ContentPackageDescriptor(name, packageArtifact, archiveUrl, manifest);
+                final ContentPackageDescriptorImpl desc = new ContentPackageDescriptorImpl(name, packageArtifact, archiveUrl, manifest,
+                    bundles, contentPaths, configs, packageProps);
                 if ( parentPackage != null ) {
-                    desc.setContentPackageInfo(parentPackage.getArtifact(), parentContentPath);
+                    desc.setParentContentPackageInfo(parentPackage.getArtifact(), parentContentPath);
                 }
-                desc.bundles.addAll(bundles);
-                desc.configs.addAll(configs);
-                desc.paths.addAll(contentPaths);
 
                 for (final File f : toProcess) {
                     final int lastDot = f.getName().lastIndexOf(".");
@@ -403,8 +410,8 @@ public class ContentPackageScanner {
             }
 
             final Configuration cfg = new Configuration(pid);
-            cfg.getProperties().put(Configuration.PROP_PREFIX + ContentPackageDescriptor.METADATA_PATH, contentPath);
-            cfg.getProperties().put(Configuration.PROP_PREFIX + ContentPackageDescriptor.METADATA_PACKAGE,
+            cfg.getProperties().put(Configuration.PROP_PREFIX + ContentPackageDescriptorImpl.METADATA_PATH, contentPath);
+            cfg.getProperties().put(Configuration.PROP_PREFIX + ContentPackageDescriptorImpl.METADATA_PACKAGE,
                     packageArtifactId.toMvnId());
 
             return cfg;
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackagesExtensionScanner.java b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackagesExtensionScanner.java
index e082acc..077f12d 100644
--- a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackagesExtensionScanner.java
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackagesExtensionScanner.java
@@ -18,6 +18,7 @@ package org.apache.sling.feature.scanner.impl;
 
 import java.io.IOException;
 import java.net.URL;
+import java.util.Properties;
 import java.util.Set;
 
 import org.apache.sling.feature.Artifact;
@@ -70,15 +71,16 @@ public class ContentPackagesExtensionScanner implements ExtensionScanner {
             }
 
             if (url != null) {
-                final Set<ContentPackageDescriptor> pcks = scanner.scan(a, url);
-                for (final ContentPackageDescriptor desc : pcks) {
+                final Set<ContentPackageDescriptorImpl> pcks = scanner.scan(a, url);
+                for (final ContentPackageDescriptorImpl desc : pcks) {
                     cd.getArtifactDescriptors().add(desc);
-                    cd.getBundleDescriptors().addAll(desc.bundles);
+                    cd.getBundleDescriptors().addAll(desc.getBundles());
                 }
             }
             else {
                 final int lastDot = a.getId().toMvnPath().lastIndexOf(".");
-                ContentPackageDescriptor desc = new ContentPackageDescriptor(a.getId().toMvnPath().substring(a.getId().toMvnPath().lastIndexOf("/") + 1, lastDot), a, url, null);
+                ContentPackageDescriptorImpl desc = new ContentPackageDescriptorImpl(a.getId().toMvnPath().substring(a.getId().toMvnPath().lastIndexOf("/") + 1, lastDot), 
+                        a, url, null, null, null, null, new Properties());
                 desc.lock();
                 cd.getArtifactDescriptors().add(desc);
             }
diff --git a/src/main/java/org/apache/sling/feature/scanner/package-info.java b/src/main/java/org/apache/sling/feature/scanner/package-info.java
index f2c897e..c0f3a77 100644
--- a/src/main/java/org/apache/sling/feature/scanner/package-info.java
+++ b/src/main/java/org/apache/sling/feature/scanner/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-@org.osgi.annotation.versioning.Version("2.2.0")
+@org.osgi.annotation.versioning.Version("2.3.0")
 package org.apache.sling.feature.scanner;
 
 
diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckApisJarsPropertiesTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckApisJarsPropertiesTest.java
index 8ba07fc..23d3dad 100644
--- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckApisJarsPropertiesTest.java
+++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckApisJarsPropertiesTest.java
@@ -18,8 +18,7 @@
  */
 package org.apache.sling.feature.analyser.task.impl;
 
-import static org.hamcrest.CoreMatchers.equalTo;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertEquals;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -169,8 +168,8 @@ public class CheckApisJarsPropertiesTest {
 
         SpyAnalyserTaskContext context = new SpyAnalyserTaskContext(f);
         check.execute(context);
-        assertThat("errors.size", context.getErrors().size(), equalTo(0));
-        assertThat("warnings.size", context.getWarnings().size(), equalTo(0));
+        assertEquals("errors.size", 0, context.getErrors().size());
+        assertEquals("warnings.size", 0, context.getWarnings().size());
     }
     
     @Test
@@ -184,7 +183,7 @@ public class CheckApisJarsPropertiesTest {
 
         SpyAnalyserTaskContext context = new SpyAnalyserTaskContext(f);
         check.execute(context);
-        assertThat("errors.size", context.getErrors().size(), equalTo(1));
-        assertThat("warnings.size", context.getWarnings().size(), equalTo(0));  
+        assertEquals("errors.size", 1, context.getErrors().size());
+        assertEquals("warnings.size", 0, context.getWarnings().size());  
     }
 }
diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesDependenciesTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesDependenciesTest.java
index a2d961f..a5289f0 100644
--- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesDependenciesTest.java
+++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesDependenciesTest.java
@@ -33,7 +33,7 @@ import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.analyser.task.AnalyserTask;
 import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
-import org.apache.sling.feature.scanner.ArtifactDescriptor;
+import org.apache.sling.feature.scanner.ContentPackageDescriptor;
 import org.apache.sling.feature.scanner.FeatureDescriptor;
 import org.apache.sling.feature.scanner.impl.FeatureDescriptorImpl;
 import org.junit.After;
@@ -89,7 +89,7 @@ public class CheckContentPackagesDependenciesTest {
             Artifact artifact = mock(Artifact.class);
             when(artifact.getId()).thenReturn(id);
 
-            ArtifactDescriptor descriptor = mock(ArtifactDescriptor.class);
+            ContentPackageDescriptor descriptor = mock(ContentPackageDescriptor.class);
             when(descriptor.getArtifact()).thenReturn(artifact);
             when(descriptor.getArtifactFile()).thenReturn(getClass().getClassLoader().getResource(resource));
 
diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForInstallablesTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForInstallablesTest.java
index 27088f2..37e3c44 100644
--- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForInstallablesTest.java
+++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForInstallablesTest.java
@@ -20,11 +20,12 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
 import java.net.URL;
+import java.util.Properties;
 
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.scanner.FeatureDescriptor;
-import org.apache.sling.feature.scanner.impl.ContentPackageDescriptor;
+import org.apache.sling.feature.scanner.impl.ContentPackageDescriptorImpl;
 import org.apache.sling.feature.scanner.impl.FeatureDescriptorImpl;
 import org.junit.Test;
 
@@ -48,7 +49,8 @@ public class CheckContentPackagesForInstallablesTest {
         final FeatureDescriptor fd = new FeatureDescriptorImpl(ctx.getFeature());
         ctx.setFeatureDescriptor(fd);
 
-        final ContentPackageDescriptor cpd = new ContentPackageDescriptor("content", new Artifact(ArtifactId.parse("g:c:1")), new URL("file:/foo"), null);
+        final ContentPackageDescriptorImpl cpd = new ContentPackageDescriptorImpl("content", new Artifact(ArtifactId.parse("g:c:1")), new URL("file:/foo"), 
+            null, null, null, null, new Properties());
         fd.getArtifactDescriptors().add(cpd);
 
         analyser.execute(ctx);
@@ -66,11 +68,13 @@ public class CheckContentPackagesForInstallablesTest {
         final FeatureDescriptor fd = new FeatureDescriptorImpl(ctx.getFeature());
         ctx.setFeatureDescriptor(fd);
 
-        final ContentPackageDescriptor cpd = new ContentPackageDescriptor("content", new Artifact(ArtifactId.parse("g:c:1")), new URL("file:/foo"), null);
+        final ContentPackageDescriptorImpl cpd = new ContentPackageDescriptorImpl("content", new Artifact(ArtifactId.parse("g:c:1")), new URL("file:/foo"),
+            null, null, null, null, new Properties());
         fd.getArtifactDescriptors().add(cpd);
 
-        final ContentPackageDescriptor embedded = new ContentPackageDescriptor("embedded", new Artifact(ArtifactId.parse("g:e:1")), new URL("file:/foo"), null);
-        embedded.setContentPackageInfo(cpd.getArtifact(), "/path");
+        final ContentPackageDescriptorImpl embedded = new ContentPackageDescriptorImpl("embedded", new Artifact(ArtifactId.parse("g:e:1")), 
+            new URL("file:/foo"), null, null, null, null, new Properties());
+        embedded.setParentContentPackageInfo(cpd.getArtifact(), "/path");
         fd.getArtifactDescriptors().add(embedded);
 
         analyser.execute(ctx);
diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForPathsTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForPathsTest.java
index c88cbbf..a241f96 100644
--- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForPathsTest.java
+++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckContentPackagesForPathsTest.java
@@ -22,12 +22,13 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.net.URL;
+import java.util.Properties;
 
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
 import org.apache.sling.feature.analyser.task.impl.CheckContentPackagesForPaths.Rules;
-import org.apache.sling.feature.scanner.impl.ContentPackageDescriptor;
+import org.apache.sling.feature.scanner.impl.ContentPackageDescriptorImpl;
 import org.junit.Test;
 
 public class CheckContentPackagesForPathsTest {
@@ -70,14 +71,14 @@ public class CheckContentPackagesForPathsTest {
         ctx.getConfiguration().put("includes", "/ab,/b");
 
         final Rules r = analyser.getRules(ctx);
-        final ContentPackageDescriptor desc = new ContentPackageDescriptor("name", new Artifact(ArtifactId.parse("g:a:1")),
-            new URL("https://sling.apache.org"), null);
-        desc.paths.add("/b/foo");
-        desc.paths.add("/a");
-        desc.paths.add("/a/foo");
-        desc.paths.add("/ab");
-        desc.paths.add("/b");
-        desc.paths.add("/c");
+        final ContentPackageDescriptorImpl desc = new ContentPackageDescriptorImpl("name", new Artifact(ArtifactId.parse("g:a:1")),
+            new URL("https://sling.apache.org"), null, null, null, null, new Properties());
+        desc.getContentPaths().add("/b/foo");
+        desc.getContentPaths().add("/a");
+        desc.getContentPaths().add("/a/foo");
+        desc.getContentPaths().add("/ab");
+        desc.getContentPaths().add("/b");
+        desc.getContentPaths().add("/c");
 
         analyser.checkPackage(ctx, desc, r);
 
@@ -94,10 +95,10 @@ public class CheckContentPackagesForPathsTest {
         ctx.getConfiguration().put("includes", "/a/foo");
 
         final Rules r = analyser.getRules(ctx);
-        final ContentPackageDescriptor desc = new ContentPackageDescriptor("name", new Artifact(ArtifactId.parse("g:a:1")),
-            new URL("https://sling.apache.org"), null);
-        desc.paths.add("/a/foo");
-        desc.paths.add("/a/bar");
+        final ContentPackageDescriptorImpl desc = new ContentPackageDescriptorImpl("name", new Artifact(ArtifactId.parse("g:a:1")),
+            new URL("https://sling.apache.org"), null, null, null, null, new Properties());
+        desc.getContentPaths().add("/a/foo");
+        desc.getContentPaths().add("/a/bar");
 
         analyser.checkPackage(ctx, desc, r);
 
diff --git a/src/test/java/org/apache/sling/feature/scanner/impl/ContentPackageScannerTest.java b/src/test/java/org/apache/sling/feature/scanner/impl/ContentPackageScannerTest.java
index 75192f8..4a54155 100644
--- a/src/test/java/org/apache/sling/feature/scanner/impl/ContentPackageScannerTest.java
+++ b/src/test/java/org/apache/sling/feature/scanner/impl/ContentPackageScannerTest.java
@@ -17,6 +17,7 @@
 package org.apache.sling.feature.scanner.impl;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -28,12 +29,9 @@ import java.net.URL;
 import java.util.Dictionary;
 import java.util.Set;
 
-import org.apache.felix.utils.repository.UrlLoader;
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Configuration;
-import org.apache.sling.feature.scanner.BundleDescriptor;
-import org.junit.Before;
 import org.junit.Test;
 
 public class ContentPackageScannerTest {
@@ -47,9 +45,9 @@ public class ContentPackageScannerTest {
         final ArtifactId TEST_PACKAGE_AID_A_10 = ArtifactId.fromMvnId(COORDINATES_TEST_PACKAGE_A_10);
 
         ContentPackageScanner scanner = new ContentPackageScanner();
-        Set<ContentPackageDescriptor> descriptors = scanner.scan(new Artifact(TEST_PACKAGE_AID_A_10), file.toURI().toURL());
+        Set<ContentPackageDescriptorImpl> descriptors = scanner.scan(new Artifact(TEST_PACKAGE_AID_A_10), file.toURI().toURL());
         assertEquals(2, descriptors.size());
-        for(ContentPackageDescriptor desc : descriptors) {
+        for(ContentPackageDescriptorImpl desc : descriptors) {
             if (desc.getName().equals("test-content")) {
                 assetDescriptor(desc, "test-content", TEST_PACKAGE_AID_A_10, file.toURI().toURL());
             } else {
@@ -64,21 +62,29 @@ public class ContentPackageScannerTest {
     }
 
     @SuppressWarnings("deprecation")
-    private void assetDescriptor(ContentPackageDescriptor desc, String descName, final ArtifactId descArtifactId, final URL descUrl) {
+    private void assetDescriptor(ContentPackageDescriptorImpl desc, String descName, final ArtifactId descArtifactId, final URL descUrl) {
         assertEquals(descName, desc.getName());
         assertEquals(descArtifactId, desc.getArtifact().getId());
         assertEquals(descUrl, desc.getArtifactFile());
 
-        assertTrue(desc.bundles != null && !desc.bundles.isEmpty());
-        BundleDescriptor bundles[] = desc.bundles.toArray(new BundleDescriptor[desc.bundles.size()]);
+        assertEquals(1, desc.getBundles().size());
 
-        assertEquals(bundles[0].getArtifact().getId().toString(), "org.apache.felix:org.apache.felix.framework:jar:bundle:6.0.1");
-        assertEquals("artifact start order",20, bundles[0].getArtifact().getStartOrder());
-        assertEquals("bundle start level",20, bundles[0].getBundleStartLevel());
+        assertEquals(desc.getBundles().get(0).getArtifact().getId().toString(), "org.apache.felix:org.apache.felix.framework:jar:bundle:6.0.1");
+        assertEquals("artifact start order",20, desc.getBundles().get(0).getArtifact().getStartOrder());
+        assertEquals("bundle start level",20, desc.getBundles().get(0).getBundleStartLevel());
 
-        assertTrue(desc.configs != null && !desc.configs.isEmpty());
-        Configuration configs[] = desc.configs.toArray(new Configuration[desc.configs.size()]);
-        assertConfiguration(configs[0]);
+        assertEquals(1, desc.getConfigurations().size());
+        assertConfiguration(desc.getConfigurations().get(0));
+
+        assertEquals(6, desc.getContentPaths().size());
+        assertTrue(desc.getContentPaths().contains("/.content.xml"));
+        assertTrue(desc.getContentPaths().contains("/libs/.content.xml"));
+        assertTrue(desc.getContentPaths().contains("/libs/a/.content.xml"));
+        assertTrue(desc.getContentPaths().contains("/libs/install/test-bundle.jar"));
+        assertTrue(desc.getContentPaths().contains("/libs/config/com.example.some.Component.xml"));
+        assertTrue(desc.getContentPaths().contains("/sub-content.zip"));
+
+        assertFalse(desc.getPackageProperties().isEmpty());
     }
 
     private void assertConfiguration(Configuration c) {