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.

ClassloaderBuilder.java 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.classloader;
  21. import java.net.URL;
  22. import java.security.AccessController;
  23. import java.security.PrivilegedAction;
  24. import java.util.ArrayList;
  25. import java.util.Collection;
  26. import java.util.HashMap;
  27. import java.util.List;
  28. import java.util.Map;
  29. import static java.util.Collections.emptyList;
  30. /**
  31. * @since 0.1
  32. */
  33. public class ClassloaderBuilder {
  34. private final Map<String, ClassRealm> previouslyCreatedClassLoaders;
  35. private final Map<String, NewRealm> newRealmsByKey = new HashMap<>();
  36. public ClassloaderBuilder() {
  37. this(emptyList());
  38. }
  39. /**
  40. * Creates a new classloader builder that can use a collection of previously created
  41. * classloaders as parent or siblings when building the new classloaders.
  42. *
  43. * @param previouslyCreatedClassLoaders Collection of classloaders that can be used as a
  44. * parent or sibling. Must be of type {@link ClassRealm}.
  45. */
  46. public ClassloaderBuilder(Collection<ClassLoader> previouslyCreatedClassLoaders) {
  47. this.previouslyCreatedClassLoaders = new HashMap<>();
  48. for (ClassLoader cl : previouslyCreatedClassLoaders) {
  49. if (!(cl instanceof ClassRealm)) {
  50. throw new IllegalArgumentException("classloader not of type ClassRealm: " + cl);
  51. }
  52. ClassRealm classRealm = (ClassRealm) cl;
  53. this.previouslyCreatedClassLoaders.put(classRealm.getKey(), classRealm);
  54. }
  55. }
  56. public enum LoadingOrder {
  57. /**
  58. * Order: siblings, then parent, then self
  59. */
  60. PARENT_FIRST(ParentFirstStrategy.INSTANCE),
  61. /**
  62. * Order: siblings, then self, then parent
  63. */
  64. SELF_FIRST(SelfFirstStrategy.INSTANCE);
  65. private final Strategy strategy;
  66. LoadingOrder(Strategy strategy) {
  67. this.strategy = strategy;
  68. }
  69. }
  70. /**
  71. * Wrapper of {@link ClassRealm} as long as associations are not fully
  72. * defined
  73. */
  74. private static class NewRealm {
  75. private final ClassRealm realm;
  76. // key of the optional parent classloader
  77. private String parentKey;
  78. private final List<String> siblingKeys = new ArrayList<>();
  79. private final Map<String, Mask> associatedMasks = new HashMap<>();
  80. private NewRealm(ClassRealm realm) {
  81. this.realm = realm;
  82. }
  83. }
  84. /**
  85. * Declares a new classloader based on system classloader.
  86. */
  87. public ClassloaderBuilder newClassloader(String key) {
  88. return newClassloader(key, getSystemClassloader());
  89. }
  90. /**
  91. * Declares a new classloader based on a given parent classloader. Key must be unique. An
  92. * {@link IllegalArgumentException} is thrown if the key is already referenced.
  93. * <p/>
  94. * Default loading order is {@link LoadingOrder#PARENT_FIRST}.
  95. */
  96. public ClassloaderBuilder newClassloader(final String key, final ClassLoader baseClassloader) {
  97. if (newRealmsByKey.containsKey(key)) {
  98. throw new IllegalStateException(String.format("The classloader '%s' already exists. Can not create it twice.", key));
  99. }
  100. if (previouslyCreatedClassLoaders.containsKey(key)) {
  101. throw new IllegalStateException(String.format("The classloader '%s' already exists in the list of previously created classloaders."
  102. + " Can not create it twice.", key));
  103. }
  104. ClassRealm realm = AccessController.<PrivilegedAction<ClassRealm>>doPrivileged(() -> new ClassRealm(key, baseClassloader));
  105. realm.setStrategy(LoadingOrder.PARENT_FIRST.strategy);
  106. newRealmsByKey.put(key, new NewRealm(realm));
  107. return this;
  108. }
  109. public ClassloaderBuilder setParent(String key, String parentKey, Mask mask) {
  110. NewRealm newRealm = getOrFail(key);
  111. newRealm.parentKey = parentKey;
  112. newRealm.associatedMasks.put(parentKey, mask);
  113. return this;
  114. }
  115. public ClassloaderBuilder setParent(String key, ClassLoader parent, Mask mask) {
  116. NewRealm newRealm = getOrFail(key);
  117. newRealm.realm.setParent(new DefaultClassloaderRef(parent, mask));
  118. return this;
  119. }
  120. public ClassloaderBuilder addSibling(String key, String siblingKey, Mask mask) {
  121. NewRealm newRealm = getOrFail(key);
  122. newRealm.siblingKeys.add(siblingKey);
  123. newRealm.associatedMasks.put(siblingKey, mask);
  124. return this;
  125. }
  126. public ClassloaderBuilder addSibling(String key, ClassLoader sibling, Mask mask) {
  127. NewRealm newRealm = getOrFail(key);
  128. newRealm.realm.addSibling(new DefaultClassloaderRef(sibling, mask));
  129. return this;
  130. }
  131. public ClassloaderBuilder addURL(String key, URL url) {
  132. getOrFail(key).realm.addConstituent(url);
  133. return this;
  134. }
  135. public ClassloaderBuilder setMask(String key, Mask mask) {
  136. getOrFail(key).realm.setMask(mask);
  137. return this;
  138. }
  139. public ClassloaderBuilder setExportMask(String key, Mask mask) {
  140. getOrFail(key).realm.setExportMask(mask);
  141. return this;
  142. }
  143. public ClassloaderBuilder setLoadingOrder(String key, LoadingOrder order) {
  144. getOrFail(key).realm.setStrategy(order.strategy);
  145. return this;
  146. }
  147. /**
  148. * Returns the new classloaders, grouped by keys. The parent and sibling classloaders
  149. * that are already existed (see {@link #setParent(String, ClassLoader, Mask)}
  150. * and {@link #addSibling(String, ClassLoader, Mask)} are not included into result.
  151. */
  152. public Map<String, ClassLoader> build() {
  153. Map<String, ClassLoader> result = new HashMap<>();
  154. // all the classloaders are created. Associations can now be resolved.
  155. for (Map.Entry<String, NewRealm> entry : newRealmsByKey.entrySet()) {
  156. NewRealm newRealm = entry.getValue();
  157. if (newRealm.parentKey != null) {
  158. ClassRealm parent = getNewOrPreviousClassloader(newRealm.parentKey);
  159. Mask parentMask = newRealm.associatedMasks.get(newRealm.parentKey);
  160. parentMask = mergeWithExportMask(parentMask, newRealm.parentKey);
  161. newRealm.realm.setParent(new DefaultClassloaderRef(parent, parentMask));
  162. }
  163. for (String siblingKey : newRealm.siblingKeys) {
  164. ClassRealm sibling = getNewOrPreviousClassloader(siblingKey);
  165. Mask siblingMask = newRealm.associatedMasks.get(siblingKey);
  166. siblingMask = mergeWithExportMask(siblingMask, siblingKey);
  167. newRealm.realm.addSibling(new DefaultClassloaderRef(sibling, siblingMask));
  168. }
  169. result.put(newRealm.realm.getKey(), newRealm.realm);
  170. }
  171. return result;
  172. }
  173. private Mask mergeWithExportMask(Mask mask, String exportKey) {
  174. NewRealm newRealm = newRealmsByKey.get(exportKey);
  175. if (newRealm != null) {
  176. return Mask.builder().copy(mask).merge(newRealm.realm.getExportMask()).build();
  177. }
  178. ClassRealm realm = previouslyCreatedClassLoaders.get(exportKey);
  179. if (realm != null) {
  180. return Mask.builder().copy(mask).merge(realm.getExportMask()).build();
  181. }
  182. return mask;
  183. }
  184. private NewRealm getOrFail(String key) {
  185. NewRealm newRealm = newRealmsByKey.get(key);
  186. if (newRealm == null) {
  187. throw new IllegalStateException(String.format("The classloader '%s' does not exist", key));
  188. }
  189. return newRealm;
  190. }
  191. private ClassRealm getNewOrPreviousClassloader(String key) {
  192. NewRealm newRealm = newRealmsByKey.get(key);
  193. if (newRealm != null) {
  194. return newRealm.realm;
  195. }
  196. ClassRealm previousClassloader = previouslyCreatedClassLoaders.get(key);
  197. if (previousClassloader != null) {
  198. return previousClassloader;
  199. }
  200. throw new IllegalStateException(String.format("The classloader '%s' does not exist", key));
  201. }
  202. /**
  203. * JRE system classloader. In Oracle JVM:
  204. * - ClassLoader.getSystemClassLoader() is sun.misc.Launcher$AppClassLoader. It contains app classpath.
  205. * - ClassLoader.getSystemClassLoader().getParent() is sun.misc.Launcher$ExtClassLoader. It is the JRE core classloader.
  206. */
  207. private static ClassLoader getSystemClassloader() {
  208. ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
  209. ClassLoader systemParent = systemClassLoader.getParent();
  210. if (systemParent != null) {
  211. systemClassLoader = systemParent;
  212. }
  213. return systemClassLoader;
  214. }
  215. }