From 6b5035087075113699015583fd4d76b75bf36894 Mon Sep 17 00:00:00 2001 From: Evgeny Mandrikov Date: Wed, 19 Jan 2011 02:20:45 +0300 Subject: [PATCH] SONAR-2106: New Java library to bootstrap project analysis * Add BatchResourcesServlet to allow downloading libraries from server * Create in memory POM for non-maven environments * Provide fake MavenPluginExecutor for non-maven environments * Add new module sonar-batch-maven-compat with shaded maven-project --- pom.xml | 2 + sonar-batch-bootstrapper/pom.xml | 27 ++++ .../batch/bootstrapper/BatchDownloader.java | 134 ++++++++++++++++++ .../bootstrapper/BootstrapperIOUtils.java | 79 +++++++++++ .../batch/bootstrapper/package-info.java | 22 +-- .../bootstrapper/BatchDownloaderTest.java | 54 +++++++ sonar-batch-maven-compat/pom.xml | 46 ++++++ sonar-batch/pom.xml | 2 +- .../src/main/java/org/sonar/batch/Batch.java | 24 ++++ .../java/org/sonar/batch/MavenReactor.java | 17 ++- .../java/org/sonar/batch/ProjectTree.java | 65 ++++++++- .../batch/bootstrap/ProjectDefinition.java | 95 ------------- .../batch/bootstrapper/ProjectDefinition.java | 55 +++++++ .../org/sonar/batch/bootstrapper/Reactor.java | 48 +++++++ .../test/java/org/sonar/batch/BatchTest.java | 31 ++++ .../resources/DefaultFileSystemDirectory.java | 98 ------------- .../api/resources/FileSystemDirectory.java | 81 ----------- .../java/org/sonar/api/resources/Natures.java | 18 --- sonar-server/pom.xml | 12 ++ .../server/plugins/BatchResourcesServlet.java | 104 ++++++++++++++ sonar-server/src/main/webapp/WEB-INF/web.xml | 10 +- .../plugins/BatchResourcesServletTest.java | 59 ++++++++ 22 files changed, 767 insertions(+), 316 deletions(-) create mode 100644 sonar-batch-bootstrapper/pom.xml create mode 100644 sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BatchDownloader.java create mode 100644 sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapperIOUtils.java rename sonar-batch/src/main/java/org/sonar/batch/Reactor.java => sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/package-info.java (70%) create mode 100644 sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BatchDownloaderTest.java create mode 100644 sonar-batch-maven-compat/pom.xml delete mode 100644 sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectDefinition.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/bootstrapper/ProjectDefinition.java create mode 100644 sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Reactor.java create mode 100644 sonar-batch/src/test/java/org/sonar/batch/BatchTest.java delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultFileSystemDirectory.java delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/resources/FileSystemDirectory.java delete mode 100644 sonar-plugin-api/src/main/java/org/sonar/api/resources/Natures.java create mode 100644 sonar-server/src/main/java/org/sonar/server/plugins/BatchResourcesServlet.java create mode 100644 sonar-server/src/test/java/org/sonar/server/plugins/BatchResourcesServletTest.java diff --git a/pom.xml b/pom.xml index f582f85f35d..58a30f967e2 100644 --- a/pom.xml +++ b/pom.xml @@ -14,6 +14,8 @@ archetypes/sonar-basic-plugin archetypes/sonar-gwt-plugin sonar-batch + sonar-batch-bootstrapper + sonar-batch-maven-compat sonar-channel sonar-check-api sonar-colorizer diff --git a/sonar-batch-bootstrapper/pom.xml b/sonar-batch-bootstrapper/pom.xml new file mode 100644 index 00000000000..75a7498f456 --- /dev/null +++ b/sonar-batch-bootstrapper/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + + org.codehaus.sonar + sonar + 2.6-SNAPSHOT + + + sonar-batch-bootstrapper + Sonar :: Batch Bootstrapper + Provides API to bootstrap Sonar Batch. + + + + junit + junit + test + + + org.hamcrest + hamcrest-all + test + + + diff --git a/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BatchDownloader.java b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BatchDownloader.java new file mode 100644 index 00000000000..b5eba165324 --- /dev/null +++ b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BatchDownloader.java @@ -0,0 +1,134 @@ +/* + * 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.bootstrapper; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +public class BatchDownloader { + + private static final String VERSION_PATH = "/api/server/version"; + private static final String BATCH_PATH = "/batch/"; + + public static final int CONNECT_TIMEOUT_MILLISECONDS = 30000; + public static final int READ_TIMEOUT_MILLISECONDS = 60000; + + private String serverUrl; + private String serverVersion; + + public BatchDownloader(String serverUrl) { + if (serverUrl.endsWith("/")) { + this.serverUrl = serverUrl.substring(0, serverUrl.length() - 1); + } else { + this.serverUrl = serverUrl; + } + } + + /** + * @return server url + */ + public String getServerUrl() { + return serverUrl; + } + + /** + * @return server version + */ + public String getServerVersion() { + if (serverVersion == null) { + try { + serverVersion = remoteContent(VERSION_PATH); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return serverVersion; + } + + /** + * @return list of downloaded files + */ + public List downloadBatchFiles(File toDir) { + try { + List files = new ArrayList(); + + String libs = remoteContent(BATCH_PATH); + + for (String lib : libs.split(",")) { + File file = new File(toDir, lib); + remoteContentToFile(BATCH_PATH + lib, file); + files.add(file); + } + + return files; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private void remoteContentToFile(String path, File toFile) { + InputStream input = null; + FileOutputStream output = null; + String fullUrl = serverUrl + path; + try { + HttpURLConnection connection = newHttpConnection(new URL(fullUrl)); + output = new FileOutputStream(toFile, false); + input = connection.getInputStream(); + BootstrapperIOUtils.copyLarge(input, output); + } catch (Exception e) { + BootstrapperIOUtils.closeQuietly(output); + BootstrapperIOUtils.deleteFileQuietly(toFile); + throw new RuntimeException("Fail to download the file: " + fullUrl); + } finally { + BootstrapperIOUtils.closeQuietly(input); + BootstrapperIOUtils.closeQuietly(output); + } + } + + String remoteContent(String path) throws IOException { + String fullUrl = serverUrl + path; + HttpURLConnection conn = newHttpConnection(new URL(fullUrl)); + Reader reader = new InputStreamReader((InputStream) conn.getContent()); + try { + int statusCode = conn.getResponseCode(); + if (statusCode != HttpURLConnection.HTTP_OK) { + throw new IOException("Status returned by url : '" + fullUrl + "' is invalid : " + statusCode); + } + return BootstrapperIOUtils.toString(reader); + } finally { + BootstrapperIOUtils.closeQuietly(reader); + conn.disconnect(); + } + } + + static HttpURLConnection newHttpConnection(URL url) throws IOException { + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setConnectTimeout(CONNECT_TIMEOUT_MILLISECONDS); + connection.setReadTimeout(READ_TIMEOUT_MILLISECONDS); + connection.setInstanceFollowRedirects(true); + connection.setRequestMethod("GET"); + // TODO connection.setRequestProperty("User-Agent", userAgent); + return connection; + } + +} diff --git a/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapperIOUtils.java b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapperIOUtils.java new file mode 100644 index 00000000000..c764ac50105 --- /dev/null +++ b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/BootstrapperIOUtils.java @@ -0,0 +1,79 @@ +package org.sonar.batch.bootstrapper; + +import java.io.*; + +final class BootstrapperIOUtils { + + private BootstrapperIOUtils() { + } + + /** + * The default buffer size to use. + */ + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + /** + * Unconditionally close a Closeable. + */ + public static void closeQuietly(Closeable closeable) { + try { + if (closeable != null) { + closeable.close(); + } + } catch (IOException ioe) { + // ignore + } + } + + /** + * Get the contents of a Reader as a String. + */ + public static String toString(Reader input) throws IOException { + StringWriter sw = new StringWriter(); + copyLarge(input, sw); + return sw.toString(); + } + + /** + * Copy bytes from an InputStream to an OutputStream. + */ + public static long copyLarge(InputStream input, OutputStream output) throws IOException { + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + long count = 0; + int n = 0; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Copy chars from a Reader to a Writer. + */ + public static long copyLarge(Reader input, Writer output) throws IOException { + char[] buffer = new char[DEFAULT_BUFFER_SIZE]; + long count = 0; + int n = 0; + while (-1 != (n = input.read(buffer))) { + output.write(buffer, 0, n); + count += n; + } + return count; + } + + /** + * Deletes a file (not a directory). + */ + public static boolean deleteFileQuietly(File file) { + if (file == null) { + return false; + } + try { + return file.delete(); + } catch (Exception e) { + return false; + } + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/Reactor.java b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/package-info.java similarity index 70% rename from sonar-batch/src/main/java/org/sonar/batch/Reactor.java rename to sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/package-info.java index 7dd900b7e4c..7c23a7aa9ec 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/Reactor.java +++ b/sonar-batch-bootstrapper/src/main/java/org/sonar/batch/bootstrapper/package-info.java @@ -17,22 +17,8 @@ * 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.maven.project.MavenProject; - -import java.util.List; - -public class Reactor { - - private List sortedProjects; - - public Reactor(List sortedProjects) { - this.sortedProjects = sortedProjects; - } - - public List getSortedProjects() { - return sortedProjects; - } - -} +/** + * Provides API to bootstrap Sonar Batch. + */ +package org.sonar.batch.bootstrapper; \ No newline at end of file diff --git a/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BatchDownloaderTest.java b/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BatchDownloaderTest.java new file mode 100644 index 00000000000..1822a53f014 --- /dev/null +++ b/sonar-batch-bootstrapper/src/test/java/org/sonar/batch/bootstrapper/BatchDownloaderTest.java @@ -0,0 +1,54 @@ +/* + * 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.bootstrapper; + +import org.junit.Test; + +import java.io.IOException; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class BatchDownloaderTest { + + @Test + public void shouldRemoveLastUrlSlash() { + BatchDownloader bootstrapper = new BatchDownloader("http://test/"); + assertThat(bootstrapper.getServerUrl(), is("http://test")); + } + + @Test(expected = Exception.class) + public void shouldFailIfCanNotConnectServer() { + BatchDownloader bootstrapper = new BatchDownloader("http://unknown.foo"); + bootstrapper.getServerVersion(); + } + + @Test + public void shouldReturnValidVersion() { + BatchDownloader bootstrapper = new BatchDownloader("http://test") { + @Override + String remoteContent(String path) throws IOException { + return "2.6"; + } + }; + assertThat(bootstrapper.getServerVersion(), is("2.6")); + } + +} diff --git a/sonar-batch-maven-compat/pom.xml b/sonar-batch-maven-compat/pom.xml new file mode 100644 index 00000000000..1ebefa3e077 --- /dev/null +++ b/sonar-batch-maven-compat/pom.xml @@ -0,0 +1,46 @@ + + + 4.0.0 + + + org.codehaus.sonar + sonar + 2.6-SNAPSHOT + + + sonar-batch-maven-compat + Sonar :: Batch Maven Compat + Compatibility layer, which provides MavenProject for non-Maven environments. + + + + org.apache.maven + maven-project + 2.0.7 + compile + + + org.codehaus.plexus + plexus-container-default + + + + + + + + + org.apache.maven.plugins + maven-shade-plugin + + + package + + shade + + + + + + + diff --git a/sonar-batch/pom.xml b/sonar-batch/pom.xml index fd1ae8dffb2..8c21ce740f7 100644 --- a/sonar-batch/pom.xml +++ b/sonar-batch/pom.xml @@ -11,7 +11,7 @@ sonar-batch jar Sonar :: Batch - + org.codehaus.sonar diff --git a/sonar-batch/src/main/java/org/sonar/batch/Batch.java b/sonar-batch/src/main/java/org/sonar/batch/Batch.java index bf5b1e348bd..373300542ec 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/Batch.java +++ b/sonar-batch/src/main/java/org/sonar/batch/Batch.java @@ -25,6 +25,7 @@ import org.picocontainer.MutablePicoContainer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.sonar.api.Plugins; +import org.sonar.api.batch.maven.MavenPluginHandler; import org.sonar.api.resources.Project; import org.sonar.api.utils.HttpDownloader; import org.sonar.api.utils.IocContainer; @@ -128,9 +129,32 @@ public class Batch { for (Object component : components) { register(container, component); } + if (!isMavenPluginExecutorRegistered()) { + register(container, FakeMavenPluginExecutor.class); + } return container; } + boolean isMavenPluginExecutorRegistered() { + for (Object component : components) { + if (component instanceof Class && MavenPluginExecutor.class.isAssignableFrom((Class) component)) { + return true; + } + } + return false; + } + + class FakeMavenPluginExecutor implements MavenPluginExecutor { + public void execute(Project project, String goal) { + // do nothing + } + + public MavenPluginHandler execute(Project project, MavenPluginHandler handler) { + // do nothing + return handler; + } + } + private void register(MutablePicoContainer container, Object component) { container.as(Characteristics.CACHE).addComponent(component); } diff --git a/sonar-batch/src/main/java/org/sonar/batch/MavenReactor.java b/sonar-batch/src/main/java/org/sonar/batch/MavenReactor.java index a33e6889ec8..1f0acf8c19e 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/MavenReactor.java +++ b/sonar-batch/src/main/java/org/sonar/batch/MavenReactor.java @@ -20,11 +20,24 @@ package org.sonar.batch; import org.apache.maven.execution.MavenSession; +import org.apache.maven.project.MavenProject; -public class MavenReactor extends Reactor { +import java.util.List; + +public class MavenReactor { + + private List sortedProjects; public MavenReactor(MavenSession mavenSession) { - super(mavenSession.getSortedProjects()); + this.sortedProjects = mavenSession.getSortedProjects(); + } + + public MavenReactor(List sortedProjects) { + this.sortedProjects = sortedProjects; + } + + public List getSortedProjects() { + return sortedProjects; } } diff --git a/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java b/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java index 1f969befd10..8adf4987039 100644 --- a/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java +++ b/sonar-batch/src/main/java/org/sonar/batch/ProjectTree.java @@ -19,8 +19,13 @@ */ package org.sonar.batch; +import org.sonar.batch.bootstrapper.ProjectDefinition; + +import org.sonar.batch.bootstrapper.Reactor; + import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import org.apache.maven.model.Reporting; import org.apache.maven.project.MavenProject; import org.slf4j.LoggerFactory; import org.sonar.api.database.DatabaseSession; @@ -36,11 +41,67 @@ public class ProjectTree { private List poms; private MavenProjectBuilder projectBuilder; - public ProjectTree(Reactor reactor, DatabaseSession databaseSession) { - this.poms = reactor.getSortedProjects(); + public ProjectTree(MavenReactor mavenReactor, DatabaseSession databaseSession) { + this.poms = mavenReactor.getSortedProjects(); this.projectBuilder = new MavenProjectBuilder(databaseSession); } + /** + * Hack for non-Maven environments. + */ + public ProjectTree(Reactor sonarReactor, DatabaseSession databaseSession) { + this(createMavenReactor(sonarReactor), databaseSession); + } + + private static MavenReactor createMavenReactor(Reactor sonarReactor) { + List sonarProjects = sonarReactor.getSortedProjects(); + List mavenProjects = Lists.newArrayList(); + for (ProjectDefinition project : sonarProjects) { + mavenProjects.add(createInMemoryPom(project)); + } + return new MavenReactor(mavenProjects); + } + + private static MavenProject createInMemoryPom(ProjectDefinition project) { + MavenProject pom = new MavenProject(); + + String key = project.getProperties().getString("project.key"); // TODO constant + String[] keys = key.split(":"); + pom.setGroupId(keys[0]); + pom.setArtifactId(keys[1]); + pom.setVersion("0.1-SNAPSHOT"); // TODO hard-coded value + + pom.setArtifacts(Collections.EMPTY_SET); + + // Configure fake directories + String buildDirectory = project.getProperties().getString("project.build.directory"); + File sonarDir = new File(buildDirectory, "sonar"); + pom.setFile(new File(sonarDir, "fake-pom.xml")); + pom.getBuild().setDirectory(buildDirectory); + pom.getBuild().setOutputDirectory(buildDirectory + "/classes"); // TODO hard-coded value + Reporting reporting = new Reporting(); + reporting.setOutputDirectory(buildDirectory + "/target/site"); // TODO hard-coded value + pom.setReporting(reporting); + + // Configure source directories + // TODO + // for (FileSystemDirectory dir : project.getDirs()) { + // if (dir.getNature() == Natures.MAIN) { + // pom.addCompileSourceRoot(dir.getLocation().getAbsolutePath()); + // } + // } + + // Configure test directories + // TODO + // for (FileSystemDirectory dir : project.getDirs()) { + // if (dir.getNature() == Natures.TEST) { + // pom.addTestCompileSourceRoot(dir.getLocation().getAbsolutePath()); + // } + // } + + return pom; + } + /** * for unit tests */ diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectDefinition.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectDefinition.java deleted file mode 100644 index d12dc1a9af1..00000000000 --- a/sonar-batch/src/main/java/org/sonar/batch/bootstrap/ProjectDefinition.java +++ /dev/null @@ -1,95 +0,0 @@ -package org.sonar.batch.bootstrap; - -import com.google.common.collect.Lists; -import org.apache.commons.configuration.Configuration; -import org.sonar.api.resources.FileSystemDirectory; - -import java.io.File; -import java.util.List; - -/** - * Defines project in a form suitable to bootstrap Sonar batch. - * We assume that project is just a set of configuration properties and directories. - * This is a part of bootstrap process, so we should take care about backward compatibility. - * - * @since 2.6 - */ -public class ProjectDefinition { - - private Configuration configuration; - - private File workDir; - private File basedir; - private List dirs = Lists.newArrayList(); - - private ProjectDefinition parent; - private List modules; - - /** - * @return project properties. - */ - public Configuration getConfiguration() { - return configuration; - } - - public void setConfiguration(Configuration configuration) { - this.configuration = configuration; - } - - /** - * @return Sonar working directory for this project. - * It's "${project.build.directory}/sonar" ("${project.basedir}/target/sonar") for Maven projects. - */ - public File getSonarWorkingDirectory() { - return workDir; - } - - public void setSonarWorkingDirectory(File workDir) { - this.workDir = workDir; - } - - /** - * @return project root directory. - * It's "${project.basedir}" for Maven projects. - */ - public File getBasedir() { - return basedir; - } - - public void setBasedir(File basedir) { - this.basedir = basedir; - } - - /** - * @return project directories. - */ - public List getDirs() { - return dirs; - } - - public void addDir(FileSystemDirectory dir) { - this.dirs.add(dir); - } - - /** - * @return parent project. - */ - public ProjectDefinition getParent() { - return parent; - } - - public void setParent(ProjectDefinition parent) { - this.parent = parent; - if (parent != null) { - parent.modules.add(this); - } - } - - /** - * @return list of sub-projects. - */ - public List getModules() { - return modules; - } - -} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/ProjectDefinition.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/ProjectDefinition.java new file mode 100644 index 00000000000..2b0a2a27205 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/ProjectDefinition.java @@ -0,0 +1,55 @@ +/* + * 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.bootstrapper; + +import org.apache.commons.configuration.Configuration; + +import java.io.File; + +/** + * Defines project in a form suitable to bootstrap Sonar batch. + * We assume that project is just a set of configuration properties and directories. + * This is a part of bootstrap process, so we should take care about backward compatibility. + * + * @since 2.6 + */ +public class ProjectDefinition { + + private File baseDir; + private Configuration properties; + + /** + * @param baseDir project base directory + * @param properties project properties + */ + public ProjectDefinition(File baseDir, Configuration properties) { + this.baseDir = baseDir; + this.properties = properties; + } + + public File getBaseDir() { + return baseDir; + } + + public Configuration getProperties() { + return properties; + } + +} diff --git a/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Reactor.java b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Reactor.java new file mode 100644 index 00000000000..909adedf847 --- /dev/null +++ b/sonar-batch/src/main/java/org/sonar/batch/bootstrapper/Reactor.java @@ -0,0 +1,48 @@ +/* + * 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.bootstrapper; + + +import java.util.Collections; +import java.util.List; + +/** + * Defines order of projects. + * This is a part of bootstrap process, so we should take care about backward compatibility. + * + * @since 2.6 + */ +public class Reactor { + + private List projects; + + public Reactor(ProjectDefinition project) { + this.projects = Collections.singletonList(project); + } + + public Reactor(List sortedProjects) { + this.projects = sortedProjects; + } + + public List getSortedProjects() { + return projects; + } + +} diff --git a/sonar-batch/src/test/java/org/sonar/batch/BatchTest.java b/sonar-batch/src/test/java/org/sonar/batch/BatchTest.java new file mode 100644 index 00000000000..9f5fbb7a7b0 --- /dev/null +++ b/sonar-batch/src/test/java/org/sonar/batch/BatchTest.java @@ -0,0 +1,31 @@ +package org.sonar.batch; + +import org.junit.Test; +import org.sonar.api.batch.maven.MavenPluginHandler; +import org.sonar.api.resources.Project; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; + +public class BatchTest { + + class MyMavenPluginExecutor implements MavenPluginExecutor { + public void execute(Project project, String goal) { + } + + public MavenPluginHandler execute(Project project, MavenPluginHandler handler) { + return handler; + } + } + + @Test + public void shouldSearchMavenPluginExecutor() { + Batch batch; + + batch = new Batch(null, MyMavenPluginExecutor.class); + assertThat(batch.isMavenPluginExecutorRegistered(), is(true)); + + batch = new Batch(null); + assertThat(batch.isMavenPluginExecutorRegistered(), is(false)); + } +} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultFileSystemDirectory.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultFileSystemDirectory.java deleted file mode 100644 index 44461fb4772..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/DefaultFileSystemDirectory.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.api.resources; - -import com.google.common.collect.Lists; - -import java.io.File; -import java.util.Collections; -import java.util.List; - -public class DefaultFileSystemDirectory implements FileSystemDirectory { - - private String nature; - private File location; - private File outputLocation; - private List inclusionPatterns; - private List exclusionPatterns; - - public String getNature() { - return nature; - } - - public DefaultFileSystemDirectory setNature(String nature) { - this.nature = nature; - return this; - } - - public File getLocation() { - return location; - } - - public DefaultFileSystemDirectory setLocation(File location) { - this.location = location; - return this; - } - - public File getOutputLocation() { - return outputLocation; - } - - public DefaultFileSystemDirectory setOutputLocation(File outputLocation) { - this.outputLocation = outputLocation; - return this; - } - - public List getInclusionPatterns() { - if (inclusionPatterns == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(inclusionPatterns); - } - - /** - * @param pattern Ant-like inclusion pattern - */ - public DefaultFileSystemDirectory addInclusionPattern(String pattern) { - if (inclusionPatterns == null) { - inclusionPatterns = Lists.newArrayList(); - } - inclusionPatterns.add(pattern); - return this; - } - - public List getExclusionPatterns() { - if (exclusionPatterns == null) { - return Collections.emptyList(); - } - return Collections.unmodifiableList(exclusionPatterns); - } - - /** - * @param pattern Ant-like exclusion pattern - */ - public DefaultFileSystemDirectory addExclusionPattern(String pattern) { - if (exclusionPatterns == null) { - exclusionPatterns = Lists.newArrayList(); - } - exclusionPatterns.add(pattern); - return this; - } -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/FileSystemDirectory.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/FileSystemDirectory.java deleted file mode 100644 index 17c8298836c..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/FileSystemDirectory.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.api.resources; - - -import java.io.File; -import java.util.List; - -/** - * Defines project directory in a form suitable for Sonar. - * This is a part of bootstrap process, so we should take care about backward compatibility. - *

- * Couple of examples to show what this structure defines: - *

    - *
  • Typical Java project based on Ant might consist of two directories: - *
      - *
    1. sources (location "src", output location "bin", includes "*.java")
    2. - *
    3. resources (location "src", output location "bin", excludes "*.java")
    4. - *
    - *
  • - *
  • Typical Java project based on Maven might consist of four directories: - *
      - *
    1. main sources (location "src/main/java", output location "target/classes")
    2. - *
    3. main resources (location "src/main/resources", output location "target/classes")
    4. - *
    5. test sources (location "src/test/java", output location "target/test-classes")
    6. - *
    7. test resources (location "src/test/resources", output location "target/test-classes")
    8. - *
    - *
  • - *
- *

- * - * @since 2.6 - */ -public interface FileSystemDirectory { - - /** - * @return nature of underlying files. - * @see Natures - */ - String getNature(); - - /** - * @return location of files for compilation. - * In case of Java this would be directory with Java source files. - */ - File getLocation(); - - /** - * @return location of binary files after compilation. - * In case of Java this would be directory with Class files. - */ - File getOutputLocation(); - - /** - * @return list of Ant-like inclusion patterns for files. - */ - List getInclusionPatterns(); - - /** - * @return list of Ant-like exclusion patterns for files. - */ - List getExclusionPatterns(); - -} diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Natures.java b/sonar-plugin-api/src/main/java/org/sonar/api/resources/Natures.java deleted file mode 100644 index 5b8f102fef6..00000000000 --- a/sonar-plugin-api/src/main/java/org/sonar/api/resources/Natures.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.sonar.api.resources; - -/** - * @since 2.6 - */ -public interface Natures { - - /** - * Everything which relate to source code (for example "src/main/java" and "src/main/resources"). - */ - String MAIN = "MAIN"; - - /** - * Everything which relate to unit tests (for example "src/test/java" and "src/test/resources"). - */ - String TEST = "TEST"; - -} diff --git a/sonar-server/pom.xml b/sonar-server/pom.xml index a90ae860b18..b240684563c 100644 --- a/sonar-server/pom.xml +++ b/sonar-server/pom.xml @@ -11,6 +11,18 @@ Sonar :: Server + + + org.codehaus.sonar + sonar-batch + ${project.version} + + + org.codehaus.sonar + sonar-batch-maven-compat + ${project.version} + + org.codehaus.sonar sonar-channel diff --git a/sonar-server/src/main/java/org/sonar/server/plugins/BatchResourcesServlet.java b/sonar-server/src/main/java/org/sonar/server/plugins/BatchResourcesServlet.java new file mode 100644 index 00000000000..1e8ab666578 --- /dev/null +++ b/sonar-server/src/main/java/org/sonar/server/plugins/BatchResourcesServlet.java @@ -0,0 +1,104 @@ +/* + * 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.server.plugins; + +import com.google.common.collect.Lists; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.List; +import java.util.Set; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * This servlet allows to load libraries from directory "WEB-INF/lib" in order to provide them for batch-bootstrapper. + * Most probably this is not a best solution. + */ +public class BatchResourcesServlet extends HttpServlet { + + private static final Logger LOG = LoggerFactory.getLogger(BatchResourcesServlet.class); + + @Override + public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + String resource = getResource(request); + if (StringUtils.isEmpty(resource)) { + PrintWriter writer = null; + try { + String libs = StringUtils.join(getLibs(), ","); + response.setContentType("text/html"); + writer = response.getWriter(); + writer.println(libs); + } catch (IOException e) { + LOG.error("Unable to provide list of batch resources", e); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } finally { + IOUtils.closeQuietly(writer); + } + } else { + InputStream in = null; + OutputStream out = null; + try { + in = getServletContext().getResourceAsStream("/WEB-INF/lib/" + resource); + if (in == null) { + // TODO + } else { + out = response.getOutputStream(); + IOUtils.copy(in, out); + } + } catch (Exception e) { + LOG.error("Unable to load batch resource '" + resource + "'", e); + response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); + } finally { + IOUtils.closeQuietly(in); + IOUtils.closeQuietly(out); + } + } + } + + List getLibs() { + List libs = Lists.newArrayList(); + Set paths = getServletContext().getResourcePaths("/WEB-INF/lib"); + for (Object obj : paths) { + String path = (String) obj; + if (StringUtils.endsWith(path, ".jar")) { + libs.add(StringUtils.removeStart(path, "/WEB-INF/lib/")); + } + } + return libs; + } + + /** + * @return part of request URL after servlet path + */ + String getResource(HttpServletRequest request) { + return StringUtils.substringAfter(request.getRequestURI(), request.getContextPath() + request.getServletPath() + "/"); + } + +} diff --git a/sonar-server/src/main/webapp/WEB-INF/web.xml b/sonar-server/src/main/webapp/WEB-INF/web.xml index 99d68dcc5f6..25869629487 100644 --- a/sonar-server/src/main/webapp/WEB-INF/web.xml +++ b/sonar-server/src/main/webapp/WEB-INF/web.xml @@ -78,6 +78,10 @@ static org.sonar.server.plugins.StaticResourcesServlet + + batch + org.sonar.server.plugins.BatchResourcesServlet + chart @@ -91,6 +95,10 @@ static /static/* + + batch + /batch/* + org.sonar.server.platform.PlatformLifecycleListener @@ -103,4 +111,4 @@ 30 - \ No newline at end of file + diff --git a/sonar-server/src/test/java/org/sonar/server/plugins/BatchResourcesServletTest.java b/sonar-server/src/test/java/org/sonar/server/plugins/BatchResourcesServletTest.java new file mode 100644 index 00000000000..3dadef9567f --- /dev/null +++ b/sonar-server/src/test/java/org/sonar/server/plugins/BatchResourcesServletTest.java @@ -0,0 +1,59 @@ +package org.sonar.server.plugins; + +import com.google.common.collect.Sets; +import org.junit.Before; +import org.junit.Test; + +import java.util.Set; + +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; + +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +public class BatchResourcesServletTest { + private BatchResourcesServlet servlet; + private HttpServletRequest request; + + @Before + public void setUp() throws Exception { + servlet = new BatchResourcesServlet(); + request = mock(HttpServletRequest.class); + } + + @Test + public void shouldDetermineResource() { + when(request.getContextPath()).thenReturn("sonar"); + when(request.getServletPath()).thenReturn("/batch"); + + when(request.getRequestURI()).thenReturn("/sonar/batch/sonar-core-2.6.jar"); + assertThat(servlet.getResource(request), is("sonar-core-2.6.jar")); + + when(request.getRequestURI()).thenReturn("/sonar/batch/"); + assertThat(servlet.getResource(request), is("")); + + when(request.getRequestURI()).thenReturn("/sonar/batch"); + assertThat(servlet.getResource(request), is("")); + } + + @Test + public void shouldDetermineListOfResources() { + ServletContext servletContext = mock(ServletContext.class); + servlet = spy(servlet); + doReturn(servletContext).when(servlet).getServletContext(); + Set libs = Sets.newHashSet(); + libs.add("/WEB-INF/lib/sonar-core-2.6.jar"); + libs.add("/WEB-INF/lib/treemap.rb"); + libs.add("/WEB-INF/lib/directory/"); + when(servletContext.getResourcePaths(anyString())).thenReturn(libs); + + assertThat(servlet.getLibs().size(), is(1)); + assertThat(servlet.getLibs().get(0), is("sonar-core-2.6.jar")); + } +} -- 2.39.5