Wednesday 27 April 2011

Handling legacy RMI using Spring remoting

For a POC, I had to work with an external system (J/XFS Device Services) which used RMI as the communication mechanism where exporting and consuming RMI services seemed little slumbering. Since Spring remoting handles it quite good, I used it to get the stuff done quickly. I have given a stripped-down version here as a simple example for RMI using Spring remoting.

The following interface InputDevice is the service interface which the clients will have access to. This is the interface which supposed to extend Remote interface in the conventional RMI implementation either directly or indirectly. It is a very simple interface here with the overhead left to Spring.

InputDevice.java

public interface InputDevice {



 public boolean open();

 

 public byte[] read();

 

 public boolean close();

 

}

A simple implementation of this InputDevice is a Scanner which is given below. This is actually the implementation which would provide the required functionality to the client which would invoke the methods in InputDevice.

Scanner.java

public class Scanner implements InputDevice {



 @Override

 public boolean open() {

  // code which opens the scannner

  return true;

 }



 @Override

 public byte[] read() {

  // code which scans the data. Only dummy data now :-(

  String message = "SCANNED_DATA_DUMMY_" + System.currentTimeMillis();

  return message.getBytes();

 }



 @Override

 public boolean close() {

  // code which closes the scanner

  return true;

 }



}


Having the remote service interface and an implementation ready, now let's focus on how to export this service through RMI. The following DeviceServer class uses the RmiServiceExporter to export the InputDevice service.

The main method in this class can be executed which would export the service at port 54321 in the localhost and ready to serve the requests.

DeviceServer.java

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStreamReader;

import java.rmi.RemoteException;



import org.springframework.remoting.rmi.RmiServiceExporter;



public class DeviceServer {



 public static void main(String[] args) {

  try {

   RmiServiceExporter serviceExporter = exportService();

   System.out.println("Server started. Enter stop to stop it.");

   BufferedReader reader = (new BufferedReader(new InputStreamReader(

     System.in)));

   while (!"stop".equalsIgnoreCase(reader.readLine()))

    ;

   serviceExporter.destroy();

  } catch (IOException e) {

   throw new RuntimeException(e.getCause());

  }

 }



 private static RmiServiceExporter exportService() throws RemoteException {

  RmiServiceExporter serviceExporter = new RmiServiceExporter();

  serviceExporter.setServiceInterface(InputDevice.class);

  serviceExporter.setService(new Scanner());

  serviceExporter.setServiceName("ScannerService");

  serviceExporter.setRegistryPort(54321);

  serviceExporter.afterPropertiesSet();

  return serviceExporter;

 }

}

This simple server would run until stop is entered.

The following simple client consumes the InputDevice service through RMI. It uses RmiProxyFactoryBean to consume the service. Here is the code which can be run to see it consume the service.

DeviceClient.java

import org.springframework.remoting.rmi.RmiProxyFactoryBean;



public class DeviceClient {



 public static void main(String[] args) {

  InputDevice inputDevice = getInputDevice();

  boolean opened = inputDevice.open();

  if (opened) {

   byte[] data = inputDevice.read();

   System.out.println("Data read:" + new String(data));

   inputDevice.close();

  }

 }



 private static InputDevice getInputDevice() {

  RmiProxyFactoryBean proxyFactory = new RmiProxyFactoryBean();

  proxyFactory.setCacheStub(false);

  proxyFactory.setServiceInterface(InputDevice.class);

  proxyFactory.setServiceUrl("rmi://127.0.0.1:54321/ScannerService");

  proxyFactory.afterPropertiesSet();

  InputDevice inputDevice = (InputDevice) proxyFactory.getObject();

  return inputDevice;

 }

}

Using Spring bean configuration, the same could be achived configuring the RmiServiceExporter and RmiProxyFactoryBean beans.


Saturday 9 April 2011

Simple Java deadlock detector

Thanks to extensive testing, Application MultithreadedMessenger from QualitySoftware Inc., has been running without any critical bugs since it's go-live last month. The customer, MrBigBank is happy faced since it is one of the important pieces of his solution architecture. One fine early morning MultithreadedMessenger stopped working. After an hour, a smart guy at the back office realised the application is busy fighting within itself and not ready to serve the incoming requests. Knowing that it is not going to return ever, he restarts the application and phones the vendor's help desk for a solution.

Though deadlocks related bugs are rare in a production quality system, when they appear, sometime there would be hours of complex investigation required. Java provides enough help in identifying threads which are deadlocked through it's management library. The following is a simple example where we create a dead lock situation and a deadlock detector to detect and report when the deadlock occurs.

Class Messenger is a mock application which provides send and receive methods to handle the messaging. This class is written (well, badly!) just to create a situation where two threads will get into waiting for each other.

Messenger.java

public class Messenger {

 private Lock senderLock = new Lock();
 private Lock receiverLock = new Lock();

 public void send(String message) {
  synchronized (senderLock) {
   synchronized (receiverLock) {
    //some code here
   }
  }
 }

 public String receive() {
  synchronized (receiverLock) {
   synchronized (senderLock) {
    // some code here
    return "SUCCESS";
   }
  }
 }
}

class Lock {
}

A DeadlockDetector uses the managed beans provided by java.lang.management package to detect the threads which are deadlocked and reports with the thread information.

DeadlockDetector.java

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

public class DeadlockDetector implements Runnable {

 private volatile boolean _detected = false;

 public synchronized boolean detected() {
  return _detected;
 }

 @Override
 public void run() {
  while (true) {
   ThreadMXBean tmx = ManagementFactory.getThreadMXBean();
   long[] deadlockedThreads = tmx.findDeadlockedThreads();
   if (deadlockedThreads != null) {
    ThreadInfo[] threadInfos = tmx.getThreadInfo(deadlockedThreads);
    for (ThreadInfo threadInfo : threadInfos) {
     System.out.println(threadInfo);
    }
    _detected = true;
    break;
   }
  }
 }
}

Finally, DeadlockTest class which runs the Messenger application and the DeadlockDetector to halt and report when the deadlock occurs.

DeadlockTest.java

public class DeadlockTest {

 public static void main(String[] args) {
  
  DeadlockDetector deadlockDetector = new DeadlockDetector();
  new Thread(deadlockDetector).start();

  final Messenger messenger = new Messenger();

  while (!deadlockDetector.detected()) {

   new Thread(new Runnable() {

    @Override
    public void run() {
     String message = "blah blah";
     messenger.send(message);
    }
   }, "foo").start();

   new Thread(new Runnable() {

    @Override
    public void run() {
     String message = messenger.receive();
    }
   }, "bar").start();
  }
 }
}

Much like the same way, other managed beans for memory, gc,class loading, compilation and runtime are accessible with simple test classes.

The same managed beans feed information to utilities like jconsole or jvisualvm, making it possible to completely monitor and manage the JVM.