Chaining Struts actions and commands
You can create a wrapper action that calls other Strut actions or other controller commands. You may need to create new actions that accomplish two tasks in a row, while reusing the actions or controller commands that are already implemented. For example, the UserGiftRegistryCreate action first registers a new user, and then creates a gift registry in the name of the newly created user. Since both UserRegistrationAdd and GiftRegistryCreate are implemented, you can reuse the implementation, and therefore, chain different Struts actions and controller commands into a single Struts action. The new action that does the chaining is considered the host action. Invoking a controller command in a Struts action is relatively easy. As controller commands have been moved to an EJB container, you cannot execute a command that is retrieved by CommandFactory as in previous versions. Instead, use the following invocation logic:
getWebAdapter().executeCommand(getCommandContext(request), inMap, YourControllCommand.class.getName())
where getWebAdapter() returns an instance of WebAdapterServiceAccessBean, getCommandContext(request) retrieves the command context in use from an HTTP request, and YourControllCommand is the interface name of the command to be invoked.
As a reference implementation, getAdapter() is really a one-liner:
private WebAdapterServiceAccessBean getWebAdapter() { return new WebAdapterServiceAccessBean(); } Likewise, getCommandContext() is also simple:
private CommandContext getCommandContext(HttpServletRequest request) { return ((RequestHandle) request.getAttribute(ECAttributes.ATTR_EC_REQUEST_HANDLE)).getViewCommandContext(); } The execution of getWebAdapter().executeCommand(getCommandContext(request), inMap, YouControllCommand.class.getName()) returns the response object returned by the executed command. Invoking another IBM Gift Center Struts action is more complex. Struts actions and action forms use action names to resolve the name of the service that will be invoked, and the service mapping group that determines how incoming request name-value pairs should be mapped to a BOD object. The action name is stored in the ActionMapping object used by the host action. Therefore, change the action name right before the chained action that is being called. Otherwise, the service mapper will not be able to find the correct mappings. For example, the Struts action UserGiftRegistryCreate chains another action, called GiftRegistryCreate. Without any change, the action name will be UserGiftRegistryCreate even if GiftRegistryCreate is being invoked. Then, GiftRegistryCreate will not be able to retrieve the mapping it expects because all the expected mappings are bound to its name, GiftRegistryCreate. You can resolve this problem using following steps:
- Associate the host action with GiftRegistryActionMapping. This action mapping provides a new property: actionPaths. Specify comma-delimited action names. For example, if you want to invoke GiftRegistryCreate from UserGiftRegistryCreate, specify <set-property property="actionPaths" value="/GiftRegistryCreate"/>. The following is the complete configuration for UserGiftRegistryCreate:
<action parameter="com.ibm.commerce.giftregistry.commands.UserGiftRegistryCreateCmd" name="UserGiftRegistryCreateActionForm" path="/UserGiftRegistryCreate" type="com.ibm.commerce.giftregistry.struts.UserGiftRegistryCreateAction" validate="false"> <set-property property="https" value="0:1"/> <!-- This property indicates that UserGiftRegistryCreate will invoke the action GiftRegistryCreate --> <set-property property="actionPaths" value="/GiftRegistryCreate"/>
</action>- To invoke a chained action, invoke super.invokeService(getMappingForNewActions(mapping), form, inMap, request, response), where getMappingForNewActions() is implemented as follows (assuming that only one action is to be invoked):
private ECActionMapping getMappingForNewActions(ActionMapping mapping) { ECActionMapping newMapping = new ECActionMapping(); newMapping.merge(mapping); newMapping.setPath(((GiftRegistryActionMapping) mapping).getActionPaths()); return newMapping; } The snippet first creates a new action mapping, and then duplicates the content of the current action mapping into the new mapping. As an ActionMapping instance is a singleton per action, you do not want to permanently change it. The new mapping uses the specified action path as the new action name. Since this new action mapping will be passed into the invokeService() subsquently, the service mapper will use the mapping information bound to the action name in this new mapping. Therefore, the chained action will be invoked properly. Sometimes, a transaction will be committed after a controller command is executed. To roll back the transaction in the host action, use WebControllerHelper.rollbackService(GiftRegistryUtils.getActivityToken()), where GiftRegistryUtils.getActivityToken() returns the activity token used in the current transaction.
(C) Copyright IBM Corporation 1996, 2006. All Rights Reserved.