This article is intended for beginners, who want to looking for a simple walk-through for running a Java application in Docker.
The vast majority of examples out there describing Java applications in a Dockerized environment include the usage of heavy frameworks like Spring Boot. We want to show here that you don't need much to get an endpoint running with Java in Docker.
In fact, we will only use a single library as a dependency: HttpMate core. For this example, we'll use the LowLevel builder of HttpMate with a single HTTP handler.
The environment used for this example
The final result is available in this git repo.
Let's create our initial project structure:
mkdir -p simple-java-http-docker/src/main/java/com/envimate/examples/http
Let's start with the project's pom file in the root directory that we called here simple-java-http-docker
:
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion><groupId>com.envimate.examples</groupId> <artifactId>simple-java-http-docker</artifactId> <version>0.0.1</version> <dependencies> <dependency> <groupId>com.envimate.httpmate</groupId> <artifactId>core</artifactId> <version>1.0.21</version> </dependency> </dependencies>
</project>
Here we have:
This is enough to start developing our endpoint in the IDE of choice. Most of those have support for Maven-based Java projects.
To start our little server, we will use a simple main method. Let’s create the entry to our application as an Application.java
file in the directory src/main/java/com/envimate/examples/http
that will for now just output the time to the console.
package com.envimate.examples.http;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;public final class Application {
public static void main(String[] args) {
final LocalDateTime time = LocalDateTime.now();
final String dateFormatted = time.format(DateTimeFormatter.ISO_TIME);
System.out.println("current time is " + dateFormatted);
}
}
Try to run this class and you will see the current time printed.
Let’s make this more functional and separate the part that prints out the time into a lambda function with no argument, a.k.a Supplier
.
package com.envimate.examples.http;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.function.Supplier;public final class Application {
public static void main(String[] args) {
Supplier handler = () -> {
final LocalDateTime time = LocalDateTime.now();
final String dateFormatted = time.format(DateTimeFormatter.ISO_TIME);
return "current time is " + dateFormatted;
};System.out.println(handler.get()); }
}
The convenience interface provided by the low-level HttpMate does not look much different, except instead of returning a String
, that String
is set to the response, alongside with the indication that everything went well (a.k.a. response code 200).
final HttpHandler httpHandler = (request, response) -> {
final LocalDateTime time = LocalDateTime.now();
final String dateFormatted = time.format(DateTimeFormatter.ISO_TIME);response.setStatus(200); response.setBody("current time is " + dateFormatted);
};
HttpMate also provides a simple Java HttpServer wrapper - PureJavaEndpoint
- that would allow you to start an endpoint without any further dependency.
All we need to do is give it the instance of the HttpMate:
package com.envimate.examples.http;import com.envimate.httpmate.HttpMate;
import com.envimate.httpmate.convenience.endpoints.PureJavaEndpoint;
import com.envimate.httpmate.convenience.handler.HttpHandler;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;import static com.envimate.httpmate.HttpMate.anHttpMateConfiguredAs;
import static com.envimate.httpmate.LowLevelBuilder.LOW_LEVEL;public final class Application {
public static void main(String[] args) {
final HttpHandler httpHandler = (request, response) -> {
final LocalDateTime time = LocalDateTime.now();
final String dateFormatted = time.format(DateTimeFormatter.ISO_TIME);response.setStatus(200); response.setBody("current time is " + dateFormatted); }; final HttpMate httpMate = anHttpMateConfiguredAs(LOW_LEVEL) .get("/time", httpHandler) .build(); PureJavaEndpoint.pureJavaEndpointFor(httpMate).listeningOnThePort(1337); }
}
Notice that we have configured our httpHandler to serve the path /time
, when invoked with method GET.
It’s time to start our application and make some requests:
curl http://localhost:1337/time
current time is 15:09:34.458756
Before we put this all into a Dockerfile, we need to package it as a good-old jar.
We’d need two maven plugins for that: maven-compiler-plugin and maven-assembly-plugin to build the executable jar.
<?xml version=“1.0” encoding=“UTF-8”?>
<project xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance” xmlns=“http://maven.apache.org/POM/4.0.0”
xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd”>
<modelVersion>4.0.0</modelVersion><groupId>com.envimate.examples</groupId> <artifactId>simple-java-http-docker</artifactId> <version>0.0.1</version> <dependencies> <dependency> <groupId>com.envimate.httpmate</groupId> <artifactId>core</artifactId> <version>1.0.21</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <release>${java.version}</release> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <executions> <execution> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <archive> <manifest> <mainClass> com.envimate.examples.http.Application </mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> </execution> </executions> </plugin> </plugins> </build>
</project>
Once we have that, let’s build our jar:
mvn clean verify
And run the resulting jar:
java -jar target/simple-java-http-docker-0.0.1-jar-with-dependencies.jar
Same curl:
curl http://localhost:1337/time
current time is 15:14:42.992563
The Dockerfile looks quite simple:
ROM openjdk:12ADD target/simple-java-http-docker-0.0.1-jar-with-dependencies.jar /opt/application.jar
EXPOSE 1337
ENTRYPOINT exec java -jar /opt/application.jar
It specifies
FROM
: which image to use as a base. We will use an openjdk image.ADD
: the jar we want to the directory we want.EXPOSE
: the port we are listening on.ENTRYPOINT
: to the command, we want to execute.To build and tag our Docker image, we run the following command from the root of the directory:
docker build --tag simple-java-http-docker .
This will produce a docker image that we can run:
docker run --publish 1337:1337 simple-java-http-docker
Notice that we are passing the –publish
parameter, which indicates that the exposed 1337 port, is available under the 1337 port of the machine.
Same curl:
curl http://localhost:1337/time
current time is 15:23:04.275515
And that is it: we have our simple HTTP endpoint dockerized!
Of course, this is a simplified example, and the endpoint we wrote is not entirely useful. It demonstrates, though, that you don’t need tons of libraries just to have a running HTTP endpoint, how easy it is to package a runnable jar, use Docker with your Java application, and the basic usage of the low-level HttpMate.
This kind of two-minute setup can be handy when you need to quickly spin a test HTTP server. The other day I was working on a Twitter-bot (stay tuned for an article about that) and I had to debug what my request really looks like on the receiving side. Obviously, I couldn’t ask Twitter to give me a dump of my request, so I needed a simple endpoint, that would output everything possible about my request.
HttpMate’s handler provides access to an object called MetaData
which is pretty much what it’s called — the meta-data of your request, meaning everything available about your request.
Using that object, we can print everything there is to the request.
public final class FakeTwitter {
public static void main(String[] args) {
final HttpMate httpMate = HttpMate.aLowLevelHttpMate()
.callingTheHandler(metaData -> {
System.out.println(metaData);
})
.forRequestPath(“/*”).andRequestMethods(GET, POST, PUT)
.build();PureJavaEndpoint.pureJavaEndpointFor(httpMate).listeningOnThePort(1337); }
}
The request path /time
is now replaced with a pattern, capturing all paths, and we can add all the HTTP methods we are interested in.
Running our FakeTwitter server and issuing request:
curl -XGET http://localhost:1337/some/path/with?someParameter=someValue
We’ll see the following output in the console (output formatted for readability: it is a map underneath, so you can format it nicely if you so wish)
{
PATH=Path(path=/some/path/with),
BODY_STRING=,
RAW_QUERY_PARAMETERS={someParameter=someValue},
QUERY_PARAMETERS=QueryParameters(
queryParameters={
QueryParameterKey(key=someParameter)=QueryParameterValue(value=someValue)
}
),
RESPONSE_STATUS=200,
RAW_HEADERS={Accept=/,
Host=localhost:1337,
User-agent=curl/7.61.0},
RAW_METHOD=GET,
IS_HTTP_REQUEST=true,
PATH_PARAMETERS=PathParameters(pathParameters={}),
BODY_STREAM=sun.net.httpserver.FixedLengthInputStream@6053cef4,
RESPONSE_HEADERS={},
HEADERS=Headers(headers={HeaderKey(value=user-agent)=HeaderValue(value=curl/7.61.0),
HeaderKey(value=host)=HeaderValue(value=localhost:1337),
HeaderKey(value=accept)=HeaderValue(value=/)}),
CONTENT_TYPE=ContentType(value=null),
RAW_PATH=/some/path/with,
METHOD=GET,
LOGGER=com.envimate.httpmate.logger.Loggers$Lambda$17/0x000000080118f040@5106c12f,
HANDLER=com.envimate.examples.http.FakeTwitter$Lambda$18/0x000000080118f440@68157191
}
Thanks for reading. If you liked this post, share it with all of your programming buddies!
Further reading
☞ Build a Basic App with Spring Boot and JPA using PostgreSQL
☞ Build a Simple CRUD App with Spring Boot and Vue.js
☞ Introducing TensorFlow.js: Machine Learning in Javascript
☞ An illustrated guide to Kubernetes Networking
☞ Google Kubernetes Engine By Example
☞ AWS DevOps: Introduction to DevOps on AWS
☞ Docker Tutorial for Beginners
☞ Kotlin Microservices With Micronaut, Spring Cloud, and JPA
Originally published on https://dzone.com
#docker #web-development #spring-boot #java