Saturday, June 20, 2020

Testing Your Java EE Thorintail Microservice

Context


Oops! Almost left one important thing, we had a view on how to "dockerize" your "Hollow JAR", but we did not focus on implementing some sort of an automated integration test for your web service. There are a lot of way one can perform this exercise. One of the recurrent practices is through the use of Postman followed by Postwoman.

For as long as your service is running then you can develop some really nice Javascript processes that can automate this for you through the tools mentioned above. Another way for traditional "Java" developers would be to go the "JUnit" route. At some point you will also need your service to be running somewhere somehow waiting for client requests.

The JUnit route later got improved for Java EE Integration Testing through the configuration and use of frameworks, Arquillian & ShrinkWrap as top ups to JUnit.


Arquillian & ShrinkWrap

The role that Arquillian plays is of a "middle-man" between an artifact (.jar .war .sar .ear) that you want to deploy and the container or application server you want to deploy to. There are two ways you can use deploy your artifact. Either you deploy the actual physical artifact that you have just built on your machine or you programmatically build one for testing. To build one programmatically you use SkrinkWrap.


Getting Started

For those that already know, you will agree that generally when you are working with a standalone application server, in this case, WildFly, setting up Arquillian is quite a bit of work, but at least you set it up once and the rewards are out of this world. On the other side I have noticed over the past 6+- (since 2013) years that it has improved with all the dependencies one must configure and configuration files that one needs to create. I am really happy with what they have done with it in Thorntail and that's what we are going to look at for our Java EE Microservice.

Once again we will continue working on the previous Thorntail repo we have been working on since our first article, Java EE Micro Services Using Thorntail to the one that followed, which is, Dockerizing Your Java EE Thorntail Microservice.

Start up by opening your "pom.xml" file and adding the following dependency. In the case of Thorntail and the fact that it's already packed nicely for you the developers. You are basically installing or including a "Fraction".

<dependency>
<groupId>
io.thorntail</groupId>
<artifactId>
arquillian</artifactId>
<scope>
test</scope>
</dependency>

Part of the normal process with Arquillian is to also include JUnit dependenies / libraries, right? Ha ha ha ha, well this Thorntail already includes it. Currently it's including JUnit 4.12 so you may exclude it using Maven and rather include JUnit 5 if you want. For the purpose of getting you up and running with Thorntail I am just going to keep it as is. This fraction also includes Arquillian & ShrinkWrap libraries that you would have to configure separately, but not today! The next thing is to configure our maven testing plugin, the Maven Failsafe Plugin
.

This plugin was desgined for integration testing which is exactly what we want since we want to integrate into our REST Service when testig and get real results.

<plugin>
<artifactId>
maven-failsafe-plugin</artifactId>
<version>
2.22.2</version>
<executions>
<execution>
<goals>
<goal>
integration-test</goal>
<goal>
verify</goal>
</goals>
</execution>
</executions>
</plugin>


Test Case Implementation

Now let's get to the fun stuff, our test case implementation. So create a new Test Suite aka Test Class. Make sure that he name of the class ends with "IT" (which means Integration Test) because by default, Maven will look for classes that end with "IT" in order to run them as Integration Tests. For example I named mine "SampleResourceIT". So let's move on to some action ...

Add an annotation at class level as follows :

...
@RunWith(
Arquillian.class)
public class
SampleResourceIT { }
...

You are instructing JUnit to run with Arquillian. Literally what's there. So this will not be a normal JUnit test case. You will notice that immediately after adding this annotation, you will have some errors already. This is because Arquillian now wants to know how you would like to package your artifact that it should deploy.

So then we should now add a new method with the @Deployment annotation. This annotation is from the Arquillian Framework. It is where we will build our artifact for Arquillian to deploy it.

...
@Deployment
public static
Archive createDeployment() { }
...

Now let's build our test ".war" file inside that method using the SkrinkWrap API.

...
@Deployment
public static
Archive createDeployment() {
WebArchive webArtifact = ShrinkWrap.create( WebArchive.class, "thorntail-test-api.war");
webArtifact.addPackages( Boolean.TRUE, "za.co.anylytical.showcase");
webArtifact.addAsWebResource("project-defaults.yml");

// Print all file and included packages
System.out.println( webArtifact.toString(true));

return
webArtifact;
}
...

So now have built our small simple test web archive file. We are giving our war file a name. "thorntail-test-api.war", I believe you know that you can name it anything you want so you are not tied to naming it almost similar to the original file name. The next thing we are doing is include a our package which contains pretty much the our REST Service and it's business logic. So in a nutshell it contains our application java classes, in "za.co.anylytical.showcase" this follows a flag that tells ShrinkWrap to search recursively. The next part is about including any of our resource file that we may want to include. So I knwo that some of our actual REST configuations are in our file, "projet-defaults.yml", and to be as close as possible to our actual application we should include it in our test web archive file. Last part is printing everything that's in our file just to see what this test war file contains and be sure that we have everything that we want in there.


Great stuff. So now we want to write a test that just calls our test service and affirm that we managed to reach the Web Resource just fine.

...
@Test
public void
test_That_We_Reach_Our_WebResource_Just_Fine_Yea() throws Exception {
Client client = ClientBuilder.newBuilder().build();
WebTarget target = client.target("http://localhost:8881/that-service/text");
Response response = target.request().get();
int statusCode = response.getStatusInfo().getStatusCode();
String reponseBody = response.readEntity(String.class);
assertEquals( 200, statusCode);

    
System.out.println("RESPONSE CODE : " + statusCode);
    System.out.println("RESPONSE BODY : " + reponseBody);
}

...

We have a simple test case that uses the standard JAX-RS 2.x Client API Client API, so no magic there. We build our client code and then call the REST Service we want to call and then validate that we get a HTTP Status Code 200 which means that things wet well. No issues, ZILCH!




These are my IntelliJ IDEA test run results. You can also go through with Maven as follows :


mvn clean install



This will perfom an build while running integration tests for us which should give you results like the ones in the image below :



If you pay close attention to this image above as you will also see that Arquillian started up Wildfly for us and also used ShrinkWrap to build a ".war" file and also deploy it for us, all done automatically. Together with this, JUnit kicked in our test case when Arquillian was done with the deploy. Now that's magic! KA-BOOM!

Full test case looks like :

@RunWith( Arquillian.class)
public class
SampleResourceIT {

@Deployment
public static
Archive createDeployment() {
WebArchive webArtifact = ShrinkWrap.create( WebArchive.class, "thorntail-test-api.war");
webArtifact.addPackages( Boolean.TRUE, "za.co.anylytical.showcase");
webArtifact.addAsWebResource("project-defaults.yml");

// Print all file and included packages
System.out.println( webArtifact.toString( true));

return webArtifact;
}

@Test
public void
test_That_We_Reach_Our_WebResource_Just_Fine_Yea() throws Exception {
Client client = ClientBuilder.newBuilder().build();
WebTarget target = client.target("http://localhost:8881/that-service/text");
Response response = target.request().get();
int statusCode = response.getStatusInfo().getStatusCode();
String reponseBody = response.readEntity(String.class);
assertEquals( 200, statusCode);

System.out.println("RESPONSE CODE : " + statusCode);
System.out.println("RESPONSE BODY : " + reponseBody);
}
}


As usual you may ...

Leave some comments below and you may checkout the GitHub Source Code to validate the steps.



Thursday, June 18, 2020

Dockerizing Your Java EE Thorntail Microservice

Context


If you have read the article about Java EE Micro Services Using Thorntail then it's about time we followed up on a question that you and some of the viewers probably asked:

"What if I want to run this as a docker service?"

Well now we can look into that. There are various ways that one achieve this, the common way has been through the use of Docker Files. The Maven users now have an alternative through the Docker Maven Plugin, which inherits a lot from the behavior of a Docker File. I believe the Gradle developers may also have something similar. Anyway may be one day we can look into it.



Starting Up The Configuration


Reach out to your previous project based on the linked article mentioned above as we will be working on that. Open your pom.xml file. The plan is to use a maven profile to configure this so that we isolate the operations we can perform using the docker plugin. Add a new maven profile :

<profile>
<id>
docker</id>
<build>
<plugins>
<plugin>
<groupId>
io.fabric8</groupId>
<artifactId>
docker-maven-plugin</artifactId>
<configuration>
<images>
<image>
<name>
${project.build.finalName}</name>
<run>
<ports>
<port>
8881:8881</port>
</ports>
</run>
<build>
<from>
java:openjdk-8-jdk</from>
<ports>
<port>
8881</port>
</ports>
<assembly>
<basedir>
/</basedir>
<inline>
<files>
<file>
<source>
${project.build.directory}/${build.finalName}-thorntail.jar</source>
<outputDirectory>
/opt</outputDirectory>
</file>
</files>
</inline>
</assembly>
<cmd>
<shell>
java -jar /opt/${build.finalName}-thorntail.jar -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true</shell>
</cmd>
</build>
</image>
</images>
</configuration>
<executions>
<execution>
<phase>
package</phase>
<goals>
<goal>
build</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>


That's all you need to get up and running, but before that, let's now get into a more detailed breakdown of what's happening.


The XML Explained


We have added the plugin with some configurations that I would like to delineate.

...
<name>
${project.build.finalName}</name>
...

This is a name of your Java Service that will be running in docker. So when you look at all your running containers, you should see the name you specified within these XML tags.


...
<run>
<ports>
<port>
8881:8881</port>
</ports>
</run>

...

We define how the container ports should be mapped to the host port. So we know that our app was initially configured at port 8881 based on our previous article, so for simplicity let's keep these the same as our initial configuration.



...
<build>
    <from>
java:openjdk-8-jdk</from>
        <ports>
            <port>
8881</port>
        </ports>
        <assembly>
            <basedir>
/</basedir>
            <inline>
                <files>
                    <file>
                        <source>
${project.build.directory}/${build.finalName}-thorntail.jar</source>
                            <outputDirectory>
/opt</outputDirectory>
                    </file>
                </files>
            </inline>
        </assembly>
        <cmd>
            <shell>
java -jar /opt/${build.finalName}-thorntail.jar -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true</shell>
        </cmd>
</build>

...

We use the <build> tag to define how our custom docker image should be built

...
<from>
java:openjdk-8-jdk</from>
...

We are basing our image off the java:openjdk-8jdk image

...
<assembly>
     <basedir>
/</basedir>
    <inline>
        <files>
            <file>
                <source>
${project.build.directory}/${build.finalName}-thorntail.jar</source>
                <outputDirectory>
/opt</outputDirectory>
            </file>
        </files>
    </inline>
</assembly>
...

Part of building our image includes the files that may be involved in our service. In this case we want our bootable / executable ".jar" included since it's the actual service that we want to run. We also specify where the jar should be placed when inside the docker container. This destination path is "/opt". So this mean that the moment you have connected to our docker container, you should be able to see the jar if you change to the "/opt" directory or path.

...
<cmd>
    <shell>
java -jar /opt/${build.finalName}-thorntail.jar -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv4Addresses=true</shell>
</cmd>
...

This last part is the command to exectue when the image built and has created a container for us. This entry point is just a simple command that you would run on your local machine in order to bootstrap your Java API Service. So you do the same thing here, I have also added some java options in my command, which you may not need.



Building & Running Your Service

Cooooool stuff! So now we can try to run our applications, to do that all we need to do is build our image and then create our container and run it.


Let's build our service image using our new maven profile :


mvn docker:build -Pdocker



Next up we create and run our new container based on the new image :


mvn docker:start -Pdocker


And that's it, your container should be running. You can repeat the same REST API Postman test steps we did in our previous article and all should work as expected. You can also check if it's running using the docker list command as follows :


docker container ls





Great stuff! So that's all you need to do to dockerize your executable ".jar" file. My thoughts are that you should be able to do the same with a Spring Boot application. You may refer to more Docker Maven Plugins Operaions. For more insight.


Leave some comments below and you may checkout the GitHub Source Code to validate the steps.