com.splout.db.engine.EmbeddedMySQL.java Source code

Java tutorial

Introduction

Here is the source code for com.splout.db.engine.EmbeddedMySQL.java

Source

package com.splout.db.engine;

/*
 * #%L
 * Splout SQL commons
 * %%
 * Copyright (C) 2012 - 2013 Datasalt Systems S.L.
 * %%
 * 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.
 * #L%
 */

import com.google.common.io.Files;
import com.mysql.management.MysqldResource;
import com.mysql.management.MysqldResourceI;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

/**
 * An interface to use MySQL(d) from Java in an embedded way. This actually starts Mysqld in a separate process with its
 * own PID.
 * <p/>
 * Classes used: http://dev.mysql.com/doc/connector-mxj/en/connector-mxj-configuration-java-object.html
 */
public class EmbeddedMySQL {

    public static final String DRIVER = "com.mysql.jdbc.Driver";

    private final static Log log = LogFactory.getLog(EmbeddedMySQL.class);

    public static class EmbeddedMySQLConfig {

        public final static int DEFAULT_PORT = 4567;
        public final static String DEFAULT_USER = "splout";
        public final static String DEFAULT_PASS = "splout";
        public final static File DEFAULT_RESIDENT_FOLDER = new File(System.getProperty("java.io.tmpdir"),
                "mysql-splout");

        final int port;
        final String user;
        final String pass;
        final File residentFolder;
        final Map<String, Object> customConfig;

        public EmbeddedMySQLConfig() {
            this(DEFAULT_PORT, DEFAULT_USER, DEFAULT_PASS, DEFAULT_RESIDENT_FOLDER, null);
        }

        public EmbeddedMySQLConfig(int port, String user, String pass, File residentFolder,
                Map<String, Object> customConfig) {
            this.port = port;
            this.user = user;
            this.pass = pass;
            this.residentFolder = residentFolder;
            this.customConfig = customConfig;
        }

        public Map<String, Object> getCustomConfig() {
            return customConfig;
        }

        public String getPass() {
            return pass;
        }

        public int getPort() {
            return port;
        }

        public File getResidentFolder() {
            return residentFolder;
        }

        public String getUser() {
            return user;
        }

        /**
         * Normal JDBC connection string to localhost with "createDatabaseIfNotExist"
         */
        public String getLocalJDBCConnection(String dbName) {
            return "jdbc:mysql://localhost:" + port + "/" + dbName + "?createDatabaseIfNotExist=true";
        }

        @Override
        public String toString() {
            return ReflectionToStringBuilder.toString(this);
        }
    }

    ;

    final EmbeddedMySQLConfig config;
    MysqldResource resource = null;

    public EmbeddedMySQL() {
        this(new EmbeddedMySQLConfig());
    }

    public EmbeddedMySQLConfig getConfig() {
        return config;
    }

    public EmbeddedMySQL(EmbeddedMySQLConfig config) {
        this.config = config;
    }

    /**
     * It's ok to call this multiple times (redundant times will be ignored).
     */
    public void stop() {
        if (resource != null) {
            if (resource.isRunning()) {
                resource.shutdown();
                resource = null;
            }
        } else {
            log.warn("Nothing to stop.");
        }
    }

    /**
     * TODO MySQL Hangs if port is busy, we should perform a timeout in a separate thred.
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void start(boolean deleteFilesIfExist) throws IOException, InterruptedException {
        if (deleteFilesIfExist && config.residentFolder.exists()) {
            File pidFile = new File(config.residentFolder, "data/MysqldResource.pid");
            if (pidFile.exists()) {
                // Issue "kill -9" if process is still alive
                String pid = Files.toString(pidFile, Charset.defaultCharset());
                log.info("Killing existing process: " + pid);
                Runtime.getRuntime().exec("kill -9 " + pid).waitFor();
            }
            log.info("Deleting contents of: " + config.residentFolder);
            FileUtils.deleteDirectory(config.residentFolder);
        }
        log.info("Using config: " + config);
        MysqldResource mysqldResource = new MysqldResource(config.residentFolder);
        Map<String, String> database_options = new HashMap();
        database_options.put(MysqldResourceI.PORT, Integer.toString(config.port));
        database_options.put(MysqldResourceI.INITIALIZE_USER, "true");
        database_options.put(MysqldResourceI.INITIALIZE_USER_NAME, config.user);
        database_options.put(MysqldResourceI.INITIALIZE_PASSWORD, config.pass);
        database_options.put("innodb-file-per-table", "true");

        if (config.customConfig != null) {
            for (Map.Entry<String, Object> entry : config.customConfig.entrySet()) {
                database_options.put(entry.getKey(), entry.getValue().toString());
            }
        }

        log.info("Using the following MySQL Configuration:");
        for (Map.Entry<String, String> entry : database_options.entrySet()) {
            log.info("MySQLConf: " + entry.getKey() + " -> " + entry.getValue());
        }
        // I have to do this checking myself, otherwise in some cases mysqldResource will block undefinitely...
        try {
            ServerSocket serverSocket = new ServerSocket(config.port);
            serverSocket.close();
        } catch (IOException e) {
            throw new RuntimeException("Port already in use: " + config.port);
        }
        if (mysqldResource.isRunning()) {
            throw new RuntimeException("MySQL already running!");
        }
        mysqldResource.start("test-mysqld-thread", database_options);
        if (!mysqldResource.isRunning()) {
            throw new RuntimeException("MySQL did not start successfully!");
        }
        log.info("MySQL is running.");
        resource = mysqldResource;
    }
}