/*
* Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software;Designed and Developed mainly by many Chinese
* opensource volunteers. you can redistribute it and/or modify it under the
* terms of the GNU General Public License version 2 only, as published by the
* Free Software Foundation.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Any questions about this component can be directed to it's project Web address
* https://code.google.com/p/opencloudb/.
*
*/
package org.opencloudb.net;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannel;
import java.nio.channels.NetworkChannel;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.log4j.Logger;
import org.opencloudb.config.ErrorCode;
import org.opencloudb.util.TimeUtil;
/**
* @author mycat
*/
public abstract class AbstractConnection implements NIOConnection {
protected String host;
protected int localPort;
protected int port;
protected long id;
protected volatile String charset;
protected static final Logger LOGGER = Logger
.getLogger(AbstractConnection.class);
protected final NetworkChannel channel;
protected NIOHandler handler;
protected int packetHeaderSize;
protected int maxPacketSize;
protected volatile int readBufferOffset;
protected volatile ByteBuffer readBuffer;
protected volatile ByteBuffer writeBuffer;
// private volatile boolean writing = false;
protected final ConcurrentLinkedQueue<ByteBuffer> writeQueue = new ConcurrentLinkedQueue<ByteBuffer>();
protected final AtomicBoolean isClosed;
protected boolean isSocketClosed;
protected long startupTime;
protected long lastReadTime;
protected long lastWriteTime;
protected long netInBytes;
protected long netOutBytes;
protected int writeAttempts;
private long idleTimeout;
private final SocketWR socketWR=null;
public AbstractConnection(NetworkChannel channel) {
this.channel = channel;
boolean isAIO = (channel instanceof AsynchronousChannel);
// if (isAIO) {
// socketWR = new AIOSocketWR(this);
// } else {
// socketWR = new NIOSocketWR(this);
// }
this.isClosed = new AtomicBoolean(false);
this.startupTime = TimeUtil.currentTimeMillis();
this.lastReadTime = startupTime;
this.lastWriteTime = startupTime;
}
public String getCharset() {
return charset;
}
public boolean setCharset(String charset) {
this.charset = charset;
return true;
}
public long getIdleTimeout() {
return idleTimeout;
}
public SocketWR getSocketWR() {
return socketWR;
}
public void setIdleTimeout(long idleTimeout) {
this.idleTimeout = idleTimeout;
}
public int getLocalPort() {
return localPort;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
public void setLocalPort(int localPort) {
this.localPort = localPort;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public boolean isIdleTimeout() {
return TimeUtil.currentTimeMillis() > Math.max(lastWriteTime,
lastReadTime) + idleTimeout;
}
public NetworkChannel getChannel() {
return channel;
}
public int getPacketHeaderSize() {
return packetHeaderSize;
}
public void setPacketHeaderSize(int packetHeaderSize) {
this.packetHeaderSize = packetHeaderSize;
}
public int getMaxPacketSize() {
return maxPacketSize;
}
public void setMaxPacketSize(int maxPacketSize) {
this.maxPacketSize = maxPacketSize;
}
public long getStartupTime() {
return startupTime;
}
public long getLastReadTime() {
return lastReadTime;
}
public long getLastWriteTime() {
return lastWriteTime;
}
public long getNetInBytes() {
return netInBytes;
}
public long getNetOutBytes() {
return netOutBytes;
}
public int getWriteAttempts() {
return writeAttempts;
}
public ByteBuffer getReadBuffer() {
return readBuffer;
}
public void setHandler(NIOHandler handler) {
this.handler = handler;
}
@Override
public void handle(byte[] data) {
try {
handler.handle(data);
} catch (Throwable e) {
close("exeption:" + e.toString());
if (e instanceof ConnectionException) {
error(ErrorCode.ERR_CONNECT_SOCKET, e);
} else {
error(ErrorCode.ERR_HANDLE_DATA, e);
}
}
}
@Override
public void register() throws IOException {
}
public void asynRead() throws IOException {
this.socketWR.asynRead();
}
public void doNextWriteCheck() throws IOException {
this.socketWR.doNextWriteCheck();
}
public void onReadData(int got) throws IOException {
if (isClosed.get()) {
return;
}
ByteBuffer buffer = this.readBuffer;
lastReadTime = TimeUtil.currentTimeMillis();
if (got <=0) {
return;
// if (!this.isClosed()) {
// this.close("socket closed");
// return;
// }
}
netInBytes += got;
// processor.addNetInBytes(got);
// 澶勭悊鏁版嵁
int offset = readBufferOffset, length = 0, position = buffer.position();
for (;;) {
length = getPacketLength(buffer, offset);
if (length == -1) {
if (!buffer.hasRemaining()) {
buffer = checkReadBuffer(buffer, offset, position);
}
break;
}
if (position >= offset + length) {
buffer.position(offset);
byte[] data = new byte[length];
buffer.get(data, 0, length);
handle(data);
offset += length;
if (position == offset) {
if (readBufferOffset != 0) {
readBufferOffset = 0;
}
buffer.clear();
break;
} else {
readBufferOffset = offset;
buffer.position(position);
continue;
}
} else {
if (!buffer.hasRemaining()) {
buffer = checkReadBuffer(buffer, offset, position);
}
break;
}
}
}
public void write(byte[] data) {
//ByteBuffer buffer = allocate();
//buffer = writeToBuffer(data, buffer);
//write(buffer);
}
private final void writeNotSend(ByteBuffer buffer) {
writeQueue.offer(buffer);
}
@Override
public final void write(ByteBuffer buffer) {
writeQueue.offer(buffer);
// if ansyn write finishe event got lock before me ,then writing
// flag is set false but not start a write request
// so we check again
try {
this.socketWR.doNextWriteCheck();
} catch (Exception e) {
LOGGER.warn("write err:", e);
this.close("write err:" + e);
}
}
public ByteBuffer checkWriteBuffer(ByteBuffer buffer, int capacity,
boolean writeSocketIfFull) {
// if (capacity > buffer.remaining()) {
// if (writeSocketIfFull) {
// writeNotSend(buffer);
// return allocate();
// } else {// Relocate a larger buffer
// buffer.flip();
// ByteBuffer newBuf = ByteBuffer.allocate(capacity);
// newBuf.put(buffer);
// this.recycle(buffer);
// return newBuf;
// }
// } else {
// return buffer;
// }
return null;
}
public ByteBuffer writeToBuffer(byte[] src, ByteBuffer buffer) {
int offset = 0;
int length = src.length;
int remaining = buffer.remaining();
while (length > 0) {
if (remaining >= length) {
buffer.put(src, offset, length);
break;
} else {
buffer.put(src, offset, remaining);
writeNotSend(buffer);
//buffer = allocate();
offset += remaining;
length -= remaining;
remaining = buffer.remaining();
continue;
}
}
return buffer;
}
@Override
public void close(String reason) {
if (!isClosed.get()) {
closeSocket();
isClosed.set(true);
this.cleanup();
LOGGER.info("close connection,reason:" + reason + " ," + this);
}
}
public boolean isClosed() {
return isClosed.get();
}
public void idleCheck() {
if (isIdleTimeout()) {
LOGGER.info(toString() + " idle timeout");
close(" idle ");
}
}
/**
* 娓呯悊閬楃暀璧勬簮
*/
protected void cleanup() {
// 鍥炴敹鎺ユ敹缂撳瓨
if (readBuffer != null) {
//recycle(readBuffer);
this.readBuffer = null;
this.readBufferOffset = 0;
}
if (writeBuffer != null) {
//recycle(writeBuffer);
this.writeBuffer = null;
}
ByteBuffer buffer = null;
while ((buffer = writeQueue.poll()) != null) {
//recycle(buffer);
}
}
protected int getPacketLength(ByteBuffer buffer, int offset) {
if (buffer.position() < offset + packetHeaderSize) {
return -1;
} else {
int length = buffer.get(offset) & 0xff;
length |= (buffer.get(++offset) & 0xff) << 8;
length |= (buffer.get(++offset) & 0xff) << 16;
return length + packetHeaderSize;
}
}
private ByteBuffer checkReadBuffer(ByteBuffer buffer, int offset,
int position) {
// if (offset == 0) {
// if (buffer.capacity() >= maxPacketSize) {
// throw new IllegalArgumentException(
// "Packet size over the limit.");
// }
// int size = buffer.capacity() << 1;
// size = (size > maxPacketSize) ? maxPacketSize : size;
// ByteBuffer newBuffer = processor.getBufferPool().allocate(size);
// buffer.position(offset);
// newBuffer.put(buffer);
// readBuffer = newBuffer;
// recycle(buffer);
// return newBuffer;
// } else {
// buffer.position(offset);
// buffer.compact();
// readBufferOffset = 0;
// return buffer;
// }
return null;
}
public void recycle(ByteBuffer buffer) {
// TODO Auto-generated method stub
}
public ConcurrentLinkedQueue<ByteBuffer> getWriteQueue() {
return writeQueue;
}
private void closeSocket() {
if (channel != null) {
boolean isSocketClosed = true;
try {
channel.close();
} catch (Throwable e) {
}
boolean closed = isSocketClosed && (!channel.isOpen());
if (closed == false) {
LOGGER.warn("close socket of connnection failed " + this);
}
}
}
}