Subproject Name: JakartaStruts
Official Web Page: http://jakarta.apache.org/struts
Metaproject Name: JakartaProject
Sponsor: ApacheSoftwareFoundation
Wiki: http://nagoya.apache.org/wiki/apachewiki.cgi?StrutsProjectPages
Struts is a ModelViewController FrameWork for simplifying WebBasedDevelopment? and a specific subproject of the Jakarta project (http://jakarta.apache.org/).. It brings JavaServerPages and JavaServlet development a bit closer to what WebObjects offers.
(There are other Jakarta frameworks - e.g., Avalon (a "server framework", Jetspeed (a "Web portal" framework), Log4J (a logging framework), Lucene (a search engine framework), Turbine (another Web framework), Velocity (a Web framework and seemingly more).)
Struts includes the following primary areas of functionality:
- A ControllerServlet that dispatches requests to appropriate ActionClasses? provided by the application developer.
- Centralized screen flow mappings, turning a web system effectively into a series of abstract & reusable state transitions.
- JSP CustomTagLibraries?, and associated support in the ControllerServlet, that assists developers in creating interactive form-based applications.
- Utility classes to support XmlParsing?, AutomaticPopulation? of JavaBeans properties based on the JavaReflection? APIs, and internationalization of prompts and messages.
June 30, 2003: Struts 1.1 released.
I used to bitch about Struts because I didn't like how you had the one master file for your application. I knew (from theory and experience) that it would get very messy to maintain as your app scales beyond a few pages. (This is that "centralized screen flow mappings", BTW).
Then I got a project using Struts, and XDoclet (http://xdoclet.sourceforge.net/). Instantly, the "centralized" screen flow became decentralized, to the developer view of the world. This made a huge difference to the usability of Struts. If you're out there and using Struts, I strongly urge you to check out XDoclet.
Now, if they could only hurry up and get that automatic validation and transformation of data type support that's in the nightly build out to release... -- RobertWatkins
Another experience of struts. We had to get a site up and running that had hundreds of pages of dynamic content (dynamic in this instance doesn't mean much - a few things in the page templates changed based on who you were, certain things you did on the site, etc). The company had a Web designer (pure HTML) already, and some Java developers who'd learned in-house, and as a result had only ever written monolithic servlets (I mean really monolithic, no layers or reuse at all). When I joined I had to propose how the site development should be done.
What I wanted to achieve was that the Web designer retained control of the design, and that we should be able to track changes in the static versions of the site he produced for marketing. I also wanted to enforce view-model separation, to give the developers some better habits. There is actually a good tool for this (xmlc) but I rejected that because Struts structured the MVC side of things better, and using xmlc it wasn't clear we'd be able to reuse other people's custom tags. What I wanted was something to make Struts more like xmlc.
So I wrote it myself. I wrote a JakartaAnt task to support converting an HTML file into a set of JavaServerPages. Its parser looks for elements with 'id' attributes and for each of these I generate a separate JSP, then finally generate a single JSP to tie them together (using the Struts 'template' tags). Config files let me change arbitrarily which files get included in which template. This lets the Java developers write small, custom JSPs for pages where code generation doesn't fulfil our needs (mainly pages which display lists of things). The task rewrites all URLs in the pages to point at where their equivalent JSPs will be, and rewrites all forms to use the Struts form tags. (the rules used in this process are quite complicated, I can explain more if asked but its unnecessary to make the point).
I would like to at some point (when the company allows) release this as open source; I haven't seen anyone else do this with Struts (even though there are a number of other Struts code generators, none of them work this way AFAIK). I can thoroughly recommend it as an approach. To give you some idea of the effect, the current site has 67k lines of html but only 5k lines of custom JSP (which is waaay more than we needed really). All of the HTML is written by the designer using WYSIWIG tools. As well as the effort saved in this version of the site, we have just reskinned the site for a different customer, in about a week. The Struts code, for the most part, has not changed at all, since we had clean model-view separation; the Java developers only had to write a few new custom JSPs. -- BrianEwins
In 2Q02 I evaluated Struts for potential inclusion in my company's product, and rejected it in favor of an internally-developed JSP model 2 architecture framework. Struts has a number of really great things going for it, including increasing momentum and nice internationalization hooks.
But it presents a SystemOfNames and AllocationOfResponsibility that departs decidedly from the ModelModelViewController paradigm promulgated by the Smalltalk community, and that increases reliance on the nebulous notion of "beans". Stateless and fine-grained, Actions are less useful in the application layer of a FourLayerArchitecture than stateful, long-lived, large-grained ApplicationModels underlying one or more Views. By encapsulating the state of a user's interaction (selections, edits, etc.) in instance variables, surviving between user interface events, and implementing behavior for responding to all events coming from its supported views, an ApplicationModel provides good factoring (high internal cohesion, low external coupling) and well-encapsulated context for deciding how to respond to a user interface event. Reuse of behavior is easily achieved by inheritance between ApplicationModel classes and delegation between ApplicationModel instances.
Struts also presents a burden and source of coupling with its external configuration. JSPs and ActionMappings? are coupled by a URL scheme you're burdened with inventing when, by contrast, a constant URL with two standard request parameters (identifying a view and an HTML element) suffices to uniquely identify a user interface event. EnterpriseApplications with hundreds of use cases could require maintenance of hundreds of ActionMappings? in the external configuration file (not to mention hundreds of Action classes) unless abstraction and parameterization of Actions is expected under the intentions of Struts' designers. Using larger-grained classes (and event dispatching from standard URL parameters instead of custom URL path segments) results in much less source to maintain in the application layer.
Perhaps in future versions Struts will complement its other valuable characteristics with a less expensive event dispatching approach, and a SystemOfNames truer to ModelModelViewController concepts proven over the last two decades in the Smalltalk community. Until then it may not be the SimplestThingThatCouldPossiblyWork.
-- RandyStafford
Firstly, let me challenge your assumptions:
- Web systems are fundamentally different beasts from interactive components that were traditionally developed with Smalltalk's MVC. Traditional MVC is not appropriate to Web systems. For application architecture in the same "spirit" of MVC, the appropriate term is PresentationAbstractionControl (PAC) outlined in Buschmann's A SystemOfPatterns?, volume 1. This is why many so-called "MVC" systems vary from the Smalltalk approach (NeXT's AppKit? or WebObjects, for example) - they're not actually MVC at all, just follow the same decoupling principles. ApplicationModel wasn't a "control", i.e. event handler, so much as it was a "binding" or a "mapping" between layers.
- OK, let me challenge your assumptions. :) You assumed that I assumed that traditional MVC is appropriate for Web systems. I didn't explicitly assume that, although I think there are some analogies that hold up quite well (e.g. the role of InputState? is played by a FrontController). My beef (ok, one of them) is with the casual usage of "MVC" to describe a completely different AllocationOfResponsibility. I'm not yet convinced that PAC is a more appropriate term - it intends "a hierarchy of cooperating agents" where each "agent" is "responsible for a specific aspect of the application's functionality". "Hierarchy" and "agent" are loaded terms that remind me of the ideas in Herzum & Sims' "Business Component Factory" (ISBN 0471327603 ). Plus I think it is appropriate to distinguish ApplicationModel from DomainModel, as is done in the original ModelModelViewController factoring. To understand traditional MVC is to understand that "control" responsibilities (in the Jacobsonian connotation) were very much allocated to ApplicationModels, in addition to the mediation and adaptation responsibilities we both recognize. For example it is common for ApplicationModels to have "action methods" that respond to the events of "action buttons" being pushed on the UI. Back in the days of fat-client two-tier architecture, it was even common for these action methods to control database transactions.
- Why are beans considered nebulous? Non-graphical JavaBeans have a specific definition: they are a class with a zero-argument constructor, and contain attributes (simple or integer-indexed) that conform to a particular design pattern (the get/set thing). Alternatively, they have a companion BeanInfo? class to describe the property accessors if they don't follow that particular pattern, though this is rare. This is all in the JavaBeans specification (though it's not something immediately easy to find on Sun's site :)
- I consider "bean" nebulous because it doesn't model anything. Yeah, I know, when properly coded per the definition, a bean follows certain trivial conventions (originally intended for snap-together UI building tools - whatever happened to that?). I've read the book too. By the way, I don't consider those bean conventions to form a "pattern" - what are the conflicting forces that are resolved? What are the other known uses? That's another example of vocabulary usage gone bad through marketing hype. Appending "Bean" to some root doesn't exactly reveal the intention of an abstraction. For example what, exactly, is the abstraction modeled by a DynaFormBean??
Here's a few points about how I view Struts (and why I like it):
- State transitions are encapsulated in the config file. I think declarative state transitions are a good thing, even if it's a large file. XDoclet can help here if you'd prefer not to centralize these.
- YouArentGonnaNeedIt. How many times have you needed to completely reconfigure the navigation paths in your user interface? I had that feature in my framework two projects ago, and it is nice and neat and tidy from the theoretical perspective, but I found I never had to change the state table after initial creation.
- The tedium of writing beans is there, I admit. But it is eliminated with Struts 1.1 DynaFormBeans? that are effectively just SymbolDictionary? that holds an object graph for a particular view.
- I'm not sure I follow your argument about how Struts URLs are less appropriate than a constant URL with 2 request parameters. I really do not prefer constant URLs - in my opinion, URLs refer to named resources on the web and hence should vary with the pages they present. This seems more of a taste issue than a matter of architectural deficiency.
- And how are tens or hundreds of Struts application URLs not constant after you invent them, one per action or use case? Are "actions" really Internet resources that should be located or identified? Contrast with "/FrontController?page=renderedPageSymbolicName&element=clickedElementName" which uniquely identifies the event needing a response without exposing implementation details.
- Struts follows a two-tiered abstract model philosophy: a client model and a business model. The client model represents "incoming" unvalidated data. The business model represents validated data, i.e. propositions and knowledge representation. ActionFormBeans? represent the client model, and in a sense, they just act as an "input buffer", or "holding area" to validate the state before copying that state into your strongly typed, validated business model (or in a simpler case, your database).
This leads to the tradeoff of requiring validation in two places, but this isn't unique to Struts (it's the same tradeoff when deciding to use
JavaScript-based validation, for example). It's a matter of the amount of strain you want to place on any particular portion of your system. In a distributed system, where your business model is implemented remotely (through web services or EJBs, or whatnot), the Struts model might make some sense - validate in the Web tier and avoid the network round-trip. Similarly, many object models will validate business rules before hitting the RDBMS thus potentially also avoiding a round-trip. Some would say all of this is b.s. and all validation should just be in the database anyway, so buy a faster interconnect between your Web tier and your database. :-) YMMV.
I'm with all that. "Input buffers" or "holding areas" to buffer/hold values and objects during validation are important - and SplitValidation? is real and appropriate. I like to do syntactic validation as close to the glass as possible (in JavaScript) and semantic validation in the domain model. Incidentally that was one of the nice things about GemStone - you didn't have to explicitly create and merge an "editing copy" of a domain object graph to apply edits and invoke semantic validation (as you have to do with TopLink). Instead GemStone implicitly created the editing copy for you at the disk page level upon first write to an object, and you could abort the edits by simply throwing a validation exception out of your EJB to rollback the JTS transaction. It was neat.
- The point about Action classes definitely is to abstract & re-use them. They represent both Commands and States. ActionMappings? are effectively just "symbols" that represent state transitions. The typical pattern in most struts projects is:
Setup Action(s) -> Render JSP -> UI Think Time -> Process Action(s)
- The beauty of the Action approach is that the commands do NOT need to know about each other and do not bind to each other. They communicate through data placed in the dictionary objects in 3 available Servlet scopes: application, session, request, and of course, HTTP parameters.
- On one project, which was a record-oriented information system, I managed to encapsulate CRUD operations into 3 Actions classes - for any object graph. The idea was to ensure your Bean graph is accessor-compatible with your business object graph, when copying the state from one into the other - or to provide a mapping between the two models. The BeanUtility? classes made this possible for any two object graphs. To me, this is very similar to the Smalltalk AspectAdaptor approach.
- That's cool. Actually your approach sounds similar to the Assemblers described in PatternsOfEnterpriseApplicationArchitecture, with these "beans" :) playing the role of DataTransferObject. AspectAdapters? typically adapted a UI widget to a DomainObject aspect that was a "simple" value (e.g., String, Number, Date) as opposed to an aggregate like a DataTransferObject bean.
- ApplicationModel is an example of the Mediator pattern at work. One of the problems with Mediator is their bulk, so it's a tradeoff. Actions+ActionForms? are examples of the command pattern, with ActionServlet? (and the struts-config file) acting as a thin mediator for state transitions. Action Forms survive user-interface events because their are placed in the Session scope by default. I'm not certain how this is different from preserving UI state in an ApplicationModel.
- Clearly there are numerous known uses of Command and Mediator in the application layer of a FourLayerArchitecture. I'm most accustomed to Mediator (ApplicationModel) because it was used in classical ModelModelViewController and Command wasn't. I've always had good luck with it, even in JSP Model 2 architectures, and have never experienced the bulk problem you mention. In fact my experience has been that its larger granularity is its strength. But we all come with the baggage of our background. :) YMMV. -- RandyStafford
Struts fits a vary narrow role: screen flow, UI data binding, and Web-tier validation. It isn't over achieving, and fits that role well, IMHO. --
StuCharlton
ApplicationModel wasn't a "control", i.e. event handler, so much as it was a "binding" or a "mapping" between layers.
I have to disagree. Action buttons on user interfaces were typically bound to ApplicationModel methods that implemented the system's response to presses of those buttons. In other words, ApplicationModels "controlled" interaction and/or "handled events", in addition to their other responsibility of adapting DomainModels to views. -- RandyStafford
See http://jakarta.apache.org/velocity/ymtd/ymtd.html for an interesting discussion comparing JakartaVelocity with JSP. My experience with velocity and turbine is that it is far more productive than struts or plain JSP. Check it out. -- ChanningWalton
See http://www.johngresh.com/doc/researchInterests/ood/collectionSwitch/paper/collectionSwitchPaper.pdf for an interesting discussion of an alternative controller servlet that can be used in web applications that opt out of the Struts framework. The Collection Switch design pattern provides for a controller class that actually scales (i.e. NEVER grows). -- Alex
CategoryFramework