<version>${mockito.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-inline</artifactId>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>com.google.testing.compile</groupId>
<artifactId>compile-testing</artifactId>
final Set<String> bucket = new HashSet<>();
try {
- Enumeration<URL> urls = getClass().getClassLoader().getResources(EXTENSIONS_RESOURCE);
+ Enumeration<URL> urls = getExtensionResource(getClass().getClassLoader());
if (urls.hasMoreElements()) {
collectExtensions(urls, bucket);
} else {
List<PluginWrapper> plugins = pluginManager.getPlugins();
for (PluginWrapper plugin : plugins) {
- String pluginId = plugin.getDescriptor().getPluginId();
+ String pluginId = plugin.getPluginId();
log.debug("Reading extensions storages for plugin '{}'", pluginId);
final Set<String> bucket = new HashSet<>();
try {
- Enumeration<URL> urls = ((PluginClassLoader) plugin.getPluginClassLoader()).findResources(EXTENSIONS_RESOURCE);
+ Enumeration<URL> urls = findExtensionResource((PluginClassLoader) plugin.getPluginClassLoader());
if (urls.hasMoreElements()) {
collectExtensions(urls, bucket);
} else {
return result;
}
+ Enumeration<URL> getExtensionResource(ClassLoader classLoader) throws IOException {
+ return classLoader.getResources(EXTENSIONS_RESOURCE);
+ }
+
+ Enumeration<URL> findExtensionResource(PluginClassLoader classLoader) throws IOException {
+ return classLoader.findResources(EXTENSIONS_RESOURCE);
+ }
+
private void collectExtensions(Enumeration<URL> urls, Set<String> bucket) throws URISyntaxException, IOException {
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
List<String> plugins = new ArrayList<>();
List<String> points = new ArrayList<>();
- private ExtensionInfo(String className) {
+ ExtensionInfo(String className) {
this.className = className;
}
--- /dev/null
+/*
+ * 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;
+
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class LoggingPluginStateListenerTest {
+
+ @Test
+ void pluginStateChangedShouldLogStateChange() {
+ Logger mockedLogger = mock(Logger.class);
+
+ try (MockedStatic<LoggerFactory> context = Mockito.mockStatic(LoggerFactory.class)) {
+ context.when(() -> LoggerFactory.getLogger(Mockito.any(Class.class)))
+ .thenReturn(mockedLogger);
+
+ // create a PluginStateEvent
+ PluginManager pluginManager = mock(PluginManager.class);
+ PluginWrapper pluginWrapper = mock(PluginWrapper.class);
+ when(pluginWrapper.getPluginId()).thenReturn("testPlugin");
+ when(pluginWrapper.getPluginState()).thenReturn(PluginState.STARTED);
+ PluginStateEvent event = new PluginStateEvent(pluginManager, pluginWrapper, PluginState.CREATED);
+
+ // call the method under test
+ LoggingPluginStateListener listener = new LoggingPluginStateListener();
+ listener.pluginStateChanged(event);
+
+ // verify that the logger was called with the expected message
+ verify(mockedLogger).debug("The state of plugin '{}' has changed from '{}' to '{}'", "testPlugin", PluginState.CREATED, PluginState.STARTED);
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+class ServiceProviderExtensionFinderTest {
+
+ private static final String GREETER_EXTENSION_POINT = "org.pf4j.demo.api.Greeting";
+ private static final String HELLO_GREETER_EXTENSION = "org.pf4j.demo.hello.HelloGreeting";
+ private static final String WELCOME_GREETER_EXTENSION = "org.pf4j.demo.welcome.WelcomeGreeting";
+
+ @TempDir
+ public Path tempDir;
+
+ @Test
+ void readClasspathStorages() {
+ PluginManager pluginManager = mock(PluginManager.class);
+ ServiceProviderExtensionFinder finder = new ServiceProviderExtensionFinder(pluginManager) {
+
+ @Override
+ Enumeration<URL> getExtensionResource(ClassLoader classLoader) throws IOException {
+ return getExtensionEnumeration();
+ }
+
+ };
+
+ Map<String, Set<String>> storages = finder.readClasspathStorages();
+ assertNotNull(storages);
+ assertTrue(storages.containsKey(null));
+ Set<String> extensions = storages.get(null);
+ assertEquals(2, extensions.size());
+ assertThat(extensions, containsInAnyOrder(HELLO_GREETER_EXTENSION, WELCOME_GREETER_EXTENSION));
+ }
+
+
+ @Test
+ void readPluginsStorages() {
+ String pluginId = "testPlugin";
+ PluginWrapper pluginWrapper = mock(PluginWrapper.class);
+ when(pluginWrapper.getPluginId()).thenReturn(pluginId);
+ when(pluginWrapper.getPluginClassLoader()).thenReturn(null); // not needed for this test
+
+ PluginManager pluginManager = mock(PluginManager.class);
+ when(pluginManager.getPlugins()).thenReturn(Collections.singletonList(pluginWrapper));
+ ServiceProviderExtensionFinder finder = new ServiceProviderExtensionFinder(pluginManager) {
+
+ @Override
+ Enumeration<URL> findExtensionResource(PluginClassLoader classLoader) throws IOException {
+ return getExtensionEnumeration();
+ }
+
+ };
+
+ Map<String, Set<String>> storages = finder.readPluginsStorages();
+ assertNotNull(storages);
+ assertTrue(storages.containsKey(pluginId));
+ Set<String> extensions = storages.get(pluginId);
+ assertEquals(2, extensions.size());
+ assertThat(extensions, containsInAnyOrder(HELLO_GREETER_EXTENSION, WELCOME_GREETER_EXTENSION));
+ }
+
+ private Enumeration<URL> getExtensionEnumeration() throws IOException {
+ Path servicesPath = tempDir.resolve("META-INF/services");
+ servicesPath.toFile().mkdirs();
+
+ Path greetingPath = servicesPath.resolve(GREETER_EXTENSION_POINT);
+ List<String> extensions = new ArrayList<>();
+ extensions.add(HELLO_GREETER_EXTENSION);
+ extensions.add(WELCOME_GREETER_EXTENSION);
+ Files.write(greetingPath, extensions, StandardCharsets.UTF_8);
+
+ return Collections.enumeration(Collections.singletonList(servicesPath.toUri().toURL()));
+ }
+
+}
--- /dev/null
+/*
+ * 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.asm;
+
+import org.junit.jupiter.api.Test;
+
+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.junit.jupiter.api.Assertions.assertTrue;
+
+class ExtensionInfoTest {
+
+ @Test
+ void loadShouldReturnExtensionInfoWhenClassExists() {
+ ExtensionInfo info = ExtensionInfo.load("org.pf4j.asm.ExtensionInfo", this.getClass().getClassLoader());
+ assertNotNull(info);
+ assertEquals("org.pf4j.asm.ExtensionInfo", info.getClassName());
+ }
+
+ @Test
+ void loadShouldReturnNullWhenClassDoesNotExist() {
+ ExtensionInfo info = ExtensionInfo.load("non.existent.Class", this.getClass().getClassLoader());
+ assertNull(info);
+ }
+
+ @Test
+ void getClassNameShouldReturnCorrectName() {
+ ExtensionInfo info = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ assertEquals("org.pf4j.asm.ExtensionInfo", info.getClassName());
+ }
+
+ @Test
+ void getOrdinalShouldReturnZeroWhenNotSet() {
+ ExtensionInfo info = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ assertEquals(0, info.getOrdinal());
+ }
+
+ @Test
+ void getPluginsShouldReturnEmptyListWhenNotSet() {
+ ExtensionInfo info = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ assertTrue(info.getPlugins().isEmpty());
+ }
+
+ @Test
+ void getPointsShouldReturnEmptyListWhenNotSet() {
+ ExtensionInfo info = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ assertTrue(info.getPoints().isEmpty());
+ }
+
+}
--- /dev/null
+/*
+ * 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.asm;
+
+import org.junit.jupiter.api.Test;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Type;
+
+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.junit.jupiter.api.Assertions.assertTrue;
+
+class ExtensionVisitorTest {
+
+ @Test
+ void visitAnnotationShouldReturnExtensionAnnotationVisitor() {
+ ExtensionInfo extensionInfo = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ ClassVisitor extensionVisitor = new ExtensionVisitor(extensionInfo);
+
+ AnnotationVisitor returnedVisitor = extensionVisitor.visitAnnotation("Lorg/pf4j/Extension;", true);
+
+ assertNotNull(returnedVisitor);
+ }
+
+ @Test
+ void visitAnnotationShouldReturnSuperVisitorForNonExtensionAnnotation() {
+ ExtensionInfo extensionInfo = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ ClassVisitor extensionVisitor = new ExtensionVisitor(extensionInfo);
+
+ AnnotationVisitor returnedVisitor = extensionVisitor.visitAnnotation("Lorg/pf4j/NonExtension;", true);
+
+ assertNull(returnedVisitor);
+ }
+
+ @Test
+ void visitArrayShouldHandleOrdinalAttribute() {
+ ExtensionInfo extensionInfo = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ ClassVisitor extensionVisitor = new ExtensionVisitor(extensionInfo);
+
+ AnnotationVisitor annotationVisitor = extensionVisitor.visitAnnotation("Lorg/pf4j/Extension;", true);
+ AnnotationVisitor arrayVisitor = annotationVisitor.visitArray("ordinal");
+
+ arrayVisitor.visit("key", 1);
+
+ assertEquals(1, extensionInfo.getOrdinal());
+ }
+
+ @Test
+ void visitArrayShouldHandlePluginsAttribute() {
+ ExtensionInfo extensionInfo = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ ClassVisitor extensionVisitor = new ExtensionVisitor(extensionInfo);
+
+ AnnotationVisitor annotationVisitor = extensionVisitor.visitAnnotation("Lorg/pf4j/Extension;", true);
+ AnnotationVisitor arrayVisitor = annotationVisitor.visitArray("plugins");
+
+ arrayVisitor.visit("key", "plugin1");
+
+ assertTrue(extensionInfo.getPlugins().contains("plugin1"));
+ }
+
+ @Test
+ void visitArrayShouldHandlePointsAttribute() {
+ ExtensionInfo extensionInfo = new ExtensionInfo("org.pf4j.asm.ExtensionInfo");
+ ClassVisitor extensionVisitor = new ExtensionVisitor(extensionInfo);
+
+ AnnotationVisitor annotationVisitor = extensionVisitor.visitAnnotation("Lorg/pf4j/Extension;", true);
+ AnnotationVisitor arrayVisitor = annotationVisitor.visitArray("points");
+
+ arrayVisitor.visit("key", Type.getType("Lorg/pf4j/Point;"));
+
+ assertTrue(extensionInfo.getPoints().contains("org.pf4j.Point"));
+ }
+
+}