Modular Web Apps with JSF2

Modularized Applications

The pattern of constructing modular applications from independent compilation units has been around for years, but the concept has been slow to take hold in mainstream Web development. Enterprise platforms like Eclipse RCP, NetBeans RCP, and OSGi are built upon on the idea of composing an application from a several interdependent modules that deliver new functionality beyond their component parts. In an ideal world, the mere inclusion of an existing module and a little glue code is enough to integrate a module with the rest of the system.

 

A blue-sky modular Web app might look like a series of modules, or plugins, that contribute libraries and presentation templates to a single application context root. These modules would be compiled, assembled and included at build time depending on the desired deployment configuration. Many of us have found ourselves of implementing this classic 'plug-in' functionality from scratch in various Web frameworks and build tools with varying degrees of success. Development on today's Java-based Web app usually takes place on a single project that builds a WAR file that might use one or more external Java libraries in the form of JAR files. Adding or removing functionality means adding views, and in the case of a WAR file we have one view root, which means you’re mixing presentation scripts. There’s no clear separation of presentation scripts between modules outside of storing them in different directories.

A Possible Solution: Multiple WAR Files

Not necessarily an impossible solution, but building a Web application out of multiple WAR files most often involves various contortions to share session and application state between WAR context roots. This approach is further complicated by the fact that it’s not a standard J2EE configuration, so it's not supported by the major IDEs and there’s no standard for how it's implemented by J2EE containers. Not to mention the classloader issues that further complicate sharing of libraries between WARs. The workaround is usually to package the WARs into an EAR file. Unfortunately Tomcat doesn’t support EAR files – you get the picture on this can of worms.

JSF2 to the Rescue

I'm hearing the phrase “JSF2 to the rescue” quite often. With JSF2, we now have the ability to package presentation markup along with class files into JAR files and have those files served from the context root of our application. Well, that knocks out one huge issue toward building modular Web apps! Since these component JARs are all deployed in a single WAR file, we've effectively eliminated any classloading issues, and we can deploy application functionality in the form of JAR modules that contribute presentation scripts and compiled code. Sprinkle in some CDI magic, and we have the makings of a modular Web app environment -- today!

 

The source code for this project is available at http://github.com/rkilcoyne/modweb. Clone a copy and follow along as I describe our example Web app.

 

ModWeb Example

Let's build an example application called ModWeb that will be comprised of two modules. These modules are added and removed from the application by adding and removing the corresponding JAR from the WEB-INF/lib directory located in the resulting WAR. We'll be using maven to build our project, which will be comprised of a root project directory and four maven modules. One maven module corresponds to the WAR file while the others represent JAR files that will be included in the WAR file upon build.

 

Screen shot 2011-05-26 at 3.55.18 PM.png

Mod01 and Mod02

Our application modules are called Mod01 and Mod02. Each module will provide at least one bean that implements the interface ModuleProvider. On application start-up, every bean implementing ModuleProvider will be loaded and exposed to the entire application. ModuleProvider gives the implementing class an opportunity to specify its name, id, description, and a default action.  In the case of this example, the default action is the name of the page to be presented to the user when accessing this module.

 

Each application module has a META-INF/resources folder that contains its own faces-config.xml file. Web resources located in this folder will be contributed to the root context path of our Web application. If a page called “hello.html” is located at META-INF/resources/hello.html, then it will be accessed from the root context path with: http://localhost:8080/modwebapp/hello.html.  Note there’s no need to specify the application module in our URL.

ModAPI

The ModAPI project will contain any API classes that will be shared between all modules and ModWebApp – the main WAR project. Keeping the API in its own JAR promotes Separation-of-Concerns and avoids circular dependencies within the Maven environment. The ModWebApp, Mod01, and Mod02 maven modules will all be dependent on ModAPI.

 

AppRegistry

In order to manage load our application modules and make them accessible to ModWebApp, we’ll create a bean called AppRegistry as an application scoped singleton EJB. The Singleton EJB is new with the EJB 3.1 spec and allows us to specify that one and only one instance of our AppRegistry be created in the assigned bean scope. In this case, the bean scope is our Web application. The AppRegistry bean will be located in the ModAPI JAR, so it will be available to all beans in the ModWebApp WAR project and its JAR libraries. The @Named annotation was added to the AppRegistry class declaration so we can access this bean from our JSF pages by name.

 

On application start, all classes implementing the interface ModuleProvider in the classpath are instantiated in the Application scope and are injected into our PostConstruct method. A reference to each module is stored in a private Map managed by the AppRegistry bean.

 

ModWebApp

Our Web app will be deployed as a WAR file and includes the JARs generated by the Mod01, Mod02, and ModAPI maven subprojects. We created a JSF page called index.xhtml. This page uses a JSF data table to invoke the getModules() method on the AppRegistry object with the EL expression: #{appRegistry.modules}. We then iterate over the list of returned ModuleProvider instances to display each module name, id, and description. We also use the getDefaultAction property of the ModuleProvider interface to generate a link to a JSF script provided by each module.

 

 

Screen shot 2011-05-26 at 3.58.20 PM.png

 

Success! Pluggable Modules

We can now control which modules are included in the deployed application through maven dependencies. Try it for yourself! Open the pom.xml for ModWebApp and remove the dependency on Mod01, clean, package, and re-deploy to your application server. After opening the home page, you’ll see that only one module, Mod02, is available.

 

I hope this little demonstration allows you to further modularize and componentize your Web applications. This ability to include presentation script functionality in a separate JAR file along with the template and component features provided by Facelets is sure to yield some interesting developments in how Web apps are composed and how functionality is reused. Best of luck – Rick.