diff --git a/.dockerignore b/.dockerignore
index 3e3fec2..ae63c8c 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,2 +1,4 @@
-**
-!target/*.jar
\ No newline at end of file
+.*
+examples
+target
+#!target/*.jar
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 48cc740..ea41fe2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,8 @@ env:
script:
- mvn clean verify -Drevisison=$(date +%Y-%V)
-- docker build --build-arg MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY} -t observabilitystack/geoip-api:latest -t observabilitystack/geoip-api:$(date +%Y-%V) .
+- docker build --build-arg MAXMIND_LICENSE_KEY=${MAXMIND_LICENSE_KEY} -t observabilitystack/geoip-api:latest -t observabilitystack/geoip-api:native -t observabilitystack/geoip-api:$(date +%Y-%V) .
+- ./test-native-image.sh
before_deploy:
- export TRAVIS_TAG=${TRAVIS_TAG:-$(date +%Y-%V)}
@@ -23,7 +24,7 @@ before_deploy:
deploy:
- provider: script
- script: bash push-to-dockerhub.sh
+ script: ./push-to-dockerhub.sh
on:
branch: master
- provider: releases
@@ -34,4 +35,4 @@ deploy:
skip_cleanup: true
body: Updated MaxMind GeoLite database as of ${TRAVIS_TAG}
on:
- branch: master
\ No newline at end of file
+ branch: master
diff --git a/Dockerfile b/Dockerfile
index 85009be..afd230e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,15 +1,41 @@
-FROM openjdk:11.0.8
-ARG MAXMIND_LICENSE_KEY
+# build stage
+FROM oracle/graalvm-ce:20.2.0-java11 AS builder
+
+ADD . /build
+WORKDIR /build
+
+# For SDKMAN to work we need unzip & zip
+RUN yum -y install unzip zip
+RUN \
+ # Install SDKMAN
+ curl -s "https://get.sdkman.io" | bash; \
+ source "$HOME/.sdkman/bin/sdkman-init.sh" && \
+ sdk install maven && \
+ # Install GraalVM Native Image
+ gu install native-image && \
+ native-image --version && \
+ mvn --version && \
+ java -version && \
+ ls -la .
-# place app
-COPY target/*.jar /opt/geoip-api.jar
+# build image
+RUN source "$HOME/.sdkman/bin/sdkman-init.sh" && mvn -P native clean package
+
+# run stage
+FROM centos:7.8.2003
+ARG MAXMIND_LICENSE_KEY
# download current maxmind databases
WORKDIR /srv
-RUN curl -sSL "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&suffix=tar.gz&license_key=${MAXMIND_LICENSE_KEY}" | tar -xz && \
+#RUN apk add curl && \
+RUN yum install -y tar gzip && \
+ curl -sfSL "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-City&suffix=tar.gz&license_key=${MAXMIND_LICENSE_KEY}" | tar -xz && \
ln -s GeoLite2-City_*/GeoLite2-City.mmdb .
+# place app
+COPY --from=builder "/build/target/geoip-api" /srv/geoip-api
+
ENV CITY_DB_FILE /srv/GeoLite2-City.mmdb
-HEALTHCHECK --interval=5s --timeout=2s CMD curl -f http://localhost:8080/actuator/health
+HEALTHCHECK --interval=5s --timeout=1s CMD curl -f http://localhost:8080/actuator/health
EXPOSE 8080
-CMD exec java ${JAVA_GC_OPTS} ${JAVA_OPTS} -jar /opt/geoip-api.jar
+CMD exec /srv/geoip-api
diff --git a/pom.xml b/pom.xml
index e652b7b..7dd8915 100644
--- a/pom.xml
+++ b/pom.xml
@@ -13,7 +13,7 @@
org.springframework.boot
spring-boot-starter-parent
- 2.3.3.RELEASE
+ 2.3.4.RELEASE
@@ -63,8 +63,26 @@
true
true
+
+ com.s24.geoip.GeoIpApi
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+
+
+
+ spring-milestones
+ Spring Milestones
+ https://repo.spring.io/milestone
+
+
+
com.google.guava
@@ -74,12 +92,14 @@
org.springframework.boot
spring-boot-starter-web
+
org.springframework.boot
@@ -89,10 +109,22 @@
io.micrometer
micrometer-registry-prometheus
+
+
+ org.springframework.experimental
+ spring-graalvm-native
+ 0.7.1
+
+
+ org.springframework
+ spring-context-indexer
+ true
+
com.maxmind.geoip2
geoip2
@@ -108,15 +140,51 @@
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
- maven-failsafe-plugin
-
-
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+ maven-failsafe-plugin
+
+
+
+
+
+ native
+
+
+
+ org.graalvm.nativeimage
+ native-image-maven-plugin
+ 20.2.0
+
+
+ -J-Xmx4G
+ -H:+TraceClassInitialization
+ -H:+ReportExceptionStackTraces
+ -H:ReflectionConfigurationFiles=/build/src/main/resources/reflection-config.json
+ -Dspring.graal.remove-unused-autoconfig=true
+ -Dspring.graal.remove-yaml-support=true
+
+ ${project.artifactId}
+
+
+
+
+ native-image
+
+ package
+
+
+
+
+
+
+
+
diff --git a/push-to-dockerhub.sh b/push-to-dockerhub.sh
old mode 100644
new mode 100755
diff --git a/src/main/java/com/s24/geoip/GeoIpApi.java b/src/main/java/com/s24/geoip/GeoIpApi.java
index 0a88c71..8d8e0cb 100644
--- a/src/main/java/com/s24/geoip/GeoIpApi.java
+++ b/src/main/java/com/s24/geoip/GeoIpApi.java
@@ -17,7 +17,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-@SpringBootApplication
+@SpringBootApplication(proxyBeanMethods = false)
public class GeoIpApi {
private static final Logger logger = LoggerFactory.getLogger(GeoIpApi.class);
diff --git a/src/main/java/com/s24/geoip/web/GeoIpRestController.java b/src/main/java/com/s24/geoip/web/GeoIpRestController.java
index f411f62..4f07037 100644
--- a/src/main/java/com/s24/geoip/web/GeoIpRestController.java
+++ b/src/main/java/com/s24/geoip/web/GeoIpRestController.java
@@ -53,8 +53,9 @@ public ResponseEntity handleKnownNotFounds() {
/**
* Lookup the geolocation information for an ip address.
*/
+
@GetMapping("/{address:.+}")
- public ResponseEntity lookup(@PathVariable InetAddress address) {
+ public ResponseEntity lookup(@PathVariable("address") InetAddress address) {
return ResponseEntity.of(geolocations.lookup(address));
}
diff --git a/src/main/resources/reflection-config.json b/src/main/resources/reflection-config.json
new file mode 100644
index 0000000..4d02793
--- /dev/null
+++ b/src/main/resources/reflection-config.json
@@ -0,0 +1,102 @@
+[
+ {
+ "name" : "com.s24.geoip.GeoIpEntry",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.model.CityResponse",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.record.City",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.record.Continent",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.record.Location",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.record.Postal",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.record.Country",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.record.RepresentedCountry",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.record.Subdivision",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.record.Traits",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ },
+ {
+ "name" : "com.maxmind.geoip2.NetworkDeserializer",
+ "allDeclaredConstructors" : true,
+ "allPublicConstructors" : true,
+ "allDeclaredMethods" : true,
+ "allPublicMethods" : true,
+ "allDeclaredFields" : true,
+ "allPublicFields" : true
+ }
+]
+
\ No newline at end of file
diff --git a/test-native-image.sh b/test-native-image.sh
new file mode 100755
index 0000000..74093d1
--- /dev/null
+++ b/test-native-image.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+set -e
+
+# clean up after test
+function shutdown {
+ docker rm -f geoip-native-test
+}
+trap shutdown EXIT
+
+# launch docker
+docker run -dp 18080:8080 --name geoip-native-test observabilitystack/geoip-api:native
+sleep 1
+
+# execute some curls
+curl -fso /dev/null "http://localhost:18080/actuator"
+curl -fso /dev/null "http://localhost:18080/actuator/health"
+curl -fso /dev/null "http://localhost:18080/actuator/prometheus"
+curl -fso /dev/null "http://localhost:18080/$(curl -s https://ifconfig.me/ip)"
+curl -fso /dev/null "http://localhost:18080/8.8.4.4"
+curl -fso /dev/null "http://localhost:18080/8.8.8.8"
+curl -fso /dev/null "http://localhost:18080/206.80.238.253"
+curl -fso /dev/null "http://localhost:18080/2.161.45.64"
+curl -fso /dev/null "http://localhost:18080/5.4.55.34"