This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch rmannibucau/java-scripting-draft
in repository https://gitbox.apache.org/repos/asf/maven-scripting-plugin.git

commit 0346ed96d069c6a4edcd6bf980fea7409169e84d
Author: Romain Manni-Bucau <rmannibu...@gmail.com>
AuthorDate: Fri Feb 26 09:41:24 2021 +0100

    basic java scripting engine support
---
 pom.xml                                            |  38 ++
 src/it/java/pom.xml                                |  68 ++++
 src/it/java/verify.bsh                             |  44 +++
 .../plugins/scripting/AbstractScriptEvaluator.java |  22 +-
 .../apache/maven/plugins/scripting/EvalMojo.java   |  70 +++-
 .../plugins/scripting/FileScriptEvaluator.java     |   5 +-
 .../plugins/scripting/StringScriptEvaluator.java   |   4 +-
 .../plugins/scripting/engine/JavaScriptEngine.java | 422 +++++++++++++++++++++
 .../scripting/engine/JavaScriptEngineFactory.java  | 133 +++++++
 .../scripting/engine/JavaScriptEngineTest.java     |  99 +++++
 10 files changed, 897 insertions(+), 8 deletions(-)

diff --git a/pom.xml b/pom.xml
index 13fb174..5b20d0d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -69,6 +69,7 @@ under the License.
     <mavenVersion>3.0</mavenVersion>
     <maven.compiler.source>1.8</maven.compiler.source>
     <maven.compiler.target>1.8</maven.compiler.target>
+    <plexus.compiler.version>2.8.8</plexus.compiler.version>
     
<project.build.outputTimestamp>2021-02-24T19:52:25Z</project.build.outputTimestamp>
   </properties>
 
@@ -96,6 +97,42 @@ under the License.
       <scope>provided</scope>
     </dependency>
 
+    <!-- java script engine -->
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-compiler-api</artifactId>
+      <version>${plexus.compiler.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-component-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-compiler-manager</artifactId>
+      <version>${plexus.compiler.version}</version>
+      <exclusions>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-component-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-compiler-javac</artifactId>
+      <version>${plexus.compiler.version}</version>
+      <scope>runtime</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>org.codehaus.plexus</groupId>
+          <artifactId>plexus-component-api</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+
     <!-- Test -->
     <dependency>
       <groupId>junit</groupId>
@@ -133,6 +170,7 @@ under the License.
                 <postBuildHookScript>verify</postBuildHookScript>
                 
<localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
                 <settingsFile>src/it/settings.xml</settingsFile>
+                <postBuildHookScript>verify.bsh</postBuildHookScript>
                 <goals>
                   <goal>scripting:eval</goal>
                 </goals>
diff --git a/src/it/java/pom.xml b/src/it/java/pom.xml
new file mode 100644
index 0000000..0db926d
--- /dev/null
+++ b/src/it/java/pom.xml
@@ -0,0 +1,68 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"; 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; 
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>org.apache.maven.plugins.scripting.its</groupId>
+  <artifactId>java</artifactId>
+  <version>1.0.0-SNAPSHOT</version>
+  <packaging>pom</packaging>
+  
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-scripting-plugin</artifactId>
+        <version>@project.version@</version>
+        <configuration>
+          <engineName>java</engineName>
+          <scriptEngineConfiguration>
+            <main>MyMain</main>
+            <arg.00>1</arg.00>
+            <arg.01>2</arg.01>
+            <arg.02>3</arg.02>
+            <arg.03>4</arg.03>
+            <arg.10>5</arg.10>
+            <arg.110>6</arg.110>
+          </scriptEngineConfiguration>
+          <script>
+          <![CDATA[
+            import java.nio.file.Files;
+            import java.nio.file.Path;
+            import java.nio.file.Paths;
+            import java.nio.file.StandardOpenOption;
+
+            public class MyMain {
+              public static void main(String... args) throws Exception {
+                Path out = Paths.get("target/out");
+                Files.createDirectories(out.getParent());
+                Files.write(out, String.join(",", args).getBytes(),
+                  StandardOpenOption.WRITE, StandardOpenOption.CREATE, 
StandardOpenOption.TRUNCATE_EXISTING);
+              }
+            }
+          ]]>
+          </script>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
+</project>
diff --git a/src/it/java/verify.bsh b/src/it/java/verify.bsh
new file mode 100644
index 0000000..a0f3f98
--- /dev/null
+++ b/src/it/java/verify.bsh
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+// beanshell does not like much java.nio.file so let's use java.io which is 
more than enough for us
+import java.util.stream.Collectors;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.File;
+
+File file = new File( basedir, "target/out" );
+if ( !file.exists() )
+{
+    throw new FileNotFoundException( "Could not find generated JAR: " + file );
+}
+String content;
+BufferedReader reader = new BufferedReader( new FileReader( file ) );
+try
+{
+  content = reader.lines().collect(Collectors.joining( "\n" ) );
+}
+finally
+{
+  reader.close();
+}
+if ( !"1,2,3,4,5,6".equals( content ) )
+{
+    throw new IllegalArgumentException( "Wrong content: '" + content + "'" );
+}
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/maven/plugins/scripting/AbstractScriptEvaluator.java 
b/src/main/java/org/apache/maven/plugins/scripting/AbstractScriptEvaluator.java
index 7605680..cfd0866 100644
--- 
a/src/main/java/org/apache/maven/plugins/scripting/AbstractScriptEvaluator.java
+++ 
b/src/main/java/org/apache/maven/plugins/scripting/AbstractScriptEvaluator.java
@@ -19,6 +19,8 @@ package org.apache.maven.plugins.scripting;
  * under the License.
  */
 
+import org.apache.maven.plugins.scripting.engine.JavaScriptEngineFactory;
+
 import javax.script.Bindings;
 import javax.script.ScriptContext;
 import javax.script.ScriptEngine;
@@ -31,6 +33,12 @@ import javax.script.ScriptException;
  */
 abstract class AbstractScriptEvaluator
 {
+  protected final EvalMojo mojo;
+
+  protected AbstractScriptEvaluator( final EvalMojo mojo )
+  {
+    this.mojo = mojo;
+  }
 
   /**
    * @param bindings not null bindings to provide to the script to execute
@@ -40,7 +48,7 @@ abstract class AbstractScriptEvaluator
    */
   public final Object eval( Bindings bindings ) throws ScriptException, 
UnsupportedScriptEngineException
   {
-    ScriptEngineManager manager = new ScriptEngineManager();
+    ScriptEngineManager manager = newScriptEngineManager();
     ScriptEngine engine = getEngine( manager );
     ScriptContext context = engine.getContext();
 
@@ -65,4 +73,16 @@ abstract class AbstractScriptEvaluator
    */
   protected abstract ScriptEngine getEngine( ScriptEngineManager manager )
       throws UnsupportedScriptEngineException;
+
+  private ScriptEngineManager newScriptEngineManager()
+  {
+    // don't go through the spi to have the mojo
+    JavaScriptEngineFactory javaScriptEngineFactory = new 
JavaScriptEngineFactory( mojo );
+    ScriptEngineManager manager = new ScriptEngineManager();
+    javaScriptEngineFactory.getExtensions().forEach( ext -> 
manager.registerEngineExtension(
+            ext, javaScriptEngineFactory ) );
+    javaScriptEngineFactory.getNames().forEach( name -> 
manager.registerEngineName(
+            name, javaScriptEngineFactory ) );
+    return manager;
+  }
 }
diff --git a/src/main/java/org/apache/maven/plugins/scripting/EvalMojo.java 
b/src/main/java/org/apache/maven/plugins/scripting/EvalMojo.java
index 4a6dbbc..27b1391 100644
--- a/src/main/java/org/apache/maven/plugins/scripting/EvalMojo.java
+++ b/src/main/java/org/apache/maven/plugins/scripting/EvalMojo.java
@@ -20,17 +20,24 @@ package org.apache.maven.plugins.scripting;
  */
 
 import java.io.File;
+import java.util.Map;
 
 import javax.script.Bindings;
 import javax.script.ScriptException;
 import javax.script.SimpleBindings;
 
+import org.apache.maven.execution.MavenSession;
 import org.apache.maven.plugin.AbstractMojo;
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
 import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.project.MavenProject;
+import org.apache.maven.toolchain.ToolchainManager;
+import org.codehaus.plexus.compiler.manager.CompilerManager;
+
+import static 
org.apache.maven.plugins.annotations.ResolutionScope.COMPILE_PLUS_RUNTIME;
 
 /**
  * Evaluate the specified script or scriptFile
@@ -38,7 +45,7 @@ import org.apache.maven.project.MavenProject;
  * @author Robert Scholte
  * @since 3.0.0
  */
-@Mojo( name = "eval" )
+@Mojo( name = "eval", requiresDependencyResolution = COMPILE_PLUS_RUNTIME )
 public class EvalMojo
     extends AbstractMojo
 {
@@ -60,9 +67,39 @@ public class EvalMojo
     @Parameter
     private File scriptFile;
 
+    /**
+     * Script Engine specific configuration.
+     * For now it configured maven java script engine.
+     * Available keys today:
+     * <ul>
+     *     <li>javaVersion: java version for the compiler (source/target)</li>
+     *     <li>output: where to put generated files by the compilation</li>
+     *     <li>encoding: encoding for the compiler</li>
+     *     <li>outputFileName: folder name to compile into</li>
+     *     <li>main: main fqn</li>
+     *     <li>method: method to execute if not a main, can have optionally a 
String array as parameter</li>
+     *     <li>arg.{i}: i-th argument of the main</li>
+     *     <li>usePluginLoader: should the parent classloader of the mojo be 
the plugin one or not</li>
+     * </ul>
+     */
+    @Parameter
+    protected Map<String, String> scriptEngineConfiguration;
+
+    @Component
+    protected ToolchainManager toolchainManager;
+
+    @Component
+    protected CompilerManager compilerManager;
+
     // script variables
     @Parameter( defaultValue = "${project}", readonly = true )
-    private MavenProject project;
+    protected MavenProject project;
+
+    /**
+     * Build session.
+     */
+    @Parameter( defaultValue = "${session}", readonly = true, required = true )
+    protected MavenSession session;
 
     @Override
     public void execute()
@@ -100,12 +137,12 @@ public class EvalMojo
 
       if ( scriptFile != null )
       {
-         execute = new FileScriptEvaluator( engineName, scriptFile );
+         execute = new FileScriptEvaluator( engineName, scriptFile, this );
 
       }
       else if ( script != null )
       {
-         execute = new StringScriptEvaluator( engineName, script );
+         execute = new StringScriptEvaluator( engineName, script, this );
 
       }
       else
@@ -114,4 +151,29 @@ public class EvalMojo
       }
       return execute;
     }
+
+    public CompilerManager getCompilerManager()
+    {
+        return compilerManager;
+    }
+
+    public MavenProject getProject()
+    {
+        return project;
+    }
+
+    public Map<String, String> getScriptEngineConfiguration()
+    {
+        return scriptEngineConfiguration;
+    }
+
+    public MavenSession getSession()
+    {
+        return session;
+    }
+
+    public ToolchainManager getToolchainManager()
+    {
+        return toolchainManager;
+    }
 }
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/maven/plugins/scripting/FileScriptEvaluator.java 
b/src/main/java/org/apache/maven/plugins/scripting/FileScriptEvaluator.java
index 9f7abd7..af056bf 100644
--- a/src/main/java/org/apache/maven/plugins/scripting/FileScriptEvaluator.java
+++ b/src/main/java/org/apache/maven/plugins/scripting/FileScriptEvaluator.java
@@ -51,10 +51,10 @@ public class FileScriptEvaluator extends 
AbstractScriptEvaluator
    * @param engineName optional engine name, used to override the engine 
selection from the file extension
    * @param scriptFile not null
    */
-  public FileScriptEvaluator( String engineName, File scriptFile )
+  public FileScriptEvaluator( String engineName, File scriptFile, EvalMojo 
mojo )
   {
+    super( mojo );
     this.scriptFile = scriptFile;
-
     this.engineName = engineName;
   }
 
@@ -69,6 +69,7 @@ public class FileScriptEvaluator extends 
AbstractScriptEvaluator
   {
     try ( FileReader reader = new FileReader( scriptFile ) )
     {
+        context.setAttribute( "maven.filename", scriptFile.getName(), 
ScriptContext.ENGINE_SCOPE );
         return engine.eval( reader, context );
     }
     catch ( IOException ex )
diff --git 
a/src/main/java/org/apache/maven/plugins/scripting/StringScriptEvaluator.java 
b/src/main/java/org/apache/maven/plugins/scripting/StringScriptEvaluator.java
index 5a31c85..f2ddd06 100644
--- 
a/src/main/java/org/apache/maven/plugins/scripting/StringScriptEvaluator.java
+++ 
b/src/main/java/org/apache/maven/plugins/scripting/StringScriptEvaluator.java
@@ -46,8 +46,9 @@ public class StringScriptEvaluator extends 
AbstractScriptEvaluator
    * @param script the script
    * @throws IllegalArgumentException if either engineName or script is null
    */
-  public StringScriptEvaluator( String engineName, String script )
+  public StringScriptEvaluator( String engineName, String script, EvalMojo 
mojo )
   {
+    super( mojo );
     if ( engineName == null || engineName.isEmpty() )
     {
       throw new IllegalArgumentException( "Expected a non-empty engine name 
provided" );
@@ -85,6 +86,7 @@ public class StringScriptEvaluator extends 
AbstractScriptEvaluator
    */
   protected Object eval( ScriptEngine engine, ScriptContext context ) throws 
ScriptException
   {
+    context.setAttribute( "maven.filename", "mem_" + script.hashCode() + 
".java", ScriptContext.ENGINE_SCOPE );
     return engine.eval( script, context );
   }
 }
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngine.java 
b/src/main/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngine.java
new file mode 100644
index 0000000..a503f24
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngine.java
@@ -0,0 +1,422 @@
+package org.apache.maven.plugins.scripting.engine;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.commons.io.input.ReaderInputStream;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugins.scripting.EvalMojo;
+import org.apache.maven.toolchain.Toolchain;
+import org.codehaus.plexus.compiler.CompilerConfiguration;
+import org.codehaus.plexus.compiler.CompilerMessage;
+import org.codehaus.plexus.compiler.CompilerResult;
+
+import javax.script.AbstractScriptEngine;
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngineFactory;
+import javax.script.SimpleBindings;
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+import static java.lang.ClassLoader.getSystemClassLoader;
+import static java.util.Collections.singletonList;
+import static java.util.Comparator.comparing;
+import static java.util.Objects.requireNonNull;
+import static java.util.Optional.ofNullable;
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+
+/**
+ * Maven-java implementation of a script engine.
+ */
+public class JavaScriptEngine extends AbstractScriptEngine
+{
+    private final JavaScriptEngineFactory factory;
+
+    private final List<Path> toDelete = new ArrayList<>();
+
+    public JavaScriptEngine(  final JavaScriptEngineFactory factory )
+    {
+        this.factory = factory;
+    }
+
+    @Override
+    public Object eval(  final String script, final ScriptContext context )
+    {
+        return eval( new StringReader( script ), context );
+    }
+
+    @Override
+    public Bindings createBindings()
+    {
+        return new SimpleBindings();
+    }
+
+    @Override
+    public ScriptEngineFactory getFactory()
+    {
+        return factory;
+    }
+
+    @Override
+    public Object eval(  final Reader reader, final ScriptContext context )
+    {
+        final EvalMojo mojo = factory.getMojo();
+        final String mainClassName = requireNonNull(
+                mojo.getScriptEngineConfiguration().get( "main" ), "missing 
main" );
+
+        final CompilerConfiguration conf = createConf( reader, mojo, 
mainClassName );
+        toDelete.add( Paths.get( conf.getOutputLocation() ) );
+        try
+        {
+            CompilerResult result;
+            try
+            {
+                result = mojo.getCompilerManager().getCompiler( "javac" 
).performCompile( conf );
+            }
+            catch (  Exception e )
+            {
+                throw new IllegalStateException( "Fatal error compiling", e );
+            }
+
+            log( mojo, result );
+
+            final String[] args = ofNullable( 
mojo.getScriptEngineConfiguration() )
+                    .map( c -> c.keySet().stream()
+                            .filter( it -> it.startsWith( "arg." ) )
+                            .sorted( comparing( i -> Integer.parseInt( 
i.substring( "arg.".length() ) ) ) )
+                            .map( c::get )
+                            .toArray( String[]::new ) )
+                    .orElseGet( () -> new String[0] );
+
+            return doExecute( mainClassName, conf, args, 
mojo.getScriptEngineConfiguration() );
+        }
+        finally
+        {
+            toDelete.forEach( path ->
+            {
+                if (  !Files.isDirectory(  path ) )
+                {
+                    try
+                    {
+                        Files.delete(  path );
+                    }
+                    catch (  final IOException e )
+                    {
+                        throw new IllegalArgumentException(  e );
+                    }
+                }
+                else
+                {
+                    try
+                    {
+                        Files.walkFileTree( path, new SimpleFileVisitor<Path>()
+                        {
+                            @Override
+                            public FileVisitResult visitFile(  final Path 
file, final BasicFileAttributes attrs )
+                                    throws IOException
+                            {
+                                Files.delete( file );
+                                return super.visitFile( file, attrs );
+                            }
+
+                            @Override
+                            public FileVisitResult postVisitDirectory(  final 
Path dir, final IOException exc )
+                                    throws IOException
+                            {
+                                Files.delete( dir );
+                                return super.postVisitDirectory( dir, exc );
+                            }
+                        } );
+                    }
+                    catch (  final IOException e )
+                    {
+                        throw new IllegalArgumentException( e );
+                    }
+                }
+            } );
+            toDelete.clear();
+        }
+    }
+
+    private Object doExecute(  final String mainClassName, final 
CompilerConfiguration conf,
+                             final String[] args, final Map<String, String> 
mojoConf )
+    {
+        try
+        {
+            try (  final URLClassLoader classLoader = new URLClassLoader( 
+                    Stream.concat( 
+                            conf.getClasspathEntries().stream()
+                                    .map( Paths::get )
+                                    .map( p ->
+                                    {
+                                        try
+                                        {
+                                            return p.toUri().toURL();
+                                        }
+                                        catch (  final MalformedURLException e 
)
+                                        {
+                                            throw new 
IllegalArgumentException( e );
+                                        }
+                                    } ),
+                            Stream.of( Paths.get( conf.getOutputLocation() )
+                                    .toUri()
+                                    .toURL() ) )
+                            .toArray( URL[]::new ),
+                    Boolean.parseBoolean( mojoConf.get( "usePluginLoader" ) )
+                            ? Thread.currentThread().getContextClassLoader()
+                            : getSystemClassLoader() ) )
+            {
+                try
+                {
+                    final Class<?> mainClass = classLoader.loadClass( 
mainClassName.trim() );
+                    final String mtd = mojoConf.get( "method" );
+                    final Method main;
+                    if ( mtd == null )
+                    {
+                        main = mainClass.getDeclaredMethod( "main", 
String[].class );
+                    }
+                    else
+                    {
+                        main = Stream.of( mainClass.getDeclaredMethods() )
+                                .filter( m -> Objects.equals( mtd, m.getName() 
) )
+                                .sorted( comparing( Method::getParameterCount 
) )
+                                .findAny()
+                                .orElseThrow( () -> new 
IllegalArgumentException(
+                                        "No method '" + mtd + "' in " + 
mainClass ) );
+                    }
+                    if ( !main.isAccessible() )
+                    {
+                        main.setAccessible( true );
+                    }
+                    main.invoke( null, new Object[]{args} );
+                    return null;
+                }
+                catch (  final Exception e )
+                {
+                    throw new IllegalStateException( e );
+                }
+            }
+        }
+        catch (  final IOException e )
+        {
+            throw new IllegalArgumentException( e );
+        }
+    }
+
+    private CompilerConfiguration createConf(  final Reader reader, final 
EvalMojo mojo,
+                                             final String mainClassName )
+    {
+        final Toolchain tc = 
mojo.getToolchainManager().getToolchainFromBuildContext( "jdk", 
mojo.getSession() );
+        final String defaultJavaVersion = ofNullable( 
mojo.getScriptEngineConfiguration() )
+                .map( map -> map.get( "javaVersion" ) )
+                .orElseGet( () -> ofNullable( 
mojo.getProject().getProperties().getProperty( "maven.compiler.source" ) )
+                        .orElse( "8" ) );
+        final String output = ofNullable( mojo.getScriptEngineConfiguration() )
+                .map( map -> map.get( "output" ) )
+                .orElseGet( () -> Paths.get( 
mojo.getProject().getBuild().getDirectory() )
+                        .resolve( "maven-scripting-plugin/classes_" + 
context.getAttribute( "maven.filename" ) )
+                        .toString() );
+        final String encoding = ofNullable( 
mojo.getScriptEngineConfiguration() )
+                .map( map -> map.get( "encoding" ) )
+                .orElseGet( () -> ofNullable( mojo.getProject()
+                        .getProperties().getProperty( 
"project.build.sourceEncoding" ) )
+                        .orElse( "UTF-8" ) );
+        final String outputFileName = ofNullable( 
mojo.getScriptEngineConfiguration() )
+                .map( map -> map.get( "outputFileName" ) )
+                .orElse( "script" );
+        final String compileSourceRoot = ofNullable( 
mojo.getScriptEngineConfiguration() )
+                .map( map -> map.get( "compileSourceRoot" ) )
+                .filter( s -> !s.isEmpty() )
+                .orElseGet( () ->
+                {
+                    final File file = new File( 
mojo.getProject().getBasedir(), "src/main/scripting" );
+                    if ( !file.exists() )
+                    {
+                        return null;
+                    }
+                    return file.getAbsolutePath();
+                } );
+        final List<String> classpath = ofNullable( 
mojo.getScriptEngineConfiguration() )
+                .map( c -> c.get( "classpath" ) )
+                .map( c -> Stream.of( c.split( ":" ) ).collect( toList() ) )
+                .orElseGet( () -> mojo.getProject().getArtifacts().stream()
+                        .map( Artifact::getFile )
+                        .filter( Objects::nonNull )
+                        .map( File::getAbsolutePath )
+                        .collect( toList() ) );
+        final File tempSources = new File( 
mojo.getProject().getBuild().getDirectory(),
+                "maven-scripting-plugin/sources" );
+        toDelete.add( tempSources.toPath() );
+        try
+        {
+            final int lastDot = mainClassName.lastIndexOf( '.' ) + 1;
+            final String className = lastDot == 0 ? mainClassName : 
mainClassName.substring( 0, lastDot );
+            final File target = new File( tempSources, className + ".java" );
+            if ( !target.getParentFile().exists() && 
!target.getParentFile().mkdirs() )
+            {
+                throw new IllegalArgumentException( "Can't create " + 
target.getParentFile() );
+            }
+            Files.copy( new ReaderInputStream( reader, StandardCharsets.UTF_8 
), target.toPath(),
+                    StandardCopyOption.REPLACE_EXISTING );
+        }
+        catch (  final IOException e )
+        {
+            throw new IllegalArgumentException( e );
+        }
+
+        final CompilerConfiguration conf = new CompilerConfiguration();
+        conf.setOutputLocation( output );
+        conf.setParameters( true );
+        conf.setShowWarnings( true );
+        conf.setShowDeprecation( true );
+        conf.setSourceVersion( defaultJavaVersion );
+        conf.setTargetVersion( defaultJavaVersion );
+        if ( !"8".equals( defaultJavaVersion ) && !"1.8".equals( 
defaultJavaVersion ) )
+        {
+            conf.setReleaseVersion( defaultJavaVersion );
+        }
+        conf.setSourceLocations( compileSourceRoot == null
+                ? singletonList( tempSources.getAbsolutePath() )
+                : Stream.concat(
+                        Stream.of( tempSources.getAbsolutePath() ),
+                        Stream.of( compileSourceRoot.split( ":" ) ) )
+                        .distinct()
+                        .collect( toList() ) );
+        conf.setSourceEncoding( encoding );
+        conf.setFork( true );
+        conf.setWorkingDirectory( mojo.getProject().getBasedir() );
+        conf.setBuildDirectory( new File( 
mojo.getProject().getBuild().getDirectory() ) );
+        conf.setOutputFileName( outputFileName );
+        conf.setCompilerReuseStrategy( 
CompilerConfiguration.CompilerReuseStrategy.AlwaysNew );
+        conf.setForceJavacCompilerUse( true );
+        conf.setClasspathEntries( classpath );
+        if ( tc != null )
+        {
+            conf.setExecutable( tc.findTool( "javac" ) );
+        }
+        try
+        {
+            conf.setSourceFiles( compileSourceRoot == null ? null : 
Files.list( Paths.get( compileSourceRoot ) )
+                    .filter( it -> it.getFileName().toString().endsWith( 
".java" ) )
+                    .map( Path::toFile )
+                    .collect( toSet() ) );
+        }
+        catch (  final IOException e )
+        {
+            throw new IllegalStateException( e );
+        }
+        return conf;
+    }
+
+    private void log(  final EvalMojo mojo, final CompilerResult result )
+    {
+        List<CompilerMessage> warnings = new ArrayList<>();
+        List<CompilerMessage> errors = new ArrayList<>();
+        List<CompilerMessage> others = new ArrayList<>();
+        for (  final CompilerMessage message : result.getCompilerMessages() )
+        {
+            if ( message.getKind() == CompilerMessage.Kind.ERROR )
+            {
+                errors.add( message );
+            }
+            else if ( message.getKind() == CompilerMessage.Kind.WARNING
+                    || message.getKind() == 
CompilerMessage.Kind.MANDATORY_WARNING )
+            {
+                warnings.add( message );
+            }
+            else
+            {
+                others.add( message );
+            }
+        }
+
+        if ( !result.isSuccess() )
+        {
+            others.forEach( message -> mojo.getLog().info( message.toString() 
) );
+            if ( !warnings.isEmpty() )
+            {
+                mojo.getLog().info( 
"-------------------------------------------------------------" );
+                mojo.getLog().warn( "COMPILATION WARNING : " );
+                mojo.getLog().info( 
"-------------------------------------------------------------" );
+                others.forEach( warning -> mojo.getLog().warn( 
warning.toString() ) );
+                mojo.getLog().info( warnings.size() + ( ( warnings.size() > 1 
) ? " warnings " : " warning" ) );
+                mojo.getLog().info( 
"-------------------------------------------------------------" );
+            }
+
+            if ( !errors.isEmpty() )
+            {
+                mojo.getLog().info( 
"-------------------------------------------------------------" );
+                mojo.getLog().error( "COMPILATION ERROR : " );
+                mojo.getLog().info( 
"-------------------------------------------------------------" );
+                others.forEach( error -> mojo.getLog().error( error.toString() 
) );
+                mojo.getLog().info( errors.size() + ( ( errors.size() > 1 ) ? 
" errors " : " error" ) );
+                mojo.getLog().info( 
"-------------------------------------------------------------" );
+            }
+
+            throw new IllegalStateException( ( !errors.isEmpty() ? errors : 
warnings ).stream()
+                    .map( CompilerMessage::toString )
+                    .collect( joining( "\n" ) ) );
+        }
+        else
+        {
+            result.getCompilerMessages().forEach( message ->
+            {
+                switch ( message.getKind() )
+                {
+                    case NOTE:
+                    case OTHER:
+                        mojo.getLog().info( message.toString() );
+                        break;
+
+                    case ERROR:
+                        mojo.getLog().error( message.toString() );
+                        break;
+
+                    case MANDATORY_WARNING:
+                    case WARNING:
+                    default:
+                        mojo.getLog().warn( message.toString() );
+                        break;
+                }
+            } );
+        }
+    }
+}
diff --git 
a/src/main/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngineFactory.java
 
b/src/main/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngineFactory.java
new file mode 100644
index 0000000..8a28443
--- /dev/null
+++ 
b/src/main/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngineFactory.java
@@ -0,0 +1,133 @@
+package org.apache.maven.plugins.scripting.engine;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.maven.plugins.scripting.EvalMojo;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import java.util.List;
+
+import static java.util.Arrays.asList;
+
+/**
+ * Maven-java script engine factory.
+ */
+public class JavaScriptEngineFactory implements ScriptEngineFactory
+{
+    private final EvalMojo mojo;
+
+    public JavaScriptEngineFactory( final EvalMojo mojo )
+    {
+        this.mojo = mojo;
+    }
+
+    EvalMojo getMojo()
+    {
+        return mojo;
+    }
+
+    @Override
+    public String getEngineName()
+    {
+        return "maven";
+    }
+
+    @Override
+    public String getEngineVersion()
+    {
+        return "1.0.0";
+    }
+
+    @Override
+    public List<String> getExtensions()
+    {
+        return asList( "java", "maven" );
+    }
+
+    @Override
+    public List<String> getMimeTypes()
+    {
+        return asList( "text/java", "application/java" );
+    }
+
+    @Override
+    public List<String> getNames()
+    {
+        return asList( getClass().getSimpleName(), "MavenJava", "Java", 
"java", "maven" );
+    }
+
+    @Override
+    public String getLanguageName()
+    {
+        return "Java";
+    }
+
+    @Override
+    public String getLanguageVersion()
+    {
+        return System.getProperty( "java.version" );
+    }
+
+    @Override
+    public Object getParameter( final String key )
+    {
+        switch ( key )
+        {
+            case "javax.script.engine_version":
+                return getEngineVersion();
+            case "javax.script.engine":
+                return getEngineName();
+            case "javax.script.language":
+            case "javax.script.name":
+                return getLanguageName();
+            case "javax.script.language_version":
+                return getLanguageVersion();
+            case "THREADING":
+            default:
+                return null;
+        }
+    }
+
+    @Override
+    public String getMethodCallSyntax( final String obj, final String method,
+                                       final String... args )
+    {
+        return obj + '.' + method + '(' + String.join( ", ", args ) + ");";
+    }
+
+    @Override
+    public String getOutputStatement( final String toDisplay )
+    {
+        return "System.out.println(" + toDisplay + ");";
+    }
+
+    @Override
+    public String getProgram( final String... statements )
+    {
+        return String.join( ";\n", statements );
+    }
+
+    @Override
+    public ScriptEngine getScriptEngine()
+    {
+        return new JavaScriptEngine( this );
+    }
+}
diff --git 
a/src/test/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngineTest.java
 
b/src/test/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngineTest.java
new file mode 100644
index 0000000..1389fa5
--- /dev/null
+++ 
b/src/test/java/org/apache/maven/plugins/scripting/engine/JavaScriptEngineTest.java
@@ -0,0 +1,99 @@
+package org.apache.maven.plugins.scripting.engine;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ */
+
+import org.apache.maven.execution.DefaultMavenExecutionRequest;
+import org.apache.maven.execution.MavenExecutionResult;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Build;
+import org.apache.maven.plugins.scripting.EvalMojo;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.toolchain.DefaultToolchainManager;
+import org.codehaus.plexus.compiler.Compiler;
+import org.codehaus.plexus.compiler.javac.JavacCompiler;
+import org.codehaus.plexus.compiler.manager.DefaultCompilerManager;
+import org.codehaus.plexus.compiler.manager.NoSuchCompilerException;
+import org.junit.Test;
+
+import javax.script.SimpleScriptContext;
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+
+public class JavaScriptEngineTest {
+    @Test
+    public void execute() {
+        System.clearProperty("JavaScriptEngineTest.execute");
+
+        final Map<String, String> config = new HashMap<>();
+        config.put("compileSourceRoot", "");
+        config.put("main", "Run");
+        config.put("arg.01", "JavaScriptEngineTest.execute");
+
+        new JavaScriptEngine(new JavaScriptEngineFactory(new EvalMojo()
+        {
+            {
+                compilerManager = new DefaultCompilerManager()
+                {
+                    @Override
+                    public Compiler getCompiler(final String compilerId)
+                    {
+                        return new JavacCompiler();
+                    }
+                };
+                toolchainManager = new DefaultToolchainManager();
+                project = new MavenProject()
+                {
+                    {
+                        setBuild(new Build()
+                        {
+                            {
+                                
setDirectory("target/test-work/JavaScriptEngineTest-execute");
+                            }
+                        });
+                    }
+
+                    @Override
+                    public File getBasedir()
+                    {
+                        return new File("target/test-work");
+                    }
+                };
+                session = new MavenSession(null, null, new 
DefaultMavenExecutionRequest(), (MavenExecutionResult) null);
+            }
+
+            @Override
+            public Map<String, String> getScriptEngineConfiguration()
+            {
+                return config;
+            }
+        })).eval("" +
+                "public class Run {\n" +
+                "  public static void main(String... args) {\n" +
+                "    System.setProperty(args[0], \"true\");\n" +
+                "  }\n" +
+                "}\n" +
+                "\n", new SimpleScriptContext());
+        assertEquals("true", 
System.getProperty("JavaScriptEngineTest.execute"));
+        System.clearProperty("JavaScriptEngineTest.execute");
+    }
+}

Reply via email to