+ * It is possible to add new plugin dependencies of script libraries supporting JSR-223. + *
+ */ + @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; /** @@ -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() @@ -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(); } /** diff --git a/src/test/java/org/codehaus/mojo/exec/ExecJavaMojoTest.java b/src/test/java/org/codehaus/mojo/exec/ExecJavaMojoTest.java index f8ed07b0..d99da28b 100644 --- a/src/test/java/org/codehaus/mojo/exec/ExecJavaMojoTest.java +++ b/src/test/java/org/codehaus/mojo/exec/ExecJavaMojoTest.java @@ -198,7 +198,7 @@ public void testWaitNonInterruptibleDaemonThreads() /** * See MEXEC-15. FIXME: this sometimes fail with * unit.framework.ComparisonFailure: expected:<...> but was:<...3(f)> - * + * * @throws Exception if any exception occurs */ public void testUncooperativeThread() @@ -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 */ diff --git a/src/test/java/org/codehaus/mojo/exec/TestServerMain.java b/src/test/java/org/codehaus/mojo/exec/TestServerMain.java new file mode 100644 index 00000000..7270fa3a --- /dev/null +++ b/src/test/java/org/codehaus/mojo/exec/TestServerMain.java @@ -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 Karsten Ohme + * (k_o_@users.sourceforge.net ) + */ +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(); + } + +} diff --git a/src/test/projects/project16/pom.xml b/src/test/projects/project16/pom.xml new file mode 100644 index 00000000..b6ccc12d --- /dev/null +++ b/src/test/projects/project16/pom.xml @@ -0,0 +1,79 @@ +