2. Defining and connecting the elements

For now we will define and connect the elements DISPLAY and MENU. The rest of the elements will be added during the next few chapters and after Chapter 7, Adding database support the example will be fully functional and ready for testing.

Example 5.3. The DISPLAY element

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE element SYSTEM "/dtd/element.dtd">

<element implementation="tutorial.friends.Display"/>     

Example 5.4. The MENU element

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE element SYSTEM "/dtd/element.dtd">

<element implementation="com.uwyn.rife.engine.elements.PrintTemplate">
  <property name="template_name">menu</property>

  <exit name="install"/>
  <exit name="add"/>
  <exit name="remove"/>
  <exit name="display"/>
</element>     

The DISPLAY element doesn't hold any surprises, but in the MENU element there is something new. It is using the built-in element implementation PrintTemplate which takes a parameter when initialized, a so-called property.

2.1. Properties

By using property, a fixed value can be provided to the element. It is not part of the data flow and is mostly used for element-specific configuration purposes. In this case it provides the name of the template that has to be used. PrintTemplate reads the property value, uses it to create in instance of the template and prints it directly to the output. Besides RIFE's filtered value tags, no modifications are made to the template. To show how property is used from the Java code, we'll take a look at the implementation of PrintTemplate in RIFE.

Example 5.5. PrintTemplate.java: Using properties from Java

package com.uwyn.rife.engine.elements;

import com.uwyn.rife.engine.Element;
import com.uwyn.rife.engine.exceptions.PropertyRequiredException;
import com.uwyn.rife.template.Template;

public class PrintTemplate extends Element
{
  public void processElement()
  {
    if (hasProperty("name"))
    {
      Template template;
         
      template = getHtmlTemplate(getPropertyString("name"));
      
      print(template);
    }
    else 
    {
      throw new PropertyRequiredException(getDeclarationName(), "name");
    }
  }
}

Instead of retrieving properties programatically, it's also possible to let RIFE inject the value automatically if a setter with the property name exists.

For example:

Example 5.6. PrintTemplateInjection.java: Injecting properties from Java

package com.uwyn.rife.engine.elements;

import com.uwyn.rife.engine.Element;
import com.uwyn.rife.engine.exceptions.PropertyRequiredException;
import com.uwyn.rife.template.Template;

public class PrintTemplate extends Element
{
  private String name;
  public void setName(String name) { this.name = name }

  public void processElement()
  {
    if (null == name)
    {
      throw new PropertyRequiredException(getDeclarationName(), "name");
    }

    Template template;
    template = getHtmlTemplate(name);
      
    print(template);
  }
}

By using this built-in element, you don't have to implement a Java element every time you just want to print a template without setting any value. Note that you can exactly do the same thing with your own element implementations. This provides you with a very powerful method to reuse your elements and still be able to completely control the application flow through data links and flow links.

2.2. The site definition

As seen in Example 5.4, “The MENU element” the MENU element has four exits, one for each page in the application. Since we haven't added the elements for the elements INSTALL, ADD and REMOVE yet, we leave them out for now.

Example 5.7. First cut on the site definition

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE site SYSTEM "/dtd/site.dtd">

<site>
  <arrival destid="DISPLAY"/>

  <globalexit name="menu" destid="MENU"/>

  <element id="DISPLAY" file="display.xml" url="/display"/>

  <element id="MENU" file="menu.xml" url="/menu">
    <flowlink srcexit="display" destid="DISPLAY"/>
  </element>
</site>

2.3. Global exits

The site definition shows that our main element is DISPLAY and we want it to function exactly as a regular html index.html page. The browser should display it if no specific url is provided, it thus becomes the home page. We have also defined a global exit with the globalexit tag. A global exit is an exit that will be defined for all the elements. In this case we want a link to the menu from all of our pages which can be used from templates like in display.html:

Example 5.8. Template for listing friends

<html>
  <head><title>My friends</title></head>
  <body>
    <h3>My Friends</h3>

    <p>This is a list of my friends that have interesting websites to visit.</p>

    <!--V 'content'/-->
    
    <!--BV 'content'-->
      <table border="1" cellpadding="5" cellspacing="0">
        <tr>
          <th>firstname</th>
          <th>lastname</th>
          <th>website</th>
        </tr>
        <!--V 'rows'--><!--/V-->
        <!--B 'row'-->
          <tr valign="top">
            <td><!--V 'firstname'/--></td>
            <td><!--V 'lastname'/--></td>
            <td><a href="[!V 'url'/]"><!--V 'description'/--></a></td>
          </tr>
        <!--/B-->
      </table> 
    <!--/BV-->

    <!--B 'content_error'-->
      <p>The display couldn't be performed due to the following error:</p>
      <p style="color: #990000;"><!--V 'error'/--></p>
    <!--/B-->
    
    <p><a href="[!V 'EXIT:QUERY:menu'/]">go to menu</a></p>
  </body>
</html>

As you can see, the menu link at the bottom is handled just like an ordinary exit.

2.4. Displaying the friends

When looking at the template in Example 5.8, “Template for listing friends” especially notice the value rows and the block row. Each row represents one friend and as we iterate over all friends in the database a row block will be appended to the value rows for each friend. This is done by the call appendBlock in the Java implementation. Looking at a part of the implementation of the DISPLAY element:

Example 5.9. The Java code used to add friends

...

mTemplate.setValue("firstname", encodeHtml(resultSet.getString("firstname")));
mTemplate.setValue("lastname", encodeHtml(resultSet.getString("lastname")));
mTemplate.setValue("description", encodeHtml(resultSet.getString("description")));
mTemplate.setValue("url", encodeHtml(resultSet.getString("url")));
mTemplate.appendBlock("rows", "row");

...

The code in Example 5.9, “The Java code used to add friends” is called once for each friend. The interesting part here is that we set the values with setValue just like we have done in previous examples, but then we use appendBlock instead of setBlock. This will cause RIFE to append the block at the end of the value instead of resetting it. The result is that all of the friends from the database will be listed one after another.

This is a great example of how well RIFE separates logic from display. In the template there are only blocks and value placeholders defined. These can later be used from the Java code to construct the page based on the logical flow of the element. There is no logic or iterating loops in the template code itself, it just contains information on how to display the data.