Tuesday, July 31, 2012

Ruminating on Session Replication

In any web application cluster, we need to configure Session Replication for failover and high availability. While configuring session replication, the following common queries often come across our minds. I have attempted to answer these questions in a product agnostic way.

Q) Is the session replicated sychronously or asynchronously?
A) Session replication can occur either synchronously or asynchronously. In sychronous replication, the request does not return until the session has been replicated across all members of the cluster. This obviously has performance implications. In asynchronous replication, the response is returned and the session data is queued to be replicated across the cluster nodes. Typically asynchronous replication is the default mode on most app servers and this is configured with "Session Affinity".

Q) Is the entire session replicated or only the delta of what has changed?
A) It could be quite difficult to keep track of all session data modifications accurately. For e.g. someone would just refer a object in session and change its properties, without calling setAttribute() again. Hence typically app servers would replicate the entire session object each time for every request.

Q) What are the different topology options for configuring the memory-to-memory session replication?
A) For small clusters, we can set up all-to-all peer replication - i.e. the session is replicated across all the nodes of the cluster. For large clusters, we can set up "replication groups" or "buddy groups" that are essentially a group of nodes that would replicate session data between themselves. In some environments such as WebSphere Extreme Scale, one can configure dedicated JVMs in a grid for storing sessions.

Monday, July 30, 2012

Exploring Google Guava

I am pretty impressed with the simplicity of Google Guava API. We have been using Apache Commons for many years, but Guava has good support for generics and hence is a better choice for new Java development.

For e.g. to read a text file as a list of strings, you need just 2 lines of code.

//read a file from the classpath, as we want to avoid absolute paths..
File file = new File(getClass().getResource("/com/company/project/test.txt").getFile());
//pass a proper charset to ensure proper encoding
List<String>lines = Files.readLines(file, Charsets.UTF_8); 
  

Another alternate method to read the entire content of the file as a string -
public static String toString(File file,Charset charset)


To read a binary file into a byte array, use the following utility method -
public static byte[] toByteArray(File file)

Similar simple methods exist for writing files too !..

Thursday, July 26, 2012

Some interesting code..to tickle your brain cells

Found this code over the internet :) ...Took me some time to debug. Please copy-paste the below code in eclipse and start analysing - why this is happening?

public class TimePass {
    public static void main(String[] args) {
        if ( false == true ) { //these characters are ignored?: \u000a\u007d\u007b
            System.out.println("false is true!");
        }
    }
}

Hint: linefeed and curly braces :)

Using Netty - Lessons learned

We were building a ISO-8583 equivalent card transaction simulator using the powerful Netty framework.The simplicity of the netty design is a great wow factor. Also most of the complexities of NIO are abstracted by the framework by concepts such as Channel, ChannelBuffer, ChannelEvent, Decoders/Encoders, etc.

While we were using the LengthFieldBasedFrameDecoder class, we faced an intriguing problem. The socket client was sending the length of the record in the first 2 bytes. For e.g. "11abcdefghijk"
But strangely, the length derived by the  BigEndianHeapChannelBuffer was something different.
To get to the bottom of this, we enabled debugging and saw the raw buffer byte array that reached the server.

The buffer array for "11abcdefghijk" was [49, 49, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107]
The first 2 bytes contained the unicode charset number of the character '1'. Hence the lenght was also encoded from a character into bytes using the default charset, which resulted in a very large number.. Obviously the LengthFieldBasedFrameDecoder failed to decode the message.

To get around this problem, we had to send the first 2 bytes without any characted encoding. We achieved this using unicode escape sequences for the lenght field - i.e. \u0000 and \u000b; essentially 0 and 11.
The buffer array for "\u0000\u000babcdefghijk" was [0, 11, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107]

Now the  netty decoder would work. But this approach has a serious flaw...for 2 chars - \u000A and \u000D. These chars correspond to \n (linefeed) and \r (carriage return) and hence would not compile itself as the Unicode escapes are pre-processed before the compiler is run.

Hence the best approach is to write the lenght field to the socket stream without using any charset encoding (write raw bytes) and then write the record as a string.

An alternate workaround is to write your own decoder, which too is very simple. 

Given below is a sample decoder that extracts the first 2 chars of a string for the length and returns the buffer upstream.

-----------------------------------------------------------------
import java.util.logging.Logger;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;
import org.jboss.netty.util.CharsetUtil;

public class NarenLengthFieldDecoder extends FrameDecoder {

    private static final Logger logger = Logger.getLogger(NarenLengthFieldDecoder.class.getName());

    public int lengthFieldLength = 2; // some default value

    public NarenLengthFieldDecoder(int length) {
        this.lengthFieldLength = length;
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
        // wait until the length prefix is available.
        if (buffer.readableBytes() < lengthFieldLength) {
            // return null to inform frame decoder that frame is not yet
            // complete and to continue reading data
            return null;
        }

        // read length field...create a byteArray for the length field and copy
        // the first bytes into it..
        byte[] array = new byte[lengthFieldLength];
        buffer.readBytes(array); // Imp: readBytes also forwards the readerIndex

        // Mark the current buffer position before reading the length field
        // because the whole frame might not be in the buffer yet.
        // We will reset the buffer position to the marked position if
        // there's not enough bytes in the buffer.
        buffer.markReaderIndex();

        int dataLength = getLength(array);// length of the record

        // wait until the whole data is available.
        if (buffer.readableBytes() < dataLength) {
            // The whole bytes were not received yet - return null.
            // This method will be invoked again when more packets are received
            // and appended to the buffer.

            // Reset to the marked position to read the length field again next
            // time.
            buffer.resetReaderIndex();
            return null;
        }

        // forward remaining buffer to higher up handlers
         // There's enough bytes in the buffer. Read it.
         ChannelBuffer frame = buffer.readBytes(dataLength);
         // Successfully decoded a frame.  Return the decoded frame.
         return frame;
    }

    /**
     * Returns the first 2 or 3 sequence of bytes...that specify the length of
     * the record
     * 
     * @param array
     *            The byte array containing the length
     * @return An integer representing the length of the record
     */
    public int getLength(byte[] array) {
        String temp = new String(array, CharsetUtil.ISO_8859_1);

        int length = 0;
        try {
            length = Integer.parseInt(temp);
        } catch (Exception ex) {
            logger.info("Could not parse the length field of the record >>>" + temp);
        }
        return length;
    }
}

Tuesday, July 10, 2012

UIaaS - UI as a Service

Some time back, Salesforce popularized the term - UIaaS (UI as a Service), when they launched VisualForce.

UIaaS essentially means the ability to create new user interfaces using pre-built components. UI components could be pages, controls, static resources, etc. So the concept is to not start from scratch, but use off-the-shelf UI widgets. Typically UI designers are web-based and allow for in-browser UI design.

The underlying technologies for creating UIaaS are –
Server Side: JSF, Portlets, .NET WebParts
Client Side: Dojo controls, JQuery controls, Ext-JS controls, etc.

IMHO, the term is just old wine in new bottle. We used to have concepts of UI Widget Factory that encompasses creating reusable widgets and storing them (with meta-data) in a repository. Application developers would then pick and choose their widgets and design new pages. The reusable widgets could be technical widgets such as a "tab-bar","menu-bar","calendar", etc. or business widgets such as "Healthcare Provider Search", "Google Maps Overlay", etc.