Sunday, February 15, 2009

A Case Study: Modularized Application Development with Struts2

In today's development world, it is not uncommon for mid-to-large scaled projects to have multiple sub-modules which are then assembled for the final product deliverable.

One of the common challenges involved with such development is pretty much the integration piece. Integration can be defined in a multitude of ways and as such there is no "one size fits all" solution. Let's scope our scenario down further. Let's say that our finished product is a web application which is composed of multiple sub-modules, where each module represents a service and this service can have an optional view layer that needs to be pulled into the finished product with all the linkages being proper. Personally I do not have a lot of work experience, but I have been faced with the same problem in two different companies, to make me believe that this could be a common scenario for quite a few other folks out there.

In my previous project, I had a similar situation. At that time I was the architect, build-boy, source control administrator, module lead, business-analyst, client connect.... basically one-guy-fits-all-positions!! And to make things worse, the technology stack for the project was already defined and the view layer would be completely written in Struts2. No offense to Struts2 what-so-ever, just that I was a Faces boy (not knowing anything about Struts except for the concepts) and here I was ... working in alien technologies :)

Coming back to the subject, each of the sub-modules were publishing pages that needed to be integrated in the final application and linked via the menus too. During the earlier builds, the integration process was a nightmare, where we would pull in all the code, merge all the struts.xml files, update links on the menu-sections of the page - in one phrase - inefficiency at its best :)

Just about when I was about to give up on the hopelessness of the situation, I decided to have a closer look at what Struts2 had to offer and I was pleasantly surprised that I could have the flexibility of distributed configurations (the type that I was used to with Faces) right within Struts! The key was the struts-plugin.xml file!!

So the first thing I had to do was rewire our current modules a little bit. Let's take the following sample layout for a module:



Let's assume that each module has a DAO layer, a model / API layer, and a web layer. First thing that I had to do was ensure that my web-tier projects are creating jars instead of bundling the compiled code in the classes directory. Next, I moved over the struts.xml for the module to src/java/struts-plugin.xml. This would mean that my-module-view.jar would contain the struts-plugin.xml in its root directory. And anyone who has written plugins in Struts2 would know that the all struts-plugins.xml files available on the classpath are loaded up when the struts application starts.

Neat. So I have one problem taken care of, which was the manual merging of the struts XML files. But I've still got one more problem to fix. The whole manual setup of the menu layouts in the application. Now why did we have to do this? 'Cause there was a need to make releases which had only a certain subset of modules available.

This is where I got thinking ... each of my menu items opens up in to a page / view of the underlying module. And each of this menu items is pointing to an Action link ... so would it be possible to read all actions, and add a custom attribute to the action definition in the Struts xml file ... and then use this information to construct my menu??? Sounds logical, and yes ... it is absolutely doable!!

All menu related actions were augmented with additional attributes in the struts xml file. So, now my struts XML files started to look as follows:



<struts>
<package name = "/mymodule/manage/">
<action name = "ModuleSettings" ... >
<result ...>...</result>
<param name="menu.id">My_Module_Settings</param>
<param name="menu.parent">ROOT_MENU</param>
<param name="menu.order">3</param>
<param name="menu.display.name">My Module Settings</param>
</action>
....
</package>
</struts>

Once I had all the required parameters, it was now about reading these action files and setting up the menu model in memory. This can be easily achieved using the following lines of code:



function loadMenuItems() {
Configuration configuration = Dispatcher.getInstance().getConfigurationManager().
getConfiguration();
Set<String> packageNames = configuration.getPackageConfigNames();
for (String pName : packageNames) {
parsePackages(config.getPackageConfig(pName));
}
}

function parsePackages(PackageConfig packageConfig) {
Map<String, ActionConfig> actionConfigs =
packageConfig.getActionConfigs();
String namespace = packageConfig.getNamespace();
for (String aName : actionConfigs.keySet()) {
parseActionConfigs(namespace, aName, actionConfigs.get(aName));
}
}

function parseActionConfigs(String namespace, String aName,
ActionConfig actionConfig) {
// Check if the action config represents a menu element.
Map<String, Object> params = actionConfig.getParams();
if (params.containsKey(MENU_ID)) {
// MenuAction found.
String menuId = (String) params.get(MENU_ID);
String parentId = (String) params.get(MENU_PARENT_ID);
String displayName = (String) params.get(DISPLAY_NAME);
String menuOrder = (String) params.get(MENU_ORDER);

// Now go ahead and create the menu item from the above information
// and setup the menu context.

}
}


Once I had the menu context initialized, then all I had to do was just rewrite the menu handling pages to use the menu context and publish information. The rest of the work of merging the wars into a single project was left to the build system =)

Note: The above parameters are just an illustration and if you choose to leverage a similar approach, then you could potentially introduce other parameters, like security related information (ROLES / PERMISSIONS / etc).

And obviously, due IP constraints, I will not be able to publish any of the code base, but here is something interesting... Among all the folks that I have interviewed in the past, almost all of them have no idea what Swing is all about and believe that Swing is purely for desktop. Yes, predominantly it is for the desktop, but hey... there is a javax.swing.tree package and this package incidentally has a fullfledged Tree implementation!! What if I created a MenuItem that extended from DefaultMutableTreeNode ?? Don't you think that the whole effort of ordering and traversal could be completely ignored?? ;-)

So this was the way I went about resolving a rather nagging problem in my development project. Do you have any interesting experiences / ideas that help solve similar situations??

-Rogue