javagwtosgideclarative-servicespax-web

Issues with GWT in OSGi+Pax-Web using Declarative Services


I'm migrating an existing GWT app running on OSGi (Equinox) and Pax-web to use Declarative Services instead of programmatic Service Tracker.

I'm using Pax-Web in Equinox. A WAR-based GWT application is loaded with no problems by PAX-WEB War extender, but you cannot have Declarative Services in this modus operandis.

I successfully refactored all servlets out of the war and converted them into declarative OSGi services (<provide interface="javax.servlet.Servlet"/>). That way I got rid of all the messy ServiceTracker code and specific OSGi dependencies in the servlets. I further replicated all other web.xml functionality to register a filter, serve static content and welcome page using the info on [1]

At this point, it normally should work, but I'm having issues with PAX-WEB and the way GWT tries to load its resources:

While loading the serialization descriptors, GWT loads the serialization policy file from the local context. In my case it tries to resolve resources like this: /ctx/ctx/62394587E47773FB1594FF.gwt.rpc This resource is created by the GWT compiler and placed under : /war/ctx/ctx/resource...

Before, using the standard wab mapping (Webapp-Context: /ctx, Webapp-Root: /war) gwt would find its resources correctly. Now that I'm using the programmatic resource mapping:

DefaultResourceMapping resourceMapping = new DefaultResourceMapping();
resourceMapping.setAlias( "/ctx" );
resourceMapping.setPath( "/war" );

GWT fails to load the resouce and produces the following error:

2012-06-20 12:46:36.283:INFO:/:AbcProxy: ERROR: The serialization policy file '/ctx/ctx/600000000000000773FB1594FF.gwt.rpc' was not found; did you forget to include it in this deployment?
2012-06-20 12:46:36.283:INFO:/:AbcProxy: WARNING: Failed to get the SerializationPolicy '600000000000000773FB1594FF' for module 'https://localhost:8443/ctx/ctx/'; a legacy, 1.3.3 compatible, serialization policy will be used.  You may experience SerializationExceptions as a result.

[N.B. The last sentence should read "you will experience a hell of serialization issues as a result"]

I've tracked the issue to the HttpServiceContext loading the resource and intrepreting the path as a file and not as an url relative to the programmatic web context:

getting resource: [/mx/mx/6ECAD5B3A6F908CE17E47773FB1594FF.gwt.rpc]
HttpServiceContext | not a URL or invalid URL: [/ctx/ctx/600000000000000773FB1594FF.gwt.rpc], treating as a file path
DefaultHttpContext | Searching bundle [bundle] for resource [/ctx/ctx/600000000000000773FB1594FF.gwt.rpc]

This obviously fails, as this resource is located under /war/ctx/ctx/ in bundle file system. This seems to relate to bug PAXWEB-314 [2] which implementation is to turn the relative path into a file path:

// IMPROVEMENT start PAXWEB-314
257              try {
258                  resource = new URL(path);
 259                  LOG.debug( "resource: [" + path + "] is already a URL, returning" );
 260                  return resource;
261              }
262                  catch (MalformedURLException e) {
 263                        // do nothing, simply log
264                      LOG.debug( "not a URL or invalid URL: [" + path + "], treating as a file path" );
 265              }
266              // IMPROVEMENT end PAXWEB-314

Is there a way to work around this issue? Is somebody using GWT and PAX-WEB using OSGi DS instead of a WAB? One possible way is to copy the /war/ctx produced by the GWT compiler back to /ctx, but I'd like to find a decent solution before going into the hack direction.

Any ideas?

1 - https://github.com/ops4j/org.ops4j.pax.web/blob/master/samples/whiteboard/src/main/java/org/ops4j/pax/web/extender/samples/whiteboard/internal/Activator.java [2] - http://team.ops4j.org/browse/PAXWEB-314


Solution

  • I did some further digging.

    On GWT, this is the relevant piece of code in charge of loading those policy files: [1]

    protected SerializationPolicy doGetSerializationPolicy(
          HttpServletRequest request, String moduleBaseURL, String strongName) {
        // The request can tell you the path of the web app relative to the
        // container root.
        String contextPath = request.getContextPath();
        String modulePath = null;
        if (moduleBaseURL != null) {
          try {
            modulePath = new URL(moduleBaseURL).getPath();
          } catch (MalformedURLException ex) {
            // log the information, we will default
            log("Malformed moduleBaseURL: " + moduleBaseURL, ex);
          }
        }
    ...
    

    I suspected the contextPath to be a potential candidate suspect in this case. To test that theory, I deployed a simple servlet that dumps its context. I deployed it using WAB (MANIFEST: Webapp-Context + web.xml). In this deployment, the servlet reports: [getContextPath]->[/ctx]

    Then, changed the deployment to OSGi-ds with a programmatic activator containing the resource mapping. DefaultResourceMapping resourceMapping = new DefaultResourceMapping(); resourceMapping.setAlias( "/ctx" ); resourceMapping.setPath( "/war" );

    In this case, the servlet reports: [getContextPath]->[]

    Translated to the gwt issue --> when gwt is deployed with a WAB, it finds its config in /ctx/app and when I use the programmatic resource mapping it is looking into /app and therefore does not find its resources.

    Bottom line: In PAX-WEB, Webapp-Context is not equivalent to the alias. The alias does not populate ContextPath the same way Webapp-Context does.

    The only current work-around for this situation is to let the build copy the GWT-generated files one level down (eliminating the application context path)

    PS: Following Achim Nierbeck from Pax-web, the OSGi spec is evolving to manage the app-ctx issue: http://wiki.osgi.org/wiki/WebExperience

    [1] http://code.google.com/p/google-web-toolkit/source/browse/trunk/user/src/com/google/gwt/user/server/rpc/RemoteServiceServlet.java?spec=svn5045&r=5045