package com.linkedin.databus2.test.container;
/*
*
* Copyright 2013 LinkedIn Corp. All rights reserved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
import java.nio.channels.ClosedChannelException;
import org.apache.log4j.Logger;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.DownstreamMessageEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.WriteCompletionEvent;
import org.jboss.netty.handler.codec.http.HttpChunk;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
import org.jboss.netty.handler.timeout.WriteTimeoutException;
import com.linkedin.databus2.test.TestUtil;
/**
* A simple channel handler, which allows to intercept and interfere with
* the flow of the messages thru the channel. The class is meant
* for testing purposes.
*/
public class MockServerChannelHandler extends SimpleChannelHandler
{
public static Logger LOG = Logger.getLogger(MockServerChannelHandler.class);
private boolean _throwWTOException = false;
private boolean _saveTheFuture = false;
private boolean _disableWriteComplete = false;
private boolean _delayWriteComplete = false;
private ChannelFuture _future = null;
public MockServerChannelHandler()
{
}
public void enableThrowWTOException(boolean enable) {
_throwWTOException = enable;
}
public void enableSaveTheFuture(boolean enable) {
_saveTheFuture = enable;
}
public void disableWriteComplete(boolean disable) {
_disableWriteComplete = disable;
}
public void delayWriteComplete(boolean delay) {
_delayWriteComplete = delay;
}
// =>=>=> UPstream from server to client
// <=<=<= DNxtream from client to server
@Override
public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) throws Exception {
Thread t = null;
if(LOG.isDebugEnabled())
LOG.debug("=>=>=>=WRite complete start" + e);
if(_disableWriteComplete) {
LOG.info("Injecting exceptions into writeComplete");
_future.setFailure(new WriteTimeoutException("Mocked WriteTimeout"));
_future.setFailure(new ClosedChannelException());
TestUtil.sleep(16000); // 16s sleep
} else if(_delayWriteComplete) {
LOG.info("Injecting delay into writeComplete");
t = startExceptionThread(ctx);
LOG.info("waiting for timeout");
TestUtil.sleep(10);
}
super.writeComplete(ctx, e);
LOG.debug("=>=>=> Write complete end");
if(t != null)
t.interrupt(); // interrupt to close the channel
}
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
if(LOG.isDebugEnabled()) {
LOG.debug("<=<=<=<=WRite requested" + e.getMessage());
printMessage("<=<=<=<=", e);
}
MessageEvent newMessage = e;
if(_saveTheFuture) {
if(_future == null) {
// save the real future - so we can fail it
_future = e.getFuture();
// to make sure client's Netty thread will not get the update - we substitute a fake future
ChannelFuture newFuture = Channels.future(ctx.getChannel());
newMessage = new DownstreamMessageEvent(ctx.getChannel(), newFuture, e.getMessage(), e.getRemoteAddress());
if(LOG.isDebugEnabled())
LOG.debug("Saving the future:" + _future);
}
}
super.writeRequested(ctx, newMessage);
}
@Override
public void messageReceived(
ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if(LOG.isDebugEnabled())
printMessage("msgReceived=>=>=>=>", e);
if(_throwWTOException) {
_throwWTOException = false;
// throw WriteTimeout exception
startExceptionThread(ctx);
}
super.messageReceived(ctx, e);
}
private Thread startExceptionThread(final ChannelHandlerContext ctx) {
Thread t = new Thread( new Runnable() {
@Override
public void run() {
LOG.info("***** ABOUT TO FIRE time out exception");
//_future.setFailure(new WriteTimeoutException("MOCK write timeout exception1"));
Channels.fireExceptionCaught(ctx, new WriteTimeoutException("MOCK write timeout exception2"));
LOG.info("**** Exception in the hole ");
try {
Thread.sleep(100000); // sleep will be interrupted
} catch (InterruptedException e) {
LOG.info("sleep interrupted");
}
ctx.getChannel().close(); //close the channel asynchronously
}
}, "Write Timeout thread");
t.setDaemon(true);
t.start();
return t;
}
//auxiliary method to print messages
private void printMessage(String prefix, MessageEvent e) {
Object msgO = e.getMessage();
String resp;
if(msgO instanceof HttpRequest) {
HttpRequest msgReq = (HttpRequest)msgO;
//Matcher result = pattern.matcher(msgReq.getUri());
resp = msgReq.getUri();
} else if(msgO instanceof HttpResponse){
HttpResponse msgReq = (HttpResponse)msgO;
resp = msgReq.toString();
} else if(msgO instanceof HttpChunk){
HttpChunk msgReq = (HttpChunk)msgO;
resp = msgReq.toString();
} else {
ChannelBuffer msg = (ChannelBuffer)msgO;
byte[] bytes = new byte[msg.capacity()];
msg.readBytes(bytes);
msg.setIndex(0, bytes.length);
StringBuilder out = new StringBuilder("MSG: ").append(e.getChannel().getRemoteAddress());
out.append("\nMESSAGE length=").append(bytes.length).append("\n").append(new String(bytes));
resp = out.toString();
}
LOG.debug(prefix + resp);
}
}