The apollo-http-service
library is a small bundle of Apollo modules. It incorporates both
apollo-api and apollo-core and ties them together with the
jetty-http-server and the okhttp-client
to make a complete service. It also adds logback-classic
as an SLF4J
implementation to give you logging capabilities.
Apollo HTTP Service gives you what you need to start your backend service. Here, for example, we
tell HttpService
to boot a service named "ping"
, defined by the function Ping::init
, and
handle any command line arguments passed in through the args
variable:
public static void main(String... args) throws LoadingException {
HttpService.boot(Ping::init, "ping", args);
}
The HttpService
class is a good example of how apollo-core
together with apollo-api
and other modules come
together to build a fully functional service. You can find documentation for each
module in its respective directory under /modules.
.
├── pom.xml
└── src/
└── main/
├── java/
│ └── com/
│ └── example/
│ └── Ping.java
└── resources/
└── ping.conf
./src/main/java/com/example/Ping.java
package com.example;
import com.spotify.apollo.AppInit;
import com.spotify.apollo.Environment;
import com.spotify.apollo.route.Route;
import com.spotify.apollo.httpservice.LoadingException;
import com.spotify.apollo.httpservice.HttpService;
public final class Ping {
/**
* The main entry point of the java process which will delegate to
* {@link HttpService#boot(AppInit, String, String...)}.
*
* @param args program arguments passed in from the command line
* @throws LoadingException if anything goes wrong during the service boot sequence
*/
public static void main(String... args) throws LoadingException {
HttpService.boot(Ping::init, "ping", args);
}
/**
* An implementation of the {@link AppInit} functional interface which simply sets up a
* "hello world" handler on the root route "/".
*
* @param environment The Apollo {@link Environment} that the service is in.
*/
static void init(Environment environment) {
environment.routingEngine()
.registerAutoRoute(Route.sync("GET", "/ping", requestContext -> "pong"));
}
}
./src/main/resources/ping.conf
# Configuration for http interface
http.server.port = 8080
http.server.port = ${?HTTP_PORT}
For more information on how to manage configuration, see Apollo Core, the logback-classic and the Typesafe Config documentation.
./pom.xml
<project>
<modelVersion>4.0.0</modelVersion>
<name>Simple Ping Service</name>
<groupId>com.example</groupId>
<artifactId>ping</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<mainClass>com.example.Ping</mainClass>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.spotify</groupId>
<artifactId>apollo-bom</artifactId>
<version>1.2.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.spotify</groupId>
<artifactId>apollo-http-service</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<compilerArg>-Xlint:all</compilerArg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.10</version>
<executions>
<execution>
<phase>prepare-package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
</execution>
</executions>
<configuration>
<useBaseVersion>false</useBaseVersion>
<overWriteReleases>false</overWriteReleases>
<overWriteSnapshots>true</overWriteSnapshots>
<includeScope>runtime</includeScope>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>${mainClass}</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
</project>
The Maven configuration is a bit lengthy so it deserves some more explanation:
- We use a property named
mainClass
to refer to our main class. This will be used later. - Under the
dependencyManagement
we import all the Apollo artifact versions through theapollo-bom
artifact. For more information about importing managed dependencies, see the Maven documentation. - We configure the compiler plugin to target JDK 8.
- We configure the
maven-dependency-plugin
to copy all runtime dependency jars into${project.build.directory}/lib
. These will be referenced from the main artifact. - We configure
maven-jar-plugin
to add the classpath jars to the manifest, prefixed withlib/
along with theMainClass
entry to use our main class.
mvn package
java -jar target/ping.jar
Try a request with curl
$ curl http://localhost:8080/ping
pong
The HTTP service logs incoming requests and their responses, using a default implementation that logs with approximately the Apache HTTPD 'combined' format.
To send this to an access log file, use a configuration similar to:
<appender name="ACCESSLOG" class="ch.qos.logback.core.FileAppender">
<file>/path/to/access.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<logger name="com.spotify.apollo.http.server.CombinedFormatLogger" level="INFO">
<appender-ref ref="ACCESSLOG"/>
</logger>
To customise the logging implementation using Guice optional injections, do something along the lines of this:
public class MyModule extends AbstractModule {
// ...
protected void configure() {
bind(com.spotify.apollo.http.server.RequestOutcomeConsumer.class).toInstance(new MyLogger());
}
}