RunData Service

RunData is an interface that is passed around within Turbine. RunData provides the threading mechanism, as there is one RunData object per HTTP request. The RunData service manages the isues surrounding multiple requests being accepted. TurbineRunData is the interface that is specific to the Turbine RunData service, and via the recyclable interface can be sent back to the Factory for recycling.

RunData objects should never be held on to across requests. They are considered one time only objects.

All this higher level processing by the service means that for each HTTP request there is an interface that is castable to, or available, that can be used to access all information to do with that request. As an example, information such as the content type of the request and the response can be queried or sent, as well as other information surrounding servlet HTTP management, such as Sessions, PrintWriters as well as Turbine specific information such as Users, AccessControlLists, Templating, Error Handling and Contexts.

Configuration

In the TurbineResources.properties the service needs to be defined for Turbine to initialize with.

# -------------------------------------------------------------------
# 
#  S E R V I C E S
#
# -------------------------------------------------------------------
# Classes for Turbine Services should be defined here.
# Format: services.[name].classname=[implementing class]
#
# To specify properties of a service use the following syntax:
# service.[name].[property]=[value]

services.TurbineRunDataService.classname=org.apache.turbine.services.rundata.TurbineRunDataService
.
.
.
# -------------------------------------------------------------------
#
#  R U N   D A T A   S E R V I C E
#
# -------------------------------------------------------------------
# Default implementations of base interfaces for request processing.
# Additional configurations can be defined by using other keys
# in the place of the <default> key.  
# -------------------------------------------------------------------

services.RunDataService.default.run.data=org.apache.turbine.services.rundata.DefaultTurbineRunData
services.RunDataService.default.parameter.parser=org.apache.turbine.util.parser.DefaultParameterParser
services.RunDataService.default.cookie.parser=org.apache.turbine.util.parser.DefaultCookieParser

Using RunData

As RunData encapsulates all aspects of Turbine's gathering the HttpRequest and sending the HttpResponse, any status to do with the Request can be queried and any manipulation of the final response can be carried out through the RunData Object.

Turbine is a servlet and the functionality equated with the javax.servlet and javax.servlet.http can be manipulated through the RunData interface. One of the most useful components of the Servlet libraries was Sessions. These are accessed through RunData; it also provides direct access to the PrintWriter, Server details, Content Types, ContextPath, Redirections, Client details, etc. Some of the functions which equate with the servlet libraries are:

getLocale()
setLocale(Locale locale)
getCharSet()
setCharSet(String charset)
getContentType()
setContentType(String mimetype)
getOut()  //get PrintWriter Object
getRedirectURI()
getRemoteAddr()
getRemoteHost()
getRequest()
getResponse()
getServletContext()
getSession()
getStatusCode()  

The get/setLocale(), get/setCharSet() and get/setContentType() methods are used for specifying the locale, character encoding and content type of the body of the servlet response.

The method setLocale() is called to specify explicitly the locale of the response. If setLocale() is not called, the "locale.default.language" and "locale.default.country" properties from Turbine Resources are used to determine the locale. If these properties are not set, the JVM's default locale determines the locale.

If the locale is set to something else than the default locale or Locale.US, an explicit encoding is not specified with the setCharSet() method or the "locale.default.charset" property, and the main MIME type of the content is "text", the getContentType() method adds a locale specific encoding (charset) to the content type automatically.

The locale specific charset is obtained from the MimeTypeService, which maintains mappings between locales and charsets.

As always consult the Javadocs for more detail.

To use Turbine to only manipulate the functions that came with the Sun Servlet libraries is to miss out on Turbine's power. Turbine is a framework which enables manipulation of the HttpResponse and HttpRequest above and beyond the simple Servlet libraries. Turbine has services and layers surrounding those available with the Response and Request that allow easier creation and management of Websites and Web-enabled Applications. In fact you wont have to type import javax.servlet.* again!

Session Management

Sessions are managed in Turbine via the User interface. The User interface allows for a blend of cookie management, memory management and relational database to be used to manage a user's session. A user of the site can have their Turbine session set as either temporary storage or permanent storage. The permanent storage will survive a servlet engine restart. How all this is managed by Turbine is transparent to the Java Engineer. As an example assume we want to monitor how often a user returns to the website we are developing and we want to reward them for their returning interaction:

//in Login Action class
public void doPerform(RunData data, Context context) 
    throws Exception
{
    //get the Parameters, username and password
    ParameterParser params = data.getParameters();
    String loginname = params.getString("username");
    String password = params.getString("password");

    try
    {
        //cast to TurbineUser interface
        //and check if user is in system
        TurbineUser user = (TurbineUser)
                             TurbineSecurity
                              .getAuthenticatedUser(loginname, password);

        //put User into Session 
        data.setUser(user);

        //mark the User as logged in
        user.setHasLoggedIn(new Boolean(true));

        //add to the access counter
        user.incrementAccessCounter();

        //add to the access counter for the session
        user.incrementAccessCounterForSession();
 
        //check to see if user is to have
        //their status changed to valued
        if(user.getAccessCounter() > 500 )
        {
            //set into persistant storage
            //that our visitor is now a 
            //valued user
            user.setPerm("valued", new Boolean(true));
        }

        data.save();
    }
    catch (Exception e)
    {
        //error handling
    }
}

While skeletal this shows a good example of permanent and temporary management of User information. The line data.setUser() puts the User into session, the hasLoggedIn() method updates the User Object to reflect the fact that the session has passed Authentication; however until the RunData's save method is called both the User and hasLoggedIn flag are only existing in Turbine's memory. Neither become a part of RunData's HttpSession until they are saved into the session through data.save(). The AccessCounter is persistent storage and is saved into the database. The AccessCounter for the session counts the number of pages that are requested throgh Turbine for the user's session. Once their session logs out or the session times out, that information is lost. The user.setPerm() method allows for information to be stored persistently into the database as a HashTable entry. The example above was intended to show that information can be handled through a consistent interface allowing for the management at the request/response, session and persistent levels without any direct manipulation of the HttpRequest, HttpResponse, HttpSession or Relational database. As always, refer to the Javadocs for more information on the RunData, User and TurbineUser interfaces.

Parameter and Cookie Parsing

One of the most useful parts of the RunData interface is the easy retrieval of parameters attached to a request. The ParameterParser and CookieParser Interfaces are available through the RunData interface and provide convenience methods for gathering parameters from either the URI or Session. Turbine handles parameters as name/value pairs through the URI via the BaseValueParser object, allowing the parameters to be requested as a Java type rather than a catch-all:

/** the doPerform method from Invoice.java Action class */
public void doPerform(RunData data, Context context) throws Exception
{
    //get parameters
    ParameterParser params = data.getParameters();

    /**
     * Where "units" is the HTML Input Form name
     */
    int units = params.getInt("units");  

    /**
     * Get the description, if there is no entry from the
     * HTML input form, set the default as "No Description". 
     */
    String description = params.getString("description","No Description");

    /**
     * If there is no total, set the value to 0.
     */
    BigDecimal total = params.getBigDecimal("total",new BigDecimal(0));
}

The above example shows gathering of name/value pairs from HTML Form Inputs and setting default values for those forms. In the case of description, if the description is null, then the default value "No Description" will be substituted. The Javadocs for ParameterParser show more information on the methods available.

Security Management

The RunData interface also exposes security management methods and information which are encapsulated into a request. The RunData interface exposes the AccessControlList Object, which encapsulates the Permissions and Roles for the Groups the User is in. The getACL() method allows for the permission of the User to be queried against their ACL.

/** the doPerform method from DeleteInvoice.java Action class */
public void doPerform(RunData data, Context context) throws Exception
{
    //check if the User is authorized before
    //deleting the invoice
    AccessControlList acl =  data.getACL();
    if(acl.hasPermission("deleteinvoice"))
    {
        //delete invoice logic
    }
    else
    {
        data.setMessage("You do not have permission to delete an invoice.");
        data.setTemplate(data, "UnauthorizedRequest.vm");
    }
}

The above example gets the AccessControlList Object for the User through the RunData interface. The ACL is used to check against the Permissions the User has, the PermissionSet, or a list of all permissions the User has, most likely taken from a database. In this example, the check is against the string "deleteinvoice". If the User has the permission, they will be able to delete the invoice, otherwise the User will get an unauthorized request Velocity screen.

Template Management

The RunData also exposes methods to manipulate Screens, Actions, Pages and Layouts. The templating service assembles the screens, actions and layouts as well as exposing template methods. The methods for managing screens and actions includes:

getAction()
getLayout()
getLayoutTemplate()
getScreen()
getTemplateInfo()  //returns TemplateInfo Object
hasAction()
hasScreen()
setAction()
setLayout()
setLayoutTemplate()
setScreen()
setScreenTemplate()    

For more information on how to use the RunData Interface with Velocity templates and the Velocity context, view the Velocity Site documentation.

Messaging

One of the other useful wrappers the RunData interface provides access to is messaging. The Message can be set as a String, an ECS Element or as a FormMessages object. RunData contains access to other convenience methods to do with Messaging such as:

addMessage(Element msg)
addMessage(String msg)
hasMessage()    //if the request has a message
getMessage()
getMessageAsHTML()
getMessages()   //returns a FormMessages Object
setMessage(String msg)
setMessage(Element msg)
setMessage(FormMessages msgs)  

An example of using messages with Velocity templates in an action is below:

/** the doPerform method from Invoice.java Action class */
public void doPerform(RunData data, Context context) throws Exception
{
    data.setMessage("A message for output.");
    data.setTemplate(data, "Test.vm");
}

This would be accessed in the Velocity template via:

#*
    Velocity file, Test.vm, showing messaging example.
*#

$data.getMessage()

And the output would be:

A message for output.

The Javadocs for RunData show all the methods available through the interface and is definitely the place to start when looking for more information on what RunData exposes. The RunData interface is one of the most important areas for a Java Engineer to understand and be familiar within the Turbine Framework. Understanding RunData is of continual benefit.