Actions are the key artifacts in a Struts application because they are the primary home for service tier invocations as well as the application's presentation logic. In Strecks, the responsibility of a traditional Struts action is divided between two objects, an controller action and an action bean.
In the next sections we talk about the roles and responsibilities of controller actions and action beans.
While it is less likely that you will need to write your own controller actions, you will probably
find it useful to know a little bit about how they work when writing action beans.
Once we've covered this, we'll talk briefly about default controllers, that is, the controller actions
implied when not explicitly specified in your action bean using the @Controller
annotation.
The idea behind the controller action (also called action controller) is that common request processing logic can be abstracted and reused across many actions. For example, simple CRUD web applications typically use three types of request handling logic:
The example below shows the implementation of a controller handling form submission:
@ActionInterface(name = BasicSubmitAction.class) public class BasicSubmitController extends BaseBasicController { @Override protected ViewAdapter executeAction(Object actionBean, ActionContext context) { BasicSubmitAction action = (BasicSubmitAction) actionBean; ActionForm form = context.getForm(); HttpServletRequest request = context.getRequest(); boolean cancelled = false; if (request.getAttribute(Globals.CANCEL_KEY) != null) { cancelled = true; } if (form instanceof BindingForm && !cancelled) { action.preBind(); BindingForm bindingForm = (BindingForm) form; bindingForm.bindInwards(); } String result = cancelled ? action.cancel() : action.execute(); return getActionForward(context, result); } }
Notice that there is no application-specific code present. All that is present is
request processing logic, which can be reused.
A number of pre-packaged action controllers are available,
including form handling controllers as well as dispatch controllers
which mimic the behaviour of DispatchAction
, MappingDispatchAction
and LookupDispatchAction
.
With request processing logic abstracted into a controller, all that remains for an action implementation is to implement the domain-specific aspects of each operation. This is the job of the action bean.
In Strecks, the action bean is registered in struts-config.xml exactly as if it were a regular
Action
. When used for the first time, an annotation on the action bean's class
definition is used to identify the controller.
An instance of the controller is created. The controller, which extends Action
,
is held in the action cache for subsequent requests.
Each time a request is received, the controller creates a new instance of the action bean.
The action controller itself holds no state, and is shared among multiple requests.
Lets take a look at an action bean implementation.
SubmitEditBookingAction
, shown below,
is invoked when submitting an update from the "Edit Holiday Booking" form.
@Controller(name = BasicSubmitController.class) public class SubmitEditBookingAction implements BasicSubmitAction { private HolidayBookingForm form; private HolidayBookingService holidayBookingService; private WebHelper webHelper; public void preBind() { } public String cancel() { webHelper.setRequestAttribute("displayMessage", "Cancelled operation"); webHelper.removeSessionAttribute("holidayBookingForm"); return "success"; } public String execute() { HolidayBooking holidayBooking = form.getBooking(); holidayBookingService.updateHolidayBooking(holidayBooking); webHelper.setRequestAttribute("displayMessage", "Successfully updated entry: " + holidayBooking.getTitle()); webHelper.removeSessionAttribute("holidayBookingForm"); return "success"; } //dependency injection setters omitted }
The action bean uses BasicSubmitController
, which
mandates that action bean collaborators implement the BasicSubmitAction
interface.
SubmitEditBookingAction
does so
by implementing the preBind()
,
cancel()
and execute()
methods.
Notice how the application-specific code in the action bean is no longer responsible for
request processing logic. Consider cancel()
, which is invoked when a
user clicks on a button rendered using the <html:cancel/>
; tag.
The logic for determining whether a form
has been cancelled is implemented in the controller, not the action bean.
Strecks has built-in support for a number of controller actions, each associated with a particular action bean interface. For simple applications, it is quite likely that certain action beans implementing one of these interfaces (e.g BasicAction) will always be associated with a particular controller (BasicController). In these cases, the @Controller interface is somewhat redundant, because it always specifies the same controller action. To save having to explicitly declare the controller for each action bean, it makes sense to have a default controller associated with each interface.
The example below provides an illustration. The action bean ExampleImplicitAction does not declare its controller. Instead, the identity of the controller defaults to the class associated with the BasicAction interface, namely BasicController.
//this information is implied: @Controller(name = BasicController.class) public class ExampleImplicitAction implements BasicAction { public String execute() { return "success"; } }
By default, the following action bean interface default controllers apply.
Action Interface | Default Controller |
---|---|
org.strecks.action.BasicAction
|
org.strecks.action.basic.BasicController
|
org.strecks.action.BasicFormAction
|
org.strecks.action.basic.BasicFormController
|
org.strecks.action.BasicSubmitAction
|
org.strecks.action.basic.BasicSubmitController
|
org.strecks.action.BasicDispatchAction
|
org.strecks.action.basic.BasicDispatchController
|
org.strecks.action.NavigableAction
|
org.strecks.action.navigable.NavigableController
|
org.strecks.action.NavigableFormAction
|
org.strecks.action.navigable.NavigableFormController
|
org.strecks.action.NavigableSubmitAction
|
org.strecks.action.navigable.NavigableSubmitController
|
org.strecks.action.NavigableDispatchAction
|
org.strecks.action.navigable.NavigableDispatchController
|