You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by kw...@apache.org on 2021/08/06 14:38:41 UTC

[sling-scriptingbundle-maven-plugin] 01/01: SLING-10693 fail build in case of invalid extends/requires file

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

kwin pushed a commit to branch feature/SLING-10693-improved-error-handling
in repository https://gitbox.apache.org/repos/asf/sling-scriptingbundle-maven-plugin.git

commit 41c47001483848f9ded6ab9970091a4ade180598
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Fri Aug 6 16:38:32 2021 +0200

    SLING-10693 fail build in case of invalid extends/requires file
---
 pom.xml                                            |   5 +-
 .../plugin/processor/FileProcessor.java            | 126 ++++++++++---------
 .../processor/ResourceTypeFolderAnalyser.java      |   5 +-
 .../plugin/processor/FileProcessorTest.java        | 134 +++++++++++++++++++++
 .../plugin/processor/Slf4jLogger.java              |  52 ++++++++
 src/test/resources/extends/invalid-attributes      |   1 +
 src/test/resources/extends/multiple-clauses        |   1 +
 src/test/resources/extends/multiple-lines          |   2 +
 .../extends => extends/valid}                      |   2 +-
 .../main/scripts/org.apache.sling.foobar/extends   |   2 +-
 src/test/resources/requires/invalid-attributes     |   1 +
 src/test/resources/requires/multiple-clauses       |   1 +
 .../extends => requires/valid}                     |   2 +-
 13 files changed, 271 insertions(+), 63 deletions(-)

diff --git a/pom.xml b/pom.xml
index c550988..6a71ee9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,7 +22,7 @@
     <parent>
         <groupId>org.apache.sling</groupId>
         <artifactId>sling</artifactId>
-        <version>41</version>
+        <version>44</version>
         <relativePath />
     </parent>
     <modelVersion>4.0.0</modelVersion>
@@ -49,6 +49,7 @@
         <maven.version>3.6.0</maven.version>
         <site.jira.version.id>12344664,12347157,12348422,12350010</site.jira.version.id>
         <maven.site.path>${project.artifactId}-archives/${project.artifactId}-LATEST</maven.site.path>
+        <project.build.outputTimestamp>2020-01-22T15:10:38Z</project.build.outputTimestamp>
     </properties>
 
     <build>
@@ -99,6 +100,8 @@
                         <exclude>src/test/resources/**/extends</exclude>
                         <exclude>src/test/resources/**/requires</exclude>
                         <exclude>src/test/resources/**/wrongbar</exclude>
+                        <exclude>src/test/resources/extends/*</exclude>
+                        <exclude>src/test/resources/requires/*</exclude>
                     </excludes>
                 </configuration>
             </plugin>
diff --git a/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/FileProcessor.java b/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/FileProcessor.java
index 73ee002..5c99ffc 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/FileProcessor.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/FileProcessor.java
@@ -19,6 +19,7 @@
 package org.apache.sling.scriptingbundle.plugin.processor;
 
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -51,7 +52,9 @@ public class FileProcessor {
     private final Map<String, String> scriptEngineMappings;
 
     private static final Collection<String> EXTENDS_ALLOWED_ATTRIBUTE_NAMES = Arrays.asList(aQute.bnd.osgi.Constants.RESOLUTION_DIRECTIVE, aQute.bnd.osgi.Constants.VERSION_ATTRIBUTE);
+    private static final Collection<String> REQUIRES_ALLOWED_ATTRIBUTE_NAMES = Arrays.asList(aQute.bnd.osgi.Constants.RESOLUTION_DIRECTIVE, aQute.bnd.osgi.Constants.VERSION_ATTRIBUTE);
 
+    
     public FileProcessor(Logger log, Set<String> searchPaths, Map<String, String> scriptEngineMappings) {
         this.log = log;
         this.searchPaths = searchPaths;
@@ -60,19 +63,19 @@ public class FileProcessor {
 
     public void processExtendsFile(@NotNull ResourceType resourceType, @NotNull Path extendsFile,
                             @NotNull Set<ProvidedResourceTypeCapability> providedCapabilities,
-                            @NotNull Set<RequiredResourceTypeCapability> requiredCapabilities) {
+                            @NotNull Set<RequiredResourceTypeCapability> requiredCapabilities) throws IllegalArgumentException {
         try {
             List<String> extendResources = Files.readAllLines(extendsFile, StandardCharsets.UTF_8);
             if (extendResources.size() == 1) {
                 String extend = extendResources.get(0);
                 Parameters parameters = OSGiHeader.parseHeader(extend);
                 if (parameters.size() < 1 || parameters.size() > 1) {
-                    log.error(String.format("The file '%s' must contain one clause only (not multiple ones separated by ','", extendsFile));
+                    throw new IllegalArgumentException(String.format("The file '%s' must contain one clause only (not multiple ones separated by ',')", extendsFile));
                 }
                 Entry<String, Attrs> extendsParameter = parameters.entrySet().iterator().next();
                 for (String attributeName : extendsParameter.getValue().keySet()) {
                     if (!EXTENDS_ALLOWED_ATTRIBUTE_NAMES.contains(attributeName)) {
-                        log.error(String.format("Found attribute/directive %s in file %s. Only the following attributes or directives may be used in the extends file: %s", attributeName, extendsFile, String.join(",", EXTENDS_ALLOWED_ATTRIBUTE_NAMES)));
+                        throw new IllegalArgumentException(String.format("Found unsupported attribute/directive '%s' in file '%s'. Only the following attributes or directives may be used in the extends file: %s", attributeName, extendsFile, String.join(",", EXTENDS_ALLOWED_ATTRIBUTE_NAMES)));
                     }
                 }
                 String extendedResourceType = FilenameUtils.normalize(extendsParameter.getKey(), true);
@@ -105,9 +108,11 @@ public class FileProcessor {
                 }
                 extractVersionRange(extendsFile, requiredBuilder, extendsParameter.getValue().getVersion());
                 requiredCapabilities.add(requiredBuilder.build());
+            } else {
+                throw new IllegalArgumentException(String.format("The file '%s' must contain one line only (not multiple ones)", extendsFile));
             }
         } catch (IOException e) {
-            log.error(String.format("Unable to read file %s.", extendsFile.toString()), e);
+            throw new UncheckedIOException(String.format("Unable to read file %s.", extendsFile.toString()), e);
         }
     }
 
@@ -118,81 +123,86 @@ public class FileProcessor {
             for (String requiredResourceType : requiredResourceTypes) {
                 Parameters parameters = OSGiHeader.parseHeader(requiredResourceType);
                 if (parameters.size() < 1 || parameters.size() > 1) {
-                    log.error(String.format("Each line in file '%s' must contain one clause only (not multiple ones separated by ',', skipping line", requiresFile));
-                    continue;
+                    throw new IllegalArgumentException(String.format("Each line in file '%s' must contain one clause only (not multiple ones separated by ',')", requiresFile));
                 }
                 Entry<String, Attrs> requiresParameter = parameters.entrySet().iterator().next();
+                for (String attributeName : requiresParameter.getValue().keySet()) {
+                    if (!REQUIRES_ALLOWED_ATTRIBUTE_NAMES.contains(attributeName)) {
+                        throw new IllegalArgumentException(String.format("Found unsupported attribute/directive '%s' in file '%s'. Only the following attributes or directives may be used in the requires file: %s", attributeName, requiresFile, String.join(",", REQUIRES_ALLOWED_ATTRIBUTE_NAMES)));
+                    }
+                }
                 String resourceType = FilenameUtils.normalize(requiresParameter.getKey(), true);
+                boolean isOptional = aQute.bnd.osgi.Constants.OPTIONAL.equals(requiresParameter.getValue().get(aQute.bnd.osgi.Constants.RESOLUTION_DIRECTIVE));
                 RequiredResourceTypeCapability.Builder requiredBuilder =
                         RequiredResourceTypeCapability.builder().withResourceType(resourceType);
+                if (isOptional) {
+                    requiredBuilder.withIsOptional();
+                }
                 extractVersionRange(requiresFile, requiredBuilder, requiresParameter.getValue().getVersion());
                 requiredCapabilities.add(requiredBuilder.build());
             }
         } catch (IOException e) {
-            log.error(String.format("Unable to read file %s.", requiresFile.toString()), e);
+            throw new UncheckedIOException(String.format("Unable to read file %s.", requiresFile.toString()), e);
         }
     }
 
     public void processScriptFile(@NotNull Path resourceTypeDirectory, @NotNull Path scriptPath,
                                    @NotNull ResourceType resourceType, @NotNull Set<ProvidedResourceTypeCapability> providedCapabilities) {
-        String filePath = scriptPath.toString();
-        String extension = FilenameUtils.getExtension(filePath);
-        if (StringUtils.isNotEmpty(extension) && scriptEngineMappings.containsKey(extension)) {
-            Path scriptFile = scriptPath.getFileName();
-            if (scriptFile != null) {
-                Path relativeResourceTypeFolder = resourceTypeDirectory.relativize(scriptPath);
-                int pathSegments = relativeResourceTypeFolder.getNameCount();
-                LinkedHashSet<String> selectors = new LinkedHashSet<>();
-                if (pathSegments > 1) {
-                    for (int i = 0; i < pathSegments - 1; i++) {
-                        selectors.add(relativeResourceTypeFolder.getName(i).toString());
-                    }
+        Path scriptFile = scriptPath.getFileName();
+        if (scriptFile != null) {
+            Path relativeResourceTypeFolder = resourceTypeDirectory.relativize(scriptPath);
+            int pathSegments = relativeResourceTypeFolder.getNameCount();
+            LinkedHashSet<String> selectors = new LinkedHashSet<>();
+            if (pathSegments > 1) {
+                for (int i = 0; i < pathSegments - 1; i++) {
+                    selectors.add(relativeResourceTypeFolder.getName(i).toString());
                 }
-                String scriptFileName = scriptFile.toString();
-                Script script = Script.parseScript(scriptFileName);
-                if (script != null) {
-                    String scriptEngine = scriptEngineMappings.get(script.getScriptExtension());
-                    if (scriptEngine != null) {
-                        String scriptName = script.getName();
-                        Set<String> searchPathProcessesResourceTypes = processSearchPathResourceTypes(resourceType);
-                        if (scriptName != null && !resourceType.getResourceLabel().equals(scriptName)) {
-                            selectors.add(script.getName());
-                        }
-                        Optional<ProvidedResourceTypeCapability> extendsCapability = Optional.empty();
-                        if (selectors.isEmpty() && StringUtils.isEmpty(script.getRequestExtension()) &&
-                                StringUtils.isEmpty(script.getRequestMethod())) {
-                            extendsCapability =
-                                    providedCapabilities.stream()
-                                            .filter(capability -> StringUtils.isNotEmpty(capability.getExtendsResourceType()) &&
-                                                    capability.getResourceTypes().equals(searchPathProcessesResourceTypes) &&
-                                                    capability.getSelectors().isEmpty() &&
-                                                    StringUtils.isEmpty(capability.getRequestExtension()) &&
-                                                    StringUtils.isEmpty(capability.getRequestMethod())).findAny();
-                        }
-                        ProvidedResourceTypeCapability.Builder builder = ProvidedResourceTypeCapability.builder()
-                                .withResourceTypes(searchPathProcessesResourceTypes)
-                                .withVersion(resourceType.getVersion())
-                                .withSelectors(selectors)
-                                .withRequestExtension(script.getRequestExtension())
-                                .withRequestMethod(script.getRequestMethod())
-                                .withScriptEngine(scriptEngine)
-                                .withScriptExtension(script.getScriptExtension());
-                        extendsCapability.ifPresent(capability -> {
-                                    builder.withExtendsResourceType(capability.getExtendsResourceType());
-                                    providedCapabilities.remove(capability);
-                                }
-                        );
-                        providedCapabilities.add(builder.build());
-                    } else {
-                        log.warn(String.format("Cannot find a script engine mapping for script %s.", scriptPath));
+            }
+            String scriptFileName = scriptFile.toString();
+            Script script = Script.parseScript(scriptFileName);
+            if (script != null) {
+                String scriptEngine = scriptEngineMappings.get(script.getScriptExtension());
+                if (scriptEngine != null) {
+                    String scriptName = script.getName();
+                    Set<String> searchPathProcessesResourceTypes = processSearchPathResourceTypes(resourceType);
+                    if (scriptName != null && !resourceType.getResourceLabel().equals(scriptName)) {
+                        selectors.add(script.getName());
+                    }
+                    Optional<ProvidedResourceTypeCapability> extendsCapability = Optional.empty();
+                    if (selectors.isEmpty() && StringUtils.isEmpty(script.getRequestExtension()) &&
+                            StringUtils.isEmpty(script.getRequestMethod())) {
+                        extendsCapability =
+                                providedCapabilities.stream()
+                                        .filter(capability -> StringUtils.isNotEmpty(capability.getExtendsResourceType()) &&
+                                                capability.getResourceTypes().equals(searchPathProcessesResourceTypes) &&
+                                                capability.getSelectors().isEmpty() &&
+                                                StringUtils.isEmpty(capability.getRequestExtension()) &&
+                                                StringUtils.isEmpty(capability.getRequestMethod())).findAny();
                     }
+                    ProvidedResourceTypeCapability.Builder builder = ProvidedResourceTypeCapability.builder()
+                            .withResourceTypes(searchPathProcessesResourceTypes)
+                            .withVersion(resourceType.getVersion())
+                            .withSelectors(selectors)
+                            .withRequestExtension(script.getRequestExtension())
+                            .withRequestMethod(script.getRequestMethod())
+                            .withScriptEngine(scriptEngine)
+                            .withScriptExtension(script.getScriptExtension());
+                    extendsCapability.ifPresent(capability -> {
+                                builder.withExtendsResourceType(capability.getExtendsResourceType());
+                                providedCapabilities.remove(capability);
+                            }
+                    );
+                    providedCapabilities.add(builder.build());
                 } else {
-                    log.warn(String.format("File %s does not denote a script.", scriptPath));
+                    log.warn(String.format("Cannot find a script engine mapping for script %s!", scriptPath));
                 }
             } else {
-                log.warn(String.format("Skipping path %s since it has 0 elements.", scriptPath));
+                log.warn(String.format("Skipping file %s not denoting a script as it does not follow the filename patterns outlined at https://sling.apache.org/documentation/the-sling-engine/url-to-script-resolution.html#script-naming-conventions", scriptPath));
             }
+        } else {
+            throw new IllegalArgumentException(String.format("Invalid path given: '%s'.", scriptPath));
         }
+        
     }
 
     private Set<String> processSearchPathResourceTypes(@NotNull ResourceType resourceType) {
diff --git a/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/ResourceTypeFolderAnalyser.java b/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/ResourceTypeFolderAnalyser.java
index 8eff945..0e5a8b2 100644
--- a/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/ResourceTypeFolderAnalyser.java
+++ b/src/main/java/org/apache/sling/scriptingbundle/plugin/processor/ResourceTypeFolderAnalyser.java
@@ -19,6 +19,7 @@
 package org.apache.sling.scriptingbundle.plugin.processor;
 
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -88,8 +89,10 @@ public class ResourceTypeFolderAnalyser {
                         }
                     }
                 });
-            } catch (IOException | IllegalArgumentException e) {
+            } /*catch (IOException | IllegalArgumentException e) {
                 logger.warn(String.format("Cannot analyse folder %s.", scriptsDirectory.resolve(resourceTypeDirectory).toString()), e);
+            }*/ catch (IOException e1) {
+                throw new UncheckedIOException(e1);
             }
         }
 
diff --git a/src/test/java/org/apache/sling/scriptingbundle/plugin/processor/FileProcessorTest.java b/src/test/java/org/apache/sling/scriptingbundle/plugin/processor/FileProcessorTest.java
new file mode 100644
index 0000000..12d59f1
--- /dev/null
+++ b/src/test/java/org/apache/sling/scriptingbundle/plugin/processor/FileProcessorTest.java
@@ -0,0 +1,134 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.plugin.processor;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.sling.api.resource.type.ResourceType;
+import org.apache.sling.scriptingbundle.plugin.capability.ProvidedResourceTypeCapability;
+import org.apache.sling.scriptingbundle.plugin.capability.RequiredResourceTypeCapability;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.VersionRange;
+
+public class FileProcessorTest {
+
+    private FileProcessor processor;
+    Set<ProvidedResourceTypeCapability> providedCapabilities;
+    Set<RequiredResourceTypeCapability> requiredCapabilities;
+    private static final ResourceType MY_RESOURCE_TYPE = ResourceType.parseResourceType("apps/my/resource");
+
+    @Before
+    public void setUp() {
+        processor = new FileProcessor(new Slf4jLogger(), Constants.DEFAULT_SEARCH_PATHS, Constants.DEFAULT_EXTENSION_TO_SCRIPT_ENGINE_MAPPING);
+        providedCapabilities = new HashSet<>();
+        requiredCapabilities = new HashSet<>();
+    }
+
+    @Test
+    public void testExtendsValid() {
+        Path extendsFile = Paths.get("src", "test", "resources", "extends", "valid");
+        processor.processExtendsFile(MY_RESOURCE_TYPE, extendsFile, providedCapabilities, requiredCapabilities);
+        Assert.assertEquals(1, requiredCapabilities.size());
+        RequiredResourceTypeCapability expectedRequiredCapability = RequiredResourceTypeCapability.builder()
+                .withResourceType("org/apache/sling/bar")
+                .withVersionRange(new VersionRange("[1.0.0,2.0.0)"))
+                .withIsOptional().build();
+        Assert.assertEquals(expectedRequiredCapability, requiredCapabilities.iterator().next());
+        Assert.assertEquals(1, providedCapabilities.size());
+        ProvidedResourceTypeCapability expectedProvidedCapability = ProvidedResourceTypeCapability.builder()
+                .withResourceTypes(new HashSet<String>(Arrays.asList("my/resource", "/apps/my/resource")))
+                .withVersion(MY_RESOURCE_TYPE.getVersion())
+                .withExtendsResourceType("org/apache/sling/bar")
+                .build();
+        Assert.assertEquals(expectedProvidedCapability, providedCapabilities.iterator().next());
+    }
+
+    @Test
+    public void testExtendsMultipleClauses() {
+        Path extendsFile = Paths.get("src", "test", "resources", "extends", "multiple-clauses");
+        Assert.assertThrows(IllegalArgumentException.class, () -> { processor.processExtendsFile(MY_RESOURCE_TYPE, extendsFile, providedCapabilities, requiredCapabilities); });
+    }
+
+    @Test
+    public void testExtendsInvalidAttributes() {
+        Path extendsFile = Paths.get("src", "test", "resources", "extends", "invalid-attributes");
+        Assert.assertThrows(IllegalArgumentException.class, () -> { processor.processExtendsFile(MY_RESOURCE_TYPE, extendsFile, providedCapabilities, requiredCapabilities); });
+    }
+
+    @Test
+    public void testExtendsMultipleLines() {
+        Path extendsFile = Paths.get("src", "test", "resources", "extends", "multiple-lines");
+        Assert.assertThrows(IllegalArgumentException.class, () -> { processor.processExtendsFile(MY_RESOURCE_TYPE, extendsFile, providedCapabilities, requiredCapabilities); });
+    }
+
+    @Test
+    public void testRequiresValid() {
+        Path requiresFile = Paths.get("src", "test", "resources", "requires", "valid");
+        processor.processRequiresFile(requiresFile, requiredCapabilities);
+        Assert.assertEquals(1, requiredCapabilities.size());
+        RequiredResourceTypeCapability expectedCapability = RequiredResourceTypeCapability.builder()
+                .withResourceType("org/apache/sling/bar")
+                .withVersionRange(new VersionRange("[1.0.0,2.0.0)"))
+                .withIsOptional().build();
+        Assert.assertEquals(expectedCapability, requiredCapabilities.iterator().next());
+    }
+
+    @Test
+    public void testRequiresMultipleClauses() {
+        Path requiresFile = Paths.get("src", "test", "resources", "requires", "multiple-clauses");
+        Assert.assertThrows(IllegalArgumentException.class, () -> { processor.processRequiresFile(requiresFile, requiredCapabilities); });
+    }
+
+    @Test
+    public void testRequiresInvalidAttributes() {
+        Path requiresFile = Paths.get("src", "test", "resources", "extends", "invalid-attributes");
+        Assert.assertThrows(IllegalArgumentException.class, () -> { processor.processRequiresFile(requiresFile, requiredCapabilities); });
+    }
+
+    @Test
+    public void testScriptValid() {
+        Path resourceTypeFolder = Paths.get("apps", "my", "resource", "2.0");
+        Path script = Paths.get("apps", "my", "resource", "2.0", "selectorb", "selectora.POST.html");
+        processor.processScriptFile(resourceTypeFolder, script, MY_RESOURCE_TYPE, providedCapabilities);
+        Assert.assertEquals(1, providedCapabilities.size());
+        ProvidedResourceTypeCapability expectedProvidedCapability = ProvidedResourceTypeCapability.builder()
+                .withResourceTypes(new HashSet<>(Arrays.asList("my/resource", "/apps/my/resource")))
+                .withVersion(MY_RESOURCE_TYPE.getVersion())
+                .withRequestMethod("POST")
+                .withSelectors(new HashSet<>(Arrays.asList("selectorb", "selectora")))
+                .withScriptEngine("htl")
+                .withScriptExtension("html")
+                .build();
+        Assert.assertEquals(expectedProvidedCapability, providedCapabilities.iterator().next());
+    }
+
+    @Test
+    public void testScriptUnknownExtension() {
+        Path resourceTypeFolder = Paths.get("scripts",  "apps", "my", "resource", "2.0");
+        Path script = Paths.get("scripts", "apps", "my", "resource", "2.0", "selectorb", "selectora.POST.abc");
+        processor.processScriptFile(resourceTypeFolder, script, MY_RESOURCE_TYPE, providedCapabilities);
+        Assert.assertEquals(0, providedCapabilities.size());
+    }
+}
diff --git a/src/test/java/org/apache/sling/scriptingbundle/plugin/processor/Slf4jLogger.java b/src/test/java/org/apache/sling/scriptingbundle/plugin/processor/Slf4jLogger.java
new file mode 100644
index 0000000..e56dceb
--- /dev/null
+++ b/src/test/java/org/apache/sling/scriptingbundle/plugin/processor/Slf4jLogger.java
@@ -0,0 +1,52 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.plugin.processor;
+
+import org.jetbrains.annotations.NotNull;
+
+public class Slf4jLogger implements Logger {
+
+    private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(Slf4jLogger.class);
+    
+    @Override
+    public void error(@NotNull String message) {
+        logger.error(message);
+    }
+
+    @Override
+    public void error(@NotNull String message, @NotNull Throwable t) {
+        logger.error(message, t);
+    }
+
+    @Override
+    public void info(@NotNull String message) {
+        logger.info(message);
+    }
+
+    @Override
+    public void warn(@NotNull String message) {
+        logger.warn(message);
+    }
+
+    @Override
+    public void warn(@NotNull String message, @NotNull Throwable t) {
+        logger.warn(message, t);
+    }
+
+}
diff --git a/src/test/resources/extends/invalid-attributes b/src/test/resources/extends/invalid-attributes
new file mode 100644
index 0000000..f273579
--- /dev/null
+++ b/src/test/resources/extends/invalid-attributes
@@ -0,0 +1 @@
+clause1;attribute1=value1
\ No newline at end of file
diff --git a/src/test/resources/extends/multiple-clauses b/src/test/resources/extends/multiple-clauses
new file mode 100644
index 0000000..d9f9961
--- /dev/null
+++ b/src/test/resources/extends/multiple-clauses
@@ -0,0 +1 @@
+clause1,clause2
\ No newline at end of file
diff --git a/src/test/resources/extends/multiple-lines b/src/test/resources/extends/multiple-lines
new file mode 100644
index 0000000..a999ba7
--- /dev/null
+++ b/src/test/resources/extends/multiple-lines
@@ -0,0 +1,2 @@
+clause1
+clause2
\ No newline at end of file
diff --git a/src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends b/src/test/resources/extends/valid
similarity index 96%
copy from src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends
copy to src/test/resources/extends/valid
index 9e61a9b..bde342d 100644
--- a/src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends
+++ b/src/test/resources/extends/valid
@@ -1 +1 @@
-org/apache/sling/bar;version="[1.0.0,2.0.0)";resolution:=optional
+org/apache/sling/bar;version="[1.0.0,2.0.0)";resolution:=optional;
\ No newline at end of file
diff --git a/src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends b/src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends
index 9e61a9b..a2631cb 100644
--- a/src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends
+++ b/src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends
@@ -1 +1 @@
-org/apache/sling/bar;version="[1.0.0,2.0.0)";resolution:=optional
+org/apache/sling/bar;version="[1.0.0,2.0.0)";resolution:=optional;
diff --git a/src/test/resources/requires/invalid-attributes b/src/test/resources/requires/invalid-attributes
new file mode 100644
index 0000000..f273579
--- /dev/null
+++ b/src/test/resources/requires/invalid-attributes
@@ -0,0 +1 @@
+clause1;attribute1=value1
\ No newline at end of file
diff --git a/src/test/resources/requires/multiple-clauses b/src/test/resources/requires/multiple-clauses
new file mode 100644
index 0000000..d9f9961
--- /dev/null
+++ b/src/test/resources/requires/multiple-clauses
@@ -0,0 +1 @@
+clause1,clause2
\ No newline at end of file
diff --git a/src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends b/src/test/resources/requires/valid
similarity index 96%
copy from src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends
copy to src/test/resources/requires/valid
index 9e61a9b..bde342d 100644
--- a/src/test/resources/project-1/src/main/scripts/org.apache.sling.foobar/extends
+++ b/src/test/resources/requires/valid
@@ -1 +1 @@
-org/apache/sling/bar;version="[1.0.0,2.0.0)";resolution:=optional
+org/apache/sling/bar;version="[1.0.0,2.0.0)";resolution:=optional;
\ No newline at end of file