diff options
author | simonbrandhof <simon.brandhof@gmail.com> | 2010-09-06 14:08:06 +0000 |
---|---|---|
committer | simonbrandhof <simon.brandhof@gmail.com> | 2010-09-06 14:08:06 +0000 |
commit | aeadc1f9129274949daaa57738c7c4550bdfbc7b (patch) | |
tree | 08dadf5ef7474fc41d1d48f74648f1ba8b55f34d /sonar-batch/src/main | |
download | sonarqube-aeadc1f9129274949daaa57738c7c4550bdfbc7b.tar.gz sonarqube-aeadc1f9129274949daaa57738c7c4550bdfbc7b.zip |
SONAR-236 remove deprecated code from checkstyle plugin + display default value of rule parameters in Q profile console
Diffstat (limited to 'sonar-batch/src/main')
35 files changed, 3382 insertions, 0 deletions
diff --git a/sonar-batch/src/main/java/org/sonar/batch/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/Batch.java new file mode 100644 index 00000000000..cf199af94be --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/Batch.java @@ -0,0 +1,114 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.configuration.Configuration; +import org.picocontainer.Characteristics; +import org.picocontainer.MutablePicoContainer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.Plugins; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.HttpDownloader; +import org.sonar.api.utils.IocContainer; +import org.sonar.api.utils.ServerHttpClient; +import org.sonar.batch.indexer.DefaultSonarIndex; +import org.sonar.core.plugin.JpaPluginDao; +import org.sonar.jpa.session.DatabaseSessionProvider; +import org.sonar.jpa.session.DriverDatabaseConnector; +import org.sonar.jpa.session.ThreadLocalDatabaseSessionFactory; + +import java.net.URLClassLoader; + +public class Batch { + + private static final Logger LOG = LoggerFactory.getLogger(Batch.class); + + private Configuration configuration; + private Object[] components; + + public Batch(Configuration configuration, Object... components) { + this.configuration = configuration; + this.components = components; + } + + public void execute() { + MutablePicoContainer container = null; + try { + container = buildPicoContainer(); + container.start(); + analyzeProjects(container); + + } finally { + if (container != null) { + container.stop(); + } + } + } + + private void analyzeProjects(MutablePicoContainer container) { + // a child container is built to ensure database connector is up + MutablePicoContainer batchContainer = container.makeChildContainer(); + batchContainer.as(Characteristics.CACHE).addComponent(ServerMetadata.class); + batchContainer.as(Characteristics.CACHE).addComponent(ProjectTree.class); + batchContainer.as(Characteristics.CACHE).addComponent(DefaultSonarIndex.class); + batchContainer.as(Characteristics.CACHE).addComponent(JpaPluginDao.class); + batchContainer.as(Characteristics.CACHE).addComponent(BatchPluginRepository.class); + batchContainer.as(Characteristics.CACHE).addComponent(Plugins.class); + batchContainer.as(Characteristics.CACHE).addComponent(ServerHttpClient.class); + batchContainer.as(Characteristics.CACHE).addComponent(HttpDownloader.class); + batchContainer.start(); + + ProjectTree projectTree = batchContainer.getComponent(ProjectTree.class); + DefaultSonarIndex index = batchContainer.getComponent(DefaultSonarIndex.class); + analyzeProject(batchContainer, index, projectTree.getRootProject()); + + // batchContainer is stopped by its parent + } + + private MutablePicoContainer buildPicoContainer() { + MutablePicoContainer container = IocContainer.buildPicoContainer(); + + register(container, configuration); + URLClassLoader fullClassloader = RemoteClassLoader.createForJdbcDriver(configuration).getClassLoader(); + // set as the current context classloader for hibernate, else it does not find the JDBC driver. + Thread.currentThread().setContextClassLoader(fullClassloader); + + register(container, new DriverDatabaseConnector(configuration, fullClassloader)); + register(container, ThreadLocalDatabaseSessionFactory.class); + container.as(Characteristics.CACHE).addAdapter(new DatabaseSessionProvider()); + for (Object component : components) { + register(container, component); + } + return container; + } + + private void register(MutablePicoContainer container, Object component) { + container.as(Characteristics.CACHE).addComponent(component); + } + + private void analyzeProject(MutablePicoContainer container, DefaultSonarIndex index, Project project) { + for (Project module : project.getModules()) { + analyzeProject(container, index, module); + } + LOG.info("------------- Analyzing " + project.getName()); + new ProjectBatch(container).execute(index, project); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java b/sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java new file mode 100644 index 00000000000..105686834e4 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java @@ -0,0 +1,95 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import com.google.common.collect.HashMultimap; +import org.picocontainer.MutablePicoContainer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.BatchExtension; +import org.sonar.api.Plugin; +import org.sonar.api.platform.PluginRepository; +import org.sonar.core.plugin.JpaPlugin; +import org.sonar.core.plugin.JpaPluginDao; +import org.sonar.core.plugin.JpaPluginFile; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class BatchPluginRepository extends PluginRepository { + + private Map<String, ClassLoader> classloaders; + private String baseUrl; + private JpaPluginDao dao; + + public BatchPluginRepository(JpaPluginDao dao, ServerMetadata server) { + this.dao= dao; + this.baseUrl = server.getUrl() + "/deploy/plugins/"; + } + + public void start() { + HashMultimap<String, URL> urlsByKey = HashMultimap.create(); + for (JpaPluginFile pluginFile : dao.getPluginFiles()) { + try { + String key = getClassloaderKey(pluginFile.getPluginKey()); + URL url = new URL(baseUrl + pluginFile.getPath()); + urlsByKey.put(key, url); + + } catch (MalformedURLException e) { + throw new RuntimeException("Can not build the classloader of the plugin " + pluginFile.getPluginKey(), e); + } + } + + classloaders = new HashMap<String, ClassLoader>(); + for (String key : urlsByKey.keySet()) { + Set<URL> urls = urlsByKey.get(key); + + Logger logger = LoggerFactory.getLogger(getClass()); + if (logger.isDebugEnabled()) { + logger.debug("Classloader of plugin " + key + ":"); + for (URL url : urls) { + logger.debug(" -> " + url); + } + } + classloaders.put(key, new RemoteClassLoader(urls, Thread.currentThread().getContextClassLoader()).getClassLoader()); + } + } + + private String getClassloaderKey(String pluginKey) { + return "sonar-plugin-" + pluginKey; + } + + public void registerPlugins(MutablePicoContainer pico) { + try { + for (JpaPlugin pluginMetadata : dao.getPlugins()) { + String classloaderKey = getClassloaderKey(pluginMetadata.getKey()); + Class claz = classloaders.get(classloaderKey).loadClass(pluginMetadata.getPluginClass()); + Plugin plugin = (Plugin) claz.newInstance(); + registerPlugin(pico, plugin, BatchExtension.class); + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/CheckProfileProvider.java b/sonar-batch/src/main/java/org/sonar/batch/CheckProfileProvider.java new file mode 100644 index 00000000000..3ec7d8b522b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/CheckProfileProvider.java @@ -0,0 +1,64 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.picocontainer.injectors.ProviderAdapter; +import org.sonar.api.checks.profiles.Check; +import org.sonar.api.checks.profiles.CheckProfile; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.ActiveRuleParam; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class CheckProfileProvider extends ProviderAdapter { + + private CheckProfile profile = null; + + public CheckProfile provide(RulesProfile ruleProfile) { + if (profile == null) { + profile = new CheckProfile(ruleProfile.getName(), ruleProfile.getLanguage()); + for (ActiveRule activeRule : ruleProfile.getActiveRules()) { + Check check = toCheck(activeRule); + profile.addCheck(check); + } + } + return profile; + } + + private Check toCheck(ActiveRule activeRule) { + Check check = new Check(activeRule.getPluginName(), activeRule.getRuleKey()); + check.setPriority(activeRule.getPriority().toCheckPriority()); + check.setProperties(toParameters(activeRule.getActiveRuleParams())); + return check; + } + + private Map<String, String> toParameters(List<ActiveRuleParam> params) { + Map<String, String> map = new HashMap<String, String>(); + if (params != null) { + for (ActiveRuleParam param : params) { + map.put(param.getRuleParam().getKey(), param.getValue()); + } + } + return map; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/CoreJob.java b/sonar-batch/src/main/java/org/sonar/batch/CoreJob.java new file mode 100644 index 00000000000..f7231d63e79 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/CoreJob.java @@ -0,0 +1,29 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.sonar.api.batch.SensorContext; +import org.sonar.api.resources.Project; + +public interface CoreJob { + + void execute(Project project, SensorContext context); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/CoreJobs.java b/sonar-batch/src/main/java/org/sonar/batch/CoreJobs.java new file mode 100644 index 00000000000..e3beb96ce2a --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/CoreJobs.java @@ -0,0 +1,43 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import java.util.ArrayList; +import java.util.List; + +/** + * Core batch extensions, instanciated by picocontainer. + */ +public final class CoreJobs { + + private CoreJobs() { + } + + public static List<Class<? extends CoreJob>> allJobs() { + List<Class<? extends CoreJob>> classes = new ArrayList<Class<? extends CoreJob>>(); + classes.add(MavenPluginsConfigurator.class); + classes.add(MavenPhaseExecutor.class); + classes.add(SensorsExecutor.class); + classes.add(DecoratorsExecutor.class); + classes.add(FinalizeSnapshotsJob.class); + classes.add(PostJobsExecutor.class); + return classes; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/DecoratorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/DecoratorsExecutor.java new file mode 100644 index 00000000000..f6f5233ae18 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/DecoratorsExecutor.java @@ -0,0 +1,81 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.BatchExtensionDictionnary; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.batch.indexer.DefaultSonarIndex; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class DecoratorsExecutor implements CoreJob { + + private DecoratorsSelector decoratorsSelector; + private DatabaseSession session; + private ViolationsDao violationsDao; + private static final Logger LOG = LoggerFactory.getLogger(DecoratorsExecutor.class); + private DefaultSonarIndex index; + + public DecoratorsExecutor(BatchExtensionDictionnary extensionDictionnary, DefaultSonarIndex index, DatabaseSession session, ViolationsDao violationsDao) { + this.decoratorsSelector = new DecoratorsSelector(extensionDictionnary); + this.session = session; + this.violationsDao = violationsDao; + this.index = index; + } + + + public void execute(Project project, SensorContext sensorContext) { + LoggerFactory.getLogger(DecoratorsExecutor.class).info("Execute decorators..."); + Collection<Decorator> decorators = decoratorsSelector.select(project); + + if (LOG.isDebugEnabled()) { + LOG.debug("Decorators: {}", StringUtils.join(decorators, " -> ")); + } + + decorateResource(project, decorators, true); + } + + private DecoratorContext decorateResource(Resource resource, Collection<Decorator> decorators, boolean executeDecorators) { + List<DecoratorContext> childrenContexts = new ArrayList<DecoratorContext>(); + for (Resource child : index.getChildren(resource)) { + boolean isModule = (child instanceof Project); + DefaultDecoratorContext childContext = (DefaultDecoratorContext) decorateResource(child, decorators, !isModule); + childrenContexts.add(childContext.setReadOnly(true)); + } + + DefaultDecoratorContext context = new DefaultDecoratorContext(resource, index, childrenContexts, session, violationsDao); + if (executeDecorators) { + for (Decorator decorator : decorators) { + decorator.decorate(resource, context); + } + } + return context; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java b/sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java new file mode 100644 index 00000000000..8f8c000099e --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java @@ -0,0 +1,62 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.sonar.api.batch.BatchExtensionDictionnary; +import org.sonar.api.batch.Decorator; +import org.sonar.api.batch.FormulaDecorator; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; + +import java.util.*; + +public class DecoratorsSelector { + + private BatchExtensionDictionnary dictionnary; + + public DecoratorsSelector(BatchExtensionDictionnary dictionnary) { + this.dictionnary = dictionnary; + } + + public Collection<Decorator> select(Project project) { + List<Decorator> decorators = new ArrayList<Decorator>(dictionnary.select(Decorator.class, project, false)); + Set<Metric> coveredMetrics = getMetricsCoveredByPlugins(decorators); + for (Metric metric : dictionnary.select(Metric.class)) { + if (metric.getFormula() != null && !coveredMetrics.contains(metric)) { + decorators.add(new FormulaDecorator(metric)); + } + } + + return dictionnary.sort(decorators); + } + + private Set<Metric> getMetricsCoveredByPlugins(Collection<Decorator> pluginDecorators) { + Set<Metric> coveredMetrics = new HashSet<Metric>(); + for (Decorator pluginDecorator : pluginDecorators) { + List dependents = dictionnary.getDependents(pluginDecorator); + for (Object dependent : dependents) { + if (dependent instanceof Metric) { + coveredMetrics.add((Metric) dependent); + } + } + } + return coveredMetrics; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java new file mode 100644 index 00000000000..05314d3040e --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java @@ -0,0 +1,177 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.sonar.api.batch.DecoratorContext; +import org.sonar.api.batch.Event; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.design.Dependency; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Violation; +import org.sonar.batch.indexer.Bucket; +import org.sonar.batch.indexer.DefaultSonarIndex; + +import java.util.*; + +public class DefaultDecoratorContext implements DecoratorContext { + + private DatabaseSession session; + private DefaultSonarIndex index; + private Resource resource; + private boolean readOnly = false; + + private List<DecoratorContext> childrenContexts; + private List<Violation> violations; + private ViolationsDao violationsDao; + + public DefaultDecoratorContext(Resource resource, + DefaultSonarIndex index, + List<DecoratorContext> childrenContexts, + DatabaseSession session, + ViolationsDao violationsDao) { + this.index = index; + this.session = session; + this.resource = resource; + this.childrenContexts = childrenContexts; + this.violationsDao = violationsDao; + } + + public DefaultDecoratorContext setReadOnly(boolean b) { + readOnly = b; + violations = null; + childrenContexts = null; + return this; + } + + public Project getProject() { + return index.getProject(); + } + + public List<DecoratorContext> getChildren() { + checkReadOnly("getModules"); + return childrenContexts; + } + + private void checkReadOnly(String methodName) { + if (readOnly) { + throw new IllegalStateException("Method DecoratorContext." + methodName + "() can not be executed on children."); + } + } + + public <M> M getMeasures(MeasuresFilter<M> filter) { + return index.getMeasures(resource, filter); + } + + public Measure getMeasure(Metric metric) { + return index.getMeasures(resource, MeasuresFilters.metric(metric)); + } + + public Collection<Measure> getChildrenMeasures(MeasuresFilter filter) { + List<Measure> result = new ArrayList<Measure>(); + for (DecoratorContext childContext : childrenContexts) { + Object childResult = childContext.getMeasures(filter); + if (childResult != null) { + if (childResult instanceof Collection) { + result.addAll((Collection) childResult); + } else { + result.add((Measure) childResult); + } + } + } + return result; + } + + public Collection<Measure> getChildrenMeasures(Metric metric) { + return getChildrenMeasures(MeasuresFilters.metric(metric)); + } + + public Resource getResource() { + return resource; + } + + public DecoratorContext saveMeasure(Measure measure) { + checkReadOnly("saveMeasure"); + index.saveMeasure(resource, measure); + return this; + } + + public DecoratorContext saveMeasure(Metric metric, Double value) { + checkReadOnly("saveMeasure"); + index.saveMeasure(resource, new Measure(metric, value)); + return this; + } + + + public List<Violation> getViolations() { + if (violations == null) { + Bucket bucket = index.getBucket(resource); + if (bucket != null && bucket.getSnapshotId() != null) { + violations = violationsDao.getViolations(resource, bucket.getSnapshotId()); + } + } + return violations; + } + + public Dependency saveDependency(Dependency dependency) { + checkReadOnly("saveDependency"); + return index.saveDependency(dependency); + } + + public Set<Dependency> getDependencies() { + return index.getDependencies(); + } + + public Collection<Dependency> getIncomingDependencies() { + return index.getIncomingEdges(resource); + } + + public Collection<Dependency> getOutgoingDependencies() { + return index.getOutgoingEdges(resource); + } + + protected DatabaseSession getSession() { + return session; + } + + public List<Event> getEvents() { + return index.getEvents(resource); + } + + public Event createEvent(String name, String description, String category, Date date) { + return index.createEvent(resource, name, description, category, date); + } + + public void deleteEvent(Event event) { + index.deleteEvent(event); + } + + public DefaultDecoratorContext saveViolation(Violation violation) { + if (violation.getResource() == null) { + violation.setResource(resource); + } + index.addViolation(violation); + return this; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java new file mode 100644 index 00000000000..de2629ed336 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java @@ -0,0 +1,155 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.sonar.api.batch.Event; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.design.Dependency; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.ProjectLink; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.Violation; +import org.sonar.batch.indexer.DefaultSonarIndex; + +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Set; + +public class DefaultSensorContext implements SensorContext { + + private DefaultSonarIndex index; + private Project project; + + public DefaultSensorContext(DefaultSonarIndex index, Project project) { + this.index = index; + this.project = project; + } + + public Project getProject() { + return project; + } + + public Measure getMeasure(Metric metric) { + return index.getMeasure(project, metric); + } + + public <M> M getMeasures(MeasuresFilter<M> filter) { + return index.getMeasures(project, filter); + } + + public Measure saveMeasure(Measure measure) { + return index.saveMeasure(project, measure); + } + + public Measure saveMeasure(Metric metric, Double value) { + return index.saveMeasure(project, new Measure(metric, value)); + } + + public Measure getMeasure(Resource resource, Metric metric) { + return index.getMeasure(resource, metric); + } + + public String saveResource(Resource resource) { + Resource persistedResource = index.addResource(resource); + if (persistedResource!=null) { + return persistedResource.getEffectiveKey(); + } + return null; + } + + public Resource getResource(Resource resource) { + return index.getResource(resource); + } + + public <M> M getMeasures(Resource resource, MeasuresFilter<M> filter) { + return index.getMeasures(resource, filter); + } + + public Measure saveMeasure(Resource resource, Metric metric, Double value) { + return index.saveMeasure(resourceOrProject(resource), new Measure(metric, value)); + } + + public Measure saveMeasure(Resource resource, Measure measure) { + return index.saveMeasure(resourceOrProject(resource), measure); + } + + public void saveViolation(Violation violation) { + if (violation.getResource()==null) { + violation.setResource(resourceOrProject(violation.getResource())); + } + index.addViolation(violation); + } + + public void saveViolations(Collection<Violation> violations) { + if (violations!=null) { + for (Violation violation : violations) { + saveViolation(violation); + } + } + } + + public Dependency saveDependency(Dependency dependency) { + return index.saveDependency(dependency); + } + + public Set<Dependency> getDependencies() { + return index.getDependencies(); + } + + public Collection<Dependency> getIncomingDependencies(Resource to) { + return index.getIncomingEdges(resourceOrProject(to)); + } + + public Collection<Dependency> getOutgoingDependencies(Resource from) { + return index.getOutgoingEdges(resourceOrProject(from)); + } + + public void saveSource(Resource resource, String source) { + index.setSource(resource, source); + } + + public void saveLink(ProjectLink link) { + index.saveLink(link); + } + + public void deleteLink(String key) { + index.deleteLink(key); + } + + public List<Event> getEvents(Resource resource) { + return index.getEvents(resource); + } + + public Event createEvent(Resource resource, String name, String description, String category, Date date) { + return index.createEvent(resource, name, description, category, date); + } + + public void deleteEvent(Event event) { + index.deleteEvent(event); + } + + private Resource resourceOrProject(Resource resource) { + return (resource!=null ? resource : project); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java new file mode 100644 index 00000000000..092b389f1f3 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java @@ -0,0 +1,116 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.sonar.api.batch.TimeMachine; +import org.sonar.api.batch.TimeMachineQuery; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.MeasureModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.measures.Measure; +import org.sonar.api.resources.Resource; +import org.sonar.batch.indexer.DefaultSonarIndex; +import org.sonar.jpa.dao.MeasuresDao; + +import java.util.*; +import javax.persistence.Query; + +public class DefaultTimeMachine implements TimeMachine { + + private DatabaseSession session; + private DefaultSonarIndex index; + private MeasuresDao measuresDao; + + public DefaultTimeMachine(DatabaseSession session, DefaultSonarIndex index, MeasuresDao measuresDao) { + this.session = session; + this.index = index; + this.measuresDao = measuresDao; + } + + public List<Measure> getMeasures(TimeMachineQuery query) { + List<Object[]> objects = execute(query, true); + List<Measure> result = new ArrayList<Measure>(); + + for (Object[] object : objects) { + MeasureModel model = (MeasureModel) object[0]; + Measure measure = model.toMeasure(); + measure.setDate((Date) object[1]); + result.add(measure); + } + return result; + } + + public List<Object[]> getMeasuresFields(TimeMachineQuery query) { + return execute(query, false); + } + + protected List execute(TimeMachineQuery query, boolean selectAllFields) { + Resource resource = index.getResource(query.getResource()); + if (resource == null) { + return Collections.emptyList(); + } + + StringBuilder sb = new StringBuilder(); + Map<String, Object> params = new HashMap<String, Object>(); + + if (selectAllFields) { + sb.append("SELECT m, s.createdAt "); + } else { + sb.append("SELECT s.createdAt, m.metric, m.value "); + } + sb.append(" FROM " + MeasureModel.class.getSimpleName() + " m, " + Snapshot.class.getSimpleName() + " s WHERE m.snapshotId=s.id AND s.resourceId=:resourceId AND s.status=:status AND m.characteristic IS NULL "); + params.put("resourceId", resource.getId()); + params.put("status", Snapshot.STATUS_PROCESSED); + + sb.append(" AND m.rule IS NULL AND m.rulePriority IS NULL AND m.rulesCategoryId IS NULL "); + if (query.getMetrics() != null) { + sb.append(" AND m.metric IN (:metrics) "); + params.put("metrics", measuresDao.getMetrics(query.getMetrics())); + } + if (query.isFromCurrentAnalysis()) { + sb.append(" AND s.createdAt>=:from "); + params.put("from", index.getProject().getAnalysisDate()); + + } else if (query.getFrom() != null) { + sb.append(" AND s.createdAt>=:from "); + params.put("from", query.getFrom()); + } + if (query.isToCurrentAnalysis()) { + sb.append(" AND s.createdAt<=:to "); + params.put("to", index.getProject().getAnalysisDate()); + + } else if (query.getTo() != null) { + sb.append(" AND s.createdAt<=:to "); + params.put("to", query.getTo()); + } + if (query.isOnlyLastAnalysis()) { + sb.append(" AND s.last=:last "); + params.put("last", Boolean.TRUE); + } + sb.append(" ORDER BY s.createdAt "); + + Query jpaQuery = session.createQuery(sb.toString()); + + for (Map.Entry<String, Object> entry : params.entrySet()) { + jpaQuery.setParameter(entry.getKey(), entry.getValue()); + } + return jpaQuery.getResultList(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/FinalizeSnapshotsJob.java b/sonar-batch/src/main/java/org/sonar/batch/FinalizeSnapshotsJob.java new file mode 100644 index 00000000000..e1763c5f403 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/FinalizeSnapshotsJob.java @@ -0,0 +1,122 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.Purge; +import org.sonar.api.batch.PurgeContext; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.TimeProfiler; +import org.sonar.core.purge.DefaultPurgeContext; + +import javax.persistence.Query; + +public class FinalizeSnapshotsJob implements CoreJob { + + private DatabaseSession session; + private Purge[] purges; + private ServerMetadata server; + private Snapshot snapshot; + + public FinalizeSnapshotsJob(ServerMetadata server, DatabaseSession session, Purge[] purges, Snapshot snapshot) { + this.session = session; + this.purges = purges; + this.server = server; + this.snapshot = snapshot; + } + + public void execute(Project project, SensorContext context) { + if (shouldExecuteOn(project)) { + Snapshot previousLastSnapshot = getPreviousLastSnapshot(snapshot); + updateFlags(snapshot, previousLastSnapshot); + purge(snapshot, previousLastSnapshot); + } + } + + private boolean shouldExecuteOn(Project project) { + return project.isRoot(); + } + + private Snapshot getPreviousLastSnapshot(Snapshot snapshot) { + Query query = session.createQuery( + "SELECT s FROM " + Snapshot.class.getSimpleName() + " s " + + "WHERE s.last=true AND s.resourceId=:resourceId"); + query.setParameter("resourceId", snapshot.getResourceId()); + return session.getSingleResult(query, null); + } + + + private void updateFlags(Snapshot rootSnapshot, Snapshot previousLastSnapshot) { + if (previousLastSnapshot != null && previousLastSnapshot.getCreatedAt().before(rootSnapshot.getCreatedAt())) { + setFlags(previousLastSnapshot, false, null); + } + + boolean isLast = (previousLastSnapshot == null || previousLastSnapshot.getCreatedAt().before(rootSnapshot.getCreatedAt())); + setFlags(rootSnapshot, isLast, Snapshot.STATUS_PROCESSED); + LoggerFactory.getLogger(getClass()).info("ANALYSIS SUCCESSFUL, you can browse {}", server.getUrl()); + } + + private void setFlags(Snapshot snapshot, boolean last, String status) { + String hql = "UPDATE " + Snapshot.class.getSimpleName() + " SET last=:last"; + if (status != null) { + hql += ", status=:status "; + } + hql += " WHERE root_snapshot_id=:rootId OR id=:rootId OR (path LIKE :path AND root_snapshot_id=:pathRootId)"; + + Query query = session.createQuery(hql); + if (status != null) { + query.setParameter("status", status); + snapshot.setStatus(status); + } + query.setParameter("last", last); + query.setParameter("rootId", snapshot.getId()); + query.setParameter("path", snapshot.getPath() + snapshot.getId() + ".%"); + query.setParameter("pathRootId", (snapshot.getRootId() == null ? snapshot.getId() : snapshot.getRootId())); + query.executeUpdate(); + session.commit(); + + snapshot.setLast(last); + } + + private void purge(Snapshot currentSnapshot, Snapshot previousLastSnapshot) { + final Logger logger = LoggerFactory.getLogger(FinalizeSnapshotsJob.class); + TimeProfiler profiler = new TimeProfiler(logger).start("Database optimization"); + PurgeContext context = createPurgeContext(currentSnapshot, previousLastSnapshot); + logger.debug("Snapshots to purge: " + context); + for (Purge purge : purges) { + logger.debug("Executing {}...", purge.getClass().getName()); + purge.purge(context); + } + profiler.stop(); + } + + private PurgeContext createPurgeContext(Snapshot currentSnapshot, Snapshot previousLastSnapshot) { + DefaultPurgeContext context = new DefaultPurgeContext(currentSnapshot); + if (previousLastSnapshot != null && previousLastSnapshot.getCreatedAt().before(currentSnapshot.getCreatedAt())) { + context.setLastSnapshotId(previousLastSnapshot.getId()); + } + return context; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/MavenPhaseExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/MavenPhaseExecutor.java new file mode 100644 index 00000000000..78b490b5428 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/MavenPhaseExecutor.java @@ -0,0 +1,42 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.resources.Project; + +public class MavenPhaseExecutor implements CoreJob { + + public static final String PROP_PHASE = "sonar.phase"; + + private MavenPluginExecutor executor; + + public MavenPhaseExecutor(MavenPluginExecutor executor) { + this.executor = executor; + } + + public void execute(Project project, SensorContext context) { + String mavenPhase = (String) project.getProperty(PROP_PHASE); + if (!StringUtils.isBlank(mavenPhase)) { + executor.execute(project, mavenPhase); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/MavenPluginExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/MavenPluginExecutor.java new file mode 100644 index 00000000000..12404e58534 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/MavenPluginExecutor.java @@ -0,0 +1,32 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.sonar.api.BatchComponent; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.resources.Project; + +public interface MavenPluginExecutor extends BatchComponent { + + void execute(Project project, String goal); + + MavenPluginHandler execute(Project project, MavenPluginHandler handler); + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/MavenPluginsConfigurator.java b/sonar-batch/src/main/java/org/sonar/batch/MavenPluginsConfigurator.java new file mode 100644 index 00000000000..ea263a21325 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/MavenPluginsConfigurator.java @@ -0,0 +1,75 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.io.IOUtils; +import org.apache.maven.project.MavenProject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.BatchExtensionDictionnary; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.maven.MavenPlugin; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.SonarException; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; + +public class MavenPluginsConfigurator implements CoreJob { + + private BatchExtensionDictionnary dictionnary = null; + + public MavenPluginsConfigurator(BatchExtensionDictionnary dictionnary) { + this.dictionnary = dictionnary; + } + + public void execute(Project project, SensorContext context) { + Logger logger = LoggerFactory.getLogger(getClass()); + logger.info("Configure maven plugins..."); + + for (MavenPluginHandler handler : dictionnary.selectMavenPluginHandlers(project)) { + logger.debug("Configure {}...", handler); + configureHandler(project, handler); + } + savePom(project); + } + + protected void configureHandler(Project project, MavenPluginHandler handler) { + MavenPlugin plugin = MavenPlugin.registerPlugin(project.getPom(), handler.getGroupId(), handler.getArtifactId(), handler.getVersion(), handler.isFixedVersion()); + handler.configure(project, plugin); + } + + protected void savePom(Project project) { + MavenProject pom = project.getPom(); + File targetPom = new File(project.getFileSystem().getSonarWorkingDirectory(), "sonar-pom.xml"); + FileWriter fileWriter = null; + try { + fileWriter = new FileWriter(targetPom, false); + pom.writeModel(fileWriter); + + } catch (IOException e) { + throw new SonarException("Can not save pom to " + targetPom, e); + } finally { + IOUtils.closeQuietly(fileWriter); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/PostJobsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/PostJobsExecutor.java new file mode 100644 index 00000000000..1df8d3fd773 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/PostJobsExecutor.java @@ -0,0 +1,81 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.BatchExtensionDictionnary; +import org.sonar.api.batch.PostJob; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.maven.DependsUponMavenPlugin; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.resources.Project; + +import java.util.Collection; + +public class PostJobsExecutor implements CoreJob { + private static final Logger LOG = LoggerFactory.getLogger(PostJobsExecutor.class); + + private Collection<PostJob> postJobs; + private MavenPluginExecutor mavenExecutor; + + public PostJobsExecutor(Project project, BatchExtensionDictionnary selector, MavenPluginExecutor mavenExecutor) { + postJobs = selector.select(PostJob.class, project, true); + this.mavenExecutor = mavenExecutor; + } + + protected PostJobsExecutor(Collection<PostJob> postJobs, MavenPluginExecutor mavenExecutor) { + this.postJobs = postJobs; + this.mavenExecutor = mavenExecutor; + } + + public void execute(Project project, SensorContext context) { + if (shouldExecuteOn(project)) { + logPostJobs(); + + for (PostJob postJob : postJobs) { + LOG.info("Executing post-job {}", postJob.getClass()); + executeMavenPlugin(project, postJob); + postJob.executeOn(project, context); + } + } + } + + private void logPostJobs() { + if (LOG.isDebugEnabled()) { + LOG.debug("Post-jobs : {}", StringUtils.join(postJobs, " -> ")); + } + } + + private boolean shouldExecuteOn(Project project) { + return postJobs != null && project.isRoot(); + } + + + private void executeMavenPlugin(Project project, PostJob job) { + if (job instanceof DependsUponMavenPlugin) { + MavenPluginHandler handler = ((DependsUponMavenPlugin) job).getMavenPluginHandler(project); + if (handler != null) { + mavenExecutor.execute(project, handler); + } + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProfileProvider.java b/sonar-batch/src/main/java/org/sonar/batch/ProfileProvider.java new file mode 100644 index 00000000000..2059f562c69 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ProfileProvider.java @@ -0,0 +1,66 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.picocontainer.injectors.ProviderAdapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.jpa.dao.ProfilesDao; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.resources.Project; +import org.sonar.api.rules.ActiveRule; + +public class ProfileProvider extends ProviderAdapter { + + public static final String PARAM_PROFILE = "sonar.profile"; + + private static final Logger LOG = LoggerFactory.getLogger(ProfileProvider.class); + private RulesProfile profile; + + public RulesProfile provide(Project project, ProfilesDao dao) { + if (profile == null) { + String profileName = (String) project.getProperty(PARAM_PROFILE); + if (profileName == null) { + Project root = project.getRoot(); + profile = dao.getActiveProfile(root.getLanguageKey(), root.getKey()); + if (profile == null) { + throw new RuntimeException("Quality profile not found for " + root.getKey() + ", language " + root.getLanguageKey()); + } + + } else { + profile = dao.getProfile(project.getLanguageKey(), profileName); + if (profile == null) { + throw new RuntimeException("Quality profile not found : " + profileName + ", language " + project.getLanguageKey()); + } + } + + // hack to lazy initialize the profile collections + profile.getActiveRules().size(); + for (ActiveRule activeRule : profile.getActiveRules()) { + activeRule.getActiveRuleParams().size(); + activeRule.getRule().getParams().size(); + } + profile.getAlerts().size(); + + LOG.info("Selected quality profile : {}", profile); + } + return profile; + } +}
\ No newline at end of file diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java new file mode 100644 index 00000000000..2f0894a2c61 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java @@ -0,0 +1,141 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.picocontainer.Characteristics; +import org.picocontainer.MutablePicoContainer; +import org.sonar.api.batch.BatchExtensionDictionnary; +import org.sonar.api.batch.ProjectClasspath; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.Metric; +import org.sonar.api.measures.Metrics; +import org.sonar.api.resources.Languages; +import org.sonar.api.resources.Project; +import org.sonar.api.rules.DefaultRulesManager; +import org.sonar.api.utils.IocContainer; +import org.sonar.batch.indexer.DefaultSonarIndex; +import org.sonar.core.qualitymodel.DefaultModelProvider; +import org.sonar.core.rule.DefaultRuleProvider; +import org.sonar.jpa.dao.*; + +public class ProjectBatch { + + private MutablePicoContainer globalContainer; + private MutablePicoContainer batchContainer; + + public ProjectBatch(MutablePicoContainer container) { + this.globalContainer = container; + } + + public void execute(DefaultSonarIndex index, Project project) { + try { + startChildContainer(index, project); + SensorContext sensorContext = batchContainer.getComponent(SensorContext.class); + for (Class<? extends CoreJob> clazz : CoreJobs.allJobs()) { + CoreJob job = getComponent(clazz); + job.execute(project, sensorContext); + commit(); + } + + } finally { + index.clear(); + stop(); + } + } + + public void startChildContainer(DefaultSonarIndex index, Project project) { + batchContainer = globalContainer.makeChildContainer(); + batchContainer.getComponent(BatchPluginRepository.class).registerPlugins(batchContainer); + + batchContainer.as(Characteristics.CACHE).addComponent(project); + batchContainer.as(Characteristics.CACHE).addComponent(project.getPom()); + batchContainer.as(Characteristics.CACHE).addComponent(ProjectClasspath.class); + batchContainer.as(Characteristics.CACHE).addComponent(index.getBucket(project).getSnapshot()); + batchContainer.as(Characteristics.CACHE).addComponent(project.getConfiguration()); + + batchContainer.as(Characteristics.CACHE).addComponent(DaoFacade.class); + batchContainer.as(Characteristics.CACHE).addComponent(RulesDao.class); + batchContainer.as(Characteristics.CACHE).addComponent(org.sonar.api.database.daos.RulesDao.class); + batchContainer.as(Characteristics.CACHE).addComponent(MeasuresDao.class); + batchContainer.as(Characteristics.CACHE).addComponent(org.sonar.api.database.daos.MeasuresDao.class); + batchContainer.as(Characteristics.CACHE).addComponent(ProfilesDao.class); + batchContainer.as(Characteristics.CACHE).addComponent(AsyncMeasuresDao.class); + batchContainer.as(Characteristics.CACHE).addComponent(AsyncMeasuresService.class); + batchContainer.as(Characteristics.CACHE).addComponent(DefaultRulesManager.class); + batchContainer.as(Characteristics.CACHE).addComponent(DefaultSensorContext.class); + batchContainer.as(Characteristics.CACHE).addComponent(Languages.class); + batchContainer.as(Characteristics.CACHE).addComponent(BatchExtensionDictionnary.class); + batchContainer.as(Characteristics.CACHE).addComponent(DefaultTimeMachine.class); + batchContainer.as(Characteristics.CACHE).addComponent(ViolationsDao.class); + batchContainer.as(Characteristics.CACHE).addComponent(ViolationFilters.class); + batchContainer.as(Characteristics.CACHE).addComponent(ResourceFilters.class); + batchContainer.as(Characteristics.CACHE).addComponent(DefaultModelProvider.class); + batchContainer.as(Characteristics.CACHE).addComponent(DefaultRuleProvider.class); + batchContainer.addAdapter(new ProfileProvider()); + batchContainer.addAdapter(new CheckProfileProvider()); + loadCoreComponents(batchContainer); + batchContainer.as(Characteristics.CACHE).addComponent(new IocContainer(batchContainer)); + batchContainer.start(); + + // post-initializations + project.setLanguage(getComponent(Languages.class).get(project.getLanguageKey())); + index.selectProject(project, getComponent(ResourceFilters.class), getComponent(ViolationFilters.class), getComponent(MeasuresDao.class), getComponent(ViolationsDao.class)); + } + + private void loadCoreComponents(MutablePicoContainer container) { + for (Class<?> clazz : CoreJobs.allJobs()) { + container.as(Characteristics.CACHE).addComponent(clazz); + } + for (Metric metric : CoreMetrics.getMetrics()) { + container.as(Characteristics.CACHE).addComponent(metric.getKey(), metric); + } + for (Metrics metricRepo : container.getComponents(Metrics.class)) { + for (Metric metric : metricRepo.getMetrics()) { + container.as(Characteristics.CACHE).addComponent(metric.getKey(), metric); + } + } + } + + private void stop() { + if (batchContainer != null) { + commit(); + try { + globalContainer.removeChildContainer(batchContainer); + batchContainer.stop(); + batchContainer=null; + } catch (Exception e) { + // do not log + } + } + } + + public void commit() { + getComponent(DatabaseSession.class).commit(); + } + + public <T> T getComponent(Class<T> clazz) { + if (batchContainer != null) { + return batchContainer.getComponent(clazz); + } + return globalContainer.getComponent(clazz); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectBuilder.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectBuilder.java new file mode 100644 index 00000000000..3389db5e268 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectBuilder.java @@ -0,0 +1,150 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.configuration.*; +import org.apache.commons.lang.time.DateUtils; +import org.apache.maven.project.MavenProject; +import org.sonar.api.CoreProperties; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.DefaultProjectFileSystem; +import org.sonar.api.resources.Java; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.SonarException; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class ProjectBuilder { + + private DatabaseSession databaseSession; + + public ProjectBuilder(DatabaseSession databaseSession) { + this.databaseSession = databaseSession; + } + + public Project create(MavenProject pom) { + Configuration configuration = getStartupConfiguration(pom); + return new Project(loadProjectKey(pom), loadProjectBranch(configuration), pom.getName()) + .setPom(pom) + .setDescription(pom.getDescription()) + .setPackaging(pom.getPackaging()); + } + + Configuration getStartupConfiguration(MavenProject pom) { + CompositeConfiguration configuration = new CompositeConfiguration(); + configuration.addConfiguration(new SystemConfiguration()); + configuration.addConfiguration(new EnvironmentConfiguration()); + configuration.addConfiguration(new MapConfiguration(pom.getModel().getProperties())); + return configuration; + } + + String loadProjectKey(MavenProject pom) { + return new StringBuilder().append(pom.getGroupId()).append(":").append(pom.getArtifactId()).toString(); + } + + String loadProjectBranch(Configuration configuration) { + return configuration.getString(CoreProperties.PROJECT_BRANCH_PROPERTY, configuration.getString("branch" /* deprecated property */)); + } + + + public void configure(Project project) { + ProjectConfiguration projectConfiguration = new ProjectConfiguration(databaseSession, project); + configure(project, projectConfiguration); + } + + + void configure(Project project, Configuration projectConfiguration) { + Date analysisDate = loadAnalysisDate(projectConfiguration); + project.setConfiguration(projectConfiguration) + .setExclusionPatterns(loadExclusionPatterns(projectConfiguration)) + .setAnalysisDate(analysisDate) + .setLatestAnalysis(isLatestAnalysis(project.getKey(), analysisDate)) + .setAnalysisVersion(loadAnalysisVersion(projectConfiguration, project.getPom())) + .setAnalysisType(loadAnalysisType(projectConfiguration)) + .setLanguageKey(loadLanguageKey(projectConfiguration)) + .setFileSystem(new DefaultProjectFileSystem(project)); + } + + static String[] loadExclusionPatterns(Configuration configuration) { + String[] exclusionPatterns = configuration.getStringArray(CoreProperties.PROJECT_EXCLUSIONS_PROPERTY); + if (exclusionPatterns == null) { + exclusionPatterns = new String[0]; + } + return exclusionPatterns; + } + + boolean isLatestAnalysis(String projectKey, Date analysisDate) { + ResourceModel persistedProject = databaseSession.getSingleResult(ResourceModel.class, "key", projectKey, "enabled", true); + if (persistedProject != null) { + Snapshot lastSnapshot = databaseSession.getSingleResult(Snapshot.class, "resourceId", persistedProject.getId(), "last", true); + return lastSnapshot == null || lastSnapshot.getCreatedAt().before(analysisDate); + } + return true; + } + + + Date loadAnalysisDate(Configuration configuration) { + String formattedDate = configuration.getString(CoreProperties.PROJECT_DATE_PROPERTY); + if (formattedDate == null) { + return new Date(); + } + + DateFormat format = new SimpleDateFormat("yyyy-MM-dd"); + try { + // see SONAR-908 make sure that a time is defined for the date. + Date date = DateUtils.setHours(format.parse(formattedDate), 0); + return DateUtils.setMinutes(date, 1); + + } catch (ParseException e) { + throw new SonarException("The property " + CoreProperties.PROJECT_DATE_PROPERTY + " does not respect the format yyyy-MM-dd (for example 2008-05-23) : " + formattedDate, e); + } + } + + Project.AnalysisType loadAnalysisType(Configuration configuration) { + String value = configuration.getString(CoreProperties.DYNAMIC_ANALYSIS_PROPERTY); + if (value == null) { + return (configuration.getBoolean("sonar.light", false) ? Project.AnalysisType.STATIC : Project.AnalysisType.DYNAMIC); + } + if ("true".equals(value)) { + return Project.AnalysisType.DYNAMIC; + } + if ("reuseReports".equals(value)) { + return Project.AnalysisType.REUSE_REPORTS; + } + return Project.AnalysisType.STATIC; + } + + String loadAnalysisVersion(Configuration configuration, MavenProject pom) { + String version = configuration.getString(CoreProperties.PROJECT_VERSION_PROPERTY); + if (version == null && pom != null) { + version = pom.getVersion(); + } + return version; + } + + String loadLanguageKey(Configuration configuration) { + return configuration.getString(CoreProperties.PROJECT_LANGUAGE_PROPERTY, Java.KEY); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectConfiguration.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectConfiguration.java new file mode 100644 index 00000000000..4226a14fe7c --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectConfiguration.java @@ -0,0 +1,68 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.configuration.*; +import org.apache.maven.project.MavenProject; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.resources.Project; + +public class ProjectConfiguration extends CompositeConfiguration { + private PropertiesConfiguration runtimeConfiguration; + + public ProjectConfiguration(DatabaseSession session, Project project) { + runtimeConfiguration = new PropertiesConfiguration(); + addConfiguration(runtimeConfiguration); + + loadSystemSettings(); + loadProjectDatabaseSettings(session, project); + loadMavenSettings(project.getPom()); + loadGlobalDatabaseSettings(session); + } + + private void loadProjectDatabaseSettings(DatabaseSession session, Project project) { + addConfiguration(new ResourceDatabaseConfiguration(session, project.getKey())); + + Project parent = project.getParent(); + while (parent != null && parent.getKey() != null) { + addConfiguration(new ResourceDatabaseConfiguration(session, parent.getKey())); + parent = parent.getParent(); + } + } + + private void loadGlobalDatabaseSettings(DatabaseSession session) { + addConfiguration(new org.sonar.api.database.configuration.DatabaseConfiguration(session)); + } + + private void loadSystemSettings() { + addConfiguration(new SystemConfiguration()); + addConfiguration(new EnvironmentConfiguration()); + } + + private void loadMavenSettings(MavenProject pom) { + addConfiguration(new MapConfiguration(pom.getModel().getProperties())); + } + + @Override + public void setProperty(String s, Object o) { + runtimeConfiguration.setProperty(s, o); + } +} + diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java new file mode 100644 index 00000000000..497c4656f58 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java @@ -0,0 +1,163 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; +import org.slf4j.LoggerFactory; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.resources.Project; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class ProjectTree { + + private List<Project> projects; + private List<MavenProject> poms; + private ProjectBuilder projectBuilder; + + public ProjectTree(MavenSession mavenSession, DatabaseSession databaseSession) { + this.poms = mavenSession.getSortedProjects(); + this.projectBuilder = new ProjectBuilder(databaseSession); + } + + /** + * for unit tests + */ + protected ProjectTree(ProjectBuilder projectBuilder, List<MavenProject> poms) { + this.projectBuilder = projectBuilder; + this.poms = poms; + } + + /** + * for unit tests + */ + protected ProjectTree(List<Project> projects) { + this.projects = new ArrayList<Project>(projects); + } + + public void start() throws IOException { + projects = Lists.newArrayList(); + Map<String, Project> paths = Maps.newHashMap(); // projects by canonical path + + for (MavenProject pom : poms) { + Project project = projectBuilder.create(pom); + projects.add(project); + paths.put(pom.getBasedir().getCanonicalPath(), project); + } + + for (Map.Entry<String, Project> entry : paths.entrySet()) { + Project project = entry.getValue(); + MavenProject pom = project.getPom(); + for (Object moduleId : pom.getModules()) { + File modulePath = new File(pom.getBasedir(), (String) moduleId); + Project module = paths.get(modulePath.getCanonicalPath()); + if (module != null) { + module.setParent(project); + } + } + } + + configureProjects(); + applyModuleExclusions(); + } + + private void configureProjects() { + for (Project project : projects) { + projectBuilder.configure(project); + } + } + + void applyModuleExclusions() { + for (Project project : projects) { + String[] excludedArtifactIds = project.getConfiguration().getStringArray("sonar.skippedModules"); + String[] includedArtifactIds = project.getConfiguration().getStringArray("sonar.includedModules"); + + Set<String> includedModulesIdSet = new HashSet<String>(); + Set<String> excludedModulesIdSet = new HashSet<String>(); + + if (includedArtifactIds != null) { + includedModulesIdSet.addAll(Arrays.asList(includedArtifactIds)); + } + + if (excludedArtifactIds != null) { + excludedModulesIdSet.addAll(Arrays.asList(excludedArtifactIds)); + includedModulesIdSet.removeAll(excludedModulesIdSet); + } + + if (!includedModulesIdSet.isEmpty()) { + for (Project currentProject : projects) { + if (!includedModulesIdSet.contains(currentProject.getPom().getArtifactId())) { + exclude(currentProject); + } + } + } else { + for (String excludedArtifactId : excludedModulesIdSet) { + Project excludedProject = getProjectByArtifactId(excludedArtifactId); + exclude(excludedProject); + } + } + } + + for (Iterator<Project> it = projects.iterator(); it.hasNext();) { + Project project = it.next(); + if (project.isExcluded()) { + LoggerFactory.getLogger(getClass()).info("Module {} is excluded from analysis", project.getName()); + project.removeFromParent(); + it.remove(); + } + } + } + + private void exclude(Project project) { + if (project != null) { + project.setExcluded(true); + for (Project module : project.getModules()) { + exclude(module); + } + } + } + + public List<Project> getProjects() { + return projects; + } + + public Project getProjectByArtifactId(String artifactId) { + for (Project project : projects) { + if (project.getPom().getArtifactId().equals(artifactId)) { + return project; + } + } + return null; + } + + public Project getRootProject() { + for (Project project : projects) { + if (project.getParent() == null) { + return project; + } + } + throw new IllegalStateException("Can not find the root project from the list of Maven modules"); + } +}
\ No newline at end of file diff --git a/sonar-batch/src/main/java/org/sonar/batch/RemoteClassLoader.java b/sonar-batch/src/main/java/org/sonar/batch/RemoteClassLoader.java new file mode 100644 index 00000000000..09987c09ecb --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/RemoteClassLoader.java @@ -0,0 +1,69 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.configuration.Configuration; + +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; + +/** + * Create classloader from remote URLs. + * + * IMPORTANT : it generates URLClassLoaders, which use the parent first delegation mode. It finds classes in the parent classloader THEN + * in the plugin classloader. + * Using a child-first delegation mode can avoid some conflicts with API dependencies (xml-api, antlr). It's + * not possible for now, but it would be simple to implement by replacing the URLClassLoader by + * the class ChildFirstClassLoader (see http://articles.qos.ch/classloader.html) + */ +public class RemoteClassLoader { + + private URLClassLoader classLoader; + + public RemoteClassLoader(URL[] urls, ClassLoader parent) { + ClassLoader parentClassLoader = (parent==null ? RemoteClassLoader.class.getClassLoader() : parent); + classLoader = URLClassLoader.newInstance(urls, parentClassLoader); + } + + public RemoteClassLoader(URL[] urls) { + this(urls, null); + } + + public RemoteClassLoader(Collection<URL> urls, ClassLoader parent) { + this(urls.toArray(new URL[urls.size()]), parent); + } + + public URLClassLoader getClassLoader() { + return classLoader; + } + + public static RemoteClassLoader createForJdbcDriver(Configuration conf) { + String baseUrl = ServerMetadata.getUrl(conf); + String url = baseUrl + "/deploy/jdbc-driver.jar"; + try { + return new RemoteClassLoader(new URL[]{new URL(url)}); + + } catch (MalformedURLException e) { + throw new RuntimeException("Fail to download the JDBC driver from server: " + url, e); + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ResourceDatabaseConfiguration.java b/sonar-batch/src/main/java/org/sonar/batch/ResourceDatabaseConfiguration.java new file mode 100644 index 00000000000..77359f427e3 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ResourceDatabaseConfiguration.java @@ -0,0 +1,83 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.configuration.BaseConfiguration; + +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.configuration.Property; +import org.sonar.api.database.model.ResourceModel; + +import java.util.List; + +public class ResourceDatabaseConfiguration extends BaseConfiguration { + private final DatabaseSession session; + private Integer resourceId = null; + + public ResourceDatabaseConfiguration(DatabaseSession session, ResourceModel resource) { + this.session = session; + if (resource != null) { + this.resourceId = resource.getId(); + } + load(); + } + + public ResourceDatabaseConfiguration(DatabaseSession session, Integer resourceId) { + this.session = session; + this.resourceId = resourceId; + load(); + } + + public ResourceDatabaseConfiguration(DatabaseSession session, String resourceKey) { + this.session = session; + + ResourceModel resource = session.getSingleResult(ResourceModel.class, "key", resourceKey); + if (resource != null) { + this.resourceId = resource.getId(); + } + load(); + } + + public void load() { + clear(); + + loadResourceProperties(); + } + + private void loadResourceProperties() { + if (resourceId != null) { + List<Property> properties = session + .createQuery("from " + Property.class.getSimpleName() + " p where p.resourceId=:resourceId") + .setParameter("resourceId", resourceId) + .getResultList(); + + registerProperties(properties); + } + } + + private void registerProperties(List<Property> properties) { + if (properties != null) { + for (Property property : properties) { + setProperty(property.getKey(), property.getValue()); + } + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ResourceFilters.java b/sonar-batch/src/main/java/org/sonar/batch/ResourceFilters.java new file mode 100644 index 00000000000..395ffd2f38b --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ResourceFilters.java @@ -0,0 +1,65 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.ResourceFilter; +import org.sonar.api.resources.Resource; + +/** + * @since 1.12 + */ +public class ResourceFilters { + + private static final Logger LOG = LoggerFactory.getLogger(ResourceFilters.class); + + private ResourceFilter[] filters; + + public ResourceFilters(ResourceFilter[] filters) { + this.filters = (filters==null ? new ResourceFilter[0] : filters); + } + + public ResourceFilters() { + this(null); + } + + public ResourceFilter[] getFilters() { + return filters; + } + + /** + * Return true if the violation must be saved. If false then it is ignored. + */ + public boolean isExcluded(Resource resource) { + boolean ignored = false; + int index = 0; + while (!ignored && index < filters.length) { + ResourceFilter filter = filters[index]; + ignored = filter.isIgnored(resource); + if (ignored && LOG.isDebugEnabled()) { + LOG.debug("Resource {} is excluded by the filter {}", resource, filter); + } + index++; + } + return ignored; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/SensorsExecutor.java b/sonar-batch/src/main/java/org/sonar/batch/SensorsExecutor.java new file mode 100644 index 00000000000..4bcdc39e9a6 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/SensorsExecutor.java @@ -0,0 +1,74 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.BatchExtensionDictionnary; +import org.sonar.api.batch.Sensor; +import org.sonar.api.batch.SensorContext; +import org.sonar.api.batch.maven.DependsUponMavenPlugin; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.resources.Project; +import org.sonar.api.utils.TimeProfiler; + +import java.util.Collection; + +public class SensorsExecutor implements CoreJob { + private static final Logger logger = LoggerFactory.getLogger(SensorsExecutor.class); + + private Collection<Sensor> sensors; + private DatabaseSession session; + private MavenPluginExecutor mavenExecutor; + + public SensorsExecutor(BatchExtensionDictionnary selector, Project project, DatabaseSession session, MavenPluginExecutor mavenExecutor) { + this.sensors = selector.select(Sensor.class, project, true); + this.session = session; + this.mavenExecutor = mavenExecutor; + } + + public void execute(Project project, SensorContext context) { + if (logger.isDebugEnabled()) { + logger.debug("Sensors : {}", StringUtils.join(sensors, " -> ")); + } + + for (Sensor sensor : sensors) { + executeMavenPlugin(project, sensor); + + TimeProfiler profiler = new TimeProfiler(logger).start("Sensor "+ sensor); + sensor.analyse(project, context); + session.commit(); + profiler.stop(); + } + } + + private void executeMavenPlugin(Project project, Sensor sensor) { + if (sensor instanceof DependsUponMavenPlugin) { + MavenPluginHandler handler = ((DependsUponMavenPlugin) sensor).getMavenPluginHandler(project); + if (handler != null) { + TimeProfiler profiler = new TimeProfiler(logger).start("Execute maven plugin " + handler.getArtifactId()); + mavenExecutor.execute(project, handler); + profiler.stop(); + } + } + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ServerMetadata.java b/sonar-batch/src/main/java/org/sonar/batch/ServerMetadata.java new file mode 100644 index 00000000000..d06e9212e91 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ServerMetadata.java @@ -0,0 +1,73 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.apache.commons.configuration.Configuration; +import org.apache.commons.lang.StringUtils; +import org.slf4j.LoggerFactory; +import org.sonar.api.CoreProperties; +import org.sonar.api.platform.Server; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class ServerMetadata extends Server { + + private String id; + private String version; + private String url; + private Date startTime; + + public ServerMetadata(Configuration conf) { + id = conf.getString(CoreProperties.SERVER_ID); + version = conf.getString(CoreProperties.SERVER_VERSION); + url = getUrl(conf); + String dateString = conf.getString(CoreProperties.SERVER_STARTTIME); + if (dateString!=null) { + try { + this.startTime = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").parse(dateString); + + } catch (ParseException e) { + LoggerFactory.getLogger(getClass()).error("The property " + CoreProperties.SERVER_STARTTIME + " is badly formatted.", e); + } + } + } + + public static String getUrl(Configuration conf) { + return StringUtils.removeEnd(conf.getString("sonar.host.url", "http://localhost:9000"), "/"); + } + + public String getId() { + return id; + } + + public String getVersion() { + return version; + } + + public Date getStartedAt() { + return startTime; + } + + public String getUrl() { + return url; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ViolationFilters.java b/sonar-batch/src/main/java/org/sonar/batch/ViolationFilters.java new file mode 100644 index 00000000000..784b01ae8b5 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ViolationFilters.java @@ -0,0 +1,60 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.rules.Violation; +import org.sonar.api.rules.ViolationFilter; + +public class ViolationFilters { + + private static final Logger LOG = LoggerFactory.getLogger(ViolationFilters.class); + + private ViolationFilter[] filters; + + public ViolationFilters(ViolationFilter[] filters) { + this.filters = (filters==null ? new ViolationFilter[0] : filters); + } + + public ViolationFilters() { + this(null); + } + + public ViolationFilter[] getFilters() { + return filters; + } + + /** + * Return true if the violation must be saved. If false then it is ignored. + */ + public boolean isIgnored(Violation violation) { + boolean ignored = false; + int index = 0; + while (!ignored && index < filters.length) { + ignored = filters[index].isIgnored(violation); + if (ignored && LOG.isDebugEnabled()) { + LOG.debug("Violation {} is excluded by the filter {}", violation, filters[index]); + } + index++; + } + return ignored; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/ViolationsDao.java b/sonar-batch/src/main/java/org/sonar/batch/ViolationsDao.java new file mode 100644 index 00000000000..bdca03188ca --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/ViolationsDao.java @@ -0,0 +1,99 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch; + +import org.slf4j.LoggerFactory; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.RuleFailureModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.profiles.RulesProfile; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; +import org.sonar.api.rules.ActiveRule; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.Violation; +import org.sonar.jpa.dao.RulesDao; + +import java.util.ArrayList; +import java.util.List; + +public class ViolationsDao { + + private RulesProfile profile; + private DatabaseSession session; + private RulesDao rulesDao; + private boolean reuseExistingRulesConfig; + + public ViolationsDao(RulesProfile profile, DatabaseSession session, RulesDao rulesDao, Project project) { + this.profile = profile; + this.session = session; + this.rulesDao = rulesDao; + this.reuseExistingRulesConfig = project.getReuseExistingRulesConfig(); + } + + public List<Violation> getViolations(Resource resource, Integer snapshotId) { + List<RuleFailureModel> models = session.getResults(RuleFailureModel.class, "snapshotId", snapshotId); + List<Violation> violations = new ArrayList<Violation>(); + for (RuleFailureModel model : models) { + Violation violation = new Violation(model.getRule(), resource); + violation.setLineId(model.getLine()); + violation.setMessage(model.getMessage()); + violation.setPriority(model.getPriority()); + violations.add(violation); + } + return violations; + } + + public void saveViolation(Snapshot snapshot, Violation violation) { + if (profile == null || snapshot == null || violation == null) { + throw new IllegalArgumentException("Missing data to save violation : profile=" + profile + ",snapshot=" + snapshot + ",violation=" + violation); + } + + ActiveRule activeRule = profile.getActiveRule(violation.getRule()); + if (activeRule == null) { + if (reuseExistingRulesConfig) { + activeRule = new ActiveRule(profile, violation.getRule(), violation.getRule().getPriority()); + } else { + LoggerFactory.getLogger(getClass()).debug("Violation is not saved because rule is not activated : violation={}", violation); + } + } + if (activeRule != null) { + RuleFailureModel model = toModel(snapshot, violation, activeRule); + session.save(model); + } + } + + private RuleFailureModel toModel(Snapshot snapshot, Violation violation, ActiveRule activeRule) { + Rule rule = reload(violation.getRule()); + if (rule == null) { + throw new IllegalArgumentException("Rule does not exist : " + violation.getRule()); + } + RuleFailureModel model = new RuleFailureModel(rule, activeRule.getPriority()); + violation.setPriority(activeRule.getPriority()); + model.setLine(violation.getLineId()); + model.setMessage(violation.getMessage()); + model.setSnapshotId(snapshot.getId()); + return model; + } + + private Rule reload(Rule rule) { + return rule.getId() != null ? rule : rulesDao.getRuleByKey(rule.getPluginName(), rule.getKey()); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/indexer/Bucket.java b/sonar-batch/src/main/java/org/sonar/batch/indexer/Bucket.java new file mode 100644 index 00000000000..98e107a2e4d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/indexer/Bucket.java @@ -0,0 +1,150 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.indexer; + +import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +public class Bucket<RESOURCE extends Resource> { + + private RESOURCE resource; + private Snapshot snapshot; + private ListMultimap<String, Measure> measuresByMetric = ArrayListMultimap.create(); + private boolean sourceSaved = false; + private Bucket<Project> project; + private Bucket<?> parent; + private List<Bucket<?>> children; + + public Bucket(RESOURCE resource) { + this.resource = resource; + } + + public RESOURCE getResource() { + return resource; + } + + public Bucket<Project> getProject() { + return project; + } + + public Bucket<RESOURCE> setProject(Bucket<Project> project) { + this.project = project; + return this; + } + + public Snapshot getSnapshot() { + return snapshot; + } + + public Integer getSnapshotId() { + if (snapshot != null) { + return snapshot.getId(); + } + return null; + } + + public void setSnapshot(Snapshot snapshot) { + this.snapshot = snapshot; + } + + public void setParent(Bucket parent) { + this.parent = parent; + if (parent != null) { + parent.addChild(this); + } + } + + private void addChild(Bucket bucket) { + if (children == null) { + children = Lists.newArrayList(); + } + children.add(bucket); + } + + private void removeChild(Bucket bucket) { + if (children != null) { + children.remove(bucket); + } + } + + public List<Bucket<?>> getChildren() { + if (children == null) { + return Collections.emptyList(); + } + return children; + } + + public Bucket getParent() { + return parent; + } + + public void addMeasure(Measure measure) { + measuresByMetric.put(measure.getMetric().getKey(), measure); + } + + public boolean isSourceSaved() { + return sourceSaved; + } + + public void setSourceSaved(boolean b) { + this.sourceSaved = b; + } + + public void clear() { + measuresByMetric = null; + children = null; + if (parent != null) { + parent.removeChild(this); + } + } + + public boolean isExcluded() { + return resource.isExcluded(); + } + + public boolean isPersisted() { + return resource.getId() != null; + } + + public Integer getResourceId() { + return resource.getId(); + } + + public <M> M getMeasures(final MeasuresFilter<M> filter) { + Collection<Measure> unfiltered; + if (filter instanceof MeasuresFilters.MetricFilter) { + unfiltered = measuresByMetric.get(((MeasuresFilters.MetricFilter) filter).filterOnMetric().getKey()); + } else { + unfiltered = measuresByMetric.values(); + } + return filter.filter(unfiltered); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultPersister.java b/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultPersister.java new file mode 100644 index 00000000000..36f8419e083 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultPersister.java @@ -0,0 +1,52 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.indexer; + +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.api.database.model.Snapshot; + +public class DefaultPersister extends ResourcePersister { + + public DefaultPersister(DatabaseSession session) { + super(session); + } + + @Override + protected Snapshot createSnapshot(Bucket bucket, ResourceModel resourceModel) { + Snapshot parentSnapshot = (bucket.getParent() != null ? bucket.getParent().getSnapshot() : null); + Snapshot snapshot = new Snapshot(resourceModel, parentSnapshot); + return snapshot; + } + + @Override + protected void prepareResourceModel(ResourceModel resourceModel, Bucket bucket) { + resourceModel.setRootId(bucket.getProject() != null ? bucket.getProject().getResourceId() : null); + } + + @Override + protected String generateEffectiveKey(Bucket bucket) { + return new StringBuilder(ResourceModel.KEY_SIZE) + .append(bucket.getProject().getResource().getKey()) + .append(':') + .append(bucket.getResource().getKey()) + .toString(); + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java b/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java new file mode 100644 index 00000000000..48a6bc72997 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java @@ -0,0 +1,465 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.indexer; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.Event; +import org.sonar.api.batch.SonarIndex; +import org.sonar.api.database.DatabaseSession; +import org.sonar.jpa.dao.MeasuresDao; +import org.sonar.api.database.model.MeasureModel; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.database.model.SnapshotSource; +import org.sonar.api.design.Dependency; +import org.sonar.api.design.DependencyDto; +import org.sonar.api.measures.Measure; +import org.sonar.api.measures.MeasuresFilter; +import org.sonar.api.measures.MeasuresFilters; +import org.sonar.api.measures.Metric; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.ProjectLink; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; +import org.sonar.api.rules.Violation; +import org.sonar.batch.ProjectTree; +import org.sonar.batch.ResourceFilters; +import org.sonar.batch.ViolationFilters; +import org.sonar.batch.ViolationsDao; + +import java.util.*; + +public class DefaultSonarIndex extends SonarIndex { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultSonarIndex.class); + + private DatabaseSession session; + private ResourcePersisters resourcePersisters; + private Bucket<Project> rootProjectBucket; + private Bucket<Project> selectedProjectBucket; + + private ViolationFilters violationFilters; + private ResourceFilters resourceFilters; + + // data + private Map<Resource, Bucket> buckets = Maps.newHashMap(); + private Set<Dependency> dependencies = Sets.newHashSet(); + private Map<Resource, Map<Resource, Dependency>> outgoingDependenciesByResource = new HashMap<Resource, Map<Resource, Dependency>>(); + private Map<Resource, Map<Resource, Dependency>> incomingDependenciesByResource = new HashMap<Resource, Map<Resource, Dependency>>(); + + // dao + private ViolationsDao violationsDao; + private MeasuresDao measuresDao; + private ProjectTree projectTree; + + public DefaultSonarIndex(DatabaseSession session, ProjectTree projectTree) { + this.session = session; + this.projectTree = projectTree; + this.resourcePersisters = new ResourcePersisters(session); + } + + public void start() { + Project rootProject = projectTree.getRootProject(); + + this.rootProjectBucket = new Bucket<Project>(rootProject); + persist(rootProjectBucket); + this.buckets.put(rootProject, rootProjectBucket); + this.selectedProjectBucket = rootProjectBucket; + + for (Project project : rootProject.getModules()) { + addProject(project); + } + session.commit(); + } + + private void addProject(Project project) { + addResource(project); + for (Project module : project.getModules()) { + addProject(module); + } + } + + + public void selectProject(Project project, ResourceFilters resourceFilters, ViolationFilters violationFilters, MeasuresDao measuresDao, ViolationsDao violationsDao) { + this.selectedProjectBucket = buckets.get(project); + this.resourceFilters = resourceFilters; + this.violationFilters = violationFilters; + this.violationsDao = violationsDao; + this.measuresDao = measuresDao; + } + + /** + * Keep only project stuff + */ + public void clear() { + Iterator<Map.Entry<Resource, Bucket>> it = buckets.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry<Resource, Bucket> entry = it.next(); + Resource resource = entry.getKey(); + if (!ResourceUtils.isSet(resource)) { + entry.getValue().clear(); + it.remove(); + } + } + + Set<Dependency> projectDependencies = getDependenciesBetweenProjects(); + dependencies.clear(); + incomingDependenciesByResource.clear(); + outgoingDependenciesByResource.clear(); + for (Dependency projectDependency : projectDependencies) { + projectDependency.setId(null); + registerDependency(projectDependency); + } + } + + /* ------------ RESOURCES */ + public Project getRootProject() { + return rootProjectBucket.getResource(); + } + + public Project getProject() { + return selectedProjectBucket.getResource(); + } + + public Bucket getBucket(Resource resource) { + return buckets.get(resource); + } + + public Resource getResource(Resource resource) { + Bucket bucket = buckets.get(resource); + if (bucket != null) { + return bucket.getResource(); + } + return null; + } + + public List<Resource> getChildren(Resource resource) { + List<Resource> children = Lists.newArrayList(); + Bucket<?> bucket = buckets.get(resource); + if (bucket != null) { + for (Bucket childBucket : bucket.getChildren()) { + children.add(childBucket.getResource()); + } + } + return children; + } + + public Resource addResource(Resource resource) { + return getOrCreateBucket(resource).getResource(); + } + + private Bucket<Resource> getOrCreateBucket(Resource resource) { + Bucket bucket = buckets.get(resource); + if (bucket != null) { + return bucket; + } + + prepareResource(resource); + bucket = new Bucket<Resource>(resource); + buckets.put(resource, bucket); + + Bucket parentBucket = null; + Resource parent = resource.getParent(); + if (parent != null) { + parentBucket = getOrCreateBucket(parent); + } else if (!ResourceUtils.isLibrary(resource)) { + parentBucket = selectedProjectBucket; + } + bucket.setParent(parentBucket); + bucket.setProject(selectedProjectBucket); + + persist(bucket); + return bucket; + } + + private void prepareResource(Resource resource) { + resource.setExcluded(resourceFilters != null && resourceFilters.isExcluded(resource)); + } + + private void persist(Bucket bucket) { + resourcePersisters.get(bucket).persist(bucket); + } + + /* ------------ MEASURES */ + public Measure getMeasure(Resource resource, Metric metric) { + return getOrCreateBucket(resource).getMeasures(MeasuresFilters.metric(metric)); + } + + public <M> M getMeasures(Resource resource, MeasuresFilter<M> filter) { + return getOrCreateBucket(resource).getMeasures(filter); + } + + + /* ------------ SOURCE CODE */ + + public void setSource(Resource resource, String source) { + Bucket bucket = getOrCreateBucket(resource); + + if (!bucket.isExcluded()) { + if (bucket.isSourceSaved()) { + LOG.warn("Trying to save twice the source of " + resource); + + } else { + session.save(new SnapshotSource(bucket.getSnapshotId(), source)); + bucket.setSourceSaved(true); + } + } + } + + + /* ------------ RULE VIOLATIONS */ + + public void addViolation(Violation violation) { + Bucket bucket; + Resource resource = violation.getResource(); + if (resource == null) { + bucket = selectedProjectBucket; + } else { + bucket = getOrCreateBucket(resource); + } + if (!bucket.isExcluded()) { + persistViolation(violation, bucket.getSnapshot()); + } + } + + private void persistViolation(Violation violation, Snapshot snapshot) { + boolean isIgnored = violationFilters != null && violationFilters.isIgnored(violation); + if (!isIgnored) { + violationsDao.saveViolation(snapshot, violation); + } + } + + + /* ------------ MEASURES */ + public Measure saveMeasure(Resource resource, Measure measure) { + if (measure.getId() == null) { + return addMeasure(resource, measure); + } + return updateMeasure(measure); + } + + public Measure addMeasure(Resource resource, Measure measure) { + Bucket bucket = getOrCreateBucket(resource); + if (!bucket.isExcluded()) { + if (bucket.getMeasures(MeasuresFilters.measure(measure))!=null) { + throw new IllegalArgumentException("This measure has already been saved: " + measure + ",resource: " + resource); + } + if (shouldPersistMeasure(resource, measure)) { + persistMeasure(bucket, measure); + } + + if (measure.getPersistenceMode().useMemory()) { + bucket.addMeasure(measure); + } + } + return measure; + } + + public Measure updateMeasure(Measure measure) { + if (measure.getId() == null) { + throw new IllegalStateException("Measure can not be updated because it has never been saved"); + } + + MeasureModel model = session.reattach(MeasureModel.class, measure.getId()); + model = MeasureModel.build(measure, model); + model.setMetric(measuresDao.getMetric(measure.getMetric())); + model.save(session); + return measure; + } + + private void persistMeasure(Bucket bucket, Measure measure) { + Metric metric = measuresDao.getMetric(measure.getMetric()); + MeasureModel measureModel = MeasureModel.build(measure); + measureModel.setMetric(metric); // hibernate synchronized metric + measureModel.setSnapshotId(bucket.getSnapshotId()); + measureModel.save(session); + + // the id is saved for future updates + measure.setId(measureModel.getId()); + } + + private boolean shouldPersistMeasure(Resource resource, Measure measure) { + Metric metric = measure.getMetric(); + return measure.getPersistenceMode().useDatabase() && !( + ResourceUtils.isEntity(resource) && + metric.isOptimizedBestValue() == Boolean.TRUE && + metric.getBestValue() != null && + metric.getBestValue().equals(measure.getValue()) && + !measure.hasOptionalData()); + } + + + /* --------------- DEPENDENCIES */ + public Dependency saveDependency(Dependency dependency) { + Dependency persistedDep = getEdge(dependency.getFrom(), dependency.getTo()); + if (persistedDep != null && persistedDep.getId()!=null) { + return persistedDep; + } + Bucket from = getOrCreateBucket(dependency.getFrom()); + Bucket to = getOrCreateBucket(dependency.getTo()); + + DependencyDto dto = new DependencyDto(); + dto.setFromResourceId(from.getResourceId()); + dto.setFromScope(from.getResource().getScope()); + dto.setFromSnapshotId(from.getSnapshotId()); + dto.setToResourceId(to.getResourceId()); + dto.setToSnapshotId(to.getSnapshotId()); + dto.setToScope(to.getResource().getScope()); + dto.setProjectSnapshotId(selectedProjectBucket.getSnapshotId()); + dto.setUsage(dependency.getUsage()); + dto.setWeight(dependency.getWeight()); + + Dependency parentDependency = dependency.getParent(); + if (parentDependency != null) { + saveDependency(parentDependency); + dto.setParentDependencyId(parentDependency.getId()); + } + session.save(dto); + dependency.setId(dto.getId()); + registerDependency(dependency); + + return dependency; + } + + protected void registerDependency(Dependency dependency) { + dependencies.add(dependency); + registerOutgoingDependency(dependency); + registerIncomingDependency(dependency); + } + + private void registerOutgoingDependency(Dependency dependency) { + Map<Resource, Dependency> outgoingDeps = outgoingDependenciesByResource.get(dependency.getFrom()); + if (outgoingDeps == null) { + outgoingDeps = new HashMap<Resource, Dependency>(); + outgoingDependenciesByResource.put(dependency.getFrom(), outgoingDeps); + } + outgoingDeps.put(dependency.getTo(), dependency); + } + + private void registerIncomingDependency(Dependency dependency) { + Map<Resource, Dependency> incomingDeps = incomingDependenciesByResource.get(dependency.getTo()); + if (incomingDeps == null) { + incomingDeps = new HashMap<Resource, Dependency>(); + incomingDependenciesByResource.put(dependency.getTo(), incomingDeps); + } + incomingDeps.put(dependency.getFrom(), dependency); + } + + public Set<Dependency> getDependencies() { + return dependencies; + } + + + /* ------------ LINKS */ + + public void saveLink(ProjectLink link) { + ResourceModel projectDao = session.reattach(ResourceModel.class, selectedProjectBucket.getResourceId()); + ProjectLink dbLink = projectDao.getProjectLink(link.getKey()); + if (dbLink == null) { + link.setResource(projectDao); + projectDao.getProjectLinks().add(link); + session.save(link); + + } else { + dbLink.copyFieldsFrom(link); + session.save(dbLink); + } + } + + public void deleteLink(String key) { + ResourceModel projectDao = session.reattach(ResourceModel.class, selectedProjectBucket.getResourceId()); + ProjectLink dbLink = projectDao.getProjectLink(key); + if (dbLink != null) { + session.remove(dbLink); + projectDao.getProjectLinks().remove(dbLink); + } + } + + + /* ----------- EVENTS */ + public List<Event> getEvents(Resource resource) { + Bucket bucket = getOrCreateBucket(resource); + return session.getResults(Event.class, "resourceId", bucket.getResourceId()); + } + + public void deleteEvent(Event event) { + session.remove(event); + } + + public Event createEvent(Resource resource, String name, String description, String category, Date date) { + Bucket bucket = getOrCreateBucket(resource); + Event event; + if (date == null) { + event = new Event(name, description, category, bucket.getSnapshot()); + } else { + event = new Event(name, description, category, date, bucket.getResourceId()); + } + return session.save(event); + } + + public Dependency getEdge(Resource from, Resource to) { + Map<Resource, Dependency> map = outgoingDependenciesByResource.get(from); + if (map != null) { + return map.get(to); + } + return null; + } + + public boolean hasEdge(Resource from, Resource to) { + return getEdge(from, to) != null; + } + + public Set<Resource> getVertices() { + return buckets.keySet(); + } + + public Collection<Dependency> getOutgoingEdges(Resource from) { + Map<Resource, Dependency> deps = outgoingDependenciesByResource.get(from); + if (deps != null) { + return deps.values(); + } + return Collections.emptyList(); + } + + public Collection<Dependency> getIncomingEdges(Resource to) { + Map<Resource, Dependency> deps = incomingDependenciesByResource.get(to); + if (deps != null) { + return deps.values(); + } + return Collections.emptyList(); + } + + public Set<Dependency> getDependenciesBetweenProjects() { + Set<Dependency> result = new HashSet<Dependency>(); + for (Project project : projectTree.getProjects()) { + Collection<Dependency> deps = getOutgoingDependencies(project); + for (Dependency dep : deps) { + if (ResourceUtils.isSet(dep.getTo())) { + result.add(dep); + } + } + } + return result; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/indexer/LibraryPersister.java b/sonar-batch/src/main/java/org/sonar/batch/indexer/LibraryPersister.java new file mode 100644 index 00000000000..640997e2f8f --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/indexer/LibraryPersister.java @@ -0,0 +1,68 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.indexer; + +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Library; +import org.sonar.api.resources.Resource; + +import java.util.Date; + +public class LibraryPersister extends ResourcePersister<Library> { + + private Date now; + + public LibraryPersister(DatabaseSession session) { + super(session); + this.now = new Date(); + } + + LibraryPersister(DatabaseSession session, Date now) { + super(session); + this.now = now; + } + + @Override + protected String generateEffectiveKey(Bucket<Library> bucket) { + return bucket.getResource().getKey(); + } + + @Override + protected void prepareResourceModel(ResourceModel resourceModel, Bucket<Library> bucket) { + } + + @Override + protected Snapshot createSnapshot(Bucket<Library> bucket, ResourceModel resourceModel) { + Snapshot snapshot = getSession().getSingleResult(Snapshot.class, + "resourceId", resourceModel.getId(), + "version", bucket.getResource().getVersion(), + "scope", Resource.SCOPE_SET, + "qualifier", Resource.QUALIFIER_LIB); + if (snapshot == null) { + snapshot = new Snapshot(resourceModel, null); + snapshot.setCreatedAt(now); + snapshot.setVersion(bucket.getResource().getVersion()); + snapshot.setStatus(Snapshot.STATUS_PROCESSED); + } + return snapshot; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/indexer/ProjectPersister.java b/sonar-batch/src/main/java/org/sonar/batch/indexer/ProjectPersister.java new file mode 100644 index 00000000000..821910b4538 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/indexer/ProjectPersister.java @@ -0,0 +1,58 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.indexer; + +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Project; + +public class ProjectPersister extends ResourcePersister<Project> { + + public ProjectPersister(DatabaseSession session) { + super(session); + } + + @Override + protected String generateEffectiveKey(Bucket<Project> bucket) { + return bucket.getResource().getKey(); + } + + @Override + protected void prepareResourceModel(ResourceModel resourceModel, Bucket<Project> bucket) { + if (bucket.getProject() != null) { + resourceModel.setRootId(bucket.getProject().getResourceId()); + } + + // LIMITATION : project.getLanguage() is set only in ProjectBatch, not in AggregatorBatch, so we + // have to explicitely use project.getLanguageKey() + resourceModel.setLanguageKey(bucket.getResource().getLanguageKey()); + } + + @Override + protected Snapshot createSnapshot(Bucket<Project> bucket, ResourceModel resourceModel) { + Project project = bucket.getResource(); + Snapshot parentSnapshot = (bucket.getParent() != null ? bucket.getParent().getSnapshot() : null); + Snapshot snapshot = new Snapshot(resourceModel, parentSnapshot); + snapshot.setVersion(project.getAnalysisVersion()); + snapshot.setCreatedAt(project.getAnalysisDate()); + return snapshot; + } +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/indexer/ResourcePersister.java b/sonar-batch/src/main/java/org/sonar/batch/indexer/ResourcePersister.java new file mode 100644 index 00000000000..6ad3be1344d --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/indexer/ResourcePersister.java @@ -0,0 +1,96 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.indexer; + +import javax.persistence.NonUniqueResultException; + +import org.apache.commons.lang.StringUtils; +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.database.model.ResourceModel; +import org.sonar.api.database.model.Snapshot; +import org.sonar.api.resources.Resource; +import org.sonar.api.resources.ResourceUtils; +import org.sonar.api.utils.SonarException; + +public abstract class ResourcePersister<RESOURCE extends Resource> { + + private DatabaseSession session; + + public ResourcePersister(DatabaseSession session) { + this.session = session; + } + + protected DatabaseSession getSession() { + return session; + } + + public final void persist(Bucket<RESOURCE> bucket) { + String effectiveKey = generateEffectiveKey(bucket); + ResourceModel model; + try { + model = session.getSingleResult(ResourceModel.class, "key", effectiveKey); + } catch (NonUniqueResultException e) { + throw new SonarException("The resource '" + effectiveKey + "' is duplicated in the Sonar DB."); + } + + RESOURCE resource = bucket.getResource(); + if (model == null) { + model = ResourceModel.build(resource); + model.setKey(effectiveKey); + + } else { + // update existing record + model.setEnabled(true); + if (StringUtils.isNotBlank(resource.getName())) { + model.setName(resource.getName()); + } + if (StringUtils.isNotBlank(resource.getLongName())) { + model.setLongName(resource.getLongName()); + } + if (StringUtils.isNotBlank(resource.getDescription())) { + model.setDescription(resource.getDescription()); + } + if ( !ResourceUtils.isLibrary(resource)) { + model.setScope(resource.getScope()); + model.setQualifier(resource.getQualifier()); + } + if (resource.getLanguage() != null) { + model.setLanguageKey(resource.getLanguage().getKey()); + } + } + + prepareResourceModel(model, bucket); + model = session.save(model); + resource.setId(model.getId()); + resource.setEffectiveKey(model.getKey()); + + Snapshot snapshot = createSnapshot(bucket, model); + if (snapshot.getId() == null) { + session.save(snapshot); + } + bucket.setSnapshot(snapshot); + } + + protected abstract void prepareResourceModel(ResourceModel resourceModel, Bucket<RESOURCE> bucket); + + protected abstract Snapshot createSnapshot(Bucket<RESOURCE> bucket, ResourceModel resourceModel); + + protected abstract String generateEffectiveKey(Bucket<RESOURCE> bucket); +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/indexer/ResourcePersisters.java b/sonar-batch/src/main/java/org/sonar/batch/indexer/ResourcePersisters.java new file mode 100644 index 00000000000..f359a0ae623 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/indexer/ResourcePersisters.java @@ -0,0 +1,50 @@ +/* + * Sonar, open source software quality management tool. + * Copyright (C) 2009 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * Sonar 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. + * + * Sonar 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 Sonar; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 + */ +package org.sonar.batch.indexer; + +import org.sonar.api.database.DatabaseSession; +import org.sonar.api.resources.Library; +import org.sonar.api.resources.Project; +import org.sonar.api.resources.Resource; + +import java.util.HashMap; +import java.util.Map; + +public final class ResourcePersisters { + + private Map<Class<? extends Resource>, ResourcePersister> persistersByClass; + private ResourcePersister defaultPersister; + + public ResourcePersisters(DatabaseSession session) { + defaultPersister = new DefaultPersister(session); + persistersByClass = new HashMap<Class<? extends Resource>, ResourcePersister>(); + persistersByClass.put(Project.class, new ProjectPersister(session)); + persistersByClass.put(Library.class, new LibraryPersister(session)); + } + + public ResourcePersister get(Bucket bucket) { + return get(bucket.getResource()); + } + + public ResourcePersister get(Resource resource) { + ResourcePersister persister = persistersByClass.get(resource.getClass()); + return persister != null ? persister : defaultPersister; + } +} diff --git a/sonar-batch/src/main/resources/org/sonar/batch/logback.xml b/sonar-batch/src/main/resources/org/sonar/batch/logback.xml new file mode 100644 index 00000000000..cd79fb299d6 --- /dev/null +++ b/sonar-batch/src/main/resources/org/sonar/batch/logback.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8" ?> + +<configuration debug="false"> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <layout class="ch.qos.logback.classic.PatternLayout"> + <pattern>[%level] %X{module} %msg%n</pattern> + </layout> + </appender> + + <logger name="org.hibernate"> + <level value="WARN"/> + </logger> + + <!-- set INFO to activate SQL logs. NOT RECOMMENDED --> + <logger name="org.hibernate.SQL"> + <level value="ERROR"/> + </logger> + + <!-- set INFO to activate SQL statistics. NOT RECOMMENDED --> + <logger name="org.sonar.DBSTATISTICS"> + <level value="ERROR"/> + </logger> + + <logger name="net.sf.ehcache"> + <level value="WARN"/> + </logger> + + <logger name="org.hibernate.cache.ReadWriteCache"> + <!-- removing "An item was expired by the cache while it was locked (increase your cache timeout)" msg --> + <level value="ERROR"/> + </logger> + <logger name="org.hibernate.cache.EhCacheProvider"> + <!-- removing "org.hibernate.cache.EhCacheProvider - Could not find configuratio)" message --> + <level value="ERROR"/> + </logger> + + <!-- see org.sonar.mojo.InternalMojo#initLogging --> + <root> + <level value="${ROOT_LOGGER_LEVEL}"/> + <appender-ref ref="STDOUT"/> + </root> + +</configuration>
\ No newline at end of file |