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; } } }