Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added async support for exec:java #148

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@
<version>1.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-jsr223</artifactId>
<version>2.5.10</version>
<scope>test</scope>
</dependency>
</dependencies>

<properties>
Expand Down Expand Up @@ -275,7 +281,7 @@
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>1.8.4</version>
<version>2.5.10</version>
</dependency>
<dependency>
<groupId>org.codehaus.gmaven.runtime</groupId>
Expand Down Expand Up @@ -329,6 +335,13 @@
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>2.5.10</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
Expand Down
125 changes: 94 additions & 31 deletions src/main/java/org/codehaus/mojo/exec/ExecJavaMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
import java.util.Set;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
Expand All @@ -28,9 +27,12 @@
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
import org.apache.maven.shared.transfer.dependencies.DefaultDependableCoordinate;
import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

/**
* Executes the supplied java class in the current VM with the enclosing project's dependencies as classpath.
*
Expand Down Expand Up @@ -173,6 +175,33 @@ public class ExecJavaMojo
@Deprecated
private long killAfter;

/**
* If set to true the Java class executes asynchronously and build execution continues in parallel.
*/
@Parameter( property = "exec.async", defaultValue = "false" )
private boolean async;

/**
* Sets the script code to check if the Java class is successfully started.
*/
@Parameter( property = "exec.asyncStartCheck")
private String asyncStartCheck;

/**
* Sets the script language to evaluate the {@link #asyncStartCheck}.
* <p>
* It is possible to add new plugin dependencies of script libraries supporting JSR-223.
* </p>
*/
@Parameter( property = "exec.asyncStartCheckScriptLanguage", defaultValue = "JavaScript")
private String asyncStartCheckScriptLanguage;

/**
* Sets the poll interval in ms to evaluate the {@link #asyncStartCheck}.
*/
@Parameter( property = "exec.asyncStartCheckPollInterval", defaultValue = "1000")
private int asyncStartCheckPollInterval;

private Properties originalSystemProperties;

/**
Expand Down Expand Up @@ -224,7 +253,7 @@ public void execute()
getLog().debug( msg );
}

IsolatedThreadGroup threadGroup = new IsolatedThreadGroup( mainClass /* name */ );
final IsolatedThreadGroup threadGroup = new IsolatedThreadGroup( mainClass /* name */ );
Thread bootstrapThread = new Thread( threadGroup, new Runnable()
{
public void run()
Expand Down Expand Up @@ -274,45 +303,79 @@ public void run()
setSystemProperties();

bootstrapThread.start();
joinNonDaemonThreads( threadGroup );
// It's plausible that spontaneously a non-daemon thread might be created as we try and shut down,
// but it's too late since the termination condition (only daemon threads) has been triggered.
if ( keepAlive )
{
getLog().warn( "Warning: keepAlive is now deprecated and obsolete. Do you need it? Please comment on MEXEC-6." );
waitFor( 0 );
if (!async) {
terminate(threadGroup);
}
else {
if (asyncStartCheck != null) {
// wait until condition to know that java thread has started
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName(asyncStartCheckScriptLanguage);
if (engine == null) {
throw new MojoExecutionException("Couldn't find engine for script language "+asyncStartCheckScriptLanguage);
}
// evaluate JavaScript code from String
while (true) {
try {
Thread.sleep(asyncStartCheckPollInterval);
} catch (InterruptedException e) {
throw new MojoExecutionException("Couldn't not sleep for poll interval.", e);
}
try {
if ((Boolean) engine.eval(asyncStartCheck)) {
getLog().info("Java class is ready.");
break;
}
else {
getLog().info("Java class is not yet ready. Waiting ...");
}
} catch (ScriptException e) {
throw new MojoExecutionException("Couldn't evaluate start check expression.", e);
}
}
Runtime.getRuntime().addShutdownHook(new Thread()
{
public void run()
{
try {
terminate(threadGroup);
} catch (MojoExecutionException e) {
getLog().error(e);
}
}
});
}
}
registerSourceRoots();
}

if ( cleanupDaemonThreads )
{
private void terminate(IsolatedThreadGroup threadGroup) throws MojoExecutionException {
joinNonDaemonThreads(threadGroup);

terminateThreads( threadGroup );
if (cleanupDaemonThreads) {

try
{
threadGroup.destroy();
}
catch ( IllegalThreadStateException e )
{
getLog().warn( "Couldn't destroy threadgroup " + threadGroup, e );
terminateThreads(threadGroup);

try {
if (!threadGroup.isDestroyed()) {
threadGroup.destroy();
}
} catch (IllegalThreadStateException e) {
getLog().warn("Couldn't destroy threadgroup " + threadGroup, e);
}
}

if ( originalSystemProperties != null )
{
System.setProperties( originalSystemProperties );
if (originalSystemProperties != null) {
System.setProperties(originalSystemProperties);
}

synchronized ( threadGroup )
{
if ( threadGroup.uncaughtException != null )
{
throw new MojoExecutionException( "An exception occured while executing the Java class. "
+ threadGroup.uncaughtException.getMessage(), threadGroup.uncaughtException );
synchronized (threadGroup) {
if (threadGroup.uncaughtException != null) {
throw new MojoExecutionException("An exception occurred while executing the Java class. "
+ threadGroup.uncaughtException.getMessage(), threadGroup.uncaughtException);
}
}

registerSourceRoots();
}

/**
Expand Down
16 changes: 15 additions & 1 deletion src/test/java/org/codehaus/mojo/exec/ExecJavaMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public void testWaitNonInterruptibleDaemonThreads()
/**
* See <a href="http://jira.codehaus.org/browse/MEXEC-15">MEXEC-15</a>. FIXME: this sometimes fail with
* unit.framework.ComparisonFailure: expected:&lt;...&gt; but was:&lt;...3(f)&gt;
*
*
* @throws Exception if any exception occurs
*/
public void testUncooperativeThread()
Expand Down Expand Up @@ -239,6 +239,20 @@ public void testRunWithArgs()
assertEquals( expectedResult, resultString );
}

/**
* Test the async feature.
*
* @throws Exception if any exception occurs
*/
public void testAsync()
throws Exception
{
File pom = new File( getBasedir(), "src/test/projects/project16/pom.xml" );
String output = execute(pom, "java");
assertTrue(output.trim().contains("Started test server."));
TestServerMain.stop();
}

/**
* @return output from System.out during mojo execution
*/
Expand Down
88 changes: 88 additions & 0 deletions src/test/java/org/codehaus/mojo/exec/TestServerMain.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2020 Karsten Ohme.
*
* 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.codehaus.mojo.exec;

import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;

import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

/**
* Starts the test server.
*
* @author <a href="mailto:[email protected] ">Karsten Ohme
* ([email protected] )</a>
*/
public class TestServerMain {

public static final int SERVER_PORT = 9081;

private static HttpServer server;

public static void start() throws Exception {
// simulate a delay of 5 seconds
int i = 0;
while (i++ < 5) {
Thread.sleep(1000);
}
server = HttpServer.create(new InetSocketAddress("localhost", SERVER_PORT), 0);
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
stop();
}
}));
try {
server.createContext("/test", new MyHttpHandler());
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
server.setExecutor(threadPoolExecutor);
server.start();
System.out.println("Started test server.");
} catch (Exception e) {
throw new RuntimeException("Could not start test server.", e);
}
}

private static class MyHttpHandler implements HttpHandler {
public void handle(HttpExchange httpExchange) throws IOException {
handleResponse(httpExchange);
}

private void handleResponse(HttpExchange httpExchange) throws IOException {
OutputStream outputStream = httpExchange.getResponseBody();
httpExchange.sendResponseHeaders(200, -1);
outputStream.flush();
outputStream.close();
}
}

public static void stop() {
if (server != null) {
server.stop(0);
}
}

public static void main(String[] args) throws Exception {
start();
}

}
Loading