Let's talk about exceptions ...
How do you handle exceptions? Do you think upfront about the type of exceptions that you want to catch or do you just let the outside world handle it?

-- Jeroen van Bergen in JW Blogs

Newsletter sign-up

Sign up for our technology specific newsletters.

Enterprise Java
View all newsletters

Email Address:

The smart approach to distributed performance monitoring with Java

Discover a lightweight, noninvasive mechanism for performance monitoring and data caching in RMI and Java IDL applications

If you're developing distributed applications, you probably want to measure the performance characteristics of your applications to find out which remote methods -- on which objects, on which machines -- take longest to run. To accomplish this, you could use a distributed profiling tool, but such products tend to be expensive and difficult to configure. You could also make changes to the client application to time each method call, but this is intrusive. Fortunately, there is a better way -- smart stubs. The smart stub approach is cheap to implement, lightweight, and requires no modification of the original client and server code.

Whilst several commercial CORBA object request brokers (ORBs) such as VisiBroker and Orbix provide smart stub (or smart proxy) implementations, this isn't a feature provided out of the box for RMI and Java IDL. With RMI and Java IDL support for smart stubs, you'd have the ability to intercept remote method calls in a completely noninvasive and fully reversible way. I have so far put this idea to use in security applications (denying access to certain remote objects according to IP address) and in audit trailing (writing a usage profile to a log file), as well as in the context of distributed performance monitoring, as described here.

This article introduces stubs, describes what it means for them to be smart, and tells how they may be used for noninvasive performance monitoring. It concludes by offering a technique for adding smart stub behavior to RMI and Java IDL applications.

What are stubs?

In any distributed environment such as RMI or CORBA, client applications communicate with server-side objects through stubs. Each stub, a client-side representation of an associated server-side object, hides the complex network communications between the client and server, as illustrated in Figure 1.

Figure 1. Client/server communication with stubs



To be more precise, communication occurs through proxies, where a proxy object is an instance of a stub class. The terms stub and proxy are often used interchangeably.

Stub classes are generated as part of the distributed development process. The stub generator for RMI applications is called rmic, while for Java IDL it is called idltojava.

What are smart stubs?

Smart stubs are direct replacements for the original stubs that perform the same function as the originals with additional behavior. They can be added and removed easily with no code changes within the client application or the server object. The additional behavior may be the timing of method calls, or caching of data so that each client request does not necessarily result in a costly invocation across the network.

The Figure 2 shows an example client-server Bank application that displays a list of accounts and allows updates to balances. The pop-up Bank Client Monitor displays average call times to the remote methods on the Bank server object, along with the percentage of calls that were cached locally.

Figure 2. Bank applet and pop-up Client Monitor



Caching kicks in when a trigger-happy user repeatedly presses the Refresh button, since we don't want to make a costly remote invocation every time the button is pressed. As the cached percent increases, the average call time decreases.

At this point, I should emphasize the fact that the call timer, the local cache, and even the Client Monitor window are handled entirely within the smart stub. No changes whatsoever have been made to the original client or server code. Furthermore, the client application has no knowledge of the Client Monitor window that appears automatically when the stub is instantiated, and the server object is not aware that its responses are being timed and cached.

A smart stubs implementation for RMI

Using RMI as an example, a remote server object will have an interface (IBank.java):

public interface IBank extends java.rmi.Remote
{
   public String[] getAllAccounts() throws java.rmi.RemoteException;
   public String getAccount(String accno) throws java.rmi.RemoteException;
   public void setAccount(String accno, int newAmount) throws java.rmi.RemoteException;
}


and an implementation (Bank.java):

public class Bank extends UnicastRemoteObject implements common.IBank
{
   public Bank() throws RemoteException {...}
   public String[] getAllAccounts() throws java.rmi.RemoteException
   { // return all account details }
   public String getAccount(String accno) throws java.rmi.RemoteException
   { // return a single account balance }
   public void setAccount(String accno, int newAmount) throws java.rmi.RemoteException
   { // update an account with a new amount }
}


Running the rmic command on the compiled Bank implementation class (Bank.class) produces a stub called Bank_Stub.class. If you run the rmic utility with the -keep option, you get the original Java source file (Bank_Stub.java) for this stub.

To make the stub smart, all you need to do is edit the Bank_Stub.java file and recompile it. Modifying the original stub is easier said than done, however. First of all, the Java code is difficult to understand and it is not obvious where changes should be made, as the code segment below demonstrates:

public final class Bank_Stub extends java.rmi.server.RemoteStub implements common.IBank, java.rmi.Remote
...
public final java.lang.String getAccount(java.lang.String $param_String_1) throws java.rmi.RemoteException
{
  try {
    if (useNewInvoke) {
      Object $result = ref.invoke(this, $method_getAccount_0, new java.lang.Object[] {$param_String_1}, 6316030027643029302L);
      return ((java.lang.String) $result);
    } else {
      java.rmi.server.RemoteCall call = ref.newCall((java.rmi.server.RemoteObject) this, operations, 0, interfaceHash);
      try {
        java.io.ObjectOutput out = call.getOutputStream();
        out.writeObject($param_String_1);
      } catch (java.io.IOException e) {
        throw new java.rmi.MarshalException("error marshalling arguments", e);
        }
      ref.invoke(call);
...


Secondly, the code begins with a stark warning:

// Stub class generated by rmic, do not edit.
// Contents subject to change without notice.


This is good, but not helpful, advice. I will bend this rule slightly and make two simple edits: I'll change the name of the class from Bank_Stub to Bank_Smub and remove the final keyword from each method. The actual content of each stub method is (wisely) left intact.

Resources