/*
 * JBoss, Home of Professional Open Source
 * Copyright 2006, JBoss Inc., and others contributors as indicated 
 * by the @authors tag. All rights reserved. 
 * 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) 2004, 2005,
 *
 * Arjuna Technologies Limited,
 * Newcastle upon Tyne,
 * Tyne and Wear,
 * UK.
 *
 * $Id$
 */

package com.arjuna.ats.internal.jdbc.recovery;

import java.util.*;
import java.sql.*;
import javax.sql.*;
import javax.transaction.xa.*;
import com.arjuna.common.util.logging.*;
import com.arjuna.common.internal.util.propertyservice.plugins.io.XMLFilePlugin;
import com.arjuna.ats.arjuna.logging.FacilityCode;
import com.arjuna.ats.jdbc.common.*;
import com.arjuna.ats.jdbc.logging.jdbcLogger;
import com.arjuna.ats.jta.recovery.XAResourceRecovery;
import oracle.jdbc.xa.client.OracleXADataSource;

/**
 * This provides resource recovery for Oracle JDBC drivers.
 * 
 * Users are responsible for DataSource creation information in a property file to 
 * setup this class. Username and password values may also be provided in
 * the same property file.
 */

public class OracleXARecovery implements XAResourceRecovery
{
    public static final String DATABASE_URL = "DatabaseURL";
    public static final String USER_NAME    = "UserName";
    public static final String PASSWORD     = "Password";

    /*
     * Some XAResourceRecovery implementations will do their startup work here.
     */

    public OracleXARecovery()
        throws SQLException
    {
        if (jdbcLogger.logger.isDebugEnabled())
            jdbcLogger.logger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_CRASH_RECOVERY, "JDBCXARecovery()");

        _props                   = null;
        _hasMoreResources        = false;
        _connectionEventListener = new LocalConnectionEventListener();
    }

    /**
     * The recovery module will have chopped off this class name already. The
     * parameter should specify a property file from which the url, user name,
     * password can be read.
     * 
     * @message com.arjuna.ats.internal.jdbc.recovery.oracle.initexp An exception
     *          occurred during initialisation.
     */

    public boolean initialise(String parameter)
        throws SQLException
    {
        if (jdbcLogger.logger.isDebugEnabled())
            jdbcLogger.logger.debug(DebugLevel.CONSTRUCTORS, VisibilityLevel.VIS_PUBLIC, FacilityCode.FAC_CRASH_RECOVERY, "JDBCXARecovery.initialise(" + parameter + ")");

        if (parameter == null)
            return false;

        try
        {
            jdbcPropertyManager.propertyManager.load(XMLFilePlugin.class.getName(), parameter);

            _props = jdbcPropertyManager.propertyManager.getProperties();

            _dbURL    = _props.getProperty(DATABASE_URL);
            _user     = _props.getProperty(USER_NAME);
            _password = _props.getProperty(PASSWORD);
        }
        catch (Exception e)
        {
            if (jdbcLogger.loggerI18N.isWarnEnabled())
            {
                jdbcLogger.loggerI18N.warn("com.arjuna.ats.internal.jdbc.recovery.oracle.initexp", new Object[] { e });

                e.printStackTrace();
            }

            return false;
        }

        return true;
    }

    /**
     * @message com.arjuna.ats.internal.jdbc.recovery.oracle.xarec {0} could not find
     *          information for connection!
     */

    public synchronized XAResource getXAResource()
        throws SQLException
    {
        createConnection();

        return _connection.getXAResource();
    }

    public boolean hasMoreResources()
    {
        if (_dataSource == null)
            try
            {
                createDataSource();
            }
            catch (SQLException sqlException)
            {
                return false;
            }
        
        if (_dataSource != null)
        {
            _hasMoreResources = ! _hasMoreResources;

            return _hasMoreResources;
        }
        else
            return false;
    }

    /**
     * Creating and initalising an OracleXADataSource instance. We got the relevant information from the
     * property file provided at input to this instance.
     * 
     * @message com.arjuna.ats.internal.jdbc.oracle.recjndierror Could not resolve JNDI
     *          XADataSource
     */

    private final void createDataSource()
        throws SQLException
    {
        try
        {
            if (_dataSource == null)
            {
                OracleXADataSource dataSource = new OracleXADataSource();
                dataSource.setURL(_dbURL);

                _dataSource = dataSource;

                if (_dataSource == null)
                    throw new SQLException(jdbcLogger.logMesg.getString("com.arjuna.ats.internal.jdbc.oracle.recjndierror"));
            }
        }
        catch (SQLException ex)
        {
            ex.printStackTrace();

            throw ex;
        }
        catch (Exception e)
        {
            e.printStackTrace();

            throw new SQLException(e.toString());
        }
    }

    /**
     * Create the XAConnection from the XADataSource.
     */

    private final void createConnection()
        throws SQLException
    {
        try
        {
            if (_dataSource == null)
                createDataSource();

            if (_connection == null)
            {
                if ((_user == null) && (_password == null))
                    _connection = _dataSource.getXAConnection();
                else
                    _connection = _dataSource.getXAConnection(_user, _password);

                _connection.addConnectionEventListener(_connectionEventListener);
            }
        }
        catch (SQLException ex)
        {
            ex.printStackTrace();

            throw ex;
        }
        catch (Exception e)
        {
            e.printStackTrace();

            throw new SQLException(e.toString());
        }
    }

    private class LocalConnectionEventListener implements ConnectionEventListener
    {
        public void connectionErrorOccurred(ConnectionEvent connectionEvent)
        {
            _connection.removeConnectionEventListener(_connectionEventListener);
            _connection = null;
        }

        public void connectionClosed(ConnectionEvent connectionEvent)
        {
            _connection.removeConnectionEventListener(_connectionEventListener);
            _connection = null;
        }
    }
    
    private XAConnection                 _connection;
    private XADataSource                 _dataSource;
    private LocalConnectionEventListener _connectionEventListener;
    private Properties                   _props;
    private String                       _dbURL;
    private String                       _user;
    private String                       _password;
    private boolean                      _hasMoreResources;
}
