Subscribe
Recent Tweets
About Me

I've worked as a consultant and software developer in the financial industry for over 10 years. I've worked for three of the top five Canadian banks in both retail banking and capital markets. I'm currently working with a small consulting firm in downtown Toronto.

Wednesday
Nov022011

Multiplayer tic-tac-toe in Java using the WebSocket API, Netty (NIO), and jQuery

What does this game of tic-tac-toe and Twitter have in common? Both have been implemented using relatively the same technologies: Java and Netty. It was big news in 2010 when Twitter migrated their search from Ruby on Rails to Java-based Netty. Not only was it big in the news department, but it was also big in the results department: Twitter reported their search performance increased by 3x. 

What is Netty?

Netty is a client server framework by JBoss that simplifies network programming. Netty is built on top of Java NIO but provides a much more simple API to work with. Netty can be used to build a custom server for network communications; it can be used to build anything including a lightweight HTTP server, a TCP / UDP server, a WebSocket server, or any other network server you can dream of. Because Netty is built on Java NIO, Netty's programming model is asynchronous. This means Netty is very well suited for any number of bi-directional communication projects such as real-time group chat or anything else requiring the server to push information to a client rather than other methods of network communication such as long-polling.

What is the WebSocket protocol?

WebSocket is a protocol used for bi-directional asynchronous communications between a client (usually a web browser) and a server that supports the WebSocket protocol.

A WebSocket client connects to a server via standard HTTP and performs a handshake, which creates a persistent tunnel between the client and server. After the handshake is performed the client and the server communicate freely using a message/event-driven programming model (binding actions/methods to events). The beautiful thing about the WebSocket protocol is the number of persistent connections that WebSocket servers can handle, easily numbering in the ten-of-thousands, and the volume of messages that can be processed (depending on the way the server is implemented).

Netty in action - A game of tic-tac-toe

Rather than build the same-old group chat application everyone else does to show off the flexibility of Netty and the WebSocket protocol, I decided to build a simple game of tic-tac-toe instead. It seems like an odd decision considering tic-tac-toe is a turn-based game rather than a real-time game, but most tic-tac-toe game demos on the net are single-player. Rather than build another single player game of tic-tac-toe, let's build a multiplayer game!

The core concepts behind the game are:

  1. The tic-tac-toe client and server can support a (theoretically) infinite number of simultaneous games, and each game supports 2 players.
  2. The player loads the client (a web page) and waits for an opponent.
  3. An opponent loads the client. 
  4. Both players are matched together automatically.
  5. The server responds to both players to let them know their game has started. The server notifies the client which player should go first and each player's assigned letter.
  6. The client only allows one player to select a cell at once.
  7. After each turn, the other player is notified of the other player's selection and their screen is updated automatically.
  8. After each turn the server determines if someone has won or if the game is a draw.

If you'd like to review the full code for the working Netty tic-tac-toe client and server before reading ahead, feel free to check it out:

https://github.com/rocketpages/Netty-TicTacToe-Server

https://github.com/rocketpages/TicTacToe-Client

Building the Netty server

The first step to creating our tic-tac-toe server is to build the server itself. Creating a new server in Netty is dead simple. We simply need to instruct Netty which port to bind to and which pipeline factory to use.

Netty works based on inbound and outbound "handlers"; upstream handlers and downstream handlers. As a message is either received by the server or sent by the server, it is acted upon by the handlers that you specify in the pipeline factory. This is a flexible architecture and lets us work in a very modular fashion on any given message. Anyone who has done MDB, MQ, or SOAP programming (SOAP handler chain) should be familiar with the concept already.

public class TicTacToeServer {
    public static void main(String[] args) throws Exception {
        ChannelFactory factory =
            new NioServerSocketChannelFactory(
                    Executors.newCachedThreadPool(),
                    Executors.newCachedThreadPool());

        ServerBootstrap bootstrap = new ServerBootstrap(factory);

        bootstrap.setPipelineFactory(new WebSocketServerPipelineFactory());

        bootstrap.setOption("child.tcpNoDelay", true);
        bootstrap.setOption("child.keepAlive", true);

        bootstrap.bind(new InetSocketAddress(9000));
        
        System.out.println("TicTacToe Server: Listening on port 9000");
    }
} 

The next step is creating the pipeline factory. In this case we're using a custom pipeline factory called WebSocketServerPipelineFactory. We could also have built this as an anonymous class as our implementation is fairly simple, but I decided to break it out into it's own high-level class.

public class WebSocketServerPipelineFactory implements ChannelPipelineFactory {
	public ChannelPipeline getPipeline() throws Exception {
		// Create a default pipeline implementation.
		ChannelPipeline pipeline = pipeline();
		pipeline.addLast("decoder", new HttpRequestDecoder());
		pipeline.addLast("aggregator", new HttpChunkAggregator(65536));
		pipeline.addLast("encoder", new HttpResponseEncoder());
		pipeline.addLast("handler", new TicTacToeServerHandler());
		return pipeline;
	}
}

Take a second to look at the above code. I'll break out some of the key terms and concepts to understand:

  • Channel: A channel is a persistent connection (tunnel) from a specific client to the server.
  • ChannelPipeline: Each channel can be customized with it's own pipeline. When you take a second to think about it, it becomes obvious how powerful this concept is. Encoders, decoders, aggregator, and handlers are grouped together to form a pipeline. A pipeline instructs Netty how to act on each channel.
  • Encoders, decoders, and aggregators: Netty is a low-level framework built on Java NIO. When a client first connects with the server, we need to perform a WebSocket handshake. NIO doesn't care about HTTP however, it cares about packets. In order to process the HTTP request and response, we need to instruct Netty how to deal with the packets we're receiving and sending. Netty makes this easy and provides multiple encoders, decoders, aggregator, and handlers. We'll typically extend Netty to create our own custom handlers, but if we're so inclined, we can also get really low-level and create our own encoders, etc. Also keep in mind that these can be swapped-out at runtime. After the initial HTTP handshake is performed, we're only going to be responding to WebSocket requests for this channel. You'll see later how we change from an HTTP-based pipeline to a WebSocket-based pipeline for a specific channel (aka client, aka player).
  • TicTacToeServerHandler: This is the heart of the application, responsible for consuming and pushing all messages to and from clients. An instance of this handler is specific to a channel, but we can also declare static variables to keep track of all the games of tic-tac-toe in progress at any given time.

Let's check out the TicTacToeServerHandler. The complete code can be viewed here. I'll discuss some of the core concepts below. 

	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
			throws Exception {
		Object msg = e.getMessage();
		if (msg instanceof HttpRequest) {
			handleHttpRequest(ctx, (HttpRequest) msg);
		} else if (msg instanceof WebSocketFrame) {
			handleWebSocketFrame(ctx, (WebSocketFrame) msg);
		}
	}

The messageReceived(...) method is the main callback method provided by Netty to process incoming messages from clients. In our case, we'll only be processing two types of messages: HttpRequest (for the initial handshake from a client) and WebSocketFrame (for all incoming communications after the tunnel has been established). 

private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req)
		throws Exception {

	// Allow only GET methods.
	if (req.getMethod() != HttpMethod.GET) {
		sendHttpResponse(ctx, req, new DefaultHttpResponse(
				HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN));
		return;
	}

	// Serve the WebSocket handshake request.
	if (req.getUri().equals(WEBSOCKET_PATH)
			&& Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION))
			&& WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) {

		// Create the WebSocket handshake response.
		HttpResponse res = new DefaultHttpResponse(
				HTTP_1_1,
				new HttpResponseStatus(101, "Web Socket Protocol Handshake"));
		res.addHeader(Names.UPGRADE, WEBSOCKET);
		res.addHeader(CONNECTION, Values.UPGRADE);

		// Fill in the headers and contents depending on handshake method.
		// New handshake specification has a challenge.
		if (req.containsHeader(SEC_WEBSOCKET_KEY1)
				&& req.containsHeader(SEC_WEBSOCKET_KEY2)) {
			
			// New handshake method with challenge
			res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
			res.addHeader(SEC_WEBSOCKET_LOCATION, getWebSocketLocation(req));
			String protocol = req.getHeader(SEC_WEBSOCKET_PROTOCOL);
			if (protocol != null) {
				res.addHeader(SEC_WEBSOCKET_PROTOCOL, protocol);
			}

			// Calculate the answer of the challenge.
			String key1 = req.getHeader(SEC_WEBSOCKET_KEY1);
			String key2 = req.getHeader(SEC_WEBSOCKET_KEY2);
			int a = (int) (Long.parseLong(key1.replaceAll("[^0-9]", "")) / key1
					.replaceAll("[^ ]", "").length());
			int b = (int) (Long.parseLong(key2.replaceAll("[^0-9]", "")) / key2
					.replaceAll("[^ ]", "").length());
			long c = req.getContent().readLong();
			ChannelBuffer input = ChannelBuffers.buffer(16);
			input.writeInt(a);
			input.writeInt(b);
			input.writeLong(c);
			ChannelBuffer output = ChannelBuffers
					.wrappedBuffer(MessageDigest.getInstance("MD5").digest(
							input.array()));
			res.setContent(output);
		} else {
			// Old handshake method with no challenge:
			res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
			res.addHeader(WEBSOCKET_LOCATION, getWebSocketLocation(req));
			String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
			if (protocol != null) {
				res.addHeader(WEBSOCKET_PROTOCOL, protocol);
			}
		}

		// Upgrade the connection and send the handshake response.
		ChannelPipeline p = ctx.getChannel().getPipeline();
		p.remove("aggregator");
		p.replace("decoder", "wsdecoder", new WebSocketFrameDecoder());

		// Write handshake response to the channel
		ctx.getChannel().write(res);
		
		// Upgrade encoder to WebSocketFrameEncoder
		p.replace("encoder", "wsencoder", new WebSocketFrameEncoder());
		
		// Initialize the game. Assign players to a game and assign them a letter (X or O)
		initGame(ctx);

		return;
	}

	// Send an error page otherwise.
	sendHttpResponse(ctx, req, new DefaultHttpResponse(
			HttpVersion.HTTP_1_1, HttpResponseStatus.FORBIDDEN));
}

The code above is performed when the client initially connects to the tic-tac-toe server. The WebSocket specification defines how a handshake needs to be performed. There are two versions of the handshake implemented above: one based on the old WebSocket specification (75), and the other based on the new version of the WebSocket specification (76). The biggest difference between the two handshake methods is the old version does not require a challenge while the new version does. To slightly complicate matters, different browsers implement different versions of the WebSocket specification. Another twist is that neither 75 or 76 is the latest version of the WebSocket specification, but these are the most commonly implemented by modern browsers. A deep dive into the WebSocket specification is beyond the scope of this article, but you can find the latest draft specification here.

This brings up an important point; the WebSocket specification is constantly evolving. Fast. I would strongly recommend exploring the option of using a higher-level framework for business programming rather than rolling your own server, because as you see above, you'll need to keep up-to-date with the latest changes in the spec and which versions are supported by which browsers. It's generally a good idea to leave this up to framework developers. That being said, even if you're planning to use a high-level framework, getting an in-depth knowledge of Netty is a very good thing. Down the road if you need the kind of flexibility and power that Twitter does you'll already know how to implement it.

Also note that we're programmatically changing the aggregator, encoder, and decoder for this channel only. As soon as the handshake is successful we will be communicating with the client exclusively using the WebSocket protocol rather than HTTP. If we were to leave the original HTTP encoder, decoder, and aggregator alone, our server would not understand how to deal with a WebSocket packet; it would still be trying to piece together usable HTTP requests and responses.

Now let's take a look at some tic-tac-toe specific logic. First we'll need to create a game and assign players to it. As players connect to our server they're paired off and assigned to a game. Our server will be able to support an infinite number of games simultaneously, although I doubt tic-tac-toe will ever become as popular as Twitter.

private void initGame(ChannelHandlerContext ctx) {
	// Try to find a game waiting for a player. If one doesn't exist, create a new one.
	Game game = findGame();
	
	// Create a new instance of player and assign their channel for WebSocket communications.
	Player player = new Player(ctx.getChannel());
	
	// Add the player to the game.
	Game.PlayerLetter letter = game.addPlayer(player);
	
	// Add the game to the collection of games.
	games.put(game.getId(), game);
	
	// Send confirmation message to player with game ID and their assigned letter (X or O) 
	ctx.getChannel().write(new DefaultWebSocketFrame(new HandshakeMessageBean(game.getId(), letter.toString()).toJson()));
	
	// If the game has begun we need to inform the players. Send them a "turn" message (either "waiting" or "your_turn")
	if (game.getStatus() == Game.Status.IN_PROGRESS) {			
		game.getPlayer(PlayerLetter.X).getChannel().write(new DefaultWebSocketFrame(new TurnMessageBean(YOUR_TURN).toJson()));
		game.getPlayer(PlayerLetter.O).getChannel().write(new DefaultWebSocketFrame(new TurnMessageBean(WAITING).toJson()));
	}
}

private Game findGame() {		
	// Find an existing game and return it
	for (Game g : games.values()) {
		if (g.getStatus().equals(Game.Status.WAITING)) {
			return g;
		}
	}
	
	// Or return a new game
	return new Game();
}

The code above is mainly responsible for creating and maintaining games, which are stored in the TicTacToeServerHandler as a static collection (shared across all instances of our handler). As players connect to our server we assign them to a game and let them know that they are either waiting for an opponent or that an opponent has connected and their game has begun. Exciting!

Finally, we need to accept incoming WebSocket messages. Each message represents a player's turn (which cell they selected). After we process the turn information we need to push data out to their opponent to let them know how badly they're getting pwned. We also need to check the status of the ongoing game; it's fairly important to be able to tell if a game has been won or tied after the last move!

private void handleWebSocketFrame(ChannelHandlerContext ctx,
		WebSocketFrame frame) {
	
	Gson gson = new Gson();
	IncomingMessageBean message = gson.fromJson(frame.getTextData(), IncomingMessageBean.class);
	
	Game game = games.get(message.getGameId());
	Player opponent = game.getOpponent(message.getPlayer());
	Player player = game.getPlayer(PlayerLetter.valueOf(message.getPlayer()));
	
	// Mark the cell the player selected.
	game.markCell(message.getGridIdAsInt(), player.getLetter());
	
	// Get the status for the current game.
	boolean winner = game.isPlayerWinner(player.getLetter());
	boolean tied = game.isTied();
	
	// Respond to the opponent in order to update their screen.
	String responseToOpponent = new OutgoingMessageBean(player.getLetter().toString(), message.getGridId(), winner, tied).toJson();		
	opponent.getChannel().write(new DefaultWebSocketFrame(responseToOpponent));
	
	// Respond to the player to let them know they won.
	if (winner) {
		player.getChannel().write(new DefaultWebSocketFrame(new GameOverMessageBean(YOU_WIN).toJson()));
	} else if (tied) {
		player.getChannel().write(new DefaultWebSocketFrame(new GameOverMessageBean(TIED).toJson()));
	}
}

Remember earlier that handleWebSocketFrame is one of our own methods, not a callback method provided by Netty. This method is invoked when we inspect the incoming message and determine it's a WebSocketFrame rather than an HttpRequest. The core of the logic above deals with updating the game board and marking the current player's last move. We also need to check for victory or draw conditions and update the player and opponent with the latest game status.

That's it! We've coded an entire tic-tac-toe server from scratch and it was very painless. Netty rocks. The only classes we didn't review are the specific POJOs for tic-tac-toe related logic. You can check out the source below:

Creating the tic-tac-toe client using jQuery

Building our tic-tac-toe server was fairly simple. Building our tic-tac-toe client is even easier thanks to the power of jQuery! If you'd like to skip straight to the source code, it can be viewed below:

https://github.com/rocketpages/TicTacToe-Client

The bulk of the logic is contained in our JavaScript file. After the initial handshake is performed, all communication with the server will be done asynchronously via the WebSocket API. Please note that not all browsers support the WebSocket protocol. This is an up-to-date list of which layout engines support which HTML5 features.

Below is a copy of the JavaScript for the tic-tac-toe client.

// Constants - Status Updates
var STRATEGIZING_STATUS = "Your opponent is strategizing.";
var WAITING_STATUS = "Waiting for an opponent.";
var YOUR_TURN_STATUS = "It's your turn!";
var YOU_WIN_STATUS = "You win!";
var TIED_STATUS = "The game is tied.";
var WEBSOCKET_CLOSED_STATUS = "The WebSocket Connection Has Been Closed.";

// Constants - Game
var PLAYER_O = "O";
var PLAYER_X = "X";

// Constants - Incoming message types
var MESSAGE_HANDSHAKE = "handshake";
var MESSAGE_OPPONENT_UPDATE = "response";
var MESSAGE_TURN_INDICATOR = "turn";
var MESSAGE_GAME_OVER = "game_over";

// Constants - Message turn indicator types
var MESSAGE_TURN_INDICATOR_YOUR_TURN = "YOUR_TURN";
var MESSAGE_TURN_INDICATOR_WAITING = "WAITING";

// Constants - Game over message types
var MESSAGE_GAME_OVER_YOU_WIN = "YOU_WIN";
var MESSAGE_GAME_OVER_TIED = "TIED";

// Constants - WebSocket URL
var WEBSOCKET_URL = "ws://localhost:9000/websocket";


// Variables
var player;
var opponent;
var gameId;
var yourTurn = false;

// WebSocket connection
var ws;

$(document).ready(function() {
	
	/* Bind to the click of all divs (tic tac toe cells) on the page
	   We would want to qualify this if we styled the game fancier! */
	$("div").click(function () {
		// Only process clicks if it's your turn.
		if (yourTurn == true) { 
	      // Stop processing clicks and invoke sendMessage(). 
		  yourTurn = false;
    	  sendMessage(this.id);
    	  // Add the X or O to the game board and update status.
	      $("#" + this.id).addClass(player);
	      $("#" + this.id).html(player);	    	  
	      $('#status').text(STRATEGIZING_STATUS);    	 					      
    	}
    });	

    // On the intial page load we perform the handshake with the server.
    ws = new WebSocket(WEBSOCKET_URL);
    
    ws.onopen = function(event) { 
    	$('#status').text(WAITING_STATUS); 
    }
    
    // Process turn message ("push") from the server.
	ws.onmessage = function(event) {
 		var message = jQuery.parseJSON(event.data);
 		
 		// Process the handshake response when the page is opened
 		if (message.type === MESSAGE_HANDSHAKE) {
   	 		gameId = message.gameId;
   	 		player = message.player;

   	 	 	if (player === PLAYER_X) {
   	 	 		opponent = PLAYER_O; 
   	 	 	} else {
   	 	 		opponent = PLAYER_X;   	 	 	
   	 	 	}
 		}
 		
 		// Process your opponent's turn data.
 		if (message.type === MESSAGE_OPPONENT_UPDATE) {
 			// Show their turn info on the game board.
 			$("#" + message.gridId).addClass(message.opponent);
 			$("#" + message.gridId).html(message.opponent);
 			
 			// Switch to your turn.
 			if (message.winner == true) {
 				$('#status').text(message.opponent + " is the winner!"); 
 			} else if (message.tied == true) {
 				$('#status').text(TIED_STATUS);   	   	 			
 			} else {
 				yourTurn = true;
    			$('#status').text(YOUR_TURN_STATUS);    	   	 			
    		}
 		}   	 	
 		
 		/* The initial turn indicator from the server. Determines who starts
 		   the game first. Both players wait until the server gives the OK
 		   to start a game. */
 		if (message.type === MESSAGE_TURN_INDICATOR) {
 			if (message.turn === MESSAGE_TURN_INDICATOR_YOUR_TURN) {
 				yourTurn = true;
	    		$('#status').text(YOUR_TURN_STATUS);    	 			
    		} else if (message.turn === MESSAGE_TURN_INDICATOR_WAITING) {
				$('#status').text(STRATEGIZING_STATUS);    	 					    	
    		}
 		}
 		
 		/* The server has determined you are the winner and sent you this message. */
 		if (message.type === MESSAGE_GAME_OVER) {
	 		if (message.result === MESSAGE_GAME_OVER_YOU_WIN) {
				$('#status').text(YOU_WIN_STATUS);
			} 
			else if (message.result === MESSAGE_GAME_OVER_TIED) {
				$('#status').text(TIED_STATUS);
			}
 		}	
 	} 
 	
 	ws.onclose = function(event) { 
 		$('#status').text(WEBSOCKET_CLOSED_STATUS); 
 	} 
		
});

// Send your turn information to the server.
function sendMessage(id) {
	var message = {gameId: gameId, player: player, gridId:id};
	var encoded = $.toJSON(message);
	ws.send(encoded);
}

The bulk of the logic above deals with two distinct activities; maintaining state and processing messages. You'll notice that when the page first loads we'll use the documentReady function to initiate the WS handshake. You'll also notice that we use jQuery to bind different WebSocket events to code blocks.

jQuery makes it fairly trivial to communicate with a WS server. Rather than polling the server, the WebSocket API allows us to bind logic to the onmessage event and perform the appropriate logic. This is awesome! Our tic-tac-toe game is a simple webpage that never needs refreshing. We also don't need to waist IO constantly polling the server. As soon as our opponent makes his or her move, our screen is immediately updated and we get to take our turn.

Just the beginning

That's a very high level look at the power of Java, Netty, the WebSocket API, and jQuery. We can make a number of improvements to this game, such as tracking and displaying ongoing stats (win, loss, draw) and even allowing the same player to compete in multiple games of tic-tac-toe at once! Using these technologies we may actually be able to make tic-tac-toe a challenge. Stay tuned!

Tuesday
Dec282010

Fixing Fancybox with AJAX (and Wicket)

A personal project I'm working on uses the jQuery Fancybox library to display images. It's a great lightbox, except for one small problem: AJAX refreshes break it.

A page I'm working on accepts multiple image uploads. Once the images are successfully processed asynchronously on the server, an image panel on the same page is automatically refreshed. The AJAX callback works fine and updates the panel view with the newly uploaded photos, but the net result is that Fancybox stops working completely.

The solution is pretty simple: Fancybox needs to be re-initialized after the view is refreshed. This involves invoking a JavaScript function after the AJAX callback completes. Fortunately, I'm using Wicket, which makes invoking JavaScript after the callback a breeze.

Here's the (condensed version of) code for my behavior class that handles the repainting, callback, and post-callback JS invocation:

        final IBehavior repaintBehavior = new AbstractDefaultAjaxBehavior() {
@Override
protected void respond(AjaxRequestTarget target)
{
ThingModel thingModel = new ThingModel(thingId);
UnprocessedPhotosModel photosModel = new UnprocessedPhotosModel(getCurrentUser().getId(), tokenId);
List photos = photosModel.getObject();

for (Photo photo : photos)
{
photo.setThing(thingModel.getObject());
ServiceFactory.getPhotoService().savePhoto(photo);
}

target.addComponent(photosPanel);
target.appendJavascript("$.fancyboxResetBusy(); reinitFancybox();");
}

@Override
public void renderHead(IHeaderResponse response)
{
super.renderHead(response);
CharSequence callback = getCallbackScript();
response.renderJavascript("function uploadCompleted() { " + callback + "}", "customUploadCompleted");
}
};

For those who aren't familiar with Wicket, the renderHead method is rendering a custom JavaScript function in the view. The multiple upload Flash component I use invokes the rendered JS function after it finishes processing the images. When the Flash uploader invokes uploadCompleted, Wicket handles all the AJAX plumbing and executes the Java code in the respond method above.

The two most important lines of code are the calls to target. target.addComponent instructs Wicket to refresh the photo panel, and target.appendJavascript instructs Wicket to invoke the functions in the String parameter after the panel is refreshed.

Let's take a look at the reinitFancybox JS function which gets invoked after the panel is refreshed.

function reinitFancybox() {	
// CSS
$(".gallery img, portfolio.list img, .portfolio.grid-2 div img, a.fancy, ul.screens img").css("opacity", "1");

// ON MOUSE OVER
$(".gallery img, .portfolio-list img, .portfolio.grid-2 div img, a.fancy, ul.screens img").hover(function () {

// SET OPACITY TO 100%
$(this).stop().animate({
opacity: 0.5
}, "fast");
},

// ON MOUSE OUT
function () {

// SET OPACITY BACK TO 100%
$(this).stop().animate({
opacity: 1
}, "fast");
});

// INIT FANCYBOX
$("li.image a, a.fancy, .portfolio.grid li a.folio-zoom, .portfolio-list.image a").fancybox({
'titlePosition' : 'over'
});
}

Most of the code above re-initializes the CSS hover effects I use to pretty up my image panel. The last piece is responsible for re-initializing Fancybox itself. At this point, we should be done, Fancybox should work perfectly again. But there's a small catch, the Fancybox script includes a global variable named busy which is set to true at the beginning of almost every single function. If for any reason the script is interrupted before busy is set back to false, re-initialization will always abort. Here's a snippet of the Fancybox code below:

$.fancybox = function(obj) {
if (busy) {
return;
}

busy = true;

...

It's pretty obvious that if busy is true Fancybox won't initialize. We need to make sure busy is set to false before attempting to re-initialize. Unfortunately, this requires a small kludge addition to the Fancybox script itself. I've added the following public function:

$.fancyboxResetBusy = function() {
busy = false;
}

Invoking this before the initialize function ensures the initialization doesn't return prematurely.

This fix worked for me, let me know if you get any mileage out of it (or if a better solution exists).

Sunday
Dec262010

Connection pooling with Hibernate 3.3.x and C3P0

What is C3P0?

Hibernate uses it's own built-in connection pool out of the box. There are some pretty heavy duty sites running with the default connection pool enabled in production despite the obvious warning against doing this:

INFO DriverManagerConnectionProvider:64 - Using Hibernate built-in connection pool (not for production use!)
INFO DriverManagerConnectionProvider:65 - Hibernate connection pool size: 1

A few minor problems creep up with this default configuration left alone, such as the inability for Hibernate to reconnect closed connections. MySQL closes unused connections after 8 hours by default, which causes problems for low-volume applications that may not experience any database usage overnight. 

One solution is to use C3P0, an incredibly simple library that augments the standard JDBC drivers essentially making them "enterprise-ready". One of the main features of C3P0 is:

Transparent pooling of Connection and PreparedStatements behind DataSources which can "wrap" around traditional drivers or arbitrary unpooled DataSources.

In other words, you don't have to change any of your code to use C3P0 connection pools if you're using Hibernate. C3P0 takes care of maintaining the pool and testing connections, so your code stays in tact.

Configuring C3P0

1. Add the C3P0 jar to your classpath. If you're using Maven, simply add the following to your pom.xml file:

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-c3p0</artifactId>
	<version>3.3.2.GA</version>
</dependency>

2. Make the following changes to your hibernate.cfg.xml file:

<property name="connection.url">jdbc:mysql://127.0.0.1/your_db</property>
<property name="connection.username">your_username</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>

<!-- JDBC connection pool (C3P0) -->
<property name="c3p0.min_size">5</property>
<property name="c3p0.max_size">20</property>
<property name="c3p0.timeout">1800</property>
<property name="c3p0.max_statements">50</property>

Pay special attention to the provider_class property. Previous versions did not require this property, so most of the tutorials on the net are missing this key piece of info.

3. Create a c3p0.properties file

In order to configure C3P0 for your specific needs, you'll need a c3p0.properties file on the classpath. Take a look here for all the available settings you can tweak. Here's an example that ensures any closed connection is tested and re-opened rather than MySQL throwing an exception:

c3p0.testConnectionOnCheckout=true

Verifying it worked

Configuring C3P0 is pretty simple! If everything works correctly, you'll see the following in your logs:

INFO ConnectionProviderFactory:95 - Initializing connection provider: org.hibernate.connection.C3P0ConnectionProvider
INFO C3P0ConnectionProvider:103 - C3P0 using driver: com.mysql.jdbc.Driver at URL: jdbc:mysql://127.0.0.1/your_db
INFO C3P0ConnectionProvider:104 - Connection properties: {user=****, password=****}
Wednesday
Dec222010

Beyond Java

A recent post by Mike Gualtieri of Forrester caught my attention. Mike strongly asserts that Java is a dead end for enterprises and business software should be built with something else. Mike mentions a few different options, but mainly promotes 4GL tools such as Powerbuilder and "all in one" business infrastructure software such as SoftwareAG webMethods. He also touches on BPM and event processing, and by event processing I'm pretty sure he means CEP tools such as Esper.

I've worked with Java for 10 years. (I admit, sometimes it feels like 20. Life isn't pain free.) I've also worked briefly with FoxPro, just spent a few months at a client site digging through SAS code (another 4GL) that's being migrated to Java, and at my previous job I spent almost a month on a  SoftwareAG training course to learn the "webMethods way" of doing things. Is Mike really advocating the use of proprietary, niche languages such as webMethods Flow over Java? Really? I'm not an analyst at Forrester, but I've been turning Powerpoint slides into functioning software for long enough that I feel (just about) qualified enough to chime in on the subject.

I agree with the spirit of Mike's post that Java can be a real pain in the ass to build typical business software with. But the problem isn't Java itself, the problem is that businesses use it for everything. "Want yet another custom time tracking intranet site? Let's build it with JEE! How about a utility that counts the number of ponies in our database? Hell yeah... it's Java time, baby! What about that UI heavy site with super-complex workflow? I've got a fever, and the only prescription is JSF, SPRING, and HIBERNATE!"

People have tried for decades to move away from third-generation languages like Java and towards the nirvana of business folks creating their own apps with some kind of flow-charting tool. One of the first guys I worked with -- he had punch cards in his drawer that *he* punched -- told me all about it. It's the longest running punch line in IT. Mike's post might have been titled "Java is a dead end", but it really should have been titled "third-generation languages are a dead end". And the date of the article could easily have been 1980 or 1990. The dead end is not third-generation languages; languages are merely a tool.

The dead end is producing crap software and expecting to keep up with the rest of the world. The dead end is promoting a stock trader or pony salesman or MBA-type to head up your IT department just because he or she really knows their way around office politics. The dead end is misusing a language or technology for years and blaming the technology and blaming your developers rather than hiring the right people to come up with your strategy and empowering your developers

The only reason these strategies worked in the past is because everyone was fucking up equally. But nothing lasts forever.

Here's a hands-on developer's opinion on the whole "Java is dead" debate, summed up with bullets 'n everything:

  • Java is no more of a dead end than C, C++, or MATLAB. They all have their uses, but using Java for everything is just as dumb as trying to use MATLAB for everything. Stop drinking the Kool Aid.
  • If you want to build great software, you need to hire great software developers. There's no magic tool that will allow your business folks to put together fantastic software all by themselves. These ideas look fantastic in a Powerpoint slide or Forrester report, but fall flat in the real world. 
  • How do you hire great software developers? Be a great team to work for. Understand what's important to top-tier developers. Here's a good place to start (this was written 10 years ago and most companies still aren't close).
  • Treat software development as a first-class citizen of your business if you're going to bother building software. Otherwise, STOP BUILDING and START BUYING; find a trusted partner to build for you, or go back to pen and paper.
  • If you've got a department writing software that operates like anything other than a software business, you've got a bunch of problems that no tool in the world will ever fix. There's nothing worse in the world than software decisions made by companies who treat software as a necessary evil.
  • Stop putting stock traders and accountants and lawyers in charge of your IT department. They may be smart folks, and that kind of strategy may work in politics, but it's painful to watch the decisions these people make day in and day out. Decisions like outsourcing a project to India with almost no oversight and wondering why it looks like a box of pudding when it finally gets delivered. Or having a budget of $100mil and not bothering to hire UX and UI ninjas. (Nothing like saving a few bucks and having colour-blind programmers design your user interfaces, eh. Can we get that icon in cornflower blue?)
  • Developers need to speak up! I've seen projects go completely off the rails because developers allow corporate inertia to dictate decisions rather than technical merits. I've also seen money saved because someone asks the question, "Why are we building a custom app when we can buy it off the shelf instead?"

Just remember that most companies currently shooting off their toes with Java wouldn't fare any better with Ruby on Rails or Python on the Subway. (Actually, pythons on the subway are usually a bad omen. Just sayin'. And don't get me wrong, I think both Ruby and Python are fantastic for certain things, just like Java is fantastic for other things.) Java is long in the tooth, absolutely, but there's still so much goodness in the Java world: GWT, Wicket, Guice, and on and on. Talented programmers can create beautiful things with these tools. Or if you think I'm dead wrong, you can always develop your next app using SAS, FoxPro, and webMethods...

Monday
Nov152010

The future of Java in the enterprise

It’s been a busy few years in the Java world. Oracle’s acquisition of Sun is obviously the largest piece of news and has a significant impact on the future of Java. Oracle’s new position as owner and commander-in-chief now places Java squarely in the enterprise realm alongside Oracle’s other products. Java has always been an enterprise technology, but without the JCP promoting collaborative innovation, who knows what kind of traction Java would have made in the world? Appealing to both academia and industry helped push Java forward and attract a lot of new blood in the early years.

Fast forward to 2010. Java is a utilitarian, status-quo ecosystem for getting things done, albeit not very quickly. This is actually a good thing. Instead of worrying about sharp edges, developers get to work with a very stable, very slow moving target. This won't change as Oracle's only motivation is to sell support licenses and create other fee-based streams of revenue. Major improvements to Java are not on the agenda. Innovation from the core Java team will almost certainly slow down and Java will move deeper into a stabilization and maturity phase. Anything considered an official spec is almost certainly going to see a reduced adoption rate as only the largest of the large corporations stick with pure Java standards. As business goes on as usual, everyone is anxiously waiting for a true Java successor to emerge. Will it be Scala? Google Go? Neither are even on the radar of most corporate cubicle farms.

Contrary to the belief of Java-contrarians, Java will not die any time soon, nor should it. Java is well suited to organizations that span many silos with many integration points as Java integrates so well with other technologies. The language itself is also well suited to large development teams; not only does it have a massive library of utilities available to Java developers, but its statically typed nature makes refactoring relatively easy. A counter point is that dynamically typed languages result in less of a need to refactor, but that's another post entirely.

In my opinion Java will actually experience a period of growth as IT spending increases with the (slowly) rebounding economy. Even if the economy takes another nosedive, organizations realize that mass cost savings can be realized through superior technology, and Java's place as a mature and stable language position it as a reasonable choice for a risk-adverse economic climate. Maturity and stability, not innovation, is what the enterprise values most. (I would wager that there are many more lines of COBOL currently in production than lines of Ruby.) Even though Java is mature, most corporations haven’t leveraged even a fraction of what Java is capable of. The problem in many shops isn’t even the language itself; a powerful shiny new language won’t fix the process problems that plague most corporate development shops, a powerful shiny new language would most likely just exacerbate them. Java on Android is also a big deal for Java developers considering that Android recently surpassed iOS in terms of mobile OS market share. Unless Google declares war on Java, the trend is growth.

Over the next decade expect mainframe systems to slowly be retired and legacy Java applications be refactored into client-centric enterprise portals. The simple fact is that Java as an ecosystem (language + tools + frameworks + runtime environment + etc) is a fantastic choice for system integration projects and some new development in large organizations. Java is rock-solid and proven.