aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-batch/src/main
diff options
context:
space:
mode:
authorsimonbrandhof <simon.brandhof@gmail.com>2010-09-06 14:08:06 +0000
committersimonbrandhof <simon.brandhof@gmail.com>2010-09-06 14:08:06 +0000
commitaeadc1f9129274949daaa57738c7c4550bdfbc7b (patch)
tree08dadf5ef7474fc41d1d48f74648f1ba8b55f34d /sonar-batch/src/main
downloadsonarqube-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')
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/Batch.java114
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/BatchPluginRepository.java95
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/CheckProfileProvider.java64
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/CoreJob.java29
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/CoreJobs.java43
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DecoratorsExecutor.java81
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DecoratorsSelector.java62
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DefaultDecoratorContext.java177
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DefaultSensorContext.java155
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/DefaultTimeMachine.java116
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/FinalizeSnapshotsJob.java122
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/MavenPhaseExecutor.java42
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/MavenPluginExecutor.java32
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/MavenPluginsConfigurator.java75
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/PostJobsExecutor.java81
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ProfileProvider.java66
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ProjectBatch.java141
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ProjectBuilder.java150
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ProjectConfiguration.java68
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java163
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/RemoteClassLoader.java69
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ResourceDatabaseConfiguration.java83
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ResourceFilters.java65
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/SensorsExecutor.java74
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ServerMetadata.java73
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ViolationFilters.java60
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/ViolationsDao.java99
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/indexer/Bucket.java150
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultPersister.java52
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/indexer/DefaultSonarIndex.java465
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/indexer/LibraryPersister.java68
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/indexer/ProjectPersister.java58
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/indexer/ResourcePersister.java96
-rw-r--r--sonar-batch/src/main/java/org/sonar/batch/indexer/ResourcePersisters.java50
-rw-r--r--sonar-batch/src/main/resources/org/sonar/batch/logback.xml44
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