package org.jboss.resource.adapter.jdbc;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Savepoint;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import javax.resource.ResourceException;
import javax.resource.spi.ConnectionEvent;
import javax.resource.spi.ConnectionEventListener;
import javax.resource.spi.ConnectionRequestInfo;
import javax.resource.spi.ManagedConnection;
import javax.resource.spi.ManagedConnectionMetaData;
import javax.security.auth.Subject;
import org.jboss.logging.Logger;
import org.jboss.resource.JBossResourceException;
public abstract class BaseWrapperManagedConnection
implements ManagedConnection
{
protected final BaseWrapperManagedConnectionFactory mcf;
protected final Connection con;
protected final Properties props;
private final int transactionIsolation;
private final boolean readOnly;
private final Collection cels = new ArrayList();
private final Set handles = new HashSet();
private PreparedStatementCache psCache = null;
protected Object stateLock = new Object();
protected boolean inManagedTransaction = false;
protected boolean jdbcAutoCommit = true;
protected boolean underlyingAutoCommit = true;
protected boolean jdbcReadOnly;
protected boolean underlyingReadOnly;
protected int jdbcTransactionIsolation;
protected boolean destroyed = false;
public BaseWrapperManagedConnection (final BaseWrapperManagedConnectionFactory mcf,
final Connection con,
final Properties props,
final int transactionIsolation,
final int psCacheSize)
throws SQLException
{
this.mcf = mcf;
this.con = con;
this.props = props;
if (psCacheSize > 0)
psCache = new PreparedStatementCache(psCacheSize);
if (transactionIsolation == -1)
this.transactionIsolation = con.getTransactionIsolation();
else
{
this.transactionIsolation = transactionIsolation;
con.setTransactionIsolation(transactionIsolation);
}
readOnly = con.isReadOnly();
if (mcf.getNewConnectionSQL() != null)
{
Statement s = con.createStatement();
try
{
s.execute(mcf.getNewConnectionSQL());
}
finally
{
s.close();
}
}
underlyingReadOnly = readOnly;
jdbcReadOnly = readOnly;
jdbcTransactionIsolation = this.transactionIsolation;
}
public void addConnectionEventListener(ConnectionEventListener cel)
{
synchronized (cels)
{
cels.add(cel);
}
}
public void removeConnectionEventListener(ConnectionEventListener cel)
{
synchronized (cels)
{
cels.remove(cel);
}
}
public void associateConnection(Object handle) throws ResourceException
{
if (!(handle instanceof WrappedConnection))
throw new JBossResourceException("Wrong kind of connection handle to associate" + handle);
((WrappedConnection)handle).setManagedConnection(this);
synchronized(handles)
{
handles.add(handle);
}
}
public PrintWriter getLogWriter() throws ResourceException
{
return null;
}
public ManagedConnectionMetaData getMetaData() throws ResourceException
{
return null;
}
public void setLogWriter(PrintWriter param1) throws ResourceException
{
}
public void cleanup() throws ResourceException
{
synchronized (handles)
{
for (Iterator i = handles.iterator(); i.hasNext(); )
{
WrappedConnection lc = (WrappedConnection)i.next();
lc.setManagedConnection(null);
}
handles.clear();
}
synchronized (stateLock)
{
jdbcAutoCommit = true;
jdbcReadOnly = readOnly;
if (jdbcTransactionIsolation != transactionIsolation)
{
try
{
con.setTransactionIsolation(jdbcTransactionIsolation);
jdbcTransactionIsolation = transactionIsolation;
}
catch (SQLException e)
{
mcf.log.warn("Error resetting transaction isolation ", e);
}
}
}
}
public Object getConnection(Subject subject, ConnectionRequestInfo cri) throws ResourceException
{
checkIdentity(subject, cri);
WrappedConnection lc = new WrappedConnection(this);
synchronized (handles)
{
handles.add(lc);
}
return lc;
}
public void destroy() throws ResourceException
{
synchronized (stateLock)
{
destroyed = true;
}
cleanup();
try
{
con.close();
}
catch (SQLException e)
{
checkException(e);
} }
public boolean checkValid()
{
SQLException e = mcf.isValidConnection(con);
if (e == null)
return true;
else
{
mcf.log.warn("Destroying connection that is not valid, due to the following exception:", e);
broadcastConnectionError(e);
return false;
}
}
void closeHandle(WrappedConnection handle)
{
synchronized (stateLock)
{
if (destroyed)
return;
}
synchronized(handles)
{
handles.remove(handle);
}
ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED);
ce.setConnectionHandle(handle);
Collection copy = null;
synchronized(cels)
{
copy = new ArrayList(cels);
}
for (Iterator i = copy.iterator(); i.hasNext(); )
{
ConnectionEventListener cel = (ConnectionEventListener)i.next();
cel.connectionClosed(ce);
}
}
void connectionError(SQLException e)
{
if (mcf.isExceptionFatal(e))
broadcastConnectionError(e);
}
protected void broadcastConnectionError(SQLException e)
{
ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_ERROR_OCCURRED, e);
Collection copy = null;
synchronized(cels)
{
copy = new ArrayList(cels);
}
for (Iterator i = copy.iterator(); i.hasNext(); )
{
ConnectionEventListener cel = (ConnectionEventListener)i.next();
cel.connectionErrorOccurred(ce);
}
}
Connection getConnection()
throws SQLException
{
if (con == null)
throw new SQLException("Connection has been destroyed!!!");
return con;
}
PreparedStatement prepareStatement(String sql) throws SQLException
{
if (psCache != null)
{
CachedPreparedStatement cachedps = (CachedPreparedStatement)psCache.get(sql);
if (cachedps != null)
{
if (canUse(cachedps))
cachedps.inUse();
else
return con.prepareStatement(sql);
}
else
{
PreparedStatement ps = con.prepareStatement(sql);
cachedps = new CachedPreparedStatement(ps);
psCache.insert(sql, cachedps);
}
return cachedps;
}
else
return con.prepareStatement(sql);
}
boolean canUse(CachedPreparedStatement cachedps)
{
if (cachedps.isInUse() == false)
return true;
if (underlyingAutoCommit == true)
return false;
return mcf.sharePS;
}
protected Logger getLog()
{
return mcf.log;
}
private void checkIdentity(Subject subject, ConnectionRequestInfo cri)
throws ResourceException
{
Properties newProps = mcf.getConnectionProperties(subject, cri);
if (!props.equals(newProps))
{
throw new JBossResourceException("Wrong credentials passed to getConnection!");
} }
void checkTransaction() throws SQLException
{
synchronized (stateLock)
{
if (inManagedTransaction)
return;
if (jdbcAutoCommit != underlyingAutoCommit)
{
con.setAutoCommit(jdbcAutoCommit);
underlyingAutoCommit = jdbcAutoCommit;
}
}
checkState();
}
protected void checkState() throws SQLException
{
synchronized (stateLock)
{
if (jdbcReadOnly != underlyingReadOnly)
{
con.setReadOnly(jdbcReadOnly);
underlyingReadOnly = jdbcReadOnly;
}
}
}
boolean isJdbcAutoCommit()
{
return inManagedTransaction? false: jdbcAutoCommit;
}
void setJdbcAutoCommit(final boolean jdbcAutoCommit)
throws SQLException
{
synchronized (stateLock)
{
if (inManagedTransaction)
throw new SQLException("You cannot set autocommit during a managed transaction!");
this.jdbcAutoCommit = jdbcAutoCommit;
}
}
boolean isJdbcReadOnly()
{
return jdbcReadOnly;
}
void setJdbcReadOnly(final boolean readOnly)
throws SQLException
{
synchronized (stateLock)
{
if (inManagedTransaction)
throw new SQLException("You cannot set read only during a managed transaction!");
this.jdbcReadOnly = readOnly;
}
}
int getJdbcTransactionIsolation()
{
return jdbcTransactionIsolation;
}
void setJdbcTransactionIsolation(final int isolationLevel)
throws SQLException
{
synchronized (stateLock)
{
this.jdbcTransactionIsolation = isolationLevel;
con.setTransactionIsolation(jdbcTransactionIsolation);
}
}
void jdbcCommit() throws SQLException
{
synchronized (stateLock)
{
if (inManagedTransaction)
throw new SQLException("You cannot commit during a managed transaction!");
if (jdbcAutoCommit)
throw new SQLException("You cannot commit with autocommit set!");
}
con.commit();
}
void jdbcRollback() throws SQLException
{
synchronized (stateLock)
{
if (inManagedTransaction)
throw new SQLException("You cannot rollback during a managed transaction!");
if (jdbcAutoCommit)
throw new SQLException("You cannot rollback with autocommit set!");
}
con.rollback();
}
void jdbcRollback(Savepoint savepoint) throws SQLException
{
synchronized (stateLock)
{
if (inManagedTransaction)
throw new SQLException("You cannot rollback during a managed transaction!");
if (jdbcAutoCommit)
throw new SQLException("You cannot rollback with autocommit set!");
}
con.rollback(savepoint);
}
int getTrackStatements()
{
return mcf.trackStatements;
}
protected void checkException(SQLException e) throws ResourceException
{
connectionError(e);
throw new JBossResourceException("SQLException", e);
}
}