/*
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 1999 Bull S.A.
 * Contact: jonas-team@objectweb.org
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or any later version.
 * 
 * This library 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 GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */


package org.objectweb.jonas.jtm;

import java.rmi.NoSuchObjectException;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.objectweb.jonas_tm.*;
import org.objectweb.jonas.common.Log;
import org.objectweb.jonas.jmx.JmxService;
import org.objectweb.jonas.jmx.JonasObjectName;
import org.objectweb.common.Env;
import org.objectweb.jonas.naming.NamingManager;
import org.objectweb.jonas.service.AbsServiceImpl;
import org.objectweb.jonas.service.ServiceManager;
import org.objectweb.jonas.service.ServiceException;
import org.objectweb.util.monolog.api.Logger;
import org.objectweb.util.monolog.api.BasicLevel;

/**
 * Transaction Service implementation.
 * This singleton class must exist in each jonas server.
 * This class manages a unique Current object that implements both
 * TransactionManager and UserTransaction interfaces.
 * @author Philippe Durieux
 * Contributor(s):
 */
public class TransactionServiceImpl extends AbsServiceImpl implements TransactionService, TransactionServiceImplMBean {

    static private Logger logger = null;

    // TM factory may be local or remote.
    // The TM factory is used by the JTA implementation when transactions
    // are distributed among several JVM (EJBServer or applet client)
    private TransactionFactory tm = null;

    // Unique Current object implementing the standard TransactionManager
    // set to null while service is not started.
    private Current current = null;

    // Configuration information used to start the Transaction service
    private int timeout;
    private boolean jtmlocal;
    private InitialContext ictx;
 
    // -------------------------------------------------------------------
    // JOnAS Service Implementation
    // -------------------------------------------------------------------

    /**
     * Init the Service.
     * Configuration information is passed thru a Context object.
     */
    public void doInit(Context ctx) throws ServiceException {

	// get logger for this service
        logger = Log.getLogger(Log.JONAS_SERVER_PREFIX);

	// Gets the configuration
	String remote = "false";
	try {
	    remote = (String) ctx.lookup("remote");
	} catch (NamingException e) {
	    // No problem if there is no value --> default value
	}
	if ((remote!=null) && (!remote.equals(""))) {
	    jtmlocal = !remote.equalsIgnoreCase("true");
	} else {
	    jtmlocal = false;
	}

	String tstr = "60";
	try {
	    tstr = (String) ctx.lookup("timeout");
	} catch (NamingException e) {
	    // No problem if there is no value --> default value
	}
	if ((tstr!=null) && (!tstr.equals(""))) {
	    timeout = (new Integer(tstr)).intValue();
	} else {
	    timeout = 60;
	}

	logger.log(BasicLevel.DEBUG, "TransactionService initialized");
    }

    /**
     * Start the Service
     * Initialization of the service is already done.
     */
    public void doStart() throws ServiceException {

	try {
	    if (Env.isDavid()) {
		// use CosNaming
		ictx = NamingManager.getInstance().getCorbaInitialContext();
	    } else {
		// use default naming
		ictx = NamingManager.getInstance().getInitialContext();
	    }
	} catch (NamingException e) {
	    logger.log(BasicLevel.ERROR, "TransactionService: Cannot get InitialContext:\n"+e);
	    throw new ServiceException("TransactionService: Cannot get InitialContext", e);
	}

	// Gets the Distributed Transaction Manager (JTM)
	if (jtmlocal) {
	    // Creates a JTM locally in this server
	    logger.log(BasicLevel.DEBUG, "working with a colocated Transaction Manager ");

	    // TODO: Recovery of the JTM

	    // Creates the ControlFactory and register it in JNDI.
	    try {
		logger.log(BasicLevel.DEBUG, "Create and register TM factory");
		tm = new TransactionFactoryImpl();
		ictx.rebind("TMFactory", (Object) tm);
	    } catch (RemoteException e) {
		logger.log(BasicLevel.ERROR, "TransactionService: Cannot create TransactionFactory:\n"+e);
		throw new ServiceException("TransactionService: Cannot create TransactionFactory",
					   e);
	    } catch (NamingException e) {
		logger.log(BasicLevel.ERROR, "TransactionService: Cannot rebind TM:\n"+e);
		throw new ServiceException("TransactionService: Cannot rebind TM", e);
	    }
	} else {
	    // JTM is remote, finds it by JNDI
	    logger.log(BasicLevel.DEBUG, "working with a remote Transaction Manager ");
	    final int maxloops = 5;
	    for (int i = 0; i <= maxloops; i++) {
		try {
		    tm = (TransactionFactory) ictx.lookup("TMFactory");
		    break;
		} catch (NamingException e) {
		    if (i < maxloops) {
			logger.log(BasicLevel.WARN, "Cannot get TM factory - retrying...");
			try {
			    Thread.currentThread().sleep(2000*(i+1));
			} catch (InterruptedException e2) {
			    throw new ServiceException("Cannot get TM factory", e2);
			}
		    } else {
			logger.log(BasicLevel.ERROR, "TransactionService: Cannot get TM factory:\n"+e);
			throw new ServiceException("TransactionService: Cannot get TM factory", e);
		    }
		}
	    }
	}

	// Create and init the unique Current object.
	// Current is the local TransactionManager. It must be present in every
	// jonas server and implements the JTA TransactionManager interface.
	//
	// In case of a JTM standalone: 
	// We must create the current only to export it via JNDI for clients
	// that want to get UserTransaction.
	current = new Current(tm, Env.isDavid());
	setTimeout(timeout);
	
	try {
	    // Register TransactionService MBean : JmxTransactionService 
	    ((JmxService)ServiceManager.getJmxService()).getJmxServer().registerMBean(this, JonasObjectName.transactionService());
	}  catch (ServiceException se) {
	    // Jmx Service not available, do nothing
	}  catch (Exception e) {
	    logger.log(BasicLevel.ERROR, "TransactionService: Cannot start the Transaction service:\n"+e);
	    throw new ServiceException("TransactionService: Cannot start the Transaction service", e);
	}

	logger.log(BasicLevel.DEBUG, "TransactionService started, default timeout= "+timeout);
    }

    /**
     * Stop the transaction service
     * Not already implementated
     */
    public void doStop() throws ServiceException {
	logger.log(BasicLevel.DEBUG, "Stop of TransactionService not already implemented");
    }

    // -------------------------------------------------------------------
    // TransactionService Implementation
    // -------------------------------------------------------------------

    /**
     * Gets the Current object instance
     */
    public Current getCurrent() {
	return current;
    }

    /**
     * Gets the TransactionManager object instance
     */
    public TransactionManager getTransactionManager() {
	return (TransactionManager) current;
    }

    /**
     * Gets the UserTransaction object instance
     */
    public UserTransaction getUserTransaction() {
	return (UserTransaction) current;
    }

    /**
     * Gets the TransactionFactory object (JTM factory)
     */
    public TransactionFactory getTransactionFactory() {
	return tm;
    }

    /**
     * Sets the default transaction timeout and register Current in JNDI
     */
    public void setTimeout(int t) {

	logger.log(BasicLevel.DEBUG, "entering for "+t);
	current.setDefaultTimeout(t);

	// Register a UserTransactionFactory in JNDI.
	// Only if we are inside the JTM
	if (jtmlocal) {
	    try {
		logger.log(BasicLevel.DEBUG, "Register UserTransactionFactory");
		NamingManager.getInstance().getInitialContext().rebind("java:comp/UserTransaction", current);
	    } catch (NamingException e) {
		logger.log(BasicLevel.ERROR, "Cannot rebind UserTransaction:"+e);
	    }
	}
    }

    /**
     * MBean  method
     * get the default timeout
     */
    public void setCurrentTimeOut(Integer t) {
	logger.log(BasicLevel.DEBUG, "entering");
	setTimeout(t.intValue());
    }

    /**
     * MBean  method
     * get the default timeout
     */
    public Integer getCurrentTimeOut() {
	logger.log(BasicLevel.DEBUG, "entering");
	return new Integer(current.getDefaultTimeout());
    }

    /**
     * MBean  method
     * @return true if the jtm is local
     */
    public Boolean getJTMLocal() {
	return new Boolean(jtmlocal);
    }

    /**
     * MBean  method	 
     * @return the jtm port number
     */
    public Integer getJTMPortNumber() {
	try {
	    return new Integer(tm.getPortNumber());
	} catch (Exception e) {
	    return null;
	}
    }  

    /**
     * MBean  method	 
     * @return the jtm host name
     */
    public String getJTMHostName() {
	try {
	    return tm.getHostName();
	} catch (Exception e) {
	    return null;
	}
    }

    /**
     *  MBean method:
     * @return all tx counter
     */
    public Integer [] getTxCounters() {
	return current.getTxCounters();
    }

   /**
     * MBean method:
     * reset all tx counter
     */
    public void resetAllTxTotalCounters() {
	current.resetAllTxTotalCounters();
    }
}
