DockerDuke

Docker und Java – Teil 4: Ein RESTful Web Service mit Spring Boot

Im letzten Teil dieser Serie programmieren wir einen RESTful Web Service mit Spring Boot und stellen diesen über ein Docker-Image zur Verfügung. Dabei stützen wir uns auf den im dritten Teil beschriebenen Build-Prozess.

Spring

Was ist Spring Boot?

Mit Hilfe von Spring Boot können auch komplexe Server-Anwendung als einfaches Java-Archiv ausgebracht und gestartet werden. Dazu werden durch einen ausgefeilten Build-Prozess die Anwendungsklassen zusammen mit allen notwendigen Server-Bibliotheken in ein einziges ausführbares Java-Archiv gepackt.

Der Build-Prozess selbst wird wie üblich mit Hilfe eines Maven-Parents definiert. Dieser wird von der Spring-Community zur Verfügung gestellt.

<project
<groupId>org.javacream</groupId>
<artifactId>org.javacream.training.spring-boot-docker</artifactId>
<version>0.1.0</version>
<packaging>pom</packaging>
<name>Spring Boot Docker</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.1.RELEASE</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

Benutzen wie dieses POM als Parent für das im 3. Teil der Artikelreihe benutzten Parents, so haben wir den Docker- und den Spring-Boot-Build vereint. Mehr ist tatsächlich nicht zu tun! Im  folgenden ist die vollständige Parent-POM dieser Anwendung gegeben, ergänzt um die (hier noch nicht benutzten) Abhängigkeiten zu Spring Data JPA und einer MySQL-Datenbank.

<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.javacream.training.docker</groupId>
	<artifactId>org.javacream.training.docker.parent</artifactId>
	<version>1.0</version>
	<packaging>pom</packaging>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.1.RELEASE</version>
		<relativePath />
	</parent>
	<properties>
		<docker.namespace.prefix>javacream</docker.namespace.prefix>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<!-- tag::plugin[] -->
			<plugin>
				<groupId>com.spotify</groupId>
				<artifactId>docker-maven-plugin</artifactId>
				<version>0.4.11</version>
				<configuration>
					<imageName>${docker.image.prefix}/${project.artifactId}</imageName>
					<dockerDirectory>src/main/docker</dockerDirectory>
					<resources>
						<resource>
							<targetPath>/</targetPath>
							<directory>${project.build.directory}</directory>
							<include>${project.build.finalName}.jar</include>
						</resource>
					</resources>
				</configuration>
			</plugin>
			<!-- end::plugin[] -->
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-dependency-plugin</artifactId>
				<executions>
					<execution>
						<id>copy-dependencies</id>
						<phase>package</phase>
						<goals>
							<goal>copy-dependencies</goal>
						</goals>
						<configuration>
							<outputDirectory>${project.build.directory}/libs</outputDirectory>
							<overWriteReleases>false</overWriteReleases>
							<overWriteSnapshots>false</overWriteSnapshots>
							<overWriteIfNewer>true</overWriteIfNewer>
						</configuration>
					</execution>
				</executions>
			</plugin>

			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>

			<plugin>
				<groupId>com.spotify</groupId>
				<artifactId>docker-maven-plugin</artifactId>
				<version>0.4.11</version>
				<configuration>
					<imageName>${docker.namespace.prefix}/${project.artifactId}</imageName>
					<dockerDirectory>src/main/docker</dockerDirectory>
					<resources>
						<resource>
							<targetPath>/</targetPath>
							<directory>${project.build.directory}</directory>
							<include>${project.build.finalName}.jar</include>
						</resource>
						<resource>
							<targetPath>/</targetPath>
							<directory>${project.build.directory}</directory>
							<include>libs/*.jar</include>
						</resource>
					</resources>
					<imageTags>
						<imageTag>${project.version}</imageTag>
						<imageTag>latest</imageTag>
					</imageTags>
				</configuration>
			</plugin>
		</plugins>
	</build>
	<distributionManagement>
		<repository>
			<uniqueVersion>false</uniqueVersion>
			<id>nexus</id>
			<name>Corporate Repository</name>
			<url>http://localhost:8081/repository/maven-releases/</url>
			<layout>default</layout>
		</repository>
		<snapshotRepository>
			<uniqueVersion>true</uniqueVersion>
			<id>nexus</id>
			<name>Corporate Snapshots</name>
			<url>http://localhost:8081/repository/maven-snapshots/</url>
			<layout>legacy</layout>
		</snapshotRepository>
	</distributionManagement>
</project>

 

Ein RESTful Web Service

RESTful Web Services werden in Java meistens mit Annotationen realisiert. Dabei wird eine URL auf eine Java-Methodensignatur abgebildet. Dies erfolgt meistens durch Annotationen, entweder mit JAX-RS oder mit den RequestMappings aus Spring-MVC. Im folgenden Beispiel benutzen wir den zweiten Ansatz:

package org.javacream.training.rest.spring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class Application {

	
	@RequestMapping(path = "/echo/{message}", method = { RequestMethod.GET })
	public String echo(@PathVariable("message") String message) {
		return "Echoing from Server: " + message;
	}
}

 

Diese Java-Klasse kann aus Eclipse heraus sofort gestartet werden und anschließend beispielsweise mit Hilfe des curl-Kommandos getestet werden:

curl -X GET http://localhost:8080/echo/Hello

Ein Maven-Build des Projekts erzeugt das Java-Archiv bzw. lädt es in das Artefakt-Repository

mvn package
mvn deploy

Ein Aufruf ist dann unter der Angabe des Java-Archivs möglich:
java -jar org.javacream.training.rest.spring.basic-0.1.0.jar

Natürlich kann das Archiv auch unbenannt werden, dann ist der Aufruf noch einfacher:
java -jar app.jar

Wichtig ist hier, dass das gebildete Artefakt alle notwendigen Bibliotheken mitbringt, um den Web Server für die http-Requests zu starten. Das Archiv ist vollständig.

Der Docker-Build

Zum Erstellen des Docker-Images brauchen wir nun nur noch ein Dockerfile! Dessen Inhalt ist aus den vorherigen Ausführungen heraus allerdings schon fast trivial:

  1. Als Basis nehmen wir eine Java-Grundinstallation.
  2. Dann kopieren wir noch das generierte Artefakt und
  3. definieren als EntryPoint den Java-Aufruf.
  4. Eine Port-Mapping oder ein Mounten des Log-Directories des Containers ist selbstverständlich noch möglich.
FROM openjdk:latest
ADD org.javacream.training.rest.spring.basic-0.0.1.jar app.jar
ENTRYPOINT java -jar app.jar

Der RESTful Web Service wird nun ganz normal gestartet:

docker run --rm javacream:org.javacream.training.rest.spring.basic:0.0.1

und könnte wieder über curl getestet werden.

Soll das Image noch auf das Artefakt-Repository geschoben werden genügt ein

mvn docker:push

Das funktioniert aber natürlich nur, wenn ein eigenes Repository betrieben wird. Aber auch dieses Problem ist bereits gelöst: Nexus und Artefactory unterstützen Docker ganz analog zu Java-Artefakten.

Damit steht das Image anderen Entwicklern, der Test&QS-Abteilung oder den System-Administratoren der Produktionsumgebung zur Verfügung.


Dieser Artikel ist der Abschluss meiner Reihe über “Docker und Java”. Wer mehr über den Einsatz von Java in einer Microservice-Systemarchitektur lesen möchte: Im Frühjahr 2017 erscheint eine Reihe von Artikeln zum Thema “Microservice-Architekturen mit Docker”.


Seminare zum Thema

 

Dr. Rainer Sawitzki / Dr. Rainer Sawitzki

Nach seinem Studium der Physik und anschließender Promotion Wechsel in die IT-Branche. Seit mehr als 20 Jahren als Entwickler, Berater und Projektleiter vorwiegend im Bereich Java und JavaScript unterwegs. Parallel dazu in der Entwicklung und Durchführung von hochwertigen Seminaren für die Integrata im Einsatz.

Schreibe einen Kommentar