5 Replies Latest reply on Mar 19, 2007 1:06 PM by youngm

    SeamRemoting Spring DelegatingVariableResolver

    Dan Allen Master

      I realize that FacesContext is not available on a Seam remoting call because the request does not go through the JSF lifecycle. However, since the FacesContext is null, the Spring beans EL integration also goes out the window. Instead, Seam uses an internal UnifiedELValueBinding that is unaware of any custom JSF variable resolvers ;(

      Would it be possible to add a configuration in Seam to add variable resolvers when the FacesContext is not available? For those of us using Spring beans, they become pretty important during a remoting call. Naturally, it would always be possible to drop back to using lookups for the Spring application context, but that would be a step backwards.

        • 1. Re: SeamRemoting Spring DelegatingVariableResolver
          Dan Allen Master

          Oh yeah! I finally figured out a way to do away with the need for the delegating variable resolver all together, which in my mind really feels like the right solution to this problem! I am now using <seam:component> to expose my Spring bean as a native Seam component instance rather than relying on the variable resolver to look it up for me each time. Just the performance gain alone is going to make this worthwhile.

          "What was the problem before?" you ask. Ah, well, Spring proxies were giving me quite a stumble. After A LOT of debugging inside of Eclipse, I finally got to the bottom of the matter. Seam can only expose Cglib proxies created by Spring, not JDK proxies (and Spring cannot do javassist, which would be another alternative). Since the default for Spring is to use JDK proxies, I was facing a sure failure.

          Long story short, imagine that you have a TransactionProxyFactoryBean that you want to inject into a Seam managed component acting as a JSF backing bean...pretty standard stuff for Spring folks. Here is how it is done in 3 steps. (I am taking a somewhat complex example, courtesy of Appfuse).

          CourseManagerImpl.java - business object

          public class CourseManagerImpl extends org.appfuse.service.impl.GenericManagerImpl<Course, Long> implements CourseManager {
           // make CGLIB happy, feed it a default constructor
           public CourseManagerImpl() {
           super( null );
           }
          
           public CourseManagerImpl( GenericDao<Course, Long> courseDao ) {
           super( courseDao );
           }
          
           // finder methods...
          }


          applicationContext.xml - Spring configuration snippet
          <bean id="baseTransactionProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
           <!-- Seam cannot work with JDK proxies, so we must instead use Cglib-enhanced objects -->
           <property name="proxyTargetClass" value="true" />
           <property name="optimize" value="true" />
           <property name="transactionAttributes">
           <props>
           <prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
           <prop key="*">PROPAGATION_REQUIRED</prop>
           </props>
           </property>
          </bean>
          
          <bean id="courseManager" parent="baseTransactionProxy" lazy-init="true">
           <seam:component beanClass="com.example.CourseManagerImpl" />
           <property name="target">
           <bean class="com.example.CourseManagerImpl" autowire="constructor"/>
           </property>
           <property name="proxyInterfaces" value="com.example.CourseManager" />
          </bean>


          CourseListAction - Seam component / JSF backing bean
          @Name( "courseListAction" )
          @Scope( ScopeType.CONVERSATION )
          public class CourseListAction implements Serializable {
           @In( create = true )
           private CourseManager courseManager;
          
           // other stuff...
          }


          Now, the injecting of the courseManager does not rely on the Spring-JSF variable resolver and hence is available during @WebRemote calls. Keep in mind that the Spring bean must be lazy or you get strange errors on startup.

          • 2. Re: SeamRemoting Spring DelegatingVariableResolver
            youngm Novice

            Thanks for working through this and providing a solution. What "strange errors" were you getting when you bean was not marked as lazy?

            Mike

            • 3. Re: SeamRemoting Spring DelegatingVariableResolver
              youngm Novice

              I created an issue to work on better spring EL integration if you want to track it: JBSEAM-1074

              • 4. Re: SeamRemoting Spring DelegatingVariableResolver
                Dan Allen Master

                If the Spring listener is configured above the Seam listener in web.xml, then Seam complains loudly that a component is being accessed outside the context of a web application when adding the SpringComponent.

                java.lang.IllegalStateException: Attempted to invoke a Seam component outside the context of a web application


                That makes sense, I guess, because Seam needs to be up and running when the Spring beans are being processed so that it has a place to put the components.

                With the listeners in the proper order, we now arrive at the non-lazy Spring bean issue. Clearly I was talking out of my ass, because it just isn't true. Let me restate. Spring beans exposed as Seam components CAN be non-lazy. You know how these things go...you thought something had to be true when in fact it isn't. I stand corrected.

                • 5. Re: SeamRemoting Spring DelegatingVariableResolver
                  youngm Novice

                  Good news, thanks for the following up.

                  Mike