Skip to content

Commit

Permalink
added async support for exec:java supporting start check with script …
Browse files Browse the repository at this point in the history
…expression
  • Loading branch information
kaoh committed May 4, 2020
1 parent dbe2c73 commit a337e76
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 33 deletions.
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

0 comments on commit a337e76

Please sign in to comment.