/* * Sonar Ant Task * Copyright (C) 2011 SonarSource * dev@sonar.codehaus.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02 */ package org.sonar.ant; import org.apache.tools.ant.*; import org.apache.tools.ant.types.Environment; import org.apache.tools.ant.types.Path; import org.sonar.batch.bootstrapper.BootstrapClassLoader; import org.sonar.batch.bootstrapper.Bootstrapper; import org.sonar.batch.bootstrapper.BootstrapperIOUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URL; import java.util.Properties; public class SonarTask extends Task { private static final String HOST_PROPERTY = "sonar.host.url"; private File workDir; private File baseDir; private Properties properties = new Properties(); private String key; private String version; private Path sources; private Path tests; private Path binaries; private Path libraries; private Bootstrapper bootstrapper; private String initTarget; /** * @return value of property "sonar.host.url", default is "http://localhost:9000" */ public String getServerUrl() { String serverUrl = getProperties().getProperty(HOST_PROPERTY); // from task if (serverUrl == null) { serverUrl = getProject().getProperty(HOST_PROPERTY); // from ant } if (serverUrl == null) { serverUrl = "http://localhost:9000"; // default } return serverUrl; } /** * @deprecated in 1.1 */ @Deprecated public void setWorkDir(File workDir) { // this.workDir = workDir; } /** * @return work directory, default is ".sonar" in project directory */ public File getWorkDir() { if (workDir == null) { workDir = new File(getBaseDir(), ".sonar"); } return workDir; } /** * @since 1.1 */ public void setBaseDir(File baseDir) { this.baseDir = baseDir; } /** * @return base directory, default is the current project base directory * @since 1.1 */ public File getBaseDir() { if (baseDir == null) { baseDir = getProject().getBaseDir(); } return baseDir; } /** * @since 1.1 */ public void setInitTarget(String initTarget) { this.initTarget = initTarget; } /** * @since 1.1 */ public String getInitTarget() { return initTarget; } public void setKey(String key) { this.key = key; } public String getKey() { return key; } public void setVersion(String version) { this.version = version; } public String getVersion() { return version; } /** * Note that name of this method is important - see http://ant.apache.org/manual/develop.html#nested-elements */ public void addConfiguredProperty(Environment.Variable property) { properties.setProperty(property.getKey(), property.getValue()); } public Properties getProperties() { return properties; } public Path createSources() { if (sources == null) { sources = new Path(getProject()); } return sources; } public Path createTests() { if (tests == null) { tests = new Path(getProject()); } return tests; } public Path createBinaries() { if (binaries == null) { binaries = new Path(getProject()); } return binaries; } public Path createLibraries() { if (libraries == null) { libraries = new Path(getProject()); } return libraries; } /** * Note about how Ant handles exceptions: according to {@link DefaultLogger#buildFinished(BuildEvent)} if we want to print stack trace, * then we shouldn't use {@link BuildException} with message. */ @Override public void execute() { log(Main.getAntVersion()); log("Sonar Ant Task version: " + getTaskVersion()); log("Loaded from: " + Utils.getJarPath()); log("Sonar work directory: " + getWorkDir().getAbsolutePath()); log("Sonar server: " + getServerUrl()); bootstrapper = new Bootstrapper("AntTask/" + getTaskVersion(), getServerUrl(), getWorkDir()); checkSonarVersion(); delegateExecution(createClassLoader()); } /** * Loads {@link Launcher} from specified {@link BootstrapClassLoader} and passes control to it. * * @see Launcher#execute() */ private void delegateExecution(BootstrapClassLoader sonarClassLoader) { ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader(); try { Thread.currentThread().setContextClassLoader(sonarClassLoader); Class<?> launcherClass = sonarClassLoader.findClass("org.sonar.ant.Launcher"); Constructor<?> constructor = launcherClass.getConstructor(SonarTask.class); Object launcher = constructor.newInstance(this); Method method = launcherClass.getMethod("execute"); method.invoke(launcher); } catch (InvocationTargetException e) { // Unwrap original exception throw new BuildException(e.getTargetException()); } catch (Exception e) { // Catch all other exceptions, which relates to reflection throw new BuildException(e); } finally { Thread.currentThread().setContextClassLoader(oldContextClassLoader); } } private BootstrapClassLoader createClassLoader() { return bootstrapper.createClassLoader( new URL[] { Utils.getJarPath() }, // Add JAR with Sonar Ant task - it's a Jar which contains this class getClass().getClassLoader(), "org.apache.tools.ant", "org.sonar.ant"); } private void checkSonarVersion() { String serverVersion = bootstrapper.getServerVersion(); log("Sonar version: " + serverVersion); if (isVersionPriorTo2Dot8(serverVersion)) { throw new BuildException("Sonar " + serverVersion + " does not support Sonar Ant Task " + getTaskVersion() + ". Please upgrade Sonar to version 2.8 or more."); } } static boolean isVersionPriorTo2Dot8(String version) { return isVersion(version, "1") || isVersion(version, "2.0") || isVersion(version, "2.1") || isVersion(version, "2.2") || isVersion(version, "2.3") || isVersion(version, "2.4") || isVersion(version, "2.5") || isVersion(version, "2.6") || isVersion(version, "2.7"); } private static boolean isVersion(String version, String prefix) { return version.startsWith(prefix + ".") || version.equals(prefix); } public static String getTaskVersion() { InputStream in = null; try { in = SonarTask.class.getResourceAsStream("/org/sonar/ant/version.txt"); Properties props = new Properties(); props.load(in); return props.getProperty("version"); } catch (IOException e) { throw new BuildException("Could not load the version information for Sonar Ant Task", e); } finally { BootstrapperIOUtils.closeQuietly(in); } } }