All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.zbus.remoting.nio.Session Maven / Gradle / Ivy

package org.zbus.remoting.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Queue;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zbus.remoting.Helper;

public class Session {
	public static enum SessionStatus {
		NEW, CONNECTED, ON_ERROR, CLOSED
	}
	private static final Logger log = LoggerFactory.getLogger(Session.class); 
	
	private SessionStatus status = SessionStatus.NEW;
	private long lastOperationTime = System.currentTimeMillis();
	private final String id; 
	
	private int bufferSize = 1024*8;
	private IoBuffer readBuffer = null;
	private Queue writeBufferQ = new LinkedBlockingQueue();
	
	private CountDownLatch connectLatch = new CountDownLatch(1);
	
	private final DispatcherManager dispatcherManager;
	private final SocketChannel channel;
	private SelectionKey registeredKey = null;
	
	private ConcurrentMap attributes = null;

	private Object attachment;
	private final EventAdaptor eventAdaptor;
	
	public Session(DispatcherManager dispatcherManager, SocketChannel channel, EventAdaptor eventAdaptor){
		this(dispatcherManager, channel, null, eventAdaptor); 
	}
	
	public Session(DispatcherManager dispatcherManager, SocketChannel channel, Object attachment, EventAdaptor eventAdaptor){
		this.dispatcherManager = dispatcherManager;
		this.id = UUID.randomUUID().toString();
		this.channel = channel; 
		this.attachment = attachment;
		this.eventAdaptor = eventAdaptor;
	}
	
	
	
	public String id(){
		return ""+this.id;
	} 
	
	public void close() throws IOException {
		if(this.status == SessionStatus.CLOSED){
			return;
		}
		this.status = SessionStatus.CLOSED;
		if(this.channel != null){
			this.channel.close();  
		}
		
		if(this.registeredKey != null){
			this.registeredKey.cancel();
			this.registeredKey = null;
		} 
	}
	
	public void asyncClose() throws IOException{ 
		if(this.registeredKey == null){
			return;
		} 
		Dispatcher dispatcher = dispatcherManager.getDispatcher(this.registeredKey);
		if(dispatcher == null){
			throw new IOException("failed to find dispatcher for session: "+this);
		}
		
		dispatcher.unregisterSession(this);
	}
	
	public void write(Object msg) throws IOException{
		write(dispatcherManager.getCodec().encode(msg));
	}
	
	public void write(IoBuffer buf) throws IOException{
		if(this.registeredKey == null){
			throw new IOException("Session not registered yet:"+this);
		}
		
		if(!writeBufferQ.offer(buf.buf())){
			String msg = "Session write buffer queue is full, message count="+writeBufferQ.size();
			log.warn(msg);
			throw new IOException(msg);
		}
		
		registeredKey.interestOps(registeredKey.interestOps() | SelectionKey.OP_WRITE);
		registeredKey.selector().wakeup(); //TODO
	}
	
	
	
	public void doRead() throws IOException { 
		if(readBuffer == null){
			readBuffer = IoBuffer.allocate(bufferSize);
		}
		ByteBuffer data = ByteBuffer.allocate(1024*4);
		
		int n = 0;
		while((n = channel.read(data)) > 0){
			data.flip();
			readBuffer.put(data.array(), data.position(), data.remaining());
			data.clear();
		}
		
		if(n < 0){
			eventAdaptor.onSessionDestroyed(this);
			asyncClose();
			return;
		} 
		
		IoBuffer tempBuf = readBuffer.duplicate().flip();
		Object msg = null;
		while(true){
			tempBuf.mark();
			if(tempBuf.remaining()>0){
				msg = dispatcherManager.getCodec().decode(tempBuf);
			} else {
				msg = null;
			}
			if(msg == null){ 
				tempBuf.reset();
				readBuffer = resetIoBuffer(tempBuf);
				break;
			}
			
			final Object theMsg = msg;  
			dispatcherManager.getExecutor().execute(new Runnable() { 

				public void run() { 
					try{
						eventAdaptor.onMessage(theMsg, Session.this);
					} catch(Throwable e){ 
						//log.error(e.getMessage(), e);
						try {
							eventAdaptor.onException(e, Session.this);
						} catch (IOException e1) { 
							try {
								close();
							} catch (Throwable e2) {
								log.error(e2.getMessage(), e2);
							}
						}  
					}
				}
			});  
			
		}  
		
	}
	protected IoBuffer resetIoBuffer(IoBuffer buffer) {
		IoBuffer newBuffer = null;

		if (buffer != null && buffer.remaining() > 0) {
			int len = buffer.remaining();
			byte[] bb = new byte[len];
			buffer.get(bb);
			newBuffer = IoBuffer.wrap(bb);
			newBuffer.position(len);
		}

		return newBuffer;
	}
	
	protected int doWrite() throws IOException{ 
		int n = 0;
		synchronized (writeBufferQ) {
			while(true){
				ByteBuffer buf = writeBufferQ.peek();
				if(buf == null){
					registeredKey.interestOps(SelectionKey.OP_READ);
					//registeredKey.selector().wakeup(); //TODO
					break;
				}
				
				int wbytes = this.channel.write(buf);
				
				if(wbytes == 0 && buf.remaining() > 0){
					//registeredKey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
					//registeredKey.selector().wakeup(); //TODO
					break;
				}
				
				n += wbytes;
				if(buf.remaining() == 0){
					writeBufferQ.remove();
					continue;
				} else {
					break;
				}
			} 
		}
		return n;
	}
	
	
	@Override
	public int hashCode() {
		return id.hashCode();
	}
	
	@Override
	public boolean equals(Object obj) { 
		if(obj instanceof Session){
			Session other = (Session)obj;
			return (this.hashCode() == other.hashCode());
		}
		return false;
	}

	public long getLastOperationTime() {
		return lastOperationTime;
	}

	public void updateLastOperationTime() {
		this.lastOperationTime = System.currentTimeMillis();
	}  
	
	public String getRemoteAddress() {
		if (this.status != SessionStatus.CLOSED) { 
			InetAddress addr = this.channel.socket().getInetAddress();
			return String.format("%s:%d", addr.getHostAddress(),channel.socket().getPort());
		} 
		return null;
	}
	
	public String getLocalAddress() {
		if (this.status != SessionStatus.CLOSED) { 
			return Helper.localAddress(this.channel);
		} 
		return null;
	}


	public int interestOps() throws IOException{
		if(this.registeredKey == null){
			throw new IOException("Session not registered yet:"+this);
		}
		return this.registeredKey.interestOps();
	}
	
	
	public void register(int interestOps) throws IOException{
		dispatcherManager.registerSession(interestOps, this);
	}
	
	public void interestOps(int ops){
		if(this.registeredKey == null){
			throw new IllegalStateException("registered session required");
		}
		this.registeredKey.interestOps(ops); 
	}
	
	public void interestOpsAndWakeup(int ops){
		interestOps(ops);
		this.registeredKey.selector().wakeup();
	}
	

	public SelectionKey getRegisteredKey() {
		return registeredKey;
	}
	public void setRegisteredKey(SelectionKey key) {
		this.registeredKey = key;
	}
	public SessionStatus getStatus() {
		return status;
	}
	
	public boolean isActive(){
		return this.status == SessionStatus.CONNECTED;
	}
	public boolean isNew(){
		return this.status == SessionStatus.NEW;
	}

	public void setStatus(SessionStatus status) {
		this.status = status;
	}

	public SocketChannel getChannel() {
		return channel;
	} 
	

	public DispatcherManager dispatcherManager() {
		return dispatcherManager;
	}

	public void finishConnect(){
		this.connectLatch.countDown();
	}
	
	
	public boolean waitToConnect(long millis){
		try { 
			return this.connectLatch.await(millis, TimeUnit.MILLISECONDS); 
		}catch (InterruptedException e) {
			log.error(e.getMessage(), e);
		}
		return false;
	}
	
	@SuppressWarnings("unchecked")
	public  T attr(String key){
		if(this.attributes == null){
			return null;
		}
		
		return (T)this.attributes.get(key);
	}
	
	public  void attr(String key, T value){
		if(this.attributes == null){
			synchronized (this) {
				if(this.attributes == null){
					this.attributes = new ConcurrentHashMap();
				}
			} 
		}
		this.attributes.put(key, value);
	}

	public String toString() {
		return "Session ["
				+ "remote=" + getRemoteAddress()
				+ ", status=" + status  
	            + ", id=" + id  
				+ ", attributes="+ attributes 
				+ "]";
	}



	public Object getAttachment() {
		return attachment;
	}

	public void setAttachment(Object attachment) {
		this.attachment = attachment;
	}

	public EventAdaptor getEventAdaptor() {
		return eventAdaptor;
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy