Forum Controls
Spotlight Features

The Rich Engineering Heritage Behind Dependency Injection

Andrew McVeigh takes us on a tour of the rich heritage behind dependency injection, what it represents, and tells us why its here to stay.

Java, the OLPC, and community responsibility

The "One Laptop Per Child" project has a great device ready to ship, but there's no Java on there. Let's think about working together to put Java on OLPC!
Replies: 1 - Pages: 1  
Threads: [ Previous | Next ]
  Click to reply to this thread Reply

Developing Eclipse/OSGi Web Applications Part 2

At 10:44 AM on Feb 16, 2006, Riyad Kalla Javalobby Editors wrote:

About the Author

The author of this article 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 Struts, Eclipse and RSP. Wolfgang is also available for speaking engagements on RSP. Wolfgang can be reached at wgehner at infonoia dot com.

About this Tutorial

This tutorial is split into two parts. The first part will cover a sample web application all the way to creating the necessary extension points for your own. The second part will pickup right where Part 1 left off and guide you through mapping your servlet, dependencies and then eventually running your new web application.

Introduction

Welcome to Part 2 of the OSGi Web Application tutorial. You should have read Part 1 by now and have everything setup appropriatley to continue working on our sample web application. We are going to dig right into extensions, servlets, resources and more. So let's get started!

Extensions and Extension points

Now let's look at the wizardry that creates these URL servlet and resource mappings behind the scenes from Part 1 . 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 9: 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 my 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 final is likely to support declarative services (since it's part of the OSGi-implentation Eclipse uses); but plug-in Editor support may only be for a later date.

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 10: 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 during precompilation 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 . replaces and 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 ". The name of our choice is "cross-context" (alluding to the Tomcat server.xml attribute as in cross-context="true" , which does similar things but across web applications.).

Now we know how a servlet is mapped from the bundle to the container. The same works for precompiled JSPs (more about precompiling below).

Mapping resources

How do we map a resource such as the stylesheet resource at http://localhost/rspwebapp/platform/myresourcenamespace/css/my.css mentioned earlier?



[Image 11: PluginXmlResource]

This is a combination of two extension points. The " httpcontexts " 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.
The " resources " extension point specifies under what (sub)namespace this happens. Again, a " httpcontext-name " is given to allow sharing of context across plug-ins.

Is this complicated? Well, we think that all three extension points could be rolled into one that more closely resembles a classic web.xml , with just a namespace and context-path ( /WebContent ) added. Someone just needs to do it, this is open source :-)

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 12: 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:\rsp\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. Single-JAR plug-ins have just one "problem": they cannot contain other JARs, while exploded plug-ins can. But there is a solution.

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. (Boy, take a deep breath, read the first part of this paragraph again, and make sure you understand it before you go on, please).



[Image 13: Dependencies for Customtaglib]

Creating "simple" plug-in JARs from existing JAR archives

Creating a plug-in JAR from an existing JAR archive is a two-step process: creating the plug-in project from the existing JAR archive, and "exporting" the plug-in as plug-in JAR archive.

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 ".



[Image 14: JAR Plugin Wizard]

We call plug-ins created from existing JAR archives " Simplejar " plug-ins, and maintain them in the "Simplejar" 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 15: Exported packages]

Once the plug-in project is created, you can create a single plug-in JAR from it as follows: Open /META-INF/MANIFEST.MF in the project, select the Overview tab and scroll to its bottom right corner.



[Image 16: Export Wizard Launch]

Click on "Export Wizard" on the bottom right corner of the Overview tab.



[Image 17: Export Wizard]

On the Wizard, as an export destination, select C:\rsp\simplejars\sharedplugins\eclipse ; this is where we keep all "simple" plug-in JARs for deployment. 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 in the C:\rsp\simplejars\sharedplugins\eclipse\plugins folder. It will be picked up by our webapp through one of the entries in the /WEB-INF/platform/links folder.

We try not to have any "non-plug-in" jars floating around in the deployed application. Some plug-ins have a /jars folder, but those JARs are not deployed, and only used as needed for precompiling JSPs.

Dependency hierarchies and two-way dependencies

Attention: if the plug-in you just created itself depends on other JARs to run, you will have to add those as "Dependent JARs" on the "Dependencies" tab, of /META-INF/MANIFST.MF before exporting it. If you did not do that, 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.

The Eclipse IDE comes with a nifty editor on the "Dependency" tab that lets you analyze your dependency graph and look for cycles in your dependency graph.



[Image 18: Dependency Cycle Analysis]

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, but it didn't work for me as expected.

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 19: 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 took me a while, expect the same if you are just starting.

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/myAct "
- 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 the 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 " httpcontexts " 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:

<alias>/do</alias>
<servlet-class>org.rsp.framework.struts.base.BundleActionServlet</servlet-class>
  <init-param>
    <param-name>config</param-name>
    <param-value>*/WEB-INF/struts-config.xml</param-value> 
  </init-param>
</servlet-class>

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]

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!

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.

Deployment

The quick run-through described under the heading "Learn-it" above shows what you need to do so that changes to your plug-in(s) show up in the running application. You may remember that we have Tomcat point directly to the webapp /WebContent .

However, Tomcat only reads classes and jars in the /WEB-INF/classes and /WEB-INF/lib folders, so what sp_redeploy does is copy the plug-in JARs in /platform/plugins and those referenced in the /links folder [ 7 ] into Tomcats /work/catalina/localhost/rspwebapp folder where they are available to the BridgeServlet implementation. Tomcats /work folder is also where Tomcat stores compiled JSPs.

At the time of writing, there is no automated mechanism to convert JSPs INSIDE plug-ins into mapped classes. So what we have done is create a precompile.xml ant script, which needs to be executed before a particular plug-in is " JARred " via the " Export wizard ". This is just a tiny bit painful, and gets in the way of development, and you still have to add each precompiled JSP into the plugin.xml with their servlet path. This precompilation script is container-specific, but can be adapted (a working version already exists for Weblogic 9). We set precompilation to write Java files to a precomp folder and class files directly to the root of the project (so in the project you just see the org package).

On the deployment theme, what would be great is to not have to manually execute the " Export Wizard " JARring of the plug-in, while in development mode. While the Eclipse editor does not have that feature "yet" (it exists for RSP plug-ins as "Run as RCP app") we could augment the FrameworkLauncher (responsible for " sp_redeploy ") to copy the applicable parts of the project into Tomcat /work in exploded form (for particular plug-ins only). All we would have to do is parse the build.properties , which contains the information what to copy during deployment using /sp_redeploy . This is open source, please come forward if you are interested in this feature and have just a little time to spend.

At the moment, we make the redeployment task a little easier by having dragged the sp_redeploy link onto the Links bar of the browser. So we can just hit it when we want to refresh the webapp with (Exported) plug-ins on the webapp.



[Image 21: /sp_redeploy via browser link: Drag link to "Links" bar and drop there]

Nirwana of course would be a watcher, that picks up on changes in plug-ins, and redeploys them automatically, so the browser will always have the latest state. This is something even the Server manager in WTP cannot do for webapps (it just always restarts Tomcat upon detected changes, if I am not mistaken).

User session management

When we started, we found that /sp_redeploy would give us class-cast exceptions when we had items in the user session which came from a redeployed bundle; this is not surprising. Tomcat has a feature to deal with user session items upon webapp redeployment, but it does not kick in here (we do not redeploy the webapp, we just redeploy plug-ins inside the webapp).

One nice strategy would be to invalidate affected user sessions only, or even find a way to allow existing user sessions run on the old version. Right now, we just brutally invalidate all user sessions upon /sp_redeploy , so we can develop without a problem.

Conclusion

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.

Someone has already taken this and created a Tapestry/Hivemind version, which we hope to make available via SVN. Someone has run this on Weblogic. What about the other containers? There is very little container-specific stuff in here. How about running Flash/Laszlo this way?

The ultimate goal may be to run all plug-ins in a native "OSGi bundle container", removing the need for a J2EE/Servlet container altogether.

- What are your ideas where this could lead?

I have set up a quicktopic thread for your thoughts, comments, ideas, feedback and discussion here [ 8 ]

If you enjoyed hearing about this, and would be interested in hearing more over time, please join the Infonoia community . Note that you can opt out at any time by unselecting "You may email me...".

Miscellaneous Tips and tricks

  • Make sure your exported JAR actually contains what you think. For this, you can unzip the deployed jar (in Tomcats /work/Catalina/localhost/rspwebapp/platform/plugins folder and see what's there, and then fix it on the " Build " tab of the plug-in Editor.
  • When you use the "Export Wizard", make sure you write the plug-in to the right place. We set up a separate /runtime folder as part of each project, except for simplejar plug-ins, and the equinox plug-ins which go directly to the /WEB-INF/platform/plugins of the webapp. If you export into the wrong one, you may have several copies around, and the old one around may be copied over the new one during deployment (as they all end up in the /platform/plugins folder under Tomcat /work ). What we did is to create an " export.xml " ant task the first time we use the Export Wizard (there is an option for that on the wizard). From then on, we launch that ant task by selecting export.xml , and launch it with Shift+Alt+X, Q.
  • 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

This Tutorial was reviewed by: Martin Schikowski, Richard Backhouse, Simon Kaegi, Jim D'Anjou, Peter Neubauer and Ricardo Giacomin

References

[1] http://www.infonoia.com/en/content.jsp?d=inf.05.07

[2] http://sourceforge.net/project/showfiles.php?group_id=122298&package_id=176796&release_id=393605

[3] http://download.eclipse.org/eclipse/downloads/drops/S-3.2M4-200512151506/

[4] Under Linux, you may register the demo webapp with Tomcat as follows: Delete $CATALINA_HOME%/conf/Catalina/localhost/rspwebapp.xml . Instead, in $CATALINA_HOME$/webapps , create a link to rsp/workspace/org.rsp.sample.webapp/WebContent .
The rspDemo-xx.zip includes Subclipse plug-ins for SVN repository support ( http://subclipse.tigris.org ) for Windows. 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

[5] NOTE: as setup in c:\rsp\apache-tomcat-5.5.15\conf\catalina\localhost\rspwebapp.xml

[6] This implementation requires calling /sp_redeploy (see section on deployment), notably reinitialization of org.rsp.framework.struts , to discover resources in other Struts bundles. This will have to be considered when moving to finer-grained deployment (where we deploy individual bundles). At that point, an OSGi-native services implementation under the hood of the registration mechanism (framework.struts provides a "service", to which the Struts bundles subscribe) may come to the rescue. To be covered by a future article!

[7] A .links file in the /links directory make the framework follow the path provided in that file. It will treat files from /eclipse/plugins and /platform/plugins under that target path the same way as if they were inside the /plugins directory which is the sibling to the /links directory.

[8] http://www.quicktopic.com/35/H/giqGy52F8Nk8b

[9] For further reading and links, see the article at http://www.infonoia.com/en/content.jsp?d=inf.05.07



[Image 22: OSGi schema]

  Click to reply to this thread Reply
1. At 2:38 PM on Dec 21, 2006, Dimitry Voytenko Javalobby Newcomers wrote:

Re: Developing Eclipse/OSGi Web Applications Part 2

Just run accross this article. It looks very interesting. General all-size-fits-all WAR is definetely hard to use for big projects.
Did you consider possible context plugin filtering? Say, user have permissions to only certain plugins and how to filter other extensions out? In some way this could be similar to the activities/capabilities concept in the Eclipse?

thread.rss_message