Creating Microservices with Bootique
September 18th, 2016 by Micha KopsWhen it comes to writing microservices in Java, plenty of tools and frameworks exist. In the following tutorial, I’d like to demonstrate another minimalistic framework called Bootique by implementing a simple microservice exposing its functions either as a RESTful web-service or as a runnable command executed using the command line.
Contents
Dependencies
Using Maven here, we’re adding the following elements to our project’s pom.xml:
- Bootiques Bill of Materials as dependency management: bootique-bom
- Bootique Jersey for our REST service: bootique-jersey
- Bootique Logback for logging: bootique-logback
- Maven Shade Plugin to assemble our fat-jar
I have documented the complete pom.xml in the appendix here.
Bootique Microservice
Out wonderful microservice allows us to obtain the current date either using a RESTful web-service or as a command in the command line.
It’s using Guice for dependency injection and wiring of our components.
Date Service
This is our date service that prints the current date using a default format or using a specific date format to print the date.
package com.hascode.tutorial; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; public class DateService { public String getDate() { return getDate("yyyy-MM-dd"); } public String getDate(String dateFormat) { ZonedDateTime dt = ZonedDateTime.now(); return String.format("In %s it's %s", dt.getZone(), dt.format(DateTimeFormatter.ofPattern(dateFormat))); } }
If you have never heard of the new Date/Time APIs in Java 8, please feel free to take a look at the following presentation of mine: “The Time is now! Date/Time APIs in Java 8“.
RESTful Webservice
Our JAX-RS webservice uses the date service to print the current date as plain text.
package com.hascode.tutorial; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import com.google.inject.Inject; @Path("/") public class DateWebService { @Inject private DateService dateService; @GET @Produces(MediaType.TEXT_PLAIN) public Response getCurrentDate() { return Response.ok(dateService.getDate()).build(); } }
Application
This is our application class that is responsible for wiring and configuring our application using Guice.
package com.hascode.tutorial; import com.google.inject.Inject; import com.google.inject.Module; import com.google.inject.Provides; import com.google.inject.Singleton; import com.google.inject.name.Named; import io.bootique.BQCoreModule; import io.bootique.Bootique; import io.bootique.jersey.JerseyModule; public class Application { public static void main(String[] args) { Module module = binder -> { JerseyModule.contributeResources(binder).addBinding().to(DateWebService.class); BQCoreModule.contributeCommands(binder).addBinding().to(DateOutputCommand.class); }; Bootique.app(args).module(module).autoLoadModules().run(); } @Provides @Singleton public DateService provideDateService() { return new DateService(); } }
Bootique Command
This is our command that allows us to specify the date format used for printing the current date via command line.
In addition we’re using the CommandMetadata builder to add some information to this additional command.
package com.hascode.tutorial; import com.google.inject.Inject; import io.bootique.cli.Cli; import io.bootique.cli.CliOption; import io.bootique.command.CommandMetadata; import io.bootique.command.CommandOutcome; import io.bootique.command.CommandWithMetadata; public class DateOutputCommand extends CommandWithMetadata { private static final String PARAM_DATEFORMAT = "dateFormat"; public DateOutputCommand() { super(CommandMetadata.builder(DateOutputCommand.class) .addOption(CliOption.builder(PARAM_DATEFORMAT, "The date format used to format the current date") .valueOptional().build()) .description("Print out the current date (specify format with --dateFormat)")); } @Inject private DateService dateService; @Override public CommandOutcome run(Cli cli) { try { String dateFormat = cli.optionString(PARAM_DATEFORMAT); if (dateFormat == null) { System.out.println(dateService.getDate()); return CommandOutcome.succeeded(); } System.out.println(dateService.getDate(dateFormat)); return CommandOutcome.succeeded(); } catch (IllegalArgumentException e) { return CommandOutcome.failed(1, e); } } }
Building the Microservice
We may now build our microservice as a fat-jar using Maven:
$ mvn clean package 130 ↵ [..] [INFO] ------------------------------------------------------------------------ [INFO] Building bootique-microservice-tutorial 1.0.0 [INFO] ------------------------------------------------------------------------ [..] [INFO] --- maven-shade-plugin:2.4.2:shade (default) @ bootique-microservice-tutorial --- [INFO] Including io.bootique.jersey:bootique-jersey:jar:0.18 in the shaded jar. [INFO] Including io.bootique:bootique:jar:0.19 in the shaded jar. [..] [INFO] Replacing original artifact with shaded artifact. [INFO] Replacing /data/project/bootique-microservices-tutorial/target/bootique-microservice-tutorial-1.0.0.jar with /data/project/bootique-microservices-tutorial/target/bootique-microservice-tutorial-1.0.0-shaded.jar [INFO] Dependency-reduced POM written at: /data/project/bootique-microservices-tutorial/dependency-reduced-pom.xml [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.909 s [INFO] Finished at: 2016-09-18T17:20:23+01:00 [INFO] Final Memory: 37M/264M [INFO] ------------------------------------------------------------------------
Running the Microservice
When running the jar, our application prints available options and commands to the command line:
$ java -jar target/bootique-microservice-tutorial-1.0.0.jar Option Description ------ ----------- --config <yaml_location> Specifies YAML config location, which can be a file path or a URL. --dateFormat The date format used to format the current date --dateoutput Print out the current date (specify format with --dateFormat) --help Prints this message. --server Starts Jetty server.
To start our RESTful webservice we now only need to add the parameter –server to the command:
$ java -jar target/bootique-microservice-tutorial-1.0.0.jar --server INFO [2016-09-18 15:36:26,611] main o.e.jetty.util.log: Logging initialized @691ms INFO [2016-09-18 15:36:26,644] main i.b.j.s.ServerFactory: Adding listener io.bootique.jetty.servlet.DefaultServletEnvironment INFO [2016-09-18 15:36:26,647] main i.b.j.s.ServletFactory: Adding servlet 'jersey' mapped to /* INFO [2016-09-18 15:36:26,659] main i.b.j.s.ServerLifecycleLogger: Starting jetty... INFO [2016-09-18 15:36:26,661] main o.e.j.server.Server: jetty-9.3.z-SNAPSHOT INFO [2016-09-18 15:36:26,985] main o.e.j.s.h.ContextHandler: Started o.e.j.s.ServletContextHandler@78461bc4{/,null,AVAILABLE} INFO [2016-09-18 15:36:26,995] main o.e.j.s.ServerConnector: Started ServerConnector@3546d80f{HTTP/1.1,[http/1.1]}{0.0.0.0:8080} INFO [2016-09-18 15:36:26,995] main o.e.j.server.Server: Started @1077ms INFO [2016-09-18 15:36:26,995] main i.b.j.s.ServerLifecycleLogger: Started Jetty in 336 ms. Base URL: http://127.0.0.1:8080/
Using Configuration File
We may also specify an external configuration file to use .. e.g. this configuration file in YAML format here:
dateservice: dateFormat: 'hh:mm:ss yyyy-MM-dd' jetty: context: /hascode connector: port: 3333
The configuration file is specified using the –config parameter here:
$ java -jar target/bootique-microservice-tutorial-1.0.0.jar --server --config config.yml 130 ↵ Reading configuration at config.yml INFO [2016-09-18 15:37:17,511] main o.e.jetty.util.log: Logging initialized @739ms INFO [2016-09-18 15:37:17,544] main i.b.j.s.ServerFactory: Adding listener io.bootique.jetty.servlet.DefaultServletEnvironment INFO [2016-09-18 15:37:17,547] main i.b.j.s.ServletFactory: Adding servlet 'jersey' mapped to /* INFO [2016-09-18 15:37:17,559] main i.b.j.s.ServerLifecycleLogger: Starting jetty... INFO [2016-09-18 15:37:17,561] main o.e.j.server.Server: jetty-9.3.z-SNAPSHOT INFO [2016-09-18 15:37:17,897] main o.e.j.s.h.ContextHandler: Started o.e.j.s.ServletContextHandler@51768776{/hascode,null,AVAILABLE} INFO [2016-09-18 15:37:17,936] main o.e.j.s.ServerConnector: Started ServerConnector@4fbdc0f0{HTTP/1.1,[http/1.1]}{0.0.0.0:3333} INFO [2016-09-18 15:37:17,936] main o.e.j.server.Server: Started @1167ms INFO [2016-09-18 15:37:17,937] main i.b.j.s.ServerLifecycleLogger: Started Jetty in 377 ms. Base URL: http://127.0.0.1:3333/hascode
As we can see, our application is now running on port 3333 instead of 8080.
Accessing the Webservice
Running the –server command starts our service so that it should be accessible with a simple GET request to localhost:8080/3333
Running a Command
We may also obtain the current date using our Bootique command and we may specify the dateformat used by adding the parameter dateFormat:
$ java -jar target/bootique-microservice-tutorial-1.0.0.jar --dateoutput In Europe/Berlin it's 2016-09-18 $ java -jar target/bootique-microservice-tutorial-1.0.0.jar --dateoutput --dateFormat "dd MMM yy hh:mm" In Europe/Berlin it's 18 Sep 16 05:39
Resources
Tutorial Sources
Please feel free to download the tutorial sources from my Bitbucket repository, fork it there or clone it using Git:
git clone https://bitbucket.org/hascode/bootique-microservice-tutorial.git
Appendix A: Maven Configuration
This is the complete pom.xml used for this application.
<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>com.hascode.tutorial</groupId> <artifactId>bootique-microservice-tutorial</artifactId> <version>1.0.0</version> <parent> <groupId>io.bootique.parent</groupId> <artifactId>bootique-parent</artifactId> <version>0.12</version> </parent> <properties> <main.class>com.hascode.tutorial.Application</main.class> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>io.bootique.bom</groupId> <artifactId>bootique-bom</artifactId> <version>0.19</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>io.bootique.jersey</groupId> <artifactId>bootique-jersey</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>io.bootique.logback</groupId> <artifactId>bootique-logback</artifactId> <scope>compile</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.3</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> </project>
Tags: bootique, cli, command, di, guice, http, jax-rs, jaxrs, jetty, maven, microservice, microservices, rest, yaml