edu.cornell.mannlib.vivo.utilities.rdbmigration.RdbMigrator.java Source code

Java tutorial

Introduction

Here is the source code for edu.cornell.mannlib.vivo.utilities.rdbmigration.RdbMigrator.java

Source

/* $This file is distributed under the terms of the license in /doc/license.txt$ */

package edu.cornell.mannlib.vivo.utilities.rdbmigration;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang.StringUtils;

import com.hp.hpl.jena.db.DBConnection;
import com.hp.hpl.jena.db.GraphRDB;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.sparql.core.DatasetGraph;
import com.hp.hpl.jena.tdb.TDBFactory;

/**
 * A free-standing application that walks the user through the process of
 * copying VIVO configuration data from RDB to TDB.
 */
public class RdbMigrator {
    private static final String DIRECTORY_TDB = "tdbModels";
    private static final String TABLE_RDB = "jena_graph";
    private static final String TABLE_MIGRATED = "vivo_rdb_migrated";
    private static final List<String> EXPECTED_MODELS = Arrays
            .asList(new String[] { "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadata",
                    "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadata-displayModel",
                    "http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadataTBOX",
                    "http://vitro.mannlib.cornell.edu/default/vitro-kb-userAccounts" });

    private final String vivoHomeDir;
    private final String jdbcUrl;
    private final String username;
    private final String password;

    private File targetDir;
    private boolean alreadyMigrated;
    private List<String> modelsToCopy;

    /**
     * Confirm all of the parameters. Ask the user for approval. Do the
     * migration.
     * 
     * @throws UserDeclinedException
     *             If the user decides not to continue.
     */
    public RdbMigrator(String vivoHomeDir, String jdbcUrl, String username, String password)
            throws UserDeclinedException, IOException, SQLException {
        this.vivoHomeDir = vivoHomeDir;
        this.jdbcUrl = jdbcUrl;
        this.username = username;
        this.password = password;

        testDbConnection();

        checkThatRdbExists();

        confirmTargetDirectory();

        if (doesTdbExist()) {
            askContinueOverTdb();
        }

        if (isAlreadyMigrated()) {
            askMigrateAgain();
        }

        getListOfRdbModels();

        if (isUnexpectedModels()) {
            askMigrateAllModels();
        }

        askApprovalForMigrationPlan();

        migrate();
    }

    private void testDbConnection() {
        try (Connection conn = getSqlConnection()) {
            // Just open and close it.
        } catch (SQLException e) {
            quit("Can't log in to database: '" + jdbcUrl + "', '" + username + "', '" + password + "'\n"
                    + e.getMessage());
        }
    }

    private void checkThatRdbExists() throws SQLException {
        try (Connection conn = getSqlConnection()) {
            DatabaseMetaData md = conn.getMetaData();
            try (ResultSet rs = md.getTables(null, null, TABLE_RDB, null);) {
                if (!rs.next()) {
                    quit("The database at '" + jdbcUrl + "' contains no RDB tables.");
                }
            }
        }
    }

    private void confirmTargetDirectory() {
        File vivoHome = new File(vivoHomeDir);
        if (!vivoHome.isDirectory()) {
            quit("'" + vivoHome + "' is not a directory.");
        }
        if (!vivoHome.canWrite()) {
            quit("Can't write to '" + vivoHome + "'.");
        }
        targetDir = new File(vivoHome, DIRECTORY_TDB);
    }

    private boolean doesTdbExist() {
        if (!targetDir.exists()) {
            return false;
        }

        if (!targetDir.isDirectory()) {
            quit("'" + targetDir + "' is not a directory.");
        }
        if (!targetDir.canWrite()) {
            quit("Can't write to '" + targetDir + "'.");
        }

        String[] filenames = targetDir.list();
        if (filenames == null || filenames.length == 0) {
            return false;
        }

        return true;
    }

    private void askContinueOverTdb() throws UserDeclinedException, IOException {
        ask("A directory of TDB files exists at '" + targetDir + "'.\n"
                + "   Migration will replace the existing triples.\n" + "Continue? (y/n)");
    }

    private boolean isAlreadyMigrated() throws SQLException {
        try (Connection conn = getSqlConnection()) {
            DatabaseMetaData md = conn.getMetaData();
            try (ResultSet rs = md.getTables(null, null, TABLE_MIGRATED, null);) {
                if (rs.next()) {
                    alreadyMigrated = true;
                    announceMigrationDate(conn);
                    return true;
                } else {
                    return false;
                }
            }
        }
    }

    private void announceMigrationDate(Connection conn) {
        String migrationDate = "UNKNOWN DATE";
        String query = String.format("SELECT date FROM %s LIMIT 1", TABLE_MIGRATED);

        try (Statement stmt = conn.createStatement(); java.sql.ResultSet rs = stmt.executeQuery(query)) {
            if (rs.next()) {
                migrationDate = rs.getString("DATE");
            }
        } catch (SQLException e) {
            // go with default answer.
        }

        System.out.println("It looks like this RDB data has already been " + "migrated to TDB, on " + migrationDate
                + "\n   (found a table named '" + TABLE_MIGRATED + "')");
    }

    private void askMigrateAgain() throws UserDeclinedException, IOException {
        ask("Migrate again? (y/n)");
    }

    private void getListOfRdbModels() throws SQLException {
        try (Connection conn = getSqlConnection(); ClosingDBConnection rdb = new ClosingDBConnection(conn)) {
            modelsToCopy = rdb.getAllModelNames().toList();
        }
    }

    private boolean isUnexpectedModels() {
        List<String> unexpectedModels = new ArrayList<>(modelsToCopy);
        unexpectedModels.removeAll(EXPECTED_MODELS);
        if (unexpectedModels.isEmpty()) {
            return false;
        }

        System.out.println(
                "VIVO requires only these models from RDB:\n   " + StringUtils.join(EXPECTED_MODELS, "\n   ")
                        + "\nYour RDB triple-store contains these additional models:\n   "
                        + StringUtils.join(unexpectedModels, "\n   "));
        return true;
    }

    private void askMigrateAllModels() throws IOException, UserDeclinedException {
        System.out.println("Migrate all models, or only the required models?\n"
                + "   Enter 'all' or 'only', or anything else to quit.");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        if (s != null) {
            if (s.trim().toLowerCase().equals("all")) {
                return;
            } else if (s.trim().toLowerCase().equals("only")) {
                modelsToCopy = EXPECTED_MODELS;
                return;
            }
        }
        throw new UserDeclinedException("Enter 'all' or 'only'.");
    }

    private void askApprovalForMigrationPlan() throws SQLException, UserDeclinedException, IOException {
        int modelCount = 0;
        int tripleCount = 0;
        try (Connection conn = getSqlConnection(); ClosingDBConnection rdb = new ClosingDBConnection(conn)) {
            for (String modelName : modelsToCopy) {
                modelCount++;
                try (ClosingGraphRDB graph = new ClosingGraphRDB(rdb, modelName)) {
                    tripleCount += graph.size();
                }
            }
        }
        String warning = alreadyMigrated ? "   Existing triples will be over-written.\n" : "";
        String question = String.format(
                "Migrating %d triples in %d models " + "to TDB files in '%s'\n%sContinue? (y/n)", tripleCount,
                modelCount, targetDir, warning);
        ask(question);
    }

    private void quit(String message) {
        throw new IllegalArgumentException("--------------------------------------------------------------\n"
                + message + "\n--------------------------------------------------------------");
    }

    private void ask(String string) throws UserDeclinedException, IOException {
        System.out.println(string);
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();
        if ((s == null) || (!s.trim().toLowerCase().equals("y"))) {
            throw new UserDeclinedException("OK.");
        }
    }

    private static class UserDeclinedException extends Exception {
        public UserDeclinedException(String message) {
            super(message);
        }
    }

    public void migrate() throws SQLException {
        copyData();
        writeMigrationRecord();
    }

    private void copyData() throws SQLException {
        try (Connection conn = getSqlConnection(); ClosingDBConnection rdb = new ClosingDBConnection(conn)) {
            Dataset tdbDataset = TDBFactory.createDataset(targetDir.getAbsolutePath());
            copyGraphs(rdb, tdbDataset);
        }
    }

    @SuppressWarnings("deprecation")
    private void copyGraphs(ClosingDBConnection rdb, Dataset tdbDataset) {
        DatasetGraph tdbDsGraph = tdbDataset.asDatasetGraph();
        for (String modelName : modelsToCopy) {
            try (ClosingGraphRDB graph = new ClosingGraphRDB(rdb, modelName)) {
                tdbDsGraph.addGraph(Node.createURI(modelName), graph);
                System.out.println(String.format("  copied %4d triples from %s", graph.size(), modelName));
            }
        }
    }

    private void writeMigrationRecord() throws SQLException {
        String createTable = String.format("CREATE TABLE %s (date DATE)", TABLE_MIGRATED);
        String deleteOldDates = String.format("DELETE FROM %s", TABLE_MIGRATED);
        String insertDate = String.format("INSERT INTO %s (date) VALUES (?)", TABLE_MIGRATED);
        try (Connection conn = getSqlConnection();
                Statement stmt = conn.createStatement();
                PreparedStatement pstmt = conn.prepareStatement(insertDate)) {
            if (alreadyMigrated) {
                stmt.executeUpdate(deleteOldDates);
            } else {
                stmt.executeUpdate(createTable);
            }
            pstmt.setDate(1, new Date(System.currentTimeMillis()));
            pstmt.executeUpdate();
        }
    }

    private Connection getSqlConnection() throws SQLException {
        Properties connectionProps;
        connectionProps = new Properties();
        connectionProps.put("user", username);
        connectionProps.put("password", password);
        return DriverManager.getConnection(jdbcUrl, connectionProps);
    }

    private static class ClosingDBConnection extends DBConnection implements AutoCloseable {
        ClosingDBConnection(Connection sqlConnection) {
            super(sqlConnection, "MySQL");
        }

    }

    private static class ClosingGraphRDB extends GraphRDB implements AutoCloseable {
        ClosingGraphRDB(DBConnection rdb, String modelName) {
            super(rdb, modelName, null, GraphRDB.OPTIMIZE_ALL_REIFICATIONS_AND_HIDE_NOTHING, false);
        }
    }

    // ----------------------------------------------------------------------
    // Main routine
    // ----------------------------------------------------------------------

    @SuppressWarnings("unused")
    public static void main(String[] args) {
        if (args.length != 4) {
            System.out.println("Usage: RdbMigrator vivoHomeDir, jdbcUrl, " + "username, password");
        }

        try {
            new RdbMigrator(args[0], args[1], args[2], args[3]);
        } catch (IllegalArgumentException | UserDeclinedException e) {
            System.out.println(e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}