org.ow2.proactive.resourcemanager.db.RMDBManager.java Source code

Java tutorial

Introduction

Here is the source code for org.ow2.proactive.resourcemanager.db.RMDBManager.java

Source

/*
 * ################################################################
 *
 * ProActive Parallel Suite(TM): The Java(TM) library for
 *    Parallel, Distributed, Multi-Core Computing for
 *    Enterprise Grids & Clouds
 *
 * Copyright (C) 1997-2011 INRIA/University of
 *                 Nice-Sophia Antipolis/ActiveEon
 * Contact: proactive@ow2.org or contact@activeeon.com
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation; version 3 of
 * the License.
 *
 * This library 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
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * If needed, contact us to obtain a release under GPL Version 2 or 3
 * or a different license than the AGPL.
 *
 *  Initial developer(s):               The ProActive Team
 *                        http://proactive.inria.fr/team_members.htm
 *  Contributor(s): ActiveEon Team - http://www.activeeon.com
 *
 * ################################################################
 * $$ACTIVEEON_CONTRIBUTOR$$
 */

package org.ow2.proactive.resourcemanager.db;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Collection;
import java.util.List;
import java.util.Properties;
import java.util.Timer;
import java.util.TimerTask;

import org.objectweb.proactive.core.util.log.ProActiveLogger;
import org.ow2.proactive.db.DatabaseManagerException;
import org.ow2.proactive.resourcemanager.core.history.Alive;
import org.ow2.proactive.resourcemanager.core.history.NodeHistory;
import org.ow2.proactive.resourcemanager.core.history.UserHistory;
import org.ow2.proactive.resourcemanager.core.properties.PAResourceManagerProperties;
import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.internal.util.config.ConfigurationException;

public class RMDBManager {

    private static final String JAVA_PROPERTYNAME_NODB = "rm.database.nodb";
    private static final Logger logger = ProActiveLogger.getLogger(RMDBManager.class);

    private final SessionFactory sessionFactory;

    private static RMDBManager instance = null;

    private Timer timer = null;

    /**
    * An internal class to encapsulate the semantic of your data base query.
    * It allows to avoid try/catch in every method. 
    */
    private static abstract class SessionWork<T> {
        abstract T executeWork(Session session);
    }

    public synchronized static RMDBManager getInstance() {
        if (instance == null) {
            instance = createUsingProperties();
        }

        return instance;
    }

    private static RMDBManager createUsingProperties() {
        if (System.getProperty(JAVA_PROPERTYNAME_NODB) != null) {
            return createInMemoryRMDBManager();
        } else {
            File configFile = new File(PAResourceManagerProperties
                    .getAbsolutePath(PAResourceManagerProperties.RM_DB_HIBERNATE_CONFIG.getValueAsString()));

            boolean drop = PAResourceManagerProperties.RM_DB_HIBERNATE_DROPDB.getValueAsBoolean();
            boolean dropNS = PAResourceManagerProperties.RM_DB_HIBERNATE_DROPDB_NODESOURCES.getValueAsBoolean();

            if (logger.isInfoEnabled()) {
                logger.info("Starting RM DB Manager " + "with drop DB = " + drop + " and drop nodesources = "
                        + dropNS + " and configuration file = " + configFile.getAbsolutePath());
            }

            Configuration configuration = new Configuration();

            if (configFile.getName().endsWith(".xml")) {
                configuration.configure(configFile);
            } else {
                try {
                    Properties properties = new Properties();
                    properties.load(Files.newBufferedReader(configFile.toPath(), Charset.defaultCharset()));
                    configuration.addProperties(properties);
                } catch (IOException e) {
                    throw new IllegalArgumentException(e);
                }
            }

            return new RMDBManager(configuration, drop, dropNS);
        }
    }

    private static RMDBManager createInMemoryRMDBManager() {
        Configuration config = new Configuration();
        config.setProperty("hibernate.connection.driver_class", "org.hsqldb.jdbc.JDBCDriver");
        config.setProperty("hibernate.connection.url",
                "jdbc:hsqldb:mem:" + System.currentTimeMillis() + ";hsqldb.tx=mvcc");
        config.setProperty("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");
        return new RMDBManager(config, true, true);
    }

    /**
     * Used only for testing purposes of the hibernate config needs to be changed.
     * RMDBManager.getInstance() should be used in most of cases.
     */
    public RMDBManager(Configuration configuration, boolean drop, boolean dropNS) {
        try {
            configuration.addAnnotatedClass(NodeSourceData.class);
            configuration.addAnnotatedClass(NodeHistory.class);
            configuration.addAnnotatedClass(UserHistory.class);
            configuration.addAnnotatedClass(Alive.class);
            if (drop) {
                configuration.setProperty("hibernate.hbm2ddl.auto", "create");

                // dropping RRD data base as well
                File ddrDB = new File(PAResourceManagerProperties.RM_HOME.getValueAsString()
                        + System.getProperty("file.separator")
                        + PAResourceManagerProperties.RM_RRD_DATABASE_NAME.getValueAsString());
                if (ddrDB.exists()) {
                    ddrDB.delete();
                }
            }

            configuration.setProperty("hibernate.id.new_generator_mappings", "true");
            configuration.setProperty("hibernate.jdbc.use_streams_for_binary", "true");

            sessionFactory = configuration.buildSessionFactory();

            List<?> lastAliveTime = sqlQuery("from Alive");
            if (lastAliveTime == null || lastAliveTime.size() == 0) {
                createRmAliveTime();
            } else if (!drop) {
                if (dropNS) {
                    removeNodeSources();
                }

                recover(((Alive) lastAliveTime.get(0)).getTime());
            }

            int periodInMilliseconds = PAResourceManagerProperties.RM_ALIVE_EVENT_FREQUENCY.getValueAsInt();

            timer = new Timer("Periodic RM live event saver");
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    updateRmAliveTime();
                }
            }, periodInMilliseconds, periodInMilliseconds);

        } catch (Throwable ex) {
            logger.error("Initial SessionFactory creation failed", ex);
            throw new DatabaseManagerException("Initial SessionFactory creation failed", ex);
        }
    }

    private void recover(final long lastAliveTime) {

        // updating node events with uncompleted end time        
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                int updated = session.createSQLQuery("update NodeHistory set endTime = :endTime where endTime = 0")
                        .setParameter("endTime", lastAliveTime).executeUpdate();
                updated = +session
                        .createSQLQuery("update NodeHistory set endTime = startTime where endTime < startTime")
                        .executeUpdate();
                logger.debug("Restoring the node history: " + updated + " raws updated");
                return null;
            }
        });

        // updating user events with uncompleted end time        
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                int updated = session.createSQLQuery("update UserHistory set endTime = :endTime where endTime = 0")
                        .setParameter("endTime", lastAliveTime).executeUpdate();
                updated = +session
                        .createSQLQuery("update UserHistory set endTime = startTime where endTime < startTime")
                        .executeUpdate();
                logger.debug("Restoring the user history: " + updated + " raws updated");
                return null;
            }
        });
    }

    /**
     * Should be used for insert/update/delete queries
     */
    private <T> T runWithTransaction(SessionWork<T> sessionWork) {
        return runWithTransaction(sessionWork, true);
    }

    /**
     * Should be used for insert/update/delete queries
     */
    private <T> T runWithTransaction(SessionWork<T> sessionWork, boolean readonly) {
        Session session = sessionFactory.openSession();
        Transaction tx = null;
        try {
            session.setDefaultReadOnly(readonly);
            tx = session.beginTransaction();
            T result = sessionWork.executeWork(session);
            tx.commit();
            return result;
        } catch (Throwable e) {
            if (tx != null) {
                try {
                    tx.rollback();
                } catch (Throwable rollbackError) {
                    logger.warn("Failed to rollback transaction", rollbackError);
                }
            }
            logger.warn("DB operation failed", e);
            return null;
        } finally {
            try {
                session.close();
            } catch (Throwable e) {
                logger.warn("Failed to close session", e);
            }
        }
    }

    /**
     * Should be used for select queries
     */
    private <T> T runWithoutTransaction(SessionWork<T> sessionWork) {
        Session session = sessionFactory.openSession();
        try {
            session.setDefaultReadOnly(true);
            T result = sessionWork.executeWork(session);
            return result;
        } catch (Throwable e) {
            logger.warn("DB operation failed", e);
            return null;
        } finally {
            try {
                session.close();
            } catch (Throwable e) {
                logger.warn("Failed to close session", e);
            }
        }
    }

    public void close() {
        try {
            if (sessionFactory != null) {
                logger.info("Closing session factory");
                sessionFactory.close();
            }
        } catch (Exception e) {
            logger.error("Error while closing database", e);
        }
    }

    //======================================================================================

    public boolean addNodeSource(final NodeSourceData nodeSourceData) {
        try {
            return runWithTransaction(new SessionWork<Boolean>() {
                @Override
                Boolean executeWork(Session session) {
                    logger.info("Adding a new node source " + nodeSourceData.getName() + " to the data base");
                    session.save(nodeSourceData);
                    return true;
                }
            });
        } catch (RuntimeException e) {
            throw new RuntimeException("Node source '" + nodeSourceData.getName() + "' already exists");
        }
    }

    public void removeNodeSource(final String sourceName) {
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                logger.info("Removing the node source " + sourceName + " from the data base");
                session.createQuery("delete from NodeSourceData where name=:name").setParameter("name", sourceName)
                        .executeUpdate();
                return null;
            }
        });
    }

    private void removeNodeSources() {
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                logger.info("Removing all node sources from the data base");
                session.createQuery("delete from NodeSourceData").executeUpdate();
                return null;
            }
        });
    }

    public Collection<NodeSourceData> getNodeSources() {
        return runWithoutTransaction(new SessionWork<Collection<NodeSourceData>>() {
            @Override
            @SuppressWarnings("unchecked")
            Collection<NodeSourceData> executeWork(Session session) {
                Query query = session.createQuery("from NodeSourceData");
                return (Collection<NodeSourceData>) query.list();
            }
        });
    }

    public void saveUserHistory(final UserHistory history) {
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                session.save(history);
                return null;
            }
        });
    }

    public void updateUserHistory(final UserHistory history) {
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                session.update(history);
                return null;
            }
        });
    }

    public void saveNodeHistory(final NodeHistory nodeHistory) {
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                session.createSQLQuery(
                        "update NodeHistory set endTime=:endTime where nodeUrl=:nodeUrl and endTime=0")
                        .setParameter("endTime", nodeHistory.getStartTime())
                        .setParameter("nodeUrl", nodeHistory.getNodeUrl()).executeUpdate();

                if (nodeHistory.isStoreInDataBase()) {
                    session.save(nodeHistory);
                }
                return null;
            }
        });
    }

    /**
     * @return prev alive time
     */
    protected void updateRmAliveTime() {
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                long curMilliseconds = System.currentTimeMillis();
                session.createSQLQuery("update Alive set time = :time").setParameter("time", curMilliseconds)
                        .executeUpdate();
                return null;
            }
        });
    }

    private void createRmAliveTime() {
        runWithTransaction(new SessionWork<Void>() {
            @Override
            Void executeWork(Session session) {
                Alive alive = new Alive();
                alive.setTime(System.currentTimeMillis());
                session.save(alive);
                return null;
            }
        });
    }

    public List<?> sqlQuery(final String queryStr) {
        return runWithoutTransaction(new SessionWork<List<?>>() {
            @Override
            @SuppressWarnings("unchecked")
            List<?> executeWork(Session session) {
                Query query = session.createQuery(queryStr);
                return query.list();
            }
        });
    }

}