001 /*
002 * SonarQube, open source software quality management tool.
003 * Copyright (C) 2008-2014 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * SonarQube is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * SonarQube is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public License
017 * along with this program; if not, write to the Free Software Foundation,
018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
019 */
020 package org.sonar.batch.bootstrap;
021
022 import com.google.common.base.Joiner;
023 import com.google.common.base.Splitter;
024 import com.google.common.collect.Lists;
025 import com.google.common.collect.Maps;
026 import org.apache.commons.lang.StringUtils;
027 import org.slf4j.Logger;
028 import org.slf4j.LoggerFactory;
029 import org.sonar.api.CoreProperties;
030 import org.sonar.api.Plugin;
031 import org.sonar.api.SonarPlugin;
032 import org.sonar.api.config.Settings;
033 import org.sonar.api.platform.PluginMetadata;
034 import org.sonar.api.platform.PluginRepository;
035 import org.sonar.core.plugins.PluginClassloaders;
036 import org.sonar.core.plugins.RemotePlugin;
037
038 import java.io.File;
039 import java.text.MessageFormat;
040 import java.util.Arrays;
041 import java.util.Collection;
042 import java.util.List;
043 import java.util.Map;
044 import java.util.Set;
045
046 import static com.google.common.collect.Lists.newArrayList;
047 import static com.google.common.collect.Sets.newHashSet;
048
049 public class BatchPluginRepository implements PluginRepository {
050
051 private static final Logger LOG = LoggerFactory.getLogger(BatchPluginRepository.class);
052 private static final String CORE_PLUGIN = "core";
053
054 private PluginsReferential pluginsReferential;
055 private Map<String, Plugin> pluginsByKey;
056 private Map<String, PluginMetadata> metadataByKey;
057 private Settings settings;
058 private PluginClassloaders classLoaders;
059 private final AnalysisMode analysisMode;
060 private final BatchPluginJarInstaller pluginInstaller;
061
062 public BatchPluginRepository(PluginsReferential pluginsReferential, Settings settings, AnalysisMode analysisMode,
063 BatchPluginJarInstaller pluginInstaller) {
064 this.pluginsReferential = pluginsReferential;
065 this.settings = settings;
066 this.analysisMode = analysisMode;
067 this.pluginInstaller = pluginInstaller;
068 }
069
070 public void start() {
071 LOG.info("Install plugins");
072 doStart(pluginsReferential.pluginList());
073
074 Map<PluginMetadata, SonarPlugin> localPlugins = pluginsReferential.localPlugins();
075 if (!localPlugins.isEmpty()) {
076 LOG.info("Install local plugins");
077 for (Map.Entry<PluginMetadata, SonarPlugin> pluginByMetadata : localPlugins.entrySet()) {
078 metadataByKey.put(pluginByMetadata.getKey().getKey(), pluginByMetadata.getKey());
079 pluginsByKey.put(pluginByMetadata.getKey().getKey(), pluginByMetadata.getValue());
080 }
081 }
082 }
083
084 void doStart(List<RemotePlugin> remotePlugins) {
085 PluginFilter filter = new PluginFilter(settings, analysisMode);
086 metadataByKey = Maps.newHashMap();
087 for (RemotePlugin remote : remotePlugins) {
088 if (filter.accepts(remote.getKey())) {
089 File pluginFile = pluginsReferential.pluginFile(remote);
090 PluginMetadata metadata = pluginInstaller.installToCache(pluginFile, remote.isCore());
091 if (StringUtils.isBlank(metadata.getBasePlugin()) || filter.accepts(metadata.getBasePlugin())) {
092 metadataByKey.put(metadata.getKey(), metadata);
093 } else {
094 LOG.debug("Excluded plugin: " + metadata.getKey());
095 }
096 }
097 }
098 classLoaders = new PluginClassloaders(Thread.currentThread().getContextClassLoader());
099 pluginsByKey = classLoaders.init(metadataByKey.values());
100 }
101
102 public void stop() {
103 if (classLoaders != null) {
104 classLoaders.clean();
105 classLoaders = null;
106 }
107 }
108
109 public Plugin getPlugin(String key) {
110 return pluginsByKey.get(key);
111 }
112
113 public Collection<PluginMetadata> getMetadata() {
114 return metadataByKey.values();
115 }
116
117 public PluginMetadata getMetadata(String pluginKey) {
118 return metadataByKey.get(pluginKey);
119 }
120
121 public Map<PluginMetadata, Plugin> getPluginsByMetadata() {
122 Map<PluginMetadata, Plugin> result = Maps.newHashMap();
123 for (Map.Entry<String, PluginMetadata> entry : metadataByKey.entrySet()) {
124 String pluginKey = entry.getKey();
125 PluginMetadata metadata = entry.getValue();
126 result.put(metadata, pluginsByKey.get(pluginKey));
127 }
128 return result;
129 }
130
131 static class PluginFilter {
132 private static final String PROPERTY_IS_DEPRECATED_MSG = "Property {0} is deprecated. Please use {1} instead.";
133 Set<String> whites = newHashSet(), blacks = newHashSet();
134
135 PluginFilter(Settings settings, AnalysisMode mode) {
136 if (settings.hasKey(CoreProperties.BATCH_INCLUDE_PLUGINS)) {
137 whites.addAll(Arrays.asList(settings.getStringArray(CoreProperties.BATCH_INCLUDE_PLUGINS)));
138 }
139 if (settings.hasKey(CoreProperties.BATCH_EXCLUDE_PLUGINS)) {
140 blacks.addAll(Arrays.asList(settings.getStringArray(CoreProperties.BATCH_EXCLUDE_PLUGINS)));
141 }
142 if (mode.isPreview()) {
143 // These default values are not supported by Settings because the class CorePlugin
144 // is not loaded yet.
145 if (settings.hasKey(CoreProperties.DRY_RUN_INCLUDE_PLUGINS)) {
146 LOG.warn(MessageFormat.format(PROPERTY_IS_DEPRECATED_MSG, CoreProperties.DRY_RUN_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS));
147 whites.addAll(propertyValues(settings,
148 CoreProperties.DRY_RUN_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS_DEFAULT_VALUE));
149 } else {
150 whites.addAll(propertyValues(settings,
151 CoreProperties.PREVIEW_INCLUDE_PLUGINS, CoreProperties.PREVIEW_INCLUDE_PLUGINS_DEFAULT_VALUE));
152 }
153 if (settings.hasKey(CoreProperties.DRY_RUN_EXCLUDE_PLUGINS)) {
154 LOG.warn(MessageFormat.format(PROPERTY_IS_DEPRECATED_MSG, CoreProperties.DRY_RUN_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS));
155 blacks.addAll(propertyValues(settings,
156 CoreProperties.DRY_RUN_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS_DEFAULT_VALUE));
157 } else {
158 blacks.addAll(propertyValues(settings,
159 CoreProperties.PREVIEW_EXCLUDE_PLUGINS, CoreProperties.PREVIEW_EXCLUDE_PLUGINS_DEFAULT_VALUE));
160 }
161 }
162 if (!whites.isEmpty()) {
163 LOG.info("Include plugins: " + Joiner.on(", ").join(whites));
164 }
165 if (!blacks.isEmpty()) {
166 LOG.info("Exclude plugins: " + Joiner.on(", ").join(blacks));
167 }
168 }
169
170 static List<String> propertyValues(Settings settings, String key, String defaultValue) {
171 String s = StringUtils.defaultIfEmpty(settings.getString(key), defaultValue);
172 return Lists.newArrayList(Splitter.on(",").trimResults().split(s));
173 }
174
175 boolean accepts(String pluginKey) {
176 if (CORE_PLUGIN.equals(pluginKey)) {
177 return true;
178 }
179
180 List<String> mergeList = newArrayList(blacks);
181 mergeList.removeAll(whites);
182 return mergeList.isEmpty() || !mergeList.contains(pluginKey);
183 }
184 }
185 }