Deploy a Github Meecrowave based microservice on Heroku
Deploying a java application was quite complicated years ago but things changed and thanks to several providers it is now quite easy and just a few minutes work.
Assuming you have a Heroku account then you can create a new application. Once the application named and saved you can configure how the application is deployed. The main choices are to deploy through a git repository (a specific heroku one or directly github), through dropbox or through a container registry.
The easiest is probably to deploy when you push on Github directly assuming you put the application on github. Then there is a great option which is to deploy on push on a branch but adding the condition a continuous integration build passes. Concretely, if you use Github, the best is to set up a Travis build which will be triggered for each push.
To summarize, our pipeline would be:
- Develop a new feature
- Push the code on the deployment branch
- Travis builds the branch with the new commit
- If the branch built, Heroku deploys the new application, else it stays at the old stage
To do that we need two integrations Github:
- Travis: will describe how to build the project
- Heroku: will describe how to launch the application
Travis
For a Java 8 application, setting up a Travis integration is as easy as adding to the root of your project a .travis.yml file with this content:
language: java
jdk:
- oraclejdk8
The language let Travis detect it will build with maven (or gradle) and it specifies to use the Oracle JDK 8 (you can also set up OpenJDK and other Java versions if needed adding JDK identifier in the jdk list).
Once this file added you go on Travis, log in, and activate Travis for the Github project which was found when you logged in.
At next commit, Travis will build your project.
Heroku
The procedure is not more complicated on Travis side, you go on your application, then select Deploy tab. On this page you select Github as deployment method and activate Wait for CI to pass before deploy option to ensure to add Travis as a condition of a deployment.
By default the deployment branch is master but don't hesitate to use deployment branches. A common naming convention can be deployment/<version>.
Meecrowave and Heroku Procfile
Now you need an application to deploy. To build an application based on Meecrowave you just need this dependency in your pom.xml (assuming you use maven):
<dependency>
<groupId>org.apache.meecrowave</groupId>
<artifactId>meecrowave-core</artifactId>
<version>${meecrowave.version}</version>
</dependency>
Then you can create a JAX-RS Application subclass (optional):
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("api")
public class MyApplication extends Application {}
And a JAX-RS endpoint (optional as well but easier to test something in the context of this post):
import static javax.ws.rs.core.MediaType.APPLICATION_JSON;
import javax.enterprise.context.ApplicationScoped;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
@Path("ping")
@ApplicationScoped
@Produces(APPLICATION_JSON)
public class PingResource {
@GET
public String get() {
return "{\"value\":\"pong\"}";
}
@HEAD
public String head() {
return get();
}
}
This endpoint just returns a constant - you can use an object instead of a string pre-serialized if you prefer. The fact it defines HEAD endpoint as well is nice if you add a healthcheck on your application like Uptime Robot which uses HEAD to test if the application is still responding.
Once the application is developped - and tested ;) - you need to say to Heroku how to start it. This is done adding a Procfile in the root of the project. It will define the list of the application and their type. For us it will be a web application since we want to expose our endpoint. To run the application, we will bundle it using meecrowave:bundle command of the Meecrowave maven plugin. It creates a zip containing the application with a script inspired from Tomcat one.
To do that we define the Meecrowave maven plugin in our pom:
<build>
<plugins>
<plugin>
<groupId>org.apache.meecrowave</groupId>
<artifactId>meecrowave-maven-plugin</artifactId>
<version>${meecrowave.version}</version>
<executions>
<execution>
<id>bundle</id>
<phase>package</phase>
<goals>
<goal>bundle</goal>
</goals>
<configuration>
<attach>true</attach>
</configuration>
</execution>
</executions>
<configuration>
<conf>src/main/meecrowave/conf</conf>
</configuration>
</plugin>
</plugins>
</build>
Note that the configuration of conf directory, which allows to define a folder in the project containing configuration like log4j2.xml file is optional but allows to include in the deployment some custom configuration. Typically, you can put this log4j2.xml file to ensure the output is redirected in Heroku logs:
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%d{HH:mm:ss.SSS}][%highlight{%-5level}][%15.15t][%30.30logger] %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
</Root>
</Loggers>
</Configuration>
Finally the command we will ask Heroku to launch is to unzip the bundle and execute the run command on the meecrowave script to start the application and wait it exists (at next deployment when Heroku will kill its container). The only point to take care is to forward the port to use for the application using the $HTTP environment variable. Heroku sets it automatically and it is rarely (never?) the default port 8080.
Since the command can be long (you'll see very soon why) it can be nice to define it in a heroku.sh script and just launch it in our Procfile:
web: chmod +x heroku.sh && heroku.sh
Now our heroku.sh contains all the extracting and run logic:
MEECROWAVE_OPTS="$MEECROWAVE_OPTS -Dsome.env.config=$SOME_VALUE"
# extract the app binary bundle and go in its root folder
cd target
unzip myapp-meecrowave-distribution.zip
cd myapp-distribution
# launch Meecrowave setting the Heroku http port
./bin/meecrowave.sh run --http=$PORT
The first line of the script is optional but a nice way to set some system properties which can be used to configure the application. It is very convenient if you use DeltaSpike configuration or Microprofile configuration specification.
Commit all the changes and you should get a Travis build to check it works and Heroku should deploy the application.
Once it is done you should be able to call with success the URL https://myapp.herokuapp.com/api/ping.
Conclusion
This small integration is just a start, you can go really further adding, for instance, environments support on Heroku (staging -> production), healthchecks, a Database, ... However it shows that it is easy to deploy a microservice today in a continuous and fully automated fashion. The other interesting point of that post is that all these integrations are free and ready to use so now time to close your browser and bootstrap some new project ;).
From the same author:
In the same category: