/*
 * JBoss, Home of Professional Open Source
 *
 * Distributable under LGPL license.
 * See terms of license at gnu.org.
 */
package org.jboss.seam.intercept;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.PostActivate;
import javax.ejb.PrePassivate;
import javax.interceptor.AroundInvoke;
import javax.interceptor.AroundTimeout;
import javax.interceptor.InvocationContext;

import org.jboss.seam.log.LogProvider;
import org.jboss.seam.log.Logging;
import org.jboss.seam.Component;
import org.jboss.seam.Seam;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.intercept.InterceptorType;

/**
 * Controller interceptor for server-side interceptors of
 * EJB3 session bean components.
 * 
 * @author Gavin King
 */
public class SessionBeanInterceptor extends RootInterceptor
{
   private static final long serialVersionUID = -7474586917199426345L;
   private static final LogProvider log = Logging.getLogProvider(SessionBeanInterceptor.class);
   
   public static ThreadLocal<Component> COMPONENT = new ThreadLocal<Component>();

   /**
    * Called when instantiated by EJB container.
    * (In this case it might be a Seam component,
    * but we won't know until postConstruct() is
    * called.)
    */
   public SessionBeanInterceptor()
   {
      super(InterceptorType.SERVER);
   }
   
   @AroundInvoke
   public Object aroundInvoke(InvocationContext invocation) throws Exception
   {
      return invoke( new EJBInvocationContext(invocation), EventType.AROUND_INVOKE);
   }
   
   @AroundTimeout
   public Object aroundTimeout(InvocationContext invocation) throws Exception
   {
      return invoke( new EJBInvocationContext(invocation), EventType.AROUND_TIMEOUT);
   }
   
   @PrePassivate
   public void prePassivate(InvocationContext invocation)
   {
      invokeAndHandle( new EJBInvocationContext(invocation), EventType.PRE_PASSIVATE);
   }
   
   @PostActivate
   public void postActivate(InvocationContext invocation)
   {
      invokeAndHandle( new EJBInvocationContext(invocation), EventType.POST_ACTIVATE);
   }
   
   @PreDestroy
   public void preDestroy(InvocationContext invocation)
   {
      invokeAndHandle( new EJBInvocationContext(invocation), EventType.PRE_DESTORY);
   }
   
   @PostConstruct
   public void postConstruct(InvocationContext invocation)
   {
      Component invokingComponent = SessionBeanInterceptor.COMPONENT.get();
      Object bean = invocation.getTarget();
      if ( invokingComponent!=null && invokingComponent.getBeanClass().isInstance(bean) )
      {
         //the session bean was obtained by the application by
         //calling Component.getInstance(), could be a role
         //other than the default role
         //note: minor bug here, since if we got another instance of the same
         //      bean from JNDI or @EJB while constructing a component,
         //      or in an interceptor while calling a component,
         //      this will cause that bean to think it is an instance of the
         //      component role (rather than the default role)
         if ( log.isTraceEnabled() ) 
         {
            log.trace("post construct phase for instance of component: " + invokingComponent.getName());
         }
         init(invokingComponent);
      }
      else if ( bean.getClass().isAnnotationPresent(Name.class) )
      {
         //the session bean was obtained by the application from
         //JNDI, or @EJB (or it was an MDB), so assume the default role
         //TODO: look at more than just @Name, consider components.xml
         String defaultComponentName = bean.getClass().getAnnotation(Name.class).value();
         if ( log.isTraceEnabled() ) 
         {
            log.trace("post construct phase for component instantiated outside Seam, assuming default role: " + defaultComponentName);
         }
         init( Seam.componentForName(defaultComponentName) );
      }
      else
      {
         if ( log.isTraceEnabled() ) 
         {
            log.trace("post construct phase for non-component bean");
         }
         initNonSeamComponent();
      }
      
      postConstruct(bean);
      invokeAndHandle( new EJBInvocationContext(invocation), EventType.POST_CONSTRUCT );
   }
 

}
