User Guide

If a highly disciplined subject like physics is vulnerable to the symptoms of groupthink, what may be happening in other, less rigorous areas?
- Lee Smolin

WEB4J Javadoc: link

Table of Contents

Geek Man Example Applications
Why WEB4J Was Built
How WEB4J Was Built
Tour of a Typical Feature
Directory Structure
Model Objects
  Simple Versus Complex Domain Models
Data Access Objects
  The .sql Files
  Ordering Conventions
  Reports
  Transactions
  Distributed Transactions
  Connections and Multiple Databases
  Conversions
  Dynamic SQL
  Database Configuration in web.xml
  Postgres Driver - Internal Conversions
Request Processing
Application Security
  SQL Injection Attacks
  Cross-Site Scripting (XSS) Attacks
  Cross-Site Request Forgery (CSRF) Attacks
  Application Firewall
  Controlling Sessions
  Logging User Data
  Preventing Spam
Fine-Grained Security Constraints
Restrict-by-User Constraints
  Direct Links To User Table
  Indirect Links To User Table
    Using Subqueries
    Using web.xml and FetchIdentifierOwner
Actions
  Convenient Object Key Names
  Display Messages To The User
  Building Model Objects
Populating Forms
Configuring web.xml
Configuring Database Items in web.xml
Configuring Implementation Classes
Listing of Interfaces and Conventional Names
Multilingual Apps
  Base Text
  Minimally Invasive
  Using Your App To Assist In Translation
  Storing Translations in Application Scope
  Using One JSP Per Language
Encoding Issues
  Page Character Encoding
Serving JSON, XML, and so on
Logging
Start-up Tasks and Code Tables
File Uploads
Paging
Miscellaneous Tags
Double-Submit Issue
Using the Database Layer Outside of a Servlet
Utilities
Unit Testing
  Where To Place Unit Tests
  Initializing WEB4J Classes
  Unit Testing Model Objects
  Unit Testing DAOs
  DAOs and Ordering Convention Errors
  Fake DAOs
  Unit Testing Actions
  Fake System Clock
Building New Apps From The Example App
  Phase 1 - Get Example App Running Against A New Database
  Phase 2 - Start Implementing Real Features
  Phase 3 - Remove Unnecessary Items
  Build Your Own Example App
Version History

Example Applications

WEB4J comes with some non-trivial example applications: If you download, configure, and run these applications, then you will gain rapid insight into how WEB4J applications work. They are large enough to be representative of typical database applications, but not so large as to be overwhelming. (The Predictions app is particularly recommended, since it's smaller, and a better overall introduction to web4j.) Their javadoc includes convenient links to source code, JSPs, and SQL statements. It's likely that you can build you own applications by modifying these example apps. Since they use package by feature, removing items unrelated to your problem domain reduces to simply deleting directories.

Please see the Getting Started Guide for more information.

For those looking for a more compact introduction to WEB4J, or for those with less experience with servlets, please see the tutorial.

Why WEB4J Was Built

WEB4J was built for one reason: other Java tools for creating a browser front end to a database are unacceptable. They take too long to learn, and are too difficult to work with. In short, they are disturbingly unproductive. This sentiment is very widely held. The attention paid by many Java programmers to Ruby On Rails vividly demonstrates a widespread hunger for something simpler.

How WEB4J Was Built

WEB4J was built slowly. It was built over several years, and its API design has always centered on giving you feelings of elegance, beauty, and concision.

WEB4J was not built like most tools, because it was not built with a specific implementation technique in mind. Rather, it was built with specific feelings or esthetics in mind. The basic steps were:

  1. build typical applications on top of the servlet and JSP APIs
  2. find all repeated code and move it from the applications into the framework
  3. do it such that the application programmer will feel the maximum elegance in their code
  4. do not presuppose any particular style of implementation
Let's clarify why this style is different, using two counter-examples: Hibernate and Java Server Faces are examples of tools having, at their core, very specific implementation ideas. In both cases, the central implementation ideas are taken as the "Prime Directive". Anything incompatible with that Prime Directive is automatically placed out of consideration. This is a dangerous way of building things. It places implementation first, and treats elegance, and the experience of the programmer using the tool, as secondary, not primary.

However, it does not seem possible to produce elegance without treating it as the single most important thing. Is Hibernate beautiful? Is all that XML configuration beautiful? Are its abundant cyclic references between packages beautiful? Is needing to configure the database schema in the application layer an elegant thing? Many would say "No, this is a bit ugly". And perhaps it was destined to be ugly, since it was based on an implementation idea, instead of on esthetic principles. If you wish to make something beautiful, you need to treat beauty as your Prime Directive.

Tour of a Typical Feature

Here is a screencast tour, giving you an excellent overview of how a typical feature is implemented in a WEB4J application. More details on each aspect are provided below.

Here are some typical line counts per feature, taken from a cross section of several features in the Fish & Chips Club example application. (Documentation comments are included in the line counts.)

ItemAvg LinesRelative Size
SQL file (.sql)25Graph
Presentation (.jsp)108Graph
Total133 
Model (.java)111Graph
Action (.java)144Graph
DAO (.java)46Graph
Total301 

In general, your code will only be roughly twice the size of the non-code elements of your application.

It's often the case that the DAO class is simple, consisting only of a few single-line methods. In such cases, it's recommended that those DAO methods be refactored into the Action class. This will slightly reduce the total line count.

Flow of control for a typical feature

The above diagram shows the relationships between the elements found in a typical feature. The above notation means, for example, that the DAO uses the Model (and not the reverse). The View here is usually a Java Server Page.

Directory Structure

Although it's not required, you are highly encouraged to use the package by feature style for your package and directory structure. For an illustration, see the WEB4J example applications.

Package by Feature

There are numerous advantages to this style. Although it may be unfamiliar to you, it's very likely that once you use it, you will find it superior to other styles.

If you do select this style, then you may find it convenient to use fixed, conventional names for both the view (the JSP) and the .sql file (which contains SQL statements), such as view.jsp and statements.sql. Using fixed names for the java classes is not recommended, however, since that would cause problems with javadoc, and would likely make your IDE less effective.

It's useful to treat your Action classes as the public face of each feature. That is, the javadoc of the Action should be the entry point into the rest of the implementation. In particular, it should link to the actual SQL statements and JSP. This is implememented in the example apps using two custom taglets. (Taglets are part of the javadoc tool.)

Example of their use:

/**
* Edit Resto objects.
*
* @sql statements.sql
* @view view.jsp
*/
public final class RestoAction ...
These two taglets are defined in the WEB4J Developer Tools.

Model Objects

Model Objects represent items in your problem domain. They roughly correspond to rows in a database table (more precisely, the rows of a ResultSet). In WEB4J, Model Objects almost always have the following public items: Example code for some Model Objects:

The primary job of a Model Object is to carry and validate data. Validation is especially important, since data is king. Most of the code in your Model Objects will deal with validation.

In WEB4J, validation is always placed in the constructor. If a validation fails, then a checked ModelCtorException must be thrown. When a validation fails, you cannot throw any other kind of exception except ModelCtorException. For example, if a required field is found to be null, then you cannot throw a NullPointerException. You must throw a ModelCtorException.

Some may object to this policy. However, you must remember that NullPointerException is an unchecked exception. Unchecked exceptions are strictly meant for bugs. Here's the important point: faulty user input is not a bug. It's to be expected as part of the normal operation of the program.

Validator and Check can help you validate your Model Objects. You are not required to use them, but you will likely find them convenient. The Check class returns common implementations of the Validator interface. For validations specific to your problem domain, you can define them as a new Validator, and then use them with the Check class, just like any other Validator.

To always implement Model Objects safely, you need to understand that mutable object fields often need special care, in the form of a defensive copy.

The ModelUtil class is provided to help you correctly implement the toString, equals, and hashCode methods.

Your Model Objects can use the SafeText, Decimal, Id, and DateTime classes as building blocks, in the same way as Integer, Boolean, and so on. An Id can hold any kind of identifer you wish. SafeText should usually be used to model free-form user input, to avoid issues with Cross-Site Scripting attacks (see below). The Decimal class is the recommended replacement for BigDecimal, since it makes calculations much easier. The DateTime class is the recommended replacement for the widely reviled java.util.Date.

When modeling search criteria entered into a form, it's often possible to create a Model Object with package-private getXXX methods. As a general guideline, you should minimize the scope of methods and classes whenever possible.

Simple Versus Complex Domain Models

When using WEB4J's data layer, simple domain models are usually necessary. In the context of WEB4J, simple domain models have two distinguishing characteristics: Is it possible to create such a domain model, while still remaining useful? It certainly is.

No extends Keyword

WEB4J isn't sensitive to the use of extends in your domain model. However, it's recommended that you avoid its use, since it's more difficult to work with. For further information, please see the topics in Joshua Bloch's Effective Java related to extends:

Another topic of interest is Obey the general contract when overriding equals, which states:

"There is no way to extend an instantiable class and add a value component while preserving the equals contract."

This means that the equals method is, in a sense, broken. If you need to extend a class, and add a new significant field, then it's not possible to provide a correct implementation of equals.

No 1..N Relations

Many are in the habit of hard-coding 1..N relations into the domain model, where one domain class has a List or Set of some other domain class. However, it can be strongly argued that it's better to avoid this, since it often doesn't correspond well with how different features use different data. For instance, take an example of a medical application, with 1 Doctor having N Patients. It's easy to imagine different features/screens having different needs:

Each feature uses different data. In some contexts, there's no need to refer to Patients at all. That is, there are common cases in which the underlying relation is of no relevance. To insist that the Doctor class must have an explicit, hard-coded relation to a Patient class disregards this fact. In effect, such a style promotes one particular use case to the detriment of the others.

On the other hand, if the Doctor and Patient classes are independent, then each feature/screen can simply retrieve the data as needed -- no more, and no less. If a feature needs to show a single Doctor along with a list of their Patients, then they are simply retrieved by the Action and placed into request scope. In effect, the relation is implemented in the Action, instead of the domain model. No wiring together of the objects is needed.

If you implement relations in your domain model, then you pay twice: first by defining an explicit link in your domain model, and then by ensuring that items are ignored in any context where they aren't needed. That's a lot of effort, for little benefit. In effect, this seems to create more problems than it solves.

There is a split among programmers between those who take data as king, and those who take the domain model as king. WEB4J agrees with those who take data is king, and as well with those who would like to keep domain classes as simple as possible.

Data Access Objects

A Data Access Object (DAO) implements persistence. It forms a bridge between java objects and database records. Each database operation has its own method in a DAO.

Web4j has tools to help you implement persistence with a relational database, but you aren't forced to use them. If you want to use some other tool for persistence, then you can certainly do so. If you use web4j's data layer, then your DAO's will usually use the Db class.

Example code for some DAOs:

The .sql files

Each DAO interacts closely with a set of SQL statements, placed in an .sql file. These are text files containing named blocks of SQL statements (example). The detailed syntax of these files is described here. These files can be placed anywhere under /WEB-INF/. You may use a single .sql file, or many.

The problem arises of how exactly these SQL statements are referenced in your source code. Incorrect references in code should be detected as soon as possible. Detecting them at compile time would be best, and detecting them at runtime would be worst. The approach taken by WEB4J is to detect bad references at startup time. This is not quite as effective as detecting such errors at compile time, but it's much more effective than detecting them at runtime.

At startup, WEB4J will read in all .sql files located under /WEB-INF/. It will also scan your code for all public static final SqlId fields. Then, these two sets are compared. WEB4J strictly enforces a one-to-one relation between the SqlId fields defined in your code, and the named statement blocks defined in your .sql files.

Here's an example. The statement named ADD_COMMENT (appearing in an .sql file) must correspond exactly to a single SqlId object (appearing in your code). The text passed to that SqlId's constructor must exactly match the name of the SQL statement.

Mismatch

If any mismatch is found, then a RuntimeException is thrown, and your application will not be able to proceed. This protects you both from trivial naming errors and from 'cruft' (items that are no longer used), both in your code and in the .sql files.

The SqlId fields are what you use in code to reference items in your .sql files. They form the bridge between Java-land and SQL-land. For more information, please see the overview of the database package.

Ordering Conventions

There are two important ordering conventions used in the data layer. They exist in order to make your job easier.

Column Order of SELECTs and Model Object Constructors - The columns in a SELECT ResultSet must match one-to-one with corresponding arguments of the target Model Object constructor. Using this convention means that you don't have to perform tedious mapping between columns and Model Object setters.

Example: note how the constructor of Resto has arguments that correspond one-to-one to the SQL statements named LIST_RESTOS and FETCH_RESTO.

Order of Parameters Passed From DAO - SQL statements usually have one or more parameters, denoted by '?' placeholders in the underlying SQL. When your DAO passes data to such SQL statements, their order is taken as matching one-to-one with the corresponding '?' placeholders.

Let's take an example. For this entry in an .sql file:

ADD_RESTO  {
 INSERT INTO Resto (Name, Location, Price, Comment) VALUES (?,?,?,?)
}

The corresponding DAO method is:

Id add(Resto aResto) throws DAOException, DuplicateException {
  Id result = Db.add(
    ADD_RESTO, 
    aResto.getName(), aResto.getLocation(), aResto.getPrice(), aResto.getComment()
  );
  return result;
}
The important point is that the order of the sequence parameters passed to Db.add(SqlId, Object...) matches the underlying SQL. The following code would be wrong, since Price and Comment are in the wrong order:
Id add(Resto aResto) throws DAOException, DuplicateException {
  Id result = Db.add(
    ADD_RESTO, 
    aResto.getName(), aResto.getLocation(), aResto.getComment(), aResto.getPrice()
  );
  return result;
}

Reports

A report is implemented as a single SELECT operation, possibly with some criteria. The implementation of a report is very often shorter than the average feature. Since there is only one database operation, and its implementation is usually a single line, it's reasonable to place that code directly in the Action itself, instead of in a separate DAO class. In the Fish & Chips Club example app, each report is implemented with an Action class that handles the data access without using an external Data Access Object.

Here are some examples:

If an appropriate Model Object already exists, then it should likely be used to return the required data, using the Db class.

If no Model Object exists for reporting a given set of data, then the Report class can be used. The Report class translates a ResultSet into a Map. It has several methods, corresponding to different policies for formatting the data.

Transactions

It's important to preserve the integrity of the database. If multiple database operations only make sense as a single unit, then they should be placed in a transaction. These classes help you implement transactions: See RoleDAO for an example of using TxTemplate and DbTx.

Distributed Transactions

Distributed transactions refer to transactions that span not only multiple operations, but multiple database vendors as well. WEB4J has no API related specifically to distributed transactions. However, if UserTransaction is available in your environment, then it can certainly be used. It's just that WEB4J classes are not currently able to help you with their implementation.

Connections and Multiple Databases

You need to tell WEB4J how to get database connections. You do that by providing an implementation of the ConnectionSource interface. The Fish & Chips Club has an example implementation that uses container connection pools for three separate databases. It's easy to use more than one database with your application.

The database names defined by ConnectionSource are used in your application as prefixes to identify which database a given operation is intended for. These prefixes are used in two places: in .sql files and in the corresponding SqlId fields (see above).

An entry in an .sql file versus the default database looks like this (block name has no prefix):

ADD_COMMENT {
 INSERT INTO Discussion (Name, Body, CreationDate) VALUES (?,?,?)
}
...
public static final SqlId ADD_COMMENT =  new SqlId("ADD_COMMENT");
An entry in an .sql file versus a non-default database named ACCESS_CONTROL looks like this (block name is prefixed with "ACCESS_CONTROL"):
ACCESS_CONTROL.USER_LIST {
 SELECT Name, Password
 FROM Users
 ORDER BY Name
}
...
public static final SqlId USER_LIST =  new SqlId("ACCESS_CONTROL", "USER_LIST");
Here, the prefix "ACCESS_CONTROL" is a database name defined by the ConnectionSource implementation.

Conversions

When building Model Objects, WEB4J converts ResultSet columns into 'building block' objects such as Integer, BigDecimal, and so on. That conversion is defined by the configured implementation of ConvertColumn. WEB4J comes with a reasonable default implemention called ConvertColumnImpl, which can almost always be used. If you wish to define your own custom conversion policies, then you will need to provide an alternate implementation of ConvertColumn.

Dynamic SQL

WEB4J's data layer is centered on the mechanism of using base SQL statements defined in .sql text files, with '?' placeholders for all dynamic data. However, sometimes you need to create your SQL statement more dynamically, either in whole or in part.

The most common occurrence of this is in search operations. Search operations are implemented with a SELECT, but they differ from other SELECTs in an important way. In many cases, the user input which defines the search criteria can come in a large number of combinations. In those cases, enumeration of all possible combinations in your .sql file is often impractical. The DynamicSql class was created to help with this issue. It allows you to programmatically add the WHERE and ORDER BY clauses to base SQL statements (defined in the usual way in an .sql file). In this case, the static parts of the statement are placed in the .sql file, and the dynamic parts are appended in code. Before you use the DynamicSql class, you must have a good understanding of SQL injection attacks.

To use DynamicSql correctly, you need to parameterize user input:

  1. build a statement (partial or complete) having '?' placeholders for all user input
  2. pass all the data input by the user as parameters to the above
These steps simply ensure that you use PreparedStatement correctly. The point is you should never place any data entered by the user directly into the SQL statement. The moment you do that, you are entering a world of pain, since your whole database is open to attack. So don't do it.

See the example app's search feature for an illustration of using DynamicSql (especially the RestoSearchAction class).

There's another use case for DynamicSql as well. It's possible to create the entire SQL statement dynamically in code. In this case, there is still an entry in your .sql file, but it's an empty block. This allows you to to specify a non-default database name in the name of the block, if desired. It also allows WEB4J to apply the same processing as it usually does to your SqlId fields.

Database Configuration in web.xml

There are a number of items in web.xml related to the database. For each application, you will need to review their values, to ensure they are appropriate. Please see below for further information.

Postgres Driver - Internal Conversions

Postgres is a popular open-source relational database. If you use web4j's data layer with postgres, then there's an issue with the postgres driver that you need to be aware of. It relates to trivial internal conversions of data - for example, converting a String '123' into a corresponding Integer. Unfortunately, in modern versions of the postgres Java driver, a decision was made by implementors to turn such conversions off by default. To turn them back on, you need to pass an extra parameter when creating a connection:
stringtype="unspecified"
If you don't turn on these conversions, then some calls to PreparedStatement.setString() made by web4j's data layer will fail. Please see the postgres JDBC driver documentation for more information.

Further Information

Please see the WEB4J javadoc for more information on the database layer.

Request Processing

Request Processing

(Your code is almost always concerned with only the last 2 steps appearing above. You don't have to remember this structure. It's presented simply as background.)

By default, WEB4J uses a reasonable naming convention to map each incoming request to an Action. That convention is defined by RequestParserImpl. If you wish to use some other mechanism, then you will need to provide an alternate implementation of RequestParser.

Here's an example of the default Action mapping: given the Action class with fully qualified name of

hirondelle.fish.main.member.MemberEdit
the implicit URI mapping is calculated as
/main/member/MemberEdit
That is, the '.' character is changed to '/', and the prefix 'hirondelle.fish.' is removed. The prefix is configured in web.xml, with a setting named ImplicitMappingRemoveBasePackage.

This default mechanism also allows for simple explicit overrides: just add to your Action a String field of the conventional form:

public static final String EXPLICIT_URI_MAPPING = "/translate/basetext/BaseTextEdit";
All Action mappings found by RequestParserImpl are logged during startup.

Once the Action class corresponding to the underlying request is found, then WEB4J creates an object of that class, and calls its execute method. That method performs the desired operation, and returns a ResponsePage, which tells WEB4J how you want to render the final response. The ResponsePage points to a JSP, and controls the forward versus redirect behavior.

Application Security

Web applications need to protect themselves from malicious attacks by hackers. The OWASP site is an excellent reference for web app security. It's highly recommended that any programmer not familiar with web app security should closely read and understand the issues discussed on the OWASP site. Any web application not built with security in mind is very likely at risk.

Security

It's strongly recommended that you review the various items below related to security.

WEB4J does its best to help you create secure web apps. In a few important cases it has built-in, default mechanisms which protect against certain kinds of attack:

In other cases, however, only optional mechanisms are available. For example, using SafeText to model free-form user input instead of String is highly recommended, but not absolutely mandatory. If you use these optional mechanisms correctly, then they will increase your confidence in the security of your app. It's important to realize that using WEB4J doesn't guarantee that your app is secure.

(Indeed, it does not seem possible for a web app framework to address all security problems. For example, how can a framework prevent you from storing passwords in clear text, or from using http when https is needed?)

SQL Injection Attacks

When using SQL statements defined completely in an .sql file, SQL Injection attacks are not possible. This is because WEB4J always uses a PreparedStatement, and PreparedStatements are not vulnerable to SQL Injection attacks.

For cases in which DynamicSql is used, please see the section on dynamic SQL for important information.

Cross-Site Scripting (XSS) Attacks

Cross-Site Scripting attacks place executable scripts into free-form user input. When such input is later rendered in the view without taking special precautions, it will execute. To protect against such attacks, you need to escape special characters when reflecting free-form user input in the view. The idea is that escaping special characters will render such scripts unexecutable.

Cross-site scripting attacks are dangerous, common, and simple to perform. They are a major problem with web applications in general, and they should be taken very seriously. WEB4J takes the position that, in a web application, Strings containing free-form user input are a dangerous substance.

WEB4J provides SafeText as a replacement for String. SafeText will automatically escape special HTML characters in its toString() method. When needed, other escaping policies are available from SafeText, by calling its getXmlSafe() and getRawString() methods. The principal advantage is that JSP Expression Language can safely be used to render SafeText objects without worrying about special characters.

Tools such as <c:out> which manually escape free-form user input in the view will likely work - if you remember to use them. When using SafeText, however, the danger of forgetting to escape in the view does not exist. Hence, using SafeText will likely increase your confidence in the security of your application.

SafeText has a second line of defense as well. In addition to escaping special characters, it allows you to specify a "white list" of acceptable characters. The white list is defined by your implementation of the PermittedCharacters interface. The default implementation will often be satisfactory.

The ConvertParam interface defines how building block classes are created out of request parameters, before being passed to Model Object constructors. The default implementation always allows SafeText, but it only conditionally allows Strings. This is controlled by the AllowStringAsBuildingBlock setting in web.xml. By default, that setting is turned off. That is the recommended setting. It provides maximum protection against XSS attacks. If you wish to allow your Model Objects to be built with Strings, then you must explicitly allow it.

Cross-Site Request Forgery (CSRF) Attacks

Cross-Site Request Forgery attacks hijack valid sessions to perform malicious operations.

In WEB4J, you defend against CSRF attacks by performing the following:

Application Firewall

An ApplicationFirewall protects your application from malicious attacks. ApplicationFirewall makes an important distinction between hard validation and soft validation. Hard validation is for low level 'sanity' checks, and detects either attacks or gross programmer errors. Failure of a hard validation can result in an unpolished response, and should not be seen during normal operation of the program. Soft validation is for high-level business validation, and is expected during normal operation. Failure of a soft validation must show a polished response to the end user. See ApplicationFirewall for more information on this important distinction.

The default ApplicationFirewallImpl works closely with RequestParameter. If an Action uses request parameters (and they usually do), then it must state explicitly what request parameters it expects: what their names are and what basic sanity checks should be performed on their values. That is done by declaring RequestParameter fields in the Action of the form (example):

public static final RequestParameter COMMENT = RequestParameter.withLengthCheck("Comment");
ApplicationFirewallImpl will perform hard validation on the incoming request parameters, both their names and values. The soft validations are done in your code, usually in a Model Object constructor.

Controlling Sessions

Sessions require some management since some attacks attempt to steal session identifiers. Unfortunately, servlets tend to be rather liberal in the creation of superfluous sessions, which leaves unwary programmers open to some attacks.

WEB4J helps in these ways:

The following are also recommended:

In addition, the OWASP project recommends that a new session id should be created after every successful login. However, given the behavior of form-based login (as implemented in Tomcat, in any case) this doesn't seem possible to fully implement as desired. When Tomcat serves the login form, the session id is passed back to the client. Thus, the session id is served just before the user has successfully logged in, not after.

Logging User Data

There's a trade-off involving user data appearing in log files. For developers, seeing user data in log files is often very useful for problem solving. From a security standpoint, however, it's best if sensitive user data does not appear at all in the log. (For instance, think of log files containing credit card numbers.)

WEB4J usually logs at the FINE level. Log entries containing user data, however, are always logged at the FINEST level. In production, where the logging level is almost always higher than FINEST, such data will not appear in the log files at all.

The following two items are strongly recommended:

Preventing Spam

For most internal back office applications located behind a firewall, spam is usually not a problem. For applications on the public web, however, spam will usually be an issue.

The SpamDetector interface and its SpamDetectorImpl help you prevent spam from getting into your database. Once you have decided on an implementation, it can be used in two ways.

Fine-Grained Security Constraints

The Servlet API allows you to configure <security-constraint> items in web.xml. Here's an example which allows access to URLs starting with /webmaster/*, if the logged in user has the corresponding role of 'webmaster':
<!-- /webmaster/* only for 'webmaster' Role -->   
<web-app>
  <security-constraint>
   <web-resource-collection>
    <web-resource-name>Webmaster Module</web-resource-name>
    <url-pattern>/webmaster/*</url-pattern>
   </web-resource-collection>
   <auth-constraint>
    <role-name>webmaster</role-name>
   </auth-constraint>
  </security-constraint>
  ...
  <security-role>
   <role-name>webmaster</role-name>
  </security-role>
</web-app>
When thinking of security, it's often useful to think in terms of nouns and verbs: In the example <security-constraint> presented above, only the noun is defined - the items under /webmaster/*. It's not fine-grained, since all operations are allowed. The Servlet API does provide the <http-method> item, but there's a problem with it: browsers typically support only two operations, POST and GET. This is often inadequate to represent fine-grained security constraints in the real world.

An effective alternative is to use the <url-pattern> to represent both the nouns and the verbs:

Multiple extensions such as .add, .delete, and so on, are used to represent the verb. This style allows you to mix and match the nouns and the verbs, to create any kind of role-based security constraint you may need.

When using this technique, incoming URLs appear as follows:

The Member feature of the example app uses this technique.

When using this technique, you must also ensure that your <servlet-mapping> entries in web.xml allows for the various suffixes.

Restrict-by-User Constraints

Many applications need to restrict access to some operations to the 'owner' of the data (the person who created it). This is commonly seen in public web apps, where the items entered by one user cannot be edited or deleted by anyone else. In general, if an attempt is made by one user to edit items owned by another user, then the system must disallow it, and failing to do so is a major security problem. (Such restrictions may even be extended to include simple 'view' operations, whereby a user's data cannot even be seen by another user.)

While the Servlet Specification allows for security constraints based on role (restrict-by-role), it says nothing about security constraints based on user (restrict-by-user). Internal intranet applications usually have restrict-by-role constraints, but public web applications usually have restrict-by-user constraints.

There are a number of ways to implement restrict-by-user constraints.

Direct Links To User Table

The simplest case is for tables which link directly back to the user table (which identifies the owner). Let's take a working example from the Predictions example application that comes with WEB4J.
Users <- PredictionList <- Prediction
The Users table holds the user login name, password, and user id. Predictions are arranged in lists, and lists are owned by a single user. Each prediction links back to the Users table like this:
Prediction.PredictionListFK => PredictionList.Id
PredictionList.UserFK => Users.Id
Operations on the PredictionList table are simpler, since both the operation and its restrict-by-user constraint can usually be expressed in a single SQL statement. Some examples:
UPDATE PredictionList SET Title=? 
WHERE Id=? AND UserFK=?

DELETE FROM PredictionList 
WHERE Id=? AND UserFK=?

SELECT 
  Id, Title, CreationDate, UserFK
FROM 
  PredictionList
WHERE Id=? AND UserFK=?
In each case, the value of the UserFK field is always passed in, to enforce the restrict-by-user constraint. Where does the value of UserFK come from? It's stored in session upon login.

This technique can't be used with INSERT operations, since they have no WHERE clause.

When a user logs in, the user login name (usually not the same as the id) is always placed into session scope by the servlet container. In most databases the user table will be normalized, such that the user id will be used as a foreign key in other tables (and not the user login name). When the WEB4J Controller detects successful logins, it calls your implementation of the LoginTasks interface. This is how you can add any 'extra' data to session scope - in this case, the user id. Thus, the user id can be easily passed to the above SQL statements.

It's important to realize that the user id is a server-side secret, and should never be displayed in a web page, or sent to the client in any way. It's critical for implementing restrict-by-user constraints, and the end user is not allowed to read or write its value. (Of course, the user's login name is not a server-side secret.)

Indirect Links To User Table

The above case is fairly simple, since the table is directly linked to the user table. If the table is indirectly linked to the user table, however, then more work may be required. Multiple techniques can be used.

Using Subqueries

The most compact technique is to use subqueries; in particular, a 'scalar' subquery that returns a single value, the user's id or login name. If your database supports subqueries, then a WHERE clause can usually be added to link back explicitly to the owner. As in the simpler case of a direct link described above, both the operation and the restrict-by-user constraint can still be performed with a single SQL statement.

Here's a deletion operation that uses a subquery to ensure that the owner of the record is the same as a logged in user, named 'bob':

DELETE FROM Prediction 
WHERE Id = 65498
AND (
 SELECT 
  Users.LoginName 
 FROM 
  Users
 JOIN 
  PredictonList ON Users.Id = PredictionList.UserFK 
 JOIN 
  Prediction ON PredictionList.Id = Prediction.PredictionListFK
 WHERE 
  Prediction.Id = 65498
) = 'bob'
Such subqueries can sometimes become difficult to read, because of the nesting. The .sql file format defined by WEB4J can help, since it gives you a simple mechanism to define items as variables, as in
-- first define the subquery
PredictionOwner { 
 SELECT 
  Users.LoginName 
 FROM 
  Users
 JOIN 
  PredictonList ON Users.Id = PredictionList.UserFK 
 JOIN 
  Prediction ON PredictionList.Id = Prediction.PredictionListFK
 WHERE 
  Prediction.Id = ?
}

-- then reference the subquery in the top-level operation
DELETE FROM Prediction 
WHERE Id = ? 
AND (${PredictionOwner}) = ?

This technique can't be used with INSERT operations, since they have no WHERE clause.

Using web.xml and FetchIdentifierOwner

WEB4J offers a second way of implementing restrict-by-user constraints. This alternative can be used when:

The general idea is to use validated proxies for the user id. Continuing the example used above, remember that the Prediction table does not link directly to the Users table, since it's 2 steps away, not 1:

Prediction.PredictionListFK => PredictionList.Id
PredictionList.UserFK => Users.Id
The PredictionAction handles all operations on predictions. Here's a listing of its URLs:
/PredictionAction.list?ParentId=5
/PredictionAction.fetchForChange?Id=7&ParentId=5
/PredictionAction.add?ParentId=5
/PredictionAction.change?Id=7&ParentId=5
/PredictionAction.delete?Id=7&ParentId=5
Here, the ParentId is really a pointer to the PredictionList table. Its value is a proxy (a substitute) for the user id, since it eventually links back to the user. The problem is that when its value is passed from the client to the server as a ParentId request parameter, it's completely unvalidated, and cannot be used as a proxy for the user id without first validating it.

Validation can be done in 2 steps:

Step 1.
First, add an entry to web.xml of the form:

<init-param>
  <param-name>UntrustedProxyForUserId</param-name>
  <param-value>
   PredictionAction.*
  </param-value>
</init-param>
This entry identifies all requests in your application that have a restrict-by-user constraint that uses an unvalidated id. In this example, only one such constraint is defined, but you can define as many as you want, one per line. Thus, you can define all such constraints in a single spot. (See the javadoc for more information on the syntax of this entry.) This is useful since you can see all such constraints in a single place.

Step 2.
Second, the Action (in this example, PredictionAction) must now implement the FetchIdentifierOwner interface, which has a single method:

Id fetchOwner() throws AppException;
This method is implemented with a Data Access Object in the typical way, using the following underlying SELECT:
 SELECT 
   Users.LoginName
 FROM 
   PredictionList, Users
 WHERE
   PredictionList.UserFK = Users.Id AND 
   PredictionList.Id = ?
The SELECT is essentially a simple lookup, which translates PredictionList.Id (the unvalidated proxy for the user id) into the login name of the person who "owns" that id. (This corresponds to the subquery mentioned earlier.) The WEB4J Controller then validates for you the result of this SELECT versus the login name stored in the current user's session. If they are the same, then the Action can continue; if not, then the Action is treated as a malicious request, is not processed any further, and an unpolished response is sent to the browser.

The Controller processes each request roughly as follows:

if the request uses an unvalidated proxy for the user id {
  cast the Action into FetchIdentifierOwner
  if the cast fails {
    fail: do not process the request
  else {
    get the result of fetchOwner()
    if it matches the user login name {
      success: continue processing the request
    }
    else {
      fail: do not process the request
    }
  } 
}
Note that if you define the request as having a restrict-by-user constraint, and then forget to change your Action to implement FetchIdentifierOwner, then the Action will fail. This protects you in case you forget.

In summary, WEB4J defines these items for validating proxies for the user id:

The drawback to this technique is that the 2 operations - the check on ownership, and then the underlying operation - aren't executed as a single unit of work.

Here's a comparison of the two techniques for implementing restrict-by-user constraints:

ItemSubqueryFetchIdentifierOwner
Database supports subqueries Required Not required
Number of SQL operations 1 2
Defined in one place No Partially
Needs coding in Action No Yes
Supports SELECT Yes Yes
Supports INSERT No Yes
Supports UPDATE Yes Yes
Supports DELETE Yes Yes

Regular DAOs

If, for some reason, you don't wish to use the mechanisms described above, you can always implement restrict-by-user constraints using a regular DAO method:

Actions

An Action performs some desired operation, or group of closely related operations. An Action ties together all other parts of a feature's implementation. The Action uses the other parts of the implementation (Model Object, DAO, JSP) to perform the desired operations.

Flow of control for a typical feature

Your Actions will usually extend the following base classes: The ActionTemplateXXX classes are all instances of the Template Method design pattern. They all use an Operation to define which operation is to be performed - add, list, and so on. The Operation is taken from a request parameter named Operation, or from the URL's 'extension'. For the URL ending in .../MemberAction.list, the operation is taken as 'list'. The Operation enum is defined to help an Action branch according to its value.

Action methods tend to be 'branchy'. This is natural, since Actions need to handle all the different possible errors that can occur. Here are some example Actions:

Convenient Object Key Names

The ActionImpl base class defines various commonly needed items. For instance, it defines two constants that can be used, if desired, as conventional key names for items placed in request scope: These items are referenced in JSPs, using the JSTL syntax: ${itemForEdit}, for example. Using such conventional names is optional. If they make your life easier, then use them.

Display Messages to the User

Displaying information or error messages to the user is done with various addMessage and addError methods. Such messages come in two styles defined by the AppResponseMessage class: simple and compound. Simple messages are simply fixed Strings. Compound messages are parameterized, and use a simple syntax. Here's an example having both styles, first a compound, and then a simple message:
/** Update an existing {@link Visit}. */
protected void attemptChange() throws DAOException {
  boolean success = fVisitDAO.change(fVisit);
  if (success){
    addMessage("Visit to _1_ changed successfully.", fVisit.getRestaurant());
  }
  else {
    addError("No update occurred. Visit likely deleted by another user.");
  }
}
In the background, the addMessage and addError methods will simply place a MessageList in session scope. Session scope is used since many messages need to survive a redirect. WEB4J does not currently have a mechanism for associating a message with a specific control in the JSP.

The example application displays messages with a reusable .tag file (see /WEB-INF/tags/displayMessages.tag), always placed at the top of a templated page. The alternative style of placing error messages near form controls is sometimes problematic:

(It's possible that a future version of WEB4J will allow for changing the CSS class of controls that have an associated error message. However, given the above issues, it's less likely that placing text beside the control will be supported.)

Building Model Objects

To build a Model Object out of request parameters, you usually use ModelFromRequest along with some RequestParameter objects.

Example

public static final RequestParameter MEMBER_ID =  RequestParameter.withLengthCheck("Id");
public static final RequestParameter IS_ACTIVE = RequestParameter.withLengthCheck("Is Active");
public static final RequestParameter NAME = RequestParameter.withLengthCheck("Name");
...
/** Ensure user input can build a {@link Member}.  */
protected void validateUserInput() {
  try {
    ModelFromRequest builder = new ModelFromRequest(getRequestParser());
    fMember = builder.build(Member.class, MEMBER_ID, NAME, IS_ACTIVE);
  }
  catch (ModelCtorException ex){
    addError(ex);
  }    
}
...
private Member fMember;
The first item passed to build is a class literal denoting the kind of Model Object to be built. The remaining arguments to build represent the data (or pointers to the data) to be passed to the Model Object's constructor. This method takes a sequence parameter, so any number of parameters can be passed. It's important to note that the build method takes either RequestParameter objects, or any Java object. If a RequestParameter is passed, however, then the underlying param is first translated into an appropriate base object such as Integer or BigDecimal before being passed to the Model Object constructor. In the above example, the Model Object constructor has the form
public Member (Id aId, String aMemberName, Boolean aIsActive) ... {
  ...
}
That is, it does not take any RequestParameter objects as params. Rather, the underlying data is translated into the desired target objects as required. In the above example, the various RequestParameter objects passed to the build method are translated internally by WEB4J into an Id, String, and Boolean, corresponding to the declared parameters of the Model Object constructor.

There are numerous examples of building Model Objects in this way in the example application:

Populating Forms

All you have to do to populate a form with data is wrap it in a Populate tag. In other web app frameworks, you cannot implement forms using the familiar controls already defined by HTML. Instead, you need to learn and use a set of custom tags which in effect replace regular HTML controls. In WEB4J, that's not necessary.

There are two use cases for the Populate tag: when there's no editing of an existing record, as in

<w:populate>
  ..form body...
</w:populate>
and when an existing item is being edited, as in
<w:populate using='some identifier'>
  ..form body...
</w:populate>
This second style requires that control names must match corresponding getXXX methods, using a naming convention. Let's take this example:
<w:populate using='item'>
   ...
   <input name="Message" type="text">
   ....
</w:populate>
This implies that the item object has a public method named getMessage() whose return value can be used to populate the control named 'Message'. The return value is formatted into text using Formats.objectToText().

Forms that edit the database must have method=POST, while forms that perform searches or implement reports must have method=GET.

Configuring web.xml

In WEB4J applications, the sole configuration file is the standard web.xml file used with all servlets (the deployment descriptor). The following is a list of all items in web.xml that affect the behavior of WEB4J. For your first WEB4J app, it's useful to scan the list, to get an idea of what's there. After you've created your first app, however, you can usually ignore most of these settings.

If you're running in a Servlet 3.0 environment, and you find it annoying that these framework settings get in the way of your own config in web.xml, then you have the option of re-packaging these settings into a META-INF/web-fragment.xml file, and embedding it yourself into web4j.jar. (See § 8.2 of the Servlet 3.0 spec for more information.)

Items Having No Default Value

Webmaster
Email address of the application admin. No default value for this item. If TroubleTickets are emailed by WEB4J, they will be sent to this address. If emails are sent by WEB4J to other parties, then they will have this as the "from" address. This email address must be in the same domain as the MailServerConfig below.

ImplicitMappingRemoveBasePackage
Example value: 'com.blah' - do not include any trailing dot. No default value for this item. By default, the RequestParserImpl class will map Action classes implicitly to a specific URI. This specific URI is constructed by taking the Action class name, and removing the 'base' package, defined by this setting.

Example: given the settings in the example application, the class:

hirondelle.fish.main.home.HomePageAction
is implicitly mapped to the URI:
/main/home/HomePageAction
by removing 'hirondelle.fish.' from the (slightly modified) result of Class.getName().

Implicit mapping can always be overridden by specifying in the Action a conventional field named EXPLICIT_URI_MAPPING, of the form:

public static final String EXPLICIT_URI_MAPPING = "/main/Blah";

Items Having A Default Value

MailServerConfig
Controls how web4j will send emails on the application's behalf. Default value: NONE. Used to send trouble-ticket emails. Name-value pairs of N properties to be used when connecting to the outgoing mail server. Each name-value pair must appear on a separate line. Example:

            mail.host = smtp1.blah.net
            mail.smtp.port = 465
If the special value "NONE" is supplied, then web4j will not send any emails.

MailServerCredentials
The user name and password for the outgoing mail server, separated by a pipe character '|'. Default value: NONE. If the special value "NONE" is supplied, then no credentials will be passed to the mail server (which, in some cases, can still work).

LoggingDirectory
Location for logging file. Default value: NONE. Example value: C:\log\fish\. Used by LoggingConfigImpl. Must end with a directory separator. Special value of 'NONE' will disable LoggingConfigImpl, such that it will not perform any logging config in code.

LoggingLevels
Logging levels for various loggers. Used by LoggingConfigImpl. Comma-separated list of items, in the same format as JDK logging.properties files:

com.wildebeest.myapp.level=INFO, hirondelle.web4j.level=FINE
It's recommended to include hirondelle.web4j.level, to allow logging by WEB4J classes. The default value for this item is:
hirondelle.web4j.level=CONFIG
The special value 'NONE' is also allowed for this item.

TroubleTicketMailingList
Comma-delimited list of one or more email addresses to be notified when a problem occurs. If TroubleTickets are emailed, they will be sent to these addresses. Default value 'NONE'.

MinimumIntervalBetweenTroubleTickets
Time interval in minutes. Suggested value: 30. Default value: 30.
TroubleTickets are expected to be rare. It's possible, however, that TroubleTickets could be generated in large numbers in a short period of time. For example, if the database connection is lost, then a trouble ticket is generated with almost every request. This parameter is used to throttle down the sending of such emails.

PoorPerformanceThreshold
Time interval in seconds. Suggested value: 20. Default value: 20.
If any request processed by the Controller takes more than this number of seconds, then an email will be sent to the configured Webmaster. The email will state the number of milliseconds taken to process the request. Often, poor performance is caused by degradation not in the web application itself, but rather in the environment in which it's running.

MaxHttpRequestSize
Suggested value: 51200. Minimum value: 1000. Default value: 51200.
Maximum size in bytes of HTTP requests carrying no uploaded files. If such a request has a total size in excess of this value, then it will be discarded by WEB4J, and will not be made available to the application. This is intended only for requests whose unusually large size identify them clearly as hack requests.

MaxFileUploadRequestSize
Suggested value: 1048576. Minimum value: 1000. Default value: 51200.
Maximum size in bytes of HTTP requests carrying uploaded files. If such a request has a total size in excess of this value, then it will be discarded by WEB4J, and will not be made available to the application. This is intended only for requests whose unusually large size identify them clearly as hack requests.

MaxRequestParamValueSize
Suggested value: 51200. Minimum value: 1000. Default: 51200.
Maximum size in bytes of HTTP request parameter values. This maximum is applied as a hard validation by ApplicationFirewallImpl, and only against items created using the RequestParameter.withLengthCheck(String) factory method. This is meant as a kind of default sanity check on parameter values, when no other (more stringent) means of performing hard validation is suitable.

SpamDetectionInFirewall
Permitted values: ON and OFF. Default value: OFF. When ON, then the default implementation of ApplicationFirewall will use the configured SpamDetector to check the values of all request parameters. If spam is detected, then an unpolished response will result.

FullyValidateFileUploads
Permitted values: ON and OFF. Default value: OFF. Controls the behavior of ApplicationFirewallImpl for file upload requests. When ON, then ApplicationFirewallImpl will treat file upload requests the same as any other request, and will check request parameter values in the usual way. When OFF, then it will not perform such validation for file upload requests.

The problem addressed here is that file upload requests are fundamentally different from regular requests. In particular, access to request parameters is possible only if the file upload request has been 'wrapped' in some way, such that request parameters can be read in the usual way. This setting should be ON only if such request wrapping has been performed. Otherwise, it should be OFF.

AllowStringAsBuildingBlock
Permitted values: YES and NO. Default value: NO. When YES, then the default implementation of ConvertParam will allow String as a building block object, along with Integer, Date, and so on. When set to NO, you will not be able to pass a String to any Model Object constructor, and you will need to use SafeText instead. The idea here is that using SafeText is preferable to String. Since String does not escape special characters, it's vulnerable to Cross-Site Scripting attacks.

UntrustedProxyForUserId
Default value: empty String. Lists all URLs having a Data Ownership Constraint implemented with an untrusted identifier. The syntax of this entry is described by UntrustedProxyForUserIdImpl.

CharacterEncoding
Character encoding used in the application. Default value is 'UTF-8'. This setting MUST match the character encoding used by the database. (Databases often use the term 'character set' for character encoding.) Character encodings define how bytes are translated into text. For more information, see the this link.

This setting will be used by the Controller to process requests. In addition, it will be used to create an application scope attribute named web4j_key_for_character_encoding. It's highly recommended that the value of this attribute be placed in template JSPs, so that all served pages will explicitly state the desired character encoding to be used by the browser. (This also increases security.)

DefaultLocale
Default Locale used by the application. Default value: en. This setting allows independence of the server JRE's default Locale, and of HTTP headers. See LocaleSource for information on how a Locale is derived from an underlying request. Example values : en, fr. See also: java.util.Locale for possible values of Locale identifiers, and Util.buildLocale(String).

DefaultUserTimeZone
Default TimeZone used by the application. Default value: GMT. This setting allows independence of the server JRE's default TimeZone, and of HTTP headers. See TimeZoneSource for information on how a TimeZone is derived from an underlying request. Example values: 'Canada/Montreal', 'UTC'. See also: java.util.TimeZone for possible values of TimeZone identifiers, and LocaleSource.

TimeZoneHint
Example values: 'NONE', 'Canada/Montreal', 'UTC'. Default value: 'NONE'. See java.util.TimeZone for possible values of TimeZone identifiers. This item is passed as hint to the database driver, for all java.util.Date columns encountered by the WEB4J data layer. Overrides the JRE default time zone. See: PreparedStatement.setTimestamp(int, Timestamp, Calendar) and ResultSet.getTimestamp(int, Calendar). The special value 'NONE' is used when the default JRE time zone suffices, or when the database already stores time zone information in the desired manner.

DecimalSeparator
Comma-separated list of names of decimal separator characters to be accepted during user input of numbers having a fractional part. Permitted values : 'PERIOD', 'COMMA', 'PERIOD,COMMA'. Default value: 'PERIOD'. Used for conversions to BigDecimal and Decimal values. This item is not affected by Locale.

BigDecimalDisplayFormat
Format for display of BigDecimal and Decimal values in reports. Default value: #,##0.00. See java.text.DecimalFormat for possible values. This pattern is non-localized. It's combined with a Locale to produce a locale-sensitive format.

BooleanTrueDisplayFormat
The text for presenting boolean 'true' values in a report. Example values: Yes, True, Oui.

The HMTL spec states that form controls can appear outside of a form. One may specify a checkbox, as in:

<![CDATA[
  <input type='checkbox' name='true' value='true' checked readonly notab>
]]>

The above is in fact the default value for this item. (CDATA is needed here because of the presence of special HTML characters.) Note that JSTL's c:out tag will need escapeXml set to false, in order to render such items as desired.

Another option is to use an IMG tag containing the desired image.

BooleanFalseDisplayFormat
The text for presenting boolean 'false' values in a report. Example values: No, False, Non.

See BooleanTrueDisplayFormat for more information on this style.

The default value for this item is:

<![CDATA[
  <input type='checkbox' name='false' value='false' checked readonly notab>
]]>

EmptyOrNullDisplayFormat
The text to display when an item has no value. Default value: '-' (a single hyphen). Many browsers do not render empty TABLE cells very well. The CSS 'empty-cells' property is the recommended way of controlling this behavior. As an alternative, this setting may be used to specify text representing empty or null items in a report. Example values: '-', '...'

IntegerDisplayFormat
The format for presenting integer quantities. Default value: #,###. See java.text.DecimalFormat for possible values. This pattern is non-localized. It's combined with a Locale to produce a locale-sensitive format.

IgnorableParamValue
Any parameter which takes this value will be ignored, by having its value replaced by an empty string, in RequestParser. Default value is an empty String.

Intended solely for SELECT tags, which show a list of items. Even when no selection is made by the user, most browsers will simply pass the first item in the SELECT as the parameter value. If such behavior is undesired, then specify this parameter, and put the IgnorableParamValue as the first item in the SELECT tag.

Typically, one would place unusual characters in IgnorableParamValue, to greatly reduce the likelihood of conflict with regular user input. You may specify an empty value as well, which corresponds to a blank initial value in the list.

In a multilingual application, this setting must not contain translatable text. For example, '----' or an empty string would be an appropriate value for a multilingual application.

IsSQLPrecompilationAttempted
Value: (true | false). Default value: true. If true, then upon startup WEB4J will attempt to precompile SQL statements appearing in the .sql file(s), by calling Connection.prepareStatement(String), and detecting if an SQLException is thrown. Any detected errors are logged as SEVERE.

It's important to note that some drivers/databases will NOT throw an SQLException even when the text is syntactically invalid. (This can be determined with a simple test.) Hence, this is always an "attempt" to precompile.

The idea here is to be defensive, and to attempt to detect errors as soon as possible. In addition, this service is very useful when porting an application to a new database: one may start with the "old" statements, and begin by seeing if they compile versus the "new" database. Recommended value is 'true', unless certain the driver/database does not support precompilation in this manner. See SqlId for more information.

MaxRows
Upper limit on the number of rows to be returned by any SELECT statement. Default value: 300. Serves as a defensive safety measure, to help avoid operations which may take an excessively long time. Set value to '0' to turn off this limit. This value is passed to Statement.setMaxRows(int).

FetchSize
Hint sent to JDBC driver regarding the number of rows to be returned when more rows are needed. Default size: 25. Applies only to SELECT statements. Set value to '0' to turn off this hint. This value is passed to Statement.setFetchSize(int).

HasAutoGeneratedKeys
Value: (true | false), ignores case. Default value: false. Indicates if the database and driver support the method Connection.prepareStatement(SqlText, Statement.RETURN_GENERATED_KEYS).

ErrorCodeForDuplicateKey
The error code(s) returned by SQLException.getErrorCode() when trying to add a duplicate record. Default value: 1 (Oracle's value). If you wish to treat more than one error code as being related to duplicate key constraints, you may specify a comma-delimited list of integers. If this error code is unknown for a given database, you may determine its value by exercising your application, and deliberately causing the error to happen. When web4j detects these error codes, then it will throw a DuplicateException, instead of a DAOException.

Some confirmed values for this item:

Oracle 1 (default)
MySQL 1062
JavaDB/Derby -1

Some unconfirmed values for this item:

SqlServer 2627
DB2 -803
Sybase 2601

ErrorCodeForForeignKey
The integer error code(s) returned by SQLException.getErrorCode() when a foreign key constraint fails. If you wish to treat more than one error code as being related to foreign key constraints, you may specify a comma-delimited list of integers. If this error code is unknown for a given database, you may determine its value by exercising the example application, and deliberately causing the error to happen. When web4j detects these error codes, then it will throw a ForeignKeyException, instead of a DAOException.

Some example values (please confirm using your database documentation):

Oracle 2291 (default)
MySQL 1216,1217,1451,1452
JavaDB/Derby 23503

SqlFetcherDefaultTxIsolationLevel
The default transaction isolation level used for queries. Default value: DATABASE_DEFAULT. The permitted values are:

DATABASE_DEFAULT (recommended)
SERIALIZABLE
REPEATABLE_READ
READ_COMMITTED
READ_UNCOMMITTED (not recommended !)

For more information, see TxIsolationLevel, and java.sql.Connection. It's important to confirm support for these levels with your database documentation.

SqlEditorDefaultTxIsolationLevel
The default transaction isolation level used for edit operations. Default value: DATABASE_DEFAULT. The permitted values are:

DATABASE_DEFAULT (recommended)
SERIALIZABLE
REPEATABLE_READ
READ_COMMITTED
READ_UNCOMMITTED (not recommended !)

For more information, see TxIsolationLevel, and java.sql.Connection. It's important to confirm support for these levels with your database documentation.

DateTimeFormatForPassingParamsToDb
Formats used when passing DateTime objects as parameters to SQL statements.
Default value: YYYY-MM-DD^hh:mm:ss^YYYY-MM-DD hh:mm:ss

Three formats must be specified here, corresponding to 3 common cases (in this order):

The format Strings are those defined by DateTime. The 3 format Strings are separated by a ^ character.

If a DateTime parameter to an SQL statement needs to be passed in a format which is not specified here, simply format the DateTime in your code as desired, and pass the result to the database as a fully formatted String, instead of a DateTime.

RedirectWelcomeFile
Simple servlet for redirecting directory requests to the home page Action. The source code underlying this item is provided with the example application. It doesn't form part of the WEB4J core. Example value appropriate for development:

http://localhost:8080/fish/main/home/HomePageAction.show

Configuring Database Items in web.xml

When a database setting has a single value applicable across all of your databases, you simply specify that single value, as usual. When a database setting takes different values for different databases, you define the various values with the following syntax:
100~Translation=200~AccessControl=300
The '~' character is used as a separator. Here, the first entry '100' represents the setting for the default database. It also represents the setting for all other databases, unless an override value is provided by one of the 'name=value' pairs. Thus, the above represents:

Database NameValue
[the default db]100
Translation200
AccessControl300
[any other db name]100

There is a single exception - the TimeZoneHint setting can only take a single value, to be applied across all databases. (The reason for this unfortunate exception is simply that changing it would have a large number of ripple effects, and would represent too much pain for too little gain.)

This syntax is particularly useful for applications which use not only multiple databases, but also multiple database vendors (MySQL and PostgreSQL in the same application, for example).

Configuring Implementation Classes

You control the behavior of WEB4J by providing implementations of various interfaces, where needed. Most of these interfaces come with reasonable default implementations. For the remainder, no reasonable default behavior is possible, so you will need to provide your own implementations. (Implementations in the example applications can be used as a guide.)

The BuildImpl class controls the lookup of implementations. BuildImpl makes a distinction between three kinds of implementations, described below.

Default Implementations

When they exist, any default implementation defined by WEB4J will be used if you take no action to override it. That is, if you do nothing, then default implementations will be used, whenever they exist.

Conventional-Name Implementations

Every interface is paired with a conventional implementation name. This conventional name exists only to reduce configuration effort. If you need to provide your own implementation, you can simply use this conventional package and class name. In that case, WEB4J will find this class upon startup, and register it internally as your desired implementation. The whole idea is that you just implement the class using a given name and package, without needing to do any further configuration of the class name in some text file.

The conventional package name is always 'hirondelle.web4j.config', while the conventional class name varies. Please see below for a complete listing.

Arbitrary-Name Implementations

If you need to provide an implementation, and for some reason using the above conventional-name style is unsuitable, then you must configure the class name in web.xml. Here is an example of such a configuration:
<init-param>
  <param-name>ImplementationFor.hirondelle.web4j.ApplicationInfo</param-name>
  <param-value>com.xyz.MyAppInfo</param-value>
  <description>
    Package-qualified name of class describing simple, 
    high level information about this application. 
  </description>
</init-param> 
That is, for the interface hirondelle.web4j.ApplicationInfo, your implementation class is com.xyz.MyAppInfo.

Upon startup, the BuildImpl class searches for implementations in the following order:

  1. Arbitrary-Name implementation
  2. Conventional-Name implementation
  3. Default implementation (if it exists)

See BuildImpl for further information.

Listing of Interfaces and Conventional Names

Here's a listing of all interfaces used by WEB4J, along with conventional class names, and either a default or an example implementation. The package for conventional class names is always 'hirondelle.web4j.config'.

Question Interface Conventional Impl Name, in hirondelle.web4j.config Default/Example Implementation
What is the application's name, version, build date, and so on? ApplicationInfo AppInfo example
What tasks need to be performed during startup? StartupTasks Startup example
What tasks need to be performed after a user successfully logs in? LoginTasks Login example
What Action is related to each request? RequestParser (an ABC) RequestToAction RequestParserImpl
Which requests should be treated as malicious attacks? ApplicationFirewall AppFirewall ApplicationFirewallImpl
Which requests use untrusted proxies for the user id? UntrustedProxyForUserId OwnerFirewall UntrustedProxyForUserIdImpl
How is spam distinguished from regular user input? SpamDetector SpamDetect SpamDetectorImpl
How is a request param translated into a given target type? ConvertParam ConvertParams ConvertParamImpl
How does the application respond when a low level conversion error takes place when parsing user input? ConvertParamError ConvertParamErrorImpl example
What characters are permitted for text input fields? PermittedCharacters PermittedChars PermittedCharactersImpl
How are dates and times formatted and parsed? DateConverter DateConverterImpl example
How is a Locale derived from the request? LocaleSource LocaleSrc LocaleSourceImpl
How is the system clock defined? TimeSource TimeSrc TimeSourceImpl
How is a TimeZone derived from the request? TimeZoneSource TimeZoneSrc TimeZoneSourceImpl
What is the translation of this text, for a given Locale? Translator TranslatorImpl example
How does the application obtain a database Connection? ConnectionSource ConnectionSrc example
How is a ResultSet column translated into a given target type? ConvertColumn ColToObject ConvertColumnImpl
How should an email be sent when a problem occurs? Emailer Email EmailerImpl
How should the logging system be configured? LoggingConfig LogConfig LoggingConfigImpl

Multilingual Apps

Language

WEB4J's translation package has a number of items to help you implement multilingual applications. The most important item is the Translator interface. This interface will usually be implemented using either ResourceBundles or a database. In addition, this interface works closely with your implementation of the LocaleSource interface, which defines the end user's preferred language.

Base Text

Any item that is to be translated is referred to by Translator as 'base text'. Base text always appears in the base language of development. For example, a team coding in English would treat English as the base language, and their base text items would always be in English. It's important to understand that this base text comes in two distinct forms: The coder key style exists simply because it's sometimes desirable to refer to a translatable item indirectly.

Natural-language base text and coder-key base text differ slightly in their behavior. To illustrate this difference, let's take an example of an app that is developed in English and presented to the user in two languages, English and French. In this case, English is the base language. When the user's language preference is English, any natural language base text such as 'Add/Edit' can be presented to the end user as is, without further processing. However, if a coder key such as 'add.edit.button' is used instead, then such text is always going to need further processing, even when the user's preferred language is English. One might think of coder keys as being in a kind of special language known only to the programmer. Thus, coder keys always need translation, even into the base language of the application.

Minimally Invasive

The translation tools in WEB4J are minimally invasive. From the point of view of the programmer, the implementation of a multilingual app looks almost the same as that of a single language app. In particular, your Java code is exactly the same in these cases: Model Objects, Actions, and DAOs are unchanged. The only differences are in the presentation layer, in the JSP. And even in the JSP the changes are minimal: your markup will appear much the same as regular, single language markup.

The following tags implement translation in your JSPs:

Using Your App to Assist in Translation

If you store your translations in a database, then you can easily use your implementation of Translator to assist your translation efforts. During development (or even production, if desired), your implementation of Translator can simply gather and record items that are in need of translation. Thus, simply by fully exercising the application, all items that need translation can be easily collected. Once all items needing translation have been placed in the database, screens and Actions can be created to add their translations. This technique is fully illustrated in the Fish & Chips Club example application.

Storing Translations in Application Scope

In a multilingual application, it's probably best to pull in all translations upon startup, and store them in application scope. This can be done as part of your StartupTasks implementation.

Storing translations in application scope allows you to avoid the cost of repeated database lookups for data which rarely changes.

Using One JSP per Language

By default, multilingual apps will use the same JSP to render pages in multiple languages, using the custom tags mentioned above. However, some organizations will still want to instead use a different JSP for each language. (This is possible, but not recommended, since it requires so much repetition of markup.)

Such a design is implemented by subclassing the Controller. By providing an override of its swapResponsePage method, you can alter the default mechanism used by WEB4J to find the ResponsePage returned by your Actions. For example, a page referenced as 'Blah.jsp' in your Action can be changed by your custom Controller, late in processing, to actually refer to some other item such as 'Blah_en.jsp' or 'Blah_fr.jsp', according to the given Locale. (See LocaleSource for more information on Locales.)

The Visit feature of the Fish & Chips Club example application can be used to demonstrate this technique. To exercise it, you simply need to change the servlet-class entry in web.xml, to point to the Visit feature's CustomController.

Encoding Issues

All data is ultimately represented as bytes. For textual data, a technique is always required for translating bytes into text. Those techniques are called encodings or character sets.

When the same encoding is not used with 100% consistency across all parts of your application, then your users will see annoying '?' (or similar) symbols where regular text should appear. Such problems are common, and they're very annoying.

The primary defense here is to use the UTF-8 encoding. UTF-8 can represent almost any kind of text. In a perfect world, all tools would default to UTF-8. In practice, the problem is that many tools default to non-UTF-8 encodings. For example:

Many items can have an associated encoding:

Page Character Encoding

When you save a JSP file (or any text file, for that matter) during development, an encoding is always used. In the JSP spec, this is called the page character encoding. You should be using an editor which allows you to apply the desired encoding when you save your JSP files.

One of the problems with encodings is that they're hard to determine precisely simply by examining the raw bytes. Hence, various means have been concocted to get around the fact that encodings are not stated explicitly within the bytes themselves. For example, the first line of an XML file can appear as:

<?xml version="1.0" encoding="UTF-8"?>
But here's the catch - if this doesn't match the encoding with which the file was really saved, then it's wrong. You have to manually ensure that the stated encoding matches the actual one. The stated encoding above says "I saved this file using UTF-8". If that's not true, then the statement is false, and problems will result.

It's natural to regard these encoding settings as specifying the encoding. But that's false; it's not a specification of the encoding, it's a (possibly incorrect) attempt to communicate the encoding. There's a big difference. (In practice, there are many such instances of mismatch between the stated and actual encoding.)

The JSP specification allows you to communicate the page character encoding in various ways. For JSP files that use standard (non-XML) syntax, the logic used to determine the intended page character encoding has the following order of evaluation:

1. JSP config in web.xml:
  <jsp-config>
    <jsp-property-group>
     <url-pattern>/*</url-pattern>    
     <page-encoding>UTF-8</page-encoding>
    </jsp-property-group>
  </jsp-config>
2. <%@ page pageEncoding="UTF-8"%> 
3. <%@ page contentType="text/html;charset=UTF-8"%> 
4. Default to ISO-8859-1
Tag files are a bit different. They aren't as flexible as JSPs, and you can't state the intended encoding as part of jsp-config in web.xml. With tag files, the order of evaluation is simply:
1. <%@ tag pageEncoding="UTF-8"%>
2. Default to ISO-8859-1

Serving JSON, XML, and so on

Web4j lets you serve any kind of text you wish - HTML, JSON, XML, plain text, CSV, and so on. Indeed, JSP's were designed for that very purpose. The only difference when serving structured data is that the data is usually already formatted into the desired String in code. So, there's no reason to 'mark up' the data in a JSP, as there is with HTML. The data is already in its final form by the time it reaches the JSP.

The only job of the JSP, in this case, is to let you control header information. Even though you are serving structured data, that data is still being passed around using HTTP, and every HTTP response should include headers for the content-type and encoding.

The Predictions example app has a simple example of serving JSON data. It's implemented by:

  1. a JSP file named /pub/view.json in the app's root directory
  2. a servlet-mapping entry in web.xml, which tells the container that .json files are to be treated as a JSP
  3. an action class in the hirondelle.predict.pub.json package
The first two steps are done once per application. Once they are set up, each feature that serves JSON will only need to do the final step of defining the Action.

Of course, the exact same technique can be used to serve data in any other format.

Logging

Your application can use any logging tool you wish. (The JDK logging tools are usually adequate.)

The WEB4J framework classes (and the example apps) use JDK logging. It's recommended that you configure JDK logging to view WEB4J's logging output, by configuring the JDK logging tool. You might get away with configuring logging.properties, but in a shared environment on a server, that will likely be inadequate: the Handlers (output files) defined in logging.properties are per JRE, not per web application. That means that different web apps will output to the same logging file.

To address that problem, the LoggingConfig interface and its default LoggingConfigImpl have been provided to allow a programmatic way of configuring logging (if you will permit that somewhat contradictory expression). This programmatic style is per class loader, not per JRE, so its Handlers (output files) are not shared between applications.

The default LoggingConfigImpl has the interesting property of allowing you to log records with a time stamp derived from your application's fake system clock. This is because it uses a TimeSource.

Start-up Tasks and Code Tables

To perform tasks once upon startup, include them in your implementation of StartupTasks. In the Fish example app, the implementation loads both code tables and translations into memory.

The most common start-up task involves code tables. A code table is an enumeration or list of related items in the problem domain. For example, these kinds of items might be implemented as code tables:

In a web application, such items are often presented to the user in a <SELECT> control.

The Code class is provided to help you implement code tables. You are not obliged to use it.

In a multilingual application backed by a database, another common startup task is to place all translations into application scope. Thus, your Translator implementation can access in-memory data, instead of continually querying the database for these relatively unimportant items which rarely change.

The single method of the StartupTasks interface has two arguments. The second argument is the name of a database, as you have defined in your implementation of the ConnectionSource interface. Upon startup, WEB4J attempts to connect to each of your databases. As each attempt succeeds, the framework will pass the database name to your StartupTasks implementation.

Your implementation should follow this form:

public void startApplication(ServletConfig aConfig, String aDbName) throws DAOException {
  if (!Util.textHasContent(aDbName)){
    //tasks not related to any db - this is called first
  }
  else if (ConnectionSrc.DEFAULT.equals(aDbName)){
    //tasks related to this db
  }
  else if (ConnectionSrc.TRANSLATION.equals(aDbName)){
    //tasks related to this db
  }
}

What happens if one or more databases are down when your app starts? If that occurs, the framework will try to connect to any remaining 'bad' databases, with each incoming request. If a connection is found, then, as usual, the framework will pass the database name to your StartupTasks implementation.

File Uploads

Version 3.0 of the Servlet API (published January, 2010) has good support for file uploads. If it's available to you, you should use it to access uploaded binary data.

Servlet API version 2.5 (upon which web4j is still based) has surprisingly poor support for file uploads. When using Servlet 2.5, third-party tools such as the Apache Commons FileUpload tool are needed. This technique uses a filter and a request wrapper, and is demonstrated in the file upload feature of the Fish & Chips Club example app.

Regardless of which technique you use to access the uploaded data, it's made available to your Action as an InputStream. Web4j includes InputStream as one of its base building block classes, for constructing Model Objects, and for passing data into and out of the database.

When an InputStream is passed to the database, setBinaryStream(int, InputStream) is used. This method is available in Java 6+, and may not be supported by older drivers. For example, Tomcat 6 has an older connection pool implementation, and it doesn't support the above method, while Tomcat 7 has no such problem. Note that this issue is related to the level of support of JDBC, not of the Servlet API.

Form controls: browsers don't seem to respect the value attribute of form input controls. This makes it impossible for web4j (or any other tool) to dynamically pre-populate the selected file. Thus, any update operations on binary data will usually need some extra care. (The simplest implementations would provide only add and delete operations, with no update operation as such.)

The RequestParser class is able to translate RequestParameter objects into building block classes. The one exception is InputStream. When you need to build a Model Object that takes an InputStream, you will have to pass the InputStream directly, without going through an intermediate RequestParameter. This minor nuisance will be removed when web4j moves to Servlet 3.0 as its base API.

Paging

When a listing contains a large number of records, it's often best to provide end users with a paging mechanism. WEB4J provides a simple way to implement such a paging mechanism, using (in part) a custom JSP tag called Pager.

The Pager tag lets you emit links corresponding to the first, next, or previous page. The links don't perform the actual 'subsetting' of the data. That's done elsewhere, in one of 3 possible places:

Please see the Pager javadoc for further information. As well, the Fish & Chips Club example application implements paging in its Discussion feature, where users post comments to a simple message board.

Note as well that there is a MaxRows setting in web.xml which controls the maximum number of records returned by a SELECT.

Miscellaneous Tags

Tags not mentioned previously in other sections of this guide include:

Double-Submit Issue

Sometimes a user may submit the same form twice. The user may click twice rapidly on the submit button merely by mistake. Or, they may click a second time in frustration due to a slow response from the server.

In the following cases, such double-submits are not an issue:

Typically, the only case in which double-submit is an issue is the case of 'double-add', in which a form with method='POST' is used to add a new record. Web4j has no explicit mechanism for dealing with the double-add problem. If you feel this is an issue for your application, here are some ideas for defensive measures:

Using the Database Layer Outside of a Servlet

It's not hard to use web4j's database layer outside of a servlet context, in any kind of application you want - a command-line app, a GUI app, and so on. In addition, you can even reuse the same DAO's and Model Objects you created for your web app.

When you use the database layer outside of a servlet context, you need to supply an implementation of ConnectionSource (as usual), and two collections. The collections define two things:

Here's an example:

//config settings 
Map<String, String> settings = new LinkedHashMap<String, String>();
//theses settings override the default settings
settings.put("Webmaster", "blah");
settings.put("ImplicitMappingRemoveBasePackage", "blah");
//no real need to use SafeText for a command line app
settings.put("AllowStringAsBuildingBlock", "true"); 
//optional logging settings
settings.put("LoggingDirectory", "C:\\log\\blah\\"); 
settings.put("LoggingLevels", "hirondelle.web4j.level=FINEST");
settings.put("HasAutoGeneratedKeys", "true");
//override any other default settings here

//fetch the raw SQL statement data 
//the FindSql class is defined by your app; it scans 
//the local file system for .sql files
FindSql findSql = new FindSql(new File(System.getProperty("user.dir") + "\\bin\\"));
List<String> rawSqls = findSql.scan();

//pass the config data and sql statements to this single call
Db.initStandalone(settings, rawSqls);

//now you can use the database layer in the usual way
To specify your implementation of the ConnectionSource interface, you just make use of the same mechanisms as usual. Most will simply place their implementation in their app, using the conventional package name hirondelle.web4j.config, and the conventional class name ConnectionSrc. Alternatively, you can use the 'settings' map to specify the implementation (in the same way you would specify it in web.xml), using the name
ImplementationFor.hirondelle.web4j.database.ConnectionSource
See above for more information.

If you use the Reports class, then you may also need to supply an implementation for a second interface: DateConverter.

In the above example, a FindSql class is referenced. You will need to create such a class, to find the SQL statements defined by your app. Typically, that class will need to scan the file system under a specific root directory (or perhaps in a jar), find all the .sql files, and read in their content using the correct encoding. Each file will be represented as a single String in the list passed to the Db class. In the context of a web app, this is performed by the framework upon startup. The reason this is possible for a web app is that the file and directory structure is well-defined for web apps, and the framework knows where to look for things. Outside of the servlet context, however, the location of that data becomes ambiguous, so your app needs to supply it explicitly.

Using the above code, the initialization tasks performed by Db.initStandalone are not as complete as those performed in a servlet context. In short, the validation and logging tasks are skipped. (The reason is the same as above: the location of items is ambiguous). For example, the SqlId's used in your code are not matched one-to-one with the corresponding names in your .sql files. In the absence of such validations, any errors, if present, will appear at run-time instead of at start-up.

Utilities

WEB4J includes a util package containing the following:

Unit Testing

Unit tests verify the behavior of individual classes. They usually increase your confidence in the correctness of your code. JUnit is often used to implement unit tests.

Different organizations take different approaches to unit testing. Some aggressively unit test all classes, while others prefer to focus on areas most likely to contain bugs. (Many casual applications don't include any unit tests at all.)

For an illustration of unit testing, please see the following packages in the Fish & Chips Club example application:

Please note that this is just an example of one way of doing unit testing. You are free to implement unit tests in your app any way you want.

To run the tests in the Fish example app, perform the following:

WEB4J helps you implement unit tests by providing test doubles. Using the terminology of Gerard Meszaros and Martin Fowler, these test doubles are fake objects (not mock objects). Such fake objects serve to mimic the real runtime environment of a class, using implementations you can control as desired.

The example application includes source code for the following fake objects:

These test doubles implement the methods most likely needed during unit testing. If, for some reason, these fake implementations don't allow you to perform certain tests, then you can simply edit them as desired. (It's not expected that this will be commonly required.)

These fake objects make a number of simplifying assumptions. The most important is that only a single thread of execution is assumed.

Also included is the source code for some utilities you may find useful:

Where To Place Unit Tests

If you follow the package-by-feature style, it's simplest to place unit tests in the same directory as the class being tested. Thus, the benefits of using package-by-feature remain intact.

Initializing WEB4J Classes

When your app starts up during regular operation, the Controller.init(ServletConfig) method is always called to initialize WEB4J with items specific to your application. When running tests, the same initialization usually needs to be called, but it requires a fake ServletConfig. The fake ServletConfig will often use a fake implementation of ConnectionSource as well.

See the TESTAll.initControllerIfNeeded() method in the example app for an illustration.

There are also 2 methods in BuildImpl which were created specifically for creating a proper test environment:

These methods allow you to swap in various implementations as desired, for the various interfaces required by WEB4J.

Unit Testing Model Objects

Model Objects encapsulate and validate data. Most unit tests for Model Objects are directed towards their validation logic. If you follow the recommended style and implement your Model Objects as immutables, then that validation logic will be implemented in the constructor, and most of your unit tests will simply throw various data sets at the constructor.

Unit Testing DAOs

To unit test Data Access Objects (DAOs), you will need to interact with the database. Various options exist for dealing with any records created as a side effect of unit tests: The last option of using a transaction is problematic when using WEB4J's data layer. Since WEB4J's data layer often (but not always) uses internally created connections, rolling back transactions after tests are finished is often not possible. As a workaround, you may be able to wrap operations in a distributed transaction, instead of an ordinary local transaction.

DAOs and Ordering Convention Errors

The WEB4J data layer uses ordering conventions. If those conventions aren't followed, then errors will result. In some cases, having an incorrect order will result in obvious errors that are easy to find. In other cases, however, such errors may not be obvious.

It's useful to test explicitly for ordering errors. In general, you will find more ordering errors if you test with data that clearly distinguishes between different fields/columns.

To illustrate, here's an example using two fields/columns in Member objects (taken from the example app):

 DispositionIsActive
Java TypeIdBoolean
Column TypeMediumIntTinyInt
Similar Values (bad)11
Distinct Values (good)14

If there is a permutation error involving Disposition and IsActive, and if you use similar values for these fields during testing, then the permutation error will not be detected. If you use distinct values, then any permutation error will be detected.

So, the guideline is to make sure your test data distinguishes clearly between fields/columns. It's likely best to avoid common, generic values such as 0, 1, nulls, and empty Strings.

Fake DAOs

A fake DAO has the same behavior as a real DAO, but stores underlying data only in an in-memory data structure (often a HashMap), not in a database. If desired, one can define fake DAOs for use with Actions. Using fake DAOs for unit testing Actions has these main benefits: It's likely prudent to unit test your fake DAOs in addition to the real ones.

Instead of fake DAOs, you might also consider a fake relational database.

Unit Testing Actions

Actions serve as the entry point into a feature. In addition, they use all the other parts of a feature's implementation, either directly or indirectly. There are two points to keep in mind when unit testing Actions:

Fake System Clock

For many applications, it's often desirable to test different "processing dates". Instead of changing the real system clock directly, however, it's usually best to use a fake system clock. The important point here is to avoid direct calls to these common items:

WEB4J provides the TimeSource interface to help you implement a fake system clock. When you define such a TimeSource, you are both defining a fake system clock for your application, and you are sharing your fake system clock with WEB4J framework classes. Thus, your application and the framework can use exactly the same clock, and will remain synchronized.

In addition, this same mechanism allows the default logging mechanism to timestamp records with your fake system clock time. See javapractices.com for an example.

Building New Apps From The Example App

The Fish & Chips Club and Predictions example applications can be used not only as a guide or reference, but as actual starting points for building your applications.

The following is an outline of the recommended steps to follow to build a new application. The general idea is that the example application coexists with your app until it's no longer needed.

Phase 1 - Get Example App Running Against A New Database

1 - Download the latest version of fish.zip. Get it running, using the Getting Started Guide. Make sure the application can be launched and run before proceeding further. Note that .class files are included with fish.zip, so it's not necessary (or recommended) at this stage to compile the source. After each of the remaining steps, it's a good idea to verify that the application remains unbroken for each completed step.

2 - Change the base package/directory from 'hirondelle.fish' to the appropriate value for your new app. The supplied build.xml has an ANT target to help you:

In web.xml, scan for settings which depend on the base package, and update them:

3 - Create databases of the correct name.

There are two ways to update the database name:

The example app uses three databases, so, in the beginning, you will need the same. You can change this later, if needed.

At this stage, databases of the correct name exist, but their tables are temporary; the 'real' tables will be added later.

If MySQL is not available, then the database script will need to be ported to the target database. Although that may sound onerous, it's usually not that much work.

4 - Update the server configuration as needed. The connection pool and security realm info will need to reflect the new database names.

5 - edit hirondelle.config.web4j.AppInfo to reflect the correct information.

At the end of this phase :

Phase 2 - Start Implementing Real Features

Design and create the desired screens and database tables.

New tables should be added beside the existing ones. Do not remove the tables from the example app - not yet. In the beginning, your tables and the tables of the example application will coexist, side by side.

Modify Template.jsp, Footer.jsp, stylesheets, and so on, to create the basic look of your new application. It's often useful to create menu links at this stage as well.

New features (JSPs, code, and .sql files) should be placed beside existing features, in their own packages. Again, don't worry too much about removing existing items until later in development.

Usually, a feature will have these parts :

It's not necessary that all of these parts be created. Use your judgement and sense of taste. For example, a simple report can usually be created without a Model Object or a DAO. (This is demonstrated by the reports in the example application.)

Phase 3 - Remove Unnecessary Items

You should only remove items related to the Fish & Chips Club only when it becomes clear that they will not be needed. If you are unsure, leave it in. It's easy to remove things later, but harder to add them back in.

In most cases, removing a feature reduces to two simple actions : removing a single directory, and removing a menu link from some related template JSP.

Build Your Own Example App

The design of the example app is meant to be representative of a typical application. However, it's likely that its design is not completely appropriate for your needs.

For instance, the example app places user access tables in a database separate from the main business domain. Your app design may call for these items to be placed in one and the same database. Or, it may call for JNDI lookup to be used instead.

Version History

Here are links to the version histories of:

The version numbers of the example apps reflects the version of web4j.jar with which it was built. For example, version 3.0.0.1 of the example app was built with version 3.0.0 of web4j.jar.