/*
* The MIT License
*
* Copyright 2014 sorrge.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.nyan.dch.communication.transport.tcpip;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.nyan.dch.communication.IAddress;
import org.nyan.dch.communication.IConnections;
/**
*
* @author sorrge
*/
public class ReceiveWorker implements Runnable
{
static abstract class WorkItem
{
final SelectionKey connection;
public WorkItem(SelectionKey acceptedConnection)
{
this.connection = acceptedConnection;
}
}
static class ConnectionChanged extends WorkItem
{
final boolean connected, iAmInitiator;
public ConnectionChanged(SelectionKey acceptedConnection, boolean connected, boolean iAmInitiator)
{
super(acceptedConnection);
this.connected = connected;
this.iAmInitiator = iAmInitiator;
}
}
static class DataReceived extends WorkItem
{
final byte[] data;
public DataReceived(SelectionKey connection, byte[] data)
{
super(connection);
this.data = data;
}
}
static class ConnectionFailed extends WorkItem
{
final IAddress address;
public ConnectionFailed(IAddress address)
{
super(null);
this.address = address;
}
}
private TCPServer server;
final BlockingQueue<WorkItem> receivedItems = new LinkedBlockingQueue<>();
private IConnections connections;
private final Random rand;
public ReceiveWorker(Random rand)
{
this.rand = rand;
}
public void SetServer(TCPServer server)
{
this.server = server;
}
public void SetConnections(IConnections connections)
{
this.connections = connections;
}
@Override
public void run()
{
while(true)
{
try
{
WorkItem it = receivedItems.take();
ProcessItem(it);
}
catch (InterruptedException ex)
{
break;
}
catch (Exception e)
{
System.err.printf("Unknown worker error: %s\n", e);
e.printStackTrace(System.err);
}
}
while(!receivedItems.isEmpty())
{
try
{
WorkItem it = receivedItems.take();
ProcessItem(it);
}
catch (InterruptedException ex)
{
break;
}
catch (Exception e)
{
System.err.printf("Unknown worker error: %s\n", e);
e.printStackTrace(System.err);
}
}
}
private void ProcessItem(WorkItem it)
{
if (it instanceof ConnectionChanged)
{
ConnectionChanged ce = (ConnectionChanged)it;
if (ce.connected)
{
if (!ce.connection.channel().isOpen())
{
// System.err.printf("Connection closed immediately: %s\n", ce.connection.channel());
return;
}
SocketChannel channel = (SocketChannel)ce.connection.channel();
TCPConnection conn = new TCPConnection((IPAddress)ce.connection.attachment(), server, ce.connection, rand.nextLong());
ce.connection.attach(conn);
connections.Connected(conn, ce.iAmInitiator);
// System.out.printf("Server %s connected to %s\n", this, ce.connection.attachment());
// server.DumpConnections();
}
else
{
if(ce.connection.attachment() instanceof TCPConnection)
{
TCPConnection conn = (TCPConnection)ce.connection.attachment();
if(conn != null)
{
conn.ReportDisconnect(ce.iAmInitiator);
// System.out.printf("Server %s disconnected from %s (cookie: %s)\n", this, conn.GetAddress(), conn.GetCookie());
}
}
// else
// System.out.printf("Connection closed: %s\n", ce.connection.attachment());
}
}
else if (it instanceof DataReceived)
{
DataReceived de = (DataReceived)it;
if (!(de.connection.attachment() instanceof TCPConnection))
{
// System.err.printf("Data received on already closed connection: %s\n", de.connection.channel());
return;
}
TCPConnection conn = (TCPConnection)de.connection.attachment();
conn.ReceivedData(de.data);
}
else if(it instanceof ConnectionFailed)
{
// System.out.printf("Connection to %s failed\n", ((ConnectionFailed)it).address);
connections.ConnectionFailed(((ConnectionFailed)it).address);
}
else
System.err.printf("Unknown item in the processing queue: %s\n", it);
}
}