org.zodiac.sdk.nio.channeling.Channeling Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of zodiac-sdk-nio Show documentation
Show all versions of zodiac-sdk-nio Show documentation
Zodiac SDK NIO2(New Non-Blocking IO)
package org.zodiac.sdk.nio.channeling;
import javax.net.ssl.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.StandardSocketOptions;
import java.nio.channels.SocketChannel;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Channeling {
private static final int DEFAULT_PEEK_TIME = -1;
private final Queue[] channelQueues;
private final Map sslEnginesOrigin;
private final ExecutorService eventRunner;
final long connectionTimeoutInMs, readWriteTimeOutInMs;
final private List channelingPlugins;
boolean noEagerSocket = false;
boolean active = true;
private final int nWorker;
private int numOfSSLWoker = -1;
public static final String CHANNELING_VERSION = "Channeling/2.3.2";
public static Channeling startNewChanneling() throws IOException {
return startNewChanneling(1, DEFAULT_PEEK_TIME);
}
public static Channeling startNewChanneling(int workers) throws IOException {
return startNewChanneling(workers, DEFAULT_PEEK_TIME);
}
public static Channeling startNewChanneling(int workers, int peekPerNano) throws IOException {
return startNewChanneling(workers, peekPerNano, 1500, 15000);
}
public static Channeling startNewChanneling(int workers, long connectionTimeoutInMs, long readWriteTimeOutInMs) throws IOException {
return startNewChanneling(workers, DEFAULT_PEEK_TIME, connectionTimeoutInMs, readWriteTimeOutInMs);
}
/**
* @param workers number of worker should have run in background
* @param peekPerNano peekPerms per count per socket, default is 16ms
* @param connectionTimeoutInMs time in milisecond to get timeoutException
* @param readWriteTimeOutInMs time in milisecond to get timeoutException
* @return Channeling
* @throws IOException Throws IO Exception
*/
public static Channeling startNewChanneling(int workers, int peekPerNano, long connectionTimeoutInMs, long readWriteTimeOutInMs) throws IOException {
return startNewChanneling(workers, peekPerNano,
connectionTimeoutInMs, readWriteTimeOutInMs, null);
}
public static Channeling startNewChanneling(int workers, int peekPerNano, long connectionTimeoutInMs,
long readWriteTimeOutInMs, List channelingPlugins) throws IOException {
return new Channeling(workers, peekPerNano, connectionTimeoutInMs, readWriteTimeOutInMs, channelingPlugins);
}
public static void KeepAlive(ChannelingSocket cs, boolean trueOrFalse) throws IOException {
SocketChannel sc = cs.getSocketChannel();
sc.socket().setKeepAlive(trueOrFalse);
sc.setOption(StandardSocketOptions.SO_KEEPALIVE, trueOrFalse);
}
public void setNoEagerSocket(boolean noEagerSocket) {
this.noEagerSocket = noEagerSocket;
}
/**
* @param numOfWorker for sslengine delegating task worker
*/
public void enableSSL(int numOfWorker) {
this.numOfSSLWoker = numOfWorker;
}
public boolean hasSSL() {
return this.numOfSSLWoker > 0;
}
private Channeling(int workers, int peekPerNano, long connectionTimeoutInMs, long readWriteTimeOutInMs, List channelingPlugins) throws IOException {
this.connectionTimeoutInMs = connectionTimeoutInMs;
this.readWriteTimeOutInMs = readWriteTimeOutInMs;
this.channelQueues = new ConcurrentLinkedQueue[workers];
this.sslEnginesOrigin = new HashMap<>();
this.channelingPlugins = new ArrayList<>();
this.channelingPlugins.add(new ChannelingTimeoutFeature(connectionTimeoutInMs, readWriteTimeOutInMs));
if (channelingPlugins != null) {
this.channelingPlugins.addAll(channelingPlugins);
}
this.nWorker = workers;
if (workers == 1) {
eventRunner = Executors.newSingleThreadExecutor();
} else {
eventRunner = Executors.newFixedThreadPool(workers);
}
for (int i = 0; i < workers; i++) {
channelQueues[i] = new ConcurrentLinkedQueue<>();
eventRunner.execute(new ChannelingProcessor(channelQueues[i], this, peekPerNano));
}
}
public List getChannelingPlugins() {
return channelingPlugins;
}
public ChannelingSocket wrap(SocketChannel socketChannel, Object attachment) {
return wrap(socketChannel, attachment, 1024);
}
public ChannelingSocket wrap(SocketChannel socketChannel, Object attachment, int bufferSize) {
int tix = (int) (Thread.currentThread().getId() % this.nWorker);
return new ChannelRunner(socketChannel, attachment, bufferSize, channelQueues[tix]);
}
public ChannelingSocket wrap(Object attachment) throws IOException {
return wrap(attachment, 1024);
}
public ChannelingSocket wrap(Object attachment, int bufferSize) throws IOException {
return wrap(SocketChannel.open(), attachment, bufferSize);
}
public ChannelingSocket wrapSSL(SSLEngine sslEngine, Object attachment) throws Exception {
return wrapSSL(sslEngine, attachment, 1024);
}
public ChannelingSocket wrapSSL(String protocols,
String remoteHandshakeAddress,
int remoteHandshakePort,
Object attachment) throws Exception {
SSLContext sslContext = getDefaultSSLContext(protocols); //SSLContext.getInstance("TLSv1.2");
return wrapSSL(sslContext,
remoteHandshakeAddress,
remoteHandshakePort, attachment);
}
public ChannelingSocket wrapSSL(SSLContext sslContext,
String remoteHandshakeAddress,
int remoteHandshakePort, Object attachment) throws Exception {
SSLEngine engine = sslContext.createSSLEngine(remoteHandshakeAddress, remoteHandshakePort);
engine.setUseClientMode(true);
return wrapSSL(engine, attachment, /*1024 not in use in ssl, ssl using session buffer*/ 1024);
}
public ChannelingSocket wrapSSL(SSLEngine sslEngine, Object attachment, int buffSize) throws Exception {
return wrapSSL(sslEngine, attachment, buffSize, null);
}
public ChannelingSocket wrapSSL(SSLEngine sslEngine, Object attachment, int buffSize, SocketChannel socketChannel) throws Exception {
if (this.numOfSSLWoker < 0) {
throw new Exception("enableSSL is required ...");
}
int tix = (int) (Thread.currentThread().getId() % this.nWorker);
// Try resize SSL Engine same tix with the same engine to prevent concurrent issue
// TODO still apply resize SSL Engine? since it's one for one socket
// tix = resideSSLEngine(sslEngine, tix);
if(socketChannel == null) {
return new ChannelSSLRunner(sslEngine, this.numOfSSLWoker, attachment, buffSize, channelQueues[tix]);
}
return new ChannelSSLRunner(sslEngine, this.numOfSSLWoker, attachment, buffSize, channelQueues[tix], socketChannel);
}
public ChannelingSocket wrapSSLServer(SSLContext sslContext,
Object attachment,
String hostAddress,
int port) throws Exception {
if (sslContext != null && this.numOfSSLWoker < 0) {
throw new Exception("enableSSL is required ...");
}
int tix = (int) (Thread.currentThread().getId() % this.nWorker);
return new ChannelServerRunner(sslContext, this.numOfSSLWoker,attachment, 1024, hostAddress, port, channelQueues[tix]);
}
public ChannelingSocket wrapServer(Object attachment,
String hostAddress,
int port) throws Exception {
return wrapSSLServer(null, attachment, hostAddress, port);
}
/***
Proxy Scope
*
*
* @param proxy the ChannelingProxy proxy config
* @param socketChannel SocketChannel reference
* @param attachment context attachment
* @return ChannelingSocket
*/
public ChannelingSocket wrapProxy(ChannelingProxy proxy, SocketChannel socketChannel, Object attachment) {
return wrapProxy(proxy, socketChannel, attachment, 1024);
}
public ChannelingSocket wrapProxy(ChannelingProxy proxy, SocketChannel socketChannel, Object attachment, int bufferSize) {
int tix = (int) (Thread.currentThread().getId() % this.nWorker);
return new ChannelProxyRunner(proxy, socketChannel, attachment, bufferSize, channelQueues[tix]);
}
public ChannelingSocket wrapProxy(ChannelingProxy proxy, Object attachment) throws IOException {
return wrapProxy(proxy, attachment, 1024);
}
public ChannelingSocket wrapProxy(ChannelingProxy proxy, Object attachment, int bufferSize) throws IOException {
return wrapProxy(proxy, SocketChannel.open(), attachment, bufferSize);
}
public ChannelingSocket wrapProxySSL(ChannelingProxy proxy, SSLEngine sslEngine, Object attachment) throws Exception {
return wrapProxySSL(proxy, sslEngine, attachment, sslEngine.getSession().getApplicationBufferSize());
}
public ChannelingSocket wrapProxySSL(ChannelingProxy proxy, String protocols,
String remoteHandshakeAddress,
int remoteHandshakePort,
Object attachment) throws Exception {
SSLContext sslContext = getDefaultSSLContext(protocols); //SSLContext.getInstance("TLSv1.2");
return wrapProxySSL(proxy, sslContext,
remoteHandshakeAddress,
remoteHandshakePort, attachment);
}
public ChannelingSocket wrapProxySSL(ChannelingProxy proxy, SSLContext sslContext,
String remoteHandshakeAddress,
int remoteHandshakePort, Object attachment) throws Exception {
SSLEngine engine = sslContext.createSSLEngine(remoteHandshakeAddress, remoteHandshakePort);
engine.setUseClientMode(true);
return wrapProxySSL(proxy, engine, attachment, /*1024 not in use in ssl, ssl using session buffer*/ engine.getSession().getApplicationBufferSize());
}
public ChannelingSocket wrapProxySSL(ChannelingProxy proxy, SSLEngine sslEngine, Object attachment, int buffSize) throws Exception {
if (this.numOfSSLWoker < 0) {
throw new Exception("enableSSL is required ...");
}
int tix = (int) (Thread.currentThread().getId() % this.nWorker);
// Try resize SSL Engine same tix with the same engine to prevent concurrent issue
// TODO still apply resize SSL Engine? since it's one for one socket
// tix = resideSSLEngine(sslEngine, tix);
return new ChannelProxySSLRunner(proxy, sslEngine, this.numOfSSLWoker, attachment, buffSize, channelQueues[tix]);
}
private synchronized int resideSSLEngine(SSLEngine sslEngine, int tix) {
String sslEngineKey = getSSLEngineKey(sslEngine);
if (sslEnginesOrigin.containsKey(sslEngineKey)) {
return sslEnginesOrigin.get(sslEngineKey);
}
sslEnginesOrigin.put(sslEngineKey, tix);
return tix;
}
private String getSSLEngineKey(SSLEngine sslEngine) {
return String.format("%s_%d", sslEngine.getPeerHost(), sslEngine.getPeerPort());
}
/**
* When shutdown now, eventRunner service thread will be shutdown immediately
*/
public void shutdownNow() {
active = false;
eventRunner.shutdownNow();
ChannelSSLRunner.shutdownSSLService();
}
public void shutdown() {
active = false;
eventRunner.shutdown();
ChannelSSLRunner.shutdownSSLService();
}
public static WhenConnectingStatus whenConnected = connectingStatus -> connectingStatus;
public static WhenClosingStatus whenClosed = closingStatus -> closingStatus;
public static WhenReadWriteProcess whenByteConsumed = bytesProcessed -> bytesProcessed > 0;
public static WhenReadWriteProcess whenBytesWritten = bytesProcessed -> bytesProcessed > 0;
public static WhenWritingByteBuffer whenNoMoreToWrite = byteBuffer -> !byteBuffer.hasRemaining();
protected static TrustManager[] createTrustManagers(String filepath, String keystorePassword) throws Exception {
KeyStore trustStore = KeyStore.getInstance("JKS");
InputStream trustStoreIS = new FileInputStream(filepath);
try {
trustStore.load(trustStoreIS, keystorePassword.toCharArray());
} finally {
if (trustStoreIS != null) {
trustStoreIS.close();
}
}
TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustFactory.init(trustStore);
return trustFactory.getTrustManagers();
}
protected static String getDefaultKeyStore() {
return System.getProperty("java.home") + "/lib/security/cacerts".replace('/', File.separatorChar);
}
protected static SSLContext getDefaultSSLContext(String protocol) throws Exception {
SSLContext context = SSLContext.getInstance(protocol);
context.init(null,
createTrustManagers(getDefaultKeyStore(), "changeit"), new SecureRandom());
return context;
}
}