/*
 * JBoss, Home of Professional Open Source
 * Copyright 2007, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags.
 * See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 * This copyrighted material is made available to anyone wishing to use,
 * modify, copy, or redistribute it subject to the terms and conditions
 * of the GNU Lesser General Public License, v. 2.1.
 * This program is distributed in the hope that it will be useful, but WITHOUT A
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License,
 * v.2.1 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 * (C) 2005-2006,
 * @author JBoss Inc.
 */
/*
 * Copyright (C) 1998, 1999, 2000,
 *
 * Arjuna Solutions Limited,
 * Newcastle upon Tyne,
 * Tyne and Wear,
 * UK.
 *
 * $Id: JNDIManager.java,v 1.10 2004/07/26 09:49:59 jcoleman Exp $
 */
package org.jboss.jbossts.qa.Utils;

import com.arjuna.ats.internal.jdbc.DynamicClass;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.XADataSource;
import java.util.Hashtable;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

public class JNDIManager
{
	public static void main(String[] args)
	{
		try
		{
			String profileName = args[args.length - 1];
			String driver = JDBCProfileStore.driver(profileName, 0 /*driver number*/);
			String binding = JDBCProfileStore.binding(profileName);
			String databaseName = JDBCProfileStore.databaseName(profileName);
			String host = JDBCProfileStore.host(profileName);
			String dynamicClass = JDBCProfileStore.databaseDynamicClass(profileName);
			String databaseURL = JDBCProfileStore.databaseURL(profileName);
			String port = JDBCProfileStore.port(profileName);

			XADataSource xaDataSourceToBind = null;

			if (driver == null || binding == null)
			{
				throw new Exception("Driver or binding was not specified");
			}

            // We use reflection to configure the data source so as to avoid a compile or runtime
            // dependency on all the drivers. see JBTM-543

			if (driver.equals("com.arjuna.ats.jdbc.TransactionalDriver"))
			{
				if ((dynamicClass == null) || (databaseURL == null))
				{
					throw new Exception("One of dynamicClass/datbaseURL was not specified for: " + profileName);
				}

				Class c = Class.forName(dynamicClass);

				DynamicClass arjunaJDBC2DynamicClass = (DynamicClass) c.newInstance();
				javax.sql.XADataSource xaDataSource = arjunaJDBC2DynamicClass.getDataSource(databaseURL);

				xaDataSourceToBind = xaDataSource;
			}
			else if (driver.equals("oracle.jdbc.driver.OracleDriver"))
			{
				if (databaseName == null)
				{
					throw new Exception("DatabaseName was not specified for profile: " + profileName);
				}

                XADataSourceReflectionWrapper wrapper = new XADataSourceReflectionWrapper("oracle.jdbc.xa.client.OracleXADataSource");

                wrapper.setProperty("databaseName", databaseName);
                wrapper.setProperty("serverName", host);
                wrapper.setProperty("portNumber", Integer.valueOf(port));
                wrapper.setProperty("driverType", "thin");

				xaDataSourceToBind = wrapper.getWrappedXADataSource();
			}
			else if( driver.equals("com.microsoft.sqlserver.jdbc.SQLServerDriver")) {

                XADataSourceReflectionWrapper wrapper = new XADataSourceReflectionWrapper("com.microsoft.sqlserver.jdbc.SQLServerXADataSource");

                wrapper.setProperty("databaseName", databaseName);
                wrapper.setProperty("serverName", host);
                wrapper.setProperty("portNumber", Integer.valueOf(port));
                wrapper.setProperty("sendStringParametersAsUnicode", false);

				xaDataSourceToBind = wrapper.getWrappedXADataSource();
			}
			else if( driver.equals("org.postgresql.Driver")) {

                XADataSourceReflectionWrapper wrapper = new XADataSourceReflectionWrapper("org.postgresql.xa.PGXADataSource");

                wrapper.setProperty("databaseName", databaseName);
                wrapper.setProperty("serverName", host);

                xaDataSourceToBind = wrapper.getWrappedXADataSource();
			}
			else if( driver.equals("com.mysql.jdbc.Driver")) {

				// Note: MySQL XA only works on InnoDB tables.
				// set 'default-storage-engine=innodb' in e.g. /etc/my.cnf
				// so that the 'CREATE TABLE ...' statments behave correctly.
				// doing this config on a per connection basis instead is
				// possible but would require lots of code changes :-(

                XADataSourceReflectionWrapper wrapper = new XADataSourceReflectionWrapper("com.mysql.jdbc.jdbc2.optional.MysqlXADataSource");

                wrapper.setProperty("databaseName", databaseName);
                wrapper.setProperty("serverName", host);
                wrapper.setProperty("pinGlobalTxToPhysicalConnection", true); // Bad Things happen if you forget this bit.

				xaDataSourceToBind = wrapper.getWrappedXADataSource();
			}
			else if( driver.equals("com.ibm.db2.jcc.DB2Driver")) {

				// for DB2 version 8.2

                XADataSourceReflectionWrapper wrapper = new XADataSourceReflectionWrapper("com.ibm.db2.jcc.DB2XADataSource");

                wrapper.setProperty("databaseName", databaseName);
                wrapper.setProperty("serverName", host);
                wrapper.setProperty("driverType", 4);
                wrapper.setProperty("portNumber", Integer.valueOf(port));

                xaDataSourceToBind = wrapper.getWrappedXADataSource();
			}
			else if( driver.equals("com.sybase.jdbc3.jdbc.SybDriver")) {

                XADataSourceReflectionWrapper wrapper = new XADataSourceReflectionWrapper("com.sybase.jdbc3.jdbc.SybXADataSource");

                wrapper.setProperty("databaseName", databaseName);
                wrapper.setProperty("serverName", host);
                wrapper.setProperty("portNumber", Integer.valueOf(port));

                xaDataSourceToBind = wrapper.getWrappedXADataSource();
			}
			else
			{
				throw new Exception("JDBC2 driver " + driver + " not recognised");
			}

			//
			// bind to JDNI
			//
			try
			{
				Hashtable env = new Hashtable();
				String initialCtx = System.getProperty("Context.INITIAL_CONTEXT_FACTORY");
				String bindingsLocation = System.getProperty("Context.PROVIDER_URL");

				if (bindingsLocation != null)
				{
					env.put(Context.PROVIDER_URL, bindingsLocation);
				}

				env.put(Context.INITIAL_CONTEXT_FACTORY, initialCtx);
				InitialContext ctx = new InitialContext(env);

				ctx.rebind(binding, xaDataSourceToBind);

				System.out.println("bound "+binding);
			}
			catch (Exception e)
			{
				System.err.println("JNDIManager.main: Problem binding resource into JNDI");
				e.printStackTrace();
				System.out.println("Failed");
				System.exit(1);
			}

			System.out.println("Passed");
		}
		catch (Exception e)
		{
            e.printStackTrace(System.err);
			System.out.println("Failed");
		}
	}
}

class XADataSourceReflectionWrapper {
    private XADataSource xaDataSource;
    XADataSourceReflectionWrapper(String classname) {
        try {
            xaDataSource = (XADataSource)Class.forName(classname).newInstance();
        } catch(Exception e) {
            throw new ExceptionInInitializerError(e);
        }
    }

    public void setProperty(String name, Object value)
            throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
        name = "set"+name.substring(0,1).toUpperCase()+name.substring(1);

        Class type = value.getClass();
        if(value instanceof Integer) {
            type = Integer.TYPE;
        }
        if(value instanceof Boolean) {
            type = Boolean.TYPE;
        }

        Method method = xaDataSource.getClass().getMethod(name, type);
        method.invoke(xaDataSource, value);
    }

    public XADataSource getWrappedXADataSource() {
        return xaDataSource;
    }
}
