Saturday, March 21, 2009

Using EnumSet when communicating over the network

Many applications use bitmasks to send flags over the wire since it is a very efficient way of representing settings etc, problem is that the code to handle them is quite hard to read and maintain, on both the server and client side. To overcome this, EnumSet in Java can be used. If the flags that are masked in the value sent are represented as enums, the bitmask value can be seen as a set of enums, this concept is used in drizzle-jdbc.

For example, to represent server capabilities the following enum is used:

public enum ServerCapabilities {
LONG_PASSWORD((short)1), /* new more secure passwords */
FOUND_ROWS((short)2), /* Found instead of affected rows */
LONG_FLAG((short)4), /* Get all column flags */
CONNECT_WITH_DB((short)8), /* One can specify db on connect */
NO_SCHEMA((short)16), /* Don't allow database.table.column */
COMPRESS((short)32), /* Can use compression protocol */
ODBC((short)64), /* Odbc client */
LOCAL_FILES((short)128), /* Can use LOAD DATA LOCAL */
IGNORE_SPACE((short)256), /* Ignore spaces before '(' */
CLIENT_PROTOCOL_41((short)512), /* New 4.1 protocol */
INTERACTIVE((short)1024), /* This is an interactive client */
SSL((short)2048), /* Switch to SSL after handshake */
IGNORE_SIGPIPE((short)4096), /* IGNORE sigpipes */
TRANSACTIONS((short)8192), /* Client knows about transactions */
RESERVED((short)16384), /* Old flag for 4.1 protocol */
SECURE_CONNECTION((short)32768), /* New 4.1 authentication */
...
private final short bitmapFlag; //holds the value
ServerCapabilities(short i) {
this.bitmapFlag = i;
}

public static short fromSet(Set<ServerCapabilities> capabilities) {
short retVal = 0;
for(ServerCapabilities cap : capabilities) {
retVal = (short) (retVal | cap.getBitmapFlag());
}
return retVal;
}


public static Set<ServerCapabilities> getServerCapabilitiesSet(short i) {
Set<ServerCapabilities> statusSet = EnumSet.noneOf(ServerCapabilities.class);
for(ServerCapabilities value : ServerCapabilities.values())
if((i & value.getBitmapFlag()) == value.getBitmapFlag())
statusSet.add(value);
return statusSet;
}
}


This enum contains all capabilities of the drizzle server we are talking to. The value in the enum constructor is the bit position in the the value sent over the network. So, two convenience methods are implemented in the enum, one for taking the actual bit-masked value and creating an enum set, and one for taking an enum set and creating a bitmasked value. fromSet creates a short which contains the flags set, and getServerCapabilitiesSet creates an EnumSet which contains the enums represented by i.

Now it is possible to handle bitmasked values like this:

    short bitmask = readShortFromNetwork(); // yeah not really, but hey
Set<ServerCapabilities> serverCapabilities = ServerCapabilities.getServerCapabilitiesSet(bitmask);
if(serverCapabilities.containsAll(EnumSet.of(ServerCapabilities.TRANSACTIONS, ServerCapabilities.SSL)) {
doSomethingSecure();
}


Also, when sending something to the server:


Set<ServerCapabilities> capabilities = EnumSet.of(ServerCapabilities.TRANSACTIONS, ServerCapabilities.SSL);
writeShortToNetwork(ServerCapabilities.fromSet(capabilities));


So, what we've got is a type safe way of sending/receiving flags to/from a server. Internally, the enum set is stored in a long, so performance-wise it is close to equivalent of handling the bit-operations yourself.

Look at these files for usage examples:

DrizzleProtocol.java

ServerCapabilities.java

No comments: