@@ -15,34 +15,26 @@ | |||
*/ | |||
package org.pf4j; | |||
import com.google.common.collect.ImmutableList; | |||
import com.google.common.io.ByteStreams; | |||
import com.google.testing.compile.Compilation; | |||
import java.util.Comparator; | |||
import java.util.UUID; | |||
import org.junit.jupiter.api.AfterEach; | |||
import org.junit.jupiter.api.Assertions; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.test.FailTestPlugin; | |||
import org.pf4j.test.JavaFileObjectClassLoader; | |||
import org.pf4j.test.JavaFileObjectUtils; | |||
import org.pf4j.test.JavaSources; | |||
import org.pf4j.test.TestExtension; | |||
import org.pf4j.test.TestExtensionPoint; | |||
import javax.tools.JavaFileObject; | |||
import java.io.IOException; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
import java.util.LinkedHashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Set; | |||
import java.util.UUID; | |||
import static com.google.testing.compile.CompilationSubject.assertThat; | |||
import static com.google.testing.compile.Compiler.javac; | |||
import static org.junit.Assert.assertNull; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertNotNull; | |||
import static org.mockito.Mockito.eq; | |||
import static org.mockito.Mockito.mock; | |||
import static org.mockito.Mockito.when; | |||
@@ -64,9 +56,9 @@ public class AbstractExtensionFinderTest { | |||
when(pluginStopped.getPluginState()).thenReturn(PluginState.STOPPED); | |||
pluginManager = mock(PluginManager.class); | |||
when(pluginManager.getPlugin(eq("plugin1"))).thenReturn(pluginStarted); | |||
when(pluginManager.getPlugin(eq("plugin2"))).thenReturn(pluginStopped); | |||
when(pluginManager.getPluginClassLoader(eq("plugin1"))).thenReturn(getClass().getClassLoader()); | |||
when(pluginManager.getPlugin("plugin1")).thenReturn(pluginStarted); | |||
when(pluginManager.getPlugin("plugin2")).thenReturn(pluginStopped); | |||
when(pluginManager.getPluginClassLoader("plugin1")).thenReturn(getClass().getClassLoader()); | |||
when(pluginManager.getExtensionFactory()).thenReturn(new DefaultExtensionFactory()); | |||
} | |||
@@ -93,7 +85,7 @@ public class AbstractExtensionFinderTest { | |||
} | |||
}; | |||
List<ExtensionWrapper<FailTestPlugin>> list = instance.find(FailTestPlugin.class); | |||
List<ExtensionWrapper<TestExtension>> list = instance.find(TestExtension.class); | |||
assertEquals(0, list.size()); | |||
} | |||
@@ -115,7 +107,6 @@ public class AbstractExtensionFinderTest { | |||
Set<String> bucket = new HashSet<>(); | |||
bucket.add("org.pf4j.test.TestExtension"); | |||
bucket.add("org.pf4j.test.FailTestExtension"); | |||
entries.put(null, bucket); | |||
return entries; | |||
@@ -124,7 +115,7 @@ public class AbstractExtensionFinderTest { | |||
}; | |||
List<ExtensionWrapper<TestExtensionPoint>> list = instance.find(TestExtensionPoint.class); | |||
assertEquals(2, list.size()); | |||
assertEquals(1, list.size()); | |||
} | |||
/** | |||
@@ -140,7 +131,6 @@ public class AbstractExtensionFinderTest { | |||
Set<String> bucket = new HashSet<>(); | |||
bucket.add("org.pf4j.test.TestExtension"); | |||
bucket.add("org.pf4j.test.FailTestExtension"); | |||
entries.put("plugin1", bucket); | |||
bucket = new HashSet<>(); | |||
bucket.add("org.pf4j.test.TestExtension"); | |||
@@ -157,12 +147,13 @@ public class AbstractExtensionFinderTest { | |||
}; | |||
List<ExtensionWrapper<TestExtensionPoint>> list = instance.find(TestExtensionPoint.class); | |||
assertEquals(2, list.size()); | |||
assertEquals(1, list.size()); | |||
list = instance.find(TestExtensionPoint.class, "plugin1"); | |||
assertEquals(2, list.size()); | |||
assertEquals(1, list.size()); | |||
list = instance.find(TestExtensionPoint.class, "plugin2"); | |||
// "0" because the status of "plugin2" is STOPPED => no extensions | |||
assertEquals(0, list.size()); | |||
} | |||
@@ -210,6 +201,16 @@ public class AbstractExtensionFinderTest { | |||
*/ | |||
@Test | |||
public void testFindExtensionWrappersFromPluginId() { | |||
// complicate the test to show hot to deal with dynamic Java classes (generated at runtime from sources) | |||
PluginWrapper plugin3 = mock(PluginWrapper.class); | |||
JavaFileObject object = JavaSources.compile(DefaultExtensionFactoryTest.FailTestExtension); | |||
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader(); | |||
classLoader.load(object); | |||
when(plugin3.getPluginClassLoader()).thenReturn(classLoader); | |||
when(plugin3.getPluginState()).thenReturn(PluginState.STARTED); | |||
when(pluginManager.getPluginClassLoader("plugin3")).thenReturn(classLoader); | |||
when(pluginManager.getPlugin("plugin3")).thenReturn(plugin3); | |||
ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { | |||
@Override | |||
@@ -218,11 +219,13 @@ public class AbstractExtensionFinderTest { | |||
Set<String> bucket = new HashSet<>(); | |||
bucket.add("org.pf4j.test.TestExtension"); | |||
bucket.add("org.pf4j.test.FailTestExtension"); | |||
entries.put("plugin1", bucket); | |||
bucket = new HashSet<>(); | |||
bucket.add("org.pf4j.test.TestExtension"); | |||
entries.put("plugin2", bucket); | |||
bucket = new HashSet<>(); | |||
bucket.add(JavaFileObjectUtils.getClassName(object)); | |||
entries.put("plugin3", bucket); | |||
return entries; | |||
} | |||
@@ -235,73 +238,40 @@ public class AbstractExtensionFinderTest { | |||
}; | |||
List<ExtensionWrapper> plugin1Result = instance.find("plugin1"); | |||
assertEquals(2, plugin1Result.size()); | |||
assertEquals(1, plugin1Result.size()); | |||
List<ExtensionWrapper> plugin2Result = instance.find("plugin2"); | |||
assertEquals(0, plugin2Result.size()); | |||
List<ExtensionWrapper> plugin3Result = instance.find(UUID.randomUUID().toString()); | |||
assertEquals(0, plugin3Result.size()); | |||
List<ExtensionWrapper> plugin3Result = instance.find("plugin3"); | |||
assertEquals(1, plugin3Result.size()); | |||
List<ExtensionWrapper> plugin4Result = instance.find(UUID.randomUUID().toString()); | |||
assertEquals(0, plugin4Result.size()); | |||
} | |||
@Test | |||
public void findExtensionAnnotation() throws Exception { | |||
Compilation compilation = javac().compile(ExtensionAnnotationProcessorTest.Greeting, | |||
ExtensionAnnotationProcessorTest.WhazzupGreeting); | |||
assertThat(compilation).succeededWithoutWarnings(); | |||
ImmutableList<JavaFileObject> generatedFiles = compilation.generatedFiles(); | |||
public void findExtensionAnnotation() { | |||
List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.Greeting, JavaSources.WhazzupGreeting); | |||
assertEquals(2, generatedFiles.size()); | |||
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader(); | |||
Map<String, Class<?>> loadedClasses = classLoader.loadClasses(new ArrayList<>(generatedFiles)); | |||
Map<String, Class<?>> loadedClasses = new JavaFileObjectClassLoader().load(generatedFiles); | |||
Class<?> clazz = loadedClasses.get("test.WhazzupGreeting"); | |||
Extension extension = AbstractExtensionFinder.findExtensionAnnotation(clazz); | |||
assertNotNull(extension); | |||
Assertions.assertNotNull(extension); | |||
} | |||
@Test | |||
public void findExtensionAnnotationThatMissing() throws Exception { | |||
Compilation compilation = javac().compile(ExtensionAnnotationProcessorTest.Greeting, | |||
public void findExtensionAnnotationThatMissing() { | |||
List<JavaFileObject> generatedFiles = JavaSources.compileAll(JavaSources.Greeting, | |||
ExtensionAnnotationProcessorTest.SpinnakerExtension_NoExtension, | |||
ExtensionAnnotationProcessorTest.WhazzupGreeting_SpinnakerExtension); | |||
assertThat(compilation).succeededWithoutWarnings(); | |||
ImmutableList<JavaFileObject> generatedFiles = compilation.generatedFiles(); | |||
assertEquals(3, generatedFiles.size()); | |||
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader(); | |||
Map<String, Class<?>> loadedClasses = classLoader.loadClasses(new ArrayList<>(generatedFiles)); | |||
Map<String, Class<?>> loadedClasses = new JavaFileObjectClassLoader().load(generatedFiles); | |||
Class<?> clazz = loadedClasses.get("test.WhazzupGreeting"); | |||
Extension extension = AbstractExtensionFinder.findExtensionAnnotation(clazz); | |||
assertNull(extension); | |||
} | |||
static class JavaFileObjectClassLoader extends ClassLoader { | |||
public Map<String, Class<?>> loadClasses(List<JavaFileObject> classes) throws IOException { | |||
// Sort generated ".class" by lastModified field | |||
classes.sort(Comparator.comparingLong(JavaFileObject::getLastModified)); | |||
// Load classes | |||
Map<String, Class<?>> loadedClasses = new HashMap<>(classes.size()); | |||
for (JavaFileObject clazz : classes) { | |||
String className = getClassName(clazz); | |||
byte[] data = ByteStreams.toByteArray(clazz.openInputStream()); | |||
Class<?> loadedClass = defineClass(className, data,0, data.length); | |||
loadedClasses.put(className, loadedClass); | |||
} | |||
return loadedClasses; | |||
} | |||
private static String getClassName(JavaFileObject object) { | |||
String name = object.getName(); | |||
// Remove "/CLASS_OUT/" from head and ".class" from tail | |||
name = name.substring(14, name.length() - 6); | |||
name = name.replace('/', '.'); | |||
return name; | |||
} | |||
Assertions.assertNull(extension); | |||
} | |||
} |
@@ -15,12 +15,16 @@ | |||
*/ | |||
package org.pf4j; | |||
import com.google.testing.compile.JavaFileObjects; | |||
import org.junit.jupiter.api.AfterEach; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.test.FailTestExtension; | |||
import org.pf4j.test.JavaFileObjectClassLoader; | |||
import org.pf4j.test.JavaSources; | |||
import org.pf4j.test.TestExtension; | |||
import javax.tools.JavaFileObject; | |||
import static org.junit.jupiter.api.Assertions.assertNotNull; | |||
import static org.junit.jupiter.api.Assertions.assertThrows; | |||
@@ -29,6 +33,19 @@ import static org.junit.jupiter.api.Assertions.assertThrows; | |||
*/ | |||
public class DefaultExtensionFactoryTest { | |||
public static final JavaFileObject FailTestExtension = JavaFileObjects.forSourceLines("FailTestExtension", | |||
"package test;", | |||
"import org.pf4j.test.TestExtensionPoint;", | |||
"import org.pf4j.Extension;", | |||
"", | |||
"@Extension", | |||
"public class FailTestExtension implements TestExtensionPoint {", | |||
" public FailTestExtension(String name) {}", | |||
"", | |||
" @Override", | |||
" public String saySomething() { return \"I am a fail test extension\";}", | |||
"}"); | |||
private ExtensionFactory extensionFactory; | |||
@BeforeEach | |||
@@ -54,7 +71,10 @@ public class DefaultExtensionFactoryTest { | |||
*/ | |||
@Test | |||
public void testCreateFailConstructor() { | |||
assertThrows(PluginRuntimeException.class, () -> extensionFactory.create(FailTestExtension.class)); | |||
JavaFileObject object = JavaSources.compile(FailTestExtension); | |||
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader(); | |||
Class<?> extensionClass = (Class<?>) classLoader.load(object).values().toArray()[0]; | |||
assertThrows(PluginRuntimeException.class, () -> extensionFactory.create(extensionClass)); | |||
} | |||
} |
@@ -15,14 +15,18 @@ | |||
*/ | |||
package org.pf4j; | |||
import com.google.testing.compile.JavaFileObjects; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.test.AnotherFailTestPlugin; | |||
import org.pf4j.test.AnotherTestPlugin; | |||
import org.pf4j.test.FailTestPlugin; | |||
import org.pf4j.test.JavaFileObjectClassLoader; | |||
import org.pf4j.test.JavaFileObjectUtils; | |||
import org.pf4j.test.JavaSources; | |||
import org.pf4j.test.TestPlugin; | |||
import javax.tools.JavaFileObject; | |||
import static org.hamcrest.CoreMatchers.instanceOf; | |||
import static org.hamcrest.MatcherAssert.assertThat; | |||
import static org.junit.jupiter.api.Assertions.assertEquals; | |||
import static org.junit.jupiter.api.Assertions.assertNotNull; | |||
import static org.junit.jupiter.api.Assertions.assertNull; | |||
import static org.mockito.Mockito.mock; | |||
@@ -33,6 +37,29 @@ import static org.mockito.Mockito.when; | |||
*/ | |||
public class DefaultPluginFactoryTest { | |||
public static final JavaFileObject FailTestPlugin = JavaFileObjects.forSourceLines("FailTestPlugin", | |||
"package test;", | |||
"import org.pf4j.Plugin;", | |||
"", | |||
"public class FailTestPlugin {", | |||
"}"); | |||
public static final JavaFileObject AnotherFailTestPlugin = JavaFileObjects.forSourceLines("AnotherFailTestPlugin", | |||
"package test;", | |||
"import org.pf4j.Plugin;", | |||
"", | |||
"public class AnotherFailTestPlugin extends Plugin {", | |||
" public AnotherFailTestPlugin() { super(null); }", | |||
"}"); | |||
public static final JavaFileObject AnotherTestPlugin = JavaFileObjects.forSourceLines("AnotherTestPlugin", | |||
"package test;", | |||
"import org.pf4j.Plugin;", | |||
"", | |||
"public class AnotherTestPlugin extends Plugin {", | |||
" public AnotherTestPlugin() { super(); }", | |||
"}"); | |||
@Test | |||
public void testCreate() { | |||
PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); | |||
@@ -52,27 +79,35 @@ public class DefaultPluginFactoryTest { | |||
@Test | |||
public void pluginConstructorNoParameters() { | |||
PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); | |||
when(pluginDescriptor.getPluginClass()).thenReturn(AnotherTestPlugin.class.getName()); | |||
JavaFileObject object = JavaSources.compile(AnotherTestPlugin); | |||
String pluginClassName = JavaFileObjectUtils.getClassName(object); | |||
when(pluginDescriptor.getPluginClass()).thenReturn(pluginClassName); | |||
PluginWrapper pluginWrapper = mock(PluginWrapper.class); | |||
when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); | |||
when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); | |||
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader(); | |||
classLoader.load(AnotherTestPlugin); | |||
when(pluginWrapper.getPluginClassLoader()).thenReturn(classLoader); | |||
PluginFactory pluginFactory = new DefaultPluginFactory(); | |||
Plugin result = pluginFactory.create(pluginWrapper); | |||
assertNotNull(result); | |||
assertThat(result, instanceOf(AnotherTestPlugin.class)); | |||
assertEquals(pluginClassName, result.getClass().getName()); | |||
} | |||
@Test | |||
public void testCreateFail() { | |||
PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); | |||
when(pluginDescriptor.getPluginClass()).thenReturn(FailTestPlugin.class.getName()); | |||
JavaFileObject object = JavaSources.compile(FailTestPlugin); | |||
String pluginClassName = JavaFileObjectUtils.getClassName(object); | |||
when(pluginDescriptor.getPluginClass()).thenReturn(pluginClassName); | |||
PluginWrapper pluginWrapper = mock(PluginWrapper.class); | |||
when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); | |||
when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); | |||
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader(); | |||
classLoader.load(FailTestPlugin); | |||
when(pluginWrapper.getPluginClassLoader()).thenReturn(classLoader); | |||
PluginFactory pluginFactory = new DefaultPluginFactory(); | |||
@@ -98,11 +133,15 @@ public class DefaultPluginFactoryTest { | |||
@Test | |||
public void testCreateFailConstructor() { | |||
PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); | |||
when(pluginDescriptor.getPluginClass()).thenReturn(AnotherFailTestPlugin.class.getName()); | |||
JavaFileObject object = JavaSources.compile(AnotherFailTestPlugin); | |||
String pluginClassName = JavaFileObjectUtils.getClassName(object); | |||
when(pluginDescriptor.getPluginClass()).thenReturn(pluginClassName); | |||
PluginWrapper pluginWrapper = mock(PluginWrapper.class); | |||
when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); | |||
when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); | |||
JavaFileObjectClassLoader classLoader = new JavaFileObjectClassLoader(); | |||
classLoader.load(AnotherFailTestPlugin); | |||
when(pluginWrapper.getPluginClassLoader()).thenReturn(classLoader); | |||
PluginFactory pluginFactory = new DefaultPluginFactory(); | |||
@@ -16,12 +16,16 @@ | |||
package org.pf4j; | |||
import com.google.testing.compile.Compilation; | |||
import com.google.testing.compile.Compiler; | |||
import com.google.testing.compile.JavaFileObjects; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.processor.ExtensionAnnotationProcessor; | |||
import org.pf4j.processor.LegacyExtensionStorage; | |||
import org.pf4j.test.JavaSources; | |||
import javax.tools.JavaFileObject; | |||
import java.io.IOException; | |||
import java.util.Collections; | |||
import java.util.HashMap; | |||
import java.util.HashSet; | |||
@@ -38,30 +42,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; | |||
*/ | |||
public class ExtensionAnnotationProcessorTest { | |||
public static final JavaFileObject Greeting = JavaFileObjects.forSourceLines( | |||
"Greeting", | |||
"package test;", | |||
"import org.pf4j.ExtensionPoint;", | |||
"", | |||
"public interface Greeting extends ExtensionPoint {", | |||
" String getGreeting();", | |||
"}"); | |||
public static final JavaFileObject WhazzupGreeting = JavaFileObjects.forSourceLines( | |||
"WhazzupGreeting", | |||
"package test;", | |||
"import org.pf4j.Extension;", | |||
"", | |||
"@Extension", | |||
"public class WhazzupGreeting implements Greeting {", | |||
" @Override", | |||
" public String getGreeting() {", | |||
" return \"Whazzup\";", | |||
" }", | |||
"}"); | |||
public static final JavaFileObject WhazzupGreeting_NoExtensionPoint = JavaFileObjects.forSourceLines( | |||
"WhazzupGreeting", | |||
public static final JavaFileObject WhazzupGreeting_NoExtensionPoint = JavaFileObjects.forSourceLines("WhazzupGreeting", | |||
"package test;", | |||
"import org.pf4j.Extension;", | |||
"", | |||
@@ -73,8 +54,7 @@ public class ExtensionAnnotationProcessorTest { | |||
" }", | |||
"}"); | |||
public static final JavaFileObject SpinnakerExtension = JavaFileObjects.forSourceLines( | |||
"SpinnakerExtension", | |||
public static final JavaFileObject SpinnakerExtension = JavaFileObjects.forSourceLines("SpinnakerExtension", | |||
"package test;", | |||
"", | |||
"import org.pf4j.Extension;", | |||
@@ -91,8 +71,7 @@ public class ExtensionAnnotationProcessorTest { | |||
"public @interface SpinnakerExtension {", | |||
"}"); | |||
public static final JavaFileObject WhazzupGreeting_SpinnakerExtension = JavaFileObjects.forSourceLines( | |||
"WhazzupGreeting", | |||
public static final JavaFileObject WhazzupGreeting_SpinnakerExtension = JavaFileObjects.forSourceLines("WhazzupGreeting", | |||
"package test;", | |||
"", | |||
"@SpinnakerExtension", | |||
@@ -104,10 +83,9 @@ public class ExtensionAnnotationProcessorTest { | |||
"}"); | |||
/** | |||
* The same like {@link #SpinnakerExtension} but without {@code Extension} annotation. | |||
* The same as {@link #SpinnakerExtension} but without {@code Extension} annotation. | |||
*/ | |||
public static final JavaFileObject SpinnakerExtension_NoExtension = JavaFileObjects.forSourceLines( | |||
"SpinnakerExtension", | |||
public static final JavaFileObject SpinnakerExtension_NoExtension = JavaFileObjects.forSourceLines("SpinnakerExtension", | |||
"package test;", | |||
"", | |||
"import org.pf4j.Extension;", | |||
@@ -124,52 +102,53 @@ public class ExtensionAnnotationProcessorTest { | |||
"public @interface SpinnakerExtension {", | |||
"}"); | |||
private ExtensionAnnotationProcessor annotationProcessor; | |||
@BeforeEach | |||
public void setUp() throws IOException { | |||
annotationProcessor = new ExtensionAnnotationProcessor(); | |||
} | |||
@Test | |||
public void getSupportedAnnotationTypes() { | |||
ExtensionAnnotationProcessor instance = new ExtensionAnnotationProcessor(); | |||
Set<String> result = instance.getSupportedAnnotationTypes(); | |||
Set<String> result = annotationProcessor.getSupportedAnnotationTypes(); | |||
assertEquals(1, result.size()); | |||
assertEquals("*", result.iterator().next()); | |||
} | |||
@Test | |||
public void getSupportedOptions() { | |||
ExtensionAnnotationProcessor instance = new ExtensionAnnotationProcessor(); | |||
Set<String> result = instance.getSupportedOptions(); | |||
Set<String> result = annotationProcessor.getSupportedOptions(); | |||
assertEquals(2, result.size()); | |||
} | |||
@Test | |||
public void options() { | |||
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor(); | |||
Compilation compilation = javac().withProcessors(processor).withOptions("-Ab=2", "-Ac=3") | |||
.compile(Greeting, WhazzupGreeting); | |||
assertEquals(compilation.status(), Compilation.Status.SUCCESS); | |||
Compilation compilation = compiler().withOptions("-Ab=2", "-Ac=3") | |||
.compile(JavaSources.Greeting, JavaSources.WhazzupGreeting); | |||
assertEquals(Compilation.Status.SUCCESS, compilation.status()); | |||
Map<String, String> options = new HashMap<>(); | |||
options.put("b", "2"); | |||
options.put("c", "3"); | |||
assertEquals(options, processor.getProcessingEnvironment().getOptions()); | |||
assertEquals(options, annotationProcessor.getProcessingEnvironment().getOptions()); | |||
} | |||
@Test | |||
public void storage() { | |||
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor(); | |||
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting); | |||
assertEquals(compilation.status(), Compilation.Status.SUCCESS); | |||
assertEquals(processor.getStorage().getClass(), LegacyExtensionStorage.class); | |||
Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting); | |||
assertEquals(Compilation.Status.SUCCESS, compilation.status()); | |||
assertEquals(annotationProcessor.getStorage().getClass(), LegacyExtensionStorage.class); | |||
} | |||
@Test | |||
public void compileWithoutError() { | |||
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor(); | |||
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting); | |||
Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting); | |||
assertThat(compilation).succeededWithoutWarnings(); | |||
} | |||
@Test | |||
public void compileWithError() { | |||
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor(); | |||
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting_NoExtensionPoint); | |||
Compilation compilation = compile(JavaSources.Greeting, WhazzupGreeting_NoExtensionPoint); | |||
assertThat(compilation).failed(); | |||
assertThat(compilation).hadErrorContaining("it doesn't implement ExtensionPoint") | |||
.inFile(WhazzupGreeting_NoExtensionPoint) | |||
@@ -179,22 +158,28 @@ public class ExtensionAnnotationProcessorTest { | |||
@Test | |||
public void getExtensions() { | |||
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor(); | |||
Compilation compilation = javac().withProcessors(processor).compile(Greeting, WhazzupGreeting); | |||
Compilation compilation = compile(JavaSources.Greeting, JavaSources.WhazzupGreeting); | |||
assertThat(compilation).succeededWithoutWarnings(); | |||
Map<String, Set<String>> extensions = new HashMap<>(); | |||
extensions.put("test.Greeting", new HashSet<>(Collections.singletonList("test.WhazzupGreeting"))); | |||
assertEquals(extensions, processor.getExtensions()); | |||
assertEquals(extensions, annotationProcessor.getExtensions()); | |||
} | |||
@Test | |||
public void compileNestedExtensionAnnotation() { | |||
ExtensionAnnotationProcessor processor = new ExtensionAnnotationProcessor(); | |||
Compilation compilation = javac().withProcessors(processor).compile(Greeting, SpinnakerExtension, WhazzupGreeting_SpinnakerExtension); | |||
Compilation compilation = compile(JavaSources.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()); | |||
assertEquals(extensions, annotationProcessor.getExtensions()); | |||
} | |||
private Compiler compiler() { | |||
return javac().withProcessors(annotationProcessor); | |||
} | |||
private Compilation compile(JavaFileObject... sources) { | |||
return compiler().compile(sources); | |||
} | |||
} |
@@ -18,7 +18,6 @@ package org.pf4j; | |||
import org.junit.jupiter.api.AfterEach; | |||
import org.junit.jupiter.api.BeforeEach; | |||
import org.junit.jupiter.api.Test; | |||
import org.pf4j.test.FailTestExtension; | |||
import org.pf4j.test.TestExtension; | |||
import java.io.File; | |||
@@ -57,7 +56,7 @@ public class SingletonExtensionFactoryTest { | |||
@Test | |||
public void createNewEachTime() { | |||
ExtensionFactory extensionFactory = new SingletonExtensionFactory(pluginManager, FailTestExtension.class.getName()); | |||
ExtensionFactory extensionFactory = new SingletonExtensionFactory(pluginManager, "FailTestExtension.class"); | |||
Object extensionOne = extensionFactory.create(TestExtension.class); | |||
Object extensionTwo = extensionFactory.create(TestExtension.class); | |||
assertNotSame(extensionOne, extensionTwo); |
@@ -1,32 +0,0 @@ | |||
/* | |||
* Copyright (C) 2012-present the original author or authors. | |||
* | |||
* Licensed 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.pf4j.test; | |||
import org.pf4j.Plugin; | |||
/** | |||
* A wrong {@link org.pf4j.Plugin}. | |||
* It's wrong because it calls super constructor with {@code null} for ({@link org.pf4j.PluginWrapper} parameter). | |||
* | |||
* @author Mario Franco | |||
*/ | |||
public class AnotherFailTestPlugin extends Plugin { | |||
public AnotherFailTestPlugin() { | |||
super(null); | |||
} | |||
} |
@@ -1,34 +0,0 @@ | |||
/* | |||
* Copyright (C) 2012-present the original author or authors. | |||
* | |||
* Licensed 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.pf4j.test; | |||
import org.pf4j.Plugin; | |||
/** | |||
* A simple {@link Plugin}. | |||
* | |||
* In real applications you don't need to create a plugin like this if you are not interested in lifecycle events. | |||
* {@code PF4J} will automatically create a plugin similar to this (empty / dummy) if no class plugin is specified. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class AnotherTestPlugin extends Plugin { | |||
public AnotherTestPlugin() { | |||
super(); | |||
} | |||
} |
@@ -15,10 +15,10 @@ | |||
*/ | |||
package org.pf4j.test; | |||
import java.io.ByteArrayOutputStream; | |||
import com.google.common.io.ByteStreams; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
/** | |||
* Get class data from the class path. | |||
@@ -35,20 +35,10 @@ public class DefaultClassDataProvider implements ClassDataProvider { | |||
throw new RuntimeException("Cannot find class data"); | |||
} | |||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { | |||
copyStream(classDataStream, outputStream); | |||
return outputStream.toByteArray(); | |||
try { | |||
return ByteStreams.toByteArray(classDataStream); | |||
} catch (IOException e) { | |||
throw new RuntimeException(e.getMessage(), e); | |||
} | |||
} | |||
private void copyStream(InputStream in, OutputStream out) throws IOException { | |||
byte[] buffer = new byte[1024]; | |||
int bytesRead; | |||
while ((bytesRead = in.read(buffer)) != -1) { | |||
out.write(buffer, 0, bytesRead); | |||
throw new IllegalStateException(e); | |||
} | |||
} | |||
@@ -1,37 +0,0 @@ | |||
/* | |||
* Copyright (C) 2012-present the original author or authors. | |||
* | |||
* Licensed 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.pf4j.test; | |||
import org.pf4j.Extension; | |||
/** | |||
* A wrong {@link org.pf4j.Extension}. | |||
* It's wrong because it doesn't contain a constructor with empty parameters (or only the default constructor). | |||
* | |||
* @author Mario Franco | |||
*/ | |||
@Extension | |||
public class FailTestExtension implements TestExtensionPoint { | |||
public FailTestExtension(String name) { | |||
} | |||
@Override | |||
public String saySomething() { | |||
return "I am a fail test extension"; | |||
} | |||
} |
@@ -1,26 +0,0 @@ | |||
/* | |||
* Copyright (C) 2012-present the original author or authors. | |||
* | |||
* Licensed 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.pf4j.test; | |||
/** | |||
* A wrong {@link org.pf4j.Plugin}. | |||
* It's wrong because it doesn't extends {@link org.pf4j.Plugin}. | |||
* | |||
* @author Mario Franco | |||
*/ | |||
public class FailTestPlugin { | |||
} |
@@ -0,0 +1,73 @@ | |||
/* | |||
* Copyright (C) 2012-present the original author or authors. | |||
* | |||
* Licensed 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.pf4j.test; | |||
import javax.tools.JavaFileObject; | |||
import java.util.ArrayList; | |||
import java.util.Arrays; | |||
import java.util.Comparator; | |||
import java.util.HashMap; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.Objects; | |||
/** | |||
* {@link ClassLoader} that loads {@link JavaFileObject.Kind#CLASS}s. | |||
* If {@code JavaFileObject} type is {@link JavaFileObject.Kind#SOURCE} them the source is compiled. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class JavaFileObjectClassLoader extends ClassLoader { | |||
public Map<String, Class<?>> load(JavaFileObject... objects) { | |||
return load(Arrays.asList(objects)); | |||
} | |||
public Map<String, Class<?>> load(List<JavaFileObject> objects) { | |||
Objects.requireNonNull(objects); | |||
List<JavaFileObject> mutableObjects = new ArrayList<>(objects); | |||
// Sort generated ".class" by lastModified field | |||
mutableObjects.sort(Comparator.comparingLong(JavaFileObject::getLastModified)); | |||
// Compile Java sources (if exists) | |||
for (int i = 0; i < mutableObjects.size(); i++) { | |||
JavaFileObject object = mutableObjects.get(i); | |||
if (object.getKind() == JavaFileObject.Kind.CLASS) { | |||
continue; | |||
} | |||
if (object.getKind() == JavaFileObject.Kind.SOURCE) { | |||
mutableObjects.set(i, JavaSources.compile(object)); | |||
} else { | |||
throw new IllegalStateException("Type " + object.getKind() + " is not supported"); | |||
} | |||
} | |||
// Load objects | |||
Map<String, Class<?>> loadedClasses = new HashMap<>(); | |||
for (JavaFileObject object : mutableObjects) { | |||
String className = JavaFileObjectUtils.getClassName(object); | |||
byte[] data = JavaFileObjectUtils.getAllBytes(object); | |||
Class<?> loadedClass = defineClass(className, data, 0, data.length); | |||
loadedClasses.put(className, loadedClass); | |||
} | |||
return loadedClasses; | |||
} | |||
} |
@@ -0,0 +1,62 @@ | |||
/* | |||
* Copyright (C) 2012-present the original author or authors. | |||
* | |||
* Licensed 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.pf4j.test; | |||
import javax.tools.FileObject; | |||
import javax.tools.JavaFileObject; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Map; | |||
import java.util.stream.Collectors; | |||
/** | |||
* Get class data from {@link JavaFileObject}. | |||
* If {@code JavaFileObject} type is {@link JavaFileObject.Kind#SOURCE} them the source is compiled. | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class JavaFileObjectDataProvider implements ClassDataProvider { | |||
private final Map<String, JavaFileObject> classes; | |||
public JavaFileObjectDataProvider(Map<String, JavaFileObject> classes) { | |||
this.classes = classes; | |||
} | |||
public static JavaFileObjectDataProvider of(List<JavaFileObject> objects) { | |||
List<JavaFileObject> tmp = new ArrayList<>(objects.size()); | |||
for (JavaFileObject object : objects) { | |||
if (object.getKind() == JavaFileObject.Kind.CLASS) { | |||
tmp.add(object); | |||
} else if (object.getKind() == JavaFileObject.Kind.SOURCE) { | |||
tmp.add(JavaSources.compile(object)); | |||
} else { | |||
throw new IllegalStateException("Type " + object.getKind() + " is not supported"); | |||
} | |||
} | |||
// TODO JavaFileObjectUtils.getClassName() ?! | |||
Map<String, JavaFileObject> classes = tmp.stream().collect(Collectors.toMap(FileObject::getName, c -> c)); | |||
return new JavaFileObjectDataProvider(classes); | |||
} | |||
@Override | |||
public byte[] getClassData(String className) { | |||
return JavaFileObjectUtils.getAllBytes(classes.get(className)); | |||
} | |||
} |
@@ -0,0 +1,52 @@ | |||
/* | |||
* Copyright (C) 2012-present the original author or authors. | |||
* | |||
* Licensed 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.pf4j.test; | |||
import com.google.common.io.ByteStreams; | |||
import javax.tools.JavaFileObject; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
/** | |||
* @author Decebal Suiu | |||
*/ | |||
public class JavaFileObjectUtils { | |||
private JavaFileObjectUtils() {} | |||
public static String getClassName(JavaFileObject object) { | |||
if (object.getKind() != JavaFileObject.Kind.CLASS) { | |||
throw new IllegalStateException("Only Kind.CLASS is supported"); | |||
} | |||
String name = object.getName(); | |||
// Remove "/CLASS_OUT/" from head and ".class" from tail | |||
name = name.substring(14, name.length() - 6); | |||
name = name.replace('/', '.'); | |||
return name; | |||
} | |||
public static byte[] getAllBytes(JavaFileObject object) { | |||
try (InputStream in = object.openInputStream()) { | |||
return ByteStreams.toByteArray(in); | |||
} catch (IOException e) { | |||
throw new IllegalStateException(e); | |||
} | |||
} | |||
} |
@@ -0,0 +1,64 @@ | |||
/* | |||
* Copyright (C) 2012-present the original author or authors. | |||
* | |||
* Licensed 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.pf4j.test; | |||
import com.google.testing.compile.JavaFileObjects; | |||
import javax.tools.JavaFileObject; | |||
import java.util.List; | |||
import static com.google.testing.compile.Compiler.javac; | |||
/** | |||
* Keep common Java sources (useful in many tests). | |||
* For Java 13+ is recommended to use Text Block feature (it's more clear). | |||
* | |||
* @author Decebal Suiu | |||
*/ | |||
public class JavaSources { | |||
public static final JavaFileObject Greeting = JavaFileObjects.forSourceLines("Greeting", | |||
"package test;", | |||
"import org.pf4j.ExtensionPoint;", | |||
"", | |||
"public interface Greeting extends ExtensionPoint {", | |||
" String getGreeting();", | |||
"}"); | |||
public static final JavaFileObject WhazzupGreeting = JavaFileObjects.forSourceLines("WhazzupGreeting", | |||
"package test;", | |||
"import org.pf4j.Extension;", | |||
"", | |||
"@Extension", | |||
"public class WhazzupGreeting implements Greeting {", | |||
" @Override", | |||
" public String getGreeting() {", | |||
" return \"Whazzup\";", | |||
" }", | |||
"}"); | |||
/** | |||
* Compile a list of sources using javac compiler. | |||
*/ | |||
public static List<JavaFileObject> compileAll(JavaFileObject... sources) { | |||
return javac().compile(sources).generatedFiles(); | |||
} | |||
public static JavaFileObject compile(JavaFileObject source) { | |||
return compileAll(source).get(0); | |||
} | |||
} |
@@ -20,9 +20,8 @@ import org.pf4j.PluginWrapper; | |||
/** | |||
* A simple {@link Plugin}. | |||
* | |||
* In real applications you don't need to create a plugin like this if you are not interested in lifecycle events. | |||
* {@codes PF4J} will automatically create a plugin similar to this (empty / dummy) if no class plugin is specified. | |||
* {@code PF4J} will automatically create a plugin similar to this (empty / dummy) if no class plugin is specified. | |||
* | |||
* @author Mario Franco | |||
*/ |