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/11 14:45:27 UTC

[sling-org-apache-sling-feature-analyser] branch master updated (7a43646 -> 00afdc8)

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

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


    from 7a43646  SLING-10796 : Provide option to error on embedded packages
     new 2188d6c  SLING-10803 : Improve content package scanning
     new 00afdc8  SLING-10803 : Improve content package scanning

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 pom.xml                                            |   1 +
 .../scanner/impl/ArtifactDescriptorImpl.java       |  25 +--
 .../scanner/impl/ContentPackageDescriptor.java     |   9 +-
 .../scanner/impl/ContentPackageScanner.java        | 238 ++++++++++++---------
 .../impl/ContentPackagesExtensionScanner.java      |   2 +-
 .../CheckContentPackagesForInstallablesTest.java   |   6 +-
 .../impl/CheckContentPackagesForPathsTest.java     |   4 +-
 .../scanner/impl/ContentPackageScannerTest.java    | 125 ++++++++---
 src/test/resources/test-config.xml                 |   4 +
 src/test/resources/test-noconfig.xml               |   6 +
 10 files changed, 263 insertions(+), 157 deletions(-)
 create mode 100644 src/test/resources/test-config.xml
 create mode 100644 src/test/resources/test-noconfig.xml

[sling-org-apache-sling-feature-analyser] 02/02: SLING-10803 : Improve content package scanning

Posted by cz...@apache.org.
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

commit 00afdc87cf072dcde0e96ff8a5f635b1d755ceda
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sat Sep 11 16:45:20 2021 +0200

    SLING-10803 : Improve content package scanning
---
 .../analyser/task/impl/CheckContentPackagesForInstallablesTest.java | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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 7e1fd99..27088f2 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
@@ -48,7 +48,7 @@ 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"));
+        final ContentPackageDescriptor cpd = new ContentPackageDescriptor("content", new Artifact(ArtifactId.parse("g:c:1")), new URL("file:/foo"), null);
         fd.getArtifactDescriptors().add(cpd);
 
         analyser.execute(ctx);
@@ -66,10 +66,10 @@ 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"));
+        final ContentPackageDescriptor cpd = new ContentPackageDescriptor("content", new Artifact(ArtifactId.parse("g:c:1")), new URL("file:/foo"), null);
         fd.getArtifactDescriptors().add(cpd);
 
-        final ContentPackageDescriptor embedded = new ContentPackageDescriptor("embedded", new Artifact(ArtifactId.parse("g:e:1")), new URL("file:/foo"));
+        final ContentPackageDescriptor embedded = new ContentPackageDescriptor("embedded", new Artifact(ArtifactId.parse("g:e:1")), new URL("file:/foo"), null);
         embedded.setContentPackageInfo(cpd.getArtifact(), "/path");
         fd.getArtifactDescriptors().add(embedded);
 

[sling-org-apache-sling-feature-analyser] 01/02: SLING-10803 : Improve content package scanning

Posted by cz...@apache.org.
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

commit 2188d6ce664b8bb8622b5ccb26370df7bd410b6b
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sat Sep 11 16:42:08 2021 +0200

    SLING-10803 : Improve content package scanning
---
 pom.xml                                            |   1 +
 .../scanner/impl/ArtifactDescriptorImpl.java       |  25 +--
 .../scanner/impl/ContentPackageDescriptor.java     |   9 +-
 .../scanner/impl/ContentPackageScanner.java        | 238 ++++++++++++---------
 .../impl/ContentPackagesExtensionScanner.java      |   2 +-
 .../impl/CheckContentPackagesForPathsTest.java     |   4 +-
 .../scanner/impl/ContentPackageScannerTest.java    | 125 ++++++++---
 src/test/resources/test-config.xml                 |   4 +
 src/test/resources/test-noconfig.xml               |   6 +
 9 files changed, 260 insertions(+), 154 deletions(-)

diff --git a/pom.xml b/pom.xml
index 2771039..10c120d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -54,6 +54,7 @@
                         <exclude>src/main/resources/META-INF/services/org.apache.sling.feature.scanner.spi.FrameworkScanner</exclude>
                         <exclude>src/test/resources/origins/**</exclude>
                         <exclude>src/test/resources/test-bundle-src/**</exclude>
+                        <exclude>src/test/resources/**.xml</exclude>
                     </excludes>
                 </configuration>
             </plugin>
diff --git a/src/main/java/org/apache/sling/feature/scanner/impl/ArtifactDescriptorImpl.java b/src/main/java/org/apache/sling/feature/scanner/impl/ArtifactDescriptorImpl.java
index cbfbdf7..6d0956c 100644
--- a/src/main/java/org/apache/sling/feature/scanner/impl/ArtifactDescriptorImpl.java
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/ArtifactDescriptorImpl.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sling.feature.scanner.impl;
 
-import java.io.IOException;
 import java.net.URL;
 import java.util.jar.Manifest;
 
@@ -43,37 +42,19 @@ public class ArtifactDescriptorImpl
      * @param name Optional name
      * @param artifact The artifact, must be provided
      * @param url Optional url
-     * @param hasManifest Whether that artifact must have a metafest
-     * @param isManifestOptional Whether the manifest is optional
-     * @throws IOException If processing fails
+     * @param Manifest manifest (optional)
      * @throws NullPointerException If artifact is {@code null}
      */
     public ArtifactDescriptorImpl(
             final String name,
             final Artifact artifact,
             final URL url,
-            final boolean hasManifest,
-            final boolean isManifestOptional) throws IOException  {
+            final Manifest manifest) {
         super(name != null ? name : artifact.getId().toMvnId());
         this.artifact = artifact;
         this.artifact.getId(); // throw NPE if artifact is null
         this.artifactFile = url;
-        Manifest mf = null;
-        if ( hasManifest ) {
-            try {
-                final Manifest origMf = url == null ? null : BundleDescriptorImpl.getManifest(url);
-                if ( origMf != null ) {
-                    mf = new Manifest(origMf);
-                } else if ( !isManifestOptional ) {
-                    throw new IOException(url + " : No manifest found.");
-                }
-            } catch ( final IOException ioe) {
-                if ( !isManifestOptional ) {
-                    throw ioe;
-                }
-            }
-        }
-        this.manifest = mf;
+        this.manifest = manifest;
     }
 
     @Override
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
index eae5513..13f1dde 100644
--- a/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageDescriptor.java
+++ b/src/main/java/org/apache/sling/feature/scanner/impl/ContentPackageDescriptor.java
@@ -16,10 +16,10 @@
  */
 package org.apache.sling.feature.scanner.impl;
 
-import java.io.IOException;
 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;
@@ -60,13 +60,14 @@ public class ContentPackageDescriptor extends ArtifactDescriptorImpl {
      * @param name The name
      * @param artifact The artifact
      * @param url The url to the binary
-     * @throws IOException If processing fails
+     * @param manifest The manifest (optional)
      * @throws NullPointerException If artifact is {@code null}
      */
     public ContentPackageDescriptor(final String name,
             final Artifact artifact,
-            final URL url) throws IOException  {
-        super(name, artifact, url, true, true);
+            final URL url,
+            final Manifest manifest) {
+        super(name, artifact, url, manifest);
     }
 
     /**
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 268735d..fff0c2a 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
@@ -25,12 +25,14 @@ import java.io.Reader;
 import java.net.URL;
 import java.nio.file.Files;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Properties;
 import java.util.Set;
 import java.util.jar.JarFile;
+import java.util.jar.Manifest;
 import java.util.zip.ZipEntry;
 
 import org.apache.sling.feature.Artifact;
@@ -50,12 +52,14 @@ public class ContentPackageScanner {
 
     private final byte[] buffer = new byte[65536];
 
-    private enum FileType {
+    enum FileType {
         BUNDLE,
         CONFIG,
-        PACKAGE
+        PACKAGE,
     }
 
+    private static final List<String> CFG_EXTENSIONS = Arrays.asList(".config", ".cfg", ".cfg.json", ".xml");
+
     /**
      * Scan the content package for embedded artifacts
      * @param artifact The content package
@@ -66,97 +70,120 @@ public class ContentPackageScanner {
     public Set<ContentPackageDescriptor> scan(final Artifact artifact, final URL url) throws IOException {
         final Set<ContentPackageDescriptor> contentPackages = new HashSet<>();
         if (url != null) {
-            final int lastDot = url.getPath().lastIndexOf(".");
-            final ContentPackageDescriptor cp = new ContentPackageDescriptor(url.getPath().substring(url.getPath().lastIndexOf("/") + 1, lastDot), artifact, url);
-
-            extractContentPackage(cp, contentPackages, url);
-
-            contentPackages.add(cp);
-            cp.lock();
+            final String path = url.getPath();
+            final int lastDotInUrl = path.lastIndexOf(".");
+            final String name = path.substring(path.lastIndexOf("/") + 1, lastDotInUrl);
+             extractContentPackage(null, null, artifact, name, url, contentPackages);
         }
 
         return contentPackages;
     }
 
-    private void extractContentPackage(final ContentPackageDescriptor cp,
-            final Set<ContentPackageDescriptor> infos,
-            final URL archive)
+    /**
+     * Detect the file type, bundle, configuration or embedded package from the content path
+     * @param contentPath The content path
+     * @return The detected file type or {@code null}
+     */
+    FileType detectContentFileType(final String contentPath) {
+        FileType fileType = null;
+        if (contentPath.endsWith(".zip")) {
+            // embedded content package
+            fileType = FileType.PACKAGE;
+
+            // check for libs or apps
+        } else if (contentPath.startsWith("/libs/") || contentPath.startsWith("/apps/")) {
+
+            // check if this is an install folder (I)
+            // install folders are either named:
+            // "install" or
+            // "install.{runmode}"
+            boolean isInstall = contentPath.indexOf("/install/") != -1;
+            if (!isInstall) {
+                final int pos = contentPath.indexOf("/install.");
+                if (pos != -1) {
+                    final int endSlashPos = contentPath.indexOf('/', pos + 1);
+                    if (endSlashPos != -1) {
+                        isInstall = true;
+                    }
+                }
+            }
+            if (!isInstall) {
+                // check if this is an install folder (II)
+                // config folders are either named:
+                // "config" or
+                // "config.{runmode}"
+                isInstall = contentPath.indexOf("/config/") != -1;
+                if (!isInstall) {
+                    final int pos = contentPath.indexOf("/config.");
+                    if (pos != -1) {
+                        final int endSlashPos = contentPath.indexOf('/', pos + 1);
+                        if (endSlashPos != -1) {
+                            isInstall = true;
+                        }
+                    }
+                }
+            }
+
+            if (isInstall) {
+
+                if (contentPath.endsWith(".jar")) {
+                    fileType = FileType.BUNDLE;
+                } else {
+                    for(final String ext : CFG_EXTENSIONS) {
+                        if ( contentPath.endsWith(ext) ) {
+                            fileType = FileType.CONFIG;
+                            break;
+                        }
+                    }
+                } 
+            }
+        }
+        return fileType;
+    }
+
+    private ContentPackageDescriptor extractContentPackage(final ContentPackageDescriptor parentPackage,
+            final String parentContentPath,
+            final Artifact packageArtifact,
+            final String name,
+            final URL archiveUrl,
+            final Set<ContentPackageDescriptor> infos)
     throws IOException {
-        logger.debug("Analyzing Content Package {}", archive);
+        logger.debug("Analyzing Content Package {}", archiveUrl);
 
         final File tempDir = Files.createTempDirectory(null).toFile();
         try {
-            final File toDir = new File(tempDir, archive.getPath().substring(archive.getPath().lastIndexOf("/") + 1));
+            final File toDir = new File(tempDir, archiveUrl.getPath().substring(archiveUrl.getPath().lastIndexOf("/") + 1));
             toDir.mkdirs();
 
+            Manifest manifest = null;
             final List<File> toProcess = new ArrayList<>();
-
-            try (final JarFile zipFile = IOUtils.getJarFileFromURL(archive, true, null)) {
+            final List<String> contentPaths = new ArrayList<>();
+            final List<BundleDescriptor> bundles = new ArrayList<>();
+            final List<Configuration> configs = new ArrayList<>();
+    
+            try (final JarFile zipFile = IOUtils.getJarFileFromURL(archiveUrl, true, null)) {
                 Enumeration<? extends ZipEntry> entries = zipFile.entries();
                 while (entries.hasMoreElements()) {
                     final ZipEntry entry = entries.nextElement();
                     final String entryName = entry.getName();
+
+                    // skip dirs
+                    if ( entryName.endsWith("/") ) {
+                        continue;
+                    }
+                    
                     logger.debug("Content package entry {}", entryName);
 
-                    if ( !entryName.endsWith("/") && entryName.startsWith("jcr_root/") ) {
+                    if ( entryName.startsWith("jcr_root/") ) {
                         final String contentPath = entryName.substring(8);
-                        cp.paths.add(contentPath);
-
-                        FileType fileType = null;
-
-                        if (entryName.endsWith(".zip")) {
-                            // embedded content package
-                            fileType = FileType.PACKAGE;
-
-                            // check for libs or apps
-                        } else if (contentPath.startsWith("/libs/") || contentPath.startsWith("/apps/")) {
-
-                            // check if this is an install folder (I)
-                            // install folders are either named:
-                            // "install" or
-                            // "install.{runmode}"
-                            boolean isInstall = contentPath.indexOf("/install/") != -1;
-                            if (!isInstall) {
-                                final int pos = contentPath.indexOf("/install.");
-                                if (pos != -1) {
-                                    final int endSlashPos = contentPath.indexOf('/', pos + 1);
-                                    if (endSlashPos != -1) {
-                                        isInstall = true;
-                                    }
-                                }
-                            }
-                            if (!isInstall) {
-                                // check if this is an install folder (II)
-                                // config folders are either named:
-                                // "config" or
-                                // "config.{runmode}"
-                                isInstall = contentPath.indexOf("/config/") != -1;
-                                if (!isInstall) {
-                                    final int pos = contentPath.indexOf("/config.");
-                                    if (pos != -1) {
-                                        final int endSlashPos = contentPath.indexOf('/', pos + 1);
-                                        if (endSlashPos != -1) {
-                                            isInstall = true;
-                                        }
-                                    }
-                                }
-                            }
-
-                            if (isInstall) {
-
-                                if (contentPath.endsWith(".jar")) {
-                                    fileType = FileType.BUNDLE;
-                                } else if (contentPath.endsWith(".xml") || contentPath.endsWith(".config") || contentPath.endsWith(".cfg.json")) {
-                                    fileType = FileType.CONFIG;
-                                }
-                            }
-                        }
+                        contentPaths.add(contentPath);
 
+                        final FileType fileType = detectContentFileType(contentPath);
                         if (fileType != null) {
-                            logger.debug("- extracting : {}", entryName);
+                            logger.debug("- extracting : {}", contentPath);
                             final File newFile = new File(toDir, entryName.replace('/', File.separatorChar));
                             newFile.getParentFile().mkdirs();
-
+    
                             try (
                                     final FileOutputStream fos = new FileOutputStream(newFile);
                                     final InputStream zis = zipFile.getInputStream(entry);
@@ -166,7 +193,7 @@ public class ContentPackageScanner {
                                     fos.write(buffer, 0, len);
                                 }
                             }
-
+    
                             if (fileType == FileType.BUNDLE) {
                                 int startLevel = 20;
                                 final int lastSlash = contentPath.lastIndexOf('/');
@@ -177,46 +204,57 @@ public class ContentPackageScanner {
                                 } catch (final NumberFormatException ignore) {
                                     // ignore
                                 }
-
+    
                                 final Artifact bundle = new Artifact(extractArtifactId(tempDir, newFile));
                                 bundle.setStartOrder(startLevel);
                                 final BundleDescriptor info = new BundleDescriptorImpl(bundle, newFile.toURI().toURL(),
                                         startLevel);
                                 bundle.getMetadata().put(ContentPackageDescriptor.METADATA_PACKAGE,
-                                        cp.getArtifact().getId().toMvnId());
+                                        packageArtifact.getId().toMvnId());
                                 bundle.getMetadata().put(ContentPackageDescriptor.METADATA_PATH, contentPath);
-
-                                cp.bundles.add(info);
-
+    
+                                bundles.add(info);
+    
                             } else if (fileType == FileType.CONFIG) {
-
-                                final Configuration configEntry = this.process(newFile, cp.getArtifact(), contentPath);
+    
+                                final Configuration configEntry = this.processConfiguration(newFile, packageArtifact.getId(), contentPath);
                                 if (configEntry != null) {
-
-                                    cp.configs.add(configEntry);
+                                    configs.add(configEntry);
                                 }
-
+    
                             } else if (fileType == FileType.PACKAGE) {
                                 toProcess.add(newFile);
-                            }
-
+                            } 
+                        }
+                    } else if ( entryName.equals("META-INF/MANIFEST.MF") ) {
+                        try ( final InputStream zis = zipFile.getInputStream(entry)) {
+                            manifest = new Manifest(zis);
                         }
-
                     }
+                }
 
+                final ContentPackageDescriptor desc = new ContentPackageDescriptor(name, packageArtifact, archiveUrl, manifest);
+                if ( parentPackage != null ) {
+                    desc.setContentPackageInfo(parentPackage.getArtifact(), parentContentPath);
                 }
+                desc.bundles.addAll(bundles);
+                desc.configs.addAll(configs);
+                desc.paths.addAll(contentPaths);
 
                 for (final File f : toProcess) {
-                    extractContentPackage(cp, infos, f.toURI().toURL());
                     final int lastDot = f.getName().lastIndexOf(".");
+                    final String subName = f.getName().substring(0, lastDot);
+                    final String contentPath = f.getAbsolutePath().substring(toDir.getAbsolutePath().length()).replace(File.separatorChar, '/');
+
                     // create synthetic artifact with a synthetic id containing the file name
-                    final Artifact artifact = new Artifact(cp.getArtifact().getId().changeClassifier(f.getName()));
-                    final ContentPackageDescriptor i = new ContentPackageDescriptor(f.getName().substring(0, lastDot), artifact, f.toURI().toURL());
-                    i.setContentPackageInfo(cp.getArtifact(), f.getName());
-                    infos.add(i);
+                    final Artifact subArtifact = new Artifact(packageArtifact.getId().changeClassifier(subName));
 
-                    i.lock();
+                    extractContentPackage(desc, contentPath, subArtifact, subName, f.toURI().toURL(), infos);
                 }
+
+                infos.add(desc);
+                desc.lock();
+                return desc;
             }
         } finally {
             deleteOnExitRecursive(tempDir);
@@ -326,13 +364,13 @@ public class ContentPackageScanner {
         throw new IOException(bundleFile.getName() + " has no maven coordinates!");
     }
 
-    private Configuration process(final File configFile,
-            final Artifact packageArtifact,
+    Configuration processConfiguration(final File configFile,
+            final ArtifactId packageArtifactId,
             final String contentPath)
     throws IOException {
 
         boolean isConfig = true;
-        if ( configFile.getName().endsWith(".xml") ) {
+        if ( contentPath.endsWith(".xml") ) {
             final String contents = Files.readAllLines(configFile.toPath()).toString();
             if ( contents.indexOf("jcr:primaryType=\"sling:OsgiConfig\"") == -1 ) {
                 isConfig = false;
@@ -341,13 +379,19 @@ public class ContentPackageScanner {
 
         if ( isConfig ) {
             final String id;
-            if ( ".content.xml".equals(configFile.getName()) ) {
+            if ( contentPath.endsWith("/.content.xml") ) {
                 final int lastSlash = contentPath.lastIndexOf('/');
                 final int previousSlash = contentPath.lastIndexOf('/', lastSlash - 1);
                 id = contentPath.substring(previousSlash + 1, lastSlash);
             } else {
-                final int lastDot = configFile.getName().lastIndexOf('.');
-                id = configFile.getName().substring(0, lastDot);
+                String name = contentPath;
+                final int lastSlash = contentPath.lastIndexOf('/');
+                for(final String ext : CFG_EXTENSIONS) {
+                    if ( name.endsWith(ext) ) {
+                        name = name.substring(lastSlash + 1, name.length() - ext.length());
+                    }
+                }
+                id = name;
             }
 
             final String pid;
@@ -361,7 +405,7 @@ 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,
-                    packageArtifact.getId().toMvnId());
+                    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 7233cee..e082acc 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
@@ -78,7 +78,7 @@ public class ContentPackagesExtensionScanner implements ExtensionScanner {
             }
             else {
                 final int lastDot = a.getId().toMvnPath().lastIndexOf(".");
-                ContentPackageDescriptor desc = new ContentPackageDescriptor(a.getId().toMvnPath().substring(a.getId().toMvnPath().lastIndexOf("/") + 1, lastDot), a, url);
+                ContentPackageDescriptor desc = new ContentPackageDescriptor(a.getId().toMvnPath().substring(a.getId().toMvnPath().lastIndexOf("/") + 1, lastDot), a, url, null);
                 desc.lock();
                 cd.getArtifactDescriptors().add(desc);
             }
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 a88834a..c88cbbf 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
@@ -71,7 +71,7 @@ public class CheckContentPackagesForPathsTest {
 
         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"));
+            new URL("https://sling.apache.org"), null);
         desc.paths.add("/b/foo");
         desc.paths.add("/a");
         desc.paths.add("/a/foo");
@@ -95,7 +95,7 @@ public class CheckContentPackagesForPathsTest {
 
         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"));
+            new URL("https://sling.apache.org"), null);
         desc.paths.add("/a/foo");
         desc.paths.add("/a/bar");
 
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 25ba20f..75192f8 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
@@ -18,14 +18,17 @@ package org.apache.sling.feature.scanner.impl;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.File;
 import java.io.IOException;
 import java.net.URISyntaxException;
+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;
@@ -35,36 +38,22 @@ import org.junit.Test;
 
 public class ContentPackageScannerTest {
 
-    private static final String COORDINATES_TEST_PACKAGE_A_10 = "my_packages:test_a:1.0";
-    private static ArtifactId TEST_PACKAGE_AID_A_10 = ArtifactId.fromMvnId(COORDINATES_TEST_PACKAGE_A_10);
-
-    private File file;
-
-    private Artifact artifact;
-
-    ContentPackageDescriptor test_descriptor;
-
-    @Before
-    public void setUp() throws Exception {
-        file = getTestFile("/test-content.zip");
-
-        artifact = new Artifact(TEST_PACKAGE_AID_A_10);
-
-        test_descriptor = new ContentPackageDescriptor("test-content", artifact, file.toURI().toURL());
-    }
 
     @Test
     public void testScan() throws URISyntaxException, IOException {
+        final File file = getTestFile("/test-content.zip");
+
+        final String COORDINATES_TEST_PACKAGE_A_10 = "my_packages:test_a:1.0";
+        final ArtifactId TEST_PACKAGE_AID_A_10 = ArtifactId.fromMvnId(COORDINATES_TEST_PACKAGE_A_10);
+
         ContentPackageScanner scanner = new ContentPackageScanner();
-        Set<ContentPackageDescriptor> descriptors = scanner.scan(artifact, file.toURI().toURL());
+        Set<ContentPackageDescriptor> descriptors = scanner.scan(new Artifact(TEST_PACKAGE_AID_A_10), file.toURI().toURL());
+        assertEquals(2, descriptors.size());
         for(ContentPackageDescriptor desc : descriptors) {
-            String name = desc.getName();
-            assertNotNull(name);
-
-            if(name.equals(test_descriptor.getName())) {
-                assetDescriptor(desc, desc.getName());
+            if (desc.getName().equals("test-content")) {
+                assetDescriptor(desc, "test-content", TEST_PACKAGE_AID_A_10, file.toURI().toURL());
             } else {
-                assertEquals(name, "sub-content");
+                assertEquals(desc.getName(), "sub-content");
             }
             assertNotNull(desc.getManifest());
         }
@@ -75,10 +64,10 @@ public class ContentPackageScannerTest {
     }
 
     @SuppressWarnings("deprecation")
-    private void assetDescriptor(ContentPackageDescriptor desc, String descName) {
-        assertEquals(descName, test_descriptor.getName());
-        assertEquals(desc.getArtifact().getId().getArtifactId(), test_descriptor.getArtifact().getId().getArtifactId());
-        assertEquals(desc.getArtifactFile().toString(), test_descriptor.getArtifactFile().toString());
+    private void assetDescriptor(ContentPackageDescriptor 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()]);
@@ -97,4 +86,84 @@ public class ContentPackageScannerTest {
         String contentPath = (String) props.get(":configurator:feature-content-path");
         assertEquals("/libs/config/com.example.some.Component.xml", contentPath);
     }
+
+    @Test
+    public void testDetectContentFileType() {
+        final ContentPackageScanner scanner = new ContentPackageScanner();
+
+        // any file ending in zip is a content package
+        assertEquals(ContentPackageScanner.FileType.PACKAGE, scanner.detectContentFileType("/etc/package/my-package.zip"));
+        assertEquals(ContentPackageScanner.FileType.PACKAGE, scanner.detectContentFileType("/libs/app/install/component.zip"));
+
+        // configs need to be below /libs, /apps in install or config folders
+        assertEquals(ContentPackageScanner.FileType.CONFIG, scanner.detectContentFileType("/libs/app/install/component.cfg"));
+        assertEquals(ContentPackageScanner.FileType.CONFIG, scanner.detectContentFileType("/libs/app/install/component.config"));
+        assertEquals(ContentPackageScanner.FileType.CONFIG, scanner.detectContentFileType("/libs/app/install/component.xml"));
+        assertEquals(ContentPackageScanner.FileType.CONFIG, scanner.detectContentFileType("/libs/app/install/component.cfg.json"));
+        assertNull(scanner.detectContentFileType("/libs/app/install/component.txt"));
+        assertNull(scanner.detectContentFileType("/content/app/install/component.cfg"));
+        assertNull(scanner.detectContentFileType("/content/app/install/component.config"));
+        assertNull(scanner.detectContentFileType("/content/app/install/component.xml"));
+        assertNull(scanner.detectContentFileType("/content/app/install/component.cfg.json"));
+
+        // bundles need to be below /libs, /apps in install orfolders
+        assertEquals(ContentPackageScanner.FileType.BUNDLE, scanner.detectContentFileType("/libs/app/install/component.jar"));
+        assertNull(scanner.detectContentFileType("/content/app/install/component.jar"));
+    }
+
+    @Test
+    public void testProcessConfiguration() throws IOException, URISyntaxException {
+        final ContentPackageScanner scanner = new ContentPackageScanner();
+
+        final ArtifactId pckId = ArtifactId.parse("g:a:1");
+
+        Configuration cfg = scanner.processConfiguration(null, pckId, "/libs/app/install/my.component.cfg");
+        assertEquals("my.component", cfg.getPid());
+        assertNull(cfg.getFactoryPid());
+
+        cfg = scanner.processConfiguration(null, pckId, "/libs/app/install/my.component.config");
+        assertEquals("my.component", cfg.getPid());
+        assertNull(cfg.getFactoryPid());
+
+        cfg = scanner.processConfiguration(null, pckId, "/libs/app/install/my.component.cfg.json");
+        assertEquals("my.component", cfg.getPid());
+        assertNull(cfg.getFactoryPid());
+
+        cfg = scanner.processConfiguration(null, pckId, "/libs/app/install/my.component-name.cfg");
+        assertEquals("my.component~name", cfg.getPid());
+        assertEquals("my.component", cfg.getFactoryPid());
+        assertEquals("name", cfg.getName());
+
+        cfg = scanner.processConfiguration(null, pckId, "/libs/app/install/my.component-name.config");
+        assertEquals("my.component~name", cfg.getPid());
+        assertEquals("my.component", cfg.getFactoryPid());
+        assertEquals("name", cfg.getName());
+
+        cfg = scanner.processConfiguration(null, pckId, "/libs/app/install/my.component~name.cfg.json");
+        assertEquals("my.component~name", cfg.getPid());
+        assertEquals("my.component", cfg.getFactoryPid());
+        assertEquals("name", cfg.getName());
+
+        final File cfgFile = getTestFile("/test-config.xml");
+        cfg = scanner.processConfiguration(cfgFile, pckId, "/libs/app/install/my.component.xml");
+        assertEquals("my.component", cfg.getPid());
+        assertNull(cfg.getFactoryPid());
+
+        cfg = scanner.processConfiguration(cfgFile, pckId, "/libs/app/install/my.component~name.xml");
+        assertEquals("my.component~name", cfg.getPid());
+        assertEquals("my.component", cfg.getFactoryPid());
+        assertEquals("name", cfg.getName());
+
+        cfg = scanner.processConfiguration(cfgFile, pckId, "/libs/app/install/my.component/.content.xml");
+        assertEquals("my.component", cfg.getPid());
+        assertNull(cfg.getFactoryPid());
+
+        cfg = scanner.processConfiguration(cfgFile, pckId, "/libs/app/install/my.component~name/.content.xml");
+        assertEquals("my.component~name", cfg.getPid());
+        assertEquals("my.component", cfg.getFactoryPid());
+        assertEquals("name", cfg.getName());
+
+        final File nocfgFile = getTestFile("/test-noconfig.xml");
+        assertNull(scanner.processConfiguration(nocfgFile, pckId, "/libs/app/install/my.component.xml"));
+    }
 }
diff --git a/src/test/resources/test-config.xml b/src/test/resources/test-config.xml
new file mode 100644
index 0000000..2157869
--- /dev/null
+++ b/src/test/resources/test-config.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
+    jcr:primaryType="sling:OsgiConfig"
+    foo="bar"/>
\ No newline at end of file
diff --git a/src/test/resources/test-noconfig.xml b/src/test/resources/test-noconfig.xml
new file mode 100644
index 0000000..754346e
--- /dev/null
+++ b/src/test/resources/test-noconfig.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0"
+    jcr:primaryType="sling:Component"
+    jcr:title="Quick Search"
+    sling:resourceSuperType="core/wcm/components/search/v1/search"
+    componentGroup="My Site - Structure"/>
\ No newline at end of file