/*
 * JBoss, the OpenSource EJB server
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.ejb3.interceptor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import javax.ejb.EJBContext;
import javax.ejb.InvocationContext;
import org.jboss.ejb3.EJBContainerInvocation;

/**
 * @author <a href="mailto:kabir.khan@jboss.org">Kabir Khan</a>
 * @version $Revision: 1.3.6.3 $
 */
public class InvocationContextImpl implements InvocationContext
{
   private int currentInterceptor;
   private Object[] interceptors;
   private Method[] aroundInvoke;
   private Method beanAroundInvoke;
   private boolean invokeBeanAroundMethod = true;
   private HashMap metadata;

   EJBContainerInvocation wrapped;

   public InvocationContextImpl(EJBContainerInvocation inv, Object[] interceptors, Method[] aroundInvoke, Method beanAroundInvoke)
   {
      wrapped = inv;
      this.beanAroundInvoke = beanAroundInvoke;
      this.interceptors = interceptors;
      this.aroundInvoke = aroundInvoke;
   }

   public Object getBean()
   {
      return wrapped.getTargetObject();
   }

   public Method getMethod()
   {
      return wrapped.getMethod();
   }

   public Object[] getParameters()
   {
      return wrapped.getArguments();
   }

   public void setParameters(Object[] params)
   {
      wrapped.setArguments(params);
   }

   public EJBContext getEJBContext()
   {
      return (EJBContext) wrapped.getBeanContext();
   }

   public java.util.Map getContextData()
   {
      if (metadata == null) metadata = new HashMap();
      return metadata;
   }

   public Object proceed() throws Exception
   {
      if (currentInterceptor < interceptors.length)
      {
         try
         {
            int curr = currentInterceptor++;
            try
            {
               return aroundInvoke[curr].invoke(interceptors[curr], this);
            }
            catch (InvocationTargetException e)
            {
               if (e.getTargetException() instanceof Exception)
               {
                  throw ((Exception) e.getCause());
               }
               else
               {
                  throw new RuntimeException(e.getCause());
               }
            }
         }
         finally
         {
            // so that interceptors like clustering can reinvoke down the chain
            currentInterceptor--;
         }
      }
      if (invokeBeanAroundMethod && beanAroundInvoke != null)
      {
         try
         {
            invokeBeanAroundMethod = false;
            return beanAroundInvoke.invoke(getBean(), this);
         }
         catch (InvocationTargetException e)
         {
            if (e.getTargetException() instanceof Exception)
            {
               throw ((Exception) e.getCause());
            }
            else
            {
               throw new RuntimeException(e.getCause());
            }
         }
         finally
         {
            invokeBeanAroundMethod = true;
         }
      }
      try
      {
         return wrapped.invokeNext();
      }
      catch (Exception e)
      {
         throw e;
      }
      catch (Throwable t)
      {
         throw new RuntimeException(t);
      }
   }
}
