Browse Source

Found extensions when using decorated annotations (#348)

#289
Decebal Suiu 5 years ago committed by GitHub
parent
commit
84461f0f6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 24
      pf4j/src/main/java/org/pf4j/AbstractExtensionFinder.java
  2. 72
      pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java
  3. 14
      pf4j/src/main/java/org/pf4j/util/ClassUtils.java
  4. 48
      pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java

24
pf4j/src/main/java/org/pf4j/AbstractExtensionFinder.java

@ -20,6 +20,7 @@ import org.pf4j.util.ClassUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@ -340,15 +341,30 @@ public abstract class AbstractExtensionFinder implements ExtensionFinder, Plugin
}
private ExtensionWrapper createExtensionWrapper(Class<?> extensionClass) {
int ordinal = 0;
if (extensionClass.isAnnotationPresent(Extension.class)) {
ordinal = extensionClass.getAnnotation(Extension.class).ordinal();
}
Extension extensionAnnotation = findExtensionAnnotation(extensionClass);
int ordinal = extensionAnnotation != null ? extensionAnnotation.ordinal() : 0;
ExtensionDescriptor descriptor = new ExtensionDescriptor(ordinal, extensionClass);
return new ExtensionWrapper<>(descriptor, pluginManager.getExtensionFactory());
}
private Extension findExtensionAnnotation(Class<?> clazz) {
if (clazz.isAnnotationPresent(Extension.class)) {
return clazz.getAnnotation(Extension.class);
}
// search recursively through all annotations
for (Annotation annotation : clazz.getAnnotations()) {
Class<? extends Annotation> annotationClass = annotation.annotationType();
Extension extensionAnnotation = findExtensionAnnotation(annotationClass);
if (extensionAnnotation != null) {
return extensionAnnotation;
}
}
return null;
}
private void checkDifferentClassLoaders(Class<?> type, Class<?> extensionClass) {
ClassLoader typeClassLoader = type.getClassLoader(); // class loader of extension point
ClassLoader extensionClassLoader = extensionClass.getClassLoader();

72
pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java

@ -25,6 +25,7 @@ import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
@ -32,6 +33,7 @@ import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -60,7 +62,7 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor {
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
info("%s init", ExtensionAnnotationProcessor.class);
info("%s init", ExtensionAnnotationProcessor.class.getName());
initStorage();
}
@ -72,10 +74,7 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor {
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotationTypes = new HashSet<>();
annotationTypes.add(Extension.class.getName());
return annotationTypes;
return Collections.singleton("*");
}
@Override
@ -92,33 +91,26 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor {
return false;
}
info("Processing @%s", Extension.class);
info("Processing @%s", Extension.class.getName());
for (Element element : roundEnv.getElementsAnnotatedWith(Extension.class)) {
// check if @Extension is put on class and not on method or constructor
if (!(element instanceof TypeElement)) {
error(element, "Put annotation only on classes (no methods, no fields)");
continue;
if (element.getKind() != ElementKind.ANNOTATION_TYPE) {
processExtensionElement(element);
}
// check if class extends/implements an extension point
if (!isExtension(element.asType())) {
error(element, "%s is not an extension (it doesn't implement ExtensionPoint)", element);
continue;
}
TypeElement extensionElement = (TypeElement) element;
// Extension annotation = element.getAnnotation(Extension.class);
List<TypeElement> extensionPointElements = findExtensionPoints(extensionElement);
if (extensionPointElements.isEmpty()) {
// TODO throw error ?
continue;
// collect nested extension annotations
List<TypeElement> extensionAnnotations = new ArrayList<>();
for (TypeElement annotation : annotations) {
if (ClassUtils.getAnnotationMirror(annotation, Extension.class) != null) {
extensionAnnotations.add(annotation);
}
}
String extension = getBinaryName(extensionElement);
for (TypeElement extensionPointElement : extensionPointElements) {
String extensionPoint = getBinaryName(extensionPointElement);
Set<String> extensionPoints = extensions.computeIfAbsent(extensionPoint, k -> new TreeSet<>());
extensionPoints.add(extension);
// process nested extension annotations
for (TypeElement te : extensionAnnotations) {
info("Processing @%s", te);
for (Element element : roundEnv.getElementsAnnotatedWith(te)) {
processExtensionElement(element);
}
}
@ -250,4 +242,32 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor {
}
}
private void processExtensionElement(Element element) {
// check if @Extension is put on class and not on method or constructor
if (!(element instanceof TypeElement)) {
error(element, "Put annotation only on classes (no methods, no fields)");
return;
}
// check if class extends/implements an extension point
if (!isExtension(element.asType())) {
error(element, "%s is not an extension (it doesn't implement ExtensionPoint)", element);
return;
}
TypeElement extensionElement = (TypeElement) element;
List<TypeElement> extensionPointElements = findExtensionPoints(extensionElement);
if (extensionPointElements.isEmpty()) {
error(element, "No extension points found for extension %s", extensionElement);
return;
}
String extension = getBinaryName(extensionElement);
for (TypeElement extensionPointElement : extensionPointElements) {
String extensionPoint = getBinaryName(extensionPointElement);
Set<String> extensionPoints = extensions.computeIfAbsent(extensionPoint, k -> new TreeSet<>());
extensionPoints.add(extension);
}
}
}

14
pf4j/src/main/java/org/pf4j/util/ClassUtils.java

@ -19,7 +19,6 @@ import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -93,9 +92,17 @@ public class ClassUtils {
return m;
}
}
return null;
}
/*
public static Element getAnnotationMirrorElement(TypeElement typeElement, Class<?> annotationClass) {
AnnotationMirror annotationMirror = getAnnotationMirror(typeElement, annotationClass);
return annotationMirror != null ? annotationMirror.getAnnotationType().asElement() : null;
}
*/
/**
* Get a certain parameter of an {@link AnnotationMirror}.
* See <a href="https://stackoverflow.com/a/10167558">stackoverflow.com</a> for more information.
@ -111,6 +118,7 @@ public class ClassUtils {
return entry.getValue();
}
}
return null;
}
@ -126,9 +134,7 @@ public class ClassUtils {
*/
public static AnnotationValue getAnnotationValue(TypeElement typeElement, Class<?> annotationClass, String annotationParameter) {
AnnotationMirror annotationMirror = getAnnotationMirror(typeElement, annotationClass);
return (annotationMirror != null) ?
getAnnotationValue(annotationMirror, annotationParameter) :
null;
return annotationMirror != null ? getAnnotationValue(annotationMirror, annotationParameter) : null;
}
/**

48
pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java

@ -60,7 +60,7 @@ public class ExtensionAnnotationProcessorTest {
" }",
"}");
private static final JavaFileObject WhazzupGreeting_No_ExtensionPoint = JavaFileObjects.forSourceLines(
private static final JavaFileObject WhazzupGreeting_NoExtensionPoint = JavaFileObjects.forSourceLines(
"WhazzupGreeting",
"package test;",
"import org.pf4j.Extension;",
@ -73,12 +73,42 @@ public class ExtensionAnnotationProcessorTest {
" }",
"}");
private static final JavaFileObject SpinnakerExtension = JavaFileObjects.forSourceLines(
"SpinnakerExtension",
"package test;",
"",
"import org.pf4j.Extension;",
"import java.lang.annotation.Documented;",
"import java.lang.annotation.ElementType;",
"import java.lang.annotation.Retention;",
"import java.lang.annotation.RetentionPolicy;",
"import java.lang.annotation.Target;",
"",
"@Extension",
"@Retention(RetentionPolicy.RUNTIME)",
"@Target(ElementType.TYPE)",
"@Documented",
"public @interface SpinnakerExtension {",
"}");
private static final JavaFileObject WhazzupGreeting_SpinnakerExtension = JavaFileObjects.forSourceLines(
"WhazzupGreeting",
"package test;",
"",
"@SpinnakerExtension",
"public class WhazzupGreeting implements Greeting {",
" @Override",
" public String getGreeting() {",
" return \"Whazzup\";",
" }",
"}");
@Test
public void getSupportedAnnotationTypes() {
ExtensionAnnotationProcessor instance = new ExtensionAnnotationProcessor();
Set<String> result = instance.getSupportedAnnotationTypes();
assertEquals(1, result.size());
assertEquals(Extension.class.getName(), result.iterator().next());
assertEquals("*", result.iterator().next());
}
@Test
@ -118,10 +148,10 @@ public class ExtensionAnnotationProcessorTest {
@Test
public void compileWithError() {
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor();
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting_No_ExtensionPoint);
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting_NoExtensionPoint);
assertThat(compilation).failed();
assertThat(compilation).hadErrorContaining("it doesn't implement ExtensionPoint")
.inFile(WhazzupGreeting_No_ExtensionPoint)
.inFile(WhazzupGreeting_NoExtensionPoint)
.onLine(5)
.atColumn(8);
}
@ -136,4 +166,14 @@ public class ExtensionAnnotationProcessorTest {
assertEquals(extensions, processor.getExtensions());
}
@Test
public void compileNestedExtensionAnnotation() {
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor();
Compilation compilation = javac().withProcessors(processor).compile(Greeting, SpinnakerExtension, WhazzupGreeting_SpinnakerExtension);
assertThat(compilation).succeededWithoutWarnings();
Map<String, Set<String>> extensions = new HashMap<>();
extensions.put("test.Greeting", new HashSet<>(Collections.singletonList("test.WhazzupGreeting")));
assertEquals(extensions, processor.getExtensions());
}
}

Loading…
Cancel
Save