Package org.jvnet.hudson.plugins

Source Code of org.jvnet.hudson.plugins.SbtPluginBuilder$DescriptorImpl

package org.jvnet.hudson.plugins;

import hudson.CopyOnWrite;
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.Computer;
import hudson.model.EnvironmentSpecific;
import hudson.model.JDK;
import hudson.model.Node;
import hudson.model.Result;
import hudson.model.TaskListener;
import hudson.remoting.Callable;
import hudson.slaves.NodeSpecific;
import hudson.tasks.BuildStepDescriptor;
import hudson.tasks.Builder;
import hudson.tools.DownloadFromUrlInstaller;
import hudson.tools.ToolDescriptor;
import hudson.tools.ToolInstallation;
import hudson.tools.ToolInstaller;
import hudson.tools.ToolProperty;
import hudson.util.ArgumentListBuilder;
import hudson.util.FormValidation;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrSubstitutor;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* sbt plugin {@link Builder}.
* <p/>
* <p/>
* When the user configures the project and enables this builder,
* {@link DescriptorImpl#newInstance(StaplerRequest)} is invoked and a new
* {@link SbtPluginBuilder} is created. The created instance is persisted to the
* project configuration XML by using XStream, so this allows you to use
* instance fields (like {@link #name}) to remember the configuration.
* <p/>
* <p/>
* When a build is performed, the
* {@link #perform(AbstractBuild, Launcher, BuildListener)} method will be
* invoked.
*
* @author Uzi Landsmann
*/
public class SbtPluginBuilder extends Builder {

    public static final Logger LOGGER = Logger.getLogger(SbtPluginBuilder.class
        .getName());

    private final String name;
    private final String jvmFlags;
    private final String sbtFlags;
    private final String actions;
    private String subdirPath;

    // Fields in config.jelly must match the parameter names in the
    // "DataBoundConstructor"
    @DataBoundConstructor
    public SbtPluginBuilder(String name, String jvmFlags, String sbtFlags,
                            String actions, String subdirPath) {
        this.name = name;
        this.jvmFlags = jvmFlags;
        this.sbtFlags = sbtFlags;
        this.actions = actions;
        this.subdirPath = subdirPath;
    }

    public String getName() {
        return name;
    }

    public String getJvmFlags() {
        return jvmFlags;
    }

    public String getSbtFlags() {
        return sbtFlags;
    }

    public String getActions() {
        return actions;
    }

    public String getSubdirPath() {
        return subdirPath;
    }

    /**
     * Perform the sbt build. Interpret the command arguments and create a
     * command line, then run it.
     */
    @Override
    public boolean perform(AbstractBuild build, Launcher launcher,
                           BuildListener listener) {

        EnvVars env = null;
        FilePath workDir = build.getModuleRoot();
        try {

            ArgumentListBuilder cmdLine = buildCmdLine(build, launcher,
                listener);
            String[] cmds = cmdLine.toCommandArray();
            env = build.getEnvironment(listener);

            if (subdirPath != null && subdirPath.length() > 0) {
                workDir = new FilePath(workDir, subdirPath);
            }

            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 (IllegalArgumentException e) {
            // Util.displayIOException(e, listener);
            e.printStackTrace(listener.fatalError("command execution failed: "
                + e.getMessage()));
            build.setResult(Result.FAILURE);
            return false;
        } catch (IOException e) {
            Util.displayIOException(e, listener);
            e.printStackTrace(listener.fatalError("command execution failed: "
                + e.getMessage()));
            build.setResult(Result.FAILURE);
            return false;
        } catch (InterruptedException e) {
            // Util.displayIOException(e, listener);
            e.printStackTrace(listener.fatalError("command execution failed: "
                + e.getMessage()));
            build.setResult(Result.ABORTED);
            return false;
        }

    }

    /**
     * Create an {@link ArgumentListBuilder} to run the build, given command
     * arguments.
     */
    private ArgumentListBuilder buildCmdLine(AbstractBuild build,
                                             Launcher launcher, BuildListener listener)
        throws IllegalArgumentException, InterruptedException, IOException {
        ArgumentListBuilder args = new ArgumentListBuilder();

//    DescriptorImpl descriptor = (DescriptorImpl) getDescriptor();

        EnvVars env = build.getEnvironment(listener);
        env.overrideAll(build.getBuildVariables());

        SbtInstallation sbt = getSbt();
        if (sbt == null) {
            throw new IllegalArgumentException("sbt-launch.jar not found");
        } else {
            sbt = sbt.forNode(Computer.currentComputer().getNode(), listener);
            sbt = sbt.forEnvironment(env);

            String launcherPath = sbt.getSbtLaunchJar(launcher);

            if (launcherPath == null) {
                throw new IllegalArgumentException("sbt-launch.jar not found");
            }

            if (!launcher.isUnix()) {
                args.add("cmd.exe", "/C");
                // add an extra set of quotes after cmd/c to handle paths with spaces in Windows
                args.add("\"");
            }

            // java
            String javaExePath;

            JDK jdk = build.getProject().getJDK();
            Computer computer = Computer.currentComputer();
            if (computer != null && jdk != null) { // just in case were not in a build
                // use node specific installers, etc
                jdk = jdk.forNode(computer.getNode(), listener);
            }

            if (jdk != null) {
                javaExePath = jdk.getHome() + "/bin/java";
            } else {
                javaExePath = "java";
            }
            args.add(javaExePath);

            splitAndAddArgs(env.expand(jvmFlags), args);
            splitAndAddArgs(env.expand(sbt.getSbtArguments()), args);
            splitAndAddArgs(env.expand(sbtFlags), args);

            // additionnal args from .sbtopts file
            FilePath sbtopts = build.getProject().getWorkspace().child(".sbtopts");
            if (sbtopts.exists()) {
                String argsToSplit = sbtopts.readToString();
                if (!StringUtils.isBlank(argsToSplit)) {
                    String[] split = argsToSplit.split("\\s+");
                    for (String flag : split) {
                        if (flag.startsWith("-J")) {
                          args.add(flag.substring(2));
                        } else {
                          args.add(flag);
                        }
                    }
                }
            }

            args.add("-jar");

            args.add(launcherPath);

            String subActions = new StrSubstitutor(env).replace(actions);
            for (String action : split(subActions)) {
                args.add(action);
            }

            if (!launcher.isUnix()) {
                args.add("\"");
            }
        }

        return args;
    }

    private SbtInstallation getSbt() {
        for (SbtInstallation sbtInstallation : getDescriptor().getInstallations()) {
            if (name != null && name.equals(sbtInstallation.getName())) {
                return sbtInstallation;
            }
        }
        return null;
    }

    /**
     * Split arguments and add them to the args list
     *
     * @param argsToSplit the arguments to split
     * @param args        java/sbt command arguments
     */
    private void splitAndAddArgs(String argsToSplit, ArgumentListBuilder args) {
        if (StringUtils.isBlank(argsToSplit)) {
            return;
        }

        String[] split = argsToSplit.split("\\s+");
        for (String flag : split) {
            args.add(flag);
        }
    }

    /*
     * Splits by whitespace except if surrounded by quotes. See
     * http://stackoverflow
     * .com/questions/366202/regex-for-splitting-a-string-using
     * -space-when-not-surrounded-by-single-or-double/366532#366532
     */
    private List<String> split(String s) {
        List<String> result = new ArrayList<String>();
        Matcher matcher = Pattern.compile("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'")
            .matcher(s);
        while (matcher.find()) {
            if (matcher.group(1) != null)
                result.add(matcher.group(1));
            else if (matcher.group(2) != null)
                result.add(matcher.group(2));
            else
                result.add(matcher.group());
        }
        return result;
    }

    @Override
    public DescriptorImpl getDescriptor() {
        return (DescriptorImpl) super.getDescriptor();
    }

    /**
     * Descriptor for {@link SbtPluginBuilder}. Used as a singleton. The class
     * is marked as public so that it can be accessed from views.
     * <p/>
     * <p/>
     * See <tt>SbtPluginBuilder/*.jelly</tt> for the actual HTML fragment for
     * the configuration screen.
     */
    @Extension
    // this marker indicates Hudson that this is an implementation of an
    // extension point.
    public static final class DescriptorImpl extends
        BuildStepDescriptor<Builder> {

//    private volatile Jar[] jars = new Jar[0];

        @CopyOnWrite
        private volatile SbtInstallation[] installations = new SbtInstallation[0];

        public DescriptorImpl() {
            super(SbtPluginBuilder.class);
            load();
        }

        @Override
        public boolean isApplicable(Class<? extends AbstractProject> aClass) {
            return true;
        }

        /**
         * This human readable name is used in the configuration screen.
         */
        @Override
        public String getDisplayName() {
            return "Build using sbt";
        }

        public SbtInstallation.DescriptorImpl getToolDescriptor() {
            return ToolInstallation.all().get(SbtInstallation.DescriptorImpl.class);
        }

        public SbtInstallation[] getInstallations() {
            return installations;
        }

        public void setInstallations(SbtInstallation... sbtInstallations) {
            this.installations = sbtInstallations;
            save();
        }

    }

    public static final class SbtInstallation extends ToolInstallation implements
        EnvironmentSpecific<SbtInstallation>, NodeSpecific<SbtInstallation>, Serializable {

        private static final long serialVersionUID = -2281774135009218882L;

        private String sbtLaunchJar;

        private String sbtArguments;

        @DataBoundConstructor
        public SbtInstallation(String name, String home, String sbtArguments, List<? extends ToolProperty<?>> properties) {
            super(name, launderHome(home), properties);
            this.sbtArguments = sbtArguments;
            LOGGER.fine("got sbtArguments config: " + sbtArguments);
        }

        private static String launderHome(String home) {
            if (home.endsWith("/") || home.endsWith("\\")) {
                // see https://issues.apache.org/bugzilla/show_bug.cgi?id=26947
                // Ant doesn't like the trailing slash, especially on Windows
                return home.substring(0, home.length() - 1);
            } else {
                return home;
            }
        }

        public String getSbtLaunchJar(Launcher launcher) throws IOException, InterruptedException {
            return launcher.getChannel().call(new Callable<String, IOException>() {
                public String call() throws IOException {
                    File sbtLaunchJarFile = getSbtLaunchJarFile();
                    if(sbtLaunchJarFile.exists())
                        return sbtLaunchJarFile.getPath();
                    return getHome();
                }
            });
        }

        private File getSbtLaunchJarFile() {
            String home = Util.replaceMacro(getHome(), EnvVars.masterEnvVars);
            return new File(home, "bin/sbt-launch.jar");
        }

        public SbtInstallation forEnvironment(EnvVars environment) {
            return new SbtInstallation(getName(), environment.expand(getHome()), sbtArguments, getProperties().toList());
        }

        public SbtInstallation forNode(Node node, TaskListener log) throws IOException, InterruptedException {
            return new SbtInstallation(getName(), translateFor(node, log), sbtArguments, getProperties().toList());
        }

        public String getSbtArguments() {
            return sbtArguments;
        }

        @Extension
        public static class DescriptorImpl extends ToolDescriptor<SbtInstallation> {

            public SbtInstallation[] getInstallations() {
                return Jenkins.getInstance().getDescriptorByType(SbtPluginBuilder.DescriptorImpl.class)
                    .getInstallations();
            }

            @Override
            public void setInstallations(SbtInstallation... installations) {
                Jenkins.getInstance().getDescriptorByType(SbtPluginBuilder.DescriptorImpl.class)
                    .setInstallations(installations);
            }

            @Override
            public List<? extends ToolInstaller> getDefaultInstallers() {
                return Collections.singletonList(new SbtInstaller(null));
            }

            @Override
            public String getDisplayName() {
                return "Sbt";
            }

            /**
             * Checks if the sbt-launch.jar is exist.
             */
            public FormValidation doCheckHome(@QueryParameter File value) {

                if (!Jenkins.getInstance().hasPermission(Jenkins.ADMINISTER)) {
                    return FormValidation.ok();
                }

                // allow empty input
                if(value.getPath().equals(""))
                    return FormValidation.ok();

                if (!value.exists() || !value.isFile()) {
                    return FormValidation.error("sbt-launch.jar not found");
                }

                return FormValidation.ok();
            }
        }
    }

    /**
     * Automatic Sbt installer from scala-sbt.org
     */
    public static class SbtInstaller extends DownloadFromUrlInstaller {

        @DataBoundConstructor
        public SbtInstaller(String id) {
            super(id);
        }

        @Extension
        public static final class DescriptorImpl extends DownloadFromUrlInstaller.DescriptorImpl<SbtInstaller> {

            @Override
            public String getDisplayName() {
                return "Install from scala-sbt.org";
            }

            @Override
            public boolean isApplicable(Class<? extends ToolInstallation> toolType) {
                return toolType == SbtInstallation.class;
            }
        }
    }
}
TOP

Related Classes of org.jvnet.hudson.plugins.SbtPluginBuilder$DescriptorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.