//$Id: RemoveInterceptor.java 8626 2008-08-07 19:50:09Z pete.muir@jboss.org $
package org.jboss.seam.ejb;

import static org.jboss.seam.ComponentType.STATEFUL_SESSION_BEAN;

import java.lang.reflect.Method;
import java.rmi.RemoteException;

import javax.ejb.ApplicationException;
import javax.ejb.Remove;

import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.annotations.intercept.AroundInvoke;
import org.jboss.seam.annotations.intercept.Interceptor;
import org.jboss.seam.annotations.intercept.InterceptorType;
import org.jboss.seam.intercept.AbstractInterceptor;
import org.jboss.seam.intercept.InvocationContext;

/**
 * Removes SFSB components from the Seam context after invocation
 * of an EJB @Remove method, or when a system exception is thrown
 * from the bean.
 * 
 * @author Gavin King
 */
@Interceptor(stateless=true, type=InterceptorType.CLIENT)
public class RemoveInterceptor extends AbstractInterceptor
{
   private static final long serialVersionUID = -6693606158918954699L;
   
   private static final LogProvider log = Logging.getLogProvider(RemoveInterceptor.class);

   @AroundInvoke
   public Object aroundInvoke(InvocationContext invocation) throws Exception
   {
      //we have the method from the local interface, get the corresponding one
      //for the actual bean class (it has the @Remove annotation)
      Method removeMethod = getComponent().getRemoveMethod( invocation.getMethod().getName() );
      Object result;
      try
      {
         result = invocation.proceed();
      }
      catch (Exception exception)
      {
         removeIfNecessary(removeMethod, exception);
         throw exception;
      }
      removeIfNecessary(removeMethod);
      return result;
   }

   private void removeIfNecessary(Method removeMethod, Exception exception) {
      if ( exception instanceof RuntimeException || exception instanceof RemoteException )
      {
         if ( !exception.getClass().isAnnotationPresent(ApplicationException.class) ) 
         {
            //it is a "system exception"
            remove();
         }
      }
      else if ( removeMethod!=null )
      {
         if ( !removeMethod.getAnnotation(Remove.class).retainIfException() ) 
         {
            remove();
         }
      }
   }

   private void removeIfNecessary(Method removeMethod)
   {
      if ( removeMethod!=null ) 
      {
         remove();
      }
   }

   private void remove() 
   {
      getComponent().getScope().getContext().remove( getComponent().getName() );
      if ( log.isDebugEnabled() )
      {
         log.debug( "Stateful component was removed: " + getComponent().getName() );
      }
   }
   
   public boolean isInterceptorEnabled()
   {
      return getComponent().getType() == STATEFUL_SESSION_BEAN;
   }

}
