/*
* JBoss, Home of Professional Open Source
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* (C) 2005-2006,
* @author JBoss Inc.
*/
/*
* Copyright (C) 2000, 2001,
*
* Arjuna Solutions Limited,
* Newcastle upon Tyne,
* Tyne and Wear,
* UK.
*
* $Id: JDBCImple.java 2342 2006-03-30 13:06:17Z $
*/
package com.arjuna.ats.internal.arjuna.objectstore.jdbc;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.NamingException;
import com.arjuna.ats.arjuna.common.ObjectStoreEnvironmentBean;
import com.arjuna.ats.arjuna.common.Uid;
import com.arjuna.ats.arjuna.exceptions.FatalError;
import com.arjuna.ats.arjuna.exceptions.ObjectStoreException;
import com.arjuna.ats.arjuna.logging.tsLogger;
import com.arjuna.ats.arjuna.objectstore.ObjectStore;
import com.arjuna.ats.arjuna.objectstore.StateStatus;
import com.arjuna.ats.arjuna.objectstore.jdbc.JDBCAccess;
import com.arjuna.ats.arjuna.state.InputObjectState;
import com.arjuna.ats.arjuna.state.OutputObjectState;
import com.arjuna.ats.internal.arjuna.common.UidHelper;
/**
* An object store implementation which uses a JDBC database for maintaining
* object states. All states are maintained within a single table.
*/
public abstract class JDBCImple_driver {
// protected Connection connection;
protected String tableName;
private JDBCAccess jdbcAccess;
public boolean commit_state(Uid objUid, String typeName)
throws ObjectStoreException {
// Taken this requirement from ObjStoreBrowser
if (typeName.startsWith("/"))
typeName = typeName.substring(1);
boolean result = false;
Connection connection = null;
PreparedStatement pstmt = null;
PreparedStatement pstmt2 = null;
try {
connection = jdbcAccess.getConnection();
// Delete any previously committed state
pstmt = connection
.prepareStatement("DELETE FROM "
+ tableName
+ " WHERE TypeName = ? AND UidString = ? AND StateType = " + StateStatus.OS_COMMITTED);
pstmt.setString(1, typeName);
pstmt.setString(2, objUid.stringForm());
int rowcount = pstmt.executeUpdate();
if (rowcount > 0) {
tsLogger.i18NLogger
.trace_JDBCImple_previouslycommitteddeleted(rowcount);
}
// now do the commit itself:
pstmt2 = connection
.prepareStatement("UPDATE "
+ tableName
+ " SET StateType = " + StateStatus.OS_COMMITTED + " WHERE TypeName = ? AND UidString = ? AND StateType = "
+ StateStatus.OS_UNCOMMITTED);
pstmt2.setString(1, typeName);
pstmt2.setString(2, objUid.stringForm());
int rowcount2 = pstmt2.executeUpdate();
if (rowcount2 > 0) {
connection.commit();
result = true;
} else {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_nothingtocommit(objUid.stringForm());
connection.rollback();
}
} catch (Exception e) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_writefailed(e);
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (pstmt2 != null) {
try {
pstmt2.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
return result;
}
public boolean hide_state(Uid objUid, String typeName)
throws ObjectStoreException {
// Taken this requirement from ObjStoreBrowser
if (typeName.startsWith("/"))
typeName = typeName.substring(1);
boolean result = false;
Connection connection = null;
PreparedStatement pstmt = null;
try {
connection = jdbcAccess.getConnection();
pstmt = connection
.prepareStatement("UPDATE "
+ tableName
+ " SET Hidden = 1 WHERE TypeName = ? AND UidString = ?");
pstmt.setString(1, typeName);
pstmt.setString(2, objUid.stringForm());
int rowcount = pstmt.executeUpdate();
connection.commit();
if (rowcount > 0) {
result = true;
}
} catch (Exception e) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_1(e);
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
return result;
}
public boolean reveal_state(Uid objUid, String typeName)
throws ObjectStoreException {
// Taken this requirement from ObjStoreBrowser
if (typeName.startsWith("/"))
typeName = typeName.substring(1);
boolean result = false;
Connection connection = null;
PreparedStatement pstmt = null;
try {
connection = jdbcAccess.getConnection();
pstmt = connection
.prepareStatement("UPDATE "
+ tableName
+ " SET Hidden = 0 WHERE TypeName = ? AND UidString = ?");
pstmt.setString(1, typeName);
pstmt.setString(2, objUid.stringForm());
int rowcount = pstmt.executeUpdate();
connection.commit();
if (rowcount > 0) {
result = true;
}
} catch (Exception e) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_2(e);
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
return result;
}
/**
* currentState - determine the current state of an object. State search is
* ordered OS_UNCOMMITTED, OS_UNCOMMITTED_HIDDEN, OS_COMMITTED,
* OS_COMMITTED_HIDDEN
*
* @throws ObjectStoreException - in case the JDBC store cannot be contacted
*/
public int currentState(Uid objUid, String typeName)
throws ObjectStoreException {
// Taken this requirement from ObjStoreBrowser
if (typeName.startsWith("/"))
typeName = typeName.substring(1);
int theState = StateStatus.OS_UNKNOWN;
ResultSet rs = null;
Connection connection = null;
PreparedStatement pstmt = null;
try {
connection = jdbcAccess.getConnection();
pstmt = connection
.prepareStatement("SELECT StateType, Hidden FROM "
+ tableName
+ " WHERE TypeName = ? AND UidString = ?");
pstmt.setString(1, typeName);
pstmt.setString(2, objUid.stringForm());
rs = pstmt.executeQuery();
// we may have multiple states. need to sort out the order of
// precedence
// without making multiple round trips out to the db. this gets
// a bit messy:
boolean have_OS_UNCOMMITTED = false;
boolean have_OS_COMMITTED = false;
boolean have_OS_UNCOMMITTED_HIDDEN = false;
boolean have_OS_COMMITTED_HIDDEN = false;
while (rs.next()) {
int stateStatus = rs.getInt(1);
int hidden = rs.getInt(2);
switch (stateStatus) {
case StateStatus.OS_UNCOMMITTED:
if (hidden == 0)
have_OS_UNCOMMITTED = true;
else
have_OS_UNCOMMITTED_HIDDEN = true;
break;
case StateStatus.OS_COMMITTED:
if (hidden == 0)
have_OS_COMMITTED = true;
else
have_OS_COMMITTED_HIDDEN = true;
break;
}
}
connection.commit();
// examine in reverse order:
if (have_OS_COMMITTED_HIDDEN) {
theState = StateStatus.OS_COMMITTED_HIDDEN;
}
if (have_OS_COMMITTED) {
theState = StateStatus.OS_COMMITTED;
}
if (have_OS_UNCOMMITTED_HIDDEN) {
theState = StateStatus.OS_UNCOMMITTED_HIDDEN;
}
if (have_OS_UNCOMMITTED) {
theState = StateStatus.OS_UNCOMMITTED;
}
} catch (Exception e) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_3(e);
throw new ObjectStoreException(e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// ignore
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
return theState;
}
/**
* allObjUids - Given a type name, return an ObjectState that contains all
* of the uids of objects of that type.
*/
public boolean allObjUids(String typeName, InputObjectState state, int match)
throws ObjectStoreException {
// Taken this requirement from ObjStoreBrowser
if (typeName.startsWith("/"))
typeName = typeName.substring(1);
try {
OutputObjectState store = new OutputObjectState();
Connection connection = jdbcAccess.getConnection();
Statement stmt = connection.createStatement();
ResultSet rs = null;
try {
/*
* Not used enough to warrant a PreparedStatement.
*/
rs = stmt.executeQuery("SELECT DISTINCT UidString FROM "
+ tableName + " WHERE TypeName = '" + typeName + "'");
boolean finished = false;
while (!finished && rs.next()) {
Uid theUid = null;
try {
theUid = new Uid(rs.getString(1));
UidHelper.packInto(theUid, store);
} catch (IOException ex) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_5(ex);
return false;
}
}
connection.commit();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// Ignore
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
UidHelper.packInto(Uid.nullUid(), store);
state.setBuffer(store.buffer());
store = null;
return true;
} catch (Exception e) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_4(e);
return false;
}
}
public boolean allTypes(InputObjectState foundTypes)
throws ObjectStoreException {
try {
OutputObjectState store = new OutputObjectState();
Connection connection = jdbcAccess.getConnection();
Statement stmt = connection.createStatement();
ResultSet rs = null;
try {
/*
* Not used enough to warrant a PreparedStatement.
*/
rs = stmt.executeQuery("SELECT DISTINCT TypeName FROM "
+ tableName);
boolean finished = false;
while (!finished && rs.next()) {
try {
String type = rs.getString(1);
store.packString(type);
} catch (IOException ex) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_7(ex);
return false;
}
}
connection.commit();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// Ignore
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
store.packString("");
foundTypes.setBuffer(store.buffer());
return true;
} catch (Exception e) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_6(e);
return false;
}
}
public boolean remove_state(Uid objUid, String typeName, int stateType)
throws ObjectStoreException {
// Taken this requirement from ObjStoreBrowser
if (typeName.startsWith("/"))
typeName = typeName.substring(1);
boolean result = false;
if (typeName != null) {
if ((stateType == StateStatus.OS_COMMITTED)
|| (stateType == StateStatus.OS_UNCOMMITTED)) {
Connection connection = null;
PreparedStatement pstmt = null;
try {
connection = jdbcAccess.getConnection();
pstmt = connection
.prepareStatement("DELETE FROM "
+ tableName
+ " WHERE TypeName = ? AND UidString = ? AND StateType = ?");
pstmt.setString(1, typeName);
pstmt.setString(2, objUid.stringForm());
pstmt.setInt(3, stateType);
if (pstmt.executeUpdate() > 0) {
result = true;
}
connection.commit();
} catch (Exception e) {
result = false;
tsLogger.i18NLogger.warn_objectstore_JDBCImple_8(e);
} finally {
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
} else {
// can only remove (UN)COMMITTED objs
tsLogger.i18NLogger.warn_objectstore_JDBCImple_9(
Integer.toString(stateType), objUid);
}
} else {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_10(objUid);
}
return result;
}
public InputObjectState read_state(Uid objUid, String typeName,
int stateType) throws ObjectStoreException {
InputObjectState result = null;
// Taken this requirement from ObjStoreBrowser
if (typeName.startsWith("/"))
typeName = typeName.substring(1);
if ((stateType == StateStatus.OS_COMMITTED)
|| (stateType == StateStatus.OS_UNCOMMITTED)) {
ResultSet rs = null;
Connection connection = null;
PreparedStatement pstmt = null;
try {
connection = jdbcAccess.getConnection();
pstmt = connection
.prepareStatement("SELECT ObjectState FROM "
+ tableName
+ " WHERE TypeName = ? AND UidString = ? AND StateType = ?");
pstmt.setString(1, typeName);
pstmt.setString(2, objUid.stringForm());
pstmt.setInt(3, stateType);
rs = pstmt.executeQuery();
if (rs.next()) {
byte[] buffer = rs.getBytes(1);
if (buffer != null) {
result = new InputObjectState(objUid, typeName, buffer);
} else {
tsLogger.i18NLogger
.warn_objectstore_JDBCImple_readfailed();
throw new ObjectStoreException(tsLogger.i18NLogger.warn_objectstore_JDBCImple_readfailed_message());
}
}
connection.commit();
} catch (Exception e) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_14(e);
throw new ObjectStoreException(e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// Ignore
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
} else {
throw new ObjectStoreException(tsLogger.i18NLogger.unexpected_state_type(stateType));
}
return result;
}
public boolean write_state(Uid objUid, String typeName,
OutputObjectState state, int stateType) throws ObjectStoreException {
// Taken this requirement from ObjStoreBrowser
if (typeName.startsWith("/"))
typeName = typeName.substring(1);
boolean result = false;
int imageSize = (int) state.length();
if (imageSize > getMaxStateSize()) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_over_max_image_size(
imageSize, getMaxStateSize());
} else if (imageSize > 0) {
byte[] b = state.buffer();
ResultSet rs = null;
Connection connection = null;
PreparedStatement pstmt = null;
try {
connection = jdbcAccess.getConnection();
pstmt = connection
.prepareStatement(
"SELECT ObjectState, UidString, StateType, TypeName FROM "
+ tableName
+ " WHERE TypeName = ? AND UidString = ? AND StateType = ?",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
pstmt.setString(1, typeName);
pstmt.setString(2, objUid.stringForm());
pstmt.setInt(3, stateType);
rs = pstmt.executeQuery();
if (rs.next()) {
PreparedStatement pstmt2 = connection
.prepareStatement("UPDATE " + tableName +
" SET ObjectState = ?" +
" WHERE TypeName=? AND UidString=? AND StateType=?");
try {
pstmt2.setBytes(1, b);
pstmt2.setString(2, typeName);
pstmt2.setString(3, objUid.stringForm());
pstmt2.setInt(4, stateType);
int executeUpdate = pstmt2.executeUpdate();
if (executeUpdate != 0) {
result = true;
} else {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_nothingtoupdate(objUid.toString());
}
} finally {
pstmt2.close();
}
} else {
// not in database, do insert:
PreparedStatement pstmt2 = connection
.prepareStatement("INSERT INTO "
+ tableName
+ " (TypeName,UidString,StateType,Hidden,ObjectState) VALUES (?,?,?,0,?)");
try {
pstmt2.setString(1, typeName);
pstmt2.setString(2, objUid.stringForm());
pstmt2.setInt(3, stateType);
pstmt2.setBytes(4, b);
int executeUpdate = pstmt2.executeUpdate();
if (executeUpdate != 0) {
result = true;
} else {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_nothingtoinsert(objUid.toString());
}
} finally {
pstmt2.close();
}
}
connection.commit();
} catch (Exception e) {
tsLogger.i18NLogger.warn_objectstore_JDBCImple_writefailed(e);
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// Ignore
}
}
if (pstmt != null) {
try {
pstmt.close();
} catch (SQLException e) {
// Ignore
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
// Ignore
}
}
}
}
return result;
}
/**
* Set up the store for use.
*
* @throws NamingException
* @throws SQLException In case the configured store cannot be connected to
*/
public void initialise(final JDBCAccess jdbcAccess, String tableName,
ObjectStoreEnvironmentBean jdbcStoreEnvironmentBean)
throws SQLException, NamingException {
this.jdbcAccess = jdbcAccess;
// connection = new ThreadLocal<Connection>() {
//
// @Override
// protected Connection initialValue() {
// try {
// return jdbcAccess.getConnection();
// } catch (Exception e) {
// throw new RuntimeException(e);
// }
// }
// };
try (Connection connection = jdbcAccess.getConnection()) {
try (Statement stmt = connection.createStatement()) {
// table [type, object UID, format, blob]
if (jdbcStoreEnvironmentBean.getDropTable()) {
try {
stmt.executeUpdate("DROP TABLE " + tableName);
} catch (SQLException ex) {
checkDropTableException(connection, ex);
}
}
if (jdbcStoreEnvironmentBean.getCreateTable()) {
try {
createTable(stmt, tableName);
} catch (SQLException ex) {
checkCreateTableError(ex);
}
}
// This can be the case when triggering via EmptyObjectStore
if (!connection.getAutoCommit()) {
connection.commit();
}
}
}
this.tableName = tableName;
}
/**
* Can be overridden by implementation-specific code to create the store
* table. Called from initialise() and addTable(), above.
*/
protected void createTable(Statement stmt, String tableName)
throws SQLException {
String statement = "CREATE TABLE "
+ tableName
+ " (StateType INTEGER NOT NULL, Hidden INTEGER NOT NULL, "
+ "TypeName VARCHAR(255) NOT NULL, UidString VARCHAR(255) NOT NULL, ObjectState "
+ getObjectStateSQLType()
+ ", PRIMARY KEY(UidString, TypeName, StateType))";
stmt.executeUpdate(statement);
}
protected String getObjectStateSQLType() {
return "bytea";
}
protected abstract void checkCreateTableError(SQLException ex)
throws SQLException;
protected abstract void checkDropTableException(Connection connection, SQLException ex)
throws SQLException;
public int getMaxStateSize() {
return 65535;
}
}