/**
*Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)]
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*Unless required by applicable law or agreed to in writing,
*software distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
*either express or implied. See the License for the specific language governing permissions and limitations under the License
*/
/**
*Copyright [2009-2010] [dennis zhuang(killme2008@gmail.com)]
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*Unless required by applicable law or agreed to in writing,
*software distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
*either express or implied. See the License for the specific language governing permissions and limitations under the License
*/
package com.google.code.hs4j.network.core.impl;
import java.io.IOException;
import java.nio.ByteOrder;
import java.nio.channels.FileChannel;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.code.hs4j.network.buffer.IoBuffer;
import com.google.code.hs4j.network.core.CodecFactory;
import com.google.code.hs4j.network.core.Dispatcher;
import com.google.code.hs4j.network.core.Handler;
import com.google.code.hs4j.network.core.Session;
import com.google.code.hs4j.network.core.SessionConfig;
import com.google.code.hs4j.network.core.WriteMessage;
import com.google.code.hs4j.network.statistics.Statistics;
/**
* Base connection
*
* @author dennis
*
*/
public abstract class AbstractSession implements Session {
protected IoBuffer readBuffer;
protected static final Logger log = LoggerFactory
.getLogger(AbstractSession.class);
protected final ConcurrentHashMap<String, Object> attributes = new ConcurrentHashMap<String, Object>();
protected Queue<WriteMessage> writeQueue;
protected volatile long sessionIdleTimeout;
protected volatile long sessionTimeout;
public long getSessionIdleTimeout() {
return sessionIdleTimeout;
}
public void setSessionIdleTimeout(long sessionIdleTimeout) {
this.sessionIdleTimeout = sessionIdleTimeout;
}
public long getSessionTimeout() {
return sessionTimeout;
}
public void setSessionTimeout(long sessionTimeout) {
this.sessionTimeout = sessionTimeout;
}
public Queue<WriteMessage> getWriteQueue() {
return writeQueue;
}
public Statistics getStatistics() {
return statistics;
}
public Handler getHandler() {
return handler;
}
public Dispatcher getDispatchMessageDispatcher() {
return dispatchMessageDispatcher;
}
public ReentrantLock getWriteLock() {
return writeLock;
}
protected CodecFactory.Encoder encoder;
protected CodecFactory.Decoder decoder;
protected volatile boolean closed;
protected Statistics statistics;
protected Handler handler;
protected boolean loopback;
public AtomicLong lastOperationTimeStamp = new AtomicLong(0);
protected AtomicLong scheduleWritenBytes = new AtomicLong(0);
protected final Dispatcher dispatchMessageDispatcher;
protected volatile boolean useBlockingWrite = false;
protected volatile boolean useBlockingRead = true;
protected volatile boolean handleReadWriteConcurrently = true;
public abstract void decode();
public void updateTimeStamp() {
lastOperationTimeStamp.set(System.currentTimeMillis());
}
public long getLastOperationTimeStamp() {
return lastOperationTimeStamp.get();
}
public final boolean isHandleReadWriteConcurrently() {
return handleReadWriteConcurrently;
}
public final void setHandleReadWriteConcurrently(
boolean handleReadWriteConcurrently) {
this.handleReadWriteConcurrently = handleReadWriteConcurrently;
}
public long getScheduleWritenBytes() {
return scheduleWritenBytes.get();
}
public CodecFactory.Encoder getEncoder() {
return encoder;
}
public void setEncoder(CodecFactory.Encoder encoder) {
this.encoder = encoder;
}
public CodecFactory.Decoder getDecoder() {
return decoder;
}
public IoBuffer getReadBuffer() {
return readBuffer;
}
public void setReadBuffer(IoBuffer readBuffer) {
this.readBuffer = readBuffer;
}
public void setDecoder(CodecFactory.Decoder decoder) {
this.decoder = decoder;
}
public final ByteOrder getReadBufferByteOrder() {
if (readBuffer == null) {
throw new IllegalStateException();
}
return readBuffer.order();
}
public final void setReadBufferByteOrder(ByteOrder readBufferByteOrder) {
if (readBuffer == null) {
throw new NullPointerException("Null ReadBuffer");
}
readBuffer.order(readBufferByteOrder);
}
// synchronized,prevent reactors invoking this method concurrently.
protected synchronized void onIdle() {
try {
// check twice
if (isIdle()) {
updateTimeStamp();
handler.onSessionIdle(this);
}
} catch (Throwable e) {
onException(e);
}
}
protected void onConnected() {
try {
handler.onSessionConnected(this);
} catch (Throwable e) {
onException(e);
}
}
public void onExpired() {
try {
if (isExpired()) {
handler.onSessionExpired(this);
}
} catch (Throwable e) {
onException(e);
}
}
protected abstract WriteMessage wrapMessage(Object msg,
Future<Boolean> writeFuture);
/**
* Pre-Process WriteMessage before writing to channel
*
* @param writeMessage
* @return
*/
protected WriteMessage preprocessWriteMessage(WriteMessage writeMessage) {
return writeMessage;
}
protected void dispatchReceivedMessage(final Object message) {
if (dispatchMessageDispatcher == null) {
long start = -1;
if (statistics != null && statistics.isStatistics()) {
start = System.currentTimeMillis();
}
onMessage(message, this);
if (start != -1) {
statistics
.statisticsProcess(System.currentTimeMillis() - start);
}
} else {
dispatchMessageDispatcher.dispatch(new Runnable() {
public void run() {
long start = -1;
if (statistics != null && statistics.isStatistics()) {
start = System.currentTimeMillis();
}
onMessage(message, AbstractSession.this);
if (start != -1) {
statistics.statisticsProcess(System.currentTimeMillis()
- start);
}
}
});
}
}
private void onMessage(final Object message, Session session) {
try {
handler.onMessageReceived(session, message);
} catch (Throwable e) {
onException(e);
}
}
public final boolean isClosed() {
return closed;
}
public final void setClosed(boolean closed) {
this.closed = closed;
}
public void close() {
synchronized (this) {
if (isClosed()) {
return;
}
setClosed(true);
}
try {
closeChannel();
clearAttributes();
log.debug("session closed");
} catch (IOException e) {
onException(e);
log.error("Close session error", e);
} finally {
onClosed();
}
}
protected abstract void closeChannel() throws IOException;
public void onException(Throwable e) {
handler.onExceptionCaught(this, e);
}
protected void onClosed() {
try {
handler.onSessionClosed(this);
} catch (Throwable e) {
onException(e);
}
}
public void setAttribute(String key, Object value) {
attributes.put(key, value);
}
public Object setAttributeIfAbsent(String key, Object value) {
return attributes.putIfAbsent(key, value);
}
public void removeAttribute(String key) {
attributes.remove(key);
}
public Object getAttribute(String key) {
return attributes.get(key);
}
public void clearAttributes() {
attributes.clear();
}
public synchronized void start() {
log.debug("session started");
onStarted();
start0();
}
protected abstract void start0();
protected void onStarted() {
try {
handler.onSessionStarted(this);
} catch (Throwable e) {
onException(e);
}
}
protected ReentrantLock writeLock = new ReentrantLock();
protected AtomicReference<WriteMessage> currentMessage = new AtomicReference<WriteMessage>();
static final class FailFuture implements Future<Boolean> {
public boolean cancel(boolean mayInterruptIfRunning) {
return Boolean.FALSE;
}
public Boolean get() throws InterruptedException, ExecutionException {
return Boolean.FALSE;
}
public Boolean get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException,
TimeoutException {
return Boolean.FALSE;
}
public boolean isCancelled() {
return false;
}
public boolean isDone() {
return true;
}
}
public void write(Object packet) {
if (closed) {
return;
}
WriteMessage message = wrapMessage(packet, null);
scheduleWritenBytes.addAndGet(message.getWriteBuffer().remaining());
writeFromUserCode(message);
}
public abstract void writeFromUserCode(WriteMessage message);
public final boolean isLoopbackConnection() {
return loopback;
}
public boolean isUseBlockingWrite() {
return useBlockingWrite;
}
public void setUseBlockingWrite(boolean useBlockingWrite) {
this.useBlockingWrite = useBlockingWrite;
}
public boolean isUseBlockingRead() {
return useBlockingRead;
}
public void setUseBlockingRead(boolean useBlockingRead) {
this.useBlockingRead = useBlockingRead;
}
public void clearWriteQueue() {
writeQueue.clear();
}
public boolean isExpired() {
return false;
}
public boolean isIdle() {
long lastOpTimestamp = getLastOperationTimeStamp();
return lastOpTimestamp > 0
&& System.currentTimeMillis() - lastOpTimestamp > sessionIdleTimeout;
}
public AbstractSession(SessionConfig sessionConfig) {
super();
lastOperationTimeStamp.set(System.currentTimeMillis());
statistics = sessionConfig.statistics;
handler = sessionConfig.handler;
writeQueue = sessionConfig.queue;
encoder = sessionConfig.codecFactory.getEncoder();
decoder = sessionConfig.codecFactory.getDecoder();
dispatchMessageDispatcher = sessionConfig.dispatchMessageDispatcher;
handleReadWriteConcurrently = sessionConfig.handleReadWriteConcurrently;
sessionTimeout = sessionConfig.sessionTimeout;
sessionIdleTimeout = sessionConfig.sessionIdelTimeout;
}
public long transferTo(long position, long count, FileChannel target)
throws IOException {
throw new UnsupportedOperationException();
}
public long transferFrom(long position, long count, FileChannel source)
throws IOException {
throw new UnsupportedOperationException();
}
protected void onCreated() {
try {
handler.onSessionCreated(this);
} catch (Throwable e) {
onException(e);
}
}
}