/*
* Copyright 1999-2008 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.accounting.impls.dbdefault;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.globus.workspace.Lager;
import org.globus.workspace.WorkspaceException;
import org.globus.workspace.accounting.ElapsedAndReservedMinutes;
import org.globus.workspace.persistence.WorkspaceDatabaseException;
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.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
/**
* package-private class, all access is from DBAccountingAdapter
*/
class DBAccountingPersistence implements DBAccountingConstants {
private static final Log logger =
LogFactory.getLog(DBAccountingPersistence.class.getName());
private boolean initialized;
private final DateFormat localFormat = DateFormat.getDateTimeInstance();
private final DataSource dataSource;
private final Lager lager;
DBAccountingPersistence(DataSource dataSourceImpl,
Lager lagerImpl) {
if (dataSourceImpl == null) {
throw new IllegalArgumentException("dataSourceImpl may not be null");
}
this.dataSource = dataSourceImpl;
if (lagerImpl == null) {
throw new IllegalArgumentException("lagerImpl may not be null");
}
this.lager = lagerImpl;
}
/* ******** */
/* DB setup */
/* ******** */
public void initialize() throws Exception {
try {
this.prepareStatements();
} catch (SQLException sql) {
throw new Exception("Problem preparing DB statements: ", sql);
}
this.initialized = true;
}
public boolean isInitialized() {
return this.initialized;
}
/**
* 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 testing indicates with Derby (in embedded mode),
* this is OK using one connection.
*
* @throws SQLException problem
*/
private void prepareStatements() throws SQLException {
if (this.lager.accounting) {
logger.debug("prepareStatements()");
}
// String[] ins = INSENSITIVE_PREPARED_STATEMENTS;
String[] pstmts = 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 e;
} finally {
try {
if (pstmt != null) {
pstmt.close();
}
if (c != null) {
returnConnection(c);
}
} catch (SQLException sql) {
logger.error("SQLException in finally cleanup", sql);
}
}
}
public int forceAllInactive() throws WorkspaceDatabaseException {
if (this.lager.accounting) {
logger.trace("forceAllInactive()");
}
int updated = -1;
Connection c = null;
PreparedStatement pstmt = null;
try {
c = getConnection();
pstmt = c.prepareStatement(SQL_FORCE_ALL_INACTIVE);
updated = pstmt.executeUpdate();
if (this.lager.accounting) {
logger.trace("updated " + updated + " 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);
}
}
return updated;
}
/* ****** */
/* Common */
/* ****** */
/**
* @return Connection conn
* @throws SQLException problem
*/
private Connection getConnection() throws SQLException {
return this.dataSource.getConnection();
}
private static void returnConnection(Connection connection) {
if(connection != null) {
try {
connection.close();
} catch(SQLException e) {
logger.error("",e);
}
}
}
/* ****************** */
/* Add/End Operations */
/* ****************** */
public synchronized void add(String uuid,
int id,
String ownerDN,
long minutesRequested,
Calendar creationTime,
int CPUCores,
int memory)
throws WorkspaceDatabaseException {
if (this.lager.accounting) {
logger.trace("add(): uuid = '" + uuid + "', id = " + id + ", " +
"ownerDN = '" + ownerDN + "', minutesRequest = " +
minutesRequested + ", creationTime = " +
creationTime.getTimeInMillis() + ", cpu cores = " +
CPUCores + ", memory = " + memory
);
}
Connection c = null;
PreparedStatement pstmt = null;
try {
c = getConnection();
pstmt = c.prepareStatement(SQL_INSERT_DEPLOYMENT);
pstmt.setString(1, uuid);
pstmt.setInt(2, id);
pstmt.setString(3, ownerDN);
pstmt.setObject(4, new Long(creationTime.getTimeInMillis()));
pstmt.setObject(5, new Long(minutesRequested));
pstmt.setInt(6, 1);
pstmt.setNull(7, Types.INTEGER);
pstmt.setInt(8, CPUCores);
pstmt.setInt(9, memory);
int inserted = pstmt.executeUpdate();
if (this.lager.accounting) {
logger.trace(Lager.id(id) + ": 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);
}
}
if (this.lager.accounting) {
logger.trace(Lager.id(id) + ": add() done (uuid: " + uuid + ")");
}
}
public synchronized String end(int id,
String ownerDN,
long minutesElapsed)
throws WorkspaceDatabaseException {
if (this.lager.accounting) {
logger.trace("end(): " + Lager.id(id) + ", ownerDN = '" + ownerDN +
"', minutesElapsed = " + minutesElapsed);
}
Connection c = null;
PreparedStatement pstmt = null;
PreparedStatement pstmt2 = null;
ResultSet rs = null;
String uuid;
try {
c = getConnection();
// begin transaction
c.setAutoCommit(false);
// check consistency of destroy request
// & retrieve necessary items for subsequent sql
String creatorDN;
Calendar creationTime;
long t;
int requestedDuration;
int CPUCores;
int memory;
pstmt = c.prepareStatement(SQL_LOAD_DEPLOYMENT);
pstmt.setInt(1, id);
rs = pstmt.executeQuery();
if (rs == null || !rs.next()) {
String err = "active deployment with id " + id + " not found";
logger.error(err);
throw new WorkspaceDatabaseException(err);
} else {
uuid = rs.getString(1);
creatorDN = rs.getString(2);
t = rs.getLong(3);
creationTime = Calendar.getInstance();
creationTime.setTimeInMillis(t);
requestedDuration = rs.getInt(4);
CPUCores = rs.getInt(5);
memory = rs.getInt(6);
}
if (this.lager.accounting) {
logger.trace("end(): found " + Lager.id(id) +
": uuid = " + uuid +
", creation time = " + t +
", requestedDuration = " + requestedDuration +
", CPU cores = " + CPUCores +
", memory = " + memory +
", creator DN = " + creatorDN);
}
rs.close();
rs = null;
if (ownerDN.equals(creatorDN)) {
if (this.lager.accounting) {
logger.trace(Lager.id(id) + ": creatorDN in DB matches " +
"destroy request");
}
} else {
String err = "active deployment with id " + id + " had " +
"non-matching creatorDN. Expected '" + ownerDN + "'," +
" stored was '" + creatorDN + "'";
logger.error(err);
throw new WorkspaceDatabaseException(err);
}
// log elapsed time
pstmt2 = c.prepareStatement(SQL_UPDATE_END_DEPLOYMENT);
pstmt2.setObject(1, new Long(minutesElapsed));
pstmt2.setString(2, uuid);
int updated = pstmt2.executeUpdate();
c.commit();
if (this.lager.accounting) {
logger.trace(Lager.id(id) + ": updated " + updated + " rows");
}
} catch(SQLException e) {
logger.error("",e);
throw new WorkspaceDatabaseException(e);
} finally {
try {
if (rs != null) {
rs.close();
}
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);
}
}
return uuid;
}
/* **************** */
/* Query Operations */
/* **************** */
public long totalElapsedMinutes(String ownerDN)
throws WorkspaceException {
Connection c = null;
try {
c = getConnection();
return totalElapsedMinutesImpl(ownerDN, c);
} catch(SQLException e) {
logger.error("",e);
throw new WorkspaceException(e);
} finally {
if (c != null) {
returnConnection(c);
}
}
}
public long currentReservedMinutes(String ownerDN)
throws WorkspaceException {
Connection c = null;
try {
c = getConnection();
return this.currentReservedMinutesImpl(ownerDN, c);
} catch(SQLException e) {
logger.error("",e);
throw new WorkspaceException(e);
} finally {
if (c != null) {
returnConnection(c);
}
}
}
public ElapsedAndReservedMinutes totalElapsedAndReservedMinutesTuple(
String ownerDN)
throws WorkspaceException {
Connection c = null;
try {
c = getConnection();
long reserved = this.currentReservedMinutesImpl(ownerDN, c);
long elapsed = totalElapsedMinutesImpl(ownerDN, c);
return new ElapsedAndReservedMinutes(elapsed, reserved);
} catch(SQLException e) {
logger.error("",e);
throw new WorkspaceException(e);
} finally {
if (c != null) {
returnConnection(c);
}
}
}
/**
* Returns a list of lines to log, one for each current reservation.
* Embedding line formatting in this method as a shortcut even though
* formatting is more appropriately the caller's concern.
*
* @return list of strings to log
* @throws WorkspaceException problem
*/
public ArrayList allActiveReservations() throws WorkspaceException {
Connection c = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
c = getConnection();
pstmt = c.prepareStatement(SQL_ALL_CURRENT_RESERVATIONS);
rs = pstmt.executeQuery();
ArrayList reservations = new ArrayList();
if (rs == null) {
if (this.lager.accounting) {
logger.trace("null result for current reservations query");
}
return reservations;
}
while (rs.next()) {
String uuid = rs.getString(1);
int id = rs.getInt(2);
String dn = rs.getString(3);
long t = rs.getLong(4);
Calendar creationTime = Calendar.getInstance();
creationTime.setTimeInMillis(t);
int duration = rs.getInt(5);
StringBuffer buf = new StringBuffer();
buf.append("dn=\"")
.append(dn)
.append("\", minutes=")
.append(duration)
.append(", uuid=\"")
.append(uuid)
.append("\", eprkey=")
.append(id)
.append(", creation=\"")
.append(this.localFormat.format(creationTime.getTime()))
.append('"');
reservations.add(buf.toString());
}
if (this.lager.accounting) {
logger.trace("sizeof(reservations) = " + reservations.size());
}
return reservations;
} catch(SQLException e) {
logger.error("",e);
throw new WorkspaceException(e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
logger.error(e.getMessage(), e);
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
logger.error(e.getMessage(), e);
}
}
if (c != null) {
returnConnection(c);
}
}
}
private static long totalElapsedMinutesImpl(String ownerDN, Connection c)
throws WorkspaceException {
PreparedStatement pstmt = null;
ResultSet rs = null;
long time = 0;
try {
pstmt = c.prepareStatement(SQL_SUM_ELAPSED);
pstmt.setString(1, ownerDN);
rs = pstmt.executeQuery();
if (rs == null || !rs.next()) {
logger.debug("no result from sum (DN not seen before)");
return time;
} else {
time = rs.getLong(1);
}
} catch(SQLException e) {
logger.error("",e);
throw new WorkspaceException(e);
} finally {
try {
if (rs != null) {
rs.close();
}
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException sql) {
logger.error("SQLException in finally cleanup", sql);
}
}
return time;
}
private long currentReservedMinutesImpl(String ownerDN, Connection c)
throws WorkspaceException {
PreparedStatement pstmt = null;
ResultSet rs = null;
long time = 0;
try {
pstmt = c.prepareStatement(SQL_SUM_RESERVED);
pstmt.setString(1, ownerDN);
rs = pstmt.executeQuery();
if (rs == null || !rs.next()) {
logger.debug("no result from sum (DN not seen before)");
return time;
} else {
time = rs.getLong(1);
}
} catch(SQLException e) {
logger.error("",e);
throw new WorkspaceException(e);
} finally {
try {
if (rs != null) {
rs.close();
}
if (pstmt != null) {
pstmt.close();
}
} catch (SQLException sql) {
logger.error("SQLException in finally cleanup", sql);
}
}
return time;
}
}