Tapestry IoC: Modularization of web applications without OSGi
In this article I’m going to describe the modularization features of Tapestry. You will learn how to create modular applications that can be customized without to change the code of the application. These customizable applications can be delivered in different editions to different customers with minimal effort. I intend to publish a series of articles on this topic. This post is the first article of the series and starts with a short introduction to OSGi and its modularity features.
Modularization with OSGi
In the last years there has been a hype around OSGi. Most of the attention that OSGi has been getting is related to its modularization features. With OSGi you can cut your application in several bundles which can be installed, updated or removed at runtime. No shutdown of the whole system is needed. Furthermore the OSGi Service Platform allows you to specify the dependencies between bundles on the package level. A bundle can hide the private API by exporting only those packages that are meant to be visible to other bundles. Hiding internal packages allows bundle developers to change the implementation details without to fear any backward compatibility issues. One of the killer features of OSGi is that you can version not only your bundle but also the packages exported by your bundles (actually exporting packages without a version is a bad practice). This way you never run into the incompatibility problem between libraries in your application. In a traditional JEE container you have a problem if your application depends on two libraries that depend on two incompatible versions of a third library. In contrast the OSGi dependency resolution mechanism allows you to deploy several versions of the same bundle.
OSGi is a great technology but there is an impact of using it. For example you will find yourself at a lower level of productivity because you spend too much time debugging the class loader problems of your bundles. If you start developing OSGi-based applications, then ClassNotFoundException will be your best friend.
Another issue is the fact that the majority of java libraries out there are not currently shipped as OSGi bundles. Fortunately SpringSource launched the Enterprise Bundle Repository where you can find OSGi-ready versions of open source libraries but this repository is not up-to-date. Often you will need to transform the needed library into a bundle. There are also too much libraries that advertise to be OSGi-ready because their developers put a MANIFEST.MF file into the JAR. The most OSGi-based developers will concede that too much bundles are just bad. Some examples are the usage of Require-Bundle manifest header or export of unversioned packages. Also unversioned imports or imports with fixed version make your life harder then it should be. Imagine you found a cool bundle and want to install it. Then you find out that the bundle has some dependencies. At first you think it is no problem: there is another bundle that exports the required packages. But then you realize that the new bundle imports the version 1.2.3 but 1.2.2 is exported. Damn, a version range would save your time.
Next annoying issue is that some cool libraries, that work in a JEE environment out of the box, are incompatible per se with OSGi because they load classes with Class.forName. Every OSGi-based developer, who had the pleasure to integrate Hibernate into OSGi, knows what I’m talking about.
Web applications in OSGi are problematic
If you ever built OSGi-based web application you surely came across HttpService. This service is described in the OSGi Compendium Specification and is intended to register Servlets and resources in the OSGi environment. The most annoying thing about HttpService is that it supports only the Servlet 2.1 specification. Filters and listeners are not available. This limitation is quite inconvenient because almost every Java web framework uses filters and listeners.
There exists several solutions to deal with the limitations of the HttpService. One of them is Pax Web. This project extends the HttpService with support of recent servlet APIs. Another solution is Spring DM that allows you to deploy a WAR file into an OSGi container. I’m not going to describe these two projects in this post. Instead I would like to describe how you can accomplish modularization of web applications without OSGi.
Do you really need OSGi?
Before you decide to build OSGi-based web applications you have to ask yourself the question: Do you have a requirement to add services to your applications at runtime without restarting the JVM? I guess the most web applications don’t have this requirement. Another question is: Do you want to build modular applications? In most cases the answer to this question is yes. You might have found a reason for using OSGi but wait. If your aim is to break your monolithic WAR files into smaller, more easily managed modules you don’t need OSGi at all. There exist much simple and probably more elegant solutions to accomplish it. Tapestry IoC is one of the them. As far as I know Tapestry is the only one Java web framework that provides this functionality out of the box.
Tapestry IoC vs. OSGi
Tapestry IoC and OSGi have a lot in common. Similar to OSGi bundles Tapestry IoC Modules are packaged into JAR files. The MANIFEST.MF file acts as deployment descriptor for OSGi bundles and for Tapestry IoC Modules. When setting up the IoC registry, Tapestry automatically locates modules packaged into JARs. The framework scans through all JARs in the classpath and searches for a manifest entry Tapestry-Module-Classes inside MANIFEST.MF of every single JAR. The value of the entry is a comma-separated list of fully qualified class names of module classes. This means that a single JAR can contain multiple modules.
Manifest-Version: 1.0 Tapestry-Module-Classes: org.example.lib.LibraryModule, org.example.internal.AnotherModule
The OSGi bundles are identified in the same way. Every OSGi bundle has a MANIFEST.MF containing Bundle Manifest Headers. The headers BundleSymbolicName and Bundle-Version are required. The combination of these two headers is the unique identifier of the bundle inside OSGi. A BundleActivator is used to perform the bundle-specific activities necessary to start or stop a bundle. This is the place where services are registered or unregistered. In Tapestry services are registered in IoC Modules.
Both OSGi and Tapestry IoC provide support for service-oriented architectures. OSGi Service Platform provides a lightweight publish, find, and bind service model for services inside the JVM with the OSGi Framework service registry. Tapestry IoC also provides a Registry which is used to access the available services. While in OSGi you need to perform a service lookup, Tapestry provides a powerful Inversion of Control container. This container allows you to assemble your application from many small, easily testable pieces. Using Tapestry IoC you don’t need to lookup the dependencies of your services. Tapestry will instantiate your classes and resolve all the dependencies of your services. More details about Tapestry IoC can be found here.
Inverting the purpose of Component Libraries
Tapestry is a component-oriented web framework and allows you to create components with ease. The components are reusable by their nature and can be packaged into component libraries. What is exciting about Tapestry is that component libraries can also be used to create modular web applications. For this purpose you can invert the purpose of a component library: Instead of creating reusable libraries that are used in several applications you can create small libraries that are used only in a single application of your customer.
For example if you deliver the same application to several customers, you often have a problem that a special requirement of a customer conflicts with a special requirement of another customer. If you include these special wishes into the application, it will become bloated sooner or later. What you need is a feature that allows you to extend your application without to change it. In Tapestry this can be accomplished by component libraries. If one of your customer wishes a special page in the delivered application, you can create a small library that contains only this special page. This library is delivered to the customer as an additional JAR fie. You don’t have to integrate this special page into your application.
A component library consists not only of components but also pages, mixins and services. This means that you can break your monolithic WAR file into several JARs, each containing a subset of pages. This way you can assemble your application from different pieces and deliver it to different customers. Your WAR file doesn’t have to contain any pages. When you deliver your application to a customer, you just include several libraries (JARs) depending on the needs of the customer.
Another use case is an application that is available in different editions. Just imagine that the Community Edition contains pages that are located inside the WAR file. The Professional Edition contains some further modules with additional pages and services. The upgrade from the Community Edition to the Professional Edition is just the matter of dropping a new JAR file onto the classpath and restarting the application. Nice, isn’t?
Creating component libraries is very simple. Pleas read this guide for more details.
Modularization example
Now let’s have a look at the modularization features of Tapestry. Imagine you need to create a Menu component which creates a navigation menu dynamically. Depending on the edition of the installed application Menu component renders a different set of menu items. For example when your customer upgrades to the Professional edition, the menu component doesn’t have to be changed. It just renders more items provided by the additional JAR.
Creating dynamic menus
The template of the Menu component might look like the following one. The Loop component iterates over a collection of page names and creates a link to the page of current iteration.
<div class="menu" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
<ul>
<li t:type="loop" source="pageNames"
value="var:currentPage">
<a t:type="pagelink" page="${var:currentPage}">
${var:currentPage}
</a>
</li>
</ul>
</div>
Next we create the Java class of the component. The component retrieves the names of the available pages from the service ComponentClassResolver. This service is responsible for resolving page names and component types to fully qualified class names and can be injected into a page or component via the @Inject annotation. Just as with Tapestry applications, Tapestry component libraries have a unique base package. That’s why the service ComponentClassResolver is aware of all the pages available in all the modules of a Tapestry application.
@IncludeStylesheet("menu.css")
public class Menu {
@Inject
private ComponentClassResolver componentClassResolver;
public List<String> getPageNames() {
return this.componentClassResolver.getPageNames();
}
}
Now create a simple Index page containing the Menu component.
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_1_0.xsd">
<body>
<t:menu/>
</body>
</html>
Start the server and call the URL of your application. The result should look like in the following picture.
If you would add a new page to your application, then the Menu component would show a new entry in the navigation menu. The same applies for component libraries. If you just drop a new JAR onto the classpath and restart your application, Tapestry will find all the pages, components, mixins and services inside the JAR. There is no need to change the code of the application if you want to add a new entry into the Menu. For example if you include the Tapestry/Hibernate integration library into you application the built-in page Statistics will be automatically added into the menu. The result looks like:
Because Tapestry contains some built-in pages the code above has to be changed slightly. You probably want to exclude the built-in pages from your menu. For this purpose we need an annotation to mark the pages to be included in the menu.
Defining the Annotation
We will place the following annotation in page classes to mark a page as an element of the menu.
@Target( { ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PluginPage {
}
For example we want the Index page to be included into the menu.
@PluginPage
public class Index {
...
}
Excluding Pages
Now let’s have a look at the modified Menu component. We iterate over the names of the available pages and convert every page name into a fully qualified class name. The class name is used to check if the @PluginPage annotation is present. If the annotation is not present, we filter out the page name.
@IncludeStylesheet("menu.css")
public class Menu {
@Inject
private ComponentClassResolver componentClassResolver;
public List<String> getPageNames() {
List<String> result = new ArrayList<String>();
List<String> pages =
this.componentClassResolver.getPageNames();
for (final String pageName : pages) {
String className = this.componentClassResolver
.resolvePageNameToClassName(pageName);
Class clazz = loadClass(className);
if (clazz.isAnnotationPresent(PluginPage.class)) {
result.add(pageName);
}
}
return result;
}
private Class loadClass(final String className) {
try {
return Thread.currentThread()
.getContextClassLoader().loadClass(className);
} catch (final ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
The result looks like:
Creating customer libraries
Now let’s assume one of your customer wishes a special page to be included into the application you delivered. Because we don’t want to change the application at all we create a simple component library that contains the new page. The structure of the library should look like in the following picture:
The class LibraryModule is the IoC Modul that is used to configure the application. Inside this module we tell Tapestry where to search for our page and component classes. This is accomplished by making a contribution to the ComponentClassResolver configuration. As you remember, we used this service in the Menu component to find the available pages. In the following example we map the prefix mylib to the root package of our library, namely org.example.lib.
public class LibraryModule {
public static void contributeComponentClassResolver(
Configuration<LibraryMapping> configuration){
configuration.add(new LibraryMapping(
"mylib", "org.example.lib"));
}
}
As you remember, Tapestry scans the classpath to find all IoC Modules. That’s why we need to put the following manifest entry into the MANIFEST.MF file.
Manifest-Version: 1.0 Tapestry-Module-Classes: org.example.lib.LibraryModule
Now build the JAR file, drop it onto the classpath and restart the application. You will see the the following result.
Conclusion
In this article you learned how to create modular applications without OSGi. If you don’t have the requirement to add services to your applications at runtime without restarting the JVM then the described solution is sufficient for your application. You don’t need to migrate to OSGi. Of course OSGi solves some other problems that can’t be handled inside an IoC Container. An example is the version incompatibility of included libraries: OSGi is the only one technology that allows you to install the same library in different versions. At the end of the day you have to decide which technology is more suitable for your needs. Tapestry IoC is a simple, lightweight (and maybe limited) solution for modularization of web applications.
In the next article I’m going to describe some more Tapestry features that allow you to create modular web applications. Stay tuned.





Great post – very inspiring!
What about adding services? What do you think about modularizing services into JARs? Any pluses or minuses?
Implementing a service interface inside a component library is a further powerful technique of modularization. This way you can deliver different implementations of a service to different customers.
I’m going to describe these techniques in one of the following article of this series. You will also see some advanced examples of ServiceOverride, Service Chains and Service Pipelines that will show you how to build modular web applications.
Stay tuned.
Hi.
Great post!
Following the instructions that has done something very similar, however when I am with the following problem, if I can do work if I put the JARs of Service (dynamic menus) and Library (customer libraries) and in the WAR. You can do without this dependence directly and explicitly? It would not be possible to simply include in the classpath of JBoss, Jetty or any other container?
Thank you.
Sure, you can make your JARs visible to all web applications via common class loader by placing them into CATALINA_HOME/lib. But as far as I know it is not recommended to put application classes into this folder. Especially if several applications depend of different versions of the same library you should ship the particular library inside the WAR file. In contrast to OSGi it is not possible to install the same library in several versions.
If you follow the described approach in this article and invert the initial purpose of the component libraries, then you have to put the customer-specific libraries into the particular WAR files.
Hi Igor,
Nice article! We met at the technical university of dortmund, after your talk. I got your book today and will read it in the near future.
I like your ideas using tapestry for product lining. It is definitively a powerful feature. The constraint to have to restart the system isn’t too hard. Reload features like hot deployment and others seem not to work properly or even reliably. Restarting a tomcat or jetty is not that time consuming at all.
My question is: You have your own IoC container and I can configure my own types of Injection in the service configuration. Additionally you have a page pool for delivering pages fast and being scalable, since you can configure the pool. But is it possible to use a PoJo-Pool or something similar for Injecting some business logic, too? I ask that question, because I have scalable business logic by using session beans in a Java EE container like JBoss (Heavy-Weight thing isn’t it). I Inject such a session bean to my Tapestry pages using a JNDI lookup, because tapestry service classes are not managed classes of the web container. I annotate the build-method with @Scope(ScopeConstants.PERTHREAD), so that the bean is retrieved only once per request. If I could use the tapestry injection to simulate scalable stateless session beans, that would be really really nice.
best regards
Johannes Neubauer
Your stateful session beans (SLSB) are managed by your JEE container and Tapestry can’t and shouldn’t manage these beans. You can replace your SLSBs which you lookup in a build method by real Tapestry services. These services will be autobuilt by Tapestry, but there is no pooling available. BTW the value of pooling SLSBs is questionable.
Hi Igor,
It is clear to me that Tapestry is no JEE container. My question targeted at the end of your answer. So there is no pooling available. Is there a possibility to facilitate Tapestry as a container, which manages transactions and similar services for me? Or should I use Tapestry to inject Spring Beans in my pages, which support transactions? A nice feature would be to have a transaction per Page request, which I can achieve with PageRenderRequestFilter opening and commiting an transaction for me.
best regards,
Johannes Neubauer
Hi Johannes,
take a look at Tapestry/Hibernate Integration Library. This library provides the per-thread service HibernateSessionManager, which creates a new session for every request. When the session is first created, a transaction is started. At the end of the request the transaction is aborted and your changes are lost unless you commit the transaction manually or you annotate a method with @CommitAfter annotation.
If you need a more comprehensive transaction support you should use Spring.
Hi Igor,
Usually a module have many dependency libraries and related file configurations. How do if I want to package all of them in a ‘bundle’ like OSGi bundle for easily add to or remove from the application?
How do you think if we embed Felix inside Tapestry, like the way Struts 2 do? (They have a Struts-OSGi plugin: http://cwiki.apache.org/WW/osgi-plugin.html)
Hi Soddino,
In OSGi you put small pieces of functionality into a bundle. The bundle and its dependencies are not packaged together. This level of granularity leads to enormous amount of installed bundles but gives you a lot of freedom and flexibility. In the described Tapestry IoC approach you put certain functionality into small IoC modules, which are then dropped into a web application. These modules should not be packed together with their dependencies.
Regarding OSGi inside Tapestry: I don’t see any advantage of starting Felix or Equinox inside Tapestry. But it is possible to integrate Tapestry into OSGi. In my day job we implemented a bundle management application similar to Felix Web Console. This application
is based on Tapestry and HttpService. Fur this purpose the issues TAP5-503 and TAPESTRY-2519 has been fixed. I’ll describe how to integrate Tapestry into OSGi in some later posts.
Hi Igor,
Thank for your reply.
I knew all dependency libraries of a bundle are managed by OSGi container. I meant the ‘private’ dependency libraries of a bundle, they can package together by using ‘Bundle-ClassPath’ header field (In this case, you can easily remove all related files of a module from the application)
About OSGi inside Tapestry: I think the advantage is Tapestry application becomes OSGi container. And you easily develop/active/inactive extension modules for that. Otherwise, it is easy to deploy on ‘traditional’ servlet container like Tomcat, Jetty with no OSGi configurations…
I am enthusiastic in a next your port about integrate Tapestry 5 into Spring dm Server and the way make Tapestry/Spring/Hibernate(JPA) work together in Spring dm Server
Hi Igor,
I think an other advantage of OSGi inside Tapestry is deployment of Tapestry-OSGi app in the Google App Engine environment
Hi Igor,
thanks a lot for this great article!
I’ve got a little question regarding the architecture of an app with modules. Is there any possibility to list all loaded modules?
Thanks a lot
Alex
Tapestry logs the modules which are added to the registry. Please see your logs, you should find something like:
[INFO] org.apache.tapestry5.ioc.RegistryBuilder Adding module definition for class org.apache.tapestry5.ioc.services.TapestryIOCModule
[INFO] org.apache.tapestry5.ioc.RegistryBuilder Adding module definition for class org.apache.tapestry5.hibernate.HibernateModule
[INFO] org.apache.tapestry5.ioc.RegistryBuilder Adding module definition for class org.apache.tapestry5.hibernate.HibernateCoreModule
[INFO] org.apache.tapestry5.ioc.RegistryBuilder Adding module definition for class org.apache.tapestry5.spring.SpringModule
[INFO] org.apache.tapestry5.ioc.RegistryBuilder Adding module definition for class org.apache.tapestry5.services.TapestryModule
[INFO] org.apache.tapestry5.ioc.RegistryBuilder Adding module definition for class org.apache.tapestry5.internal.services.InternalModule