From 5916ee64d9c05a4f085f6f6562ed383ff3203e4a Mon Sep 17 00:00:00 2001 From: Decebal Suiu Date: Wed, 20 Sep 2017 16:55:30 +0200 Subject: Change root package from ro.fortsoft.pf4j to org.pf4j (#168) --- .travis.yml | 5 +- README.md | 34 +- demo/api/pom.xml | 4 +- .../src/main/java/org/pf4j/demo/api/Greeting.java | 27 + .../java/ro/fortsoft/pf4j/demo/api/Greeting.java | 27 - demo/app/pom.xml | 8 +- demo/app/src/main/java/org/pf4j/demo/Boot.java | 125 +++ .../src/main/java/org/pf4j/demo/HowdyGreeting.java | 32 + .../main/java/org/pf4j/demo/WhazzupGreeting.java | 32 + .../src/main/java/ro/fortsoft/pf4j/demo/Boot.java | 125 --- .../java/ro/fortsoft/pf4j/demo/HowdyGreeting.java | 32 - .../ro/fortsoft/pf4j/demo/WhazzupGreeting.java | 32 - .../META-INF/services/org.pf4j.demo.api.Greeting | 1 + .../services/ro.fortsoft.pf4j.demo.api.Greeting | 1 - demo/app/src/main/resources/log4j.properties | 12 +- demo/plugins/plugin1/plugin.properties | 2 +- demo/plugins/plugin1/pom.xml | 4 +- .../java/org/pf4j/demo/welcome/WelcomePlugin.java | 59 ++ .../fortsoft/pf4j/demo/welcome/WelcomePlugin.java | 59 -- demo/plugins/plugin2/plugin.properties | 2 +- demo/plugins/plugin2/pom.xml | 4 +- .../main/java/org/pf4j/demo/hello/HelloPlugin.java | 54 ++ .../ro/fortsoft/pf4j/demo/hello/HelloPlugin.java | 54 -- demo/plugins/pom.xml | 6 +- demo/pom.xml | 4 +- demo_gradle/api/build.gradle | 6 +- .../src/main/java/org/pf4j/demo/api/Greeting.java | 27 + .../java/ro/fortsoft/pf4j/demo/api/Greeting.java | 27 - demo_gradle/app/build.gradle | 18 +- .../app/src/main/java/org/pf4j/demo/Boot.java | 100 +++ .../main/java/org/pf4j/demo/WhazzupGreeting.java | 32 + .../src/main/java/ro/fortsoft/pf4j/demo/Boot.java | 100 --- .../ro/fortsoft/pf4j/demo/WhazzupGreeting.java | 32 - .../app/src/main/resources/log4j.properties | 6 +- demo_gradle/build.gradle | 12 +- demo_gradle/plugins/plugin1/build.gradle | 39 +- demo_gradle/plugins/plugin1/plugin.properties | 2 +- .../java/org/pf4j/demo/welcome/WelcomePlugin.java | 58 ++ .../fortsoft/pf4j/demo/welcome/WelcomePlugin.java | 58 -- demo_gradle/plugins/plugin2/build.gradle | 47 +- demo_gradle/plugins/plugin2/plugin.properties | 2 +- .../main/java/org/pf4j/demo/hello/HelloPlugin.java | 57 ++ .../ro/fortsoft/pf4j/demo/hello/HelloPlugin.java | 57 -- demo_gradle/plugins/plugin3/build.gradle | 14 +- .../kotlin/org/pf4j/demo/kotlin/KotlinPlugin.kt | 48 ++ .../ro/fortsoft/pf4j/demo/kotlin/KotlinPlugin.kt | 48 -- demo_gradle/settings.gradle | 1 - pf4j/pom.xml | 2 +- .../java/org/pf4j/AbstractExtensionFinder.java | 247 ++++++ .../main/java/org/pf4j/AbstractPluginManager.java | 900 +++++++++++++++++++++ .../main/java/org/pf4j/BasePluginRepository.java | 79 ++ .../java/org/pf4j/CompoundPluginRepository.java | 55 ++ .../java/org/pf4j/DefaultExtensionFactory.java | 48 ++ .../main/java/org/pf4j/DefaultExtensionFinder.java | 103 +++ .../main/java/org/pf4j/DefaultPluginClasspath.java | 32 + .../org/pf4j/DefaultPluginDescriptorFinder.java | 64 ++ .../main/java/org/pf4j/DefaultPluginFactory.java | 72 ++ .../main/java/org/pf4j/DefaultPluginLoader.java | 80 ++ .../main/java/org/pf4j/DefaultPluginManager.java | 134 +++ .../java/org/pf4j/DefaultPluginRepository.java | 83 ++ .../java/org/pf4j/DefaultPluginStatusProvider.java | 98 +++ .../main/java/org/pf4j/DefaultVersionManager.java | 33 + .../src/main/java/org/pf4j/DependencyResolver.java | 309 +++++++ .../java/org/pf4j/DevelopmentPluginClasspath.java | 32 + pf4j/src/main/java/org/pf4j/Extension.java | 37 + .../main/java/org/pf4j/ExtensionDescriptor.java | 42 + pf4j/src/main/java/org/pf4j/ExtensionFactory.java | 25 + pf4j/src/main/java/org/pf4j/ExtensionFinder.java | 46 ++ pf4j/src/main/java/org/pf4j/ExtensionPoint.java | 23 + pf4j/src/main/java/org/pf4j/ExtensionWrapper.java | 59 ++ pf4j/src/main/java/org/pf4j/JarPluginManager.java | 115 +++ .../main/java/org/pf4j/LegacyExtensionFinder.java | 111 +++ .../org/pf4j/ManifestPluginDescriptorFinder.java | 82 ++ pf4j/src/main/java/org/pf4j/Plugin.java | 71 ++ pf4j/src/main/java/org/pf4j/PluginClassLoader.java | 145 ++++ pf4j/src/main/java/org/pf4j/PluginClasspath.java | 54 ++ pf4j/src/main/java/org/pf4j/PluginDependency.java | 51 ++ pf4j/src/main/java/org/pf4j/PluginDescriptor.java | 177 ++++ .../main/java/org/pf4j/PluginDescriptorFinder.java | 31 + pf4j/src/main/java/org/pf4j/PluginException.java | 51 ++ pf4j/src/main/java/org/pf4j/PluginFactory.java | 25 + pf4j/src/main/java/org/pf4j/PluginLoader.java | 29 + pf4j/src/main/java/org/pf4j/PluginManager.java | 183 +++++ pf4j/src/main/java/org/pf4j/PluginRepository.java | 44 + pf4j/src/main/java/org/pf4j/PluginState.java | 56 ++ pf4j/src/main/java/org/pf4j/PluginStateEvent.java | 60 ++ .../main/java/org/pf4j/PluginStateListener.java | 32 + .../main/java/org/pf4j/PluginStatusProvider.java | 48 ++ pf4j/src/main/java/org/pf4j/PluginWrapper.java | 147 ++++ .../org/pf4j/PropertiesPluginDescriptorFinder.java | 116 +++ pf4j/src/main/java/org/pf4j/RuntimeMode.java | 58 ++ .../org/pf4j/ServiceProviderExtensionFinder.java | 148 ++++ pf4j/src/main/java/org/pf4j/VersionManager.java | 34 + .../processor/ExtensionAnnotationProcessor.java | 232 ++++++ .../java/org/pf4j/processor/ExtensionStorage.java | 73 ++ .../org/pf4j/processor/LegacyExtensionStorage.java | 106 +++ .../processor/ServiceProviderExtensionStorage.java | 119 +++ .../src/main/java/org/pf4j/util/AndFileFilter.java | 84 ++ pf4j/src/main/java/org/pf4j/util/ClassUtils.java | 91 +++ .../src/main/java/org/pf4j/util/DirectedGraph.java | 192 +++++ .../java/org/pf4j/util/DirectoryFileFilter.java | 33 + .../java/org/pf4j/util/ExtensionFileFilter.java | 40 + pf4j/src/main/java/org/pf4j/util/FileUtils.java | 202 +++++ pf4j/src/main/java/org/pf4j/util/HiddenFilter.java | 33 + .../src/main/java/org/pf4j/util/JarFileFilter.java | 35 + .../main/java/org/pf4j/util/NameFileFilter.java | 40 + .../src/main/java/org/pf4j/util/NotFileFilter.java | 39 + pf4j/src/main/java/org/pf4j/util/OrFileFilter.java | 84 ++ pf4j/src/main/java/org/pf4j/util/StringUtils.java | 40 + pf4j/src/main/java/org/pf4j/util/Unzip.java | 102 +++ .../src/main/java/org/pf4j/util/ZipFileFilter.java | 35 + .../ro/fortsoft/pf4j/AbstractExtensionFinder.java | 247 ------ .../ro/fortsoft/pf4j/AbstractPluginManager.java | 900 --------------------- .../ro/fortsoft/pf4j/BasePluginRepository.java | 79 -- .../ro/fortsoft/pf4j/CompoundPluginRepository.java | 55 -- .../ro/fortsoft/pf4j/DefaultExtensionFactory.java | 48 -- .../ro/fortsoft/pf4j/DefaultExtensionFinder.java | 103 --- .../ro/fortsoft/pf4j/DefaultPluginClasspath.java | 32 - .../pf4j/DefaultPluginDescriptorFinder.java | 64 -- .../ro/fortsoft/pf4j/DefaultPluginFactory.java | 72 -- .../java/ro/fortsoft/pf4j/DefaultPluginLoader.java | 80 -- .../ro/fortsoft/pf4j/DefaultPluginManager.java | 134 --- .../ro/fortsoft/pf4j/DefaultPluginRepository.java | 83 -- .../fortsoft/pf4j/DefaultPluginStatusProvider.java | 98 --- .../ro/fortsoft/pf4j/DefaultVersionManager.java | 33 - .../java/ro/fortsoft/pf4j/DependencyResolver.java | 309 ------- .../fortsoft/pf4j/DevelopmentPluginClasspath.java | 32 - pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java | 37 - .../java/ro/fortsoft/pf4j/ExtensionDescriptor.java | 42 - .../java/ro/fortsoft/pf4j/ExtensionFactory.java | 25 - .../java/ro/fortsoft/pf4j/ExtensionFinder.java | 46 -- .../main/java/ro/fortsoft/pf4j/ExtensionPoint.java | 23 - .../java/ro/fortsoft/pf4j/ExtensionWrapper.java | 59 -- .../java/ro/fortsoft/pf4j/JarPluginManager.java | 115 --- .../ro/fortsoft/pf4j/LegacyExtensionFinder.java | 111 --- .../pf4j/ManifestPluginDescriptorFinder.java | 82 -- pf4j/src/main/java/ro/fortsoft/pf4j/Plugin.java | 71 -- .../java/ro/fortsoft/pf4j/PluginClassLoader.java | 145 ---- .../java/ro/fortsoft/pf4j/PluginClasspath.java | 54 -- .../java/ro/fortsoft/pf4j/PluginDependency.java | 51 -- .../java/ro/fortsoft/pf4j/PluginDescriptor.java | 177 ---- .../ro/fortsoft/pf4j/PluginDescriptorFinder.java | 31 - .../java/ro/fortsoft/pf4j/PluginException.java | 51 -- .../main/java/ro/fortsoft/pf4j/PluginFactory.java | 25 - .../main/java/ro/fortsoft/pf4j/PluginLoader.java | 29 - .../main/java/ro/fortsoft/pf4j/PluginManager.java | 183 ----- .../java/ro/fortsoft/pf4j/PluginRepository.java | 44 - .../main/java/ro/fortsoft/pf4j/PluginState.java | 56 -- .../java/ro/fortsoft/pf4j/PluginStateEvent.java | 60 -- .../java/ro/fortsoft/pf4j/PluginStateListener.java | 32 - .../ro/fortsoft/pf4j/PluginStatusProvider.java | 48 -- .../main/java/ro/fortsoft/pf4j/PluginWrapper.java | 147 ---- .../pf4j/PropertiesPluginDescriptorFinder.java | 116 --- .../main/java/ro/fortsoft/pf4j/RuntimeMode.java | 58 -- .../pf4j/ServiceProviderExtensionFinder.java | 148 ---- .../main/java/ro/fortsoft/pf4j/VersionManager.java | 34 - .../processor/ExtensionAnnotationProcessor.java | 232 ------ .../fortsoft/pf4j/processor/ExtensionStorage.java | 73 -- .../pf4j/processor/LegacyExtensionStorage.java | 106 --- .../processor/ServiceProviderExtensionStorage.java | 119 --- .../java/ro/fortsoft/pf4j/util/AndFileFilter.java | 85 -- .../java/ro/fortsoft/pf4j/util/ClassUtils.java | 91 --- .../java/ro/fortsoft/pf4j/util/DirectedGraph.java | 192 ----- .../ro/fortsoft/pf4j/util/DirectoryFileFilter.java | 33 - .../ro/fortsoft/pf4j/util/ExtensionFileFilter.java | 40 - .../main/java/ro/fortsoft/pf4j/util/FileUtils.java | 202 ----- .../java/ro/fortsoft/pf4j/util/HiddenFilter.java | 33 - .../java/ro/fortsoft/pf4j/util/JarFileFilter.java | 35 - .../java/ro/fortsoft/pf4j/util/NameFileFilter.java | 40 - .../java/ro/fortsoft/pf4j/util/NotFileFilter.java | 39 - .../java/ro/fortsoft/pf4j/util/OrFileFilter.java | 84 -- .../java/ro/fortsoft/pf4j/util/StringUtils.java | 40 - .../src/main/java/ro/fortsoft/pf4j/util/Unzip.java | 102 --- .../java/ro/fortsoft/pf4j/util/ZipFileFilter.java | 35 - .../services/javax.annotation.processing.Processor | 2 +- .../java/org/pf4j/AbstractExtensionFinderTest.java | 195 +++++ .../java/org/pf4j/DefaultExtensionFactoryTest.java | 50 ++ .../java/org/pf4j/DefaultPluginFactoryTest.java | 109 +++ .../java/org/pf4j/DefaultPluginManagerTest.java | 108 +++ .../java/org/pf4j/DefaultPluginRepositoryTest.java | 99 +++ .../org/pf4j/DefaultPluginStatusProviderTest.java | 165 ++++ .../java/org/pf4j/DefaultVersionManagerTest.java | 52 ++ .../test/java/org/pf4j/DependencyResolverTest.java | 142 ++++ .../org/pf4j/ExtensionAnnotationProcessorTest.java | 42 + .../java/org/pf4j/LegacyExtensionStorageTest.java | 50 ++ pf4j/src/test/java/org/pf4j/LoadPluginsTest.java | 172 ++++ .../pf4j/ManifestPluginDescriptorFinderTest.java | 236 ++++++ .../test/java/org/pf4j/PluginDependencyTest.java | 46 ++ .../pf4j/PropertiesPluginDescriptorFinderTest.java | 181 +++++ .../org/pf4j/plugin/AnotherFailTestPlugin.java | 30 + .../java/org/pf4j/plugin/FailTestExtension.java | 29 + .../test/java/org/pf4j/plugin/FailTestPlugin.java | 24 + .../java/org/pf4j/plugin/MockPluginManager.java | 51 ++ .../test/java/org/pf4j/plugin/TestExtension.java | 26 + .../java/org/pf4j/plugin/TestExtensionPoint.java | 25 + pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java | 31 + .../src/test/java/org/pf4j/util/FileUtilsTest.java | 75 ++ .../fortsoft/pf4j/AbstractExtensionFinderTest.java | 195 ----- .../fortsoft/pf4j/DefaultExtensionFactoryTest.java | 50 -- .../ro/fortsoft/pf4j/DefaultPluginFactoryTest.java | 109 --- .../ro/fortsoft/pf4j/DefaultPluginManagerTest.java | 108 --- .../fortsoft/pf4j/DefaultPluginRepositoryTest.java | 99 --- .../pf4j/DefaultPluginStatusProviderTest.java | 165 ---- .../fortsoft/pf4j/DefaultVersionManagerTest.java | 52 -- .../ro/fortsoft/pf4j/DependencyResolverTest.java | 142 ---- .../pf4j/ExtensionAnnotationProcessorTest.java | 42 - .../fortsoft/pf4j/LegacyExtensionStorageTest.java | 50 -- .../java/ro/fortsoft/pf4j/LoadPluginsTest.java | 172 ---- .../pf4j/ManifestPluginDescriptorFinderTest.java | 236 ------ .../ro/fortsoft/pf4j/PluginDependencyTest.java | 46 -- .../pf4j/PropertiesPluginDescriptorFinderTest.java | 181 ----- .../pf4j/plugin/AnotherFailTestPlugin.java | 30 - .../ro/fortsoft/pf4j/plugin/FailTestExtension.java | 29 - .../ro/fortsoft/pf4j/plugin/FailTestPlugin.java | 24 - .../ro/fortsoft/pf4j/plugin/MockPluginManager.java | 51 -- .../ro/fortsoft/pf4j/plugin/TestExtension.java | 26 - .../fortsoft/pf4j/plugin/TestExtensionPoint.java | 25 - .../java/ro/fortsoft/pf4j/plugin/TestPlugin.java | 31 - .../java/ro/fortsoft/pf4j/util/FileUtilsTest.java | 75 -- pf4j/src/test/resources/log4j.properties | 12 +- pom.xml | 2 +- 221 files changed, 8766 insertions(+), 8763 deletions(-) create mode 100644 demo/api/src/main/java/org/pf4j/demo/api/Greeting.java delete mode 100644 demo/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java create mode 100644 demo/app/src/main/java/org/pf4j/demo/Boot.java create mode 100644 demo/app/src/main/java/org/pf4j/demo/HowdyGreeting.java create mode 100644 demo/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java delete mode 100644 demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java delete mode 100644 demo/app/src/main/java/ro/fortsoft/pf4j/demo/HowdyGreeting.java delete mode 100644 demo/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java create mode 100644 demo/app/src/main/resources/META-INF/services/org.pf4j.demo.api.Greeting delete mode 100644 demo/app/src/main/resources/META-INF/services/ro.fortsoft.pf4j.demo.api.Greeting create mode 100644 demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java delete mode 100644 demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java create mode 100644 demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java delete mode 100644 demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java create mode 100644 demo_gradle/api/src/main/java/org/pf4j/demo/api/Greeting.java delete mode 100644 demo_gradle/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java create mode 100644 demo_gradle/app/src/main/java/org/pf4j/demo/Boot.java create mode 100644 demo_gradle/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java delete mode 100644 demo_gradle/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java delete mode 100644 demo_gradle/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java create mode 100644 demo_gradle/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java delete mode 100644 demo_gradle/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java create mode 100644 demo_gradle/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java delete mode 100644 demo_gradle/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java create mode 100644 demo_gradle/plugins/plugin3/src/main/kotlin/org/pf4j/demo/kotlin/KotlinPlugin.kt delete mode 100644 demo_gradle/plugins/plugin3/src/main/kotlin/ro/fortsoft/pf4j/demo/kotlin/KotlinPlugin.kt create mode 100644 pf4j/src/main/java/org/pf4j/AbstractExtensionFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/AbstractPluginManager.java create mode 100644 pf4j/src/main/java/org/pf4j/BasePluginRepository.java create mode 100644 pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultPluginFactory.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultPluginManager.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java create mode 100644 pf4j/src/main/java/org/pf4j/DefaultVersionManager.java create mode 100644 pf4j/src/main/java/org/pf4j/DependencyResolver.java create mode 100644 pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java create mode 100644 pf4j/src/main/java/org/pf4j/Extension.java create mode 100644 pf4j/src/main/java/org/pf4j/ExtensionDescriptor.java create mode 100644 pf4j/src/main/java/org/pf4j/ExtensionFactory.java create mode 100644 pf4j/src/main/java/org/pf4j/ExtensionFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/ExtensionPoint.java create mode 100644 pf4j/src/main/java/org/pf4j/ExtensionWrapper.java create mode 100644 pf4j/src/main/java/org/pf4j/JarPluginManager.java create mode 100644 pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/Plugin.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginClassLoader.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginClasspath.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginDependency.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginDescriptor.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginException.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginFactory.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginLoader.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginManager.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginRepository.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginState.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginStateEvent.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginStateListener.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginStatusProvider.java create mode 100644 pf4j/src/main/java/org/pf4j/PluginWrapper.java create mode 100644 pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/RuntimeMode.java create mode 100644 pf4j/src/main/java/org/pf4j/ServiceProviderExtensionFinder.java create mode 100644 pf4j/src/main/java/org/pf4j/VersionManager.java create mode 100644 pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java create mode 100644 pf4j/src/main/java/org/pf4j/processor/ExtensionStorage.java create mode 100644 pf4j/src/main/java/org/pf4j/processor/LegacyExtensionStorage.java create mode 100644 pf4j/src/main/java/org/pf4j/processor/ServiceProviderExtensionStorage.java create mode 100644 pf4j/src/main/java/org/pf4j/util/AndFileFilter.java create mode 100644 pf4j/src/main/java/org/pf4j/util/ClassUtils.java create mode 100644 pf4j/src/main/java/org/pf4j/util/DirectedGraph.java create mode 100644 pf4j/src/main/java/org/pf4j/util/DirectoryFileFilter.java create mode 100644 pf4j/src/main/java/org/pf4j/util/ExtensionFileFilter.java create mode 100644 pf4j/src/main/java/org/pf4j/util/FileUtils.java create mode 100644 pf4j/src/main/java/org/pf4j/util/HiddenFilter.java create mode 100644 pf4j/src/main/java/org/pf4j/util/JarFileFilter.java create mode 100644 pf4j/src/main/java/org/pf4j/util/NameFileFilter.java create mode 100644 pf4j/src/main/java/org/pf4j/util/NotFileFilter.java create mode 100644 pf4j/src/main/java/org/pf4j/util/OrFileFilter.java create mode 100644 pf4j/src/main/java/org/pf4j/util/StringUtils.java create mode 100644 pf4j/src/main/java/org/pf4j/util/Unzip.java create mode 100644 pf4j/src/main/java/org/pf4j/util/ZipFileFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/AbstractExtensionFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/BasePluginRepository.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/CompoundPluginRepository.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFactory.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginClasspath.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginLoader.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginStatusProvider.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DefaultVersionManager.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionDescriptor.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFactory.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionPoint.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/JarPluginManager.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/LegacyExtensionFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/Plugin.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginDependency.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginFactory.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginRepository.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateEvent.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateListener.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginStatusProvider.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/ServiceProviderExtensionFinder.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/VersionManager.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/processor/ExtensionAnnotationProcessor.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/processor/ExtensionStorage.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/processor/LegacyExtensionStorage.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/processor/ServiceProviderExtensionStorage.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/AndFileFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/ClassUtils.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFileFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFileFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/HiddenFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFileFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/NameFileFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/NotFileFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/OrFileFilter.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/StringUtils.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java delete mode 100644 pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFileFilter.java create mode 100644 pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java create mode 100644 pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java create mode 100644 pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java create mode 100644 pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java create mode 100644 pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java create mode 100644 pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java create mode 100644 pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java create mode 100644 pf4j/src/test/java/org/pf4j/DependencyResolverTest.java create mode 100644 pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java create mode 100644 pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java create mode 100644 pf4j/src/test/java/org/pf4j/LoadPluginsTest.java create mode 100644 pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java create mode 100644 pf4j/src/test/java/org/pf4j/PluginDependencyTest.java create mode 100644 pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java create mode 100644 pf4j/src/test/java/org/pf4j/plugin/AnotherFailTestPlugin.java create mode 100644 pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java create mode 100644 pf4j/src/test/java/org/pf4j/plugin/FailTestPlugin.java create mode 100644 pf4j/src/test/java/org/pf4j/plugin/MockPluginManager.java create mode 100644 pf4j/src/test/java/org/pf4j/plugin/TestExtension.java create mode 100644 pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java create mode 100644 pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java create mode 100644 pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/AbstractExtensionFinderTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/DefaultExtensionFactoryTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginFactoryTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginManagerTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginRepositoryTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginStatusProviderTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/DefaultVersionManagerTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/DependencyResolverTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/ExtensionAnnotationProcessorTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/LegacyExtensionStorageTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/LoadPluginsTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinderTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/PluginDependencyTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinderTest.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/plugin/AnotherFailTestPlugin.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/plugin/FailTestExtension.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/plugin/FailTestPlugin.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/plugin/MockPluginManager.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestExtension.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestExtensionPoint.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestPlugin.java delete mode 100644 pf4j/src/test/java/ro/fortsoft/pf4j/util/FileUtilsTest.java diff --git a/.travis.yml b/.travis.yml index 01f65da..6856742 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,8 @@ language: java jdk: - - oraclejdk7 + - openjdk7 +# JDK7 is not supported anymore; https://github.com/travis-ci/travis-ci/issues/7884#issuecomment-308451879 +# - oraclejdk7 + - oraclejdk8 after_success: - mvn clean cobertura:cobertura coveralls:report diff --git a/README.md b/README.md index c570de4..c50199b 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,12 @@ Plugin Framework for Java (PF4J) [![Join the chat at https://gitter.im/decebals/pf4j](https://badges.gitter.im/decebals/pf4j.svg)](https://gitter.im/decebals/pf4j?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Travis CI Build Status](https://travis-ci.org/decebals/pf4j.png)](https://travis-ci.org/decebals/pf4j) [![Coverage Status](https://coveralls.io/repos/decebals/pf4j/badge.svg?branch=master&service=github)](https://coveralls.io/github/decebals/pf4j?branch=master) -[![Maven Central](http://img.shields.io/maven-central/v/ro.fortsoft.pf4j/pf4j.svg)](http://search.maven.org/#search|ga|1|pf4j) +[![Maven Central](http://img.shields.io/maven-central/v/org.pf4j/pf4j.svg)](http://search.maven.org/#search|ga|1|pf4j) A plugin is a way for a third party to extend the functionality of an application. A plugin implements extension points declared by application or other plugins. Also a plugin can define extension points. -**NOTE:** Starting with version 0.9 you can define an extension directly in the application jar (you're not obligated to put the extension in a plugin - you can see this extension as a default/system extension). See [WhazzupGreeting](https://github.com/decebals/pf4j/blob/master/demo/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java) for a real example. +**NOTE:** Starting with version 0.9 you can define an extension directly in the application jar (you're not obligated to put the extension in a plugin - you can see this extension as a default/system extension). See [WhazzupGreeting](https://github.com/decebals/pf4j/blob/master/demo/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java) for a real example. Features/Benefits ------------------- @@ -50,7 +50,7 @@ In your pom.xml you must define the dependencies to PF4J artifacts with: ```xml - ro.fortsoft.pf4j + org.pf4j pf4j ${pf4j.version} @@ -119,14 +119,14 @@ Archiver-Version: Plexus Archiver Created-By: Apache Maven Built-By: decebal Build-Jdk: 1.6.0_17 -Plugin-Class: ro.fortsoft.pf4j.demo.welcome.WelcomePlugin +Plugin-Class: org.pf4j.demo.welcome.WelcomePlugin Plugin-Dependencies: x, y, z Plugin-Id: welcome-plugin Plugin-Provider: Decebal Suiu Plugin-Version: 0.0.1 ``` -In above manifest I described a plugin with id `welcome-plugin`, with class `ro.fortsoft.pf4j.demo.welcome.WelcomePlugin`, with version `0.0.1` and with dependencies +In above manifest I described a plugin with id `welcome-plugin`, with class `org.pf4j.demo.welcome.WelcomePlugin`, with version `0.0.1` and with dependencies to plugins `x, y, z`. **NOTE:** The plugin version must be compliant with [Semantic Versioning](http://semver.org) (PF4J uses `jsemver` as implementation for SemVer because it comes with support for comparing versions) @@ -195,7 +195,7 @@ protected PluginDescriptorFinder createPluginDescriptorFinder() { and in plugin repository you must have a plugin.properties file with the below content: ``` -plugin.class=ro.fortsoft.pf4j.demo.welcome.WelcomePlugin +plugin.class=org.pf4j.demo.welcome.WelcomePlugin plugin.dependencies=x, y, z plugin.id=welcome-plugin plugin.provider=Decebal Suiu @@ -219,7 +219,7 @@ The `maven-compiler-plugin` can be configured to do this like so: 2.5.1 - ro.fortsoft.pf4j.processor.ExtensionAnnotationProcessor + org.pf4j.processor.ExtensionAnnotationProcessor @@ -240,7 +240,7 @@ If you use `apache ant` then your build.xml file must looks like [this](https:// Plugin lifecycle -------------------------- -Each plugin passes through a pre-defined set of states. [PluginState](https://github.com/decebals/pf4j/blob/master/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java) defines all possible states. +Each plugin passes through a pre-defined set of states. [PluginState](https://github.com/decebals/pf4j/blob/master/pf4j/src/main/java/org/pf4j/PluginState.java) defines all possible states. The primary plugin states are: * CREATED * DISABLED @@ -342,7 +342,7 @@ Lets describe how DEVELOPMENT runtime mode works. First, you can change the runtime mode using the "pf4j.mode" system property or overriding `DefaultPluginManager.getRuntimeMode()`. For example I run the pf4j demo in eclipse in DEVELOPMENT mode adding only `"-Dpf4j.mode=development"` to the pf4j demo launcher. -You can retrieve the current runtime mode using `PluginManager.getRuntimeMode()` or in your Plugin implementation with `getWrapper().getRuntimeMode()`(see [WelcomePlugin](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java)). +You can retrieve the current runtime mode using `PluginManager.getRuntimeMode()` or in your Plugin implementation with `getWrapper().getRuntimeMode()`(see [WelcomePlugin](https://github.com/decebals/pf4j/blob/master/demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java)). The DefaultPluginManager determines automatically the correct runtime mode and for DEVELOPMENT mode overrides some components(pluginsDirectory is __"../plugins"__, __PropertiesPluginDescriptorFinder__ as PluginDescriptorFinder, __DevelopmentPluginClasspath__ as PluginClassPath). Another advantage of DEVELOPMENT runtime mode is that you can execute some code lines only in this mode (for example more debug messages). @@ -394,7 +394,7 @@ Default/System extension ------------------- Starting with version 0.9 you can define an extension directly in the application jar (you're not obligated to put the extension in a plugin - you can see this extension as a default/system extension). -See [WhazzupGreeting](https://github.com/decebals/pf4j/blob/master/demo/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java) +See [WhazzupGreeting](https://github.com/decebals/pf4j/blob/master/demo/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java) for a real example. This is great for starting application phase. In this scenario you have a minimalist plugin framework with one class loader @@ -441,20 +441,20 @@ calls from your application with `PluginManager.getExtensions()` and migrate smo Also you have the possibility to change the `ExtensionStorage` used in `ExtensionAnnotationProcessor`. By default we use the format with `META-INF/extensions.idx` ``` -ro.fortsoft.pf4j.demo.HowdyGreeting -ro.fortsoft.pf4j.demo.WhazzupGreeting +org.pf4j.demo.HowdyGreeting +org.pf4j.demo.WhazzupGreeting ``` but you can use a more standard location and format, `META-INF/services/`, used by Java Service Provider (see `java.util.ServiceLoader`) via `ServiceProviderExtensionStorage` implementation. -In this case the format of `META-INF/services/ro.fortsoft.pf4j.demo.api.Greeting` is +In this case the format of `META-INF/services/org.pf4j.demo.api.Greeting` is ``` # Generated by PF4J -ro.fortsoft.pf4j.demo.HowdyGreeting -ro.fortsoft.pf4j.demo.WhazzupGreeting # pf4j extension +org.pf4j.demo.HowdyGreeting +org.pf4j.demo.WhazzupGreeting # pf4j extension ``` -where the `ro.fortsoft.pf4j.demo.HowdyGreeting` entry is legacy (it's not generated by PF4J) but it's seen as +where the `org.pf4j.demo.HowdyGreeting` entry is legacy (it's not generated by PF4J) but it's seen as an extension of `Greeting` by PF4J (at runtime). You can plug your custom `ExtensionStorage` implementation in `ExtensionAnnotationProcessor` in two possible modes: @@ -462,7 +462,7 @@ You can plug your custom `ExtensionStorage` implementation in `ExtensionAnnotati - set the system property with key `pf4j.storageClassName` For example if I want to use `ServiceProviderExtensionStorage` then the value for the `pf4j.storageClassName` key must be -`ro.fortsoft.pf4j.processor.ServiceProviderExtensionStorage` +`org.pf4j.processor.ServiceProviderExtensionStorage` **NOTE:** `ServiceLoaderExtensionFinder`, the class that lookups for extensions stored in `META-INF/services` folder, is not added/enabled by default. To do this please override `createExtensionFinder` from `DefaultPluginManager`: diff --git a/demo/api/pom.xml b/demo/api/pom.xml index a031848..11f4cad 100644 --- a/demo/api/pom.xml +++ b/demo/api/pom.xml @@ -2,7 +2,7 @@ - ro.fortsoft.pf4j.demo + org.pf4j.demo pf4j-demo-parent 1.4.0-SNAPSHOT @@ -26,7 +26,7 @@ - ro.fortsoft.pf4j + org.pf4j pf4j ${project.version} diff --git a/demo/api/src/main/java/org/pf4j/demo/api/Greeting.java b/demo/api/src/main/java/org/pf4j/demo/api/Greeting.java new file mode 100644 index 0000000..1e4a767 --- /dev/null +++ b/demo/api/src/main/java/org/pf4j/demo/api/Greeting.java @@ -0,0 +1,27 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.demo.api; + +import org.pf4j.ExtensionPoint; + +/** + * @author Decebal Suiu + */ +public interface Greeting extends ExtensionPoint { + + String getGreeting(); + +} diff --git a/demo/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java b/demo/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java deleted file mode 100644 index bdf2fd4..0000000 --- a/demo/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo.api; - -import ro.fortsoft.pf4j.ExtensionPoint; - -/** - * @author Decebal Suiu - */ -public interface Greeting extends ExtensionPoint { - - String getGreeting(); - -} diff --git a/demo/app/pom.xml b/demo/app/pom.xml index b24e568..27b2afc 100644 --- a/demo/app/pom.xml +++ b/demo/app/pom.xml @@ -2,7 +2,7 @@ - ro.fortsoft.pf4j.demo + org.pf4j.demo pf4j-demo-parent 1.4.0-SNAPSHOT @@ -14,7 +14,7 @@ Demo App - ro.fortsoft.pf4j.demo.Boot + org.pf4j.demo.Boot 1.7.5 @@ -68,13 +68,13 @@ - ro.fortsoft.pf4j + org.pf4j pf4j ${project.version} - ro.fortsoft.pf4j.demo + org.pf4j.demo pf4j-demo-api ${project.version} diff --git a/demo/app/src/main/java/org/pf4j/demo/Boot.java b/demo/app/src/main/java/org/pf4j/demo/Boot.java new file mode 100644 index 0000000..76145a4 --- /dev/null +++ b/demo/app/src/main/java/org/pf4j/demo/Boot.java @@ -0,0 +1,125 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.demo; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; + +import org.pf4j.PluginManager; +import org.pf4j.PluginWrapper; +import org.pf4j.demo.api.Greeting; +import org.pf4j.DefaultPluginManager; +import org.pf4j.JarPluginManager; + +/** + * A boot class that start the demo. + * + * @author Decebal Suiu + */ +public class Boot { + + public static void main(String[] args) { + // print logo + printLogo(); + + // create the plugin manager + final PluginManager pluginManager = new DefaultPluginManager(); +// final PluginManager pluginManager = new JarPluginManager(); + + // load the plugins + pluginManager.loadPlugins(); + + // enable a disabled plugin +// pluginManager.enablePlugin("welcome-plugin"); + + // start (active/resolved) the plugins + pluginManager.startPlugins(); + + // retrieves the extensions for Greeting extension point + List greetings = pluginManager.getExtensions(Greeting.class); + System.out.println(String.format("Found %d extensions for extension point '%s'", greetings.size(), Greeting.class.getName())); + for (Greeting greeting : greetings) { + System.out.println(">>> " + greeting.getGreeting()); + } + + // print extensions from classpath (non plugin) + System.out.println("Extensions added by classpath:"); + Set extensionClassNames = pluginManager.getExtensionClassNames(null); + for (String extension : extensionClassNames) { + System.out.println(" " + extension); + } + + // print extensions ids for each started plugin + List startedPlugins = pluginManager.getStartedPlugins(); + for (PluginWrapper plugin : startedPlugins) { + String pluginId = plugin.getDescriptor().getPluginId(); + System.out.println(String.format("Extensions added by plugin '%s':", pluginId)); + extensionClassNames = pluginManager.getExtensionClassNames(pluginId); + for (String extension : extensionClassNames) { + System.out.println(" " + extension); + } + } + + // print the extensions instances for Greeting extension point for each started plugin + for (PluginWrapper plugin : startedPlugins) { + String pluginId = plugin.getDescriptor().getPluginId(); + System.out.println(String.format("Extensions instances added by plugin '%s' for extension point '%s':", pluginId, Greeting.class.getName())); + List extensions = pluginManager.getExtensions(Greeting.class, pluginId); + for (Object extension : extensions) { + System.out.println(" " + extension); + } + } + + // print extensions instances from classpath (non plugin) + System.out.println("Extensions instances added by classpath:"); + List extensions = pluginManager.getExtensions((String) null); + for (Object extension : extensions) { + System.out.println(" " + extension); + } + + // print extensions instances for each started plugin + for (PluginWrapper plugin : startedPlugins) { + String pluginId = plugin.getDescriptor().getPluginId(); + System.out.println(String.format("Extensions instances added by plugin '%s':", pluginId)); + extensions = pluginManager.getExtensions(pluginId); + for (Object extension : extensions) { + System.out.println(" " + extension); + } + } + + // stop the plugins + pluginManager.stopPlugins(); + /* + Runtime.getRuntime().addShutdownHook(new Thread() { + + @Override + public void run() { + pluginManager.stopPlugins(); + } + + }); + */ + } + + private static void printLogo() { + System.out.println(StringUtils.repeat("#", 40)); + System.out.println(StringUtils.center("PF4J-DEMO", 40)); + System.out.println(StringUtils.repeat("#", 40)); + } + +} diff --git a/demo/app/src/main/java/org/pf4j/demo/HowdyGreeting.java b/demo/app/src/main/java/org/pf4j/demo/HowdyGreeting.java new file mode 100644 index 0000000..51888b8 --- /dev/null +++ b/demo/app/src/main/java/org/pf4j/demo/HowdyGreeting.java @@ -0,0 +1,32 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.demo; + +import org.pf4j.demo.api.Greeting; + +/** + * A Service Implementation (no @Extension) declared via Java Service Provider mechanism (using META-INF/services). + * + * @author Decebal Suiu + */ +public class HowdyGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Howdy"; + } + +} diff --git a/demo/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java b/demo/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java new file mode 100644 index 0000000..c742127 --- /dev/null +++ b/demo/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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.demo; + +import org.pf4j.Extension; +import org.pf4j.demo.api.Greeting; + +/** + * @author Decebal Suiu + */ +@Extension +public class WhazzupGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Whazzup"; + } + +} diff --git a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java b/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java deleted file mode 100644 index c9738eb..0000000 --- a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo; - -import java.util.List; -import java.util.Set; - -import org.apache.commons.lang.StringUtils; - -import ro.fortsoft.pf4j.DefaultPluginManager; -import ro.fortsoft.pf4j.JarPluginManager; -import ro.fortsoft.pf4j.PluginManager; -import ro.fortsoft.pf4j.PluginWrapper; -import ro.fortsoft.pf4j.demo.api.Greeting; - -/** - * A boot class that start the demo. - * - * @author Decebal Suiu - */ -public class Boot { - - public static void main(String[] args) { - // print logo - printLogo(); - - // create the plugin manager - final PluginManager pluginManager = new DefaultPluginManager(); -// final PluginManager pluginManager = new JarPluginManager(); - - // load the plugins - pluginManager.loadPlugins(); - - // enable a disabled plugin -// pluginManager.enablePlugin("welcome-plugin"); - - // start (active/resolved) the plugins - pluginManager.startPlugins(); - - // retrieves the extensions for Greeting extension point - List greetings = pluginManager.getExtensions(Greeting.class); - System.out.println(String.format("Found %d extensions for extension point '%s'", greetings.size(), Greeting.class.getName())); - for (Greeting greeting : greetings) { - System.out.println(">>> " + greeting.getGreeting()); - } - - // print extensions from classpath (non plugin) - System.out.println("Extensions added by classpath:"); - Set extensionClassNames = pluginManager.getExtensionClassNames(null); - for (String extension : extensionClassNames) { - System.out.println(" " + extension); - } - - // print extensions ids for each started plugin - List startedPlugins = pluginManager.getStartedPlugins(); - for (PluginWrapper plugin : startedPlugins) { - String pluginId = plugin.getDescriptor().getPluginId(); - System.out.println(String.format("Extensions added by plugin '%s':", pluginId)); - extensionClassNames = pluginManager.getExtensionClassNames(pluginId); - for (String extension : extensionClassNames) { - System.out.println(" " + extension); - } - } - - // print the extensions instances for Greeting extension point for each started plugin - for (PluginWrapper plugin : startedPlugins) { - String pluginId = plugin.getDescriptor().getPluginId(); - System.out.println(String.format("Extensions instances added by plugin '%s' for extension point '%s':", pluginId, Greeting.class.getName())); - List extensions = pluginManager.getExtensions(Greeting.class, pluginId); - for (Object extension : extensions) { - System.out.println(" " + extension); - } - } - - // print extensions instances from classpath (non plugin) - System.out.println("Extensions instances added by classpath:"); - List extensions = pluginManager.getExtensions((String) null); - for (Object extension : extensions) { - System.out.println(" " + extension); - } - - // print extensions instances for each started plugin - for (PluginWrapper plugin : startedPlugins) { - String pluginId = plugin.getDescriptor().getPluginId(); - System.out.println(String.format("Extensions instances added by plugin '%s':", pluginId)); - extensions = pluginManager.getExtensions(pluginId); - for (Object extension : extensions) { - System.out.println(" " + extension); - } - } - - // stop the plugins - pluginManager.stopPlugins(); - /* - Runtime.getRuntime().addShutdownHook(new Thread() { - - @Override - public void run() { - pluginManager.stopPlugins(); - } - - }); - */ - } - - private static void printLogo() { - System.out.println(StringUtils.repeat("#", 40)); - System.out.println(StringUtils.center("PF4J-DEMO", 40)); - System.out.println(StringUtils.repeat("#", 40)); - } - -} diff --git a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/HowdyGreeting.java b/demo/app/src/main/java/ro/fortsoft/pf4j/demo/HowdyGreeting.java deleted file mode 100644 index 3d7d24b..0000000 --- a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/HowdyGreeting.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo; - -import ro.fortsoft.pf4j.demo.api.Greeting; - -/** - * A Service Implementation (no @Extension) declared via Java Service Provider mechanism (using META-INF/services). - * - * @author Decebal Suiu - */ -public class HowdyGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Howdy"; - } - -} diff --git a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java b/demo/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java deleted file mode 100644 index e182f2a..0000000 --- a/demo/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo; - -import ro.fortsoft.pf4j.Extension; -import ro.fortsoft.pf4j.demo.api.Greeting; - -/** - * @author Decebal Suiu - */ -@Extension -public class WhazzupGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Whazzup"; - } - -} diff --git a/demo/app/src/main/resources/META-INF/services/org.pf4j.demo.api.Greeting b/demo/app/src/main/resources/META-INF/services/org.pf4j.demo.api.Greeting new file mode 100644 index 0000000..615a943 --- /dev/null +++ b/demo/app/src/main/resources/META-INF/services/org.pf4j.demo.api.Greeting @@ -0,0 +1 @@ +org.pf4j.demo.HowdyGreeting diff --git a/demo/app/src/main/resources/META-INF/services/ro.fortsoft.pf4j.demo.api.Greeting b/demo/app/src/main/resources/META-INF/services/ro.fortsoft.pf4j.demo.api.Greeting deleted file mode 100644 index 4f8f4ad..0000000 --- a/demo/app/src/main/resources/META-INF/services/ro.fortsoft.pf4j.demo.api.Greeting +++ /dev/null @@ -1 +0,0 @@ -ro.fortsoft.pf4j.demo.HowdyGreeting diff --git a/demo/app/src/main/resources/log4j.properties b/demo/app/src/main/resources/log4j.properties index 692e39b..a05d9eb 100644 --- a/demo/app/src/main/resources/log4j.properties +++ b/demo/app/src/main/resources/log4j.properties @@ -3,13 +3,13 @@ log4j.rootLogger=DEBUG, Console # # PF4J log # -log4j.logger.ro.fortsoft.pf4j=DEBUG, Console +log4j.logger.org.pf4j=DEBUG, Console # !!! Put the bellow classes on level TRACE when you are in trouble -log4j.logger.ro.fortsoft.pf4j.PluginClassLoader=DEBUG, Console -log4j.logger.ro.fortsoft.pf4j.AbstractExtensionFinder=DEBUG, Console -log4j.additivity.ro.fortsoft.pf4j=false -log4j.additivity.ro.fortsoft.pf4j.PluginClassLoader=false -log4j.additivity.ro.fortsoft.pf4j.AbstractExtensionFinder=false +log4j.logger.org.pf4j.PluginClassLoader=DEBUG, Console +log4j.logger.org.pf4j.AbstractExtensionFinder=DEBUG, Console +log4j.additivity.org.pf4j=false +log4j.additivity.org.pf4j.PluginClassLoader=false +log4j.additivity.org.pf4j.AbstractExtensionFinder=false # # Appenders diff --git a/demo/plugins/plugin1/plugin.properties b/demo/plugins/plugin1/plugin.properties index 4f95d99..9da9bcc 100644 --- a/demo/plugins/plugin1/plugin.properties +++ b/demo/plugins/plugin1/plugin.properties @@ -1,5 +1,5 @@ plugin.id=welcome-plugin -plugin.class=ro.fortsoft.pf4j.demo.welcome.WelcomePlugin +plugin.class=org.pf4j.demo.welcome.WelcomePlugin plugin.version=0.0.1 plugin.provider=Decebal Suiu plugin.dependencies= diff --git a/demo/plugins/plugin1/pom.xml b/demo/plugins/plugin1/pom.xml index bf597d1..970788e 100644 --- a/demo/plugins/plugin1/pom.xml +++ b/demo/plugins/plugin1/pom.xml @@ -2,7 +2,7 @@ - ro.fortsoft.pf4j.demo + org.pf4j.demo pf4j-demo-plugins 1.4.0-SNAPSHOT @@ -15,7 +15,7 @@ welcome-plugin - ro.fortsoft.pf4j.demo.welcome.WelcomePlugin + org.pf4j.demo.welcome.WelcomePlugin 0.0.1 Decebal Suiu diff --git a/demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java b/demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java new file mode 100644 index 0000000..7ea2740 --- /dev/null +++ b/demo/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java @@ -0,0 +1,59 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.demo.welcome; + +import org.apache.commons.lang.StringUtils; + +import org.pf4j.PluginWrapper; +import org.pf4j.RuntimeMode; +import org.pf4j.demo.api.Greeting; +import org.pf4j.Extension; +import org.pf4j.Plugin; + +/** + * @author Decebal Suiu + */ +public class WelcomePlugin extends Plugin { + + public WelcomePlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + System.out.println("WelcomePlugin.start()"); + // for testing the development mode + if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { + System.out.println(StringUtils.upperCase("WelcomePlugin")); + } + } + + @Override + public void stop() { + System.out.println("WelcomePlugin.stop()"); + } + + @Extension + public static class WelcomeGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Welcome"; + } + + } + +} diff --git a/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java b/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java deleted file mode 100644 index a944a6a..0000000 --- a/demo/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo.welcome; - -import org.apache.commons.lang.StringUtils; - -import ro.fortsoft.pf4j.Extension; -import ro.fortsoft.pf4j.Plugin; -import ro.fortsoft.pf4j.PluginWrapper; -import ro.fortsoft.pf4j.RuntimeMode; -import ro.fortsoft.pf4j.demo.api.Greeting; - -/** - * @author Decebal Suiu - */ -public class WelcomePlugin extends Plugin { - - public WelcomePlugin(PluginWrapper wrapper) { - super(wrapper); - } - - @Override - public void start() { - System.out.println("WelcomePlugin.start()"); - // for testing the development mode - if (RuntimeMode.DEVELOPMENT.equals(wrapper.getRuntimeMode())) { - System.out.println(StringUtils.upperCase("WelcomePlugin")); - } - } - - @Override - public void stop() { - System.out.println("WelcomePlugin.stop()"); - } - - @Extension - public static class WelcomeGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Welcome"; - } - - } - -} diff --git a/demo/plugins/plugin2/plugin.properties b/demo/plugins/plugin2/plugin.properties index 0de45e6..60b6f33 100644 --- a/demo/plugins/plugin2/plugin.properties +++ b/demo/plugins/plugin2/plugin.properties @@ -1,5 +1,5 @@ plugin.id=hello-plugin -plugin.class=ro.fortsoft.pf4j.demo.hello.HelloPlugin +plugin.class=org.pf4j.demo.hello.HelloPlugin plugin.version=0.0.1 plugin.provider=Decebal Suiu plugin.dependencies= diff --git a/demo/plugins/plugin2/pom.xml b/demo/plugins/plugin2/pom.xml index 278949c..91d2dc7 100644 --- a/demo/plugins/plugin2/pom.xml +++ b/demo/plugins/plugin2/pom.xml @@ -2,7 +2,7 @@ - ro.fortsoft.pf4j.demo + org.pf4j.demo pf4j-demo-plugins 1.4.0-SNAPSHOT @@ -15,7 +15,7 @@ hello-plugin - ro.fortsoft.pf4j.demo.hello.HelloPlugin + org.pf4j.demo.hello.HelloPlugin 0.0.1 Decebal Suiu diff --git a/demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java b/demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java new file mode 100644 index 0000000..c6f1e52 --- /dev/null +++ b/demo/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java @@ -0,0 +1,54 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.demo.hello; + +import org.pf4j.Extension; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import org.pf4j.demo.api.Greeting; + +/** + * A very simple plugin. + * + * @author Decebal Suiu + */ +public class HelloPlugin extends Plugin { + + public HelloPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + System.out.println("HelloPlugin.start()"); + } + + @Override + public void stop() { + System.out.println("HelloPlugin.stop()"); + } + + @Extension(ordinal=1) + public static class HelloGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Hello"; + } + + } + +} diff --git a/demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java b/demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java deleted file mode 100644 index b5e5392..0000000 --- a/demo/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo.hello; - -import ro.fortsoft.pf4j.Extension; -import ro.fortsoft.pf4j.Plugin; -import ro.fortsoft.pf4j.PluginWrapper; -import ro.fortsoft.pf4j.demo.api.Greeting; - -/** - * A very simple plugin. - * - * @author Decebal Suiu - */ -public class HelloPlugin extends Plugin { - - public HelloPlugin(PluginWrapper wrapper) { - super(wrapper); - } - - @Override - public void start() { - System.out.println("HelloPlugin.start()"); - } - - @Override - public void stop() { - System.out.println("HelloPlugin.stop()"); - } - - @Extension(ordinal=1) - public static class HelloGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Hello"; - } - - } - -} diff --git a/demo/plugins/pom.xml b/demo/plugins/pom.xml index c385dcb..b8f262f 100644 --- a/demo/plugins/pom.xml +++ b/demo/plugins/pom.xml @@ -2,7 +2,7 @@ - ro.fortsoft.pf4j.demo + org.pf4j.demo pf4j-demo-parent 1.4.0-SNAPSHOT @@ -85,7 +85,7 @@ - ro.fortsoft.pf4j + org.pf4j pf4j ${project.version} @@ -93,7 +93,7 @@ - ro.fortsoft.pf4j.demo + org.pf4j.demo pf4j-demo-api ${project.version} diff --git a/demo/pom.xml b/demo/pom.xml index 86a191a..b304fed 100644 --- a/demo/pom.xml +++ b/demo/pom.xml @@ -2,13 +2,13 @@ - ro.fortsoft.pf4j + org.pf4j pf4j-parent 1.4.0-SNAPSHOT 4.0.0 - ro.fortsoft.pf4j.demo + org.pf4j.demo pf4j-demo-parent 1.4.0-SNAPSHOT pom diff --git a/demo_gradle/api/build.gradle b/demo_gradle/api/build.gradle index 774655d..616520e 100644 --- a/demo_gradle/api/build.gradle +++ b/demo_gradle/api/build.gradle @@ -1,5 +1,5 @@ dependencies { - compile 'ro.fortsoft.pf4j:pf4j:1.3.0' - compile 'org.apache.commons:commons-lang3:3.0' - testCompile group: 'junit', name: 'junit', version: '4.+' + compile 'org.pf4j:pf4j:1.4.0-SNAPSHOT' + compile 'org.apache.commons:commons-lang3:3.0' + testCompile group: 'junit', name: 'junit', version: '4.+' } diff --git a/demo_gradle/api/src/main/java/org/pf4j/demo/api/Greeting.java b/demo_gradle/api/src/main/java/org/pf4j/demo/api/Greeting.java new file mode 100644 index 0000000..1e4a767 --- /dev/null +++ b/demo_gradle/api/src/main/java/org/pf4j/demo/api/Greeting.java @@ -0,0 +1,27 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.demo.api; + +import org.pf4j.ExtensionPoint; + +/** + * @author Decebal Suiu + */ +public interface Greeting extends ExtensionPoint { + + String getGreeting(); + +} diff --git a/demo_gradle/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java b/demo_gradle/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java deleted file mode 100644 index c451b09..0000000 --- a/demo_gradle/api/src/main/java/ro/fortsoft/pf4j/demo/api/Greeting.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo.api; - -import ro.fortsoft.pf4j.ExtensionPoint; - -/** - * @author Decebal Suiu - */ -public interface Greeting extends ExtensionPoint { - - public String getGreeting(); - -} diff --git a/demo_gradle/app/build.gradle b/demo_gradle/app/build.gradle index 2478937..cd473b6 100644 --- a/demo_gradle/app/build.gradle +++ b/demo_gradle/app/build.gradle @@ -1,16 +1,16 @@ -apply plugin:'application' +apply plugin: 'application' -mainClassName = 'ro.fortsoft.pf4j.demo.Boot' +mainClassName = 'org.pf4j.demo.Boot' dependencies { - compile project(':api') - compile 'ro.fortsoft.pf4j:pf4j:1.3.0' - compile 'org.apache.commons:commons-lang3:3.5' - testCompile group: 'junit', name: 'junit', version: '4.+' - compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.25' + compile project(':api') + compile 'org.pf4j:pf4j:1.4.0-SNAPSHOT' + compile 'org.apache.commons:commons-lang3:3.5' + testCompile group: 'junit', name: 'junit', version: '4.+' + compile group: 'org.slf4j', name: 'slf4j-simple', version: '1.7.25' } jar { - baseName = 'Plugin Demo' - version = '0.1.0' + baseName = 'Plugin Demo' + version = '0.1.0' } diff --git a/demo_gradle/app/src/main/java/org/pf4j/demo/Boot.java b/demo_gradle/app/src/main/java/org/pf4j/demo/Boot.java new file mode 100644 index 0000000..46261d6 --- /dev/null +++ b/demo_gradle/app/src/main/java/org/pf4j/demo/Boot.java @@ -0,0 +1,100 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.demo; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.pf4j.DefaultPluginManager; +import org.pf4j.PluginManager; +import org.pf4j.PluginWrapper; +import org.pf4j.demo.api.Greeting; + +import java.util.List; + +/** + * A boot class that start the demo. + * + * @author Decebal Suiu + */ +public class Boot { + private static final Logger logger = LoggerFactory.getLogger(Boot.class); + + public static void main(String[] args) { + // print logo + printLogo(); + + // create the plugin manager + final PluginManager pluginManager = new DefaultPluginManager(); + + // load the plugins + pluginManager.loadPlugins(); + + // enable a disabled plugin +// pluginManager.enablePlugin("welcome-plugin"); + + // start (active/resolved) the plugins + pluginManager.startPlugins(); + + logger.info("Plugindirectory: "); + logger.info("\t" + System.getProperty("pf4j.pluginsDir", "plugins") + "\n"); + + // retrieves the extensions for Greeting extension point + List greetings = pluginManager.getExtensions(Greeting.class); + logger.info(String.format("Found %d extensions for extension point '%s'", greetings.size(), Greeting.class.getName())); + for (Greeting greeting : greetings) { + logger.info(">>> " + greeting.getGreeting()); + } + + // // print extensions from classpath (non plugin) + // logger.info(String.format("Extensions added by classpath:")); + // Set extensionClassNames = pluginManager.getExtensionClassNames(null); + // for (String extension : extensionClassNames) { + // logger.info(" " + extension); + // } + + // print extensions for each started plugin + List startedPlugins = pluginManager.getStartedPlugins(); + for (PluginWrapper plugin : startedPlugins) { + String pluginId = plugin.getDescriptor().getPluginId(); + logger.info(String.format("Extensions added by plugin '%s':", pluginId)); + // extensionClassNames = pluginManager.getExtensionClassNames(pluginId); + // for (String extension : extensionClassNames) { + // logger.info(" " + extension); + // } + } + + // stop the plugins + pluginManager.stopPlugins(); + /* + Runtime.getRuntime().addShutdownHook(new Thread() { + + @Override + public void run() { + pluginManager.stopPlugins(); + } + + }); + */ + } + + private static void printLogo() { + logger.info(StringUtils.repeat("#", 40)); + logger.info(StringUtils.center("PF4J-DEMO", 40)); + logger.info(StringUtils.repeat("#", 40)); + } + +} diff --git a/demo_gradle/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java b/demo_gradle/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java new file mode 100644 index 0000000..c742127 --- /dev/null +++ b/demo_gradle/app/src/main/java/org/pf4j/demo/WhazzupGreeting.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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.demo; + +import org.pf4j.Extension; +import org.pf4j.demo.api.Greeting; + +/** + * @author Decebal Suiu + */ +@Extension +public class WhazzupGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Whazzup"; + } + +} diff --git a/demo_gradle/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java b/demo_gradle/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java deleted file mode 100644 index d3e1502..0000000 --- a/demo_gradle/app/src/main/java/ro/fortsoft/pf4j/demo/Boot.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.DefaultPluginManager; -import ro.fortsoft.pf4j.PluginManager; -import ro.fortsoft.pf4j.PluginWrapper; -import ro.fortsoft.pf4j.demo.api.Greeting; - -import java.util.List; - -/** - * A boot class that start the demo. - * - * @author Decebal Suiu - */ -public class Boot { - private static final Logger logger = LoggerFactory.getLogger(Boot.class); - - public static void main(String[] args) { - // print logo - printLogo(); - - // create the plugin manager - final PluginManager pluginManager = new DefaultPluginManager(); - - // load the plugins - pluginManager.loadPlugins(); - - // enable a disabled plugin -// pluginManager.enablePlugin("welcome-plugin"); - - // start (active/resolved) the plugins - pluginManager.startPlugins(); - - logger.info("Plugindirectory: "); - logger.info("\t" + System.getProperty("pf4j.pluginsDir", "plugins") + "\n"); - - // retrieves the extensions for Greeting extension point - List greetings = pluginManager.getExtensions(Greeting.class); - logger.info(String.format("Found %d extensions for extension point '%s'", greetings.size(), Greeting.class.getName())); - for (Greeting greeting : greetings) { - logger.info(">>> " + greeting.getGreeting()); - } - - // // print extensions from classpath (non plugin) - // logger.info(String.format("Extensions added by classpath:")); - // Set extensionClassNames = pluginManager.getExtensionClassNames(null); - // for (String extension : extensionClassNames) { - // logger.info(" " + extension); - // } - - // print extensions for each started plugin - List startedPlugins = pluginManager.getStartedPlugins(); - for (PluginWrapper plugin : startedPlugins) { - String pluginId = plugin.getDescriptor().getPluginId(); - logger.info(String.format("Extensions added by plugin '%s':", pluginId)); - // extensionClassNames = pluginManager.getExtensionClassNames(pluginId); - // for (String extension : extensionClassNames) { - // logger.info(" " + extension); - // } - } - - // stop the plugins - pluginManager.stopPlugins(); - /* - Runtime.getRuntime().addShutdownHook(new Thread() { - - @Override - public void run() { - pluginManager.stopPlugins(); - } - - }); - */ - } - - private static void printLogo() { - logger.info(StringUtils.repeat("#", 40)); - logger.info(StringUtils.center("PF4J-DEMO", 40)); - logger.info(StringUtils.repeat("#", 40)); - } - -} diff --git a/demo_gradle/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java b/demo_gradle/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java deleted file mode 100644 index e182f2a..0000000 --- a/demo_gradle/app/src/main/java/ro/fortsoft/pf4j/demo/WhazzupGreeting.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo; - -import ro.fortsoft.pf4j.Extension; -import ro.fortsoft.pf4j.demo.api.Greeting; - -/** - * @author Decebal Suiu - */ -@Extension -public class WhazzupGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Whazzup"; - } - -} diff --git a/demo_gradle/app/src/main/resources/log4j.properties b/demo_gradle/app/src/main/resources/log4j.properties index 16ce034..f42e226 100644 --- a/demo_gradle/app/src/main/resources/log4j.properties +++ b/demo_gradle/app/src/main/resources/log4j.properties @@ -3,9 +3,9 @@ log4j.rootLogger=DEBUG, Console # # PF4J log # -log4j.logger.ro.fortsoft.pf4j=DEBUG, Console -log4j.logger.ro.fortsoft.pf4j.PluginClassLoader=WARN, Console -log4j.additivity.ro.fortsoft.pf4j=false +log4j.logger.org.pf4j=DEBUG, Console +log4j.logger.org.pf4j.PluginClassLoader=WARN, Console +log4j.additivity.org.pf4j=false # # Appenders diff --git a/demo_gradle/build.gradle b/demo_gradle/build.gradle index e38a949..d86746d 100644 --- a/demo_gradle/build.gradle +++ b/demo_gradle/build.gradle @@ -1,10 +1,10 @@ subprojects { - apply plugin: 'java' + apply plugin: 'java' - repositories { - mavenLocal() - mavenCentral() - } + repositories { + mavenLocal() + mavenCentral() + } } task copyPlugins() { @@ -14,7 +14,7 @@ task copyPlugins() { subprojects.each { p -> if (p.path.contains(":plugins/")) { - System.out.println("Copying plugin from " + p.path); + System.out.println("Copying plugin from " + p.path); copy { from p.projectDir.toString() + '/build/libs' into 'app/plugins' diff --git a/demo_gradle/plugins/plugin1/build.gradle b/demo_gradle/plugins/plugin1/build.gradle index 596101e..f0998a1 100644 --- a/demo_gradle/plugins/plugin1/build.gradle +++ b/demo_gradle/plugins/plugin1/build.gradle @@ -2,31 +2,32 @@ jar { baseName = 'WelcomePlugin' version = '0.1.0' manifest { - attributes 'Plugin-Class' : 'ro.fortsoft.pf4j.demo.welcome.WelcomePlugin', - 'Plugin-Id' : 'WelcomePlugin', - 'Plugin-Version' : '1.0.0', - 'Plugin-Provider' : 'Decebal Suiu' + attributes 'Plugin-Class': 'org.pf4j.demo.welcome.WelcomePlugin', + 'Plugin-Id': 'WelcomePlugin', + 'Plugin-Version': '1.0.0', + 'Plugin-Provider': 'Decebal Suiu' } } task plugin(type: Jar) { - baseName = 'WelcomePlugin' - version = '0.1.0' - into('classes'){ - with jar - } - into('lib'){ - from configurations.compile - } - extension('zip') + baseName = 'WelcomePlugin' + version = '0.1.0' + into('classes') { + with jar + } + into('lib') { + from configurations.compile + } + extension('zip') } assemble.dependsOn plugin dependencies { - compileOnly project(':api') // compileOnly important!!! We do not want to put the api into the zip file since the main program has it already! - compile ('ro.fortsoft.pf4j:pf4j:1.3.0') { - exclude group: "org.slf4j" - } - compile 'org.apache.commons:commons-lang3:3.5' - testCompile group: 'junit', name: 'junit', version: '4.+' + compileOnly project(':api') + // compileOnly important!!! We do not want to put the api into the zip file since the main program has it already! + compile('org.pf4j:pf4j:1.4.0-SNAPSHOT') { + exclude group: "org.slf4j" + } + compile 'org.apache.commons:commons-lang3:3.5' + testCompile group: 'junit', name: 'junit', version: '4.+' } diff --git a/demo_gradle/plugins/plugin1/plugin.properties b/demo_gradle/plugins/plugin1/plugin.properties index 4f95d99..9da9bcc 100644 --- a/demo_gradle/plugins/plugin1/plugin.properties +++ b/demo_gradle/plugins/plugin1/plugin.properties @@ -1,5 +1,5 @@ plugin.id=welcome-plugin -plugin.class=ro.fortsoft.pf4j.demo.welcome.WelcomePlugin +plugin.class=org.pf4j.demo.welcome.WelcomePlugin plugin.version=0.0.1 plugin.provider=Decebal Suiu plugin.dependencies= diff --git a/demo_gradle/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java b/demo_gradle/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java new file mode 100644 index 0000000..040c9fd --- /dev/null +++ b/demo_gradle/plugins/plugin1/src/main/java/org/pf4j/demo/welcome/WelcomePlugin.java @@ -0,0 +1,58 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.demo.welcome; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.pf4j.Extension; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import org.pf4j.demo.api.Greeting; + +/** + * @author Decebal Suiu + */ +public class WelcomePlugin extends Plugin { + private static final Logger logger = LoggerFactory.getLogger(WelcomePlugin.class); + + public WelcomePlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + logger.info("WelcomePlugin.start()"); + logger.info(StringUtils.upperCase("WelcomePlugin")); + } + + @Override + public void stop() { + logger.info("WelcomePlugin.stop()"); + } + + @Extension + public static class WelcomeGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Welcome"; + } + + } + +} diff --git a/demo_gradle/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java b/demo_gradle/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java deleted file mode 100644 index 46ca250..0000000 --- a/demo_gradle/plugins/plugin1/src/main/java/ro/fortsoft/pf4j/demo/welcome/WelcomePlugin.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo.welcome; - -import org.apache.commons.lang3.StringUtils; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.Extension; -import ro.fortsoft.pf4j.Plugin; -import ro.fortsoft.pf4j.PluginWrapper; -import ro.fortsoft.pf4j.demo.api.Greeting; - -/** - * @author Decebal Suiu - */ -public class WelcomePlugin extends Plugin { - private static final Logger logger = LoggerFactory.getLogger(WelcomePlugin.class); - - public WelcomePlugin(PluginWrapper wrapper) { - super(wrapper); - } - - @Override - public void start() { - logger.info("WelcomePlugin.start()"); - logger.info(StringUtils.upperCase("WelcomePlugin")); - } - - @Override - public void stop() { - logger.info("WelcomePlugin.stop()"); - } - - @Extension - public static class WelcomeGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Welcome"; - } - - } - -} diff --git a/demo_gradle/plugins/plugin2/build.gradle b/demo_gradle/plugins/plugin2/build.gradle index 3a96d1e..0e292fb 100644 --- a/demo_gradle/plugins/plugin2/build.gradle +++ b/demo_gradle/plugins/plugin2/build.gradle @@ -1,32 +1,33 @@ jar { - baseName = 'HelloPlugin' - version = '0.1.0' - manifest { - attributes 'Plugin-Class' : 'ro.fortsoft.pf4j.demo.hello.HelloPlugin', - 'Plugin-Id' : 'HelloPlugin', - 'Plugin-Version' : '1.0.0', - 'Plugin-Provider' : 'Decebal Suiu' - } + baseName = 'HelloPlugin' + version = '0.1.0' + manifest { + attributes 'Plugin-Class': 'org.pf4j.demo.hello.HelloPlugin', + 'Plugin-Id': 'HelloPlugin', + 'Plugin-Version': '1.0.0', + 'Plugin-Provider': 'Decebal Suiu' + } } task plugin(type: Jar) { - baseName = 'HelloPlugin' - version = '0.1.0' - into('classes'){ - with jar - } - into('lib'){ - from configurations.compile - } - extension('zip') + baseName = 'HelloPlugin' + version = '0.1.0' + into('classes') { + with jar + } + into('lib') { + from configurations.compile + } + extension('zip') } assemble.dependsOn plugin dependencies { - compileOnly project(':api') // compileOnly important!!! We do not want to put the api into the zip file since the main program has it already! - compile ('ro.fortsoft.pf4j:pf4j:1.3.0') { - exclude group: "org.slf4j" - } - compile 'org.apache.commons:commons-lang3:3.5' - testCompile group: 'junit', name: 'junit', version: '4.+' + compileOnly project(':api') + // compileOnly important!!! We do not want to put the api into the zip file since the main program has it already! + compile('org.pf4j:pf4j:1.4.0-SNAPSHOT') { + exclude group: "org.slf4j" + } + compile 'org.apache.commons:commons-lang3:3.5' + testCompile group: 'junit', name: 'junit', version: '4.+' } diff --git a/demo_gradle/plugins/plugin2/plugin.properties b/demo_gradle/plugins/plugin2/plugin.properties index 0de45e6..60b6f33 100644 --- a/demo_gradle/plugins/plugin2/plugin.properties +++ b/demo_gradle/plugins/plugin2/plugin.properties @@ -1,5 +1,5 @@ plugin.id=hello-plugin -plugin.class=ro.fortsoft.pf4j.demo.hello.HelloPlugin +plugin.class=org.pf4j.demo.hello.HelloPlugin plugin.version=0.0.1 plugin.provider=Decebal Suiu plugin.dependencies= diff --git a/demo_gradle/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java b/demo_gradle/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java new file mode 100644 index 0000000..f8e83d3 --- /dev/null +++ b/demo_gradle/plugins/plugin2/src/main/java/org/pf4j/demo/hello/HelloPlugin.java @@ -0,0 +1,57 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.demo.hello; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.pf4j.Extension; +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; +import org.pf4j.demo.api.Greeting; + +/** + * A very simple plugin. + * + * @author Decebal Suiu + */ +public class HelloPlugin extends Plugin { + private static final Logger logger = LoggerFactory.getLogger(HelloPlugin.class); + + public HelloPlugin(PluginWrapper wrapper) { + super(wrapper); + } + + @Override + public void start() { + logger.info("HelloPlugin.start()"); + } + + @Override + public void stop() { + logger.info("HelloPlugin.stop()"); + } + + @Extension(ordinal=1) + public static class HelloGreeting implements Greeting { + + @Override + public String getGreeting() { + return "Hello"; + } + + } + +} diff --git a/demo_gradle/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java b/demo_gradle/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java deleted file mode 100644 index 82c0046..0000000 --- a/demo_gradle/plugins/plugin2/src/main/java/ro/fortsoft/pf4j/demo/hello/HelloPlugin.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.demo.hello; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.Extension; -import ro.fortsoft.pf4j.Plugin; -import ro.fortsoft.pf4j.PluginWrapper; -import ro.fortsoft.pf4j.demo.api.Greeting; - -/** - * A very simple plugin. - * - * @author Decebal Suiu - */ -public class HelloPlugin extends Plugin { - private static final Logger logger = LoggerFactory.getLogger(HelloPlugin.class); - - public HelloPlugin(PluginWrapper wrapper) { - super(wrapper); - } - - @Override - public void start() { - logger.info("HelloPlugin.start()"); - } - - @Override - public void stop() { - logger.info("HelloPlugin.stop()"); - } - - @Extension(ordinal=1) - public static class HelloGreeting implements Greeting { - - @Override - public String getGreeting() { - return "Hello"; - } - - } - -} diff --git a/demo_gradle/plugins/plugin3/build.gradle b/demo_gradle/plugins/plugin3/build.gradle index d77258e..7dd77dc 100644 --- a/demo_gradle/plugins/plugin3/build.gradle +++ b/demo_gradle/plugins/plugin3/build.gradle @@ -13,20 +13,20 @@ jar { baseName = 'KotlinPlugin' version = '0.1.0' manifest { - attributes 'Plugin-Class' : 'ro.fortsoft.pf4j.demo.kotlin.KotlinPlugin', - 'Plugin-Id' : 'KotlinPlugin', - 'Plugin-Version' : '1.0.0', - 'Plugin-Provider' : 'Anindya Chatterjee' + attributes 'Plugin-Class': 'org.pf4j.demo.kotlin.KotlinPlugin', + 'Plugin-Id': 'KotlinPlugin', + 'Plugin-Version': '1.0.0', + 'Plugin-Provider': 'Anindya Chatterjee' } } task plugin(type: Jar) { baseName = 'KotlinPlugin' version = '0.1.0' - into('classes'){ + into('classes') { with jar } - into('lib'){ + into('lib') { from configurations.compile } extension('zip') @@ -42,7 +42,7 @@ repositories { dependencies { compileOnly project(':api') - kapt ('ro.fortsoft.pf4j:pf4j:1.3.0') { + kapt('org.pf4j:pf4j:1.4.0-SNAPSHOT') { exclude group: "org.slf4j" } compile 'org.apache.commons:commons-lang3:3.5' diff --git a/demo_gradle/plugins/plugin3/src/main/kotlin/org/pf4j/demo/kotlin/KotlinPlugin.kt b/demo_gradle/plugins/plugin3/src/main/kotlin/org/pf4j/demo/kotlin/KotlinPlugin.kt new file mode 100644 index 0000000..2b2bc17 --- /dev/null +++ b/demo_gradle/plugins/plugin3/src/main/kotlin/org/pf4j/demo/kotlin/KotlinPlugin.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 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.demo.kotlin + +import org.apache.commons.lang3.StringUtils +import org.slf4j.LoggerFactory +import org.pf4j.Extension +import org.pf4j.Plugin +import org.pf4j.PluginWrapper +import org.pf4j.demo.api.Greeting + +/** + * A sample plugin written in Kotlin + * + * @author Anindya Chatterjee + */ +class KotlinPlugin(wrapper: PluginWrapper) : Plugin(wrapper) { + private val logger = LoggerFactory.getLogger(KotlinPlugin::class.java) + + override fun start() { + logger.info("KotlinPlugin.start()") + logger.info(StringUtils.upperCase("KotlinPlugin")) + } + + override fun stop() { + logger.info("KotlinPlugin.stop()") + } +} + +@Extension +class KotlinGreeting : Greeting { + override fun getGreeting(): String { + return "KotlinGreetings" + } +} diff --git a/demo_gradle/plugins/plugin3/src/main/kotlin/ro/fortsoft/pf4j/demo/kotlin/KotlinPlugin.kt b/demo_gradle/plugins/plugin3/src/main/kotlin/ro/fortsoft/pf4j/demo/kotlin/KotlinPlugin.kt deleted file mode 100644 index f8df469..0000000 --- a/demo_gradle/plugins/plugin3/src/main/kotlin/ro/fortsoft/pf4j/demo/kotlin/KotlinPlugin.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2017 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 ro.fortsoft.pf4j.demo.kotlin - -import org.apache.commons.lang3.StringUtils -import org.slf4j.LoggerFactory -import ro.fortsoft.pf4j.Extension -import ro.fortsoft.pf4j.Plugin -import ro.fortsoft.pf4j.PluginWrapper -import ro.fortsoft.pf4j.demo.api.Greeting - -/** - * A sample plugin written in Kotlin - * - * @author Anindya Chatterjee - */ -class KotlinPlugin(wrapper: PluginWrapper) : Plugin(wrapper) { - private val logger = LoggerFactory.getLogger(KotlinPlugin::class.java) - - override fun start() { - logger.info("KotlinPlugin.start()") - logger.info(StringUtils.upperCase("KotlinPlugin")) - } - - override fun stop() { - logger.info("KotlinPlugin.stop()") - } -} - -@Extension -class KotlinGreeting : Greeting { - override fun getGreeting(): String { - return "KotlinGreetings" - } -} diff --git a/demo_gradle/settings.gradle b/demo_gradle/settings.gradle index 8bbcb21..7ee71ba 100644 --- a/demo_gradle/settings.gradle +++ b/demo_gradle/settings.gradle @@ -3,4 +3,3 @@ include 'app' include 'plugins/plugin1' include 'plugins/plugin2' include 'plugins/plugin3' - diff --git a/pf4j/pom.xml b/pf4j/pom.xml index a669078..a3c0601 100644 --- a/pf4j/pom.xml +++ b/pf4j/pom.xml @@ -2,7 +2,7 @@ - ro.fortsoft.pf4j + org.pf4j pf4j-parent 1.4.0-SNAPSHOT diff --git a/pf4j/src/main/java/org/pf4j/AbstractExtensionFinder.java b/pf4j/src/main/java/org/pf4j/AbstractExtensionFinder.java new file mode 100644 index 0000000..49be73b --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/AbstractExtensionFinder.java @@ -0,0 +1,247 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.pf4j.util.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * @author Decebal Suiu + */ +public abstract class AbstractExtensionFinder implements ExtensionFinder, PluginStateListener { + + private static final Logger log = LoggerFactory.getLogger(AbstractExtensionFinder.class); + + protected PluginManager pluginManager; + protected volatile Map> entries; // cache by pluginId + + public AbstractExtensionFinder(PluginManager pluginManager) { + this.pluginManager = pluginManager; + } + + public abstract Map> readPluginsStorages(); + + public abstract Map> readClasspathStorages(); + + @Override + @SuppressWarnings("unchecked") + public List> find(Class type) { + log.debug("Finding extensions of extension point '{}'", type.getName()); + Map> entries = getEntries(); + List> result = new ArrayList<>(); + + // add extensions found in classpath and plugins + for (String pluginId : entries.keySet()) { + // classpath's extensions <=> pluginId = null + List> pluginExtensions = find(type, pluginId); + result.addAll(pluginExtensions); + } + + if (entries.isEmpty()) { + log.debug("No extensions found for extension point '{}'", type.getName()); + } else { + log.debug("Found {} extensions for extension point '{}'", result.size(), type.getName()); + } + + // sort by "ordinal" property + Collections.sort(result); + + return result; + } + + @Override + @SuppressWarnings("unchecked") + public List> find(Class type, String pluginId) { + log.debug("Finding extensions of extension point '{}' for plugin '{}'", type.getName(), pluginId); + List> result = new ArrayList<>(); + + // classpath's extensions <=> pluginId = null + Set classNames = findClassNames(pluginId); + if (classNames == null || classNames.isEmpty()) { + return result; + } + + if (pluginId != null) { + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); + if (PluginState.STARTED != pluginWrapper.getPluginState()) { + return result; + } + + log.trace("Checking extensions from plugin '{}'", pluginId); + } else { + log.trace("Checking extensions from classpath"); + } + + ClassLoader classLoader = (pluginId != null) ? pluginManager.getPluginClassLoader(pluginId) : getClass().getClassLoader(); + + for (String className : classNames) { + try { + log.debug("Loading class '{}' using class loader '{}'", className, classLoader); + Class extensionClass = classLoader.loadClass(className); + + log.debug("Checking extension type '{}'", className); + if (type.isAssignableFrom(extensionClass)) { + ExtensionWrapper extensionWrapper = createExtensionWrapper(extensionClass); + result.add(extensionWrapper); + log.debug("Added extension '{}' with ordinal {}", className, extensionWrapper.getOrdinal()); + } else { + log.trace("'{}' is not an extension for extension point '{}'", className, type.getName()); + if (RuntimeMode.DEVELOPMENT.equals(pluginManager.getRuntimeMode())) { + checkDifferentClassLoaders(type, extensionClass); + } + } + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + } + } + + if (result.isEmpty()) { + log.debug("No extensions found for extension point '{}'", type.getName()); + } else { + log.debug("Found {} extensions for extension point '{}'", result.size(), type.getName()); + } + + // sort by "ordinal" property + Collections.sort(result); + + return result; + } + + @Override + public List find(String pluginId) { + log.debug("Finding extensions from plugin '{}'", pluginId); + List result = new ArrayList<>(); + + Set classNames = findClassNames(pluginId); + if (classNames.isEmpty()) { + return result; + } + + if (pluginId != null) { + PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); + if (PluginState.STARTED != pluginWrapper.getPluginState()) { + return result; + } + + log.trace("Checking extensions from plugin '{}'", pluginId); + } else { + log.trace("Checking extensions from classpath"); + } + + ClassLoader classLoader = (pluginId != null) ? pluginManager.getPluginClassLoader(pluginId) : getClass().getClassLoader(); + + for (String className : classNames) { + try { + log.debug("Loading class '{}' using class loader '{}'", className, classLoader); + Class extensionClass = classLoader.loadClass(className); + + ExtensionWrapper extensionWrapper = createExtensionWrapper(extensionClass); + result.add(extensionWrapper); + log.debug("Added extension '{}' with ordinal {}", className, extensionWrapper.getOrdinal()); + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + } + } + + if (result.isEmpty()) { + log.debug("No extensions found for plugin '{}'", pluginId); + } else { + log.debug("Found {} extensions for plugin '{}'", result.size(), pluginId); + } + + // sort by "ordinal" property + Collections.sort(result); + + return result; + } + + @Override + public Set findClassNames(String pluginId) { + return getEntries().get(pluginId); + } + + @Override + public void pluginStateChanged(PluginStateEvent event) { + // TODO optimize (do only for some transitions) + // clear cache + entries = null; + } + + protected void debugExtensions(Set extensions) { + if (log.isDebugEnabled()) { + if (extensions.isEmpty()) { + log.debug("No extensions found"); + } else { + log.debug("Found possible {} extensions:", extensions.size()); + for (String extension : extensions) { + log.debug(" " + extension); + } + } + } + } + + private Map> readStorages() { + Map> result = new LinkedHashMap<>(); + + result.putAll(readClasspathStorages()); + result.putAll(readPluginsStorages()); + + return result; + } + + private Map> getEntries() { + if (entries == null) { + entries = readStorages(); + } + + return entries; + } + + private ExtensionWrapper createExtensionWrapper(Class extensionClass) { + ExtensionDescriptor descriptor = new ExtensionDescriptor(); + int ordinal = 0; + if (extensionClass.isAnnotationPresent(Extension.class)) { + ordinal = extensionClass.getAnnotation(Extension.class).ordinal(); + } + descriptor.setOrdinal(ordinal); + descriptor.setExtensionClass(extensionClass); + + ExtensionWrapper extensionWrapper = new ExtensionWrapper<>(descriptor); + extensionWrapper.setExtensionFactory(pluginManager.getExtensionFactory()); + + return extensionWrapper; + } + + private void checkDifferentClassLoaders(Class type, Class extensionClass) { + ClassLoader typeClassLoader = type.getClassLoader(); // class loader of extension point + ClassLoader extensionClassLoader = extensionClass.getClassLoader(); + boolean match = ClassUtils.getAllInterfacesNames(extensionClass).contains(type.getSimpleName()); + if (match && !extensionClassLoader.equals(typeClassLoader)) { + // in this scenario the method 'isAssignableFrom' returns only FALSE + // see http://www.coderanch.com/t/557846/java/java/FWIW-FYI-isAssignableFrom-isInstance-differing + log.error("Different class loaders: '{}' (E) and '{}' (EP)", extensionClassLoader, typeClassLoader); + } + } + +} diff --git a/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java new file mode 100644 index 0000000..d2f2b42 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/AbstractPluginManager.java @@ -0,0 +1,900 @@ +/* + * Copyright 2016 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.pf4j.util.StringUtils; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * This class implements the boilerplate plugin code that any {@link PluginManager} + * implementation would have to support. + * It helps cut the noise out of the subclass that handles plugin management. + * + * @author Decebal Suiu + */ +public abstract class AbstractPluginManager implements PluginManager { + + private static final Logger log = LoggerFactory.getLogger(AbstractPluginManager.class); + + private Path pluginsRoot; + + private ExtensionFinder extensionFinder; + + private PluginDescriptorFinder pluginDescriptorFinder; + + /* + * A map of plugins this manager is responsible for (the key is the 'pluginId'). + */ + protected Map plugins; + + /* + * A map of plugin class loaders (the key is the 'pluginId'). + */ + private Map pluginClassLoaders; + + /* + * A list with unresolved plugins (unresolved dependency). + */ + private List unresolvedPlugins; + + /** + * A list with all resolved plugins (resolved dependency). + */ + private List resolvedPlugins; + + /* + * A list with started plugins. + */ + private List startedPlugins; + + /* + * The registered {@link PluginStateListener}s. + */ + private List pluginStateListeners; + + /* + * Cache value for the runtime mode. + * No need to re-read it because it wont change at runtime. + */ + private RuntimeMode runtimeMode; + + /* + * The system version used for comparisons to the plugin requires attribute. + */ + private String systemVersion = "0.0.0"; + + private PluginRepository pluginRepository; + private PluginFactory pluginFactory; + private ExtensionFactory extensionFactory; + private PluginStatusProvider pluginStatusProvider; + private DependencyResolver dependencyResolver; + private PluginLoader pluginLoader; + private boolean exactVersionAllowed = false; + + private VersionManager versionManager; + + /** + * The plugins root is supplied by {@code System.getProperty("pf4j.pluginsDir", "plugins")}. + */ + public AbstractPluginManager() { + initialize(); + } + + /** + * Constructs {@code AbstractPluginManager} with the given plugins root. + * + * @param pluginsRoot the root to search for plugins + */ + public AbstractPluginManager(Path pluginsRoot) { + this.pluginsRoot = pluginsRoot; + + initialize(); + } + + @Override + public void setSystemVersion(String version) { + systemVersion = version; + } + + @Override + public String getSystemVersion() { + return systemVersion; + } + + /** + * Returns a copy of plugins. + * + * @return + */ + @Override + public List getPlugins() { + return new ArrayList<>(plugins.values()); + } + + /** + * Returns a copy of plugins with that state. + * + * @param pluginState + * @return + */ + @Override + public List getPlugins(PluginState pluginState) { + List plugins = new ArrayList<>(); + for (PluginWrapper plugin : getPlugins()) { + if (pluginState.equals(plugin.getPluginState())) { + plugins.add(plugin); + } + } + + return plugins; + } + + @Override + public List getResolvedPlugins() { + return resolvedPlugins; + } + + @Override + public List getUnresolvedPlugins() { + return unresolvedPlugins; + } + + @Override + public List getStartedPlugins() { + return startedPlugins; + } + + @Override + public PluginWrapper getPlugin(String pluginId) { + return plugins.get(pluginId); + } + + @Override + public String loadPlugin(Path pluginPath) { + if ((pluginPath == null) || Files.notExists(pluginPath)) { + throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginPath)); + } + + log.debug("Loading plugin from '{}'", pluginPath); + + try { + PluginWrapper pluginWrapper = loadPluginFromPath(pluginPath); + // TODO uninstalled plugin dependencies? + getUnresolvedPlugins().remove(pluginWrapper); + getResolvedPlugins().add(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, null)); + + return pluginWrapper.getDescriptor().getPluginId(); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + + return null; + } + + /** + * Load plugins. + */ + @Override + public void loadPlugins() { + log.debug("Lookup plugins in '{}'", pluginsRoot); + // check for plugins root + if (Files.notExists(pluginsRoot) || !Files.isDirectory(pluginsRoot)) { + log.error("No '{}' root", pluginsRoot); + return; + } + + // get all plugin paths from repository + List pluginPaths = pluginRepository.getPluginPaths(); + + // check for no plugins + if (pluginPaths.isEmpty()) { + log.info("No plugins"); + return; + } + + log.debug("Found {} possible plugins: {}", pluginPaths.size(), pluginPaths); + + // load plugins from plugin paths + // TODO + for (Path pluginPath : pluginPaths) { + try { + loadPluginFromPath(pluginPath); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + + // resolve 'unresolvedPlugins' + try { + resolvePlugins(); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + + /** + * Unload the specified plugin and it's dependents. + */ + @Override + public boolean unloadPlugin(String pluginId) { + return unloadPlugin(pluginId, true); + } + + private boolean unloadPlugin(String pluginId, boolean unloadDependents) { + try { + if (unloadDependents) { + List dependents = dependencyResolver.getDependents(pluginId); + while (!dependents.isEmpty()) { + String dependent = dependents.remove(0); + unloadPlugin(dependent, false); + dependents.addAll(0, dependencyResolver.getDependents(dependent)); + } + } + + PluginState pluginState = stopPlugin(pluginId, false); + if (PluginState.STARTED == pluginState) { + return false; + } + + PluginWrapper pluginWrapper = getPlugin(pluginId); + log.info("Unload plugin '{}'", getPluginLabel(pluginWrapper.getDescriptor())); + + // remove the plugin + plugins.remove(pluginId); + getResolvedPlugins().remove(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + + // remove the classloader + Map pluginClassLoaders = getPluginClassLoaders(); + if (pluginClassLoaders.containsKey(pluginId)) { + ClassLoader classLoader = pluginClassLoaders.remove(pluginId); + if (classLoader instanceof Closeable) { + try { + ((Closeable) classLoader).close(); + } catch (IOException e) { + log.error("Cannot close classloader", e); + } + } + } + + return true; + } catch (IllegalArgumentException e) { + // ignore not found exceptions because this method is recursive + } + + return false; + } + + @Override + public boolean deletePlugin(String pluginId) { + checkPluginId(pluginId); + + PluginWrapper pluginWrapper = getPlugin(pluginId); + PluginState pluginState = stopPlugin(pluginId); + if (PluginState.STARTED == pluginState) { + log.error("Failed to stop plugin '{}' on delete", pluginId); + return false; + } + + if (!unloadPlugin(pluginId)) { + log.error("Failed to unload plugin '{}' on delete", pluginId); + return false; + } + + Path pluginPath = pluginWrapper.getPluginPath(); + + return pluginRepository.deletePluginPath(pluginPath); + } + + /** + * Start all active plugins. + */ + @Override + public void startPlugins() { + for (PluginWrapper pluginWrapper : resolvedPlugins) { + PluginState pluginState = pluginWrapper.getPluginState(); + if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { + try { + log.info("Start plugin '{}'", getPluginLabel(pluginWrapper.getDescriptor())); + pluginWrapper.getPlugin().start(); + pluginWrapper.setPluginState(PluginState.STARTED); + startedPlugins.add(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + } + } + + /** + * Start the specified plugin and it's dependencies. + */ + @Override + public PluginState startPlugin(String pluginId) { + checkPluginId(pluginId); + + PluginWrapper pluginWrapper = getPlugin(pluginId); + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STARTED == pluginState) { + log.debug("Already started plugin '{}'", getPluginLabel(pluginDescriptor)); + return PluginState.STARTED; + } + + if (PluginState.DISABLED == pluginState) { + // automatically enable plugin on manual plugin start + if (!enablePlugin(pluginId)) { + return pluginState; + } + } + + for (PluginDependency dependency : pluginDescriptor.getDependencies()) { + startPlugin(dependency.getPluginId()); + } + + try { + log.info("Start plugin '{}'", getPluginLabel(pluginDescriptor)); + pluginWrapper.getPlugin().start(); + pluginWrapper.setPluginState(PluginState.STARTED); + startedPlugins.add(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + + return pluginWrapper.getPluginState(); + } + + /** + * Stop all active plugins. + */ + @Override + public void stopPlugins() { + // stop started plugins in reverse order + Collections.reverse(startedPlugins); + Iterator itr = startedPlugins.iterator(); + while (itr.hasNext()) { + PluginWrapper pluginWrapper = itr.next(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STARTED == pluginState) { + try { + log.info("Stop plugin '{}'", getPluginLabel(pluginWrapper.getDescriptor())); + pluginWrapper.getPlugin().stop(); + pluginWrapper.setPluginState(PluginState.STOPPED); + itr.remove(); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + } + } + } + + /** + * Stop the specified plugin and it's dependents. + */ + @Override + public PluginState stopPlugin(String pluginId) { + return stopPlugin(pluginId, true); + } + + private PluginState stopPlugin(String pluginId, boolean stopDependents) { + checkPluginId(pluginId); + + PluginWrapper pluginWrapper = getPlugin(pluginId); + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.STOPPED == pluginState) { + log.debug("Already stopped plugin '{}'", getPluginLabel(pluginDescriptor)); + return PluginState.STOPPED; + } + + // test for disabled plugin + if (PluginState.DISABLED == pluginState) { + // do nothing + return pluginState; + } + + if (stopDependents) { + List dependents = dependencyResolver.getDependents(pluginId); + while (!dependents.isEmpty()) { + String dependent = dependents.remove(0); + stopPlugin(dependent, false); + dependents.addAll(0, dependencyResolver.getDependents(dependent)); + } + } + + try { + log.info("Stop plugin '{}'", getPluginLabel(pluginDescriptor)); + pluginWrapper.getPlugin().stop(); + pluginWrapper.setPluginState(PluginState.STOPPED); + startedPlugins.remove(pluginWrapper); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + } catch (PluginException e) { + log.error(e.getMessage(), e); + } + + return pluginWrapper.getPluginState(); + } + + private void checkPluginId(String pluginId) { + if (!plugins.containsKey(pluginId)) { + throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); + } + } + + @Override + public boolean disablePlugin(String pluginId) { + checkPluginId(pluginId); + + PluginWrapper pluginWrapper = getPlugin(pluginId); + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.DISABLED == pluginState) { + log.debug("Already disabled plugin '{}'", getPluginLabel(pluginDescriptor)); + return true; + } + + if (PluginState.STOPPED == stopPlugin(pluginId)) { + pluginWrapper.setPluginState(PluginState.DISABLED); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); + + if (!pluginStatusProvider.disablePlugin(pluginId)) { + return false; + } + + log.info("Disabled plugin '{}'", getPluginLabel(pluginDescriptor)); + + return true; + } + + return false; + } + + @Override + public boolean enablePlugin(String pluginId) { + checkPluginId(pluginId); + + PluginWrapper pluginWrapper = getPlugin(pluginId); + if (!isPluginValid(pluginWrapper)) { + log.warn("Plugin '{}' can not be enabled", getPluginLabel(pluginWrapper.getDescriptor())); + return false; + } + + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + PluginState pluginState = pluginWrapper.getPluginState(); + if (PluginState.DISABLED != pluginState) { + log.debug("Plugin '{}' is not disabled", getPluginLabel(pluginDescriptor)); + return true; + } + + if (!pluginStatusProvider.enablePlugin(pluginId)) { + return false; + } + + pluginWrapper.setPluginState(PluginState.CREATED); + + firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); + + log.info("Enabled plugin '{}'", getPluginLabel(pluginDescriptor)); + + return true; + } + + /** + * Get the {@link ClassLoader} for plugin. + */ + @Override + public ClassLoader getPluginClassLoader(String pluginId) { + return pluginClassLoaders.get(pluginId); + } + + @Override + public List getExtensions(Class type) { + List> extensionsWrapper = extensionFinder.find(type); + List extensions = new ArrayList<>(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + extensions.add(extensionWrapper.getExtension()); + } + + return extensions; + } + + @Override + public List getExtensions(Class type, String pluginId) { + List> extensionsWrapper = extensionFinder.find(type, pluginId); + List extensions = new ArrayList<>(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + extensions.add(extensionWrapper.getExtension()); + } + + return extensions; + } + + @Override + @SuppressWarnings("unchecked") + public List getExtensions(String pluginId) { + List extensionsWrapper = extensionFinder.find(pluginId); + List extensions = new ArrayList<>(extensionsWrapper.size()); + for (ExtensionWrapper extensionWrapper : extensionsWrapper) { + extensions.add(extensionWrapper.getExtension()); + } + + return extensions; + } + + @Override + public Set getExtensionClassNames(String pluginId) { + return extensionFinder.findClassNames(pluginId); + } + + @Override + public ExtensionFactory getExtensionFactory() { + return extensionFactory; + } + + // TODO remove + public PluginLoader getPluginLoader() { + return pluginLoader; + } + + public Path getPluginsRoot() { + return pluginsRoot; + } + + @Override + public RuntimeMode getRuntimeMode() { + if (runtimeMode == null) { + // retrieves the runtime mode from system + String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString()); + runtimeMode = RuntimeMode.byName(modeAsString); + } + + return runtimeMode; + } + + @Override + public PluginWrapper whichPlugin(Class clazz) { + ClassLoader classLoader = clazz.getClassLoader(); + for (PluginWrapper plugin : resolvedPlugins) { + if (plugin.getPluginClassLoader() == classLoader) { + return plugin; + } + } + + return null; + } + + @Override + public synchronized void addPluginStateListener(PluginStateListener listener) { + pluginStateListeners.add(listener); + } + + @Override + public synchronized void removePluginStateListener(PluginStateListener listener) { + pluginStateListeners.remove(listener); + } + + public String getVersion() { + String version = null; + + Package pf4jPackage = PluginManager.class.getPackage(); + if (pf4jPackage != null) { + version = pf4jPackage.getImplementationVersion(); + if (version == null) { + version = pf4jPackage.getSpecificationVersion(); + } + } + + return (version != null) ? version : "0.0.0"; + } + + protected abstract PluginRepository createPluginRepository(); + + protected abstract PluginFactory createPluginFactory(); + + protected abstract ExtensionFactory createExtensionFactory(); + + protected abstract PluginDescriptorFinder createPluginDescriptorFinder(); + + protected abstract ExtensionFinder createExtensionFinder(); + + protected abstract PluginStatusProvider createPluginStatusProvider(); + + protected abstract PluginLoader createPluginLoader(); + + protected abstract VersionManager createVersionManager(); + + protected PluginDescriptorFinder getPluginDescriptorFinder() { + return pluginDescriptorFinder; + } + + protected PluginFactory getPluginFactory() { + return pluginFactory; + } + + protected Map getPluginClassLoaders() { + return pluginClassLoaders; + } + + protected void initialize() { + plugins = new HashMap<>(); + pluginClassLoaders = new HashMap<>(); + unresolvedPlugins = new ArrayList<>(); + resolvedPlugins = new ArrayList<>(); + startedPlugins = new ArrayList<>(); + + pluginStateListeners = new ArrayList<>(); + + if (pluginsRoot == null) { + pluginsRoot = createPluginsRoot(); + } + + System.setProperty("pf4j.pluginsDir", pluginsRoot.toString()); + + pluginRepository = createPluginRepository(); + pluginFactory = createPluginFactory(); + extensionFactory = createExtensionFactory(); + pluginDescriptorFinder = createPluginDescriptorFinder(); + extensionFinder = createExtensionFinder(); + pluginStatusProvider = createPluginStatusProvider(); + pluginLoader = createPluginLoader(); + + versionManager = createVersionManager(); + dependencyResolver = new DependencyResolver(versionManager); + } + + /** + * Add the possibility to override the plugins root. + * If a {@code pf4j.pluginsDir} system property is defined than this method returns that root. + * If {@link #getRuntimeMode()} returns {@link RuntimeMode#DEVELOPMENT} than {@code ../plugins} + * is returned else this method returns {@code plugins}. + * + * @return the plugins root + */ + protected Path createPluginsRoot() { + String pluginsDir = System.getProperty("pf4j.pluginsDir"); + if (pluginsDir == null) { + if (isDevelopment()) { + pluginsDir = "../plugins"; + } else { + pluginsDir = "plugins"; + } + } + + return Paths.get(pluginsDir); + } + + /** + * Check if this plugin is valid (satisfies "requires" param) for a given system version. + * + * @param pluginWrapper the plugin to check + * @return true if plugin satisfies the "requires" or if requires was left blank + */ + protected boolean isPluginValid(PluginWrapper pluginWrapper) { + String requires = pluginWrapper.getDescriptor().getRequires().trim(); + if (!isExactVersionAllowed() && requires.matches("^\\d+\\.\\d+\\.\\d+$")) { + // If exact versions are not allowed in requires, rewrite to >= expression + requires = ">=" + requires; + } + if (systemVersion.equals("0.0.0") || versionManager.satisfies(requires, systemVersion)) { + return true; + } + + PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); + log.warn("Plugin '{}' requires a minimum system version of {}, and you have {}", + getPluginLabel(pluginDescriptor), + pluginWrapper.getDescriptor().getRequires(), + getSystemVersion()); + + return false; + } + + protected boolean isPluginDisabled(String pluginId) { + return pluginStatusProvider.isPluginDisabled(pluginId); + } + + protected void resolvePlugins() throws PluginException { + // retrieves the plugins descriptors from "unresolvedPlugins" list + List descriptors = new ArrayList<>(); + for (PluginWrapper plugin : unresolvedPlugins) { + descriptors.add(plugin.getDescriptor()); + } + + DependencyResolver.Result result = dependencyResolver.resolve(descriptors); + + if (result.hasCyclicDependency()) { + throw new DependencyResolver.CyclicDependencyException(); + } + + List notFoundDependencies = result.getNotFoundDependencies(); + if (!notFoundDependencies.isEmpty()) { + throw new DependencyResolver.DependenciesNotFoundException(notFoundDependencies); + } + + List wrongVersionDependencies = result.getWrongVersionDependencies(); + if (!wrongVersionDependencies.isEmpty()) { + throw new DependencyResolver.DependenciesWrongVersionException(wrongVersionDependencies); + } + + List sortedPlugins = result.getSortedPlugins(); + + // move plugins from "unresolved" to "resolved" + for (String pluginId : sortedPlugins) { + PluginWrapper pluginWrapper = plugins.get(pluginId); + unresolvedPlugins.remove(pluginWrapper); + resolvedPlugins.add(pluginWrapper); + log.info("Plugin '{}' resolved", getPluginLabel(pluginWrapper.getDescriptor())); + } + } + + protected synchronized void firePluginStateEvent(PluginStateEvent event) { + for (PluginStateListener listener : pluginStateListeners) { + log.debug("Fire '{}' to '{}'", event, listener); + listener.pluginStateChanged(event); + } + } + + protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { + // test for plugin duplication + String pluginId = idForPath(pluginPath); + if (pluginId != null) { + log.warn("Plugin '{}' already loaded with id '{}'", pluginPath, pluginId); + return null; + } + + // retrieves the plugin descriptor + log.debug("Finding plugin descriptor for plugin '{}'", pluginPath); + PluginDescriptor pluginDescriptor = getPluginDescriptorFinder().find(pluginPath); + validatePluginDescriptor(pluginDescriptor); + log.debug("Found descriptor {}", pluginDescriptor); + String pluginClassName = pluginDescriptor.getPluginClass(); + log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); + + // load plugin + log.debug("Loading plugin '{}'", pluginPath); + ClassLoader pluginClassLoader = getPluginLoader().loadPlugin(pluginPath, pluginDescriptor); + log.debug("Loaded plugin '{}' with class loader '{}'", pluginPath, pluginClassLoader); + + // create the plugin wrapper + log.debug("Creating wrapper for plugin '{}'", pluginPath); + PluginWrapper pluginWrapper = new PluginWrapper(this, pluginDescriptor, pluginPath, pluginClassLoader); + pluginWrapper.setPluginFactory(getPluginFactory()); + pluginWrapper.setRuntimeMode(getRuntimeMode()); + + // test for disabled plugin + if (isPluginDisabled(pluginDescriptor.getPluginId())) { + log.info("Plugin '{}' is disabled", pluginPath); + pluginWrapper.setPluginState(PluginState.DISABLED); + } + + // validate the plugin + if (!isPluginValid(pluginWrapper)) { + log.info("Plugin '{}' is disabled", pluginPath); + pluginWrapper.setPluginState(PluginState.DISABLED); + } + + log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); + + pluginId = pluginDescriptor.getPluginId(); + + // add plugin to the list with plugins + plugins.put(pluginId, pluginWrapper); + getUnresolvedPlugins().add(pluginWrapper); + + // add plugin class loader to the list with class loaders + getPluginClassLoaders().put(pluginId, pluginClassLoader); + + return pluginWrapper; + } + + /** + * Tests for already loaded plugins on given path. + * + * @param pluginPath the path to investigate + * @return id of plugin or null if not loaded + */ + protected String idForPath(Path pluginPath) { + for (PluginWrapper plugin : plugins.values()) { + if (plugin.getPluginPath().equals(pluginPath)) { + return plugin.getPluginId(); + } + } + + return null; + } + + /** + * Override this to change the validation criteria. + * + * @param descriptor the plugin descriptor to validate + * @throws PluginException if validation fails + */ + protected void validatePluginDescriptor(PluginDescriptor descriptor) throws PluginException { + if (StringUtils.isEmpty(descriptor.getPluginId())) { + throw new PluginException("Field 'id' cannot be empty"); + } + + if (StringUtils.isEmpty(descriptor.getPluginClass())) { + throw new PluginException("Field 'class' cannot be empty"); + } + + if (descriptor.getVersion() == null) { + throw new PluginException("Field 'version' cannot be empty"); + } + } + + // TODO add this method in PluginManager as default method for Java 8. + protected boolean isDevelopment() { + return RuntimeMode.DEVELOPMENT.equals(getRuntimeMode()); + } + + /** + * @return true if exact versions in requires is allowed + */ + public boolean isExactVersionAllowed() { + return exactVersionAllowed; + } + + /** + * Set to true to allow requires expression to be exactly x.y.z. + * The default is false, meaning that using an exact version x.y.z will + * implicitly mean the same as >=x.y.z + * + * @param exactVersionAllowed set to true or false + */ + public void setExactVersionAllowed(boolean exactVersionAllowed) { + this.exactVersionAllowed = exactVersionAllowed; + } + + @Override + public VersionManager getVersionManager() { + return versionManager; + } + + /** + * The plugin label is used in logging and it's a string in format {@code pluginId@pluginVersion}. + * @param pluginDescriptor + * @return + */ + protected String getPluginLabel(PluginDescriptor pluginDescriptor) { + return pluginDescriptor.getPluginId() + "@" + pluginDescriptor.getVersion(); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/BasePluginRepository.java b/pf4j/src/main/java/org/pf4j/BasePluginRepository.java new file mode 100644 index 0000000..c5d5281 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/BasePluginRepository.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.pf4j.util.FileUtils; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * @author Decebal Suiu + * @author Mário Franco + */ +public class BasePluginRepository implements PluginRepository { + + protected final Path pluginsRoot; + protected FileFilter filter; + + public BasePluginRepository(Path pluginsRoot) { + this.pluginsRoot = pluginsRoot; + } + + public BasePluginRepository(Path pluginsRoot, FileFilter filter) { + this.pluginsRoot = pluginsRoot; + this.filter = filter; + } + + public void setFilter(FileFilter filter) { + this.filter = filter; + } + + @Override + public List getPluginPaths() { + File[] files = pluginsRoot.toFile().listFiles(filter); + + if ((files == null) || files.length == 0) { + return Collections.emptyList(); + } + + List paths = new ArrayList<>(files.length); + for (File file : files) { + paths.add(file.toPath()); + } + + return paths; + } + + @Override + public boolean deletePluginPath(Path pluginPath) { + try { + FileUtils.delete(pluginPath); + return true; + } catch (NoSuchFileException nsf) { + return false; // Return false on not found to be compatible with previous API + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java b/pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java new file mode 100644 index 0000000..105929c --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/CompoundPluginRepository.java @@ -0,0 +1,55 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Decebal Suiu + * @author Mário Franco + */ +public class CompoundPluginRepository implements PluginRepository { + + private final PluginRepository[] repositories; + + public CompoundPluginRepository(PluginRepository... repositories) { + this.repositories = repositories; + } + + @Override + public List getPluginPaths() { + List paths = new ArrayList<>(); + for (PluginRepository repository : repositories) { + paths.addAll(repository.getPluginPaths()); + } + + return paths; + } + + @Override + public boolean deletePluginPath(Path pluginPath) { + for (PluginRepository repository : repositories) { + if (repository.deletePluginPath(pluginPath)) { + return true; + } + } + + return false; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java b/pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java new file mode 100644 index 0000000..7a2683b --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultExtensionFactory.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The default implementation for ExtensionFactory. + * It uses Class.newInstance() method. + * + * @author Decebal Suiu + */ +public class DefaultExtensionFactory implements ExtensionFactory { + + private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFactory.class); + + /** + * Creates an extension instance. If an error occurs than that error is logged and the method returns null. + * @param extensionClass + * @return + */ + @Override + public Object create(Class extensionClass) { + log.debug("Create instance for extension '{}'", extensionClass.getName()); + try { + return extensionClass.newInstance(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + return null; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java new file mode 100644 index 0000000..43a03fb --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultExtensionFinder.java @@ -0,0 +1,103 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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 java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * The default implementation for {@link ExtensionFinder}. + * It's a compound {@code ExtensionFinder}. + * + * @author Decebal Suiu + */ +public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListener { + + protected PluginManager pluginManager; + protected List finders = new ArrayList<>(); + + public DefaultExtensionFinder(PluginManager pluginManager) { + this.pluginManager = pluginManager; + + finders = new ArrayList<>(); + + addExtensionFinder(new LegacyExtensionFinder(pluginManager)); +// addExtensionFinder(new ServiceProviderExtensionFinder(pluginManager)); + } + + @Override + public List> find(Class type) { + List> extensions = new ArrayList<>(); + for (ExtensionFinder finder : finders) { + extensions.addAll(finder.find(type)); + } + + return extensions; + } + + @Override + public List> find(Class type, String pluginId) { + List> extensions = new ArrayList<>(); + for (ExtensionFinder finder : finders) { + extensions.addAll(finder.find(type, pluginId)); + } + + return extensions; + } + + @Override + public List find(String pluginId) { + List extensions = new ArrayList<>(); + for (ExtensionFinder finder : finders) { + extensions.addAll(finder.find(pluginId)); + } + + return extensions; + } + + + @Override + public Set findClassNames(String pluginId) { + Set classNames = new HashSet<>(); + for (ExtensionFinder finder : finders) { + classNames.addAll(finder.findClassNames(pluginId)); + } + + return classNames; + } + + @Override + public void pluginStateChanged(PluginStateEvent event) { + for (ExtensionFinder finder : finders) { + if (finder instanceof PluginStateListener) { + ((PluginStateListener) finder).pluginStateChanged(event); + } + } + } + + public DefaultExtensionFinder addServiceProviderExtensionFinder() { + return addExtensionFinder(new ServiceProviderExtensionFinder(pluginManager)); + } + + public DefaultExtensionFinder addExtensionFinder(ExtensionFinder finder) { + finders.add(finder); + + return this; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java b/pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java new file mode 100644 index 0000000..c62f223 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginClasspath.java @@ -0,0 +1,32 @@ +/* + * Copyright 2016 Decebal Suiu + * + * 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; + +/** + * The default values are {@code classes} and {@code lib}. + * + * @author Decebal Suiu + */ +public class DefaultPluginClasspath extends PluginClasspath { + + public DefaultPluginClasspath() { + super(); + + addClassesDirectories("classes"); + addLibDirectories("lib"); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java new file mode 100644 index 0000000..c803f07 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginDescriptorFinder.java @@ -0,0 +1,64 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.jar.Manifest; + +/** + * The default implementation for {@link PluginDescriptorFinder}. + * Now, this class it's a "link" to {@link ManifestPluginDescriptorFinder}. + * + * @author Decebal Suiu + */ +public class DefaultPluginDescriptorFinder extends ManifestPluginDescriptorFinder { + + private static final Logger log = LoggerFactory.getLogger(ManifestPluginDescriptorFinder.class); + + private PluginClasspath pluginClasspath; + + public DefaultPluginDescriptorFinder(PluginClasspath pluginClasspath) { + this.pluginClasspath = pluginClasspath; + } + + @Override + public Manifest readManifest(Path pluginPath) throws PluginException { + // TODO it's ok with first classes root? Another idea is to specify in PluginClasspath the folder. + if (pluginClasspath.getClassesDirectories().size() == 0) { + throw new PluginException("Failed to read manifest, no classes folder in classpath"); + } + String classes = pluginClasspath.getClassesDirectories().get(0); + Path manifestPath = pluginPath.resolve(Paths.get(classes,"/META-INF/MANIFEST.MF")); + log.debug("Lookup plugin descriptor in '{}'", manifestPath); + if (Files.notExists(manifestPath)) { + throw new PluginException("Cannot find '{}' path", manifestPath); + } + + try (InputStream input = Files.newInputStream(manifestPath)) { + return new Manifest(input); + } catch (IOException e) { + throw new PluginException(e.getMessage(), e); + } + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginFactory.java b/pf4j/src/main/java/org/pf4j/DefaultPluginFactory.java new file mode 100644 index 0000000..93b0a19 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginFactory.java @@ -0,0 +1,72 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +/** + * The default implementation for {@link PluginFactory}. + * It uses {@link Class#newInstance()} method. + * + * @author Decebal Suiu + */ +public class DefaultPluginFactory implements PluginFactory { + + private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFactory.class); + + /** + * Creates a plugin instance. If an error occurs than that error is logged and the method returns null. + * @param pluginWrapper + * @return + */ + @Override + public Plugin create(final PluginWrapper pluginWrapper) { + String pluginClassName = pluginWrapper.getDescriptor().getPluginClass(); + log.debug("Create instance for plugin '{}'", pluginClassName); + + Class pluginClass; + try { + pluginClass = pluginWrapper.getPluginClassLoader().loadClass(pluginClassName); + } catch (ClassNotFoundException e) { + log.error(e.getMessage(), e); + return null; + } + + // once we have the class, we can do some checks on it to ensure + // that it is a valid implementation of a plugin. + int modifiers = pluginClass.getModifiers(); + if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers) + || (!Plugin.class.isAssignableFrom(pluginClass))) { + log.error("The plugin class '{}' is not valid", pluginClassName); + return null; + } + + // create the plugin instance + try { + Constructor constructor = pluginClass.getConstructor(PluginWrapper.class); + return (Plugin) constructor.newInstance(pluginWrapper); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + + return null; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java b/pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java new file mode 100644 index 0000000..bdd31eb --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginLoader.java @@ -0,0 +1,80 @@ +/* + * Copyright 2016 Decebal Suiu + * + * 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.pf4j.util.FileUtils; + +import java.io.File; +import java.nio.file.Path; +import java.util.List; + +/** + * Load all information needed by a plugin. + * This means add to classpath all jar files from {@code lib} directory + * and all class files from {@code classes}. + * + * @author Decebal Suiu + */ +public class DefaultPluginLoader implements PluginLoader { + + protected PluginManager pluginManager; + protected PluginClasspath pluginClasspath; + + public DefaultPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { + this.pluginManager = pluginManager; + this.pluginClasspath = pluginClasspath; + } + + @Override + public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { + PluginClassLoader pluginClassLoader = createPluginClassLoader(pluginPath, pluginDescriptor); + + loadClasses(pluginPath, pluginClassLoader); + loadJars(pluginPath, pluginClassLoader); + + return pluginClassLoader; + } + + protected PluginClassLoader createPluginClassLoader(Path pluginPath, PluginDescriptor pluginDescriptor) { + return new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); + } + + /** + * Add all {@code *.class} files from {@code classes} directories to plugin class loader. + */ + protected void loadClasses(Path pluginPath, PluginClassLoader pluginClassLoader) { + for (String directory : pluginClasspath.getClassesDirectories()) { + File file = pluginPath.resolve(directory).toFile(); + if (file.exists() && file.isDirectory()) { + pluginClassLoader.addFile(file); + } + } + } + + /** + * Add all {@code *.jar} files from {@code lib} directories to plugin class loader. + */ + protected void loadJars(Path pluginPath, PluginClassLoader pluginClassLoader) { + for (String libDirectory : pluginClasspath.getLibDirectories()) { + Path file = pluginPath.resolve(libDirectory); + List jars = FileUtils.getJars(file); + for (File jar : jars) { + pluginClassLoader.addFile(jar); + } + } + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java new file mode 100644 index 0000000..0d79e82 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginManager.java @@ -0,0 +1,134 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.pf4j.util.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.nio.file.Path; + +/** + * Default implementation of the {@link PluginManager} interface. + * + * @author Decebal Suiu + */ +public class DefaultPluginManager extends AbstractPluginManager { + + private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); + + protected PluginClasspath pluginClasspath; + + public DefaultPluginManager() { + super(); + } + + @Deprecated + public DefaultPluginManager(File pluginsDir) { + this(pluginsDir.toPath()); + } + + public DefaultPluginManager(Path pluginsRoot) { + super(pluginsRoot); + } + + /** + * By default if {@link DefaultPluginManager#isDevelopment()} returns {@code true} + * than a {@link PropertiesPluginDescriptorFinder} is returned + * else this method returns {@link DefaultPluginDescriptorFinder}. + */ + @Override + protected PluginDescriptorFinder createPluginDescriptorFinder() { + return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new DefaultPluginDescriptorFinder(pluginClasspath); + } + + @Override + protected ExtensionFinder createExtensionFinder() { + DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this); + addPluginStateListener(extensionFinder); + + return extensionFinder; + } + + @Override + protected PluginFactory createPluginFactory() { + return new DefaultPluginFactory(); + } + + @Override + protected ExtensionFactory createExtensionFactory() { + return new DefaultExtensionFactory(); + } + + @Override + protected PluginStatusProvider createPluginStatusProvider() { + return new DefaultPluginStatusProvider(getPluginsRoot()); + } + + @Override + protected PluginRepository createPluginRepository() { + return new DefaultPluginRepository(getPluginsRoot(), isDevelopment()); + } + + @Override + protected PluginLoader createPluginLoader() { + return new DefaultPluginLoader(this, pluginClasspath); + } + + @Override + protected VersionManager createVersionManager() { + return new DefaultVersionManager(); + } + + /** + * By default if {@link DefaultPluginManager#isDevelopment()} returns true + * than a {@link DevelopmentPluginClasspath} is returned + * else this method returns {@link DefaultPluginClasspath}. + */ + protected PluginClasspath createPluginClasspath() { + return isDevelopment() ? new DevelopmentPluginClasspath() : new DefaultPluginClasspath(); + } + + @Override + protected void initialize() { + pluginClasspath = createPluginClasspath(); + + super.initialize(); + + log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode()); + } + + /** + * Load a plugin from disk. If the path is a zip file, first unpack + * @param pluginPath plugin location on disk + * @return PluginWrapper for the loaded plugin or null if not loaded + * @throws PluginException if problems during load + */ + @Override + protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { + // First unzip any ZIP files + try { + pluginPath = FileUtils.expandIfZip(pluginPath); + } catch (Exception e) { + log.warn("Failed to unzip " + pluginPath, e); + return null; + } + + return super.loadPluginFromPath(pluginPath); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java b/pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java new file mode 100644 index 0000000..a97313f --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginRepository.java @@ -0,0 +1,83 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.pf4j.util.AndFileFilter; +import org.pf4j.util.DirectoryFileFilter; +import org.pf4j.util.FileUtils; +import org.pf4j.util.HiddenFilter; +import org.pf4j.util.NotFileFilter; +import org.pf4j.util.OrFileFilter; +import org.pf4j.util.ZipFileFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.pf4j.util.NameFileFilter; + +import java.io.File; +import java.io.FileFilter; +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +/** + * @author Decebal Suiu + */ +public class DefaultPluginRepository extends BasePluginRepository { + + private static final Logger log = LoggerFactory.getLogger(DefaultPluginRepository.class); + + public DefaultPluginRepository(Path pluginsRoot, boolean development) { + super(pluginsRoot); + + AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); + pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter(development))); + setFilter(pluginsFilter); + } + + @Override + public List getPluginPaths() { + // expand plugins zip files + File[] pluginZips = pluginsRoot.toFile().listFiles(new ZipFileFilter()); + if ((pluginZips != null) && pluginZips.length > 0) { + for (File pluginZip : pluginZips) { + try { + FileUtils.expandIfZip(pluginZip.toPath()); + } catch (IOException e) { + log.error("Cannot expand plugin zip '{}'", pluginZip); + log.error(e.getMessage(), e); + } + } + } + + return super.getPluginPaths(); + } + + @Override + public boolean deletePluginPath(Path pluginPath) { + FileUtils.optimisticDelete(FileUtils.findWithEnding(pluginPath, ".zip", ".ZIP", ".Zip")); + return super.deletePluginPath(pluginPath); + } + + protected FileFilter createHiddenPluginFilter(boolean development) { + OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); + + if (development) { + hiddenPluginFilter.addFileFilter(new NameFileFilter("target")); + } + + return hiddenPluginFilter; + } +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java b/pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java new file mode 100644 index 0000000..08c4607 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultPluginStatusProvider.java @@ -0,0 +1,98 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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.pf4j.util.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +/** + * The default implementation for {@link PluginStatusProvider}. + * + * @author Decebal Suiu + * @author Mário Franco + */ +public class DefaultPluginStatusProvider implements PluginStatusProvider { + + private static final Logger log = LoggerFactory.getLogger(DefaultPluginStatusProvider.class); + + private final Path pluginsRoot; + + private List enabledPlugins; + private List disabledPlugins; + + public DefaultPluginStatusProvider(Path pluginsRoot) { + this.pluginsRoot = pluginsRoot; + + initialize(); + } + + private void initialize() { + try { + // create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file) + enabledPlugins = FileUtils.readLines(pluginsRoot.resolve("enabled.txt"), true); + log.info("Enabled plugins: {}", enabledPlugins); + + // create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file) + disabledPlugins = FileUtils.readLines(pluginsRoot.resolve("disabled.txt"), true); + log.info("Disabled plugins: {}", disabledPlugins); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + + @Override + public boolean isPluginDisabled(String pluginId) { + if (disabledPlugins.contains(pluginId)) { + return true; + } + + return !enabledPlugins.isEmpty() && !enabledPlugins.contains(pluginId); + } + + @Override + public boolean disablePlugin(String pluginId) { + if (disabledPlugins.add(pluginId)) { + try { + FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); + } catch (IOException e) { + log.error("Failed to disable plugin {}", pluginId, e); + return false; + } + } + + return true; + } + + @Override + public boolean enablePlugin(String pluginId) { + try { + if (disabledPlugins.remove(pluginId)) { + FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); + } + } catch (IOException e) { + log.error("Failed to enable plugin {}", pluginId, e); + return false; + } + + return true; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DefaultVersionManager.java b/pf4j/src/main/java/org/pf4j/DefaultVersionManager.java new file mode 100644 index 0000000..96adf56 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DefaultVersionManager.java @@ -0,0 +1,33 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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 com.github.zafarkhaja.semver.Version; + +/** + * Default implementation for {@link VersionManager}. + * This implementation uses jSemVer (a Java implementation of the SemVer Specification). + * + * @author Decebal Suiu + */ +public class DefaultVersionManager implements VersionManager { + + @Override + public boolean satisfies(String constraint, String version) { + return Version.valueOf(version).satisfies(constraint); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DependencyResolver.java b/pf4j/src/main/java/org/pf4j/DependencyResolver.java new file mode 100644 index 0000000..05e8d11 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DependencyResolver.java @@ -0,0 +1,309 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.pf4j.util.DirectedGraph; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This class builds a dependency graph for a list of plugins (descriptors). + * The entry point is the {@link #resolve(List)} method, method that returns a {@link Result} object. + * The {@code Result} class contains nice information about the result of resolve operation (if it's a cyclic dependency, + * they are not found dependencies, they are dependencies with wrong version). + * This class is very useful for if-else scenarios. + * + * Only some attributes (pluginId, dependencies and pluginVersion) from {@link PluginDescriptor} are used in + * the process of {@code resolve} operation. + * + * @author Decebal Suiu + */ +public class DependencyResolver { + + private static final Logger log = LoggerFactory.getLogger(DependencyResolver.class); + + private VersionManager versionManager; + + private DirectedGraph dependenciesGraph; // the value is 'pluginId' + private DirectedGraph dependentsGraph; // the value is 'pluginId' + private boolean resolved; + + public DependencyResolver(VersionManager versionManager) { + this.versionManager = versionManager; + } + + public Result resolve(List plugins) { + // create graphs + dependenciesGraph = new DirectedGraph<>(); + dependentsGraph = new DirectedGraph<>(); + + // populate graphs + Map pluginByIds = new HashMap<>(); + for (PluginDescriptor plugin : plugins) { + addPlugin(plugin); + pluginByIds.put(plugin.getPluginId(), plugin); + } + + log.debug("Graph: {}", dependenciesGraph); + + // get a sorted list of dependencies + List sortedPlugins = dependenciesGraph.reverseTopologicalSort(); + log.debug("Plugins order: {}", sortedPlugins); + + // create the result object + Result result = new Result(sortedPlugins); + + resolved = true; + + if (sortedPlugins != null) { // no cyclic dependency + // detect not found dependencies + for (String pluginId : sortedPlugins) { + if (!pluginByIds.containsKey(pluginId)) { + result.addNotFoundDependency(pluginId); + } + } + } + + // check dependencies versions + for (PluginDescriptor plugin : plugins) { + String pluginId = plugin.getPluginId(); + String existingVersion = plugin.getVersion(); + + List dependents = getDependents(pluginId); + while (!dependents.isEmpty()) { + String dependentId = dependents.remove(0); + PluginDescriptor dependent = pluginByIds.get(dependentId); + String requiredVersion = getDependencyVersionSupport(dependent, pluginId); + boolean ok = checkDependencyVersion(requiredVersion, existingVersion); + if (!ok) { + result.addWrongDependencyVersion(new WrongDependencyVersion(pluginId, dependentId, existingVersion, requiredVersion)); + } + } + } + + return result; + } + + /** + * Retrieves the plugins ids that the given plugin id directly depends on. + * + * @param pluginId + * @return + */ + public List getDependencies(String pluginId) { + checkResolved(); + return dependenciesGraph.getNeighbors(pluginId); + } + + /** + * Retrieves the plugins ids that the given content is a direct dependency of. + * + * @param pluginId + * @return + */ + public List getDependents(String pluginId) { + checkResolved(); + return dependentsGraph.getNeighbors(pluginId); + } + + /** + * Check if an existing version of dependency is compatible with the required version (from plugin descriptor). + * + * @param requiredVersion + * @param existingVersion + * @return + */ + protected boolean checkDependencyVersion(String requiredVersion, String existingVersion) { + return versionManager.satisfies(requiredVersion, existingVersion); + } + + private void addPlugin(PluginDescriptor descriptor) { + String pluginId = descriptor.getPluginId(); + List dependencies = descriptor.getDependencies(); + if (dependencies.isEmpty()) { + dependenciesGraph.addVertex(pluginId); + dependentsGraph.addVertex(pluginId); + } else { + for (PluginDependency dependency : dependencies) { + dependenciesGraph.addEdge(pluginId, dependency.getPluginId()); + dependentsGraph.addEdge(dependency.getPluginId(), pluginId); + } + } + } + + private void checkResolved() { + if (!resolved) { + throw new IllegalStateException("Call 'resolve' method first"); + } + } + + private String getDependencyVersionSupport(PluginDescriptor dependent, String dependencyId) { + List dependencies = dependent.getDependencies(); + for (PluginDependency dependency : dependencies) { + if (dependencyId.equals(dependency.getPluginId())) { + return dependency.getPluginVersionSupport(); + } + } + + throw new IllegalStateException("Cannot find a dependency with id '" + dependencyId + + "' for plugin '" + dependent.getPluginId() + "'"); + } + + public static class Result { + + private boolean cyclicDependency; + private List notFoundDependencies; // value is "pluginId" + private List sortedPlugins; // value is "pluginId" + private List wrongVersionDependencies; + + Result(List sortedPlugins) { + if (sortedPlugins == null) { + cyclicDependency = true; + this.sortedPlugins = Collections.emptyList(); + } else { + this.sortedPlugins = new ArrayList<>(sortedPlugins); + } + + notFoundDependencies = new ArrayList<>(); + wrongVersionDependencies = new ArrayList<>(); + } + + /** + * Returns true is a cyclic dependency was detected. + */ + public boolean hasCyclicDependency() { + return cyclicDependency; + } + + /** + * Returns a list with dependencies required that were not found. + */ + public List getNotFoundDependencies() { + return notFoundDependencies; + } + + /** + * Returns a list with dependencies with wrong version. + */ + public List getWrongVersionDependencies() { + return wrongVersionDependencies; + } + + /** + * Get the list of plugins in dependency sorted order. + */ + public List getSortedPlugins() { + return sortedPlugins; + } + + void addNotFoundDependency(String pluginId) { + notFoundDependencies.add(pluginId); + } + + void addWrongDependencyVersion(WrongDependencyVersion wrongDependencyVersion) { + wrongVersionDependencies.add(wrongDependencyVersion); + } + + } + + public static class WrongDependencyVersion { + + private String dependencyId; // value is "pluginId" + private String dependentId; // value is "pluginId" + private String existingVersion; + private String requiredVersion; + + WrongDependencyVersion(String dependencyId, String dependentId, String existingVersion, String requiredVersion) { + this.dependencyId = dependencyId; + this.dependentId = dependentId; + this.existingVersion = existingVersion; + this.requiredVersion = requiredVersion; + } + + public String getDependencyId() { + return dependencyId; + } + + public String getDependentId() { + return dependentId; + } + + public String getExistingVersion() { + return existingVersion; + } + + public String getRequiredVersion() { + return requiredVersion; + } + + } + + /** + * It will be thrown if a cyclic dependency is detected. + */ + public static class CyclicDependencyException extends PluginException { + + public CyclicDependencyException() { + super("Cyclic dependencies"); + } + + } + + /** + * Indicates that the dependencies required were not found. + */ + public static class DependenciesNotFoundException extends PluginException { + + private List dependencies; + + public DependenciesNotFoundException(List dependencies) { + super("Dependencies '{}' not found", dependencies); + + this.dependencies = dependencies; + } + + public List getDependencies() { + return dependencies; + } + + } + + /** + * Indicates that some dependencies have wrong version. + */ + public static class DependenciesWrongVersionException extends PluginException { + + private List dependencies; + + public DependenciesWrongVersionException(List dependencies) { + super("Dependencies '{}' have wrong version", dependencies); + + this.dependencies = dependencies; + } + + public List getDependencies() { + return dependencies; + } + + } + +} diff --git a/pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java b/pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java new file mode 100644 index 0000000..ba22e56 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/DevelopmentPluginClasspath.java @@ -0,0 +1,32 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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; + +/** + * Overwrite classes directories to {@code target/classes} and lib directories to {@code target/lib}. + * + * @author Decebal Suiu + */ +public class DevelopmentPluginClasspath extends PluginClasspath { + + public DevelopmentPluginClasspath() { + super(); + + addClassesDirectories("target/classes"); + addLibDirectories("target/lib"); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/Extension.java b/pf4j/src/main/java/org/pf4j/Extension.java new file mode 100644 index 0000000..69a2049 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/Extension.java @@ -0,0 +1,37 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * @author Decebal Suiu + */ +@Retention(RUNTIME) +@Target(TYPE) +@Inherited +@Documented +public @interface Extension { + + int ordinal() default 0; + +} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionDescriptor.java b/pf4j/src/main/java/org/pf4j/ExtensionDescriptor.java new file mode 100644 index 0000000..cb0ebc1 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ExtensionDescriptor.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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; + +/** + * @author Decebal Suiu + */ +public class ExtensionDescriptor { + + private int ordinal; + private Class extensionClass; + + public Class getExtensionClass() { + return extensionClass; + } + + public int getOrdinal() { + return ordinal; + } + + void setExtensionClass(Class extensionClass) { + this.extensionClass = extensionClass; + } + + void setOrdinal(int ordinal) { + this.ordinal = ordinal; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionFactory.java b/pf4j/src/main/java/org/pf4j/ExtensionFactory.java new file mode 100644 index 0000000..27ab5cc --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ExtensionFactory.java @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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; + +/** + * Creates an extension instance. + */ +public interface ExtensionFactory { + + Object create(Class extensionClass); + +} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionFinder.java b/pf4j/src/main/java/org/pf4j/ExtensionFinder.java new file mode 100644 index 0000000..146f040 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ExtensionFinder.java @@ -0,0 +1,46 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 java.util.List; +import java.util.Set; + +/** + * @author Decebal Suiu + */ +public interface ExtensionFinder { + + /** + * Retrieves a list with all extensions found for an extension point. + */ + List> find(Class type); + + /** + * Retrieves a list with all extensions found for an extension point and a plugin. + */ + List> find(Class type, String pluginId); + + /** + * Retrieves a list with all extensions found for a plugin + */ + List find(String pluginId); + + /** + * Retrieves a list with all extension class names found for a plugin. + */ + Set findClassNames(String pluginId); + +} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionPoint.java b/pf4j/src/main/java/org/pf4j/ExtensionPoint.java new file mode 100644 index 0000000..3e64e72 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ExtensionPoint.java @@ -0,0 +1,23 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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; + +/** + * @author Decebal Suiu + */ +public interface ExtensionPoint { + +} diff --git a/pf4j/src/main/java/org/pf4j/ExtensionWrapper.java b/pf4j/src/main/java/org/pf4j/ExtensionWrapper.java new file mode 100644 index 0000000..50c9612 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ExtensionWrapper.java @@ -0,0 +1,59 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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; + +/** + * A wrapper over extension instance. + * + * @author Decebal Suiu + */ +public class ExtensionWrapper implements Comparable> { + + ExtensionDescriptor descriptor; + ExtensionFactory extensionFactory; + T extension; // cache + + public ExtensionWrapper(ExtensionDescriptor descriptor) { + this.descriptor = descriptor; + } + + @SuppressWarnings("unchecked") + public T getExtension() { + if (extension == null) { + extension = (T) extensionFactory.create(descriptor.getExtensionClass()); + } + + return extension; + } + + public ExtensionDescriptor getDescriptor() { + return descriptor; + } + + public int getOrdinal() { + return descriptor.getOrdinal(); + } + + @Override + public int compareTo(ExtensionWrapper o) { + return (getOrdinal() - o.getOrdinal()); + } + + void setExtensionFactory(ExtensionFactory extensionFactory) { + this.extensionFactory = extensionFactory; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/JarPluginManager.java b/pf4j/src/main/java/org/pf4j/JarPluginManager.java new file mode 100644 index 0000000..232a8eb --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/JarPluginManager.java @@ -0,0 +1,115 @@ +/* + * Copyright 2016 Decebal Suiu + * + * 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.pf4j.util.AndFileFilter; +import org.pf4j.util.DirectoryFileFilter; +import org.pf4j.util.HiddenFilter; +import org.pf4j.util.JarFileFilter; +import org.pf4j.util.NotFileFilter; +import org.pf4j.util.OrFileFilter; +import org.pf4j.util.NameFileFilter; + +import java.io.FileFilter; +import java.io.IOException; +import java.nio.file.Path; +import java.util.jar.JarFile; +import java.util.jar.Manifest; + +/** + * It's a {@link PluginManager} that loads plugin from a jar file. + * Actually, a plugin is a fat jar, a jar which contains classes from all the libraries, + * on which your project depends and, of course, the classes of current project. + * + * @author Decebal Suiu + */ +public class JarPluginManager extends DefaultPluginManager { + + @Override + protected PluginRepository createPluginRepository() { + return new JarPluginRepository(getPluginsRoot(), isDevelopment()); + } + + @Override + protected PluginDescriptorFinder createPluginDescriptorFinder() { + return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new JarPluginDescriptorFinder(); + } + + @Override + protected PluginLoader createPluginLoader() { + return new JarPluginLoader(this, pluginClasspath); + } + + class JarPluginRepository extends BasePluginRepository { + + public JarPluginRepository(Path pluginsRoot, boolean development) { + super(pluginsRoot); + + if (development) { + AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); + pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter(development))); + setFilter(pluginsFilter); + } else { + setFilter(new JarFileFilter()); + } + } + + protected FileFilter createHiddenPluginFilter(boolean development) { + OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); + + if (development) { + hiddenPluginFilter.addFileFilter(new NameFileFilter("target")); + } + + return hiddenPluginFilter; + } + + } + + class JarPluginDescriptorFinder extends ManifestPluginDescriptorFinder { + + @Override + public Manifest readManifest(Path pluginPath) throws PluginException { + try { + return new JarFile(pluginPath.toFile()).getManifest(); + } catch (IOException e) { + throw new PluginException(e); + } + } + + } + + class JarPluginLoader extends DefaultPluginLoader { + + public JarPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { + super(pluginManager, pluginClasspath); + } + + @Override + public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { + if (isDevelopment()) { + return super.loadPlugin(pluginPath, pluginDescriptor); + } + + PluginClassLoader pluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); + pluginClassLoader.addFile(pluginPath.toFile()); + + return pluginClassLoader; + } + + } + +} diff --git a/pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java b/pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java new file mode 100644 index 0000000..da51889 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/LegacyExtensionFinder.java @@ -0,0 +1,111 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.pf4j.processor.LegacyExtensionStorage; + +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * All extensions declared in a plugin are indexed in a file {@code META-INF/extensions.idx}. + * This class lookup extensions in all extensions index files {@code META-INF/extensions.idx}. + * + * @author Decebal Suiu + */ +public class LegacyExtensionFinder extends AbstractExtensionFinder { + + private static final Logger log = LoggerFactory.getLogger(LegacyExtensionFinder.class); + + public LegacyExtensionFinder(PluginManager pluginManager) { + super(pluginManager); + } + + @Override + public Map> readClasspathStorages() { + log.debug("Reading extensions storages from classpath"); + Map> result = new LinkedHashMap<>(); + + Set bucket = new HashSet<>(); + try { + Enumeration urls = getClass().getClassLoader().getResources(getExtensionsResource()); + while (urls.hasMoreElements()) { + URL url = urls.nextElement(); + log.debug("Read '{}'", url.getFile()); + try (Reader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { + LegacyExtensionStorage.read(reader, bucket); + } + } + + debugExtensions(bucket); + + result.put(null, bucket); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + + return result; + } + + @Override + public Map> readPluginsStorages() { + log.debug("Reading extensions storages from plugins"); + Map> result = new LinkedHashMap<>(); + + List plugins = pluginManager.getPlugins(); + for (PluginWrapper plugin : plugins) { + String pluginId = plugin.getDescriptor().getPluginId(); + log.debug("Reading extensions storage from plugin '{}'", pluginId); + Set bucket = new HashSet<>(); + + try { + URL url = ((PluginClassLoader) plugin.getPluginClassLoader()).findResource(getExtensionsResource()); + if (url != null) { + log.debug("Read '{}'", url.getFile()); + try (Reader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { + LegacyExtensionStorage.read(reader, bucket); + } + } else { + log.debug("Cannot find '{}'", getExtensionsResource()); + } + + debugExtensions(bucket); + + result.put(pluginId, bucket); + } catch (IOException e) { + log.error(e.getMessage(), e); + } + } + + return result; + } + + private static String getExtensionsResource() { + return LegacyExtensionStorage.EXTENSIONS_RESOURCE; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java new file mode 100644 index 0000000..1c3b9c2 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ManifestPluginDescriptorFinder.java @@ -0,0 +1,82 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.pf4j.util.StringUtils; + +import java.nio.file.Path; +import java.util.jar.Attributes; +import java.util.jar.Manifest; + +/** + * Read the plugin descriptor from the manifest file. + * + * @author Decebal Suiu + */ +public abstract class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { + + @Override + public PluginDescriptor find(Path pluginPath) throws PluginException { + Manifest manifest = readManifest(pluginPath); + + return createPluginDescriptor(manifest); + } + + public abstract Manifest readManifest(Path pluginPath) throws PluginException; + + protected PluginDescriptor createPluginDescriptor(Manifest manifest) { + PluginDescriptor pluginDescriptor = createPluginDescriptorInstance(); + + // TODO validate !!! + Attributes attributes = manifest.getMainAttributes(); + String id = attributes.getValue("Plugin-Id"); + pluginDescriptor.setPluginId(id); + + String description = attributes.getValue("Plugin-Description"); + if (StringUtils.isEmpty(description)) { + pluginDescriptor.setPluginDescription(""); + } else { + pluginDescriptor.setPluginDescription(description); + } + + String clazz = attributes.getValue("Plugin-Class"); + pluginDescriptor.setPluginClass(clazz); + + String version = attributes.getValue("Plugin-Version"); + if (StringUtils.isNotEmpty(version)) { + pluginDescriptor.setPluginVersion(version); + } + + String provider = attributes.getValue("Plugin-Provider"); + pluginDescriptor.setProvider(provider); + String dependencies = attributes.getValue("Plugin-Dependencies"); + pluginDescriptor.setDependencies(dependencies); + + String requires = attributes.getValue("Plugin-Requires"); + if (StringUtils.isNotEmpty(requires)) { + pluginDescriptor.setRequires(requires); + } + + pluginDescriptor.setLicense(attributes.getValue("Plugin-License")); + + return pluginDescriptor; + } + + protected PluginDescriptor createPluginDescriptorInstance() { + return new PluginDescriptor(); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/Plugin.java b/pf4j/src/main/java/org/pf4j/Plugin.java new file mode 100644 index 0000000..1972664 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/Plugin.java @@ -0,0 +1,71 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class will be extended by all plugins and + * serve as the common class between a plugin and the application. + * + * @author Decebal Suiu + */ +public abstract class Plugin { + + /** + * Makes logging service available for descending classes. + */ + protected final Logger log = LoggerFactory.getLogger(getClass()); + + /** + * Wrapper of the plugin. + */ + protected PluginWrapper wrapper; + + /** + * Constructor to be used by plugin manager for plugin instantiation. + * Your plugins have to provide constructor with this exact signature to + * be successfully loaded by manager. + */ + public Plugin(final PluginWrapper wrapper) { + if (wrapper == null) { + throw new IllegalArgumentException("Wrapper cannot be null"); + } + + this.wrapper = wrapper; + } + + /** + * Retrieves the wrapper of this plug-in. + */ + public final PluginWrapper getWrapper() { + return wrapper; + } + + /** + * Start method is called by the application when the plugin is loaded. + */ + public void start() throws PluginException { + } + + /** + * Stop method is called by the application when the plugin is unloaded. + */ + public void stop() throws PluginException { + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginClassLoader.java b/pf4j/src/main/java/org/pf4j/PluginClassLoader.java new file mode 100644 index 0000000..586f1b3 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginClassLoader.java @@ -0,0 +1,145 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.List; + +/** + * One instance of this class should be created by plugin manager for every available plug-in. + * This class loader is a Parent Last ClassLoader - it loads the classes from the plugin's jars + * before delegating to the parent class loader. + * + * @author Decebal Suiu + */ +public class PluginClassLoader extends URLClassLoader { + + private static final Logger log = LoggerFactory.getLogger(PluginClassLoader.class); + + private static final String PLUGIN_PACKAGE_PREFIX = "org.pf4j."; + + private PluginManager pluginManager; + private PluginDescriptor pluginDescriptor; + + public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent) { + super(new URL[0], parent); + + this.pluginManager = pluginManager; + this.pluginDescriptor = pluginDescriptor; + } + + @Override + public void addURL(URL url) { + log.debug("Add '{}'", url); + super.addURL(url); + } + + public void addFile(File file) { + try { + addURL(file.getCanonicalFile().toURI().toURL()); + } catch (IOException e) { +// throw new RuntimeException(e); + log.error(e.getMessage(), e); + } + } + + /** + * Uses a child first delegation model rather than the standard parent first. + * If the requested class cannot be found in this class loader, the parent class loader will be consulted + * via the standard {@link ClassLoader#loadClass(String)} mechanism. + */ + @Override + public Class loadClass(String className) throws ClassNotFoundException { + synchronized (getClassLoadingLock(className)) { + log.trace("Received request to load class '{}'", className); + // if the class it's a part of the plugin engine use parent class loader + if (className.startsWith(PLUGIN_PACKAGE_PREFIX)) { + log.trace("Delegate the loading of class '{}' to parent", className); + try { + return getClass().getClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + // try next step + } + } + + // second check whether it's already been loaded + Class clazz = findLoadedClass(className); + if (clazz != null) { + log.trace("Found loaded class '{}'", className); + return clazz; + } + + // nope, try to load locally + try { + clazz = findClass(className); + log.trace("Found class '{}' in plugin classpath", className); + return clazz; + } catch (ClassNotFoundException e) { + // try next step + } + + // look in dependencies + log.trace("Search in dependencies for class '{}'", className); + List dependencies = pluginDescriptor.getDependencies(); + for (PluginDependency dependency : dependencies) { + ClassLoader classLoader = pluginManager.getPluginClassLoader(dependency.getPluginId()); + try { + return classLoader.loadClass(className); + } catch (ClassNotFoundException e) { + // try next dependency + } + } + + log.trace("Couldn't find class '{}' in plugin classpath. Delegating to parent", className); + + // use the standard ClassLoader (which follows normal parent delegation) + return super.loadClass(className); + } + } + + /** + * Load the named resource from this plugin. + * This implementation checks the plugin's classpath first then delegates to the parent. + * + * @param name the name of the resource. + * @return the URL to the resource, null if the resource was not found. + */ + @Override + public URL getResource(String name) { + log.trace("Trying to find resource '{}' in plugin classpath", name); + URL url = findResource(name); + if (url != null) { + log.trace("Found resource '{}' in plugin classpath", name); + return url; + } + + log.trace("Couldn't find resource '{}' in plugin classpath. Delegating to parent"); + + return super.getResource(name); + } + + @Override + public URL findResource(String name) { + return super.findResource(name); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginClasspath.java b/pf4j/src/main/java/org/pf4j/PluginClasspath.java new file mode 100644 index 0000000..06b9ec5 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginClasspath.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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 java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * The classpath of the plugin. + * It contains {@code classes} directories and {@code lib} directories (directories that contains jars). + * + * @author Decebal Suiu + */ +public class PluginClasspath { + + private List classesDirectories; + private List libDirectories; + + public PluginClasspath() { + classesDirectories = new ArrayList<>(); + libDirectories = new ArrayList<>(); + } + + public List getClassesDirectories() { + return classesDirectories; + } + + public void addClassesDirectories(String... classesDirectories) { + this.classesDirectories.addAll(Arrays.asList(classesDirectories)); + } + + public List getLibDirectories() { + return libDirectories; + } + + public void addLibDirectories(String... libDirectories) { + this.libDirectories.addAll(Arrays.asList(libDirectories)); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginDependency.java b/pf4j/src/main/java/org/pf4j/PluginDependency.java new file mode 100644 index 0000000..fa2f280 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginDependency.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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; + +/** + * @author Decebal Suiu + */ +public class PluginDependency { + + private String pluginId; + private String pluginVersionSupport = "*"; + + public PluginDependency(String dependency) { + int index = dependency.indexOf('@'); + if (index == -1) { + this.pluginId = dependency; + } else { + this.pluginId = dependency.substring(0, index); + if (dependency.length() > index + 1) { + this.pluginVersionSupport = dependency.substring(index + 1); + } + } + } + + public String getPluginId() { + return pluginId; + } + + public String getPluginVersionSupport() { + return pluginVersionSupport; + } + + @Override + public String toString() { + return "PluginDependency [pluginId=" + pluginId + ", pluginVersionSupport=" + pluginVersionSupport + "]"; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginDescriptor.java b/pf4j/src/main/java/org/pf4j/PluginDescriptor.java new file mode 100644 index 0000000..6dddbbf --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginDescriptor.java @@ -0,0 +1,177 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * A plugin descriptor contains information about a plug-in obtained + * from the manifest (META-INF) file. + * + * @author Decebal Suiu + */ +public class PluginDescriptor { + + private String pluginId; + private String pluginDescription; + private String pluginClass; + private String version; + private String requires = "*"; // SemVer format + private String provider; + private List dependencies; + private String license; + + public PluginDescriptor() { + dependencies = new ArrayList<>(); + } + + /** + * Returns the unique identifier of this plugin. + */ + public String getPluginId() { + return pluginId; + } + + /** + * Returns the description of this plugin. + */ + public String getPluginDescription() { + return pluginDescription; + } + + /** + * Returns the name of the class that implements Plugin interface. + */ + public String getPluginClass() { + return pluginClass; + } + + /** + * Returns the version of this plugin. + */ + public String getVersion() { + return version; + } + + /** + * Returns string version of requires + * @return String with requires expression on SemVer format + */ + public String getRequires() { + return requires; + } + + /** + * Returns the provider name of this plugin. + */ + public String getProvider() { + return provider; + } + + /** + * Returns the legal license of this plugin, e.g. "Apache-2.0", "MIT" etc + */ + public String getLicense() { + return license; + } + + /** + * Returns all dependencies declared by this plugin. + * Returns an empty array if this plugin does not declare any require. + */ + public List getDependencies() { + return dependencies; + } + + @Override + public String toString() { + return "PluginDescriptor [pluginId=" + pluginId + ", pluginClass=" + + pluginClass + ", version=" + version + ", provider=" + + provider + ", dependencies=" + dependencies + ", description=" + + pluginDescription + ", requires=" + requires + ", license=" + + license + "]"; + } + + PluginDescriptor setPluginId(String pluginId) { + this.pluginId = pluginId; + + return this; + } + + PluginDescriptor setPluginDescription(String pluginDescription) { + this.pluginDescription = pluginDescription; + + return this; + } + + PluginDescriptor setPluginClass(String pluginClassName) { + this.pluginClass = pluginClassName; + + return this; + } + + PluginDescriptor setPluginVersion(String version) { + this.version = version; + + return this; + } + + PluginDescriptor setProvider(String provider) { + this.provider = provider; + + return this; + } + + PluginDescriptor setRequires(String requires) { + this.requires = requires; + + return this; + } + + PluginDescriptor setDependencies(String dependencies) { + if (dependencies != null) { + dependencies = dependencies.trim(); + if (dependencies.isEmpty()) { + this.dependencies = Collections.emptyList(); + } else { + this.dependencies = new ArrayList<>(); + String[] tokens = dependencies.split(","); + for (String dependency : tokens) { + dependency = dependency.trim(); + if (!dependency.isEmpty()) { + this.dependencies.add(new PluginDependency(dependency)); + } + } + if (this.dependencies.isEmpty()) { + this.dependencies = Collections.emptyList(); + } + } + } else { + this.dependencies = Collections.emptyList(); + } + + return this; + } + + public PluginDescriptor setLicense(String license) { + this.license = license; + + return this; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java new file mode 100644 index 0000000..2edfbb5 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginDescriptorFinder.java @@ -0,0 +1,31 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 java.nio.file.Path; + +/** + * Find a plugin descriptor for a plugin path. + * You can find in manifest file {@link DefaultPluginDescriptorFinder}, + * xml file, properties file, java services (with {@link java.util.ServiceLoader}), etc. + * + * @author Decebal Suiu + */ +public interface PluginDescriptorFinder { + + PluginDescriptor find(Path pluginPath) throws PluginException; + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginException.java b/pf4j/src/main/java/org/pf4j/PluginException.java new file mode 100644 index 0000000..4a57af4 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginException.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.pf4j.util.StringUtils; + +/** + * An exception used to indicate that a plugin problem occurred. + * + * @author Decebal Suiu + */ +public class PluginException extends Exception { + + public PluginException() { + super(); + } + + public PluginException(String message) { + super(message); + } + + public PluginException(Throwable cause) { + super(cause); + } + + public PluginException(String message, Throwable cause) { + super(message, cause); + } + + public PluginException(Throwable cause, String message, Object... args) { + super(StringUtils.format(message, args), cause); + } + + public PluginException(String message, Object... args) { + super(StringUtils.format(message, args)); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginFactory.java b/pf4j/src/main/java/org/pf4j/PluginFactory.java new file mode 100644 index 0000000..4909ade --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginFactory.java @@ -0,0 +1,25 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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; + +/** + * Creates a plugin instance. + */ +public interface PluginFactory { + + Plugin create(PluginWrapper pluginWrapper); + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginLoader.java b/pf4j/src/main/java/org/pf4j/PluginLoader.java new file mode 100644 index 0000000..5ac2755 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginLoader.java @@ -0,0 +1,29 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 java.nio.file.Path; + +/** + * Load all information (classes) needed by a plugin. + * + * @author Decebal Suiu + */ +public interface PluginLoader { + + ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor); + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginManager.java b/pf4j/src/main/java/org/pf4j/PluginManager.java new file mode 100644 index 0000000..06fc68e --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginManager.java @@ -0,0 +1,183 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 java.nio.file.Path; +import java.util.List; +import java.util.Set; + +/** + * Provides the functionality for plugin management such as load, + * start and stop the plugins. + * + * @author Decebal Suiu + */ +public interface PluginManager { + + /** + * Retrieves all plugins. + */ + List getPlugins(); + + /** + * Retrieves all plugins with this state. + */ + List getPlugins(PluginState pluginState); + + /** + * Retrieves all resolved plugins (with resolved dependency). + */ + List getResolvedPlugins(); + + /** + * Retrieves all unresolved plugins (with unresolved dependency). + */ + List getUnresolvedPlugins(); + + /** + * Retrieves all started plugins. + */ + List getStartedPlugins(); + + /** + * Retrieves the plugin with this id, or null if the plugin does not exist. + * + * @param pluginId The pluginId for the plugin you are trying to get. + * @return A PluginWrapper object for this plugin, or null if it does not exist. + */ + PluginWrapper getPlugin(String pluginId); + + /** + * Load plugins. + */ + void loadPlugins(); + + /** + * Load a plugin. + * + * @param pluginPath + * @return the pluginId of the installed plugin or null + */ + String loadPlugin(Path pluginPath); + + /** + * Start all active plugins. + */ + void startPlugins(); + + /** + * Start the specified plugin and it's dependencies. + * + * @return the plugin state + */ + PluginState startPlugin(String pluginId); + + /** + * Stop all active plugins. + */ + void stopPlugins(); + + /** + * Stop the specified plugin and it's dependencies. + * + * @return the plugin state + */ + PluginState stopPlugin(String pluginId); + + /** + * Unload a plugin. + * + * @param pluginId + * @return true if the plugin was unloaded + */ + boolean unloadPlugin(String pluginId); + + /** + * Disables a plugin from being loaded. + * + * @param pluginId + * @return true if plugin is disabled + */ + boolean disablePlugin(String pluginId); + + /** + * Enables a plugin that has previously been disabled. + * + * @param pluginId + * @return true if plugin is enabled + */ + boolean enablePlugin(String pluginId); + + /** + * Deletes a plugin. + * + * @param pluginId + * @return true if the plugin was deleted + */ + boolean deletePlugin(String pluginId); + + ClassLoader getPluginClassLoader(String pluginId); + + List getExtensions(Class type); + + List getExtensions(Class type, String pluginId); + + List getExtensions(String pluginId); + + Set getExtensionClassNames(String pluginId); + + ExtensionFactory getExtensionFactory(); + + /** + * The runtime mode. Must currently be either DEVELOPMENT or DEPLOYMENT. + */ + RuntimeMode getRuntimeMode(); + + /** + * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. + */ + PluginWrapper whichPlugin(Class clazz); + + void addPluginStateListener(PluginStateListener listener); + + void removePluginStateListener(PluginStateListener listener); + + /** + * Set the system version. This is used to compare against the plugin + * requires attribute. The default system version is 0.0.0 which + * disables all version checking. + * + * @default 0.0.0 + * @param version + */ + void setSystemVersion(String version); + + /** + * Returns the system version. + * + * @return the system version + */ + String getSystemVersion(); + + /** + * Gets the path of the folder where plugins are installed + * @return Path of plugins root + */ + Path getPluginsRoot(); + + VersionManager getVersionManager(); + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginRepository.java b/pf4j/src/main/java/org/pf4j/PluginRepository.java new file mode 100644 index 0000000..de1347d --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginRepository.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 java.nio.file.Path; +import java.util.List; + +/** + * Directory that contains plugins. A plugin could be a zip file. + * + * @author Decebal Suiu + * @author Mário Franco + */ +public interface PluginRepository { + + /** + * List all plugin paths. + * + * @return a list of files + */ + List getPluginPaths(); + + /** + * Removes a plugin from the repository. + * + * @param pluginPath the plugin path + * @return true if deleted + */ + boolean deletePluginPath(Path pluginPath); + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginState.java b/pf4j/src/main/java/org/pf4j/PluginState.java new file mode 100644 index 0000000..2de2523 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginState.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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; + +/** + * @author Decebal Suiu + */ +public class PluginState { + + public static final PluginState CREATED = new PluginState("CREATED"); + public static final PluginState DISABLED = new PluginState("DISABLED"); + public static final PluginState STARTED = new PluginState("STARTED"); + public static final PluginState STOPPED = new PluginState("STOPPED"); + + private String status; + + private PluginState(String status) { + this.status = status; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + PluginState that = (PluginState) o; + + if (!status.equals(that.status)) return false; + + return true; + } + + @Override + public int hashCode() { + return status.hashCode(); + } + + @Override + public String toString() { + return status; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginStateEvent.java b/pf4j/src/main/java/org/pf4j/PluginStateEvent.java new file mode 100644 index 0000000..361fd3f --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginStateEvent.java @@ -0,0 +1,60 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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 java.util.EventObject; + +/** + * @author Decebal Suiu + */ +public class PluginStateEvent extends EventObject { + + private PluginWrapper plugin; + private PluginState oldState; + + public PluginStateEvent(PluginManager source, PluginWrapper plugin, PluginState oldState) { + super(source); + + this.plugin = plugin; + this.oldState = oldState; + } + + @Override + public PluginManager getSource() { + return (PluginManager) super.getSource(); + } + + public PluginWrapper getPlugin() { + return plugin; + } + + public PluginState getPluginState() { + return plugin.getPluginState(); + } + + public PluginState getOldState() { + return oldState; + } + + @Override + public String toString() { + return "PluginStateEvent [plugin=" + plugin.getPluginId() + + ", newState=" + getPluginState() + + ", oldState=" + oldState + + ']'; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginStateListener.java b/pf4j/src/main/java/org/pf4j/PluginStateListener.java new file mode 100644 index 0000000..5312b1b --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginStateListener.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 Decebal Suiu + * + * 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 java.util.EventListener; + +/** + * PluginStateListener defines the interface for an object that listens to plugin state changes. + * + * @author Decebal Suiu + */ +public interface PluginStateListener extends EventListener { + + /** + * Invoked when a plugin's state (for example DISABLED, STARTED) is changed. + */ + void pluginStateChanged(PluginStateEvent event); + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginStatusProvider.java b/pf4j/src/main/java/org/pf4j/PluginStatusProvider.java new file mode 100644 index 0000000..07d5f43 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginStatusProvider.java @@ -0,0 +1,48 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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; + +/** + * @author Decebal Suiu + * @author Mário Franco + */ +public interface PluginStatusProvider { + + /** + * Checks if the plugin is disabled or not + * + * @param pluginId the plugin id + * @return if the plugin is disabled or not + */ + boolean isPluginDisabled(String pluginId); + + /** + * Disables a plugin from being loaded. + * + * @param pluginId + * @return true if plugin is disabled + */ + boolean disablePlugin(String pluginId); + + /** + * Enables a plugin that has previously been disabled. + * + * @param pluginId + * @return true if plugin is enabled + */ + boolean enablePlugin(String pluginId); + +} diff --git a/pf4j/src/main/java/org/pf4j/PluginWrapper.java b/pf4j/src/main/java/org/pf4j/PluginWrapper.java new file mode 100644 index 0000000..5ed641f --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PluginWrapper.java @@ -0,0 +1,147 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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 java.nio.file.Path; + +/** + * A wrapper over plugin instance. + * + * @author Decebal Suiu + */ +public class PluginWrapper { + + private PluginManager pluginManager; + private PluginDescriptor descriptor; + private Path pluginPath; + private ClassLoader pluginClassLoader; + private PluginFactory pluginFactory; + private PluginState pluginState; + private RuntimeMode runtimeMode; + + Plugin plugin; // cache + + public PluginWrapper(PluginManager pluginManager, PluginDescriptor descriptor, Path pluginPath, ClassLoader pluginClassLoader) { + this.pluginManager = pluginManager; + this.descriptor = descriptor; + this.pluginPath = pluginPath; + this.pluginClassLoader = pluginClassLoader; + + pluginState = PluginState.CREATED; + } + + /** + * Returns the plugin manager. + */ + public PluginManager getPluginManager() { + return pluginManager; + } + + /** + * Returns the plugin descriptor. + */ + public PluginDescriptor getDescriptor() { + return descriptor; + } + + /** + * Returns the path of this plugin. + */ + public Path getPluginPath() { + return pluginPath; + } + + /** + * Returns the plugin class loader used to load classes and resources + * for this plug-in. The class loader can be used to directly access + * plug-in resources and classes. + */ + public ClassLoader getPluginClassLoader() { + return pluginClassLoader; + } + + public Plugin getPlugin() { + if (plugin == null) { + plugin = pluginFactory.create(this); + } + + return plugin; + } + + public PluginState getPluginState() { + return pluginState; + } + + public RuntimeMode getRuntimeMode() { + return runtimeMode; + } + + /** + * Shortcut + */ + public String getPluginId() { + return getDescriptor().getPluginId(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + descriptor.getPluginId().hashCode(); + + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj == null) { + return false; + } + + if (getClass() != obj.getClass()) { + return false; + } + + PluginWrapper other = (PluginWrapper) obj; + if (!descriptor.getPluginId().equals(other.descriptor.getPluginId())) { + return false; + } + + return true; + } + + @Override + public String toString() { + return "PluginWrapper [descriptor=" + descriptor + ", pluginPath=" + pluginPath + "]"; + } + + void setPluginState(PluginState pluginState) { + this.pluginState = pluginState; + } + + void setRuntimeMode(RuntimeMode runtimeMode) { + this.runtimeMode = runtimeMode; + } + + void setPluginFactory(PluginFactory pluginFactory) { + this.pluginFactory = pluginFactory; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java b/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java new file mode 100644 index 0000000..0391bce --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/PropertiesPluginDescriptorFinder.java @@ -0,0 +1,116 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.pf4j.util.StringUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Properties; + +/** + * Find a plugin descriptor in a properties file (in plugin repository). + * + * @author Decebal Suiu + */ +public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder { + + private static final Logger log = LoggerFactory.getLogger(PropertiesPluginDescriptorFinder.class); + + private static final String DEFAULT_PROPERTIES_FILE_NAME = "plugin.properties"; + + protected String propertiesFileName; + + public PropertiesPluginDescriptorFinder() { + this(DEFAULT_PROPERTIES_FILE_NAME); + } + + public PropertiesPluginDescriptorFinder(String propertiesFileName) { + this.propertiesFileName = propertiesFileName; + } + + @Override + public PluginDescriptor find(Path pluginPath) throws PluginException { + Properties properties = readProperties(pluginPath); + + return createPluginDescriptor(properties); + } + + protected Properties readProperties(Path pluginPath) throws PluginException { + Path propertiesPath = pluginPath.resolve(Paths.get(propertiesFileName)); + log.debug("Lookup plugin descriptor in '{}'", propertiesPath); + if (Files.notExists(propertiesPath)) { + throw new PluginException("Cannot find '{}' path", pluginPath); + } + + Properties properties = new Properties(); + try (InputStream input = Files.newInputStream(propertiesPath)) { + properties.load(input); + } catch (IOException e) { + throw new PluginException(e.getMessage(), e); + } + + return properties; + } + + protected PluginDescriptor createPluginDescriptor(Properties properties) { + PluginDescriptor pluginDescriptor = createPluginDescriptorInstance(); + + // TODO validate !!! + String id = properties.getProperty("plugin.id"); + pluginDescriptor.setPluginId(id); + + String description = properties.getProperty("plugin.description"); + if (StringUtils.isEmpty(description)) { + pluginDescriptor.setPluginDescription(""); + } else { + pluginDescriptor.setPluginDescription(description); + } + + String clazz = properties.getProperty("plugin.class"); + pluginDescriptor.setPluginClass(clazz); + + String version = properties.getProperty("plugin.version"); + if (StringUtils.isNotEmpty(version)) { + pluginDescriptor.setPluginVersion(version); + } + + String provider = properties.getProperty("plugin.provider"); + pluginDescriptor.setProvider(provider); + + String dependencies = properties.getProperty("plugin.dependencies"); + pluginDescriptor.setDependencies(dependencies); + + String requires = properties.getProperty("plugin.requires"); + if (StringUtils.isNotEmpty(requires)) { + pluginDescriptor.setRequires(requires); + } + + pluginDescriptor.setLicense(properties.getProperty("plugin.license")); + + return pluginDescriptor; + } + + protected PluginDescriptor createPluginDescriptorInstance() { + return new PluginDescriptor(); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/RuntimeMode.java b/pf4j/src/main/java/org/pf4j/RuntimeMode.java new file mode 100644 index 0000000..f4bec88 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/RuntimeMode.java @@ -0,0 +1,58 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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 java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * @author Decebal Suiu + */ +public enum RuntimeMode { + + DEVELOPMENT("development"), // development + DEPLOYMENT("deployment"); // deployment + + private final String name; + + private static final Map map = new HashMap<>(); + + static { + for (RuntimeMode mode : RuntimeMode.values()) { + map.put(mode.name, mode); + } + } + + private RuntimeMode(final String name) { + this.name = name; + } + + @Override + public String toString() { + return name; + } + + public static RuntimeMode byName(String name) { + if (map.containsKey(name)) { + return map.get(name); + } + + throw new NoSuchElementException("Cannot found PF4J runtime mode with name '" + name + + "'. Must be 'development' or 'deployment'."); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/ServiceProviderExtensionFinder.java b/pf4j/src/main/java/org/pf4j/ServiceProviderExtensionFinder.java new file mode 100644 index 0000000..bd7c768 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/ServiceProviderExtensionFinder.java @@ -0,0 +1,148 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.pf4j.processor.ServiceProviderExtensionStorage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.Reader; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitOption; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * The {@link java.util.ServiceLoader} base implementation for {@link ExtensionFinder}. + * This class lookup extensions in all extensions index files {@code META-INF/services}. + * + * @author Decebal Suiu + */ +public class ServiceProviderExtensionFinder extends AbstractExtensionFinder { + + private static final Logger log = LoggerFactory.getLogger(ServiceProviderExtensionFinder.class); + + public ServiceProviderExtensionFinder(PluginManager pluginManager) { + super(pluginManager); + } + + @Override + public Map> readClasspathStorages() { + log.debug("Reading extensions storages from classpath"); + Map> result = new LinkedHashMap<>(); + + final Set bucket = new HashSet<>(); + try { + URL url = getClass().getClassLoader().getResource(getExtensionsResource()); + if (url != null) { + Path extensionPath; + if (url.toURI().getScheme().equals("jar")) { + FileSystem fileSystem = FileSystems.newFileSystem(url.toURI(), Collections.emptyMap()); + extensionPath = fileSystem.getPath(getExtensionsResource()); + } else { + extensionPath = Paths.get(url.toURI()); + } + + bucket.addAll(readExtensions(extensionPath)); + } + + debugExtensions(bucket); + + result.put(null, bucket); + } catch (IOException | URISyntaxException e) { + log.error(e.getMessage(), e); + } + + return result; + } + + @Override + public Map> readPluginsStorages() { + log.debug("Reading extensions storages from plugins"); + Map> result = new LinkedHashMap<>(); + + List plugins = pluginManager.getPlugins(); + for (PluginWrapper plugin : plugins) { + String pluginId = plugin.getDescriptor().getPluginId(); + log.debug("Reading extensions storages for plugin '{}'", pluginId); + final Set bucket = new HashSet<>(); + + try { + URL url = ((PluginClassLoader) plugin.getPluginClassLoader()).findResource(getExtensionsResource()); + if (url != null) { + Path extensionPath; + if (url.toURI().getScheme().equals("jar")) { + FileSystem fileSystem = FileSystems.newFileSystem(url.toURI(), Collections.emptyMap()); + extensionPath = fileSystem.getPath(getExtensionsResource()); + } else { + extensionPath = Paths.get(url.toURI()); + } + + bucket.addAll(readExtensions(extensionPath)); + } else { + log.debug("Cannot find '{}'", getExtensionsResource()); + } + + debugExtensions(bucket); + + result.put(pluginId, bucket); + } catch (IOException | URISyntaxException e) { + log.error(e.getMessage(), e); + } + } + + return result; + } + + private static String getExtensionsResource() { + return ServiceProviderExtensionStorage.EXTENSIONS_RESOURCE; + } + + private Set readExtensions(Path extensionPath) throws IOException { + final Set result = new HashSet<>(); + Files.walkFileTree(extensionPath, Collections.emptySet(), 1, new SimpleFileVisitor() { + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + log.debug("Read '{}'", file); + try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) { + ServiceProviderExtensionStorage.read(reader, result); + } + + return FileVisitResult.CONTINUE; + } + + }); + + return result; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/VersionManager.java b/pf4j/src/main/java/org/pf4j/VersionManager.java new file mode 100644 index 0000000..ae9187b --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/VersionManager.java @@ -0,0 +1,34 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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; + +/** + * Manager responsible for versions of plugins. + * + * @author Decebal Suiu + */ +public interface VersionManager { + + /** + * Check if a {@code constraint} and a {@code version} match. + * + * @param constraint + * @param version + * @return + */ + boolean satisfies(String constraint, String version); + +} diff --git a/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java b/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java new file mode 100644 index 0000000..30c604c --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/processor/ExtensionAnnotationProcessor.java @@ -0,0 +1,232 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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.processor; + +import org.pf4j.Extension; +import org.pf4j.ExtensionPoint; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.tools.Diagnostic; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * @author Decebal Suiu + */ +public class ExtensionAnnotationProcessor extends AbstractProcessor { + + private static final String STORAGE_CLASS_NAME = "pf4j.storageClassName"; + + private Map> extensions = new HashMap<>(); // the key is the extension point + private Map> oldExtensions = new HashMap<>(); // the key is the extension point + + private ExtensionStorage storage; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + + info("%s init", ExtensionAnnotationProcessor.class); + storage = createStorage(); + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + @Override + public Set getSupportedAnnotationTypes() { + Set annotationTypes = new HashSet<>(); + annotationTypes.add(Extension.class.getName()); + + return annotationTypes; + } + + @Override + public Set getSupportedOptions() { + Set options = new HashSet<>(); + options.add(STORAGE_CLASS_NAME); + + return options; + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return false; + } + + info("Processing @%s", Extension.class); + for (Element element : roundEnv.getElementsAnnotatedWith(Extension.class)) { + // check if @Extension is put on class and not on method or constructor + if (!(element instanceof TypeElement)) { + continue; + } + + // check if class extends/implements an extension point + if (!isExtension(element.asType())) { + continue; + } + + TypeElement extensionElement = (TypeElement) element; +// Extension annotation = element.getAnnotation(Extension.class); + List extensionPointElements = findExtensionPoints(extensionElement); + if (extensionPointElements.isEmpty()) { + // TODO throw error ? + continue; + } + + String extension = getBinaryName(extensionElement); + for (TypeElement extensionPointElement : extensionPointElements) { + String extensionPoint = getBinaryName(extensionPointElement); + Set extensionPoints = extensions.get(extensionPoint); + if (extensionPoints == null) { + extensionPoints = new TreeSet<>(); + extensions.put(extensionPoint, extensionPoints); + } + extensionPoints.add(extension); + } + } + + // read old extensions + oldExtensions = storage.read(); + for (Map.Entry> entry : oldExtensions.entrySet()) { + String extensionPoint = entry.getKey(); + if (extensions.containsKey(extensionPoint)) { + extensions.get(extensionPoint).addAll(entry.getValue()); + } else { + extensions.put(extensionPoint, entry.getValue()); + } + } + + // write extensions + storage.write(extensions); + + return false; + } + + public ProcessingEnvironment getProcessingEnvironment() { + return processingEnv; + } + + public void error(String message, Object... args) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format(message, args)); + } + + public void error(Element element, String message, Object... args) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format(message, args), element); + } + + public void info(String message, Object... args) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format(message, args)); + } + + public void info(Element element, String message, Object... args) { + processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format(message, args), element); + } + + public String getBinaryName(TypeElement element) { + return processingEnv.getElementUtils().getBinaryName(element).toString(); + } + + public Map> getExtensions() { + return extensions; + } + + public Map> getOldExtensions() { + return oldExtensions; + } + + private List findExtensionPoints(TypeElement extensionElement) { + List extensionPointElements = new ArrayList<>(); + + // search in interfaces + for (TypeMirror item : extensionElement.getInterfaces()) { + boolean isExtensionPoint = processingEnv.getTypeUtils().isSubtype(item, getExtensionPointType()); + if (isExtensionPoint) { + TypeElement extensionPointElement = (TypeElement) ((DeclaredType) item).asElement(); + extensionPointElements.add(extensionPointElement); + } + } + + // search in superclass + TypeMirror superclass = extensionElement.getSuperclass(); + if (superclass.getKind() != TypeKind.NONE) { + boolean isExtensionPoint = processingEnv.getTypeUtils().isSubtype(superclass, getExtensionPointType()); + if (isExtensionPoint) { + TypeElement extensionPointElement = (TypeElement) ((DeclaredType) superclass).asElement(); + extensionPointElements.add(extensionPointElement); + } + } + + return extensionPointElements; + } + + private boolean isExtension(TypeMirror typeMirror) { + return processingEnv.getTypeUtils().isAssignable(typeMirror, getExtensionPointType()); + } + + + private TypeMirror getExtensionPointType() { + return processingEnv.getElementUtils().getTypeElement(ExtensionPoint.class.getName()).asType(); + } + + @SuppressWarnings("unchecked") + private ExtensionStorage createStorage() { + ExtensionStorage storage = null; + + // search in processing options + String storageClassName = processingEnv.getOptions().get(STORAGE_CLASS_NAME); + if (storageClassName == null) { + // search in system properties + storageClassName = System.getProperty(STORAGE_CLASS_NAME); + } + + if (storageClassName != null) { + // use reflection to create the storage instance + try { + Class storageClass = getClass().getClassLoader().loadClass(storageClassName); + Constructor constructor = storageClass.getConstructor(ExtensionAnnotationProcessor.class); + storage = (ExtensionStorage) constructor.newInstance(this); + } catch (Exception e) { + error(e.getMessage()); + } + } + + if (storage == null) { + // default storage + storage = new LegacyExtensionStorage(this); + } + + return storage; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/processor/ExtensionStorage.java b/pf4j/src/main/java/org/pf4j/processor/ExtensionStorage.java new file mode 100644 index 0000000..55394b7 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/processor/ExtensionStorage.java @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.processor; + +import javax.annotation.processing.Filer; +import javax.lang.model.element.Element; +import java.util.Map; +import java.util.Set; + +/** + * @author Decebal Suiu + */ +public abstract class ExtensionStorage { + + protected final ExtensionAnnotationProcessor processor; + + public ExtensionStorage(ExtensionAnnotationProcessor processor) { + this.processor = processor; + } + + public abstract Map> read(); + + public abstract void write(Map> extensions); + + /** + * Helper method. + */ + protected Filer getFiler() { + return processor.getProcessingEnvironment().getFiler(); + } + + /** + * Helper method. + */ + protected void error(String message, Object... args) { + processor.error(message, args); + } + + /** + * Helper method. + */ + protected void error(Element element, String message, Object... args) { + processor.error(element, message, args); + } + + /** + * Helper method. + */ + protected void info(String message, Object... args) { + processor.info(message, args); + } + + /** + * Helper method. + */ + protected void info(Element element, String message, Object... args) { + processor.info(element, message, args); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/processor/LegacyExtensionStorage.java b/pf4j/src/main/java/org/pf4j/processor/LegacyExtensionStorage.java new file mode 100644 index 0000000..d664e4e --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/processor/LegacyExtensionStorage.java @@ -0,0 +1,106 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.processor; + +import javax.annotation.processing.FilerException; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Reader; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * @author Decebal Suiu + */ +public class LegacyExtensionStorage extends ExtensionStorage { + + public static final String EXTENSIONS_RESOURCE = "META-INF/extensions.idx"; + + private static final Pattern COMMENT = Pattern.compile("#.*"); + private static final Pattern WHITESPACE = Pattern.compile("\\s+"); + + public LegacyExtensionStorage(ExtensionAnnotationProcessor processor) { + super(processor); + } + + public static void read(Reader reader, Set entries) throws IOException { + BufferedReader bufferedReader = new BufferedReader(reader); + + String line; + while ((line = bufferedReader.readLine()) != null) { + line = COMMENT.matcher(line).replaceFirst(""); + line = WHITESPACE.matcher(line).replaceAll(""); + if (line.length() > 0) { + entries.add(line); + } + } + + bufferedReader.close(); + } + + @Override + public Map> read() { + Map> extensions = new HashMap<>(); + + try { + FileObject file = getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE); + // TODO try to calculate the extension point + Set entries = new HashSet<>(); + read(file.openReader(true), entries); + extensions.put(null, entries); + } catch (FileNotFoundException e) { + // ignore + } catch (FilerException e) { + // re-opening the file for reading or after writing is ignorable + } catch (IOException e) { + error(e.getMessage()); + } + + return extensions; + } + + @Override + public void write(Map> extensions) { + try { + FileObject file = getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE); + BufferedWriter writer = new BufferedWriter(file.openWriter()); + writer.write("# Generated by PF4J"); // write header + writer.newLine(); + for (Map.Entry> entry : extensions.entrySet()) { + for (String extension : entry.getValue()) { + writer.write(extension); + writer.newLine(); + } + } + + writer.close(); + } catch (FileNotFoundException e) { + // it's the first time, create the file + } catch (FilerException e) { + // re-opening the file for reading or after writing is ignorable + } catch (IOException e) { + error(e.toString()); + } + } + +} diff --git a/pf4j/src/main/java/org/pf4j/processor/ServiceProviderExtensionStorage.java b/pf4j/src/main/java/org/pf4j/processor/ServiceProviderExtensionStorage.java new file mode 100644 index 0000000..135063f --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/processor/ServiceProviderExtensionStorage.java @@ -0,0 +1,119 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.processor; + +import javax.annotation.processing.FilerException; +import javax.tools.FileObject; +import javax.tools.StandardLocation; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.Reader; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * @author Decebal Suiu + */ +public class ServiceProviderExtensionStorage extends ExtensionStorage { + + public static final String EXTENSIONS_RESOURCE = "META-INF/services"; + + private static final Pattern COMMENT = Pattern.compile("#.*"); + private static final Pattern WHITESPACE = Pattern.compile("\\s+"); + + public ServiceProviderExtensionStorage(ExtensionAnnotationProcessor processor) { + super(processor); + } + + public static void read(Reader reader, Set entries) throws IOException { + BufferedReader bufferedReader = new BufferedReader(reader); + + String line; + while ((line = bufferedReader.readLine()) != null) { + line = COMMENT.matcher(line).replaceFirst(""); + line = WHITESPACE.matcher(line).replaceAll(""); + if (line.length() > 0) { + entries.add(line); + } + } + + bufferedReader.close(); + } + + @Override + public Map> read() { + Map> extensions = new HashMap<>(); + + for (String extensionPoint : processor.getExtensions().keySet()) { + try { + FileObject file = getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE + + "/" + extensionPoint); + Set entries = new HashSet<>(); + read(file.openReader(true), entries); + extensions.put(extensionPoint, entries); + } catch (FileNotFoundException e) { + // doesn't exist, ignore + } catch (FilerException e) { + // re-opening the file for reading or after writing is ignorable + } catch (IOException e) { + error(e.getMessage()); + } + } + + return extensions; + } + + @Override + public void write(Map> extensions) { + for (Map.Entry> entry : extensions.entrySet()) { + String extensionPoint = entry.getKey(); + try { + FileObject file = getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE + + "/" + extensionPoint); + BufferedWriter writer = new BufferedWriter(file.openWriter()); + // write header + writer.write("# Generated by PF4J"); // write header + writer.newLine(); + // write extensions + for (String extension : entry.getValue()) { + writer.write(extension); + if (!isExtensionOld(extensionPoint, extension)) { + writer.write(" # pf4j extension"); + } + writer.newLine(); + } + writer.close(); + } catch (FileNotFoundException e) { + // it's the first time, create the file + } catch (FilerException e) { + // re-opening the file for reading or after writing is ignorable + } catch (IOException e) { + error(e.toString()); + } + } + } + + private boolean isExtensionOld(String extensionPoint, String extension) { + return processor.getOldExtensions().containsKey(extensionPoint) + && processor.getOldExtensions().get(extensionPoint).contains(extension); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/AndFileFilter.java b/pf4j/src/main/java/org/pf4j/util/AndFileFilter.java new file mode 100644 index 0000000..0add9f4 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/AndFileFilter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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.util; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * This filter providing conditional AND logic across a list of + * file filters. This filter returns true if all filters in the + * list return true. Otherwise, it returns false. + * Checking of the file filter list stops when the first filter returns + * false. + * + * @author Decebal Suiu + */ +public class AndFileFilter implements FileFilter { + + /** The list of file filters. */ + private List fileFilters; + + public AndFileFilter() { + this(new ArrayList()); + } + + public AndFileFilter(FileFilter... fileFilters) { + this(Arrays.asList(fileFilters)); + } + + public AndFileFilter(List fileFilters) { + this.fileFilters = new ArrayList<>(fileFilters); + } + + public AndFileFilter addFileFilter(FileFilter fileFilter) { + fileFilters.add(fileFilter); + + return this; + } + + public List getFileFilters() { + return Collections.unmodifiableList(fileFilters); + } + + public boolean removeFileFilter(FileFilter fileFilter) { + return fileFilters.remove(fileFilter); + } + + public void setFileFilters(List fileFilters) { + this.fileFilters = new ArrayList<>(fileFilters); + } + + @Override + public boolean accept(File file) { + if (this.fileFilters.size() == 0) { + return false; + } + + for (FileFilter fileFilter : this.fileFilters) { + if (!fileFilter.accept(file)) { + return false; + } + } + + return true; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/ClassUtils.java b/pf4j/src/main/java/org/pf4j/util/ClassUtils.java new file mode 100644 index 0000000..ca2632d --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/ClassUtils.java @@ -0,0 +1,91 @@ +/* + * Copyright 2016 Decebal Suiu + * + * 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.util; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +/** + * @author Decebal Suiu + */ +public class ClassUtils { + + public static List getAllInterfacesNames(Class aClass) { + return toString(getAllInterfaces(aClass)); + } + + public static List> getAllInterfaces(Class aClass) { + List> list = new ArrayList<>(); + + while (aClass != null) { + Class[] interfaces = aClass.getInterfaces(); + for (Class anInterface : interfaces) { + if (!list.contains(anInterface)) { + list.add(anInterface); + } + + List> superInterfaces = getAllInterfaces(anInterface); + for (Class superInterface : superInterfaces) { + if (!list.contains(superInterface)) { + list.add(superInterface); + } + } + } + + aClass = aClass.getSuperclass(); + } + + return list; + } + + /* + public static List getAllAbstractClassesNames(Class aClass) { + return toString(getAllInterfaces(aClass)); + } + + public static List getAllAbstractClasses(Class aClass) { + List> list = new ArrayList<>(); + + Class superclass = aClass.getSuperclass(); + while (superclass != null) { + if (Modifier.isAbstract(superclass.getModifiers())) { + list.add(superclass); + } + superclass = superclass.getSuperclass(); + } + + return list; + } + */ + + /** + * Uses {@link Class#getSimpleName()} to convert from {@link Class} to {@link String}. + * + * @param classes + * @return + */ + private static List toString(List> classes) { + List list = new ArrayList<>(); + + for (Class aClass : classes) { + list.add(aClass.getSimpleName()); + } + + return list; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java b/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java new file mode 100644 index 0000000..bc2b8fb --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/DirectedGraph.java @@ -0,0 +1,192 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; + +/** + * See Wikipedia for more information. + * + * @author Decebal Suiu + */ +public class DirectedGraph { + + /** + * The implementation here is basically an adjacency list, but instead + * of an array of lists, a Map is used to map each vertex to its list of + * adjacent vertices. + */ + private Map> neighbors = new HashMap<>(); + + /** + * Add a vertex to the graph. Nothing happens if vertex is already in graph. + */ + public void addVertex(V vertex) { + if (containsVertex(vertex)) { + return; + } + + neighbors.put(vertex, new ArrayList()); + } + + /** + * True if graph contains vertex. + */ + public boolean containsVertex(V vertex) { + return neighbors.containsKey(vertex); + } + + public void removeVertex(V vertex) { + neighbors.remove(vertex); + } + + /** + * Add an edge to the graph; if either vertex does not exist, it's added. + * This implementation allows the creation of multi-edges and self-loops. + */ + public void addEdge(V from, V to) { + addVertex(from); + addVertex(to); + neighbors.get(from).add(to); + } + + /** + * Remove an edge from the graph. Nothing happens if no such edge. + * @throws {@link IllegalArgumentException} if either vertex doesn't exist. + */ + public void removeEdge(V from, V to) { + if (!containsVertex(from)) { + throw new IllegalArgumentException("Nonexistent vertex " + from); + } + + if (!containsVertex(to)) { + throw new IllegalArgumentException("Nonexistent vertex " + to); + } + + neighbors.get(from).remove(to); + } + + public List getNeighbors(V vertex) { + return containsVertex(vertex) ? neighbors.get(vertex) : new ArrayList(); + } + + /** + * Report (as a Map) the out-degree (the number of tail ends adjacent to a vertex) of each vertex. + */ + public Map outDegree() { + Map result = new HashMap<>(); + for (V vertex : neighbors.keySet()) { + result.put(vertex, neighbors.get(vertex).size()); + } + + return result; + } + + /** + * Report (as a Map) the in-degree (the number of head ends adjacent to a vertex) of each vertex. + */ + public Map inDegree() { + Map result = new HashMap<>(); + for (V vertex : neighbors.keySet()) { + result.put(vertex, 0); // all in-degrees are 0 + } + for (V from : neighbors.keySet()) { + for (V to : neighbors.get(from)) { + result.put(to, result.get(to) + 1); // increment in-degree + } + } + + return result; + } + + /** + * Report (as a List) the topological sort of the vertices; null for no such sort. + * See this for more information. + */ + public List topologicalSort() { + Map degree = inDegree(); + + // determine all vertices with zero in-degree + Stack zeroVertices = new Stack<>(); // stack as good as any here + for (V v : degree.keySet()) { + if (degree.get(v) == 0) { + zeroVertices.push(v); + } + } + + // determine the topological order + List result = new ArrayList<>(); + while (!zeroVertices.isEmpty()) { + V vertex = zeroVertices.pop(); // choose a vertex with zero in-degree + result.add(vertex); // vertex 'v' is next in topological order + // "remove" vertex 'v' by updating its neighbors + for (V neighbor : neighbors.get(vertex)) { + degree.put(neighbor, degree.get(neighbor) - 1); + // remember any vertices that now have zero in-degree + if (degree.get(neighbor) == 0) { + zeroVertices.push(neighbor); + } + } + } + + // check that we have used the entire graph (if not, there was a cycle) + if (result.size() != neighbors.size()) { + return null; + } + + return result; + } + + /** + * Report (as a List) the reverse topological sort of the vertices; null for no such sort. + */ + public List reverseTopologicalSort() { + List list = topologicalSort(); + if (list == null) { + return null; + } + + Collections.reverse(list); + + return list; + } + + /** + * True if graph is a dag (directed acyclic graph). + */ + public boolean isDag () { + return topologicalSort() != null; + } + + /** + * String representation of graph. + */ + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + for (V vertex : neighbors.keySet()) { + sb.append("\n " + vertex + " -> " + neighbors.get(vertex)); + } + + return sb.toString(); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/DirectoryFileFilter.java b/pf4j/src/main/java/org/pf4j/util/DirectoryFileFilter.java new file mode 100644 index 0000000..fab20fa --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/DirectoryFileFilter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.util; + +import java.io.File; +import java.io.FileFilter; + +/** + * Filter accepts files that are directories. + * + * @author Decebal Suiu + */ +public class DirectoryFileFilter implements FileFilter { + + @Override + public boolean accept(File file) { + return file.isDirectory(); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/ExtensionFileFilter.java b/pf4j/src/main/java/org/pf4j/util/ExtensionFileFilter.java new file mode 100644 index 0000000..a8636fc --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/ExtensionFileFilter.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.util; + +import java.io.File; +import java.io.FileFilter; + +/** + * Filter accepts any file ending in extension. The case of the filename is ignored. + * + * @author Decebal Suiu + */ +public class ExtensionFileFilter implements FileFilter { + + private String extension; + + public ExtensionFileFilter(String extension) { + this.extension = extension; + } + + @Override + public boolean accept(File file) { + // perform a case insensitive check. + return file.getName().toUpperCase().endsWith(extension.toUpperCase()); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/FileUtils.java b/pf4j/src/main/java/org/pf4j/util/FileUtils.java new file mode 100644 index 0000000..9399588 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/FileUtils.java @@ -0,0 +1,202 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.util; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileFilter; +import java.io.FileReader; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * @author Decebal Suiu + */ +public class FileUtils { + + private static final Logger log = LoggerFactory.getLogger(FileUtils.class); + + public static List readLines(Path path, boolean ignoreComments) throws IOException { + File file = path.toFile(); + if (!file.exists() || !file.isFile()) { + return new ArrayList<>(); + } + + List lines = new ArrayList<>(); + + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + String line; + while ((line = reader.readLine()) != null) { + if (ignoreComments && !line.startsWith("#") && !lines.contains(line)) { + lines.add(line); + } + } + } + + return lines; + } + + public static void writeLines(Collection lines, File file) throws IOException { + Files.write(file.toPath(), lines, StandardCharsets.UTF_8); + } + + /** + * Delete a file or recursively delete a folder, do not follow symlinks. + * + * @param path the file or folder to delete + * @throws IOException if something goes wrong + */ + public static void delete(Path path) throws IOException { + Files.walkFileTree(path, new SimpleFileVisitor() { + + @Override + public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { + if (!attrs.isSymbolicLink()) { + Files.delete(path); + } + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + + return FileVisitResult.CONTINUE; + } + + }); + } + + public static List getJars(Path folder) { + List bucket = new ArrayList<>(); + getJars(bucket, folder); + + return bucket; + } + + private static void getJars(final List bucket, Path folder) { + FileFilter jarFilter = new JarFileFilter(); + FileFilter directoryFilter = new DirectoryFileFilter(); + + if (Files.exists(folder) && Files.isDirectory(folder)) { + File[] jars = folder.toFile().listFiles(jarFilter); + for (int i = 0; (jars != null) && (i < jars.length); ++i) { + bucket.add(jars[i]); + } + + File[] directories = folder.toFile().listFiles(directoryFilter); + for (int i = 0; (directories != null) && (i < directories.length); ++i) { + File directory = directories[i]; + getJars(bucket, directory.toPath()); + } + } + } + + /** + * Finds a path with various endings or null if not found. + * + * @param basePath the base name + * @param endings a list of endings to search for + * @return new path or null if not found + */ + public static Path findWithEnding(Path basePath, String... endings) { + for (String ending : endings) { + Path newPath = basePath.resolveSibling(basePath.getFileName() + ending); + if (Files.exists(newPath)) { + return newPath; + } + } + + return null; + } + + /** + * Delete a file (not recursively) and ignore any errors. + * + * @param path the path to delete + */ + public static void optimisticDelete(Path path) { + if (path == null) { + return; + } + + try { + Files.delete(path); + } catch (IOException ignored) { } + } + + /** + * Unzip a zip file in a directory that has the same name as the zip file. + * For example if the zip file is {@code my-plugin.zip} then the resulted directory + * is {@code my-plugin}. + * + * @param filePath the file to evaluate + * @return Path of unzipped folder or original path if this was not a zip file + * @throws IOException on error + */ + public static Path expandIfZip(Path filePath) throws IOException { + if (!isZipFile(filePath)) { + return filePath; + } + + FileTime pluginZipDate = Files.getLastModifiedTime(filePath); + String fileName = filePath.getFileName().toString(); + Path pluginDirectory = filePath.resolveSibling(fileName.substring(0, fileName.lastIndexOf("."))); + + if (!Files.exists(pluginDirectory) || pluginZipDate.compareTo(Files.getLastModifiedTime(pluginDirectory)) > 0) { + // do not overwrite an old version, remove it + if (Files.exists(pluginDirectory)) { + FileUtils.delete(pluginDirectory); + } + + // create root for plugin + Files.createDirectories(pluginDirectory); + + // expand '.zip' file + Unzip unzip = new Unzip(); + unzip.setSource(filePath.toFile()); + unzip.setDestination(pluginDirectory.toFile()); + unzip.extract(); + log.info("Expanded plugin zip '{}' in '{}'", filePath.getFileName(), pluginDirectory.getFileName()); + } + + return pluginDirectory; + } + + /** + * Return true only if path is a zip file. + * + * @param path to a file/dir + * @return true if file with {@code .zip} ending + */ + public static boolean isZipFile(Path path) { + return Files.isRegularFile(path) && path.toString().toLowerCase().endsWith(".zip"); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/HiddenFilter.java b/pf4j/src/main/java/org/pf4j/util/HiddenFilter.java new file mode 100644 index 0000000..649e8e0 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/HiddenFilter.java @@ -0,0 +1,33 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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.util; + +import java.io.File; +import java.io.FileFilter; + +/** + * Filter that only accepts hidden files. + * + * @author decebal.suiu + */ +public class HiddenFilter implements FileFilter { + + @Override + public boolean accept(File file) { + return file.isHidden(); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/JarFileFilter.java b/pf4j/src/main/java/org/pf4j/util/JarFileFilter.java new file mode 100644 index 0000000..f015850 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/JarFileFilter.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.util; + +/** + * File filter that accepts all files ending with .JAR. + * This filter is case insensitive. + * + * @author Decebal Suiu + */ +public class JarFileFilter extends ExtensionFileFilter { + + /** + * The extension that this filter will search for. + */ + private static final String JAR_EXTENSION = ".JAR"; + + public JarFileFilter() { + super(JAR_EXTENSION); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/NameFileFilter.java b/pf4j/src/main/java/org/pf4j/util/NameFileFilter.java new file mode 100644 index 0000000..c114352 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/NameFileFilter.java @@ -0,0 +1,40 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.util; + +import java.io.File; +import java.io.FileFilter; + +/** + * Filter accepts any file with this name. The case of the filename is ignored. + * + * @author Decebal Suiu + */ +public class NameFileFilter implements FileFilter { + + private String name; + + public NameFileFilter(String name) { + this.name = name; + } + + @Override + public boolean accept(File file) { + // perform a case insensitive check. + return file.getName().equalsIgnoreCase(name); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/NotFileFilter.java b/pf4j/src/main/java/org/pf4j/util/NotFileFilter.java new file mode 100644 index 0000000..06f6140 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/NotFileFilter.java @@ -0,0 +1,39 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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.util; + +import java.io.File; +import java.io.FileFilter; + +/** + * This filter produces a logical NOT of the filters specified. + * + * @author Decebal Suiu + */ +public class NotFileFilter implements FileFilter { + + private FileFilter filter; + + public NotFileFilter(FileFilter filter) { + this.filter = filter; + } + + @Override + public boolean accept(File file) { + return !filter.accept(file); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/OrFileFilter.java b/pf4j/src/main/java/org/pf4j/util/OrFileFilter.java new file mode 100644 index 0000000..8bf3f24 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/OrFileFilter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013 Decebal Suiu + * + * 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.util; + +import java.io.File; +import java.io.FileFilter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * This filter providing conditional OR logic across a list of + * file filters. This filter returns true if one filter in the + * list return true. Otherwise, it returns false. + * Checking of the file filter list stops when the first filter returns + * true. + * + * @author Decebal Suiu + */ +public class OrFileFilter implements FileFilter { + + /** The list of file filters. */ + private List fileFilters; + + public OrFileFilter() { + this(new ArrayList()); + } + + public OrFileFilter(FileFilter... fileFilters) { + this(Arrays.asList(fileFilters)); + } + + public OrFileFilter(List fileFilters) { + this.fileFilters = new ArrayList<>(fileFilters); + } + + public OrFileFilter addFileFilter(FileFilter fileFilter) { + fileFilters.add(fileFilter); + + return this; + } + + public List getFileFilters() { + return Collections.unmodifiableList(fileFilters); + } + + public boolean removeFileFilter(FileFilter fileFilter) { + return fileFilters.remove(fileFilter); + } + + public void setFileFilters(List fileFilters) { + this.fileFilters = new ArrayList<>(fileFilters); + } + + @Override + public boolean accept(File file) { + if (this.fileFilters.size() == 0) { + return true; + } + + for (FileFilter fileFilter : this.fileFilters) { + if (fileFilter.accept(file)) { + return true; + } + } + + return false; + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/StringUtils.java b/pf4j/src/main/java/org/pf4j/util/StringUtils.java new file mode 100644 index 0000000..49657d5 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/StringUtils.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.util; + +/** + * @author Decebal Suiu + */ +public class StringUtils { + + public static boolean isEmpty(String str) { + return (str == null) || str.isEmpty(); + } + + public static boolean isNotEmpty(String str) { + return !isEmpty(str); + } + + /** + * Format the string. Replace "{}" with %s and format the string using {@link String#format(String, Object...)}. + */ + public static String format(String str, Object... args) { + str = str.replaceAll("\\{}", "%s"); + + return String.format(str, args); + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/Unzip.java b/pf4j/src/main/java/org/pf4j/util/Unzip.java new file mode 100644 index 0000000..5d0c34a --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/Unzip.java @@ -0,0 +1,102 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class extracts the content of the plugin zip into a directory. + * It's a class for only the internal use. + * + * @author Decebal Suiu + */ +public class Unzip { + + private static final Logger log = LoggerFactory.getLogger(Unzip.class); + + /** + * Holds the destination directory. + * File will be unzipped into the destination directory. + */ + private File destination; + + /** + * Holds path to zip file. + */ + private File source; + + public Unzip() { + } + + public Unzip(File source, File destination) { + this.source = source; + this.destination = destination; + } + + public void setSource(File source) { + this.source = source; + } + + public void setDestination(File destination) { + this.destination = destination; + } + + public void extract() throws IOException { + log.debug("Extract content of '{}' to '{}'", source, destination); + + // delete destination file if exists + if (destination.exists() && destination.isDirectory()) { + FileUtils.delete(destination.toPath()); + } + + try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(source))) { + ZipEntry zipEntry; + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + try { + File file = new File(destination, zipEntry.getName()); + + // create intermediary directories - sometimes zip don't add them + File dir = new File(file.getParent()); + dir.mkdirs(); + + if (zipEntry.isDirectory()) { + file.mkdirs(); + } else { + byte[] buffer = new byte[1024]; + int length; + try (FileOutputStream fos = new FileOutputStream(file)) { + while ((length = zipInputStream.read(buffer)) >= 0) { + fos.write(buffer, 0, length); + } + } + } + } catch (FileNotFoundException e) { + log.error("File '{}' not found", zipEntry.getName()); + } + } + } + } + +} diff --git a/pf4j/src/main/java/org/pf4j/util/ZipFileFilter.java b/pf4j/src/main/java/org/pf4j/util/ZipFileFilter.java new file mode 100644 index 0000000..d4748e6 --- /dev/null +++ b/pf4j/src/main/java/org/pf4j/util/ZipFileFilter.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.util; + +/** + * File filter that accepts all files ending with .ZIP. + * This filter is case insensitive. + * + * @author Decebal Suiu + */ +public class ZipFileFilter extends ExtensionFileFilter { + + /** + * The extension that this filter will search for. + */ + private static final String ZIP_EXTENSION = ".ZIP"; + + public ZipFileFilter() { + super(ZIP_EXTENSION); + } + +} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractExtensionFinder.java deleted file mode 100644 index 8e4a506..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractExtensionFinder.java +++ /dev/null @@ -1,247 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.ClassUtils; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * @author Decebal Suiu - */ -public abstract class AbstractExtensionFinder implements ExtensionFinder, PluginStateListener { - - private static final Logger log = LoggerFactory.getLogger(AbstractExtensionFinder.class); - - protected PluginManager pluginManager; - protected volatile Map> entries; // cache by pluginId - - public AbstractExtensionFinder(PluginManager pluginManager) { - this.pluginManager = pluginManager; - } - - public abstract Map> readPluginsStorages(); - - public abstract Map> readClasspathStorages(); - - @Override - @SuppressWarnings("unchecked") - public List> find(Class type) { - log.debug("Finding extensions of extension point '{}'", type.getName()); - Map> entries = getEntries(); - List> result = new ArrayList<>(); - - // add extensions found in classpath and plugins - for (String pluginId : entries.keySet()) { - // classpath's extensions <=> pluginId = null - List> pluginExtensions = find(type, pluginId); - result.addAll(pluginExtensions); - } - - if (entries.isEmpty()) { - log.debug("No extensions found for extension point '{}'", type.getName()); - } else { - log.debug("Found {} extensions for extension point '{}'", result.size(), type.getName()); - } - - // sort by "ordinal" property - Collections.sort(result); - - return result; - } - - @Override - @SuppressWarnings("unchecked") - public List> find(Class type, String pluginId) { - log.debug("Finding extensions of extension point '{}' for plugin '{}'", type.getName(), pluginId); - List> result = new ArrayList<>(); - - // classpath's extensions <=> pluginId = null - Set classNames = findClassNames(pluginId); - if (classNames == null || classNames.isEmpty()) { - return result; - } - - if (pluginId != null) { - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); - if (PluginState.STARTED != pluginWrapper.getPluginState()) { - return result; - } - - log.trace("Checking extensions from plugin '{}'", pluginId); - } else { - log.trace("Checking extensions from classpath"); - } - - ClassLoader classLoader = (pluginId != null) ? pluginManager.getPluginClassLoader(pluginId) : getClass().getClassLoader(); - - for (String className : classNames) { - try { - log.debug("Loading class '{}' using class loader '{}'", className, classLoader); - Class extensionClass = classLoader.loadClass(className); - - log.debug("Checking extension type '{}'", className); - if (type.isAssignableFrom(extensionClass)) { - ExtensionWrapper extensionWrapper = createExtensionWrapper(extensionClass); - result.add(extensionWrapper); - log.debug("Added extension '{}' with ordinal {}", className, extensionWrapper.getOrdinal()); - } else { - log.trace("'{}' is not an extension for extension point '{}'", className, type.getName()); - if (RuntimeMode.DEVELOPMENT.equals(pluginManager.getRuntimeMode())) { - checkDifferentClassLoaders(type, extensionClass); - } - } - } catch (ClassNotFoundException e) { - log.error(e.getMessage(), e); - } - } - - if (result.isEmpty()) { - log.debug("No extensions found for extension point '{}'", type.getName()); - } else { - log.debug("Found {} extensions for extension point '{}'", result.size(), type.getName()); - } - - // sort by "ordinal" property - Collections.sort(result); - - return result; - } - - @Override - public List find(String pluginId) { - log.debug("Finding extensions from plugin '{}'", pluginId); - List result = new ArrayList<>(); - - Set classNames = findClassNames(pluginId); - if (classNames.isEmpty()) { - return result; - } - - if (pluginId != null) { - PluginWrapper pluginWrapper = pluginManager.getPlugin(pluginId); - if (PluginState.STARTED != pluginWrapper.getPluginState()) { - return result; - } - - log.trace("Checking extensions from plugin '{}'", pluginId); - } else { - log.trace("Checking extensions from classpath"); - } - - ClassLoader classLoader = (pluginId != null) ? pluginManager.getPluginClassLoader(pluginId) : getClass().getClassLoader(); - - for (String className : classNames) { - try { - log.debug("Loading class '{}' using class loader '{}'", className, classLoader); - Class extensionClass = classLoader.loadClass(className); - - ExtensionWrapper extensionWrapper = createExtensionWrapper(extensionClass); - result.add(extensionWrapper); - log.debug("Added extension '{}' with ordinal {}", className, extensionWrapper.getOrdinal()); - } catch (ClassNotFoundException e) { - log.error(e.getMessage(), e); - } - } - - if (result.isEmpty()) { - log.debug("No extensions found for plugin '{}'", pluginId); - } else { - log.debug("Found {} extensions for plugin '{}'", result.size(), pluginId); - } - - // sort by "ordinal" property - Collections.sort(result); - - return result; - } - - @Override - public Set findClassNames(String pluginId) { - return getEntries().get(pluginId); - } - - @Override - public void pluginStateChanged(PluginStateEvent event) { - // TODO optimize (do only for some transitions) - // clear cache - entries = null; - } - - protected void debugExtensions(Set extensions) { - if (log.isDebugEnabled()) { - if (extensions.isEmpty()) { - log.debug("No extensions found"); - } else { - log.debug("Found possible {} extensions:", extensions.size()); - for (String extension : extensions) { - log.debug(" " + extension); - } - } - } - } - - private Map> readStorages() { - Map> result = new LinkedHashMap<>(); - - result.putAll(readClasspathStorages()); - result.putAll(readPluginsStorages()); - - return result; - } - - private Map> getEntries() { - if (entries == null) { - entries = readStorages(); - } - - return entries; - } - - private ExtensionWrapper createExtensionWrapper(Class extensionClass) { - ExtensionDescriptor descriptor = new ExtensionDescriptor(); - int ordinal = 0; - if (extensionClass.isAnnotationPresent(Extension.class)) { - ordinal = extensionClass.getAnnotation(Extension.class).ordinal(); - } - descriptor.setOrdinal(ordinal); - descriptor.setExtensionClass(extensionClass); - - ExtensionWrapper extensionWrapper = new ExtensionWrapper<>(descriptor); - extensionWrapper.setExtensionFactory(pluginManager.getExtensionFactory()); - - return extensionWrapper; - } - - private void checkDifferentClassLoaders(Class type, Class extensionClass) { - ClassLoader typeClassLoader = type.getClassLoader(); // class loader of extension point - ClassLoader extensionClassLoader = extensionClass.getClassLoader(); - boolean match = ClassUtils.getAllInterfacesNames(extensionClass).contains(type.getSimpleName()); - if (match && !extensionClassLoader.equals(typeClassLoader)) { - // in this scenario the method 'isAssignableFrom' returns only FALSE - // see http://www.coderanch.com/t/557846/java/java/FWIW-FYI-isAssignableFrom-isInstance-differing - log.error("Different class loaders: '{}' (E) and '{}' (EP)", extensionClassLoader, typeClassLoader); - } - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java deleted file mode 100644 index 94fee2f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/AbstractPluginManager.java +++ /dev/null @@ -1,900 +0,0 @@ -/* - * Copyright 2016 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.StringUtils; - -import java.io.Closeable; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * This class implements the boilerplate plugin code that any {@link PluginManager} - * implementation would have to support. - * It helps cut the noise out of the subclass that handles plugin management. - * - * @author Decebal Suiu - */ -public abstract class AbstractPluginManager implements PluginManager { - - private static final Logger log = LoggerFactory.getLogger(AbstractPluginManager.class); - - private Path pluginsRoot; - - private ExtensionFinder extensionFinder; - - private PluginDescriptorFinder pluginDescriptorFinder; - - /* - * A map of plugins this manager is responsible for (the key is the 'pluginId'). - */ - protected Map plugins; - - /* - * A map of plugin class loaders (the key is the 'pluginId'). - */ - private Map pluginClassLoaders; - - /* - * A list with unresolved plugins (unresolved dependency). - */ - private List unresolvedPlugins; - - /** - * A list with all resolved plugins (resolved dependency). - */ - private List resolvedPlugins; - - /* - * A list with started plugins. - */ - private List startedPlugins; - - /* - * The registered {@link PluginStateListener}s. - */ - private List pluginStateListeners; - - /* - * Cache value for the runtime mode. - * No need to re-read it because it wont change at runtime. - */ - private RuntimeMode runtimeMode; - - /* - * The system version used for comparisons to the plugin requires attribute. - */ - private String systemVersion = "0.0.0"; - - private PluginRepository pluginRepository; - private PluginFactory pluginFactory; - private ExtensionFactory extensionFactory; - private PluginStatusProvider pluginStatusProvider; - private DependencyResolver dependencyResolver; - private PluginLoader pluginLoader; - private boolean exactVersionAllowed = false; - - private VersionManager versionManager; - - /** - * The plugins root is supplied by {@code System.getProperty("pf4j.pluginsDir", "plugins")}. - */ - public AbstractPluginManager() { - initialize(); - } - - /** - * Constructs {@code AbstractPluginManager} with the given plugins root. - * - * @param pluginsRoot the root to search for plugins - */ - public AbstractPluginManager(Path pluginsRoot) { - this.pluginsRoot = pluginsRoot; - - initialize(); - } - - @Override - public void setSystemVersion(String version) { - systemVersion = version; - } - - @Override - public String getSystemVersion() { - return systemVersion; - } - - /** - * Returns a copy of plugins. - * - * @return - */ - @Override - public List getPlugins() { - return new ArrayList<>(plugins.values()); - } - - /** - * Returns a copy of plugins with that state. - * - * @param pluginState - * @return - */ - @Override - public List getPlugins(PluginState pluginState) { - List plugins = new ArrayList<>(); - for (PluginWrapper plugin : getPlugins()) { - if (pluginState.equals(plugin.getPluginState())) { - plugins.add(plugin); - } - } - - return plugins; - } - - @Override - public List getResolvedPlugins() { - return resolvedPlugins; - } - - @Override - public List getUnresolvedPlugins() { - return unresolvedPlugins; - } - - @Override - public List getStartedPlugins() { - return startedPlugins; - } - - @Override - public PluginWrapper getPlugin(String pluginId) { - return plugins.get(pluginId); - } - - @Override - public String loadPlugin(Path pluginPath) { - if ((pluginPath == null) || Files.notExists(pluginPath)) { - throw new IllegalArgumentException(String.format("Specified plugin %s does not exist!", pluginPath)); - } - - log.debug("Loading plugin from '{}'", pluginPath); - - try { - PluginWrapper pluginWrapper = loadPluginFromPath(pluginPath); - // TODO uninstalled plugin dependencies? - getUnresolvedPlugins().remove(pluginWrapper); - getResolvedPlugins().add(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, null)); - - return pluginWrapper.getDescriptor().getPluginId(); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - - return null; - } - - /** - * Load plugins. - */ - @Override - public void loadPlugins() { - log.debug("Lookup plugins in '{}'", pluginsRoot); - // check for plugins root - if (Files.notExists(pluginsRoot) || !Files.isDirectory(pluginsRoot)) { - log.error("No '{}' root", pluginsRoot); - return; - } - - // get all plugin paths from repository - List pluginPaths = pluginRepository.getPluginPaths(); - - // check for no plugins - if (pluginPaths.isEmpty()) { - log.info("No plugins"); - return; - } - - log.debug("Found {} possible plugins: {}", pluginPaths.size(), pluginPaths); - - // load plugins from plugin paths - // TODO - for (Path pluginPath : pluginPaths) { - try { - loadPluginFromPath(pluginPath); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - - // resolve 'unresolvedPlugins' - try { - resolvePlugins(); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - - /** - * Unload the specified plugin and it's dependents. - */ - @Override - public boolean unloadPlugin(String pluginId) { - return unloadPlugin(pluginId, true); - } - - private boolean unloadPlugin(String pluginId, boolean unloadDependents) { - try { - if (unloadDependents) { - List dependents = dependencyResolver.getDependents(pluginId); - while (!dependents.isEmpty()) { - String dependent = dependents.remove(0); - unloadPlugin(dependent, false); - dependents.addAll(0, dependencyResolver.getDependents(dependent)); - } - } - - PluginState pluginState = stopPlugin(pluginId, false); - if (PluginState.STARTED == pluginState) { - return false; - } - - PluginWrapper pluginWrapper = getPlugin(pluginId); - log.info("Unload plugin '{}'", getPluginLabel(pluginWrapper.getDescriptor())); - - // remove the plugin - plugins.remove(pluginId); - getResolvedPlugins().remove(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - - // remove the classloader - Map pluginClassLoaders = getPluginClassLoaders(); - if (pluginClassLoaders.containsKey(pluginId)) { - ClassLoader classLoader = pluginClassLoaders.remove(pluginId); - if (classLoader instanceof Closeable) { - try { - ((Closeable) classLoader).close(); - } catch (IOException e) { - log.error("Cannot close classloader", e); - } - } - } - - return true; - } catch (IllegalArgumentException e) { - // ignore not found exceptions because this method is recursive - } - - return false; - } - - @Override - public boolean deletePlugin(String pluginId) { - checkPluginId(pluginId); - - PluginWrapper pluginWrapper = getPlugin(pluginId); - PluginState pluginState = stopPlugin(pluginId); - if (PluginState.STARTED == pluginState) { - log.error("Failed to stop plugin '{}' on delete", pluginId); - return false; - } - - if (!unloadPlugin(pluginId)) { - log.error("Failed to unload plugin '{}' on delete", pluginId); - return false; - } - - Path pluginPath = pluginWrapper.getPluginPath(); - - return pluginRepository.deletePluginPath(pluginPath); - } - - /** - * Start all active plugins. - */ - @Override - public void startPlugins() { - for (PluginWrapper pluginWrapper : resolvedPlugins) { - PluginState pluginState = pluginWrapper.getPluginState(); - if ((PluginState.DISABLED != pluginState) && (PluginState.STARTED != pluginState)) { - try { - log.info("Start plugin '{}'", getPluginLabel(pluginWrapper.getDescriptor())); - pluginWrapper.getPlugin().start(); - pluginWrapper.setPluginState(PluginState.STARTED); - startedPlugins.add(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - } - } - - /** - * Start the specified plugin and it's dependencies. - */ - @Override - public PluginState startPlugin(String pluginId) { - checkPluginId(pluginId); - - PluginWrapper pluginWrapper = getPlugin(pluginId); - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.STARTED == pluginState) { - log.debug("Already started plugin '{}'", getPluginLabel(pluginDescriptor)); - return PluginState.STARTED; - } - - if (PluginState.DISABLED == pluginState) { - // automatically enable plugin on manual plugin start - if (!enablePlugin(pluginId)) { - return pluginState; - } - } - - for (PluginDependency dependency : pluginDescriptor.getDependencies()) { - startPlugin(dependency.getPluginId()); - } - - try { - log.info("Start plugin '{}'", getPluginLabel(pluginDescriptor)); - pluginWrapper.getPlugin().start(); - pluginWrapper.setPluginState(PluginState.STARTED); - startedPlugins.add(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - - return pluginWrapper.getPluginState(); - } - - /** - * Stop all active plugins. - */ - @Override - public void stopPlugins() { - // stop started plugins in reverse order - Collections.reverse(startedPlugins); - Iterator itr = startedPlugins.iterator(); - while (itr.hasNext()) { - PluginWrapper pluginWrapper = itr.next(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.STARTED == pluginState) { - try { - log.info("Stop plugin '{}'", getPluginLabel(pluginWrapper.getDescriptor())); - pluginWrapper.getPlugin().stop(); - pluginWrapper.setPluginState(PluginState.STOPPED); - itr.remove(); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - } - } - } - - /** - * Stop the specified plugin and it's dependents. - */ - @Override - public PluginState stopPlugin(String pluginId) { - return stopPlugin(pluginId, true); - } - - private PluginState stopPlugin(String pluginId, boolean stopDependents) { - checkPluginId(pluginId); - - PluginWrapper pluginWrapper = getPlugin(pluginId); - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.STOPPED == pluginState) { - log.debug("Already stopped plugin '{}'", getPluginLabel(pluginDescriptor)); - return PluginState.STOPPED; - } - - // test for disabled plugin - if (PluginState.DISABLED == pluginState) { - // do nothing - return pluginState; - } - - if (stopDependents) { - List dependents = dependencyResolver.getDependents(pluginId); - while (!dependents.isEmpty()) { - String dependent = dependents.remove(0); - stopPlugin(dependent, false); - dependents.addAll(0, dependencyResolver.getDependents(dependent)); - } - } - - try { - log.info("Stop plugin '{}'", getPluginLabel(pluginDescriptor)); - pluginWrapper.getPlugin().stop(); - pluginWrapper.setPluginState(PluginState.STOPPED); - startedPlugins.remove(pluginWrapper); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - } catch (PluginException e) { - log.error(e.getMessage(), e); - } - - return pluginWrapper.getPluginState(); - } - - private void checkPluginId(String pluginId) { - if (!plugins.containsKey(pluginId)) { - throw new IllegalArgumentException(String.format("Unknown pluginId %s", pluginId)); - } - } - - @Override - public boolean disablePlugin(String pluginId) { - checkPluginId(pluginId); - - PluginWrapper pluginWrapper = getPlugin(pluginId); - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.DISABLED == pluginState) { - log.debug("Already disabled plugin '{}'", getPluginLabel(pluginDescriptor)); - return true; - } - - if (PluginState.STOPPED == stopPlugin(pluginId)) { - pluginWrapper.setPluginState(PluginState.DISABLED); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, PluginState.STOPPED)); - - if (!pluginStatusProvider.disablePlugin(pluginId)) { - return false; - } - - log.info("Disabled plugin '{}'", getPluginLabel(pluginDescriptor)); - - return true; - } - - return false; - } - - @Override - public boolean enablePlugin(String pluginId) { - checkPluginId(pluginId); - - PluginWrapper pluginWrapper = getPlugin(pluginId); - if (!isPluginValid(pluginWrapper)) { - log.warn("Plugin '{}' can not be enabled", getPluginLabel(pluginWrapper.getDescriptor())); - return false; - } - - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - PluginState pluginState = pluginWrapper.getPluginState(); - if (PluginState.DISABLED != pluginState) { - log.debug("Plugin '{}' is not disabled", getPluginLabel(pluginDescriptor)); - return true; - } - - if (!pluginStatusProvider.enablePlugin(pluginId)) { - return false; - } - - pluginWrapper.setPluginState(PluginState.CREATED); - - firePluginStateEvent(new PluginStateEvent(this, pluginWrapper, pluginState)); - - log.info("Enabled plugin '{}'", getPluginLabel(pluginDescriptor)); - - return true; - } - - /** - * Get the {@link ClassLoader} for plugin. - */ - @Override - public ClassLoader getPluginClassLoader(String pluginId) { - return pluginClassLoaders.get(pluginId); - } - - @Override - public List getExtensions(Class type) { - List> extensionsWrapper = extensionFinder.find(type); - List extensions = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getExtension()); - } - - return extensions; - } - - @Override - public List getExtensions(Class type, String pluginId) { - List> extensionsWrapper = extensionFinder.find(type, pluginId); - List extensions = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getExtension()); - } - - return extensions; - } - - @Override - @SuppressWarnings("unchecked") - public List getExtensions(String pluginId) { - List extensionsWrapper = extensionFinder.find(pluginId); - List extensions = new ArrayList<>(extensionsWrapper.size()); - for (ExtensionWrapper extensionWrapper : extensionsWrapper) { - extensions.add(extensionWrapper.getExtension()); - } - - return extensions; - } - - @Override - public Set getExtensionClassNames(String pluginId) { - return extensionFinder.findClassNames(pluginId); - } - - @Override - public ExtensionFactory getExtensionFactory() { - return extensionFactory; - } - - // TODO remove - public PluginLoader getPluginLoader() { - return pluginLoader; - } - - public Path getPluginsRoot() { - return pluginsRoot; - } - - @Override - public RuntimeMode getRuntimeMode() { - if (runtimeMode == null) { - // retrieves the runtime mode from system - String modeAsString = System.getProperty("pf4j.mode", RuntimeMode.DEPLOYMENT.toString()); - runtimeMode = RuntimeMode.byName(modeAsString); - } - - return runtimeMode; - } - - @Override - public PluginWrapper whichPlugin(Class clazz) { - ClassLoader classLoader = clazz.getClassLoader(); - for (PluginWrapper plugin : resolvedPlugins) { - if (plugin.getPluginClassLoader() == classLoader) { - return plugin; - } - } - - return null; - } - - @Override - public synchronized void addPluginStateListener(PluginStateListener listener) { - pluginStateListeners.add(listener); - } - - @Override - public synchronized void removePluginStateListener(PluginStateListener listener) { - pluginStateListeners.remove(listener); - } - - public String getVersion() { - String version = null; - - Package pf4jPackage = PluginManager.class.getPackage(); - if (pf4jPackage != null) { - version = pf4jPackage.getImplementationVersion(); - if (version == null) { - version = pf4jPackage.getSpecificationVersion(); - } - } - - return (version != null) ? version : "0.0.0"; - } - - protected abstract PluginRepository createPluginRepository(); - - protected abstract PluginFactory createPluginFactory(); - - protected abstract ExtensionFactory createExtensionFactory(); - - protected abstract PluginDescriptorFinder createPluginDescriptorFinder(); - - protected abstract ExtensionFinder createExtensionFinder(); - - protected abstract PluginStatusProvider createPluginStatusProvider(); - - protected abstract PluginLoader createPluginLoader(); - - protected abstract VersionManager createVersionManager(); - - protected PluginDescriptorFinder getPluginDescriptorFinder() { - return pluginDescriptorFinder; - } - - protected PluginFactory getPluginFactory() { - return pluginFactory; - } - - protected Map getPluginClassLoaders() { - return pluginClassLoaders; - } - - protected void initialize() { - plugins = new HashMap<>(); - pluginClassLoaders = new HashMap<>(); - unresolvedPlugins = new ArrayList<>(); - resolvedPlugins = new ArrayList<>(); - startedPlugins = new ArrayList<>(); - - pluginStateListeners = new ArrayList<>(); - - if (pluginsRoot == null) { - pluginsRoot = createPluginsRoot(); - } - - System.setProperty("pf4j.pluginsDir", pluginsRoot.toString()); - - pluginRepository = createPluginRepository(); - pluginFactory = createPluginFactory(); - extensionFactory = createExtensionFactory(); - pluginDescriptorFinder = createPluginDescriptorFinder(); - extensionFinder = createExtensionFinder(); - pluginStatusProvider = createPluginStatusProvider(); - pluginLoader = createPluginLoader(); - - versionManager = createVersionManager(); - dependencyResolver = new DependencyResolver(versionManager); - } - - /** - * Add the possibility to override the plugins root. - * If a {@code pf4j.pluginsDir} system property is defined than this method returns that root. - * If {@link #getRuntimeMode()} returns {@link RuntimeMode#DEVELOPMENT} than {@code ../plugins} - * is returned else this method returns {@code plugins}. - * - * @return the plugins root - */ - protected Path createPluginsRoot() { - String pluginsDir = System.getProperty("pf4j.pluginsDir"); - if (pluginsDir == null) { - if (isDevelopment()) { - pluginsDir = "../plugins"; - } else { - pluginsDir = "plugins"; - } - } - - return Paths.get(pluginsDir); - } - - /** - * Check if this plugin is valid (satisfies "requires" param) for a given system version. - * - * @param pluginWrapper the plugin to check - * @return true if plugin satisfies the "requires" or if requires was left blank - */ - protected boolean isPluginValid(PluginWrapper pluginWrapper) { - String requires = pluginWrapper.getDescriptor().getRequires().trim(); - if (!isExactVersionAllowed() && requires.matches("^\\d+\\.\\d+\\.\\d+$")) { - // If exact versions are not allowed in requires, rewrite to >= expression - requires = ">=" + requires; - } - if (systemVersion.equals("0.0.0") || versionManager.satisfies(requires, systemVersion)) { - return true; - } - - PluginDescriptor pluginDescriptor = pluginWrapper.getDescriptor(); - log.warn("Plugin '{}' requires a minimum system version of {}, and you have {}", - getPluginLabel(pluginDescriptor), - pluginWrapper.getDescriptor().getRequires(), - getSystemVersion()); - - return false; - } - - protected boolean isPluginDisabled(String pluginId) { - return pluginStatusProvider.isPluginDisabled(pluginId); - } - - protected void resolvePlugins() throws PluginException { - // retrieves the plugins descriptors from "unresolvedPlugins" list - List descriptors = new ArrayList<>(); - for (PluginWrapper plugin : unresolvedPlugins) { - descriptors.add(plugin.getDescriptor()); - } - - DependencyResolver.Result result = dependencyResolver.resolve(descriptors); - - if (result.hasCyclicDependency()) { - throw new DependencyResolver.CyclicDependencyException(); - } - - List notFoundDependencies = result.getNotFoundDependencies(); - if (!notFoundDependencies.isEmpty()) { - throw new DependencyResolver.DependenciesNotFoundException(notFoundDependencies); - } - - List wrongVersionDependencies = result.getWrongVersionDependencies(); - if (!wrongVersionDependencies.isEmpty()) { - throw new DependencyResolver.DependenciesWrongVersionException(wrongVersionDependencies); - } - - List sortedPlugins = result.getSortedPlugins(); - - // move plugins from "unresolved" to "resolved" - for (String pluginId : sortedPlugins) { - PluginWrapper pluginWrapper = plugins.get(pluginId); - unresolvedPlugins.remove(pluginWrapper); - resolvedPlugins.add(pluginWrapper); - log.info("Plugin '{}' resolved", getPluginLabel(pluginWrapper.getDescriptor())); - } - } - - protected synchronized void firePluginStateEvent(PluginStateEvent event) { - for (PluginStateListener listener : pluginStateListeners) { - log.debug("Fire '{}' to '{}'", event, listener); - listener.pluginStateChanged(event); - } - } - - protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { - // test for plugin duplication - String pluginId = idForPath(pluginPath); - if (pluginId != null) { - log.warn("Plugin '{}' already loaded with id '{}'", pluginPath, pluginId); - return null; - } - - // retrieves the plugin descriptor - log.debug("Finding plugin descriptor for plugin '{}'", pluginPath); - PluginDescriptor pluginDescriptor = getPluginDescriptorFinder().find(pluginPath); - validatePluginDescriptor(pluginDescriptor); - log.debug("Found descriptor {}", pluginDescriptor); - String pluginClassName = pluginDescriptor.getPluginClass(); - log.debug("Class '{}' for plugin '{}'", pluginClassName, pluginPath); - - // load plugin - log.debug("Loading plugin '{}'", pluginPath); - ClassLoader pluginClassLoader = getPluginLoader().loadPlugin(pluginPath, pluginDescriptor); - log.debug("Loaded plugin '{}' with class loader '{}'", pluginPath, pluginClassLoader); - - // create the plugin wrapper - log.debug("Creating wrapper for plugin '{}'", pluginPath); - PluginWrapper pluginWrapper = new PluginWrapper(this, pluginDescriptor, pluginPath, pluginClassLoader); - pluginWrapper.setPluginFactory(getPluginFactory()); - pluginWrapper.setRuntimeMode(getRuntimeMode()); - - // test for disabled plugin - if (isPluginDisabled(pluginDescriptor.getPluginId())) { - log.info("Plugin '{}' is disabled", pluginPath); - pluginWrapper.setPluginState(PluginState.DISABLED); - } - - // validate the plugin - if (!isPluginValid(pluginWrapper)) { - log.info("Plugin '{}' is disabled", pluginPath); - pluginWrapper.setPluginState(PluginState.DISABLED); - } - - log.debug("Created wrapper '{}' for plugin '{}'", pluginWrapper, pluginPath); - - pluginId = pluginDescriptor.getPluginId(); - - // add plugin to the list with plugins - plugins.put(pluginId, pluginWrapper); - getUnresolvedPlugins().add(pluginWrapper); - - // add plugin class loader to the list with class loaders - getPluginClassLoaders().put(pluginId, pluginClassLoader); - - return pluginWrapper; - } - - /** - * Tests for already loaded plugins on given path. - * - * @param pluginPath the path to investigate - * @return id of plugin or null if not loaded - */ - protected String idForPath(Path pluginPath) { - for (PluginWrapper plugin : plugins.values()) { - if (plugin.getPluginPath().equals(pluginPath)) { - return plugin.getPluginId(); - } - } - - return null; - } - - /** - * Override this to change the validation criteria. - * - * @param descriptor the plugin descriptor to validate - * @throws PluginException if validation fails - */ - protected void validatePluginDescriptor(PluginDescriptor descriptor) throws PluginException { - if (StringUtils.isEmpty(descriptor.getPluginId())) { - throw new PluginException("Field 'id' cannot be empty"); - } - - if (StringUtils.isEmpty(descriptor.getPluginClass())) { - throw new PluginException("Field 'class' cannot be empty"); - } - - if (descriptor.getVersion() == null) { - throw new PluginException("Field 'version' cannot be empty"); - } - } - - // TODO add this method in PluginManager as default method for Java 8. - protected boolean isDevelopment() { - return RuntimeMode.DEVELOPMENT.equals(getRuntimeMode()); - } - - /** - * @return true if exact versions in requires is allowed - */ - public boolean isExactVersionAllowed() { - return exactVersionAllowed; - } - - /** - * Set to true to allow requires expression to be exactly x.y.z. - * The default is false, meaning that using an exact version x.y.z will - * implicitly mean the same as >=x.y.z - * - * @param exactVersionAllowed set to true or false - */ - public void setExactVersionAllowed(boolean exactVersionAllowed) { - this.exactVersionAllowed = exactVersionAllowed; - } - - @Override - public VersionManager getVersionManager() { - return versionManager; - } - - /** - * The plugin label is used in logging and it's a string in format {@code pluginId@pluginVersion}. - * @param pluginDescriptor - * @return - */ - protected String getPluginLabel(PluginDescriptor pluginDescriptor) { - return pluginDescriptor.getPluginId() + "@" + pluginDescriptor.getVersion(); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/BasePluginRepository.java b/pf4j/src/main/java/ro/fortsoft/pf4j/BasePluginRepository.java deleted file mode 100644 index e9ee47f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/BasePluginRepository.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import ro.fortsoft.pf4j.util.FileUtils; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.nio.file.NoSuchFileException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * @author Decebal Suiu - * @author Mário Franco - */ -public class BasePluginRepository implements PluginRepository { - - protected final Path pluginsRoot; - protected FileFilter filter; - - public BasePluginRepository(Path pluginsRoot) { - this.pluginsRoot = pluginsRoot; - } - - public BasePluginRepository(Path pluginsRoot, FileFilter filter) { - this.pluginsRoot = pluginsRoot; - this.filter = filter; - } - - public void setFilter(FileFilter filter) { - this.filter = filter; - } - - @Override - public List getPluginPaths() { - File[] files = pluginsRoot.toFile().listFiles(filter); - - if ((files == null) || files.length == 0) { - return Collections.emptyList(); - } - - List paths = new ArrayList<>(files.length); - for (File file : files) { - paths.add(file.toPath()); - } - - return paths; - } - - @Override - public boolean deletePluginPath(Path pluginPath) { - try { - FileUtils.delete(pluginPath); - return true; - } catch (NoSuchFileException nsf) { - return false; // Return false on not found to be compatible with previous API - } catch (IOException e) { - throw new RuntimeException(e); - } - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/CompoundPluginRepository.java b/pf4j/src/main/java/ro/fortsoft/pf4j/CompoundPluginRepository.java deleted file mode 100644 index b0341ea..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/CompoundPluginRepository.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Decebal Suiu - * @author Mário Franco - */ -public class CompoundPluginRepository implements PluginRepository { - - private final PluginRepository[] repositories; - - public CompoundPluginRepository(PluginRepository... repositories) { - this.repositories = repositories; - } - - @Override - public List getPluginPaths() { - List paths = new ArrayList<>(); - for (PluginRepository repository : repositories) { - paths.addAll(repository.getPluginPaths()); - } - - return paths; - } - - @Override - public boolean deletePluginPath(Path pluginPath) { - for (PluginRepository repository : repositories) { - if (repository.deletePluginPath(pluginPath)) { - return true; - } - } - - return false; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFactory.java deleted file mode 100644 index 63d7cee..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFactory.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * The default implementation for ExtensionFactory. - * It uses Class.newInstance() method. - * - * @author Decebal Suiu - */ -public class DefaultExtensionFactory implements ExtensionFactory { - - private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFactory.class); - - /** - * Creates an extension instance. If an error occurs than that error is logged and the method returns null. - * @param extensionClass - * @return - */ - @Override - public Object create(Class extensionClass) { - log.debug("Create instance for extension '{}'", extensionClass.getName()); - try { - return extensionClass.newInstance(); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - - return null; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java deleted file mode 100644 index 4f5f3bb..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultExtensionFinder.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -/** - * The default implementation for {@link ExtensionFinder}. - * It's a compound {@code ExtensionFinder}. - * - * @author Decebal Suiu - */ -public class DefaultExtensionFinder implements ExtensionFinder, PluginStateListener { - - protected PluginManager pluginManager; - protected List finders = new ArrayList<>(); - - public DefaultExtensionFinder(PluginManager pluginManager) { - this.pluginManager = pluginManager; - - finders = new ArrayList<>(); - - addExtensionFinder(new LegacyExtensionFinder(pluginManager)); -// addExtensionFinder(new ServiceProviderExtensionFinder(pluginManager)); - } - - @Override - public List> find(Class type) { - List> extensions = new ArrayList<>(); - for (ExtensionFinder finder : finders) { - extensions.addAll(finder.find(type)); - } - - return extensions; - } - - @Override - public List> find(Class type, String pluginId) { - List> extensions = new ArrayList<>(); - for (ExtensionFinder finder : finders) { - extensions.addAll(finder.find(type, pluginId)); - } - - return extensions; - } - - @Override - public List find(String pluginId) { - List extensions = new ArrayList<>(); - for (ExtensionFinder finder : finders) { - extensions.addAll(finder.find(pluginId)); - } - - return extensions; - } - - - @Override - public Set findClassNames(String pluginId) { - Set classNames = new HashSet<>(); - for (ExtensionFinder finder : finders) { - classNames.addAll(finder.findClassNames(pluginId)); - } - - return classNames; - } - - @Override - public void pluginStateChanged(PluginStateEvent event) { - for (ExtensionFinder finder : finders) { - if (finder instanceof PluginStateListener) { - ((PluginStateListener) finder).pluginStateChanged(event); - } - } - } - - public DefaultExtensionFinder addServiceProviderExtensionFinder() { - return addExtensionFinder(new ServiceProviderExtensionFinder(pluginManager)); - } - - public DefaultExtensionFinder addExtensionFinder(ExtensionFinder finder) { - finders.add(finder); - - return this; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginClasspath.java deleted file mode 100644 index 71631c1..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginClasspath.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2016 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * The default values are {@code classes} and {@code lib}. - * - * @author Decebal Suiu - */ -public class DefaultPluginClasspath extends PluginClasspath { - - public DefaultPluginClasspath() { - super(); - - addClassesDirectories("classes"); - addLibDirectories("lib"); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java deleted file mode 100644 index 2696fcd..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginDescriptorFinder.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.jar.Manifest; - -/** - * The default implementation for {@link PluginDescriptorFinder}. - * Now, this class it's a "link" to {@link ManifestPluginDescriptorFinder}. - * - * @author Decebal Suiu - */ -public class DefaultPluginDescriptorFinder extends ManifestPluginDescriptorFinder { - - private static final Logger log = LoggerFactory.getLogger(ManifestPluginDescriptorFinder.class); - - private PluginClasspath pluginClasspath; - - public DefaultPluginDescriptorFinder(PluginClasspath pluginClasspath) { - this.pluginClasspath = pluginClasspath; - } - - @Override - public Manifest readManifest(Path pluginPath) throws PluginException { - // TODO it's ok with first classes root? Another idea is to specify in PluginClasspath the folder. - if (pluginClasspath.getClassesDirectories().size() == 0) { - throw new PluginException("Failed to read manifest, no classes folder in classpath"); - } - String classes = pluginClasspath.getClassesDirectories().get(0); - Path manifestPath = pluginPath.resolve(Paths.get(classes,"/META-INF/MANIFEST.MF")); - log.debug("Lookup plugin descriptor in '{}'", manifestPath); - if (Files.notExists(manifestPath)) { - throw new PluginException("Cannot find '{}' path", manifestPath); - } - - try (InputStream input = Files.newInputStream(manifestPath)) { - return new Manifest(input); - } catch (IOException e) { - throw new PluginException(e.getMessage(), e); - } - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java deleted file mode 100644 index 9bc3173..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginFactory.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.lang.reflect.Constructor; -import java.lang.reflect.Modifier; - -/** - * The default implementation for {@link PluginFactory}. - * It uses {@link Class#newInstance()} method. - * - * @author Decebal Suiu - */ -public class DefaultPluginFactory implements PluginFactory { - - private static final Logger log = LoggerFactory.getLogger(DefaultExtensionFactory.class); - - /** - * Creates a plugin instance. If an error occurs than that error is logged and the method returns null. - * @param pluginWrapper - * @return - */ - @Override - public Plugin create(final PluginWrapper pluginWrapper) { - String pluginClassName = pluginWrapper.getDescriptor().getPluginClass(); - log.debug("Create instance for plugin '{}'", pluginClassName); - - Class pluginClass; - try { - pluginClass = pluginWrapper.getPluginClassLoader().loadClass(pluginClassName); - } catch (ClassNotFoundException e) { - log.error(e.getMessage(), e); - return null; - } - - // once we have the class, we can do some checks on it to ensure - // that it is a valid implementation of a plugin. - int modifiers = pluginClass.getModifiers(); - if (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers) - || (!Plugin.class.isAssignableFrom(pluginClass))) { - log.error("The plugin class '{}' is not valid", pluginClassName); - return null; - } - - // create the plugin instance - try { - Constructor constructor = pluginClass.getConstructor(PluginWrapper.class); - return (Plugin) constructor.newInstance(pluginWrapper); - } catch (Exception e) { - log.error(e.getMessage(), e); - } - - return null; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginLoader.java deleted file mode 100644 index e4c6b9f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginLoader.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2016 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import ro.fortsoft.pf4j.util.FileUtils; - -import java.io.File; -import java.nio.file.Path; -import java.util.List; - -/** - * Load all information needed by a plugin. - * This means add to classpath all jar files from {@code lib} directory - * and all class files from {@code classes}. - * - * @author Decebal Suiu - */ -public class DefaultPluginLoader implements PluginLoader { - - protected PluginManager pluginManager; - protected PluginClasspath pluginClasspath; - - public DefaultPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { - this.pluginManager = pluginManager; - this.pluginClasspath = pluginClasspath; - } - - @Override - public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { - PluginClassLoader pluginClassLoader = createPluginClassLoader(pluginPath, pluginDescriptor); - - loadClasses(pluginPath, pluginClassLoader); - loadJars(pluginPath, pluginClassLoader); - - return pluginClassLoader; - } - - protected PluginClassLoader createPluginClassLoader(Path pluginPath, PluginDescriptor pluginDescriptor) { - return new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); - } - - /** - * Add all {@code *.class} files from {@code classes} directories to plugin class loader. - */ - protected void loadClasses(Path pluginPath, PluginClassLoader pluginClassLoader) { - for (String directory : pluginClasspath.getClassesDirectories()) { - File file = pluginPath.resolve(directory).toFile(); - if (file.exists() && file.isDirectory()) { - pluginClassLoader.addFile(file); - } - } - } - - /** - * Add all {@code *.jar} files from {@code lib} directories to plugin class loader. - */ - protected void loadJars(Path pluginPath, PluginClassLoader pluginClassLoader) { - for (String libDirectory : pluginClasspath.getLibDirectories()) { - Path file = pluginPath.resolve(libDirectory); - List jars = FileUtils.getJars(file); - for (File jar : jars) { - pluginClassLoader.addFile(jar); - } - } - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java deleted file mode 100644 index a7aaac5..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginManager.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.FileUtils; - -import java.io.File; -import java.nio.file.Path; - -/** - * Default implementation of the {@link PluginManager} interface. - * - * @author Decebal Suiu - */ -public class DefaultPluginManager extends AbstractPluginManager { - - private static final Logger log = LoggerFactory.getLogger(DefaultPluginManager.class); - - protected PluginClasspath pluginClasspath; - - public DefaultPluginManager() { - super(); - } - - @Deprecated - public DefaultPluginManager(File pluginsDir) { - this(pluginsDir.toPath()); - } - - public DefaultPluginManager(Path pluginsRoot) { - super(pluginsRoot); - } - - /** - * By default if {@link DefaultPluginManager#isDevelopment()} returns {@code true} - * than a {@link PropertiesPluginDescriptorFinder} is returned - * else this method returns {@link DefaultPluginDescriptorFinder}. - */ - @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new DefaultPluginDescriptorFinder(pluginClasspath); - } - - @Override - protected ExtensionFinder createExtensionFinder() { - DefaultExtensionFinder extensionFinder = new DefaultExtensionFinder(this); - addPluginStateListener(extensionFinder); - - return extensionFinder; - } - - @Override - protected PluginFactory createPluginFactory() { - return new DefaultPluginFactory(); - } - - @Override - protected ExtensionFactory createExtensionFactory() { - return new DefaultExtensionFactory(); - } - - @Override - protected PluginStatusProvider createPluginStatusProvider() { - return new DefaultPluginStatusProvider(getPluginsRoot()); - } - - @Override - protected PluginRepository createPluginRepository() { - return new DefaultPluginRepository(getPluginsRoot(), isDevelopment()); - } - - @Override - protected PluginLoader createPluginLoader() { - return new DefaultPluginLoader(this, pluginClasspath); - } - - @Override - protected VersionManager createVersionManager() { - return new DefaultVersionManager(); - } - - /** - * By default if {@link DefaultPluginManager#isDevelopment()} returns true - * than a {@link DevelopmentPluginClasspath} is returned - * else this method returns {@link DefaultPluginClasspath}. - */ - protected PluginClasspath createPluginClasspath() { - return isDevelopment() ? new DevelopmentPluginClasspath() : new DefaultPluginClasspath(); - } - - @Override - protected void initialize() { - pluginClasspath = createPluginClasspath(); - - super.initialize(); - - log.info("PF4J version {} in '{}' mode", getVersion(), getRuntimeMode()); - } - - /** - * Load a plugin from disk. If the path is a zip file, first unpack - * @param pluginPath plugin location on disk - * @return PluginWrapper for the loaded plugin or null if not loaded - * @throws PluginException if problems during load - */ - @Override - protected PluginWrapper loadPluginFromPath(Path pluginPath) throws PluginException { - // First unzip any ZIP files - try { - pluginPath = FileUtils.expandIfZip(pluginPath); - } catch (Exception e) { - log.warn("Failed to unzip " + pluginPath, e); - return null; - } - - return super.loadPluginFromPath(pluginPath); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java deleted file mode 100644 index 197ae22..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginRepository.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.AndFileFilter; -import ro.fortsoft.pf4j.util.DirectoryFileFilter; -import ro.fortsoft.pf4j.util.FileUtils; -import ro.fortsoft.pf4j.util.HiddenFilter; -import ro.fortsoft.pf4j.util.NameFileFilter; -import ro.fortsoft.pf4j.util.NotFileFilter; -import ro.fortsoft.pf4j.util.OrFileFilter; -import ro.fortsoft.pf4j.util.ZipFileFilter; - -import java.io.File; -import java.io.FileFilter; -import java.io.IOException; -import java.nio.file.Path; -import java.util.List; - -/** - * @author Decebal Suiu - */ -public class DefaultPluginRepository extends BasePluginRepository { - - private static final Logger log = LoggerFactory.getLogger(DefaultPluginRepository.class); - - public DefaultPluginRepository(Path pluginsRoot, boolean development) { - super(pluginsRoot); - - AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); - pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter(development))); - setFilter(pluginsFilter); - } - - @Override - public List getPluginPaths() { - // expand plugins zip files - File[] pluginZips = pluginsRoot.toFile().listFiles(new ZipFileFilter()); - if ((pluginZips != null) && pluginZips.length > 0) { - for (File pluginZip : pluginZips) { - try { - FileUtils.expandIfZip(pluginZip.toPath()); - } catch (IOException e) { - log.error("Cannot expand plugin zip '{}'", pluginZip); - log.error(e.getMessage(), e); - } - } - } - - return super.getPluginPaths(); - } - - @Override - public boolean deletePluginPath(Path pluginPath) { - FileUtils.optimisticDelete(FileUtils.findWithEnding(pluginPath, ".zip", ".ZIP", ".Zip")); - return super.deletePluginPath(pluginPath); - } - - protected FileFilter createHiddenPluginFilter(boolean development) { - OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); - - if (development) { - hiddenPluginFilter.addFileFilter(new NameFileFilter("target")); - } - - return hiddenPluginFilter; - } -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginStatusProvider.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginStatusProvider.java deleted file mode 100644 index 2c669b1..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultPluginStatusProvider.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.FileUtils; - -import java.io.IOException; -import java.nio.file.Path; -import java.util.List; - -/** - * The default implementation for {@link PluginStatusProvider}. - * - * @author Decebal Suiu - * @author Mário Franco - */ -public class DefaultPluginStatusProvider implements PluginStatusProvider { - - private static final Logger log = LoggerFactory.getLogger(DefaultPluginStatusProvider.class); - - private final Path pluginsRoot; - - private List enabledPlugins; - private List disabledPlugins; - - public DefaultPluginStatusProvider(Path pluginsRoot) { - this.pluginsRoot = pluginsRoot; - - initialize(); - } - - private void initialize() { - try { - // create a list with plugin identifiers that should be only accepted by this manager (whitelist from plugins/enabled.txt file) - enabledPlugins = FileUtils.readLines(pluginsRoot.resolve("enabled.txt"), true); - log.info("Enabled plugins: {}", enabledPlugins); - - // create a list with plugin identifiers that should not be accepted by this manager (blacklist from plugins/disabled.txt file) - disabledPlugins = FileUtils.readLines(pluginsRoot.resolve("disabled.txt"), true); - log.info("Disabled plugins: {}", disabledPlugins); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - - @Override - public boolean isPluginDisabled(String pluginId) { - if (disabledPlugins.contains(pluginId)) { - return true; - } - - return !enabledPlugins.isEmpty() && !enabledPlugins.contains(pluginId); - } - - @Override - public boolean disablePlugin(String pluginId) { - if (disabledPlugins.add(pluginId)) { - try { - FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); - } catch (IOException e) { - log.error("Failed to disable plugin {}", pluginId, e); - return false; - } - } - - return true; - } - - @Override - public boolean enablePlugin(String pluginId) { - try { - if (disabledPlugins.remove(pluginId)) { - FileUtils.writeLines(disabledPlugins, pluginsRoot.resolve("disabled.txt").toFile()); - } - } catch (IOException e) { - log.error("Failed to enable plugin {}", pluginId, e); - return false; - } - - return true; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultVersionManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultVersionManager.java deleted file mode 100644 index 0d11e90..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DefaultVersionManager.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import com.github.zafarkhaja.semver.Version; - -/** - * Default implementation for {@link VersionManager}. - * This implementation uses jSemVer (a Java implementation of the SemVer Specification). - * - * @author Decebal Suiu - */ -public class DefaultVersionManager implements VersionManager { - - @Override - public boolean satisfies(String constraint, String version) { - return Version.valueOf(version).satisfies(constraint); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java deleted file mode 100644 index 0ea15a3..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DependencyResolver.java +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.DirectedGraph; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * This class builds a dependency graph for a list of plugins (descriptors). - * The entry point is the {@link #resolve(List)} method, method that returns a {@link Result} object. - * The {@code Result} class contains nice information about the result of resolve operation (if it's a cyclic dependency, - * they are not found dependencies, they are dependencies with wrong version). - * This class is very useful for if-else scenarios. - * - * Only some attributes (pluginId, dependencies and pluginVersion) from {@link PluginDescriptor} are used in - * the process of {@code resolve} operation. - * - * @author Decebal Suiu - */ -public class DependencyResolver { - - private static final Logger log = LoggerFactory.getLogger(DependencyResolver.class); - - private VersionManager versionManager; - - private DirectedGraph dependenciesGraph; // the value is 'pluginId' - private DirectedGraph dependentsGraph; // the value is 'pluginId' - private boolean resolved; - - public DependencyResolver(VersionManager versionManager) { - this.versionManager = versionManager; - } - - public Result resolve(List plugins) { - // create graphs - dependenciesGraph = new DirectedGraph<>(); - dependentsGraph = new DirectedGraph<>(); - - // populate graphs - Map pluginByIds = new HashMap<>(); - for (PluginDescriptor plugin : plugins) { - addPlugin(plugin); - pluginByIds.put(plugin.getPluginId(), plugin); - } - - log.debug("Graph: {}", dependenciesGraph); - - // get a sorted list of dependencies - List sortedPlugins = dependenciesGraph.reverseTopologicalSort(); - log.debug("Plugins order: {}", sortedPlugins); - - // create the result object - Result result = new Result(sortedPlugins); - - resolved = true; - - if (sortedPlugins != null) { // no cyclic dependency - // detect not found dependencies - for (String pluginId : sortedPlugins) { - if (!pluginByIds.containsKey(pluginId)) { - result.addNotFoundDependency(pluginId); - } - } - } - - // check dependencies versions - for (PluginDescriptor plugin : plugins) { - String pluginId = plugin.getPluginId(); - String existingVersion = plugin.getVersion(); - - List dependents = getDependents(pluginId); - while (!dependents.isEmpty()) { - String dependentId = dependents.remove(0); - PluginDescriptor dependent = pluginByIds.get(dependentId); - String requiredVersion = getDependencyVersionSupport(dependent, pluginId); - boolean ok = checkDependencyVersion(requiredVersion, existingVersion); - if (!ok) { - result.addWrongDependencyVersion(new WrongDependencyVersion(pluginId, dependentId, existingVersion, requiredVersion)); - } - } - } - - return result; - } - - /** - * Retrieves the plugins ids that the given plugin id directly depends on. - * - * @param pluginId - * @return - */ - public List getDependencies(String pluginId) { - checkResolved(); - return dependenciesGraph.getNeighbors(pluginId); - } - - /** - * Retrieves the plugins ids that the given content is a direct dependency of. - * - * @param pluginId - * @return - */ - public List getDependents(String pluginId) { - checkResolved(); - return dependentsGraph.getNeighbors(pluginId); - } - - /** - * Check if an existing version of dependency is compatible with the required version (from plugin descriptor). - * - * @param requiredVersion - * @param existingVersion - * @return - */ - protected boolean checkDependencyVersion(String requiredVersion, String existingVersion) { - return versionManager.satisfies(requiredVersion, existingVersion); - } - - private void addPlugin(PluginDescriptor descriptor) { - String pluginId = descriptor.getPluginId(); - List dependencies = descriptor.getDependencies(); - if (dependencies.isEmpty()) { - dependenciesGraph.addVertex(pluginId); - dependentsGraph.addVertex(pluginId); - } else { - for (PluginDependency dependency : dependencies) { - dependenciesGraph.addEdge(pluginId, dependency.getPluginId()); - dependentsGraph.addEdge(dependency.getPluginId(), pluginId); - } - } - } - - private void checkResolved() { - if (!resolved) { - throw new IllegalStateException("Call 'resolve' method first"); - } - } - - private String getDependencyVersionSupport(PluginDescriptor dependent, String dependencyId) { - List dependencies = dependent.getDependencies(); - for (PluginDependency dependency : dependencies) { - if (dependencyId.equals(dependency.getPluginId())) { - return dependency.getPluginVersionSupport(); - } - } - - throw new IllegalStateException("Cannot find a dependency with id '" + dependencyId + - "' for plugin '" + dependent.getPluginId() + "'"); - } - - public static class Result { - - private boolean cyclicDependency; - private List notFoundDependencies; // value is "pluginId" - private List sortedPlugins; // value is "pluginId" - private List wrongVersionDependencies; - - Result(List sortedPlugins) { - if (sortedPlugins == null) { - cyclicDependency = true; - this.sortedPlugins = Collections.emptyList(); - } else { - this.sortedPlugins = new ArrayList<>(sortedPlugins); - } - - notFoundDependencies = new ArrayList<>(); - wrongVersionDependencies = new ArrayList<>(); - } - - /** - * Returns true is a cyclic dependency was detected. - */ - public boolean hasCyclicDependency() { - return cyclicDependency; - } - - /** - * Returns a list with dependencies required that were not found. - */ - public List getNotFoundDependencies() { - return notFoundDependencies; - } - - /** - * Returns a list with dependencies with wrong version. - */ - public List getWrongVersionDependencies() { - return wrongVersionDependencies; - } - - /** - * Get the list of plugins in dependency sorted order. - */ - public List getSortedPlugins() { - return sortedPlugins; - } - - void addNotFoundDependency(String pluginId) { - notFoundDependencies.add(pluginId); - } - - void addWrongDependencyVersion(WrongDependencyVersion wrongDependencyVersion) { - wrongVersionDependencies.add(wrongDependencyVersion); - } - - } - - public static class WrongDependencyVersion { - - private String dependencyId; // value is "pluginId" - private String dependentId; // value is "pluginId" - private String existingVersion; - private String requiredVersion; - - WrongDependencyVersion(String dependencyId, String dependentId, String existingVersion, String requiredVersion) { - this.dependencyId = dependencyId; - this.dependentId = dependentId; - this.existingVersion = existingVersion; - this.requiredVersion = requiredVersion; - } - - public String getDependencyId() { - return dependencyId; - } - - public String getDependentId() { - return dependentId; - } - - public String getExistingVersion() { - return existingVersion; - } - - public String getRequiredVersion() { - return requiredVersion; - } - - } - - /** - * It will be thrown if a cyclic dependency is detected. - */ - public static class CyclicDependencyException extends PluginException { - - public CyclicDependencyException() { - super("Cyclic dependencies"); - } - - } - - /** - * Indicates that the dependencies required were not found. - */ - public static class DependenciesNotFoundException extends PluginException { - - private List dependencies; - - public DependenciesNotFoundException(List dependencies) { - super("Dependencies '{}' not found", dependencies); - - this.dependencies = dependencies; - } - - public List getDependencies() { - return dependencies; - } - - } - - /** - * Indicates that some dependencies have wrong version. - */ - public static class DependenciesWrongVersionException extends PluginException { - - private List dependencies; - - public DependenciesWrongVersionException(List dependencies) { - super("Dependencies '{}' have wrong version", dependencies); - - this.dependencies = dependencies; - } - - public List getDependencies() { - return dependencies; - } - - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java deleted file mode 100644 index ea70f9a..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/DevelopmentPluginClasspath.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * Overwrite classes directories to {@code target/classes} and lib directories to {@code target/lib}. - * - * @author Decebal Suiu - */ -public class DevelopmentPluginClasspath extends PluginClasspath { - - public DevelopmentPluginClasspath() { - super(); - - addClassesDirectories("target/classes"); - addLibDirectories("target/lib"); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java b/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java deleted file mode 100644 index eb8234f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/Extension.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -import java.lang.annotation.Documented; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -/** - * @author Decebal Suiu - */ -@Retention(RUNTIME) -@Target(TYPE) -@Inherited -@Documented -public @interface Extension { - - int ordinal() default 0; - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionDescriptor.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionDescriptor.java deleted file mode 100644 index 4096da5..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionDescriptor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * @author Decebal Suiu - */ -public class ExtensionDescriptor { - - private int ordinal; - private Class extensionClass; - - public Class getExtensionClass() { - return extensionClass; - } - - public int getOrdinal() { - return ordinal; - } - - void setExtensionClass(Class extensionClass) { - this.extensionClass = extensionClass; - } - - void setOrdinal(int ordinal) { - this.ordinal = ordinal; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFactory.java deleted file mode 100644 index be8c62f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * Creates an extension instance. - */ -public interface ExtensionFactory { - - Object create(Class extensionClass); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java deleted file mode 100644 index d00e838..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionFinder.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.util.List; -import java.util.Set; - -/** - * @author Decebal Suiu - */ -public interface ExtensionFinder { - - /** - * Retrieves a list with all extensions found for an extension point. - */ - List> find(Class type); - - /** - * Retrieves a list with all extensions found for an extension point and a plugin. - */ - List> find(Class type, String pluginId); - - /** - * Retrieves a list with all extensions found for a plugin - */ - List find(String pluginId); - - /** - * Retrieves a list with all extension class names found for a plugin. - */ - Set findClassNames(String pluginId); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionPoint.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionPoint.java deleted file mode 100644 index e01aa65..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionPoint.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * @author Decebal Suiu - */ -public interface ExtensionPoint { - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java deleted file mode 100644 index f150c0d..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ExtensionWrapper.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * A wrapper over extension instance. - * - * @author Decebal Suiu - */ -public class ExtensionWrapper implements Comparable> { - - ExtensionDescriptor descriptor; - ExtensionFactory extensionFactory; - T extension; // cache - - public ExtensionWrapper(ExtensionDescriptor descriptor) { - this.descriptor = descriptor; - } - - @SuppressWarnings("unchecked") - public T getExtension() { - if (extension == null) { - extension = (T) extensionFactory.create(descriptor.getExtensionClass()); - } - - return extension; - } - - public ExtensionDescriptor getDescriptor() { - return descriptor; - } - - public int getOrdinal() { - return descriptor.getOrdinal(); - } - - @Override - public int compareTo(ExtensionWrapper o) { - return (getOrdinal() - o.getOrdinal()); - } - - void setExtensionFactory(ExtensionFactory extensionFactory) { - this.extensionFactory = extensionFactory; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/JarPluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/JarPluginManager.java deleted file mode 100644 index bc66f03..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/JarPluginManager.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2016 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import ro.fortsoft.pf4j.util.AndFileFilter; -import ro.fortsoft.pf4j.util.DirectoryFileFilter; -import ro.fortsoft.pf4j.util.HiddenFilter; -import ro.fortsoft.pf4j.util.JarFileFilter; -import ro.fortsoft.pf4j.util.NameFileFilter; -import ro.fortsoft.pf4j.util.NotFileFilter; -import ro.fortsoft.pf4j.util.OrFileFilter; - -import java.io.FileFilter; -import java.io.IOException; -import java.nio.file.Path; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -/** - * It's a {@link PluginManager} that loads plugin from a jar file. - * Actually, a plugin is a fat jar, a jar which contains classes from all the libraries, - * on which your project depends and, of course, the classes of current project. - * - * @author Decebal Suiu - */ -public class JarPluginManager extends DefaultPluginManager { - - @Override - protected PluginRepository createPluginRepository() { - return new JarPluginRepository(getPluginsRoot(), isDevelopment()); - } - - @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return isDevelopment() ? new PropertiesPluginDescriptorFinder() : new JarPluginDescriptorFinder(); - } - - @Override - protected PluginLoader createPluginLoader() { - return new JarPluginLoader(this, pluginClasspath); - } - - class JarPluginRepository extends BasePluginRepository { - - public JarPluginRepository(Path pluginsRoot, boolean development) { - super(pluginsRoot); - - if (development) { - AndFileFilter pluginsFilter = new AndFileFilter(new DirectoryFileFilter()); - pluginsFilter.addFileFilter(new NotFileFilter(createHiddenPluginFilter(development))); - setFilter(pluginsFilter); - } else { - setFilter(new JarFileFilter()); - } - } - - protected FileFilter createHiddenPluginFilter(boolean development) { - OrFileFilter hiddenPluginFilter = new OrFileFilter(new HiddenFilter()); - - if (development) { - hiddenPluginFilter.addFileFilter(new NameFileFilter("target")); - } - - return hiddenPluginFilter; - } - - } - - class JarPluginDescriptorFinder extends ManifestPluginDescriptorFinder { - - @Override - public Manifest readManifest(Path pluginPath) throws PluginException { - try { - return new JarFile(pluginPath.toFile()).getManifest(); - } catch (IOException e) { - throw new PluginException(e); - } - } - - } - - class JarPluginLoader extends DefaultPluginLoader { - - public JarPluginLoader(PluginManager pluginManager, PluginClasspath pluginClasspath) { - super(pluginManager, pluginClasspath); - } - - @Override - public ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor) { - if (isDevelopment()) { - return super.loadPlugin(pluginPath, pluginDescriptor); - } - - PluginClassLoader pluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, getClass().getClassLoader()); - pluginClassLoader.addFile(pluginPath.toFile()); - - return pluginClassLoader; - } - - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/LegacyExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/LegacyExtensionFinder.java deleted file mode 100644 index 6814fa3..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/LegacyExtensionFinder.java +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.processor.LegacyExtensionStorage; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.Enumeration; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * All extensions declared in a plugin are indexed in a file {@code META-INF/extensions.idx}. - * This class lookup extensions in all extensions index files {@code META-INF/extensions.idx}. - * - * @author Decebal Suiu - */ -public class LegacyExtensionFinder extends AbstractExtensionFinder { - - private static final Logger log = LoggerFactory.getLogger(LegacyExtensionFinder.class); - - public LegacyExtensionFinder(PluginManager pluginManager) { - super(pluginManager); - } - - @Override - public Map> readClasspathStorages() { - log.debug("Reading extensions storages from classpath"); - Map> result = new LinkedHashMap<>(); - - Set bucket = new HashSet<>(); - try { - Enumeration urls = getClass().getClassLoader().getResources(getExtensionsResource()); - while (urls.hasMoreElements()) { - URL url = urls.nextElement(); - log.debug("Read '{}'", url.getFile()); - try (Reader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { - LegacyExtensionStorage.read(reader, bucket); - } - } - - debugExtensions(bucket); - - result.put(null, bucket); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - - return result; - } - - @Override - public Map> readPluginsStorages() { - log.debug("Reading extensions storages from plugins"); - Map> result = new LinkedHashMap<>(); - - List plugins = pluginManager.getPlugins(); - for (PluginWrapper plugin : plugins) { - String pluginId = plugin.getDescriptor().getPluginId(); - log.debug("Reading extensions storage from plugin '{}'", pluginId); - Set bucket = new HashSet<>(); - - try { - URL url = ((PluginClassLoader) plugin.getPluginClassLoader()).findResource(getExtensionsResource()); - if (url != null) { - log.debug("Read '{}'", url.getFile()); - try (Reader reader = new InputStreamReader(url.openStream(), StandardCharsets.UTF_8)) { - LegacyExtensionStorage.read(reader, bucket); - } - } else { - log.debug("Cannot find '{}'", getExtensionsResource()); - } - - debugExtensions(bucket); - - result.put(pluginId, bucket); - } catch (IOException e) { - log.error(e.getMessage(), e); - } - } - - return result; - } - - private static String getExtensionsResource() { - return LegacyExtensionStorage.EXTENSIONS_RESOURCE; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java deleted file mode 100644 index a30fb67..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinder.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import ro.fortsoft.pf4j.util.StringUtils; - -import java.nio.file.Path; -import java.util.jar.Attributes; -import java.util.jar.Manifest; - -/** - * Read the plugin descriptor from the manifest file. - * - * @author Decebal Suiu - */ -public abstract class ManifestPluginDescriptorFinder implements PluginDescriptorFinder { - - @Override - public PluginDescriptor find(Path pluginPath) throws PluginException { - Manifest manifest = readManifest(pluginPath); - - return createPluginDescriptor(manifest); - } - - public abstract Manifest readManifest(Path pluginPath) throws PluginException; - - protected PluginDescriptor createPluginDescriptor(Manifest manifest) { - PluginDescriptor pluginDescriptor = createPluginDescriptorInstance(); - - // TODO validate !!! - Attributes attributes = manifest.getMainAttributes(); - String id = attributes.getValue("Plugin-Id"); - pluginDescriptor.setPluginId(id); - - String description = attributes.getValue("Plugin-Description"); - if (StringUtils.isEmpty(description)) { - pluginDescriptor.setPluginDescription(""); - } else { - pluginDescriptor.setPluginDescription(description); - } - - String clazz = attributes.getValue("Plugin-Class"); - pluginDescriptor.setPluginClass(clazz); - - String version = attributes.getValue("Plugin-Version"); - if (StringUtils.isNotEmpty(version)) { - pluginDescriptor.setPluginVersion(version); - } - - String provider = attributes.getValue("Plugin-Provider"); - pluginDescriptor.setProvider(provider); - String dependencies = attributes.getValue("Plugin-Dependencies"); - pluginDescriptor.setDependencies(dependencies); - - String requires = attributes.getValue("Plugin-Requires"); - if (StringUtils.isNotEmpty(requires)) { - pluginDescriptor.setRequires(requires); - } - - pluginDescriptor.setLicense(attributes.getValue("Plugin-License")); - - return pluginDescriptor; - } - - protected PluginDescriptor createPluginDescriptorInstance() { - return new PluginDescriptor(); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/Plugin.java b/pf4j/src/main/java/ro/fortsoft/pf4j/Plugin.java deleted file mode 100644 index 690258f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/Plugin.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class will be extended by all plugins and - * serve as the common class between a plugin and the application. - * - * @author Decebal Suiu - */ -public abstract class Plugin { - - /** - * Makes logging service available for descending classes. - */ - protected final Logger log = LoggerFactory.getLogger(getClass()); - - /** - * Wrapper of the plugin. - */ - protected PluginWrapper wrapper; - - /** - * Constructor to be used by plugin manager for plugin instantiation. - * Your plugins have to provide constructor with this exact signature to - * be successfully loaded by manager. - */ - public Plugin(final PluginWrapper wrapper) { - if (wrapper == null) { - throw new IllegalArgumentException("Wrapper cannot be null"); - } - - this.wrapper = wrapper; - } - - /** - * Retrieves the wrapper of this plug-in. - */ - public final PluginWrapper getWrapper() { - return wrapper; - } - - /** - * Start method is called by the application when the plugin is loaded. - */ - public void start() throws PluginException { - } - - /** - * Stop method is called by the application when the plugin is unloaded. - */ - public void stop() throws PluginException { - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java deleted file mode 100644 index 4e31fc3..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClassLoader.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.File; -import java.io.IOException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.List; - -/** - * One instance of this class should be created by plugin manager for every available plug-in. - * This class loader is a Parent Last ClassLoader - it loads the classes from the plugin's jars - * before delegating to the parent class loader. - * - * @author Decebal Suiu - */ -public class PluginClassLoader extends URLClassLoader { - - private static final Logger log = LoggerFactory.getLogger(PluginClassLoader.class); - - private static final String PLUGIN_PACKAGE_PREFIX = "ro.fortsoft.pf4j."; - - private PluginManager pluginManager; - private PluginDescriptor pluginDescriptor; - - public PluginClassLoader(PluginManager pluginManager, PluginDescriptor pluginDescriptor, ClassLoader parent) { - super(new URL[0], parent); - - this.pluginManager = pluginManager; - this.pluginDescriptor = pluginDescriptor; - } - - @Override - public void addURL(URL url) { - log.debug("Add '{}'", url); - super.addURL(url); - } - - public void addFile(File file) { - try { - addURL(file.getCanonicalFile().toURI().toURL()); - } catch (IOException e) { -// throw new RuntimeException(e); - log.error(e.getMessage(), e); - } - } - - /** - * Uses a child first delegation model rather than the standard parent first. - * If the requested class cannot be found in this class loader, the parent class loader will be consulted - * via the standard {@link ClassLoader#loadClass(String)} mechanism. - */ - @Override - public Class loadClass(String className) throws ClassNotFoundException { - synchronized (getClassLoadingLock(className)) { - log.trace("Received request to load class '{}'", className); - // if the class it's a part of the plugin engine use parent class loader - if (className.startsWith(PLUGIN_PACKAGE_PREFIX)) { - log.trace("Delegate the loading of class '{}' to parent", className); - try { - return getClass().getClassLoader().loadClass(className); - } catch (ClassNotFoundException e) { - // try next step - } - } - - // second check whether it's already been loaded - Class clazz = findLoadedClass(className); - if (clazz != null) { - log.trace("Found loaded class '{}'", className); - return clazz; - } - - // nope, try to load locally - try { - clazz = findClass(className); - log.trace("Found class '{}' in plugin classpath", className); - return clazz; - } catch (ClassNotFoundException e) { - // try next step - } - - // look in dependencies - log.trace("Search in dependencies for class '{}'", className); - List dependencies = pluginDescriptor.getDependencies(); - for (PluginDependency dependency : dependencies) { - ClassLoader classLoader = pluginManager.getPluginClassLoader(dependency.getPluginId()); - try { - return classLoader.loadClass(className); - } catch (ClassNotFoundException e) { - // try next dependency - } - } - - log.trace("Couldn't find class '{}' in plugin classpath. Delegating to parent", className); - - // use the standard ClassLoader (which follows normal parent delegation) - return super.loadClass(className); - } - } - - /** - * Load the named resource from this plugin. - * This implementation checks the plugin's classpath first then delegates to the parent. - * - * @param name the name of the resource. - * @return the URL to the resource, null if the resource was not found. - */ - @Override - public URL getResource(String name) { - log.trace("Trying to find resource '{}' in plugin classpath", name); - URL url = findResource(name); - if (url != null) { - log.trace("Found resource '{}' in plugin classpath", name); - return url; - } - - log.trace("Couldn't find resource '{}' in plugin classpath. Delegating to parent"); - - return super.getResource(name); - } - - @Override - public URL findResource(String name) { - return super.findResource(name); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java deleted file mode 100644 index 3a6e99d..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginClasspath.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * The classpath of the plugin. - * It contains {@code classes} directories and {@code lib} directories (directories that contains jars). - * - * @author Decebal Suiu - */ -public class PluginClasspath { - - private List classesDirectories; - private List libDirectories; - - public PluginClasspath() { - classesDirectories = new ArrayList<>(); - libDirectories = new ArrayList<>(); - } - - public List getClassesDirectories() { - return classesDirectories; - } - - public void addClassesDirectories(String... classesDirectories) { - this.classesDirectories.addAll(Arrays.asList(classesDirectories)); - } - - public List getLibDirectories() { - return libDirectories; - } - - public void addLibDirectories(String... libDirectories) { - this.libDirectories.addAll(Arrays.asList(libDirectories)); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDependency.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDependency.java deleted file mode 100644 index 44a147f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDependency.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * @author Decebal Suiu - */ -public class PluginDependency { - - private String pluginId; - private String pluginVersionSupport = "*"; - - public PluginDependency(String dependency) { - int index = dependency.indexOf('@'); - if (index == -1) { - this.pluginId = dependency; - } else { - this.pluginId = dependency.substring(0, index); - if (dependency.length() > index + 1) { - this.pluginVersionSupport = dependency.substring(index + 1); - } - } - } - - public String getPluginId() { - return pluginId; - } - - public String getPluginVersionSupport() { - return pluginVersionSupport; - } - - @Override - public String toString() { - return "PluginDependency [pluginId=" + pluginId + ", pluginVersionSupport=" + pluginVersionSupport + "]"; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java deleted file mode 100644 index e7bcb89..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptor.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * A plugin descriptor contains information about a plug-in obtained - * from the manifest (META-INF) file. - * - * @author Decebal Suiu - */ -public class PluginDescriptor { - - private String pluginId; - private String pluginDescription; - private String pluginClass; - private String version; - private String requires = "*"; // SemVer format - private String provider; - private List dependencies; - private String license; - - public PluginDescriptor() { - dependencies = new ArrayList<>(); - } - - /** - * Returns the unique identifier of this plugin. - */ - public String getPluginId() { - return pluginId; - } - - /** - * Returns the description of this plugin. - */ - public String getPluginDescription() { - return pluginDescription; - } - - /** - * Returns the name of the class that implements Plugin interface. - */ - public String getPluginClass() { - return pluginClass; - } - - /** - * Returns the version of this plugin. - */ - public String getVersion() { - return version; - } - - /** - * Returns string version of requires - * @return String with requires expression on SemVer format - */ - public String getRequires() { - return requires; - } - - /** - * Returns the provider name of this plugin. - */ - public String getProvider() { - return provider; - } - - /** - * Returns the legal license of this plugin, e.g. "Apache-2.0", "MIT" etc - */ - public String getLicense() { - return license; - } - - /** - * Returns all dependencies declared by this plugin. - * Returns an empty array if this plugin does not declare any require. - */ - public List getDependencies() { - return dependencies; - } - - @Override - public String toString() { - return "PluginDescriptor [pluginId=" + pluginId + ", pluginClass=" - + pluginClass + ", version=" + version + ", provider=" - + provider + ", dependencies=" + dependencies + ", description=" - + pluginDescription + ", requires=" + requires + ", license=" - + license + "]"; - } - - PluginDescriptor setPluginId(String pluginId) { - this.pluginId = pluginId; - - return this; - } - - PluginDescriptor setPluginDescription(String pluginDescription) { - this.pluginDescription = pluginDescription; - - return this; - } - - PluginDescriptor setPluginClass(String pluginClassName) { - this.pluginClass = pluginClassName; - - return this; - } - - PluginDescriptor setPluginVersion(String version) { - this.version = version; - - return this; - } - - PluginDescriptor setProvider(String provider) { - this.provider = provider; - - return this; - } - - PluginDescriptor setRequires(String requires) { - this.requires = requires; - - return this; - } - - PluginDescriptor setDependencies(String dependencies) { - if (dependencies != null) { - dependencies = dependencies.trim(); - if (dependencies.isEmpty()) { - this.dependencies = Collections.emptyList(); - } else { - this.dependencies = new ArrayList<>(); - String[] tokens = dependencies.split(","); - for (String dependency : tokens) { - dependency = dependency.trim(); - if (!dependency.isEmpty()) { - this.dependencies.add(new PluginDependency(dependency)); - } - } - if (this.dependencies.isEmpty()) { - this.dependencies = Collections.emptyList(); - } - } - } else { - this.dependencies = Collections.emptyList(); - } - - return this; - } - - public PluginDescriptor setLicense(String license) { - this.license = license; - - return this; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java deleted file mode 100644 index c84adaf..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginDescriptorFinder.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.nio.file.Path; - -/** - * Find a plugin descriptor for a plugin path. - * You can find in manifest file {@link DefaultPluginDescriptorFinder}, - * xml file, properties file, java services (with {@link java.util.ServiceLoader}), etc. - * - * @author Decebal Suiu - */ -public interface PluginDescriptorFinder { - - PluginDescriptor find(Path pluginPath) throws PluginException; - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java deleted file mode 100644 index 3d4aec4..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginException.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import ro.fortsoft.pf4j.util.StringUtils; - -/** - * An exception used to indicate that a plugin problem occurred. - * - * @author Decebal Suiu - */ -public class PluginException extends Exception { - - public PluginException() { - super(); - } - - public PluginException(String message) { - super(message); - } - - public PluginException(Throwable cause) { - super(cause); - } - - public PluginException(String message, Throwable cause) { - super(message, cause); - } - - public PluginException(Throwable cause, String message, Object... args) { - super(StringUtils.format(message, args), cause); - } - - public PluginException(String message, Object... args) { - super(StringUtils.format(message, args)); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginFactory.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginFactory.java deleted file mode 100644 index 85215ee..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginFactory.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * Creates a plugin instance. - */ -public interface PluginFactory { - - Plugin create(PluginWrapper pluginWrapper); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java deleted file mode 100644 index ecd0e08..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginLoader.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.nio.file.Path; - -/** - * Load all information (classes) needed by a plugin. - * - * @author Decebal Suiu - */ -public interface PluginLoader { - - ClassLoader loadPlugin(Path pluginPath, PluginDescriptor pluginDescriptor); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java deleted file mode 100644 index 793784b..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginManager.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.nio.file.Path; -import java.util.List; -import java.util.Set; - -/** - * Provides the functionality for plugin management such as load, - * start and stop the plugins. - * - * @author Decebal Suiu - */ -public interface PluginManager { - - /** - * Retrieves all plugins. - */ - List getPlugins(); - - /** - * Retrieves all plugins with this state. - */ - List getPlugins(PluginState pluginState); - - /** - * Retrieves all resolved plugins (with resolved dependency). - */ - List getResolvedPlugins(); - - /** - * Retrieves all unresolved plugins (with unresolved dependency). - */ - List getUnresolvedPlugins(); - - /** - * Retrieves all started plugins. - */ - List getStartedPlugins(); - - /** - * Retrieves the plugin with this id, or null if the plugin does not exist. - * - * @param pluginId The pluginId for the plugin you are trying to get. - * @return A PluginWrapper object for this plugin, or null if it does not exist. - */ - PluginWrapper getPlugin(String pluginId); - - /** - * Load plugins. - */ - void loadPlugins(); - - /** - * Load a plugin. - * - * @param pluginPath - * @return the pluginId of the installed plugin or null - */ - String loadPlugin(Path pluginPath); - - /** - * Start all active plugins. - */ - void startPlugins(); - - /** - * Start the specified plugin and it's dependencies. - * - * @return the plugin state - */ - PluginState startPlugin(String pluginId); - - /** - * Stop all active plugins. - */ - void stopPlugins(); - - /** - * Stop the specified plugin and it's dependencies. - * - * @return the plugin state - */ - PluginState stopPlugin(String pluginId); - - /** - * Unload a plugin. - * - * @param pluginId - * @return true if the plugin was unloaded - */ - boolean unloadPlugin(String pluginId); - - /** - * Disables a plugin from being loaded. - * - * @param pluginId - * @return true if plugin is disabled - */ - boolean disablePlugin(String pluginId); - - /** - * Enables a plugin that has previously been disabled. - * - * @param pluginId - * @return true if plugin is enabled - */ - boolean enablePlugin(String pluginId); - - /** - * Deletes a plugin. - * - * @param pluginId - * @return true if the plugin was deleted - */ - boolean deletePlugin(String pluginId); - - ClassLoader getPluginClassLoader(String pluginId); - - List getExtensions(Class type); - - List getExtensions(Class type, String pluginId); - - List getExtensions(String pluginId); - - Set getExtensionClassNames(String pluginId); - - ExtensionFactory getExtensionFactory(); - - /** - * The runtime mode. Must currently be either DEVELOPMENT or DEPLOYMENT. - */ - RuntimeMode getRuntimeMode(); - - /** - * Retrieves the {@link PluginWrapper} that loaded the given class 'clazz'. - */ - PluginWrapper whichPlugin(Class clazz); - - void addPluginStateListener(PluginStateListener listener); - - void removePluginStateListener(PluginStateListener listener); - - /** - * Set the system version. This is used to compare against the plugin - * requires attribute. The default system version is 0.0.0 which - * disables all version checking. - * - * @default 0.0.0 - * @param version - */ - void setSystemVersion(String version); - - /** - * Returns the system version. - * - * @return the system version - */ - String getSystemVersion(); - - /** - * Gets the path of the folder where plugins are installed - * @return Path of plugins root - */ - Path getPluginsRoot(); - - VersionManager getVersionManager(); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginRepository.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginRepository.java deleted file mode 100644 index 6f57c57..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginRepository.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.nio.file.Path; -import java.util.List; - -/** - * Directory that contains plugins. A plugin could be a zip file. - * - * @author Decebal Suiu - * @author Mário Franco - */ -public interface PluginRepository { - - /** - * List all plugin paths. - * - * @return a list of files - */ - List getPluginPaths(); - - /** - * Removes a plugin from the repository. - * - * @param pluginPath the plugin path - * @return true if deleted - */ - boolean deletePluginPath(Path pluginPath); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java deleted file mode 100644 index 8df6a0f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginState.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * @author Decebal Suiu - */ -public class PluginState { - - public static final PluginState CREATED = new PluginState("CREATED"); - public static final PluginState DISABLED = new PluginState("DISABLED"); - public static final PluginState STARTED = new PluginState("STARTED"); - public static final PluginState STOPPED = new PluginState("STOPPED"); - - private String status; - - private PluginState(String status) { - this.status = status; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - PluginState that = (PluginState) o; - - if (!status.equals(that.status)) return false; - - return true; - } - - @Override - public int hashCode() { - return status.hashCode(); - } - - @Override - public String toString() { - return status; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateEvent.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateEvent.java deleted file mode 100644 index 0314eae..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateEvent.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.util.EventObject; - -/** - * @author Decebal Suiu - */ -public class PluginStateEvent extends EventObject { - - private PluginWrapper plugin; - private PluginState oldState; - - public PluginStateEvent(PluginManager source, PluginWrapper plugin, PluginState oldState) { - super(source); - - this.plugin = plugin; - this.oldState = oldState; - } - - @Override - public PluginManager getSource() { - return (PluginManager) super.getSource(); - } - - public PluginWrapper getPlugin() { - return plugin; - } - - public PluginState getPluginState() { - return plugin.getPluginState(); - } - - public PluginState getOldState() { - return oldState; - } - - @Override - public String toString() { - return "PluginStateEvent [plugin=" + plugin.getPluginId() + - ", newState=" + getPluginState() + - ", oldState=" + oldState + - ']'; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateListener.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateListener.java deleted file mode 100644 index 99e2fec..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStateListener.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2014 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.util.EventListener; - -/** - * PluginStateListener defines the interface for an object that listens to plugin state changes. - * - * @author Decebal Suiu - */ -public interface PluginStateListener extends EventListener { - - /** - * Invoked when a plugin's state (for example DISABLED, STARTED) is changed. - */ - void pluginStateChanged(PluginStateEvent event); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStatusProvider.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStatusProvider.java deleted file mode 100644 index 4943530..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginStatusProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * @author Decebal Suiu - * @author Mário Franco - */ -public interface PluginStatusProvider { - - /** - * Checks if the plugin is disabled or not - * - * @param pluginId the plugin id - * @return if the plugin is disabled or not - */ - boolean isPluginDisabled(String pluginId); - - /** - * Disables a plugin from being loaded. - * - * @param pluginId - * @return true if plugin is disabled - */ - boolean disablePlugin(String pluginId); - - /** - * Enables a plugin that has previously been disabled. - * - * @param pluginId - * @return true if plugin is enabled - */ - boolean enablePlugin(String pluginId); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java deleted file mode 100644 index e594f68..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PluginWrapper.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.nio.file.Path; - -/** - * A wrapper over plugin instance. - * - * @author Decebal Suiu - */ -public class PluginWrapper { - - private PluginManager pluginManager; - private PluginDescriptor descriptor; - private Path pluginPath; - private ClassLoader pluginClassLoader; - private PluginFactory pluginFactory; - private PluginState pluginState; - private RuntimeMode runtimeMode; - - Plugin plugin; // cache - - public PluginWrapper(PluginManager pluginManager, PluginDescriptor descriptor, Path pluginPath, ClassLoader pluginClassLoader) { - this.pluginManager = pluginManager; - this.descriptor = descriptor; - this.pluginPath = pluginPath; - this.pluginClassLoader = pluginClassLoader; - - pluginState = PluginState.CREATED; - } - - /** - * Returns the plugin manager. - */ - public PluginManager getPluginManager() { - return pluginManager; - } - - /** - * Returns the plugin descriptor. - */ - public PluginDescriptor getDescriptor() { - return descriptor; - } - - /** - * Returns the path of this plugin. - */ - public Path getPluginPath() { - return pluginPath; - } - - /** - * Returns the plugin class loader used to load classes and resources - * for this plug-in. The class loader can be used to directly access - * plug-in resources and classes. - */ - public ClassLoader getPluginClassLoader() { - return pluginClassLoader; - } - - public Plugin getPlugin() { - if (plugin == null) { - plugin = pluginFactory.create(this); - } - - return plugin; - } - - public PluginState getPluginState() { - return pluginState; - } - - public RuntimeMode getRuntimeMode() { - return runtimeMode; - } - - /** - * Shortcut - */ - public String getPluginId() { - return getDescriptor().getPluginId(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + descriptor.getPluginId().hashCode(); - - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - - if (obj == null) { - return false; - } - - if (getClass() != obj.getClass()) { - return false; - } - - PluginWrapper other = (PluginWrapper) obj; - if (!descriptor.getPluginId().equals(other.descriptor.getPluginId())) { - return false; - } - - return true; - } - - @Override - public String toString() { - return "PluginWrapper [descriptor=" + descriptor + ", pluginPath=" + pluginPath + "]"; - } - - void setPluginState(PluginState pluginState) { - this.pluginState = pluginState; - } - - void setRuntimeMode(RuntimeMode runtimeMode) { - this.runtimeMode = runtimeMode; - } - - void setPluginFactory(PluginFactory pluginFactory) { - this.pluginFactory = pluginFactory; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java deleted file mode 100644 index 259040c..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinder.java +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.util.StringUtils; - -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.Properties; - -/** - * Find a plugin descriptor in a properties file (in plugin repository). - * - * @author Decebal Suiu - */ -public class PropertiesPluginDescriptorFinder implements PluginDescriptorFinder { - - private static final Logger log = LoggerFactory.getLogger(PropertiesPluginDescriptorFinder.class); - - private static final String DEFAULT_PROPERTIES_FILE_NAME = "plugin.properties"; - - protected String propertiesFileName; - - public PropertiesPluginDescriptorFinder() { - this(DEFAULT_PROPERTIES_FILE_NAME); - } - - public PropertiesPluginDescriptorFinder(String propertiesFileName) { - this.propertiesFileName = propertiesFileName; - } - - @Override - public PluginDescriptor find(Path pluginPath) throws PluginException { - Properties properties = readProperties(pluginPath); - - return createPluginDescriptor(properties); - } - - protected Properties readProperties(Path pluginPath) throws PluginException { - Path propertiesPath = pluginPath.resolve(Paths.get(propertiesFileName)); - log.debug("Lookup plugin descriptor in '{}'", propertiesPath); - if (Files.notExists(propertiesPath)) { - throw new PluginException("Cannot find '{}' path", pluginPath); - } - - Properties properties = new Properties(); - try (InputStream input = Files.newInputStream(propertiesPath)) { - properties.load(input); - } catch (IOException e) { - throw new PluginException(e.getMessage(), e); - } - - return properties; - } - - protected PluginDescriptor createPluginDescriptor(Properties properties) { - PluginDescriptor pluginDescriptor = createPluginDescriptorInstance(); - - // TODO validate !!! - String id = properties.getProperty("plugin.id"); - pluginDescriptor.setPluginId(id); - - String description = properties.getProperty("plugin.description"); - if (StringUtils.isEmpty(description)) { - pluginDescriptor.setPluginDescription(""); - } else { - pluginDescriptor.setPluginDescription(description); - } - - String clazz = properties.getProperty("plugin.class"); - pluginDescriptor.setPluginClass(clazz); - - String version = properties.getProperty("plugin.version"); - if (StringUtils.isNotEmpty(version)) { - pluginDescriptor.setPluginVersion(version); - } - - String provider = properties.getProperty("plugin.provider"); - pluginDescriptor.setProvider(provider); - - String dependencies = properties.getProperty("plugin.dependencies"); - pluginDescriptor.setDependencies(dependencies); - - String requires = properties.getProperty("plugin.requires"); - if (StringUtils.isNotEmpty(requires)) { - pluginDescriptor.setRequires(requires); - } - - pluginDescriptor.setLicense(properties.getProperty("plugin.license")); - - return pluginDescriptor; - } - - protected PluginDescriptor createPluginDescriptorInstance() { - return new PluginDescriptor(); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java b/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java deleted file mode 100644 index 1ceb6d9..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/RuntimeMode.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import java.util.HashMap; -import java.util.Map; -import java.util.NoSuchElementException; - -/** - * @author Decebal Suiu - */ -public enum RuntimeMode { - - DEVELOPMENT("development"), // development - DEPLOYMENT("deployment"); // deployment - - private final String name; - - private static final Map map = new HashMap<>(); - - static { - for (RuntimeMode mode : RuntimeMode.values()) { - map.put(mode.name, mode); - } - } - - private RuntimeMode(final String name) { - this.name = name; - } - - @Override - public String toString() { - return name; - } - - public static RuntimeMode byName(String name) { - if (map.containsKey(name)) { - return map.get(name); - } - - throw new NoSuchElementException("Cannot found PF4J runtime mode with name '" + name + - "'. Must be 'development' or 'deployment'."); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/ServiceProviderExtensionFinder.java b/pf4j/src/main/java/ro/fortsoft/pf4j/ServiceProviderExtensionFinder.java deleted file mode 100644 index 4912507..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/ServiceProviderExtensionFinder.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import ro.fortsoft.pf4j.processor.ServiceProviderExtensionStorage; - -import java.io.IOException; -import java.io.Reader; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.FileVisitOption; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * The {@link java.util.ServiceLoader} base implementation for {@link ExtensionFinder}. - * This class lookup extensions in all extensions index files {@code META-INF/services}. - * - * @author Decebal Suiu - */ -public class ServiceProviderExtensionFinder extends AbstractExtensionFinder { - - private static final Logger log = LoggerFactory.getLogger(ServiceProviderExtensionFinder.class); - - public ServiceProviderExtensionFinder(PluginManager pluginManager) { - super(pluginManager); - } - - @Override - public Map> readClasspathStorages() { - log.debug("Reading extensions storages from classpath"); - Map> result = new LinkedHashMap<>(); - - final Set bucket = new HashSet<>(); - try { - URL url = getClass().getClassLoader().getResource(getExtensionsResource()); - if (url != null) { - Path extensionPath; - if (url.toURI().getScheme().equals("jar")) { - FileSystem fileSystem = FileSystems.newFileSystem(url.toURI(), Collections.emptyMap()); - extensionPath = fileSystem.getPath(getExtensionsResource()); - } else { - extensionPath = Paths.get(url.toURI()); - } - - bucket.addAll(readExtensions(extensionPath)); - } - - debugExtensions(bucket); - - result.put(null, bucket); - } catch (IOException | URISyntaxException e) { - log.error(e.getMessage(), e); - } - - return result; - } - - @Override - public Map> readPluginsStorages() { - log.debug("Reading extensions storages from plugins"); - Map> result = new LinkedHashMap<>(); - - List plugins = pluginManager.getPlugins(); - for (PluginWrapper plugin : plugins) { - String pluginId = plugin.getDescriptor().getPluginId(); - log.debug("Reading extensions storages for plugin '{}'", pluginId); - final Set bucket = new HashSet<>(); - - try { - URL url = ((PluginClassLoader) plugin.getPluginClassLoader()).findResource(getExtensionsResource()); - if (url != null) { - Path extensionPath; - if (url.toURI().getScheme().equals("jar")) { - FileSystem fileSystem = FileSystems.newFileSystem(url.toURI(), Collections.emptyMap()); - extensionPath = fileSystem.getPath(getExtensionsResource()); - } else { - extensionPath = Paths.get(url.toURI()); - } - - bucket.addAll(readExtensions(extensionPath)); - } else { - log.debug("Cannot find '{}'", getExtensionsResource()); - } - - debugExtensions(bucket); - - result.put(pluginId, bucket); - } catch (IOException | URISyntaxException e) { - log.error(e.getMessage(), e); - } - } - - return result; - } - - private static String getExtensionsResource() { - return ServiceProviderExtensionStorage.EXTENSIONS_RESOURCE; - } - - private Set readExtensions(Path extensionPath) throws IOException { - final Set result = new HashSet<>(); - Files.walkFileTree(extensionPath, Collections.emptySet(), 1, new SimpleFileVisitor() { - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - log.debug("Read '{}'", file); - try (Reader reader = Files.newBufferedReader(file, StandardCharsets.UTF_8)) { - ServiceProviderExtensionStorage.read(reader, result); - } - - return FileVisitResult.CONTINUE; - } - - }); - - return result; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/VersionManager.java b/pf4j/src/main/java/ro/fortsoft/pf4j/VersionManager.java deleted file mode 100644 index c3de913..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/VersionManager.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -/** - * Manager responsible for versions of plugins. - * - * @author Decebal Suiu - */ -public interface VersionManager { - - /** - * Check if a {@code constraint} and a {@code version} match. - * - * @param constraint - * @param version - * @return - */ - boolean satisfies(String constraint, String version); - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ExtensionAnnotationProcessor.java b/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ExtensionAnnotationProcessor.java deleted file mode 100644 index 5174f64..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ExtensionAnnotationProcessor.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.processor; - -import ro.fortsoft.pf4j.Extension; -import ro.fortsoft.pf4j.ExtensionPoint; - -import javax.annotation.processing.AbstractProcessor; -import javax.annotation.processing.ProcessingEnvironment; -import javax.annotation.processing.RoundEnvironment; -import javax.lang.model.SourceVersion; -import javax.lang.model.element.Element; -import javax.lang.model.element.TypeElement; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import javax.tools.Diagnostic; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - -/** - * @author Decebal Suiu - */ -public class ExtensionAnnotationProcessor extends AbstractProcessor { - - private static final String STORAGE_CLASS_NAME = "pf4j.storageClassName"; - - private Map> extensions = new HashMap<>(); // the key is the extension point - private Map> oldExtensions = new HashMap<>(); // the key is the extension point - - private ExtensionStorage storage; - - @Override - public synchronized void init(ProcessingEnvironment processingEnv) { - super.init(processingEnv); - - info("%s init", ExtensionAnnotationProcessor.class); - storage = createStorage(); - } - - @Override - public SourceVersion getSupportedSourceVersion() { - return SourceVersion.latest(); - } - - @Override - public Set getSupportedAnnotationTypes() { - Set annotationTypes = new HashSet<>(); - annotationTypes.add(Extension.class.getName()); - - return annotationTypes; - } - - @Override - public Set getSupportedOptions() { - Set options = new HashSet<>(); - options.add(STORAGE_CLASS_NAME); - - return options; - } - - @Override - public boolean process(Set annotations, RoundEnvironment roundEnv) { - if (roundEnv.processingOver()) { - return false; - } - - info("Processing @%s", Extension.class); - for (Element element : roundEnv.getElementsAnnotatedWith(Extension.class)) { - // check if @Extension is put on class and not on method or constructor - if (!(element instanceof TypeElement)) { - continue; - } - - // check if class extends/implements an extension point - if (!isExtension(element.asType())) { - continue; - } - - TypeElement extensionElement = (TypeElement) element; -// Extension annotation = element.getAnnotation(Extension.class); - List extensionPointElements = findExtensionPoints(extensionElement); - if (extensionPointElements.isEmpty()) { - // TODO throw error ? - continue; - } - - String extension = getBinaryName(extensionElement); - for (TypeElement extensionPointElement : extensionPointElements) { - String extensionPoint = getBinaryName(extensionPointElement); - Set extensionPoints = extensions.get(extensionPoint); - if (extensionPoints == null) { - extensionPoints = new TreeSet<>(); - extensions.put(extensionPoint, extensionPoints); - } - extensionPoints.add(extension); - } - } - - // read old extensions - oldExtensions = storage.read(); - for (Map.Entry> entry : oldExtensions.entrySet()) { - String extensionPoint = entry.getKey(); - if (extensions.containsKey(extensionPoint)) { - extensions.get(extensionPoint).addAll(entry.getValue()); - } else { - extensions.put(extensionPoint, entry.getValue()); - } - } - - // write extensions - storage.write(extensions); - - return false; - } - - public ProcessingEnvironment getProcessingEnvironment() { - return processingEnv; - } - - public void error(String message, Object... args) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format(message, args)); - } - - public void error(Element element, String message, Object... args) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, String.format(message, args), element); - } - - public void info(String message, Object... args) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format(message, args)); - } - - public void info(Element element, String message, Object... args) { - processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE, String.format(message, args), element); - } - - public String getBinaryName(TypeElement element) { - return processingEnv.getElementUtils().getBinaryName(element).toString(); - } - - public Map> getExtensions() { - return extensions; - } - - public Map> getOldExtensions() { - return oldExtensions; - } - - private List findExtensionPoints(TypeElement extensionElement) { - List extensionPointElements = new ArrayList<>(); - - // search in interfaces - for (TypeMirror item : extensionElement.getInterfaces()) { - boolean isExtensionPoint = processingEnv.getTypeUtils().isSubtype(item, getExtensionPointType()); - if (isExtensionPoint) { - TypeElement extensionPointElement = (TypeElement) ((DeclaredType) item).asElement(); - extensionPointElements.add(extensionPointElement); - } - } - - // search in superclass - TypeMirror superclass = extensionElement.getSuperclass(); - if (superclass.getKind() != TypeKind.NONE) { - boolean isExtensionPoint = processingEnv.getTypeUtils().isSubtype(superclass, getExtensionPointType()); - if (isExtensionPoint) { - TypeElement extensionPointElement = (TypeElement) ((DeclaredType) superclass).asElement(); - extensionPointElements.add(extensionPointElement); - } - } - - return extensionPointElements; - } - - private boolean isExtension(TypeMirror typeMirror) { - return processingEnv.getTypeUtils().isAssignable(typeMirror, getExtensionPointType()); - } - - - private TypeMirror getExtensionPointType() { - return processingEnv.getElementUtils().getTypeElement(ExtensionPoint.class.getName()).asType(); - } - - @SuppressWarnings("unchecked") - private ExtensionStorage createStorage() { - ExtensionStorage storage = null; - - // search in processing options - String storageClassName = processingEnv.getOptions().get(STORAGE_CLASS_NAME); - if (storageClassName == null) { - // search in system properties - storageClassName = System.getProperty(STORAGE_CLASS_NAME); - } - - if (storageClassName != null) { - // use reflection to create the storage instance - try { - Class storageClass = getClass().getClassLoader().loadClass(storageClassName); - Constructor constructor = storageClass.getConstructor(ExtensionAnnotationProcessor.class); - storage = (ExtensionStorage) constructor.newInstance(this); - } catch (Exception e) { - error(e.getMessage()); - } - } - - if (storage == null) { - // default storage - storage = new LegacyExtensionStorage(this); - } - - return storage; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ExtensionStorage.java b/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ExtensionStorage.java deleted file mode 100644 index d184ea7..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ExtensionStorage.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.processor; - -import javax.annotation.processing.Filer; -import javax.lang.model.element.Element; -import java.util.Map; -import java.util.Set; - -/** - * @author Decebal Suiu - */ -public abstract class ExtensionStorage { - - protected final ExtensionAnnotationProcessor processor; - - public ExtensionStorage(ExtensionAnnotationProcessor processor) { - this.processor = processor; - } - - public abstract Map> read(); - - public abstract void write(Map> extensions); - - /** - * Helper method. - */ - protected Filer getFiler() { - return processor.getProcessingEnvironment().getFiler(); - } - - /** - * Helper method. - */ - protected void error(String message, Object... args) { - processor.error(message, args); - } - - /** - * Helper method. - */ - protected void error(Element element, String message, Object... args) { - processor.error(element, message, args); - } - - /** - * Helper method. - */ - protected void info(String message, Object... args) { - processor.info(message, args); - } - - /** - * Helper method. - */ - protected void info(Element element, String message, Object... args) { - processor.info(element, message, args); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/processor/LegacyExtensionStorage.java b/pf4j/src/main/java/ro/fortsoft/pf4j/processor/LegacyExtensionStorage.java deleted file mode 100644 index 15d417a..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/processor/LegacyExtensionStorage.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.processor; - -import javax.annotation.processing.FilerException; -import javax.tools.FileObject; -import javax.tools.StandardLocation; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.Reader; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * @author Decebal Suiu - */ -public class LegacyExtensionStorage extends ExtensionStorage { - - public static final String EXTENSIONS_RESOURCE = "META-INF/extensions.idx"; - - private static final Pattern COMMENT = Pattern.compile("#.*"); - private static final Pattern WHITESPACE = Pattern.compile("\\s+"); - - public LegacyExtensionStorage(ExtensionAnnotationProcessor processor) { - super(processor); - } - - public static void read(Reader reader, Set entries) throws IOException { - BufferedReader bufferedReader = new BufferedReader(reader); - - String line; - while ((line = bufferedReader.readLine()) != null) { - line = COMMENT.matcher(line).replaceFirst(""); - line = WHITESPACE.matcher(line).replaceAll(""); - if (line.length() > 0) { - entries.add(line); - } - } - - bufferedReader.close(); - } - - @Override - public Map> read() { - Map> extensions = new HashMap<>(); - - try { - FileObject file = getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE); - // TODO try to calculate the extension point - Set entries = new HashSet<>(); - read(file.openReader(true), entries); - extensions.put(null, entries); - } catch (FileNotFoundException e) { - // ignore - } catch (FilerException e) { - // re-opening the file for reading or after writing is ignorable - } catch (IOException e) { - error(e.getMessage()); - } - - return extensions; - } - - @Override - public void write(Map> extensions) { - try { - FileObject file = getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE); - BufferedWriter writer = new BufferedWriter(file.openWriter()); - writer.write("# Generated by PF4J"); // write header - writer.newLine(); - for (Map.Entry> entry : extensions.entrySet()) { - for (String extension : entry.getValue()) { - writer.write(extension); - writer.newLine(); - } - } - - writer.close(); - } catch (FileNotFoundException e) { - // it's the first time, create the file - } catch (FilerException e) { - // re-opening the file for reading or after writing is ignorable - } catch (IOException e) { - error(e.toString()); - } - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ServiceProviderExtensionStorage.java b/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ServiceProviderExtensionStorage.java deleted file mode 100644 index 054dcdd..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/processor/ServiceProviderExtensionStorage.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.processor; - -import javax.annotation.processing.FilerException; -import javax.tools.FileObject; -import javax.tools.StandardLocation; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.Reader; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * @author Decebal Suiu - */ -public class ServiceProviderExtensionStorage extends ExtensionStorage { - - public static final String EXTENSIONS_RESOURCE = "META-INF/services"; - - private static final Pattern COMMENT = Pattern.compile("#.*"); - private static final Pattern WHITESPACE = Pattern.compile("\\s+"); - - public ServiceProviderExtensionStorage(ExtensionAnnotationProcessor processor) { - super(processor); - } - - public static void read(Reader reader, Set entries) throws IOException { - BufferedReader bufferedReader = new BufferedReader(reader); - - String line; - while ((line = bufferedReader.readLine()) != null) { - line = COMMENT.matcher(line).replaceFirst(""); - line = WHITESPACE.matcher(line).replaceAll(""); - if (line.length() > 0) { - entries.add(line); - } - } - - bufferedReader.close(); - } - - @Override - public Map> read() { - Map> extensions = new HashMap<>(); - - for (String extensionPoint : processor.getExtensions().keySet()) { - try { - FileObject file = getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE - + "/" + extensionPoint); - Set entries = new HashSet<>(); - read(file.openReader(true), entries); - extensions.put(extensionPoint, entries); - } catch (FileNotFoundException e) { - // doesn't exist, ignore - } catch (FilerException e) { - // re-opening the file for reading or after writing is ignorable - } catch (IOException e) { - error(e.getMessage()); - } - } - - return extensions; - } - - @Override - public void write(Map> extensions) { - for (Map.Entry> entry : extensions.entrySet()) { - String extensionPoint = entry.getKey(); - try { - FileObject file = getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", EXTENSIONS_RESOURCE - + "/" + extensionPoint); - BufferedWriter writer = new BufferedWriter(file.openWriter()); - // write header - writer.write("# Generated by PF4J"); // write header - writer.newLine(); - // write extensions - for (String extension : entry.getValue()) { - writer.write(extension); - if (!isExtensionOld(extensionPoint, extension)) { - writer.write(" # pf4j extension"); - } - writer.newLine(); - } - writer.close(); - } catch (FileNotFoundException e) { - // it's the first time, create the file - } catch (FilerException e) { - // re-opening the file for reading or after writing is ignorable - } catch (IOException e) { - error(e.toString()); - } - } - } - - private boolean isExtensionOld(String extensionPoint, String extension) { - return processor.getOldExtensions().containsKey(extensionPoint) - && processor.getOldExtensions().get(extensionPoint).contains(extension); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/AndFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/AndFileFilter.java deleted file mode 100644 index b076a69..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/AndFileFilter.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.io.File; -import java.io.FileFilter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -/** - * This filter providing conditional AND logic across a list of - * file filters. This filter returns true if all filters in the - * list return true. Otherwise, it returns false. - * Checking of the file filter list stops when the first filter returns - * false. - * - * @author Decebal Suiu - */ -public class AndFileFilter implements FileFilter { - - /** The list of file filters. */ - private List fileFilters; - - public AndFileFilter() { - this(new ArrayList()); - } - - public AndFileFilter(FileFilter... fileFilters) { - this(Arrays.asList(fileFilters)); - } - - public AndFileFilter(List fileFilters) { - this.fileFilters = new ArrayList<>(fileFilters); - } - - public AndFileFilter addFileFilter(FileFilter fileFilter) { - fileFilters.add(fileFilter); - - return this; - } - - public List getFileFilters() { - return Collections.unmodifiableList(fileFilters); - } - - public boolean removeFileFilter(FileFilter fileFilter) { - return fileFilters.remove(fileFilter); - } - - public void setFileFilters(List fileFilters) { - this.fileFilters = new ArrayList<>(fileFilters); - } - - @Override - public boolean accept(File file) { - if (this.fileFilters.size() == 0) { - return false; - } - - for (FileFilter fileFilter : this.fileFilters) { - if (!fileFilter.accept(file)) { - return false; - } - } - - return true; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ClassUtils.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ClassUtils.java deleted file mode 100644 index b435c2f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ClassUtils.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2016 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -/** - * @author Decebal Suiu - */ -public class ClassUtils { - - public static List getAllInterfacesNames(Class aClass) { - return toString(getAllInterfaces(aClass)); - } - - public static List> getAllInterfaces(Class aClass) { - List> list = new ArrayList<>(); - - while (aClass != null) { - Class[] interfaces = aClass.getInterfaces(); - for (Class anInterface : interfaces) { - if (!list.contains(anInterface)) { - list.add(anInterface); - } - - List> superInterfaces = getAllInterfaces(anInterface); - for (Class superInterface : superInterfaces) { - if (!list.contains(superInterface)) { - list.add(superInterface); - } - } - } - - aClass = aClass.getSuperclass(); - } - - return list; - } - - /* - public static List getAllAbstractClassesNames(Class aClass) { - return toString(getAllInterfaces(aClass)); - } - - public static List getAllAbstractClasses(Class aClass) { - List> list = new ArrayList<>(); - - Class superclass = aClass.getSuperclass(); - while (superclass != null) { - if (Modifier.isAbstract(superclass.getModifiers())) { - list.add(superclass); - } - superclass = superclass.getSuperclass(); - } - - return list; - } - */ - - /** - * Uses {@link Class#getSimpleName()} to convert from {@link Class} to {@link String}. - * - * @param classes - * @return - */ - private static List toString(List> classes) { - List list = new ArrayList<>(); - - for (Class aClass : classes) { - list.add(aClass.getSimpleName()); - } - - return list; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java deleted file mode 100644 index 8970c78..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectedGraph.java +++ /dev/null @@ -1,192 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Stack; - -/** - * See Wikipedia for more information. - * - * @author Decebal Suiu - */ -public class DirectedGraph { - - /** - * The implementation here is basically an adjacency list, but instead - * of an array of lists, a Map is used to map each vertex to its list of - * adjacent vertices. - */ - private Map> neighbors = new HashMap<>(); - - /** - * Add a vertex to the graph. Nothing happens if vertex is already in graph. - */ - public void addVertex(V vertex) { - if (containsVertex(vertex)) { - return; - } - - neighbors.put(vertex, new ArrayList()); - } - - /** - * True if graph contains vertex. - */ - public boolean containsVertex(V vertex) { - return neighbors.containsKey(vertex); - } - - public void removeVertex(V vertex) { - neighbors.remove(vertex); - } - - /** - * Add an edge to the graph; if either vertex does not exist, it's added. - * This implementation allows the creation of multi-edges and self-loops. - */ - public void addEdge(V from, V to) { - addVertex(from); - addVertex(to); - neighbors.get(from).add(to); - } - - /** - * Remove an edge from the graph. Nothing happens if no such edge. - * @throws {@link IllegalArgumentException} if either vertex doesn't exist. - */ - public void removeEdge(V from, V to) { - if (!containsVertex(from)) { - throw new IllegalArgumentException("Nonexistent vertex " + from); - } - - if (!containsVertex(to)) { - throw new IllegalArgumentException("Nonexistent vertex " + to); - } - - neighbors.get(from).remove(to); - } - - public List getNeighbors(V vertex) { - return containsVertex(vertex) ? neighbors.get(vertex) : new ArrayList(); - } - - /** - * Report (as a Map) the out-degree (the number of tail ends adjacent to a vertex) of each vertex. - */ - public Map outDegree() { - Map result = new HashMap<>(); - for (V vertex : neighbors.keySet()) { - result.put(vertex, neighbors.get(vertex).size()); - } - - return result; - } - - /** - * Report (as a Map) the in-degree (the number of head ends adjacent to a vertex) of each vertex. - */ - public Map inDegree() { - Map result = new HashMap<>(); - for (V vertex : neighbors.keySet()) { - result.put(vertex, 0); // all in-degrees are 0 - } - for (V from : neighbors.keySet()) { - for (V to : neighbors.get(from)) { - result.put(to, result.get(to) + 1); // increment in-degree - } - } - - return result; - } - - /** - * Report (as a List) the topological sort of the vertices; null for no such sort. - * See this for more information. - */ - public List topologicalSort() { - Map degree = inDegree(); - - // determine all vertices with zero in-degree - Stack zeroVertices = new Stack<>(); // stack as good as any here - for (V v : degree.keySet()) { - if (degree.get(v) == 0) { - zeroVertices.push(v); - } - } - - // determine the topological order - List result = new ArrayList<>(); - while (!zeroVertices.isEmpty()) { - V vertex = zeroVertices.pop(); // choose a vertex with zero in-degree - result.add(vertex); // vertex 'v' is next in topological order - // "remove" vertex 'v' by updating its neighbors - for (V neighbor : neighbors.get(vertex)) { - degree.put(neighbor, degree.get(neighbor) - 1); - // remember any vertices that now have zero in-degree - if (degree.get(neighbor) == 0) { - zeroVertices.push(neighbor); - } - } - } - - // check that we have used the entire graph (if not, there was a cycle) - if (result.size() != neighbors.size()) { - return null; - } - - return result; - } - - /** - * Report (as a List) the reverse topological sort of the vertices; null for no such sort. - */ - public List reverseTopologicalSort() { - List list = topologicalSort(); - if (list == null) { - return null; - } - - Collections.reverse(list); - - return list; - } - - /** - * True if graph is a dag (directed acyclic graph). - */ - public boolean isDag () { - return topologicalSort() != null; - } - - /** - * String representation of graph. - */ - @Override - public String toString() { - StringBuffer sb = new StringBuffer(); - for (V vertex : neighbors.keySet()) { - sb.append("\n " + vertex + " -> " + neighbors.get(vertex)); - } - - return sb.toString(); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFileFilter.java deleted file mode 100644 index 05cce35..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/DirectoryFileFilter.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.io.File; -import java.io.FileFilter; - -/** - * Filter accepts files that are directories. - * - * @author Decebal Suiu - */ -public class DirectoryFileFilter implements FileFilter { - - @Override - public boolean accept(File file) { - return file.isDirectory(); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFileFilter.java deleted file mode 100644 index 8146f5f..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ExtensionFileFilter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.io.File; -import java.io.FileFilter; - -/** - * Filter accepts any file ending in extension. The case of the filename is ignored. - * - * @author Decebal Suiu - */ -public class ExtensionFileFilter implements FileFilter { - - private String extension; - - public ExtensionFileFilter(String extension) { - this.extension = extension; - } - - @Override - public boolean accept(File file) { - // perform a case insensitive check. - return file.getName().toUpperCase().endsWith(extension.toUpperCase()); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java deleted file mode 100644 index 6ce7f42..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/FileUtils.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileFilter; -import java.io.FileReader; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; - -/** - * @author Decebal Suiu - */ -public class FileUtils { - - private static final Logger log = LoggerFactory.getLogger(FileUtils.class); - - public static List readLines(Path path, boolean ignoreComments) throws IOException { - File file = path.toFile(); - if (!file.exists() || !file.isFile()) { - return new ArrayList<>(); - } - - List lines = new ArrayList<>(); - - try (BufferedReader reader = new BufferedReader(new FileReader(file))) { - String line; - while ((line = reader.readLine()) != null) { - if (ignoreComments && !line.startsWith("#") && !lines.contains(line)) { - lines.add(line); - } - } - } - - return lines; - } - - public static void writeLines(Collection lines, File file) throws IOException { - Files.write(file.toPath(), lines, StandardCharsets.UTF_8); - } - - /** - * Delete a file or recursively delete a folder, do not follow symlinks. - * - * @param path the file or folder to delete - * @throws IOException if something goes wrong - */ - public static void delete(Path path) throws IOException { - Files.walkFileTree(path, new SimpleFileVisitor() { - - @Override - public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException { - if (!attrs.isSymbolicLink()) { - Files.delete(path); - } - - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { - Files.delete(dir); - - return FileVisitResult.CONTINUE; - } - - }); - } - - public static List getJars(Path folder) { - List bucket = new ArrayList<>(); - getJars(bucket, folder); - - return bucket; - } - - private static void getJars(final List bucket, Path folder) { - FileFilter jarFilter = new JarFileFilter(); - FileFilter directoryFilter = new DirectoryFileFilter(); - - if (Files.exists(folder) && Files.isDirectory(folder)) { - File[] jars = folder.toFile().listFiles(jarFilter); - for (int i = 0; (jars != null) && (i < jars.length); ++i) { - bucket.add(jars[i]); - } - - File[] directories = folder.toFile().listFiles(directoryFilter); - for (int i = 0; (directories != null) && (i < directories.length); ++i) { - File directory = directories[i]; - getJars(bucket, directory.toPath()); - } - } - } - - /** - * Finds a path with various endings or null if not found. - * - * @param basePath the base name - * @param endings a list of endings to search for - * @return new path or null if not found - */ - public static Path findWithEnding(Path basePath, String... endings) { - for (String ending : endings) { - Path newPath = basePath.resolveSibling(basePath.getFileName() + ending); - if (Files.exists(newPath)) { - return newPath; - } - } - - return null; - } - - /** - * Delete a file (not recursively) and ignore any errors. - * - * @param path the path to delete - */ - public static void optimisticDelete(Path path) { - if (path == null) { - return; - } - - try { - Files.delete(path); - } catch (IOException ignored) { } - } - - /** - * Unzip a zip file in a directory that has the same name as the zip file. - * For example if the zip file is {@code my-plugin.zip} then the resulted directory - * is {@code my-plugin}. - * - * @param filePath the file to evaluate - * @return Path of unzipped folder or original path if this was not a zip file - * @throws IOException on error - */ - public static Path expandIfZip(Path filePath) throws IOException { - if (!isZipFile(filePath)) { - return filePath; - } - - FileTime pluginZipDate = Files.getLastModifiedTime(filePath); - String fileName = filePath.getFileName().toString(); - Path pluginDirectory = filePath.resolveSibling(fileName.substring(0, fileName.lastIndexOf("."))); - - if (!Files.exists(pluginDirectory) || pluginZipDate.compareTo(Files.getLastModifiedTime(pluginDirectory)) > 0) { - // do not overwrite an old version, remove it - if (Files.exists(pluginDirectory)) { - FileUtils.delete(pluginDirectory); - } - - // create root for plugin - Files.createDirectories(pluginDirectory); - - // expand '.zip' file - Unzip unzip = new Unzip(); - unzip.setSource(filePath.toFile()); - unzip.setDestination(pluginDirectory.toFile()); - unzip.extract(); - log.info("Expanded plugin zip '{}' in '{}'", filePath.getFileName(), pluginDirectory.getFileName()); - } - - return pluginDirectory; - } - - /** - * Return true only if path is a zip file. - * - * @param path to a file/dir - * @return true if file with {@code .zip} ending - */ - public static boolean isZipFile(Path path) { - return Files.isRegularFile(path) && path.toString().toLowerCase().endsWith(".zip"); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/HiddenFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/HiddenFilter.java deleted file mode 100644 index f18372a..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/HiddenFilter.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.io.File; -import java.io.FileFilter; - -/** - * Filter that only accepts hidden files. - * - * @author decebal.suiu - */ -public class HiddenFilter implements FileFilter { - - @Override - public boolean accept(File file) { - return file.isHidden(); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFileFilter.java deleted file mode 100644 index d947c34..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/JarFileFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -/** - * File filter that accepts all files ending with .JAR. - * This filter is case insensitive. - * - * @author Decebal Suiu - */ -public class JarFileFilter extends ExtensionFileFilter { - - /** - * The extension that this filter will search for. - */ - private static final String JAR_EXTENSION = ".JAR"; - - public JarFileFilter() { - super(JAR_EXTENSION); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/NameFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/NameFileFilter.java deleted file mode 100644 index 9071882..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/NameFileFilter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.io.File; -import java.io.FileFilter; - -/** - * Filter accepts any file with this name. The case of the filename is ignored. - * - * @author Decebal Suiu - */ -public class NameFileFilter implements FileFilter { - - private String name; - - public NameFileFilter(String name) { - this.name = name; - } - - @Override - public boolean accept(File file) { - // perform a case insensitive check. - return file.getName().equalsIgnoreCase(name); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/NotFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/NotFileFilter.java deleted file mode 100644 index c30ce40..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/NotFileFilter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.io.File; -import java.io.FileFilter; - -/** - * This filter produces a logical NOT of the filters specified. - * - * @author Decebal Suiu - */ -public class NotFileFilter implements FileFilter { - - private FileFilter filter; - - public NotFileFilter(FileFilter filter) { - this.filter = filter; - } - - @Override - public boolean accept(File file) { - return !filter.accept(file); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/OrFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/OrFileFilter.java deleted file mode 100644 index 33e3e83..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/OrFileFilter.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2013 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.io.File; -import java.io.FileFilter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * This filter providing conditional OR logic across a list of - * file filters. This filter returns true if one filter in the - * list return true. Otherwise, it returns false. - * Checking of the file filter list stops when the first filter returns - * true. - * - * @author Decebal Suiu - */ -public class OrFileFilter implements FileFilter { - - /** The list of file filters. */ - private List fileFilters; - - public OrFileFilter() { - this(new ArrayList()); - } - - public OrFileFilter(FileFilter... fileFilters) { - this(Arrays.asList(fileFilters)); - } - - public OrFileFilter(List fileFilters) { - this.fileFilters = new ArrayList<>(fileFilters); - } - - public OrFileFilter addFileFilter(FileFilter fileFilter) { - fileFilters.add(fileFilter); - - return this; - } - - public List getFileFilters() { - return Collections.unmodifiableList(fileFilters); - } - - public boolean removeFileFilter(FileFilter fileFilter) { - return fileFilters.remove(fileFilter); - } - - public void setFileFilters(List fileFilters) { - this.fileFilters = new ArrayList<>(fileFilters); - } - - @Override - public boolean accept(File file) { - if (this.fileFilters.size() == 0) { - return true; - } - - for (FileFilter fileFilter : this.fileFilters) { - if (fileFilter.accept(file)) { - return true; - } - } - - return false; - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/StringUtils.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/StringUtils.java deleted file mode 100644 index 62afecc..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/StringUtils.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -/** - * @author Decebal Suiu - */ -public class StringUtils { - - public static boolean isEmpty(String str) { - return (str == null) || str.isEmpty(); - } - - public static boolean isNotEmpty(String str) { - return !isEmpty(str); - } - - /** - * Format the string. Replace "{}" with %s and format the string using {@link String#format(String, Object...)}. - */ - public static String format(String str, Object... args) { - str = str.replaceAll("\\{}", "%s"); - - return String.format(str, args); - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java deleted file mode 100644 index bcf6c9d..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/Unzip.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** - * This class extracts the content of the plugin zip into a directory. - * It's a class for only the internal use. - * - * @author Decebal Suiu - */ -public class Unzip { - - private static final Logger log = LoggerFactory.getLogger(Unzip.class); - - /** - * Holds the destination directory. - * File will be unzipped into the destination directory. - */ - private File destination; - - /** - * Holds path to zip file. - */ - private File source; - - public Unzip() { - } - - public Unzip(File source, File destination) { - this.source = source; - this.destination = destination; - } - - public void setSource(File source) { - this.source = source; - } - - public void setDestination(File destination) { - this.destination = destination; - } - - public void extract() throws IOException { - log.debug("Extract content of '{}' to '{}'", source, destination); - - // delete destination file if exists - if (destination.exists() && destination.isDirectory()) { - FileUtils.delete(destination.toPath()); - } - - try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(source))) { - ZipEntry zipEntry; - while ((zipEntry = zipInputStream.getNextEntry()) != null) { - try { - File file = new File(destination, zipEntry.getName()); - - // create intermediary directories - sometimes zip don't add them - File dir = new File(file.getParent()); - dir.mkdirs(); - - if (zipEntry.isDirectory()) { - file.mkdirs(); - } else { - byte[] buffer = new byte[1024]; - int length; - try (FileOutputStream fos = new FileOutputStream(file)) { - while ((length = zipInputStream.read(buffer)) >= 0) { - fos.write(buffer, 0, length); - } - } - } - } catch (FileNotFoundException e) { - log.error("File '{}' not found", zipEntry.getName()); - } - } - } - } - -} diff --git a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFileFilter.java b/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFileFilter.java deleted file mode 100644 index 08913a0..0000000 --- a/pf4j/src/main/java/ro/fortsoft/pf4j/util/ZipFileFilter.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -/** - * File filter that accepts all files ending with .ZIP. - * This filter is case insensitive. - * - * @author Decebal Suiu - */ -public class ZipFileFilter extends ExtensionFileFilter { - - /** - * The extension that this filter will search for. - */ - private static final String ZIP_EXTENSION = ".ZIP"; - - public ZipFileFilter() { - super(ZIP_EXTENSION); - } - -} diff --git a/pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor index c616e6e..164832f 100644 --- a/pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor +++ b/pf4j/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -1 +1 @@ -ro.fortsoft.pf4j.processor.ExtensionAnnotationProcessor +org.pf4j.processor.ExtensionAnnotationProcessor diff --git a/pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java b/pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java new file mode 100644 index 0000000..9e1c40e --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/AbstractExtensionFinderTest.java @@ -0,0 +1,195 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.After; +import org.junit.Before; +import org.junit.Test; +import org.pf4j.plugin.FailTestPlugin; +import org.pf4j.plugin.TestExtensionPoint; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Mario Franco + */ +public class AbstractExtensionFinderTest { + + private PluginManager pluginManager; + + @Before + public void setUp() { + PluginWrapper pluginStarted = mock(PluginWrapper.class); + when(pluginStarted.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); + when(pluginStarted.getPluginState()).thenReturn(PluginState.STARTED); + + PluginWrapper pluginStopped = mock(PluginWrapper.class); + when(pluginStopped.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); + 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.getExtensionFactory()).thenReturn(new DefaultExtensionFactory()); + } + + @After + public void tearDown() { + pluginManager = null; + } + + /** + * Test of {@link AbstractExtensionFinder#find(Class)}. + */ + @Test + public void testFindFailType() { + ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { + + @Override + public Map> readPluginsStorages() { + return Collections.emptyMap(); + } + + @Override + public Map> readClasspathStorages() { + return Collections.emptyMap(); + } + + }; + List> list = instance.find(FailTestPlugin.class); + assertEquals(0, list.size()); + } + + /** + * Test of {@link AbstractExtensionFinder#find(Class)}. + */ + @Test + public void testFindFromClasspath() { + ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { + + @Override + public Map> readPluginsStorages() { + return Collections.emptyMap(); + } + + @Override + public Map> readClasspathStorages() { + Map> entries = new LinkedHashMap<>(); + + Set bucket = new HashSet<>(); + bucket.add("org.pf4j.plugin.TestExtension"); + bucket.add("org.pf4j.plugin.FailTestExtension"); + entries.put(null, bucket); + + return entries; + } + + }; + + List> list = instance.find(TestExtensionPoint.class); + assertEquals(2, list.size()); + } + + /** + * Test of {@link AbstractExtensionFinder#find(Class, String)}. + */ + @Test + public void testFindFromPlugin() { + ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { + + @Override + public Map> readPluginsStorages() { + Map> entries = new LinkedHashMap<>(); + + Set bucket = new HashSet<>(); + bucket.add("org.pf4j.plugin.TestExtension"); + bucket.add("org.pf4j.plugin.FailTestExtension"); + entries.put("plugin1", bucket); + bucket = new HashSet<>(); + bucket.add("org.pf4j.plugin.TestExtension"); + entries.put("plugin2", bucket); + + return entries; + } + + @Override + public Map> readClasspathStorages() { + return Collections.emptyMap(); + } + + }; + + List> list = instance.find(TestExtensionPoint.class); + assertEquals(2, list.size()); + + list = instance.find(TestExtensionPoint.class, "plugin1"); + assertEquals(2, list.size()); + + list = instance.find(TestExtensionPoint.class, "plugin2"); + assertEquals(0, list.size()); + } + + /** + * Test of {@link AbstractExtensionFinder#findClassNames(String)}. + */ + @Test + public void testFindClassNames() { + ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { + + @Override + public Map> readPluginsStorages() { + Map> entries = new LinkedHashMap<>(); + + Set bucket = new HashSet<>(); + bucket.add("org.pf4j.plugin.TestExtension"); + entries.put("plugin1", bucket); + + return entries; + } + + @Override + public Map> readClasspathStorages() { + Map> entries = new LinkedHashMap<>(); + + Set bucket = new HashSet<>(); + bucket.add("org.pf4j.plugin.TestExtension"); + bucket.add("org.pf4j.plugin.FailTestExtension"); + entries.put(null, bucket); + + return entries; + } + + }; + + Set result = instance.findClassNames(null); + assertEquals(2, result.size()); + + result = instance.findClassNames("plugin1"); + assertEquals(1, result.size()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java b/pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java new file mode 100644 index 0000000..01822d0 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/DefaultExtensionFactoryTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.Test; +import org.pf4j.plugin.TestExtension; +import org.pf4j.plugin.FailTestExtension; + +import static org.junit.Assert.*; + +/** + * + * @author Mario Franco + */ +public class DefaultExtensionFactoryTest { + + /** + * Test of create method, of class DefaultExtensionFactory. + */ + @Test + public void testCreate() { + DefaultExtensionFactory instance = new DefaultExtensionFactory(); + Object result = instance.create(TestExtension.class); + assertNotNull(result); + } + + /** + * Test of create method, of class DefaultExtensionFactory. + */ + @Test + public void testCreateFailConstructor() { + DefaultExtensionFactory instance = new DefaultExtensionFactory(); + Object result = instance.create(FailTestExtension.class); + assertNull(result); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java new file mode 100644 index 0000000..5735328 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginFactoryTest.java @@ -0,0 +1,109 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.Test; +import org.pf4j.plugin.FailTestPlugin; +import org.pf4j.plugin.TestPlugin; +import org.pf4j.plugin.AnotherFailTestPlugin; + +import static org.hamcrest.CoreMatchers.instanceOf; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * + * @author Mario Franco + */ +public class DefaultPluginFactoryTest { + + /** + * Test of create method, of class DefaultPluginFactory. + */ + @Test + public void testCreate() { + PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); + when(pluginDescriptor.getPluginClass()).thenReturn(TestPlugin.class.getName()); + + PluginWrapper pluginWrapper = mock(PluginWrapper.class); + when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); + when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); + + DefaultPluginFactory instance = new DefaultPluginFactory(); + + Plugin result = instance.create(pluginWrapper); + assertNotNull(result); + assertThat(result, instanceOf(TestPlugin.class)); + } + + /** + * Test of create method, of class DefaultPluginFactory. + */ + @Test + public void testCreateFail() { + PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); + when(pluginDescriptor.getPluginClass()).thenReturn(FailTestPlugin.class.getName()); + + PluginWrapper pluginWrapper = mock(PluginWrapper.class); + when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); + when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); + + DefaultPluginFactory instance = new DefaultPluginFactory(); + + Plugin result = instance.create(pluginWrapper); + assertNull(result); + } + + /** + * Test of create method, of class DefaultPluginFactory. + */ + @Test + public void testCreateFailNotFound() { + PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); + when(pluginDescriptor.getPluginClass()).thenReturn("org.pf4j.plugin.NotFoundTestPlugin"); + + PluginWrapper pluginWrapper = mock(PluginWrapper.class); + when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); + when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); + + DefaultPluginFactory instance = new DefaultPluginFactory(); + + Plugin result = instance.create(pluginWrapper); + assertNull(result); + } + + /** + * Test of create method, of class DefaultPluginFactory. + */ + @Test + public void testCreateFailConstructor() { + PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); + when(pluginDescriptor.getPluginClass()).thenReturn(AnotherFailTestPlugin.class.getName()); + + PluginWrapper pluginWrapper = mock(PluginWrapper.class); + when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); + when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); + + DefaultPluginFactory instance = new DefaultPluginFactory(); + + Plugin result = instance.create(pluginWrapper); + assertNull(result); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java new file mode 100644 index 0000000..03f8496 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginManagerTest.java @@ -0,0 +1,108 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Files; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class DefaultPluginManagerTest { + + private PluginDescriptor pd1 = null; + private DefaultPluginManager pluginManager = new DefaultPluginManager(); + private PluginWrapper pw1; + + @Before + public void init() throws IOException { + pd1 = new PluginDescriptor(); + pd1.setPluginId("myPlugin"); + pd1.setPluginVersion("1.2.3"); + pd1.setPluginClass("foo"); + pd1.setPluginDescription("My plugin"); + pd1.setDependencies("bar, baz"); + pd1.setProvider("Me"); + pd1.setRequires("5.0.0"); + + pw1 = new PluginWrapper(pluginManager, pd1, Files.createTempDirectory("test"), getClass().getClassLoader()); + } + + @Test + public void validateOK() throws PluginException { + pluginManager.validatePluginDescriptor(pd1); + } + + @Test(expected = PluginException.class) + public void validateFailsOnId() throws PluginException { + pd1.setPluginId(""); + pluginManager.validatePluginDescriptor(pd1); + } + + @Test(expected = PluginException.class) + public void validateFailsOnVersion() throws PluginException { + pd1.setPluginVersion(null); + pluginManager.validatePluginDescriptor(pd1); + } + + @Test(expected = PluginException.class) + public void validateFailsOnClass() throws PluginException { + pd1.setPluginClass(null); + pluginManager.validatePluginDescriptor(pd1); + } + + @Test + public void isPluginValid() throws Exception { + // By default accept all since system version not given + assertTrue(pluginManager.isPluginValid(pw1)); + + pluginManager.setSystemVersion("1.0.0"); + assertFalse(pluginManager.isPluginValid(pw1)); + + pluginManager.setSystemVersion("5.0.0"); + assertTrue(pluginManager.isPluginValid(pw1)); + + pluginManager.setSystemVersion("6.0.0"); + assertTrue(pluginManager.isPluginValid(pw1)); + } + + @Test + public void isPluginValidAllowExact() throws Exception { + pluginManager.setExactVersionAllowed(true); + + // By default accept all since system version not given + assertTrue(pluginManager.isPluginValid(pw1)); + + pluginManager.setSystemVersion("1.0.0"); + assertFalse(pluginManager.isPluginValid(pw1)); + + pluginManager.setSystemVersion("5.0.0"); + assertTrue(pluginManager.isPluginValid(pw1)); + + pluginManager.setSystemVersion("6.0.0"); + assertFalse(pluginManager.isPluginValid(pw1)); + } + + @Test + public void testDefaultExactVersionAllowed() throws Exception { + assertEquals(false, pluginManager.isExactVersionAllowed()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java new file mode 100644 index 0000000..b66b3b5 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginRepositoryTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Mario Franco + * @author Decebal Suiu + */ +public class DefaultPluginRepositoryTest { + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Before + public void setUp() throws IOException { + testFolder.newFolder("plugin-1"); + // Prove that we can delete a folder with a file inside + Files.createFile(Paths.get(testFolder.getRoot().getAbsolutePath()).resolve("plugin-1").resolve("myfile")); + // Create a zip file for plugin-1 to test that it is deleted when plugin is deleted + Files.createFile(Paths.get(testFolder.getRoot().getAbsolutePath()).resolve("plugin-1.zip")); + testFolder.newFolder("plugin-2"); + testFolder.newFolder("plugin-3"); + } + + /** + * Test of {@link DefaultPluginRepository#getPluginPaths()} method. + */ + @Test + public void testGetPluginArchives() { + Path pluginsRoot = getPluginsRoot(); + + PluginRepository instance = new DefaultPluginRepository(pluginsRoot, false); + + List result = instance.getPluginPaths(); + + assertEquals(3, result.size()); + assertPathExists(result, pluginsRoot.resolve("plugin-1")); + assertPathExists(result, pluginsRoot.resolve("plugin-2")); + assertPathExists(result, pluginsRoot.resolve("plugin-3")); + } + + /** + * Test of {@link DefaultPluginRepository#deletePluginPath(Path)} method. + */ + @Test + public void testDeletePluginPath() { + Path pluginsRoot = getPluginsRoot(); + + PluginRepository instance = new DefaultPluginRepository(pluginsRoot, false); + + assertTrue(Files.exists(pluginsRoot.resolve("plugin-1.zip"))); + assertTrue(instance.deletePluginPath(pluginsRoot.resolve("plugin-1"))); + assertFalse(Files.exists(pluginsRoot.resolve("plugin-1.zip"))); + assertTrue(instance.deletePluginPath(pluginsRoot.resolve("plugin-3"))); + assertFalse(instance.deletePluginPath(pluginsRoot.resolve("plugin-4"))); + + List result = instance.getPluginPaths(); + + assertEquals(1, result.size()); + assertEquals(pluginsRoot.relativize(result.get(0)).toString(), "plugin-2"); + } + + private void assertPathExists(List paths, Path path) { + assertTrue("The directory must contain the file " + path, paths.contains(path)); + } + + private Path getPluginsRoot() { + return testFolder.getRoot().toPath(); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java b/pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java new file mode 100644 index 0000000..da2752e --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/DefaultPluginStatusProviderTest.java @@ -0,0 +1,165 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.pf4j.util.FileUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * @author Mario Franco + * @author Decebal Suiu + */ +public class DefaultPluginStatusProviderTest { + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + /** + * Test of isPluginDisabled method, of class DefaultPluginStatusProvider. + */ + @Test + public void testIsPluginDisabled() throws IOException { + createEnabledFile(); + createDisabledFile(); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); + + assertFalse(instance.isPluginDisabled("plugin-1")); + assertTrue(instance.isPluginDisabled("plugin-2")); + assertTrue(instance.isPluginDisabled("plugin-3")); + } + + /** + * Test of isPluginDisabled method, of class DefaultPluginStatusProvider. + */ + @Test + public void testIsPluginDisabledWithEnableEmpty() throws IOException { + createDisabledFile(); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); + + assertFalse(instance.isPluginDisabled("plugin-1")); + assertTrue(instance.isPluginDisabled("plugin-2")); + assertFalse(instance.isPluginDisabled("plugin-3")); + } + + /** + * Test of disablePlugin method, of class DefaultPluginStatusProvider. + */ + @Test + public void testDisablePlugin() throws IOException { + createEnabledFile(); + createDisabledFile(); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); + + assertTrue(instance.disablePlugin("plugin-1")); + assertTrue(instance.isPluginDisabled("plugin-1")); + assertTrue(instance.isPluginDisabled("plugin-2")); + assertTrue(instance.isPluginDisabled("plugin-3")); + } + + /** + * Test of disablePlugin method, of class DefaultPluginStatusProvider. + */ + @Test + public void testDisablePluginWithEnableEmpty() throws IOException { + createDisabledFile(); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); + + assertTrue(instance.disablePlugin("plugin-1")); + assertTrue(instance.isPluginDisabled("plugin-1")); + assertTrue(instance.isPluginDisabled("plugin-2")); + assertFalse(instance.isPluginDisabled("plugin-3")); + } + + /** + * Test of enablePlugin method, of class DefaultPluginStatusProvider. + */ + @Test + public void testEnablePlugin() throws IOException { + createEnabledFile(); + + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); + + assertTrue(instance.enablePlugin("plugin-2")); + assertFalse(instance.isPluginDisabled("plugin-1")); + assertFalse(instance.isPluginDisabled("plugin-2")); + assertTrue(instance.isPluginDisabled("plugin-3")); + } + + /** + * Test of enablePlugin method, of class DefaultPluginStatusProvider. + */ + @Test + public void testEnablePluginWithEnableEmpty() { + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); + + assertTrue(instance.enablePlugin("plugin-2")); + assertFalse(instance.isPluginDisabled("plugin-1")); + assertFalse(instance.isPluginDisabled("plugin-2")); + assertFalse(instance.isPluginDisabled("plugin-3")); + } + + /** + * Test of disablePlugin method without a disabled.txt file. + */ + @Test + public void testDisablePluginWithoutDisabledFile() throws IOException { + PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); + + assertFalse(instance.isPluginDisabled("plugin-1")); + assertTrue(instance.disablePlugin("plugin-1")); + assertTrue(instance.isPluginDisabled("plugin-1")); + } + + private void createDisabledFile() throws IOException { + List plugins = new ArrayList<>(); + plugins.add("plugin-2"); + + writeLines(plugins, "disabled.txt"); + } + + private void createEnabledFile() throws IOException { + List plugins = new ArrayList<>(); + plugins.add("plugin-1"); + plugins.add("plugin-2"); + + writeLines(plugins, "enabled.txt"); + } + + private void writeLines(List lines, String fileName) throws IOException { + File file = testFolder.newFile(fileName); + FileUtils.writeLines(lines, file); + } + + private Path getPluginsRoot() { + return testFolder.getRoot().toPath(); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java b/pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java new file mode 100644 index 0000000..aaa5fa9 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/DefaultVersionManagerTest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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 com.github.zafarkhaja.semver.ParseException; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author Decebal Suiu + */ +public class DefaultVersionManagerTest { + + private VersionManager versionManager; + + @Before + public void init() { + versionManager = new DefaultVersionManager(); + } + + @Test + public void satisfies() { + assertFalse(versionManager.satisfies(">2.0.0", "1.4.3")); // simple + assertTrue(versionManager.satisfies(">=1.4.0 & <1.6.0", "1.4.3")); // range + } + + @Test(expected = IllegalArgumentException.class) + public void nullOrEmptyVersion() { + assertFalse(versionManager.satisfies(">2.0.0", null)); + } + + @Test(expected = ParseException.class) + public void invalidVersion() { + assertFalse(versionManager.satisfies(">2.0.0", "1.0")); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/DependencyResolverTest.java b/pf4j/src/test/java/org/pf4j/DependencyResolverTest.java new file mode 100644 index 0000000..44d2c93 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/DependencyResolverTest.java @@ -0,0 +1,142 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @author Decebal Suiu + */ +public class DependencyResolverTest { + + private DependencyResolver resolver; + + @Before + public void init() { + VersionManager versionManager = new DefaultVersionManager(); + resolver = new DependencyResolver(versionManager); + } + + @Test + public void sortedPlugins() { + // create incomplete plugin descriptor (ignore some attributes) + PluginDescriptor pd1 = new PluginDescriptor() + .setPluginId("p1") + .setDependencies("p2"); + + PluginDescriptor pd2 = new PluginDescriptor() + .setPluginId("p2") + .setPluginVersion("0.0.0"); // needed in "checkDependencyVersion" method + + List plugins = new ArrayList<>(); + plugins.add(pd1); + plugins.add(pd2); + + DependencyResolver.Result result = resolver.resolve(plugins); + + assertTrue(result.getNotFoundDependencies().isEmpty()); + assertEquals(result.getSortedPlugins(), Arrays.asList("p2", "p1")); + } + + @Test + public void notFoundDependencies() throws Exception { + PluginDescriptor pd1 = new PluginDescriptor() + .setPluginId("p1") + .setDependencies("p2, p3"); + + List plugins = new ArrayList<>(); + plugins.add(pd1); + + DependencyResolver.Result result = resolver.resolve(plugins); + + assertFalse(result.getNotFoundDependencies().isEmpty()); + assertEquals(result.getNotFoundDependencies(), Arrays.asList("p2", "p3")); + } + + @Test + public void cyclicDependencies() { + PluginDescriptor pd1 = new PluginDescriptor() + .setPluginId("p1") + .setPluginVersion("0.0.0") + .setDependencies("p2"); + + PluginDescriptor pd2 = new PluginDescriptor() + .setPluginId("p2") + .setPluginVersion("0.0.0") + .setDependencies("p3"); + + PluginDescriptor pd3 = new PluginDescriptor() + .setPluginId("p3") + .setPluginVersion("0.0.0") + .setDependencies("p1"); + + List plugins = new ArrayList<>(); + plugins.add(pd1); + plugins.add(pd2); + plugins.add(pd3); + + DependencyResolver.Result result = resolver.resolve(plugins); + + assertTrue(result.hasCyclicDependency()); + } + + @Test + public void wrongDependencyVersion() { + PluginDescriptor pd1 = new PluginDescriptor() + .setPluginId("p1") +// .setDependencies("p2@2.0.0"); // simple version + .setDependencies("p2@>=1.5.0 & <1.6.0"); // range version + + PluginDescriptor pd2 = new PluginDescriptor() + .setPluginId("p2") + .setPluginVersion("1.4.0"); + + List plugins = new ArrayList<>(); + plugins.add(pd1); + plugins.add(pd2); + + DependencyResolver.Result result = resolver.resolve(plugins); + + assertFalse(result.getWrongVersionDependencies().isEmpty()); + } + + @Test + public void goodDependencyVersion() { + PluginDescriptor pd1 = new PluginDescriptor() + .setPluginId("p1") + .setDependencies("p2@2.0.0"); + + PluginDescriptor pd2 = new PluginDescriptor() + .setPluginId("p2") + .setPluginVersion("2.0.0"); + + List plugins = new ArrayList<>(); + plugins.add(pd1); + plugins.add(pd2); + + DependencyResolver.Result result = resolver.resolve(plugins); + + assertTrue(result.getWrongVersionDependencies().isEmpty()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java b/pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java new file mode 100644 index 0000000..c2e7c0e --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/ExtensionAnnotationProcessorTest.java @@ -0,0 +1,42 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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.Test; +import org.pf4j.processor.ExtensionAnnotationProcessor; + +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author Mario Franco + */ +public class ExtensionAnnotationProcessorTest { + + /** + * Test of {@link ExtensionAnnotationProcessor#getSupportedAnnotationTypes()}. + */ + @Test + public void testGetSupportedAnnotationTypes() { + ExtensionAnnotationProcessor instance = new ExtensionAnnotationProcessor(); + Set result = instance.getSupportedAnnotationTypes(); + assertEquals(1, result.size()); + assertTrue(result.contains(Extension.class.getName())); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java b/pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java new file mode 100644 index 0000000..14daa6e --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/LegacyExtensionStorageTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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.Test; +import org.pf4j.processor.LegacyExtensionStorage; + +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.HashSet; +import java.util.Set; + +import static org.junit.Assert.*; + +/** + * @author Decebal Suiu + */ +public class LegacyExtensionStorageTest { + + /** + * Test of {@link LegacyExtensionStorage#read(Reader, Set)}. + */ + @Test + public void testRead() throws IOException { + Reader reader = new StringReader( + "# comment\n" + + "org.pf4j.demo.hello.HelloPlugin$HelloGreeting\n" + + "org.pf4j.demo.welcome.WelcomePlugin$WelcomeGreeting\n" + + "org.pf4j.demo.welcome.OtherGreeting\n"); + + Set entries = new HashSet<>(); + LegacyExtensionStorage.read(reader, entries); + assertEquals(3, entries.size()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java b/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java new file mode 100644 index 0000000..5c46d66 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/LoadPluginsTest.java @@ -0,0 +1,172 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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.Before; +import org.junit.Test; +import org.pf4j.plugin.MockPluginManager; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URI; +import java.nio.file.*; +import java.util.Collections; + +import static junit.framework.TestCase.assertNull; +import static org.junit.Assert.*; + +public class LoadPluginsTest { + + private Path tmpDir; + private MockPluginManager pluginManager; + private MockZipPlugin p1; + private MockZipPlugin p2; + private MockZipPlugin p3; + + @Before + public void setup() throws IOException { + tmpDir = Files.createTempDirectory("pf4j-test"); + tmpDir.toFile().deleteOnExit(); + p1 = new MockZipPlugin("myPlugin", "1.2.3", "my-plugin-1.2.3", "my-plugin-1.2.3.zip"); + p2 = new MockZipPlugin("myPlugin", "2.0.0", "my-plugin-2.0.0", "my-plugin-2.0.0.ZIP"); + p3 = new MockZipPlugin("other", "3.0.0", "other-3.0.0", "other-3.0.0.Zip"); + pluginManager = new MockPluginManager( + tmpDir, + new PropertiesPluginDescriptorFinder("my.properties")); + } + + @Test + public void load() throws Exception { + p1.create(); + assertTrue(Files.exists(p1.zipFile)); + assertEquals(0, pluginManager.getPlugins().size()); + pluginManager.loadPlugins(); + assertTrue(Files.exists(p1.zipFile)); + assertTrue(Files.exists(p1.unzipped)); + assertEquals(1, pluginManager.getPlugins().size()); + assertEquals(p1.id, pluginManager.idForPath(p1.unzipped)); + } + + @Test(expected = IllegalArgumentException.class) + public void loadNonExisting() throws Exception { + pluginManager.loadPlugin(Paths.get("nonexisting")); + } + + @Test + public void loadTwiceFails() throws Exception { + p1.create(); + assertNotNull(pluginManager.loadPluginFromPath(p1.zipFile)); + assertNull(pluginManager.loadPluginFromPath(p1.zipFile)); + } + + @Test + public void loadUnloadLoad() throws Exception { + p1.create(); + pluginManager.loadPlugins(); + assertEquals(1, pluginManager.getPlugins().size()); + assertTrue(pluginManager.unloadPlugin(pluginManager.idForPath(p1.unzipped))); + // duplicate check + assertNull(pluginManager.idForPath(p1.unzipped)); + // Double unload ok + assertFalse(pluginManager.unloadPlugin(pluginManager.idForPath(p1.unzipped))); + assertNotNull(pluginManager.loadPlugin(p1.unzipped)); + } + + @Test + public void upgrade() throws Exception { + p1.create(); + pluginManager.loadPlugins(); + pluginManager.startPlugins(); + assertEquals(1, pluginManager.getPlugins().size()); + assertEquals("1.2.3", pluginManager.getPlugin(p2.id).getDescriptor().getVersion()); + assertEquals(1, pluginManager.getStartedPlugins().size()); + p2.create(); + pluginManager.loadPlugins(); + pluginManager.startPlugin(p2.id); + assertEquals(1, pluginManager.getPlugins().size()); + assertEquals("2.0.0", pluginManager.getPlugin(p2.id).getDescriptor().getVersion()); + assertEquals("2.0.0", pluginManager.getStartedPlugins().get(1).getDescriptor().getVersion()); + } + + @Test + public void getRoot() throws Exception { + assertEquals(tmpDir, pluginManager.getPluginsRoot()); + } + + @Test + public void notAPlugin() throws Exception { + Path notAPlugin = tmpDir.resolve("not-a-zip"); + Files.createFile(notAPlugin); + pluginManager.loadPlugins(); + assertEquals(0, pluginManager.getPlugins().size()); + } + + @Test + public void deletePlugin() throws Exception { + p1.create(); + p3.create(); + pluginManager.loadPlugins(); + pluginManager.startPlugins(); + assertEquals(2, pluginManager.getPlugins().size()); + pluginManager.deletePlugin(p1.id); + assertEquals(1, pluginManager.getPlugins().size()); + assertFalse(Files.exists(p1.zipFile)); + assertFalse(Files.exists(p1.unzipped)); + assertTrue(Files.exists(p3.zipFile)); + assertTrue(Files.exists(p3.unzipped)); + } + + private class MockZipPlugin { + + public final String id; + public final String version; + public final String filename; + public final Path zipFile; + public final Path unzipped; + public final Path propsFile; + public final URI fileURI; + public String zipname; + + public MockZipPlugin(String id, String version, String filename, String zipname) throws IOException { + this.id = id; + this.version = version; + this.filename = filename; + this.zipname = zipname; + + zipFile = tmpDir.resolve(zipname).toAbsolutePath(); + unzipped = tmpDir.resolve(filename); + propsFile = tmpDir.resolve("my.properties"); + fileURI = URI.create("jar:file:"+zipFile.toString()); + } + + public void create() throws IOException { + try (FileSystem zipfs = FileSystems.newFileSystem(fileURI, Collections.singletonMap("create", "true"))) { + Path propsInZip = zipfs.getPath("/" + propsFile.getFileName().toString()); + BufferedWriter br = new BufferedWriter(new FileWriter(propsFile.toString())); + br.write("plugin.id=" + id); + br.newLine(); + br.write("plugin.version=" + version); + br.newLine(); + br.write("plugin.class=org.pf4j.plugin.TestPlugin"); + br.close(); + Files.move(propsFile, propsInZip); + } + } + + } + +} diff --git a/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java new file mode 100644 index 0000000..8c19733 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/ManifestPluginDescriptorFinderTest.java @@ -0,0 +1,236 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * @author Mario Franco + * @author Decebal Suiu + */ +public class ManifestPluginDescriptorFinderTest { + + private VersionManager versionManager; + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Before + public void setUp() throws IOException { + Charset charset = Charset.forName("UTF-8"); + + Path pluginPath = testFolder.newFolder("test-plugin-1", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin1Manifest(), charset); + + pluginPath = testFolder.newFolder("test-plugin-2", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin2Manifest(), charset); + + // empty plugin + testFolder.newFolder("test-plugin-3"); + + // no plugin class + pluginPath = testFolder.newFolder("test-plugin-4", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin4Manifest(), charset); + + // no plugin version + pluginPath = testFolder.newFolder("test-plugin-5", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin5Manifest(), charset); + + // no plugin id + pluginPath = testFolder.newFolder("test-plugin-6", "classes", "META-INF").toPath(); + Files.write(pluginPath.resolve("extensions.idx"), "org.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); + Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin6Manifest(), charset); + + versionManager = new DefaultVersionManager(); + } + + /** + * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. + */ + @Test + public void testFind() throws Exception { + PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + + PluginDescriptor plugin1 = instance.find(getPluginsRoot().resolve("test-plugin-1")); + PluginDescriptor plugin2 = instance.find(getPluginsRoot().resolve("test-plugin-2")); + + assertEquals("test-plugin-1", plugin1.getPluginId()); + assertEquals("Test Plugin 1", plugin1.getPluginDescription()); + assertEquals("org.pf4j.plugin.TestPlugin", plugin1.getPluginClass()); + assertEquals("0.0.1", plugin1.getVersion()); + assertEquals("Decebal Suiu", plugin1.getProvider()); + assertEquals(2, plugin1.getDependencies().size()); + assertEquals("test-plugin-2", plugin1.getDependencies().get(0).getPluginId()); + assertEquals("test-plugin-3", plugin1.getDependencies().get(1).getPluginId()); + assertEquals("~1.0", plugin1.getDependencies().get(1).getPluginVersionSupport()); + assertEquals("Apache-2.0", plugin1.getLicense()); + assertTrue(versionManager.satisfies(plugin1.getRequires(), "1.0.0")); + + assertEquals("test-plugin-2", plugin2.getPluginId()); + assertEquals("", plugin2.getPluginDescription()); + assertEquals("org.pf4j.plugin.TestPlugin", plugin2.getPluginClass()); + assertEquals("0.0.1", plugin2.getVersion()); + assertEquals("Decebal Suiu", plugin2.getProvider()); + assertEquals(0, plugin2.getDependencies().size()); + assertTrue(versionManager.satisfies(plugin2.getRequires(), "1.0.0")); + } + + /** + * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. + */ + @Test(expected = PluginException.class) + public void testFindNotFound() throws Exception { + PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + instance.find(getPluginsRoot().resolve("test-plugin-3")); + } + + private List getPlugin1Manifest() { + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + + "Implementation-Title: Test Plugin #1\n" + + "Implementation-Version: 0.10.0-SNAPSHOT\n" + + "Archiver-Version: Plexus Archiver\n" + + "Built-By: Mario Franco\n" + + "Specification-Title: Test Plugin #1\n" + + "Implementation-Vendor-Id: org.pf4j.demo\n" + + "Plugin-Version: 0.0.1\n" + + "Plugin-Id: test-plugin-1\n" + + "Plugin-Description: Test Plugin 1\n" + + "Plugin-Provider: Decebal Suiu\n" + + "Plugin-Class: org.pf4j.plugin.TestPlugin\n" + + "Plugin-Dependencies: test-plugin-2,test-plugin-3@~1.0\n" + + "Plugin-Requires: *\n" + + "Plugin-License: Apache-2.0\n" + + "Created-By: Apache Maven 3.0.5\n" + + "Build-Jdk: 1.8.0_45\n" + + "Specification-Version: 0.10.0-SNAPSHOT\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private List getPlugin2Manifest() { + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + + "Plugin-Dependencies: \n" + + "Implementation-Title: Test Plugin #2\n" + + "Implementation-Version: 0.10.0-SNAPSHOT\n" + + "Archiver-Version: Plexus Archiver\n" + + "Built-By: Mario Franco\n" + + "Specification-Title: Test Plugin #2\n" + + "Implementation-Vendor-Id: org.pf4j.demo\n" + + "Plugin-Version: 0.0.1\n" + + "Plugin-Id: test-plugin-2\n" + + "Plugin-Provider: Decebal Suiu\n" + + "Plugin-Class: org.pf4j.plugin.TestPlugin\n" + + "Created-By: Apache Maven 3.0.5\n" + + "Build-Jdk: 1.8.0_45\n" + + "Specification-Version: 0.10.0-SNAPSHOT\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private List getPlugin4Manifest() { + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + + "Implementation-Title: Test Plugin #4\n" + + "Implementation-Version: 0.10.0-SNAPSHOT\n" + + "Archiver-Version: Plexus Archiver\n" + + "Built-By: Mario Franco\n" + + "Specification-Title: Test Plugin #4\n" + + "Implementation-Vendor-Id: org.pf4j.demo\n" + + "Plugin-Version: 0.0.1\n" + + "Plugin-Id: test-plugin-2\n" + + "Plugin-Provider: Decebal Suiu\n" + + "Created-By: Apache Maven 3.0.5\n" + + "Build-Jdk: 1.8.0_45\n" + + "Specification-Version: 0.10.0-SNAPSHOT\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private List getPlugin5Manifest() { + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + + "Implementation-Title: Test Plugin #5\n" + + "Implementation-Version: 0.10.0-SNAPSHOT\n" + + "Archiver-Version: Plexus Archiver\n" + + "Built-By: Mario Franco\n" + + "Specification-Title: Test Plugin #5\n" + + "Implementation-Vendor-Id: org.pf4j.demo\n" + + "Plugin-Id: test-plugin-2\n" + + "Plugin-Provider: Decebal Suiu\n" + + "Plugin-Class: org.pf4j.plugin.TestPlugin\n" + + "Created-By: Apache Maven 3.0.5\n" + + "Build-Jdk: 1.8.0_45\n" + + "Specification-Version: 0.10.0-SNAPSHOT\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private List getPlugin6Manifest() { + String[] lines = new String[] { + "Manifest-Version: 1.0\n" + + "Implementation-Title: Test Plugin #6\n" + + "Implementation-Version: 0.10.0-SNAPSHOT\n" + + "Archiver-Version: Plexus Archiver\n" + + "Built-By: Mario Franco\n" + + "Specification-Title: Test Plugin #6\n" + + "Implementation-Vendor-Id: org.pf4j.demo\n" + + "Plugin-Provider: Decebal Suiu\n" + + "Plugin-Class: org.pf4j.plugin.TestPlugin\n" + + "Created-By: Apache Maven 3.0.5\n" + + "Build-Jdk: 1.8.0_45\n" + + "Specification-Version: 0.10.0-SNAPSHOT\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private Path getPluginsRoot() { + return testFolder.getRoot().toPath(); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/PluginDependencyTest.java b/pf4j/src/test/java/org/pf4j/PluginDependencyTest.java new file mode 100644 index 0000000..f65ee51 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/PluginDependencyTest.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015 Mario Franco. + * + * 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.Test; + +import static org.junit.Assert.*; + +/** + * @author Mario Franco + */ +public class PluginDependencyTest { + + /** + * Test of getPluginId method, of class PluginDependency. + */ + @Test + public void testPluginDependecy() { + PluginDependency instance = new PluginDependency("test"); + assertEquals("test", instance.getPluginId()); + assertEquals("*", instance.getPluginVersionSupport()); + + instance = new PluginDependency("test@"); + assertEquals("test", instance.getPluginId()); + assertEquals("*", instance.getPluginVersionSupport()); + + instance = new PluginDependency("test@1.0"); + assertEquals("test", instance.getPluginId()); + assertEquals("1.0", instance.getPluginVersionSupport()); + assertEquals("PluginDependency [pluginId=test, pluginVersionSupport=1.0]", instance.toString()); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java b/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java new file mode 100644 index 0000000..e308f59 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/PropertiesPluginDescriptorFinderTest.java @@ -0,0 +1,181 @@ +/* + * Copyright 2012 Decebal Suiu + * + * 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.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import static org.junit.Assert.*; + +public class PropertiesPluginDescriptorFinderTest { + + private VersionManager versionManager; + + @Rule + public TemporaryFolder testFolder = new TemporaryFolder(); + + @Before + public void setUp() throws IOException { + Charset charset = Charset.forName("UTF-8"); + + Path pluginPath = testFolder.newFolder("test-plugin-1").toPath(); + Files.write(pluginPath.resolve("plugin.properties"), getPlugin1Properties(), charset); + + pluginPath = testFolder.newFolder("test-plugin-2").toPath(); + Files.write(pluginPath.resolve("plugin.properties"), getPlugin2Properties(), charset); + + // empty plugin + testFolder.newFolder("test-plugin-3"); + + // no plugin class + pluginPath = testFolder.newFolder("test-plugin-4").toPath(); + Files.write(pluginPath.resolve("plugin.properties"), getPlugin4Properties(), charset); + + // no plugin version + pluginPath = testFolder.newFolder("test-plugin-5").toPath(); + Files.write(pluginPath.resolve("plugin.properties"), getPlugin5Properties(), charset); + + // no plugin id + pluginPath = testFolder.newFolder("test-plugin-6").toPath(); + Files.write(pluginPath.resolve("plugin.properties"), getPlugin6Properties(), charset); + + versionManager = new DefaultVersionManager(); + } + + @Test + public void testFind() throws Exception { + PluginDescriptorFinder instance = new PropertiesPluginDescriptorFinder(); + + PluginDescriptor plugin1 = instance.find(getPluginsRoot().resolve("test-plugin-1")); + PluginDescriptor plugin2 = instance.find(getPluginsRoot().resolve("test-plugin-2")); + + assertEquals("test-plugin-1", plugin1.getPluginId()); + assertEquals("Test Plugin 1", plugin1.getPluginDescription()); + assertEquals("org.pf4j.plugin.TestPlugin", plugin1.getPluginClass()); + assertEquals("0.0.1", plugin1.getVersion()); + assertEquals("Decebal Suiu", plugin1.getProvider()); + assertEquals(2, plugin1.getDependencies().size()); + assertEquals("test-plugin-2", plugin1.getDependencies().get(0).getPluginId()); + assertEquals("test-plugin-3", plugin1.getDependencies().get(1).getPluginId()); + assertEquals("~1.0", plugin1.getDependencies().get(1).getPluginVersionSupport()); + assertEquals("Apache-2.0", plugin1.getLicense()); + assertEquals(">=1", plugin1.getRequires()); + assertTrue(versionManager.satisfies(plugin1.getRequires(), "1.0.0")); + assertFalse(versionManager.satisfies(plugin1.getRequires(), "0.1.0")); + + assertEquals("test-plugin-2", plugin2.getPluginId()); + assertEquals("", plugin2.getPluginDescription()); + assertEquals("org.pf4j.plugin.TestPlugin", plugin2.getPluginClass()); + assertEquals("0.0.1", plugin2.getVersion()); + assertEquals("Decebal Suiu", plugin2.getProvider()); + assertEquals(0, plugin2.getDependencies().size()); + assertEquals("*", plugin2.getRequires()); // Default is * + assertTrue(versionManager.satisfies(plugin2.getRequires(), "1.0.0")); + } + + @Test(expected = PluginException.class) + public void testFindNotFound() throws Exception { + PluginDescriptorFinder instance = new PropertiesPluginDescriptorFinder(); + instance.find(getPluginsRoot().resolve("test-plugin-3")); + } + + private List getPlugin1Properties() { + String[] lines = new String[] { + "plugin.id=test-plugin-1\n" + + "plugin.version=0.0.1\n" + + "plugin.description=Test Plugin 1\n" + + "plugin.provider=Decebal Suiu\n" + + "plugin.class=org.pf4j.plugin.TestPlugin\n" + + "plugin.dependencies=test-plugin-2,test-plugin-3@~1.0\n" + + "plugin.requires=>=1\n" + + "plugin.license=Apache-2.0\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private List getPlugin2Properties() { + String[] lines = new String[] { + "plugin.id=test-plugin-2\n" + + "plugin.version=0.0.1\n" + + "plugin.provider=Decebal Suiu\n" + + "plugin.class=org.pf4j.plugin.TestPlugin\n" + + "plugin.dependencies=\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private List getPlugin4Properties() { + String[] lines = new String[] { + "plugin.id=test-plugin-2\n" + + "plugin.version=0.0.1\n" + + "plugin.provider=Decebal Suiu\n" + + "plugin.dependencies=\n" + + "plugin.requires=*\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private List getPlugin5Properties() { + String[] lines = new String[] { + "plugin.id=test-plugin-2\n" + + "plugin.provider=Decebal Suiu\n" + + "plugin.class=org.pf4j.plugin.TestPlugin\n" + + "plugin.dependencies=\n" + + "plugin.requires=*\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private List getPlugin6Properties() { + String[] lines = new String[] { + "plugin.version=0.0.1\n" + + "plugin.provider=Decebal Suiu\n" + + "plugin.class=org.pf4j.plugin.TestPlugin\n" + + "plugin.dependencies=\n" + + "plugin.requires=*\n" + + "\n" + + "" + }; + + return Arrays.asList(lines); + } + + private Path getPluginsRoot() { + return testFolder.getRoot().toPath(); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/AnotherFailTestPlugin.java b/pf4j/src/test/java/org/pf4j/plugin/AnotherFailTestPlugin.java new file mode 100644 index 0000000..aa124ad --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/AnotherFailTestPlugin.java @@ -0,0 +1,30 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.plugin; + +import org.pf4j.Plugin; + +/** + * + * @author Mario Franco + */ +public class AnotherFailTestPlugin extends Plugin { + + public AnotherFailTestPlugin() { + super(null); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java b/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java new file mode 100644 index 0000000..2fa6062 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/FailTestExtension.java @@ -0,0 +1,29 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.plugin; + +import org.pf4j.Extension; + +/** + * @author Mario Franco + */ +@Extension +public class FailTestExtension implements TestExtensionPoint { + + public FailTestExtension(String name) { + } + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/FailTestPlugin.java b/pf4j/src/test/java/org/pf4j/plugin/FailTestPlugin.java new file mode 100644 index 0000000..c53ae38 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/FailTestPlugin.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.plugin; + +/** + * + * @author Mario Franco + */ +public class FailTestPlugin { + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/MockPluginManager.java b/pf4j/src/test/java/org/pf4j/plugin/MockPluginManager.java new file mode 100644 index 0000000..eb1956d --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/MockPluginManager.java @@ -0,0 +1,51 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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.plugin; + +import org.pf4j.DefaultPluginClasspath; +import org.pf4j.DefaultPluginDescriptorFinder; +import org.pf4j.DefaultPluginManager; +import org.pf4j.PluginDescriptorFinder; + +import java.nio.file.Path; + +/** + * Manager for testing + */ +public class MockPluginManager extends DefaultPluginManager { + + private PluginDescriptorFinder finder = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); + + public MockPluginManager() { + super(); + } + + public MockPluginManager(Path root, PluginDescriptorFinder finder) { + super(root); + this.finder = finder; + } + + @Override + protected PluginDescriptorFinder getPluginDescriptorFinder() { + return finder; + } + + @Override + protected PluginDescriptorFinder createPluginDescriptorFinder() { + return finder; + } + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java b/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java new file mode 100644 index 0000000..9134358 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/TestExtension.java @@ -0,0 +1,26 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.plugin; + +import org.pf4j.Extension; + +/** + * @author Mario Franco + */ +@Extension +public class TestExtension implements TestExtensionPoint { + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java b/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java new file mode 100644 index 0000000..f8a1a64 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/TestExtensionPoint.java @@ -0,0 +1,25 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.plugin; + +import org.pf4j.ExtensionPoint; + +/** + * @author Mario Franco + */ +public interface TestExtensionPoint extends ExtensionPoint { + +} diff --git a/pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java b/pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java new file mode 100644 index 0000000..f0f8c87 --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/plugin/TestPlugin.java @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Decebal Suiu + * + * 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.plugin; + +import org.pf4j.Plugin; +import org.pf4j.PluginWrapper; + +/** + * + * @author Mario Franco + */ +public class TestPlugin extends Plugin { + + public TestPlugin(PluginWrapper wrapper) { + super(wrapper); + } + +} diff --git a/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java b/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java new file mode 100644 index 0000000..6b52fdf --- /dev/null +++ b/pf4j/src/test/java/org/pf4j/util/FileUtilsTest.java @@ -0,0 +1,75 @@ +/* + * Copyright 2017 Decebal Suiu + * + * 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.util; + +import org.junit.Before; +import org.junit.Test; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; + +import static org.junit.Assert.*; + +public class FileUtilsTest { + + private Path zipFile; + private Path tmpDir; + private Path propsFile; + + @Before + public void setup() throws IOException { + tmpDir = Files.createTempDirectory("pf4j-test"); + tmpDir.toFile().deleteOnExit(); + zipFile = tmpDir.resolve("my.zip").toAbsolutePath(); + propsFile = tmpDir.resolve("plugin.properties"); + URI file = URI.create("jar:file:"+zipFile.toString()); + try (FileSystem zipfs = FileSystems.newFileSystem(file, Collections.singletonMap("create", "true"))) { + Path propsInZip = zipfs.getPath("/plugin.properties"); + BufferedWriter br = new BufferedWriter(new FileWriter(propsFile.toString())); + br.write("plugin.id=test"); + br.newLine(); + br.write("plugin.version=1.2.3"); + br.newLine(); + br.write("plugin.class=foo.bar"); + br.close(); + Files.move(propsFile, propsInZip); + } + } + + @Test + public void expandIfZip() throws Exception { + Path unzipped = FileUtils.expandIfZip(zipFile); + assertEquals(tmpDir.resolve("my"), unzipped); + assertTrue(Files.exists(tmpDir.resolve("my/plugin.properties"))); + + // Non-zip file remains unchanged + assertEquals(propsFile, FileUtils.expandIfZip(propsFile)); + // File without .suffix + Path extra = Files.createFile(tmpDir.resolve("extra")); + assertEquals(extra, FileUtils.expandIfZip(extra)); + // Folder + Path folder = Files.createFile(tmpDir.resolve("folder")); + assertEquals(folder, FileUtils.expandIfZip(folder)); + } + +} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/AbstractExtensionFinderTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/AbstractExtensionFinderTest.java deleted file mode 100644 index 47a1b20..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/AbstractExtensionFinderTest.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import ro.fortsoft.pf4j.plugin.FailTestPlugin; -import ro.fortsoft.pf4j.plugin.TestExtensionPoint; - -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * @author Mario Franco - */ -public class AbstractExtensionFinderTest { - - private PluginManager pluginManager; - - @Before - public void setUp() { - PluginWrapper pluginStarted = mock(PluginWrapper.class); - when(pluginStarted.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); - when(pluginStarted.getPluginState()).thenReturn(PluginState.STARTED); - - PluginWrapper pluginStopped = mock(PluginWrapper.class); - when(pluginStopped.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); - 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.getExtensionFactory()).thenReturn(new DefaultExtensionFactory()); - } - - @After - public void tearDown() { - pluginManager = null; - } - - /** - * Test of {@link AbstractExtensionFinder#find(Class)}. - */ - @Test - public void testFindFailType() { - ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { - - @Override - public Map> readPluginsStorages() { - return Collections.emptyMap(); - } - - @Override - public Map> readClasspathStorages() { - return Collections.emptyMap(); - } - - }; - List> list = instance.find(FailTestPlugin.class); - assertEquals(0, list.size()); - } - - /** - * Test of {@link AbstractExtensionFinder#find(Class)}. - */ - @Test - public void testFindFromClasspath() { - ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { - - @Override - public Map> readPluginsStorages() { - return Collections.emptyMap(); - } - - @Override - public Map> readClasspathStorages() { - Map> entries = new LinkedHashMap<>(); - - Set bucket = new HashSet<>(); - bucket.add("ro.fortsoft.pf4j.plugin.TestExtension"); - bucket.add("ro.fortsoft.pf4j.plugin.FailTestExtension"); - entries.put(null, bucket); - - return entries; - } - - }; - - List> list = instance.find(TestExtensionPoint.class); - assertEquals(2, list.size()); - } - - /** - * Test of {@link AbstractExtensionFinder#find(Class, String)}. - */ - @Test - public void testFindFromPlugin() { - ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { - - @Override - public Map> readPluginsStorages() { - Map> entries = new LinkedHashMap<>(); - - Set bucket = new HashSet<>(); - bucket.add("ro.fortsoft.pf4j.plugin.TestExtension"); - bucket.add("ro.fortsoft.pf4j.plugin.FailTestExtension"); - entries.put("plugin1", bucket); - bucket = new HashSet<>(); - bucket.add("ro.fortsoft.pf4j.plugin.TestExtension"); - entries.put("plugin2", bucket); - - return entries; - } - - @Override - public Map> readClasspathStorages() { - return Collections.emptyMap(); - } - - }; - - List> list = instance.find(TestExtensionPoint.class); - assertEquals(2, list.size()); - - list = instance.find(TestExtensionPoint.class, "plugin1"); - assertEquals(2, list.size()); - - list = instance.find(TestExtensionPoint.class, "plugin2"); - assertEquals(0, list.size()); - } - - /** - * Test of {@link AbstractExtensionFinder#findClassNames(String)}. - */ - @Test - public void testFindClassNames() { - ExtensionFinder instance = new AbstractExtensionFinder(pluginManager) { - - @Override - public Map> readPluginsStorages() { - Map> entries = new LinkedHashMap<>(); - - Set bucket = new HashSet<>(); - bucket.add("ro.fortsoft.pf4j.plugin.TestExtension"); - entries.put("plugin1", bucket); - - return entries; - } - - @Override - public Map> readClasspathStorages() { - Map> entries = new LinkedHashMap<>(); - - Set bucket = new HashSet<>(); - bucket.add("ro.fortsoft.pf4j.plugin.TestExtension"); - bucket.add("ro.fortsoft.pf4j.plugin.FailTestExtension"); - entries.put(null, bucket); - - return entries; - } - - }; - - Set result = instance.findClassNames(null); - assertEquals(2, result.size()); - - result = instance.findClassNames("plugin1"); - assertEquals(1, result.size()); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultExtensionFactoryTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultExtensionFactoryTest.java deleted file mode 100644 index 28a4495..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultExtensionFactoryTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Test; -import ro.fortsoft.pf4j.plugin.FailTestExtension; -import ro.fortsoft.pf4j.plugin.TestExtension; - -import static org.junit.Assert.*; - -/** - * - * @author Mario Franco - */ -public class DefaultExtensionFactoryTest { - - /** - * Test of create method, of class DefaultExtensionFactory. - */ - @Test - public void testCreate() { - DefaultExtensionFactory instance = new DefaultExtensionFactory(); - Object result = instance.create(TestExtension.class); - assertNotNull(result); - } - - /** - * Test of create method, of class DefaultExtensionFactory. - */ - @Test - public void testCreateFailConstructor() { - DefaultExtensionFactory instance = new DefaultExtensionFactory(); - Object result = instance.create(FailTestExtension.class); - assertNull(result); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginFactoryTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginFactoryTest.java deleted file mode 100644 index aef1fcc..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginFactoryTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Test; -import ro.fortsoft.pf4j.plugin.AnotherFailTestPlugin; -import ro.fortsoft.pf4j.plugin.FailTestPlugin; -import ro.fortsoft.pf4j.plugin.TestPlugin; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * - * @author Mario Franco - */ -public class DefaultPluginFactoryTest { - - /** - * Test of create method, of class DefaultPluginFactory. - */ - @Test - public void testCreate() { - PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); - when(pluginDescriptor.getPluginClass()).thenReturn(TestPlugin.class.getName()); - - PluginWrapper pluginWrapper = mock(PluginWrapper.class); - when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); - when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); - - DefaultPluginFactory instance = new DefaultPluginFactory(); - - Plugin result = instance.create(pluginWrapper); - assertNotNull(result); - assertThat(result, instanceOf(TestPlugin.class)); - } - - /** - * Test of create method, of class DefaultPluginFactory. - */ - @Test - public void testCreateFail() { - PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); - when(pluginDescriptor.getPluginClass()).thenReturn(FailTestPlugin.class.getName()); - - PluginWrapper pluginWrapper = mock(PluginWrapper.class); - when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); - when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); - - DefaultPluginFactory instance = new DefaultPluginFactory(); - - Plugin result = instance.create(pluginWrapper); - assertNull(result); - } - - /** - * Test of create method, of class DefaultPluginFactory. - */ - @Test - public void testCreateFailNotFound() { - PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); - when(pluginDescriptor.getPluginClass()).thenReturn("ro.fortsoft.pf4j.plugin.NotFoundTestPlugin"); - - PluginWrapper pluginWrapper = mock(PluginWrapper.class); - when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); - when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); - - DefaultPluginFactory instance = new DefaultPluginFactory(); - - Plugin result = instance.create(pluginWrapper); - assertNull(result); - } - - /** - * Test of create method, of class DefaultPluginFactory. - */ - @Test - public void testCreateFailConstructor() { - PluginDescriptor pluginDescriptor = mock(PluginDescriptor.class); - when(pluginDescriptor.getPluginClass()).thenReturn(AnotherFailTestPlugin.class.getName()); - - PluginWrapper pluginWrapper = mock(PluginWrapper.class); - when(pluginWrapper.getDescriptor()).thenReturn(pluginDescriptor); - when(pluginWrapper.getPluginClassLoader()).thenReturn(getClass().getClassLoader()); - - DefaultPluginFactory instance = new DefaultPluginFactory(); - - Plugin result = instance.create(pluginWrapper); - assertNull(result); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginManagerTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginManagerTest.java deleted file mode 100644 index 4c154d1..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginManagerTest.java +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; -import java.nio.file.Files; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class DefaultPluginManagerTest { - - private PluginDescriptor pd1 = null; - private DefaultPluginManager pluginManager = new DefaultPluginManager(); - private PluginWrapper pw1; - - @Before - public void init() throws IOException { - pd1 = new PluginDescriptor(); - pd1.setPluginId("myPlugin"); - pd1.setPluginVersion("1.2.3"); - pd1.setPluginClass("foo"); - pd1.setPluginDescription("My plugin"); - pd1.setDependencies("bar, baz"); - pd1.setProvider("Me"); - pd1.setRequires("5.0.0"); - - pw1 = new PluginWrapper(pluginManager, pd1, Files.createTempDirectory("test"), getClass().getClassLoader()); - } - - @Test - public void validateOK() throws PluginException { - pluginManager.validatePluginDescriptor(pd1); - } - - @Test(expected = PluginException.class) - public void validateFailsOnId() throws PluginException { - pd1.setPluginId(""); - pluginManager.validatePluginDescriptor(pd1); - } - - @Test(expected = PluginException.class) - public void validateFailsOnVersion() throws PluginException { - pd1.setPluginVersion(null); - pluginManager.validatePluginDescriptor(pd1); - } - - @Test(expected = PluginException.class) - public void validateFailsOnClass() throws PluginException { - pd1.setPluginClass(null); - pluginManager.validatePluginDescriptor(pd1); - } - - @Test - public void isPluginValid() throws Exception { - // By default accept all since system version not given - assertTrue(pluginManager.isPluginValid(pw1)); - - pluginManager.setSystemVersion("1.0.0"); - assertFalse(pluginManager.isPluginValid(pw1)); - - pluginManager.setSystemVersion("5.0.0"); - assertTrue(pluginManager.isPluginValid(pw1)); - - pluginManager.setSystemVersion("6.0.0"); - assertTrue(pluginManager.isPluginValid(pw1)); - } - - @Test - public void isPluginValidAllowExact() throws Exception { - pluginManager.setExactVersionAllowed(true); - - // By default accept all since system version not given - assertTrue(pluginManager.isPluginValid(pw1)); - - pluginManager.setSystemVersion("1.0.0"); - assertFalse(pluginManager.isPluginValid(pw1)); - - pluginManager.setSystemVersion("5.0.0"); - assertTrue(pluginManager.isPluginValid(pw1)); - - pluginManager.setSystemVersion("6.0.0"); - assertFalse(pluginManager.isPluginValid(pw1)); - } - - @Test - public void testDefaultExactVersionAllowed() throws Exception { - assertEquals(false, pluginManager.isExactVersionAllowed()); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginRepositoryTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginRepositoryTest.java deleted file mode 100644 index 95c153b..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginRepositoryTest.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -/** - * @author Mario Franco - * @author Decebal Suiu - */ -public class DefaultPluginRepositoryTest { - - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(); - - @Before - public void setUp() throws IOException { - testFolder.newFolder("plugin-1"); - // Prove that we can delete a folder with a file inside - Files.createFile(Paths.get(testFolder.getRoot().getAbsolutePath()).resolve("plugin-1").resolve("myfile")); - // Create a zip file for plugin-1 to test that it is deleted when plugin is deleted - Files.createFile(Paths.get(testFolder.getRoot().getAbsolutePath()).resolve("plugin-1.zip")); - testFolder.newFolder("plugin-2"); - testFolder.newFolder("plugin-3"); - } - - /** - * Test of {@link DefaultPluginRepository#getPluginPaths()} method. - */ - @Test - public void testGetPluginArchives() { - Path pluginsRoot = getPluginsRoot(); - - PluginRepository instance = new DefaultPluginRepository(pluginsRoot, false); - - List result = instance.getPluginPaths(); - - assertEquals(3, result.size()); - assertPathExists(result, pluginsRoot.resolve("plugin-1")); - assertPathExists(result, pluginsRoot.resolve("plugin-2")); - assertPathExists(result, pluginsRoot.resolve("plugin-3")); - } - - /** - * Test of {@link DefaultPluginRepository#deletePluginPath(Path)} method. - */ - @Test - public void testDeletePluginPath() { - Path pluginsRoot = getPluginsRoot(); - - PluginRepository instance = new DefaultPluginRepository(pluginsRoot, false); - - assertTrue(Files.exists(pluginsRoot.resolve("plugin-1.zip"))); - assertTrue(instance.deletePluginPath(pluginsRoot.resolve("plugin-1"))); - assertFalse(Files.exists(pluginsRoot.resolve("plugin-1.zip"))); - assertTrue(instance.deletePluginPath(pluginsRoot.resolve("plugin-3"))); - assertFalse(instance.deletePluginPath(pluginsRoot.resolve("plugin-4"))); - - List result = instance.getPluginPaths(); - - assertEquals(1, result.size()); - assertEquals(pluginsRoot.relativize(result.get(0)).toString(), "plugin-2"); - } - - private void assertPathExists(List paths, Path path) { - assertTrue("The directory must contain the file " + path, paths.contains(path)); - } - - private Path getPluginsRoot() { - return testFolder.getRoot().toPath(); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginStatusProviderTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginStatusProviderTest.java deleted file mode 100644 index db33d65..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultPluginStatusProviderTest.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; -import ro.fortsoft.pf4j.util.FileUtils; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Path; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -/** - * @author Mario Franco - * @author Decebal Suiu - */ -public class DefaultPluginStatusProviderTest { - - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(); - - /** - * Test of isPluginDisabled method, of class DefaultPluginStatusProvider. - */ - @Test - public void testIsPluginDisabled() throws IOException { - createEnabledFile(); - createDisabledFile(); - - PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); - - assertFalse(instance.isPluginDisabled("plugin-1")); - assertTrue(instance.isPluginDisabled("plugin-2")); - assertTrue(instance.isPluginDisabled("plugin-3")); - } - - /** - * Test of isPluginDisabled method, of class DefaultPluginStatusProvider. - */ - @Test - public void testIsPluginDisabledWithEnableEmpty() throws IOException { - createDisabledFile(); - - PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); - - assertFalse(instance.isPluginDisabled("plugin-1")); - assertTrue(instance.isPluginDisabled("plugin-2")); - assertFalse(instance.isPluginDisabled("plugin-3")); - } - - /** - * Test of disablePlugin method, of class DefaultPluginStatusProvider. - */ - @Test - public void testDisablePlugin() throws IOException { - createEnabledFile(); - createDisabledFile(); - - PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); - - assertTrue(instance.disablePlugin("plugin-1")); - assertTrue(instance.isPluginDisabled("plugin-1")); - assertTrue(instance.isPluginDisabled("plugin-2")); - assertTrue(instance.isPluginDisabled("plugin-3")); - } - - /** - * Test of disablePlugin method, of class DefaultPluginStatusProvider. - */ - @Test - public void testDisablePluginWithEnableEmpty() throws IOException { - createDisabledFile(); - - PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); - - assertTrue(instance.disablePlugin("plugin-1")); - assertTrue(instance.isPluginDisabled("plugin-1")); - assertTrue(instance.isPluginDisabled("plugin-2")); - assertFalse(instance.isPluginDisabled("plugin-3")); - } - - /** - * Test of enablePlugin method, of class DefaultPluginStatusProvider. - */ - @Test - public void testEnablePlugin() throws IOException { - createEnabledFile(); - - PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); - - assertTrue(instance.enablePlugin("plugin-2")); - assertFalse(instance.isPluginDisabled("plugin-1")); - assertFalse(instance.isPluginDisabled("plugin-2")); - assertTrue(instance.isPluginDisabled("plugin-3")); - } - - /** - * Test of enablePlugin method, of class DefaultPluginStatusProvider. - */ - @Test - public void testEnablePluginWithEnableEmpty() { - PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); - - assertTrue(instance.enablePlugin("plugin-2")); - assertFalse(instance.isPluginDisabled("plugin-1")); - assertFalse(instance.isPluginDisabled("plugin-2")); - assertFalse(instance.isPluginDisabled("plugin-3")); - } - - /** - * Test of disablePlugin method without a disabled.txt file. - */ - @Test - public void testDisablePluginWithoutDisabledFile() throws IOException { - PluginStatusProvider instance = new DefaultPluginStatusProvider(getPluginsRoot()); - - assertFalse(instance.isPluginDisabled("plugin-1")); - assertTrue(instance.disablePlugin("plugin-1")); - assertTrue(instance.isPluginDisabled("plugin-1")); - } - - private void createDisabledFile() throws IOException { - List plugins = new ArrayList<>(); - plugins.add("plugin-2"); - - writeLines(plugins, "disabled.txt"); - } - - private void createEnabledFile() throws IOException { - List plugins = new ArrayList<>(); - plugins.add("plugin-1"); - plugins.add("plugin-2"); - - writeLines(plugins, "enabled.txt"); - } - - private void writeLines(List lines, String fileName) throws IOException { - File file = testFolder.newFile(fileName); - FileUtils.writeLines(lines, file); - } - - private Path getPluginsRoot() { - return testFolder.getRoot().toPath(); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultVersionManagerTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultVersionManagerTest.java deleted file mode 100644 index 802291d..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DefaultVersionManagerTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import com.github.zafarkhaja.semver.ParseException; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * @author Decebal Suiu - */ -public class DefaultVersionManagerTest { - - private VersionManager versionManager; - - @Before - public void init() { - versionManager = new DefaultVersionManager(); - } - - @Test - public void satisfies() { - assertFalse(versionManager.satisfies(">2.0.0", "1.4.3")); // simple - assertTrue(versionManager.satisfies(">=1.4.0 & <1.6.0", "1.4.3")); // range - } - - @Test(expected = IllegalArgumentException.class) - public void nullOrEmptyVersion() { - assertFalse(versionManager.satisfies(">2.0.0", null)); - } - - @Test(expected = ParseException.class) - public void invalidVersion() { - assertFalse(versionManager.satisfies(">2.0.0", "1.0")); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/DependencyResolverTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/DependencyResolverTest.java deleted file mode 100644 index 14aba39..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/DependencyResolverTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.*; - -/** - * @author Decebal Suiu - */ -public class DependencyResolverTest { - - private DependencyResolver resolver; - - @Before - public void init() { - VersionManager versionManager = new DefaultVersionManager(); - resolver = new DependencyResolver(versionManager); - } - - @Test - public void sortedPlugins() { - // create incomplete plugin descriptor (ignore some attributes) - PluginDescriptor pd1 = new PluginDescriptor() - .setPluginId("p1") - .setDependencies("p2"); - - PluginDescriptor pd2 = new PluginDescriptor() - .setPluginId("p2") - .setPluginVersion("0.0.0"); // needed in "checkDependencyVersion" method - - List plugins = new ArrayList<>(); - plugins.add(pd1); - plugins.add(pd2); - - DependencyResolver.Result result = resolver.resolve(plugins); - - assertTrue(result.getNotFoundDependencies().isEmpty()); - assertEquals(result.getSortedPlugins(), Arrays.asList("p2", "p1")); - } - - @Test - public void notFoundDependencies() throws Exception { - PluginDescriptor pd1 = new PluginDescriptor() - .setPluginId("p1") - .setDependencies("p2, p3"); - - List plugins = new ArrayList<>(); - plugins.add(pd1); - - DependencyResolver.Result result = resolver.resolve(plugins); - - assertFalse(result.getNotFoundDependencies().isEmpty()); - assertEquals(result.getNotFoundDependencies(), Arrays.asList("p2", "p3")); - } - - @Test - public void cyclicDependencies() { - PluginDescriptor pd1 = new PluginDescriptor() - .setPluginId("p1") - .setPluginVersion("0.0.0") - .setDependencies("p2"); - - PluginDescriptor pd2 = new PluginDescriptor() - .setPluginId("p2") - .setPluginVersion("0.0.0") - .setDependencies("p3"); - - PluginDescriptor pd3 = new PluginDescriptor() - .setPluginId("p3") - .setPluginVersion("0.0.0") - .setDependencies("p1"); - - List plugins = new ArrayList<>(); - plugins.add(pd1); - plugins.add(pd2); - plugins.add(pd3); - - DependencyResolver.Result result = resolver.resolve(plugins); - - assertTrue(result.hasCyclicDependency()); - } - - @Test - public void wrongDependencyVersion() { - PluginDescriptor pd1 = new PluginDescriptor() - .setPluginId("p1") -// .setDependencies("p2@2.0.0"); // simple version - .setDependencies("p2@>=1.5.0 & <1.6.0"); // range version - - PluginDescriptor pd2 = new PluginDescriptor() - .setPluginId("p2") - .setPluginVersion("1.4.0"); - - List plugins = new ArrayList<>(); - plugins.add(pd1); - plugins.add(pd2); - - DependencyResolver.Result result = resolver.resolve(plugins); - - assertFalse(result.getWrongVersionDependencies().isEmpty()); - } - - @Test - public void goodDependencyVersion() { - PluginDescriptor pd1 = new PluginDescriptor() - .setPluginId("p1") - .setDependencies("p2@2.0.0"); - - PluginDescriptor pd2 = new PluginDescriptor() - .setPluginId("p2") - .setPluginVersion("2.0.0"); - - List plugins = new ArrayList<>(); - plugins.add(pd1); - plugins.add(pd2); - - DependencyResolver.Result result = resolver.resolve(plugins); - - assertTrue(result.getWrongVersionDependencies().isEmpty()); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/ExtensionAnnotationProcessorTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/ExtensionAnnotationProcessorTest.java deleted file mode 100644 index 1c99396..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/ExtensionAnnotationProcessorTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Test; -import ro.fortsoft.pf4j.processor.ExtensionAnnotationProcessor; - -import java.util.Set; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -/** - * @author Mario Franco - */ -public class ExtensionAnnotationProcessorTest { - - /** - * Test of {@link ExtensionAnnotationProcessor#getSupportedAnnotationTypes()}. - */ - @Test - public void testGetSupportedAnnotationTypes() { - ExtensionAnnotationProcessor instance = new ExtensionAnnotationProcessor(); - Set result = instance.getSupportedAnnotationTypes(); - assertEquals(1, result.size()); - assertTrue(result.contains(Extension.class.getName())); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/LegacyExtensionStorageTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/LegacyExtensionStorageTest.java deleted file mode 100644 index aeeb371..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/LegacyExtensionStorageTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Test; -import ro.fortsoft.pf4j.processor.LegacyExtensionStorage; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.HashSet; -import java.util.Set; - -import static org.junit.Assert.*; - -/** - * @author Decebal Suiu - */ -public class LegacyExtensionStorageTest { - - /** - * Test of {@link LegacyExtensionStorage#read(Reader, Set)}. - */ - @Test - public void testRead() throws IOException { - Reader reader = new StringReader( - "# comment\n" - + "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting\n" - + "ro.fortsoft.pf4j.demo.welcome.WelcomePlugin$WelcomeGreeting\n" - + "ro.fortsoft.pf4j.demo.welcome.OtherGreeting\n"); - - Set entries = new HashSet<>(); - LegacyExtensionStorage.read(reader, entries); - assertEquals(3, entries.size()); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/LoadPluginsTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/LoadPluginsTest.java deleted file mode 100644 index 9ee9899..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/LoadPluginsTest.java +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Before; -import org.junit.Test; -import ro.fortsoft.pf4j.plugin.MockPluginManager; - -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; -import java.net.URI; -import java.nio.file.*; -import java.util.Collections; - -import static junit.framework.TestCase.assertNull; -import static org.junit.Assert.*; - -public class LoadPluginsTest { - - private Path tmpDir; - private MockPluginManager pluginManager; - private MockZipPlugin p1; - private MockZipPlugin p2; - private MockZipPlugin p3; - - @Before - public void setup() throws IOException { - tmpDir = Files.createTempDirectory("pf4j-test"); - tmpDir.toFile().deleteOnExit(); - p1 = new MockZipPlugin("myPlugin", "1.2.3", "my-plugin-1.2.3", "my-plugin-1.2.3.zip"); - p2 = new MockZipPlugin("myPlugin", "2.0.0", "my-plugin-2.0.0", "my-plugin-2.0.0.ZIP"); - p3 = new MockZipPlugin("other", "3.0.0", "other-3.0.0", "other-3.0.0.Zip"); - pluginManager = new MockPluginManager( - tmpDir, - new PropertiesPluginDescriptorFinder("my.properties")); - } - - @Test - public void load() throws Exception { - p1.create(); - assertTrue(Files.exists(p1.zipFile)); - assertEquals(0, pluginManager.getPlugins().size()); - pluginManager.loadPlugins(); - assertTrue(Files.exists(p1.zipFile)); - assertTrue(Files.exists(p1.unzipped)); - assertEquals(1, pluginManager.getPlugins().size()); - assertEquals(p1.id, pluginManager.idForPath(p1.unzipped)); - } - - @Test(expected = IllegalArgumentException.class) - public void loadNonExisting() throws Exception { - pluginManager.loadPlugin(Paths.get("nonexisting")); - } - - @Test - public void loadTwiceFails() throws Exception { - p1.create(); - assertNotNull(pluginManager.loadPluginFromPath(p1.zipFile)); - assertNull(pluginManager.loadPluginFromPath(p1.zipFile)); - } - - @Test - public void loadUnloadLoad() throws Exception { - p1.create(); - pluginManager.loadPlugins(); - assertEquals(1, pluginManager.getPlugins().size()); - assertTrue(pluginManager.unloadPlugin(pluginManager.idForPath(p1.unzipped))); - // duplicate check - assertNull(pluginManager.idForPath(p1.unzipped)); - // Double unload ok - assertFalse(pluginManager.unloadPlugin(pluginManager.idForPath(p1.unzipped))); - assertNotNull(pluginManager.loadPlugin(p1.unzipped)); - } - - @Test - public void upgrade() throws Exception { - p1.create(); - pluginManager.loadPlugins(); - pluginManager.startPlugins(); - assertEquals(1, pluginManager.getPlugins().size()); - assertEquals("1.2.3", pluginManager.getPlugin(p2.id).getDescriptor().getVersion()); - assertEquals(1, pluginManager.getStartedPlugins().size()); - p2.create(); - pluginManager.loadPlugins(); - pluginManager.startPlugin(p2.id); - assertEquals(1, pluginManager.getPlugins().size()); - assertEquals("2.0.0", pluginManager.getPlugin(p2.id).getDescriptor().getVersion()); - assertEquals("2.0.0", pluginManager.getStartedPlugins().get(1).getDescriptor().getVersion()); - } - - @Test - public void getRoot() throws Exception { - assertEquals(tmpDir, pluginManager.getPluginsRoot()); - } - - @Test - public void notAPlugin() throws Exception { - Path notAPlugin = tmpDir.resolve("not-a-zip"); - Files.createFile(notAPlugin); - pluginManager.loadPlugins(); - assertEquals(0, pluginManager.getPlugins().size()); - } - - @Test - public void deletePlugin() throws Exception { - p1.create(); - p3.create(); - pluginManager.loadPlugins(); - pluginManager.startPlugins(); - assertEquals(2, pluginManager.getPlugins().size()); - pluginManager.deletePlugin(p1.id); - assertEquals(1, pluginManager.getPlugins().size()); - assertFalse(Files.exists(p1.zipFile)); - assertFalse(Files.exists(p1.unzipped)); - assertTrue(Files.exists(p3.zipFile)); - assertTrue(Files.exists(p3.unzipped)); - } - - private class MockZipPlugin { - - public final String id; - public final String version; - public final String filename; - public final Path zipFile; - public final Path unzipped; - public final Path propsFile; - public final URI fileURI; - public String zipname; - - public MockZipPlugin(String id, String version, String filename, String zipname) throws IOException { - this.id = id; - this.version = version; - this.filename = filename; - this.zipname = zipname; - - zipFile = tmpDir.resolve(zipname).toAbsolutePath(); - unzipped = tmpDir.resolve(filename); - propsFile = tmpDir.resolve("my.properties"); - fileURI = URI.create("jar:file:"+zipFile.toString()); - } - - public void create() throws IOException { - try (FileSystem zipfs = FileSystems.newFileSystem(fileURI, Collections.singletonMap("create", "true"))) { - Path propsInZip = zipfs.getPath("/" + propsFile.getFileName().toString()); - BufferedWriter br = new BufferedWriter(new FileWriter(propsFile.toString())); - br.write("plugin.id=" + id); - br.newLine(); - br.write("plugin.version=" + version); - br.newLine(); - br.write("plugin.class=ro.fortsoft.pf4j.plugin.TestPlugin"); - br.close(); - Files.move(propsFile, propsInZip); - } - } - - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinderTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinderTest.java deleted file mode 100644 index c858767..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/ManifestPluginDescriptorFinderTest.java +++ /dev/null @@ -1,236 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.*; - -/** - * @author Mario Franco - * @author Decebal Suiu - */ -public class ManifestPluginDescriptorFinderTest { - - private VersionManager versionManager; - - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(); - - @Before - public void setUp() throws IOException { - Charset charset = Charset.forName("UTF-8"); - - Path pluginPath = testFolder.newFolder("test-plugin-1", "classes", "META-INF").toPath(); - Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin1Manifest(), charset); - - pluginPath = testFolder.newFolder("test-plugin-2", "classes", "META-INF").toPath(); - Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin2Manifest(), charset); - - // empty plugin - testFolder.newFolder("test-plugin-3"); - - // no plugin class - pluginPath = testFolder.newFolder("test-plugin-4", "classes", "META-INF").toPath(); - Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin4Manifest(), charset); - - // no plugin version - pluginPath = testFolder.newFolder("test-plugin-5", "classes", "META-INF").toPath(); - Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin5Manifest(), charset); - - // no plugin id - pluginPath = testFolder.newFolder("test-plugin-6", "classes", "META-INF").toPath(); - Files.write(pluginPath.resolve("extensions.idx"), "ro.fortsoft.pf4j.demo.hello.HelloPlugin$HelloGreeting".getBytes()); - Files.write(pluginPath.resolve("MANIFEST.MF"), getPlugin6Manifest(), charset); - - versionManager = new DefaultVersionManager(); - } - - /** - * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. - */ - @Test - public void testFind() throws Exception { - PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); - - PluginDescriptor plugin1 = instance.find(getPluginsRoot().resolve("test-plugin-1")); - PluginDescriptor plugin2 = instance.find(getPluginsRoot().resolve("test-plugin-2")); - - assertEquals("test-plugin-1", plugin1.getPluginId()); - assertEquals("Test Plugin 1", plugin1.getPluginDescription()); - assertEquals("ro.fortsoft.pf4j.plugin.TestPlugin", plugin1.getPluginClass()); - assertEquals("0.0.1", plugin1.getVersion()); - assertEquals("Decebal Suiu", plugin1.getProvider()); - assertEquals(2, plugin1.getDependencies().size()); - assertEquals("test-plugin-2", plugin1.getDependencies().get(0).getPluginId()); - assertEquals("test-plugin-3", plugin1.getDependencies().get(1).getPluginId()); - assertEquals("~1.0", plugin1.getDependencies().get(1).getPluginVersionSupport()); - assertEquals("Apache-2.0", plugin1.getLicense()); - assertTrue(versionManager.satisfies(plugin1.getRequires(), "1.0.0")); - - assertEquals("test-plugin-2", plugin2.getPluginId()); - assertEquals("", plugin2.getPluginDescription()); - assertEquals("ro.fortsoft.pf4j.plugin.TestPlugin", plugin2.getPluginClass()); - assertEquals("0.0.1", plugin2.getVersion()); - assertEquals("Decebal Suiu", plugin2.getProvider()); - assertEquals(0, plugin2.getDependencies().size()); - assertTrue(versionManager.satisfies(plugin2.getRequires(), "1.0.0")); - } - - /** - * Test of {@link DefaultPluginDescriptorFinder#find(Path)} method. - */ - @Test(expected = PluginException.class) - public void testFindNotFound() throws Exception { - PluginDescriptorFinder instance = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); - instance.find(getPluginsRoot().resolve("test-plugin-3")); - } - - private List getPlugin1Manifest() { - String[] lines = new String[] { - "Manifest-Version: 1.0\n" - + "Implementation-Title: Test Plugin #1\n" - + "Implementation-Version: 0.10.0-SNAPSHOT\n" - + "Archiver-Version: Plexus Archiver\n" - + "Built-By: Mario Franco\n" - + "Specification-Title: Test Plugin #1\n" - + "Implementation-Vendor-Id: ro.fortsoft.pf4j.demo\n" - + "Plugin-Version: 0.0.1\n" - + "Plugin-Id: test-plugin-1\n" - + "Plugin-Description: Test Plugin 1\n" - + "Plugin-Provider: Decebal Suiu\n" - + "Plugin-Class: ro.fortsoft.pf4j.plugin.TestPlugin\n" - + "Plugin-Dependencies: test-plugin-2,test-plugin-3@~1.0\n" - + "Plugin-Requires: *\n" - + "Plugin-License: Apache-2.0\n" - + "Created-By: Apache Maven 3.0.5\n" - + "Build-Jdk: 1.8.0_45\n" - + "Specification-Version: 0.10.0-SNAPSHOT\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private List getPlugin2Manifest() { - String[] lines = new String[] { - "Manifest-Version: 1.0\n" - + "Plugin-Dependencies: \n" - + "Implementation-Title: Test Plugin #2\n" - + "Implementation-Version: 0.10.0-SNAPSHOT\n" - + "Archiver-Version: Plexus Archiver\n" - + "Built-By: Mario Franco\n" - + "Specification-Title: Test Plugin #2\n" - + "Implementation-Vendor-Id: ro.fortsoft.pf4j.demo\n" - + "Plugin-Version: 0.0.1\n" - + "Plugin-Id: test-plugin-2\n" - + "Plugin-Provider: Decebal Suiu\n" - + "Plugin-Class: ro.fortsoft.pf4j.plugin.TestPlugin\n" - + "Created-By: Apache Maven 3.0.5\n" - + "Build-Jdk: 1.8.0_45\n" - + "Specification-Version: 0.10.0-SNAPSHOT\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private List getPlugin4Manifest() { - String[] lines = new String[] { - "Manifest-Version: 1.0\n" - + "Implementation-Title: Test Plugin #4\n" - + "Implementation-Version: 0.10.0-SNAPSHOT\n" - + "Archiver-Version: Plexus Archiver\n" - + "Built-By: Mario Franco\n" - + "Specification-Title: Test Plugin #4\n" - + "Implementation-Vendor-Id: ro.fortsoft.pf4j.demo\n" - + "Plugin-Version: 0.0.1\n" - + "Plugin-Id: test-plugin-2\n" - + "Plugin-Provider: Decebal Suiu\n" - + "Created-By: Apache Maven 3.0.5\n" - + "Build-Jdk: 1.8.0_45\n" - + "Specification-Version: 0.10.0-SNAPSHOT\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private List getPlugin5Manifest() { - String[] lines = new String[] { - "Manifest-Version: 1.0\n" - + "Implementation-Title: Test Plugin #5\n" - + "Implementation-Version: 0.10.0-SNAPSHOT\n" - + "Archiver-Version: Plexus Archiver\n" - + "Built-By: Mario Franco\n" - + "Specification-Title: Test Plugin #5\n" - + "Implementation-Vendor-Id: ro.fortsoft.pf4j.demo\n" - + "Plugin-Id: test-plugin-2\n" - + "Plugin-Provider: Decebal Suiu\n" - + "Plugin-Class: ro.fortsoft.pf4j.plugin.TestPlugin\n" - + "Created-By: Apache Maven 3.0.5\n" - + "Build-Jdk: 1.8.0_45\n" - + "Specification-Version: 0.10.0-SNAPSHOT\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private List getPlugin6Manifest() { - String[] lines = new String[] { - "Manifest-Version: 1.0\n" - + "Implementation-Title: Test Plugin #6\n" - + "Implementation-Version: 0.10.0-SNAPSHOT\n" - + "Archiver-Version: Plexus Archiver\n" - + "Built-By: Mario Franco\n" - + "Specification-Title: Test Plugin #6\n" - + "Implementation-Vendor-Id: ro.fortsoft.pf4j.demo\n" - + "Plugin-Provider: Decebal Suiu\n" - + "Plugin-Class: ro.fortsoft.pf4j.plugin.TestPlugin\n" - + "Created-By: Apache Maven 3.0.5\n" - + "Build-Jdk: 1.8.0_45\n" - + "Specification-Version: 0.10.0-SNAPSHOT\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private Path getPluginsRoot() { - return testFolder.getRoot().toPath(); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/PluginDependencyTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/PluginDependencyTest.java deleted file mode 100644 index 9304e57..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/PluginDependencyTest.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2015 Mario Franco. - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * @author Mario Franco - */ -public class PluginDependencyTest { - - /** - * Test of getPluginId method, of class PluginDependency. - */ - @Test - public void testPluginDependecy() { - PluginDependency instance = new PluginDependency("test"); - assertEquals("test", instance.getPluginId()); - assertEquals("*", instance.getPluginVersionSupport()); - - instance = new PluginDependency("test@"); - assertEquals("test", instance.getPluginId()); - assertEquals("*", instance.getPluginVersionSupport()); - - instance = new PluginDependency("test@1.0"); - assertEquals("test", instance.getPluginId()); - assertEquals("1.0", instance.getPluginVersionSupport()); - assertEquals("PluginDependency [pluginId=test, pluginVersionSupport=1.0]", instance.toString()); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinderTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinderTest.java deleted file mode 100644 index aa6d7c5..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/PropertiesPluginDescriptorFinderTest.java +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2012 Decebal Suiu - * - * 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 ro.fortsoft.pf4j; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - -import java.io.IOException; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Arrays; -import java.util.List; - -import static org.junit.Assert.*; - -public class PropertiesPluginDescriptorFinderTest { - - private VersionManager versionManager; - - @Rule - public TemporaryFolder testFolder = new TemporaryFolder(); - - @Before - public void setUp() throws IOException { - Charset charset = Charset.forName("UTF-8"); - - Path pluginPath = testFolder.newFolder("test-plugin-1").toPath(); - Files.write(pluginPath.resolve("plugin.properties"), getPlugin1Properties(), charset); - - pluginPath = testFolder.newFolder("test-plugin-2").toPath(); - Files.write(pluginPath.resolve("plugin.properties"), getPlugin2Properties(), charset); - - // empty plugin - testFolder.newFolder("test-plugin-3"); - - // no plugin class - pluginPath = testFolder.newFolder("test-plugin-4").toPath(); - Files.write(pluginPath.resolve("plugin.properties"), getPlugin4Properties(), charset); - - // no plugin version - pluginPath = testFolder.newFolder("test-plugin-5").toPath(); - Files.write(pluginPath.resolve("plugin.properties"), getPlugin5Properties(), charset); - - // no plugin id - pluginPath = testFolder.newFolder("test-plugin-6").toPath(); - Files.write(pluginPath.resolve("plugin.properties"), getPlugin6Properties(), charset); - - versionManager = new DefaultVersionManager(); - } - - @Test - public void testFind() throws Exception { - PluginDescriptorFinder instance = new PropertiesPluginDescriptorFinder(); - - PluginDescriptor plugin1 = instance.find(getPluginsRoot().resolve("test-plugin-1")); - PluginDescriptor plugin2 = instance.find(getPluginsRoot().resolve("test-plugin-2")); - - assertEquals("test-plugin-1", plugin1.getPluginId()); - assertEquals("Test Plugin 1", plugin1.getPluginDescription()); - assertEquals("ro.fortsoft.pf4j.plugin.TestPlugin", plugin1.getPluginClass()); - assertEquals("0.0.1", plugin1.getVersion()); - assertEquals("Decebal Suiu", plugin1.getProvider()); - assertEquals(2, plugin1.getDependencies().size()); - assertEquals("test-plugin-2", plugin1.getDependencies().get(0).getPluginId()); - assertEquals("test-plugin-3", plugin1.getDependencies().get(1).getPluginId()); - assertEquals("~1.0", plugin1.getDependencies().get(1).getPluginVersionSupport()); - assertEquals("Apache-2.0", plugin1.getLicense()); - assertEquals(">=1", plugin1.getRequires()); - assertTrue(versionManager.satisfies(plugin1.getRequires(), "1.0.0")); - assertFalse(versionManager.satisfies(plugin1.getRequires(), "0.1.0")); - - assertEquals("test-plugin-2", plugin2.getPluginId()); - assertEquals("", plugin2.getPluginDescription()); - assertEquals("ro.fortsoft.pf4j.plugin.TestPlugin", plugin2.getPluginClass()); - assertEquals("0.0.1", plugin2.getVersion()); - assertEquals("Decebal Suiu", plugin2.getProvider()); - assertEquals(0, plugin2.getDependencies().size()); - assertEquals("*", plugin2.getRequires()); // Default is * - assertTrue(versionManager.satisfies(plugin2.getRequires(), "1.0.0")); - } - - @Test(expected = PluginException.class) - public void testFindNotFound() throws Exception { - PluginDescriptorFinder instance = new PropertiesPluginDescriptorFinder(); - instance.find(getPluginsRoot().resolve("test-plugin-3")); - } - - private List getPlugin1Properties() { - String[] lines = new String[] { - "plugin.id=test-plugin-1\n" - + "plugin.version=0.0.1\n" - + "plugin.description=Test Plugin 1\n" - + "plugin.provider=Decebal Suiu\n" - + "plugin.class=ro.fortsoft.pf4j.plugin.TestPlugin\n" - + "plugin.dependencies=test-plugin-2,test-plugin-3@~1.0\n" - + "plugin.requires=>=1\n" - + "plugin.license=Apache-2.0\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private List getPlugin2Properties() { - String[] lines = new String[] { - "plugin.id=test-plugin-2\n" - + "plugin.version=0.0.1\n" - + "plugin.provider=Decebal Suiu\n" - + "plugin.class=ro.fortsoft.pf4j.plugin.TestPlugin\n" - + "plugin.dependencies=\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private List getPlugin4Properties() { - String[] lines = new String[] { - "plugin.id=test-plugin-2\n" - + "plugin.version=0.0.1\n" - + "plugin.provider=Decebal Suiu\n" - + "plugin.dependencies=\n" - + "plugin.requires=*\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private List getPlugin5Properties() { - String[] lines = new String[] { - "plugin.id=test-plugin-2\n" - + "plugin.provider=Decebal Suiu\n" - + "plugin.class=ro.fortsoft.pf4j.plugin.TestPlugin\n" - + "plugin.dependencies=\n" - + "plugin.requires=*\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private List getPlugin6Properties() { - String[] lines = new String[] { - "plugin.version=0.0.1\n" - + "plugin.provider=Decebal Suiu\n" - + "plugin.class=ro.fortsoft.pf4j.plugin.TestPlugin\n" - + "plugin.dependencies=\n" - + "plugin.requires=*\n" - + "\n" - + "" - }; - - return Arrays.asList(lines); - } - - private Path getPluginsRoot() { - return testFolder.getRoot().toPath(); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/AnotherFailTestPlugin.java b/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/AnotherFailTestPlugin.java deleted file mode 100644 index 5cfe611..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/AnotherFailTestPlugin.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.plugin; - -import ro.fortsoft.pf4j.Plugin; - -/** - * - * @author Mario Franco - */ -public class AnotherFailTestPlugin extends Plugin { - - public AnotherFailTestPlugin() { - super(null); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/FailTestExtension.java b/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/FailTestExtension.java deleted file mode 100644 index 3832b7f..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/FailTestExtension.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.plugin; - -import ro.fortsoft.pf4j.Extension; - -/** - * @author Mario Franco - */ -@Extension -public class FailTestExtension implements TestExtensionPoint { - - public FailTestExtension(String name) { - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/FailTestPlugin.java b/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/FailTestPlugin.java deleted file mode 100644 index 0f4ba65..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/FailTestPlugin.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.plugin; - -/** - * - * @author Mario Franco - */ -public class FailTestPlugin { - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/MockPluginManager.java b/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/MockPluginManager.java deleted file mode 100644 index 139a0e8..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/MockPluginManager.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.plugin; - -import ro.fortsoft.pf4j.DefaultPluginClasspath; -import ro.fortsoft.pf4j.DefaultPluginDescriptorFinder; -import ro.fortsoft.pf4j.DefaultPluginManager; -import ro.fortsoft.pf4j.PluginDescriptorFinder; - -import java.nio.file.Path; - -/** - * Manager for testing - */ -public class MockPluginManager extends DefaultPluginManager { - - private PluginDescriptorFinder finder = new DefaultPluginDescriptorFinder(new DefaultPluginClasspath()); - - public MockPluginManager() { - super(); - } - - public MockPluginManager(Path root, PluginDescriptorFinder finder) { - super(root); - this.finder = finder; - } - - @Override - protected PluginDescriptorFinder getPluginDescriptorFinder() { - return finder; - } - - @Override - protected PluginDescriptorFinder createPluginDescriptorFinder() { - return finder; - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestExtension.java b/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestExtension.java deleted file mode 100644 index 181e90d..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestExtension.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.plugin; - -import ro.fortsoft.pf4j.Extension; - -/** - * @author Mario Franco - */ -@Extension -public class TestExtension implements TestExtensionPoint { - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestExtensionPoint.java b/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestExtensionPoint.java deleted file mode 100644 index 3c1ee1e..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestExtensionPoint.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.plugin; - -import ro.fortsoft.pf4j.ExtensionPoint; - -/** - * @author Mario Franco - */ -public interface TestExtensionPoint extends ExtensionPoint { - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestPlugin.java b/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestPlugin.java deleted file mode 100644 index 3557a16..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/plugin/TestPlugin.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2015 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.plugin; - -import ro.fortsoft.pf4j.Plugin; -import ro.fortsoft.pf4j.PluginWrapper; - -/** - * - * @author Mario Franco - */ -public class TestPlugin extends Plugin { - - public TestPlugin(PluginWrapper wrapper) { - super(wrapper); - } - -} diff --git a/pf4j/src/test/java/ro/fortsoft/pf4j/util/FileUtilsTest.java b/pf4j/src/test/java/ro/fortsoft/pf4j/util/FileUtilsTest.java deleted file mode 100644 index 6b98549..0000000 --- a/pf4j/src/test/java/ro/fortsoft/pf4j/util/FileUtilsTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2017 Decebal Suiu - * - * 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 ro.fortsoft.pf4j.util; - -import org.junit.Before; -import org.junit.Test; - -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Collections; - -import static org.junit.Assert.*; - -public class FileUtilsTest { - - private Path zipFile; - private Path tmpDir; - private Path propsFile; - - @Before - public void setup() throws IOException { - tmpDir = Files.createTempDirectory("pf4j-test"); - tmpDir.toFile().deleteOnExit(); - zipFile = tmpDir.resolve("my.zip").toAbsolutePath(); - propsFile = tmpDir.resolve("plugin.properties"); - URI file = URI.create("jar:file:"+zipFile.toString()); - try (FileSystem zipfs = FileSystems.newFileSystem(file, Collections.singletonMap("create", "true"))) { - Path propsInZip = zipfs.getPath("/plugin.properties"); - BufferedWriter br = new BufferedWriter(new FileWriter(propsFile.toString())); - br.write("plugin.id=test"); - br.newLine(); - br.write("plugin.version=1.2.3"); - br.newLine(); - br.write("plugin.class=foo.bar"); - br.close(); - Files.move(propsFile, propsInZip); - } - } - - @Test - public void expandIfZip() throws Exception { - Path unzipped = FileUtils.expandIfZip(zipFile); - assertEquals(tmpDir.resolve("my"), unzipped); - assertTrue(Files.exists(tmpDir.resolve("my/plugin.properties"))); - - // Non-zip file remains unchanged - assertEquals(propsFile, FileUtils.expandIfZip(propsFile)); - // File without .suffix - Path extra = Files.createFile(tmpDir.resolve("extra")); - assertEquals(extra, FileUtils.expandIfZip(extra)); - // Folder - Path folder = Files.createFile(tmpDir.resolve("folder")); - assertEquals(folder, FileUtils.expandIfZip(folder)); - } - -} diff --git a/pf4j/src/test/resources/log4j.properties b/pf4j/src/test/resources/log4j.properties index 0454ba2..7edd29c 100644 --- a/pf4j/src/test/resources/log4j.properties +++ b/pf4j/src/test/resources/log4j.properties @@ -3,13 +3,13 @@ log4j.rootLogger=DEBUG, Console # # PF4J log # -log4j.logger.ro.fortsoft.pf4j=DEBUG, Console +log4j.logger.org.pf4j=DEBUG, Console # !!! Put the bellow classes on level TRACE when you are in trouble -log4j.logger.ro.fortsoft.pf4j.PluginClassLoader=WARN, Console -log4j.logger.ro.fortsoft.pf4j.AbstractExtensionFinder=DEBUG, Console -log4j.additivity.ro.fortsoft.pf4j=false -log4j.additivity.ro.fortsoft.pf4j.PluginClassLoader=false -log4j.additivity.ro.fortsoft.pf4j.AbstractExtensionFinder=false +log4j.logger.org.pf4j.PluginClassLoader=WARN, Console +log4j.logger.org.pf4j.AbstractExtensionFinder=DEBUG, Console +log4j.additivity.org.pf4j=false +log4j.additivity.org.pf4j.PluginClassLoader=false +log4j.additivity.org.pf4j.AbstractExtensionFinder=false # # Appenders diff --git a/pom.xml b/pom.xml index 0b644c4..1a1fc51 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ 4.0.0 - ro.fortsoft.pf4j + org.pf4j pf4j-parent 1.4.0-SNAPSHOT pom -- cgit v1.2.3