From: David J. <d_j...@us...> - 2001-05-25 16:21:51
|
Update of /cvsroot/firebird/client-java/src/org/firebirdsql/jca In directory usw-pr-cvs1:/tmp/cvs-serv12737 Modified Files: FBManagedConnection.java FBManagedConnectionFactory.java Added Files: FBXid.java TestFBManagedConnectionFactory.java Log Message: Implemented some ManagedConnectionFactory functionality and most XAResource functionality except recover (requires blob support) --- FBXid.java ADDED --- --- TestFBManagedConnectionFactory.java ADDED --- Index: FBManagedConnection.java =================================================================== RCS file: /cvsroot/firebird/client-java/src/org/firebirdsql/jca/FBManagedConnection.java,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -U3 -r1.1.1.1 -r1.2 --- FBManagedConnection.java 2001/05/09 14:28:29 1.1.1.1 +++ FBManagedConnection.java 2001/05/25 16:21:49 1.2 @@ -34,8 +34,17 @@ import javax.resource.spi.ConnectionEventListener; import javax.resource.spi.ConnectionRequestInfo; +import java.util.ArrayList; +import javax.transaction.xa.XAException; +import javax.transaction.xa.XAResource; +import javax.transaction.xa.Xid; + import javax.security.auth.Subject; +import org.firebirdsql.gds.isc_db_handle; +import org.firebirdsql.gds.isc_tr_handle; +import org.firebirdsql.gds.GDS; + /** * * @see <related> @@ -51,7 +60,28 @@ * <p> */ -public class FBManagedConnection implements ManagedConnection { +public class FBManagedConnection implements ManagedConnection, XAResource { + + private FBManagedConnectionFactory mcf; + + private ArrayList connectionEventListeners = new ArrayList(); + + private int timeout = 0; + + private Subject s; + + + private isc_tr_handle currentTr; + + private isc_db_handle currentDbHandle; + + FBManagedConnection(Subject s, FBManagedConnectionFactory mcf) { + this.mcf = mcf; + this.s = s; + } + + + //javax.resource.spi.ManagedConnection implementation /** @@ -136,6 +166,7 @@ /**<P> Add an event listener. */ public void addConnectionEventListener(ConnectionEventListener listener) { + connectionEventListeners.add(listener); } @@ -143,6 +174,7 @@ /**<P> Remove an event listener. */ public void removeConnectionEventListener(ConnectionEventListener listener) { + connectionEventListeners.remove(listener); } /**Used by the container to change the association of an application-level connection handle with a @@ -253,8 +285,172 @@ * @exception SQLException if a database-access error occurs */ public javax.transaction.xa.XAResource getXAResource() throws ResourceException { - throw new ResourceException("not yet implemented"); + return this; + } + + //-------------------------------------------------------------- + //XAResource implementation + //-------------------------------------------------------------- + + + /** + * Commits a transaction. + * @throws XAException + * Occurs when the state was not correct (end never called), the + * transaction ID is wrong, the connection was set to Auto-Commit, + * or the commit on the underlying connection fails. The error code + * differs depending on the exact situation. + */ + public void commit(Xid id, boolean twoPhase) throws XAException { + if (mcf.lookupXid(id) == null) { + throw new XAException("commit called with unknown transaction"); + } + if (mcf.lookupXid(id) == currentTr) { + throw new XAException("commit called with current xid"); + } + mcf.commit(id); + } + + /** + * Dissociates a resource from a global transaction. + * @throws XAException + * Occurs when the state was not correct (end called twice), or the + * transaction ID is wrong. + */ + //what do we do with flags????? + public void end(Xid id, int flags) throws javax.transaction.xa.XAException { + if (currentTr == null) { + throw new XAException("end called with no transaction associated"); + } + if (mcf.lookupXid(id) != currentTr) { + throw new XAException("end called with wrong xid"); + } + currentTr = null; } + + /** + * Indicates that no further action will be taken on behalf of this + * transaction (after a heuristic failure). It is assumed this will be + * called after a failed commit or rollback. + * @throws XAException + * Occurs when the state was not correct (end never called), or the + * transaction ID is wrong. + */ + public void forget(Xid id) throws javax.transaction.xa.XAException { + if (mcf.lookupXid(id) == null) { + throw new XAException("forget called with unknown transaction"); + } + if (mcf.lookupXid(id) == currentTr) { + throw new XAException("forget called with current xid"); + } + mcf.forgetXid(id); + } + + /** + * Gets the transaction timeout. + */ + public int getTransactionTimeout() throws javax.transaction.xa.XAException { + return timeout; + } + + public boolean isSameRM(XAResource res) throws javax.transaction.xa.XAException { + return (res instanceof FBManagedConnection) + && (mcf == ((FBManagedConnection)res).mcf); + } + + /** + * Prepares a transaction to commit. + * @throws XAException + * Occurs when the state was not correct (end never called), the + * transaction ID is wrong, or the connection was set to Auto-Commit. + */ + public int prepare(Xid id) throws javax.transaction.xa.XAException { + if (mcf.lookupXid(id) == null) { + throw new XAException("prepare called with unknown transaction"); + } + if (mcf.lookupXid(id) == currentTr) { + throw new XAException("prepare called with current xid"); + } + mcf.prepare(id); + return XA_OK; + } + + public Xid[] recover(int flag) throws javax.transaction.xa.XAException { +/* if(fbmc.getCurrentXid() == null) + return new Xid[0]; + else + return new Xid[]{fbmc.getCurrentXid()};*/ + return null; + } + + /** + * Rolls back the work, assuming it was done on behalf of the specified + * transaction. + * @throws XAException + * Occurs when the state was not correct (end never called), the + * transaction ID is wrong, the connection was set to Auto-Commit, + * or the rollback on the underlying connection fails. The error code + * differs depending on the exact situation. + */ + public void rollback(Xid id) throws javax.transaction.xa.XAException { + if (mcf.lookupXid(id) == null) { + throw new XAException("rollback called with unknown transaction"); + } + if (mcf.lookupXid(id) == currentTr) { + throw new XAException("rollback called with current xid"); + } + mcf.rollback(id); + } + + /** + * Sets the transaction timeout. This is saved, but the value is not used + * by the current implementation. + */ + public boolean setTransactionTimeout(int timeout) throws javax.transaction.xa.XAException { + this.timeout = timeout; + return true; + } + + /** + * Associates a JDBC connection with a global transaction. We assume that + * end will be called followed by prepare, commit, or rollback. + * If start is called after end but before commit or rollback, there is no + * way to distinguish work done by different transactions on the same + * connection). If start is called more than once before + * end, either it's a duplicate transaction ID or illegal transaction ID + * (since you can't have two transactions associated with one DB + * connection). + * @throws XAException + * Occurs when the state was not correct (start called twice), the + * transaction ID is wrong, or the instance has already been closed. + */ + public void start(Xid id, int flags) throws XAException { + if (currentTr != null) { + throw new XAException("start called with transaction associated"); + } + findIscTrHandle(id, flags); + } + + + //-------------------------------------------------------------------- + //package visibility + //-------------------------------------------------------------------- + + void findIscTrHandle(Xid xid, int flags) throws XAException { + currentTr = mcf.getCurrentIscTrHandle(xid, this, flags); + if (currentTr.getDbHandle() != currentDbHandle) { + mcf.returnDbHandle(currentDbHandle); + currentDbHandle = currentTr.getDbHandle(); + } + } + //temporarily public for testing + public isc_db_handle getIscDBHandle() throws XAException { + if (currentDbHandle == null) { + currentDbHandle = mcf.getDbHandle(); + } + return currentDbHandle; + } + Index: FBManagedConnectionFactory.java =================================================================== RCS file: /cvsroot/firebird/client-java/src/org/firebirdsql/jca/FBManagedConnectionFactory.java,v retrieving revision 1.1.1.1 retrieving revision 1.2 diff -U3 -r1.1.1.1 -r1.2 --- FBManagedConnectionFactory.java 2001/05/09 14:28:38 1.1.1.1 +++ FBManagedConnectionFactory.java 2001/05/25 16:21:49 1.2 @@ -34,7 +34,22 @@ import javax.resource.spi.ManagedConnectionFactory; import javax.security.auth.Subject; +import javax.transaction.xa.XAResource; + import java.util.Set; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.NoSuchElementException; + +import javax.transaction.xa.XAException; +import javax.transaction.xa.Xid; + +import org.firebirdsql.gds.isc_tr_handle; +import org.firebirdsql.gds.isc_db_handle; +import org.firebirdsql.gds.GDS; +import org.firebirdsql.gds.GDSException; +import org.firebirdsql.gds.Clumplet; +import org.firebirdsql.jgds.GDS_Impl; /** * @@ -54,6 +69,47 @@ public class FBManagedConnectionFactory implements ManagedConnectionFactory { + GDS gds = new GDS_Impl(); + + private String dbAlias; + + private LinkedList freeDbHandles = new LinkedList(); + + private HashMap xidMap = new HashMap(); //Maps supplied XID to internal transaction handle. + + private Clumplet dpbClumplet; + + private Set tpbSet; + + + public FBManagedConnectionFactory() {}; //Default constructor. + + //rar properties + + + public void setDatabase(String database) { + this.dbAlias = database; + } + + public String getDatabase() { + return dbAlias; + } + + public void setDpb(Clumplet dpb) { + dpbClumplet = dpb; + } + + public Clumplet getDpb() { + return dpbClumplet; + } + + public void setTpb(Set tpb) { + tpbSet = tpb; + } + + public Set getTpb() { + return tpbSet; + } /** Creates a Connection Factory instance. The Connection Factory instance gets initialized with @@ -116,7 +172,8 @@ public ManagedConnection createManagedConnection(Subject subject, ConnectionRequestInfo cxRequestInfo) throws ResourceException { - throw new ResourceException("not yet implemented"); + //ignore ConnectionRequestInfo till we think of something to use it for. + return new FBManagedConnection(subject, this); } @@ -234,9 +291,120 @@ public boolean equals(java.lang.Object other) { return false; } + + //needs synchronization! + isc_tr_handle lookupXid(Xid xid) { + return (isc_tr_handle) xidMap.get(xid); + } + + void forgetXid(Xid xid) { + xidMap.remove(xid); + } + + //needs synchronization! + //Also needs to use firebirds multi-db transactions! + //returns uninitialized tr_handle if first use. + isc_tr_handle getCurrentIscTrHandle(Xid xid, FBManagedConnection mc, int flags) throws XAException { + isc_tr_handle tr = lookupXid(xid); + if (tr == null) { + if (flags != XAResource.TMNOFLAGS) { + throw new XAException("Transaction flags wrong, this xid new for this rm"); + } + //new xid for us + isc_db_handle db = mc.getIscDBHandle(); + tr = gds.get_new_isc_tr_handle(); + try { + gds.isc_start_transaction(tr, db, getTpb()); + } + catch (GDSException ge) { + throw new XAException(ge.toString()); + } + xidMap.put(xid, tr); + } + else { + if (flags != XAResource.TMJOIN && flags != XAResource.TMRESUME) { + throw new XAException("Transaction flags wrong, this xid already known"); + } + } + return tr; + } - } + isc_db_handle getDbHandle() throws XAException { + try { + synchronized (freeDbHandles) { + return (isc_db_handle)freeDbHandles.removeLast(); + } + } catch (NoSuchElementException e) { + isc_db_handle db = gds.get_new_isc_db_handle(); + try { + gds.isc_attach_database(dbAlias, db, getDpb()); + } + catch (GDSException ge) { + throw new XAException(ge.toString()); + } + return db; + } + } + + synchronized void returnDbHandle(isc_db_handle db) { + freeDbHandles.addLast(db); + } + + void commit(Xid xid) throws XAException { + try { + gds.isc_commit_transaction(lookupXid(xid)); + } + catch (GDSException ge) { + throw new XAException(ge.toString()); + } + finally { + forgetXid(xid); + } + } + + void prepare(Xid xid) throws XAException { + try { + FBXid fbxid; + if (xid instanceof FBXid) { + fbxid = (FBXid)xid; + } + else { + fbxid = new FBXid(xid); + } +// gds.isc_prepare_transaction(lookupXid(xid)); + gds.isc_prepare_transaction2(lookupXid(xid), fbxid.toBytes()); +// gds.isc_prepare_transaction2s(lookupXid(xid), xid.toString()); + } + catch (GDSException ge) { + ge.printStackTrace(); + forgetXid(xid); + throw new XAException(ge.toString()); + } + } + + void rollback(Xid xid) throws XAException { + try { + gds.isc_rollback_transaction(lookupXid(xid)); + } + catch (GDSException ge) { + throw new XAException(ge.toString()); + } + finally { + forgetXid(xid); + } + } + + +/*public at the moment, at the top + private Clumplet getDpb() { + return dpbClumplet; + } + + private Set getTpb() { + return tpbSet; + }*/ +} |