org.unitime.commons.hibernate.util.DatabaseUpdate.java Source code

Java tutorial

Introduction

Here is the source code for org.unitime.commons.hibernate.util.DatabaseUpdate.java

Source

/*
 * UniTime 3.2 - 3.5 (University Timetabling Application)
 * Copyright (C) 2008 - 2013, UniTime LLC, and individual contributors
 * as indicated by the @authors tag.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * 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.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
*/
package org.unitime.commons.hibernate.util;

import java.io.File;
import java.net.URL;
import java.net.URLDecoder;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.hibernate.Query;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.jdbc.Work;
import org.unitime.commons.Debug;
import org.unitime.timetable.ApplicationProperties;
import org.unitime.timetable.defaults.ApplicationProperty;
import org.unitime.timetable.model.ApplicationConfig;
import org.unitime.timetable.model.dao._RootDAO;

/**
 * Process resource given by application property tmtbl.db.update (defaults to file dbupdate.xml) 
 * and the update database accordingly.
 * 
 * @author Tomas Muller, Stephanie Schluttenhofer
 *
 */
public abstract class DatabaseUpdate {
    protected static Log sLog = LogFactory.getLog(DatabaseUpdate.class);
    private Element iRoot = null;
    private String iDialectSQL = null;

    protected abstract String findDbUpdateFileName();

    protected abstract String versionParameterName();

    protected abstract String updateName();

    protected DatabaseUpdate(Document document) throws Exception {
        if (!"dbupdate".equals(document.getRootElement().getName()))
            throw new Exception("Unknown format.");
        iRoot = document.getRootElement();
    }

    protected DatabaseUpdate() throws Exception {
        Document document = null;
        String dbUpdateFile = findDbUpdateFileName();
        URL dbUpdateFileUrl = ApplicationProperties.class.getClassLoader().getResource(dbUpdateFile);
        if (dbUpdateFileUrl != null) {
            Debug.info("Reading " + URLDecoder.decode(dbUpdateFileUrl.getPath(), "UTF-8") + " ...");
            document = (new SAXReader()).read(dbUpdateFileUrl.openStream());
        } else if (new File(dbUpdateFile).exists()) {
            Debug.info("Reading " + dbUpdateFile + " ...");
            document = (new SAXReader()).read(new File(dbUpdateFile));
        }
        if (document == null) {
            sLog.error("Unable to execute " + updateName() + " database auto-update, reason: resource "
                    + dbUpdateFile + " not found.");
            return;
        }

        if (!"dbupdate".equals(document.getRootElement().getName()))
            throw new Exception("Unknown format.");
        iRoot = document.getRootElement();
    }

    public int getVersion() {
        return Integer.parseInt(ApplicationConfig.getConfigValue(versionParameterName(), "0"));
    }

    public void performUpdate() {
        sLog.info("Current " + updateName() + " database version: " + getVersion());
        String dialect = _RootDAO.getConfiguration().getProperty("dialect");
        for (Iterator i = iRoot.elementIterator("dialect"); i.hasNext();) {
            Element dialectElement = (Element) i.next();
            if (dialect.equals(dialectElement.getTextTrim()))
                iDialectSQL = dialectElement.attributeValue("type");
        }
        for (Iterator i = iRoot.elementIterator("update"); i.hasNext();) {
            Element updateElement = (Element) i.next();
            int updateVersion = Integer.parseInt(updateElement.attributeValue("version"));
            if (updateVersion > getVersion() && !performUpdate(updateElement))
                break;
        }
        sLog.info("New " + updateName() + " database version: " + getVersion());
    }

    public boolean performUpdate(Element updateElement) {
        int version = Integer.parseInt(updateElement.attributeValue("version"));
        Session hibSession = new _RootDAO().getSession();
        String schema = _RootDAO.getConfiguration().getProperty("default_schema");
        Transaction tx = null;
        Hashtable variables = new Hashtable();
        try {
            tx = hibSession.beginTransaction();
            sLog.info("  Performing " + updateName() + " update to version " + version + " ("
                    + updateElement.attributeValue("comment") + ")");
            for (Iterator i = updateElement.elementIterator(); i.hasNext();) {
                Element queryElement = (Element) i.next();
                String type = queryElement.getName();
                String query = queryElement.getText().trim().replaceAll("%SCHEMA%", schema);
                for (Iterator j = variables.entrySet().iterator(); j.hasNext();) {
                    Map.Entry entry = (Map.Entry) j.next();
                    query = query.replaceAll("%" + entry.getKey() + "%", entry.getValue().toString());
                }
                String condition = queryElement.attributeValue("condition", "none");
                String action = queryElement.attributeValue("action", "next");
                String value = queryElement.attributeValue("value");
                String into = queryElement.attributeValue("into");
                if (queryElement.attribute("onFail") != null) {
                    condition = "fail";
                    action = queryElement.attributeValue("onFail");
                }
                if (queryElement.attribute("onEqual") != null) {
                    condition = "equal";
                    action = queryElement.attributeValue("onEqual");
                }
                if (queryElement.attribute("onNotEqual") != null) {
                    condition = "notEqual";
                    action = queryElement.attributeValue("onNotEqual");
                }
                if (query.length() == 0)
                    continue;
                try {
                    if (type.equals("hql") || type.equals("sql") || type.equals(iDialectSQL)) {
                        sLog.debug("  -- HQL: " + query + " (con:" + condition + ", act:" + action + ", val:"
                                + value + ")");
                        Query q = null;
                        try {
                            q = (type.equals("hql") ? hibSession.createQuery(query)
                                    : hibSession.createSQLQuery(query));
                        } catch (QueryException e) {
                            // Work-around Hibernate issue HHH-2697 (https://hibernate.onjira.com/browse/HHH-2697)
                            if (!"hql".equals(type)) {
                                final String sql = query;
                                hibSession.doWork(new Work() {
                                    @Override
                                    public void execute(Connection connection) throws SQLException {
                                        Statement statement = connection.createStatement();
                                        int lines = statement.executeUpdate(sql);
                                        sLog.debug("  -- " + lines + " lines affected.");
                                        statement.close();
                                    }
                                });
                            } else
                                throw e;
                        }
                        boolean ok = true;
                        if (into != null) {
                            variables.put(into, q.uniqueResult().toString());
                        } else if ("equal".equals(condition) && value != null) {
                            ok = value.equals(q.uniqueResult().toString());
                        } else if ("notEqual".equals(condition) && value != null) {
                            ok = !value.equals(q.uniqueResult().toString());
                        } else if (q != null) {
                            int lines = q.executeUpdate();
                            sLog.debug("  -- " + lines + " lines affected.");
                            if ("noChange".equals(condition))
                                ok = (lines == 0);
                            else if ("change".equals(condition))
                                ok = (lines > 0);
                        }
                        if (ok) {
                            if ("next".equals(action))
                                continue;
                            if ("done".equals(action))
                                break;
                            if ("fail".equals(action)) {
                                sLog.error("Update to " + updateName() + " version " + version
                                        + " failed (condition not met for query '" + query + "', con:" + condition
                                        + ", act:" + action + ", val:" + value + ").");
                                tx.rollback();
                                return false;
                            }
                        }
                    } else {
                        sLog.debug("  -- skip: " + query + " (con:" + condition + ", act:" + action + ", val:"
                                + value + ")");
                    }
                } catch (Exception e) {
                    sLog.warn("Query '" + query + "' failed, " + e.getMessage(), e);
                    if (e.getCause() != null && e.getCause().getMessage() != null)
                        sLog.warn("Cause: " + e.getCause().getMessage());
                    if ("fail".equals(condition)) {
                        if ("next".equals(action))
                            continue;
                        if ("done".equals(action))
                            break;
                    }
                    sLog.error("Update to version " + version + " failed.");
                    tx.rollback();
                    return false;
                }
            }

            ApplicationConfig versionCfg = ApplicationConfig.getConfig(versionParameterName());
            if (versionCfg == null) {
                versionCfg = new ApplicationConfig(versionParameterName());
                versionCfg.setDescription("Timetabling " + updateName()
                        + " DB version (do not change -- this is used by automatic database update)");
            }
            versionCfg.setValue(String.valueOf(version));
            hibSession.saveOrUpdate(versionCfg);
            sLog.info("    " + updateName() + " Database version increased to: " + version);

            if (tx != null && tx.isActive())
                tx.commit();
            HibernateUtil.clearCache();
            return true;
        } catch (Exception e) {
            if (tx != null && tx.isActive())
                tx.rollback();
            sLog.error("Update to version " + version + " failed, reason:" + e.getMessage(), e);
            return false;
        }
    }

    public static void update() {
        try {
            new UniTimeCoreDatabaseUpdate().performUpdate();

            String additionalUpdates = ApplicationProperty.DatabaseUpdateAddonClass.value();
            if (additionalUpdates != null && !additionalUpdates.trim().isEmpty()) {
                DatabaseUpdate du = (DatabaseUpdate) (Class.forName(additionalUpdates).newInstance());
                ;
                du.performUpdate();
            }
        } catch (Exception e) {
            sLog.error("Unable to execute database auto-update, reason: " + e.getMessage(), e);
        } finally {
            _RootDAO.closeCurrentThreadSessions();
        }
    }
}