/* * This file is part of aion-emu <aion-emu.com>. * * aion-emu 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. * * aion-emu 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 aion-emu. If not, see <http://www.gnu.org/licenses/>. */ package com.aionemu.commons.network; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; import com.aionemu.commons.options.Assertion; /** * Class that represent Connection with server socket. Connection is created by <code>ConnectionFactory</code> and * attached to <code>SelectionKey</code> key. Selection key is registered to one of Dispatchers <code>Selector</code> to * handle io read and write. * * @author -Nemesiss- */ public abstract class AConnection { /** * SocketChannel representing this connection */ private final SocketChannel socketChannel; /** * Dispatcher [AcceptReadWriteDispatcherImpl] to witch this connection SelectionKey is registered. */ private final Dispatcher dispatcher; /** * SelectionKey representing this connection. */ private SelectionKey key; /** * True if this connection should be closed after sending last server packet. */ protected boolean pendingClose; /** * True if OnDisconnect() method should be called immediately after this connection was closed. */ protected boolean isForcedClosing; /** * True if this connection is already closed. */ protected boolean closed; /** * Object on witch some methods are synchronized */ protected final Object guard = new Object(); /** * ByteBuffer for io write. */ public final ByteBuffer writeBuffer; /** * ByteBuffer for io read. */ public final ByteBuffer readBuffer; /** * Caching ip address to make sure that {@link #getIP()} method works even after disconnection */ private final String ip; /** * Used only for PacketProcessor synchronization purpose */ private boolean locked = false; /** * Constructor * * @param sc * @param d * @throws IOException */ public AConnection(SocketChannel sc, Dispatcher d) throws IOException { socketChannel = sc; dispatcher = d; writeBuffer = ByteBuffer.allocate(8192 * 2); writeBuffer.flip(); writeBuffer.order(ByteOrder.LITTLE_ENDIAN); readBuffer = ByteBuffer.allocate(8192 * 2); readBuffer.order(ByteOrder.LITTLE_ENDIAN); dispatcher.register(socketChannel, SelectionKey.OP_READ, this); this.ip = socketChannel.socket().getInetAddress().getHostAddress(); } /** * Set selection key - result of registration this AConnection socketChannel to one of dispatchers. * * @param key */ final void setKey(SelectionKey key) { this.key = key; } /** * Notify Dispatcher Selector that we want write some data here. */ protected final void enableWriteInterest() { if(key.isValid()) { key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); key.selector().wakeup(); } } /** * @return Dispatcher to witch this connection is registered. */ final Dispatcher getDispatcher() { return dispatcher; } /** * @return SocketChannel representing this connection. */ public SocketChannel getSocketChannel() { return socketChannel; } /** * Connection will be closed at some time [by Dispatcher Thread], after that onDisconnect() method will be called to * clear all other things. * * @param forced * is just hint that getDisconnectionDelay() should return 0 so OnDisconnect() method will be called * without any delay. */ public final void close(boolean forced) { synchronized(guard) { if(isWriteDisabled()) return; isForcedClosing = forced; getDispatcher().closeConnection(this); } } /** * This will only close the connection without taking care of the rest. May be called only by Dispatcher Thread. * Returns true if connection was not closed before. * * @return true if connection was not closed before. */ final boolean onlyClose() { /** * Test if this build should use assertion. If NetworkAssertion == false javac will remove this code block */ if(Assertion.NetworkAssertion) assert Thread.currentThread() == dispatcher; synchronized(guard) { if(closed) return false; try { if(socketChannel.isOpen()) { socketChannel.close(); key.attach(null); key.cancel(); } closed = true; } catch(IOException ignored) { } } return true; } /** * @return True if this connection is pendingClose and not closed yet. */ final boolean isPendingClose() { return pendingClose && !closed; } /** * @return True if write to this connection is possible. */ protected final boolean isWriteDisabled() { return pendingClose || closed; } /** * @return IP address of this Connection. */ public final String getIP() { return ip; } /** * Used only for PacketProcessor synchronization purpose. Return true if locked successful - if wasn't locked * before. * * @return locked */ boolean tryLockConnection() { if(locked) return false; return locked = true; } /** * Used only for PacketProcessor synchronization purpose. Unlock this connection. */ void unlockConnection() { locked = false; } /** * @param data * @return True if data was processed correctly, False if some error occurred and connection should be closed NOW. */ abstract protected boolean processData(ByteBuffer data); /** * This method will be called by Dispatcher, and will be repeated till return false. * * @param data * @return True if data was written to buffer, False indicating that there are not any more data to write. */ abstract protected boolean writeData(ByteBuffer data); /** * This method is called by Dispatcher when connection is ready to be closed. * * @return time in ms after witch onDisconnect() method will be called. */ abstract protected long getDisconnectionDelay(); /** * This method is called by Dispatcher to inform that this connection was closed and should be cleared. This method * is called only once. */ abstract protected void onDisconnect(); /** * This method is called by NioServer to inform that NioServer is shouting down. This method is called only once. */ abstract protected void onServerClose(); }