Monday, February 21, 2011

Java Observer Design Pattern using Rmi

Recently I had a requirement in my work, where I had to PUSH data from server to client, when new data arrives.
Now obviously I considered using the OBSERVER design pattern to solve my problem.
The clients were remote but in the same company network. And there was no messaging service available.

I could not find a way to use observer pattern over HTTP, so I chose to use RMI.

Implementing Observer design pattern over RMI is a bit tricky. After searching on the internet,
I found out that a good samaritan has already done the job.
Here is the link, where I took the basic codebase from
http://sites.google.com/site/jamespandavan/Home/java/sample-remote-observer-based-on-rmi

Now there is a problem in this design approach. Once you start the server and the client, the client perfectly receives the messages pushed by the server.
But if the server goes down for some reason, then there is no way for the client to know it. Yes, there is no RemoteException or ConnectException thrown in this case.
The client keeps itself running, wondering, why it is not getting any data from server.
The existing client will not receive any data, even when the server is back up.

To solve this problem, I used the last timestamp, when the client received PUSH data from server. If the time is more than a specified time, client considers the connection dead and tries to reconnect to the server.
This trying to reconnect goes on forever, until the server goes up. Then everything works fine, just like before, as if nothing happened.

Few words before sharing the codebase:
1) Don't forget to make your data serializable.
2) Generate the rmi stub class using the rmic command. Details about this command can be found in the Oracle website here.
2) After the server code is written, create a jar file, containing the RMIService interface, the Model class and the rmi stub.
3) Clients on separate JVM, have to put this jar in their classpath.

Here's the code, more or less the same code found in the above link, with slight modifications:

Running the Server/Client:

java -Djava.security.licy=security.policy RmiServer
java -Djava.security.licy=security.policy RmiClient
The Security Policy file:

grant
{
    Permission java.security.AllPermission;
};
The POJO DTO (Don't forget the serializable part)

import java.io.Serializable;

public class Model implements Serializable{

 /**
  * 
  */
 private static final long serialVersionUID = 1L;
 private String name;
 private int id;
 private long lastTimsStamp;
 public Model(String name, int id,long lastTimsStamp) {
  super();
  this.name = name;
  this.id = id;
  this.lastTimsStamp = lastTimsStamp;
 }
 public String getName() {
  return name;
 }
 public int getId() {
  return id;
 }
 public long getLastTimsStamp() {
  return lastTimsStamp;
 }
 
 
}
Remote observer interface

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface RemoteObserver extends Remote {

    void update(Object observable, Object updateMsg) throws RemoteException;

}
RMI client program implementing the interface

import java.net.MalformedURLException;
import java.rmi.ConnectException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import rmisubject.Model;
import rmisubject.RemoteObserver;
import rmisubject.RmiSubjectService;

public class RmiObserverClient extends UnicastRemoteObject implements RemoteObserver {
    
 /**
  * If the client hasn't received any data for the span of connectionCheckInterval, it will try to reconnect the server.
  * It will keep trying until the server is not back up.
  * @param connectionCheckInterval
  * @throws RemoteException
  */
 public RmiObserverClient(long connectionCheckInterval) throws RemoteException{
        super();
        this.connectionCheckInterval = connectionCheckInterval;
    }
    private static long lastTimeStamp;
    private static final long serialVersionUID = 1L;
    private static long connectionCheckInterval = 10*1000;
    private static RmiSubjectService remoteService;
    Thread checkConnectionAlive = new Thread() {
       @Override
       public void run() {
           while (true) {
               try {
                   Thread.sleep(connectionCheckInterval);
               } catch (InterruptedException e) {
                   // ignore
               }
               long currentTimeStamp = System.currentTimeMillis();
               
               System.out.println("Inside client checkConnectionAlive : " + currentTimeStamp);
               
               if (currentTimeStamp - lastTimeStamp>connectionCheckInterval )subscribe();
           }
       };
   };
   
    public static void main(String[] args) {
     try {
      RmiObserverClient rc = new RmiObserverClient(10 * 1000);
   rc.subscribe();
   rc.checkConnectionAlive.start();
  } catch (RemoteException e) {
   e.printStackTrace();
  }
     
    }
    public void subscribe(){
     if (System.getSecurityManager() == null)
            System.setSecurityManager(new RMISecurityManager());
        try {
            remoteService = (RmiSubjectService) Naming.lookup("//localhost:9999/RmiService");
            remoteService.addObserver(this);
        } catch (ConnectException ex) {
            ex.printStackTrace();
        }catch (RemoteException e) {
          e.printStackTrace();
  }catch (MalformedURLException e) {
   e.printStackTrace();
  }catch (NotBoundException e) {
   e.printStackTrace();
  }
    }
    @Override
    public void update(Object observable, Object updateMsg)
            throws RemoteException {
        System.out.println("===============start message=============");
        Model model = (Model)updateMsg;
        System.out.println(model.getName());
        System.out.println(model.getId());
        lastTimeStamp = model.getLastTimsStamp();
        System.out.println(lastTimeStamp);
        System.out.println("===============end message=============");
    }
}
RMI service interface

public interface RmiSubjectService extends Remote {

   void addObserver(RemoteObserver o) throws RemoteException;
}
RMI server (the observable)

import java.io.Serializable;
import java.rmi.RMISecurityManager;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Observable;
import java.util.Observer;

public class RmiSubjectImpl extends Observable implements RmiSubjectService {

    private class WrappedObserver implements Observer, Serializable {

        private static final long serialVersionUID = 1L;
       
        private RemoteObserver ro = null;

        public WrappedObserver(RemoteObserver ro) {
            this.ro = ro;
        }

        @Override
        public void update(Observable o, Object arg) {
            try {
                ro.update(o.toString(), arg);
            } catch (RemoteException e) {
                System.out
                        .println("Remote exception removing observer:" + this);
                o.deleteObserver(this);
            }
        }

    }

    @Override
    public void addObserver(RemoteObserver o) throws RemoteException {
        WrappedObserver mo = new WrappedObserver(o);
        addObserver(mo);
        System.out.println("Added observer:" + mo);
    }

    Thread thread = new Thread() {
      private int i=0;
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(5 * 1000);
                } catch (InterruptedException e) {
                    // ignore
                }
                setChanged();
                notifyObservers(new Model("model", i++,System.currentTimeMillis()));
                //notifyObservers(new Date());
            }
        };
    };

    public RmiSubjectImpl() {
        thread.start();
    }

    private static final long serialVersionUID = 1L;

    public static void main(String[] args) {
        if (System.getSecurityManager() == null)
            System.setSecurityManager(new RMISecurityManager());
        try {
            Registry rmiRegistry = LocateRegistry.createRegistry(9999);
            RmiSubjectService rmiService = (RmiSubjectService) UnicastRemoteObject
                    .exportObject(new RmiSubjectImpl(), 9999);
            rmiRegistry.bind("RmiService", rmiService);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}

Hope this helps. Let me know if it doesn't.

Wednesday, February 16, 2011

How to Convert XML attributes to elements using XSLT

Recently I was using Apache Digester to convert( Transform) a XML content into List of Objects.
Out of all the APIs I tried, I found digester was the best solution for my of requirement.
For people who didnot use Digester before, I would recommend giving it a try. It will make your life
easier. You can go home from work early and spend some quality time with quality people.
I will probably post some code snippets on how to use Digester, but this post is not about that.

Coming back to point.
Just like any other XML, the XML content I was trying to parse, contained few attributes, alongwith the elements.
Now digester might be having some features to take care of attributes, but I found it hard to follow.
I created the following XSLT instead. Which, when applied to a XML, transforms all it's attributes into TAGS.
The XSLT is generic enough to be used on any xml content.
If you are not sure, how to apply this xslt to your XML, keep reading. I have some sample code too.

Firstly, the XSLT content:



<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet 
    version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" encoding="UTF-8"/>
  <xsl:strip-space elements="*" />
  <xsl:template match="*">
  <xsl:copy >
      <xsl:if test="@*">
        <xsl:for-each select="@*">
          <xsl:element name="{name()}" >
            <xsl:value-of select="." />
          </xsl:element>
        </xsl:for-each>
      </xsl:if>
      <xsl:apply-templates />
    </xsl:copy>
    </xsl:template>
</xsl:stylesheet> 



It is a small XSLT and should be easy to understand. In case you have problem understanding the logic, ask me.

Sample code to transform any XML using any XSLT:

Prerequisite:-
A JAVA environment offcourse. Lets not get into that deep. Amongst all the libraries that come with the distribution, the one you actively need is

rt.jar

Here is some sample code. The method names are self-explanatory.


 public static String convertAllAttributesToElements(String xmlContent) throws TransformerException{
   javax.xml.transform.Source xmlSource =new javax.xml.transform.stream.StreamSource(new StringReader(xmlContent));
   javax.xml.transform.Source xsltSource =new javax.xml.transform.stream.StreamSource(xsltFile);
   return transformContent(xmlSource,xsltSource);
 }
 
 public static String convertAllAttributesToElements(File xmlFile) throws TransformerException{
   javax.xml.transform.Source xmlSource = new javax.xml.transform.stream.StreamSource(xmlFile);
   javax.xml.transform.Source xsltSource = new javax.xml.transform.stream.StreamSource(xsltFile);
   return transformContent(xmlSource,xsltSource);
 }
 
 private static String transformContent(javax.xml.transform.Source xmlSource,javax.xml.transform.Source xsltSource) throws TransformerException{
  String modifiedContent = null;
  javax.xml.transform.TransformerFactory transFact = javax.xml.transform.TransformerFactory.newInstance();
  ByteArrayOutputStream outStream = new ByteArrayOutputStream();
  StreamResult result = new javax.xml.transform.stream.StreamResult(outStream);
  javax.xml.transform.Transformer trans = transFact.newTransformer(xsltSource);
  trans.transform(xmlSource, result);
  byte[] b = ((ByteArrayOutputStream)result.getOutputStream()).toByteArray();  
  modifiedContent = new String (b);
  //removing any line feed or spaces to save bytes.
  modifiedContent = modifiedContent.replaceAll("[\\n\\r\\s]+","");
  return modifiedContent;
 }
 


I hope this helps guys. Let me know if there are any issues.