1 Reply Latest reply on Jan 26, 2011 5:52 PM by jeff.howard

    weld 1.1 breaks when working with proxied groovy objects

    jeff.howard

      I've been working on a webapp that uses weld 1.0 and includes quite a bit of groovy code (using groovy 1.7.6) and I've run into a show stopper while attempting to move to weld 1.1. It appears that the weld proxy mechanism in 1.1 does not work well (or at all) with groovy object proxies.  A very simple example is included below. This same code works perfectly in 1.0.x.


      First, the web bean (groovy):



      @Named("testBean")
      @SessionScoped
      class TestBean1 implements Serializable{
      
        @Inject InjectedClass test1       // proxied java class
        @Inject InjectedGroovy test2      // proxied groovy class
      
        public TestBean1(){
      
        }
      
        public void doSomeAction(AjaxBehaviorEvent event){
          System.out.println("Action called")
          try{
            new ProcessorClass().doSomeStuff(test1)
          } catch (Exception e){
            System.out.println("Failed test1")
          }
          try{
            new ProcessorClass().doSomeStuff(test2)
          } catch (Exception e){
            System.out.println("Failed test2")
          }
        }
      }
      



      The simple processor groovy class.  Simply prints the message to the console:



      class ProcessorClass {
      
        void doSomeStuff(InjectedClass c){
          System.out.println(c.message)
        }
        void doSomeStuff(InjectedGroovy c){
          System.out.println(c.message)
        }
      }





      Some facelet code to use the web bean:



              <h:form id="form">
                 <h:commandButton value="Click me" type="button">
                     <f:ajax listener="#{testBean.doSomeAction}"/>
                  </h:commandButton>
              </h:form>
              #{testBean.test1.message}<br/>
              #{testBean.test2.message}




      The injected java object (test 1):



      @Named
      @SessionScoped  // Session scoped to force weld proxy
      public class InjectedClass implements Serializable {
          public String getMessage(){
              return "Message from injected java";
          }
      }





      The injected groovy object (test2):



      @Named
      @SessionScoped // session scoped to force weld proxy
      class InjectedGroovy implements Serializable{
       String message = "Message from injected groovy"
      }




      The initial load of the facelets page works fine.  The EL expressions render as expected yielding




      Message from injected java

      Message from injected groovy

      Clicking the button in the facelet causes testBean.doSomeAction to execute.  This is where things break down. 



      Action called
      Message from injected java
      Failed test2
      



      The exception stacktrace is as follows:



      java.lang.IllegalArgumentException: object is not an instance of declaring class
              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
              at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
              at java.lang.reflect.Method.invoke(Unknown Source)
              at org.codehaus.groovy.reflection.CachedMethod.invoke(CachedMethod.java:88)
              at groovy.lang.MetaMethod.doMethodInvoke(MetaMethod.java:233)
              at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:1600)
              at groovy.lang.MetaClassImpl.getProperty(MetaClassImpl.java:3305)
              at InjectedGroovy.getProperty(InjectedGroovy.groovy)
              at org$jboss$weld$bean-flat-ManagedBean-class_InjectedGroovy_$$_WeldClientProxy.getProperty(org$jboss$weld$bean-flat-ManagedBean-class_InjectedGroovy_$$_WeldClientProxy.java)
              at org.codehaus.groovy.runtime.callsite.PogoGetPropertySite.getProperty(PogoGetPropertySite.java:47)
              at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGroovyObjectGetProperty(AbstractCallSite.java:241)
              at ProcessorClass.doSomeStuff(ProcessorClass.groovy:16)
              at ProcessorClass$doSomeStuff$0.call(Unknown Source)
              at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:40)
              at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
              at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:125)
              at TestBean1.doSomeAction(TestBean1.groovy:32)
              at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
              at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
              at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
              at java.lang.reflect.Method.invoke(Unknown Source)
              at org.jboss.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:329)
              at org.jboss.el.util.ReflectionUtil.invokeMethod(ReflectionUtil.java:342)
              at org.jboss.el.parser.AstPropertySuffix.invoke(AstPropertySuffix.java:58)
              at org.jboss.el.parser.AstValue.invoke(AstValue.java:96)
              at org.jboss.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)
              at org.jboss.weld.util.el.ForwardingMethodExpression.invoke(ForwardingMethodExpression.java:43)
              at org.jboss.weld.el.WeldMethodExpression.invoke(WeldMethodExpression.java:56)
              at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:102)
              at com.sun.faces.facelets.tag.jsf.core.AjaxBehaviorListenerImpl.processAjaxBehavior(AjaxHandler.java:447)
              at javax.faces.event.AjaxBehaviorEvent.processListener(AjaxBehaviorEvent.java:109)
              at javax.faces.component.behavior.BehaviorBase.broadcast(BehaviorBase.java:98)
              at javax.faces.component.UIComponentBase.broadcast(UIComponentBase.java:764)
              at javax.faces.component.UICommand.broadcast(UICommand.java:296)
              at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:781)
              at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1246)
              at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:77)
              at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:97)
              at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:114)
              at javax.faces.webapp.FacesServlet.service(FacesServlet.java:308)
              at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
              at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
              at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
              at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
              at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
              at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
              at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
              at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
              at org.apache.jk.server.JkCoyoteHandler.invoke(JkCoyoteHandler.java:190)
              at org.apache.jk.common.HandlerRequest.invoke(HandlerRequest.java:291)
              at org.apache.jk.common.ChannelSocket.invoke(ChannelSocket.java:769)
              at org.apache.jk.common.ChannelSocket.processConnection(ChannelSocket.java:698)
              at org.apache.jk.common.ChannelSocket$SocketConnection.runIt(ChannelSocket.java:891)
              at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
              at java.lang.Thread.run(Unknown Source)



      To summarize, the proxied java object works as expected but the proxied groovy object cannot be used outside of the bean into which it was injected.  Have you guys seen this behavior before?  As it stands now, I cannot use Weld 1.1 without rewriting a large amount of groovy code as straight java.


      Thanks for looking at this!


      --jeff howard

        • 1. Re: weld 1.1 breaks when working with proxied groovy objects
          jeff.howard

          Due to the massive response this generated I decided to do some digging to see what is going wrong in weld 1.1  ;)


          The first property is that the weld proxy mechanism must be made aware of groovy classes.  As it is now, weld happily attempts to proxy the methods from the groovy.lang.GroovyObject interface (an interface implicitly defined for all Groovy classes) which really confuses Groovy.
          Another problem surfaces when attempting to invoke a method on a groovy object through from groovy through the weld proxy.  The root of this problem seems to be the hyphens in the weld generated proxy class names.


          Here's a patch that addresses these issues and restores a bit of the groovy support in weld 1.1:




          diff --git a/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java b/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java
          index db8909e..f15ef63 100644
          --- a/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java
          +++ b/impl/src/main/java/org/jboss/weld/bean/proxy/ProxyFactory.java
          @@ -106,6 +106,18 @@ public class ProxyFactory<T>
           
              protected static final BytecodeMethodResolver DEFAULT_METHOD_RESOLVER = new DefaultBytecodeMethodResolver();
           
          +   private static final Set GROOVY_METHODS = new HashSet() {{
          +      add("invokeMethod");
          +      add("getMetaClass");
          +      add("setMetaClass");
          +      add("metaClass");
          +      add("getProperty");
          +      add("setProperty");
          +      add("$getStaticMetaClass");
          +   }};
          +
          +   private boolean isGroovyClass = false;
          +
              /**
               * created a new proxy factory from a bean instance. The proxy name is
               * generated from the bean id
          @@ -183,7 +195,7 @@ public class ProxyFactory<T>
                    }
                 }
                 String beanId = Container.instance().services().get(ContextualStore.class).putIfAbsent(bean);
          -      String className = beanId.replace('.', '$').replace(' ', '_').replace('/', '$').replace(';', '$');
          +      String className = beanId.replace('.', '$').replace(' ', '_').replace('/', '$').replace(';', '$').replace('-','_');
                 return proxyPackage + '.' + className;
              }
           
          @@ -378,7 +390,12 @@ public class ProxyFactory<T>
                 // Add interfaces which require method generation
                 for (Class<?> clazz : additionalInterfaces)
                 {
          -         proxyClassType.addInterface(clazz.getName());
          +         // Groovy classes require special handling
          +         if ("groovy.lang.GroovyObject".equals(clazz.getName())){
          +            isGroovyClass = true;
          +         } else {
          +            proxyClassType.addInterface(clazz.getName());
          +         }
                 }
                 Bytecode initialValueBytecode = new Bytecode(proxyClassType.getConstPool());
           
          @@ -626,7 +643,7 @@ public class ProxyFactory<T>
                    {
                       for (Method method : cls.getDeclaredMethods())
                       {
          -               if (!Modifier.isStatic(method.getModifiers()) && !Modifier.isFinal(method.getModifiers()) && (method.getDeclaringClass() != Object.class || method.getName().equals("toString")))
          +               if (!Modifier.isStatic(method.getModifiers()) && !Modifier.isFinal(method.getModifiers()) && (method.getDeclaringClass() != Object.class || method.getName().equals("toString")) && !(isGroovyClass && GROOVY_METHODS.contains(method.getName())) )
                          {
                             try
                             {
          



          There is still an issue with the proxies generated by InterceptedSubclassFactory.java that I haven't been able to wrap my noodle around yet.