/* * SonarQube * Copyright (C) 2009-2017 SonarSource SA * mailto:info AT sonarsource DOT com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package org.sonar.core.platform; import java.io.File; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.junit.Rule; import org.junit.Test; import org.sonar.api.server.rule.RulesDefinition; import org.sonar.api.utils.internal.JUnitTempFolder; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; public class PluginClassloaderFactoryTest { static final String BASE_PLUGIN_CLASSNAME = "org.sonar.plugins.base.BasePlugin"; static final String DEPENDENT_PLUGIN_CLASSNAME = "org.sonar.plugins.dependent.DependentPlugin"; static final String BASE_PLUGIN_KEY = "base"; static final String DEPENDENT_PLUGIN_KEY = "dependent"; @Rule public JUnitTempFolder temp = new JUnitTempFolder(); PluginClassloaderFactory factory = new PluginClassloaderFactory(temp); @Test public void create_isolated_classloader() { PluginClassLoaderDef def = basePluginDef(); Map<PluginClassLoaderDef, ClassLoader> map = factory.create(asList(def)); assertThat(map).containsOnlyKeys(def); ClassLoader classLoader = map.get(def); // plugin can access to API classes, and of course to its own classes ! assertThat(canLoadClass(classLoader, RulesDefinition.class.getCanonicalName())).isTrue(); assertThat(canLoadClass(classLoader, BASE_PLUGIN_CLASSNAME)).isTrue(); // plugin can not access to core classes assertThat(canLoadClass(classLoader, PluginClassloaderFactory.class.getCanonicalName())).isFalse(); assertThat(canLoadClass(classLoader, Test.class.getCanonicalName())).isFalse(); assertThat(canLoadClass(classLoader, StringUtils.class.getCanonicalName())).isFalse(); } @Test public void create_classloader_compatible_with_with_old_api_dependencies() { PluginClassLoaderDef def = basePluginDef(); def.setCompatibilityMode(true); ClassLoader classLoader = factory.create(asList(def)).get(def); // Plugin can access to API and its transitive dependencies as defined in version 5.1. // It can not access to core classes though, even if it was possible in previous versions. assertThat(canLoadClass(classLoader, RulesDefinition.class.getCanonicalName())).isTrue(); assertThat(canLoadClass(classLoader, StringUtils.class.getCanonicalName())).isTrue(); assertThat(canLoadClass(classLoader, BASE_PLUGIN_CLASSNAME)).isTrue(); assertThat(canLoadClass(classLoader, PluginClassloaderFactory.class.getCanonicalName())).isFalse(); } @Test public void classloader_exports_resources_to_other_classloaders() { PluginClassLoaderDef baseDef = basePluginDef(); PluginClassLoaderDef dependentDef = dependentPluginDef(); Map<PluginClassLoaderDef, ClassLoader> map = factory.create(asList(baseDef, dependentDef)); ClassLoader baseClassloader = map.get(baseDef); ClassLoader dependentClassloader = map.get(dependentDef); // base-plugin exports its API package to other plugins assertThat(canLoadClass(dependentClassloader, "org.sonar.plugins.base.api.BaseApi")).isTrue(); assertThat(canLoadClass(dependentClassloader, BASE_PLUGIN_CLASSNAME)).isFalse(); assertThat(canLoadClass(dependentClassloader, DEPENDENT_PLUGIN_CLASSNAME)).isTrue(); // dependent-plugin does not export its classes assertThat(canLoadClass(baseClassloader, DEPENDENT_PLUGIN_CLASSNAME)).isFalse(); assertThat(canLoadClass(baseClassloader, BASE_PLUGIN_CLASSNAME)).isTrue(); } private static PluginClassLoaderDef basePluginDef() { PluginClassLoaderDef def = new PluginClassLoaderDef(BASE_PLUGIN_KEY); def.addMainClass(BASE_PLUGIN_KEY, BASE_PLUGIN_CLASSNAME); def.getExportMask().addInclusion("org/sonar/plugins/base/api/"); def.addFiles(asList(fakePluginJar("base-plugin/target/base-plugin-0.1-SNAPSHOT.jar"))); return def; } private static PluginClassLoaderDef dependentPluginDef() { PluginClassLoaderDef def = new PluginClassLoaderDef(DEPENDENT_PLUGIN_KEY); def.addMainClass(DEPENDENT_PLUGIN_KEY, DEPENDENT_PLUGIN_CLASSNAME); def.getExportMask().addInclusion("org/sonar/plugins/dependent/api/"); def.addFiles(asList(fakePluginJar("dependent-plugin/target/dependent-plugin-0.1-SNAPSHOT.jar"))); return def; } private static File fakePluginJar(String path) { // Maven way File file = new File("src/test/projects/" + path); if (!file.exists()) { // Intellij way file = new File("sonar-core/src/test/projects/" + path); if (!file.exists()) { throw new IllegalArgumentException("Fake projects are not built: " + path); } } return file; } private static boolean canLoadClass(ClassLoader classloader, String classname) { try { classloader.loadClass(classname); return true; } catch (ClassNotFoundException e) { return false; } } }