13.6 More Factors That Affect Performance

The following sections discuss some aspects of the application that may not immediately strike you as part of the performance of the application. But they do affect the user's perception of the application performance, and so are relevant.

13.6.1 User Interface Usability

The application's user interface has a significant effect on the user's perception of performance. The time required to navigate through the user interface to execute some functionality is seen by the user as part of the application's response time. If window and menu navigation is difficult, performance is seen to be bad (and, actually, it is bad).

The user interface should support the natural flow of the user's activity; otherwise, you are forcing the user to perform less efficiently. Improving only the navigability of the user interface, even with no other changes to the application, improves the perceived performance of an application.

13.6.2 Training

Training users to use the application is also a performance issue. Without proper training, users may not use the application efficiently and may compare the application unfavorably with another application they are comfortable with. Since they are comparing similar functionality, the user immediately focuses on the differences. The main difference, of course, is the perceived performance.

The user never thinks he is untrained. He simply feels that executing some function in your application takes forever, as he stumbles through menu options trying to find what he wants, fills in forms incorrectly, etc. The result is a perception of bad performance.

Note that making help desks available is an essential part of the training program. Training is seldom so thorough that all parts of the application are covered in enough detail, and it is also common for people to forget some of their training. A help desk keeps the users from getting lost and giving up on the most efficient route to solve their tasks.

13.6.3 Server Downtime

If people can't start a piece of software, they can get frustrated, but they don't normally view this as bad performance. Most people would instead get annoyed at the quality of the software. But when servers are not running, this can be perceived differently. Sometimes a server that isn't running is perceived as bad-quality software, but sometimes it is seen as poor performance. If the server stops responding in the middle of processing, this is invariably seen as slow performance. Consider your own response to a stalled download from an HTTP server.

Avoiding server downtime is a robustness issue as well as a performance issue. Servers should be designed to avoid unnecessary downtime and minimize necessary downtime. One issue when running servers is altering their configuration and patching them. If you need to stop a server from running while you make changes, this affects its perceived performance. You must signal it to stop accepting requests and either wait for current requests to terminate or forcibly terminate all requests; either way, this causes interruptions in service to the clients.[15]

[15] Load-balancing products often provide features that allow server maintenance with minimum downtime.

It is possible to design servers that can be reconfigured and patched with no downtime. Designing for reconfiguration on the fly is easier. In this case, you typically have a configuration file. One common solution is for the server to periodically test the timestamp of the configuration file and reread it if the timestamp changes. This solution also provides good security (as presumably, the configuration file can be changed only by authorized persons). Another solution is for the server to recognize and accept a particular signal as a sign to reset its configuration. In most servers using this solution, the signal is an operating-system signal that is trapped. However, Java does not support operating-system signal handling, so if you steer down this path, you need either to install operating-system handlers yourself (using the Java native interface) or use another communication mechanism, such as sockets. If you do use sockets, you need to consider security aspects; you don't want unauthorized persons triggering a server reconfiguration.

Patching a running server is more complex. You need to provide some level of indirection on how the request-processing classes (and all the classes they depend on) are loaded. The most basic solution is to use the configuration file to list names of all classes. Then the server must be built using Class.forName( ) to access and create any classes and instances. This way, providing a new version requires only changing the class names in the configuration (in an atomic way to avoid corruption).

A more sophisticated solution is to use different ClassLoaders. Note that any particular class in Java is identified by its package and class name and by its ClassLoader. It is possible to have classes with the same package and class names loaded multiple times in the same VM (whether the implementation is the same or different for those classes) using multiple ClassLoader instances. This is easiest in 1.2 and later, where there is a proliferation of ClassLoaders. A useful classloader for this type of runtime patching is the URLClassLoader:

//This method gets a new implementation of a RequestProcessor
//every time.
public RequestProcessor getNewRequestProcessor(  )
  URL[  ] urls = {new URL(...)};
  Class c = RequestServerMain.class;
  ClassLoader cl = c.getClassLoader(  );
  URLClassLoader xtra_cl = new URLClassLoader(urls , cl);
  c = xtra_cl.loadClass("RequestProcessor");
  RequestProcessor proc = (RequestProcessor) c.newInstance(  );
  return proc;
public void processRequest(Request aRequest, RequestProcessor proc)
  //Signal to get a new implementation of a request processor
  //by passing a null value in the <proc> variable
  if (proc =  = null)
    proc = getNewRequestProcessor(  );

In most cases, you will find that a customized classloader is the best solution, especially because you can include consistency checking within that classloader, as well as ensuring atomicity of changes. You can even provide unloading and loading of classes, which is probably the most sophisticated solution available for runtime patching.