Java Servlet con Docker
Uno dei problemi più comuni nell’usare Docker è prendere la propria applicazione e capire come “metterla” in un container… E uno dei peggiori casi è quando parliamo di un’applicazione Java.
Quindi, perché non dargli una chance? In questa guida, vediamo come prendere una semplice applicazione che usa le Java Servlet e vediamo come farla funzionare all’interno di un container basato su Tomcat.
Iniziamo con l’articolo Java Servlet con Docker!
Panoramica dell’applicazione Java
HelloServlet
Il primo esempio di servlet è quello che fornisce una classe che restituisca una pagina HTML. L’esempio è volutamente semplice, perché cerca di concentrarsi su come far funzionare un’applicazione che avrete già scritta in Java su Docker!
Di seguito, l’esempio della classe Servlet che, importando la libreria_javax.servlet_, ritorna una pagina con il classico messaggio “hello world” grazie al metodo init: usando l’annotazione @WebServlet, definiamo il nome della servlet ed anche l’endpoint che sarà possibile chiamare.
import java.io.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
@WebServlet(name = "helloServlet", value = "/hello-servlet")
public class HelloServlet extends HttpServlet {
private String message;
public void init() {
message = "Hello World!";
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("text/html");
// Hello
PrintWriter out = response.getWriter();
out.println("<html><body>");
out.println("<h1>" + message + "</h1>");
out.println("</body></html>");
}
public void destroy() {
}
}
Nel metodo doGet indichiamo il tipo di Content-Type e poi, tramite un PrintWriter, andiamo a restituire il codice HTML della pagina all’interno dell’oggetto response.
Questa servlet, una volta avviata tramite un server Tomcat, risponderà quindi all’indirizzo http://localhost:8080/hello-servlet:
Pagina principale dell’applicazione Java
Pagina di risposta della servlet
JSONServlet
Per rendere l’esempio più completo, vediamo anche una servlet che restituisce un oggetto JSON: in questo caso, nel message andiamo a salvare una stringa che rappresenti un JSON. Questa può facilmente essere sostituita con un oggetto JSON che prendiamo da un database!
Le operazioni da compiere per restituire questo oggetto sono le stesse fatte nella precedente servlet, cambia solo il Content-Type: in questo caso, specifichiamo application/json.
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
@WebServlet(name = "helloJsonServlet", value = "/hello-json-servlet")
public class JSONServlet extends HttpServlet{
private String message;
public void init() {
message = "{\"message\": \"hello JSON!\"}";
}
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
response.setContentType("application/json");
// Hello
PrintWriter out = response.getWriter();
response.setCharacterEncoding("UTF-8");
out.print(message);
}
public void destroy() {
}
}
index.jsp
Il file .jsp ci servirà come view del progetto: in questa pagina abbiamo aggiunto due link che puntano alle due diverse servlet, per verificare che le chiamate vadano a buon fine.
Il risultato dovrebbe essere quello mostrato nella prima immagine:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html>
<head>
<title>JSP - Hello World</title>
</head>
<body>
<h1><%= "Hello World!" %>
</h1>
<br/>
<p>
Questa è la mia prima servlet.
</p>
<%--Qui vengono definiti i contesti a cui punteranno i link: "hello-servlet" e "hello-json-servlet"--%>
<a href="hello-servlet">Hello Servlet</a>
<a href="hello-json-servlet">Hello JSON Servlet</a>
</body>
</html>
pom.xml
A questo punto, produciamo il file .war. Per farlo utilizziamo Maven e specifichiamo tutte le informazioni necessarie per produrre l’artifact all’interno del file pom.xml!
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>it.theredcode.example</groupId>
<artifactId>REST-API</artifactId>
<version>1.0-SNAPSHOT</version>
<name>REST-API</name>
<packaging>war</packaging>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<junit.version>5.7.1</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.1</version>
</plugin>
</plugins>
</build>
</project>
Definire il Dockerfile
Per far sì che la nostra webapp funzioni con Docker, dobbiamo scrivere un Dockerfile. Lo facciamo partendo da un’immagine base di Tomcat che includa già JRE: in questo modo, non avremo bisogno di installare altre dipendenze.
La versione di JRE è la 8 e utilizza OpenJDK; in questo caso, questa immagine è la_slim-buster_.
Questo vuol dire che n_on contiene i pacchetti comuni contenuti nel tag predefinito_ e contiene solo i pacchetti minimi necessari per eseguire Tomcat.
Questa immagine è perfetta se lavoriamo in un ambiente di sviluppo e vogliamo ridurre al minimo lo spazio occupato dall’immagine!
Detto ciò, nel Dockerfile andremo a specificare questa immagine nell’istruzione FROM e poi andremo ad aggiungere il file .war generato precedentemente tramite l’istruzione ADD e lo andremo a copiare nella cartella /user/local/tomcat/webapps.
In ultimo, andremo ad esporre la porta 8080 tramite la direttiva EXPOSE e infine andremo ad eseguire il comando che ci permetta di far avviare Tomcat nell’istruzione CMD:
FROM tomcat:9.0.58-jre8-openjdk-slim-buster
LABEL maintainer="test@mail.com"
ADD ./target/REST-API-1.0-SNAPSHOT.war /usr/local/tomcat/webapps/
EXPOSE 8080
CMD ["catalina.sh", "run"]
A questo punto, è tempo di avviare Docker: lo faremo eseguendo prima la build dell’immagine e poi mettendo in run il container, dove andremo anche a specificare la porta 8080 tramite il parametro -p.
Questo ci permetterà di raggiungere l’applicazione anche in locale:
docker build -t my-app .
docker run -d -p 8080:8080 my-app
In questo caso, andremo a raggiungere l’applicazione tramite http://localhost:8080; attenzione però: è necessario specificare anche il nome del .war, che in questo caso è REST-API-1.0-SNAPSHOT.
Applicazione Java Servlet con Docker
E il gioco è fatto!