Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   /*
    * Firebird Open Source J2ee connector - jdbc driver
    *
    * Distributable under LGPL license.
    * You may obtain a copy of the License at http://www.gnu.org/copyleft/lgpl.html
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * LGPL License for more details.
   *
   * This file was created by members of the firebird development team.
   * All individual contributions remain the Copyright (C) of those
   * individuals.  Contributors to this file are either listed here or
   * can be obtained from a CVS history command.
   *
   * All rights reserved.
   */
  package org.firebirdsql.jca;
  
  import java.sql.*;
  import java.util.*;
  
  
The class FBManagedConnection implements both the ManagedConnection and XAResource interfaces.

Author(s):
David Jencks
Version:
1.0
  
  
      public static final String WARNING_NO_CHARSET = "WARNING: No connection characterset specified (property lc_ctype, encoding, charSet or localEncoding), defaulting to characterset NONE";
  
      private static final Logger log = LoggerFactory.getLogger(FBManagedConnection.classfalse);
  
      private final FBManagedConnectionFactory mcf;
  
      private final ArrayList connectionEventListeners = new ArrayList();
      private final ArrayList connectionHandles = new ArrayList();
  
      private int timeout = 0;
  
      private final Map xidMap = Collections.synchronizedMap(new HashMap());
      
      private final GDS gds;
      private final IscDbHandle dbHandle;
      private GDSHelper gdsHelper;
  
      private final FBConnectionRequestInfo cri;
      private FBTpb tpb;
      private int transactionIsolation;
  
      private volatile boolean managedEnvironment = true;
      private volatile boolean connectionSharing = true;
      private final Set preparedXid = Collections.synchronizedSet(new HashSet());
      private volatile boolean inDistributedTransaction = false;
  
              FBManagedConnectionFactory mcfthrows ResourceException {
          
          this. = mcf;
          this. = mcf.getGDS();
          this. = getCombinedConnectionRequestInfo(subjectcri);
          this. = mcf.getDefaultTpb();
          this. = mcf.getDefaultTransactionIsolation();
          
          //TODO: XIDs in limbo should be loaded so that XAER_DUPID can be thrown appropriately
          
          try {
               = .createIscDbHandle();
  
              DatabaseParameterBuffer dpb = this..getDpb();
              if (dpb.getArgumentAsString(.) == null) {
                  if ( != null) {
                      .warn();
                  }
                  .addWarning(new GDSWarning());
              }
             
             if (!dpb.hasArgument(.) && DriverManager.getLoginTimeout() > 0) {
                 dpb.addArgument(., DriverManager.getLoginTimeout());
             }
             
             .iscAttachDatabase(mcf.getDatabase(), dpb);
             
              = new GDSHelper(dpbthis);
         } catch(GDSException ex) {
             throw new FBResourceException(ex);
         }
     }

    
Notify GDS container that error occured, if the ex represents a "fatal" one

 
     public void errorOccured(GDSException ex) {
         
         if ( != null.trace(ex.getMessage());
         
         if (!FatalGDSErrorHelper.isFatal(ex))
             return;
         
         ConnectionEvent event = new ConnectionEvent(
             FBManagedConnection.this
             .ex);
         
         FBManagedConnection.this.notify(
             event);
     }
 
     
             Subject subjectConnectionRequestInfo cri)
             throws ResourceException {
         if (cri == null) {
             cri = .getDefaultConnectionRequestInfo();
         }
         try {
             FBConnectionRequestInfo fbcri = (FBConnectionRequestInfocri;
             if (subject != null) {
                 // see connector spec, section 8.2.6, contract for
                 // ManagedConnectinFactory, option A.
                 for (Iterator i = subject.getPrivateCredentials().iterator(); i
                         .hasNext();) {
                     Object cred = i.next();
                     if (cred instanceof PasswordCredential
                             && .equals(((PasswordCredentialcred)
                                     .getManagedConnectionFactory())) {
                         PasswordCredential pcred = (PasswordCredentialcred;
                         String user = pcred.getUserName();
                         String password = new String(pcred.getPassword());
                         fbcri.setPassword(password);
                         fbcri.setUserName(user);
                         break;
                     } 
                 } 
             } 
     
             return fbcri;
         } catch (ClassCastException cce) {
             throw new FBResourceException(
                     "Incorrect ConnectionRequestInfo class supplied");
         }
     }
    
    
Get instance of org.firebirdsql.gds.impl.GDSHelper connected with this managed connection.

Returns:
instance of org.firebirdsql.gds.impl.GDSHelper.
Throws:
org.firebirdsql.gds.GDSException If this connection has no GDSHelper
 
     public GDSHelper getGDSHelper() throws GDSException {
         if ( == null)
             throw new GDSException(..);
         
         return ;
     }
     
     public String getDatabase() {
         return .getDatabase();
     }
 
     public boolean isManagedEnvironment() {
         return ;
     }
     
     public boolean inTransaction() {
         return  != null && .inTransaction();
     }
     
     public void setManagedEnvironment(boolean managedEnvironmentthrows ResourceException{
         this. = managedEnvironment;
         
         // if connection sharing is not enabled, notify currently associated
         // connection handle about the state change.
         if (!) {
             if (.size() > 1)
                 throw new javax.resource.spi.IllegalStateException(
                     "Multiple connections associated with this managed " +
                     "connection in non-sharing mode.");
             
             // there will be at most one connection.
             for (Iterator iter = .iterator(); iter.hasNext();) {
                 AbstractConnection connection = (AbstractConnectioniter.next();
 
                 try {
                     connection.setManagedEnvironment(managedEnvironment);
                 } catch(SQLException ex) {
                     throw new FBResourceException(ex);
                 }
             }
         }
     }
    
    
Check if connection sharing is enabled. When connection sharing is enabled, multiple connection handles (org.firebirdsql.jdbc.AbstractConnection instances) can access this managed connection in thread-safe manner (they synchronize on this instance). This feature can be enabled only in JCA environment, any other environment must not use connection sharing.

Returns:
true if connection sharing is enabled.
 
     public boolean isConnectionSharing() {
         return ;
     }
    
    
Enable or disable connection sharing. See isConnectionSharing() method for details.

Parameters:
connectionSharing true if connection sharing must be enabled.
Throws:
javax.resource.ResourceException If connection sharing state cannot be changed
 
     public void setConnectionSharing(boolean connectionSharingthrows ResourceException {
         if (!.isEmpty())
             throw new javax.resource.spi.IllegalStateException(
                 "Cannot change connection sharing with active connection handles.");
         
         this. = connectionSharing;
     }
    
Returns a javax.resource.spi.LocalTransaction instance. The LocalTransaction interface is used by the container to manage local transactions for a RM instance.

Returns:
LocalTransaction instance
Throws:
javax.resource.ResourceException generic exception if operation fails
javax.resource.NotSupportedException if the operation is not supported
javax.resource.spi.ResourceAdapterInternalException resource adapter internal error condition
 
         return new FBLocalTransaction(thisnull);
     }

    
Gets the metadata information for this connection's underlying EIS resource manager instance. The ManagedConnectionMetaData interface provides information about the underlying EIS instance associated with the ManagedConenction instance.

Returns:
ManagedConnectionMetaData instance
Throws:
javax.resource.ResourceException generic exception if operation fails
javax.resource.NotSupportedException if the operation is not supported
 
         return new FBManagedConnectionMetaData(this);
     }

    
Sets the log writer for this ManagedConnection instance.

The log writer is a character output stream to which all logging and tracing messages for this ManagedConnection instance will be printed. Application Server manages the association of output stream with the ManagedConnection instance based on the connection pooling requirements.

When a ManagedConnection object is initially created, the default log writer associated with this instance is obtained from the ManagedConnectionFactory. An application server can set a log writer specific to this ManagedConnection to log/trace this instance using setLogWriter method.

Parameters:
out Character Output stream to be associated
Throws:
javax.resource.ResourceException generic exception if operation fails
javax.resource.spi.ResourceAdapterInternalException resource adapter related error condition
 
     public void setLogWriter(PrintWriter out) {
         // ignore, we are using log4j.
     }

    
Gets the log writer for this ManagedConnection instance.

The log writer is a character output stream to which all logging and tracing messages for this ManagedConnection instance will be printed. ConnectionManager manages the association of output stream with the ManagedConnection instance based on the connection pooling requirements.

The Log writer associated with a ManagedConnection instance can be one set as default from the ManagedConnectionFactory (that created this connection) or one set specifically for this instance by the application server.

Returns:
Character ourput stream associated with this ManagedConnection
Throws:
javax.resource.ResourceException generic exception if operation fails
 
     public PrintWriter getLogWriter() {
         return null;// we are using log4j.
     }

    
Add an ConnectionEventListener listener. The listener will be notified when a ConnectionEvent occurs.

Parameters:
listener The ConnectionEventListener to be added
 
     public void addConnectionEventListener(ConnectionEventListener listener) {
         .add(listener);
     }

    
Remove a ConnectionEventListner from the listing of listeners that will be notified for a ConnectionEvent.

Parameters:
listener The ConnectionEventListener to be removed
 
     public void removeConnectionEventListener(ConnectionEventListener listener) {
         .remove(listener);
     }

    
Used by the container to change the association of an application-level connection handle with a ManagedConneciton instance. The container should find the right ManagedConnection instance and call the associateConnection method.

The resource adapter is required to implement the associateConnection method. The method implementation for a ManagedConnection should dissociate the connection handle (passed as a parameter) from its currently associated ManagedConnection and associate the new connection handle with itself.

Parameters:
connection Application-level connection handle
Throws:
javax.resource.ResourceException Failed to associate the connection handle with this ManagedConnection instance
javax.resource.spi.IllegalStateException Illegal state for invoking this method
javax.resource.spi.ResourceAdapterInternalException Resource adapter internal error condition
 
     public void associateConnection(Object connectionthrows ResourceException {
         if (!)
             disassociateConnections();
         
         try {
             ((AbstractConnectionconnection).setManagedConnection(this);
             .add(connection);
         } catch (ClassCastException cce) {
             throw new FBResourceException(
                     "invalid connection supplied to associateConnection."cce);
         }
     }

    
Application server calls this method to force any cleanup on the ManagedConnection instance.

The method javax.resource.spi.ManagedConnection.cleanup()initiates a cleanup of the any client-specific state as maintained by a ManagedConnection instance. The cleanup should invalidate all connection handles that had been created using this ManagedConnection instance. Any attempt by an application component to use the connection handle after cleanup of the underlying ManagedConnection should result in an exception.

The cleanup of ManagedConnection is always driven by an application server. An application server should not invoke javax.resource.spi.ManagedConnection.cleanup()when there is an uncompleted transaction (associated with a ManagedConnection instance) in progress.

The invocation of javax.resource.spi.ManagedConnection.cleanup()method on an already cleaned-up connection should not throw an exception. The cleanup of ManagedConnection instance resets its client specific state and prepares the connection to be put back in to a connection pool. The cleanup method should not cause resource adapter to close the physical pipe and reclaim system resources associated with the physical connection.

Throws:
javax.resource.ResourceException generic exception if operation fails
javax.resource.spi.ResourceAdapterInternalException resource adapter internal error condition
javax.resource.spi.IllegalStateException Illegal state for calling connection cleanup. Example - if a local transaction is in progress that doesn't allow connection cleanup
 
     public void cleanup() throws ResourceException {
         disassociateConnections();
 
         if ( != null)
             .setCurrentTrHandle(null);
 
         // reset the TPB from the previous transaction.
          = .getDefaultTpb();
     }

    
Disassociate connections from current managed connection.
 
     private void disassociateConnections() throws ResourceException {
         SQLExceptionChainBuilder chain = new SQLExceptionChainBuilder();
         
         // Iterate over copy of list as connection.close() will remove connection
         List connectionHandleCopy = new ArrayList();
         for (Iterator i = connectionHandleCopy.iterator(); i.hasNext();) {
             AbstractConnection connection = (AbstractConnectioni.next();
             
             try {
                 connection.close();
             } catch(SQLException sqlex) {
                 chain.append(sqlex);
             }
         }
         
         .clear();
         
         if (chain.hasException())
             throw new FBResourceException(chain.getException());
     }

    
Creates a new connection handle for the underlying physical connection represented by the ManagedConnection instance. This connection handle is used by the application code to refer to the underlying physical connection. A connection handle is tied to its ManagedConnection instance in a resource adapter implementation specific way.

The ManagedConnection uses the Subject and additional ConnectionRequestInfo (which is specific to resource adapter and opaque to application server) to set the state of the physical connection.

Parameters:
subject security context as JAAS subject
cri ConnectionRequestInfo instance
Returns:
generic Object instance representing the connection handle. For CCI, the connection handle created by a ManagedConnection instance is of the type javax.resource.cci.Connection.
Throws:
javax.resource.ResourceException generic exception if operation fails
javax.resource.spi.ResourceAdapterInternalException resource adapter internal error condition
javax.resource.spi.SecurityException security related error condition
javax.resource.spi.CommException failed communication with EIS instance
javax.resource.spi.EISSystemException internal error condition in EIS instance - used if EIS instance is involved in setting state of ManagedConnection
 
     public Object getConnection(Subject subjectConnectionRequestInfo cri)
             throws ResourceException {
         
         if (!matches(subjectcri))
             throw new FBResourceException("Incompatible subject or "
                     + "ConnectionRequestInfo in getConnection!");  
 
         if (!)
             disassociateConnections();
         
         AbstractConnection c = .newConnection(this);
         try {
             c.setManagedEnvironment(isManagedEnvironment());
             .add(c);
             return c;
         } catch(SQLException ex) {
             throw new FBResourceException(ex);
         }
     }

    
Destroys the physical connection to the underlying resource manager. To manage the size of the connection pool, an application server can explictly call javax.resource.spi.ManagedConnection.destroy()to destroy a physical connection. A resource adapter should destroy all allocated system resources for this ManagedConnection instance when the method destroy is called.

Throws:
javax.resource.ResourceException generic exception if operation failed
javax.resource.spi.IllegalStateException illegal state for destroying connection
 
     public void destroy() throws ResourceException {
         if ( == null)
             return;
         
         if (.inTransaction())
             throw new javax.resource.spi.IllegalStateException(
                 "Can't destroy managed connection  with active transaction");
         
         try {
             .detachDatabase();
         } catch (GDSException ge) {
             throw new FBResourceException("Can't detach from db."ge);
         } finally {
              = null;
         }
     }

    
Return an XA resource to the caller.

In both javax.sql.XAConnection and javax.resource.spi.MangagedConnection.

Returns:
the XAResource
 
     public XAResource getXAResource() {
         if ( != null)
             .debug("XAResource requested from FBManagedConnection");
         return this;
     }
 
     // --------------------------------------------------------------
     // XAResource implementation
     // --------------------------------------------------------------
 
     boolean isXidActive(Xid xid) {
         IscTrHandle trHandle = (IscTrHandle).get(xid); //mcf.getTrHandleForXid(xid);
 
         if (trHandle == nullreturn false;
 
         IscDbHandle dbHandle = trHandle.getDbHandle();
 
         if (dbHandle == nullreturn false;
 
         return dbHandle.isValid();
     }

    
Commits a transaction.

Throws:
javax.transaction.xa.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 idboolean onePhasethrows XAException {
         try {
             .notifyCommit(thisidonePhase);
         } catch (GDSException ge) {
             throw new XAException(ge.getXAErrorCode());
         }
     }

    
The internalCommit method performs the requested commit and may throw a GDSException to be interpreted by the caller.

Parameters:
xid a Xid value
onePhase a boolean value
Throws:
org.firebirdsql.gds.GDSException if an error occurs
 
     void internalCommit(Xid xidboolean onePhasethrows XAException,
             GDSException {
         if ( != null.trace("Commit called: " + xid);
         AbstractIscTrHandle committingTr = (AbstractIscTrHandle).get(xid);
         
         // check that prepare has NOT been called when onePhase = true
         if (onePhase && isPrepared(xid))
             throw new FBXAException("Cannot commit one-phase when transaction has been prepared".);
             
         // check that prepare has been called when onePhase = false
         if (!onePhase && !isPrepared(xid))
             throw new FBXAException("Cannot commit two-phase when transaction has not been prepared".);
         
         if (committingTr == null)
             throw new FBXAException("Commit called with unknown transaction",
                     .);
 
         if (committingTr == getGDSHelper().getCurrentTrHandle())
             throw new FBXAException("Commit called with non-ended xid",
                     .);
 
         try {
             committingTr.forgetResultSets();
             try {
                 getGDSHelper().commitTransaction(committingTr);
             } catch (GDSException ge) {
                 if ( != null) {
                     try {
                         .rollbackTransaction(committingTr);
                     } catch (GDSException ge2) {
                         if ( != null)
                             .debug("Exception rolling back failed tx: "ge2);
                     }
                 } else if ( != null) {
                     .warn("Unable to rollback failed tx, connection closed or lost");
                 }
                 throw ge;
             } finally {
                 .remove(xid);
                 .remove(xid);
             }
             
         } catch (GDSException ge) {
             ge.setXAErrorCode(.);
             throw ge;
         }
     }
 
     private boolean isPrepared(Xid xid) {
         return .contains(xid);
     }

    
Dissociates a resource from a global transaction.

Throws:
javax.transaction.xa.XAException Occurs when the state was not correct (end called twice), or the transaction ID is wrong.
 
     public void end(Xid idint flagsthrows XAException {
         if (flags != . && flags != . && flags != .)
             throw new FBXAException("flag not allowed in this context: " + flags + ", valid flags are TMSUCCESS, TMFAIL, TMSUSPEND".);
 
         internalEnd(idflags);
         .notifyEnd(thisid);
          = false;
 
         try {
             // This will reset the managed environment of the associated connections and set the transaction coordinator to local
             // TODO This is a bit of a hack; need to find a better way; this doesn't work with connectionSharing = true
             setManagedEnvironment(isManagedEnvironment());
         } catch (ResourceException ex) {
             throw new FBXAException("Reset of managed state failed".);
         }
     }

    
The internalEnd method ends the xid as requested if appropriate and throws a GDSException including the appropriate XA error code and a message if not. The caller can decode the exception as necessary.

Parameters:
xid a Xid value
flags an int value
Throws:
javax.transaction.xa.XAException if an error occurs
 
     void internalEnd(Xid xidint flagsthrows XAException {
         if ( != null.debug("End called: " + xid);
         IscTrHandle endingTr = (IscTrHandle).get(xid);
         
         if (endingTr == null)
             throw new FBXAException("Unrecognized transaction".);
 
         if (flags == .) {
             try {
                 .iscRollbackTransaction(endingTr);
                 getGDSHelper().setCurrentTrHandle(null);
             } catch (GDSException ex) {
                 throw new FBXAException("can't rollback transaction".ex);
             }
         }
         else if (flags == .) {
             if (endingTr == .getCurrentTrHandle())
                 .setCurrentTrHandle(null);
             else
                 throw new FBXAException("You are trying to end a transaction "
                         + "that is not the current transaction".);
         }
         else if (flags == .) {
             if (endingTr == .getCurrentTrHandle())
                 .setCurrentTrHandle(null);
             else 
                 throw new FBXAException("You are trying to suspend a transaction "
                         + "that is not the current transaction".);
             
         }
     }
     
     private final static String FORGET_FIND_QUERY = "SELECT RDB$TRANSACTION_ID, RDB$TRANSACTION_DESCRIPTION "
                                                   + "FROM RDB$TRANSACTIONS WHERE RDB$TRANSACTION_STATE IN (2, 3)";
     private final static String FORGET_DELETE_QUERY = "DELETE FROM RDB$TRANSACTIONS WHERE RDB$TRANSACTION_ID = ";

    
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:
javax.transaction.xa.XAException Occurs when the state was not correct (end never called), or the transaction ID is wrong.
 
     public void forget(Xid idthrows XAException {
         long inLimboId = -1;
 
         try {
             // find XID
             
             AbstractIscTrHandle trHandle2 = (AbstractIscTrHandle).createIscTrHandle();
             
             AbstractIscStmtHandle stmtHandle2 = (AbstractIscStmtHandle).createIscStmtHandle();
             .iscDsqlAllocateStatement(getGDSHelper().getCurrentDbHandle(), stmtHandle2);
             
             GDSHelper gdsHelper2 = new GDSHelper(getGDSHelper().getDatabaseParameterBuffer(), getGDSHelper().getCurrentDbHandle(), null);
             gdsHelper2.setCurrentTrHandle(trHandle2);
             
             gdsHelper2.prepareStatement(stmtHandle2false);
             gdsHelper2.executeStatement(stmtHandle2false);
             gdsHelper2.fetch(stmtHandle2, 10);
             
             DataProvider dataProvider0 = new DataProvider(stmtHandle2, 0);
             DataProvider dataProvider1 = new DataProvider(stmtHandle2, 1);
             
             FBField field0 = FBField.createField(stmtHandle2.getOutSqlda().[0], dataProvider0gdsHelper2false);
             FBField field1 = FBField.createField(stmtHandle2.getOutSqlda().[1], dataProvider1gdsHelper2false);
             
             field0.setConnection(gdsHelper2);
             field1.setConnection(gdsHelper2);
             
             int row = 0;
             while(row < stmtHandle2.getRows().length) {
             
                 if (stmtHandle2.getRows()[row] == null) {
                     row++;
                     continue;
                 }
                 
                 dataProvider0.setRow(row);
                 dataProvider1.setRow(row);
                 
                 long inLimboTxId = field0.getLong();
                 byte[] inLimboMessage = field1.getBytes();
             
                 try {
                     FBXid xid = new FBXid(new ByteArrayInputStream(inLimboMessage), inLimboTxId);
                     
                     boolean gtridEquals = Arrays.equals(xid.getGlobalTransactionId(), id.getGlobalTransactionId());
                     boolean bqualEquals = Arrays.equals(xid.getBranchQualifier(), id.getBranchQualifier());
                     
                     if (gtridEquals && bqualEquals) {
                         inLimboId = inLimboTxId;
                         break;
                     }
                 } catch(FBIncorrectXidException ex) {
                     if ( != null)
                         .warn("incorrect XID format in RDB$TRANSACTIONS where RDB$TRANSACTION_ID=" + inLimboTxIdex);
                 }
 
                 row++;
             }
 
             gdsHelper2.closeStatement(stmtHandle2true);
             .iscCommitTransaction(trHandle2);
 
         } catch (GDSException ex) {
             if ( != null)
                 .debug("can't perform query to fetch xids"ex);
             throw new FBXAException(.ex);
         } catch (SQLException ex) {
             if ( != null)
                 .debug("can't perform query to fetch xids"ex);
             throw new FBXAException(.ex);
         } catch (ResourceException ex) {
             if ( != null)
                 .debug("can't perform query to fetch xids"ex);
             throw new FBXAException(.ex);
         }
 
         
         if (inLimboId == -1)
             throw new FBXAException("XID not found".); // TODO: is XAER_NOTA the proper error code ?
             
         try {    
             // delete XID
 
             AbstractIscTrHandle trHandle2 = (AbstractIscTrHandle).createIscTrHandle();
             
             AbstractIscStmtHandle stmtHandle2 = (AbstractIscStmtHandle).createIscStmtHandle();
             .iscDsqlAllocateStatement(getGDSHelper().getCurrentDbHandle(), stmtHandle2);
 
             stmtHandle2 = (AbstractIscStmtHandle).createIscStmtHandle();
             .iscDsqlAllocateStatement(getGDSHelper().getCurrentDbHandle(), stmtHandle2);
             
             GDSHelper gdsHelper2 = new GDSHelper(getGDSHelper().getDatabaseParameterBuffer(), getGDSHelper().getCurrentDbHandle(), null);
             gdsHelper2.setCurrentTrHandle(trHandle2);
 
             gdsHelper2.prepareStatement(stmtHandle2 + inLimboIdfalse);
             gdsHelper2.executeStatement(stmtHandle2false);
 
             gdsHelper2.closeStatement(stmtHandle2true);
             .iscCommitTransaction(trHandle2);
             
         } catch (GDSException ex) {
             throw new FBXAException("can't perform query to fetch xids".ex);
         } catch (SQLException ex) {
             throw new FBXAException("can't perform query to fetch xids".ex);
         }
     }

    
Gets the transaction timeout.
 
     public int getTransactionTimeout() throws javax.transaction.xa.XAException {
         return ;
     }

    
Retrieve whether this FBManagedConnection uses the same ResourceManager as res. This method relies on res being a Firebird implementation of XAResource.

Parameters:
res The other XAResource to compare to
Returns:
true if res uses the same ResourceManager, false otherwise
 
     public boolean isSameRM(XAResource resthrows XAException {
         return (res instanceof FBManagedConnection)
                 && (.equals(((FBManagedConnectionres).));
     }

    
Prepares a transaction to commit.

Throws:
javax.transaction.xa.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 xidthrows javax.transaction.xa.XAException {
         try {
             return .notifyPrepare(thisxid);
         } catch (GDSException ge) {
             throw new FBXAException(.ge);
         }
     }
 
     int internalPrepare(Xid xidthrows FBXAExceptionGDSException {
         if ( != null.trace("prepare called: " + xid);
         AbstractIscTrHandle committingTr = (AbstractIscTrHandle).get(xid);
         if (committingTr == null)
             throw new FBXAException("Prepare called with unknown transaction",
                     .);
         if (committingTr == getGDSHelper().getCurrentTrHandle())
             throw new FBXAException("Prepare called with non-ended xid",
                     .);
         
         try {
             FBXid fbxid;
             if (xid instanceof FBXid) {
                 fbxid = (FBXidxid;
             } else {
                 fbxid = new FBXid(xid);
             }
             byte[] message = fbxid.toBytes();
             
             getGDSHelper().prepareTransaction(committingTrmessage);
         } catch (GDSException ge) {
             try {
                 if ( != null) {
                     .rollbackTransaction(committingTr);
                 } else if ( != null) {
                     .warn("Unable to rollback failed tx, connection closed or lost");
                 }
             } catch (GDSException ge2) {
                 if ( != null)
                     .debug("Exception rolling back failed tx: "ge2);
             } finally {
                 .remove(xid);
             } 
             
             if ( != null.warn("error in prepare"ge);
             throw ge;
         }
 
         .add(xid);
         return ;
     }
 
     private static final String RECOVERY_QUERY =
             "SELECT RDB$TRANSACTION_ID, RDB$TRANSACTION_DESCRIPTION "
             + "FROM RDB$TRANSACTIONS";

    
Obtain a list of prepared transaction branches from a resource manager. The transaction manager calls this method during recovery to obtain the list of transaction branches that are currently in prepared or heuristically completed states.

Parameters:
flags One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be used when no other flags are set in flags.
Returns:
The resource manager returns zero or more XIDs for the transaction branches that are currently in a prepared or heuristically completed state. If an error occurs during the operation, the resource manager should throw the appropriate XAException.
Throws:
javax.transaction.xa.XAException An error has occurred. Possible values are XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
 
     public Xid[] recover(int flagsthrows javax.transaction.xa.XAException {
         if (flags != . && flags != . && flags != . && flags != (.|.))
             throw new FBXAException("flag not allowed in this context: " + flags + ", valid flags are TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS, TMSTARTRSCAN|TMENDRSCAN".);
         
         try {
             // if (!((flags & XAResource.TMSTARTRSCAN) == 0))
 //            if ((flags & XAResource.TMENDRSCAN) == 0 && (flags & XAResource.TMNOFLAGS) == 0)
 //                return new Xid[0];
             
             ArrayList xids = new ArrayList();
             
             AbstractIscTrHandle trHandle2 = (AbstractIscTrHandle).createIscTrHandle();
             
             AbstractIscStmtHandle stmtHandle2 = (AbstractIscStmtHandle).createIscStmtHandle();
             .iscDsqlAllocateStatement(getGDSHelper().getCurrentDbHandle(), stmtHandle2);
             
             GDSHelper gdsHelper2 = new GDSHelper(getGDSHelper().getDatabaseParameterBuffer(), getGDSHelper().getCurrentDbHandle(), null);
             gdsHelper2.setCurrentTrHandle(trHandle2);
             
             gdsHelper2.prepareStatement(stmtHandle2false);
             gdsHelper2.executeStatement(stmtHandle2false);
             gdsHelper2.fetch(stmtHandle2, 10);
             
             DataProvider dataProvider0 = new DataProvider(stmtHandle2, 0);
             DataProvider dataProvider1 = new DataProvider(stmtHandle2, 1);
             
             FBField field0 = FBField.createField(stmtHandle2.getOutSqlda().[0], dataProvider0gdsHelper2false);
             FBField field1 = FBField.createField(stmtHandle2.getOutSqlda().[1], dataProvider1gdsHelper2false);
             
             field0.setConnection(gdsHelper2);
             field1.setConnection(gdsHelper2);
             
             int row = 0;
             while(row < stmtHandle2.getRows().length) {
                 if (stmtHandle2.getRows()[row] == null) {
                     row++;
                     continue;
                 }
                 
                 dataProvider0.setRow(row);
                 dataProvider1.setRow(row);
                 
                 long inLimboTxId = field0.getLong();
                 byte[] inLimboMessage = field1.getBytes();
             
                 try {
                     FBXid xid = new FBXid(new ByteArrayInputStream(inLimboMessage), inLimboTxId);
                     xids.add(xid);
                 } catch(FBIncorrectXidException ex) {
                     if ( != null)
                         .warn("ignoring XID stored with invalid format in RDB$TRANSACTIONS for RDB$TRANSACTION_ID=" + inLimboTxId);
                 }
     
                 row++;
            }
    
            gdsHelper2.closeStatement(stmtHandle2true);
            .iscCommitTransaction(trHandle2);
            
            return (FBXid[])xids.toArray(new FBXid[xids.size()]);
        } catch(GDSException ex) {
            throw new FBXAException("can't perform query to fetch xids".ex);
        } catch (SQLException sqle) {
            throw new FBXAException("can't perform query to fetch xids".sqle);
        } catch (ResourceException re) {
            throw new FBXAException("can't perform query to fetch xids".re);
        } 
    }
    private static final String RECOVERY_QUERY_PARAMETRIZED =
            "SELECT RDB$TRANSACTION_ID, RDB$TRANSACTION_DESCRIPTION "
            + "FROM RDB$TRANSACTIONS "
            + "WHERE RDB$TRANSACTION_DESCRIPTION = CAST(? AS VARCHAR(32764) CHARACTER SET OCTETS)";

    
Obtain a single prepared transaction branch from a resource manager, based on a Xid

Parameters:
externalXid The Xid to find
Returns:
The Xid if found, otherwise null.
Throws:
javax.transaction.xa.XAException An error has occurred. Possible values are XAER_RMERR, XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
    protected Xid findSingleXid(Xid externalXidthrows javax.transaction.xa.XAException {
        try {
            AbstractIscTrHandle trHandle2 = (AbstractIscTrHandle).createIscTrHandle();
            AbstractIscStmtHandle stmtHandle2 = (AbstractIscStmtHandle).createIscStmtHandle();
            .iscDsqlAllocateStatement(getGDSHelper().getCurrentDbHandle(), stmtHandle2);
            GDSHelper gdsHelper2 = new GDSHelper(getGDSHelper().getDatabaseParameterBuffer(), getGDSHelper().getCurrentDbHandle(), null);
            gdsHelper2.setCurrentTrHandle(trHandle2);
            gdsHelper2.prepareStatement(stmtHandle2true);
            FBXid tempXid = new FBXid(externalXid);
            stmtHandle2.getInSqlda().[0]. = tempXid.toBytes();
            gdsHelper2.executeStatement(stmtHandle2false);
            gdsHelper2.fetch(stmtHandle2, 1);
            DataProvider dataProvider0 = new DataProvider(stmtHandle2, 0);
            DataProvider dataProvider1 = new DataProvider(stmtHandle2, 1);
            FBField field0 = FBField.createField(stmtHandle2.getOutSqlda().[0], dataProvider0gdsHelper2false);
            FBField field1 = FBField.createField(stmtHandle2.getOutSqlda().[1], dataProvider1gdsHelper2false);
            field0.setConnection(gdsHelper2);
            field1.setConnection(gdsHelper2);
            FBXid xid = null;
            if (stmtHandle2.getRows().length > 0) {
                dataProvider0.setRow(0);
                dataProvider1.setRow(0);
                long inLimboTxId = field0.getLong();
                byte[] inLimboMessage = field1.getBytes();
                try {
                    xid = new FBXid(new ByteArrayInputStream(inLimboMessage), inLimboTxId);
                } catch(FBIncorrectXidException ex) {
                    if ( != null)
                        .warn("ignoring XID stored with invalid format in RDB$TRANSACTIONS for RDB$TRANSACTION_ID=" + inLimboTxId);
                }
            }
            gdsHelper2.closeStatement(stmtHandle2true);
            .iscCommitTransaction(trHandle2);
            return xid;
        } catch(GDSException ex) {
            throw new FBXAException("can't perform query to fetch xids".ex);
        } catch (SQLException sqle) {
            throw new FBXAException("can't perform query to fetch xids".sqle);
        } catch (ResourceException re) {
            throw new FBXAException("can't perform query to fetch xids".re);
        }
    }
    private static class DataProvider implements FieldDataProvider {
        private AbstractIscStmtHandle stmtHandle;
        private int fieldPos;
        private int row;
        
        private DataProvider(AbstractIscStmtHandle stmtHandleint fieldPos) {
            this. = stmtHandle;
            this. = fieldPos;
        }
        
        public void setRow(int row) {
            this. = row;
        }
        
        public byte[] getFieldData() {
            return ((byte[][]).getRows()[])[];
        }
        public void setFieldData(byte[] data) {
            throw new UnsupportedOperationException();
        }
    }
    
    
Rolls back the work, assuming it was done on behalf of the specified transaction.

Throws:
javax.transaction.xa.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 xidthrows XAException {
        try {
            .notifyRollback(thisxid);
        } catch (GDSException ge) {
            throw new FBXAException(ge.getXAErrorCode(), ge);
        }
    }
    void internalRollback(Xid xidthrows XAExceptionGDSException {
        if ( != null.trace("rollback called: " + xid);
        AbstractIscTrHandle committingTr = (AbstractIscTrHandle).get(xid); //mcf.getTrHandleForXid(id);
        if (committingTr == null) {
            throw new FBXAException ("Rollback called with unknown transaction: " + xid);
        }
        if (committingTr == getGDSHelper().getCurrentTrHandle())
            throw new FBXAException("Rollback called with non-ended xid",
                    .);
        try {
            committingTr.forgetResultSets();
            try {
                getGDSHelper().rollbackTransaction(committingTr);
            } finally {
                .remove(xid);
                .remove(xid);
            }
        } catch (GDSException ge) {
            if ( != null.debug("Exception in rollback"ge);
            ge.setXAErrorCode(.);
            throw ge;
        }
    }

    
Sets the transaction timeout. This is saved, but the value is not used by the current implementation.

Parameters:
timeout The timeout to be set in seconds
    public boolean setTransactionTimeout(int timeout)
            throws javax.transaction.xa.XAException {
        this. = timeout;
        return true;
    }
    
    public boolean inDistributedTransaction() {
        return ;
    }

    
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).

Parameters:
id A global transaction identifier to be associated with the resource
flags One of TMNOFLAGS, TMJOIN, or TMRESUME
Throws:
javax.transaction.xa.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 idint flagsthrows XAException {
        if (flags != . && flags != . && flags != .)
            throw new FBXAException("flag not allowed in this context: " + flags + ", valid flags are TMNOFLAGS, TMJOIN, TMRESUME".);
        if (flags == .)
            throw new FBXAException("Joining two transactions is not supported".);
        
        try {
            // reset the transaction parameters for the managed scenario 
            
            internalStart(idflags);
            
            .notifyStart(thisid);
            
             = true;
            // This will reset the managed environment of the associated connections and set the transaction coordinator to managed
            // TODO This is a bit of a hack; need to find a better way; this doesn't work with connectionSharing = true
            
        } catch (GDSException ge) {
            throw new FBXAException(ge.getXAErrorCode());
        } catch(ResourceException ex) {
            throw new FBXAException(.ex);
        }
    }

    
Perform the internal processing to start associate a JDBC connection with a global transaction.

Parameters:
id A global transaction identifier to be associated with the resource
flags One of TMNOFLAGS, TMJOIN, or TMRESUME
Throws:
javax.transaction.xa.XAException If the transaction is already started, or this connection cannot participate in the distributed transaction
org.firebirdsql.gds.GDSException
See also:
start(javax.transaction.xa.Xid,int)
    public void internalStart(Xid idint flagsthrows XAExceptionGDSException {
        if ( != null.trace("start called: " + id);
        if (getGDSHelper().getCurrentTrHandle() != null)
            throw new FBXAException("Transaction already started".);
        findIscTrHandle(idflags);
    }
    // FB public methods. Could be package if packages reorganized.

    
Close this connection with regards to a wrapping AbstractConnection.

Parameters:
c The AbstractConnection that is being closed
    public void close(AbstractConnection c) {
        c.setManagedConnection(null);
        .remove(c);
        ConnectionEvent ce = new ConnectionEvent(this,
                .null);
        ce.setConnectionHandle(c);
        notify(ce);
    }

    
Get information about the current connection parameters.

Returns:
instance of FBConnectionRequestInfo.
        return ;
    }
        return .getTransactionParameterBuffer();
    }
    
    public void setTransactionParameters(TransactionParameterBuffer transactionParameters) {
        .setTransactionParameterBuffer(transactionParameters);
    }
    
    public TransactionParameterBuffer getTransactionParameters(int isolation) {
        return .getTransactionParameters(isolation);
    }
    
    public void setTransactionParameters(int isolationTransactionParameterBuffer transactionParams) {
        .setTransactionParameters(isolationtransactionParams);
    }
    
    // --------------------------------------------------------------------
    // package visibility
    // --------------------------------------------------------------------
    private void findIscTrHandle(Xid xidint flagsthrows GDSExceptionXAException {
        // FIXME return old tr handle if it is still valid before proceeding
        getGDSHelper().setCurrentTrHandle(null);
        
        if (flags == .) {
            AbstractIscTrHandle trHandle = (AbstractIscTrHandle.get(xid);
            if (trHandle == null) {
                throw new FBXAException(
                        "You are trying to resume a transaction that is not attached to this XAResource",
                        .);
            }
            
            getGDSHelper().setCurrentTrHandle(trHandle);
            return;
        }
        
        Iterator it = .keySet().iterator();
        while (it.hasNext()) {
            Xid knownXid = (Xidit.next();
            boolean sameFormatId = knownXid.getFormatId() == xid.getFormatId();
            boolean sameGtrid = Arrays.equals(knownXid.getGlobalTransactionId(), xid.getGlobalTransactionId());
            boolean sameBqual = Arrays.equals(knownXid.getBranchQualifier(), xid.getBranchQualifier());
            if (sameFormatId && sameGtrid && sameBqual)
                throw new FBXAException(
                        "A transaction with the same XID has already been started",
                        .);
        }
        
        // new xid for us
        AbstractIscTrHandle trHandle = getGDSHelper().