/**
 * Copyright (c) 2015 GEMALTO. All Rights Reserved.
 *
 * This software is the confidential and proprietary information of GEMALTO.
 *
 * -----------------------------------------------------------------------------
 * GEMALTO MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
 * PURPOSE OR NON-INFRINGEMENT. GEMALTO SHALL NOT BE LIABLE FOR ANY
 * DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 *
 * THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
 * CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
 * PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
 * NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
 * SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
 * SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
 * PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). GEMALTO
 * SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
 * HIGH RISK ACTIVITIES.
 */
package com.gemalto.test.mysql.xa;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.Statement;

import javax.sql.XAConnection;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;

/**
 * @author fcourtau
 *
 */
public class TestMySQLXAConnectorJ {

    public static void run() {
        final Statement stmt = null;
        final Statement stmt2 = null;

        try {

            xaRes = xaConn.getXAResource();
            conn = xaConn.getConnection();

            conn.setAutoCommit(false);

            System.out.println("Starting XA tx...");
            xid1 = TestUtil.createXid(xidBase++, 1);
            xaRes.start(xid1, XAResource.TMNOFLAGS);

            System.out.println("Doing a query...");
            /*
                  PreparedStatement p = conn.prepareStatement("SELECT COUNT(*) FROM SYSIBM.SYSTABLES");
                  p.execute();
                  p.getResultSet().close();
                  p.close();
             */

            System.out.println("Ending first XA tx...");
            xaRes.end(xid1, XAResource.TMSUCCESS);
            xaRes.start(xid1, XAResource.TMJOIN);
            xaRes.end(xid1, XAResource.TMSUCCESS);
            xaRes.start(xid1, XAResource.TMJOIN);
            xaRes.end(xid1, XAResource.TMSUSPEND); 
            xaRes.start(xid1, XAResource.TMRESUME);
            xaRes.end(xid1, XAResource.TMSUCCESS);
            xaRes.commit(xid1, true);
            System.out.println("First XA tx done.\n");

            conn.rollback();

        } catch (final Exception sqle) {
            sqle.printStackTrace();
        } finally {
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (final Exception ex) {}
        }
    }


    // Set these below based on database driver/info
    private static String UserName;

    private static String Password;

    private static String ServerName;

    private static String datasourceName;

    private static int PortNumber;

    private static String Company;

    private static String DatabaseName;

    private static String HostName;

    private static String SID;

    private static String URL;

    private static XAResource xaRes = null;

    private static XAConnection xaConn = null;

    private static Connection conn = null;

    private static int xidBase = 12170;

    private static Xid xid1 = null, xid2 = null;

    private static com.mysql.jdbc.jdbc2.optional.MysqlXADataSource ds = null;


    private static void initProp(final Object ds, final String propName, final Object value) {
        try {
            final Method[] meths = ds.getClass().getMethods();
            Method meth = null;
            final String setterName = "set" + propName;
            Class paramType = null;
            for (int i = 0; i < meths.length; i++) {
                if (meths[i].getName().equalsIgnoreCase(setterName) && (meths[i].getParameterTypes().length == 1)) {
                    meth = meths[i];
                    paramType = (meths[i].getParameterTypes())[0];
                    break;
                }
            }
            if (meth != null) {
                Object val = value;
                if (value instanceof String) {
                    if (paramType == int.class) {
                        val = Integer.valueOf((String)value);
                    } else if (paramType == boolean.class) {
                        val = Boolean.valueOf((String)value);
                    }
                }
                meth.invoke(ds, new Object[] { val });
            } else {
                meth = ds.getClass().getMethod("setProperty", new Class[] { String.class, Object.class });
                meth.invoke(ds, new Object[] { propName, value });
            }
        } catch (final Exception ignore) {}
    }


    private static void initProp(final Object ds) {
        try {
            final Method[] meths = ds.getClass().getMethods();
            final Method meth = null;
            for (int i = 0; i < meths.length; i++) {
                System.out.println("We have a method " + meths[i].getName());
            }
        } catch (final Exception ignore) {}
    }


    /**
     * @param args
     */
    public static void main(final String[] args) {
        try {
            // load XA driver and get connection
            ds = (com.mysql.jdbc.jdbc2.optional.MysqlXADataSource)(Class.forName("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource").newInstance());

            ds.setUrl("jdbc:mysql://localhost:3306/idsdb?jdbcCompliantTruncation=false");
            ds.setUser("MGRGEMALTO");
            ds.setPassword("MGRGEMALTO");

            //            ds.setUrl("jdbc:mysql://lcqw2k32:3306/wl?jdbcCompliantTruncation=false");
            //            ds.setUser("jdbcqa");
            //            ds.setPassword("jdbcqa");

            xaConn = ds.getXAConnection();
            conn = xaConn.getConnection();
            System.out.println("Connection obtained with Driver version= " + conn.getMetaData().getDriverVersion());
            System.out.println("Driver product= " + conn.getMetaData().getDatabaseProductVersion());
            System.out.println("Driver name= " + conn.getMetaData().getDriverName());
            System.out.println("Driver major version= " + conn.getMetaData().getDriverMajorVersion());
            System.out.println("Driver minor veresion= " + conn.getMetaData().getDriverMinorVersion());
            System.out.println("db product name= " + conn.getMetaData().getDatabaseProductName());
            conn.close();
            System.out.println("Got a connection");

            run();
        } catch (final Exception e) {
            e.printStackTrace();
        }

    }

}

class TestUtil {

    static Object xaLock = new Object();


    public TestUtil() {}


    public static void println(final String msg) {
        System.out.println(msg);
    }


    public static void dumpXAError(final Exception xae) {
        System.out.println("err msg: " + xae.getMessage());
        xae.printStackTrace();
    }


    public static Xid createXid(final int gd, final int bd) throws XAException {
        final byte[] gid = new byte[1];
        gid[0] = (byte)gd;
        final byte[] bid = new byte[1];
        bid[0] = (byte)bd;
        final byte[] gtrid = new byte[64];
        final byte[] bqual = new byte[64];
        System.arraycopy(gid, 0, gtrid, 0, 1);
        System.arraycopy(bid, 0, bqual, 0, 1);
        final Xid xid = new XidImpl(0x1, gtrid, bqual);
        return xid;
    }


    public static String getXAErrStr(final int err) {
        switch (err) {
        case XAException.XA_RBROLLBACK:
            return "XA_RBROLLBACK";
        case XAException.XA_RBCOMMFAIL:
            return "XA_RBCOMMFAIL";
        case XAException.XA_RBDEADLOCK:
            return "XA_RBDEADLOCK";
        case XAException.XA_RBINTEGRITY:
            return "XA_RBINTEGRITY";
        case XAException.XA_RBOTHER:
            return "XA_RBOTHER";
        case XAException.XA_RBPROTO:
            return "XA_RBPROTO";
        case XAException.XA_RBTIMEOUT:
            return "XA_RBTIMEOUT";
        case XAException.XA_RBTRANSIENT:
            return "XA_RBTRANSIENT";
        case XAException.XA_NOMIGRATE:
            return "XA_NOMIGRATE";
        case XAException.XA_HEURHAZ:
            return "XA_HEURHAZ";
        case XAException.XA_HEURCOM:
            return "XA_HEURCOM";
        case XAException.XA_HEURRB:
            return "XA_HEURRB";
        case XAException.XA_HEURMIX:
            return "XA_HEURMIX";
        case XAException.XA_RETRY:
            return "XA_RETRY";
        case XAException.XA_RDONLY:
            return "XA_RDONLY";
        case XAException.XAER_ASYNC:
            return "XAER_ASYNC";
        case XAException.XAER_RMERR:
            return "XAER_RMERR";
        case XAException.XAER_NOTA:
            return "XAER_NOTA";
        case XAException.XAER_INVAL:
            return "XAER_INVAL";
        case XAException.XAER_PROTO:
            return "XAER_PROTO";
        case XAException.XAER_RMFAIL:
            return "XAER_RMFAIL";
        case XAException.XAER_DUPID:
            return "XAER_DUPID";
        case XAException.XAER_OUTSIDE:
            return "XAER_OUTSIDE";
        default:
            return (new Integer(err)).toString();
        }
    }
}

class XidImpl implements Xid {

    public int formatId;

    public byte[] gtrid;

    public byte[] bqual;


    @Override
    public byte[] getGlobalTransactionId() {
        return gtrid;
    }


    @Override
    public byte[] getBranchQualifier() {
        return bqual;
    }


    @Override
    public int getFormatId() {
        return formatId;
    }


    XidImpl(final int formatId, final byte[] gtrid, final byte[] bqual) {
        this.formatId = formatId;
        this.gtrid = gtrid;
        this.bqual = bqual;
    }


    private static char DIGITS[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };


    @Override
    public String toString() {
        return toString(this);
    }


    public static String toString(final Xid xid) {
        final StringBuffer sb = new StringBuffer().append(Integer.toHexString(xid.getFormatId()).toUpperCase()).append("-")
                .append(byteArrayToString(xid.getGlobalTransactionId()));
        final String bqual = byteArrayToString(xid.getBranchQualifier());
        if (!bqual.equals("")) {
            sb.append("-").append(byteArrayToString(xid.getBranchQualifier()));
        }
        return sb.toString();
    }


    private static String byteArrayToString(final byte[] barray) {
        if (barray == null) {
            return "";
        }
        final StringBuffer buf = new StringBuffer();
        for (int i = 0; i < barray.length; i++) {
            buf.append(DIGITS[(barray[i] & 0xF0) >>> 4]);
            buf.append(DIGITS[barray[i] & 0x0F]);
        }
        return buf.toString();
    }
}
