Cernunnos Manual


Request Attributes

In Cernunnos, tasks and phrases use Request Attributes as a common means of collaborating with one another. Request attributes are a collection of contextual information organized as a Map: attribute values may be any type of Object, but keys are always Strings.

Setting Request Attributes

You can create request attributes in several ways; perhaps the most common (and most broadly applicable) method is the <with-attribute> task . This task registers any Object (specified by the VALUE reagent) under any name (specified by the KEY). The resulting name/value pair will be visible to all subtasks: this is the role and the entire purpose of the <with-attribute> task.

Conversely, some tasks that set request attributes have other duties. The <sql-connection> task is a great example. <sql-connection> is responsible for opening a JDBC connection to an SQL data source. This connection is then made available to subtasks of <sql-connection>, which use it to execute SQL statements. Once all subtasks have completed, <sql-connection> closes the SQL connection in accordance with JDBC best practices.

You can optionally designate the name of the request attribute that will contain the SQL connection through the ATTRIBUTE_NAME reagent. If you don't specify a name, a default name will be used (more on this topic below). Tasks that open or establish resources for the benefit of subtasks commonly support the ATTRIBUTE_NAME reagent. These include <invoke-method> , <node-iterator> , and <print-stream> .

Getting Request Attributes

The usual way to access the value of a request attribute is through the ${req()} phrase . Just put the name of the desired request attribute between the parentheses, like so:

${req(Attributes.LOCATION)}

Just like tasks, phrases use reagents to accept inputs; the expression ' Attributes.LOCATION' above is associated with the KEY reagent of ${req()}. Unlike tasks, phrases typically have only one reagent.

Accessing request attributes is very common in Cernunnos. The ${req()} phrase, therefore, is the default phrase implementation. Since it's the default, you don't have to specify it explicitly to use it.

The expression:

${req(Attributes.LOCATION)}

is usually abbreviated as:

${Attributes.LOCATION}

Other Important Considerations

Request attributes exhibit some characteristics that may catch unwary programmers off guard; don't worry, they will make complete sense one you get used to them (we hope). Be aware of the following considerations when dealing with request attributes.

Last In, First Out (LIFO)

Request attributes have a lifecycle: they exist only within the scope of the task that creates them. In other words, they are visible to descendants, but not to ancestors or siblings.

Request attributes may also cover other request attributes. This process occurs whenever a task registers an attribute with the same name as an existing attribute. In this case, the new attribute will be visible within the scope of the task that (re)registered it; the covered attribute will be inaccessible.

Default Reagent Values

Many tasks and phrases provide default values for some or all of their reagents through standard, well-known request attributes. Consider this example:

          
            <with-attribute key="SqlAttributes.DATA_SOURCE" value="${jndi(java:comp/env/jdbc/PortalDb)}"> 
              <sql-connection> 
                <sql-statement sql="DELETE FROM up_layout_struct WHERE user_id = ?"> 
                  <parameter value="${sql(SELECT user_id FROM up_user WHERE user_name = '${req(username)}')}"/> 
                </sql-statement> 
              </sql-connection> 
            </with-attribute>
          
        

Who told <sql-connection> how to connect to the database? Who told <sql-statement> or ${sql()} what JDBC connection to use?

No one did -- at least, not explicitly. This information is communicated through a system of conventional request attributes. Since <sql-connection>, <sql-statement>, and ${sql()} each agree on the convention, you can leave these mundane details out of your scripts in 99% of cases. For the remaining 1% of cases, you have the opportunity to specify a DataSource or Connection object explicitly.

Naming Schemes for Common Attributes

Request attributes that serve as default values for reagents are given names according to a convention: first a name like "Attributes" or "SqlAttributes" (roughly corresponding to Java package names), followed by a period '.' character, followed by a noun in uppercase (multi-word nouns are separated by underscore '_' characters). For example:

Ad-hoc request attributes need not -- and should not -- adhere to this convention. User-defined request attributes that contain special characters may not play well with some Task or Phrase implementations. Use camel-case names for request attributes, like Java variables ( e.g. 'myFile' or 'theXslTemplateLocation').

Request Attributes at Startup

Depending on how you use Cernunnos, one or more request attributes may be present in the collection even before the first task is invoked.

Attributes.ORIGIN

The value of this attribute is a URL (in String form) representing the absolute location of the current script. It set by the Cernunnos runtime whenever a Task object is created from a file -- whether read over HTTP, over FTP, from the classpath, from the local file system, etc.

Attributes.ORIGIN is tremendously useful because it allows you to reference additional resources -- XSLT stylesheets, properties files, other Cernunnos scripts, etc. -- using relative path expressions. Thanks to this attribute, you don't have to know where resources will be when they are ultimately deployed; you only have to know where they are relative to the script that uses them. Pass Attributes.ORIGIN to the CONTEXT reagent of a task that uses an external resource; it will then evaluate the LOCATION of that resource relative to the current script.

There are, moreover, several tasks that use Attributes.ORIGIN as the default value for their CONTEXT reagent. Some examples are <properties> and <crn> . These tasks will evaluate LOCATION relative to the current script if CONTEXT is omitted.

Command Line Attributes

When you invoke Cernunnos from the command line, you may optionally specify request attributes as command line parameters. Take a look at this example:

> crn good-advice.crn many hands make light work INFO [main] runtime.ScriptRunner.[] Nov/28 21:00:14 - ************************************************** ** Invoking ScriptRunner.run(Task, TaskRequest) ** TaskRequest contains 6 elements ** - $3=make ** - $1=many ** - Attributes.ORIGIN=file:/C:/HOME/danann/cernunnos/good-advice.crn ** - $5=work ** - $2=hands ** - $4=light **************************************************

The first parameter is always the location of the script (can be relative, absolute, or a URL). Subsequent parameters are added to the collection of request attributes as $1, $2, and so on.

Web Application Attributes

Cernunnos has been used to develop several Java Portlets. For detailed instructions on how to create portlets with Cernunnos, refer to this article.

The Cernunnos portlet class ( org.danann.cernunnos.runtime.web.CernunnosPortlet) can pre-load the request with useful attributes whenever it invokes a script. For starters, it creates request attributes for the two objects provided by the portlet container in each action or render cycle:

These will be set to instances of ActionRequest/ ActionResponse or RenderRequest/ RenderResponse in the processAction and render methods, respectively.

In addition, you may optionally define a collection of request attributes for your portlet using spring dependency injection. These attributes my be of any type, and will be accessible to all scripts in both processAction and render.