You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PluginClassLoaderTest.java 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. /*
  2. * Copyright (C) 2012-present the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.pf4j;
  17. import org.junit.jupiter.api.AfterEach;
  18. import org.junit.jupiter.api.BeforeAll;
  19. import org.junit.jupiter.api.BeforeEach;
  20. import org.junit.jupiter.api.Test;
  21. import org.junit.jupiter.api.io.TempDir;
  22. import org.pf4j.test.PluginZip;
  23. import org.pf4j.util.FileUtils;
  24. import java.io.File;
  25. import java.io.IOException;
  26. import java.io.PrintWriter;
  27. import java.net.URISyntaxException;
  28. import java.net.URL;
  29. import java.nio.file.Files;
  30. import java.nio.file.Path;
  31. import java.nio.file.Paths;
  32. import java.util.Collections;
  33. import java.util.Enumeration;
  34. import java.util.List;
  35. import static org.junit.jupiter.api.Assertions.*;
  36. /**
  37. * @author Sebastian Lövdahl
  38. */
  39. public class PluginClassLoaderTest {
  40. private TestPluginManager pluginManager;
  41. private TestPluginManager pluginManagerParentFirst;
  42. private DefaultPluginDescriptor pluginDependencyDescriptor;
  43. private DefaultPluginDescriptor pluginDescriptor;
  44. private PluginClassLoader parentLastPluginClassLoader;
  45. private PluginClassLoader parentFirstPluginClassLoader;
  46. private PluginClassLoader parentLastPluginDependencyClassLoader;
  47. private PluginClassLoader parentFirstPluginDependencyClassLoader;
  48. @TempDir
  49. Path pluginsPath;
  50. @BeforeAll
  51. static void setUpGlobal() throws IOException, URISyntaxException {
  52. Path parentClassPathBase = Paths.get(PluginClassLoaderTest.class.getClassLoader().getResource(".").toURI());
  53. File metaInfFile = parentClassPathBase.resolve("META-INF").toFile();
  54. if (metaInfFile.mkdir()) {
  55. // Only delete the directory if this test created it, guarding for any future usages of the directory.
  56. metaInfFile.deleteOnExit();
  57. }
  58. createFile(parentClassPathBase.resolve("META-INF").resolve("file-only-in-parent"));
  59. createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-dependency-and-plugin"));
  60. createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-dependency"));
  61. createFile(parentClassPathBase.resolve("META-INF").resolve("file-in-both-parent-and-plugin"));
  62. }
  63. private static void createFile(Path pathToFile) throws IOException {
  64. File file = pathToFile.toFile();
  65. file.deleteOnExit();
  66. assertTrue(file.exists() || file.createNewFile(), "failed to create '" + pathToFile + "'");
  67. try (PrintWriter printWriter = new PrintWriter(file)) {
  68. printWriter.write("parent");
  69. }
  70. }
  71. @BeforeEach
  72. void setUp() throws IOException {
  73. pluginManager = new TestPluginManager(pluginsPath);
  74. pluginManagerParentFirst = new TestPluginManager(pluginsPath);
  75. pluginDependencyDescriptor = new DefaultPluginDescriptor();
  76. pluginDependencyDescriptor.setPluginId("myDependency");
  77. pluginDependencyDescriptor.setPluginVersion("1.2.3");
  78. pluginDependencyDescriptor.setPluginDescription("My plugin");
  79. pluginDependencyDescriptor.setDependencies("");
  80. pluginDependencyDescriptor.setProvider("Me");
  81. pluginDependencyDescriptor.setRequires("5.0.0");
  82. Path pluginDependencyPath = pluginsPath.resolve(pluginDependencyDescriptor.getPluginId() + "-" + pluginDependencyDescriptor.getVersion() + ".zip");
  83. PluginZip pluginDependencyZip = new PluginZip.Builder(pluginDependencyPath, pluginDependencyDescriptor.getPluginId())
  84. .pluginVersion(pluginDependencyDescriptor.getVersion())
  85. .addFile(Paths.get("classes/META-INF/dependency-file"), "dependency")
  86. .addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency-and-plugin"), "dependency")
  87. .addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency"), "dependency")
  88. .build();
  89. FileUtils.expandIfZip(pluginDependencyZip.path());
  90. PluginClasspath pluginDependencyClasspath = new DefaultPluginClasspath();
  91. parentLastPluginDependencyClassLoader = new PluginClassLoader(pluginManager, pluginDependencyDescriptor, PluginClassLoaderTest.class.getClassLoader());
  92. parentFirstPluginDependencyClassLoader = new PluginClassLoader(pluginManagerParentFirst, pluginDependencyDescriptor, PluginClassLoaderTest.class.getClassLoader(), true);
  93. pluginManager.addClassLoader(pluginDependencyDescriptor.getPluginId(), parentLastPluginDependencyClassLoader);
  94. pluginManagerParentFirst.addClassLoader(pluginDependencyDescriptor.getPluginId(), parentFirstPluginDependencyClassLoader);
  95. for (String classesDirectory : pluginDependencyClasspath.getClassesDirectories()) {
  96. File classesDirectoryFile = pluginDependencyZip.unzippedPath().resolve(classesDirectory).toFile();
  97. parentLastPluginDependencyClassLoader.addFile(classesDirectoryFile);
  98. parentFirstPluginDependencyClassLoader.addFile(classesDirectoryFile);
  99. }
  100. for (String jarsDirectory : pluginDependencyClasspath.getJarsDirectories()) {
  101. Path jarsDirectoryPath = pluginDependencyZip.unzippedPath().resolve(jarsDirectory);
  102. List<File> jars = FileUtils.getJars(jarsDirectoryPath);
  103. for (File jar : jars) {
  104. parentLastPluginDependencyClassLoader.addFile(jar);
  105. parentFirstPluginDependencyClassLoader.addFile(jar);
  106. }
  107. }
  108. pluginDescriptor = new DefaultPluginDescriptor();
  109. pluginDescriptor.setPluginId("myPlugin");
  110. pluginDescriptor.setPluginVersion("1.2.3");
  111. pluginDescriptor.setPluginDescription("My plugin");
  112. pluginDescriptor.setDependencies("myDependency");
  113. pluginDescriptor.setProvider("Me");
  114. pluginDescriptor.setRequires("5.0.0");
  115. Path pluginPath = pluginsPath.resolve(pluginDescriptor.getPluginId() + "-" + pluginDescriptor.getVersion() + ".zip");
  116. PluginZip pluginZip = new PluginZip.Builder(pluginPath, pluginDescriptor.getPluginId())
  117. .pluginVersion(pluginDescriptor.getVersion())
  118. .addFile(Paths.get("classes/META-INF/plugin-file"), "plugin")
  119. .addFile(Paths.get("classes/META-INF/file-in-both-parent-and-dependency-and-plugin"), "plugin")
  120. .addFile(Paths.get("classes/META-INF/file-in-both-parent-and-plugin"), "plugin")
  121. .build();
  122. FileUtils.expandIfZip(pluginZip.path());
  123. PluginClasspath pluginClasspath = new DefaultPluginClasspath();
  124. parentLastPluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, PluginClassLoaderTest.class.getClassLoader());
  125. parentFirstPluginClassLoader = new PluginClassLoader(pluginManager, pluginDescriptor, PluginClassLoaderTest.class.getClassLoader(), true);
  126. pluginManager.addClassLoader(pluginDescriptor.getPluginId(), parentLastPluginClassLoader);
  127. pluginManagerParentFirst.addClassLoader(pluginDescriptor.getPluginId(), parentFirstPluginClassLoader);
  128. for (String classesDirectory : pluginClasspath.getClassesDirectories()) {
  129. File classesDirectoryFile = pluginZip.unzippedPath().resolve(classesDirectory).toFile();
  130. parentLastPluginClassLoader.addFile(classesDirectoryFile);
  131. parentFirstPluginClassLoader.addFile(classesDirectoryFile);
  132. }
  133. for (String jarsDirectory : pluginClasspath.getJarsDirectories()) {
  134. Path jarsDirectoryPath = pluginZip.unzippedPath().resolve(jarsDirectory);
  135. List<File> jars = FileUtils.getJars(jarsDirectoryPath);
  136. for (File jar : jars) {
  137. parentLastPluginClassLoader.addFile(jar);
  138. parentFirstPluginClassLoader.addFile(jar);
  139. }
  140. }
  141. }
  142. @AfterEach
  143. void tearDown() {
  144. pluginManager = null;
  145. pluginDependencyDescriptor = null;
  146. pluginDescriptor = null;
  147. parentLastPluginClassLoader = null;
  148. parentFirstPluginClassLoader = null;
  149. }
  150. @Test
  151. void parentLastGetResourceNonExisting() {
  152. assertNull(parentLastPluginClassLoader.getResource("META-INF/non-existing-file"));
  153. }
  154. @Test
  155. void parentFirstGetResourceNonExisting() {
  156. assertNull(parentFirstPluginClassLoader.getResource("META-INF/non-existing-file"));
  157. }
  158. @Test
  159. void parentLastGetResourceExistsInParent() throws IOException, URISyntaxException {
  160. URL resource = parentLastPluginClassLoader.getResource("META-INF/file-only-in-parent");
  161. assertFirstLine("parent", resource);
  162. }
  163. @Test
  164. void parentFirstGetResourceExistsInParent() throws IOException, URISyntaxException {
  165. URL resource = parentFirstPluginClassLoader.getResource("META-INF/file-only-in-parent");
  166. assertFirstLine("parent", resource);
  167. }
  168. @Test
  169. void parentLastGetResourceExistsOnlyInPlugin() throws IOException, URISyntaxException {
  170. URL resource = parentLastPluginClassLoader.getResource("META-INF/plugin-file");
  171. assertFirstLine("plugin", resource);
  172. }
  173. @Test
  174. void parentFirstGetResourceExistsOnlyInPlugin() throws IOException, URISyntaxException {
  175. URL resource = parentFirstPluginClassLoader.getResource("META-INF/plugin-file");
  176. assertFirstLine("plugin", resource);
  177. }
  178. @Test
  179. void parentLastGetResourceExistsOnlyInDependnecy() throws IOException, URISyntaxException {
  180. URL resource = parentLastPluginClassLoader.getResource("META-INF/dependency-file");
  181. assertFirstLine("dependency", resource);
  182. }
  183. @Test
  184. void parentFirstGetResourceExistsOnlyInDependency() throws IOException, URISyntaxException {
  185. URL resource = parentFirstPluginClassLoader.getResource("META-INF/dependency-file");
  186. assertFirstLine("dependency", resource);
  187. }
  188. @Test
  189. void parentLastGetResourceExistsInBothParentAndPlugin() throws URISyntaxException, IOException {
  190. URL resource = parentLastPluginClassLoader.getResource("META-INF/file-in-both-parent-and-plugin");
  191. assertFirstLine("plugin", resource);
  192. }
  193. @Test
  194. void parentFirstGetResourceExistsInBothParentAndPlugin() throws URISyntaxException, IOException {
  195. URL resource = parentFirstPluginClassLoader.getResource("META-INF/file-in-both-parent-and-plugin");
  196. assertFirstLine("parent", resource);
  197. }
  198. @Test
  199. void parentLastGetResourceExistsInParentAndDependencyAndPlugin() throws URISyntaxException, IOException {
  200. URL resource = parentLastPluginClassLoader.getResource("META-INF/file-in-both-parent-and-dependency-and-plugin");
  201. assertFirstLine("plugin", resource);
  202. }
  203. @Test
  204. void parentFirstGetResourceExistsInParentAndDependencyAndPlugin() throws URISyntaxException, IOException {
  205. URL resource = parentFirstPluginClassLoader.getResource("META-INF/file-in-both-parent-and-dependency-and-plugin");
  206. assertFirstLine("parent", resource);
  207. }
  208. @Test
  209. void parentLastGetResourcesNonExisting() throws IOException {
  210. assertFalse(parentLastPluginClassLoader.getResources("META-INF/non-existing-file").hasMoreElements());
  211. }
  212. @Test
  213. void parentFirstGetResourcesNonExisting() throws IOException {
  214. assertFalse(parentFirstPluginClassLoader.getResources("META-INF/non-existing-file").hasMoreElements());
  215. }
  216. @Test
  217. void parentLastGetResourcesExistsInParent() throws IOException, URISyntaxException {
  218. Enumeration<URL> resources = parentLastPluginClassLoader.getResources("META-INF/file-only-in-parent");
  219. assertNumberOfResourcesAndFirstLineOfFirstElement(1, "parent", resources);
  220. }
  221. @Test
  222. void parentFirstGetResourcesExistsInParent() throws IOException, URISyntaxException {
  223. Enumeration<URL> resources = parentFirstPluginClassLoader.getResources("META-INF/file-only-in-parent");
  224. assertNumberOfResourcesAndFirstLineOfFirstElement(1, "parent", resources);
  225. }
  226. @Test
  227. void parentLastGetResourcesExistsOnlyInDependency() throws IOException, URISyntaxException {
  228. Enumeration<URL> resources = parentLastPluginClassLoader.getResources("META-INF/dependency-file");
  229. assertNumberOfResourcesAndFirstLineOfFirstElement(1, "dependency", resources);
  230. }
  231. @Test
  232. void parentFirstGetResourcesExistsOnlyInDependency() throws IOException, URISyntaxException {
  233. Enumeration<URL> resources = parentFirstPluginClassLoader.getResources("META-INF/dependency-file");
  234. assertNumberOfResourcesAndFirstLineOfFirstElement(1, "dependency", resources);
  235. }
  236. @Test
  237. void parentLastGetResourcesExistsOnlyInPlugin() throws IOException, URISyntaxException {
  238. Enumeration<URL> resources = parentLastPluginClassLoader.getResources("META-INF/plugin-file");
  239. assertNumberOfResourcesAndFirstLineOfFirstElement(1, "plugin", resources);
  240. }
  241. @Test
  242. void parentFirstGetResourcesExistsOnlyInPlugin() throws IOException, URISyntaxException {
  243. Enumeration<URL> resources = parentFirstPluginClassLoader.getResources("META-INF/plugin-file");
  244. assertNumberOfResourcesAndFirstLineOfFirstElement(1, "plugin", resources);
  245. }
  246. @Test
  247. void parentLastGetResourcesExistsInBothParentAndPlugin() throws URISyntaxException, IOException {
  248. Enumeration<URL> resources = parentLastPluginClassLoader.getResources("META-INF/file-in-both-parent-and-plugin");
  249. assertNumberOfResourcesAndFirstLineOfFirstElement(2, "plugin", resources);
  250. }
  251. @Test
  252. void parentFirstGetResourcesExistsInBothParentAndPlugin() throws URISyntaxException, IOException {
  253. Enumeration<URL> resources = parentFirstPluginClassLoader.getResources("META-INF/file-in-both-parent-and-plugin");
  254. assertNumberOfResourcesAndFirstLineOfFirstElement(2, "parent", resources);
  255. }
  256. @Test
  257. void parentLastGetResourcesExistsInParentAndDependencyAndPlugin() throws URISyntaxException, IOException {
  258. Enumeration<URL> resources = parentLastPluginClassLoader.getResources("META-INF/file-in-both-parent-and-dependency-and-plugin");
  259. assertNumberOfResourcesAndFirstLineOfFirstElement(3, "plugin", resources);
  260. }
  261. @Test
  262. void parentFirstGetResourcesExistsInParentAndDependencyAndPlugin() throws URISyntaxException, IOException {
  263. Enumeration<URL> resources = parentFirstPluginClassLoader.getResources("META-INF/file-in-both-parent-and-dependency-and-plugin");
  264. assertNumberOfResourcesAndFirstLineOfFirstElement(3, "parent", resources);
  265. }
  266. private static void assertFirstLine(String expected, URL resource) throws URISyntaxException, IOException {
  267. assertNotNull(resource);
  268. assertEquals(expected, Files.readAllLines(Paths.get(resource.toURI())).get(0));
  269. }
  270. private static void assertNumberOfResourcesAndFirstLineOfFirstElement(int expectedCount, String expectedFirstLine, Enumeration<URL> resources) throws URISyntaxException, IOException {
  271. List<URL> list = Collections.list(resources);
  272. assertEquals(expectedCount, list.size());
  273. URL firstResource = list.get(0);
  274. assertEquals(expectedFirstLine, Files.readAllLines(Paths.get(firstResource.toURI())).get(0));
  275. }
  276. class TestPluginManager extends DefaultPluginManager {
  277. public TestPluginManager(Path pluginsPath) {
  278. super(pluginsPath);
  279. }
  280. void addClassLoader(String pluginId, PluginClassLoader classLoader) {
  281. getPluginClassLoaders().put(pluginId, classLoader);
  282. }
  283. }
  284. }