mavenmaven-failsafe-pluginexec-maven-plugin

Maven Exec Plugin is Blocked and HTTP Server starts after Integration Tests are executed


I have been trying to convert our Integration tests to use the Maven Exec Plugin to start a server but it will only start the HTTP Server after the Integration tests have been executed, even though I have pre-integration-test specified.

Questions

Q1. Is there any other way to get the Integration tests to start after?

Q2. Is there a way to get the Integration phase to wait for the Server to start?

Q3. What's the difference between the Maven Exec and the Bazaar Maven Exec plugins?

Note: this worked previously using bazaar maven exec plugin but this is broken and can't seem to get it back working for JDK 11.0.9 on Jenkins.

The below is a snippet of our POM, we have various other plugins creating the jar and starting MockServer.

        <plugins>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <id>exec</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>exec</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <asyncDestroyOnShutdown>true</asyncDestroyOnShutdown>
                    <executable>java</executable>
                    <commandlineArgs>-Dserver.port=8089 -jar /gitRepos/public-api/target/api-jar-with-dependencies.jar</commandlineArgs>
                    <async>true</async>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>3.0.0-M5</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <excludedGroups>${it.excluded}</excludedGroups>
                    <argLine>--add-opens=java.base/jdk.internal.misc=ALL-UNNAMED</argLine>
                    <argLine>
                        --illegal-access=permit
                    </argLine>
                </configuration>
            </plugin>
          </plugins>


<!--            this what was previously working. -->
<!--            <plugin>-->
<!--                <groupId>com.bazaarvoice.maven.plugins</groupId>-->
<!--                <artifactId>process-exec-maven-plugin</artifactId>-->
<!--                <version>0.9</version>-->
<!--                <configuration>-->
<!--                    <processLogFile>${project.build.directory}/my-log.log</processLogFile>-->
<!--                </configuration>-->
<!--            </plugin>-->

The strange part for me is the HTTP Server starting after the Integration Tests, it's like other plugins are blocking the server from starting. See the logs below.

[DEBUG] Freed 4 thread-local buffer(s) from thread: nioEventLoopGroup-3-20
[DEBUG] Freed 4 thread-local buffer(s) from thread: nioEventLoopGroup-3-19
[DEBUG] Freed 5 thread-local buffer(s) from thread: nioEventLoopGroup-3-28
[INFO] [main] 13:13:37.594 [main] INFO  c.i.p.s.Server - Grizzly ThreadPool for listener grizzly set to 24 worker threads.
[INFO] [main] 13:13:37.724 [main] INFO  o.g.g.http.server.NetworkListener - Started listener bound to [0.0.0.0:8089]
[INFO] [main] 13:13:37.726 [main] INFO  o.g.grizzly.http.server.HttpServer - [HttpServer] Started.

Solution

  • Thanks for your the comment below your question.

    After digging a little bit into your question I've decided to reproduce your suspicion and came to the conclusion that everything works as expected with the exec-maven-plugin.

    Let me explain – I took your pom.xml snippet unchanged and implemented just a small Java main class which prints a line every 100 ms.

    public class Ocp {
        public static void main(String[] args) {
            IntStream.range(1, 100).forEach(i -> {
                System.err.println("====== " + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
    }
    

    The result is the following:

    ~/dev/git/so-mvn$ mvn clean verify
    [INFO] Scanning for projects...
    [INFO] 
    [INFO] ---------------------------< de.mle:so-mvn >----------------------------
    [INFO] Building stackoverflow 0.0.1-SNAPSHOT
    [INFO] --------------------------------[ jar ]---------------------------------
    [INFO] 
    
    ***intentionally left out***
    
    [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ so-mvn ---
    [INFO] Building jar: /home/marco/dev/git/so-mvn/target/so-mvn-0.0.1-SNAPSHOT.jar
    [INFO] 
    [INFO] --- exec-maven-plugin:3.0.0:exec (exec) @ so-mvn ---
    [INFO] 
    [INFO] --- maven-failsafe-plugin:3.0.0-M5:integration-test (default) @ so-mvn ---
    ====== 1
    ====== 2
    ====== 3
    ====== 4
    [INFO] 
    [INFO] -------------------------------------------------------
    [INFO]  T E S T S
    [INFO] -------------------------------------------------------
    ====== 5
    ====== 6
    ====== 7
    ====== 8
    [INFO] Running so.mvn.Ocp11IT
    ====== 9
    [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.057 s - in so.mvn.Ocp11IT
    [INFO] 
    [INFO] Results:
    [INFO] 
    [INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
    [INFO] 
    [INFO] 
    [INFO] --- maven-failsafe-plugin:3.0.0-M5:verify (default) @ so-mvn ---
    [INFO] ------------------------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ------------------------------------------------------------------------
    [INFO] Total time:  2.398 s
    [INFO] Finished at: 2021-02-12T19:17:24+01:00
    [INFO] ------------------------------------------------------------------------
    

    As you can see, the process starts clearly before the integration test phase (Q1 & Q2). Only due to your async config, the integration tests simply do not wait for my stub server to finish starting up. So everything fine with Maven here.

    Your mentioned bazaarvoice plugin just did a little trick to mitigate this. It has two features which wait a little bit until they give their "go" for the integration tests. They are:

    Summary In your case, your bazaarvoice plugin just waited the default 30 seconds and that seems to be enough for your external server(s) to come up. There is no such "wait" feature in the exec-maven-plugin but you could e.g. move this yet missing health check programmatically in your integration tests itself (Q3), like in real life health checks for dependent (micro)services. A further option (my preferred one) is, to move all external server dependencies or processes in docker containers and then use again health check on the (Docker) plugin level.

    A good starter for you could be if you take a look in my sample project with its little wait for an ES container to come up and the corresponding integration test.

    Yet another option for you that comes without any waiting time at plugin level is the really sophisticated Awaitility lib which comes with nice waiting features or polling intervals right within your assertions self.