/*
 * The contents of this file are subject to the JOnAS Public License Version 
 * 1.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License on the JOnAS web site.
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 * See the License for the specific terms governing rights and limitations under 
 * the License.
 *
 * The Original Code is JOnAS application server code released July 1999.
 *
 * The Initial Developer of the Original Code is Bull S.A.
 * The Original Code and portions created by Bull S.A. are
 *    Copyright (C) 1999 Bull S.A. All Rights Reserved.
 *
 * Contributor(s): Xavier Spengler
 *
 * --------------------------------------------------------------------------
 * $Id: JBean.java,v 1.4 2000/12/20 10:43:11 jonas Exp $
 * --------------------------------------------------------------------------
 */


package org.objectweb.jonas_ejb.container;

import java.rmi.RemoteException;
import java.rmi.NoSuchObjectException;
import java.security.Guard;
import javax.ejb.EJBContext;
import javax.ejb.EJBHome;
import javax.ejb.EJBObject;
import javax.ejb.Handle;
import javax.ejb.RemoveException;
import javax.transaction.RollbackException;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import javax.transaction.TransactionRolledbackException;
import org.objectweb.common.RemoteObject;

// TGU
import org.objectweb.jonas_tm.*;
import java.rmi.*;
import org.objectweb.jonas.common.Trace;
// TGU


/**
 * This class implements javax.ejb.EJBObject class.
 * This class is extended by all the interposition generated classes 
 *  corresponding to an EJB.
 * It mainly implements two operations <preinvoke> and <postinvoke>
 *  that are called before and after the business method of the Bean.
 */
public abstract class JBean extends RemoteObject implements EJBObject {

    /**
     * @serial
     */
    TransactionManager tm = null;

    // Must be declared as "protected" because accessed by generated classes

    /**
     * @serial
     */
    protected JBeanHome home = null;

    /**
     * @serial
     */
    protected JContainerImpl container = null;

    /**
     * @serial
     */
    Guard methodGuard = null;

    /**
     * @serial
     */
    protected Handle handleEJBObject = null;

    /**
     * true if remote object no more accessible
     * @serial
     */
    protected boolean nosuchobject = false;
   
    /**
     * Constructor for a type of bean
     *
     * @param      EJBhome home
     * @exception  RemoteException
     */
    JBean(JBeanHome home) throws RemoteException {
	this.home = home;
	container = home.getContainer();
	methodGuard = container.getMethodGuard();
	tm = home.getTransactionManager();
    }

    // --------------------------------------------------------------------------
    // EJBObject implementation
    // --------------------------------------------------------------------------

    /**
     * Obtains the enterprise Bean's home interface. The home interface defines the enterprise
     *  Bean's create, finder, and remove operations.
     *
     * @return    A reference to the enterprise Bean's home interface. 
     * @exception RemoteException: Thrown when the method failed due to a system-level failure. 
     *
     */
    public EJBHome getEJBHome() throws RemoteException {
	if (nosuchobject)
	    throw new NoSuchObjectException("EJBObject already removed");
	return (EJBHome) home;  
    }

    /**
     * Obtains the primary key of the EJB object.
     *
     * @return    The EJB's object primary key.
     * @exception RemoteException: Thrown when the method failed due to a system-level failure. 
     *
     */
    abstract public Object getPrimaryKey() throws RemoteException;
  

    /**
     * Removes the EJB object.
     *
     * @exception RemoteException: Thrown when the method failed due to a system-level failure. 
     * @exception RemoveException: The enterprise Bean or the container does not allow 
     *            destruction of the object.
     *
     */
    abstract public void remove() throws RemoteException, RemoveException;


    /**
     * Obtains a handle for the EJB object. The handle can be used at later time to re-obtain
     *  a reference to the EHB object, possibly in a different Java Virtual Machine
     *
     * @return    A handle for the EJB object. 
     * @exception RemoteException: Thrown when the method failed due to a system-level failure. 
     *
     */
    abstract public Handle getHandle() throws RemoteException;

    /**
     * Tests if a given EJB is identical to the invoked EJB object.
     *
     * @param     EJBObject obj - An object to test for identity with the invoked object. 
     * @return    True if the given EJB object is identical to the invoked object. 
     * @throws RemoteException: Thrown when the method failed due to a system-level failure.
     */
    abstract public boolean isIdentical(EJBObject obj) throws RemoteException;

    // --------------------------------------------------------------------------
    // Other methods
    // --------------------------------------------------------------------------
 
    /**
     * Common postinvoke routine for Session or Entity
     * See "Container Provider Responsabilities" in EJB 1.1 spec.
     */
    public void doPostInvoke(RequestCtx rctx) throws RemoteException {
	TraceEjb.debugInvoke("doPostInvoke");

	// Check reference to EJBContext
	EJBContext ctx = rctx.getEJBContext();
	if (ctx == null) {
	    TraceEjb.error("null EJBContext in RequestCtx!");
	}

	// Commit the transaction started in preInvoke
	if (rctx.mustCommit()) {
	    if (rctx.getSysExc() != null) {
		TraceEjb.error("Exception: "+(rctx.getSysExc()));
		//rollback the transaction
		try {
		    TraceEjb.error("System exc. => Rollback the transaction.");
		    tm.rollback();
		} catch (Exception e) {
		    TraceEjb.error("Exception during rollback:", e);
		}
	    } else {
		// try to commit the transaction, even in case of appl. Exc.
		// If rollbackonly, do not throw any runtime exception to the client.
		boolean rollb = ctx.getRollbackOnly();
		try {
		    if (rollb) {
			TraceEjb.verbose("doPostInvoke: Rolling back transaction");
			tm.rollback();
		    } else {
			TraceEjb.debugInvoke("doPostInvoke: Committing transaction");
			tm.commit();
		    }
		} catch (RollbackException e) {
		    // Probably a pb while writing data on database.
		    throw new RemoteException("Container cannot commit a transaction", e);
		} catch (Exception e) {
		    TraceEjb.error("doPostInvoke: ", e);
		    throw new RemoteException("Container exception", e);
		}
	    }
	}

	// We got a system Exception in business method:
	// - log exception
	// - discard instance
	// - set client transaction rollback only
	// - throw remote exception
	if (rctx.getSysExc() != null) {
	    // Log system exception
	    TraceEjb.error("System Exception in business method:"+rctx.getSysExc());

	    // If client transaction: set it rollback only and throw TransactionRolledBackException
	    Transaction currentTx = null;
	    try {
		currentTx = tm.getTransaction();
		if (currentTx != null) {
		    currentTx.setRollbackOnly();
		    throw new TransactionRolledbackException("System exception in business method");
		}
	    } catch (SystemException e) {
		TraceEjb.error("doPostInvoke: cannot set rollback only current tx:", e);
	    }
	    return;
	}

	// Resume client transaction suspended in preInvoke
	Transaction tx = rctx.getClientTx();
	if (tx != null) {
	    try {
		tm.resume(tx);
	    } catch (SystemException e) {
		TraceEjb.error("doPostInvoke: Cannot resume transaction");
	    }
	}

    }

    /**
     * 
     * Set nosuchobject if the EJBObject must not be accessible
     */
    public void noMoreInUse() {
	nosuchobject = true;
	try {
	    unexportObject(this,true);
	}catch (Exception e){
	}
    }
 
   // TGU   
    protected void raiseUserException(Exception e) {
        Current ut = Current.getCurrent();
	// Should be never true
	if (ut == null) return; 

	PropagationContext pctx = ut.getPropagationContext(false);

	// Should be never true
	if (pctx == null) return;
	pctx.setUserException(e);
    }    	
   // TGU   
    
    protected void finalize() {
	TraceEjb.debugInvoke("JBean.finalize");
    }
}
