/*
 * 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: JBeanHome.java,v 1.4 2000/12/20 10:43:11 jonas Exp $
 * --------------------------------------------------------------------------
 */

package org.objectweb.jonas_ejb.container;

import java.rmi.RemoteException;
import java.security.Guard;
import java.util.Vector;
import java.util.Stack;
import java.util.EmptyStackException;
import java.util.Properties;
import javax.ejb.EJBHome;
import javax.ejb.EJBMetaData;
import javax.ejb.Handle;
import javax.ejb.HomeHandle;
import javax.ejb.RemoveException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.transaction.TransactionManager;

import org.objectweb.common.RemoteObject;
import org.objectweb.jonas_ejb.deployment.api.BeanDesc;
import org.objectweb.jonas_ejb.lib.BeanNaming;
import org.enhydra.naming.ContainerNaming;

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


/**
 * This class implements javax.ejb.EJBHome interface.
 * This class must be extended by the EJBHome class generated for a given bean.
 * 
 * Contains all information related to the bean.
 */
public abstract class JBeanHome extends RemoteObject implements EJBHome  {

    /**
     * @serial
     */
    private BeanDesc ejbDescriptor = null; 

    /**
     * @serial
     */
    private JBeanMetaData ejbMetaData = null; 

    /**
     * @serial
     */
    private String beanName = null;

    /**
     * @serial
     */
    private Properties ejb10Env = null;

    /**
     * @serial
     */
    private HomeHandle handleEJBHome = null;

    /**
     * @serial
     */
    private boolean isSession = true;

    /**
     * @serial
     */
    private String beanClassName = null;

 
    /**
     * @serial
     */
    boolean TxBeanManaged = false;
 

    /**
     * JNDI Context for the bean. 
     * @serial
     */
    protected Context JNDICtx = null;

    /**
     * @serial
     */
    ContainerNaming naming = null;

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

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

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

    /**
     * @serial
     */
    protected Stack pooledinstances = new Stack();

    // ------------------------------------------------------------------
    // Constructors
    // ------------------------------------------------------------------

    /**
     * Constructor for the base class of the specific generated Home object.
     */
    public JBeanHome(BeanDesc desc, String beanName, JContainerImpl container) throws RemoteException {

	if (TraceEjb.isDebug) TraceEjb.debug("JBeanHome constructor for "+ beanName);

	this.beanName   = beanName;
	this.container = container;
	tm = container.getTransactionManager();
	methodGuard = container.getMethodGuard();
	naming = container.getContainerNaming();

	ejbDescriptor = desc;
	BeanNaming bn = new BeanNaming(ejbDescriptor);
	beanClassName = bn.getFullDerivedBeanName();
	ejb10Env = desc.getEjb10Environment();

	try {
	    register();
	} catch(NamingException e) {
	    throw new RemoteException("failed to register bean to jndi", e);
	}
    }

    // ------------------------------------------------------------------
    // EJBHome implementation
    // ------------------------------------------------------------------

    /**
     * Obtains the EJBMetaData interface for the enterprise Bean. 
     * @return The enterprise Bean's EJBMetaData
     */
    public EJBMetaData getEJBMetaData() throws RemoteException {
	TraceEjb.debug("JBeanHome.getEJBMetaData");
	return ejbMetaData;
    }

    /**
     * Removes an EJB object identified by its handle. 
     */
    abstract public void remove(Handle handle) throws RemoteException, RemoveException;

    /**
     * Removes an EJB object identified by its primary key.
     */
    abstract public void remove(Object primaryKey) throws RemoteException, RemoveException;

    /**
     * Obtain a handle for the home object. The handle can be used at later time to re-obtain a
     * reference to the home object, possibly in a different Java Virtual Machine.
     * 
     * @return A handle for the home object.
     * 
     * @exception java.rmi.RemoteException - Thrown when the method failed due to a system-level
     * failure.
     */
    public HomeHandle getHomeHandle() throws java.rmi.RemoteException {

	TraceEjb.debug("JBeanHome.getHomeHandle()");

	if (handleEJBHome == null) {
	    handleEJBHome = new HomeHandleImpl(getDeploymentDescriptor().getJndiName(), naming.getEnv());
	}
	return handleEJBHome;
    }

    // ------------------------------------------------------------------
    // Other methods
    // ------------------------------------------------------------------

    /**
     * register this bean to JNDI (rebind)
     */
    private void register() throws NamingException {
	String name = ejbDescriptor.getJndiName();
	naming.getInitialContext().rebind(name, this);
    }

    /**
     * unregister this bean in JNDI (unbind)
     */
    protected void unregister() throws NamingException {
	String name = ejbDescriptor.getJndiName();
	naming.getInitialContext().unbind(name);
    }

    public void setEJBMetaData(JBeanMetaData md) {
	TraceEjb.debug("JBeanHome.setEJBMetaData");	
	ejbMetaData = md;
    }


    /**
     * return true if the bean manages itself the transactions
     */
    public boolean isTxBeanManaged() {
	return TxBeanManaged;
    }

    // DeploymentDescriptor
    public BeanDesc getDeploymentDescriptor() {
	TraceEjb.debug("JBeanHome.getDeploymentDescriptor()");
	return ejbDescriptor;
    }


    /**
     * Returns the EJB 1.0 style environment associated with the Bean
     */
    public Properties getEjb10Environment() {
	if (TraceEjb.isDebug) TraceEjb.debug("JBeanHome.getEjb10Environment "+beanName);
	return ejb10Env;
    }

    /**
     * Gets the full name of the Enterprise Bean's Class Name
     * (Gives the derived class generated by GenIC, if it exists)
     */
    public String getEnterpriseBeanClassName(){
	return beanClassName;
    }

    
    /**
     * Set the JNDI Context for the bean
     */
    public void initJNDIContext(Context ctx) {
	JNDICtx = ctx;
    }

    /**
     * set the Component Context for JNDI environment
     */
    public Context setComponentContext() {
	return naming.setComponentContext(JNDICtx);
    }

    /**
     * reset old Component Context for JNDI environment
     */
    public void resetComponentContext(Context oldctx) {
	naming.setComponentContext(oldctx);
    }

    /**
     * get the Container
     */
    public JContainerImpl getContainer() {
	return container;
    }

    /**
     * Check Security before calling a method.
     * @param methodSignature String representing the method
     */
    public void checkSecurity(String methodSignature) throws RemoteException {

	// A "null" string means : no security control.
	if (methodSignature.equals("null")) {
	    return;
	}
	TraceEjb.debugSecurity("JBeanHome.checkSecurity()");
	try {
	    methodGuard.checkGuard(methodSignature);
	} catch (SecurityException e) {
	    throw new RemoteException("checkSecurity", e);
	}
    }
    
    /**
     * return the reference on the Transaction Manager
     */
    public TransactionManager getTransactionManager() {
	return tm;
    }

    /**
     * preinvoke for home methods
     */
    abstract protected RequestCtx preinvoke(int txattr, String methodSignature) throws RemoteException;
    
    /**
     * postinvoke for home methods
     */
    abstract protected void postinvoke(RequestCtx rctx) throws RemoteException;
    

    // ----------------------------------------------------------------------
    // Pool of entity bean instance/context
    // -> LATER: could be replaced by the generic Pool (org.enhydra)
    // ----------------------------------------------------------------------

    /**
     * Get an instance/context from the pool
     */
    public EJBContextImpl fromThePool() {
	EJBContextImpl ec = null;
	synchronized(pooledinstances) {
	    try {
		ec = (EJBContextImpl)pooledinstances.pop();
	    } catch(EmptyStackException ex) {
		TraceEjb.debugPool("JBeanHome.fromThePool: EmptyStack");
		return ec;
	    }
	}
	return ec;
    }

    /**
     * Return an instance/context to the pool
     */
    public void toThePool(EJBContextImpl ec) {

	synchronized(pooledinstances) {
	    pooledinstances.push(ec);
	    if (TraceEjb.isDebugPool) 
		TraceEjb.debugPool("JBeanHome.toThePool: size=" + pooledinstances.size());
	}
    }

    public int poolSize(){
	return pooledinstances.size();
    }
  // 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   

}
