You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ra...@apache.org on 2020/03/05 21:23:46 UTC

[sling-scriptingbundle-maven-plugin] branch master updated: SLING-9161 - The Scripting Bundle Maven Plugin should allow more flexible ways for the project structure

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

radu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-scriptingbundle-maven-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new d50e074  SLING-9161 - The Scripting Bundle Maven Plugin should allow more flexible ways for the project structure
d50e074 is described below

commit d50e0742d6a20d6126e0687c90233cf1395a1287
Author: Radu Cotescu <17...@users.noreply.github.com>
AuthorDate: Thu Mar 5 22:23:39 2020 +0100

    SLING-9161 - The Scripting Bundle Maven Plugin should allow more flexible ways for the project structure
---
 .sling-module.json                                 |   5 +
 pom.xml                                            | 158 ++++++++++++-
 .../scriptingbundle/maven/plugin/Capabilities.java |  41 ++++
 .../scriptingbundle/maven/plugin/MetadataMojo.java | 195 ++++++++++++++++
 .../maven/plugin/ProvidedCapability.java           | 156 +++++++++++++
 .../maven/plugin/RequiredCapability.java           |  97 ++++++++
 .../maven/plugin/ResourceTypeFolderAnalyser.java   | 247 +++++++++++++++++++++
 .../maven/plugin/ResourceTypeFolderPredicate.java  |  83 +++++++
 .../maven/plugin/ScriptingMavenPlugin.java         | 213 ------------------
 .../maven/plugin/MetadataMojoTest.java             | 137 ++++++++++++
 .../maven/plugin/ScriptingMavenPluginTest.java     |  80 -------
 src/test/resources/project-1/pom.xml               |  46 ++++
 .../org.apache.sling.foobar/1.0.0/depth1/100.html  |  18 ++
 .../org.apache.sling.foobar/1.0.0/depth1/200.html  |  18 ++
 .../1.0.0/depth1/depth2/100.html                   |  18 ++
 .../org.apache.sling.foobar/1.0.0/extends          |   1 +
 .../org.apache.sling.foobar/1.0.0/foobar.html      |  18 ++
 .../org.apache.sling.foobar/1.0.0/requires         |   1 +
 .../javax.script/org.apache.sling.foobar/GET.html  |  18 ++
 .../org.apache.sling.foobar/depth1/100.html        |  18 ++
 .../org.apache.sling.foobar/depth1/200.html        |  18 ++
 .../org.apache.sling.foobar/depth1/depth2/100.html |  18 ++
 .../javax.script/org.apache.sling.foobar/extends   |   1 +
 .../org.apache.sling.wrongbar/wrongbar             |   0
 .../wrongbar.has.too.many.selectors.html           |  18 ++
 .../org/apache/sling/bar/1.0.0/bar.html            |  18 ++
 .../org/apache/sling/bar/1.0.0/depth1/100.html     |  18 ++
 .../org/apache/sling/bar/1.0.0/depth1/200.html     |  18 ++
 .../apache/sling/bar/1.0.0/depth1/depth2/100.html  |  18 ++
 .../org/apache/sling/foo/depth1/100.html           |  18 ++
 .../org/apache/sling/foo/depth1/200.html           |  18 ++
 .../org/apache/sling/foo/depth1/depth2/100.html    |  18 ++
 .../javax.script/org/apache/sling/foo/foo.html     |  19 ++
 .../main/resources/javax.script/sling/sling.html   |  18 ++
 src/test/resources/spotbugs-exclude.xml            |  24 ++
 35 files changed, 1506 insertions(+), 304 deletions(-)

diff --git a/.sling-module.json b/.sling-module.json
new file mode 100644
index 0000000..b83f728
--- /dev/null
+++ b/.sling-module.json
@@ -0,0 +1,5 @@
+{
+  "jenkins": {
+    "jdks": [8, 11, 13]
+  }
+}
diff --git a/pom.xml b/pom.xml
index d0d4654..ffe6217 100644
--- a/pom.xml
+++ b/pom.xml
@@ -40,38 +40,174 @@
 
     <properties>
         <sling.java.version>8</sling.java.version>
+        <maven.version>3.6.0</maven.version>
     </properties>
 
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-plugin-plugin</artifactId>
+                <version>${maven.version}</version>
+                <executions>
+                    <execution>
+                        <id>mojo-descriptor</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>descriptor</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>generated-helpmojo</id>
+                        <goals>
+                            <goal>helpmojo</goal>
+                        </goals>
+                    </execution>
+                </executions>
+                <configuration>
+                    <skipErrorNoDescriptorsFound>true</skipErrorNoDescriptorsFound>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.codehaus.plexus</groupId>
+                <artifactId>plexus-component-metadata</artifactId>
+                <version>2.1.0</version>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>generate-metadata</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>**/*.iml</exclude>
+                        <exclude>**/target/**/*</exclude>
+                        <exclude>src/site/markdown/**</exclude>
+                        <exclude>src/test/resources/**/extends</exclude>
+                        <exclude>src/test/resources/**/requires</exclude>
+                        <exclude>src/test/resources/**/wrongbar</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-scm-publish-plugin</artifactId>
+                <configuration>
+                    <checkoutDirectory>${user.home}/maven-sites/${maven.site.path}</checkoutDirectory>
+                    <tryUpdate>true</tryUpdate>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>com.github.spotbugs</groupId>
+                <artifactId>spotbugs-maven-plugin</artifactId>
+                <version>3.1.12.2</version>
+                <configuration>
+                    <effort>Max</effort>
+                    <xmlOutput>true</xmlOutput>
+                    <excludeFilterFile>src/test/resources/spotbugs-exclude.xml</excludeFilterFile>
+                </configuration>
+                <executions>
+                    <execution>
+                        <id>find-bugs</id>
+                        <phase>process-classes</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
+
+    <!-- force maven-plugin-testing-harness to use newer plexus container (https://issues.apache.org/jira/browse/MPLUGINTESTING-53) -->
+    <dependencyManagement>
+        <dependencies>
+            <!-- maven -->
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-core</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-compat</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-model</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-plugin-api</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.maven</groupId>
+                <artifactId>maven-aether-provider</artifactId>
+                <version>${maven.version}</version>
+            </dependency>
+        </dependencies>
+    </dependencyManagement>
+
 
     <dependencies>
         <dependency>
-          <groupId>org.apache.maven</groupId>
-          <artifactId>maven-plugin-api</artifactId>
-          <version>3.0</version>
+            <groupId>org.jetbrains</groupId>
+            <artifactId>annotations</artifactId>
         </dependency>
 
-        <!-- dependencies to annotations -->
         <dependency>
-          <groupId>org.apache.maven.plugin-tools</groupId>
-          <artifactId>maven-plugin-annotations</artifactId>
-          <version>3.4</version>
-          <scope>provided</scope>
+          <groupId>org.apache.maven</groupId>
+          <artifactId>maven-plugin-api</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
-            <artifactId>maven-project</artifactId>
-            <version>2.2.1</version>
+            <artifactId>maven-model</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-core</artifactId>
-            <version>3.5.0</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.maven</groupId>
+            <artifactId>maven-compat</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.maven.plugin-tools</groupId>
+            <artifactId>maven-plugin-annotations</artifactId>
+            <version>3.4</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
             <groupId>org.osgi</groupId>
             <artifactId>osgi.core</artifactId>
             <version>7.0.0</version>
             <scope>compile</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.apache.maven.plugin-testing</groupId>
+            <artifactId>maven-plugin-testing-harness</artifactId>
+            <version>3.3.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.sonatype.plexus</groupId>
+            <artifactId>plexus-build-api</artifactId>
+            <version>0.0.7</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/Capabilities.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/Capabilities.java
new file mode 100644
index 0000000..0551c39
--- /dev/null
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/Capabilities.java
@@ -0,0 +1,41 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scriptingbundle.maven.plugin;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class Capabilities {
+
+    private final Set<ProvidedCapability> providedCapabilities;
+    private final Set<RequiredCapability> requiredCapabilities;
+
+    Capabilities(Set<ProvidedCapability> providedCapabilities, Set<RequiredCapability> requiredCapabilities) {
+        this.providedCapabilities = providedCapabilities;
+        this.requiredCapabilities = requiredCapabilities;
+    }
+
+    public Set<ProvidedCapability> getProvidedCapabilities() {
+        return Collections.unmodifiableSet(providedCapabilities);
+    }
+
+    public Set<RequiredCapability> getRequiredCapabilities() {
+        return Collections.unmodifiableSet(requiredCapabilities);
+    }
+}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojo.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojo.java
new file mode 100644
index 0000000..e614675
--- /dev/null
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojo.java
@@ -0,0 +1,195 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scriptingbundle.maven.plugin;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.shared.utils.io.DirectoryScanner;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.framework.Constants;
+import org.osgi.framework.VersionRange;
+
+/**
+ * The {@code metadata} goal will generate two Maven project properties, namely {@code org.apache.sling.scriptingbundle.maven.plugin
+ * .Require-Capability} and {@code org.apache.sling.scriptingbundle.maven.plugin.Provide-Capability} which can be used to generate the
+ * corresponding OSGi bundle headers for bundles providing scripts executable by a {@link javax.script.ScriptEngine}.
+ */
+@Mojo(name = "metadata",
+      defaultPhase = LifecyclePhase.PACKAGE)
+public class MetadataMojo extends AbstractMojo {
+
+    @Parameter(defaultValue = "${project}",
+               readonly = true)
+    private MavenProject project;
+
+    /**
+     * Defines where this goal will look for scripts in the project.
+     *
+     * @since 0.1.0
+     */
+    @Parameter(defaultValue = "${project.basedir}/src/main/resources/javax.script")
+    private String scriptsDirectory;
+
+    static final Set<String> METHODS = new HashSet<>(Arrays.asList("TRACE", "OPTIONS", "GET", "HEAD", "POST", "PUT",
+            "DELETE", "PATCH"));
+    static final String EXTENDS_FILE = "extends";
+    static final String REQUIRES_FILE = "requires";
+    static final String CAPABILITY_NS = "sling.resourceType";
+    static final String CAPABILITY_SELECTORS_AT = CAPABILITY_NS + ".selectors:List<String>";
+    static final String CAPABILITY_EXTENSIONS_AT = CAPABILITY_NS + ".extensions:List<String>";
+    static final String CAPABILITY_METHODS_AT = "sling.servlet.methods:List<String>";
+    static final String CAPABILITY_VERSION_AT = "version:Version";
+    static final String CAPABILITY_EXTENDS_AT = "extends";
+
+    private Capabilities capabilities;
+
+    public void execute() throws MojoExecutionException {
+        File sdFile = new File(scriptsDirectory);
+        if (!sdFile.exists()) {
+            sdFile = new File(project.getBasedir(), scriptsDirectory);
+            if (!sdFile.exists()) {
+                throw new MojoExecutionException("Cannot find file " + scriptsDirectory + ".");
+            }
+        }
+        final String root = sdFile.getAbsolutePath();
+        DirectoryScanner scanner = new DirectoryScanner();
+        scanner.setBasedir(sdFile);
+        scanner.setIncludes("**");
+        scanner.setExcludes("**/*.class");
+        scanner.addDefaultExcludes();
+        scanner.scan();
+        List<String> includedDirectories = Arrays.asList(scanner.getIncludedDirectories());
+        includedDirectories.sort(Collections.reverseOrder());
+        List<Path> resourceTypeDirectories =
+                includedDirectories.stream()
+                        .map(includedDirectory -> Paths.get(root, includedDirectory))
+                        .filter(new ResourceTypeFolderPredicate(getLog())).collect(Collectors.toList());
+
+        getLog().info("Detected resource type directories: " + resourceTypeDirectories);
+        capabilities = generateCapabilities(root, resourceTypeDirectories);
+        String providedCapabilitiesDefinition = getProvidedCapabilitiesString(capabilities);
+        String requiredCapabilitiesDefinition = getRequiredCapabilitiesString(capabilities);
+        project.getProperties().put(this.getClass().getPackage().getName() + "." + Constants.PROVIDE_CAPABILITY,
+                    providedCapabilitiesDefinition);
+        project.getProperties().put(this.getClass().getPackage().getName() + "." + Constants.REQUIRE_CAPABILITY,
+                    requiredCapabilitiesDefinition);
+    }
+
+    @NotNull
+    private Capabilities generateCapabilities(@NotNull String root, @NotNull List<Path> resourceTypeDirectories) {
+        ResourceTypeFolderAnalyser analyser = new ResourceTypeFolderAnalyser(getLog(), Paths.get(root));
+        Set<ProvidedCapability> providedCapabilities = new LinkedHashSet<>();
+        Set<RequiredCapability> requiredCapabilities = new LinkedHashSet<>();
+        for (Path resourceTypeDirectory : resourceTypeDirectories) {
+            Capabilities resourceTypeCapabilities = analyser.getCapabilities(resourceTypeDirectory);
+            providedCapabilities.addAll(resourceTypeCapabilities.getProvidedCapabilities());
+            requiredCapabilities.addAll(resourceTypeCapabilities.getRequiredCapabilities());
+        }
+        return new Capabilities(providedCapabilities, requiredCapabilities);
+    }
+
+    @NotNull
+    private String getProvidedCapabilitiesString(Capabilities capabilities) {
+        StringBuilder builder = new StringBuilder();
+        int pcNum = capabilities.getProvidedCapabilities().size();
+        int pcIndex = 0;
+        for (ProvidedCapability capability : capabilities.getProvidedCapabilities()) {
+            builder.append(CAPABILITY_NS).append(";");
+            builder.append(CAPABILITY_NS).append("=").append("\"").append(capability.getResourceType()).append("\"");
+            Optional.ofNullable(capability.getVersion()).ifPresent(version ->
+                    builder.append(";")
+                            .append(CAPABILITY_VERSION_AT).append("=").append("\"").append(version).append("\"")
+            );
+            Optional.ofNullable(capability.getExtendsResourceType()).ifPresent(extendedResourceType ->
+                    builder.append(";")
+                            .append(CAPABILITY_EXTENDS_AT).append("=").append("\"").append(extendedResourceType).append("\"")
+            );
+            Optional.ofNullable(capability.getRequestMethod()).ifPresent(method ->
+                    builder.append(";")
+                            .append(CAPABILITY_METHODS_AT).append("=").append("\"").append(method).append("\"")
+            );
+            Optional.ofNullable(capability.getRequestExtension()).ifPresent(requestExtension ->
+                    builder.append(";")
+                            .append(CAPABILITY_EXTENSIONS_AT).append("=").append("\"").append(requestExtension).append("\"")
+            );
+            if (!capability.getSelectors().isEmpty()) {
+                builder.append(";").append(CAPABILITY_SELECTORS_AT).append("=").append("\"");
+                List<String> selectors = capability.getSelectors();
+                int selectorsSize = selectors.size();
+                int selectorIndex = 0;
+                for (String selector : selectors) {
+                    builder.append(selector);
+                    if (selectorIndex < selectorsSize - 1) {
+                        builder.append(",");
+                    }
+                    selectorIndex++;
+                }
+                builder.append("\"");
+            }
+            if (pcIndex < pcNum - 1) {
+                builder.append(",");
+            }
+            pcIndex++;
+        }
+        return builder.toString();
+    }
+
+    @NotNull
+    private String getRequiredCapabilitiesString(Capabilities capabilities) {
+        StringBuilder builder = new StringBuilder();
+        int pcNum = capabilities.getRequiredCapabilities().size();
+        int pcIndex = 0;
+        for (RequiredCapability capability : capabilities.getRequiredCapabilities()) {
+            builder.append(CAPABILITY_NS).append(";filter:=\"").append("(&(!(sling.resourceType.selectors=*))");
+            VersionRange versionRange = capability.getVersionRange();
+            if (versionRange != null) {
+                builder.append("(&").append(versionRange.toFilterString("version")).append("(").append(CAPABILITY_NS).append("=").append(capability.getResourceType()).append("))");
+            } else {
+                builder.append("(").append(CAPABILITY_NS).append("=").append(capability.getResourceType()).append(")");
+            }
+            builder.append(")\"");
+            if (pcIndex < pcNum - 1) {
+                builder.append(",");
+            }
+            pcIndex++;
+        }
+        return builder.toString();
+    }
+
+    Capabilities getCapabilities() {
+        return capabilities;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ProvidedCapability.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ProvidedCapability.java
new file mode 100644
index 0000000..deb7e9c
--- /dev/null
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ProvidedCapability.java
@@ -0,0 +1,156 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scriptingbundle.maven.plugin;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+class ProvidedCapability {
+    private final String resourceType;
+    private final String extendsResourceType;
+    private final String version;
+    private final String requestExtension;
+    private final String requestMethod;
+    private final List<String> selectors;
+
+    private ProvidedCapability(@NotNull String resourceType, @Nullable String extendsResourceType,
+                               @Nullable String version, @Nullable String requestExtension, @Nullable String requestMethod,
+                               @NotNull List<String> selectors) {
+        this.resourceType = resourceType;
+        this.extendsResourceType = extendsResourceType;
+        this.version = version;
+        this.requestExtension = requestExtension;
+        this.requestMethod = requestMethod;
+        this.selectors = selectors;
+    }
+
+    static Builder builder() {
+        return new Builder();
+    }
+
+    @NotNull
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    @Nullable
+    public String getVersion() {
+        return version;
+    }
+
+    @Nullable
+    public String getRequestExtension() {
+        return requestExtension;
+    }
+
+    @Nullable
+    public String getExtendsResourceType() {
+        return extendsResourceType;
+    }
+
+    @Nullable
+    public String getRequestMethod() {
+        return requestMethod;
+    }
+
+    @NotNull
+    public List<String> getSelectors() {
+        return Collections.unmodifiableList(selectors);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(resourceType, version, requestExtension, extendsResourceType, requestMethod, selectors);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof ProvidedCapability) {
+            ProvidedCapability other = (ProvidedCapability) obj;
+            return Objects.equals(resourceType, other.resourceType) && Objects.equals(version, other.version) &&
+                    Objects.equals(requestExtension, other.requestExtension) && Objects.equals(extendsResourceType,
+                    other.extendsResourceType) && Objects.equals(requestMethod, other.requestMethod) &&
+                    Objects.equals(selectors, other.selectors);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s{resourceType=%s, version=%s, selectors=%s, requestExtension=%s, requestMethod=%s, extendsResourceType=%s}",
+                this.getClass().getSimpleName(), resourceType, version, selectors, requestExtension, requestMethod, extendsResourceType);
+    }
+
+    static class Builder {
+        private String resourceType;
+        private String extendsResourceType;
+        private String version;
+        private String requestExtension;
+        private String requestMethod;
+        private List<String> selectors = Collections.emptyList();
+
+        Builder withResourceType(String resourceType) {
+            if (StringUtils.isEmpty(resourceType)) {
+                throw new NullPointerException("The script's resourceType cannot be null or empty.");
+            }
+            this.resourceType = resourceType;
+            return this;
+        }
+
+        Builder withExtendsResourceType(String extendsResourceType) {
+            this.extendsResourceType = extendsResourceType;
+            return this;
+        }
+
+        Builder withVersion(String version) {
+            this.version = version;
+            return this;
+        }
+
+        Builder withRequestExtension(String requestExtension) {
+            this.requestExtension = requestExtension;
+            return this;
+        }
+
+        Builder withRequestMethod(String requestMethod) {
+            this.requestMethod = requestMethod;
+            return this;
+        }
+
+        Builder withSelectors(List<String> selectors) {
+            if (selectors == null) {
+                throw new NullPointerException("The resourceType selectors list cannot be null.");
+            }
+            this.selectors = selectors;
+            return this;
+        }
+
+        ProvidedCapability build() {
+            return new ProvidedCapability(resourceType, extendsResourceType, version, requestExtension, requestMethod, selectors);
+        }
+    }
+}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/RequiredCapability.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/RequiredCapability.java
new file mode 100644
index 0000000..1a3d6b6
--- /dev/null
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/RequiredCapability.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.scriptingbundle.maven.plugin;
+
+import java.util.Objects;
+
+import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.framework.VersionRange;
+
+public class RequiredCapability {
+
+    private final String resourceType;
+    private final VersionRange versionRange;
+
+    private RequiredCapability(@NotNull String resourceType, @Nullable VersionRange versionRange) {
+        this.resourceType = resourceType;
+        this.versionRange = versionRange;
+    }
+
+    static Builder builder() {
+        return new Builder();
+    }
+
+    @NotNull
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    @Nullable
+    public VersionRange getVersionRange() {
+        return versionRange;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("%s{resourceType=%s, versionRange=%s}", this.getClass().getSimpleName(),
+                resourceType, versionRange);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(resourceType, versionRange);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof RequiredCapability) {
+            RequiredCapability other = (RequiredCapability) obj;
+            return Objects.equals(resourceType, other.resourceType) && Objects.equals(versionRange, other.versionRange);
+        }
+        return false;
+    }
+
+    static class Builder {
+        private String resourceType;
+        private VersionRange versionRange;
+
+        RequiredCapability build() {
+            return new RequiredCapability(resourceType, versionRange);
+        }
+
+        Builder withResourceType(String resourceType) {
+            if (StringUtils.isEmpty(resourceType)) {
+                throw new NullPointerException("The required resourceType cannot be null or empty.");
+            }
+            this.resourceType = resourceType;
+            return this;
+        }
+
+        Builder withVersionRange(VersionRange versionRange) {
+            this.versionRange = versionRange;
+            return this;
+        }
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderAnalyser.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderAnalyser.java
new file mode 100644
index 0000000..d5be570
--- /dev/null
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderAnalyser.java
@@ -0,0 +1,247 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scriptingbundle.maven.plugin;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.maven.plugin.logging.Log;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.framework.Version;
+import org.osgi.framework.VersionRange;
+
+
+class ResourceTypeFolderAnalyser {
+
+    private final Log log;
+    private final Path scriptsDirectory;
+    private final ResourceTypeFolderPredicate resourceTypeFolderPredicate;
+
+    ResourceTypeFolderAnalyser(@NotNull Log log, @NotNull Path scriptsDirectory) {
+        this.log = log;
+        this.scriptsDirectory = scriptsDirectory;
+        this.resourceTypeFolderPredicate = new ResourceTypeFolderPredicate(log);
+    }
+
+    Capabilities getCapabilities(@NotNull Path resourceTypeDirectory) {
+        Set<ProvidedCapability> providedCapabilities = new LinkedHashSet<>();
+        Set<RequiredCapability> requiredCapabilities = new LinkedHashSet<>();
+        String version = null;
+        String resourceType = null;
+        try {
+            Path fileName = resourceTypeDirectory.getFileName();
+            if (fileName != null) {
+                version = Version.parseVersion(fileName.toString()).toString();
+            }
+        } catch (IllegalArgumentException ignored) {
+            log.debug("No resourceType version detected in " + scriptsDirectory.resolve(resourceTypeDirectory).toString());
+        }
+        if (StringUtils.isNotEmpty(version)) {
+            Path parent = resourceTypeDirectory.getParent();
+            if (parent != null) {
+                resourceType = getResourceType(parent);
+            }
+        } else {
+            resourceType = getResourceType(resourceTypeDirectory);
+        }
+        if (resourceType != null) {
+            String resourceTypeLabel = getResourceTypeLabel(resourceType);
+            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(scriptsDirectory.resolve(resourceTypeDirectory))) {
+                for (Path directoryEntry : directoryStream) {
+                    if (Files.isRegularFile(directoryEntry)) {
+                        Path file = directoryEntry.getFileName();
+                        if (file != null) {
+                            if (MetadataMojo.EXTENDS_FILE.equals(file.toString())) {
+                                processExtendsFile(resourceType, version, directoryEntry, providedCapabilities, requiredCapabilities);
+                            } else if (MetadataMojo.REQUIRES_FILE.equals(file.toString())) {
+                                processRequiresFile(directoryEntry, requiredCapabilities);
+                            } else {
+                                processScriptFile(resourceTypeDirectory, directoryEntry, resourceType, version, resourceTypeLabel,
+                                        providedCapabilities);
+                            }
+                        }
+                    } else if (Files.isDirectory(directoryEntry) && !resourceTypeFolderPredicate.test(directoryEntry)) {
+                        try (Stream<Path> subtree = Files.walk(directoryEntry)) {
+                            Iterator<Path> subtreeIterator = subtree.iterator();
+                            while (subtreeIterator.hasNext()) {
+                                Path subtreeEntry = subtreeIterator.next();
+                                if (Files.isRegularFile(subtreeEntry)) {
+                                    processScriptFile(resourceTypeDirectory, subtreeEntry, resourceType, version, resourceTypeLabel,
+                                            providedCapabilities);
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (IOException | IllegalArgumentException e) {
+                log.warn(String.format("Cannot analyse folder %s.", scriptsDirectory.resolve(resourceTypeDirectory).toString()), e);
+            }
+        }
+
+        return new Capabilities(providedCapabilities, requiredCapabilities);
+    }
+
+    private void processExtendsFile(@NotNull String resourceType, @Nullable String version, @NotNull Path extendsFile,
+                                    @NotNull Set<ProvidedCapability> providedCapabilities,
+                                    @NotNull Set<RequiredCapability> requiredCapabilities)
+            throws IOException {
+        List<String> extendResources = Files.readAllLines(extendsFile, StandardCharsets.UTF_8);
+        if (extendResources.size() == 1) {
+            String extend = extendResources.get(0);
+            if (StringUtils.isNotEmpty(extend)) {
+                String[] extendParts = extend.split(";");
+                String extendedResourceType = extendParts[0];
+                String extendedResourceTypeVersion = extendParts.length > 1 ? extendParts[1] : null;
+                providedCapabilities.add(
+                        ProvidedCapability.builder()
+                                .withResourceType(resourceType)
+                                .withVersion(version)
+                                .withExtendsResourceType(extendedResourceType)
+                                .build());
+                RequiredCapability.Builder requiredBuilder =
+                        RequiredCapability.builder().withResourceType(extendedResourceType);
+                try {
+                    if (extendedResourceTypeVersion != null) {
+                        requiredBuilder.withVersionRange(VersionRange.valueOf(extendedResourceTypeVersion.substring(extendedResourceTypeVersion.indexOf('=') + 1)));
+                    }
+                } catch (IllegalArgumentException ignored) {
+                    log.warn(String.format("Invalid version range '%s' defined for extended resourceType '%s'" +
+                                    " in file %s.", extendedResourceTypeVersion, extendedResourceType,
+                            extendsFile.toString()));
+                }
+                requiredCapabilities.add(requiredBuilder.build());
+            }
+        }
+    }
+
+    private void processRequiresFile(@NotNull Path requiresFile,
+                                     @NotNull Set<RequiredCapability> requiredCapabilities)
+            throws IOException {
+        List<String> requiredResourceTypes = Files.readAllLines(requiresFile, StandardCharsets.UTF_8);
+        for (String requiredResourceType : requiredResourceTypes) {
+            if (StringUtils.isNotEmpty(requiredResourceType)) {
+                String[] requireParts = requiredResourceType.split(";");
+                String resourceType = requireParts[0];
+                String version = requireParts.length > 1 ? requireParts[1] : null;
+                RequiredCapability.Builder requiredBuilder =
+                        RequiredCapability.builder().withResourceType(resourceType);
+                try {
+                    if (version != null) {
+                        requiredBuilder.withVersionRange(VersionRange.valueOf(version.substring(version.indexOf('=') + 1)));
+                    }
+                } catch (IllegalArgumentException ignored) {
+                    log.warn(String.format("Invalid version range '%s' defined for required resourceType '%s'" +
+                                    " in file %s.", version, resourceType,
+                            requiresFile.toString()));
+                }
+                requiredCapabilities.add(requiredBuilder.build());
+            }
+        }
+    }
+
+    private void processScriptFile(@NotNull Path resourceTypeDirectory, @NotNull Path script, @NotNull String resourceType,
+                                   @Nullable String version, @NotNull String resourceTypeLabel,
+                                   @NotNull Set<ProvidedCapability> providedCapabilities) {
+        Path scriptFile = script.getFileName();
+        if (scriptFile != null) {
+            Path relativeResourceTypeFolder = resourceTypeDirectory.relativize(script);
+            int pathSegments = relativeResourceTypeFolder.getNameCount();
+            ArrayList<String> selectors = new ArrayList<>();
+            if (pathSegments > 1) {
+                for (int i = 0; i < pathSegments - 1; i++) {
+                    selectors.add(relativeResourceTypeFolder.getName(i).toString());
+                }
+            }
+            String[] parts = scriptFile.toString().split("\\.");
+            if (parts.length >= 2 && parts.length <= 4) {
+                String method = null;
+                String extension = null;
+                String first = parts[0];
+                if (parts.length >= 3) {
+                    extension = parts[parts.length - 2];
+                }
+                if (!first.equals(resourceTypeLabel)) {
+                    if (MetadataMojo.METHODS.contains(first)) {
+                        method = first;
+                        if (parts.length == 4) {
+                            selectors.add(parts[1]);
+                        }
+                    } else {
+                        selectors.add(first);
+                    }
+                }
+                providedCapabilities.add(ProvidedCapability.builder().withResourceType(resourceType).withVersion(version)
+                        .withSelectors(selectors).withRequestExtension(extension).withRequestMethod(method).build());
+            } else {
+                log.warn(String.format("Skipping script %s since it either does not target a script engine or it provides too many " +
+                        "selector parts in its name.", script.toString()));
+            }
+        } else {
+            log.warn(String.format("Skipping path %s since it has 0 elements.", script.toString()));
+        }
+
+    }
+
+    private String getResourceType(@NotNull Path resourceTypeFolder) {
+        StringBuilder stringBuilder = new StringBuilder();
+        Path relativeResourceTypePath = scriptsDirectory.relativize(resourceTypeFolder);
+        if (StringUtils.isNotEmpty(relativeResourceTypePath.toString())) {
+            int parts = relativeResourceTypePath.getNameCount();
+            for (int i = 0; i < parts; i++) {
+                stringBuilder.append(relativeResourceTypePath.getName(i));
+                if (i < parts - 1) {
+                    stringBuilder.append('/');
+                }
+            }
+        }
+        return stringBuilder.toString();
+    }
+
+    private @NotNull String getResourceTypeLabel(@NotNull String resourceType) {
+        String resourceTypeLabel = null;
+        if (resourceType.contains("/")) {
+            int lastIndex = resourceType.lastIndexOf('/');
+            if (lastIndex < resourceType.length() - 2) {
+                resourceTypeLabel = resourceType.substring(++lastIndex);
+            }
+        } else if (resourceType.contains(".")) {
+            int lastIndex = resourceType.lastIndexOf('.');
+            if (lastIndex < resourceType.length() - 2) {
+                resourceTypeLabel = resourceType.substring(++lastIndex);
+            }
+        } else {
+            resourceTypeLabel = resourceType;
+        }
+        if (StringUtils.isEmpty(resourceTypeLabel)) {
+            throw new IllegalArgumentException(String.format("Resource type '%s' does not provide a resourceTypeLabel.", resourceType));
+        }
+        return resourceTypeLabel;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderPredicate.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderPredicate.java
new file mode 100644
index 0000000..a656bd6
--- /dev/null
+++ b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ResourceTypeFolderPredicate.java
@@ -0,0 +1,83 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scriptingbundle.maven.plugin;
+
+import java.io.IOException;
+import java.nio.file.DirectoryStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.function.Predicate;
+
+import org.apache.maven.plugin.logging.Log;
+import org.osgi.framework.Version;
+
+public class ResourceTypeFolderPredicate implements Predicate<Path> {
+
+    private Log log;
+
+    public ResourceTypeFolderPredicate(Log log) {
+        this.log = log;
+    }
+
+    @Override
+    public boolean test(Path folder) {
+        if (folder == null) {
+            return false;
+        }
+        Path lastSegment = folder.getFileName();
+        if (lastSegment == null) {
+            return false;
+        }
+        try {
+            Version.parseVersion(lastSegment.toString());
+            Path parent = folder.getParent();
+            if (parent != null) {
+                lastSegment = parent.getFileName();
+            }
+        } catch (IllegalArgumentException ignored) {
+            // last segment does not denote a version
+        }
+        String resourceTypeLabel;
+        if (lastSegment != null) {
+            String lastSegmentString = lastSegment.toString();
+            int lastDotIndex = lastSegmentString.lastIndexOf('.');
+            if (lastDotIndex != -1 && lastDotIndex < lastSegmentString.length() - 1) {
+                resourceTypeLabel = lastSegmentString.substring(++lastDotIndex);
+            } else {
+                resourceTypeLabel = lastSegmentString;
+            }
+            try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(folder, Files::isRegularFile)) {
+                for (Path path : directoryStream) {
+                    Path fileName = path.getFileName();
+                    if (fileName != null) {
+                        String childName = fileName.toString();
+                        String scriptName = childName.indexOf('.') != -1 ? childName.substring(0, childName.indexOf('.')) : null;
+                        if (resourceTypeLabel.equals(scriptName) || MetadataMojo.EXTENDS_FILE.equals(childName) ||
+                                MetadataMojo.METHODS.contains(scriptName)) {
+                            return true;
+                        }
+                    }
+                }
+            } catch (IOException e) {
+                log.error(String.format("Could not check if folder %s denotes a resource type.", folder.toString()), e);
+            }
+        }
+        return false;
+    }
+}
diff --git a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptingMavenPlugin.java b/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptingMavenPlugin.java
deleted file mode 100644
index 1c13d5e..0000000
--- a/src/main/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptingMavenPlugin.java
+++ /dev/null
@@ -1,213 +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.scriptingbundle.maven.plugin;
-
-import org.apache.commons.lang3.StringEscapeUtils;
-import org.apache.maven.execution.MavenSession;
-import org.apache.maven.plugin.AbstractMojo;
-import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.plugins.annotations.LifecyclePhase;
-import org.apache.maven.plugins.annotations.Mojo;
-import org.apache.maven.plugins.annotations.Parameter;
-import org.apache.maven.project.MavenProject;
-import org.apache.maven.shared.utils.io.DirectoryScanner;
-import org.osgi.framework.Constants;
-import org.osgi.framework.Version;
-import org.osgi.framework.VersionRange;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileReader;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-@Mojo(name = "metadata",
-      defaultPhase = LifecyclePhase.PACKAGE)
-public class ScriptingMavenPlugin extends AbstractMojo {
-
-    @Parameter(defaultValue = "${project}",
-               readonly = true)
-    private MavenProject project;
-
-    @Parameter(defaultValue = "${session}",
-               readonly = true)
-    private MavenSession session;
-
-    @Parameter(defaultValue = "${project.basedir}/src/main/resources/javax.script")
-    private String scriptsDirectory;
-
-    private static final Set<String> METHODS = new HashSet<>(Arrays.asList(new String[]{"TRACE", "OPTIONS", "GET", "HEAD", "POST", "PUT",
-            "DELETE", "PATCH"}));
-
-    private static final Set<String> FILE_SEPARATORS = new HashSet<>(Arrays.asList("\\", "/"));
-
-    public void execute() throws MojoExecutionException {
-        File sdFile = new File(scriptsDirectory);
-        if (!sdFile.exists()) {
-            sdFile = new File(project.getBasedir(), scriptsDirectory);
-            if (!sdFile.exists()) {
-                throw new MojoExecutionException("Cannot find file " + scriptsDirectory + ".");
-            }
-        }
-        final AtomicReference<File> scriptsDirectoryReference = new AtomicReference<>();
-        scriptsDirectoryReference.set(sdFile);
-        ;
-        DirectoryScanner scanner = new DirectoryScanner();
-        scanner.setBasedir(sdFile);
-        scanner.setIncludes("**");
-        scanner.setExcludes("**/*.class");
-        scanner.addDefaultExcludes();
-        scanner.scan();
-
-        List<String> scriptPaths = Stream.of(scanner.getIncludedFiles()).map(path -> new File(scriptsDirectoryReference.get(), path))
-                .map(file -> file.getPath().substring((scriptsDirectoryReference.get().getPath() + File.pathSeparatorChar).length()))
-                .collect(Collectors.toList());
-
-
-        List<String> requires = new ArrayList<>();
-
-        List<String> capabilities = new ArrayList<>();
-        for (String scriptPath : scriptPaths) {
-            Script script = getScripts(scriptPath);
-
-            String capability = "sling.resourceType;sling.resourceType=\"" + script.rt.replace("\"", "\\\"") + "\"";
-
-            if (!(script.rt.equals(script.name) || script.rt.endsWith("." + script.name) || script.name.isEmpty())) {
-                if (!script.name.equalsIgnoreCase("requires")) {
-                    if (!script.name.equalsIgnoreCase("extends")) {
-                        capability += ";sling.resourceType.selectors:List<String>=\"" + script.name.replace("\"", "\\\"") + "\"";
-                    } else {
-                        try (BufferedReader input = new BufferedReader(
-                                new FileReader(new File(scriptsDirectoryReference.get(), scriptPath)))) {
-                            String extend = input.readLine();
-
-                            capability += ";extends=\"" + extend.split(";")[0].replace("\"", "\\\"") + "\"";
-                            requires.add(extend + ";extends=true");
-                        } catch (Exception ex) {
-                            getLog().error(ex);
-                        }
-                    }
-                } else {
-                    try (BufferedReader input = new BufferedReader(new FileReader(new File(scriptsDirectoryReference.get(), scriptPath)))) {
-                        for (String line = input.readLine(); line != null; line = input.readLine()) {
-                            requires.add(line);
-                        }
-                    } catch (Exception ex) {
-                        getLog().error(ex);
-                    }
-                }
-            }
-            if (script.extension != null) {
-                capability += ";sling.resourceType.extensions:List<String>=\"" + script.extension.replace("\"", "\\\"") + "\"";
-            }
-
-            if (script.method != null) {
-                capability += ";sling.servlet.methods:List<String>=\"" + script.method.replace("\"", "\\\"") + "\"";
-            }
-            if (script.version != null) {
-                capability += ";version:Version=\"" + script.version + "\"";
-            }
-            capabilities.add(capability);
-        }
-        List<String> requirements = new ArrayList<>();
-        for (String require : requires) {
-            String[] parts = require.split(";");
-            String rt = parts[0];
-            String filter = "(sling.resourceType=" + rt.replace("\"", "\\\"") + ")";
-
-            if (parts.length > 1) {
-                VersionRange range = new VersionRange(parts[1].substring(parts[1].indexOf("=") + 1).replace("\"", "").trim());
-                filter = "(&" + filter + range.toFilterString("version") + ")";
-            }
-            if (parts.length > 2) {
-                filter = "(&" + filter + "(!(sling.resourceType.selectors=*)))";
-            }
-            requirements.add("sling.resourceType;filter:=\"" + filter + "\"");
-        }
-
-        project.getProperties().setProperty(ScriptingMavenPlugin.class.getPackage().getName() + "." + Constants.PROVIDE_CAPABILITY,
-                String.join(",", capabilities));
-        project.getProperties().setProperty(ScriptingMavenPlugin.class.getPackage().getName() + "." + Constants.REQUIRE_CAPABILITY,
-                String.join(",", requirements));
-    }
-
-    static class Script {
-        String rt;
-        String version;
-        String name;
-        String extension;
-        String scriptExtension;
-        String method;
-    }
-
-    static Script getScripts(String script) {
-        String fileSeparator = null;
-        for (String sep : FILE_SEPARATORS) {
-            if (script.contains(sep)) {
-                fileSeparator = sep;
-                break;
-            }
-        }
-        Script result = new Script();
-        String[] parts = script.split(Pattern.quote(fileSeparator));
-
-        result.rt = parts[0];
-        result.version = parts.length > 2 ? new Version(parts[1]).toString() : null;
-        result.name = parts.length > 2 ? parts[2] : parts[1];
-        int idx = result.name.lastIndexOf('.');
-        if (idx != -1) {
-            result.scriptExtension = result.name.substring(idx + 1);
-            result.name = result.name.substring(0, idx);
-            if (result.scriptExtension.isEmpty()) {
-                result.scriptExtension = null;
-            }
-        }
-
-        idx = result.name.lastIndexOf('.');
-        if (idx != -1) {
-            result.extension = result.name.substring(idx + 1);
-            result.name = result.name.substring(0, idx);
-            if (result.extension.isEmpty() || result.extension.equalsIgnoreCase("html")) {
-                result.extension = null;
-            }
-        } else {
-            result.extension = null;
-        }
-
-        idx = result.name.indexOf('.');
-        if (idx != -1) {
-            String methodString = result.name.substring(0, idx).toUpperCase();
-            if (METHODS.contains(methodString)) {
-                result.method = methodString;
-                result.name = result.name.substring(idx + 1);
-            }
-        } else if (METHODS.contains(result.name.toUpperCase())) {
-            result.method = result.name.toUpperCase();
-            result.name = "";
-        }
-        return result;
-    }
-}
diff --git a/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojoTest.java b/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojoTest.java
new file mode 100644
index 0000000..33c4931
--- /dev/null
+++ b/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/MetadataMojoTest.java
@@ -0,0 +1,137 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.scriptingbundle.maven.plugin;
+
+import java.io.File;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.testing.MojoRule;
+import org.apache.maven.project.MavenProject;
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.osgi.framework.VersionRange;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class MetadataMojoTest {
+
+    @Rule
+    public MojoRule mojoRule = new MojoRule();
+
+    @After
+    public void after() {
+        System.clearProperty("basedir");
+    }
+
+    @Test
+    public void testProject1() throws Exception {
+        MojoProject mojoProject = getMojoProject(getProjectLocation("project-1"));
+        mojoProject.mojo.execute();
+        Capabilities capabilities = mojoProject.mojo.getCapabilities();
+
+        Set<ProvidedCapability> pExpected = new HashSet<>(Arrays.asList(
+                // org/apache/sling/bar/1.0.0
+                ProvidedCapability.builder().withResourceType("org/apache/sling/bar").withVersion("1.0.0").build(),
+                ProvidedCapability.builder().withResourceType("org/apache/sling/bar").withVersion("1.0.0").withSelectors(Arrays.asList("depth1"
+                        , "100")).build(),
+                ProvidedCapability.builder().withResourceType("org/apache/sling/bar").withVersion("1.0.0").withSelectors(Arrays.asList("depth1"
+                        , "200")).build(),
+                ProvidedCapability.builder().withResourceType("org/apache/sling/bar").withVersion("1.0.0").withSelectors(Arrays.asList("depth1"
+                        , "depth2", "100")).build(),
+
+                // org/apache/sling/foo
+                ProvidedCapability.builder().withResourceType("org/apache/sling/foo").build(),
+                ProvidedCapability.builder().withResourceType("org/apache/sling/foo").withSelectors(Arrays.asList("depth1"
+                        , "100")).build(),
+                ProvidedCapability.builder().withResourceType("org/apache/sling/foo").withSelectors(Arrays.asList("depth1"
+                        , "200")).build(),
+                ProvidedCapability.builder().withResourceType("org/apache/sling/foo").withSelectors(Arrays.asList("depth1"
+                        , "depth2", "100")).build(),
+
+                // org.apache.sling.foobar/1.0.0
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withVersion("1.0.0").build(),
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withVersion("1.0.0").withExtendsResourceType(
+                        "org/apache/sling/bar").build(),
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withVersion("1.0.0").withSelectors(Arrays.asList("depth1"
+                        , "100")).build(),
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withVersion("1.0.0").withSelectors(Arrays.asList("depth1"
+                        , "200")).build(),
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withVersion("1.0.0").withSelectors(Arrays.asList("depth1"
+                        , "depth2", "100")).build(),
+
+                // org.apache.sling.foobar
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withExtendsResourceType("org/apache/sling/bar").build(),
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withSelectors(Arrays.asList("depth1"
+                        , "100")).build(),
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withSelectors(Arrays.asList("depth1"
+                        , "200")).build(),
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withSelectors(Arrays.asList("depth1"
+                        , "depth2", "100")).build(),
+                ProvidedCapability.builder().withResourceType("org.apache.sling.foobar").withRequestMethod("GET").build(),
+
+                // sling
+                ProvidedCapability.builder().withResourceType("sling").build()
+        ));
+        Set<ProvidedCapability> provided = new HashSet<>(capabilities.getProvidedCapabilities());
+        assertEquals(pExpected.size(), provided.size());
+        for (ProvidedCapability capability : pExpected) {
+            boolean removed = provided.remove(capability);
+            assertTrue(String.format("Did not find expected provided capability %s.", capability), removed);
+        }
+
+        Set<RequiredCapability> rExpected = new HashSet<>(Arrays.asList(
+                RequiredCapability.builder().withResourceType("sling/default").withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).build(),
+                RequiredCapability.builder().withResourceType("org/apache/sling/bar").build(),
+                RequiredCapability.builder().withResourceType("org/apache/sling/bar").withVersionRange(VersionRange.valueOf("[1.0.0,2.0.0)")).build()
+        ));
+        Set<RequiredCapability> required = new HashSet<>(capabilities.getRequiredCapabilities());
+        assertEquals(rExpected.size(), required.size());
+        for (RequiredCapability capability : rExpected) {
+            boolean removed = required.remove(capability);
+            assertTrue(String.format("Did not find expected required capability %s.", capability), removed);
+        }
+    }
+
+    private MojoProject getMojoProject(File projectDirectory) throws Exception {
+        MavenProject project = mojoRule.readMavenProject(projectDirectory);
+        MavenSession session = mojoRule.newMavenSession(project);
+        MojoExecution execution = mojoRule.newMojoExecution("metadata");
+        MetadataMojo validateMojo = (MetadataMojo) mojoRule.lookupConfiguredMojo(session, execution);
+        MojoProject mojoProject = new MojoProject();
+        mojoProject.mojo = validateMojo;
+        mojoProject.project = project;
+        return mojoProject;
+    }
+
+    private static class MojoProject {
+        MetadataMojo mojo;
+        MavenProject project;
+    }
+
+    private File getProjectLocation(String projectName) {
+        return Paths.get("src", "test", "resources", projectName).toFile();
+    }
+}
diff --git a/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptingMavenPluginTest.java b/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptingMavenPluginTest.java
deleted file mode 100644
index abc4f7b..0000000
--- a/src/test/java/org/apache/sling/scriptingbundle/maven/plugin/ScriptingMavenPluginTest.java
+++ /dev/null
@@ -1,80 +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.scriptingbundle.maven.plugin;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-public class ScriptingMavenPluginTest {
-    @Test
-    public void testScriptNameFullCalculation() {
-        String scriptPath = "org.apache.foo/1.0.0/POST.hi.xml.jsp";
-
-        ScriptingMavenPlugin.Script script = ScriptingMavenPlugin.getScripts(scriptPath);
-
-        Assert.assertEquals("org.apache.foo", script.rt);
-        Assert.assertEquals("1.0.0", script.version);
-        Assert.assertEquals("hi", script.name);
-        Assert.assertEquals("POST", script.method);
-        Assert.assertEquals("xml", script.extension);
-        Assert.assertEquals("jsp", script.scriptExtension);
-    }
-
-    @Test
-    public void testScriptNameMinCalculation() {
-        String scriptPath = "org.apache.foo/foo";
-
-        ScriptingMavenPlugin.Script script = ScriptingMavenPlugin.getScripts(scriptPath);
-
-        Assert.assertEquals("org.apache.foo", script.rt);
-        Assert.assertNull("1.0.0", script.version);
-        Assert.assertEquals("foo", script.name);
-        Assert.assertNull(script.method);
-        Assert.assertNull(script.extension);
-        Assert.assertNull(script.scriptExtension);
-    }
-
-    @Test
-    public void testScriptNameVersionAndMethodCalculation() {
-        String scriptPath = "org.apache.foo/1.2.0/Post.jsp";
-
-        ScriptingMavenPlugin.Script script = ScriptingMavenPlugin.getScripts(scriptPath);
-
-        Assert.assertEquals("org.apache.foo", script.rt);
-        Assert.assertEquals("1.2.0", script.version);
-        Assert.assertEquals("", script.name);
-        Assert.assertEquals("POST", script.method);
-        Assert.assertNull(script.extension);
-        Assert.assertEquals("jsp", script.scriptExtension);
-    }
-
-    @Test
-    public void testScriptNameVersionAndMethodMinCalculation() {
-        String scriptPath = "org.apache.foo/1.2.0/Post.";
-
-        ScriptingMavenPlugin.Script script = ScriptingMavenPlugin.getScripts(scriptPath);
-
-        Assert.assertEquals("org.apache.foo", script.rt);
-        Assert.assertEquals("1.2.0", script.version);
-        Assert.assertEquals("", script.name);
-        Assert.assertEquals("POST", script.method);
-        Assert.assertNull(script.extension);
-        Assert.assertNull(script.scriptExtension);
-    }
-}
diff --git a/src/test/resources/project-1/pom.xml b/src/test/resources/project-1/pom.xml
new file mode 100644
index 0000000..0718199
--- /dev/null
+++ b/src/test/resources/project-1/pom.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.apache.sling</groupId>
+    <artifactId>scriptingbundle-maven-plugin-project-1</artifactId>
+    <version>0.0.1</version>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.sling</groupId>
+                <artifactId>scriptingbundle-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>prepare-package</phase>
+                        <goals>
+                            <goal>metadata</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/100.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/100.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/100.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/200.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/200.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/200.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/depth2/100.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/depth2/100.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/depth1/depth2/100.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/extends b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/extends
new file mode 100644
index 0000000..2d24f10
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/extends
@@ -0,0 +1 @@
+org/apache/sling/bar
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/foobar.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/foobar.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/foobar.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/requires b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/requires
new file mode 100644
index 0000000..ddfb7af
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/1.0.0/requires
@@ -0,0 +1 @@
+sling/default;version=[1.0.0,2)
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/GET.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/GET.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/GET.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/100.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/100.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/100.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/200.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/200.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/200.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/depth2/100.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/depth2/100.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/depth1/depth2/100.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/extends b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/extends
new file mode 100644
index 0000000..74c19c3
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.foobar/extends
@@ -0,0 +1 @@
+org/apache/sling/bar;version=[1.0.0,2.0.0)
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.wrongbar/wrongbar b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.wrongbar/wrongbar
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.wrongbar/wrongbar.has.too.many.selectors.html b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.wrongbar/wrongbar.has.too.many.selectors.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org.apache.sling.wrongbar/wrongbar.has.too.many.selectors.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/bar.html b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/bar.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/bar.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/100.html b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/100.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/100.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/200.html b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/200.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/200.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/depth2/100.html b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/depth2/100.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/bar/1.0.0/depth1/depth2/100.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/100.html b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/100.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/100.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/200.html b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/200.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/200.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/depth2/100.html b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/depth2/100.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/depth1/depth2/100.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/foo.html b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/foo.html
new file mode 100644
index 0000000..93f1f4d
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/org/apache/sling/foo/foo.html
@@ -0,0 +1,19 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<span>It works!</span>
diff --git a/src/test/resources/project-1/src/main/resources/javax.script/sling/sling.html b/src/test/resources/project-1/src/main/resources/javax.script/sling/sling.html
new file mode 100644
index 0000000..2853663
--- /dev/null
+++ b/src/test/resources/project-1/src/main/resources/javax.script/sling/sling.html
@@ -0,0 +1,18 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
diff --git a/src/test/resources/spotbugs-exclude.xml b/src/test/resources/spotbugs-exclude.xml
new file mode 100644
index 0000000..fd5f18d
--- /dev/null
+++ b/src/test/resources/spotbugs-exclude.xml
@@ -0,0 +1,24 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<FindBugsFilter>
+    <!-- false positive in Java 11, see https://github.com/spotbugs/spotbugs/issues/756 -->
+    <Match>
+        <Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"/>
+    </Match>
+</FindBugsFilter>