// This software is released into the Public Domain. See copying.txt for details.
package org.openstreetmap.osmosis.replicationhttp.v0_6.impl;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.handler.codec.http.DefaultHttpChunk;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.util.CharsetUtil;
import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
/**
* A sequence server handler implementation that sends the sequence number
* itself.
*
* @author Brett Henderson
*/
public class SequenceNumberServerHandler extends SequenceServerHandler {
/**
* Creates a new instance.
*
* @param control
* Provides the Netty handlers with access to the controller.
*/
public SequenceNumberServerHandler(SequenceServerControl control) {
super(control);
}
@Override
protected void handleRequest(ChannelHandlerContext ctx, HttpRequest request) {
final String sequenceNumberUri = "sequenceNumber";
final String contentType = "text/plain";
// Split the request Uri into its path elements.
String uri = request.getUri();
if (!uri.startsWith("/")) {
throw new OsmosisRuntimeException("Uri doesn't start with a / character: " + uri);
}
Queue<String> uriElements = new LinkedList<String>(Arrays.asList(uri.split("/")));
// First element is empty due to leading '/', unless there is only a '/'
// in which case there will be no elements.
if (uriElements.size() > 0) {
uriElements.remove();
}
// First element must be the sequence number base uri.
if (uriElements.isEmpty() || !sequenceNumberUri.equals(uriElements.remove())) {
throw new ResourceNotFoundException();
}
/*
* The next element determines which replication number to start from.
* The request is one of "current" or N where is the last sequence
* number received by the client.
*/
long nextSequenceNumber;
if (uriElements.isEmpty()) {
throw new ResourceNotFoundException();
}
String sequenceStartString = uriElements.remove();
if ("current".equals(sequenceStartString)) {
nextSequenceNumber = getControl().getLatestSequenceNumber();
} else {
try {
nextSequenceNumber = Long.parseLong(sequenceStartString);
} catch (NumberFormatException e) {
throw new BadRequestException("Requested sequence number of " + sequenceStartString
+ " is not a number.");
}
}
// If the next element exists and is "tail" it means that the client
// wants to stay connected and receive updated sequences as they become
// available.
boolean follow;
if (!uriElements.isEmpty()) {
String tailElement = uriElements.remove();
if ("tail".equals(tailElement)) {
follow = true;
} else {
throw new ResourceNotFoundException();
}
} else {
follow = false;
}
// Validate that that no more URI elements are available.
if (!uriElements.isEmpty()) {
throw new ResourceNotFoundException();
}
// Begin sending replication sequence information to the client.
initiateSequenceWriting(ctx, contentType, nextSequenceNumber, follow);
}
@Override
protected void writeSequence(ChannelHandlerContext ctx, ChannelFuture future, long sequenceNumber) {
// Convert the sequence to a string and then a buffer.
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(Long.toString(sequenceNumber), CharsetUtil.UTF_8);
// Wrap the buffer in a HTTP chunk.
DefaultHttpChunk chunk = new DefaultHttpChunk(buffer);
// Pass the chunk downstream.
Channels.write(ctx, future, chunk);
}
}