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

com.barchart.udt.nio.SelectionKeyUDT Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (C) 2009-2013 Barchart, Inc. 
 *
 * All rights reserved. Licensed under the OSI BSD License.
 *
 * http://www.opensource.org/licenses/bsd-license.php
 */
package com.barchart.udt.nio;

import java.nio.channels.CancelledKeyException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.barchart.udt.EpollUDT;
import com.barchart.udt.EpollUDT.Opt;
import com.barchart.udt.ExceptionUDT;
import com.barchart.udt.OptionUDT;
import com.barchart.udt.SocketUDT;
import com.barchart.udt.StatusUDT;

/**
 * UDT selection key implementation.
 */
public class SelectionKeyUDT extends SelectionKey implements
		Comparable {

	/**
	 * JDK interest to Epoll READ mapping.
	 */
	protected static final int HAS_READ = OP_ACCEPT | OP_READ;

	/**
	 * JDK interest to Epoll WRITE mapping.
	 */
	protected static final int HAS_WRITE = OP_CONNECT | OP_WRITE;

	protected static final Logger log = LoggerFactory
			.getLogger(SelectionKeyUDT.class);

	/**
	 * Convert select options : from jdk into epoll.
	 */
	protected static Opt from(final int interestOps) {

		final boolean hasRead = (interestOps & HAS_READ) != 0;
		final boolean hasWrite = (interestOps & HAS_WRITE) != 0;

		if (hasRead && hasWrite) {
			return Opt.ALL;
		}

		if (hasRead) {
			return Opt.ERROR_READ;
		}

		if (hasWrite) {
			return Opt.ERROR_WRITE;
		}

		return Opt.ERROR;

	}

	/**
	 * Render select options.
	 */
	public static final String toStringOps(final int selectOps) {
		final char A = (OP_ACCEPT & selectOps) != 0 ? 'A' : '-';
		final char C = (OP_CONNECT & selectOps) != 0 ? 'C' : '-';
		final char R = (OP_READ & selectOps) != 0 ? 'R' : '-';
		final char W = (OP_WRITE & selectOps) != 0 ? 'W' : '-';
		return String.format("%c%c%c%c", A, C, R, W);
	}

	/**
	 * Channel bound to the key.
	 */
	private final ChannelUDT channelUDT;

	/**
	 * Requested interest in epoll format.
	 */
	private volatile Opt epollOpt;

	/**
	 * Requested interest in JDK format.
	 */
	private volatile int interestOps;

	/**
	 * Key validity state. Key is valid when created, and invalid when canceled.
	 */
	private volatile boolean isValid;

	/**
	 * Reported ready interest.
	 */
	private volatile int readyOps;

	/**
	 * Correlation index for {@link #doRead(int)} vs {@link #doWrite(int)}
	 */
	private volatile int resultIndex;

	/**
	 * Selector bound to the key.
	 */
	private final SelectorUDT selectorUDT;

	protected SelectionKeyUDT( //
			final SelectorUDT selectorUDT, //
			final ChannelUDT channelUDT, //
			final Object attachment //
	) {

		super.attach(attachment);

		this.selectorUDT = selectorUDT;
		this.channelUDT = channelUDT;

		makeValid(true);

	}

	/**
	 * Ensure key is NOT canceled.
	 */
	protected void assertValidKey() throws CancelledKeyException {
		if (isValid()) {
			return;
		}
		throw new CancelledKeyException();
	}

	/**
	 * Ensure only permitted interest mask bits are present.
	 */
	protected void assertValidOps(final int interestOps) {
		if ((interestOps & ~(channel().validOps())) != 0) {
			throw new IllegalArgumentException("invalid interestOps="
					+ interestOps);
		}
	}

	@Override
	public void cancel() {
		if (isValid()) {
			selector().cancel(this);
		}
	}

	@Override
	public SelectableChannel channel() {
		return (SelectableChannel) channelUDT;
	}

	/**
	 * Underlying UDT channel.
	 */
	protected ChannelUDT channelUDT() {
		return channelUDT;
	}

	@Override
	public int compareTo(final SelectionKeyUDT that) {
		final int thisId = this.socketId();
		final int thatId = that.socketId();
		if (thisId > thatId) {
			return +1;
		}
		if (thisId < thatId) {
			return -1;
		}
		return 0;
	}

	/**
	 * Apply READ readiness according to {@link KindUDT} channel role.
	 * 

* Note: {@link #doRead(int)} is invoked before {@link #doWrite(int)} *

* Sockets with exceptions are returned to both read and write sets. * * @return Should report ready-state change? */ protected boolean doRead(final int resultIndex) { int readyOps = 0; final int interestOps = this.interestOps; /** Store read/write verifier. */ this.resultIndex = resultIndex; try { /** Check error report. */ if (!epollOpt.hasRead()) { if (isSocketBroken()) { readyOps = channel().validOps(); return true; } else { logError("Unexpected error report."); return false; } } switch (kindUDT()) { case ACCEPTOR: if ((interestOps & OP_ACCEPT) != 0) { readyOps = OP_ACCEPT; return true; } else { logError("Ready to ACCEPT while not interested."); return false; } case CONNECTOR: case RENDEZVOUS: if ((interestOps & OP_READ) != 0) { readyOps = OP_READ; return true; } else { logError("Ready to READ while not interested."); return false; } default: logError("Wrong kind."); return false; } } finally { this.readyOps = readyOps; } } /** * Apply WRITE readiness according to {@link KindUDT} channel role. *

* Note: {@link #doRead(int)} is invoked before {@link #doWrite(int)} *

* Sockets with exceptions are returned to both read and write sets. * * @return Should report ready-state change? */ protected boolean doWrite(final int resultIndex) { int readyOps = 0; final int interestOps = this.interestOps; /** Verify read/write relationship. */ final boolean hadReadBeforeWrite = this.resultIndex == resultIndex; try { /** Check error report. */ if (!epollOpt.hasWrite()) { if (isSocketBroken()) { readyOps = channel().validOps(); return true; } else { logError("Unexpected error report."); return false; } } switch (kindUDT()) { case ACCEPTOR: logError("Ready to WRITE for acceptor."); return false; case CONNECTOR: case RENDEZVOUS: if (channelUDT().isConnectFinished()) { if ((interestOps & OP_WRITE) != 0) { readyOps = OP_WRITE; return true; } else { logError("Ready to WRITE when not insterested."); return false; } } else { if ((interestOps & OP_CONNECT) != 0) { readyOps = OP_CONNECT; return true; } else { logError("Ready to CONNECT when not interested."); return false; } } default: logError("Wrong kind."); return false; } } finally { if (hadReadBeforeWrite) { this.readyOps |= readyOps; } else { this.readyOps = readyOps; } } } /** * Requested interest in epoll format. */ protected Opt epollOpt() { return epollOpt; } /** * Epoll bound to this key. */ protected EpollUDT epollUDT() { return selector().epollUDT(); } /** * Key equality based on socket-id. */ @Override public boolean equals(final Object otherKey) { if (otherKey instanceof SelectionKeyUDT) { final SelectionKeyUDT other = (SelectionKeyUDT) otherKey; return other.socketId() == this.socketId(); } return false; } /** * Check socket error condition. */ boolean hasError() throws ExceptionUDT { final int code = socketUDT().getOption(OptionUDT.Epoll_Event_Mask); return EpollUDT.Opt.from(code).hasError(); } /** * Key hach code based on socket-id. */ @Override public int hashCode() { return socketId(); } @Override public int interestOps() { return interestOps; } @Override public SelectionKey interestOps(final int interestOps) { assertValidKey(); assertValidOps(interestOps); try { final Opt epollNew = from(interestOps); if (epollNew != epollOpt) { if (Opt.ERROR == epollNew) { epollUDT().remove(socketUDT()); } else { epollUDT().remove(socketUDT()); epollUDT().add(socketUDT(), epollNew); } epollOpt = epollNew; } } catch (final Exception e) { log.error("epoll udpate failure", e); } finally { this.interestOps = interestOps; } return this; } /** * Check socket termination status. * * @return true if status is {@link StatusUDT#BROKEN} or worse */ protected boolean isSocketBroken() { switch (socketUDT().status()) { case INIT: case OPENED: case LISTENING: case CONNECTING: case CONNECTED: return false; case BROKEN: case CLOSING: case CLOSED: case NONEXIST: return true; default: logError("Unknown socket status."); return true; } } @Override public boolean isValid() { return isValid; } /** * Channel role. */ protected KindUDT kindUDT() { return channelUDT.kindUDT(); } /** * Key processing logic error logger. */ protected void logError(final String comment) { final String message = "logic error : \n\t" + this; log.warn(message, new Exception("" + comment)); } /** * Change socket registration with epoll, and change key validity status. */ protected void makeValid(final boolean isValid) { try { if (isValid) { epollOpt = Opt.ERROR; epollUDT().add(socketUDT(), epollOpt); } else { epollUDT().remove(socketUDT()); } } catch (final Throwable e) { log.error("Epoll failure.", e); } finally { this.isValid = isValid; } } @Override public int readyOps() { return readyOps; } protected void readyOps(final int ops) { readyOps = ops; } @Override public SelectorUDT selector() { return selectorUDT; } /** * Id of a socket bound to this key. */ protected int socketId() { return socketUDT().id(); } /** * Socket bound to this key. */ protected SocketUDT socketUDT() { return channelUDT.socketUDT(); } @Override public String toString() { return String .format("[id: 0x%08x] poll=%s ready=%s inter=%s %s %s %s bind=%s:%s peer=%s:%s", // socketUDT().id(), // epollOpt, // toStringOps(readyOps), // toStringOps(interestOps), // channelUDT.typeUDT(), // channelUDT.kindUDT(), // socketUDT().status(), // socketUDT().getLocalInetAddress(), // socketUDT().getLocalInetPort(), // socketUDT().getRemoteInetAddress(), // socketUDT().getRemoteInetPort() // ); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy