All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala_maven.ScalaScriptMojo Maven / Gradle / Ivy

Go to download

The scala-maven-plugin (previously maven-scala-plugin) is used for compiling/testing/running/documenting scala code of any maven project.

There is a newer version: 4.9.2
Show newest version
package scala_maven;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.URLClassLoader;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.codehaus.plexus.classworlds.ClassWorld;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.classworlds.strategy.SelfFirstStrategy;
import org.codehaus.plexus.classworlds.strategy.Strategy;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.StringUtils;

import scala_maven_executions.JavaMainCaller;
import scala_maven_executions.MainHelper;


/**
 * Run a scala script.
 *
 * @goal script
 * @requiresDependencyResolution runtime
 * @executionStrategy always
 * @since 2.7
 * @threadSafe
 */
public class ScalaScriptMojo extends ScalaMojoSupport {

    /*
     * If the maven-scala-project is a dependency of the project then the
     * MavenProject object describing the project will be passed to the script.
     */

    /**
     * The build directory of the project
     *
     * @parameter property="project.build.directory"
     */
    protected File outputDir;

    /**
     * The file containing script to be executed. Either 'scriptFile'
     * or 'script' must be defined.
     *
     * @parameter property="scriptFile"
     */
    protected File scriptFile;

    /**
     * The encoding of file containing script to be executed.
     *
     * @parameter property="scriptEncoding" default-value="UTF-8"
     */
    protected String scriptEncoding;

    /**
     * The script that will be executed. Either 'scriptFile' or '
     * script' must be defined.
     *
     * @parameter property="script"
     */
    protected String script;

    /**
     * If set to true the Scala classfile that is generated will not be deleted
     * after the goal completes. This is to allows easier debugging of the
     * script especially since line numbers will be wrong because lines are
     * added to the compiled script (see script examples)
     *
     * @parameter property="maven.scala.keepGeneratedScript"
     *            default-value="false"
     */
    protected boolean keepGeneratedScript;

    /**
     * Comma separated list of scopes to add to the classpath.
     * The possible scopes are : test,compile, system, runtime, plugin.
     * By default embedded script into pom.xml run with 'plugin' scope
     * and script read from scriptFile run with 'compile, test, runtime'
     *
     * @parameter property="maven.scala.includeScopes"
     */
    protected String includeScopes;

    /**
     * Comma separated list of scopes to remove from the classpath. Eg:
     * test,compile
     *
     * @parameter property="maven.scala.excludeScopes"
     */
    protected String excludeScopes;

    /**
     * Comma seperated list of directories or jars to add to the classpath
     *
     * @parameter property="addToClasspath"
     */
    protected String addToClasspath;

    /**
     * Comma separated list of directories or jars to remove from the classpath.
     * This is useful for resolving conflicts in the classpath. For example, the
     * script uses Ant 1.7 and the compiler dependencies pull in Ant 1.5
     * optional which conflicts and causes a crash
     *
     * @parameter property="removeFromClasspath"
     */
    protected String removeFromClasspath;

    private static AtomicInteger _lastScriptIndex = new AtomicInteger(0);

    private static String scriptBaseNameOf(File scriptFile, int idx) {
      if (scriptFile == null) {
          return "embeddedScript_" + idx;
      }
      int dot = scriptFile.getName().lastIndexOf('.');
      if (dot == -1) {
          return scriptFile.getName() + "_" + idx;
      }
      return scriptFile.getName().substring(0, dot) + "_" + idx;
    }

    @Override
    protected void doExecute() throws Exception {
        if (script == null && scriptFile == null) {
            throw new MojoFailureException(
                    "Either script or scriptFile must be defined");
        }
        if (script != null && scriptFile != null) {
            throw new MojoFailureException(
                    "Only one of script or scriptFile can be defined");
        }
        if (StringUtils.isEmpty(includeScopes)) {
            if (scriptFile != null) {
                includeScopes = "compile, test, runtime";
            } else {
                includeScopes = "plugin";
            }
        }
        if (excludeScopes == null) {
          excludeScopes = "";
        }
        // prepare
        File scriptDir = new File(outputDir, ".scalaScriptGen");
        scriptDir.mkdirs();
        String baseName = scriptBaseNameOf(scriptFile, _lastScriptIndex.incrementAndGet());
        File destFile = new File(scriptDir, baseName  + ".scala");

        Set classpath = new HashSet();
        configureClasspath(classpath);


        boolean mavenProjectDependency = includeScopes.contains("plugin");
        wrapScript(destFile, mavenProjectDependency);

        try {
            URLClassLoader loader = createScriptClassloader(scriptDir, classpath);
        	getLog().debug(("classpath : " + Arrays.asList(loader.getURLs())));
            compileScript(scriptDir, destFile, loader);
            runScript(mavenProjectDependency, loader, baseName);
        } finally {
            if (!keepGeneratedScript) {
                delete(scriptDir);
            }
        }

    }

    private void runScript(boolean mavenProjectDependency, URLClassLoader loader, String baseName) throws Exception {
        Class compiledScript = loader.loadClass(baseName);

        ClassLoader currentCL = Thread.currentThread().getContextClassLoader();
        Thread.currentThread().setContextClassLoader(loader);
        try {
            try {
                Object instance;
                if (mavenProjectDependency) {
                    Constructor constructor = compiledScript.getConstructor(MavenProject.class, MavenSession.class, Log.class);
                    instance = constructor.newInstance(project, session, getLog());
                } else {
                    instance = compiledScript.newInstance();
                }
                try {
                    compiledScript.getMethod("run").invoke(instance);
                } catch (NoSuchMethodException e) {
                    // ignore because if there is no method then its ok and we
                    // just
                    // don't run the method. initialization of the class must
                    // have been
                    // enough
                }
            } catch (InvocationTargetException e) {
                if (e.getTargetException() != null) {
                    throw e.getTargetException();
                } else if (e.getCause() != null) {
                    throw e.getCause();
                } else {
                    throw e;
                }
            } catch (ExceptionInInitializerError e) {
                if (e.getException() != null) {
                    throw e.getException();
                } else if (e.getCause() != null) {
                    throw e.getCause();
                } else {
                    throw e;
                }
            }
        } catch (Throwable e) {
            if (e instanceof Exception) {
                throw (Exception) e;
            }
            throw new Exception("A " + e.getClass().getSimpleName() + " exception was thrown", e);
        } finally {
            Thread.currentThread().setContextClassLoader(currentCL);
        }
    }

    private URLClassLoader createScriptClassloader(File scriptDir, Set classpath) throws Exception {
        ClassWorld w = new ClassWorld("zero", null);
        w.newRealm("mojo", getClass().getClassLoader());
        Strategy s = new SelfFirstStrategy(w.newRealm("scalaScript", null));
        ClassRealm rScript = s.getRealm();
        rScript.setParentClassLoader(getClass().getClassLoader());
        //rScript.importFrom("mojo", MavenProject.class.getPackage().getName());
        //rScript.importFrom("mojo", MavenSession.class.getPackage().getName());
        //rScript.importFrom("mojo", Log.class.getPackage().getName());
        rScript.importFrom("mojo", "org.apache.maven");
        // add the script directory to the classpath
        rScript.addURL(scriptDir.toURI().toURL());

        for (String string : classpath) {
        	rScript.addURL(new File(string).toURI().toURL());
        }
        return rScript;
    }

    private void compileScript(File scriptDir, File destFile, URLClassLoader loader) throws Exception {
        JavaMainCaller jcmd = getScalaCommand();
        jcmd.addArgs("-classpath", MainHelper.toClasspathString(loader));
        jcmd.addArgs("-d", scriptDir.getAbsolutePath());
        jcmd.addArgs("-sourcepath", scriptDir.getAbsolutePath());
        jcmd.addArgs(destFile.getAbsolutePath());

        jcmd.run(displayCmd);
    }

    private void configureClasspath(Set classpath) throws Exception, DependencyResolutionRequiredException {
        Set includes = new TreeSet(Arrays.asList(includeScopes.toLowerCase().split(",")));
        Set excludes = new TreeSet(Arrays.asList(excludeScopes.toLowerCase().split(",")));

        for(Artifact a : project.getArtifacts()) {
          if (includes.contains(a.getScope().toLowerCase()) && !excludes.contains(a.getScope())) {
            addToClasspath(a, classpath, true);
          }
        }

        if (includes.contains("plugin") && !excludes.contains("plugin")) {
            //Plugin plugin = project.getPlugin("scala-maven-plugin");
            for(Plugin p : project.getBuildPlugins()) {
                if ("scala-maven-plugin".equals(p.getArtifactId())) {
                    for(Dependency d : p.getDependencies()) {
                        addToClasspath(factory.createDependencyArtifact(d), classpath, true);
                    }
                }
            }
            for(Artifact a : project.getPluginArtifacts()) {
                if ("scala-maven-plugin".equals(a.getArtifactId())) {
                    addToClasspath(a, classpath, true);
                }
            }
        }

        if (addToClasspath != null) {
            classpath.addAll(Arrays.asList(addToClasspath.split(",")));
        }

        if (removeFromClasspath != null) {
            ArrayList toRemove = new ArrayList();
            String[] jars = removeFromClasspath.trim().split(",");
            for (String string : classpath) {
                for (String jar : jars) {
                    if (string.contains(jar.trim())) {
                        toRemove.add(string);
                    }
                }
            }
            classpath.removeAll(toRemove);
        }

//        String outputDirectory = project.getBuild().getOutputDirectory();
//        if(!outputDirectory.endsWith("/")){
//            // need it to end with / for URLClassloader
//            outputDirectory+="/";
//        }
//        classpath.add( outputDirectory);
        addCompilerToClasspath(classpath);
        addLibraryToClasspath(classpath);
        //TODO check that every entry from the classpath exists !
        boolean ok = true;
        for (String s : classpath) {
            File f = new File(s);
            getLog().debug("classpath entry for running and compiling scripts: " + f);
            if (!f.exists()) {
                getLog().error("classpath entry for script not found : " + f);
                ok = false;
            }
        }
        if (!ok) {
            throw new MojoFailureException("some script dependencies not found (see log)");
        }
        getLog().debug("Using the following classpath for running and compiling scripts: "+classpath);

    }

    private void wrapScript(File destFile, boolean mavenProjectDependency) throws IOException {
        destFile.delete();

        FileOutputStream fileOutputStream = new FileOutputStream(destFile);
        PrintStream out = new PrintStream(fileOutputStream, false, encoding);
        BufferedReader reader = null;
        try {
            if (scriptFile != null) {
                reader = new BufferedReader(new InputStreamReader(new FileInputStream(scriptFile), Charset.forName(scriptEncoding)));
            } else {
                reader = new BufferedReader(new StringReader(script));
            }

            String baseName = FileUtils.basename(destFile.getName(), ".scala");
            if (mavenProjectDependency) {
//                out.println("import scala.collection.jcl.Conversions._");
                out.println("class " + baseName
                        + "(project :" + MavenProject.class.getCanonicalName()
                        + ",session :" + MavenSession.class.getCanonicalName()
                        + ",log :"+Log.class.getCanonicalName()
                        +") {"
                        );
            } else {
                out.println("class " + baseName + " {");
            }

            String line = reader.readLine();
            while (line != null) {
                out.print("  ");
                out.println(line);
                line = reader.readLine();
            }

            out.println("}");
        } finally {
            IOUtil.close(out);
            IOUtil.close(fileOutputStream);
            IOUtil.close(reader);
        }
    }

    private void delete(File scriptDir) {
        if (scriptDir.isDirectory()) {
            for (File file : scriptDir.listFiles()) {
                delete(file);
            }
        }

        scriptDir.deleteOnExit();
        scriptDir.delete();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy