org.awesomeagile.dao.testing.TestDatabase.java Source code

Java tutorial

Introduction

Here is the source code for org.awesomeagile.dao.testing.TestDatabase.java

Source

package org.awesomeagile.dao.testing;

/*
 * ================================================================================================
 * Awesome Agile
 * %%
 * Copyright (C) 2015 Mark Warren, Phillip Heller, Matt Kubej, Linghong Chen, Stanislav Belov, Qanit Al
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ------------------------------------------------------------------------------------------------
 */

import static java.util.Arrays.asList;

import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;

import com.spotify.docker.client.DefaultDockerClient;
import com.spotify.docker.client.messages.ContainerConfig;
import com.spotify.docker.client.messages.ContainerCreation;
import com.spotify.docker.client.messages.HostConfig;
import com.spotify.docker.client.messages.PortBinding;

import org.awesomeagile.testutils.NetworkUtils;
import org.flywaydb.core.Flyway;
import org.junit.rules.ExternalResource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.ConnectionCallback;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.jdbc.datasource.init.ScriptUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

/**
 * @author sbelov@google.com (Stan Belov)
 */
public class TestDatabase extends ExternalResource {

    public static final int TIMEOUT = 5;
    public static final int RETRIES = 30;
    public static final int POSTGRESQL_PORT = 5432;
    public static final String POSTGRES_9_2_IMAGE = "postgres:9.4";
    private static final String USERNAME = "postgres";
    private static final String PASSWORD = "";
    private String hostName;
    private ContainerCreation container;
    private DefaultDockerClient docker;
    private int localPort;
    private final String databaseName;
    private final List<String> scripts;

    public TestDatabase(String databaseName, String... scripts) {
        Preconditions.checkArgument(!Strings.isNullOrEmpty(databaseName));
        this.databaseName = databaseName;
        this.scripts = asList(scripts);
    }

    @Override
    protected void before() throws Throwable {
        // Create a client based on DOCKER_HOST and DOCKER_CERT_PATH env vars
        docker = DefaultDockerClient.fromEnv().build();
        hostName = docker.getHost();
        docker.pull(POSTGRES_9_2_IMAGE);
        // Bind container ports to host ports
        localPort = NetworkUtils.findAvailablePort();
        final HostConfig hostConfig = HostConfig.builder()
                .portBindings(createPortBinding(POSTGRESQL_PORT, localPort)).build();
        this.container = docker.createContainer(ContainerConfig.builder().image(POSTGRES_9_2_IMAGE)
                .hostConfig(hostConfig).exposedPorts(String.valueOf(POSTGRESQL_PORT)).build());
        docker.startContainer(container.id());
        waitForDatabase(RETRIES);
        createDatabase();
        Flyway flyway = new Flyway();
        flyway.setSqlMigrationPrefix("v");
        flyway.setSqlMigrationSeparator("-");
        flyway.setDataSource(getDataSource(databaseName));
        flyway.migrate();
        for (String script : scripts) {
            executeScript(script);
        }
    }

    public void executeScript(final String script) {
        executeScript(Optional.of(databaseName), script);
    }

    public JdbcTemplate jdbcTemplate() {
        return new JdbcTemplate(getDataSource());
    }

    public DriverManagerDataSource getDataSource() {
        return new DriverManagerDataSource(/* url */ getUrl(), /* username */ "postgres", /* password */ "");
    }

    public JdbcTemplate jdbcTemplate(String database) {
        return new JdbcTemplate(getDataSource(database));
    }

    public DataSource getDataSource(String database) {
        return new DriverManagerDataSource(/* url */ getUrl(database), /* username */ "postgres",
                /* password */ "");
    }

    public String getUrl() {
        return String.format("jdbc:postgresql://%s:%d/", hostName, localPort);
    }

    public String getUrl(String database) {
        return String.format("jdbc:postgresql://%s:%d/%s", hostName, localPort, database);
    }

    @Override
    protected void after() {
        try {
            docker.stopContainer(container.id(), TIMEOUT);
            docker.removeContainer(container.id());
        } catch (Exception ex) {
            throw new RuntimeException("Unable to stop the Docker container", ex);
        }
    }

    private static Map<String, List<PortBinding>> createPortBinding(int from, int to) {
        final Map<String, List<PortBinding>> portBindings = new HashMap<>();
        List<PortBinding> hostPorts = new ArrayList<>();
        hostPorts.add(PortBinding.of("0.0.0.0", to));
        portBindings.put(String.valueOf(from), hostPorts);
        return portBindings;
    }

    private void waitForDatabase(int retries) {
        JdbcTemplate jdbcTemplate = jdbcTemplate();
        int left = retries;
        while (left > 0) {
            left--;
            System.out.println(left + " attempts left");
            try {
                Thread.sleep(1000);
                jdbcTemplate.execute("select 1");
                return;
            } catch (Exception ex) {
                // ignore
            }
        }
        throw new RuntimeException("Database did not come up after " + retries + " attempts");
    }

    private void executeScript(Optional<String> databaseName, final String script) {
        JdbcTemplate jdbcTemplate = databaseName.isPresent() ? jdbcTemplate(databaseName.get()) : jdbcTemplate();
        jdbcTemplate.execute(new ConnectionCallback<Object>() {
            @Override
            public Object doInConnection(Connection con) throws SQLException, DataAccessException {
                ScriptUtils.executeSqlScript(con, new ClassPathResource(script));
                return null;
            }
        });
    }

    private void createDatabase() {
        JdbcTemplate jdbcTemplate = jdbcTemplate();
        jdbcTemplate.execute(String.format("DROP DATABASE IF EXISTS %s", databaseName));
        jdbcTemplate.execute(String.format("CREATE DATABASE %s", databaseName));
    }

    public String getUserName() {
        return USERNAME;
    }

    public String getPassword() {
        return PASSWORD;
    }
}