SITE
  Documentation
  Download
  License
  Performance
  Mailing List

FACADE
  Documentation
  Download
  License
  Mailing List

STORE
  Overview
  Tools
  Download
  License
  Mailing List

JUNIT EXTENSIONS
  Documentation
  Download
  License

AMANDA CD-RW
TAPER

  Overview
  Installation & Usage
  Internals

Facade - Tutorial

1. Quickstart

You need:

  • JDK >= 1.3 (installed).
  • A servlet engine (installed).
  • Ant (installed).
  • The Facade demo application.
  • You might want to read the SiTE documentation because we will be using SiTE in this tutorial. (It is safe to start without reading SiTE docs.)

The Facade full distribution contains a JspViewHandler example.

1.1 Unpack demo application and install as web application

  1. Unpack the demo application whereever you want to put it.
  2. Goto the top level application directory.
  3. Run 'ant compile'.
  4. Install the 'webapp/' directory as a web application in your servlet engine. Using Tomcat, create a Context in your server.xml file:
    
     <Context path="/demo" docBase="/path/to/webapp" debug="0" 
    
        privileged="true" reloadable="true"/>
    
     
    Note: your context path must not be set to 'facade'!
  5. Start the servlet engine.
  6. Access http://yourserver/demo/facade/en/Index.html . You should se a webpage displaying time and date in english.
  7. Access http://yourserver/demo/facade/de/Index.html . You should se a webpage displaying time and date in german.

1.2 What is happening?

1.2.1 Request processing

Take a look at your WEB-INF/web.xml. As you can see the class de.tivano.facade.multiskin.MultiskinServlet is installed as a servlet and mapped to the url /facade/*. The MultiskinServlet enables the use of multiple skins in your webapplication, a german and an english one in the demo. Of course you could also decide to create a skin for kids, one for adults for example. Or one in your one CD and another one in the CD of your affiliate partner.

The MultiskinServlet maps the request URI to an event URI which is handled by Facade. It looks for the first occurance of the servlet's name in the URI. The servlet's name is "facade", as defined in web.xml. The path element following the servlet's name is considered to be the skin's name (de or en in this case). The rest of the URI is the event URI.

Example:

URI:/demo/facade/en/Index.html
skin name:en
event URI:Index.html

The MultiskinServlet now fires the "Index.html" event using the ControlEventDispatcher. The ControlEventDispatcher tries to find a Controller listening to "Index.html". The event mappings (event listeners) are defined in "WEB-INF/sitefacade.properties". Open this file now.

The following shows up at the top of the file:


  # the packagename where the controllers are located

  de.tivano.facade.framework.FacadeServlet.controllerPackage 

                                               = de.tivano.facadedemo.controller

 

This denotes that Facade will try to find all your controllers in the package de.tivano.facadedemo.controller.

Contoller mappings and requirements are defined below:


 # controller requirements

 # a controller can require other controllers

 # syntax: Controller.<controller class name>.requires 

                                          = <required controller class name>



 # controller mappings

 # syntax: ControllerMapping.<eventurl>.listeners = <listener class>, ...

 # ControllerMapping.prefix/.listeners = my.controllers.PrefixController

 # ControllerMapping.event/url.listeners = my.controllers.OneController

 # ControllerMapping.*suffix.listeners = my.controllers.SuffixController

 

As you can see nothing is mapped to an event url and no controller requires another controller. We do not need this right now and will get to it later.

The javadoc of the EventDispatcher class tells us:

The handler classes are called in the following order:

  1. Handlers mapped to event url prefix
  2. Handlers mapped to event url
  3. Default handlers
  4. Handlers mapped to event url suffix

As there is no controller mapped to the event URI prefix and no controller mapped to the event URI "Index.html" Facade now tries to find the default controller (default handler). The default controller class name is computed from the event url. The .html suffix is removed and "Controller" is appended. So the default controller class name computed from the event url "Index.html" is "IndexController". The controller package is "de.tivano.facadedemo.controller" so the FQN is "de.tivano.facadedemo.controller.IndexController".

As there is no IndexController class and there is no controller mapped to the ".html" suffix no controller is called and the request is passed to the ViewEventDispatcher.

Note: If you want to make sure that a request is handled by a controller you can define an explicit mapping.

1.2.2 Response componsing

The MultiskinServlet fires the "Index.html" event using the ViewEventDispatcher. Let's take another look at WEB-INF/sitefacade.properties:


 # the packagename where the view handlers are located

 de.tivano.facade.framework.FacadeServlet.viewHandlerPackage = de.tivano.facadedemo.view



 # define fallback handlers for view handler classes

 # syntax: ViewHandler.<view handler class name>.fallback = <fallback handler class name>



 # view handler mappings

 # syntax: ViewHandlerMapping.<eventurl>.listeners = <listener class>, ...

 # ViewHandlerMapping.prefix/.listeners = my.view.PrefixViewHandler

 # ViewHandlerMapping.event/url.listeners = my.handler.OneViewHandler

 # ViewHandlerMapping.*suffix.listeners = my.view.SuffixViewHandler

 ViewHandlerMapping.*html.listeners = de.tivano.facade.multiskin.MultiskinViewHandler

 

There is no prefix or event URI mapping for "Index.html". Next, the ViewEventDispatcher tries to find the default handler class using the FQN de.tivano.facadedemo.view.IndexViewHandler. As there is such a class it will be called. Take a look at the source code in src/de/tivano/facadedemo/view/IndexViewHandler.java .

IndexViewHandler extends MultiskinViewHandler. The MultiskinViewHandler extends SiteViewHandler. The SiteViewHandler takes care of automactially loading the correct template, filling the template with values and sending a response back to the client. By extending the SiteViewHandler and overriding one method, one can concentrate on providing the values which have to be inserted in the template. The SiteViewHandler and hence the MultiskinViewHandler provides two hooks for extending subclasses, the preProcess and postProcess methods. We choose to override the preProcess method:


    /**

     * Hook for subclasses, is called before the template is processed.

     */

    protected void preProcess(Node rootNode, 

                                 FacadeContext context, String eventUrl) 

    throws EventHandlerException {

        Locale locale;

        if (getSkinName(context).equals("de")) {

            locale = Locale.GERMAN;

        } else {

            locale = Locale.ENGLISH;

        }

        DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL,

                                                       DateFormat.FULL,

                                                       locale);

        context.put("daytime", df.format(new Date()));

    }

 

You can see what the code does. The only thing mentionable: all event handlers have access to the FacadeContext, which is basically a HashMap. The SiteViewHandler looks for a value in the context for every variable found in the template. If it finds something, it sets the contents to the given value. As you can see the code above puts date and time using the correct locale in the context, where the SiteViewHandler will find it.

Lets take a look at the template. The event URI is "Index.html" and the skin name is "en". The SiteViewHandler hence parses the template "en/Index.html" found in the templates directory. The template directory is configured in sitefacade.properties, relative to the web application root:


 de.tivano.site.generic.ServletSourceRepository.BasePath=/sitetemplates

 

Use your browser and open the file webapp/sitetemplates/en/Index.html . As you can see it looks like a normal HTML page except that the time and date are not set correctly. Note: every SiTE template can be displayed in a browser and can be used as mockup (unlike JSP or other templates...). It should be totally possible to use any nondestructive HTML authoring tool with SiTE templates.

Open the template's source code:


 <title SNAME="daytime">Ho</title>

 

The title tag contains the special attribute SNAME (by the way, you can configure SiTE to use any other attribute name, like ID or anything else, if you do not like SNAME, by setting the property de.tivano.site.generic.SimpleTextParser.NameAttribute). SiTE's ConfigurableHtmlParser recognizes any HTML tag containing the SNAME attribute and hence the title tag.

The SiteViewHandler subsitutes the tag's contents with the value stored in the FacadeContext using the key "daytime". This kind of syntax works for any *ML tag, unless the tag contains other nested tags. If a *ML tag contains nested tags, you will only have access to the *ML tag's attributes, not it's contents. This makes it possible to process every HTML page, even if it is not wellformed. You have to use the <!-- SITE SNAME="name" --> - syntax if you need to mark areas containing serveral *ML tags. The SiTE documentation explains this in detail.


 Date and time: <!--SITE SNAME="daytime"-->25.04.2002 10:33<!--/SITE-->

 

The syntax above is another possibility of marking an area where text should be replaced. The text between the two SITE comments is replaced. Of course you could also rewrite this row:


 Date and time: <span SNAME="daytime">25.04.2002 10:33</span>

 

The SiteViewHandler processes the template, inserts the "daytime" value found in the context and sends the final HTML page back to the client.

2. Advanced use, Controllers and the SiteViewHandler

Let's say your marketing manger thinks showing the time to anybody without knowing their name is very impersonal. He wants you to create a "personalized" version of the time display page and query the name of every user before displaying the time to them. Additionally, only names longer than three characters should be accepted. (Marketing mangers always have ideas like that.. ;-).

2.1 Page components: the use of the FACADEEVENT attribute

First we need a page that queries the user's name. The template templates/en/QueryName.html does this. Open the file in your browser and then take a look at the source.

As you expected there is an input form. The interesting part of the page is the error message:


  <!--SITE SNAME="username" FACADEEVENT="ShowOnError"-->

  <p><font color="red">Your name must be longer 

         than three characters.</font></p>

  <!--/SITE--> 

 

If you define an additional FACADEEVENT attribute inside a SiTE-tag, the value of the FACADEEVENT attribute is treated as an event URL. This event URL is fired using the ViewEventDispatcher as soon as the SiteViewHandler processes the node. In this case the event URL is "ShowOnError". Take a look at the de.tivano.facadedemo.view.ShowOnErrorViewHandler class which handles this event by default.


    public void process(FacadeContext context, String eventUrl) 

    throws EventHandlerException {

        Node parent = getParentNode(context);

        Node node = getNode(context);

        String nodeName = node.getName();

        HashMap errorCodes = 

            (HashMap) context.get(ERROR_CODES_KEY);

        if ((errorCodes == null) || (!errorCodes.containsKey(nodeName))) {

            parent.removeNode(node);

        }

    }

 

A SiTE-template is represented as a tree, containing a node for every tag with the SNAME-attribute. The eventhandler itself can either access and use the node which triggered the event using the getNode() method or load another template itself. The template manipulation is SiTE-specific and has nothing to do with Facade. The ShowOnErrorViewHandler deletes the node in the template, if no matching errorcode is present in the errorcodes map. You can easily reuse the ShowOnErrorViewHandler by attaching it to arbitrary nodes using the FACADEVENT attribute. This technique becomes very powerful as soon as you start to think of list filling, automatic form filling, content syndication etc.. As long as you use SiTE your view handler code (Java) is 100% sperated from presentation code (HTML in this example), easing reuse of handler classes.

Using the SiteViewHandler this code could be reduced if the ShowOnErrorViewHandler would be an extension of the MultiskinViewHandler and override preProcess(). You would then put a Boolean value into the context and leave the rest up to the MultiskinViewHandler.

2.2 Using a controller

The target of the QueryName.html form is the PersonalTime.html page. Before we display the page, we need to check if the user has entered a correct username. If not, the QueryName.html page should be displayed again, showing an error message. Generally speaking, depending on the user's input a different view has to be displayed. This is where a controller comes into play.

2.2.1 Controller mapping

First we map the controller on the "PersonalTime.html" event URI, making sure that every request to "PersonalTime.html" is checked by the controller first:


 # controller mappings

 # syntax: ControllerMapping.<eventurl>.listeners = <listener class>, ...

 # ControllerMapping.prefix/.listeners = my.controllers.PrefixController

 # ControllerMapping.event/url.listeners = my.controllers.OneController

 # ControllerMapping.*suffix.listeners = my.controllers.SuffixController

 ControllerMapping.PersonalTime.listeners 

                   = de.tivano.facadedemo.controller.TellYourNameController

 

If you plan to provide more than one "personalized" page it would be a good idea to move all pages into a subdirectory and map the TellYouNameController to that subdirectory prefix. Le'ts say all your personalized pages are in the "personal" subdirectory. So a request URL to a personalized page would be something like "http://yourserver/demo/facade/en/personal/PersonalPage.html". You then map the TellYourNameController to the "personal" prefix:


 ControllerMapping.personal/.listeners 

                   = de.tivano.facadedemo.controller.TellYourNameController

 

2.2.2 Current mapping limitations

  1. No wildcards allowed or regexps supported.
  2. Prefix mappings must start with the top level event URL pathelement.
  3. A prefix mapping must end with a slash (/).
  4. Facade assumes the suffixes are separated from the rest of the request URL by a dot (.) .

2.2.3 Controller implementation

Open the source code of de.tivano.facadedemo.controller.TellYourNameController. The controller implements the EventHandler interface and hence implements a process method which is called by the ControlEventDispatcher. There are two interesting things in the body of the process method:

  1. There is no need to call a view handler manually if the user is succesfully identified. As the controller is mapped to the PersonalTime.html event, the appropriate view handler will be called automatically. After all the process methods of all controllers are called, the MultiskinServlet dispatches the "PersonalTime.html" event URI using the ViewEventDispatcher.
  2. The statement
    
        context.getViewEventDispatcher().dispatch("QueryName.html");
    
        
    shows how to dispatch a new view event. As a result the QueryName.html page will be redisplayed to the user, showing error codes if the user has entered an illegal username.


    public void process(FacadeContext context, String str) 

    throws EventHandlerException {

        boolean firstTime = false;

        // fetch session

        HttpSession httpSession = context.getHttpServletRequest().getSession(false);        

        if (httpSession == null) {

            firstTime = true;

            httpSession = context.getHttpServletRequest().getSession(true);

        }



        String username = (String) httpSession.getAttribute(NAME_KEY);

        if (username != null) {

            // put username in context, it will be automatically displayed by

            // the MutliskinViewHandler

            context.put(NAME_KEY, username);

            // ok, go ahed

            return;

        }



        // unidentified user

        username = context.getHttpServletRequest().getParameter(NAME_KEY);

        if ((username != null) && (username.length() > 2)) {

            // successfully validated username

            httpSession.setAttribute(NAME_KEY, username);

            // put username in context, it will be automatically displayed by

            // the MutliskinViewHandler

            context.put(NAME_KEY, username);

            // ok, go ahed

            return;

        }



        // display QueryName again and set error.

        if (!firstTime) {

            // errorCodes are used by the ShowOnErrorViewHandler

            HashMap errorCodes = new HashMap();

            errorCodes.put(NAME_KEY, "dummy");

            context.put(ShowOnErrorViewHandler.ERROR_CODES_KEY, errorCodes);

        }

        context.getViewEventDispatcher().dispatch("QueryName.html");

    }

2.3 ViewHandler mapping

There are two things left to do. First, we need a template for the PersonalTime.html page, which additionally contains the username. You can find an example in the "webapp/sitetemplate/en/" directory. Second we need to fill in the appropriate values. As we already have the IndexViewHandler which places the correct time in the Facade context, we can easily reuse it. The username is already put in the context by the TellYourNameController. So all we need to do is to map the IndexViewHandler on the "PersonalTime.html" event URL:


 # view handler mappings

 # syntax: ViewHandlerMapping.<eventurl>.listeners = <listener class>, ...

 # ViewHandlerMapping.*suffix.listeners = my.view.SuffixViewHandler

 # ViewHandlerMapping.prefix/.listeners = my.view.PrefixViewHandler

 # ViewHandlerMapping.event/url.listeners = my.handler.OneViewHandler, my.Handler

 ViewHandlerMapping.*html.listeners 

                               = de.tivano.facade.multiskin.MultiskinViewHandler

 ViewHandlerMapping.PersonalTime.listeners 

                               = de.tivano.facadedemo.view.IndexViewHandler

 

2.4 Automatic form filling

In your web.xml change the Facade configuration file to "controllerex.properties" and restart your webapplication. Access "http://yourserver/demo/facade/PersonalTime.html" and input a username of no more than two characters. The result will be a redisplay of the QueryName.html page showing the error message. Note: the input field still contains the username you entered. How comes? You did not write any code to put it there. The answer: the SiteViewHandler performs this automatically.

You can experiment with this feature. Open the QueryName.html page, extend the form with some input fields at will and hit reload. As long as you input a username shorter than three characters the QueryName.html page will be redisplayed. Read the javadocs of de.tivano.facade.viewhandler.site.SiteFormViewHandler and de.tivano.site.util.FormFiller for more information.

2.5 Automatic URL rewriting

As long as you use the SiteViewHandler all application local links are automatically appended with the session id, if the user does not accept cookies.

3. Examples

Download the Facade source distribution. The tests directory contains some examples making use of controller requirements and fallback handlers for view handlers. The corresponding Facade configuration file is in webapp/facade.properties.

4. Bugs

As this is an early version of Facade, there will be bugs. Please report them to rt-facade-request@bugs.tivano.net.


[ t]ivano software gmbh
www.tivano.de