Skip to content

Commit ddddc3c

Browse files
author
Karsten Ohme
committed
added async support for exec:java supporting start check with script expression
1 parent 581e6ed commit ddddc3c

File tree

5 files changed

+283
-33
lines changed

5 files changed

+283
-33
lines changed

pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@
184184
<version>1.9.0</version>
185185
<scope>test</scope>
186186
</dependency>
187+
<dependency>
188+
<groupId>org.codehaus.groovy</groupId>
189+
<artifactId>groovy-jsr223</artifactId>
190+
<version>2.5.10</version>
191+
<scope>test</scope>
192+
</dependency>
187193
</dependencies>
188194

189195
<properties>
@@ -275,7 +281,7 @@
275281
<dependency>
276282
<groupId>org.codehaus.groovy</groupId>
277283
<artifactId>groovy</artifactId>
278-
<version>1.8.4</version>
284+
<version>2.5.10</version>
279285
</dependency>
280286
<dependency>
281287
<groupId>org.codehaus.gmaven.runtime</groupId>

src/main/java/org/codehaus/mojo/exec/ExecJavaMojo.java

Lines changed: 94 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import java.util.Set;
1818

1919
import org.apache.maven.artifact.Artifact;
20-
import org.apache.maven.artifact.factory.ArtifactFactory;
2120
import org.apache.maven.plugin.MojoExecutionException;
2221
import org.apache.maven.plugin.MojoFailureException;
2322
import org.apache.maven.plugins.annotations.Component;
@@ -28,9 +27,12 @@
2827
import org.apache.maven.project.ProjectBuilder;
2928
import org.apache.maven.project.ProjectBuildingRequest;
3029
import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResult;
31-
import org.apache.maven.shared.transfer.dependencies.DefaultDependableCoordinate;
3230
import org.apache.maven.shared.transfer.dependencies.resolve.DependencyResolver;
3331

32+
import javax.script.ScriptEngine;
33+
import javax.script.ScriptEngineManager;
34+
import javax.script.ScriptException;
35+
3436
/**
3537
* Executes the supplied java class in the current VM with the enclosing project's dependencies as classpath.
3638
*
@@ -173,6 +175,33 @@ public class ExecJavaMojo
173175
@Deprecated
174176
private long killAfter;
175177

178+
/**
179+
* If set to true the Java class executes asynchronously and build execution continues in parallel.
180+
*/
181+
@Parameter( property = "exec.async", defaultValue = "false" )
182+
private boolean async;
183+
184+
/**
185+
* Sets the script code to check if the Java class is successfully started.
186+
*/
187+
@Parameter( property = "exec.asyncStartCheck")
188+
private String asyncStartCheck;
189+
190+
/**
191+
* Sets the script language to evaluate the {@link #asyncStartCheck}.
192+
* <p>
193+
* It is possible to add new plugin dependencies of script libraries supporting JSR-223.
194+
* </p>
195+
*/
196+
@Parameter( property = "exec.asyncStartCheckScriptLanguage", defaultValue = "JavaScript")
197+
private String asyncStartCheckScriptLanguage;
198+
199+
/**
200+
* Sets the poll interval in ms to evaluate the {@link #asyncStartCheck}.
201+
*/
202+
@Parameter( property = "exec.asyncStartCheckPollInterval", defaultValue = "1000")
203+
private int asyncStartCheckPollInterval;
204+
176205
private Properties originalSystemProperties;
177206

178207
/**
@@ -224,7 +253,7 @@ public void execute()
224253
getLog().debug( msg );
225254
}
226255

227-
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup( mainClass /* name */ );
256+
final IsolatedThreadGroup threadGroup = new IsolatedThreadGroup( mainClass /* name */ );
228257
Thread bootstrapThread = new Thread( threadGroup, new Runnable()
229258
{
230259
public void run()
@@ -274,45 +303,79 @@ public void run()
274303
setSystemProperties();
275304

276305
bootstrapThread.start();
277-
joinNonDaemonThreads( threadGroup );
278-
// It's plausible that spontaneously a non-daemon thread might be created as we try and shut down,
279-
// but it's too late since the termination condition (only daemon threads) has been triggered.
280-
if ( keepAlive )
281-
{
282-
getLog().warn( "Warning: keepAlive is now deprecated and obsolete. Do you need it? Please comment on MEXEC-6." );
283-
waitFor( 0 );
306+
if (!async) {
307+
terminate(threadGroup);
284308
}
309+
else {
310+
if (asyncStartCheck != null) {
311+
// wait until condition to know that java thread has started
312+
ScriptEngineManager factory = new ScriptEngineManager();
313+
// create a JavaScript engine
314+
ScriptEngine engine = factory.getEngineByName(asyncStartCheckScriptLanguage);
315+
if (engine == null) {
316+
throw new MojoExecutionException("Couldn't find engine for script language "+asyncStartCheckScriptLanguage);
317+
}
318+
// evaluate JavaScript code from String
319+
while (true) {
320+
try {
321+
Thread.sleep(asyncStartCheckPollInterval);
322+
} catch (InterruptedException e) {
323+
throw new MojoExecutionException("Couldn't not sleep for poll interval.", e);
324+
}
325+
try {
326+
if ((Boolean) engine.eval(asyncStartCheck)) {
327+
getLog().info("Java class is ready.");
328+
break;
329+
}
330+
else {
331+
getLog().info("Java class is not yet ready. Waiting ...");
332+
}
333+
} catch (ScriptException e) {
334+
throw new MojoExecutionException("Couldn't evaluate start check expression.", e);
335+
}
336+
}
337+
Runtime.getRuntime().addShutdownHook(new Thread()
338+
{
339+
public void run()
340+
{
341+
try {
342+
terminate(threadGroup);
343+
} catch (MojoExecutionException e) {
344+
getLog().error(e);
345+
}
346+
}
347+
});
348+
}
349+
}
350+
registerSourceRoots();
351+
}
285352

286-
if ( cleanupDaemonThreads )
287-
{
353+
private void terminate(IsolatedThreadGroup threadGroup) throws MojoExecutionException {
354+
joinNonDaemonThreads(threadGroup);
288355

289-
terminateThreads( threadGroup );
356+
if (cleanupDaemonThreads) {
290357

291-
try
292-
{
293-
threadGroup.destroy();
294-
}
295-
catch ( IllegalThreadStateException e )
296-
{
297-
getLog().warn( "Couldn't destroy threadgroup " + threadGroup, e );
358+
terminateThreads(threadGroup);
359+
360+
try {
361+
if (!threadGroup.isDestroyed()) {
362+
threadGroup.destroy();
363+
}
364+
} catch (IllegalThreadStateException e) {
365+
getLog().warn("Couldn't destroy threadgroup " + threadGroup, e);
298366
}
299367
}
300368

301-
if ( originalSystemProperties != null )
302-
{
303-
System.setProperties( originalSystemProperties );
369+
if (originalSystemProperties != null) {
370+
System.setProperties(originalSystemProperties);
304371
}
305372

306-
synchronized ( threadGroup )
307-
{
308-
if ( threadGroup.uncaughtException != null )
309-
{
310-
throw new MojoExecutionException( "An exception occured while executing the Java class. "
311-
+ threadGroup.uncaughtException.getMessage(), threadGroup.uncaughtException );
373+
synchronized (threadGroup) {
374+
if (threadGroup.uncaughtException != null) {
375+
throw new MojoExecutionException("An exception occurred while executing the Java class. "
376+
+ threadGroup.uncaughtException.getMessage(), threadGroup.uncaughtException);
312377
}
313378
}
314-
315-
registerSourceRoots();
316379
}
317380

318381
/**

src/test/java/org/codehaus/mojo/exec/ExecJavaMojoTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public void testWaitNonInterruptibleDaemonThreads()
198198
/**
199199
* See <a href="http://jira.codehaus.org/browse/MEXEC-15">MEXEC-15</a>. FIXME: this sometimes fail with
200200
* unit.framework.ComparisonFailure: expected:&lt;...&gt; but was:&lt;...3(f)&gt;
201-
*
201+
*
202202
* @throws Exception if any exception occurs
203203
*/
204204
public void testUncooperativeThread()
@@ -239,6 +239,20 @@ public void testRunWithArgs()
239239
assertEquals( expectedResult, resultString );
240240
}
241241

242+
/**
243+
* Test the async feature.
244+
*
245+
* @throws Exception if any exception occurs
246+
*/
247+
public void testAsync()
248+
throws Exception
249+
{
250+
File pom = new File( getBasedir(), "src/test/projects/project16/pom.xml" );
251+
String output = execute(pom, "java");
252+
assertTrue(output.trim().contains("Started test server."));
253+
TestServerMain.stop();
254+
}
255+
242256
/**
243257
* @return output from System.out during mojo execution
244258
*/
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright 2020 Karsten Ohme.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
18+
package org.codehaus.mojo.exec;
19+
20+
import com.sun.net.httpserver.HttpExchange;
21+
import com.sun.net.httpserver.HttpHandler;
22+
import com.sun.net.httpserver.HttpServer;
23+
24+
import java.io.IOException;
25+
import java.io.OutputStream;
26+
import java.net.InetSocketAddress;
27+
import java.util.concurrent.Executors;
28+
import java.util.concurrent.ThreadPoolExecutor;
29+
30+
/**
31+
* Starts the test server.
32+
*
33+
* @author <a href="mailto:[email protected] ">Karsten Ohme
34+
35+
*/
36+
public class TestServerMain {
37+
38+
public static final int SERVER_PORT = 9081;
39+
40+
private static HttpServer server;
41+
42+
public static void start() throws Exception {
43+
// simulate a delay of 5 seconds
44+
int i = 0;
45+
while (i++ < 5) {
46+
Thread.sleep(1000);
47+
}
48+
server = HttpServer.create(new InetSocketAddress("localhost", SERVER_PORT), 0);
49+
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
50+
public void run() {
51+
stop();
52+
}
53+
}));
54+
try {
55+
server.createContext("/test", new MyHttpHandler());
56+
ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) Executors.newFixedThreadPool(1);
57+
server.setExecutor(threadPoolExecutor);
58+
server.start();
59+
System.out.println("Started test server.");
60+
} catch (Exception e) {
61+
throw new RuntimeException("Could not start test server.", e);
62+
}
63+
}
64+
65+
private static class MyHttpHandler implements HttpHandler {
66+
public void handle(HttpExchange httpExchange) throws IOException {
67+
handleResponse(httpExchange);
68+
}
69+
70+
private void handleResponse(HttpExchange httpExchange) throws IOException {
71+
OutputStream outputStream = httpExchange.getResponseBody();
72+
httpExchange.sendResponseHeaders(200, -1);
73+
outputStream.flush();
74+
outputStream.close();
75+
}
76+
}
77+
78+
public static void stop() {
79+
if (server != null) {
80+
server.stop(0);
81+
}
82+
}
83+
84+
public static void main(String[] args) throws Exception {
85+
start();
86+
}
87+
88+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<project>
2+
<modelVersion>4.0.0</modelVersion>
3+
<groupId>org.cb.maven.plugins.exec</groupId>
4+
<artifactId>project16</artifactId>
5+
<version>0.1</version>
6+
<packaging>jar</packaging>
7+
<name>Maven Exec Plugin</name>
8+
<description>Test that test the async function of the java mojo</description>
9+
10+
<inceptionYear>2020</inceptionYear>
11+
<licenses>
12+
<license>
13+
<name>Apache License 2</name>
14+
<url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
15+
<distribution>repo</distribution>
16+
</license>
17+
</licenses>
18+
19+
<developers>
20+
<developer>
21+
<name>Karsten Ohme</name>
22+
<id>kohme</id>
23+
<email>[email protected]</email>
24+
<roles>
25+
<role>Java Developer</role>
26+
</roles>
27+
<timezone>+1</timezone>
28+
</developer>
29+
</developers>
30+
31+
<dependencies>
32+
<dependency>
33+
<groupId>junit</groupId>
34+
<artifactId>junit</artifactId>
35+
<version>3.8.1</version>
36+
<scope>test</scope>
37+
</dependency>
38+
</dependencies>
39+
40+
<build>
41+
<!-- for manual tests. Can't be automated in the unit tests as the plugin's not installed. What about integration tests? -->
42+
<plugins>
43+
<plugin>
44+
<groupId>org.codehaus.mojo</groupId>
45+
<artifactId>exec-maven-plugin</artifactId>
46+
<executions>
47+
<execution>
48+
<phase>test</phase>
49+
<goals>
50+
<goal>java</goal>
51+
</goals>
52+
</execution>
53+
</executions>
54+
<configuration>
55+
<async>true</async>
56+
<asyncStartCheckScriptLanguage>JavaScript</asyncStartCheckScriptLanguage>
57+
<asyncStartCheckPollInterval>1500</asyncStartCheckPollInterval>
58+
<mainClass>org.codehaus.mojo.exec.TestServerMain</mainClass>
59+
<asyncStartCheckScriptLanguage>groovy</asyncStartCheckScriptLanguage>
60+
<asyncStartCheck>
61+
try {
62+
def code = new URL('http://localhost:9081/test').openConnection().with {
63+
requestMethod = 'HEAD'
64+
connect()
65+
responseCode
66+
}
67+
68+
code == 200
69+
}
70+
catch (java.net.ConnectException e) {
71+
false
72+
}
73+
</asyncStartCheck>
74+
</configuration>
75+
</plugin>
76+
</plugins>
77+
</build>
78+
79+
</project>

0 commit comments

Comments
 (0)