RSF Training

These notes will be used to create a new training course on using RSF to develop Sakai tools.

General Concepts

Template Creation

Creating templates for use in RSF is the easy part. Stress the use of wireframes and state transition diagrams. Templates can be pretty much fully mocked up with links that work, etc. Select the names of templates to correspond to states. Arcs become action commands or links. Time needs to be spent on rsf:id naming conventions and the syntax used to distingish different kinds of ids.

Component Tree

Generating the component tree is at the heart of the matter for RSF. In JSF, this was an area for specialists. In RSF, everyone is involved. Beyond the basics of understanding how ViewProducer's works, serious consideration should be paid to the stock RSF components (UIInternalLink, UIBoundList, UIForm, etc.). While JSF had an XML syntax to accomplish these sorts of things (JSF tags), the core of RSF view generation lays in these objects. Time should be spent presenting the individually with examples (if possible).

Use of Spring

Since RSF depends heavily on Spring, time should be included to explain basic Spring concepts. Beyond that, the basic syntax to provide application and request context needs to be explained. Cover beans and peas. The RSF expression language might also be included here.

The three levels of context scoping needs to be carefully presented to RSF developers. Reference between beans can only be longer in scope. Thus a request scope bean may access a sesson scope bean which may access an application scope bean. Simplicity pushes development down into request scope. The OTP prefers to push things into application scope. OTP is a more advanced RSF topic, however.

Use of the RSF Expression Language (EL) needs to be explained to students.

State in RSF

RSF supports two major kinds of state in applications: request parameter passing and bean persistance. It is very simple to setup an object that defines information to be passed with an HTTP request (URL) using the VeiwParameters object. The base object is extend to include new parameters. Parameter names must agree with the object properties and added to the parse spec.

As explained above, beans can be defined to last longer than the duration of a request. Information (state) can be kept in session-scope beans or application scope beans. In general, session scope information should be related directly to the user's current state on-line. The application scope can support caching, where appropriate and long term state shared by all users in the system. Generally, state should be avoided in application scope.

Skills to be Mastered

At a minimum, all Sakai developers should be able to use RSF to accomplish the following design principles. These roughly correspond to the experiments documented in RSF Research Notes.

RSF Applications

  1. Applications and Module Structure
    1. File structures (tick)
    2. Notes on web.xml (tick)
    3. Notes on maven (tick)
    4. Notes on organizing content
    5. Notes on organizing code
    6. Concepts
      1. The template file
      2. The producer
      3. Building component trees

Content and Layout

  1. Content and Layout(tick)
    1. Messages (tick)
      1. Using RSF components to create a static page - single message
      2. Externalized string: Message Locator
        1. Expression Language
        2. Verbatim
        3. Other tips
    2. Images, etc. (tick)
    3. Conditional generation of content. (tick)
    4. Verbatim: when to use it. (tick)
    5. Decoration (tick)
      1. Image example
      2. Style example
      3. DHTML example (error)
      4. Others
      5. Sakai Style
    6. Layout
      1. Spans and Divs
      2. Tables (tick)
        1. Using BranchContainer
        2. Iterative row generation
        3. Iternative row and column generation (error)
        4. Table styling and headers
      3. Lists (tick)
    7. Configuration (tick)
      1. Spring parameters
      2. Sakai properties
      3. Tool parameters

Interaction

  1. Interactive Applications
    1. Navigation and Linking (tick)
      1. Internal link to self - simple link.
      2. Internal links between views: Back and Forth - adding views.
      3. External links
      4. Internal Links with CGI paramerers.
    2. Forms (tick)
      1. Data collection via forms - GET method
        1. Passing state in GET parameters
      2. Data collection via forms - POST method.
        1. State in POST parameters
      3. Form Elements
        1. Inputs
        2. Command Buttons
        3. Textarea and Editors
        4. Forms with select elements including radio buttons
    3. DHTML (error)
    4. Javascript (error)
    5. Components (error)
      1. Theory of Components
      2. Exising Components
      3. Making New Components

Advanced Topics

  1. Advanced Topics
    1. Scope and State
      1. Request
      2. Session
      3. Application
    2. ORM Integration (One True Path)
      1. EL Conventions
      2. ORM object addressing
      3. Adding and deleting objects
    3. Web Flows
      1. Spring flows
      2. RSF flows
      3. Design a flow
    4. AJAX
      1. Overview of AJAX
      2. RSF support for AJAX
      3. AJAX UI Libraries - Dojo, etc.
    5. Fluid

Exercises

Static Content

Static context can be created an entered anywhere in an RSF comopnent tree. Generally, some placeholder is used for the string or message in the template, such as a DIV, SPAN, etc. However, anything can be used.

Consider this template:

<!DOCTYPE html      PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns:rsf="http://ponder.org.uk/rsf">
  <head>
    <title>Static Text Case</title>
  </head>
  <body>
    <div rsf:id="firstmsg">First message goes here.</div><br />
    <div rsf:id="secondtmsg">Secondmessage goes here.</div><br />
  </body>
</html>

Two messages are to be inserted. The text in the template will only be shown if no component is included in the compoment tree built by the ViewProducer. Here is the coresponding view producer, called SampleViewProducer:

public class MainProducer implements ViewComponentProducer, DefaultView {
  public static final String VIEW_ID = "main";
  public String getViewID() {
    return VIEW_ID;
  }
  public HelloBean helloBean = null;

  public void setHelloBean (HelloBean hb) {
	  this.helloBean = hb;
  }
  public HelloBean getHelloBean () {
	  return this.helloBean;
  }

  public void fillComponents(UIContainer tofill, ViewParameters params, ComponentChecker checker) {
    UIOutput.make(tofill, "firstmsg", null, "#{hellobean.message}");
    UIOutput.make(tofill, "secondmsg", "Second message composed by the application.");
  }
}

The root container being filled is called tofill, which represents the body of the HTML page. Two components are added to this root. The first one is UIOutput compoment that draws it's content from an injected bean called HelloBean. This injection and intialization would need to be set up using Spring. Note that this technical can also be used to draw messages out of a property bundle.

The second component added uses a string composed directly by the code. It uses the initvalue parameter instead of the binding parameter of the first. Note that this can be a useful way to generate error messages. Have a compoment at the bottom of the page that is generated only if an error occurs and stuff the error message in using UIOutput.

Passing Parameters using a GET Request

Parameter passing is encapsulated in a pea that extends SimpleViewParameters. Thus, if a slide number parameter needs to be passed in the URL, something like this could be used:

public class AddSlideViewParameters extends SimpleViewParameters {
	public int slide = 1; // the slide number, which defaults to one if not supplied.

	public AddSlideViewParameters() {
	}

	public AddSlideViewParameters(String viewID, int slide) {
		this.slide = slide;
		this.viewID = viewID;
	}

	public String getParseSpec() {
		// include a comma delimited list of the
		// public properties in this class
		return super.getParseSpec() + ",slide";
	}
}

This allows the parameter called "slide" to be associated with a view reference. If more than one parameter was needed, they would be added in the comma separated string returned by getParseSpec(). In the fillComponents() method of the ViewProducer, we can add code like this:

  public void fillComponents(UIContainer tofill, ViewParameters params, ComponentChecker checker) {
	try {
		this.slide = ((AddSlideViewParameters) params).slide;
	}
	catch (Exception e) {
		e.printStackTrace();
		//  Error reporting could happen here.
		return;
	}

    //  Links for Previous and Next.
    UIInternalLink.make(tofill, "prevlink", new AddSlideViewParameters(MainProducer.VIEW_ID, this.slide-1));
    UIInternalLink.make(tofill, "nextlink", new AddSlideViewParameters(MainProducer.VIEW_ID, this.slide+1));

This shows the parameter pea being used on both sides of the transaction. A UIInternalLink is created with the slide number included as a parameter. On the response side, we upcase params into being an AddSlideViewParameters object. If there is a problem the exception is caught and handled appropriately.

Notes on Forms

Forms don't require a container branch, so the rsf:id of the form will not have a trailing colon:

<form rsf:id="my-form">
    <input type="submit" rsf:id="submit-my-form" />
</form>

A custom ViewParameters object needs to be created that will contain all of the parameters associated with the form.

In the producer, a new ViewParameters object is created and initialized for the UIForm compoment:

UIForm form = UIForm.make(tofill, "my-form", new MyViewParameters(SequenceProducer.VIEW_ID, fooParameter));
UICommand.make(form, "submit-my-form", "Label-on-button", "#{mybean.proccessActionSubmit}");

Also note that all action handlers need to be in their own bean. The view producer likely will have this bean injected into it, so that it can grab any state set when the action was handled.

<form method="#" rsf:id="my-form">
    <input type="submit" rsf:id="submit-my-form" />
</form>

Note the "#" in method parameter. This forces the form into a GET method.

Training Examples

In many ways, the notes example still serves as the simplest case for developing an application. Rather than distracting the participants with file access, however, create a Notes application service that has access to notes files in place. Include file root as a configurable parameter to illustrate how configuration parameters can be used in Sakai. The UI might be imporved in the notes example, as Aaron has in the TaskList example. Still, leave it simple for now. Good user experience design is not really the focus of this exercise.

Things have changed a bit for RSF 0.7.1.
See ModifyTemplateProducer.java for examples of the new style.

Debugging Tips

An internal link to another RSF view was coded as:

    UIInternalLink.make(tofill, "mainlink", new SimpleViewParameters("main.html"));

When rendered, the link looks right with the ".html" appended. However, this is not how the URL should be contstructed. An error is generated when the link is clicked.

Close examination of the error messages in catalinal.out from RSF gives a clue. There is indication that various attempts ared made to find the name of the second view page including: "second.second.html.html" and "second.html.html". The view reference should be to its view id. Thus, it could be coded as:

    UIInternalLink.make(tofill, "mainlink", new SimpleViewParameters("main"));

Or, following RSF conventions:

    UIInternalLink.make(tofill, "mainlink", new SimpleViewParameters(MainProducer.VIEW_ID));

Invalid Template XML

I got the following error message:

Fatal internal error handling request: 
Target exception of class org.xmlpull.v1.XmlPullParserException
Successive lines until stack trace show causes progressing to exception site:
Error setting dependency viewRender of bean RSFRenderHandler
--> Error setting dependency viewTemplate of bean viewRender
--> Error parsing view template file /content/templates/main.html
--> Error parsing template
--> attribute value must start with quotation or apostrophe not 1 (position: TEXT seen ...<br />\r\n\t<br />\r\n\t<table id="thinglist" border=1... @12:32) 
org.xmlpull.v1.XmlPullParserException: attribute value must start with quotation or apostrophe not 1 (position: TEXT seen ...<br />\r\n\t<br />\r\n\t<table id="thinglist" border=1... @12:32) 

This message indicates an XML parse error. In this case, a table definition was defined as:

    <table rsf:id="thinglist" border=1>
    ...
    </table>
{codde}

The problem is the border width parameter,  "border=1".  This is invalid XHTML which requires that all parameters be surrounded by quotes.  The correct markup is:

{code:xml}
    <table rsf:id="thinglist" border="1">
    ...
    </table>

Branch Container Colon

The following error was observed:

Fatal internal error handling request: 
Target exception of class java.lang.IllegalArgumentException
Successive lines until stack trace show causes progressing to exception site:
Branch container ID must contain a colon character :
java.lang.IllegalArgumentException: Branch container ID must contain a colon character :

In this case the BranchContainer construction looked like:

	UIBranchContainer thingrow = UIBranchContainer.make(tofill, "thing-row");

As the error message indicates, all branch containers must contain a colon. It could be done this way:

	UIBranchContainer thingrow = UIBranchContainer.make(tofill, "thing-row:things");

or even more simply as:

	UIBranchContainer thingrow = UIBranchContainer.make(tofill, "thing-row:");