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>