/*******************************************************************************
* gMix open source project - https://svs.informatik.uni-hamburg.de/gmix/
* Copyright (C) 2014 SVS
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*******************************************************************************/
package userGeneratedContent.testbedPlugIns.layerPlugIns.layer1network.cascade_TCP_v0_001;
//import java.lang.management.ManagementFactory;
//import java.lang.management.ThreadMXBean;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.concurrent.Executors;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.jboss.netty.handler.codec.replay.ReplayingDecoder;
import staticContent.framework.controller.SubImplementation;
import staticContent.framework.message.MixMessage;
import staticContent.framework.message.Reply;
import staticContent.framework.message.Request;
import staticContent.framework.userDatabase.DatabaseEventListener;
import staticContent.framework.userDatabase.User;
import staticContent.framework.util.Util;
//TODO: may block in duplex mode...
public class ClientHandler_TCP_FCFS_async_netty extends SubImplementation implements DatabaseEventListener {
private ChannelFactory _factory;
private ChannelGroup _channelGroup;
private HashMap<Channel, User> _userMap;
private HashMap<User, Channel> _channelMap;
private HashMap<User, Integer> _userPackets;
private HashMap<User, Long> _userResetTimestamps;
private long _timestamp;
private Channel _serverChannel;
private int _maxRequestLength;
private int _maxReadsInARow;
private int _blackListDuration;
private int _port;
private InetAddress _bindAddress;
@Override
public void constructor() {
_channelGroup = new DefaultChannelGroup("Connections");
_factory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());
_port = settings.getPropertyAsInt("GLOBAL_MIX_BIND_PORT");
_bindAddress = settings.getPropertyAsInetAddress("GLOBAL_MIX_BIND_ADDRESS");
_maxReadsInARow = settings.getPropertyAsInt("MAX_READS_IN_A_ROW");
_userMap = new HashMap<Channel, User>();
_channelMap = new HashMap<User, Channel>();
_userPackets = new HashMap<User, Integer>();
_userResetTimestamps = new HashMap<User, Long>();
_timestamp = 0L;
_blackListDuration = 1;
_maxRequestLength = settings.getPropertyAsInt("MAX_REQUEST_LENGTH");
}
@Override
public void initialize() {
}
@Override
public void begin() {
new NettyThread().start();
new ReplyThread().start();
}
@Override
public void userAdded(User user) {
}
@Override
public void userRemoved(User user) {
}
private void openSocket() {
ServerBootstrap bootstrap = new ServerBootstrap(_factory);
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline(new ReplayingDecoderImplemenatation(),
new FrameForwarder());
}
});
bootstrap.setOption("child.tcpNoDelay", true);
bootstrap.setOption("child.keepAlive", true);
_serverChannel = bootstrap.bind(new InetSocketAddress(_bindAddress,
_port));
_channelGroup.add(_serverChannel);
}
private class NettyThread extends Thread {
@Override
public void run() {
openSocket();
// System.out.println("Adding Thread #" +
// Thread.currentThread().getId() + " to Netty pool.");
// GlobalLauncher.NettyThreads.add((Long)Thread.currentThread().getId());
}
}
private class ReplayingDecoderImplemenatation extends
ReplayingDecoder<States> {
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, States state) {
// long threadID = Thread.currentThread().getId();
// if(!GlobalLauncher.NettyThreads.contains(threadID))
// GlobalLauncher.NettyThreads.add(threadID);
return buffer.readBytes(buffer.readInt());
}
}
private class FrameForwarder extends SimpleChannelHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
User user;
if (e.getChannel().getAttachment() == null) {
user = userDatabase.generateUser();
userDatabase.addUser(user);
e.getChannel().setAttachment(true);
_userMap.put(e.getChannel(), user);
_channelMap.put(user, e.getChannel());
_userPackets.put(user, 0);
} else {
user = _userMap.get(e.getChannel());
}
_userMap.put(e.getChannel(), user);
ChannelBuffer buf = (ChannelBuffer) e.getMessage();
byte[] upBuf = new byte[buf.readableBytes()];
buf.readBytes(upBuf);
if (upBuf.length > _maxRequestLength) {
System.err.println("warning: user " + user
+ " sent a too large message");
}
Request r = MixMessage.getInstanceRequest(upBuf, user);
int currentPackets = getUserpackets(user);
if (currentPackets <= _maxReadsInARow) {
increaseUserpackets(user, currentPackets);
// _userPackets.put(user, currentPackets + 1);
} else {
long targetTime = _timestamp + _blackListDuration;
while (_userResetTimestamps.containsValue(targetTime))
--targetTime;
_userResetTimestamps.put(user, targetTime);
do {
try {
Thread.sleep(1);
} catch (InterruptedException e1) {
}
} while (_userResetTimestamps.get(user) > _timestamp);
_userPackets.put(user, 0);
_blackListDuration = _userMap.size();
}
// int currentPackets = increaseUserpackets(user);
//
// if(currentPackets % 1000 == 0)
// System.out.println("Accepted " + currentPackets +
// " frames from user " + user);
anonNode.putInRequestInputQueue(r);
increaseTimestamp();
// System.out.println("I'm a Netty Thread and have got ID #" +
// Thread.currentThread().getId());
}
}
private synchronized void increaseUserpackets(User user, int currentPackets) {
// int currentPackets = _userPackets.get(user);
_userPackets.put(user, currentPackets + 1);
// return currentPackets;
}
private synchronized int getUserpackets(User user) {
return _userPackets.get(user);
}
private enum States {
LENGTH, PAYLOAD;
}
private class ReplyThread extends Thread {
@Override
public void run() {
while (true) {
Reply[] replies = anonNode.getFromReplyOutputQueue();
for (Reply reply : replies) {
assert reply != null;
assert reply.getOwner() != null;
Channel chn = _channelMap.get(reply.getOwner());
ChannelBuffer outBuf = ChannelBuffers.copiedBuffer(
Util.intToByteArray(reply.getByteMessage().length),
reply.getByteMessage());
chn.write(outBuf);
}
}
}
}
private synchronized void increaseTimestamp() {
++_timestamp;
}
}