/* * Copyright (C) 2010 JFrog Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jfrog.hudson.maven3; import com.google.common.collect.Maps; import hudson.EnvVars; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; import hudson.Util; import hudson.model.AbstractBuild; import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Cause; import hudson.model.CauseAction; import hudson.model.Hudson; import hudson.model.Result; import hudson.model.Run; import hudson.remoting.Which; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.Builder; import hudson.tasks.Maven; import hudson.tools.ToolInstallation; import hudson.util.ArgumentListBuilder; import net.sf.json.JSONObject; import org.apache.commons.lang.StringUtils; import org.jfrog.build.api.BuildInfoProperties; import org.jfrog.build.client.ClientProperties; import org.jfrog.build.extractor.maven.AbstractPropertyResolver; import org.jfrog.build.extractor.maven.BuildInfoRecorder; import org.jfrog.hudson.maven3.util.ActionableHelper; import org.kohsuke.stapler.DataBoundConstructor; import org.kohsuke.stapler.StaplerRequest; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; import java.util.Map; /** * Maven3 builder. * * @author Yossi Shaul */ public class Maven3Builder extends Builder { public static final String CLASSWORLDS_LAUNCHER = "org.codehaus.plexus.classworlds.launcher.Launcher"; private final String mavenName; private final String rootPom; private final String goals; private final String mavenOpts; @DataBoundConstructor public Maven3Builder(String mavenName, String rootPom, String goals, String mavenOpts) { this.mavenName = mavenName; this.rootPom = rootPom; this.goals = goals; this.mavenOpts = mavenOpts; } public String getMavenName() { return mavenName; } public String getRootPom() { return rootPom; } public String getGoals() { return goals; } public String getMavenOpts() { return mavenOpts; } @Override public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { EnvVars env = build.getEnvironment(listener); FilePath workDir = build.getModuleRoot(); ArgumentListBuilder cmdLine = buildMavenCmdLine(build, launcher, listener); String[] cmds = cmdLine.toCommandArray(); try { //listener.getLogger().println("Executing: " + cmdLine.toStringWithQuote()); int exitValue = launcher.launch().cmds(cmds).envs(env).stdout(listener).pwd(workDir).join(); boolean success = (exitValue == 0); build.setResult(success ? Result.SUCCESS : Result.FAILURE); return success; } catch (IOException e) { Util.displayIOException(e, listener); e.printStackTrace(listener.fatalError("command execution failed")); build.setResult(Result.FAILURE); return false; } } private ArgumentListBuilder buildMavenCmdLine(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { Maven.MavenInstallation mvn = getMavenInstallation(); if (mvn == null) { listener.error("Maven version is not configured for this project. Can't determine which Maven to run"); throw new Run.RunnerAbortedException(); } if (mvn.getHome() == null) { listener.error("Maven '%s' doesn't have its home set", mvn.getName()); throw new Run.RunnerAbortedException(); } ArgumentListBuilder args = new ArgumentListBuilder(); if (!launcher.isUnix()) { args.add("cmd.exe", "/C"); } // java String java = build.getProject().getJDK() != null ? build.getProject().getJDK().getBinDir() + "/java" : "java"; args.add(new File(java).getAbsolutePath()); // maven opts args.addTokenized(getMavenOpts()); File bootDir = new File(mvn.getHomeDir(), "boot"); File[] candidates = bootDir.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.startsWith("plexus-classworlds"); } }); if (candidates == null || candidates.length == 0) { listener.error("Couldn't find classworlds jar under " + bootDir.getAbsolutePath()); throw new Run.RunnerAbortedException(); } File classWorldsJar = candidates[0]; // classpath args.add("-cp"); String cpSeparator = launcher.isUnix() ? ":" : ";"; StringBuilder cpBuilder = new StringBuilder(); cpBuilder.append(classWorldsJar.getAbsolutePath()); getClass().getClassLoader().getResource("classworlds.conf"); // extractor jars File extractorJar = Which.jarFile(AbstractPropertyResolver.class); if (extractorJar == null || !extractorJar.exists()) { listener.error("Couldn't find maven3 extractor jar (class not found: " + AbstractPropertyResolver.class.getName() + ")."); throw new Run.RunnerAbortedException(); } File extractorLibDir = extractorJar.getParentFile(); for (File extractorDependency : extractorLibDir.listFiles()) { //cpBuilder.append(cpSeparator).append(extractorDependency.getAbsolutePath()); } args.add(cpBuilder.toString()); // maven home args.add("-Dmaven.home=" + mvn.getHome()); // classworlds conf args.add("-Dm3plugin.lib=" + extractorLibDir); File pluginClasses = Which.jarFile(this.getClass()); File classworldsConf = new File(pluginClasses, "classworlds.conf"); if (!classworldsConf.exists()) { listener.error( "Unable to locate classworlds configuration file under " + classworldsConf.getAbsolutePath()); throw new Run.RunnerAbortedException(); } args.add("-Dclassworlds.conf=" + classworldsConf.getAbsolutePath()); addBuilderInfoArguments(args, build, listener); // maven opts args.add(Util.replaceMacro(mavenOpts, build.getBuildVariableResolver())); // classworlds launcher main class args.add(CLASSWORLDS_LAUNCHER); // pom file to build args.add("-f", rootPom); // maven goals args.addTokenized(goals); return args; } private void addBuilderInfoArguments(ArgumentListBuilder args, AbstractBuild<?, ?> build, BuildListener listener) throws IOException, InterruptedException { Map<String, String> props = Maps.newHashMap(); props.put(BuildInfoRecorder.ACTIVATE_RECORDER, Boolean.TRUE.toString()); String buildName = build.getDisplayName(); props.put(BuildInfoProperties.PROP_BUILD_NAME, buildName); props.put(ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX + "build.name", buildName); String buildNumber = build.getNumber() + ""; props.put(BuildInfoProperties.PROP_BUILD_NUMBER, buildNumber); props.put(ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX + "build.number", buildName); // TODO: what is the expected format? String buildStarted = build.getTimestamp().getTimeInMillis() + ""; props.put(BuildInfoProperties.PROP_BUILD_STARTED, buildStarted); EnvVars envVars = build.getEnvironment(listener); String vcsRevision = envVars.get("SVN_REVISION"); if (StringUtils.isNotBlank(vcsRevision)) { props.put(BuildInfoProperties.PROP_VCS_REVISION, vcsRevision); props.put(ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX + BuildInfoProperties.PROP_VCS_REVISION, vcsRevision); } String buildUrl = Hudson.getInstance().getRootUrl() + build.getUrl(); props.put(BuildInfoProperties.PROP_BUILD_URL, buildUrl); Cause.UpstreamCause parent = ActionableHelper.getUpstreamCause(build); if (parent != null) { String parentProject = parent.getUpstreamProject(); props.put(BuildInfoProperties.PROP_PARENT_BUILD_NAME, parentProject); props.put(ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX + BuildInfoProperties.PROP_PARENT_BUILD_NAME, parentProject); String parentBuildName = parent.getUpstreamBuild() + ""; props.put(BuildInfoProperties.PROP_PARENT_BUILD_NUMBER, parentBuildName); props.put(ClientProperties.PROP_DEPLOY_PARAM_PROP_PREFIX + BuildInfoProperties.PROP_PARENT_BUILD_NUMBER, parentBuildName); } CauseAction action = ActionableHelper.getLatestAction(build, CauseAction.class); if (action != null) { for (Cause cause : action.getCauses()) { if (cause instanceof Cause.UserCause) { String userName = ((Cause.UserCause) cause).getUserName(); props.put(BuildInfoProperties.PROP_PRINCIPAL, userName); } } } props.put(BuildInfoProperties.PROP_AGENT_NAME, "hudson"); props.put(BuildInfoProperties.PROP_AGENT_VERSION, build.getHudsonVersion()); //props.put(ClientProperties.PROP_CONTEXT_URL, serverConfig.getUrl()); //props.put(ClientProperties.PROP_TIMEOUT, Integer.toString(serverConfig.getTimeout())); //props.put(ClientProperties.PROP_PUBLISH_REPOKEY, builder.getDeployableRepo()); /*String deployerUsername = builder.getDeployerUsername(); if (StringUtils.isNotBlank(deployerUsername)) { props.put(ClientProperties.PROP_PUBLISH_USERNAME, deployerUsername); props.put(ClientProperties.PROP_PUBLISH_PASSWORD, builder.getDeployerPassword()); }*/ props.put(ClientProperties.PROP_PUBLISH_ARTIFACT, Boolean.FALSE.toString()); props.put(ClientProperties.PROP_PUBLISH_BUILD_INFO, Boolean.FALSE.toString()); File outputFile = new File(build.getRootDir(), "maven3/buildinfo.json"); outputFile.getParentFile().mkdirs(); props.put(BuildInfoProperties.PROP_BUILD_INFO_OUTPUT_FILE, outputFile.getAbsolutePath()); args.addKeyValuePairs("-D", props); } public Maven.MavenInstallation getMavenInstallation() { Maven.MavenInstallation[] installations = getDescriptor().getInstallations(); for (Maven.MavenInstallation installation : installations) { if (installation.getName().equals(mavenName)) { return installation; } } // not found, return the first installation if exists return installations.length > 0 ? installations[0] : null; } @Override public DescriptorImpl getDescriptor() { return (DescriptorImpl) super.getDescriptor(); } @Extension public static final class DescriptorImpl extends BuildStepDescriptor<Builder> { public DescriptorImpl() { load(); } protected DescriptorImpl(Class<? extends Maven3Builder> clazz) { super(clazz); } /** * Obtains the {@link Maven.MavenInstallation.DescriptorImpl} instance. */ public Maven.MavenInstallation.DescriptorImpl getToolDescriptor() { return ToolInstallation.all().get(Maven.MavenInstallation.DescriptorImpl.class); } @Override public boolean isApplicable(Class<? extends AbstractProject> jobType) { return true; } @Override public String getHelpFile() { return "/plugin/maven3/help.html"; } @Override public String getDisplayName() { return Messages.step_displayName(); } public Maven.DescriptorImpl getMavenDescriptor() { return Hudson.getInstance().getDescriptorByType(Maven.DescriptorImpl.class); } public Maven.MavenInstallation[] getInstallations() { return getMavenDescriptor().getInstallations(); } @Override public Maven3Builder newInstance(StaplerRequest request, JSONObject formData) throws FormException { return (Maven3Builder) request.bindJSON(clazz, formData); } } }