Authors: Wolfgang Gehner (Infonoia SA), Simon Kaegi (Cognos), October 2006. Peer reviewers (updated version): Jochen Hiller, Peter Neubauer
Peer reviewers (original version): Richard Backhouse, Jim D'Anjou, Ricardo Giacomin, Simon Kaegi, Peter Neubauer and Martin Schikowski
Introduction This article walks you through a repeatable process of creating a pluggable server-side application with Eclipse 3.2, JSP and Struts 1.3. The article and code has been updated substantially from a previous version published in February 2006. It brings together Eclipse/OSGi plug-in mechanisms with standard Java web application development, using an established MVC web framework (Struts just as example in this case). It also provides a very basic insight into the concepts the "RSP-UI" project submitted to the Eclipse Foundation http://www.eclipse.org/proposals/rsp. For a general background, please refer to previous article on "Rich Server Platform" (RSP) here [1]. The code referred to in this article is available for download here [2]. The download includes a fully configured Eclipse 3.2 development environment with Web Tools and other plug-ins installed.
Run it!
Here is what you do to run the code:
1. Unzip rsp-0.3.x.zip into C:\. This will create a folder named \rsp03 and subfolders. with some content. The installation of Eclipse contains Web Tools plug-ins, SVN plug-ins and one very important plug-in named org.eclipse.pde.http.ui in its own installation locations.
2. Start Eclipse with C:\rsp03\eclipse\eclipse.exe. Accept the workspace selection as c:\rsp03\workspace. You will likely have to fix jre configuration to match your environment (Window-Preferences-Java-Installed JREs and Window-Preferences-Server-Installed runtimes-Edit). If you have Windows XP: On the green "Run" icon in the menu bar, select "Run...", select "Equinox Http ? New_configuration" and hit the "Run" button. This should start up the Tomcat installed from the Zip under step 1, if you have no other web server instances already running on port 80.
In a web browser, go to http://localhost/mywebapp. You should see the welcome/output page of the rsp demo webapp titled "You are running Eclipse plug-ins on the server-side!" shown below: 
[Image 1: RSP-SimpleDemo Screenshot]
3. In the browser, now go to http://localhost/mywebapp/struts.jsp You should see a Struts Demo page which contains Struts modules, originating from different plug-ins.

[Image 2: RSP-StrutsDemo Screenshot]
Click the "Increment" button; this will trigger a server-side action (resp. command in Struts 1.3) and increase the value of the counter for the particular bean in the user session, and redisplay the page. You can reset a counter by clicking "Reset". To test validation, click on the "Setup validation" button; this will fill the edit field with an invalid value. Then click "Increment", and you should see a client-side validation message provided by Struts, and configured in the respective plug-in.
Learn it!
4. Inspect the web project mywebapp in the working set "samples". In /WebContent/WEB-INF/platform you will find a folder named "plugins". This is the default physical location for most of the OSGI-related server-side content running within this webapp, much like the folder /eclipse/plugins for the Eclipse IDE.
5. Open the java file org.rsp.sample.usage.servlet.MyServlet. Modify the servlet output in the doGet method to your liking & save.
6. On the "Servers" tab in the bottom right view, Click on the red "Stop" button. Then relaunch by clicking the "Run" icon on the top Eclipse menu bar.
7. In your browser, go back to http://localhost/mywebapp. You should see the output from the modified plug-in, as part of the output of the usage sample plugins. Note that if you were launching in Debug mode instead (The "Debug" icon on the top Eclipe menu bar to the left of the "Run" button), step 6 would normally not be needed.
Explore the Eclipse workspace. We use Working Sets to keep the workspace uncluttered.

[Image 3: Eclipse Working Sets]
The working set "equinox" contains some infrastructure plugins provided by the Eclipse Equinox Incubator,in slightly tweaked form. The working set "samples" contains the http://localhost/mywebapp, which you just saw running in Tomcat, and its "component" plug-ins. The working set "simplejars" contains simple JAR plug-ins, which wrap around various open source libraries, and are drawn upon by the "component" plug-ins.
The sample webapp
Let us start by looking at the mywebapp, which is a classic WAR-style webapp, with some special features, as we will explore. But as a classic WAR-style webapp, it can be deployed into a web container (here: Tomcat) like any other WAR-style webapp.
For development, we make our job easier by using a plug-in written and contributed to the RSP-UI project by John Wilson. It starts the Tomcat server for the org.rsp.sample.webapp and installs/runs the Eclipse/OSGi plug-ins configured in the launch configuration as part of this project. This has the same effect as manually copying the plug-ins into the /WebContent/WEB-INF/platform/plugins directory of this Eclipse project and reloading the webapp. 
[Image 4: Webapp Folders]
What is special about this WAR project is that the /WEB-INF/platform folder hosts an Eclipse/OSGi runtime, with /plugins, /configuration and potential /features folders. This is very similar to what you find in the C:/rsp/eclipse folder. This is not surprising, since the Eclipse IDE ("Workbench") uses the same infrastructure for itself: The Eclipse workbench "composes" itself from the plug-ins stored in the /plugins folder. An Eclipse RCP application [www.eclipse.org] has the same structure.

[Image 5: Equinox Http launch configuration]
To see the plug-ins that are part of the launch configuration on the "Plug-ins" tab of the "Run..." Equinox Http launch configuration.
When you inspect the /WEB-INF/web.xml you will find the "equinoxbridgeservlet".

[Image 6: Servlet Bridge in web.xml]
Among other things, the Equinox Bridge Servlet ensures that all URL requests under /mywebapp/platform/* get handled by servlets or resources in plug-in JARs. This means that if you type http://localhost/mywebapp/platform/do/my12Act, the response should be handled by a plug-in that has a servlet which is configured to respond to the /do/* pattern. We will use this, for this example, with the Struts controller servlet. In true MVC manner, the Struts controller servlet will actually forward the request to a JSP which is returned as a response to the request. But we are jumping ahead.
Under the hood, the Equinox Bridge Servlet uses what is called "HTTP services" provided by OSGi, in order to connect the container with plug-ins.
Namespacing plug-ins that use HTTP services
Let us go back to what you see when you just type http://localhost/mywebapp/ in your browser. This is actually the response from the JSP at /WebContent/index.jsp of the webapp, as this is what is specified in the web.xml as a "welcome-file". If you open index.jsp in the Eclipse editor, you see that it draws on resources in various "subnamespaces", corresponding to different plug-ins. An example is the stylesheet located at ./platform/myresourcenamespace/css/my.css. You will find the actual CSS file in the plug-in org.rsp.sample.usage.resource at /WebContent/css/my.css.
 [Image 7: Link to a resource]
Having an individual (sub)namespace per plug-in is not a requirement; all we should try to ensure that the URLs are unique. A unique namespace comes handy for this. For the Struts sample plug-ins, we actually adopted the convention that a my_special_plugin namespace addresses resources in the plug-in my.special.plugin. Therefore, when you type http://localhost/mywebapp/platform/org_rsp_sample_usage_struts12/dummy.jsp in the browser, you see the response of dummy.jsp in the org.rsp.sample.usage.struts12 plug-in.
If you think that these are long directory names for URLs, I agree. However, there is much to be said for having explicit namespaces. Maybe for more automated webapps we might think of using some kind of short, but stable and reversible hashcode[Note: Bundle-ID generated by OSGi is no good, since the numbering is dynamic]. We are really free to provide any or no namespace, but a convention is good, and using plug-in names is probably not the worst. We might keep the convention but just keep dots instead of underscores. OSGi also provides a PID, persistent plug-in ID which does not change across restarts or additions of bundles which is worth exploring further.
Extensions and Extension points
Now let's look at the wizardry that creates these URL servlet and resource mappings behind the scenes. The magic is in the file plugin.xml included in each plug-in. It uses an Eclipse-specific mechanism (invented even before Eclipse migrated to OSGi infrastructure) called "Extensions" and "Extension points". Extensions provide a way to specify possible interactions between plug-ins.

[Image 8: Extensions in the plug-in editor]
Extension points in Eclipse are what makes, for example menu items, show up on the menu bar (the org.eclipse.ui.menus extension point, among others, for historical reasons, in this particular case). The menu renderer obtains the "Extensions" from the "Extension registry" and builds the menu from that information.
Part of our vision for a "Eclipse/OSGi server-side UI framework is to use the same mechanism to build menus, views, editors etc. the same way for server-side applications. However, this demo app "just" uses the extension points to make HTTP output from different plug-ins accessible.
Note: Release 4 of the OSGi-standard provides a different mechanism called "declarative services" to achieve what the Eclipse-specific extensions do, and more. Eclipse 3.2 with the Equinox subproject already supports declarative services (since it's part of the OSGi-implentation Eclipse uses); but doesn't yet provide plug-in Editor support for declarative services.
Mapping servlets
Go to the project org.rsp.framework.struts, open plugin.xml, select the plugin.xml tab and inspect it. You will find the following:

[Image 9: PluginXmlServlet]
This is an Extension which provides the description for a servlet as you would normally find in your application's WEB-INF/web.xml. The webapp has a web.xml, but it is outside of this particular plug-in, and providing the mapping there would of course defeat the purpose of "component" plug-ins. [Note: You will find a web.xml also in /WEB-INF/ of the org.rsp.framework.struts plug-in. However, it is only there to avoid errors for Jasper compilation of JSPs, and otherwise defunct.] However, code in the webapp (in the Equinox plug-in jars, actually) reads the information from the Extensions, and registers the servlets described there in the BridgeServlet shown earlier.
The structure of servlet "extension points" is similar, but not quite identical to what you would find in a web.xml. <servlet alias="/myservleturl" replaces <servlet> and <servlet-mapping> and <httpcontext name="strutscontext" is new. The OSGi standard specifies that each HTTP service (used by the BridgeServlet, under the hood) uses a different ServletContext* unless the HttpContext is shared*. This could create problems in case of MVC forwards, where usually certain information is passed from the first servlet to the second via the request context. That transport would be broken under Eclipse-OSGi if different or "null" HttpContexts are used among the related servlets. The trick is to give all servlets and resources the same "httpcontext-name", and specify this context in another extension under <httpcontext> . The name of our choice for this demo is "strutscontext".
Now we know how a servlet is mapped from the bundle to the container. JSPs that are automatically recompiled on change runtime work a bit differently (More about JSPs below).
Mapping resources
How do we map a resource such as the stylesheet resource at http://localhost/mywebapp/platform/myresourcenamespace/css/my.css mentioned earlier?
[Image 10: PluginXmlResource]
The "resources" extension point specifies which directories in a plug-in JAR actually get exposed as a http resource. In our case, this is *everything* recursively under the /WebContent directory in the plug-in JAR. It also specifies under what (sub)namespace this happens. A "httpcontext-name" may also be given to allow sharing of context across plug-ins.
Maybe, eventually, extension points get deprecated in favor of OSGi-standard "declarative services". Or wrapped. Now Eclipse uses a custom "extension point registry" for lookup. If a reader of this article is interested in implementing web.xml-style declarative services, please let me know.
Please do not forget that not everything that is seen in the Eclipse IDE plug-in project automatically goes into the plug-in JAR. The plug-in jar contents need to be specified on the "Build" tab of the plug-in editor.

[Image 11: Build tab]
Now we understand how resources are mapped from the bundle to the container, and what goes into a plug-in jar. One more important conceptual thing: Dependencies. The good news: it is downhill from there.
Dependencies
The OSGi specification recommends that a bundle is deployed as a single JAR. For the Eclipse IDE, most plug-ins (see the C:\rsp03\eclipse\plugins folder) are now single-JAR plug-ins. However, some plug-ins are "exploded" into the file system; however, single-JAR plug-ins are more performant. Moving to single-JAR plug-ins mostly is what made startup of the Eclipse Workbench faster in the latest releases.
More involved (=advanced) plug-ins will usually depend on the presence of other libraries (JARs) in the runtime environment. For example, the org.rsp.sample.usage.customtaglib plug-in uses the displaytag libary, which is provided in a separate JAR. Could we just add that JAR inside the customtaglib plug-in? Yes, we could. We would just have to declare the JAR on the bundle classpath. Of course we would have to repeat that for every bundle which has that dependency. And we might end up with a maintenance nightmare.
The clean solution is to make that dependent JAR a separate Eclipse plug-in, and declare it as a "Required Plug-in" for the plug-in that depends on it. You can do that on the "Dependencies" tab of plugin.xml of the plug-in that requires the other.

[Image 12: Dependencies for Customtaglib]
Creating "simple" plug-in JARs from existing JAR archives
Creating a plug-in from an existing "classic" JAR archive is simple with Eclipse. You can create a plug-in from an existing JAR archive with the "File/New/Other" Wizard "Plug-in Development/Plug-in from existing JAR archives". As Target Platform, select "an OSGi framework ? Equinox" and uncheck "Unzip the JAR archives into the project". The latter will create a more compact plug-in.

[Image 13: JAR Plugin Wizard]
We call plug-ins created from existing JAR archives "simplejar" plug-ins, and maintain them in the "simplejars" Working Set. (Menu Window-Working Sets-Edit, select simplejars, press Edit, and check the newly added project).
By the way, just having code in a plug-in jar does not make that code accessible to other plug-ins that declare the dependency to this plug-in! You also need to make sure that the packages you wish to make accessible are listed as "Exported Packages" on the "Runtime" tab of the plug-in. In the Eclipse IDE, you can access the "Runtime" tab by opening either build.properties, /META-INF/MANIFEST.MF or plugin.xml (if available). The abovementioned wizard automatically creates entries in the "Exported Packages" list, so for simple plug-ins created from existing JAR archives this is less of an issue.
But you are almost bound to forget this when you create your own plug-ins from scratch, or add new code packages to an existing plug-in. But after a while, this becomes natural. It becomes also more obvious that in many cases you have code in a plug-in that you do not need to export to other plug-ins.

[Image 14: Exported packages]
Deployment The Http launcher treats all plug-in projects in the workbench and selected on the plug-ins tab, as if they were physical plug-in jars under /WebContent/WEB-INF/platform/plugins and makes them available in the launch.
However, at a certain point, you may want to deploy your application to a production environment, either as a complete WAR or an exploded WAR on the file system, to which you can add individual plug-ins or replace or remove plug-ins from time to time. At this point, you will want to create jars of the plug-ins yourself and copy them to a physical location such as the /WebContent/WEB-INF/platform/plugins of the demo webapp. You can do this with the Eclipse Export Wizard. Open /META-INF/MANIFEST.MF in any project in the project, select the Overview tab and scroll to its bottom right corner.

[Image 15: Export Wizard Launch]
Click on "Export Wizard" on the bottom right corner of the Overview tab.

[Image 16: Export Wizard]
On the Wizard, as an export destination, select C:\rsp03\workspace\mywebapp\WebContent\WEB-INF\platform. Also select "Package plug-ins as individual JAR archives". This will export the plug-in in non-exploded form. You can see the generated plug-in JAR at the given location. After creating an appropriate config.ini in the platform folder, you will be able to zip up the entire contents of \WebContent and simply rename the zip to .war for deployment. Dependency hierarchies and two-way dependencies
Eclipse 3.2. is normally able to alert you to unresolved dependencies between projects that are loaded in the workbench. If the plug-in you just created itself depends on classes in other Plug-ins to run, you have two choices. Add the plug-in that exports the packages of the classes you need as a "Required plug-in" on the "Dependencies" tab, of /META-INF/MANIFST.MF. This is simple and a reasonable choice if you are sure that no other plug-in will be able to provide the same classes and functionalities you need. Add the package of the classes you need as "Imported Packages" on the "Dependencies" tab. This allows OSGi to "resolve" or find those packages among any of the plug-ins installed. A good example for this is the javax.servlet package, where the implementation may be provided by different bundles or the specific container used.
If you did not do either, you will soon find out when you run the application, as you will get Class-not-found exceptions in the Tomcat console. This happens because of the smart class-loading/isolation rules of OSGi. At first you think it is a pain, but after a while you really appreciate having the dependencies explicit. Tip: if the library has been "mavenized" with Maven 2, you may find its dependencies documented in a Maven 2 repository.
Another thing to watch out for: Classes that are in a "sub-dependent" plug-in do not automatically get exposed to the top plug-in. Only if you select "Reexport this dependency" on the Properties dialog of the list of Dependencies this should be the case.
At this point, we can take a look at the raw descriptor generated by all the Eclipse graphical plug-in editors, used to describe the plug-in. This is /META-INF/MANIFEST.MF

[Image 17: MANIFEST.MF]
In this file you find Dependent (=Required) plug-ins listed under Require-Bundle, you find packages exported under Export-package.
Sometimes you have cases where your dependency goes two ways. For example, our plug-in org.rsp.framework.struts depends on org.rsp.jar.commons.chain, because some classes are indirectly referenced by it. On the other hand, since commons.chain uses reflection to instantiate classes declared in an xml configuration file (chain-config.xml) and not contained in org.rsp.jar.commons.chain, it needs access to those classes to instantiate them. The suggested (Eclipse-specific) mechanism to use here is called "buddy classloading". You can make your plug-in's exported packages available to plug-ins listed in a special registry. In our case, we added Eclipse-BuddyPolicy: registered to the MANIFEST.MF of the org.rsp.jar.commons.chain plug-in, and Eclipse-RegisterBuddy: org.rsp.jar.commons_chain to the MANIFEST.MF of the org.rsp.framework.struts plug-in. Working out dependencies takes a while. The bundle descriptor provides for version info. Why did we give the simplejars project names that end with a version number? This seems redundant at first. Well, we found the Eclipse editor can to date not hold two plug-ins with the same name but different versions. OSGi with its class-isolation mechanisms provides for the ability to *run* different versions of the same plug-in at the same time (nice if certain parts of the code depend on previous versions of other plug-ins and you do not want to migrate or break that code). We found that adding the version number to the plug-in name was an acceptable work-around.
Walkthrough of the Struts plug-ins
The interesting part of "OSGi-fying" Struts was of course that individual plug-ins that use Struts should be capable of "registering themselves" with the framework, without having to hard-code a reference to each plug-in. Struts uses one or several struts-config.xml files that contain so-called "action mappings", which define - what class or classes gets executed when a URL matching a certain pattern is called (so-called "action URLs", such as "/do/my12Act" - which view/JSP to forward to (and return as a response) once the action has been executed. In "pre-OSGi" Struts, sub-modules had to be merged declaratively, via the "config" init-param attribute in the struts action servlet definition of the webapp web.xml, for example:
<init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml, /WEB-INF/moduleA/struts-config.xml, /WEB-INF/moduleB/struts-config.xml </param-value> </init-param>
The Struts controller would parse the param-value, and merge the configuration from the different struts-config.xml files. What we were looking for was a way that the core Struts plug-in (org.rsp.framework.struts) would be looking across thse various plug-ins and "auto-load" struts-config.xml files across plug-ins.
We had several options to do this. One was to create our own extension points. This is a bit involved, since you need to create an xml schema for the definition of the extension point. Another one was to use OSGi-native "declarative services" for registration. The third one, most easily accessible, was to go via the existing "resources" and "httpcontext" extension points, and construct URLs that permit the OSGified Struts to load the resources from the different plug-ins. We chose the third option.
So now, the Struts action servlet in plugin.xml of org.rsp.framework.struts is defined as follows:
<servlet
alias="/do" class="org.rsp.framework.struts.base.BundleActionServlet" httpcontext-name="strutscontext"> <init-param name="config" value="*/WEB-INF/struts-config.xml" /> <init-param name="chainConfig" value="*/WEB-INF/chain-config.xml" /> </servlet>
You notice the wildcard "*" at the beginning of param-value. This means that the BundleActionServlet will look for (and register) all resources at /WEB-INF/struts-config.xml in all bundles using the "httpcontexts" Extension points, ignoring those that don't happen to have one. As long as all struts-using plug-ins keep their struts-config.xml files in that standard location, they will be registered with the Struts controller in the org.rsp.framework.struts plug-in.
You can take a look at the implementation of this lookup mechanism in BundleActionServlet [6].
The end result is that you can write a JSP page which draws functionality from multiple plug-ins that use Struts.

[Image 20: Struts demo]
The code of the demo also shows how you can leverage the CoR (Chain of Responsibility/Command) pattern with Struts 1.3. More about what is new in Struts 1.3 here: http:www.infonoia.com/en/struts_13.jsp. Back to Eclipse/OSGi webapp fundamentals: This demo does not go all the way because there is still a physical JSP in the webapp, which "has" the URLs of the plug-in resources hardwired. If we were to add a declared "View" Extension to each Struts plug-in, we could easily imagine a servlet, which composes a page dynamically from all plug-ins that have that Extension declared, as in an RCP app or the Eclipse IDE as an example for an RCP app. Then of course the next item would be to define navigation menus declaratively via extension points, or OSGi declarative services. The sky's the limit! And the Eclipse/OSGi server-side UI framework is near (see www.eclipse.org/proposals/rsp/ for more details about RSP-UI, Rich Server Platform - User Interface Framework).
If you use another framework that declaratively brings together various configuration files, you might implement it in a similar fashion.
Another important note: the demo is still very simple in the sense that we do not use any heavy duty "non-UI" plug-ins. Imagine reusing the "Eclipse Update manager" for provisioning all the clients of your industrial plug-ins with updates to their server-side applications.
JSPs Thanks to some great work in the Equinox incubator, JSPs are now automatically recompiled (through a special Jasper plug-in). This is a major relief compared to previously, where JSPs had to be precompiled and treated like servlets. There is to-date no extension point for JSPs, and the URL pattern /mypluginnamespace/*.jsp is registered with the shared "named" context in the "Activator" class of each plug-in that uses JSPs. Inspect the Activator class of any of the struts plug-ins for more details. Note that the .tlds used in JSPs should be in the WEB-INF/tlds folder of the plug-in (while the implementation jars of the tag library can and usually are in a dependent plug-in. Right now, it seems that the Jasper plug-in requires JSP paths inside bundles to be unique across plug-ins, but I am sure that that will be fixed soon.
Where does that lead us?
You can create an Eclipse/OSGi-fied webapp, with your own web-enabled plug-ins, probably by copying one of the existing sample or using them as blueprints.
To date we already use this approach of componentizing webapps for a financial management platform with 450 user screens and 14 application modules. Please contact Infonoia SA for further information and how we may be able to assist you with your projects. I have set up a quicktopic thread for your thoughts, comments, ideas, feedback and discussion here [8] |
|
Miscellaneous Tips and tricks
Make sure your exported JAR actually contains what you think. For this, you can unzip the deployed jar (in /mywebapp/WebContent/platform/plugins folder and see what's there after you have run the Export Wizard, and then fix it on the "Build" tab of the plug-in Editor. Always make sure that the packages you wish to make accessible are listed as "Exported Packages" on the "Runtime" tab of the plug-in.

Enjoy! Wolfgang Gehner www.infonoia.com wgehner [at] infonoia [dot] com
Wolfgang Gehner is co-author of the book "Struts Best Practices" published in German (2004) and French (Feb 2005). He has been working with Java n-tier web since 1998, including SilverStream appserver, before the advent of OS and J2EE. His company, Infonoia SA is editor of multi-platform, multi-repository software and solutions around document retrieval and publishing. Infonoia also provides consulting services, mentoring and training (English, French, German) on Best Practices in the web technologies and frameworks they use, such as Eclipse and RSP. Wolfgang can be reached at wgehner [at] infonoia [dot] com
This article was reviewed by: Jochen Hiller and Peter Neubauer.
[1] http://www.infonoia.com/en/server_side_eclipse.jsp [2] http://sourceforge.net/project/showfiles.php?group_id=122298&package_id=176796&release_id=454645 [4] For installation under Linux download Eclipse3.2 final (optional) install Subclipse. When you have installed the Eclipse build for Linux, you may use the update manager to install Subclipse for your platform. The update site is at http://subclipse.tigris.org/update_1.0.x install WST and JST from the update manager via the Callisto release install Tomcat. Adjust the JRE settings in Eclipse. Adjust the tomcat server runtime under Window->Preferences->Server->Installed Runtimes->Apache Tomcat v5.5 to point to your local JRE and Tomcat installation.
[8] http://www.quicktopic.com/35/H/giqGy52F8Nk8b
[9] For further reading and links, see the article at http://www.infonoia.com/en/server_side_eclipse.jsp
|