groovy
  1. groovy
  2. GROOVY-861

Expclit Groovy Types or embedding in Tomcat

    Details

    • Type: Bug Bug
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 1.0-JSR-1
    • Fix Version/s: 1.0-JSR-3
    • Component/s: bytecode, class generator
    • Labels:
      None
    • Number of attachments :
      0

      Description

      • Alan <alan-groovy-user@engrm.com> [2005-06-03 14:57]:

      >> * Alan <alan-groovy-user@engrm.com> [2005-05-21 12:31]:
      >>> > * Christof Vollrath <christof@taobits.net> [2005-05-20 15:13]:
      >>>> > > I had the same problem while developping GvTags (www.gvtags.org).
      >>>> > > I solved it by writing a proxy object for HttpServletRequest in Java,
      >>>> > > which accesses HttpServletRequest and is included in my application,
      >>>> > > so that it can be accessed by Groovy.

      >>>> > > This is much better then turning bytecode compilation off.

      >>>> > > But I would be happy if you find a solution without the need for
      >>>> > > a proxy object.

      >>> > The following is a bunch of guessing. Not wild guesses, I don't
      >>> > think, reasonable guesses, but I've not read through the source
      >>> > code for Groovy nor Tomcat. I claim no experience, nor
      >>> > authority.
      >>> >
      >>> > The Groovy ClassLoader (this is a guess) gets a copy of the class
      >>> > file as a stream, using the ClassLoader().getResource() method.
      >>> > It then runs it through ASM to generate bytecode for a proxy
      >>> > object that supports the GroovyObject interface.
      >>> >
      >>> > Tomcat has a special class loader (guessing) to support the
      >>> > class loading requirements of web applications, each web
      >>> > application can have it's own class path. The servlet class
      >>> > loader and the Tomcat application class loader share an instance
      >>> > of the interface HttpServletRequest (and friends), but the
      >>> > implementation is not available as a class file to the servlet
      >>> > class loader. Only the class in memory.
      >>> >
      >>> > The easy solution was to put a set of proxy classes in place,
      >>> > since their bytecode will be avaialable to the servlet class
      >>> > loader. This makes the problem go away. It is very easy to do. I
      >>> > knocked out all the proxy objects using Eclipse's refactoring
      >>> > tricks. It would be a nice to have for the Groovy servlet. We
      >>> > could add a switch to use reflection or use proxies. It is
      >>> > better than turning bytecode compilation off.

      >>> > Then to go without the proxy object requires looking at the class
      >>> > loader implementation in Tomcat. The class file is probably
      >>> > available through the parent class loader. However the parent
      >>> > class loader is probably protected by Java security. For all I
      >>> > know, Groovy might already make the attempt to search the parent
      >>> > class loader, but backs off quietly on a security exception.

      >>> > Including the Tomcat jars in a web application archive is a
      >>> > non-starter. A very broken solution.

      >>> > Another solution might be to create a GroovyObject around the
      >>> > interface, so that the Groovy code:

      >>> > HttpSession session = request.getSession()
      >>> > Would create a GroovyObject that wraps the HttpSession
      >>> > interface, rather than the actual implementation. This is what I
      >>> > expected to happen when I added the explicit casts.

      >> This last part here, am I on the right track?

      I'm thinking that you'd want to have the nice features of
      Groovy's full reflection when you can have them.

      ---- In Java ----

      import com.fubar.Foo;
      import com.fubar.Bar;
      import com.fubar.Fubar;

      Foo foo = new Fubar();
      Bar bar = (Bar) foo;

      bar.bar();

      ---- In Groovy ----

      import com.fubar.Foo;
      import com.fubar.Fubar;

      Foo foo = new Fubar()
      foo.bar()


      Thus, the current design, where the GroovyObject is devined from
      the bytecode of the class file is probably correct.

      The ideas I had before about casts being explicit, probably not
      so hot, unless there is a way in groovy of specifying any.

      def doFoo(foo)

      { foo.bar() }

      def doFoo(Foo foo)

      { foo.bar() // Exception raised. }

      Otherwise, I'd imagine that it is done in the class loader,
      which could fall back to reflection if the bytecode for the
      underlying object could not be found. And by falling back to
      refelction, I'd imagine that it would only use reflection to
      enumerate the interfaces and base classes, and create a
      GroovyObject that implements as many as are available.

        Activity

        Hide
        Christian Stein added a comment -

        Created the issue for not losing the information gathered and solutions proposed. Alan and me need some feedback from old Groovy hands and heads first.

        Show
        Christian Stein added a comment - Created the issue for not losing the information gathered and solutions proposed. Alan and me need some feedback from old Groovy hands and heads first.
        Hide
        Christian Stein added a comment -

        Still collection information, Oyvind wrote:

        Yes, here is the stacktrace. What I observe is that Tomcat set up different
        classloaders for web-apps than for the container and that is the way it.
        should be. Webapps should not need to know about the internal Tomcat
        classes like e.g. ResponseFacade, but it should be enough to know the
        interface HttpServletResponse.

        What Groovy seem to do is to generate a reflector using the ResponseFacade
        (gjdk.org.apache.catalina.connector.HttpResponseFacade_GroovyReflector),
        instead of the HttpServletResponse interface.

        I put everything in a jar file, and subfolders inside the jar
        corresponding to the package hierarchy.

        Regards
        Řyvind
        ---------
        java.lang.NoClassDefFoundError: org/apache/catalina/connector/ResponseFacade
        at
        gjdk.org.apache.catalina.connector.HttpResponseFacade_GroovyReflector.invoke(Unknown
        Source)
        at groovy.lang.MetaMethod.invoke(MetaMethod.java:110)
        at groovy.lang.MetaClass.doMethodInvoke(MetaClass.java:1386)
        at groovy.lang.MetaClass.invokeMethod(MetaClass.java:309)
        at org.codehaus.groovy.runtime.Invoker.invokeMethod(Invoker.java:145)
        at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:104)
        at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod(ScriptBytecodeAdapter.java:85)
        at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeNoArgumentsMethod(ScriptBytecodeAdapter.java:154)
        at wcomp.gfunc.Test.execute(Unknown Source)
        at wcomp.core.SumFunc.execute(SumFunc.java:55)
        at wcomp.ScriptServlet.doContent(ScriptServlet.java:137)
        at wcomp.ScriptServlet.doGet(ScriptServlet.java:59)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:748)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:200)

        Show
        Christian Stein added a comment - Still collection information, Oyvind wrote: Yes, here is the stacktrace. What I observe is that Tomcat set up different classloaders for web-apps than for the container and that is the way it. should be. Webapps should not need to know about the internal Tomcat classes like e.g. ResponseFacade, but it should be enough to know the interface HttpServletResponse. What Groovy seem to do is to generate a reflector using the ResponseFacade (gjdk.org.apache.catalina.connector.HttpResponseFacade_GroovyReflector), instead of the HttpServletResponse interface. I put everything in a jar file, and subfolders inside the jar corresponding to the package hierarchy. Regards Řyvind --------- java.lang.NoClassDefFoundError: org/apache/catalina/connector/ResponseFacade at gjdk.org.apache.catalina.connector.HttpResponseFacade_GroovyReflector.invoke(Unknown Source) at groovy.lang.MetaMethod.invoke(MetaMethod.java:110) at groovy.lang.MetaClass.doMethodInvoke(MetaClass.java:1386) at groovy.lang.MetaClass.invokeMethod(MetaClass.java:309) at org.codehaus.groovy.runtime.Invoker.invokeMethod(Invoker.java:145) at org.codehaus.groovy.runtime.InvokerHelper.invokeMethod(InvokerHelper.java:104) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeMethod(ScriptBytecodeAdapter.java:85) at org.codehaus.groovy.runtime.ScriptBytecodeAdapter.invokeNoArgumentsMethod(ScriptBytecodeAdapter.java:154) at wcomp.gfunc.Test.execute(Unknown Source) at wcomp.core.SumFunc.execute(SumFunc.java:55) at wcomp.ScriptServlet.doContent(ScriptServlet.java:137) at wcomp.ScriptServlet.doGet(ScriptServlet.java:59) at javax.servlet.http.HttpServlet.service(HttpServlet.java:748) at javax.servlet.http.HttpServlet.service(HttpServlet.java:853) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:200)
        Hide
        Christian Stein added a comment -

        And another one from Oyvind:

        Now, I fully understand what happens. The Invoker.InvokeMethod in the
        Groovy runtime uses object.getClass() to get the class of the object
        which is used to generate a reflector. Of course, getClass() returns
        the implementing class, not the declared type, and in my case the
        implementing class wasn't directly available.

        My workaround is simply to write a wrapper method in Java:

        public static PrintWriter getWriter(HttpServletResponse res)
        throws IOException

        { return res.getWriter(); }

        In the Groovy program i replace
        out = res.getWriter();
        with
        out = getWriter(res);

        And it works.

        Regards
        Řyvind

        The snippet looks like:

        public class Test...
        {
        ...
        public void execute (HttpServletRequest req, HttpServletResponse res)
        {
        try

        { out = getWriter(res); out.println("Hello, this is the groovy test function"); }

        catch (Throwable e)

        { ... }

        }
        }

        Show
        Christian Stein added a comment - And another one from Oyvind: Now, I fully understand what happens. The Invoker.InvokeMethod in the Groovy runtime uses object.getClass() to get the class of the object which is used to generate a reflector. Of course, getClass() returns the implementing class, not the declared type, and in my case the implementing class wasn't directly available. My workaround is simply to write a wrapper method in Java: public static PrintWriter getWriter(HttpServletResponse res) throws IOException { return res.getWriter(); } In the Groovy program i replace out = res.getWriter(); with out = getWriter(res); And it works. Regards Řyvind The snippet looks like: public class Test... { ... public void execute (HttpServletRequest req, HttpServletResponse res) { try { out = getWriter(res); out.println("Hello, this is the groovy test function"); } catch (Throwable e) { ... } } }
        Hide
        Christian Stein added a comment -

        And a last one from Alan:

        >> I tested by calling it from my Groovy function, before I use the
        >> the response object. My Groovy code is called from a Java servlet.
        >> The call can be done from the Java code as well like done in the
        >> GroovyServlet class.
        >>
        >> Yes, my problem disappears.
        >>
        >> public void execute (HttpServletRequest req, HttpServletResponse res)
        >> {
        >> try
        >>

        { >> MetaClass.setUseReflection(true); >> out = res.getWriter(); >> out.println("hello, world"); >> }

        >> catch (Throwable e)
        >>

        { ... }


        >> }

        This is an issue I was barking about last month. I'm trying to
        float the issue again.

        I solved it by creating proxy classes for the
        HttpServletRequest and HttpServletResponse interfaces, including
        all the interfaces in javax.servlet that are visible through
        those interfaces.

        Basically, the Tomcat implementation is not avaiable to Groovy,
        and Groovy wants to manipulate the bytecode of that
        implementation.

        Show
        Christian Stein added a comment - And a last one from Alan: >> I tested by calling it from my Groovy function, before I use the >> the response object. My Groovy code is called from a Java servlet. >> The call can be done from the Java code as well like done in the >> GroovyServlet class. >> >> Yes, my problem disappears. >> >> public void execute (HttpServletRequest req, HttpServletResponse res) >> { >> try >> { >> MetaClass.setUseReflection(true); >> out = res.getWriter(); >> out.println("hello, world"); >> } >> catch (Throwable e) >> { ... } >> } This is an issue I was barking about last month. I'm trying to float the issue again. I solved it by creating proxy classes for the HttpServletRequest and HttpServletResponse interfaces, including all the interfaces in javax.servlet that are visible through those interfaces. Basically, the Tomcat implementation is not avaiable to Groovy, and Groovy wants to manipulate the bytecode of that implementation.
        Hide
        Christian Stein added a comment -

        Jetty 5.1.3 says when logging the first request object...

        c = request.getClass() : "class org.mortbay.jetty.servlet.ServletHttpRequest"
        l = c.getClassLoader() : "sun.misc.Launcher$AppClassLoader@53ba3d"
        l.getClass() : "class sun.misc.Launcher$AppClassLoader"

        Tomcat 5.5.9 uses its own implementations and says...

        c = request.getClass() : "class org.apache.catalina.connector.RequestFacade"
        l = c.getClassLoader() : "org.apache.catalina.loader.StandardClassLoader@1ef8cf3"
        l.getClass() : "class org.apache.catalina.loader.StandardClassLoader"

        Show
        Christian Stein added a comment - Jetty 5.1.3 says when logging the first request object... c = request.getClass() : "class org.mortbay.jetty.servlet.ServletHttpRequest" l = c.getClassLoader() : "sun.misc.Launcher$AppClassLoader@53ba3d" l.getClass() : "class sun.misc.Launcher$AppClassLoader" Tomcat 5.5.9 uses its own implementations and says... c = request.getClass() : "class org.apache.catalina.connector.RequestFacade" l = c.getClassLoader() : "org.apache.catalina.loader.StandardClassLoader@1ef8cf3" l.getClass() : "class org.apache.catalina.loader.StandardClassLoader"
        Hide
        blackdrag blackdrag added a comment -
        Show
        blackdrag blackdrag added a comment - this issue should be fixed now, see http://cvs.codehaus.org/changelog/groovy?cs=MAIN:blackdrag:20050630161205&csize=2 for details
        Hide
        Christian Stein added a comment -

        blackdrag fixed it.

        Show
        Christian Stein added a comment - blackdrag fixed it.

          People

          • Assignee:
            blackdrag blackdrag
            Reporter:
            Christian Stein
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: