Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

A roadmap to flexibly configurable apps

Give your client/server programs a variety of user interfaces and access methods

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Any program that processes user requests can be viewed as a client and a server. The client consists of feature code, UI code, and a UI layout. The server consists of server code and usually a database. UI code is responsible for acquiring user input and presenting user output. Feature code is the UI-independent part of the client; it fulfills user-requested transactions by invoking methods on one or more servers. Server code does the real work, such as managing a bank account, so it is often called the program's business logic.

You can configure these code pieces -- UI code, UI layout, and so forth -- in different ways across nodes. Figure 1 shows two common approaches. The upper part of Figure 1 shows one approach: The client code is an app on a desktop machine; the UI layout is created using Swing; and the server code is part of the app. (The term app is used here to mean an applet or an application.)

The bottom part shows another approach: The UI layout is HTML running in a browser on a desktop machine; the UI code and feature code are in a servlet running on a Web-server machine; and the server code and database run on a server-application machine. (Note: The term servlet derives from the fact that it runs in a Web server, not that it contains server code. Although a servlet can contain server code, its main function is UI-related -- namely, it interfaces to browsers.)

Figure 1. Two common program configurations

When you first start to develop a program, these code pieces often correspond one to one, so there is no strong motivation to keep them distinct. For instance, developers often intermix UI code and feature code. But as a program evolves, you may need to configure its pieces in different ways, support multiple UI styles, or provide features that use multiple servers. So keeping these pieces distinct from day one can provide great benefits down the road. However, that is easier said than done, for the following reasons:

  • A servlet gets and presents UI data using code that's different from a client app's code.

  • A client app supports a single user, whereas a servlet supports multiple users, one per servlet thread.

  • Extra machinery needs to be written in clients and servers to allow the use of multiple communication protocols.

  • The sheer complexity of delivering fast, robust, distributed programs can keep you too busy to attempt to keep the code pieces distinct.


This article presents guidelines on how to develop distributed programs that are flexibly configurable, fast, and robust. There are two levels of guidelines. The general guidelines help you effectively organize your program and, as a side effect, enable you to take full advantage of the Servit package (see Resources). The remaining guidelines explain how to use the Servit package. The main benefits of this server invocation tool are:

  • It can start multiple servers and the Remote Method Invocation (RMI) machinery from a single command line.

  • It takes care of the detailed machinery of using a server, like locating and instantiating server objects.

  • It helps you share feature code among servlets, applets, and applications.

  • It lets you define your server configuration through properties set outside your code. For example, during testing you might want to have an in-process server to facilitate debugging, but then use an RMI server when you release your program.

  • It optimizes server-object use by detecting session-stateless servers and by maintaining connection pools within multiple server-object clients (such as servlets).

  • It provides a framework for recovering from network errors.


Developing servers

Granularity of server methods

Accessing a "small" method in a remote server takes milliseconds rather than microseconds. Thus, you should define high-level server methods. If a single server can perform a user transaction, you should probably create a single-server method to do the work of this transaction. If the transaction calculates a number of output items, you should define a class that contains those items and return an object of that class, rather than using individual accessor methods (for example, getAccountBalance) to retrieve each output item.

Modifiable server state

Servers are usually multithreaded so that multiple client requests can be processed in parallel. If your server code can modify fields in a server thread, you must synchronize the code sections that use the modifiable state. Obviously, synchronization reduces the degree of possible client parallelism. So you should define a modifiable server state only when necessary. For example, a stock price monitor needs to maintain a database of those who register to receive stock price changes. On the other hand, servers that manipulate client-specific data, like client bank accounts, could often be developed without a modifiable state.

Normally, you would have to use synchronized methods or blocks to achieve any needed thread synchronization. But if your servers are accessed in process and only from servlets, a second synchronization mechanism is available to you.

Session state

You need to decide how the session state is managed. You essentially have two choices as to how to organize your server. It can be

  • Session stateless. This kind of server receives the client state only from method arguments and does not cache any data in server fields during a transaction. Thus, two client transactions executing in parallel can safely use the same server object. (In RMI terms, this means that all clients can perform operations on the server object returned by Naming.lookup; the clients do not each acquire a unique server object.)

  • Session stateful. This kind of server maintains the client state in the server object's fields. Servit will treat a server as session stateful if it has a public Server createSession() method. That method's job is to return a unique server object. Its code can be a simple return new ServerImpl();. The reference object used with createSession is called a factory object.


Loosely speaking, there are two types of session state:

  • Internal state. If a server object contains just an internal state, the only issue is the transaction integrity. That is, each client transaction needs its own server object so that two client transactions executing in parallel do not step on each other's state.

  • Identifying state. If a server object contains an identifying state, even a single user could need to access multiple server objects. For example, a user could need to access two bank account objects, one for checking and one for savings. A client inserts an identifying state into a server object after constructing it because there is no way to specify an identifying state while the server object is constructed. The bank account case might incorporate a setAccount method:

    Servit handle = factoryBank.startSession();
    Bank acct = (Bank)handle.getServer();
    acct.setAccount("Acct#");


Session-stateless servers initialize faster and use fewer resources. If your server can be coded this way, the extra work of maintaining all the states in arguments and local variables may well be worth it.

Server-access methods

This section describes how to write servers that can be accessed in process, by RMI, and by sockets. For in-process access, server objects are constructed within the client's Java Virtual Machine. RMI access means that the client can invoke methods on remote server objects. With a socket-based server, the client and server communicate via application-defined socket messages. For example, see the code in Broker*.java in the Servit kit's demo (see Resources).

There are some general rules that you must follow. First, you must create a public interface named Server.It should contain declarations of all the client-accessible methods of your server. Also, any method that passes or returns a server object should specify Server (and not ServerImpl). You must also create a public class that implements Server -- this is the class that actually contains your server code. It must be named ServerImpl.

The remaining rules are access-method specific. If you follow all these rules, your server can be accessed all three ways.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources