2010-01-25

GWT with RPCs on Tomcat 6

The Tomcat Security Manager has bitten me again.
I'm liking GWT development with the Eclipse plugin. I ran through the StockWatcher tutorial, added RPCs and it was working great in hosted mode, but when I deployed it on Tomcat 6 on Ubuntu 9.04, RPCs failed with the following:
The call failed on the server; see server log for details
There were no errors in the catalina log, so following troubleshooting in the GWT dev guide on RPC deployment, I turned on access logging in /etc/tomcat6/server.xml by uncommenting the following block near the end:
<!-- Access log processes all example.
     Documentation at: /docs/config/valve.html -->
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
       prefix="localhost_access_log." suffix=".txt" pattern="common" resolveHosts="false"/>
Restarted Tomcat, tried the app again, and saw a whole lot of these in the access log:
192.168.1.22 - - [20/Jan/2010:23:59:57 -0600] "POST /StockWatcher/stockwatcher/stockPrices HTTP/1.1" 500 57
I verified that the URL used in setServiceEntryPoint(URL) and the <url-pattern> in my web.xml matched, then did more searching and found a post to the GWT google group. There are a couple of good replies in there, and it seems to come down to this: reflection is used by GWT RPC to serialize objects for transmission to the client browser, but it requires access to private members, which Tomcat doesn't allow by default.
As I understand it, there are two ways you can go:
  1. Make all members public in classes that require serialization.
  2. Grant reflection permission to the app in the Tomcat Security Manager config.
Not wanting to make all members public, I tried the least-invasive implementation described in the last post of the thread and added the following to /etc/tomcat6/policy.d/50local.policy, then restarted.
grant codeBase "file:${catalina.base}/webapps/StockWatcher/-" { 
    permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; 
};
I've modified it from the post by changing ${catalina.home} to ${catalina.base}, but this cleared up the errors.

4 comments:

  1. Thanks. Your crystal clear explanation saved me from a world of pain.

    ReplyDelete
  2. You mentioned to options for fixing this problem:

    1. Make all members public in classes that require serialization.
    2. Grant reflection permission to the app in the Tomcat Security Manager config.

    Did you ever test option 1? I can get option 2 to work, but when I deploy to a server where I have limited control, I'm wondering if option 1 will work?

    ReplyDelete
  3. I believe I did test the public members solution and that it worked, but give it a shot with the security manager enabled.

    ReplyDelete
  4. I gave it a try and have some interesting results. Here's what I tried:

    1. Created a simple object that was serializable. It had a single String field which was public. It had a single accessor to retrieve the String data. This object was successfully marshalled from the server to the client.

    2. Using the same object above, I then tried to pass the object from client to server. This does not work. Have yet to find a solution.

    ReplyDelete