Thursday, 30 July 2009

Multiple Log4J instances in a WebApp

Many times I've come across the classic problem of different logging instances in an web app server. Most of them involve classloader conflicts:
  • The webapp has log4j.jar but the webapp has bundled log4j.jar as well and you get errors.
  • OR you have the log4j.jar in the application server's shared library directory and you have 2 webapps but the configuration conflict - or even just a configuration for the application server itself and one for the webapp.

I've finally come across a solution for log4j. It involves using a repository selector based on a context, this is fully described here - Supporting the log4j RepositorySelector in Servlet Containers.

So the solution to stop conflicts and independently configure each application is this.
First put ALL logging jars in the shared library directory including (log4j.jar, slf4j-api.jar, slf4j-log4j.jar, commons-logging.jar, etc). This ensures that if they one of the libraries is loaded in the main classloader it can access the other jars.
Next is a little extension for log4j: log4j-contrib. Put its jar into the shared library as well.

Next you have to configure each individual app. Add the following to the web.xml (at the top so it is the first called).
<!-- Use log4j.properties from the classpath -->
<context-param>
<param-name>log4jConfigLocations</param-name>
<param-value>logj.properties</param-value>
</context-param>
<listener>
<listener-class>com.mathieucarbou.log4j.web.ContextLoggingListener</listener-class>
</listener>


If you want to have Tomcat use log4j (not just the apps), then add the following configuration:

For catalina.bat add somewhere in the file (line 119):
rem JavaUtilLogging to SLF4J required to be on classpath.
set CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\lib\jul-to-slf4j-1.5.6.jar;%CATALINA_HOME%\lib\slf4j-api-1.5.6.jar;%CATALINA_HOME%\lib\slf4j-log4j12-1.5.6.jar;%CATALINA_HOME%\lib\log4j-1.2.15.jar

Or under linux, catalina.sh add somewhere appropriate (line 163):
# JavaUtilLogging to SLF4J required to be on classpath.
CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/lib/jul-to-slf4j-1.5.6.jar:"$CATALINA_HOME"/lib/slf4j-api-1.5.6.jar:"$CATALINA_HOME"/lib/slf4j-log4j12-1.5.6.jar:"$CATALINA_HOME"/lib/log4j-1.2.15.jar

Additionally if you put the log4j.properties in somewhere other than CATALINA_HOME/lib then you'll need to add that directory to the classpath. e.g.
rem if you put it in /conf
set CLASSPATH=%CLASSPATH%;%CATALINA_HOME%\conf

Now alter conf/logging.properties and replace the entire contents with:
handlers = org.slf4j.bridge.SLF4JBridgeHandler
.handlers = org.slf4j.bridge.SLF4JBridgeHandler

And that's it for the command line.

Running it from Eclipse

Eclipse has a guide how to set it up at Enable JULI logging in Eclipse. To set it up, with the configuration files stored in the workspace under Servers for a server named "Tomcat6-Localhost" add the following to the end of the Server's launch configuration arguments:
-Dlog4j.configuration="log4j.properties"
-Djava.util.logging.config.file="${workspace_loc:Servers/Tomcat6-Localhost-config/logging.properties}"
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
Copy in logging.properties and log4.properties into the workspace configuration directory as per the command line version.

Note: the argument -Dlog4j.configuration, sets the file name to search for on the Tomcat start up classpath. You CANNOT specify the exact file to load, instead you have to add the folder as a class folder (add external folder in eclipse), in order for it to be located.

Under the launch configuration classpath, add the following jars/folders:
  • jdk:lib/tools.jar
  • bin/bootstrap.ja
  • lib/jul-to-slf4j-1.5.6.jar
  • lib/slf4j-api-1.5.6.jar
  • lib/slf4j-log4j12-1.5.6.jar
  • lib/log4j-1.2.15.jar
  • workspace:Servers/Tomcat6-Localhost-config

Monday, 13 July 2009

Access Control in JSF using a PhaseListener

After doing a quick search of the web, I did not find any nice solutions to implementing access control in JSF. Using a servlet filter mapping seemed inadequate, and there wasn't any obvious place to start.

I initially tried using a custom NavigationHandler, however that is only used after an action is performed (e.g. #{someBean.action}), and not for directly accessing a URL (e.g. typing in /test.faces). Some more searching revealed that a PhaseListener was the place to do it. After checking the JSF lifecycles I determined that RESTORE_VIEW was the correct place to do it - ALL pages go through at least the RESTORE_VIEW and RENDER_VIEW phases.
You can check the viewId in the afterPhase (as the view has been loaded in this phase, hence can't check in beforePhase) and redirect using the navigation handler as nesecary.

Below is my implementation of it. I used a flexible inclusion/exclusion filter so I can make the rules as complex as I want. This implementation determines the highest level required for each URL and checks the current security level and redirects accordingly.
Alternatively, you could check each level inidividually - starting with LOGGED_IN, checking if the user is logged in and working up.
package devgrok.jsf;

import static devgrok.jsf.AccessControlPhaseListener.AccessLevel.ADMIN;
import static devgrok.jsf.AccessControlPhaseListener.AccessLevel.LOGGED_IN;
import static devgrok.jsf.AccessControlPhaseListener.AccessLevel.NONE;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.sun.faces.util.MessageFactory;

import devgrok.jsf.SessionForm;
import devgrok.jsf.UrlFilter;

/**
 * Phase Listener that checks the viewId (URL) against a set of filters to determine the required access level. If the
 * correct level is not there then redirect.
 * 
 * See {@link UrlFilter} for details on the url matching.
 * 
 * @author Chris Watts 2009
 * 
 */
public class AccessControlPhaseListener implements PhaseListener
{
  /** Logger for this class */
  private static final Logger log = LoggerFactory.getLogger(AccessControlPhaseListener.class);

  /** */
  private static final long serialVersionUID = 1L;
  private final static String SESSION_BEAN = "sessionBean";
  private final HashMap<AccessLevel, List<UrlFilter>> levelFilters = new HashMap<AccessLevel, List<UrlFilter>>();

  public enum AccessLevel
  {
    NONE, LOGGED_IN, USER_ACTIVE, ADMIN;
  }

  /**
    * 
    */
  public AccessControlPhaseListener()
  {
    initLevels();
    
    requires(LOGGED_IN)
      .include("*")
      .exclude("/index.xhtml")
      .exclude("/login.xhtml")
      .exclude("/user/newUser.xhtml");

    requires(USER_ACTIVE)
      .include("/user/*")
      .exclude("/user/newUser.xhtml");

    requires(ADMIN)
      .include("/admin/*");
  }

  private void initLevels()
  {
    AccessLevel[] levels = AccessLevel.values();
    for (int i = 1; i < levels.length; i++)
    {
      levelFilters.put(levels[i], new ArrayList<UrlFilter>());
    }
  }

  private UrlFilter requires(AccessLevel level)
  {
    //ALL is default
    if (level == NONE)
      return null;

    UrlFilter filter = new UrlFilter();
    List<UrlFilter> list = levelFilters.get(level);
    list.add(filter);
    return filter;
  }

  /*
   * (non-Javadoc)
   * 
   * @see javax.faces.event.PhaseListener#afterPhase(javax.faces.event.PhaseEvent)
   */
  public void afterPhase(PhaseEvent event)
  {
    try
    {
      //check have correct access
      FacesContext context = event.getFacesContext();
      HttpSession session = (HttpSession) context.getExternalContext().getSession(true);
      SessionForm sessionBean = (SessionForm) session.getAttribute(SESSION_BEAN);
      if (sessionBean == null)
      {
        log.error("Could not obtain instance of sessionBean");
        return;
      }

      //can't use this here. only valid at render response phase?
      String viewId = context.getViewRoot().getViewId();
      AccessLevel required = requiredLevel(viewId);
      log.debug("Required level={} for viewId={}", required, viewId);

      //check if page require access:
      switch (required) {
      case NONE:
        break;
      case LOGGED_IN:
        if (!sessionBean.isLoggedIn())
          redirectLogin(event.getFacesContext(), sessionBean);
        break;
      case USER_ACTIVE:
        if (!sessionBean.isActive())
          redirectActive(event.getFacesContext());
        break;
      case ADMIN:
        if (!sessionBean.isAdmin())
          redirectAdmin(event.getFacesContext());
        break;
      default:
        //error
        log.error("huh?");
        throw new IllegalArgumentException("Not a valid access level");
      }
    }
    catch (Exception e)
    {
      // TODO Auto-generated catch block
      log.error("beforePhase caught exception", e);
    }

  }

  /*
   * (non-Javadoc)
   * 
   * @see javax.faces.event.PhaseListener#beforePhase(javax.faces.event.PhaseEvent)
   */
  public void beforePhase(PhaseEvent event)
  {

  }

  private void redirectLogin(FacesContext context, SessionForm sessionForm)
  {
    //trigger login popup to be shown on render.
    sessionForm.logIn();
    addError(context, "access.loginrequired");
    context.getApplication().getNavigationHandler().handleNavigation(context, null, "index");
  }

  private void redirectActive(FacesContext context)
  {
    addError(context, "access.activerequired");
    context.getApplication().getNavigationHandler().handleNavigation(context, null, "userActivate");
  }

  private void redirectAdmin(FacesContext context)
  {
    addError(context, "access.adminrequired");
    context.getApplication().getNavigationHandler().handleNavigation(context, null, "home");
  }

  /**
   * Add keyed error/message.
   * 
   * @param level
   * @param key
   *           message key
   */
  private void addError(FacesContext context, String key)
  {
    FacesMessage fMessage = MessageFactory.getMessage(key);
    if (fMessage != null)
    {
      FacesContext facesContext = FacesContext.getCurrentInstance();
      fMessage.setSeverity(FacesMessage.SEVERITY_ERROR);
      facesContext.addMessage(null, fMessage);
    }
  }

  /**
   * Checks defined filters for view id, checks starting at the highest level down to NONE.
   * 
   * @return the matching level or {@link AccessLevel#NONE} if none matching.
   */
  private AccessLevel requiredLevel(String viewId)
  {
    AccessLevel[] levels = AccessLevel.values();
    for (int i = levels.length - 1; i > 0; i--)
    {
      if (checkLevel(levels[i], viewId))
        return levels[i];
    }

    return AccessLevel.NONE;
  }

  private boolean checkLevel(AccessLevel level, String viewId)
  {
    return matchUri(levelFilters.get(level), viewId);
  }

  private boolean matchUri(List<UrlFilter> list, String uri)
  {
    for (UrlFilter filter : list)
    {
      if (filter.matches(uri))
        return true;
    }
    return false;
  }

  /*
   * (non-Javadoc)
   * 
   * @see javax.faces.event.PhaseListener#getPhaseId()
   */
  public PhaseId getPhaseId()
  {
    //ALL access go through RESTORE_VIEW and RENDER_VIEW (even direct url)
    return PhaseId.RESTORE_VIEW;
  }

}
package devgrok.jsf;

import java.util.ArrayList;
import java.util.regex.Pattern;

/**
 * An inclusion/exclusion filterset, similar to ant's fileset but does not support directories in the same style(**,
 * etc).
 * 
 * For example:
 * <ul>
 * <li>/servlet/* matches all urls starting with "/servlet/" e.g. /servlet/this.html
 * <li>*.do matches all urls that end in ".do" - e.g. mypage.do
 * <li>/servlet/*.do matches all urls starting with "/servlet/" and end in ".do"  - e.g. /servlet/mypage.do
 * </ul>
 * 
 * @author Chris Watts 2009
 * 
 */
public class UrlFilter
{
  private ArrayList<Pattern> include = new ArrayList<Pattern>();
  private ArrayList<Pattern> exclude = new ArrayList<Pattern>();

  public UrlFilter()
  {

  }

  /**
   * Include the wildcard(*) built pattern.
   * 
   * @param pattern
   * @return
   */
  public UrlFilter include(String pattern)
  {
    include.add(generateExpression(pattern));
    return this;
  }

  /**
   * Exclude the wildcard(*) built pattern.
   * 
   * @param pattern
   * @return
   */
  public UrlFilter exclude(String pattern)
  {
    exclude.add(generateExpression(pattern));
    return this;
  }

  /**
   * Checks to see if uri matches at least ONE inclusion filter and doesn't match ANY exclusion filters.
   * 
   * @param uri
   * @return
   */
  public boolean matches(String uri)
  {
    boolean match = false;

    //check inclusions
    for (Pattern pattern : include)
    {
      match = match || pattern.matcher(uri).matches();
    }

    if (!match)
      return false;

    //check exclusions
    for (Pattern pattern : exclude)
    {
      match = match && !pattern.matcher(uri).matches();
    }
    return match;
  }
  
  /** regular expression special character */
  private static char[] specialChars = { '[', '\\', '^', '$', '.', '|', '?', '*', '+', '(', ')' };

  /**
   * 
   * @param input
   * @return
   */
  private static Pattern generateExpression(String input)
  {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < input.length(); i++)
    {
      char letter = input.charAt(i);
      if (letter == '*')
      {
        sb.append(".*");
      }
      else if (contains(specialChars, letter))
      {
        sb.append("\\" + letter);
      }
      else
      {
        sb.append(letter);
      }
    }
    return Pattern.compile(sb.toString());
  }

  private static boolean contains(char[] array, char value)
  {
    if (array == null || array.length == 0)
    {
      return false;
    }

    for (int i = 0; i < array.length; i++)
    {
      char o = array[i];
      if (o == value)
      {
        return true;
      }
    }

    return false;
  }
}

Update 2010/10/21: Here is a zip of the source

JSF - Injecting Managed Beans into Managed Beans

Using Google Guice and JSF with something like GuiceSF to inject the JSF managed beans (backing beans) gives you a lot of power over the injection. However, by doing so JSF no longer resolves the EL expressions from faces-config.xml and injects them into the managed beans. An easy way around this, and somewhat cleaner, is to create an annotation that is 'bound' to an EL expression. To do so requires the use of GuiceyFruit's extensions to Guice.

First create the annotation:
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import com.google.inject.BindingAnnotation;

/** 
 * Injection by expression language.
 * @see com.google.inject.name.Named
 */
@BindingAnnotation
@Target( { FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
public @interface NamedEL
{
   /** expression language (without the #{}). */
   String value();
}

Next you configure the annotation and the provider inside your module:
public final class GuiceModule extends GuiceyFruitModule
{
   @Override
   protected void configure()
   {
      bindAnnotationInjector(NamedEL.class, ELProvider.class);
   }
}

And finally the provider:
public class ELProvider extends AnnotationMemberProviderSupport<NamedEL>
{
   /** Logger for this class */
   private static final Logger log = LoggerFactory.getLogger(ELProvider.class);

   private Injector injector;
   private FacesContext context;

   @Inject
   public ELProvider(Injector injector)
   {
      this.injector = injector;
   }

   public FacesContext getContext()
   {
      return context;
   }

   @Inject(optional = true)
   public void setContext(FacesContext context)
   {
      this.context = context;
   }

   public boolean isNullParameterAllowed(NamedEL annotation, Method method, Class<?> parameterType, int parameterIndex)
   {
      return false;
   }

   protected Object provide(NamedEL resource, Member member, TypeLiteral<?> requiredType, Class<?> memberType, Annotation[] annotations)
   {
      String name = getName(resource, member);

      Binding<?> binding = Injectors.getBinding(injector, Key.get(requiredType, Names.named(name)));
      if (binding != null)
      {
         return binding.getProvider().get();
      }

      try
      {
         if (context == null)
         {
            context = FacesContext.getCurrentInstance();
         }
         return getBean(name, requiredType.getRawType());
      }
      catch (ProvisionException e)
      {
         throw e;
      }
      catch (Exception e)
      {
         throw new ProvisionException("Failed to find name '" + name + "' ExpressionResolver. Cause: " + e, e);
      }
   }

   String getName(NamedEL resource, Member member)
   {
      String answer = resource.value();
      if (answer == null || answer.length() == 0)
      {
         answer = member.getName();
      }
      if (answer == null || answer.length() == 0)
      {
         throw new IllegalArgumentException("No name defined");
      }
      return answer;
   }
   
   private <T> T getBean(String expr, Class<T> expected)
   {
      try
      {
         Application app = context.getApplication();
         ValueExpression ve = app.getExpressionFactory().createValueExpression(context.getELContext(), "#{" + expr + "}", expected);
         return (T) ve.getValue(context.getELContext());
      }
      catch (RuntimeException e)
      {
         log.error("getBean caught RuntimeException", e);
         throw e;
      }
   }
}
It uses a custom FacesContext provider bound to ServletScopes.REQUEST but because its optional it defaults to looking it up the standard JSF way. This allows startup time initialisation of singletons, which occurs outside the request scope.

Usage

To use it is pretty simple. Say you had a bean called mybean, then the following would inject it during the normal creation time (e.g. when first referencing a session scoped bean).
/** managed bean 'mybean' */
@NamedEL("mybean")
private String mybean;

/** request parameter 'key' */
@NamedEL("param.key")
private String paramKey;
The second reference is a nifty trick which injects the value of the request parameter 'key'.

Resources

Guicesf seems to be down so here are my copies of them (slightly modified). binary source
Source for this example: download source

Wednesday, 8 July 2009

EJB3 Stateful Session Beans with ejbCreate

I had trouble obtaining a stateful session bean using the EJB3 pattern when trying to pass initial data to it. You can use injection to create instances of stateful beans by simply declaring an instance variable in a bean, for example: @EJB MyStatefulLocal mylocal;. But this doesn't allow you to pass any data on creation. The answer is to use a EJBLocalHome interface like in EJB2 but it's a bit complicated with all the annotations. The ejbCreate method from EJB2.1 is called create in the local home interface and called whatever you want in the bean itself (as long as it is annotated with @Init Below is an example. You can download the source here: download source.
/** Local Interface. Dummy interface to enable us to return the Business interface. */
public interface MyStatefulLocalEjb2 extends EJBLocalObject, MyStatefulLocal 
{}

/** Local Business Interface. Put business method definitions here */
public interface MyStatefulLocal
{}

/** Define method(s) which create the EJB. Must be of name create([optional parameters]),  can be anything e.g. createMyBean(). */
public interface MyStatefulLocalHome extends EJBLocalHome
{
   public MyStatefulLocalEjb2 create(MyEntity initialData);
}

/** This is the EJB itself, note that it implements the "Local Business Interface" not the "Local Interface". */
@Stateful
@LocalHome(MyStatefulLocalHome.class)
public class MyStatefulBean implements MyStatefulLocal
{
   /** matches method in {@link MyStatefulLocalHome#create(MyEntity)} */
   @Init
   public void init(MyEntity initialData)
   {
   }
}

/** This shows using it from another session bean or message bean. */
public class SomeBean
{
   @EJB
   private MyStatefulLocalHome home;

   public void obtainStateful()
   {
      MyEntity initialData = new MyEntity();
      MyStatefulLocal myStateful = home.create(initialData);
   }
}

Thursday, 2 July 2009

Reacquiring a Stateful Session Bean on NoSuchEJBException

In the current project I'm working on I'm using a Tomcat server connecting to a remote JBoss instance. We're using a Stateful Session Bean (SFSB) to hold the session information for the current user for authentication/access control purposes. The bean is stored in tomcat within the session (within a session-scoped managed bean actually) and generally it works fine.
However, if the bean is destroyed on the server then, as per the EJB specification, an NoSuchEJBException is thrown. This could be handled individually by catching the exception, printing a user friendly message and getting a new instance of the session bean. However, it you've got several method calls across multiple classes, having this try/catch code throughout all the classes bloats the code and just plain looks ugly.

I wanted a solution where I didn't need to touch the module variable that stored the EJB given to me by jboss - I wanted it to obtain a new instance itself. The reason being, if I have this instance shared across multiple JSF managed beans (some stored in session-scope, some in request), the other classes shouldn't have to know that a new instance of the EJB was obtained.

The solution is to use a nifty feature of Java reflection API - a Proxy that passes calls to a InvocationHandler instance that you write. Actually the EJB instance JBoss's context factory provides you is a Proxy (and the same is true for other app servers), which makes wrapping it even easier. Observe:
First create a class that implements InvocationHandler and a custom interface we'll use to directly access the handler called StatefulProxy:
package devgrok.proxy;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import javax.ejb.NoSuchEJBException;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Utility wrapper, automatically obtains a new instance of the session bean when the existing instance is lost. Uses
* jndi to obtain new instance.
* @author Chris Watts 2009
*/
public class JndiRemoteProxyInvocationHandler implements InvocationHandler, StatefulProxy, Serializable
{
/** Logger for this class */
private static final Logger log = LoggerFactory.getLogger(JndiRemoteProxyInvocationHandler.class);

private Proxy remoteBean = null;
private final String jndiName;

/*
* (non-Javadoc)
* 
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
if (method.getDeclaringClass().equals(StatefulProxy.class))
return method.invoke(this, args);

if (remoteBean == null)
getNewInstance();

try
{
//relay it on to JBoss's handler
InvocationHandler handler = Proxy.getInvocationHandler(remoteBean);
return handler.invoke(remoteBean, method, args);
}
catch (NoSuchEJBException e)
{
log.warn("caught exception {}", e.toString());
remoteBean = null;
throw e;
}
}

/**
* Constructs the proxy, obtaining the instance from the given jndi name.
* @param jndiName
*/
public JndiRemoteProxyInvocationHandler(String jndiName)
{
this.jndiName = jndiName;
getNewInstance();
}

/**
* Obtains a new instance of the bean from the context.
* @return
*/
public void getNewInstance()
{
try
{
Context context = new InitialContext();
this.remoteBean = (Proxy) context.lookup("java:/comp/env/" + jndiName);
}
catch (NamingException e)
{
log.warn("caught exception {}", e.toString());
throw new RuntimeException(e);
}
}

public Object getRemoteBean()
{
return remoteBean;
}

public void clearInstance()
{
remoteBean = null;
}
}
Next the interface to provide access to the utility methods:
package devgrok.proxy;

/**
* Interface to allow calls to the InvocationHandler for the stateful bean proxy.
* @author Chris Watts 2009
*/
public interface StatefulProxy<T>
{
/**
* Get a new instance of the underlying class.
*/
public void getNewInstance();

/**
* Returns the remote bean.
* 
* @return
*/
public T getRemoteBean();

/**
* Clears the stored instance of the bean (useful for logout/cleanup).
*/
public void clearInstance();
}
Now all that is left is obtaining an instance. Since I'm using Google Guice I wrote a provider (aka a factory) but you could easily adapt it:
package devgrok.proxy;

import java.lang.reflect.Proxy;

import com.google.inject.Provider;

/**
* A guice provider, providing a proxied EJB looked up by JNDI.
* @author Chris Watts 2009
*/
public class ProxyProvider<T> implements Provider<T>
{
private final String jndiName;
private final Class<T> clazz;

/**
* @param jndiName
* @param clazz
*/
public ProxyProvider(String jndiName, Class<T> clazz)
{
this.jndiName = jndiName;
this.clazz = clazz;
}

/*
* (non-Javadoc)
* 
* @see com.google.inject.Provider#get()
*/
public T get()
{
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[] { clazz, StatefulProxy.class },
new JndiRemoteProxyInvocationHandler(jndiName));
}

}
To bind it:
bind(MySessionRemote.class).toProvider(new ProxyProvider<MySessionRemote>("ejb/myEjb", MySessionRemote.class)).in(ServletScopes.SESSION);