Apache » Cocoon »

  Cocoon Core
      2.2
   homepage

Cocoon Core 2.2

Creating an Action

Creating an Action

Actions are unique in the world of Sitemap components. They don't implement the SitemapModelComponent and they merely exist to perform processing. For most business level processing, flowscript usually works better. However there are some systemic things we can implement using actions quite nicely. Actions are also unique in the sense that they do not need to be pooled. There is nothing in the contract that requires you to call multiple methods or do things in a certain order. Now a ThreadSafe component has its own challenges to ensure that the action is not a choke-point. Things to look out for are the use of synchronized when it is not necessary or trying to do too much in the action. Actions can be chained so keep things simple.

Actions have only one method to worry about. The sitemap invokes the method supplying several pieces of information and expects the return result of a java.util.Map. There are three outcomes from processing an action: the action succeeds and returns a java.util.Map, the action fails and return null, or the action throws an Exception. In the sitemap, an Action is invoked like this:

<map:act type="my-action" src="foo">
  <map:generate src="{actionval}.xml"/>
  <map:serialize/>
</map:act>

The sitemap only executes the elements internal to the map:act element when the Action returns a Map. If the Action returns null, that snippet is skipped. Using this to your knowledge you can provide confirmation pages that only show up when everything is processed successfully, otherwise we fall through to the remainder of the sitemap. Obviously we don't want to throw an exception if we can help it.

What the Sitemap Provides

The sitemap provides the following elements:

  • Redirector--to perform redirects within the Action based on your logic.
  • SourceResolver--to find resources.
  • Object Model--to gain access to the request, context, and session objects.
  • Source--the string defined in the "src" attribute in the Sitemap.
  • Parameters--the parameters defined in the Sitemap at runtime.
Most of these items were covered in the SitemapModelComponent Contracts documentation. The only item not covered so far is the Redirector. The org.apache.cocoon.environment.Redirector provides an interface to send a redirect so tha the browser requests a new document and we stop processing this one.
Fixme: We need more information on when to call which method on the Redirector

Creating the Action

It's not too hard to create an Action, but it can be difficult to keep track of what you are expecting the Sitemap to give you and what you give the Sitemap. As long as the purpose for your action is small and focused we can keep everything straight. What we are going to do here is create a "theming" action. The responsibility of the action is to select the stylesheet for the theme, but if the source is "xml" then we don't apply a stylesheet at all. It's actually not too difficult to perform. First let's set up our Sitemap:
<map:choose pattern="**.xhtml"
  <map:generate src="{1}.xml"/>
  <map:act type="theme">
    <map:transform src="{theme}2xhtml.xsl"/>
  </map:act>
  <map:serialize/>
</map:choose/>
The plan is to use a parameter passed in to determine the theme. That means we aren't going to need the Source or SourceResolver parameters to perform our processing. We will need to extract the request parameter "theme" and interpret it. If "theme" has the value "xml" we don't do any transformations. If "theme" doesn't exist, we will provide a default value. And lastly, we will copy the request parameter "theme" to the returned Map so it is accessible inside.First the boiler plate code:
import java.util.Map;
import java.util.HashMap;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.excalibur.source.SourceResolver;
import org.apache.cocoon.environment.ObjectModelHelper;
import org.apache.cocoon.environment.Redirector;
import org.apache.cocoon.environment.Request;
import org.apache.cocoon.acting.Action;

public class ThemeAction implements Action, ThreadSafe
{
    private static final DEFAULT_THEME = "default";

    public Map act( Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters params )
        throws Exception
    {
        // .... the contents will be called out specifically below.
    }
}
Now we can concentrate on the method itself. First things first, we need to get the "theme" request parameter. We do this by accessing the Request object from the object model:
Request request = ObjectModelHelper.getRequest( objectModel );
String theme = request.getParameter("theme");

Ok, so we have the theme from the request object. Now lets check if it is empty and provide a default value if it is empty..

if ( null == theme || theme.length() == 0 )
{
    theme = DEFAULT_THEME;
}

Next, let's check to see if the theme is the "xml" theme. We'll do it in a way that does not matter what case the "xml" value is. We return null in this case so that the transformer in the sitemap snippet above does not get added to the pipeline.

if ( "xml".equalsIgnoreCase(theme.trim()) )
{
    return null;
}

Now we can assume that the value we have in the parameter "theme" is the name of a valid theme. We aren't going to do validation here, that's something you can have if you have inclination.

Map returnValues = new HashMap();
returnValues.put("theme", theme);

return returnValues;

That's it! We're done. We have an action that does not have to be pooled, selects a theme, provides a default, and does not apply formatting if the theme is "xml".