// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
import java.nio.channels.ClosedChannelException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpMethod;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
import org.jboss.netty.handler.codec.http.HttpVersion;
import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
/**
* Netty handler for receiving replication sequence information and notifying
* listeners.
*
* @author Brett Henderson
*/
public abstract class SequenceClientHandler extends SimpleChannelHandler {
private static final Logger LOG = Logger.getLogger(SequenceClientHandler.class.getName());
private SequenceClientControl control;
private String serverHost;
private boolean midStream;
private boolean active;
/**
* Creates a new instance.
*
* @param control
* Provides the Netty handlers with access to the controller.
* @param serverHost
* The name of the host system running the sequence server.
*/
public SequenceClientHandler(SequenceClientControl control, String serverHost) {
this.control = control;
this.serverHost = serverHost;
active = true;
}
/**
* Gets the URI to request from the server to initialise message processing.
*
* @return The request URI.
*/
protected abstract String getRequestUri();
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
// Send a request to the server asking for sequence number
// notifications.
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, getRequestUri());
request.headers().add("Host", serverHost);
Channels.write(ctx, e.getFuture(), request);
midStream = false;
}
/**
* Processes the contents of a single HTTP chunk.
*
* @param buffer
* The data contained in the chunk.
*/
protected abstract void processMessageData(ChannelBuffer buffer);
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (active) {
ChannelBuffer buffer;
if (!midStream) {
HttpResponse response = (HttpResponse) e.getMessage();
HttpResponseStatus status = response.getStatus();
if (!HttpResponseStatus.OK.equals(status)) {
throw new OsmosisRuntimeException("Received a " + status + " response from the server.");
}
buffer = response.getContent();
midStream = true;
} else {
HttpChunk chunk = (HttpChunk) e.getMessage();
buffer = chunk.getContent();
}
// Perform implementation specific processing of the buffer contents.
if (buffer.readableBytes() > 0) {
processMessageData(buffer);
}
}
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
control.channelClosed();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
// When we close the channel we may still continue to receive some
// data. Flag that this should be ignored.
active = false;
// Get the cause of the exception.
Throwable t = e.getCause();
// A ClosedChannelException occurs if the client disconnects and is not
// an error scenario.
if (!(t instanceof ClosedChannelException)) {
LOG.log(Level.SEVERE, "Error during processing for channel " + ctx.getChannel() + ".", t);
}
// We must stop sending to this client if any errors occur during
// processing.
e.getChannel().close();
}
}