org.geowebcache.sqlite.SqliteConnectionManagerTest.java Source code

Java tutorial

Introduction

Here is the source code for org.geowebcache.sqlite.SqliteConnectionManagerTest.java

Source

/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <p>
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @author Nuno Oliveira, GeoSolutions S.A.S., Copyright 2016
 */
package org.geowebcache.sqlite;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.geowebcache.storage.StorageException;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

import java.io.File;
import java.nio.file.Files;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import static org.geowebcache.sqlite.Utils.Tuple;
import static org.geowebcache.sqlite.Utils.Tuple.tuple;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;

public final class SqliteConnectionManagerTest extends TestSupport {

    private static Log LOGGER = LogFactory.getLog(SqliteConnectionManagerTest.class);

    private List<SqliteConnectionManager> connectionManagersToClean;

    @Before
    public void beforeTest() throws Exception {
        super.beforeTest();
        connectionManagersToClean = new ArrayList<>();
    }

    @After
    public void afterTest() throws Exception {
        for (SqliteConnectionManager connectionManager : connectionManagersToClean) {
            try {
                connectionManager.reapAllConnections();
                connectionManager.stopPoolReaper();
            } catch (Exception exception) {
                // nothing that we can do, so just ignoring this exception
            }
        }
        super.afterTest();
    }

    @Test
    public void testGetConnection() throws StorageException {
        SqliteConnectionManager connectionManager = new SqliteConnectionManager(Integer.MAX_VALUE, 1000);
        connectionManagersToClean.add(connectionManager);
        connectionManager.doWork(buildRootFile("tiles", "data_base.sqlite"), true, connection -> {
            insertInTestTable(connection, "name", "europe");
        });
        connectionManager.reapAllConnections();
        assertThat(connectionManager.getPool().size(), is(0));
        connectionManager.doWork(buildRootFile("tiles", "data_base.sqlite"), true, connection -> {
            String value = getFromTestTable(connection, "name");
            assertThat(value, notNullValue());
            assertThat(value, is("europe"));
            closeConnectionQuietly(connection);
        });
    }

    @Test
    @Ignore
    public void testMultiThreadsWithSingleFile() throws Exception {
        genericMultiThreadsTest(10, 500, Integer.MAX_VALUE, buildRootFile("data_base_a.sqlite"));
    }

    @Test
    @Ignore
    public void testMultiThreadsWithMultipleFiles() throws Exception {
        genericMultiThreadsTest(10, 500, 10, buildRootFile("data_base_a.sqlite"),
                buildRootFile("data_base_b.sqlite"), buildRootFile("data_base_c.sqlite"),
                buildRootFile("data_base_d.sqlite"), buildRootFile("data_base_e.sqlite"));
    }

    @Test
    @Ignore
    public void testMultiThreadsWithMultipleFilesWithCacheLimit() throws Exception {
        genericMultiThreadsTest(10, 500, 1, buildRootFile("data_base_a.sqlite"),
                buildRootFile("data_base_b.sqlite"), buildRootFile("data_base_c.sqlite"),
                buildRootFile("data_base_d.sqlite"), buildRootFile("data_base_e.sqlite"));
    }

    @Test
    @Ignore
    public void testReplaceOperation() throws Exception {
        SqliteConnectionManager connectionManager = new SqliteConnectionManager(Integer.MAX_VALUE, 1000);
        connectionManagersToClean.add(connectionManager);
        File file1 = buildRootFile("tiles", "data_base_1.sqlite");
        Utils.createFileParents(file1);
        File file2 = buildRootFile("tiles", "data_base_2.sqlite");
        Utils.createFileParents(file2);
        connectionManager.doWork(file1, false, connection -> {
            insertInTestTable(connection, "name", "europe");
            closeConnectionQuietly(connection);
        });
        file2.createNewFile();
        connectionManager.replace(file1, file2);
        connectionManager.doWork(file1, false, connection -> {
            createTestTable(connection);
            String value = getFromTestTable(connection, "name");
            assertThat(value, nullValue());
            closeConnectionQuietly(connection);
        });
    }

    private void genericMultiThreadsTest(int threadsNumber, int workersNumber, long poolSize, File... files)
            throws Exception {
        SqliteConnectionManager connectionManager = new SqliteConnectionManager(poolSize, 10);
        connectionManagersToClean.add(connectionManager);
        ExecutorService executor = Executors.newFixedThreadPool(threadsNumber);
        Random random = new Random();
        List<Future<Tuple<File, String>>> results = new ArrayList<>();
        for (int i = 0; i < workersNumber; i++) {
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(String.format("Submitted worker '%d'\\'%d'.", i, workersNumber));
            }
            executor.submit(() -> {
                File file = files[random.nextInt(files.length)];
                String key = UUID.randomUUID().toString();
                return connectionManager.doWork(file, false, connection -> {
                    insertInTestTable(connection, key, "value-" + key);
                    closeConnectionQuietly(connection);
                    return tuple(file, key);
                });
            });
        }
        executor.shutdown();
        executor.awaitTermination(60, TimeUnit.SECONDS);
        connectionManager.reapAllConnections();
        assertThat(connectionManager.getPool().size(), is(0));
        for (Future<Tuple<File, String>> result : results) {
            File file = result.get().first;
            String key = result.get().second;
            connectionManager.doWork(file, true, connection -> {
                String value = getFromTestTable(connection, key);
                assertThat(value, notNullValue());
                assertThat(value, is("value-" + key));
                closeConnectionQuietly(connection);
            });
        }
    }

    private static void closeConnectionQuietly(Connection connection) {
        try {
            connection.close();
        } catch (Exception exception) {
            throw Utils.exception(exception, "Error closing connection.");
        }
    }

    private static void createTestTable(Connection connection) {
        execute(connection, "CREATE TABLE IF NOT EXISTS test "
                + "(key text, value text, CONSTRAINT pk_metadata PRIMARY KEY(key));");
    }

    private static void insertInTestTable(Connection connection, String key, String value) {
        createTestTable(connection);
        execute(connection, "INSERT INTO test VALUES ('%s', '%s');", key, value);
    }

    private static String getFromTestTable(Connection connection, String key) {
        return new ExecuteQuery(connection, "SELECT value FROM test WHERE key = '%s' ORDER BY key;", key) {

            String result;

            @Override
            public void extract(ResultSet resultSet) throws Exception {
                if (resultSet.next()) {
                    result = resultSet.getString(1);
                }
            }
        }.result;
    }

    private static void execute(Connection connection, String sql, Object... arguments) {
        String finalSql = String.format(sql, arguments);
        try (PreparedStatement statement = connection.prepareStatement(finalSql)) {
            statement.execute();
        } catch (Exception exception) {
            throw Utils.exception(exception, "Error executing SQL '%s'.", finalSql);
        }
    }

    private static abstract class ExecuteQuery {

        public abstract void extract(ResultSet resultSet) throws Exception;

        public ExecuteQuery(Connection connection, String query, Object... arguments) {
            String finalQuery = String.format(query, arguments);
            try (PreparedStatement statement = connection.prepareStatement(finalQuery)) {
                extract(statement.executeQuery());
            } catch (Exception exception) {
                throw Utils.exception(exception, "Error executing query '%s'.", finalQuery);
            }
        }
    }
}