org.globus.workspace.scheduler.defaults.DefaultSchedulerAdapterDB.java Source code

Java tutorial

Introduction

Here is the source code for org.globus.workspace.scheduler.defaults.DefaultSchedulerAdapterDB.java

Source

/*
 * Copyright 1999-2010 University of Chicago
 *
 * 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.globus.workspace.scheduler.defaults;

import org.globus.workspace.persistence.WorkspaceDatabaseException;
import org.globus.workspace.Lager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Calendar;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

public class DefaultSchedulerAdapterDB {

    private static final Log logger = LogFactory.getLog(DefaultSchedulerAdapterDB.class.getName());

    private final DataSource source;
    private final Lager lager;

    public DefaultSchedulerAdapterDB(DataSource dataSource, Lager lagerImpl) {
        if (dataSource == null) {
            throw new IllegalArgumentException("dataSource is null");
        }
        this.source = dataSource;

        if (lagerImpl == null) {
            throw new IllegalArgumentException("lagerImpl may not be null");
        }
        this.lager = lagerImpl;
    }

    /**
     * This moves significant prepared statement setup times to service
     * initialization instead of the first time they're used.
     *
     * Documentation states that PreparedStatement caches are per pool
     * connection but preliminary testing indicates it is effective to
     * just use the first one from the pool.
     *
     * @throws org.globus.workspace.persistence.WorkspaceDatabaseException exc
     */
    void prepareStatements() throws WorkspaceDatabaseException {

        final String[] ins = DefaultSchedulerConstants.INSENSITIVE_PREPARED_STATEMENTS;

        final String[] pstmts = DefaultSchedulerConstants.PREPARED_STATEMENTS;

        Connection c = null;
        PreparedStatement pstmt = null;
        try {
            c = getConnection();

            for (int i = 0; i < ins.length; i++) {
                pstmt = c.prepareStatement(ins[i], ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
                pstmt.close();
            }

            for (int i = 0; i < pstmts.length; i++) {
                pstmt = c.prepareStatement(pstmts[i]);
                pstmt.close();
            }

            pstmt = null;

        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException e) {
                logger.error("SQLException in finally cleanup", e);
            }
        }
    }

    void backOutTasks(int id) throws WorkspaceDatabaseException {

        Connection c = null;
        PreparedStatement pstmt = null;
        try {
            c = getConnection();
            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_DELETE_TASKS);
            pstmt.setInt(1, id);
            int deleted = pstmt.executeUpdate();

            // this will be zero deletes in the case where slot has not
            // been activated yet but client has called destroy (the only
            // legal operation it can call before slot is activated)

            if (lager.dbLog) {
                logger.trace("deleted " + deleted + " rows");
            }

        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    void scheduleTasks(int id, Calendar stop) throws WorkspaceDatabaseException {

        if (lager.traceLog) {
            logger.trace("scheduleTasks(): " + Lager.id(id));
        }

        Connection c = null;
        PreparedStatement pstmt = null;
        try {
            c = getConnection();
            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_INSERT_TASK);
            pstmt.setInt(1, id);
            pstmt.setLong(2, stop.getTimeInMillis());
            pstmt.setInt(3, 0);

            int inserted = pstmt.executeUpdate();

            if (lager.dbLog) {
                logger.trace("inserted " + inserted + " rows");
            }

        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    /**
     * Database agnostic way to manage indexes
     *
     * What is stored in the row is the last ID that was USED already.
     *
     * This is O(1) now, not O(numNodes)
     *
     * @param numNodes number of IDs needed
     * @return next request IDs
     * @throws WorkspaceDatabaseException exc
     */
    synchronized int[] getNextTasktIds(int numNodes) throws WorkspaceDatabaseException {

        PreparedStatement pstmt = null;
        PreparedStatement pstmt2 = null;
        ResultSet rs = null;
        Connection c = null;
        int lastTaskId = -1;
        int newLastTaskId = -1;
        try {
            c = getConnection();

            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_SELECT_DEFAULT_SCHED_REQ_ID,
                    ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
            rs = pstmt.executeQuery();

            if (!rs.next()) {
                // if there is no row in database, this is first time an ID
                // is needed, insert the value do not update it
                lastTaskId = 0;
                newLastTaskId = numNodes;
                pstmt2 = c.prepareStatement(DefaultSchedulerConstants.SQL_INSERT_DEFAULT_SCHED_REQ_ID);
                pstmt2.setInt(1, newLastTaskId);
                pstmt2.executeUpdate();
            } else {
                lastTaskId = rs.getInt(1);

                // Get the req Id and increment it
                newLastTaskId = lastTaskId + numNodes;

                pstmt2 = c.prepareStatement(DefaultSchedulerConstants.SQL_UPDATE_DEFAULT_SCHED_REQ_ID);
                pstmt2.setInt(1, newLastTaskId);
                pstmt2.executeUpdate();
            }
        } catch (SQLException e) {
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (pstmt2 != null) {
                    pstmt2.close();
                }
                if (rs != null) {
                    rs.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException e) {
                logger.error("SQLException in finally cleanup", e);
            }
        }

        if (lastTaskId < 0) {
            throw new WorkspaceDatabaseException("lastTaskId not expected " + "to be negative here");
        }
        if (newLastTaskId < 0) {
            throw new WorkspaceDatabaseException("newLastTaskId not expected" + " to be negative here");
        }

        if (newLastTaskId - lastTaskId != numNodes) {
            throw new WorkspaceDatabaseException("difference expected to be " + "equal to numNodes here");
        }

        final int[] ret = new int[numNodes];
        for (int i = 0; i < numNodes; i++) {
            lastTaskId += 1;
            ret[i] = lastTaskId;
        }
        return ret;
    }

    int[] findWorkspacesToShutdown() throws WorkspaceDatabaseException {

        if (lager.schedLog) {
            logger.trace("findWorkspacesToShutdown()");
        }

        Connection c = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            c = getConnection();
            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_SELECT_SHUTDOWN);
            pstmt.setLong(1, Calendar.getInstance().getTimeInMillis());
            rs = pstmt.executeQuery();

            ArrayList results = new ArrayList();
            if (rs == null || !rs.next()) {
                if (lager.schedLog) {
                    logger.trace("no workspaces to shutdown");
                }
                return null;
            } else {
                do {
                    Integer id = new Integer(rs.getInt(1));
                    results.add(id);
                    if (lager.schedLog) {
                        logger.trace("found id: " + id);
                    }
                } while (rs.next());
            }

            // can't use toArray without converting to Integer[]
            int[] ret = new int[results.size()];
            for (int i = 0; i < ret.length; i++) {
                ret[i] = ((Number) results.get(i)).intValue();
            }
            return ret;

        } catch (SQLException e) {
            logger.error("", e);
            return null;
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    int anyLeft() throws WorkspaceDatabaseException {
        Connection c = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            c = getConnection();
            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_SELECT_ANY_LEFT);
            rs = pstmt.executeQuery();

            Integer left = null;

            if (rs != null && rs.next()) {
                left = new Integer(rs.getInt(1));
            }

            if (left != null) {
                return left.intValue();
            } else {
                return 0;
            }

        } catch (SQLException e) {
            logger.error("", e);
            return 0;
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    void markShutdown(int id) throws WorkspaceDatabaseException {

        Connection c = null;
        PreparedStatement pstmt = null;
        try {
            c = getConnection();
            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_UPDATE_SHUTDOWN);
            pstmt.setInt(1, id);
            pstmt.executeUpdate();
        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    /* Coscheduling related */

    void addNodeRequest(NodeRequest req, String coschedid) throws WorkspaceDatabaseException {

        if (lager.traceLog) {
            logger.trace("addNodeRequest(): " + Lager.ensembleid(coschedid));
        }

        String assocString = null;
        final String[] assocs = req.getNeededAssociations();
        if (assocs != null && assocs.length > 0) {
            StringBuffer buf = new StringBuffer(256);
            buf.append(assocs[0]);
            for (int i = 1; i < assocs.length; i++) {
                buf.append(",").append(assocs[i]);
            }
            assocString = buf.toString();
        }

        // this nastily loops because failure situation to scheduler will only
        // send vmids and scheduler needs to be able to back out ensemble
        // pieces one by one but keep others intact (unless entire ensemble is
        // destroyed).  won't have to loop like this in the future when inner
        // abstractions are themselves unlooped more.

        Connection c = null;
        PreparedStatement pstmt = null;
        try {
            c = getConnection();
            c.setAutoCommit(false);

            for (int i = 0; i < req.getIds().length; i++) {

                pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_INSERT_NODE_REQUEST);

                pstmt.setString(1, coschedid);

                if (req.getGroupid() != null) {
                    pstmt.setString(2, req.getGroupid());
                } else {
                    pstmt.setNull(2, Types.VARCHAR);
                }

                pstmt.setInt(3, req.getIds()[i]);

                pstmt.setInt(4, req.getDuration());

                pstmt.setInt(5, req.getMemory());

                if (assocString != null) {
                    pstmt.setString(6, assocString);
                } else {
                    pstmt.setNull(6, Types.VARCHAR);
                }

                pstmt.executeUpdate();
                pstmt.close();
                pstmt = null;

            }

            c.commit();

        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    c.setAutoCommit(true);
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    NodeRequest[] getNodeRequests(String coschedid) throws WorkspaceDatabaseException {

        Connection c = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            c = getConnection();

            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_SELECT_LOAD_NODE_REQUESTS);
            pstmt.setString(1, coschedid);
            rs = pstmt.executeQuery();

            final Map groupMap = new HashMap(16);
            final ArrayList reqs = new ArrayList(64);
            if (rs == null || !rs.next()) {
                logger.warn("no requests with coschedid '" + coschedid + "'?");
                return null;
            } else {

                // already next'd
                do {
                    String groupid = rs.getString(1);
                    int id = rs.getInt(2);
                    int duration = rs.getInt(3);
                    int memory = rs.getInt(4);
                    String assocStr = rs.getString(5);

                    final NodeRequest req;
                    if (groupid != null) {
                        if (groupMap.containsKey(groupid)) {
                            req = (NodeRequest) groupMap.get(groupid);
                            req.addId(id);
                            continue;
                        } else {
                            req = new NodeRequest(memory, duration);
                            req.setGroupid(groupid);
                        }
                    } else {
                        req = new NodeRequest(memory, duration);
                    }

                    req.addId(id);

                    if (assocStr != null) {
                        String[] assocs = assocStr.split(",");
                        req.setNeededAssociations(assocs);
                    }

                    reqs.add(req);

                } while (rs.next());
            }

            if (reqs.isEmpty()) {
                return null;
            }

            return (NodeRequest[]) reqs.toArray(new NodeRequest[reqs.size()]);

        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    // transaction that removes node requests and sets a done flag
    void deleteNodeRequestsAndBeDone(String coschedid) throws WorkspaceDatabaseException {

        if (lager.dbLog) {
            logger.trace("deleteNodeRequestsAndBeDone(): " + Lager.ensembleid(coschedid));
        }

        Connection c = null;
        PreparedStatement pstmt = null;
        PreparedStatement pstmt2 = null;
        try {
            c = getConnection();
            c.setAutoCommit(false);

            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_DELETE_NODE_REQUESTS);
            pstmt.setString(1, coschedid);
            int deleted = pstmt.executeUpdate();

            pstmt2 = c.prepareStatement(DefaultSchedulerConstants.SQL_INSERT_NODE_REQUESTS_SENT);
            pstmt2.setString(1, coschedid);
            pstmt2.executeUpdate();

            c.commit();

            if (lager.dbLog) {
                logger.debug("deleted " + deleted + " rows, " + Lager.ensembleid(coschedid));
            }

        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (pstmt2 != null) {
                    pstmt2.close();
                }
                if (c != null) {
                    c.setAutoCommit(true);
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    // deletes just one node request
    void deleteNodeRequest(int vmid) throws WorkspaceDatabaseException {

        if (lager.dbLog) {
            logger.trace("deleteNodeRequest(): " + Lager.id(vmid));
        }

        Connection c = null;
        PreparedStatement pstmt = null;
        try {
            c = getConnection();

            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_DELETE_ONE_NODE_REQUEST);
            pstmt.setInt(1, vmid);
            int deleted = pstmt.executeUpdate();

            if (lager.dbLog) {
                logger.debug("deleted " + deleted + " rows, " + Lager.id(vmid));
            }

        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    void coschedDone(String coschedid) throws WorkspaceDatabaseException {

        Connection c = null;
        PreparedStatement pstmt = null;
        try {
            c = getConnection();
            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_INSERT_NODE_REQUESTS_SENT);

            pstmt.setString(1, coschedid);
            pstmt.executeUpdate();

        } catch (SQLException e) {
            logger.error("", e);
            throw new WorkspaceDatabaseException(e);
        } finally {
            try {
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    boolean isCoschedDone(String coschedid) throws WorkspaceDatabaseException {

        Connection c = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try {
            c = getConnection();
            pstmt = c.prepareStatement(DefaultSchedulerConstants.SQL_SELECT_CHECK_NODEREQS_SENT);
            pstmt.setString(1, coschedid);
            rs = pstmt.executeQuery();

            int count = -1;

            if (rs != null && rs.next()) {
                count = rs.getInt(1);
            }

            if (count < 0) {
                throw new WorkspaceDatabaseException("no count (?)");
            }

            return count > 0;

        } catch (SQLException e) {
            throw new WorkspaceDatabaseException(e.getMessage(), e);
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (pstmt != null) {
                    pstmt.close();
                }
                if (c != null) {
                    returnConnection(c);
                }
            } catch (SQLException sql) {
                logger.error("SQLException in finally cleanup", sql);
            }
        }
    }

    /* Connection methods */

    /**
     * @return connection
     * @throws WorkspaceDatabaseException exc
     */
    private Connection getConnection() throws WorkspaceDatabaseException {
        try {
            return this.source.getConnection();
        } catch (SQLException e) {
            throw new WorkspaceDatabaseException(e);
        }
    }

    private static void returnConnection(Connection connection) {
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException e) {
                logger.error("", e);
            }
        }
    }

}