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.

PluginClassloaderFactory.java 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2018 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.core.platform;
  21. import java.io.File;
  22. import java.net.MalformedURLException;
  23. import java.net.URL;
  24. import java.util.Collection;
  25. import java.util.HashMap;
  26. import java.util.Map;
  27. import org.sonar.api.batch.ScannerSide;
  28. import org.sonar.api.ce.ComputeEngineSide;
  29. import org.sonar.api.server.ServerSide;
  30. import org.sonar.classloader.ClassloaderBuilder;
  31. import org.sonar.classloader.Mask;
  32. import static org.sonar.classloader.ClassloaderBuilder.LoadingOrder.PARENT_FIRST;
  33. import static org.sonar.classloader.ClassloaderBuilder.LoadingOrder.SELF_FIRST;
  34. /**
  35. * Builds the graph of classloaders to be used to instantiate plugins. It deals with:
  36. * <ul>
  37. * <li>isolation of plugins against core classes (except api)</li>
  38. * <li>sharing of some packages between plugins</li>
  39. * <li>loading of the libraries embedded in plugin JAR files (directory META-INF/libs)</li>
  40. * </ul>
  41. */
  42. @ScannerSide
  43. @ServerSide
  44. @ComputeEngineSide
  45. public class PluginClassloaderFactory {
  46. // underscores are used to not conflict with plugin keys (if someday a plugin key is "api")
  47. private static final String API_CLASSLOADER_KEY = "_api_";
  48. /**
  49. * Creates as many classloaders as requested by the input parameter.
  50. */
  51. public Map<PluginClassLoaderDef, ClassLoader> create(Collection<PluginClassLoaderDef> defs) {
  52. ClassLoader baseClassLoader = baseClassLoader();
  53. ClassloaderBuilder builder = new ClassloaderBuilder();
  54. builder.newClassloader(API_CLASSLOADER_KEY, baseClassLoader);
  55. builder.setMask(API_CLASSLOADER_KEY, apiMask());
  56. for (PluginClassLoaderDef def : defs) {
  57. builder.newClassloader(def.getBasePluginKey());
  58. builder.setParent(def.getBasePluginKey(), API_CLASSLOADER_KEY, new Mask());
  59. builder.setLoadingOrder(def.getBasePluginKey(), def.isSelfFirstStrategy() ? SELF_FIRST : PARENT_FIRST);
  60. for (File jar : def.getFiles()) {
  61. builder.addURL(def.getBasePluginKey(), fileToUrl(jar));
  62. }
  63. exportResources(def, builder, defs);
  64. }
  65. return build(defs, builder);
  66. }
  67. /**
  68. * A plugin can export some resources to other plugins
  69. */
  70. private void exportResources(PluginClassLoaderDef def, ClassloaderBuilder builder, Collection<PluginClassLoaderDef> allPlugins) {
  71. // export the resources to all other plugins
  72. builder.setExportMask(def.getBasePluginKey(), def.getExportMask());
  73. for (PluginClassLoaderDef other : allPlugins) {
  74. if (!other.getBasePluginKey().equals(def.getBasePluginKey())) {
  75. builder.addSibling(def.getBasePluginKey(), other.getBasePluginKey(), new Mask());
  76. }
  77. }
  78. }
  79. /**
  80. * Builds classloaders and verifies that all of them are correctly defined
  81. */
  82. private Map<PluginClassLoaderDef, ClassLoader> build(Collection<PluginClassLoaderDef> defs, ClassloaderBuilder builder) {
  83. Map<PluginClassLoaderDef, ClassLoader> result = new HashMap<>();
  84. Map<String, ClassLoader> classloadersByBasePluginKey = builder.build();
  85. for (PluginClassLoaderDef def : defs) {
  86. ClassLoader classloader = classloadersByBasePluginKey.get(def.getBasePluginKey());
  87. if (classloader == null) {
  88. throw new IllegalStateException(String.format("Fail to create classloader for plugin [%s]", def.getBasePluginKey()));
  89. }
  90. result.put(def, classloader);
  91. }
  92. return result;
  93. }
  94. ClassLoader baseClassLoader() {
  95. return getClass().getClassLoader();
  96. }
  97. private static URL fileToUrl(File file) {
  98. try {
  99. return file.toURI().toURL();
  100. } catch (MalformedURLException e) {
  101. throw new IllegalArgumentException(e);
  102. }
  103. }
  104. /**
  105. * The resources (packages) that API exposes to plugins. Other core classes (SonarQube, MyBatis, ...)
  106. * can't be accessed.
  107. * <p>To sum-up, these are the classes packaged in sonar-plugin-api.jar or available as
  108. * a transitive dependency of sonar-plugin-api</p>
  109. */
  110. private static Mask apiMask() {
  111. return new Mask()
  112. .addInclusion("org/sonar/api/")
  113. .addInclusion("org/sonar/check/")
  114. .addInclusion("org/codehaus/stax2/")
  115. .addInclusion("org/codehaus/staxmate/")
  116. .addInclusion("com/ctc/wstx/")
  117. .addInclusion("org/slf4j/")
  118. // SLF4J bridges. Do not let plugins re-initialize and configure their logging system
  119. .addInclusion("org/apache/commons/logging/")
  120. .addInclusion("org/apache/log4j/")
  121. .addInclusion("ch/qos/logback/")
  122. // Exposed by org.sonar.api.server.authentication.IdentityProvider
  123. .addInclusion("javax/servlet/")
  124. // required for some internal SonarSource plugins (billing, orchestrator, ...)
  125. .addInclusion("org/sonar/server/platform/")
  126. // required for commercial plugins at SonarSource
  127. .addInclusion("com/sonarsource/plugins/license/api/")
  128. // API exclusions
  129. .addExclusion("org/sonar/api/internal/");
  130. }
  131. }