Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.rouplex.platform.tcp;
import org.rouplex.commons.Supplier;
import org.rouplex.commons.annotations.Nullable;
import org.rouplex.nio.channels.SSLSelector;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.Selector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* This class serves as a binder between TCP endpoints, such as {@link RouplexTcpClient}s or {@link RouplexTcpServer}s,
* and the rouplex platform. Internally, it manages the communication channels by registering them to channel selectors
* and using the executors threads to select upon them.
*
* There are two creation patterns for instances of this class:
*
* 1. Created by {@link RouplexTcpClient.Builder} or {@link RouplexTcpServer.Builder} when there is no RouplexTcpBinder
* assigned to it, in which case a RouplexTcpBinder will be created automatically and used by the endpoint created. In
* that case, the RouplexTcpBinder will be closed by that endpoint when the endpoint is closed in its turn.
*
* 2. Created via a constructor and passed to one or more {@link RouplexTcpClient}s or {{@link RouplexTcpServer}s via
* {@link RouplexTcpClient.Builder#withRouplexTcpBinder(RouplexTcpBinder)} or
* {@link RouplexTcpServer.Builder#withRouplexTcpBinder(RouplexTcpBinder)}. In this case, the various clients and/or
* servers created will not close this instance when they are closed.
*
* @author Andi Mullaraj (andimullaraj at gmail.com)
*/
public class RouplexTcpBinder implements Closeable {
private final static Supplier DEFAULT_SELECTOR_SUPPLIER = new Supplier() {
@Override
public Selector get() {
try {
return SSLSelector.open();
} catch (IOException ioe) {
throw new RuntimeException("Could not create SSLSelector", ioe);
}
}
};
private static final ThreadFactory DEFAULT_THREAD_FATORY = new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable);
thread.setDaemon(true);
return thread;
}
};
private final Object lock = new Object();
private final ExecutorService executorService;
private final boolean sharedExecutorService;
private final RouplexTcpSelector[] tcpSelectors;
private final AtomicInteger tcpSelectorIndex = new AtomicInteger();
private boolean closed;
@Nullable
protected RouplexTcpClientListener rouplexTcpClientListener;
@Nullable
protected RouplexTcpServerListener rouplexTcpServerListener;
/**
* Construct an instance using a default selector supplier, a default executor service and default read buffer size.
*/
public RouplexTcpBinder() {
this(DEFAULT_SELECTOR_SUPPLIER);
}
/**
* Construct an instance using the specified selector supplier, a default executor service and default read buffer
* size.
*
* @param selectorSupplier
* a supplier for selector instances. Expect it to be called once per cpu core available.
*/
public RouplexTcpBinder(Supplier selectorSupplier) {
this(selectorSupplier, null);
}
/**
* Construct an instance using the specified selector supplier, executor service and a default read buffer size.
*
* @param selectorSupplier
* a supplier for selector instances. Expect it to be called once per cpu core available.
* @param executorService
* an executor service suggested to have at least as many available threads as there are cpu cores. This
* executor will not be shutdown when this instance is closed. On the other hand, if this field is left
* null, an executor service will be created and used; when this instance will be closed, that executor
* will be shut down.
*/
public RouplexTcpBinder(Supplier selectorSupplier, ExecutorService executorService) {
this(selectorSupplier, executorService, 1024 * 1024);
}
/**
* Construct an instance using the specified selector supplier, executor service and the read buffer size.
*
* @param selectorSupplier
* a supplier for selector instances. Expect it to be called once per cpu core available.
* @param executorService
* an executor service suggested to have at least as many available threads as there are cpu cores. This
* executor will not be shutdown when this instance is closed. On the other hand, if this field is left
* null, an executor service will be created and used; when this instance will be closed, that executor
* will be shut down.
* @param readBufferSize
* a positive value indicating how big the read buffer size should be.
*/
public RouplexTcpBinder(Supplier selectorSupplier, ExecutorService executorService, int readBufferSize) {
tcpSelectors = new RouplexTcpSelector[Runtime.getRuntime().availableProcessors()];
this.executorService = (sharedExecutorService = executorService != null)
? executorService : Executors.newFixedThreadPool(tcpSelectors.length, DEFAULT_THREAD_FATORY);
if (readBufferSize <= 0) {
throw new IllegalArgumentException("Read buffer size must be positive");
}
for (int counter = 0; counter < tcpSelectors.length; counter++) {
tcpSelectors[counter] = new RouplexTcpSelector(this, selectorSupplier.get(), readBufferSize);
}
}
/**
* We assign each new channel to the next {@link RouplexTcpSelector} in a round-robin fashion.
*
* @return
* the next RouplexTcpSelector to be used
*/
RouplexTcpSelector nextRouplexTcpSelector() {
return tcpSelectors[tcpSelectorIndex.getAndIncrement() % tcpSelectors.length];
}
ExecutorService getExecutorService() {
return executorService;
}
@Override
public void close() {
synchronized (this) {
if (closed) {
return;
}
closed = true;
}
for (int counter = 0; counter < tcpSelectors.length; counter++) {
tcpSelectors[counter].close();
}
if (!sharedExecutorService) {
executorService.shutdownNow();
}
}
/**
* Set the listener to be notified on added / removed {@link RouplexTcpClient}s. There is only one such listener,
* and once set, it cannot be unset or changed.
*
* @param rouplexTcpClientListener
* the new listener
*/
public void setRouplexTcpClientListener(@Nullable RouplexTcpClientListener rouplexTcpClientListener) {
synchronized (lock) {
if (this.rouplexTcpClientListener != null) {
throw new IllegalStateException("RouplexTcpClientListener already set.");
}
this.rouplexTcpClientListener = rouplexTcpClientListener;
}
}
/**
* Set the listener to be notified on added / removed {@link RouplexTcpServer}s. There is only one such listener,
* and once set, it cannot be unset or changed.
*
* @param rouplexTcpServerListener
* the new listener
*/
public void setRouplexTcpServerListener(@Nullable RouplexTcpServerListener rouplexTcpServerListener) {
synchronized (lock) {
if (this.rouplexTcpServerListener != null) {
throw new IllegalStateException("RouplexTcpServerListener already set.");
}
this.rouplexTcpServerListener = rouplexTcpServerListener;
}
}
// In the future, if needed, we can support this
// Throttle throttle;
// public Throttle getTcpClientAcceptThrottle() {
// return throttle;
// }
}