Showing posts with label seam. Show all posts
Showing posts with label seam. Show all posts

Monday, March 31, 2014

Seam 2 Gotchas - Part 1

A few common mistakes I've seen made with Seam 2:

Referencing EJB Components Directly

I think it's pretty easy to know that referencing a Seam component that happens to be an EJB directly (with @EJB or with a JNDI lookup) is probably not going to end well, but a novice Seam developer might make this mistake.  This mistake is more likely when writing code in a non-JSF part of the system (e.g. a Servlet).

Here's what you can do about it:
  1. Make sure EJB Seam components are injected using @In, or look them up using Seam.   Make sure you have a clear distinction between 'regular' EJBs and Seam component EJBs.  Here are the two main things to avoid:    
    • INJECTING SEAM COMPONENTS WITH @EJB - Use @In instead!
    • LOOKING UP SEAM COMPONENTS WITH JNDI - Use Component.getInstance(name) or  Contexts.lookupInStatefulContexts(name) instead!
  2. In a non-JSF environment, use ContextualHttpServletRequest, for example, in a Servlet:
        @Override
        protected void service(HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException
        {
            //... do some stuff...
            new ContextualHttpServletRequest(request) 
            {
                @Override
                public void process() throws Exception
                {
                    // Access the components, do work.   The contexts will be properly set up here.
                    MyComponent component = (MyComponent)Component.getInstance("myComponent");
                }
            }.run();    // Run the request.
     
        }
    
    See this JBoss community post.   Personally, I strongly prefer using ContextFilter.
  3. In a non-JSF environment, apply the ContextFilter.   This will automatically wrap all requests in ContextualHttpServletRequest().

    For example:  <web:context-filter url-pattern="/servlet/*"/> in components.xml.
Note that when using ContextFilter or ContextualHttpServletRequest, exceptions may be handled differently than you might expect!

If anything inside the ContextFilter/ContextualHttpServletRequest throws an exception, then all the contexts will be torn down.   You may get other filters throwing java.lang.IllegalStateException: No active event context! after the ContextFilter/ContextualHttpServletRequest has finished!

Component Lookup - Component is not automatically created?

While injecting Seam components with @In is the simplest way to access another component, there are cases where a lookup is needed (e.g. in a Servlet).   The problem is, there is more than one way to look up components, and the method used to look up the component will determine the behavior:

  1. Contexts.lookupInStatefulContexts(name) - This is similar to @In : It will not create components automatically!
  2. Component.getInstance(name) - This is similar to @In(autocreate=true) : Components will be created automatically if they don't exist.
 Make sure you use the appropriate method for your use case.

Injected values are null?!?

If you are used to other DI frameworks, you may be expecting injected values to stick around.   Not always the case with Seam :

Seam will set injected fields to null when the request is finished.  Injection / uninjection happen before and after every method invocation.

So, if you access an instance outside of Seam's control, then the injected values might be null.


Thursday, December 12, 2013

Write Your Own Seam 2 Interceptors

When using Seam2, there are two main ways to implement a backing bean component:
  1. EJB3 - If EJB3 is available, this is a good choice.   Not only do you get all the Seam capabilities (@In, etc.) you also get all the EJB3 capabilties such as Interceptors.
  2. POJO - This is a good choice when EJB3 is not available, or if for some reason you are EJB-averse (you should see a doctor about that).
Today I was working with some existing POJO Seam components that could not use the nice EJB3 interceptors that I had already created.   I usually just convert these to stateful session beans, but I decided I'd try my hand at using a Seam interceptor.

What you need to create a Seam interceptor

  1. An interceptor class, annotated with  org.jboss.seam.annotations.intercept.Interceptor.   Usually the @Interceptor annotation will specify the interceptor order, for example:

    @Interceptor(around = {
            BijectionInterceptor.class,
            MethodContextInterceptor.class,
            ConversationInterceptor.class,
            SynchronizationInterceptor.class,
            ConversationalInterceptor.class,
            RemoveInterceptor.class,
            SeamInterceptor.class,
            SecurityInterceptor.class,
            TransactionInterceptor.class,
            EventInterceptor.class,
            HibernateSessionProxyInterceptor.class,
            ManagedEntityInterceptor.class
    })
    public class MyInterceptor {
    ...
        @AroundInvoke
        public Object aroundInvoke(InvocationContext invocation) throws Exception
        {
    ...    
    }
    
    Note: This uses org.jboss.seam.annotations.intercept.AroundInvoke, not the EJB3 annotation.
  2. Extend AbstractInterceptor to simplify getting access to the Component (the meta-data) for the intercepted object.
  3. An annotation that allows you to apply the interceptor to components.

    import org.jboss.seam.annotations.intercept.Interceptors;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Interceptors(MyInterceptor.class)
    public @interface MyInterceptorAnnotation {}
    
    
  4. The component class, with the custom annotation on it.

    @Name("myBackingBean")
    @Scope(ScopeType.CONVERSATION)
    @MyInterceptorAnnotation
    public class MyBackingBean implements Serializable
    {
       ...
    

Similarities and Differences between Seam Interceptors and EJB3 Interceptors

  • In both systems, the interceptor instance is scoped to the component.   That is, there is one instance of the interceptor for each intercepted component by default.
  • Seam interceptors can be forced to be stateless, so you can have stateless interceptor instances used with stateful components.
  • EJB3 interceptors have specific lifecycle methods.   With Seam Interceptors, you need to determine this in the @AroundInvoke method.
  • It's not as easy to apply Seam Interceptors to all components.   With an EJB3 interceptor, you can specify default interceptor bindings in META-INF/ejb-jar.xml.   (I'm not currently sure how you apply custom Seam interceptors implicitly)