Have you ever wondered how a taxi was ordered before the taxi app existed? There were two options: Book a taxi by telephone or take a taxi on the street. But today mobile applications are revolutionizing the taxi industry with innovative technology that offers alternative solutions for both drivers and customers.

If you are planning to build an app like Uber, a fundamental aspect of the Uber application is its pricing strategy. Taxi companies generally increase travel rates when there is more demand than supply. Instead, Uber charges the customer a fee based on traffic density and the time it takes to get from point A to point B.

Following are some of the functionalities of Uber you should know:

    Ease of registration: The option to be able to register through Facebook, Google Plus or the classic way with a phone number and a password. Offering the option to access through a social network allows the application to transform personal information into the application in a matter of seconds. Additionally, it is important to differentiate the users of the drivers when they enter the app.

    Personal profile: Users and drivers can add, edit or delete their personal information at any time through the app.

    Mail models: Notifications via email are very useful to send payment information, trip details, payment confirmation, digital wallet information or to reset the password in case of forgetting.

    Car search function: To be able to assign a taxi as quickly as possible to the customer, Uber integrated a search option for cars that are at the minimum distance in kilometers. This allows the customer to filter only the taxis closest to their location.

    Availability of cars: If the status of a car is active, users can request a ride. Once the car is reserved, its status automatically changes to inactive so the driver does not need to change it manually. For the user, when the path request is accepted, he can no longer change the destination address.

    Rate estimation: Since the business model depends on the trips paid, it is important to include a rate estimation mechanism. In this way, the application can calculate the tariff appropriately based on the standard prices for a particular distance.

    Multiple means of payment: Users have the possibility to pay by credit or debit card through the application. Another option is to transfer money to the Wallet application.

    Ease of location: Integrating Google Maps helps calculate the distance and route between a point A and a point B. It also allows the driver to locate the client and the latter, once the trip accepted, see the location of the driver.

The ease of use and which represents an alternative to traditional taxi services are the most important reasons for the popularity of the taxi app. They have revolutionized an industry that was static for a long time.

In many countries, the taxi industry has been subject to tariffs in the manner imposed by the local government. However, taxi applications create a free market in terms of tariffs and allow customers to customize their journey.

The perpetual debate on exception handling in Java can at best be described as a religious war: On one side, you have the proponents of checked exceptions arguing that callers should always deal with error situations arising in code they call. On the other side stand the followers of unchecked exceptions pointing out that checked exceptions clutter the code and often can’t be handled in immediate clients anyway, so why force it?

As junior engineers, we first sided with the proselytes of checked exceptions, but over the years, and many, many catch blocks later, we have gradually converted to the order of the unchecked. Why? We have come to believe in a simple set of rules for dealing with error situations:

  1. If it makes sense to handle the exception, do so
  2. If you can’t handle it, throw it
  3. If you can’t throw it, wrap it in an unchecked base exception and then throw it

But what about those exceptions that bubble all the way to the top, you ask? For those, we install a last line of defense to ensure error messages are always logged and the user is properly notified.

This article presents yet another framework for exception handling, which extends the enterprise-wide application session facility presented in “Create an Application-Wide User Session for J2EE” (JavaWorld,March 2005). J2EE applications that use this framework will:

  1. Always present meaningful error messages to users
  2. Log unhandled error situations once and only once
  3. Correlate exceptions with unique request IDs in log files for high-precision debugging
  4. Have a powerful, extensible, yet simple strategy for exception handling at all tiers

To forge the framework, we will wield aspect-oriented programming (AOP), design patterns, and code generation using XDoclet.

You will find all the code in Resources along with a sample J2EE application that uses it. The source constitutes a complete framework named Rampart, which was developed for Copenhagen County, Denmark in the context of J2EE-based electronic healthcare records (EHR) applications.

Why do we need common error handling?
During a project’s initial state, significant architecture decisions are made: How will software elements interact? Where will session state reside? What communication protocols will be used? And so on. More often than not, though, these decisions do not include error handling. Thus, some variant on the happy-go-lucky strategy is implemented, and, subsequently, every developer gets to arbitrarily decide how errors are declared, categorized, modeled, and handled. As an engineer, you most likely recognize the results of this “strategy”:

  1. Swollen logs:Every catch block contains a log statement, leading to bloated and redundant log entries caused by polluted source code.
  2. Redundant implementations:The same type of error has different representations, which complicates how it is handled.
  3. Broken encapsulation:Exceptions from other components are declared as part of the method signature, breaking the clear division between interface and implementation.
  4. Noncommittal exception declaration:The method signature is generalized to throw java.lang.Exception. This way, clients are ensured not to have the least clue about the method’s error semantics.

A common excuse for not defining a strategy is that “Java already provides error handling.” This is true, but the language also offers facilities for consistently declaring, signaling, propagating, and responding to errors. The language user is responsible for deciding how these services should be used in an actual project. Several decisions must be made, including:

  1. To check or not to check:Should new exception classes be checked or unchecked?
  2. Error consumers:Who needs to know when an unhandled error occurs, and who is responsible for logging and notifying operations staff?
  3. Basic exception hierarchy:What kind of information should an exception carry, and what semantics does the exception hierarchy reflect?
  4. Propagation:Are nonhandled exceptions declared or transformed into other exception classes, and how are they propagated in a distributed environment?
  5. Interpretation:How are unhandled exceptions turned into human readable, even multilingual messages?

Encapsulate rules in a framework, but hurry!
Our recipe for a common error handling strategy builds on the following shortlist of ingredients:

  1. Use unchecked exceptions:By using checked exceptions, clients are forced to take a position on errors they can rarely handle. Unchecked exceptions leave the client with a choice. When using third-party libraries, you don’t control whether exceptions are modeled as checked or unchecked ones. In this case, you need unchecked wrapper exceptions to carry the checked ones. The biggest tradeoff in using only unchecked exceptions is that you can’t force clients to handle them anymore. Yet, when declared as part of the interface, they remain a crucial element of the contract and continue to be part of Javadoc documentation.
  2. Encapsulate error handling and install a handler on top of each tier:By having a safety net, you can focus on handling only exceptions relevant to business logic. The handler performs the safe touchdown for the remaining exceptions at the specific tier executing standardized steps: logging, system management notification, transformations, etc.
  3. Model the exception hierarchy using a “simple living” approach:Don’t automatically create new exception classes whenever new error types are discovered. Ask yourself if you are simply dealing with a variation of another type, and if the client code is likely to explicitly catch it. Remember that exceptions are objects whose attributes can, at least to some extent, model the variation of different situations. Less than a handful of exception classes will most likely prove enough to satisfy a starting point, and only those that are likely to be handled need specialized attributes.
  4. Give meaningful messages to end users:Unhandled exceptions represent unpredictable events and bugs. Tell this to the user and save the details for the technical staff.

Although needs and constraints, exception hierarchies, and notification mechanisms will differ across projects, many elements remain constant. So why not go whole hog and implement common policies in a framework? A framework followed by simple rules of usage is the best way to enforce a policy. Executable artifacts talk to developers, and it is easier to preach architectural principles with a jar file and some Javadoc than with whitepapers and slide shows.

However, you cannot ask the development team to postpone error handling until a policy and complementing framework support is ready. Error handling must already be determined when the first source file is created.

A basic exception hierarchy
Our first practical task is to define an exception hierarchy common enough to be used across projects. The base class for our own unchecked exceptions is UnrecoverableException, whose name, for historical reasons, remains slightly misleading. You might consider a better title for your own hierarchies.

When you want to get rid of a checked exception, one that, conceivably, clients will always be able to handle, WrappedExceptionoffers a simple, generic transport mechanism: wrap and throw. WrappedException offers to serve as a simple, generic transport mechanism: wrap and throw.” jo — > The WrappedExceptionkeeps the cause as an internal reference, which works well when the classes for the original exception are still available. When this is not the case, use the SerializableException, which resembles WrappedExceptionexcept that it can be used when no assumptions are made on available libraries at the client side.

Although we prefer and recommend unchecked exceptions, you might keep checked exceptions as an option. The InstrumentedExceptioninterface acts as an interface for both checked and unchecked exceptions that follow a certain pattern of attribute implementation. The interface allows exception consumers to consistently inspect the source — whether it inherits from a checked or an unchecked base.

The class diagram below shows our Basic Exception Hierarchy.

 

Figure 1. The Basic Exception Hierarchy

The last line of defense
The article Create an Application-Wide User Session presented Rampart, a layered architecture consisting of an EIS (enterprise information system) tier, a business logic tier made from stateless session beans, and a client tier with both Web and standalone J2SE clients. Exceptions can be thrown from all tiers in this architecture, and can either be handled on site or bubble up until they reach the end of the call chain. Then what? Both J2SE and J2EE application servers guard themselves against offensive behavior by catching stray Errors and RuntimeExceptions, and either dumping the stack trace to System.out, logging it, or performing some other default action. In any case, if a user is presented with any kind of output, mostly likely, it will prove absolutely meaningless, and, worse, the error will probably have disrupted program stability. We must install our own rampart to provide a more sound exception-handling mechanism for this last line of defense.

Consider Figure 2:

 

Figure 2. Exception paths in the sample architecture

Exceptions may occur on the server side in the EJB (Enterprise JavaBeans) tier and in the Web tier, or on the stand-alone client. In the first case, exceptions stay in the VM from which they originated as they make their way up to the Web tier. This is where we install our top-level exception handler.

In the stand-alone case, exceptions eventually reach the rim of the EJB container and travel along the RMI (remote method invocation) connection to the client tier. Care must be taken not to send any exceptions belonging to classes that live only on the server side, e.g., from object-relational mapping frameworks or the like. The EJB exception handler handles this responsibility by using SerializableExceptionas a vehicle. On the client side, a top-level Swing exception handler catches any stray errors and takes appropriate action.

Exception-handler framework
An exception handler in the Rampart framework is a class that implements the ExceptionHandlerinterface. This interface’s only method takes two arguments: Throwable, to handle, and the current Thread. For convenience, the framework contains an implementation, ExceptionHandlerBase, which tastes Throwableand delegates handling to dedicated abstract methods for the flavors RuntimeException, Error, Throwable, and the Rampart-specific Unrecoverable. Subclasses then provide implementations for these methods and handle each situation differently.

The class diagram below shows the exception-handler hierarchy with its three default exception handlers.

 

Figure 3. Exception-handler hierarchy. Click on thumbnail to view full-sized image.

Some among The Order of the Unchecked believe that Sun should have added hooks into all containers in the J2EE architecture on a per-application basis. This would have allowed custom error-handling schemes, security, and more to be gracefully installed, without reliance on vendor-specific schemes and frameworks. Unfortunately, Sun failed to provide such mechanisms in the EJB specification; so, we pull out the AOP hammer from our toolbox and add exception handling as around aspects on stateless session bean facade methods. AspectWerkz, our chosen AOP framework, uses the following aspect for that task:

public class EJBExceptionHandler implements AroundAdvice {

private ExceptionHandler handler;

public EJBExceptionHandler() {
handler = ConfigHelper.getEJBExceptionHandler();
}

public Object invoke(JoinPoint joinPoint) throws Throwable {

Log log = LogFactory.getLog(joinPoint.getEnclosingStaticJoinPoint().getClass().getName());
log.debug("EJB Exception Handler bean context aspect!!");
try {
return joinPoint.proceed();
} catch (RuntimeException e) {
handler.handle(Thread.currentThread(), e);
} catch (Error e) {
handler.handle(Thread.currentThread(), e);
}
return null;
}
}

The actual handler is configurable and obtained through the ConfigHelperclass. If a RuntimeExceptionor Erroris thrown during execution of the bean business logic, the handler will be asked to handle it.

The DefaultEJBExceptionHandlerserializes the stack trace of any exception not originating from Sun’s core packages into a dedicated SerializableException, which, on the plus side, allows the stack trace of exceptions whose classes don’t exist on remote clients to be propagated over anyway. On the downside, the original exception is lost in translation.

EJB containers faithfully take any RuntimeExceptionor Errorand wrap it in a java.rmi.RemoteException, if the client is remote, and in a javax.ejb.EJBException, otherwise. In the interest of keeping causes precise and stack traces at a minimum, the framework peels off these transport exceptions inside client BusinessDelegates and rethrows the original.

A Rampart BusinessDelegateclass exposes an EJB-agnostic interface to clients, while wrapping local and remote EJB interfaces internally. BusinessDelegateclasses are generated via XDoclet from EJB implementation classes and follow the structure shown in Figure 4’s UML diagram:

 

Figure 4. An example BusinessDelegate

The BusinessDelegateclass exposes all business methods from the source EJB implementation class and delegates to the LocalProxyclass or the RemoteProxyclass as appropriate. Internally, the two proxies handle EJB-specific exceptions and, hence, shield the calling BusinessDelegatefrom implementation details. The code below is a method from some LocalProxyclass:

public java.lang.String someOtherMethod()  {
try {
return serviceInterface.someOtherMethod();
} catch (EJBException e) {
BusinessDelegateUtil.throwActualException(e);
}
return null; // Statement is never reached
}

The serviceInterfacevariable represents the EJB local interface. Any EJBExceptioninstances thrown by the container to indicate an unexpected error are caught and handled by the BusinessDelegateUtilclass, in which the following action takes place:

public static void throwActualException(EJBException e) {
doThrowActualException(e);
}

private static void doThrowActualException(Throwable actual) {
boolean done = false;
while(!done) {
if(actual instanceof RemoteException) {
actual = ((RemoteException)actual).detail;
} else if (actual instanceof EJBException) {
actual = ((EJBException)actual).getCausedByException();
} else {
done = true;
}
}
if(actual instanceof RuntimeException) {
throw (RuntimeException)actual;
} else if (actual instanceof Error) {
throw (Error)actual;
}
}

The actual exception is extracted and rethrown to the top-level client exception handler. When the exception reaches the handler, the stack trace will be that of the original exception from the server containing the actual error. No superfluous client-side trace is added.

Swing exception handler
The JVM provides a default top-level exception handler for each control thread. When exercised, this handler dumps the stack trace of Errorand RuntimeExceptioninstances onto System.err, and kills the thread! This rather obstructive behavior is quite far from anything a user needs and not elegant from a debugging perspective. We want a mechanism that allows us to notify the user while retaining the stack trace and a unique request ID for later debugging. “Create an Application-Wide User Session for J2EE” describes how such a request ID becomes available at all tiers.

For J2SE versions up to 1.4, uncaught exceptions in Threadinstances cause the owner ThreadGroup‘s uncaughtException()method to execute. A simple way to control exception handling in an application, then, is to simply extend the ThreadGroupclass, overwrite the uncaughtException()method, and ensure all Threadinstances start within an instance of the custom ThreadGroupclass.

J2SE 5 provides an even sweeter mechanism by allowing the installation of an UncaughtExceptionHandlerimplementation on instances of the Threadclass itself. The handler is used as a callback mechanism when uncaught exceptions reach the Threadinstance’s run method. We built the framework with J2SE 1.3+ compatibility in mind; hence we used the ThreadGroupinheritance approach:

private static class SwingThreadGroup extends ThreadGroup {

private ExceptionHandler handler;

public SwingThreadGroup(ExceptionHandler handler) {
super("Swing ThreadGroup");
this.handler = handler;
}

public void uncaughtException(Thread t, Throwable e) {
handler.handle(t, e);
}
}

The SwingThreadGroupclass shown in the code snippet above overrides the uncaughtException()method and passes the current Threadinstance and the thrown Throwableto the configured exception handler.

A little more magic is needed before we can claim control of all stray exceptions in the client tier. For the scheme to work, all threads must be associated with our specialized SwingThreadGroupinstance. This is accomplished by spawning a new master Threadinstance and passing the SwingThreadGroupinstance with a Runnableimplementation, which executes the entire main program. All Threadinstances spawned from the body of this new master Threadinstance automatically join the SwingThreadGroupinstance and, hence, use our new exception handler when unchecked exceptions are thrown.

Figure 5. Swing exception handler class hierarchy. Click on thumbnail to view full-sized image.

The framework implements this logic in the SwingExceptionHandlerControllerconvenience class shown in Figure 5. Applications provide an implementation of the SwingMaininterface along with an exception handler to the controller. The controller must then be started, and the old main Threadcan join the new one and wait for termination. The code below shows how the demo application that accompanies this article accomplishes that task. The method createAndShowGUI()constitutes the actual application body, which initializes Swing components and transfers control to the user.

public DemoApp() {

SwingExceptionHandlerController.setHandler(new DefaultSwingExceptionHandler());
SwingExceptionHandlerController.setMain(new SwingMain() {

public Component getParentComponent() {
return frame;
}

public void run() {
createAndShowGUI();
}
});

SwingExceptionHandlerController.start();
SwingExceptionHandlerController.join();
}

The last line of defense is now in place for the Swing layer, but we still need to provide a meaningful message to the user. The demo application supplies a rather basic implementation that simply displays a dialog box with an internationalized message and the unique request ID as a support ticket. At the same time, the exception is logged via log4j along with the unique request ID. A more sophisticated error handler might send an email, SNMP message, or the like to technical support with the ID in it. The point is that both client and server logs can be filtered on the support ticket ID to allow precise pinpointing of errors on a per-request basis.

Figure 6 shows merged Swing client and J2EE server logs to provide the precise flow for the request with ID 1cffeb4:feb53del38:-7ff6where an exception was thrown. Note that the stack trace contains only information from the server side, where the exception was thrown.

Figure 6. Log trace from both client and server with request ID and server stack trace. Click on thumbnail to view full-sized image.

WAR exception handler
Web applications are among the fortunate components in the J2EE world to have their own explicit way of installing exception-handling capabilities. Through the web.xmldeployment descriptor, exceptions and HTTP errors can be mapped to error pages in the shape of servlets or JavaServer Pages (JSP) pages. Consider the following fragment from a sample web.xmlfile:

<servlet>
<servlet-name>ErrorHandlerServlet</servlet-name>
<servlet-class>dk.rhos.fw.rampart.util.errorhandling.ErrorHandlerServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>ErrorHandlerServlet</servlet-name>
<url-pattern>/errorhandler</url-pattern>
</servlet-mapping>

<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/errorhandler</location>
</error-page>

These tags direct all uncaught exceptions to the URL /errorhandler, which in this case is mapped to the ErrorHandlerServletclass. The latter is a dedicated servlet whose sole raison d’etre is to act as a bridge between Web components and the exception-handling framework. When an uncaught exception from this Web application reaches the servlet container, a set of parameters with information about the exception will be added to the HttpServletRequestinstance and passed to the ErrorHandlerServletclass’s service method. The fragment below shows the service()method:

...
private static final String CONST_EXCEPTION = "javax.servlet.error.exception";
...

protected void service(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse)
throws ServletException, IOException
{

Throwable exception = (Throwable)httpServletRequest.getAttribute(CONST_EXCEPTION);

ExceptionHandler handler = ConfigHelper.getWARExceptionHandler();
handler.handle(Thread.currentThread(), exception);

String responsePage = (String)ConfigHelper.getRequestContextFactory().
getRequestContext().
getAttribute(ExceptionConstants.CONST_RESPONSEPAGE);

if(responsePage == null) {
responsePage = "/error.jsp";
}

httpServletResponse.setStatus(HttpServletResponse.SC_OK);

RequestDispatcher dispatcher = httpServletRequest.getRequestDispatcher(responsePage);
try {
dispatcher.include(httpServletRequest, httpServletResponse);
} catch (Exception e) {
log.error("Failed to dispatch error to responsePage " + responsePage, e);
}
}

In the service()method, first, the actual exception from the HttpServletRequestinstance is retrieved through the key javax.servlet.error.exception. Next, the exception handler instance is retreived. From there on, the handler is invoked, and the HttpServletRequestinstance is forwarded to the page specified by the key rampart.servlet.exception.responsepage.

The DefaultWARExceptionHandlerclass looks up an internationalized text based on the exception message and redirects output response to the JSP /error.jsp. This page is then free to display the message to the user, including the current request ID as a support ticket. A much more sophisticated mechanism can easily be implemented by simply extending or replacing this handler.

Wrap up
Often, exception handling is not handled stringently enough, thereby complicating debugging and error tracking, and, many times, disrupting the overall user experience. It is therefore crucial to have policies and frameworks in place before system development starts. Adding this aspect as an after-thought is doable, but time consuming and expensive.

This article has armed you with a starting point for defining an exception policy and introduced you to a simple, yet extensible, unchecked exception hierarchy. We have walked through business and client tiers of a sample J2EE architecture and shown how you can install top-level exception handlers for each tier to provide a last line of defense. The framework code has given you a way to precisely pinpoint errors on a per-user request basis through the unique request ID attached to exceptions and log entries.

So download the framework, try it out, modify it to your heart’s delight, and get those exceptions under control.

ome people like olives. Some don’t. I don’t like olives. I don’t like XML either.Unfortunately, for me, all major Web frameworks rely on XML to do basically everything: setup, configuration, form handling, validation, etc. Welcome to the world of XML “programming.”

Another problem for me is that I like to do things really fast. That’s why I had a feeling something was wrong with C++. To do a project with C++, medium programmers end up spending more than half their time fighting with the language syntax, pointers, handlers, memory management, unbound arrays, etc., and very little time doing what they’re paid to do: the project. The Java language comes to the rescue with an abstraction power that makes everything easy. Just think something and Java can do it! You don’t have to care how, you just have to use the APIs.

Unfortunately, the Web frameworks out there are not for the medium guys. They require time to master and can scare rookies with XML all over the place and little tricks they are expected to know and get away with. It is like showing a Hello World EJB example to someone that has never seen an Enterprise JavaBeans component before. The result is shock and fear.

Mentawai, an open source framework, tries to break free from this scenario by implementing a small, simple, flexible, idiot-proof MVC (Model-View-Controller) Web framework with no XML at all. Instead of XML, we can use Java code to achieve the same results, and guess what: Java code has Javadocs! You can even compile Java code to catch errors! Java code is object-oriented programming. How about XML?

The most common reply I get from people when I defend this point is: “Hey, you don’t have to compile and restart your Web application with XML!” I may be losing something deeper here, but, if you change your XML, you do have to restart your Web context for most of the Web frameworks I have seen. Plus compiling is definitely not a bad thing. It is a good thing! If you could compile XML to quickly discover a wrong attribute, our debbuging time would decrease.

Now that I have argued my point, let’s check what Mentawai can offer us as an alternative.

Setup
Let’s begin by setting up our Web application to use the Mentawai framework. First, place the mentawai.jar file (downloadable from Resources) inside your application’s /WEB-INF/libdirectory and declare in your web.xmlfile that you want to use the Mentawai controller:

<!-- The Mentawai controller -->
<servlet>
<servlet-name>Controller</servlet-name>
<servlet-class>org.mentawai.core.Controller</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<!-- You must choose an extension to indicate a mentawai action -->
<servlet-mapping>
<servlet-name>Controller</servlet-name>
<url-pattern>*.mtw</url-pattern>
</servlet-mapping>

The action paradigm
Mentawai uses the action paradigm. The main characteristics of a Mentawai action are:

  • An action has an input(org.mentawai.core.Input) and an output(org.mentawai.core.Output).
  • An action generates a result(java.lang.String) after it executes. The result is usually SUCCESSor ERROR, but you can create your own.
  • For each result, there is a consequence(org.mentawai.core.Consequence). Consequences for a Web application are usually FORWARDor REDIRECT, but you can create your own.
  • An action has access to contexts(org.mentawai.core.Context). Contexts for a Web application are usually a session context (org.mentawai.core.SessionContext) and an application context (org.mentawai.core.ApplicationContext), but you can create your own.

Let’s see an example Mentawai action:

package examples.hello;

import org.mentawai.core.*;

public class HelloMentawai extends BaseAction {

public String execute() throws ActionException {
String username = input.getStringValue("username");
if (username == null || username.trim().equals("")) {
return ERROR;
}
output.setValue("username", username.toUpperCase());
return SUCCESS;
}

}

This action looks for a username input parameter and outputs it in uppercase. If it does not find the username parameter, the result is ERROR, otherwise SUCCESS. HelloMentawaiinherits from BaseAction, which is a useful implementation of the Actioninterface. BaseActionprovides, through protected data members, access to input, output, session context, application context, and more. For each possible result, we must associate a consequence. Instead of using a boring XML file, Mentawai let’s you do that with Java code, by creating an ApplicationManagerclass that extends org.mentawai.core.ApplicationManager:

// No package (root package)

public class ApplicationManager extends org.mentawai.core.ApplicationManager {

public void loadActions() {
ActionConfig ac = new ActionConfig("/HelloWorld", HelloMentawai.class);
ac.addConsequence(HelloMentawai.SUCCESS, new Forward("/hello.jsp"));
ac.addConsequence(HelloMentawai.ERROR, new Forward("/username.jsp"));
addActionConfig(ac);

// TIP: you can also use this less error-prone syntax
/*
addActionConfig(new ActionConfig("/HelloWorld", HelloMentawai.class)
.addConsequence(HelloMentawai.SUCCESS, new Forward("/hello.jsp"))
.addConsequence(HelloMentawai.ERROR, new Forward("/username.jsp")));
*/
}
}

The consequence for a SUCCESSis a forward to /hello.jsp. The consequence for an ERRORis a forward to /username.jsp. When the container starts, the Mentawai controller calls the loadActions()method. Through the ActionConfigclass, you can define different names and consequences for the same action implementation. The Mentawai controller finds actions by name, so “/HelloWorld” will be called by http://www.myapp.org/HelloWorld.mtw, and “/customers/ListCustomers” will be called by http://www.myapp.org/customers/ListCustomers.mtw. You can put your ApplicationManagerclass in any package, but, by default, Mentawai tries to load it from the root package.

Below is the code for the username.jsppage:

<html>
<body>
<h1>Hello Metawai!</h1>
<form action="HelloWorld.mtw" method="post">
Type an username: <input name="username" size="25" />
<input type="submit" value="Send">
</form>
</body>
</html>

Below is the code for the hello.jsppage:

<%@ taglib uri="/WEB-INF/lib/mentawai.jar" prefix="mtw" %>
<html>
<body>
<h3>Hello <mtw:out value="username" /> from Mentawai!</h3>
</body>
</html>

Notice the use of a Mentawai custom tag to display the action output. Mentawai provides many useful and simple tags for displaying action output. It also provides a base custom tag implementation from where you can easily create your own Mentawai tags. Mentawai also supports JSTL (JavaServer Pages Standard Tag Libraries), expression language, and Velocity for the view layer.

To test the above example, place the mentawai.jarin your classpath, compile the above classes, create the hello.jspand username.jsppages and set up everything in your Web application. (If you want, download the HelloWorld.war archive with all the files for this example.) Access the first page with http://www.yourapp.org/yourcontext/username.jsp. Type a username, and you should see the username in uppercase. Congratulations! You have run your first Mentawai Web application!

Best practices
Notice that through the ActionConfigclass, you can reuse the same action implementation in different situations. For example, you may create two ActionConfigs with the same action implementation, but with different consequences that lead to different JSP (JavaServer Pages) pages. In other words, the same model can have different views, which is what the MVC pattern dictates.

Mentawai filters
A filter intercepts an action so it can modify its input and output, before and after the action executes. A filter is Mentawai’s most important concept, and most of the framework’s features are implemented as transparent filters. To learn about filters, let’s code a generic filter that tries to populate a value object from the action input parameters. But, before we dive into the code, let’s understand the org.mentawai.core.Filterinterface:

package org.mentawai.core;

public interface Filter {

public String filter(InvocationChain chain) throws FilterException, ActionException;

}

An action may have more than one filter, so for each action, an InvocationChainclass is created, which has two important methods:

  • getAction(): Returns the invocation chain’s action.
  • invoke(): Executes the next step in the invocation chain — in other words, the next filter or the action. The action is the last step in an InvocationChain.

It is important to understand that a filter can alter an action before and after it executes. Check the code below for an example:

import org.mentawai.core.*;

public class MyFilter implements Filter {

public String filter(InvocationChain chain) throws FilterException, ActionException {
Action action = chain.getAction();
Output output = action.getOutput();
output.setValue("before", "doing something before!");
String result = chain.invoke();
output.setValue("after", "doing something after!");
return result;
}
}

Now, let’s code our generic filter.

The VOFilter
The org.mentawai.filter.VOFiltercomes with Mentawai and proves useful for populating a value object with form parameters. For example, the simple class User.javahas two attributes: username and password. We can use the VOFilterto populate a user bean before an action executes. Let’s first check VOFilter‘s source code:

package org.mentawai.filter;

import java.lang.reflect.*;
import java.util.*;

import org.mentawai.core.*;

public class VOFilter implements Filter {

private Class klass;

public VOFilter(Class klass) {

this.klass = klass;
}

private Object createObject() {
try {
return klass.newInstance();
} catch(Exception e) {
e.printStackTrace();
}
return null;
}

/*
* Use reflection to set a property in the bean
*/
private void setValue(Object bean, String name, Object value) {
try {
StringBuffer sb = new StringBuffer(30);
sb.append("set");
sb.append(name.substring(0,1).toUpperCase());
sb.append(name.substring(1));
Method m = klass.getMethod(sb.toString(), new Class[] { value.getClass() });
if (m != null) {
m.invoke(bean, new Object[] { value });
}
} catch(Exception e) {
e.printStackTrace();
}
}

public String filter(InvocationChain chain) throws FilterException, ActionException {
Action action = chain.getAction();
Input input = action.getInput();
Object bean = createObject();
if (bean != null) {
Iterator iter = input.keys();
while(iter.hasNext()) {
String name = (String) iter.next();
Object value = input.getValue(name);
setValue(bean, name, value);
}
// Set the bean in the action input
input.setValue(klass.getName(), bean);
}
return chain.invoke();
}
}

Our user bean:

package examples.vofilter;

public class User {
private String username;
private String password;

public User() { }

public void setUsername(String username) {
this.username = username;
}

public void setPassword(String password) {
this.password = password;
}

public String getUsername() { return username; }
public String getPassword() { return password; }

}

The VOFilterloops through every action input parameter and tries to inject these values in the class passed to the filter’s constructor. It uses the class name to place the new object in the input so the action can access it.

The code for a simple action that might use this filter is:

package examples.vofilter;

import java.util.*;

import org.mentawai.core.*;

public class HelloVOFilter extends BaseAction {

public String execute() throws ActionException {
User user = (User) input.getValue("examples.vofilter.User");
if (user == null) return ERROR;
// Do something with the User object

output.setValue("user", user);
return SUCCESS;
}
}

Setting up the VOFilterto filter our action is easy:

// No package (root package)!

import org.mentawai.core.*;
import org.mentawai.filter.*;
import examples.vofilter.*;

public class ApplicationManager extends org.mentawai.core.ApplicationManager {

public void loadActions() {

ActionConfig ac = new ActionConfig("/HelloVOFilter", HelloVOFilter.class);
ac.addConsequence(HelloVOFilter.SUCCESS, new Forward("/hello.jsp"));
addActionConfig(ac);

// Add a VOFilter to load a User object...
ac.addFilter(new VOFilter(User.class));
}
}

Through the method addFilter(), you can add filters to your actions. Notice that an action may have more than one filter, and you can also set global filters that intercept all actions with the addGlobalFilter()method.

Refer to the resources to download the complete VOFilterexample as a war archive.

Best practices
Inside your actions, you should place only code related to your model layer, either through model beans or plain code. With filters, you can better separate the model layer that goes inside your action from authentication, validation, connection pooling, and any other logic that has nothing to do with your model. Filters are an elegant approach for keeping your model layer clean!

Validation
Validation is an important topic in any Web application that must guarantee only valid data is written to the database. Frameworks exist solely for this purpose, like Apache Commons Validator ; however, they encourage the use of XML. Mentawai takes a different approach by using the classes org.mentawai.filter.ValidationFilterand org.mentawai.validator.Ruleto obtain a similar result with plain Java code.

You should do validation for your actions through a filter. Your validation filter should inherit from org.mentawai.filter.ValidationFilterand override the initValidator()method to set up rules for each field, along with the error you want to show in case of a validation failure. Check the example below:

public class HelloWorldValidator extends ValidationFilter {

private static final int FIELD_REQUIRED_ERROR = 1;
private static final int INVALID_USERNAME_LENGTH = 2;
private static final int INVALID_AGE = 3;
private static final int INVALID_PASSWORD_LENGTH = 4;
private static final int PASSWORD_DOES_NOT_MATCH = 5;

public void initValidator() {
add("username", new RequiredFieldRule(), FIELD_REQUIRED_ERROR);
add("username", new StringRule(6, 30), INVALID_USERNAME_LENGTH);

add("age", new RequiredFieldRule(), FIELD_REQUIRED_ERROR);

add("age", new IntegerRule(18, 50), INVALID_AGE);

add("password", new RequiredFieldRule(), FIELD_REQUIRED_ERROR);
add("password", new StringRule(4, 20), INVALID_PASSWORD_LENGTH);
add("password", new EqualRule("password", "passconf"), PASSWORD_DOES_NOT_MATCH);

add("passconf", new RequiredFieldRule(), FIELD_REQUIRED_ERROR);

}
}

Note that validation happens in a chain for each field. In the example above, the agefield has two validation rules: RequiredFieldRuleand IntegerRule. The second one executes only if the first one succeeds. You can add as many rules as you want for a single field.

By default, validation looks for error messages in the directory /validation, inside the document root. The i18n file inside this directory must have the same name of the validation filter, so, in our case, it is HelloWorldValidator_loc.i18n.

The contents for the file /validation/HelloWorldValidator_en_US.i18nappear below:

###############################################
# Messages for the filter HelloWorldValidator #
###############################################

1 = Required field cannot be left blank
2 = Your username must be between %min% and %max% characters long.
3 = You must be %min% years old or older.
4 = Your password must be between %min% and %max% characters long.
5 = Passwords do not match.

What are %min%and %max%in the error messages? Each rule can place special tokens inside error messages by implementing the method getTokens(). These tokens replace the marker %token%. RequiredFieldRuleand EqualRulehave no tokens. StringRuleand IntegerRulereturn the minimum and maximum values as tokens, so, if desired, you can show these values in the error messages.

A validation filter is totally decoupled from the action, so the same filter may be used to validate different actions. This avoids unnecessary code duplication and strives for better code maintenance.

Below is our ApplicationManager, which registers the validation filter for our action:

public class ApplicationManager extends org.mentawai.core.ApplicationManager {

public void loadActions() {
ActionConfig ac = new ActionConfig("/HelloWorld", HelloWorld.class);
ac.addConsequence(HelloWorld.SUCCESS, new Forward("/hello.jsp"));
ac.addConsequence(HelloWorld.ERROR, new Forward("/form.jsp"));
addActionConfig(ac);
ac.addFilter(new HelloWorldValidator());
}

public void loadLocales() {
LocaleManager.add(new Locale("pt", "BR"));
LocaleManager.add(new Locale("en", "US"));
}
}

Notice that we specify in the loadLocales()method the locales our application supports. If you don’t do anything, Mentawai will have just one default locale: en_US. Therefore, it will expect to find at least *_en_US.i18nfiles. Since we are specifying two locales, you should have two files inside the validation directory: HelloWorldValidator_en_US.i18nand HelloWorldValidator_pt_BR.i18n. You can change your browser’s default language to test these files.

In the view layer, we can use the handy <mtw:hasError>and <mtw:error field="" />to display validation error messages. The contents of the form.jspfile appear below:

<%@ taglib uri="/WEB-INF/lib/mentawai.jar" prefix="mtw" %>
<!--
Note: The form loses all data on each submission!
Mentawai can solve this problem automatically for you through its HTML tags.
-->
<html>
<body>
<h1>Hello Validation!</h1>
<form action="HelloWorld.mtw" method="post">
Your username: <input name="username" size="25" />

<mtw:hasError>
<font color="red"><mtw:error field="username" /></font>
</mtw:hasError>

<br>Your age: <input type="text" name="age" size="10" maxlength="3" />

<mtw:hasError>
<font color="red"><mtw:error field="age" /></font>
</mtw:hasError>

<br>Your password: <input type="password" name="password" size="25" />

<mtw:hasError>
<font color="red"><mtw:error field="password" /></font>
</mtw:hasError>

<br>Again please: <input type="password" name="passconf" size="25" />

<mtw:hasError>
<font color="red"><mtw:error field="passconf" /></font>
</mtw:hasError>

<input type="submit" value="Enviar">
</form>
</body>
</html>

Notice that the Mentawai tag <mtw:hasError>is conditional; in other words, it will only show its body contents if its conditional is true.

Refer to the resources to download the complete validation example as a war archive.

Creating more validation rules
When it comes to validation, you can create an unlimited number of validation rules. For example, you may need to have a CreditCardRule, a URLRule, an EmailRule(provided by Mentawai), or a PasswordRule. You can either use one of the available Mentawai rules or create your own.

Creating your own rules in Mentawai is easy and just a matter of implementing the org.mentawai.validator.Ruleinterface. You can also use one of the abstract rules that come with Mentawai:

  • BasicRule: A simple rule to validate a single field (StringRule)
  • LocaleRule: A rule to validate a single field considering its locale (DateRule)
  • CrossRule: A rule to compare different fields in the same form for the validation (EqualRule)

Mentawai comes with a useful rule: org.mentawai.rule.RegexRule. With this rule, you can validate a field with a regular expression. Therefore, this rule should be subclassed to create more rules that can be performed with a regular expression. org.mentawai.rule.EmailRuleis such an example. You should refer to the API documentation for more details on how to extend these classes.

Keeping the form data
You probably noticed that our form loses all its data after each submission, forcing the user to type everything again and resulting in a terrible experience for the user and also for the developer who must code a solution. However, through Mentawai’s HTML tags, you can solve this problem without a single line of code. Just use the tags <mtw:input>, <mtw:select>, <mtw:checkboxes>, and <mtw:radiobuttons>to construct your HTML and the form will not lose its data anymore. These custom tags have the same attributes of normal HTML tags, so you should have no problems using them.

SUBHEAD Conclusion

In this article, I have covered a small part of the Mentawai Web framework. You have been introduced to the framework’s architecture and have seen how most of its features can be implemented as filters and, best of all, with no XML. We also examined the validation feature and saw how we can easily configure localized error messages in i18n files. With Mentawai, you don’t have to deal with those boring XML files anymore. You can use the ApplicationManagerclass to achieve the same results with plain Java code.

Similarly to how XML became the well-known standard adapted by many software vendors for data exchange, Resource Description Framework (RDF) is going in the same direction for describing and interchanging metadata. XML describes data using a document type definition (DTD) or an XML Schema Definition (XSD). RDF uses XML syntax and RDF Schema (RFDS) to describe the metadata as a data model.

This article explains how to use custom utilities developed with the Jena RDF API for managing RDF models stored in either a relational database or a file. Developed by HP Labs, the Jena framework is an open source implementation of RDF, RDFS, and OWL (Web Ontology Language) and includes a rule-based inference engine. It provides a Java API for creating and manipulating RDF models. In this article, I explain scripts that I developed using Jena for maintaining RDF database or file models. This article also explains how to use Protégé for creating semantic RDF files that include the schema (.rdfs) and the data file (.rdf).

Software installation
The following software must be installed before using SemanticRDFUtils.bat, a script with several tasks that can be used for maintaining Jena RDF metadata models stored in a relational database or a flat file. The links are included in Resources.

  • Java SDK 1.3 or a more recent version
  • Jena 2.0
  • Oracle 9.2.0.1.0
  • Apache ANT 1.5.4 or a more recent version
  • Protégé _2.1

A quick look at RDF and RDFS files
The following XML listings show the RDF and RDFS files for a sample alphabet cross reference model. They were created using the Protégé 2.1 GUI tool. The RDF file can be used as an input while running the scripts and RDF query tool. The RDFS file is useful when you work with Protégé to add more data to the RDF file.

Listing 1. RDFTest1.rdf

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE rdf:RDF [
<!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<!ENTITY rdfs 'http://www.w3.org/TR/1999/PR-rdf-schema-19990303#'>
<!ENTITY Maana 'http://www.vvasam.com/Maana#'>
]>
<rdf:RDF xmlns:rdf="&rdf;"
xmlns:Maana="&Maana;"
xmlns:rdfs="&rdfs;">
<Maana:ASCII rdf:about="&Maana;RDFTest_Instance_0"
Maana:Name="A"
Maana:value="65"
rdfs:label="A:65">
<Maana:system rdf:resource="&Maana;RDFTest_Instance_2"/>
</Maana:ASCII>
<Maana:System rdf:about="&Maana;RDFTest_Instance_1"
Maana:Name="lowercase"
rdfs:label="lowercase"/>
<Maana:ASCII rdf:about="&Maana;RDFTest_Instance_10000"
Maana:Name="b"
Maana:value="98"
rdfs:label="b:98">
<Maana:system rdf:resource="&Maana;RDFTest_Instance_1"/>
</Maana:ASCII>
<Maana:ASCII rdf:about="&Maana;RDFTest_Instance_10001"
Maana:Name="B"
Maana:value="66"
rdfs:label="B:66">
<Maana:system rdf:resource="&Maana;RDFTest_Instance_2"/>
</Maana:ASCII>
<Maana:AscXRef rdf:about="&Maana;RDFTest_Instance_10002"
rdfs:label="b:98:B:66">
<Maana:keyName rdf:resource="&Maana;RDFTest_Instance_10000"/>
<Maana:keyValue rdf:resource="&Maana;RDFTest_Instance_10001"/>
</Maana:AscXRef>
<Maana:AscXRef rdf:about="&Maana;RDFTest_Instance_10005"
rdfs:label="a:97:A:65">
<Maana:keyValue rdf:resource="&Maana;RDFTest_Instance_0"/>
<Maana:keyName rdf:resource="&Maana;RDFTest_Instance_8"/>
</Maana:AscXRef>
<Maana:System rdf:about="&Maana;RDFTest_Instance_2"
Maana:Name="uppercase"
rdfs:label="uppercase"/>
<Maana:ASCII rdf:about="&Maana;RDFTest_Instance_8"
Maana:Name="a"
Maana:value="97"
rdfs:label="a:97">
<Maana:system rdf:resource="&Maana;RDFTest_Instance_1"/>
</Maana:ASCII>
</rdf:RDF>

Listing 2. RDFTest1.rdfs

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE rdf:RDF [
<!ENTITY rdf 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'>
<!ENTITY system 'http://protege.stanford.edu/system#'>
<!ENTITY Maana 'http://www.vvasam.com/Maana#'>
<!ENTITY rdfs 'http://www.w3.org/TR/1999/PR-rdf-schema-19990303#'>
]>
<rdf:RDF xmlns:rdf="&rdf;"
xmlns:system="&system;"
xmlns:rdfs="&rdfs;"
xmlns:Maana="&Maana;">
<rdf:Property rdf:about="&system;maxCardinality"
rdfs:label="system:maxCardinality"/>
<rdf:Property rdf:about="&system;minCardinality"
rdfs:label="system:minCardinality"/>
<rdf:Property rdf:about="&system;range"
rdfs:label="system:range"/>
<rdfs:Class rdf:about="&Maana;ASCII"
rdfs:label="ASCII">
<rdfs:subClassOf rdf:resource="&rdfs;Resource"/>
</rdfs:Class>
<rdfs:Class rdf:about="&Maana;AscXRef"
rdfs:label="AscXRef">
<rdfs:subClassOf rdf:resource="&rdfs;Resource"/>
</rdfs:Class>
<rdf:Property rdf:about="&Maana;Name"
rdfs:label="Name">
<rdfs:domain rdf:resource="&Maana;ASCII"/>
<rdfs:domain rdf:resource="&Maana;System"/>
<rdfs:range rdf:resource="&rdfs;Literal"/>
</rdf:Property>
<rdf:Property rdf:about="&Maana;RDFTest_Slot_10003"
rdfs:label="RDFTest_Slot_10003">
<rdfs:range rdf:resource="&rdfs;Literal"/>
</rdf:Property>
<rdfs:Class rdf:about="&Maana;System"
rdfs:label="System">
<rdfs:subClassOf rdf:resource="&rdfs;Resource"/>
</rdfs:Class>
<rdf:Property rdf:about="&Maana;keyName"
rdfs:label="keyName">
<rdfs:range rdf:resource="&Maana;ASCII"/>
<rdfs:domain rdf:resource="&Maana;AscXRef"/>
</rdf:Property>
<rdf:Property rdf:about="&Maana;keyValue"
rdfs:label="keyValue">
<rdfs:range rdf:resource="&Maana;ASCII"/>
<rdfs:domain rdf:resource="&Maana;AscXRef"/>
</rdf:Property>
<rdf:Property rdf:about="&Maana;system"
rdfs:label="system">
<rdfs:domain rdf:resource="&Maana;ASCII"/>
<rdfs:range rdf:resource="&Maana;System"/>

</rdf:Property>
<rdf:Property rdf:about="&Maana;value"
rdfs:label="value">
<rdfs:domain rdf:resource="&Maana;ASCII"/>
<rdfs:range rdf:resource="&rdfs;Literal"/>
</rdf:Property>
</rdf:RDF>

Overview of Jena and Protégé
The following sections give a high-level overview of Jena and Protégé. You can get more detailed information on both productsfrom the links in Resources. In this article, I expect you already have a good understanding of Jena and Protégé.

Jena RDF and RDQL
The RDF data model is a collection of statements, with each statement consisting of three parts: resource, property, and a value. The resource can be anything that can be identified by a URI and it can have properties. Each property contains values. A property, value, and statement can be a resource and have their own properties and values.

Jena persists the RDF model in either a database or a file. RDQL is query language for querying the RDF model. RDF provides a graph with directed edges where the nodes can be either resources or literals. RDQL offers a way of specifying a graph pattern that is matched against the graph to yield a set of matches. Figure 1 shows the RDF graph representation of the files shown in Listings 1 and 2.

 

Figure 1. RDF graph representation for the sample RDF file. Click on thumbnail to view full-sized image.

The resource is represented by ellipse and the literal is represented by rectangle. The resource(subject) is linked to another resource or literal( object or value) through an arc(predicate or property) can be considered as a triple and called as a statement.

The query below is an example RDQL query. The triple (?x “97”) in the query is a statement. The xis a bind variable that represents a resource; http://www.vvasam.com/Maana#value is a property with name value; and 97 is the value of the property.

SELECT ?x WHERE (?x “97”)

The Jena toolkit provides a Java class (jjena.rdfquery) that can be executed from a command line to carry out RDQL queries. The following shows the results from executing the query shown above by saving it as test1.rdql.

java jena.rdfquery --data RDFTest1.rdf --query test1.rdql
x
================================================
http://www.vvasam.com/Maana#RDFTest_Instance_8

Note: Browse through the links in Resourcesfor more information on RDF and RDQL.

RDF using Protégé
Protégé is a GUI tool for creating and editing ontologies and knowledge bases. Protégé can create and store data in RDF format. To create an RDF model in Protégé, the RDF Schema format must be selected when creating a project, as shown in Figure 2.

 

Figure 2. RDF Schema project

The Select Format box appears when New is selected from Protégé’s Project menu. After clicking the OK button, the window in Figure 3 appears.

 

Figure 3. Default Protégé project screen. Click on thumbnail to view full-sized image.

As you can see from Figure 3 Protégé has several tabs. In this article, I briefly discuss the Classes, Instances, and Algernon tabs.

Figure 4 shows Protégé’s Save dialog box. It includes entries for typing the names of the project, the classes file, instances file, and namespace. As shown in Figure 4, Classes File Name contains RDF Schema information, and Instances File Name contains the RDF data. Namespace identifies the RDF model with a unique URI.

 

Figure 4: Protégé’s Save dialog

Figures 5 and 6 show the Protégé Classes and Instances tab, respectively, of the .rdfand .rdfsfiles shown in Listings 1 and 2. The files are created using Protégé RDF schema format.

 

Figure 5. Protégé class tab. Click on thumbnail to view full-sized image.

:END_IMAGE

 

 

 

Figure 6. Protégé instances tab. Click on thumbnail to view full-sized image.

Algernon queries in Protégé
Protégé’s Algernon query tab provides a UI for running Algernon queries and viewing the results, see Figure 7. Algernon is triple based query language that returns resources based on the traversal path as shown in Figure 7. By default, the Algernon query tab doesn’t appear in the view. To see this tab, it must be selected from the Configure submenu in the Project menu.

 

Figure 7. Algernon tab. Click on thumbnail to view full-sized image.

Terminology mapping between Jena and Protégé
Jena and Protégé are two separate open source technologies, so they have RDF terminology differences. The following table maps the terminology to make things easier when using them for creating and manipulating RDF documents.

Table 1. Jena and Protégé terminology comparison

Jena Protégé Comments
Resource Class The values of resource’s properties can be seen in the instances tab of Protégé .
Property
RDF Model
Slot
RDF Project
Jena can manipulate .rdf files without .rdfs file. But Protégé requires both .rdf and .rdfs files.

Jena Semantic RDF utilities
This section explains utilities/scripts useful for maintaining the Jena database and file models. The script files are in the SemanticRDFUtils-scripts-files.zip file. The list below describes the tasks you can perform using the scripts. When you execute the SemanticRDFUtilsbatch file with no command line parameters as a task ID, the following appears on your console:

C:\RDF\SemanticRDFUtils
Usage: SemanticRDFUtils taskid
Where taskid should be any one of the following:
1 --> To create and initialize the Jena system tables with a system model name as JenaRDFSystem
2 --> To create a database model
3 --> To remove a database model.
4 --> To list the contents of a given model.
5 --> To import RDF/XML file to a database.
6 --> To list existing database model names
7 --> To export a database model to a RDF/XML file
8 --> To delete all the contents of a database model
9 --> To create a union(RDF/XML file) of RDF/XML file models
10 --> To create an intersection(RDF/XML file) of RDF/XML file models
11 --> To create a difference(RDF/XML file) of RDF/XML file models
12 --> To get the size of the given model
13 --> Export the RDF query results as RDF/XML file.
14 --> Delete the resource(s) from a model based on RDF query.

The SemanticRDFUtilsscript uses the SemanticRDFUtils.propertiesfile to get configuration information. The following table shows all the properties you can configure in that file.

Table 1. Title

Property names Possible values Comments
rdf_sytem_model_name
rdf_namespace URL followed by # Example: http://www.vvasam.net/vvasam#
isRDFInDB True or false If true, then the db_user, db_password, and url properties should be populated. The URL should have value with JDBC protocol. If false, then url property should have value with file protocol convention.
db_user User name and password of the database
url JDBC URL or file URL If isRDFInDB‘s value is true then URL format should be jdbc:oracle:thin:@<<hostname>>:<<port>>:<<sid >>. Example: url=jdbc:oracle:thin:@rdfhost:1521:alpc. If isRDFInDB‘s value is false then URL format should be file:///c:/…... Example: url=file:///c:/temp/refDataTest.rdf.
modelName Name of the database model. This is only used if isRDFInDB‘s value is true. This property can be empty for executing some tasks/options/services (methods)in SemanticRDFUtilslike

  1. List existing models
  2. Create a new model
  3. Create and initialize Jena system tables
  4. Remove model
rdf_query and bind_var_name RDF query and name of the bind variable in RDF query. If rdf_query property is empty, then the utility prompts the user to enter a value.
export_rdffile_abs_name If this property is empty, then the utility prompts the user to enter a value.
import_rdffile_abs_name If this property is empty, then the utility prompts the user to enter a value.
file_rdfmodel1_abs_name, file_rdfmodel2_abs_name, result_rdfmodel_abs_name The following properties are for model operations like union, intersection, and difference:

  1. file_model1_abs_name
  2. file_model2_abs_name
  3. output_model_abs_name

If any of the model operation properties are empty, then the utility prompts the user to enter a value.

The first task creates Jena system tables in a database and requires appropriate values for the following properties:

  1. rdf_sytem_model_name
  2. db_user
  3. db_password
  4. isRDFInDB
  5. url

The second task creates a new RDF model in a database and requires appropriate values for the following properties:

  • rdf_sytem_model_name
  • db_user
  • db_password
  • isRDFInDB
  • url
  • modelName(if this property is empty, then the script prompts for a value to be entered from the keyboard)

The third task deletes an RDF model from the database and requires appropriate values for the following properties:

  • rdf_sytem_model_name
  • db_user
  • db_password
  • isRDFInDB
  • url
  • modelName(if this property is empty, then the script prompts for a value to be entered from the keyboard)

The fourth task lists the contents of an RDF database model and requires appropriate values for the following properties:

  • rdf_sytem_model_name
  • db_user
  • db_password
  • isRDFInDB
  • url
  • modelName(if this property is empty, then the script prompts for a value to be entered from the keyboard)

The fifth task imports an RDF file to a database model and requires appropriate values for the following properties:

    • rdf_sytem_model_name
    • db_user

db_password

  • isRDFInDB
  • url
  • modelName(if this property is empty, then the script prompts for a value to be entered from the keyboard)
  • import_rdffile_abs_name

 

The value of the property import_rdffile_abs_nameis populated with the absolute path of the .rdffile as shown below. If the value is empty, then the script prompts for an input value.

import_rdffile_abs_name=C:\temp\RDFTest1.rdf

The sixth task lists all the models in a database model and requires appropriate values for the following properties:

  • rdf_sytem_model_name
  • db_user
  • db_password
  • isRDFInDB
  • url

The seventh task exports the contents of the given database model to an RDF file and requires appropriate values for the following properties:

    • rdf_sytem_model_name
    • db_user

db_password

  • isRDFInDB
  • url
  • modelName(if this property is empty, then the script prompts for a value to be entered from the keyboard)
  • import_rdffile_abs_name

 

The value of the property export_rdffile_abs_nameis populated with the absolute path of the .rdffile as shown below. If the value is empty, then the script prompts for an input value.

export_rdffile_abs_name=C:/temp/export.rdf

The eighth task deletes a database model’s contents and requires appropriate values for the following properties:

    • rdf_sytem_model_name
    • db_user

db_password

  • isRDFInDB
  • url
  •  

 

The ninth task performs a union operation on two file models and requires appropriate values for the following properties:

  • isRDFInDB
  • url
  • file_rdfmodel1_abs_name
  • file_rdfmodel2_abs_name
  • result_rdfmodel_abs_name

The following shows the sample property values:

file_rdfmodel1_abs_name=C:/temp/RDFTest1.rdf

file_rdfmodel2_abs_name=C:/temp/RDFTest2.rdf

result_rdfmodel_abs_name=C:/temp/RDFTestUnion.rdf

When SemanticRDFUtilsis executed with the task ID 9, the two .rdffiles are merged. To create a Protégé project on the merged .rdffile, the .rdfsfiles must be merged manually.

The tenth task performs an intersection operation on two file models and requires appropriate values for the following properties:

  • isRDFInDB
  • url
  • file_rdfmodel1_abs_name
  • file_rdfmodel2_abs_name
  • result_rdfmodel_abs_name

The following shows the sample property values:

file_rdfmodel1_abs_name=C:/temp/RDFTest1.rdf

file_rdfmodel2_abs_name=C:/temp/RDFTest2.rdf

result_rdfmodel_abs_name=C:/temp/RDFTestInterSection.rdf

The eleventh task performs a difference operation on two file models and requires appropriate values for the following properties:

  • isRDFInDB
  • url
  • file_rdfmodel1_abs_name
  • file_rdfmodel2_abs_name
  • result_rdfmodel_abs_name

The following shows the sample property values:

file_rdfmodel1_abs_name=C:/temp/RDFTest1.rdf

file_rdfmodel2_abs_name=C:/temp/RDFTest2.rdf

result_rdfmodel_abs_name=C:/temp/ RDFTestDifference.rdf

The twelfth task lists the size of a given database model and requires appropriate values for the following properties:

    • rdf_sytem_model_name
    • db_user

db_password

  • isRDFInDB
  • url
  • modelName(if this property is empty, then the script prompts for a value to be entered from the keyboard)

 

The thirteenth task exports the results of a RDF query on a database model to an RDF file and requires appropriate values for the following properties:

    • rdf_sytem_model_name
    • db_user

db_password

  • isRDFInDB
  • url
  • modelName(if this property is empty, then the script prompts for a value to be entered from the keyboard)
  • rdf_query
  • bind_var_name
  • export_rdffile_abs_name

 

The following shows the sample property values:

rdf_query=SELECT ?x WHERE (?x    "65")

bind_var_name=x

export_rdffile_abs_name=C:/temp/exportquery.rdf

The fourteenth task deletes the results of an RDF query from a database model and requires appropriate values for the following properties:

    • rdf_sytem_model_name
    • db_user

db_password

  • isRDFInDB
  • url
  • modelName(if this property is empty, then the script prompts for a value to be entered from the keyboard)
  • rdf_query
  • bind_var_name

 

The following shows the sample property values:

rdf_query=SELECT ?x WHERE (?x    "65")

bind_var_name=x

Conclusion
This article has given you insight into how to create RDF metadata models using Jena and Protégé. It described how to use the SemanticRDFUtilscommand line script for maintaining the RDF models. The SemanticRDFUtils-source-files.zip included in this article’s accompanying source files can be used to create a Web-based interface or Protégé GUI plug-in for manipulating the RDF models. The load scripts for each RDF model would differ and should be handled case by case.

JavaServer Faces was developed with IDES and rapid development of Web applications in mind. But is JSF living up to its promise? Can an IDE built around JSF potentially improve our productivity? Can such a tool help us create and deploy functioning Web applications faster than currently available options? The best way to answer this question is to test-drive one of the current IDEs that support drag-and-drop JSF development and attempt to develop a real Web application, a task I describe in this article.

My experiment includes the following particulars:

  • The tool selected:Sun Microsystems’ Java Studio Creator (JSC), a choice that, of course, is debatable. Reasons for this selection include fairly good reviews on the Web, its selection as runner-up for Developer.com’s 2005 Development Tool of the Year (behind Eclipse, so it’s perfectly understandable why it was only a runner-up), and, price ($99). There are a number of other tools in this space, many of which, based on a quick review, are worthy of deeper exploration. Some don’t provide drag-and-drop functionality, but have other nice features. These IDEs include:
    • Borland JBuilder Enterprise Edition 2005
    • Eclipse with Exadel Studio Pro
    • Eclipse with Nitrox JSF
    • IBM Rational with JSF support
    • Macromedia Dreamweaver with JSTL and JSF extensions
    • MyEclipse with JSF Designer
    • Oracle’s JDeveloper
  • The project:A simple Web application that searches an employee database.
  • The developer: I am a reasonably competent developer with infrequent flashes of astonishing brilliance balanced by equally infrequent flashes of profound stupidity; lots of Java development experience; quite a bit of experience developing Web applications with servlets, JavaServer Pages/JSP Standard Tag Library, and Struts; and a basic understanding of JSF. I’m fairly skeptical about using IDEs for real software development. Before starting this project, I spent a few hours playing around with JSC to get somewhat familiar with its capabilities.

Now that the particulars are out of the way, I’m ready to start developing.

Time: 8:00 a.m. I fire up JSC on my laptop at the coffee shop. The first screen looks like this (it may look a little different on your machine, depending upon how you’ve configured your panels):

Figure 1. Click on thumbnail to view full-sized image.

I select Create New Project. I type JSFExperimentfor the project name and click the OK button. The initial design project comes up and looks like Figure 2.

The window resembles the NetBeans IDE, of course, because JSC is built on NetBeans. In the bottom left panel is the actual page I’m working on, with tabs for a visual layout and the page source.

To the left is a panel that allows me to navigate through the pieces of my Web application, including the pages and the session and application beans. The top right panel has several tabs, which I explain in more detail below. By default, the project is created with a single JSP page.

The JSC download comes with the platform edition of Sun Java System Application Server, which eases the deployment and testing of JSF applications developed with the tool. Alternatively, we could install Tomcat and add/configure JSF support, but having a Web container included with the tool, with built-in JSF support is certainly helpful. There’s also a SQL database server (PointBase) installed to support creation and testing of Java Database Connectivity (JDBC) functionality. In addition, a JDK is included along with plenty of documentation. All this makes for a large download, but the cost of a few (hundred) MB is offset by convenience.

I want my simple application to have two pages: a search page that sets up my query, and a results page. I go to the Project Navigator tab, right-click on Page1, and rename it search.Then I right-click on the Web Pages page, select New, then Page, and type resultsin the resulting dialog box. I now have a Web application with two pages. Now I will add some components and set up basic navigation between the pages.

In the search page, I want some text, a dropdown list for selecting search criteria, and a submit button. So, I select the Palette tab, then JSF Standard Components, and drag an Output Text, a Dropdown List, and a Button component to my search page. After placing the components, I select the Output Text and Button components, then select the Properties tab, and edit the Value property for each of the components to change the output text. After I complete these tasks, the page looks like this:

 

Figure 2. Click on thumbnail to view full-sized image.

For the results page, I drag an Output Text component and a Data Table component. For the Output Text component, I change the Value property in the Properties tab under in the Palette window to edit the display text. You’ll see how to customize the headers for the Data Table component below.

Now is a good time to set up basic page navigation. One approach is to right-click on the search page’s design page anywhere outside one of the components and select the Page Navigation action. A graphical navigation editor then displays in the design panel. If I click on the search page, it will enlarge, and I can drag a navigation line from the Button component to the results page. Once I’ve done that, the name of the action is selected; this is a good time to rename it. I call it doSearch.The navigation editor now looks like this:

 

Figure 3. Click on thumbnail to view full-sized image.

If I click on the Source tab in the design panel, I’ll see the faces-configfile display with navigation rules defined that are consistent with what I just completed in the editor. JSC also supports dynamically controlling page navigation, which I do not cover in detail here.

This has all been pretty easy so far–almost too easy. Could it be possible that I’ve created something that actually works? All I need to do to find out is select Build Project from the Build menu in the top menu bar, then Run Project from that same menu. Running the project will start the Sun application server with my built and deployed Web application, and fire up the default browser with the URL pointing to the Web application. Sure enough–I see this page:

 

Figure 4

When I select the Do Search, button I see this page:

 

Figure 5

It worked!!! It’s now 8:45 a.m. I haven’t written a single line of code yet and I have a working (although simple and, admittedly, completely worthless, since I’m not accessing the database yet) Web application. If I had not documented each step in the process, I’m quite sure I could have done this a lot faster. Plus I had to stand in line for a while to get a bagel.

Now things are going to get a little more interesting. I want my application to talk to a database. JSC comes with a PointBase database server installed, which is very cool. However, for this experiment, I want to connect to a different database. I want to use the Apache Derby open source embedded database. I also just want to connect to a database that’s already been created. My database is simple and just contains one employees table. I won’t debate the merits of this particular choice for this particular application; I just want to see how easy it is to use a relatively arbitrary database with JSC.

So, from the top menu bar, I select Data, then Add Data Source, add the JARs I want to use and JDBC driver information (after a bit of refreshing on how to set up the Derby URL to point to a specific place in the filesystem):

 

Figure 6. Click on thumbnail to view full-sized image

:END-IMAGE

Wow, that was easy. I wonder if I actually did anything. I hit the Test Connection dialog and receive:

IMAGE: images/jw-0704-jsf7.jpg 425 135 CENTER

Figure 7

That’s encouraging. I attempt to view the tables in my database (I actually only have one) from the Server Navigator tab and I see the following–even more encouraging:

 

Figure 8. Click on thumbnail to view full-sized image.

Time: 9:00 a.m. I usually don’t order the extra large black coffee–I usually just get wimpy mochas that don’t have nearly the caffeine level. My head feels a little funny, but I am also feeling extremely productive. It’s probably too early to tell yet whether that feeling is just an illusion.

I haven’t covered (and won’t in detail) support for page layout. However, JSC offers two options for page layout: grid layout, which places components at an absolute location on the page; and flow layout, which lays out components left to right on the page. I switch from the grid layout (the default, which uses absolute positioning on the page) to a flow layout, which seems a bit more adaptable and less Visual Basic-like. A couple of JSF components also support the laying out of subcomponents. This functionality and JSC’s support for easily using stylesheets offer many options for controlling layout, a subject that could be the focus of a future article.

I start using the database by attempting to populate the dropdown list on the search page. When the page loads, I want the list to be populated with only those employees with a position equal to MANAGER. Then I want to allow searching by a particular manager and display attributes for those employees in that manager’s department.

To do this, I select my results page, then go to the Server Navigator window, select the EMPLOYEE table, and select the Add to Page context action. Now I double-click on the employeeRowSet tab and get a panel that graphically displays the table rows. I can unselect the columns I don’t care about (specifically, all except LNAME and DEPT). This creates a query that returns all the rows from the table with two columns in each row. I only want to return the employees that are managers, so in the query display panel I add the following criteria

‘WHERE APP.EMPLOYEE.POSITION=’MANAGER’

Now the query editor looks like this:

 

Figure 9. Click on thumbnail to view full-sized image

I right-click on my DropdownList in my JSP design page and select Fill List from Database. In the resulting dialog, I select EMPLOYEE.DEPT for the Value field, and EMPLOYEE.LNAME for the Display field. If this works correctly, an abcshould display in the dropdown list indicating that the data type is a string. This happens, and all seems to work properly.

OK, I can’t wait. I haven’t written a line of code yet, with the arguable exception of one WHERE clause. Have I produced anything that actually works? I select the File menu, then Save All to save my project; then, from the Build menu, I select Build Project. If there are no errors, there should be a dist/under a directory matching the project name containing a war file—which occurs as expected.

Now things get a bit tricky only because of my experimentation with my database of choice. The embedded Derby JDBC driver I selected allows only one connection to a given database, which I’ve already made in the course of working with the database within JSC. So, if I run the project (which would work fine with a typical database by just firing up the default browser from within JSC with our Web application URL), I’ll receive an exception indicating that the page couldn’t establish the database connection because it’s already been established. Alternatively, I could shut down JSC, copy the war file to Tomcat or some other Web container that has JSF support installed, and start the Web application. I may need to remove the db.lckfile to successfully establish a new connection (before I send this Web application to production, I’ll make sure I invoke the Derby shutdown URL to properly clean things up).

If you don’t have another server configured with JSF support yet, a reasonable (and quicker) option is to just use the Sun application server that comes with the JSC installation (there’s a reason why it’s such a big download). On a Windows box, from the Start Menu, select the Run submenu, then Sun, then J2EE 1.4 SDK, then Start Default Server to start the server. For a project named JSFExperiment, the URL should be: http://localhost:18080/jsfexperiment/.

It works! I now have a Web application with a dropdown that’s actually populated with real data from a real database!

Now I begin working on the page that gives the results. For the Data Table component in the results page, I select the EMPLOYEE table from the Server Navigator, and drop it on to the Data Table (note the entire Data Table needs to be selected). In the query editor, I unselect all columns except for LNAME, MI, FNAME, and POSITION. Then, in the query string, I add WHERE APP.EMPLOYEE.DEPT=? as the criteria.

I want to customize this table a bit, similar to what I did earlier, by removing some of the rows and renaming the column headers. In the Application Outline panel on the left side of the screen, I right-click the Data Table component, and select Table Layout. In this dialog I can customize the display strings for the column headings:

 

Figure 10. Click on thumbnail to view full-sized image.

I want to populate the Data Table based on the value of the dropdown selection. Finally, I’m at the point where I can write a bit of source code! But, it’s not much.

I want to populate a value in the session bean (the current menu selection) to make it available in the next page. To do that, in the Project Navigator panel, I select Java Sources, then databoundcomponents, then SessionBean1, and then select Add, then Property from the Context Action menu. I name the new property departmentand give it a data type of string.

Now I want to populate this value when the dropdown list changes. To do that, I select the Page1.jsp tab and double-click on the Dropdown List component. This is interesting–the event-handler method stub isn’t created for the dropdown1_processValueChange()method until the first time you double-click on the Dropdown List component. The Page1 source code displays in the Editor area, and the cursor is placed inside the body of the dropdown1_processValueChange()method.

I add a bit of code to the method body:

try {
getSessionBean1().setDepartment(""+dropdown1.getValue());
} catch (Exception e) {
log("Exception selecting departments", e);
error("Exception selecting departments " + e);
}

In the constructor for the results page, I use this value in my query:

try {
dataTable1Model.setObject(1, getSessionBean1().getDepartment());
dataTable1Model.execute();
} catch (Exception e) {
log("Exception selecting managers", e);
error("Exception selecting managers: " + e);
}

I rebuild the Web application and see this results page:

 

Figure 11. Click on thumbnail to view full-sized image

It all works! The time is now 9:45 a.m. In less than two hours, I’ve completed a Web application with a deployable WAR that talks to a database and actually does something useful. Again, I believe the amount of time would have been much less if I weren’t recording my steps along the way. Plus, people at a neighboring table have been distracting me with their proposed solution for world hunger.

A few conclusions from this experiment:

  1. To create a basic, functional Web application that queried to a database, I didn’t write a single line of code!
  2. When I did get to the point of adding some code (to add the results of a database query to a session bean and make them available to the next page) I was working with generic Web application concepts and didn’t need to know or care about the intricacies of JSF.
  3. If I had to produce this application immediately using JSC, without the chance to become familiar with its features, would I have developed faster than if I had just used some combination of servlets, JSP/JSTL, and Struts? Definitely not. Would I have developed faster the second time around? Absolutely.

I’d like to qualify some of the conclusions above. Many issues are being hotly debated in the JSF community, but most of these are related to JSF itself rather then the fact that a JSF application can be developed with an IDE. Some examples of these issues: Because of JSF’s use of JavaScript and hidden form fields, some problems result when developing automated tests for JSF applications with tools like jWebUnit and WebTest from Canoo. Plus, JSF 1.1 isn’t good at handling duplicate form posts (although that should be improved with JSF 1.2). Also being sorted out is the precise relationship of JavaServer Faces to Struts.

For my experimental project, I wanted to start with a static page mockup developed by an imaginary graphic design team in conjunction with imaginary informed representatives from an imaginary user community, and then populate that page with dynamic content. I’ve found this to be a pretty good way to at least start converging on UI functionality before wasting development time on something the users didn’t really want. JSC documentation describes a fairly straightforward-sounding way to do this via the Add Existing Item option from the File menu. However, all the options under this selection appeared to be disabled. Was this feature just not quite finished in my JSC version? I don’t know and couldn’t seem to get the feature to work. The clock was ticking and I had to move on. I assume that in some future JSC version, this feature will be available and will be solid, because it’s just too useful not to have.

For this article, I didn’t have the chance to detail many JSC features. One feature that is particularly interesting is JSC’s support for easily wiring up a JSF Web application to Web services. The quick-and-dirty application I developed doesn’t attempt to hide database details. Being able to easily connect to Web services is going to be useful.

I didn’t intend for this article to offer an endorsement of any particular IDE, but I came away impressed with JSC. Sun’s had its share of hard knocks in recent years. It’s nice to see them produce such a high-quality product. If the other tools in this space are anywhere close to this level of quality, things look promising for the future of JavaServer Faces

JMeter is a popular open source tool for load testing, with many useful modeling features such as thread group, timer, and HTTP sampler elements. This article complements the JMeter User’s Manual and provides guidelines for using some of the JMeter modeling elements to develop a quality test script.

This article also addresses an important issue in a larger context: specifying precise response time requirements and validating test results. Specifically, a rigorous statistical method, the confidence interval analysis, is applied.

Please note that I assume readers know the basics of JMeter. This article’s examples are based on JMeter 2.0.3.

Determine a thread group’s ramp-up period
The first element of your JMeter script is a thread group, so let’s review it first. As shown in Figure 1, a Thread Group element contains the following parameters:

  • Number of threads.
  • The ramp-up period.
  • The number of times to execute the test.
  • When started, whether the test runs immediately or waits until a scheduled time. If the latter, the tread group element must also include the start and end times.
 

Figure 1. JMeter Thread Group. Click on thumbnail to view full-sized image.

Each thread executes the test plan independent of other threads. Therefore, a thread group is used to model concurrent users. If the client machine running JMeter lacks enough computing power to model a heavy load, JMeter’s distributive testing feature allows you to control multiple remote JMeter engines from a single JMeter console.

The ramp-up period tells JMeter the amount of time to create the total number of threads. The default value is 0. If the ramp-up period is left unspecified, i.e., the ramp-up period is zero, JMeter will create all the threads immediately. If the ramp-up period is set to T seconds, and the total number of threads is N, JMeter will create a thread every T/N seconds.

Most of a thread group’s parameters are self-explanatory, but the ramp-up period is a bit weird, since the appropriate number is not always obvious.

For one thing, the ramp-up period should not be zero if you have a large number of threads. At the beginning of a load test, if the ramp-up period is zero, JMeter will create all the threads at once and send out requests immediately, thus potentially saturating the server and, more importantly, deceivingly increasing the load. That is, the server could become overloaded, not because the average hit rate is high, but because you send the all the threads’ first requests simultaneously, causing an unusual initial peak hit rate. You can see this effect with a JMeter Aggregate Report listener.

As this anomaly is not desirable, therefore, the rule of thumb for determining a reasonable ramp-up period is to keep the initial hit rate close to the average hit rate. Of course, you may need to run the test plan once before discovering a reasonable number.

By the same token, a large ramp-up period is also not appropriate, since the peak load may be underestimated. That is, some of the threads might not have even started, while some initial threads have already terminated. ” jo — >

So, how do you verify that the ramp-up period is neither too small nor too large? First, guess the average hit rate and then calculate the initial ramp-up period by dividing the number of threads by the guessed hit rate. For example, if the number of threads is 100, and the estimated hit rate is 10 hits per second, the estimated ideal ramp-up period is 100/10 = 10 seconds. How do you come up with an estimated hit rate? There is no easy way. You just have to run the test script once first.

Second, Add an Aggregate Report listener, shown in Figure 2, to the test plan, because it contains the average hit rate of each individual request (JMeter samplers). The hit rate of the first sampler (e.g., an HTTP request) is closely related to the ramp-up period and the number of threads. Adjust the ramp-up period so the hit rate of the test plan’s first sampler is close to the average hit rate of all other samplers.

 

Figure 2. JMeter Aggregate Report. Click on thumbnail to view full-sized image.

Third, verify in the JMeter log (located in JMeter_Home_Directory/bin) that the first thread that finishes does indeed finish after the last thread starts. The time difference between the two should be as far apart as possible.

In summary, the determination of a good ramp-up time is governed by the following two rules:

  • The first sampler’s hit rate should be close to the average hit rate of other samplers, thereby preventing a small ramp-up period
  • The first thread that finishes does indeed finish after the last thread starts, preferably as far apart as possible, thereby preventing a large ramp-up period

Sometimes the two rules conflict with each other. That is, you simply cannot find a suitable ramp-up period that passes both rules. A trivial test plan usually causes this problem, because, in such a plan, you lack enough samplers for each thread; thus, the test plan is too short, and a thread quickly finishes its work.

User think time, timer, and proxy server
An important element to consider in a load test is the think time,or the pause between successive requests. Various circumstances cause the delay: user needs time to read the content, or to fill out a form, or to search for the right link. Failure to properly consider think time often leads to seriously biased test results. For example, the estimated scalability, i.e., the maximum load (concurrent users) that the system can sustain, will appear low.

JMeter provides a set of timer elements to model the think time, but a question still remains: how do you determine an appropriate think time? Fortunately, JMeter offers a good answer: the JMeter HTTP Proxy Server element.

The proxy server records your actions while you browse a Web application with a normal browser (such as FireFox or Internet Explorer). In addition, JMeter creates a test plan when recording your actions. This feature is extremely convenient for several purposes:

  • You don’t need to create a request manually, especially those tedious form parameters. (However, non-English parameters may not work correctly.) JMeter will record everything in the auto-generated requests, including hidden fields.
  • In the generated test plan, JMeter includes all the browser-generated HTTP headers for you, such as User-Agent (e.g., Mozilla/4.0), or AcceptLanguage (e.g., zh-tw,en-us;q=0.7,zh-cn;q=0.3).
  • JMeter can create timers of your choice, where delay time is set according to the actual delay during the recording period.

Let’s see how to configure JMeter with the recording feature. In the JMeter console, right-click the WorkBench element and add the HTTP Proxy Server element. Note that you right-click the WorkBench element, not the Test Plan element, because the configuration here is for recording, not for an executable test plan. The HTTP Proxy Server element’s purpose is for you to configure the browser’s proxy server so all requests go through JMeter.

As illustrated in Figure 3, several fields must be configured for the HTTP Proxy Server element:

  • Port:The listening port used by the proxy server.
  • Target Controller:The controller where the proxy stores the generated samples. By default, JMeter will look for a recording controller in the current test plan and store the samples there. Alternatively, you can select any controller element listed in the menu. Usually, the default is okay.
  • Grouping:How you would like to group the generated elements in the test plan. Several options are available, and the most sensible one is probably “Store 1st sampler of each group only,” otherwise, URLs embedded in a page such as those for images and JavaScripts will be recorded as well. However, you may want to try the default “Do not group samples” option to find out what exactly JMeter creates for you in the test plan.
  • Patterns to Includeand Patterns to Exclude: Help you filter out some unwanted requests.
 

Figure 3. JMeter Proxy Server. Click on thumbnail to view full-sized image.

When you click the Start button, the proxy server starts and begins recording the HTTP requests it receives. Of course, before clicking Start, you must configure your browser’s proxy server setting.

You can add a timer as a child of the HTTP Proxy Server element, which will instruct JMeter to automatically add a timer as a child of the HTTP request it generates. JMeter automatically stores the actual time delay to a JMeter variable called T,so if you add a Gaussian random timer to the HTTP Proxy Server element, you should type ${T} in the Constant Delay field, as shown in Figure 4. This is another convenient feature that saves you a lot of time.

 

Figure 4. Add a Gaussian random timer to the HTTP Proxy Server element. Click on thumbnail to view full-sized image.

Note that a timer causes the affected samplers to be delayed. That is, the affected sampling requests are not sent before the specified delay time has passed since the last received response. Therefore, you should manually remove the first sampler’s generated timer since the first sampler usually does not need one.

Before starting the HTTP proxy server, you should add a thread group to the test plan, and then, to the thread group, add a recording controller, where the generated elements will be stored. Otherwise, those elements will be added to WorkBench directly. In addition, it is important to add an HTTP Request Defaults element (a Configuration element) to the recording controller, so that JMeter will leave blank those fields specified by the HTTP request defaults.

After the recording, stop the HTTP proxy server; right-click the Recording Controller element to save the recorded elements in a separate file so you can retrieve them later. Don’t forget to resume your browser’s proxy server setting.

Specify response time requirements and validate test results
Although not directly related to JMeter, specifying response-time requirements and validating test results are two critical tasks of load testing, with JMeter being the bridge that connects them.

In the context of Web applications, response time refers to the time elapsed between the submission of a request and the receipt of the resulting HTML. Technically, response time should include time for the browser to render the HTML page, but a browser typically displays the page piece by piece, making the perceived response time less. In addition, typically, a load test tool calculates the response time without considering rendering time. Therefore, for practical purposes of performance testing, we adopt the definition described above. If in doubt, add a constant to the measured response time, say 0.5 seconds.

There is a set of well-known rules for determining response time criteria:

  • Users do not notice a delay of less than 0.1 second
  • A delay of less than 1 second does not interrupt a user’s flow of thought, but some delay is noticed
  • Users will still wait for the response if it is delayed by less than 10 seconds
  • After 10 seconds, users lose focus and start doing something else

These thresholds are well known and won’t change since they are directly related to the cognitive characteristics of humans. Though you should set your response time requirements in accordance with these rules, you should also adjust them for your particular application. For example, Amazon’s homepage abides by the rules above, but because it prefers a more stylistic look, it sacrifices a little response time.

At first glance, there appears to be two different ways to specify response time requirements:

  • Average response time
  • Absolute response time; that is, the response times of all responses must be under the threshold

Specifying average response time requirements is straightforward, but the fact that this requirement fails to take into account data variation is disturbing. What if the response time of 20 percent of the samples is more than three times the average? Note that JMeter calculates the average response time as well as the standard deviation for you in the Graph Results listener.

On the other hand, the absolute response time requirement is quite stringent and statistically not practical. What if only 0.5 percent of the samples failed to pass the tests? Again, this is related to sampling variation. Fortunately, a rigorous statistical method does consider sampling variation: the confidence interval analysis.

Let’s review basic statistics before going further.

The central limit theorem
The central limit theorem states that if the population distribution has mean μ and standard deviation σ, then, for sufficiently large n (>30), the sampling distribution of the sampling mean is approximately normal, with mean μmean= μ and standard deviation σmean= σ/√n.

Note that the distribution of the sampling meanis normal. The distribution of the sampling itself is not necessarily normal. That is, if you run your test script many times, the distribution of the resulting average response times will be normal.

Figures 5 and 6 below show two normal distributions. In our context, the horizontal axis is the sampling mean of response time, shifted so the population mean is at the origin. Figure 5 shows that 90 percent of the time, the sampling means are within the interval ±Zσ, where Z=1.645 and σ is the standard deviation. Figure 6 shows the 95-percent case, where Z=2.576. For a given probability, say 90 percent, we can look up the corresponding Z value with a normal curve and vice versa.

 

Figure 5. Z value for 90 percent

 

 

Figure 6. Z value for 95 percent

A few Web sites for normal curve calculation are listed in Resources. Note that in those sites, we can calculate the probability of either a symmetric bounded region (e.g., -1.5 < X < 1.5 ) or a cumulated area (e.g. X < 1.5 ). You may also look up approximate values from the tables below.

Table 1. Standard deviation range corresponding to a given confidence interval

Confidence Interval Z
0.800 ±1.28155
0.900 ±1.64485
0.950 ±1.95996
0.990 ±2.57583
0.995 ±2.80703
0.999 ±3.29053

Table 2. Confidence interval corresponding to given standard deviation

Z Confidence Interval
1 0.6826895
2 0.9544997
3 0.9973002
4 0.9999366
5 0.9999994

 

Confidence interval
The confidence interval is defined as [sampling mean – Z*σ/√n, sampling mean + Z*σ/√n]. For example, if the confidence interval is 90 percent, we can look up the Z value to be 1.645, and the confidence interval is [sampling mean – 1.645*σ/√n, sampling mean + 1.645*σ/√n], which means that 90 percent of the time, the (unknown) population mean is within this interval. That is, our measurement is “close.” Note that if σ is larger, the confidence interval will be larger, which means that it is more likely that the upper bound of the interval will exceed an acceptable value. That is, if σ is larger, most likely the result is not acceptable.

SUBHEAD3: Response time requirements

Let’s translate all this information into response time requirements. First, you can define the performance requirements like so:

The upper bound of the 95 percent confidence interval of the average response time must be less than 5 seconds.

Of course, you must add loading requirements and specify a particular scenario as well.

Now, after the performance tests, suppose you analyze the results and discover that the average response time is 4.5 seconds, while the standard deviation is 4.9 seconds. The sample size is 120. You then calculate the 95 percent confidence interval. By looking in Table 1, you find the Z value is 1.95996. Therefore the confidence interval is [4.5 – 1.95996*4.9/√120, 4.5 + 1.95996*4.9/√120], which is [3.62, 5.38]. The result is not acceptable, even though the average response time looks pretty good. In fact, you can verify that the result is not acceptable even for an 80 percent confidence interval. As you can see, applying confidence interval analysis gives you a much more precise method to estimating the quality of your tests.

Note that in the context of Web applications, to measure a scenario’s response time, we typically need to instruct the load testing tool to send multiple requests, for example:

  1. Login
  2. Display a form
  3. Submit the form

Assume we are interested in Request 3. To conduct a confidence interval analysis, we need the average response time and the standard deviation of all of Request 3’s samples, not the statistics of all samples.

Note that JMeter’s Graph Result listener calculates the average response time and standard deviation of all requests. JMeter’s Aggregate Report listener calculates the average response time of individual samplers for you, but, unfortunately, does not give the standard deviation.

In summary, specifying the requirement of average response times alone is dangerous, since it says nothing about data variation. What if the average response time is acceptable but your confidence interval is only 75 percent? Most likely, you cannot accept the result. Applying the confidence internal analysis, however, gives you much more certainty.