Tutorial

Tutorial

This tutorial will teach you everything you need to know about Weaver.

What is Weaver?

Weaver is a pure-Java, XML programmed Controller component for developing Model View Controller (MVC) structured, servlet based, Web applications.

First we'll look at the Model View Controller architecture and then we'll consider how this relates to Weaver.

The Model View Controller Architecture

The Model View Controller (MVC) architecture divides an application in to three components.

Model View Controller Architecture

Referring to the above figure these are:

  • Model - The Model is the computational part of your application; the bit that does all of the real work. The Model may well be further subdivided in to additional layers.
  • View - The View component is the presentation of your application to the user. So in the case of a Web based application it is what the user sees in their browser.
  • Controller - The Controller links and separates the Model and the View. The Controller decides how to act upon Model components as a result of user interactions with the View and (in the case of a web application) typically causes a new view (web page) to be displayed.

This separation decouples the View from the Model, so any given application may have multiple user interfaces and the user interface and the Model may be developed and may be changed independently.

Consider a hypothetical Internet search engine. The View component, developed using JSP technology, displays a search form. The user enters their query and submits the form. The Controller component examines the submission, determines that a search action is indicated and extracts the query string. It calls the Model, passing the search query, in order to actually have the search executed. Once the Model has done its job, a set of search results are returned to the Controller which then forwards to a JSP page to render the hitlist in the user's browser.

Next we'll consider Weaver's role in the MVC architecture.

Where Does Weaver Fit In?

Weaver is an implementation of a Controller component for Web applications using servlet technology. The following figure shows Weaver in relation to the MVC architecture:

Weaver in the Model View Controller Architecture

In the figure the View component is implemented using JSP pages - this is typical but is not necessarily the case, any servlet-friendly View technology may be used.

The Model component is implemented by Weaver Beans (W-Beans). A W-Bean is a Java class that implements a particular interface. The W-Bean interface allows information to be passed to and from the View and the Controller independently of the fact that the W-Bean is running in a servlet. Typically W-Beans are thin classes that call Business Layer objects to do the real work of the application.

Weaver itself, implements the Controller component. Weaver reacts to Web page submissions, by calling W-Beans in a manner programmed by an XML configuration file; this file is known as the Weaver Application Definition (WAD) file. The XML tag vocabulary used in the WAD file allows you to control how given events are processed, more of this later.

Now we'll take a brief look at some of the features of Weaver that might make it an attractive choice for the development of your application.

  • Weaver is programmed via a simple but powerful XML syntax whose elements are modeled after those provided by JSTL. This means that you can reuse much of your JSTL experience.
  • An Expression Language (EL) syntax that is identical to that of JSP 2.0 is used by Weaver tags to control the processing of user actions.
  • The tag vocabulary itself is easily extensible (see the Tag HOWTO).
  • Any servlet-friendly View technology may be used e.g. JSP.
  • Model functionality is easily added via the implementation of a simple Java W-Bean interface.
  • Any application based on Weaver comes with an Administrative Console for free. This allows you to monitor, to configure and to diagnose faults in running applications (see Console Docs).
  • Weaver has simple and flexible authentication and error handling mechanisms.
  • An application may be composed of multiple cooperating servlets.
  • Weaver will run in any suitable servlet container while completely insulating the developer from the fact that a servlet container is being used at all.
  • Rapid application development is supported via automatic reloading triggered by the modification of the WAD file or other configuration files. This greatly reduces application development time.
  • Weaver has a very low execution overhead.

The tutorial will now proceed by exploring the files contained in the distribution.

Examining the Distribution

If you have not yet downloaded Weaver now is the time to do it, please see the Downloading and Installing guide.

Both the binary distribution and the installer contain the same files layed out as follows:

  • license.txt - License agreement.
  • README.txt - Readme file.
  • docs - Documentation, start by opening docs/index.html.
  • core - Weaver's kernel.
    • lib - The lib directory contains the most important file in the distribution, weaver-starter-app.war - the starter application. This is a fully working, deployable empty Weaver application (including the Administrative Console). This is the usual starting point for developing new Weaver applications.
    • src - Weaver's source code.
  • taglet - A java doc taglet that can be used to document Weaver beans. Click here for more.
    • lib - Contains the compiled taglet as wbean-param-taglet.jar.
    • src - The taglet's source code.
  • wbeans - Weaver beans packages. Click here for more.
    • xml - W-Beans for processing XML.
      • lib - Contains the copile xml W-Beans in xml-wbeans.jar.
      • src - The xml W-Bean's source code.

In the following sections of the tutorial we will deploy the starter application, discuss its contents and examine the structure of a Weaver application.

Deploying the Starter Application

Before proceeding you should have installed a JDK (1.4 or later) and Tomcat (5.0.19 or later) or equivalent. I will assume that you are using Tomcat listening on port 8080 (the default).

Copy the starter WAR file from core/lib/weaver-starter-app.war in to Tomcat's webapps directory and start Tomcat. You should see Weaver's output to standard error along the lines of:

Weaver version 3.0a3, Copyright (C) 2001-2004 Paul Harvey (Paul.Harvey@oldlight.com) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. *************************************************************** * Should you wish to license Weaver under different terms and * * conditions then please contact the author. * *************************************************************** 0 [main] INFO root - Weaver Initialisation Servlet - starting initialisation 0 [main] INFO root - Welcome to Weaver, version 3.0a3 by Paul Harvey (Paul.Harvey@oldlight.com) 0 [main] DEBUG root - Loading configuration 0 [main] DEBUG root - Properties config file is at C:\Program Files\ Apache Software Foundation\jakarta-tomcat-5.0.19\webapps\ weaver-starter-app-3.0a3\WEB-INF\application\conf\application.properties 0 [main] DEBUG root - Setting system properties from config 10 [main] INFO root - Configuration settings - #Sun May 16 17:33:09 BST 2004 log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.rootLogger=info, stdout log4j.appender.file.layout.ConversionPattern= %d{dd MMM yyyy HH\:mm\:ss,SSS} - [%p] (%c) - %m%n log4j.appender.file.File=C\:\\Program Files\\Apache Software Foundation\\ jakarta-tomcat-5.0.19\\webapps\\weaver-starter-app-3.0a3\\WEB-INF\\ application\\logs\\log.txt log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.MaxBackupIndex=1 log4j.appender.file.MaxFileSize=100KB log4j.appender.stdout.layout.ConversionPattern=%m%n log4j.appender.file=org.apache.log4j.RollingFileAppender weaver.attribute_asserts_on=false log4j.appender.stdout.layout=org.apache.log4j.PatternLayout property.org.xml.sax.driver=org.apache.xerces.parsers.SAXParser Turning logging over to configured logger Started on BANDAI Java class path C:\Program Files\j2sdk_nb\j2sdk1.4.2\lib\tools.jar; C:\Program Files\Apache Software Foundation\jakarta-tomcat-5.0.19\bin\ bootstrap.jar Java class version 48.0 Java installation home C:\Program Files\j2sdk_nb\j2sdk1.4.2\jre Java vendor Sun Microsystems Inc. Java vendor url http://java.sun.com/ Java version 1.4.2 OS Architecture x86 OS Name Windows XP OS Version 5.1 User directory C:\Program Files\Apache Software Foundation\ jakarta-tomcat-5.0.19\bin User's home directory C:\Documents and Settings\pharvey User name PHarvey Servlet directory C:\Program Files\Apache Software Foundation\ jakarta-tomcat-5.0.19\webapps\weaver-starter-app-3.0a3\. Weaver Initialisation Servlet - complete Starting Weaver application Welcome to Weaver, version 3.0a3 by Paul Harvey(Paul.Harvey@oldlight.com) Configuration settings - #Sun May 16 17:33:09 BST 2004 weaver.password_file=WEB-INF/admin/conf/CREDENTIALS weaver.reload_if_modified=false Weaver application initialised OK - Weaver Administrative Console (admin) Starting Weaver application Welcome to Weaver, version 3.0a3 by Paul Harvey(Paul.Harvey@oldlight.com) Configuration settings - #Sun May 16 17:33:10 BST 2004 weaver.reload_if_modified=true Weaver application initialised OK - Weaver Starter Application (myapp) *** Application reloading is ON *** Application reloading should always be disabled in a production environment

Now open a Web browser to:

http://localhost:8080/weaver-starter-app/myapp/
The view component of the starter application and the Administrative Console are built using JSP. As a consequence the first time any given page is visited after deployment it will be compiled - so the first visit will be significantly slower than subsequent visits.

You should see something like the following in your browser:

Starter Application

Now have a look at he Administrative Console - either follow the link on the current page or just tag admin on the end of the URL:

http://localhost:8080/weaver-starter-app/myapp/admin/

Should any of the functions on the Console be unclear then either consult the built in help or see the Console Documentation.

In the next section we will look at the file/directory structure of a typical Weaver application.

Structure of a Weaver Application

In this section we'll look at the file structure of a typical Weaver application using the starter application as an example. So either unpack the starter war, or if like Tomcat 5, your servlet container unpacks deployed wars take a look at the version of the starter app you have already deployed.

weaver-starter-app +---admin-static | +---css | \---images | +---META-INF | MANIFEST.MF | \---WEB-INF | web.xml | +---admin | +---conf | | servlet.properties | | wad.xml | | | \---jsp | +---application | \---conf | application.properties | tagmap.xml | +---logs | +---uploads | +---lib | +---myapp +---conf | servlet.properties | wad.xml | \---jsp index.jsp

The above structure will be familiar to any servlet developer, but we'll run through the function of the most important files and directories from top to bottom:

  • admin-static - This contains those "static" files used by the Administrative Console that must be directly downloadable by the user's browser; this amounts to image files and CSS. You will often create a similar directory to hold such resources for your application.
  • META-INFO - The war file's manifest.
  • WEB-INF - The servlet's WEB-INF directory, files contained in WEB-INF are not directly accessible to the user's browser.
    • web.xml - The web application's deployment descriptor, more of this later.
    • admin - The Administrative Console. The administrative console is itself a Weaver application, it may be informative to explore its source code and configuration.
      • conf - Configuration files for the Administrative Console. Most importantly conf contains servlet.properties - which holds configuration settings for the servlet, and wad.xml - which programs the behavior of the Weaver controller in response to user events.
      • jsp - The JSP pages that make up the Administrative Console.
  • application
    • conf
      • application.properties - global configuration.
      • tagmap.xml - a mapping of tag names to implementation classes. See the Tag HOWTO.
    • logs - default logs directory.
    • uploads - default file uploads temporary directory.
  • lib - jar files used by your application.
  • myapp - This is the bare-bones empty Weaver application. You will see that the directory structure is identical to that of the admin console. This is no accident as it contains the files required by all Weaver applications.

Next we'll go on to look at the contents of the WAD file and at Weaver Beans.

Overview of the Weaver Application Definition File

The Weaver Application Definition or WAD file has already been introduced as the XML file that programs the behaviour of Weaver. In this section we'll look in a little more detail at its contents.

The following shows the "top-level" elements in the WAD file:

<weaver-app> <information/> <load-beans/> <load-functions/> <application> <init/> <destroy/> </application> <session/> <init/> <destroy/> <time-out/> </session> <main> <dispatch/> <default/> <security/> </main> </weaver-app>

We will now consider each of these top-level elements in turn.

The information, load-beans and load-functions elements are called declaration tags, they are used only as the application is loaded.

  • information - The information tag takes attributes that describe your application e.g. version, name and release date.
  • load-beans - Declares and loads the Weaver Beans that implement the Model component of your application.
  • load-functions - Declares and loads functions that are used to extend the functionality offered by the Expression Language (EL). The EL is used by other tags to, for example, take decisions about the processing of user events.

All of the other tags introduce sections of the WAD file that contain tags that are executed as your application is run, they are called execution tags.

  • application - The contents of the application tag relate to application startup and shutdown. Typically the application tag is used to initialise global resources at startup and then uninitialise them at application close down.
    • init - Called as the application is initialised.
    • destroy - Called as the application is closed down.
  • session - The contents of the session tag relate to user-browser session creation and destruction. Typically the session tag is used to initialise resources that are session-specific when the session is created and to uninitialise them on session closedown.
    • init - Called as each session is created.
    • destroy - Called as each session is destroyed (including as a result of session timeout).
    • time-out - Called when an action is taken that requires a session but no session exists, this is typically the result of a user executing an action from a browser which contains a session that has been timed out.
  • main - the contents of the main section are called as your application executes. It is the main section that decides how to handle user events.
    • dispatch - Called to handle user actions in the normal process of executing your application. This is the core of the WAD file.
    • default - Called when a user action could not be handled in the dispatch section.
    • security - Called when user authentication is required.

So What Exactly Goes on in the WAD file

As an introduction we will consider a very simple voting application.

The application displays a JSP page which poses the question "Do you like Weaver?", to which you may answer "yes" or "no". The application echoes a message appropriate to your selection. Using Weaver here is of course purely illustrative!

We can develop this application based on the starter app. You could, should you wish, edit the following changes directly in to the starter app you deployed earlier.

First we need to create our JSP pages. We will create our pages under the myapp/jsp directory

We'll write a JSP page called welcome.jsp as follows:

<html> <head></head> <body> <p>Do you like Weaver? <p> <form action="myapp" method="post"> <input type="hidden" name="event" value="vote"> Yes <input type="radio" name="opinion" value="yes" checked> No <input type="radio" name="opinion" value="no"> <input type="submit"> </form> </body> </html>

I have kept the above absolutely as simple as possible. There should be no surprises here for anyone who as developed an application with a web-based front end.

The only interesting parts of the above are the values supplied by the form. The event field is used to indicate the action taken by the user - voting in this case.

There is nothing special about the naming of the event parameter. You may use as many parameters as you need and call them whatever suits you.

The opinion radio buttons allow the user to vote yes or no.

Now we'll create a JSP to echo a response to the user, call it thanks.jsp.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head></head> <body> <p>Thanks for your vote <p><c:out value="${requestScope.response}"/> </body> </html>

This is getting a little more interesting, but again no surprises to anyone who has developed JSP 2.0 pages. The page simply uses the c:out JSTL tag to display the result of evaluating the Expression Language (EL) statement ${requestScope.response} - that is the value of the request scoped attribute called response.

If you are not familiar with JSP 2, JSTL or the EL used in JSP 2 then there are a whole bunch of ideas to get to grips with in this simple page. None of this is Weaver per se and you may decide not to use JSPs at all in your application - but as Weaver uses the same EL as JSP 2.0 and the execution tags in the WAD file are modeled after those in JSTL, relevant experience is easily transferable.

Next we will take a quick aside to introduce the EL before we look inside the WAD file.

The Expression Language - A Quick Aside

The Expression Language (EL) used by Weaver is identical in syntax and semantics to that used in JSP 2.0. A full discussion of the EL is beyond the scope of this document, the following are recommended tutorials:

For the definitive reference you should see:

Weaver has its own set of scopes in the EL. Some of these are shared with the EL as used in JSP pages others are unique either to Weaver or to JSP pages. Values stored in a shared context either by Weaver or in JSP pages are available to both. Most of the scopes used by Weaver are introduced in this tutorial.

When a value is referred to in the EL without a scope e.g.

${varName}

Weaver's scopes are searched for the value in a particular order. For a definition of this order and for a comprehensive list of scopes please see the Reference Manual.

The Reference Manual also shows which servlet API objects are used to store the values in each scope. This information is essential if you are using a View technology other than JSP with the EL.

An Overview of the WAD File

OK now we are ready to take a look at the Weaver Application Definition (WAD) file. Remember it is the WAD file that defines the behavior of Weaver.

<weaver-app> <information version="1.0" date="26th April 2004" author-name="Paul Harvey" author-email="Paul.Harvey@oldlight.com" vendor-name="Old Light Software" application-name="Vote"/> <load-beans/> <load-functions/> <application> <init/> <destroy/> </application> <session> <init/> <destroy/> <time-out/> </session> <main> <dispatch> <if test="${param.event eq 'vote'}"> <choose> <when test="${param.opinion eq 'yes'}"> <set var="response" value="I'm glad hear you like Weaver." scope="request"/> </when> <when test="${param.opinion eq 'no'}"> <set var="response" value="Sorry to hear you don't like Weaver." scope="request"/> </when> </choose> <forward url="/WEB-INF/myapp/jsp/thanks.jsp"/> </if> </dispatch> <default> <forward url="/WEB-INF/myapp/jsp/welcome.jsp"/> </default> <security/> </main> </weaver-app>

If you modify the WAD file of a running application (and you have reloading turned on) your application will be automatically reloaded by Weaver - this should be confirmed by log messages along the lines of the following:

Reloading application - Weaver Starter Application (myapp) Starting Weaver application Welcome to Weaver, version 3.0a3 by Paul Harvey(Paul.Harvey@oldlight.com) Configuration settings - #Sun May 16 17:38:18 BST 2004 weaver.reload_if_modified=true Weaver application initialised OK - Weaver Starter Application (myapp)
Automatic application reloading should be disabled in a production deployment. Failure to do so will degrade performance. The Reference Manual describes the relevant configuration setting.

Now let's examine our modifications to the WAD file.

You may have noticed that the tags added to the dispatch section look a lot like some JSTL tags. They perform the same functions as their JSTL namesakes and take similar attributes. Where possible this approach has been adopted throughout.

The if tag is like a Java if keyword. Its test attribute is evaluated according to the EL, then if the result is true the contents of the if tag are executed. In our example we are testing to see if the value of the event parameter is "vote". Two things to notice here - whenever an EL expression is used it is wrapped by ${ and }, and parameters supplied from Web pages, whether via a URL or via form submissions, are referred to using the param prefix.

The choose tag is like a Java switch statement. At most one of several alternatives are executed. The alternatives are declared as when tags - each has an associated test attribute containing an EL statement. The contents of the first when tag whose test attribute evaluates to true is executed. If none of the tests evaluate to true then the contents of the optional otherwise tag are executed.

Our choose tag contains when tags that test whether the vote parameter has the value "yes" or "no". The set tag is used to store an appropriate value in the request scoped response attribute.

The forward tag is then used to cause thanks.jsp to be sent to the user's browser. thanks.jsp pulls out the value of the response attribute and displays it to the user.

The default section of the WAD file is executed should a web page submission not be handled in dispatch section. So as the application executes and you pull up http://localhost:8080/weaver-starter-app/myapp/ initially the event parameter is not specified and so the default section is executed and greeting.jsp page is displayed.

If you wish you, you can watch Weaver processing incoming events by going to the Administrative Console and turning on debug logging.

A complete list of the standard tags and the scopes used by Weaver may be found in the Reference Manual and a description of how to add tags of your own may be found in the Tag HOWTO.

In the next section of the tutorial we'll see how to start calling Model functionality from the WAD file via W-Beans.

Adding a Weaver Bean

Weaver Beans (W-Beans) provide the route via which you integrate and call Model code.

A W-Bean is a class that conforms to the com.oldlight.weaver.clientbinding.WeaverBean interface:

public interface WeaverBean { public void handle(WeaverExecutionContext context) throws WeaverClientException; }

The clientbinding and context packages contain most of the Weaver classes you will need with when developing your application - take a look at the javadoc.

When a Weaver Bean is called from the WAD file its handle method is executed. As you can see handle takes a single parameter called context of type WeaverExecutionContext and throws WeaverClientException.

The context parameter is your W-Bean's interface to Weaver - amongst other functions it provides you with access to the scoped parameters and attributes that may have been passed through from the View or may have been set elsewhere.

Should an error occur during processing in one of your W-Beans you should indicate this to Weaver by throwing a WeaverClientException.

Now we'll look at a simple W-Bean and how it is called from the WAD file.

We'll create a very simple bean that does pretty much what we did previously to illustrate some of the tags used in the WAD file - that is to supply a response to the user based on their vote.

package com.oldlight.weaver.tutorial; import com.oldlight.weaver.clientbinding.*; import com.oldlight.weaver.context.*; public class VoteBean implements WeaverBean { public void handle(WeaverExecutionContext context) throws WeaverClientException { String var=(String)context. getBeanContext().getAttribute("var"); String scope=(String)context.getBeanContext(). getAttribute("scope"); String opinion=(String)context.getBeanContext(). getAttribute("opinion"); String message; if (opinion.equals("yes")) { message="Thanks for that"; } else { if (opinion.equals("no")) { message="Maybe you could try Apache Struts instead?"; } } WeaverAttributeUtil.setAttribute(context, var, message, scope); } }

Of course, your beans will do whatever is needed for your application. Typically W-Beans form a thin layer and the real work is done by calling down to underlying business objects.

Examining our bean; the first thing that happens in the handle method is that the value of three "bean context" attributes are read - we'll see where they come from shortly. The first two - called var and scope - are commonly used to refer to the name of a variable that is to be set and the scope in which the variable is to be be set, respectively. This usage is consistent with that of JSTL. The third attribute, called opinion passes in the user's vote.

The if statement acts on the value of opinion and sets message appropriately.

Finally the resulting message is stored in an attribute for later display in a JSP page. The use of the WeaverAttributeUtil class should be noted here. You should always use this utility in circumstances when you wish to set an attribute in this fashion as it looks after handling the different possible values for the scope parameter in a consistent manner.

Now let's look again at the WAD file. Before a W-Bean may be called from the WAD file it must be declared in the load-beans section using the bean tag:

<load-beans> <bean name="vote" class="com.oldlight.weaver.tutorial.VoteBean"/> </load-beans>

The bean tag takes a name and a class attribute. The name is simply an alias that will be used elsewhere in the WAD file to refer to the bean. The class is the fully qualified name of your W-Bean Java class.

So we have declared the bean - how about calling it? W-Beans are invoked using the call tag:

<call name="vote"> <param name="var" value="response"/> <param name="scope" value="request"/> <param name="opinion" value="${param.opinion}"/> </call>

You can see the call tag invoking our vote W-Bean. The param subtags are used to explicitly pass values to the bean via the BeanContext. You may be asking why bother when the bean has direct access to the attributes set in the View and the parameters supplied with the GET/POST. Adding this level of indirection is good practice as it further decouples the View from the rest of the application, and allows the bean to be reused with parameters coming from different sources. If you make a change to the View you just need to change the WAD file to still have the correct values fed through to your W-Bean. The same arguments hold for passing in the name and scope of the attribute to store a result in - rather than just having the code "know" a hardcoded attribute name in which a bean will store its result.

In the context of the rest of the WAD file - without changing our JSPs we may change the dispatch section as follows:

<dispatch> <if test="${param.event eq 'vote'}"> <call name="vote"> <param name="var" value="response"/> <param name="scope" value="request"/> <param name="opinion" value="${param.opinion}"/> </call> <forward url="/WEB-INF/myapp/jsp/thanks.jsp"/> </if> </dispatch>

We test to see if the action taken was a voting action. If it was then we call our new W-Bean passing in the details of the attribute we'd like the result storing in and the opinion expressed by the user.

So far we have covered the basics - next we'll take a look at session and application initialisation and destruction.

The Session

Each browser session is represented in Weaver and may have associated attributes.

When a session is initialised the contents of the session/init section of the WAD file are executed. When a session is destroyed - for whatever reason - the contents of the session/destroy section of the WAD file are executed.

Session scoped values are accessed in the EL via the sessionScope:

<if test="sessionScope.numRequest gt 1"> ...

In a scope attribute they are referred to using the value session:

<set var="name" scope="session" value="${param.userName}"/>

And are available programatically via WeaverExecutionContext.getSessionContext:

... public void handle(WeaverExecutionContext context) throws WeaverClientException { String name=(String)context.getSessionContext().getAttribute("name"); ...

In the WAD file the session-control tag is used in the dispatch section to control how sessions are handled. The session-control tag takes a single attribute called action with values as follows:

  • init - If the session has not previously been initialised then the contents of the session/init tag are executed. If the session is already initialised then no action is taken.
  • destroy - Causes the session to be destroyed and the contents of the session/destroy tag to be executed. You would only use this tag if you wish to explicitly destroy the session - in normal operation an inactive session will expire and the contents of session/destroy will be executed automatically.
  • require - Requires that the current request is taking place in the context of an initialised session. Otherwise the contents of the session/time-out section are executed.

Let's look at storing the time of voting in the session - and stopping two votes taking place in the same session.

We'll modify our Vote W-Bean as follows:

... Date voteDate=(Date)context.getSessionContext().getAttribute("voteDate"); if (voteDate!=null) { message="You have already voted in the session at "+voteDate; } else { context.getSessionContext().setAttribute("voteDate", new Date()); // Original code ... } ...

We try to retrieve voteDate from the session. If it is present then a vote has already been made in the session and we set the message to the user appropriately. If voteDate is not present then the vote is allowed and the date of the vote is recorded in the session.

Despite my somewhat artificial example you will find that you often need to store per-session data in your applications and release it when the session is destroyed. For example I have a search application that stores the most recent query and hit list in the session.

The Application

When a WAD file/Weaver Servlet is (re)loaded the contents of the application/init section of the WAD file are executed. When terminated the contents of the application/destroy section of the WAD file are executed.

There are two attribute scopes that are relevant to the application:

  • servlet - Servlet scoped values are available within a given WAD file and the W-Beans it calls - they are not available in the View.
  • application - Application scoped values are available to all servlets in the application - that is all WAD files, all View components and all W-Beans within the context.

Servlet scoped values are accessed in the EL via the servletScope:

<if test="servletScope.fu eq 'bar'"> ...

In a scope attribute they are referred using the value servlet:

<set var="name" scope="servlet" value="${bean.attributeName}"/>

And are available programatically via WeaverExecutionContext.getServletContext:

... public void handle(WeaverExecutionContext context) throws WeaverClientException { String name=(String)context.getServletContext().getAttribute("name"); ...

Application scoped values are accessed in the EL via the applicationScope:

<if test="applicationScope.fu eq 'bar'"> ...

In a scope attribute they are referred using the value application:

<set var="name" scope="application" value="${bean.attributeName}"/>

And are available programatically via WeaverExecutionContext.getApplicationContext:

... public void handle(WeaverExecutionContext context) throws WeaverClientException { String name=(String)context. getApplicationContext().getAttribute("name"); ...

Let's look at counting the number of votes made. There are many ways in which we could do this. For the sake of demonstration we'll do it without altering our W-Bean, really just to show another use of the EL.

We will store the number of votes cast in the application scoped variable called voteCount and modify thanks.jsp to display it:

... The number of votes cast so far is <c:out value="${applicationScope.voteCount}"/> ...

In our WAD file we will initialise the count to zero on application loading and increment it for each vote cast:

... <application> <init> <set var="voteCount" scope="application" value="0"/> </init> ... </application> ... <main> <dispatch> <if test="${param.event eq 'vote'}"> <set var="voteCount" scope="application" value="${applicationScope.voteCount + 1}"/> ... </if> ... </dispatch> ... </main> ...

As an exercise you could extend the above to store the running total in a file on application destruction and then reload it when the application is restarted. In this way the count would not be reset to zero every time that the application is restarted.

An Important Note about Usage

This is a brief note concerning the usage and structure of the WAD file and of W-Beans. The following comments may well be self-evident but I would rather spell things out than leave room for misunderstanding.

Typically there is not a one-to-one mapping between user events and W-Beans. You will often make calls on several W-Beans in order to service a single event. Conversely a given W-Bean may implement a group of related actions e.g. connect to database, search database, disconnect from database; and the various actions may be selected by passing an appropriate parameter.

The EL is capable of evaluating complex expressions. Often the selection of execution path/call of W-Beans is dependent on multiple values.

W-Beans should be written in a mind-set of processing inputs and providing outputs. The fact that the ultimate consequence is the display of a Web page should be irrelevant. The job of deciding what action to take is the responsibility of the Controller and the job of how to display the output is that of the View.

You should try to write generic W-Beans where possible - in this way they may be reusable in future applications. With the same goal in mind the operation of W-Beans should not be coupled - unless of course you are writing a library of W-Beans that are intender to work closely together.

Finally although the XML syntax used in the WAD file is powerful and flexible it should not be used as a general purpose programming language - its intended usage is to program the controller. If your WAD file becomes very complex you may reconsider which functionality should be implemented in the View, the Controller and the Model.

Extending the EL with Custom Functions

The EL used in the WAD file can easily be extended by adding your own customer functions, these functions then become an integral part of the EL.

To add a new function to the EL you need to:

  • Write some Java code to implement your function.
  • Load the function via a function tag in your WAD file.

An EL function must be defined as a public static member of a public class. We'll look at an example that will be used later in our discussion of authentication. The function takes two String parameters - a username and password. The function returns a Boolean value to indicate the validity of the supplied credentials.

package com.oldlight.weaver.tutorial.functions; public class Functions { public static Boolean checkPassword(String username, String password) { // Some logic to check the supplied credentials setting isValid ... return new Boolean(isValid); } }

Before the function may be called we must load it in our WAD file. This is done with a function tag inside the load-functions section:

<load-functions> <function name="checkPassword" class="com.oldlight.weaver.tutorial.Functions" signature="java.lang.Boolean checkPassword(java.lang.String, java.lang.String)"/> </load-functions>

The function tag takes attributes as follows:

  • name - The name by which the function will be called in the EL.
  • class - The fully-qualified name of the class that implements the function.
  • signature - The declaration of the function as it appears in the class. The public and static keywords are omitted as are the parameter names and all class names are fully-qualified.

Once loaded, the function is then available anywhere an EL statement is legal in the WAD file.

For example we will soon see the following use when we discuss authentication:

<when test="${checkPassword(param.username, param.password)}"> <auth action="resume" domain="weaveradmin"/> </when>

The username and password parameters are passed to the checkPassword function and the when tag acts based on the returned value.

In the next section we'll discuss Weaver's support for authentication.

Authentication

Weaver has in-built and extremely flexible support for authentication. The approach taken is that you provide the actual logic for authentication and Weaver provides generic supporting functionality.

Typically a JSP page is used to collect credentials and a W-Bean or an EL Custom Function is used to determine their validity.

You define the circumstances under which authentication is required in your WAD file. Weaver then automatically handles the issuing of the security challenge when necessary and the resumption of execution on successful authentication. Execution is automatically resumed at the point in the WAD file that caused the security challenge to be issued. So, if the user was trying to reach our JSP page to cast a vote when the security challenge was issued, then they will automatically be taken to that page on successful authentication - with all request related state restored.

You can, should you wish, have multiple areas of authentication in your application - this is supported via a mechamism known as authentication domains. Authentication domains allow your application to be partitioned in to different security areas, each having their own authentication requirements. For example the Administrative Console has its own security domain, but once you have authenticated here it does not mean you have access to, say, secure parts of your application that have their own security domains.

Authentication is controlled in the WAD file using the auth tag. The auth tag takes two attributes:

  • action - Selects the required authentication function, it may take any of the following values:
    • required - If the session is not authenticated for the given domain then the current execution state is stored and the main/security section of the WAD file is executed. Usually the security section issues an authentication challenge to the user in the form of a login page.
    • resume - The resume action is used after a successful authentication challenge. It causes execution to restart at the state stored by a previous required action and adds the given domain to those authenticated for the current session.
    • add - The add action explicitly adds this session to those authenticated for the domain.
    • remove - The remove action explicitly removes this session from those authenticated for the domain.
  • domain - The name of the authentication domain.

Consider if, in our example, we wanted the user to authenticate before voting. First we write a function checkPassword that takes a username and a password, checks their validity and returns appropriately true or false. As described in the section on Adding Custom Functions to the Expression Language, this function is made available to the EL as follows:

<function name="checkPassword" class="com.oldlight.weaver.tutorial.Functions" signature="java.lang.Boolean checkPassword(java.lang.String, java.lang.String)"/>

Now we write a JSP page that displays a login form - two text input fields named username and password and a hidden field called event with value doCheckAuth that is used in the WAD file to determine that a login attempt is being made. In the WAD file we forward the user to our new JSP page from the security section:

<security> <forward url="/WEB-INF/myapp/jsp/loginPage.jsp"/> </security>

Now we need to arrange for the user to be required to login and take to appropriate action depending on the validity of the username and password they supply. This is configured in the WAD file as follows:

<choose> <when test="${param.event eq 'doCheckAuth'}"> <choose> <when test="${checkPassword(param.username, param.password)}"> <auth action="resume" domain="weaveradmin"/> </when> <otherwise> <set var="loginMessage" scope="request" value="Incorrect username or password"/> <forward url="/WEB-INF/myapp/jsp/loginPage.jsp"/> </otherwise> </choose> </when> <otherwise> .... <auth action="require" domain="weaveradmin"/> <!-- Main event dispatching --> </otherwise> </choose>

The first when clause handles the submission of the login form. The choose inside this takes a different action depending on whether our checkPassword function returns true or false.

If authentication is successful we call the auth tag with a resume action. This causes execution to resume automatically at the point it left off when the security challenge was originally made. The user will be taken back to whatever they were trying to do when they were required to authenticate.

If authentication fails then we set the loginMessage attribute to an error message and send the user back to the login page. The login page displays the error message and gives the user an opportunity to try to login again.

But how do we actually indicate that parts of our application require authentication. In our example this is done in the otherwise section of the outside choice (usually where the main event dispatching goes on) with the auth tag taking the require action attribute. This will cause the security section to be executed if the current session has not been successfully authenticated for the declared domain.

Setting up authentication is probably the fundamentally most complex activity undertaken in the WAD file. However, given Weaver's in built support for storing state and automatically resuming after a successful security challenge, and its ability to partition authentication via domains the work in the WAD file is it is well worth the trouble. After you have written one application with support for authentication you have cracked it - the structures required in the WAD file become an easily reproducible cliche.

Next we'll take a look at how Weaver deals with errors thrown by your W-Beans.

Error Handling

Weaver is able to handle, in the WAD file, errors raised by W-Beans (that is WeaverClientExceptions thrown). The structure of the error handling tags is taken from JSTL. I am not wonderfully happy with the design - but have stumped for consistency with JSTL rather than for producing something arguably better but different.

In the WAD file we use the catch tag to trap any WeaverClientExceptions raised by its subtags. The catch tag takes two optional attributes that define an attribute where any exception thrown should be stored:

  • var - The name of the attribute in which to store the WeaverClientException.
  • scope - The scope of the attribute in which to store the WeaverClientException.

To handle (rather than to just ignore) an error you must check for the presence of the stored exception in your WAR file and take appropriate action - most crudely by forwarding to a JSP page that displays details of the error to the user. For example:

<main> <dispatch> <catch var="error" scope="request"> ... other tags </catch> <if test="${requestScope.error ne null"}> <forward url="/WEB-INF/myapp/jsp/errorPage.jsp"/> </if> </dispatch> </main>

Next we'll take a look at the configuration files used by Weaver.

Configuration Files

Every Weaver application must have the following configuration files:

  • WEB-INF/application/conf/application.properties - This is the global, application-wide configuration file, there must be exactly one per servlet context/application.
  • WEB-INF/[servletname]/servlet.properties - This is a per servlet (equivalent to per WAD file) configuration file. There must be one of these for each servlet/WAD file.

These configuration files take the form of standard properties files.

For details of the standard settings contained in the configuration files please see the Reference Manual. You may, of course, add additional configuration values for your application.

Within a W-Bean values from the application configuration file are available from the ApplicationContext via the getConfigValue method.

For example:

... public void handle(WeaverExecutionContext context) throws WeaverClientException { String value=context. getApplicationContext().getConfigValue("app.property"); ...

Within a W-Bean values from the per-servlet configuration file are from the ServletContext via the getConfigValue method.

For example:

... public void handle(WeaverExecutionContext context) throws WeaverClientException { String value=context. getServletContext().getConfigValue("servlet.property"); ...

Note here how the name of required parameter is formed in the standard dot ('.') separated manner.

Bean Parameter Assertions

You will often find that your W-Beans need to confirm that certain values are present before they may proceed. Weaver provides a utility class, WeaverAttributeAssestion that is able to check:

  • Presence of a named value in a given scope or in any scope.
  • The Java class of the value.
  • Whether a String value (the most common case in this situation) is from a given set.

Should an assertion fail then a WeaverClientException is thrown including an informative message.

Typical usage is as follows:

... private static final WeaverAttributeAssertion[] assertions = new WeaverAttributeAssertion[] { new WeaverAttributeAssertion("var", WeaverAttributeUtil.SCOPE_BEAN, java.lang.String.class, true), new WeaverAttributeAssertion("colour", WeaverAttributeUtil.SCOPE_BEAN, java.lang.String.class, true, new String[] { "red", "green" } ), }; ... public void handle(WeaverExecutionContext context) throws WeaverClientException { WeaverAttributeAssertion.assertAttributes(null, context, assertions); ...

The first assertion declares that a value called var must be found in the Bean scope (that is must have been supplied via the param tag in the WAD file) and that it must be a String.

The second assertion is similar to the first but insists that the value of colour attribute is take from the values red and green.

Attribute assertions can be a great help during development but are an overhead in a production deployment. Attribute assertions may be turned on or off in the application configuration file (WEB-INF/application/conf/application.properties) using the following setting:

weaver.attribute_asserts_on: false

Please see the javadoc for a full description of the WeaverAttributeAssertion class.

A Note About the Deployment Descriptor and the Starter Application

As we have seen Weaver's starter application ships with a ready-made deployment descriptor (web.xml).

There are some parts of the deployment descriptor that are mandatory for Weaver and others that may be modified to suit your application.

The deployment descriptor shipped with the starter application is as follows:

<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd" version="2.4"> <!-- START: OK to customise --> <display-name>Weaver - Starter Application</display-name> <!-- END: OK to customise --> <listener> <listener-class> com.oldlight.weaver.interpreter.WeaverSessionListener </listener-class> </listener> <listener> <listener-class> com.oldlight.weaver.admin.WeaverStatisticsListener </listener-class> </listener> <servlet> <servlet-name>init</servlet-name> <servlet-class> com.oldlight.weaver.initservlet.WeaverInitServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>admin</servlet-name> <servlet-class> com.oldlight.weaver.appservlet.WeaverApplicationServlet </servlet-class> <load-on-startup>2</load-on-startup> </servlet> <!-- START: OK to customise --> <servlet> <servlet-name>myapp</servlet-name> <servlet-class> com.oldlight.weaver.appservlet.WeaverApplicationServlet </servlet-class> <load-on-startup>3</load-on-startup> </servlet> <!-- END: OK to customise --> <servlet-mapping> <servlet-name>init</servlet-name> <url-pattern>/init/*</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>admin</servlet-name> <url-pattern>/myapp/admin/*</url-pattern> </servlet-mapping> <!-- START: OK to customise --> <servlet-mapping> <servlet-name>myapp</servlet-name> <url-pattern>/myapp/*</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>myapp</welcome-file> </welcome-file-list> <session-config> <session-timeout>10</session-timeout> </session-config> <!-- END: OK to customise --> </web-app>

I have added comments to the above to flag those areas you might choose to modify. You would modify them if you wanted to add additional servlets or wanted to change the servlet name myapp to something more meaningful.

Should you change the servlet name (myapp) then you must correspondingly rename the sub-directory of WEB-INF that holds its configuration.

Other Tags

We have already covered most of the core tags shipped with Weaver. You can see the full list in the Reference Manual. Those not discussed to date are:

  • xparse - Parses an XML document into a DOM tree representation.
  • xtransform - Applies an XSLT stylesheet to an XML document. This is primarily useful in transforming XML generated by your application into HTML to display in the user's browser.
  • import - Includes other XML files in the WAD file. Each included file must be a well-formed XML document.
  • out - The out tag sends output to the browser.
  • remove - The remove tag deletes a scoped value.

In the fullness of time more tag libraries may be written - contributions are welcomed.

Next we'll take a look at the logging facilities offered by Weaver.

Logging

You may optionally use, in your W-Beans (and in other Model code), the same logging facility as used by Weaver itself. This has the distinct advantage that your logging may be controlled from the Administrative Console.

Weaver logging is implemented using Apache Log4j, for a detailed description of its functionality and of its configuration please see the Log4j web site.

Most simply, in your code you aquire a Logger object as follows:

import org.apache.log4j.Logger; ... Logger l=Logger.getLogger("mycom.mydomain"); ...

The parameter to getLogger names a area of your application (often a package name). You may then enable and disable logging on a per-area basis. This facility is often useful to elimate the logs you don't need in order to more easily inspect those you do.

Once you have your logger you may use it as follows:

l.debug("This is a detailed message for debug"); l.info("This is an informational message"); l.warn("Some thing is not quite right"); l.error("This is definitely wrong"); l.fatal("OK - I am going to bail");

All of the above methods have a peer that takes a second Throwable parameter - useful should you need to log an exception.

On application startup, logging configuration is read from the application configuration file (WEB-INF/application/conf/application.properties) using the standard log4j properties.

The configuration as shipped with the starter application is as follows:

# --- Log4j Configuration (see http://logging.apache.org) --- log4j.rootLogger=info, stdout # stdout appender log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=[%p] (%c) - %m%n # file appender log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=/WEB-INF/application/logs/log.txt log4j.appender.file.MaxFileSize=100KB log4j.appender.file.MaxBackupIndex=1 log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern= \ %d{dd MMM yyyy HH:mm:ss,SSS} - [%p] (%c) - %m%n # --- Log4j Configuration ---

Logging settings of a running application may be modified via the Administrative Console.

Life Cycle Listeners

Life Cycle Listeners provide a mechanism by which W-Beans may be informed of session or application creation or destruction independent of any call made from the WAD file.

To be informed of application creation and destruction your W-Bean should implement the WeaverApplicationLifeCycleListener interface.

To be informed of session creation and destruction your W-Bean should implement the WeaverSessionLifeCycleListener interface.

Please see the javadoc for full details of these interfaces.

The created methods of Life Cycle Listeners are triggered before the corresponding section of the WAD file is executed and the destroy methods are triggered after the corresponding section of the WAD file is executed.

What Next?

You should now know all you need in order to begin developing applications using Weaver.

The following sources of information about Weaver may be useful:

  • The Reference Manual.
  • Javadoc of the classes used in application development.
  • The Tag HOWTO - will show you how to add you own tags to those available in the WAD file.
  • The Upload HOWTO - will show you how to handle file uploads from the user's browser.
  • The From Source HOWTO - will show you how to build Weaver from its source code.
  • The WBean Param Taglet documentation - will show you how to use the WBean Param Taglet to include WBean usage information in your Javadoc.
  • Use the EMail lists to ask others for help.
  • The Java, WAD file and JSP pages that make up the Administrative Console may prove illustrative.
  • Javadoc of the whole of Weaver - should you wish to look under the hood.
  • The source code to Weaver itself may be useful when tracking down problems, reporting bugs or extending functionality. A hyperlinked HTML version may be browsed here.

The following references contain related information:

Top