📄 xaconnectionimpl.java
字号:
/*
* Licensed under the X license (see http://www.x.org/terms.htm)
*/
package org.ofbiz.minerva.pool.jdbc.xa.wrapper;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Vector;
import javax.sql.ConnectionEvent;
import javax.sql.ConnectionEventListener;
import javax.sql.XAConnection;
import javax.transaction.xa.XAResource;
import org.ofbiz.minerva.pool.PoolEvent;
import org.ofbiz.minerva.pool.PoolEventListener;
import org.ofbiz.minerva.pool.PooledObject;
import org.ofbiz.minerva.pool.cache.ObjectCache;
import org.ofbiz.minerva.pool.jdbc.ConnectionInPool;
/**
* A transaction wrapper around a java.sql.Connection. This provides access to
* an XAResource (there is a one-to-one mapping between XAResource and
* XAConnection) and a java.sql.Connection (in this implementation, there is
* also a one-to-one mapping between XAConnection and java.sql.Connection).
* In order to pool java.sql.Connections in a transactional environment, this
* is the class that should be pooled - though you could pool the connections,
* there is no need to create and destroy these wrappers so frequently.
*
* <P>Note that there con only be one transaction at a time accessing one of
* these wrappers, and requests to a pool for multiple connections on behalf of
* one transaction should use the same wrapper. This is because there is no
* distinction between connections and transactions in a java.sql.Connection,
* and work done by one connection on behalf of a transaction would not be
* visible to another connection working on behalf of the same transaction - you
* would have effectively created two transactions.</P>
*
* <P>This also implies that an XAConnection should not be released to a
* connection pool until the work has been committed or rolled back. However,
* it must sent the close notification as usual in order to be delisted from
* the transaction. So the ConnectionEventListener must not release the
* XAConnection to a pool when it receives the close event. Instead, it should
* also register a TransactionListener that will be notified when the
* Transaction is finished, and release the XAConnection at that time.</P>
* @see org.ofbiz.minerva.pool.jdbc.xa.wrapper.TransactionListener
*
* @author Aaron Mulder (ammulder@alumni.princeton.edu)
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
*
* REVISIONS:
* 20010703 bill added code for transaction isolation
*/
public class XAConnectionImpl implements XAConnection, PooledObject {
private final static String CLOSED = "Connection has been closed!";
private Connection con;
private XAResourceImpl resource;
private Vector listeners, poolListeners;
private ArrayList clientConnections;
private TransactionListener transListener;
private int preparedStatementCacheSize = 0;
private int clientConnectionCount = 0;
/** The JDBC user name used to open an underlying connection */
private String user;
/** The JDBC password used to open an underlying connection */
private String password;
private boolean saveStackTrace;
/**
* Creates a new transactional wrapper.
* @param con The underlying non-transactional Connection.
* @param resource The transaction resource used to enlist this
* connection in a transaction.
*/
public XAConnectionImpl(Connection con, XAResourceImpl resource, boolean saveStackTrace) {
this.con = con;
this.resource = resource;
listeners = new Vector();
poolListeners = new Vector();
clientConnections = new ArrayList();
this.saveStackTrace = saveStackTrace;
}
/**
* Sets the transaction listener.
*/
public void setTransactionListener(TransactionListener tl) {
transListener = tl;
}
/**
* Clears the transaction listener.
*/
public void clearTransactionListener() {
transListener = null;
}
/**
* Sets the number of PreparedStatements to be cached for each
* Connection. Your DB product may impose a limit on the number
* of open PreparedStatements.
*/
public void setPSCacheSize(int maxSize) {
preparedStatementCacheSize = maxSize;
}
/**
* Gets the number of PreparedStatements to be cached for each
* Connection.
*/
public int getPSCacheSize() {
return preparedStatementCacheSize;
}
public void setTransactionIsolation(int iso) throws SQLException {
con.setTransactionIsolation(iso);
}
/**
* Shuts down this wrapper (and the underlying Connection) permanently.
*/
public void close() {
try {
con.close();
} catch (SQLException e) {
}
ObjectCache cache = (ObjectCache) ConnectionInPool.psCaches.remove(con);
if (cache != null)
cache.close();
con = null;
resource = null;
listeners.clear();
listeners = null;
}
/**
* Indicates that the connection given to the client has been closed.
* If there is currently a transaction, this object should not be closed or
* returned to a pool. If not, it can be closed or returned immediately.
*/
public void clientConnectionClosed(XAClientConnection clientCon) {
synchronized(clientConnections) {
clientConnections.remove(clientCon);
}
if (clientConnections.size() > 0)
return; // Only take action if the last connection referring to this is closed
boolean trans = resource.isTransaction(); // could be committed directly on notification? Seems unlikely, but let's not rule it out.
Vector local = (Vector) listeners.clone();
for (int i = local.size() - 1; i >= 0; i--)
((ConnectionEventListener) local.elementAt(i)).connectionClosed(new ConnectionEvent(this));
// if(!trans)
// transactionFinished();
}
/**
* Indicates that the outstanding transaction has finished and this object
* can be closed or returned to a pool. This dispatches a close event to
* all listeners.
* @see #addConnectionEventListener
*/
public void transactionFinished() {
if (transListener != null)
transListener.transactionFinished(this);
}
/**
* Indicates that the outstanding transaction has finished with a fatal
* error, and this object should be closed or permanently removed from a
* pool. This dispatches a close event to all listeners.
* @see #addConnectionEventListener
*/
public void transactionFailed() {
if (transListener != null)
transListener.transactionFailed(this);
}
/**
* Indicates that the connection given to the client has had an error.
* If there is currently a transaction, this object should not be closed or
* returned to a pool. If not, it can be closed or returned immediately.
*/
public void setConnectionError(SQLException e) {
Vector local = (Vector) listeners.clone();
for (int i = local.size() - 1; i >= 0; i--) {
try {
((ConnectionEventListener) local.elementAt(i)).connectionErrorOccurred(new ConnectionEvent(this, e));
} catch (RuntimeException ex) {
// there can be thrown an induced exception,
// but we must report to client the original one, right?
ex.printStackTrace();
}
}
}
/**
* Rolls back the underlying connection. This is used when there is no
* current transaction and the connection is returned to the pool - since
* no transaction will be committed or rolled back but this connection
* will be reused, we must roll it back. This is only done if autocommit is
* false.
*/
public void rollback() throws SQLException {
if (con.getAutoCommit() == false)
con.rollback();
}
// ---- Implementation of javax.sql.XAConnection ----
public XAResource getXAResource() {
return resource;
}
public void addConnectionEventListener(ConnectionEventListener listener) {
listeners.addElement(listener);
}
public void removeConnectionEventListener(ConnectionEventListener listener) {
if (!listeners.remove(listener))
throw new IllegalArgumentException();
}
public Connection getConnection() {
XAClientConnection xaCon;
synchronized (clientConnections) {
xaCon = new XAClientConnection(this, con, saveStackTrace);
xaCon.setPSCacheSize(preparedStatementCacheSize);
clientConnections.add(xaCon);
}
return xaCon;
}
// ---- Implementation of javax.sql.XAConnection ----
public void addPoolEventListener(PoolEventListener listener) {
poolListeners.addElement(listener);
}
public void removePoolEventListener(PoolEventListener listener) {
poolListeners.removeElement(listener);
}
/**
* Dispatches an event to the pool event listeners.
*/
void firePoolEvent(PoolEvent evt) {
Vector local = (Vector) poolListeners.clone();
for (int i = local.size() - 1; i >= 0; i--)
if (evt.getType() == PoolEvent.OBJECT_CLOSED)
((PoolEventListener) local.elementAt(i)).objectClosed(evt);
else if (evt.getType() == PoolEvent.OBJECT_ERROR)
((PoolEventListener) local.elementAt(i)).objectError(evt);
else
((PoolEventListener) local.elementAt(i)).objectUsed(evt);
}
/** Getter for property password.
* @return Value of property password.
*/
public java.lang.String getPassword() {
return password;
}
/** Setter for property password.
* @param password New value of property password.
*/
public void setPassword(java.lang.String password) {
this.password = password;
}
/** Getter for property user.
* @return Value of property user.
*/
public java.lang.String getUser() {
return user;
}
/** Setter for property user.
* @param user New value of property user.
*/
public void setUser(java.lang.String user) {
this.user = user;
}
public void forceClientConnectionsClose() {
for (int i = 0; i < clientConnections.size(); i++) {
XAClientConnection client = (XAClientConnection) clientConnections.get(i);
try {
client.forcedClose();
} catch (SQLException ignored) {
}
}
clientConnections.clear();
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -