org.ambraproject.service.migration.BootstrapMigratorServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.ambraproject.service.migration.BootstrapMigratorServiceImpl.java

Source

/*
 * $HeadURL$
 * $Id$
 *
 * Copyright (c) 2006-2011 by Public Library of Science
 *     http://plos.org
 *     http://ambraproject.org
 *
 * 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.
 */
package org.ambraproject.service.migration;

import com.google.common.collect.ImmutableList;
import org.ambraproject.models.Version;
import org.ambraproject.service.hibernate.HibernateServiceImpl;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.hibernate3.HibernateCallback;

import java.io.IOException;
import java.io.InputStream;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * Does migrations on startup.
 *
 * @author Joe Osowski
 */
public class BootstrapMigratorServiceImpl extends HibernateServiceImpl implements BootstrapMigratorService {
    private static final Logger log = LoggerFactory.getLogger(BootstrapMigratorServiceImpl.class);

    /**
     * Apply all migrations.
     *
     * @throws Exception on an error
     */
    public void migrate() throws Exception {
        ImmutableList<Migration> migrations = Migrations.getAllMigrations();
        int binaryVersion = migrations.get(migrations.size() - 1).getVersion();
        int dbVersion = fetchDatabaseVersion();

        //Throws an exception if the database version is further into
        //the future then this version of the ambra war
        if (binaryVersion < dbVersion) {
            log.error("Binary version: " + binaryVersion + ", DB version: " + dbVersion);
            throw new Exception("The ambra war is out of date with the database, "
                    + "update this war file to the latest version.");
        }

        waitForOtherMigrations();

        for (Migration migration : migrations) {
            if (dbVersion < migration.getVersion()) {
                migration.migrate(hibernateTemplate);
            }
        }
    }

    /*
     * Wait for other migrations to complete.  This will prevent two instances of ambra from attempting to execute the
     * same migration
     */
    private void waitForOtherMigrations() throws InterruptedException {
        while (isMigrateRunning()) {
            log.debug("Waiting for another migration to complete.");
            Thread.sleep(10000);
        }
    }

    /*
     * Determine if a migration is already running
     */
    private boolean isMigrateRunning() {
        return (Boolean) hibernateTemplate.execute(new HibernateCallback() {
            @Override
            public Object doInHibernate(Session session) throws HibernateException, SQLException {
                SQLQuery q = session.createSQLQuery("show tables");
                List<String> tables = q.list();

                //Check to see if the version table exists.
                //If it does not exist then no migrations have been run yet
                if (!tables.contains("version")) {
                    return false;
                }

                //If we get this far, return the version column out of the database
                Criteria c = session.createCriteria(Version.class).setProjection(Projections.max("version"));

                Integer version = (Integer) c.uniqueResult();
                if (version == null) {
                    return false; // no migrations have been run yet
                }

                c = session.createCriteria(Version.class).add(Restrictions.eq("version", version));

                Version v = (Version) c.uniqueResult();

                return (v == null) ? false : v.getUpdateInProcess();
            }
        });
    }

    /*
     * Load a mysql script from a resource
     */
    static String getSQLScript(String filename) throws IOException {
        InputStream is = BootstrapMigratorServiceImpl.class.getResourceAsStream(filename);
        StringBuilder out = new StringBuilder();

        byte[] b = new byte[4096];
        for (int n; (n = is.read(b)) != -1;) {
            out.append(new String(b, 0, n));
        }

        return out.toString();
    }

    private static List<String> getSQLCommands(String filename) throws IOException {
        String sqlString = getSQLScript(filename);
        List<String> sqlCommands = new ArrayList<String>();

        String sqlCommandsTemp[] = sqlString.split(";");

        for (String sqlCommand : sqlCommandsTemp) {
            if (sqlCommand.trim().length() > 0) {
                sqlCommands.add(sqlCommand);
            }
        }
        return sqlCommands;
    }

    /*
     * Get the current version of the database
     */
    @SuppressWarnings("unchecked")
    private int fetchDatabaseVersion() {
        return hibernateTemplate.execute(new HibernateCallback<Integer>() {
            @Override
            public Integer doInHibernate(Session session) throws HibernateException, SQLException {
                SQLQuery q = session.createSQLQuery("show tables");
                List<String> tables = q.list();

                //Check to see if the version table exists.
                //If it does not exist then it's ambra 2.00
                if (!tables.contains("version")) {
                    return LegacyMigration.MIN_VERSION;
                }

                //If we get this far, return the version column out of the database
                Criteria c = session.createCriteria(Version.class).setProjection(Projections.max("version"));

                Integer i = (Integer) c.uniqueResult();

                return (i == null) ? LegacyMigration.MIN_VERSION : i;
            }
        });
    }

    static void execSQLScript(Session session, String sqlScript) throws SQLException, HibernateException {
        log.debug("{} started.", sqlScript);
        List<String> sqlStatements;

        Transaction transaction = session.getTransaction();

        try {
            sqlStatements = getSQLCommands(sqlScript);
        } catch (IOException ex) {
            throw new HibernateException(ex.getMessage(), ex);
        }

        transaction.begin();

        for (String sqlStatement : sqlStatements) {
            log.debug("Running: {}", sqlStatement);
            session.createSQLQuery(sqlStatement).executeUpdate();
        }

        transaction.commit();
        log.debug("{} completed.", sqlScript);
    }
}