The Source for Java Technology Collaboration
User: Password:



   

The WARS Architectural Style The WARS Architectural Style

by N. Alex Rupp
01/27/2004


Contents
Introduction
Any Implementation Today
The Client API
Framework Architecture
Framework Checklist
Conclusion

"Style is everything. I mean, the blues are three chords. But every guy that plays the blues plays differently, because that's their own style. Right?" —Skip Engblom

Introduction

The open source software community is building a wall to contain Microsoft and other monopolistic forces, and middleware is the mortar.

It should come as no surprise, then, that the most important feature a piece of middleware should provide is the ability to wire together disparate subsystems and to allow systems to be broken down, so that specialized communities of developers can each focus on building their bricks, their pieces of the whole.

With enough of these communities working together, there are very few limits to what we'll build. Open source Java will dominate the enterprise market. The days of per-processor licensing fees will be gone forever. Medium-sized businesses and cottage industries all over the world will spring into a new life. The global 2000 enterprises from the 20th century will meet a hundred thousand reinvigorated family enterprises in the 21st. The era of big ideas is ending, and the insects shall inherit the earth.

Okay, seriously — that was a bit dramatic, but you get the point. Competition from the bottom will force the companies at the top to either lower their prices or dramatically improve the quality of their products and services to remain competitive. And open source software will play a driving role in that shift.

The goals of a framework (which qualifies as middleware because of its role in lacing together disparate information systems) were neatly outlined by one Dr. Trygve Reenskaug in the "Administrative Controls in the Shipyard" essay he presented at the ICCAS conference in 1973. The goals and principles he identified are as fresh and relevant today as they were 30 years ago. Dr. Reenskaug suggested that the goals of a framework were to support:

  1. Simplified manual override
  2. Division into modular subsystems
  3. Behavioral transparency of subsystems
  4. Pluggable subsystem modules
  5. The capability of continuous growth for the total system

But large systems are complex. The only hope for developers who wish to model them is to decompose the larger "total" system into subsystems, and then repeat the process for subsystems, breaking them down into role categories and objects that fulfill the requirements of those roles. In Java, the terms "interface" and "role" are practically interchangeable. Interfaces define the nature of an object but not its specifics, and can limit the scope of interactions between objects in the system according to their public roles. This both reduces the complexity of the system and makes it more flexible and extensible. Therefore, breaking down complex systems into families of interfaces is something we should at least attempt.

The Model, View, and Controller roles of the MVC architectural style have been offered as a means of categorizing components in web application frameworks, but in my previous article on this topic, I argued that the attempts to implement these distinctions have failed to show that this is a correct approach. The primary evidence of this failure is the absence of a single component framework that has managed to isolate the concerns of its components into Model, View, and Controller interfaces.

A correct approach would be to create a new hypothetical style that describes best practices already used in the problem space, instead of prescribing an architectural style from a distantly related problem space. Then the hypothetical new style should be tested through implementation. Any flaws uncovered in the style should be addressed in the refactoring stage, and the cycle repeats itself: hypothesize, implement, refactor.

If these principles are applied to the complex systems that drive dynamic web applications on the server side, we find a family of roles, which should be utterly unsurprising. The roles I outlined in my first article have themselves undergone a refactoring. I've renamed the DataSource interface to State because of confusion between it and javax.sql.DataSource, and also removed the Presentation interface entirely. Presentation components are officially beyond the scope of this problem space, but the representation of the application's state is not. At the suggestion of others in the community, I've added a Representation role, which I'll describe in more detail below. After incorporating these changes, we are left with:

  1. Application State components that maintain the incoming data.
  2. Action components that can alter the State.
  3. Workflow components that decide the order in which Action objects execute.
  4. A Representation subsystem to limit the complexity of the State for component developers by restructuring or "pre-chewing" the state to make it more intuitive for component developers.

Since writing the first article, I've been notified of a better term than "pattern" to describe what to call this collection of roles. It's just an "architectural style." Nothing more, and simply put. I call this architectural style WARS, which is very appropriate considering the use to which we put it in designing Java web apps.

I've yet to find a middleware/command-pattern/servlet/MVC (call it what you like) framework whose components can be neatly separated into implementations of Model, View, and Controller interfaces. That isn't to say MVC is a hoax — not at all. It's absolutely great for building user interfaces. It just isn't the best way to describe the architectural style of our middleware systems. On the other hand, there is at least one middleware framework whose components implement the WARS architectural style. Its name is Shocks, and I wrote it as a proof of concept for this architectural style.

But before we get to that, I'd like to note that other frameworks, most notably Apache Cocoon, have already been using this architectural style (or something very close to it) for some time. Since I developed it by studying how things were actually being done in popular systems like Struts and Webwork, and by disregarding the MVC marketing speak, this should not come as a surprise. WARS is a descriptive term for what is already common practice and satisfies the principle of least astonishment.

It's just that we hadn't hit on the right words to describe it. Until now — or so I hope. An insightful (if somewhat aged) letter by Berin Loritsch in the Cocoon archives shows that the Cocoon project was aware of very similar concepts over two years ago — it just never crystallized into our lingua franca. Struts has been decomposing toward the commons-chain package for some time now, so this idea reinforces their work in that area and vice versa. WebWork introduced an extremely sophisticated state machine (in their value stack concept) quite a long time ago. My guess is that the terminology surrounding these ideas prevented them from taking wing. "The difference", wrote Samuel Clemens, "between the almost-right word and the right word is really quite a large thing. It's the difference between the lightning bug and the lightning." Whatever the case, I look forward with renewed interest to all their projects.

Having said that, I want to turn now toward my proof of concept — the Shocks framework. It's a far cry from perfect. But like the first hot air balloon flight in Paris, it has exciting and unexpected implications. There will be better versions, better implementations, evolution — there always is.

After all, you know what they say ...

Any Implementation Today

... Is better than a perfect implementation tomorrow. Already, based on advice from Craig McClanahan of the Struts project, dIon Gillard from Codehaus, and dozens of others, I've made significant changes to the internal structure of Shocks. What I have to present today is a step beyond simply getting "there and back again;" from the web page to one or more action classes that perform some kind of business logic, and back out to the user by way of some kind of presentation subsystem.

The internal architecture of Shocks is very simple and straightforward. There's a shocks.wars package that contains the master interfaces. Figure 1 displays this arrangement.

Figure 1
Figure 1. Arrangement of shocks.wars package

As you can see, the Representation interface is quite shy. The idea for representation components was inspired by Greg Wilkins' recent blog entry about "contentlets" and Roy Fielding's Representational State Transfer architectural style.

A representation component will have access to the application's runtime state, but will not alter the state in any way. Instead, it'll harvest information from the state at runtime, restructure it for easy access, and make it available to a higher context — such as a data repository through JMX.

Representation components aren't action components, because they don't alter the state at runtime. They're not workflow components either, because they make no decisions about the execution of actions. And they're not really state components, because their job is not to passively store data — although the State interface would be their closest relative. If anything, the Representation interface serves as a protocol-neutral bridge between the application state and developers. If well-developed enough, it could completely shelter action and workflow components (and their developers) from the complexity of the state. So Representation gets its own seat at the table.

The State interface is also very minimal. I'm erring on the side of caution for the time being, and allowing other interfaces to extend the basic State requirements. Implementations of those objects can be recast inside of the different Action and Workflow implementations without cluttering up the method signatures with specific dependencies. A toMap() method will probably be in order eventually. Both of State's methods return java.lang.Object, which likewise must be recast. This flexibility comes with an accompanying degree of complexity, which we'll explore more later.

The Action interface is simple. Actions are given a reference to the State and they don't return anything. Their role is to alter the State in some way, or to trigger an external process as a result of the state. The other components in the framework make no assumptions about what actions actually do. A Workflow component can check the state before and after an action has fired, and use that information to figure out what to do next, but has no other way of knowing what goes on therein.

Workflow implementations accept a reference to the State object but, unlike Action objects, they also return a State reference. This is important, because it might not be the same State as the one they received. Right now, Workflow components examine the application state, decide what to do next, maybe fire off some actions, forward control to another Workflow component, and once they're done doing their thing, they give control back to whatever object called them. All they do is examine the state and decide whether or not to execute actions. By themselves, action objects aren't capable of deciding the order in which they execute. They can set a flag in the state, thereby suggesting the order, but ultimately, the workflow components make that decision.

The best part about Workflow components is that the brunt of their decision-making functionality can be replaced with a JSR-94-compatible rules engine. In the future, the role of Shocks' Workflow components will be to delegate decisions to that JSR-94 rules engine by way of JMX. The entire Workflow engine will therefore be completely modular, extensible, and manageable.

Pages: 1, 2

Next Page » 

View all java.net Articles.

 Feed java.net RSS Feeds