All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.microsoft.sqlserver.jdbc.SQLServerXAResource Maven / Gradle / Ivy

There is a newer version: 12.8.1.jre11
Show newest version
//---------------------------------------------------------------------------------------------------------------------------------
// File: SQLServerXAResource.java
//
//
// Microsoft JDBC Driver for SQL Server
// Copyright(c) Microsoft Corporation
// All rights reserved.
// MIT License
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""), 
//  to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//  and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
//  IN THE SOFTWARE.
//---------------------------------------------------------------------------------------------------------------------------------
 

package com.microsoft.sqlserver.jdbc;
import java.sql.*;

import javax.transaction.xa.*;

import java.util.Vector;
import java.util.Properties;
import java.util.logging.*;
import java.text.MessageFormat;

/**
* Transaction id implementation used to recover transactions.
*/

final class XidImpl implements Xid {
  private final int formatId;
  private final byte gtrid[];
  private final byte bqual[];
  private final String traceID;

  /* XA Flags
   public static final int TMENDRSCAN = 8388608;
   public static final int TMFAIL = 536870912;
   public static final int TMJOIN = 2097152;
   public static final int TMNOFLAGS = 0;
   public static final int TMONEPHASE = 1073741824;
   public static final int TMRESUME = 134217728;
   public static final int TMSTARTRSCAN = 16777216;
   public static final int TMSUCCESS = 67108864;
   public static final int TMSUSPEND = 33554432;
   public static final int XA_RDONLY = 3;
   public static final int XA_OK = 0;
   */

  /**
   * Create a new XID
   * @param formatId format id
   * @param gtrid global id
   * @param bqual branch id
   */
  /*L0*/ public XidImpl(int formatId, byte gtrid[], byte bqual[]) {
    this.formatId = formatId;
    this.gtrid = gtrid;
    this.bqual = bqual;
    traceID = " XID:" +  xidDisplay(this);
  }

  /*L0*/ public byte[] getGlobalTransactionId() {
    return gtrid;
  }

  /*L0*/  public byte[] getBranchQualifier() {
    return bqual;
  }

  /*L0*/ public int getFormatId() {
    return formatId;
  }
	public String toString() 
	{
		 return traceID;
	}

	// Returns displayable representation of xid for logging purposes.
	static String xidDisplay(Xid xid)
	{
		if (null == xid) return "(null)";
			StringBuilder sb = new StringBuilder(300);
		sb.append("formatId=");
		sb.append(xid.getFormatId());
		sb.append(" gtrid=");
		sb.append(Util.byteToHexDisplayString(xid.getGlobalTransactionId()));
		sb.append(" bqual=");
		sb.append(Util.byteToHexDisplayString(xid.getBranchQualifier()));
		return sb.toString();
	}
  
}

final class XAReturnValue {
  int nStatus;
  byte bData[];
}

/**
SQLServerXAResource provides an XAResource for XA distributed transaction management.
XA transactions are implemented over SQL Server using Microsoft Distributed Transaction
Manager (DTC). SQLServerXAResource makes calls to a SQL Server extended dll called SQLServer_XA.dll which
interfaces with DTC.

XA calls received by SQLServerXAResource (XA_START, XA_END, XA_PREPARE etc) are mapped to the
corresponding calls to DTC functions.

SQLServerXAResource may also be configured not to use DTC. In this case distributed transactions
are simply implemented as local transactions.
*/

public final class SQLServerXAResource implements javax.transaction.xa.XAResource{
	/*	In the Java transaction API doc a 'resource manager' appears to be (for JDBC) a 'particular
	DBMS server that participates in distributed transaction'. More accurately an instance of a
	connection to a database since commit/rollback is done at the DB connection level.
	A resource adapter is the implementation below */

	/*	In the JDBC XA spec the 'middle tier server' is the application server. We assume that this module
	implements the pooling of connections since it must also pass the XAResouce obtained when a connection
	is handed to an application to the transaction manager. IE JPoolingDataSource is not used - the
	JConnectionPoolDataSource and JPoolied connections are managed for pooling by the app server. */

	/*	Examples http://oradoc.photo.net/ora816/java.816/a81354/xadistr1.htm#1064452 */

	/*	Note that EJB componenents performing getConnection() may be using the same XAConnection/XAResource
	since it is a pooled connection */

	private int timeoutSeconds;

	final static int XA_START		= 0;
	final static int XA_END			= 1;
	final static int XA_PREPARE		= 2;
	final static int XA_COMMIT		= 3;
	final static int XA_ROLLBACK	= 4;
	final static int XA_FORGET		= 5;
	final static int XA_RECOVER		= 6;
    final static int XA_PREPARE_EX  = 7;
    final static int XA_ROLLBACK_EX = 8;
    final static int XA_FORGET_EX   = 9;
    final static int XA_INIT        = 10;

            

	private SQLServerConnection controlConnection;
    private SQLServerConnection con; // original connection
    
    private boolean serverInfoRetrieved;
	private String version, instanceName;
	private int ArchitectureMSSQL, ArchitectureOS;
        
	private static boolean xaInitDone;
	private static final Object xaInitLock;
	private String sResourceManagerId;
	private int enlistedTransactionCount;
	final private Logger xaLogger;
	static private int baseResourceID = 0;	// Unique id generator for each  instance (used for logging).
    private int tightlyCoupled = 0;
    private int isTransacrionTimeoutSet = 0;	// set to 1 if setTransactionTimeout() is called
    
    public static final int SSTRANSTIGHTLYCPLD = 0x8000;
    private SQLServerCallableStatement[] xaStatements = {null,null,null,null,null,null,null, null,null,null};
    private final String  traceID;
    

	static 
    {
	    xaInitLock = new Object();
	}
	
	public String toString() 
	{
		 return traceID;
	}

	/*L0*/ SQLServerXAResource(SQLServerConnection original, SQLServerConnection control, String loginfo)
	{
            traceID = " XAResourceID:"  + nextResourceID();
            // Grab SQLServerXADataSource's static XA logger instance.
            xaLogger = SQLServerXADataSource.xaLogger;
            controlConnection = control;
            con =original;
            Properties p = original.activeConnectionProperties;
            if (p==null)
                sResourceManagerId = "";
            else 
            {
                sResourceManagerId = p.getProperty(SQLServerDriverStringProperty.SERVER_NAME.toString())+"."+
                p.getProperty(SQLServerDriverStringProperty.DATABASE_NAME.toString())+"."+
                p.getProperty(SQLServerDriverIntProperty.PORT_NUMBER.toString());
            }
            if (xaLogger.isLoggable(Level.FINE))
                xaLogger.fine(toString() + " created by (" + loginfo+ ")");
            
            // Information about the server, needed for XA timeout logic in the DLL. 
            serverInfoRetrieved = false;
    		version = "0";
    		instanceName = "";
    		ArchitectureMSSQL = 0;
    		ArchitectureOS = 0;
            
	}

    private synchronized SQLServerCallableStatement getXACallableStatementHandle(int number)  throws SQLServerException
    {
        assert number >= XA_START && number<=XA_FORGET_EX;
        assert number < xaStatements.length;
        if(null != xaStatements[number])
           return xaStatements[number];

        CallableStatement CS = null;

        switch (number)
        {
        case SQLServerXAResource.XA_START:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_start(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}");
            break;
        case   SQLServerXAResource.XA_END:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_end(?, ?, ?, ?, ?, ?, ?)}");
            break;
        case SQLServerXAResource.XA_PREPARE:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_prepare(?, ?, ?, ?, ?)}");
            break;
        case SQLServerXAResource.XA_COMMIT:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_commit(?, ?, ?, ?, ?, ?)}");
            break;
        case SQLServerXAResource.XA_ROLLBACK:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_rollback(?, ?, ?, ?, ?)}");
            break;
        case SQLServerXAResource.XA_FORGET:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_forget(?, ?, ?, ?, ?)}");
            break;
        case SQLServerXAResource.XA_RECOVER:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_recover(?, ?, ?, ?)}");
            break;
        case SQLServerXAResource.XA_PREPARE_EX:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_prepare_ex(?, ?, ?, ?, ?, ?)}");
            break;
        case SQLServerXAResource.XA_ROLLBACK_EX:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_rollback_ex(?, ?, ?, ?, ?, ?)}");
            break;  
        case SQLServerXAResource.XA_FORGET_EX:
            CS = controlConnection.prepareCall("{call master..xp_sqljdbc_xa_forget_ex(?, ?, ?, ?, ?, ?)}");
            break;
        default:
            assert false: "Bad handle request:"+ number;
            break;
        }

        xaStatements[number] = (SQLServerCallableStatement)CS;
        return xaStatements[number];
    }
    private synchronized void closeXAStatements() throws SQLServerException
    {
        for(int i=0; i0) sb.append("|");
			sb.append("TMFAIL");
		}
		if (0 != (TMJOIN & flags))
		{
			if (sb.length()>0) sb.append("|");
				sb.append("TMJOIN");
		}
		if (0 != (TMONEPHASE & flags))
		{
			if (sb.length()>0) sb.append("|");
				sb.append("TMONEPHASE");
		}
		if (0 != (TMRESUME & flags))
		{
			if (sb.length()>0) sb.append("|");
				sb.append("TMRESUME");
		}
		if (0 != (TMSTARTRSCAN & flags))
		{
			if (sb.length()>0) sb.append("|");
				sb.append("TMSTARTRSCAN");
		}
		if (0 != (TMSUCCESS & flags))
		{
			if (sb.length()>0) sb.append("|");
				sb.append("TMSUCCESS");
		}
		if (0 != (TMSUSPEND & flags))
		{
			if (sb.length()>0) sb.append("|");
				sb.append("TMSUSPEND");
		}

              if (0 != (SSTRANSTIGHTLYCPLD & flags))
		{
			if (sb.length()>0) sb.append("|");
				sb.append("SSTRANSTIGHTLYCPLD");
		}  
		return sb.toString();
	}

	// Returns displayable representation of XID cookie for logging purposes.
	private String cookieDisplay(byte [] cookie)
	{
		return Util.byteToHexDisplayString(cookie);
	}

	// Returns displayable representation of XA type flag.
	private String typeDisplay(int type)
	{
		switch(type)
		{
			case XA_START:    return "XA_START";
			case XA_END:      return "XA_END";
			case XA_PREPARE:  return "XA_PREPARE";
			case XA_COMMIT:   return "XA_COMMIT";
			case XA_ROLLBACK: return "XA_ROLLBACK";
			case XA_FORGET:   return "XA_FORGET";
			case XA_RECOVER:  return "XA_RECOVER";
                    default:              return "UNKNOWN" + type;
		}
		
	}

      
    /*L0*/ final private XAReturnValue DTC_XA_Interface(int nType, Xid xid, int xaFlags) throws XAException 
    {

        if (xaLogger.isLoggable(Level.FINER))
        xaLogger.finer( toString() + " Calling XA function for type:" + typeDisplay(nType) + " flags:"+flagsDisplay(xaFlags) +   " xid:" +  XidImpl.xidDisplay(xid) );

        int formatId = 0;
        byte gid[] = null;
        byte bid[] = null;
        if (xid != null)
        {
        	formatId = xid.getFormatId();
        	gid = xid.getGlobalTransactionId();
        	bid = xid.getBranchQualifier();
        }

        String sContext = "DTC_XA_";
        int n=1;
        int nStatus=0;
        XAReturnValue returnStatus = new XAReturnValue();

        SQLServerCallableStatement cs = null;
        try
        {        
    		synchronized(this) 
    		{
    			if (controlConnection == null) 
    			{
    				try 
    				{
						synchronized (xaInitLock)
						{
							if (!xaInitDone)
							{
							   	SQLServerCallableStatement initCS = null;
                                                           
								initCS = (SQLServerCallableStatement)controlConnection.prepareCall("{call master..xp_sqljdbc_xa_init_ex(?, ?,?)}");
								initCS.registerOutParameter(1, Types.INTEGER); // Return status
								initCS.registerOutParameter(2, Types.CHAR);    // Return error message
								initCS.registerOutParameter(3, Types.CHAR);    // Return version number
                                                           try
                                                            { 
                                                                initCS.execute();
                                                            }
                                                           catch (SQLServerException eX)
                                                            {
                                                                try
                                                                {
                                                                    initCS.close();
                                                                    // Mapping between control connection and xaresource is 1:1
                                                                    controlConnection.close();
                                                                }
                                                                catch(SQLException e3)
                                                                {
                                                                     // we really want to ignore this failue
                                                                     if (xaLogger.isLoggable(Level.FINER))
                                                                        xaLogger.finer(toString() + " Ignoring exception when closing failed execution. exception:" + e3);
                                                                }
                                                                if (xaLogger.isLoggable(Level.FINER))
                                                                    xaLogger.finer(toString() + " exception:" + eX);
                                                                throw eX;
                                                            }

								// Check for error response from xp_sqljdbc_xa_init.
								int initStatus = initCS.getInt(1);
								String initErr = initCS.getString(2);
                                                        String versionNumberXADLL =  initCS.getString(3);
                                                        if (xaLogger.isLoggable(Level.FINE))
                                                            xaLogger.fine(toString() + " Server XA DLL version:" + versionNumberXADLL);
                                                           initCS.close(); 
								if (XA_OK != initStatus)
								{
								    assert null != initErr && initErr.length() > 1;
                                                            controlConnection.close();
                                                              
                                                            MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToInitializeXA"));
                                                            Object[] msgArgs = {String.valueOf(initStatus), initErr};
                                                            XAException xex = new XAException(form.format(msgArgs));
                                                            xex.errorCode = initStatus;
                                                            if (xaLogger.isLoggable(Level.FINER))
                                                                    xaLogger.finer(toString() + " exception:" + xex);
                                                            throw xex;
								}
                                                       
								xaInitDone = true;
							}
         					}
    				}
    				catch (SQLServerException e1) 
    				{
                                    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToCreateXAConnection"));
                                    Object[] msgArgs = {new String(e1.getMessage())};
                                    if (xaLogger.isLoggable(Level.FINER))
                                        xaLogger.finer(toString() + " exception:" + form.format(msgArgs));
                                    SQLServerException.makeFromDriverError(null,null, form.format(msgArgs), null, true);
                   		}
        		}
        	}
        
        	switch (nType) {
        	case XA_START:    
        		
        	if (!serverInfoRetrieved)
        	{
	    		try{
	    			serverInfoRetrieved = true;
	    			// data are converted to varchar as type variant returned by SERVERPROPERTY is not supported by driver
	    			String query = "select convert(varchar(100), SERVERPROPERTY('Edition'))as edition, "
	    							+" convert(varchar(100), SERVERPROPERTY('InstanceName'))as instance,"
	    							+" convert(varchar(100), SERVERPROPERTY('ProductVersion')) as version,"
	    							+" SUBSTRING(@@VERSION, CHARINDEX('<', @@VERSION)+2, 2)";
	    			
	    			Statement stmt = controlConnection.createStatement();
			    	ResultSet rs = stmt.executeQuery(query);
			    	rs.next();
			    	
			    	String edition = rs.getString(1);
			    	ArchitectureMSSQL = ((null != edition) && (edition.contains("(64-bit)"))) ? 64 : 32 ;
						
			    	// if InstanceName is null use the default instance without name (MSSQLSERVER) 
			    	instanceName = ( rs.getString(2) == null ) ? "MSSQLSERVER" : rs.getString(2);		    	
			    	version = rs.getString(3);
			    	if (null == version)
			    	{
			    		version = "0";
			    	}
			    	else if (-1 != version.indexOf('.'))
			    	{
			    		version =  version.substring(0, version.indexOf('.'));
			    	}
			    	
			    	// @@VERSION returns single nvarchar string with SQL version, architecture, build date, edition and OS version
			    	// Version of the OS running MS SQL is retrieved as substring
			    	ArchitectureOS = Integer.parseInt(rs.getString(4));
			    	
			    	rs.close();
			    	stmt.close();
	            }
	    		// Got caught in static analysis. Catch only the thrown exceptions, do not catch
	    		// run time exceptions.
	            catch(Exception e){
	            	if (xaLogger.isLoggable(Level.WARNING))
	            		xaLogger.warning(toString() + " Cannot retrieve server information: :" + e.getMessage());            		
	            }			    	
        	}
    		
        	sContext = "START:";
        	cs = getXACallableStatementHandle(XA_START);
        	cs.registerOutParameter(n++, Types.INTEGER);	// Return status
        	cs.registerOutParameter(n++, Types.CHAR);		// Return error message
        	cs.setBytes(n++, gid);							// Global XID
        	cs.setBytes(n++, bid);							// Branch ID
        	cs.setInt(n++, xaFlags);						// XA transaction flags
        	cs.registerOutParameter(n++, Types.BINARY);		// Returned OLE transaction cookie
        	cs.setInt(n++, timeoutSeconds);					// Transaction timeout in seconds.	
        	cs.setInt(n++, formatId);						// Format ID
        	cs.registerOutParameter(n++, Types.CHAR);		// DLL Version number
        	cs.setInt(n++, Integer.parseInt(version));		// Version of SQL Server
        	cs.setInt(n++, instanceName.length());			// Length of SQL Server instance name
        	cs.setBytes(n++, instanceName.getBytes());		// SQL Server instance name
        	cs.setInt(n++, ArchitectureMSSQL);				// Architecture of SQL Server
        	cs.setInt(n++, ArchitectureOS);					// Architecture of OS running SQL Server
        	cs.setInt(n++, isTransacrionTimeoutSet);		// pass 1 if setTransactionTimeout() is called
        	cs.registerOutParameter(n++, Types.BINARY);		// Return UoW
        	
        	break;

        	case XA_END:
        	sContext = "END:";
        	cs = getXACallableStatementHandle(XA_END);
        	cs.registerOutParameter(n++, Types.INTEGER);
        	cs.registerOutParameter(n++, Types.CHAR);
        	cs.setBytes(n++, gid);
        	cs.setBytes(n++, bid);
        	cs.setInt(n++, xaFlags);
        	cs.setInt(n++, formatId);
        	cs.registerOutParameter(n++, Types.BINARY);		// Return UoW
        	break;

        	case XA_PREPARE:
        	sContext = "PREPARE:";
                  if((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)
            		cs = getXACallableStatementHandle(XA_PREPARE_EX);
                  else
                        cs = getXACallableStatementHandle(XA_PREPARE);
                  
        	cs.registerOutParameter(n++, Types.INTEGER);
        	cs.registerOutParameter(n++, Types.CHAR);
        	cs.setBytes(n++, gid);
        	cs.setBytes(n++, bid);
        	if((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)
        		cs.setInt(n++, xaFlags);					// XA transaction flags  
            cs.setInt(n++, formatId);						// Format ID n=5 for loosely coupled, n=6 for tightly coupled
        	break;

        	case XA_COMMIT:
        	sContext = "COMMIT:";
        	cs = getXACallableStatementHandle(XA_COMMIT);
        	cs.registerOutParameter(n++, Types.INTEGER);
        	cs.registerOutParameter(n++, Types.CHAR);
        	cs.setBytes(n++, gid);
        	cs.setBytes(n++, bid);
        	cs.setInt(n++, xaFlags);
        	cs.setInt(n++, formatId);
        	break;

        	case XA_ROLLBACK:
        	sContext = "ROLLBACK:";
                  if((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)  
                        cs = getXACallableStatementHandle(XA_ROLLBACK_EX);
                  else
                        cs = getXACallableStatementHandle(XA_ROLLBACK);

                   cs.registerOutParameter(n++, Types.INTEGER);
        	cs.registerOutParameter(n++, Types.CHAR);
        	cs.setBytes(n++, gid);
        	cs.setBytes(n++, bid);
        	if((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)  
        		cs.setInt(n++, xaFlags);					// XA transaction flags
        	cs.setInt(n++, formatId);						// Format ID n=5 for loosely coupled, n=6 for tightly coupled
        	break;

        	case XA_FORGET:
        	sContext = "FORGET:";
                  if((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)    
                    cs = getXACallableStatementHandle(XA_FORGET_EX);
                  else
                    cs = getXACallableStatementHandle(XA_FORGET);
        	cs.registerOutParameter(n++, Types.INTEGER);
        	cs.registerOutParameter(n++, Types.CHAR);
        	cs.setBytes(n++, gid);
        	cs.setBytes(n++, bid);
        	if((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD)  
        		cs.setInt(n++, xaFlags);					// XA transaction flags
        	cs.setInt(n++, formatId);						// Format ID n=5 for loosely coupled, n=6 for tightly coupled
        	break;

        	case XA_RECOVER:
        	sContext = "RECOVER:";
        	cs = getXACallableStatementHandle(XA_RECOVER);
        	cs.registerOutParameter(n++, Types.INTEGER);
        	cs.registerOutParameter(n++, Types.CHAR);
        	cs.setInt(n++, xaFlags);
        	cs.registerOutParameter(n++, Types.BINARY);
        	// Format Id need not be sent for recover action
        	break;
              default: 
                assert false : "Unknown execution type:"+ nType;
               break; 
                
        	}

        	/* execute the interface procedure */

        	cs.execute();
        	nStatus = cs.getInt(1);
        	String sErr = cs.getString(2);
        	if(nType == XA_START)
        	{
        		String versionNumberXADLL =  cs.getString(9);
        		if (xaLogger.isLoggable(Level.FINE))
        		{
        			xaLogger.fine(toString() + " Server XA DLL version:" + versionNumberXADLL);
        			if (null != cs.getString(16))
        			{
	        			StringBuffer strBuf = new StringBuffer(cs.getString(16));
	        			strBuf.insert(20, '-');
	        			strBuf.insert(16, '-');
	        			strBuf.insert(12, '-');
	        			strBuf.insert(8, '-');
	        			xaLogger.fine(toString() + " XID to UoW mapping for XA type:XA_START XID: " +  XidImpl.xidDisplay(xid) + " UoW: " + strBuf.toString());
        			}
        		}
        	}
        	if(nType == XA_END)
        	{
        		if (xaLogger.isLoggable(Level.FINE))
        		{
        			if (null != cs.getString(7))
        			{
	        			StringBuffer strBuf = new StringBuffer(cs.getString(7));
	        			strBuf.insert(20, '-');
	        			strBuf.insert(16, '-');
	        			strBuf.insert(12, '-');
	        			strBuf.insert(8, '-');
	        			xaLogger.fine(toString() + " XID to UoW mapping for XA type:XA_END XID: " +  XidImpl.xidDisplay(xid) + " UoW: " + strBuf.toString());
        			}
        		}
        	}
              // prepare and end can return XA_RDONLY
              // Think should we just check for nStatus to be greater than or equal to zero instead of this check
        	if ( ((XA_RDONLY == nStatus) && (XA_END != nType && XA_PREPARE != nType)) 
                        || (XA_OK != nStatus && XA_RDONLY != nStatus)) 
               {
                        assert (null != sErr) && (sErr.length() > 1);
                        MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedFunctionXA"));
                        Object[] msgArgs = {sContext, String.valueOf(nStatus), sErr};
                        XAException e = new XAException(form.format(msgArgs));
                        e.errorCode = nStatus;
 			    // if the request is end make sure we delist from the DTC transaction on rm failure.
                        if (nType == XA_END && (XAException.XAER_RMFAIL == nStatus)) 
                        {
                            try 
                            {
                                if (xaLogger.isLoggable(Level.FINER))
                                    xaLogger.finer(toString() + " Begin un-enlist, enlisted count:"+enlistedTransactionCount );
                                con.JTAUnenlistConnection();
                                enlistedTransactionCount--;
                                if (xaLogger.isLoggable(Level.FINER))
                                    xaLogger.finer(toString() + " End un-enlist, enlisted count:"+enlistedTransactionCount  );
                            }
                            catch (SQLServerException e1) 
                            {
                                // ignore this message as the previous error message is more important.
				if (xaLogger.isLoggable(Level.FINER))
                                    xaLogger.finer(toString() + " Ignoring exception:" + e1);	
                            }
                        }

                        
                        throw e;
               }
        	else 
        	{
        		if (nType == XA_START) 
        		{
        			//A physical connection may not have been enlisted yet so always enlist.
        			byte transactionCookie[] = cs.getBytes(6);
        			if (transactionCookie == null)
        			{
        			    MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_noTransactionCookie"));
                                Object[] msgArgs = {sContext};
                                SQLServerException.makeFromDriverError(null,null, form.format(msgArgs), null, true);

        			}
        			else
        			{
        				try 
        				{
        					if (xaLogger.isLoggable(Level.FINER))
        						xaLogger.finer(toString() + " Begin enlisting, cookie:"+cookieDisplay(transactionCookie)+" enlisted count:"+enlistedTransactionCount  );
        					con.JTAEnlistConnection(transactionCookie);
        					enlistedTransactionCount++;
        					if (xaLogger.isLoggable(Level.FINER))
        						xaLogger.finer(toString() + " End enlisting, cookie:"+cookieDisplay(transactionCookie)+" enlisted count:"+enlistedTransactionCount );
        				}
        				catch (SQLServerException e1)
        				{
                                        MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToEnlist"));
                                        Object[] msgArgs = {e1.getMessage()};
                                       SQLServerException.makeFromDriverError(null,null, form.format(msgArgs), null, true);
        				}
        			}
        		}
        		if (nType == XA_END) 
        		{
        			try 
        			{
        				if (xaLogger.isLoggable(Level.FINER))
        					xaLogger.finer(toString() + " Begin un-enlist, enlisted count:"+enlistedTransactionCount );
        				con.JTAUnenlistConnection();
        				enlistedTransactionCount--;
        				if (xaLogger.isLoggable(Level.FINER))
        					xaLogger.finer(toString() + " End un-enlist, enlisted count:"+enlistedTransactionCount  );
        			}
        			catch (SQLServerException e1) 
        			{
                                MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToUnEnlist"));
                                Object[] msgArgs = {e1.getMessage()};
                                SQLServerException.makeFromDriverError(null,null, form.format(msgArgs), null, true);
        			}
        		}
        		if (nType == XA_RECOVER) 

        		{
        			try 
        			{
        				returnStatus.bData = cs.getBytes(4);
        			}
        			catch (SQLServerException e1)
        			{
                                MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_failedToReadRecoveryXIDs"));
                                Object[] msgArgs = {e1.getMessage()};
                                SQLServerException.makeFromDriverError(null,null, form.format(msgArgs), null, true);
        			}
        		}
        	    }
        }
        catch(SQLServerException ex)
        {
            if (xaLogger.isLoggable(Level.FINER))
                xaLogger.finer(toString() + " exception:" + ex);
            XAException e = new XAException(ex.toString());        
            e.errorCode =  XAException.XAER_RMERR;
            throw e;
        }
	
	if (xaLogger.isLoggable(Level.FINER))
		xaLogger.finer(toString() + " Status:" + nStatus);

	returnStatus.nStatus=nStatus;
	return returnStatus;
	}

	/*L0*/ public void start(Xid xid, int flags) throws XAException 
	{
		/*Transaction mgr will use this resource in the global transaction. After this call the app
		server will call getConnection() to get a connection to give the application

		The xid holds the global transaction id + the transaction branch id. The getGlobalTransactionId
		should be the same for each call until the transaction is committed */

		/* XA API DOC : Start work on behalf of a transaction branch specified in xid If TMJOIN is specified,
		the start is for joining a transaction previously seen by the resource manager. If TMRESUME is
		specified, the start is to resume a suspended transaction specified in the parameter xid.
		If neither TMJOIN nor TMRESUME is specified and the transaction specified by xid has previously
		been seen by the resource manager, the resource manager throws the XAException exception with
		XAER_DUPID error code. */

		// TMNOFLAGS indicates this is the first time this physical connection has seen the transaction.
		// EG if the physical connection has generated multiple connection handles only work on the first
		// of those will be prefixed by the transaction manager with a call to start with TMNOFLAGS

		       tightlyCoupled  = flags & SSTRANSTIGHTLYCPLD;
			DTC_XA_Interface(XA_START, xid, flags);
	}

	/*L0*/ public void end(Xid xid, int flags) throws XAException 
	{
		//Called by the transaction mgr after the app closes the connection it was given from this physical
		//connection
		/*
		Ends the work performed on behalf of a transaction branch. The resource manager disassociates the
		XA resource from the transaction branch specified and let the transaction be completed.
		If TMSUSPEND is specified in flags, the transaction branch is temporarily suspended in incomplete state. The transaction context is in suspened state and must be resumed via start with TMRESUME specified.
		If TMFAIL is specified, the portion of work has failed. The resource manager may mark the transaction
		as rollback-only. If TMSUCCESS is specified, the portion of work has completed successfully. */
		DTC_XA_Interface(XA_END, xid, flags|tightlyCoupled );
	}

        /*L0*/ public int prepare(Xid xid) throws XAException 
        {
            /*Ask the resource manager to prepare for a transaction commit of the transaction specified in xid.
            Parameters:
            xid - A global transaction identifier
            Returns:
            A value indicating the resource manager's vote on the outcome of the transaction. The possible values are: XA_RDONLY or XA_OK. If the resource manager wants to roll back the transaction,
            it should do so by raising an appropriate XAException in the prepare method.
            */
            int nStatus = XA_OK;
            XAReturnValue r = DTC_XA_Interface(XA_PREPARE, xid, tightlyCoupled);
            nStatus = r.nStatus;

            return nStatus;
        }

	/*L0*/ public void commit(Xid xid, boolean onePhase) throws XAException 
	{
            DTC_XA_Interface(XA_COMMIT, xid, ((onePhase) ? TMONEPHASE : TMNOFLAGS) | tightlyCoupled);
	}

	/*L0*/ public void rollback(Xid xid) throws XAException 
	{
            DTC_XA_Interface(XA_ROLLBACK, xid, tightlyCoupled);
	}

	/*L0*/ public void forget(Xid xid) throws XAException 
	{
            DTC_XA_Interface(XA_FORGET, xid, tightlyCoupled);
	}

	/*L0*/ public Xid[] recover(int flags) throws XAException 
	{
            XAReturnValue r = DTC_XA_Interface(XA_RECOVER, null, flags|tightlyCoupled);
            int offset=0;
            Vector v = new Vector();

            // If no XID's found, return zero length XID array (don't return null).
            //
            // Per Java 1.4.2 spec:
            //
            // The resource manager returns zero or more XIDs of 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.

            if (null == r.bData) return new XidImpl[0];

            while (offset < r.bData.length) 
            {
                int power=1;
                int formatId=0;
                for (int i=0; i<4; i++) 
                {
                    int x = (r.bData[offset+i] & 0x00FF);
                    x = x * power;
                    formatId += x;
                    power = power *256;
                }
                offset += 4;
                int gid_len = (r.bData[offset++] & 0x00FF);
                int bid_len = (r.bData[offset++] & 0x00FF);
                byte gid[] = new byte[gid_len];
                byte bid[] = new byte[bid_len];
                System.arraycopy(r.bData, offset, gid, 0, gid_len);
                offset += gid_len;
                System.arraycopy(r.bData, offset, bid, 0, bid_len);
                offset += bid_len;
                XidImpl xid = new XidImpl(formatId, gid, bid);
                v.add(xid);
            }
            XidImpl xids[] = new XidImpl[v.size()];
            for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy