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 2022/08/11 15:30:32 UTC

[sling-maven-plugin] branch master updated: SLING-11523 replace scannotation and ASM by classgraph (#7)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 7ef1df3  SLING-11523 replace scannotation and ASM by classgraph (#7)
7ef1df3 is described below

commit 7ef1df38a4202581c4b044b5ebe535c72b8e5f9e
Author: Konrad Windszus <kw...@apache.org>
AuthorDate: Thu Aug 11 17:30:27 2022 +0200

    SLING-11523 replace scannotation and ASM by classgraph (#7)
    
    add test for adapter annotation metadata generator
---
 sling-maven-plugin/pom.xml                         |  33 +---
 .../bundlesupport/GenerateAdapterMetadataMojo.java | 219 +++++++--------------
 .../GenerateAdapterMetadataMojoTest.java           |  83 ++++++++
 .../bundlesupport/annotationtest/Adapter1.java     |  31 +++
 .../bundlesupport/annotationtest/Adapter2.java     |  27 +++
 5 files changed, 212 insertions(+), 181 deletions(-)

diff --git a/sling-maven-plugin/pom.xml b/sling-maven-plugin/pom.xml
index fbc5f69..e976a53 100644
--- a/sling-maven-plugin/pom.xml
+++ b/sling-maven-plugin/pom.xml
@@ -47,7 +47,6 @@
         <github.project.id>apache/sling-maven-plugin</github.project.id>
         <sling.java.version>8</sling.java.version>
         <mavenVersion>3.3.1</mavenVersion><!-- minimum target Maven version this plugin is compatible with -->
-        <asmVersion>9.3</asmVersion>
         <maven.compiler.target>${sling.java.version}</maven.compiler.target><!-- also set target next to release due to https://issues.apache.org/jira/browse/MPLUGIN-404 -->
     </properties>
 
@@ -262,21 +261,9 @@
         </dependency>
         <!-- use for evaluating Adapter annotations -->
         <dependency>
-            <groupId>net.sf.scannotation</groupId>
-            <artifactId>scannotation</artifactId>
-            <version>1.0.2</version>
-            <scope>compile</scope>
-            <exclusions>
-                <exclusion>
-                    <groupId>javassist</groupId>
-                    <artifactId>javassist</artifactId>
-                </exclusion>
-            </exclusions>
-        </dependency>
-        <dependency>
-            <groupId>org.javassist</groupId>
-            <artifactId>javassist</artifactId>
-            <version>3.18.2-GA</version>
+            <groupId>io.github.classgraph</groupId>
+            <artifactId>classgraph</artifactId>
+            <version>4.8.149</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
@@ -285,20 +272,6 @@
             <version>1.0.0</version>
             <scope>compile</scope>
         </dependency>
-        <!-- https://mvnrepository.com/artifact/org.ow2.asm/asm -->
-        <dependency>
-            <groupId>org.ow2.asm</groupId>
-            <artifactId>asm</artifactId>
-            <version>${asmVersion}</version>
-            <scope>compile</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.ow2.asm</groupId>
-            <artifactId>asm-commons</artifactId>
-            <version>${asmVersion}</version>
-            <scope>compile</scope>
-        </dependency>
-
         <dependency>
             <groupId>org.apache.maven.plugin-tools</groupId>
             <artifactId>maven-plugin-annotations</artifactId>
diff --git a/sling-maven-plugin/src/main/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojo.java b/sling-maven-plugin/src/main/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojo.java
index fabf269..8b36663 100644
--- a/sling-maven-plugin/src/main/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojo.java
+++ b/sling-maven-plugin/src/main/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojo.java
@@ -16,21 +16,15 @@
  */
 package org.apache.sling.maven.bundlesupport;
 
-import static org.objectweb.asm.ClassReader.SKIP_CODE;
-import static org.objectweb.asm.ClassReader.SKIP_DEBUG;
-import static org.objectweb.asm.ClassReader.SKIP_FRAMES;
-
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.FileWriter;
 import java.io.IOException;
-import java.lang.annotation.Annotation;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import javax.json.Json;
 import javax.json.JsonException;
@@ -48,11 +42,18 @@ import org.apache.maven.project.MavenProject;
 import org.apache.sling.adapter.annotations.Adaptable;
 import org.apache.sling.adapter.annotations.Adaptables;
 import org.codehaus.plexus.util.StringUtils;
-import org.objectweb.asm.ClassReader;
-import org.objectweb.asm.Type;
-import org.objectweb.asm.tree.AnnotationNode;
-import org.objectweb.asm.tree.ClassNode;
-import org.scannotation.AnnotationDB;
+
+import io.github.classgraph.AnnotationClassRef;
+import io.github.classgraph.AnnotationInfo;
+import io.github.classgraph.AnnotationInfoList.AnnotationInfoFilter;
+import io.github.classgraph.AnnotationParameterValue;
+import io.github.classgraph.AnnotationParameterValueList;
+import io.github.classgraph.ClassGraph;
+import io.github.classgraph.ClassGraph.ClasspathElementFilter;
+import io.github.classgraph.ClassGraph.ClasspathElementURLFilter;
+import io.github.classgraph.ClassInfo;
+import io.github.classgraph.ClassInfoList;
+import io.github.classgraph.ScanResult;
 
 /**
  * Build  <a href="http://sling.apache.org/documentation/the-sling-engine/adapters.html">adapter metadata (JSON)</a> for the Web Console Plugin at {@code /system/console/status-adapters} and
@@ -63,37 +64,24 @@ import org.scannotation.AnnotationDB;
     threadSafe = true, requiresDependencyResolution = ResolutionScope.COMPILE)
 public class GenerateAdapterMetadataMojo extends AbstractMojo {
 
-    private static final String ADAPTABLE_DESC = "L" + Adaptable.class.getName().replace('.', '/') + ";";
-
-    private static final String ADAPTABLES_DESC = "L" + Adaptables.class.getName().replace('.', '/') + ";";
-
     private static final String DEFAULT_CONDITION = "If the adaptable is a %s.";
 
-    private static String getSimpleName(final ClassNode clazz) {
-        final String internalName = clazz.name;
-        final int idx = internalName.lastIndexOf('/');
-        if (idx == -1) {
-            return internalName;
-        } else {
-            return internalName.substring(idx + 1);
-        }
-    }
 
     /** The directory which to check for classes with adapter metadata annotations. */
     @Parameter(defaultValue = "${project.build.outputDirectory}")
-    private File buildOutputDirectory;
+    File buildOutputDirectory;
 
     /**
      * Name of the generated descriptor file.
      */
     @Parameter(property = "adapter.descriptor.name", defaultValue = "SLING-INF/adapters.json")
-    private String fileName;
+    String fileName;
 
     /**
      * The output directory in which to emit the descriptor file with name {@link GenerateAdapterMetadataMojo#fileName}.
      */
     @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
-    private File outputDirectory;
+    File outputDirectory;
 
     /**
      * The Maven project.
@@ -102,39 +90,30 @@ public class GenerateAdapterMetadataMojo extends AbstractMojo {
     private MavenProject project;
 
     public void execute() throws MojoExecutionException, MojoFailureException {
-        try {
-            final Map<String,Object> descriptor = new HashMap<>();
-
-            final AnnotationDB annotationDb = new AnnotationDB();
-            annotationDb.scanArchives(buildOutputDirectory.toURI().toURL());
-
-            final Set<String> annotatedClassNames = new HashSet<>();
-            addAnnotatedClasses(annotationDb, annotatedClassNames, Adaptable.class);
-            addAnnotatedClasses(annotationDb, annotatedClassNames, Adaptables.class);
-
-            for (final String annotatedClassName : annotatedClassNames) {
-                getLog().info(String.format("found adaptable annotation on %s", annotatedClassName));
-                final String pathToClassFile = annotatedClassName.replace('.', '/') + ".class";
-                final File classFile = new File(buildOutputDirectory, pathToClassFile);
-                final FileInputStream input = new FileInputStream(classFile);
-                final ClassReader classReader;
-                try {
-                    classReader = new ClassReader(input);
-                } finally {
-                    input.close();
-                }
-                final ClassNode classNode = new ClassNode();
-                classReader.accept(classNode, SKIP_CODE | SKIP_DEBUG | SKIP_FRAMES);
-
-                final List<AnnotationNode> annotations = classNode.invisibleAnnotations;
-                for (final AnnotationNode annotation : annotations) {
-                    if (ADAPTABLE_DESC.equals(annotation.desc)) {
-                        parseAdaptableAnnotation(annotation, classNode, descriptor);
-                    } else if (ADAPTABLES_DESC.equals(annotation.desc)) {
-                        parseAdaptablesAnnotation(annotation, classNode, descriptor);
+        final Map<String,Object> descriptor = new HashMap<>();
+        ClassGraph classGraph = new ClassGraph()
+                                    .enableAnnotationInfo()                  // only consider annotation info
+                                    .overrideClasspath(buildOutputDirectory) // just the classpath of the output directory needed (annotation classes themselves not relevant)
+                                    .enableExternalClasses();
+        if (getLog().isDebugEnabled()) {
+            classGraph.verbose();
+        }
+        try (ScanResult result = classGraph.scan()) {
+            ClassInfoList classInfoList = result.getClassesWithAnnotation(Adaptable.class);
+            classInfoList = classInfoList.union(result.getClassesWithAnnotation(Adaptables.class));
+
+            for (ClassInfo annotationClassInfo : classInfoList) {
+                getLog().info(String.format("found adaptable annotation on %s", annotationClassInfo.getSimpleName()));
+                for (AnnotationInfo annotationInfo : annotationClassInfo.getAnnotationInfo().filter(new AdaptableAnnotationInfoFilter())) {
+                    AnnotationParameterValueList annotationParameterValues = annotationInfo.getParameterValues();
+                    if (annotationInfo.getName().equals(Adaptables.class.getName())) {
+                        parseAdaptablesAnnotation(annotationParameterValues, annotationClassInfo.getSimpleName(), descriptor);
+                    } else if (annotationInfo.getName().equals(Adaptable.class.getName())) {
+                        parseAdaptableAnnotation(annotationParameterValues, annotationClassInfo.getSimpleName(), descriptor);
+                    } else {
+                        throw new IllegalStateException("Unexpected annotation class found: " + annotationInfo);
                     }
                 }
-
             }
 
             final File outputFile = new File(outputDirectory, fileName);
@@ -143,84 +122,34 @@ public class GenerateAdapterMetadataMojo extends AbstractMojo {
                     JsonWriter jsonWriter = Json.createWriter(writer)) {
                 jsonWriter.writeObject(JsonSupport.toJson(descriptor));
             }
-            addResource();
 
-        } catch (IOException e) {
-            throw new MojoExecutionException("Unable to generate metadata", e);
-        } catch (JsonException e) {
+        } catch (IOException|JsonException e) {
             throw new MojoExecutionException("Unable to generate metadata", e);
         }
 
     }
 
-    private void addAnnotatedClasses(final AnnotationDB annotationDb, final Set<String> annotatedClassNames, final Class<? extends Annotation> clazz) {
-        Set<String> classNames = annotationDb.getAnnotationIndex().get(clazz.getName());
-        if (classNames == null || classNames.isEmpty()) {
-            getLog().debug("No classes found with adaptable annotations.");
-        } else {
-            annotatedClassNames.addAll(classNames);
-        }
-    }
-
-    private void addResource() {
-        final String ourRsrcPath = this.outputDirectory.getAbsolutePath();
-        boolean found = false;
-        final Iterator<Resource> rsrcIterator = this.project.getResources().iterator();
-        while (!found && rsrcIterator.hasNext()) {
-            final Resource rsrc = rsrcIterator.next();
-            found = rsrc.getDirectory().equals(ourRsrcPath);
-        }
-        if (!found) {
-            final Resource resource = new Resource();
-            resource.setDirectory(this.outputDirectory.getAbsolutePath());
-            this.project.addResource(resource);
+    private static final class AdaptableAnnotationInfoFilter implements AnnotationInfoFilter {
+        @Override
+        public boolean accept(AnnotationInfo annotationInfo) {
+            return (annotationInfo.getName().equals(Adaptables.class.getName()) || annotationInfo.getName().equals(Adaptable.class.getName()));
         }
-
     }
 
-    private void parseAdaptablesAnnotation(final AnnotationNode annotation, final ClassNode classNode,
-            final Map<String,Object> descriptor) throws JsonException {
-        final Iterator<?> it = annotation.values.iterator();
-        while (it.hasNext()) {
-            Object name = it.next();
-            Object value = it.next();
-            if ("value".equals(name)) {
-                @SuppressWarnings("unchecked")
-                final List<AnnotationNode> annotations = (List<AnnotationNode>) value;
-                for (final AnnotationNode innerAnnotation : annotations) {
-                    if (ADAPTABLE_DESC.equals(innerAnnotation.desc)) {
-                        parseAdaptableAnnotation(innerAnnotation, classNode, descriptor);
-                    }
-                }
-            }
+    private void parseAdaptablesAnnotation(AnnotationParameterValueList annotationParameterValues, String annotatedClassName, final Map<String,Object> descriptor) throws JsonException {
+        // only one mandatory parameter "value" of type Adaptable[]
+        Object[] annotationInfos = (Object[])annotationParameterValues.get(0).getValue();
+        for (Object annotationInfo : annotationInfos) {
+            parseAdaptableAnnotation(((AnnotationInfo)annotationInfo).getParameterValues(), annotatedClassName, descriptor);
         }
     }
 
     @SuppressWarnings("unchecked")
-    private void parseAdaptableAnnotation(final AnnotationNode annotation, final ClassNode annotatedClass,
-            final Map<String,Object> descriptor) throws JsonException {
-        String adaptableClassName = null;
-        List<AnnotationNode> adapters = null;
-
-        final List<?> values = annotation.values;
-
-        final Iterator<?> it = values.iterator();
-        while (it.hasNext()) {
-            Object name = it.next();
-            Object value = it.next();
-
-            if ("adaptableClass".equals(name)) {
-                adaptableClassName = ((Type) value).getClassName();
-            } else if ("adapters".equals(name)) {
-                adapters = (List<AnnotationNode>) value;
-            }
-        }
-
-        if (adaptableClassName == null || adapters == null) {
-            throw new IllegalArgumentException(
-                    "Adaptable annotation is malformed. Expecting a classname and a list of adapter annotation.");
-        }
-
+    private void parseAdaptableAnnotation(AnnotationParameterValueList annotationParameterValues, String annotatedClassName, final Map<String,Object> descriptor) throws JsonException {
+        // two parameters: adaptableClass and Adapter[] adapters
+        String adaptableClassName = ((AnnotationClassRef) annotationParameterValues.get("adaptableClass").getValue()).getName();
+        Object[] adapters = (Object[]) annotationParameterValues.get("adapters").getValue();
+        
         Map<String,Object> adaptableDescription;
         if (descriptor.containsKey(adaptableClassName)) {
             adaptableDescription = (Map<String,Object>)descriptor.get(adaptableClassName);
@@ -229,41 +158,29 @@ public class GenerateAdapterMetadataMojo extends AbstractMojo {
             descriptor.put(adaptableClassName, adaptableDescription);
         }
 
-        for (final AnnotationNode adapter : adapters) {
-            parseAdapterAnnotation(adapter, annotatedClass, adaptableDescription);
+        for (final Object adapter : adapters) {
+            parseAdapterAnnotation(((AnnotationInfo)adapter).getParameterValues(), annotatedClassName, adaptableDescription);
         }
     }
 
-    @SuppressWarnings("unchecked")
-    private void parseAdapterAnnotation(final AnnotationNode annotation, final ClassNode annotatedClass,
-            final Map<String,Object> adaptableDescription) throws JsonException {
+    private void parseAdapterAnnotation(AnnotationParameterValueList annotationParameterValues, String annotatedClassName, final Map<String,Object> adaptableDescription) throws JsonException {
+        AnnotationParameterValue conditionParameterValue = annotationParameterValues.get("condition");
         String condition = null;
-        List<Type> adapterClasses = null;
-
-        final List<?> values = annotation.values;
-
-        final Iterator<?> it = values.iterator();
-        while (it.hasNext()) {
-            final Object name = it.next();
-            final Object value = it.next();
-
-            if (StringUtils.isEmpty(condition)) {
-                condition = String.format(DEFAULT_CONDITION, getSimpleName(annotatedClass));
-            }
-
-            if ("condition".equals(name)) {
-                condition = (String) value;
-            } else if ("value".equals(name)) {
-                adapterClasses = (List<Type>) value;
-            }
+        if (conditionParameterValue != null) {
+            condition = (String) conditionParameterValue.getValue();
+        }
+        if (StringUtils.isEmpty(condition)) {
+            condition = String.format(DEFAULT_CONDITION, annotatedClassName);
         }
+        Object[] adapterClasses = (Object[]) annotationParameterValues.get("value").getValue();
 
         if (adapterClasses == null) {
             throw new IllegalArgumentException("Adapter annotation is malformed. Expecting a list of adapter classes");
         }
 
-        for (final Type adapterClass : adapterClasses) {
-            JsonSupport.accumulate(adaptableDescription, condition, adapterClass.getClassName());
+        for (final Object adapterClass : adapterClasses) {
+            String adapterClassName = ((AnnotationClassRef)adapterClass).getName();
+            JsonSupport.accumulate(adaptableDescription, condition, adapterClassName);
         }
     }
 
diff --git a/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojoTest.java b/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojoTest.java
new file mode 100644
index 0000000..50809b5
--- /dev/null
+++ b/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/GenerateAdapterMetadataMojoTest.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.maven.bundlesupport;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import javax.json.Json;
+import javax.json.JsonObjectBuilder;
+import javax.json.JsonReader;
+
+import org.apache.commons.io.file.PathUtils;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.sling.maven.bundlesupport.annotationtest.Adapter1;
+import org.apache.sling.maven.bundlesupport.annotationtest.Adapter2;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+
+public class GenerateAdapterMetadataMojoTest {
+
+    @Rule
+    public TemporaryFolder tmpDirectory = new TemporaryFolder();
+
+    static final Path RELATIVE_ANNOTATIONTEST_PACKAGE_PATH = Paths.get("org", "apache", "sling", "maven", "bundlesupport", "annotationtest");
+    @Test
+    public void testExecute() throws MojoExecutionException, MojoFailureException, IOException {
+        GenerateAdapterMetadataMojo mojo = new GenerateAdapterMetadataMojo();
+        // copy classes in package "annotationtest" to classpath?
+        File classpathFolder = tmpDirectory.newFolder("test-classpath");
+        Path testClasspath = Paths.get(GenerateAdapterMetadataMojoTest.class.getProtectionDomain().getCodeSource().getLocation().getPath());
+        // only support directory right now
+        if (!Files.isDirectory(testClasspath)) {
+            throw new IllegalStateException("Only supposed to be called from a directory, not a jar file");
+        }
+        Path annotationTestSource = testClasspath.resolve(RELATIVE_ANNOTATIONTEST_PACKAGE_PATH);
+        Path annotationTestTarget = classpathFolder.toPath().resolve(RELATIVE_ANNOTATIONTEST_PACKAGE_PATH);
+        Files.createDirectories(annotationTestTarget);
+        PathUtils.copyDirectory(annotationTestSource, annotationTestTarget);
+        mojo.buildOutputDirectory = classpathFolder;
+        mojo.outputDirectory = tmpDirectory.getRoot();
+        mojo.fileName = "output.json";
+        mojo.execute();
+
+        // check output file
+        Path outputFile = new File(tmpDirectory.getRoot(), "output.json").toPath();
+        assertTrue(Files.exists(outputFile));
+        JsonObjectBuilder expectedJsonObjectBuilder = Json.createObjectBuilder();
+        expectedJsonObjectBuilder.add("java.lang.Long", Json.createObjectBuilder().add("first condition", Adapter2.class.getName()));
+        expectedJsonObjectBuilder.add("java.lang.String", Json.createObjectBuilder().add("If the adaptable is a Adapter1.", Json.createArrayBuilder().add(Adapter1.class.getName()).add(Adapter2.class.getName())));
+        expectedJsonObjectBuilder.add("java.lang.Integer", Json.createObjectBuilder().add("If the adaptable is a Adapter1.", Adapter1.class.getName()));
+        
+        try (InputStream input = Files.newInputStream(outputFile);
+             JsonReader jsonReader = Json.createReader(input)) {
+            assertEquals(expectedJsonObjectBuilder.build(), jsonReader.readObject());
+        }
+    }
+}
diff --git a/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/annotationtest/Adapter1.java b/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/annotationtest/Adapter1.java
new file mode 100644
index 0000000..cd31c8d
--- /dev/null
+++ b/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/annotationtest/Adapter1.java
@@ -0,0 +1,31 @@
+/*
+ * 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.maven.bundlesupport.annotationtest;
+
+import org.apache.sling.adapter.annotations.Adaptable;
+import org.apache.sling.adapter.annotations.Adaptables;
+import org.apache.sling.adapter.annotations.Adapter;
+
+@Adaptables({
+    @Adaptable( adaptableClass = String.class, adapters = { @Adapter(value = {Adapter1.class, Adapter2.class}) }),
+    @Adaptable( adaptableClass = Integer.class, adapters = { @Adapter(Adapter1.class) })
+})
+public class Adapter1 {
+
+}
diff --git a/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/annotationtest/Adapter2.java b/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/annotationtest/Adapter2.java
new file mode 100644
index 0000000..20380b7
--- /dev/null
+++ b/sling-maven-plugin/src/test/java/org/apache/sling/maven/bundlesupport/annotationtest/Adapter2.java
@@ -0,0 +1,27 @@
+/*
+ * 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.maven.bundlesupport.annotationtest;
+
+import org.apache.sling.adapter.annotations.Adaptable;
+import org.apache.sling.adapter.annotations.Adapter;
+
+@Adaptable(adaptableClass = Long.class, adapters = { @Adapter(condition = "first condition", value = Adapter2.class) })
+public class Adapter2 {
+
+}