Java
docker
JDBC

JDBCドライバをビルド後にダウンロードする

複数のデータベースをサポートするプロダクトは、ふつうはMavenのプロファイルを使って、dependenciesを切り替えると思いますが、プロファイルごとのMavenビルドが必要になって面倒です。

そこで、アプリケーションのJarは1つにしておいて、あとからランタイムで必要なJDBCドライバを足すことを考えます。

以下のようなシェルを用意します。

fetch_jdbc_driver.sh
#!/bin/bash -u

## requires: curl xmllint

declare -A drivers
drivers["h2"]="com.h2database:h2:RELEASE"
drivers["mysql"]="mysql:mysql-connector-java:RELEASE"
drivers["postgres"]="org.postgresql:postgresql:42.2.1"
drivers["sqlserver"]="com.microsoft.sqlserver:mssql-jdbc:6.4.0.jre8"

remote_repository=https://repo.maven.apache.org/maven2

function resolve_version () {
    local version_alias=$1
    local metadata_url=${remote_repository}/$2/maven-metadata.xml
    local xpath="/metadata/versioning/$(echo ${version_alias} | tr 'A-Z' 'a-z')/text()|/metadata/version/text()"

    curl -s ${metadata_url} | xmllint --xpath "$xpath" -
}

function fetch_driver () {
    local dbtype=$1
    local version=$2
    local deps=(`echo ${drivers[$dbtype]} | tr -s ':' ' '`)

    if [ ${#deps[@]} -eq 0 ]; then
        echo "Unknown database type: $dbtype"
        exit 1
    fi

    local group_path=$(echo ${deps[0]} | tr -s '.' '/')
    local artifact_path=${deps[1]}
    if [ -z "${version}" ]; then
        version=${deps[2]}
    fi

    if [ $version = "RELEASE" -o $version = "LATEST" ]; then
        version=$(resolve_version $version "${group_path}/${artifact_path}")
    fi

    local jarfile=${deps[1]}-${version}.jar
    curl -sL -o $dest_dir/$jarfile -w '%{http_code}' \
         ${remote_repository}/${group_path}/${deps[1]}/$version/$jarfile
}

dest_dir=runtime

if [ ! -e "$dest_dir" ]; then
    mkdir -p $dest_dir
fi

for arg in "$@"
do
    dbtypes=(`echo $arg | tr ':' ' '`)
    ret=$(fetch_driver ${dbtypes[0]} ${dbtypes[1]:-""})

    if [ $ret -eq 200 ]; then
        echo "Install driver: ${dbtypes[0]}"
    else
        echo "Not found: ${dbtypes[0]}"
    fi
done
% ./fetch_jdbc_driver mysql

のような感じで、最新のJDBCドライバをダウンロードします。特定のバージョンを指定することも可能です。

% ./fetch_jdbc_driver mysql:8.0.9-rc

Dockerfileでいうとこんな感じで使えます。

Dockerfile
FROM alpine AS build

ARG RASCALOID_VERSION=0.1.0-SNAPSHOT
ARG dbtype=h2
RUN apk --no-cache add curl jq libxml2-utils bash

RUN mkdir /rascaloid
WORKDIR /rascaloid
RUN curl 'https://circleci.com/api/v1.1/project/github/kawasima/rascaloid/latest/artifacts?branch=develop&filter=successful' \
    | jq "map(select(.[\"path\"] == \"home/circleci/repo/target/rascaloid-${RASCALOID_VERSION}.jar\"))" \
    | jq '.[0]["url"]' \
    | xargs curl -o rascaloid.jar

ADD fetch_jdbc_driver.sh /usr/local/bin
RUN fetch_jdbc_driver.sh $dbtype

RUN apk del --purge curl jq libxml2-utils

FROM openjdk:8-alpine
COPY --from=build /rascaloid/ /rascaloid/
WORKDIR rascaloid
ENTRYPOINT ["java", "-cp", "rascaloid.jar:runtime/*", "net.unit8.rascaloid.Main"]