package com.hadooparchitecturebook.frauddetection;
import com.hadooparchitecturebook.frauddetection.model.Action;
import com.hadooparchitecturebook.frauddetection.model.UserEvent;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.log4j.Logger;
import org.codehaus.jettison.json.JSONException;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.*;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;
/**
* This class emulates the end-point of our Fraud Detection system.
* This is where events come from. For example, this can represent the machine where users swipe credit-cards
* Or a mobile app for payment processing
*/
public class EventClient {
Logger LOG = Logger.getLogger(EventClient.class);
String host;
int port;
NettyClientHandler handler;
static ChannelGroup allChannels;
public EventClient(String host, int port) {
this.host = host;
this.port = port;
}
public void startClient() {
ClientBootstrap bootstrap = new ClientBootstrap(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
try {
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
ChannelPipeline p = Channels.pipeline();
handler = new NettyClientHandler();
p.addLast("handler", handler);
return p;
}
});
bootstrap.setOption("tcpNoDelay", true);
bootstrap.setOption("receiveBufferSize", 1048576);
bootstrap.setOption("sendBufferSize", 1048576);
// Start the connection attempt.
LOG.info("EventClient: Connecting " + host + "," + port);
ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
LOG.info("EventClient: Connected " + host + "," + port);
allChannels = new DefaultChannelGroup();
// Wait until the connection is closed or the connection attempt fails.
allChannels.add(future.getChannel());
LOG.info("EventClient: Added to Channels ");
} catch (Exception e) {
e.printStackTrace();
}
}
public void closeClient() {
allChannels.close();
}
public void submitUserEvent(UserEvent userEvent) throws Exception{
handler.submitUserEvent(userEvent);
}
public class NettyClientHandler extends SimpleChannelUpstreamHandler {
private final byte[] content;
ChannelStateEvent channelStateEvent;
Action action;
public NettyClientHandler() {
content = new byte[10000];
}
@Override
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception {
if (e instanceof ChannelStateEvent) {
if (((ChannelStateEvent) e).getState() != ChannelState.INTEREST_OPS) {
System.err.println(e);
}
this.channelStateEvent = (ChannelStateEvent)e;
}
// Let SimpleChannelHandler call actual event handler methods below.
super.handleUpstream(ctx, e);
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
this.channelStateEvent = e;
}
@Override
public void channelInterestChanged(ChannelHandlerContext ctx, ChannelStateEvent e) {
this.channelStateEvent = e;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
// Server is supposed to send nothing. Therefore, do nothing.
try {
String actionStr = Bytes.toString(((ChannelBuffer) e.getMessage()).array());
LOG.info("EventClient:ActionReplied:" + actionStr);
action = new Action(actionStr);
/* This is where a real end-point system would show the user whether the transaction was authorized or denied */
LOG.info("Was transaction accepted? " + action.accept);
} catch (JSONException e1) {
LOG.error(e1);
throw new RuntimeException("Unable to parse Action JSON", e1);
}
synchronized (this) {
this.notify();
}
}
@Override
public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e) {
//transferredBytes += e.getWrittenAmount();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) {
// Close the connection when an exception is raised.
ex.getCause().printStackTrace();
ex.getChannel().close();
}
public Action submitUserEvent(UserEvent userEvent) throws Exception {
Channel channel = channelStateEvent.getChannel();
if (channel.isWritable()) {
String json = userEvent.getJSONObject().toString();
ChannelBuffer m = ChannelBuffers.wrappedBuffer(Bytes.toBytes(json));
LOG.info("EventClient:sending " + json);
channel.write(m);
} else {
throw new RuntimeException("Channel is not writable");
}
synchronized (this) {
long startTime = System.currentTimeMillis();
LOG.info("-- EventClient:Waiting for response " + startTime);
this.wait();
LOG.info("-- EventClient:Got response " + (System.currentTimeMillis() - startTime));
}
LOG.info("EventClient:action " + action.alert);
return action;
}
}
}