Get ahead
VMware offers training and certification to turbo-charge your progress.
Learn moreAs mentioned in Part 1, I am not using the Spring Framework in this second blog posting, rather I am focusing on the SpringSource dm Server™ and SpringSource Tool Suite to deploy "pure" GWT.
Please also see Part 1 for the background to the GWT StockWatcher sample and the software I'm using.
The step by step approach described here will build on what we did in Part 1, rather than start over. The only thing we did in Part 1 that we're now going to change is to remove the explicit dependency on the gwt-servlet.jar library.
The good news is that much of work to create these dependencies has already been done for you. The SpringSource Enterprise Bundle Repository contains "bundlized" versions of most common libraries. However, at the time of writing, our GWT dependency is an example of a library that you have to turn into a bundle yourself. Fortunately for us, Eclipse 3.4 makes the process very simple.
- Right click in the Project explorer and select "New" -> "Other" -> "Plug-in Development" -> "Plug-in from existing Jar archives". - Click "Add external" and browse for gwt-servlet.jar. - Click "Next" and call the project "com.google.gwt". - Set the plugin version to reflect the GWT version, in this case it's 1.5.3. - Select "Analyze library contents and add dependencies" - Select the Equinox OSGi framework
Click "Finish" and don't bother switching to the Plug-in development perspective. We're just going to export our bundle straight out again.
You should now see an overview of the generated MANIFEST.MF file, which is where a bundle's dependencies are defined. You'll see in the "Runtime" tab that the tool has added all of the com.google.gwt.. packages as exports. You'll also see in the "Dependencies" tab that it has worked out that this bundle requires a couple of javax.servlet.. packages and a junit package.
We'll remove the JUnit dependency for now as it will be unresolved by default in dm Server. Of course if we wanted we could add a JUnit bundle to dm Server to satisfy the dependency or alternatively we could make the dependency optional by adding required:=optional. Select the junit.framework package, click "Remove" and then save.
It's worth pointing out that this is a crude way of turning a JAR file into a bundle and it is not guaranteed to work in all circumstances. For one thing, we may not want all of our packages to be exported. More significantly however, there are one or two source-level gotchas which may not play well in OSGi, such as the use of Class.forName(). If in doubt, always go first to the SpringSource Enterprise Bundle Repository rather than trying to roll your own.
Finally, we need to export our Plug-in project as an OSGi bundle. Select "Export" -> "Plug-in development" -> "Deployable plug-ins and fragments". Select a convenient location as the output directory.
We should now see a file in <export path>/plugins/com.google.gwt_1.5.3.jar. The next step is to rename the file to make it consistent with the format of the other named bundles in dm Server: simply change the underscore to a dash: com.google.gwt-1.5.3.jar. Without this change, the current version of STS won't recognise the bundle version. If you want to skip this step, you can download it here.
Finally, copy the bundle into the dm Server bundle repository: <dm Server installation root>/repository/bundles/usr. Any bundle in here can be a dependency of other bundles in dm Server, but importantly it will also be visible as a dependency in STS, as we'll see in the next step.
At this point, you can delete the com.google.gwt project from the workspace as it has served its purpose.
The next step is to break our application!
Right-click on StockWatcherWar, select "Properties" -> "Java EE Module Dependencies", unselect the gwt-server.jar file and click on "OK". We've now removed our WAR's explicit dependency on GWT and caused a number of new problems for ourselves, most of which are compiler errors.
So we need to turn our Dynamic Web project into a dm Server bundle so that it can explicitly import the dependencies it needs from our newly created GWT bundle. To do this, right-click on StockWatcherWar and select "Spring Tools" -> "Add OSGi Bundle Project Nature". You should notice that the project now carries an all-important "S" symbol and the MANIFEST.MF has also changed character:
It's broken because it doesn't look like a bundle manifest. Let's add some sane defaults (you can either cut-and-paste the following, or use the fields in the tabs). Note that you can also use ctrl-space in the editor to prompt you with valid options.
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: com.google.sample.stockwatcher Bundle-SymbolicName: com.google.sample.stockwatcher Bundle-Version: 1.0.0 Bundle-Description: Shared Libraries StockWatcher demo
So this has fixed one problem, in that our bundle manifest now looks sane. However, we haven't yet defined the dependency on the GWT bundle, so we've still got a number of compiler errors.
Select the "Dependencies" tab and click on "Add". Right at the top, you should see that a com.google.gwt bundle has magically appeared. Now, it's important to understand that the GWT bundle appearing in this list has nothing to do with the plug-in project we created in Step 1. If you delete that project from the workspace, the bundle will still appear. It works because the dm Server installation is set as the project's runtime and the tooling is therefore looking in the dm Server's repository for any bundles which you may want to import. If this hasn't worked, then either the GWT bundle wasn't copied to the correct location or the WAR project's runtime wasn't set to dm Server. Check the latter in "Properties" -> "Targeted Runtimes".
Next, select the GWT bundle, click on "OK" and then save the manifest. You should now see the GWT bundle added to a new classpath element called "Bundle Dependencies":
You should now notice that most of the errors have been resolved, but a couple are still outstanding. This is because our GWT bundle has a dependency on the javax.servlet APIs and until we have those classes available, the type hierarchy is incomplete.
So why didn't we have this problem previously when we just had gwt-servlet.jar in our build path? Well, before we made our changes, our WAR would pick up everything on its build path, which includes all of the JARs made available by the dm Server Target Runtime. However, you may have noticed that when we added the OSGi Bundle Project Nature, the dm Server JARs on the build path disappeared. This is because, by becoming an OSGi bundle, we have entered a different world of dependencies where imports and exports must be declared explicitly.
Declaring our dependency on the javax.servlet APIs is a simple matter of importing another bundle. You'll see it in the list as com.springsource.javax.servlet. Once you've added the two bundles, save the manifest and you should now see all errors resolved. Phew!
If we navigate now to the MANIFEST.MF tab, we can see the effect of all of our button pushing: A green "Import-Bundle" entry in the manifest. Note that we could just as easily have used ctrl-space after "Import-Bundle" and it would have suggested all possible imports:
If you want to see my projects, you can download a zipped up copy here. It includes Runtime Configurations to launch in hosted mode with the embedded Tomcat, hosted mode with dm Server and to launch the GWT compiler. Note that hosted mode with the embedded Tomcat works fine without having to modify the bundle WAR project. This is because the javax.servlet packages from dm Server are no longer on the build path. To use my projects, you'll need the GWT_ROOT_INSTALL variable and you may need to select your dm Server Target Runtime instance, as described in Part 1.
Just for fun, let's have a peek under the covers and look at our bundles interacting.
When dm Server starts up, it notifies you that its OSGi console can be accessed using a telnet client:
[2008-10-27 16:48:04.266] main <SPOF0001I> OSGi telnet console available on port 2401.
Let's open up a terminal window and see what we can find out:
> telnet localhost 2401 Trying ::1... Connected to localhost. Escape character is '^]'.
osgi>
You can get a list of the commands available to the Equinox console by typing help. There is also a useful DeveloperWorks article about it you can read here.
Typing ss gives us a list of all the running plugins:
osgi> ss
Framework is launched.
id State Bundle . . . . 73 ACTIVE com.google.sample.stockwatcher_1.0.0 74 ACTIVE com.google.gwt_1.5.3
Typing packages 74 gives us all of the exported packages from the GWT bundle. It shows that all of the exported packages are being imported by the StockWatcherWar bundle. This is a result of our use of "Import-Bundle". As it happens, the only packages our StockWatcherWar actually requires are com.google.gwt.user.client.rpc and com.google.gwt.user.server.rpc. If we'd wanted to, we could instead have been more selective by using "Import-Package" to import these packages explicitly. This is described in the dm Server Programmer Guide.
file:////Users/bcorrie/dmServer-1.0.0/springsource-dm-server-1.0.0.RELEASE/work/com.springsource.server.deployer/Module/StockWatcherWar.war-0/StockWatcherWar.war [73] imports com.google.gwt.i18n.client.constants; version="0.0.0"<file:////Users/bcorrie/dmServer-1.0.0/springsource-dm-server-1.0.0.RELEASE/repository/bundles/usr/com.google.gwt-1.5.3.jar [74]>
However, one example included with the GWT distribution which does use some simple remoting is called DynaTable. Using the same steps and principles described in these blogs, I turned this sample into a Shared Libraries WAR file. To see what I did, you can download the zipped up projects or WAR file and have a look. This very simply demonstrates the principle of running multiple applications in dm Server, both of which are sharing the GWT bundle we created.