Using GWT to create an OSGi-aware web application


Update 2010-02-20 Both Pax Runner 1.3.0 and GWT 2.0 have caused quite some changes to this post. I have tried to stay up to date as well as I could (the zipped project now uses GWT 2.0), but you might find some inconsistencies when following the tutorial.

Google Web Toolkit is cool, and so is OSGi. However, when building a web UI for Apache ACE, I found out that creating a web application that can use OSGi services is not that easy. By the end of this tutorial, you will have created a GWT project that delivers a usable jar. If you’re impatient, skip to the end for the downloadable Eclipse project.

Step 1: Create a GWT project

Create a regular GWT project using the regular webAppCreator; this will give you a project that includes an Ant buildfile, we will need that later on.

angelos:workspace angelos$ ./gwt-mac-1.6.4/webAppCreator -out GwtDemo net.luminis.gwt.gwtdemo
Created directory GwtDemo/src
Created directory GwtDemo/war
Created directory GwtDemo/war/WEB-INF
Created directory GwtDemo/war/WEB-INF/lib
Created directory GwtDemo/src/net/luminis/gwt
Created directory GwtDemo/src/net/luminis/gwt/client
Created directory GwtDemo/src/net/luminis/gwt/server
Created file GwtDemo/src/net/luminis/gwt/gwtdemo.gwt.xml
Created file GwtDemo/war/gwtdemo.html
Created file GwtDemo/war/gwtdemo.css
Created file GwtDemo/war/WEB-INF/web.xml
Created file GwtDemo/src/net/luminis/gwt/client/gwtdemo.java
Created file GwtDemo/src/net/luminis/gwt/client/GreetingService.java
Created file GwtDemo/src/net/luminis/gwt/client/GreetingServiceAsync.java
Created file GwtDemo/src/net/luminis/gwt/server/GreetingServiceImpl.java
Created file GwtDemo/build.xml
Created file GwtDemo/README.txt
Created file GwtDemo/.project
Created file GwtDemo/.classpath
Created file GwtDemo/gwtdemo.launch
Created file GwtDemo/war/WEB-INF/lib/gwt-servlet.jar

If you want to, you can import this project directly into your Eclipse. If you check the mark “use Google Web Toolkit” in the project properties, you can use all the same goodies that creating the project in Eclipse would have given you. Remember to replace the buildpath entries for gwt-user.jar and gwt-dev-*.jar by a Library import for GWT.

Step 2: Include the necessary OSGi references

Create an ‘ext’ directory, and add org.osgi.core.jar to that. In Eclipse, add this jar to your build path.

Step 3: Use OSGi services from your web applicaiton

We will first add a simple Activator on the server side.

package net.luminis.gwt.server;
 
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
 
public class Activator implements BundleActivator {
    private static BundleContext m_context;
 
    public static BundleContext getContext() {
        return m_context;
    }
 
    public void start(BundleContext context) throws Exception {
        m_context = context;
    }
 
    public void stop(BundleContext context) throws Exception {
    }
}

Then, we up the GreetingServiceImpl to actually use this BundleContext (note that we use it directly here, but you could use it to get other services, create a ServiceTracker, etc.)

public String greetServer(String input) {
  String serverInfo = getServletContext().getServerInfo();
  String userAgent = getThreadLocalRequest().getHeader("User-Agent");
  return "Hello, " + input + "!
 
I am running " + serverInfo
    + ".
 
It looks like you are using:" + userAgent +
    "The framework we run from has " + Activator.getContext().getBundles().length + " bundles in it.";
}

Step 4: Add OSGi dependencies for the compiler

Add our OSGi dependencies to the classpath, so the compiler can find all of it.

    <!-- Add any additional non-server libs (such as JUnit) -->

Right, let’s give it a try!

angelos:GwtDemo angelos$ ant war
Buildfile: build.xml
 
...some output removed...
 
war:
[zip] Building zip: /Users/angelos/workspace/workspace/GwtDemo/gwtdemo.war
 
BUILD SUCCESSFUL
Total time: 36 seconds

You will find a war in your project directory now. There still is one ingredient we need. We need to make this into a proper bundle. We can use bnd to help us with that.

Step 5: use bnd to create a proper war

Download bnd, and put into a lib directory, and add it to your buildfile.

We create a new target that transforms our war into a jar.

<target name="jar">
    <copy file="gwtdemo.war" tofile="gwtdemo.jar"/>
    <echo file="gwtdemo.bnd">Import-Package: junit.framework;resolution:=optional, com.google.gwt.*;resolution:=optional, org.w3c.*;resolution:=optional, sun.misc;resolution:=optional, javax.imageio;resolution:=optional, javax.servlet.*;resolution:=optional, *
Bundle-Name: GWT Demo
Bundle-ClassPath: WEB-INF/classes, WEB-INF/lib/gwt-servlet.jar
Bundle-SymbolicName: net.luminis.gwt.gwtdemo
Webapp-Context: gwtdemo
Bundle-Activator: net.luminis.gwt.server.Activator
    </echo>
    <bndwrap jars="gwtdemo.jar" output="gwtdemo.jar"/>
    <jar file="gwtdemo.jar" update="true">
    <manifest>
        <attribute name="Bundle-ClassPath" value="WEB-INF/classes, WEB-INF/lib/gwt-servlet.jar, ."/>
     </manifest>
    </jar>
    <delete file="gwtdemo.bnd"/>
</target>

What’s happening here?

  • we copy our war to the same file, but with a jar extension,
  • we create a file for bnd to use, stating that we
    • want optional imports for junit and the gwt benchmarks, and non-optional imports for everything else (that what the * is for),
    • have some classes that we want bnd to scan for finding dependencies,
    • want to use a given Webapp-Context (this is a Pax war extender specific entry),
  • let bnd do its magic,
  • update our manifest: we put the . back on the classpath. This is important for the web application to find all resources, but if we would tell bnd to do it like this, it would treat . as the root of the classpath.
  • Finally, we delete that temporary bnd file.

What does that give us?

angelos:GwtDemo angelos$ ant jar
Buildfile: build.xml
 
...some output removed...
 
jar:
[copy] Copying 1 file to /Users/angelos/workspace/workspace/GwtDemo
[bndwrap] gwtdemo 41 910305
[bndwrap] Warnings
[bndwrap] Superfluous export-package instructions: [WEB-INF.classes.net, gwtdemo.gwt.standard.images, WEB-INF, gwtdemo, WEB-INF.classes.net.luminis.gwt, gwtdemo.gwt.standard, WEB-INF.classes.net.luminis, WEB-INF.lib, WEB-INF.classes, gwtdemo.gwt.standard.images.ie6, WEB-INF.classes.net.luminis.gwt.client, WEB-INF.classes.net.luminis.gwt.server, gwtdemo.gwt]
[jar] Updating jar: /Users/angelos/workspace/workspace/GwtDemo/gwtdemo.jar
[delete] Deleting: /Users/angelos/workspace/workspace/GwtDemo/gwtdemo.bnd
 
BUILD SUCCESSFUL
Total time: 23 seconds

That’s it! You can now deploy this jar into a framework that uses the pax web tools. Right, let’s give that a try.

Download pax-runner, and unzip that somewhere. Copy in your new jar, an try the following command

angelos:pax-runner angelos$ sh bin/pax-run.sh --profiles=war,compendium gwtdemo.jar

Now visit http://localhost:8080/gwtdemo:

gwtdemo

Summary

So, what did we need?

  • A fairly regular GWT project, create with an Ant file,
  • some code that tries to use OSGi services,
  • some bnd magic to make the war into a jar,
  • Pax tools to get it all running quickly.

If you don’t want to use pax runner, you can need to deploy pax-web-extender-war(jar, snapshot 23 June) and an http server, preferably pax-web-service(jar), into your framework.

You can download the gwtdemo Eclipse project to play around with it. I have not provided the GWT runtime in this download; you should edit line 4 of the build.xml to point to your installation of GWT.

, , , , , , , , , , ,

  1. #1 door hoschi om 12 januari 2010

    Hi,

    I get all to work with the antfile stuff and it throw no errors. If I run it via pax runner I get the following errors:
    http://paste.lisp.org/display/93290

    Have you an idea what is the problem?
    FYI I tried it with gwt 1.6.4 and 2.0.0 but none run :(

  2. #2 door Angelo van der Sijpt om 13 januari 2010

    You’re right, it shouldn’t do that.
    It has to do with a change starting Pax Runner 1.2.0: “OSGi Alliance compendium bundle not provisioned automatically anymore (make your own choice via “compendium” profile)”.

    The Pax Runner command should be
    pax-run.sh –profiles=war,compendium gwtdemo.jar

    I’ll update the post, thanks for the notice!

  3. #3 door dodo om 5 februari 2010

    Hi,
    thank you for that great introduction.
    I had to mark every imported package in the Manifest as optional to make it work. Otherwise i got the error hoschi posted above (the second one he posted)

  4. #4 door Angelo van der Sijpt om 6 februari 2010

    For the Compendium stuff, you should include the Runner profile for that, and the GWT imports should be optional because of GWT magic. What other imports do you have that you need to mark optional?

  5. #5 door dodo om 7 februari 2010

    The error I got when running your tutorial was
    http://yfrog.com/jubasicp

    So I included the gwt-jars (resulting in pretty big gwt-demo.jar).
    But that didn’t work and gave me :
    http://yfrog.com/11includegwtlibsp
    So I thought including every demanded jar was not the right way to go, because I probably would have ended with a jar containing every other jar in the java-world ;)

    bnd creates a long list of packages to import, but marking them all as optional did the trick.
    http://yfrog.com/jualloptionalp

  6. #6 door Angelo van der Sijpt om 7 februari 2010

    I understand that BND creates all the imports, but as long as its GWT-related, you should get by with trusting the GWT magic, and mark those as optional. The list might indeed have changed, I haven’t tried this on GWT 2 yet.

    Could you try marking just the GWT packages as optional, and keep the rest the way they were?

  7. #7 door Raul om 20 februari 2010

    Hello, I have tried your method with GWT 2.0. But each time I am having the error
    org.osgi.framework.BundleException: Unresolved constraint in bundle com.cisco [1]: package; (package=com.google.gwt.core.ext)->
    at org.apache.felix.framework.Felix.resolveBundle(Felix.java:3263)
    at org.apache.felix.framework.Felix.startBundle(Felix.java:1597)
    at org.apache.felix.framework.Felix.setActiveStartLevel(Felix.java:1077)
    at org.apache.felix.framework.StartLevelImpl.run(StartLevelImpl.java:264)
    at java.lang.Thread.run(Thread.java:619)

    Then I have installed gwt-dev.jar also where the package com.google.core.ext resides, but it does not work.

    any suggestion please!

  8. #8 door Raul om 20 februari 2010

    Hello #5 by DODO solve my problem, but I cannot connect to the server. It is displaying the index page demo GWT page, but when I click the Send button, I got the following error:

    Sending name to the server:
    GWT User

    Server replies:
    An error occurred while attempting to contact the server. Please check your network connection and try again.

  9. #9 door Angelo van der Sijpt om 20 februari 2010

    The error message on the server had to do with an old servlet jar I included in the zipped project, which is incompatible with the old GWT servlet jar. In the server log, there is a message about this:

    com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException: Parameter 0 of is of an unknown type 'java.lang.String/2004016611'

    This can be solved by just deleting the jar from war/WEB-INF/lib; the build will put the right one there.

    I updated the zipped project to include this change.

  10. #10 door Jaime Soriano om 5 april 2010

    Thanks a lot for this tutorial, I could use it without any problem to deploy a GWT 2.0.3 application with Pax Runner 1.4.0.
    I saw that if “/” is used as webapp context, the application still works but all the contents of the jar file are exposed as static files, so it can be downloaded by anyone.
    Do you know if there is another way to set the context to root so the files are not exposed?

  11. #11 door Angelo van der Sijpt om 5 april 2010

    I’m afraid I do not; I’m pretty sure someone with more knowledge of wars could answer this?

  12. #12 door Stefan om 1 april 2011

    Hello,

    i try this tutorial and it failed by pax-runner

    org.osgi.framework.BundleException: Unresolved constraint in bundle net.luminis.gwt.gwtdemo [1]: Unable to resolve 1.0: missing requirement [1.0] package; (package=javax.validation)
    at org.apache.felix.framework.Felix.resolveBundle(Felix.java:3404)
    at org.apache.felix.framework.Felix.startBundle(Felix.java:1714)ERROR: Error starting file:bundles/net.luminis.gwt.gwtdemo_0.jar (org.osgi.framework.BundleException: Unresolved constraint in bundle net.luminis.gwt.gwtdemo [1]: Unable to resolve 1.0: missing requirement [1.0] package; (package=javax.validation))

    at org.apache.felix.framework.Felix.setActiveStartLevel(Felix.java:1143)
    at org.apache.felix.framework.StartLevelImpl.run(StartLevelImpl.java:264)
    at java.lang.Thread.run(Thread.java:680)

    Any idea?

  13. #13 door Angelo van der Sijpt om 22 april 2011

    Hm, I’m not sure what’s going on. I rebuilt the bundle with a current GWT version, and it does not seem to import javax.validation, and running it with Pax Runner seems to work fine. Are you sure you followed all steps?

  14. #14 door blaxter om 11 april 2012

    Probably it’s something related with felix, but with last version of felix if I install the gwtdemo.jar file in your zip, no errors are shown (after install file:/path/to/gwtdemo.jar and start bundle_id) but the url does not work :(

  15. #15 door Angelo van der Sijpt om 22 november 2012

    Did you remember to include a web service bundle? I have some pointers for this in the second-to-last paragraph of the post.

(wordt niet gepubliceerd)