You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 7 Next »

Roller Struts2 Migration

This page provides info relevant to the Roller struts2 migration effort which is starting as of Roller 4.0 and will end ??

Changes in Design

UIAction class

One of the goals of the struts2 migration is to make our actions simpler and easier to work on by having all the code and features which are common to all (or most) actions take place automatically and the UIAction class is how we do that. The UIAction class is a base class for actions to extend and provides support for all the things that we expect most of our actions will need to do, such as looking up the authenticated user, applying security checks on weblog permissions, setting action messages and errors, defining a page title, setting the tabbed menu preferences, etc, etc. Many of these things which were handled in custom or inconsistent ways in our struts1 forms are now handled in a simple and consolidated manner now and are provided to action developers for free.

Feel free to take a look at the UIAction class when you first start working with struts2 actions in Roller, but i'll talk about some of it's features in the coming sections. The main thing to know is that the UIAction class is meant to serve as a solid base for all Roller struts2 actions.

Action Freebies

One of the main features of the UIAction class is that it provides free access to some of the things that were handled in custom ways in the old struts1 actions. The best examples of this are how to access the UserData object representing the authenticated user, and the WebsiteData object representing the weblog being dealt with by the action. Pretty much all of Roller's actions require these 2 objects to work properly and so the UIAction has taken care of them for you by providing you getAuthenticatedUser() and getActionWeblog() methods.

These objects are automatically populated for all actions which extend UIAction so that the writer of the action can simply expect them to be there and focus on the action logic rather than filling their action with lots of boiler plate code for checking for these objects and looking them up. These objects are populated by a special struts2 interceptor which is applied to all actions which extend UIAction, so effectively what you get as an action developer is a freebie =)

Currently these are the only 2 objects which are extracted from the request and made available via UIAction, all other objects should be loaded by the action itself since they are more action specific.

Security Enforcement

One of the major things that the old struts1 code did poorly was action security enforcement. In specific, in all of the old struts1 code you would see a bunch of boilerplate code which basically did this ...

RollerSession rses = RollerSession.getRollerSession(request);
RollerRequest rreq = RollerRequest.getRollerRequest(request);
UserData user = rses.getAuthenticatedUser();
WebsiteData weblog = rreq.getWebsite();
if(user.hasPermissions(weblog, PermissionsData.AUTHOR)) {
  // do action logic
} else {
  return mapping.findForward("access-denied");
}

Now that's a lot of code to be duplicating in every action and lucky for you, with the new struts2 actions it's no longer necessary. Instead of having all that security enforcement code inside of the action methods its now been pushed higher up the execution chain and it happens in a custom interceptor before the action method ever gets executed, so all you need to know is that if the request actually gets to your action method then all the right permission checking has been done already.

Now, obviously each action will have different security constraints and so we still need a way to specify those constraints, so you job isn't completely taken care. All you need to do to ensure that the proper security rules are applied for your action is potentially override a couple of methods. The UIAction base class implements an interface called UISecurityEnforced which looks like this ...

    public boolean isUserRequired();
    
    public boolean isWeblogRequired();
    
    public String requiredUserRole();
    
    public short requiredWeblogPermissions();

These methods provide

myPrepare() method

Tabbed Menu controls

Migration Walkthrough

Migrating Actions

Here are some notes on the series of steps that you might run through to migrate an existing Roller struts1 action to a new struts2 action. The specific details of the work will vary depending on the situation.

  • copy old action code to new action class and rename (makes things less confusing)
  • make sure package name is appropriate at the top of your copied code
  • extend UIAction (MigratingUIAction if request object is really needed, but try not to)
  • define security needs of your action.
    • remove all attempts to get authenticated user via RollerSession and replace with a simple call to getAuthenticatedUser(). access to the authenticated UserData object is provide for you by having your action extend the UIAction class, so there is nothing that you need to do. by default, all actions require an authenticated user, so if the user is not properly authenticated when trying to access an action then they will get an access denied page.
    • remove all attempts to get the weblog used by the action via a RollerRequest object and replace with a simple call to getActionWeblog(). just like above, this is extracted from the request and populated for you.
    • to see how you can control the security options for your action, check out the UISecurityEnforced interface which provides control points. this interface is implemented by the UIAction class which you most likely should be extending, so you can modify the default behavior by simply overriding any of the methods from that interface in your action class.
  • fix action method declarations to
    • return just a String
    • not accept any params
    • not throw any exceptions (handle these inside your action method!)
  • fix action method results to just return a String instead of an ActionForward
  • if your action handles a lot of fields then define a class level attribute called "bean" with getters and setters. if your action only needs to make use of a couple fields then you can just define those attributes directly in your action and provide getters and setters.
    • NOTE: if you are defining a "bean" in your action, make sure that the bean gets initiated. i.e. you can't define your bean as "MyBean bean = null" because that's not an initiated attribute. instead you should probably do this ... "MyBean bean = new MyBean()"
    • once that is done you need to change usages of the old action form.xxx() calls to getBean().xxx() or just xxx() if you didn't define a "bean".
    • now you can delete all old code which trys to extract the form bean from the passed in actionForm
  • fix up error/message handling
    • delete all instances of ActionMessages() and ActionErrors()
    • delete all calls to saveMessages() and saveErrors()
    • replace all attempts to set action messages/errors with calls to addMessage(key), addMessage(key, param), addError(key), and addError(key, param). these methods are convenience methods built into the UIAction class which make it trivial to set errors and messages which will automatically get rendered on the page.
  • fix up form validation
    • if there is a custom validate() method then replace it with a no arg method myValidate().
  • tidy up the action results.
    • try to use the defaults like SUCCESS and INPUT, and custom ones where applicable, like "cancel".
  • fix up use of "models"
    • replace all attempts to set a "model" attribute on the request object with a call to setModel()
    • modify all custom "model" objects either just use UIModel or extend UIModel
    • NOTE: you may need to begin the process of migrating the jsps a bit in order to decide how much of the old model class you need to keep. it's entirely possible that once you rework the jsp a bit you'll find that most of the elements of the old model class are no longer needed.

Migrating JSPs

  • replace include for taglibs.jsp to taglibs-struts2.jsp
  • do a search and replace for "fmt:message key" with "s:text name"
  • replace html:form with s:form and delete the method param
  • change the new s:form action attribute to the correct action name, like myAction!method
  • replace instances of "html:text property" with "s:textfield name"
  • add "bean." in front of all name attributes for form fields
  • No labels