Action Events

Action events allow you tie events that occur on the user interface, such as a form being submitted, or a button clicked, to post back to a specific event handler on a VelocityPortletAction class. We will look at a third Velocity portlet that demonstrates action events to perform user actions. This portlet is a simple data entry form with 5 input fields. The data is stored in the session for the current user, but does not persist past the lifetime of the session.


<portlet-entry name="CobiJonesPortlet" hidden="false" type="ref"
               parent="Velocity" application="false">
  <meta-info>
      <title>The Cobi Jones Portlet</title>
      <description>Tutorial showing an Action Event Executing, dedicated 
                   to the man Cobi</description>
        </meta-info>
  <parameter name="template" value="cobi-jones-form" hidden="true"/>
  <parameter name="action" value="portlets.CobiJonesPortletAction"
             hidden="true" />
  <media-type ref="html"/>
    <category group="Jetspeed">tutorial</category>
</portlet-entry>

And here is what the portlet looks like when running:

When you press ?Save Cobi?, the values on the form are passed to the Turbine (the MVC controller servlet), from where you can get the values of the input fields into your Java action class. Let's look at our action event:


public void doUpdate(RunData rundata, Context context) throws Exception
{
    Player player = (Player)rundata.getUser().getTemp(PLAYER);
    if (null == player)
    {
        player = createNewPlayer();
        rundata.getUser().setTemp(PLAYER, player);
    }
    String cobi = rundata.getParameters().getString(INPUT_FIRST_NAME);
    String jones = rundata.getParameters().getString(INPUT_LAST_NAME);
    if (!cobi.equalsIgnoreCase("Cobi") ||
        !jones.equalsIgnoreCase("Jones"))
    {
        rundata.getRequest().setAttribute(COBI_ERROR,
               "Hey now, you cant substitute Cobi with "
                + cobi + " " + jones + "!");           
    }
    player.setFirstName(cobi);
    player.setLastName(jones);
    player.setPosition(rundata.getParameters().getString(INPUT_POSITION));
    player.setCaps(rundata.getParameters().getInt(INPUT_CAPS));
    player.setActive(rundata.getParameters().getBoolean(INPUT_ACTIVE));
         
    rundata.getUser().setTemp(PLAYER, player);
}

This code above is from the CobiJonesPortletAction. This is the action event. As we found out in previous section, Velocity action have three standard methods: BuildNormalContext, BuildConfigureContext, and BuildMaximizedContext. In addition, you can zero or more action events. Action events are how your portlet code reacts to user interface interactions. In our example, we have a simple data entry form. The "Save Cobi" button is a submit button which posts the contents of the HTML form back to the portlet.

We can get the contents of the form from the rundata parameter passed into our action event. Jetspeed puts all the form parameters into a parameter parser object. You can get strings and other data types and then set the values into the portlet session.


    String cobi = rundata.getParameters().getString(INPUT_FIRST_NAME);
...
    player.setActive(rundata.getParameters().getBoolean(INPUT_ACTIVE));

In this simple example state is kept in the servlet session. In future examples we can store the values to the database. Jetspeed provides methods to get and set objects into the session:


    rundata.getUser().setTemp(PLAYER, player);
...
    Player player = (Player)rundata.getUser().getTemp(PLAYER);

Let's look at how to setup form posting from the Velocity template (cobi-jones-form.vm):


<form method="post"
      action="$jslink.setAction("portlets.CobiJonesPortletAction")">
...
 <input type="submit" name="eventSubmit_doUpdate" value="Save Cobi"/>

First, the form must link back to the current portal page. The $jslink tool handles this. Next the action must be specified. We specify the action to be our portlet's action with the setAction parameter on the action attribute of the form element. The submit input requires a convention for finding the correct event on the specified action: you must prefix the name attribute of the action with the string "eventSubmit_" and then enter the name of the action event method. In this case, it is "eventSubmit_doUpdate".

The input fields are specified using standard Jetspeed data entry macros defined in


      <tr>
        #formCell ("First Name" "firstname" $player.FirstName)
      </tr>
      <tr>
        #formCell ("Last Name" "lastname" $player.LastName)
      </tr>
      <tr>
        #formCell ("Position" "position" $player.Position)
      </tr>
      <tr>
        #formCell ("Caps" "caps" $player.Caps)
      </tr>
      <tr>
        #formCheckBox2 ("Active" "active" $player.Active)
      </tr>

The first parameter to #formCell is the input caption, the second is the input's name attribute, and the third is the player object from the Velocity context. Ensure that the input's name attribute is the same as in your Java action event:


        #formCell ("Position" "position" $player.Position)
 
    public static final String INPUT_POSITION   = "position";
 
          player.setPosition(rundata.getParameters().getString(INPUT_POSITION));

Finally, the doUpdate action event shows how to do some basic exception handling by passing an exception string from the action event to the BuildNormalContext method.


        if (!cobi.equalsIgnoreCase("Cobi") || 
            !jones.equalsIgnoreCase("Jones"))
        {
            rundata.getRequest().setAttribute(COBI_ERROR,
                  "Hey now, you cant substitute Cobi with "
                  + cobi + " " + jones + "!");           
        }

We set the error message text into a servlet request attribute (from the Servlet API), and then later on in the pipeline, in the BuildNormalContext method, we check for existence of the error message text and display an error message in case an exception occurred in the action event.


// make sure they don't sub Cobi!
String cobiError = (String)rundata.getRequest().getAttribute(COBI_ERROR);
if (null != cobiError)
{
    context.put(COBI_ERROR, cobiError);
}

This error message is then retrieved from the context in the template:


      #if ($cobierror)
      <tr>
        <td colspan="2">
          <table bgcolor="red">
            <tr>
              <td>
                $cobierror
              </td>
            </tr>
          </table>
        </td>
      </tr>
      #end