RIFE logo
This page last changed on Jan 17, 2006 by mradtke@abigale.de.

A friends database

Introduction

The numberguess example showed some of RIFE's power when it comes to using templates and submissions to handle forms and user input. In the next few chapters we are going to develop a somewhat more advanced application: a friends database application.

Our application will display a title and a list of friends and let us add new ones to the database. We also want to send a copy of the application to a friend when it's done, so that he can use it too. We don't want to make him change the code just in order to change the title though, so we will make sure that can be done easily through a configuration file instead.

The example is available in the same rife-examples package as the numberguess example. To make it easier to follow, we provide you with the directory structure of the finished application:

Directory structure of the friends application
src/
    elements/
        add.xml
        display.xml
        install.xml
        menu.xml
        remove.xml

    implementations/
        tutorial/
           friends/
                Add.java
                Display.java
                Install.java
                Remove.java

    rep/
        config.xml
        datasources.xml
        participants.xml

    sites/
        friends.xml

    templates/
        add.html
        display.html
        install.html
        menu.html
        remove.html

    tutorial/
        friends/
            backend/
                Friend.java
                FriendManager.java

The application consists out of five different elements. Four of them have their own Java implementations and one uses an element that's already part of RIFE. All content is generated through templates and each element has a dedicated one. A friend is represented by the Friend object and all friends are managed by a FriendManager. We'll start off by setting up the site participant and defining the elements DISPLAY, for listing the friends, and MENU, for navigating the application.

Like in the previous example we use a ParticipantSite participant to define the main site file.

Participant file for Friends database
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE rep SYSTEM "/dtd/rep.dtd">

<rep>
  <participant param="sites/friends.xml">ParticipantSite</participant>
</rep>

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 the database chapter, the example will be fully functional and ready for testing.

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

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

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.

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 an 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.

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:

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.

The site definition

As seen in the {{*MENU*}} element declaration, it 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.

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>

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:

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.

Displaying the friends

When looking at the template above, 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:

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");

...

This code 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.

Now that basic elements are in place, we need to configure the application.

Document generated by Confluence on Oct 19, 2010 14:56