Updated Netty, passes all unit tests
This commit is contained in:
parent
9a58cc52f4
commit
cbb4a1942c
|
@ -12,7 +12,6 @@
|
|||
<classpathentry kind="lib" path="/Dependencies/logging/logback-core-1.0.13.jar" sourcepath="/Dependencies/logging/logback-core-1.0.13-sources.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/logging/minlog-1.2-to-slf4j.jar" sourcepath="/Dependencies/logging/minlog-1.2-to-slf4j-sources.zip"/>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/Dorkbox-Util"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/kryo/reflectasm.jar" sourcepath="/Dependencies/kryo/reflectasm-src.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/BouncyCastleCrypto/bcpkix-jdk15on-151.jar" sourcepath="/Dependencies/BouncyCastleCrypto/bcpkix-jdk15on-151-src.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/BouncyCastleCrypto/bcprov-debug-jdk15on-151.jar" sourcepath="/Dependencies/BouncyCastleCrypto/bcprov-jdk15on-151-src.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/netty/netty-all-4.1.0.jar" sourcepath="/Dependencies/netty/netty-all-4.1.0-sources.zip">
|
||||
|
@ -22,8 +21,10 @@
|
|||
</classpathentry>
|
||||
<classpathentry combineaccessrules="false" kind="src" path="/JavaLauncher-Util"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/kryo/objenesis-2.1.jar" sourcepath="/Dependencies/kryo/objenesis-2.1-srcs.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/kryo/kryo3-debug.jar"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/dorkbox/object_pool/ObjectPool_v1.1.jar" sourcepath="/Dependencies/dorkbox/object_pool/ObjectPool_v1.1_src.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/asm/asm-5.0.4.jar"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/JCTools/JCTools-v1.1-alpha-MTAQ.jar" sourcepath="/Dependencies/JCTools/JCTools-1.1-alpha-MTAQ-src.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/kryo/kryo3-debug.jar" sourcepath="/Dependencies/kryo/kryo3_src.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/kryo/reflectasm-1.11.0.jar" sourcepath="/Dependencies/kryo/reflectasm-1.11.0-sources.zip"/>
|
||||
<classpathentry kind="lib" path="/Dependencies/dorkbox/object_pool/ObjectPool_v1.2.jar" sourcepath="/Dependencies/dorkbox/object_pool/ObjectPool_v1.2_src.zip"/>
|
||||
<classpathentry kind="output" path="classes"/>
|
||||
</classpath>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<src_folder value="file://$MODULE_DIR$/" expected_position="2" />
|
||||
</src_description>
|
||||
</component>
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
|
@ -32,12 +32,13 @@
|
|||
<excludeFolder url="file://$MODULE_DIR$/natives" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="jdk" jdkName="1.7" jdkType="JavaSDK" />
|
||||
<orderEntry type="module" module-name="Dorkbox-Util" />
|
||||
<orderEntry type="module" module-name="JavaLauncher-Util" />
|
||||
<orderEntry type="library" name="bouncyCastle bcpkix-jdk15on (1.51)" level="application" />
|
||||
<orderEntry type="library" name="bouncyCastle bcprov-jdk15on (1.51)" level="application" />
|
||||
<orderEntry type="library" name="dorkbox annotations" level="application" />
|
||||
<orderEntry type="library" name="dorkbox object_pool" level="application" />
|
||||
<orderEntry type="library" name="asm (5.0.4)" level="application" />
|
||||
<orderEntry type="library" name="javassist" level="application" />
|
||||
<orderEntry type="library" name="logging slf4j-api (1.7.5)" level="application" />
|
||||
|
@ -46,7 +47,6 @@
|
|||
<orderEntry type="library" name="kryo3" level="application" />
|
||||
<orderEntry type="library" name="netty-all (4.1.0)" level="application" />
|
||||
<orderEntry type="library" name="reflectasm" level="application" />
|
||||
<orderEntry type="module" module-name="ObjectPool" />
|
||||
<orderEntry type="library" name="objenesis (2.1)" level="application" />
|
||||
<orderEntry type="module-library">
|
||||
<library name="JUnit4">
|
||||
|
@ -59,6 +59,7 @@
|
|||
<SOURCES />
|
||||
</library>
|
||||
</orderEntry>
|
||||
<orderEntry type="library" name="JCTools (1.1 alpha MTAQ)" level="application" />
|
||||
</component>
|
||||
<component name="org.twodividedbyzero.idea.findbugs">
|
||||
<option name="_basePreferences">
|
||||
|
|
|
@ -186,7 +186,7 @@ public class Broadcast {
|
|||
// don't error out on one particular octect
|
||||
try {
|
||||
InetAddress byAddress = InetAddress.getByAddress(ip);
|
||||
channel1.write(new DatagramPacket(buffer, new InetSocketAddress(byAddress, udpPort)));
|
||||
channel1.writeAndFlush(new DatagramPacket(buffer, new InetSocketAddress(byAddress, udpPort)));
|
||||
|
||||
|
||||
// response is received. If the channel is not closed within 5 seconds, move to the next one.
|
||||
|
|
|
@ -1,10 +1,26 @@
|
|||
package dorkbox.network;
|
||||
|
||||
import dorkbox.network.connection.BootstrapWrapper;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.EndPointClient;
|
||||
import dorkbox.network.connection.idle.IdleBridge;
|
||||
import dorkbox.network.connection.idle.IdleSender;
|
||||
import dorkbox.network.connection.registration.local.RegistrationLocalHandlerClient;
|
||||
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerClientTCP;
|
||||
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerClientUDP;
|
||||
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerClientUDT;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import dorkbox.network.util.udt.UdtEndpointProxy;
|
||||
import dorkbox.util.NamedThreadFactory;
|
||||
import dorkbox.util.OS;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.DefaultEventLoopGroup;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollDatagramChannel;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollSocketChannel;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
|
@ -16,58 +32,50 @@ import io.netty.channel.socket.nio.NioSocketChannel;
|
|||
import io.netty.channel.socket.oio.OioDatagramChannel;
|
||||
import io.netty.channel.socket.oio.OioSocketChannel;
|
||||
import io.netty.util.internal.PlatformDependent;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.connection.BootstrapWrapper;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPointClient;
|
||||
import dorkbox.network.connection.idle.IdleBridge;
|
||||
import dorkbox.network.connection.idle.IdleSender;
|
||||
import dorkbox.network.connection.registration.local.RegistrationLocalHandlerClient;
|
||||
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerClientTCP;
|
||||
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerClientUDP;
|
||||
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerClientUDT;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import dorkbox.network.util.udt.UdtEndpointProxy;
|
||||
import dorkbox.util.NamedThreadFactory;
|
||||
import dorkbox.util.OS;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
/**
|
||||
* The client is both SYNC and ASYNC. It starts off SYNC (blocks thread until it's done), then once it's connected to the server, it's ASYNC.
|
||||
*/
|
||||
public class Client extends EndPointClient {
|
||||
public
|
||||
class Client extends EndPointClient {
|
||||
|
||||
private final Configuration options;
|
||||
|
||||
/**
|
||||
* Starts a LOCAL <b>only</b> client, with the default local channel name and serialization scheme
|
||||
*/
|
||||
public Client() throws InitializationException, SecurityException {
|
||||
this(new ConnectionOptions(LOCAL_CHANNEL));
|
||||
public
|
||||
Client() throws InitializationException, SecurityException, IOException {
|
||||
this(new Configuration(LOCAL_CHANNEL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a TCP & UDP client (or a LOCAL client), with the specified serialization scheme
|
||||
*/
|
||||
public Client(String host, int tcpPort, int udpPort, int udtPort, String localChannelName, SerializationManager serializationManager)
|
||||
throws InitializationException, SecurityException {
|
||||
this(new ConnectionOptions(host, tcpPort, udpPort, udtPort, localChannelName, serializationManager));
|
||||
public
|
||||
Client(String host, int tcpPort, int udpPort, int udtPort, String localChannelName)
|
||||
throws InitializationException, SecurityException, IOException {
|
||||
this(new Configuration(host, tcpPort, udpPort, udtPort, localChannelName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a REMOTE <b>only</b> client, which will connect to the specified host using the specified Connections Options
|
||||
*/
|
||||
public Client(ConnectionOptions options) throws InitializationException, SecurityException {
|
||||
super("Client", options);
|
||||
public
|
||||
Client(final Configuration options) throws InitializationException, SecurityException, IOException {
|
||||
super(options);
|
||||
this.options = options;
|
||||
|
||||
String threadName = Client.class.getSimpleName();
|
||||
|
||||
Logger logger2 = this.logger;
|
||||
if (options.localChannelName != null && (options.tcpPort > 0 || options.udpPort > 0 || options.host != null) ||
|
||||
options.localChannelName == null && (options.tcpPort == 0 || options.udpPort == 0 || options.host == null)
|
||||
) {
|
||||
String msg = this.name + " Local channel use and TCP/UDP use are MUTUALLY exclusive. Unable to determine intent.";
|
||||
options.localChannelName == null && (options.tcpPort == 0 || options.udpPort == 0 || options.host == null)) {
|
||||
String msg = threadName + " Local channel use and TCP/UDP use are MUTUALLY exclusive. Unable to determine intent.";
|
||||
logger2.error(msg);
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
|
@ -87,7 +95,10 @@ public class Client extends EndPointClient {
|
|||
|
||||
// setup the thread group to easily ID what the following threads belong to (and their spawned threads...)
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
ThreadGroup nettyGroup = new ThreadGroup(s != null ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(), this.name + " (Netty)");
|
||||
ThreadGroup nettyGroup = new ThreadGroup(s != null ?
|
||||
s.getThreadGroup() :
|
||||
Thread.currentThread()
|
||||
.getThreadGroup(), threadName + " (Netty)");
|
||||
|
||||
if (options.localChannelName != null && options.tcpPort < 0 && options.udpPort < 0 && options.udtPort < 0) {
|
||||
// no networked bootstraps. LOCAL connection only
|
||||
|
@ -96,13 +107,12 @@ public class Client extends EndPointClient {
|
|||
|
||||
EventLoopGroup boss;
|
||||
|
||||
boss = new DefaultEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-LOCAL", nettyGroup));
|
||||
boss = new DefaultEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-LOCAL", nettyGroup));
|
||||
|
||||
localBootstrap.group(boss)
|
||||
.channel(LocalChannel.class)
|
||||
.remoteAddress(new LocalAddress(options.localChannelName))
|
||||
.handler(new RegistrationLocalHandlerClient(this.name,
|
||||
this.registrationWrapper));
|
||||
.handler(new RegistrationLocalHandlerClient(threadName, this.registrationWrapper));
|
||||
|
||||
manageForShutdown(boss);
|
||||
}
|
||||
|
@ -115,23 +125,23 @@ public class Client extends EndPointClient {
|
|||
|
||||
if (isAndroid) {
|
||||
// android ONLY supports OIO (not NIO)
|
||||
boss = new OioEventLoopGroup(0, new NamedThreadFactory(this.name + "-TCP", nettyGroup));
|
||||
boss = new OioEventLoopGroup(0, new NamedThreadFactory(threadName + "-TCP", nettyGroup));
|
||||
tcpBootstrap.channel(OioSocketChannel.class);
|
||||
} else {
|
||||
if (OS.isLinux()) {
|
||||
// JNI network stack is MUCH faster (but only on linux)
|
||||
boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-TCP", nettyGroup));
|
||||
tcpBootstrap.channel(EpollSocketChannel.class);
|
||||
} else {
|
||||
boss = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-TCP", nettyGroup));
|
||||
tcpBootstrap.channel(NioSocketChannel.class);
|
||||
}
|
||||
}
|
||||
else if (OS.isLinux()) {
|
||||
// JNI network stack is MUCH faster (but only on linux)
|
||||
boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-TCP", nettyGroup));
|
||||
tcpBootstrap.channel(EpollSocketChannel.class);
|
||||
}
|
||||
else {
|
||||
boss = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-TCP", nettyGroup));
|
||||
tcpBootstrap.channel(NioSocketChannel.class);
|
||||
}
|
||||
|
||||
tcpBootstrap.group(boss)
|
||||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.remoteAddress(options.host, options.tcpPort)
|
||||
.handler(new RegistrationRemoteHandlerClientTCP(this.name,
|
||||
.handler(new RegistrationRemoteHandlerClientTCP(threadName,
|
||||
this.registrationWrapper,
|
||||
this.serializationManager));
|
||||
|
||||
|
@ -152,11 +162,16 @@ public class Client extends EndPointClient {
|
|||
|
||||
if (isAndroid) {
|
||||
// android ONLY supports OIO (not NIO)
|
||||
boss = new OioEventLoopGroup(0, new NamedThreadFactory(this.name + "-UDP", nettyGroup));
|
||||
boss = new OioEventLoopGroup(0, new NamedThreadFactory(threadName + "-UDP", nettyGroup));
|
||||
udpBootstrap.channel(OioDatagramChannel.class);
|
||||
} else {
|
||||
// CANNOT USE EpollDatagramChannel on the client!
|
||||
boss = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-UDP", nettyGroup));
|
||||
}
|
||||
else if (OS.isLinux()) {
|
||||
// JNI network stack is MUCH faster (but only on linux)
|
||||
boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-UDP", nettyGroup));
|
||||
udpBootstrap.channel(EpollDatagramChannel.class);
|
||||
}
|
||||
else {
|
||||
boss = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-UDP", nettyGroup));
|
||||
udpBootstrap.channel(NioDatagramChannel.class);
|
||||
}
|
||||
|
||||
|
@ -164,7 +179,7 @@ public class Client extends EndPointClient {
|
|||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.localAddress(new InetSocketAddress(0))
|
||||
.remoteAddress(new InetSocketAddress(options.host, options.udpPort))
|
||||
.handler(new RegistrationRemoteHandlerClientUDP(this.name,
|
||||
.handler(new RegistrationRemoteHandlerClientUDP(threadName,
|
||||
this.registrationWrapper,
|
||||
this.serializationManager));
|
||||
|
||||
|
@ -200,14 +215,14 @@ public class Client extends EndPointClient {
|
|||
|
||||
EventLoopGroup boss;
|
||||
|
||||
boss = UdtEndpointProxy.getClientWorker(DEFAULT_THREAD_POOL_SIZE, this.name, nettyGroup);
|
||||
boss = UdtEndpointProxy.getClientWorker(DEFAULT_THREAD_POOL_SIZE, threadName, nettyGroup);
|
||||
|
||||
UdtEndpointProxy.setChannelFactory(udtBootstrap);
|
||||
|
||||
udtBootstrap.group(boss)
|
||||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.remoteAddress(options.host, options.udtPort)
|
||||
.handler(new RegistrationRemoteHandlerClientUDT(this.name,
|
||||
.handler(new RegistrationRemoteHandlerClientUDT(threadName,
|
||||
this.registrationWrapper,
|
||||
this.serializationManager));
|
||||
|
||||
|
@ -220,14 +235,16 @@ public class Client extends EndPointClient {
|
|||
/**
|
||||
* Allows the client to reconnect to the last connected server
|
||||
*/
|
||||
public void reconnect() {
|
||||
public
|
||||
void reconnect() {
|
||||
reconnect(this.connectionTimeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the client to reconnect to the last connected server
|
||||
*/
|
||||
public void reconnect(int connectionTimeout) {
|
||||
public
|
||||
void reconnect(int connectionTimeout) {
|
||||
// close out all old connections
|
||||
close();
|
||||
|
||||
|
@ -237,21 +254,21 @@ public class Client extends EndPointClient {
|
|||
|
||||
/**
|
||||
* will attempt to connect to the server, with a 30 second timeout.
|
||||
*
|
||||
* @param connectionTimeout wait for x milliseconds. 0 will wait indefinitely
|
||||
*/
|
||||
public void connect() {
|
||||
public
|
||||
void connect() {
|
||||
connect(30000);
|
||||
}
|
||||
|
||||
/**
|
||||
* will attempt to connect to the server, and will the specified timeout.
|
||||
* <p>
|
||||
* <p/>
|
||||
* will BLOCK until completed
|
||||
*
|
||||
* @param connectionTimeout wait for x milliseconds. 0 will wait indefinitely
|
||||
*/
|
||||
public void connect(int connectionTimeout) {
|
||||
public
|
||||
void connect(int connectionTimeout) {
|
||||
this.connectionTimeout = connectionTimeout;
|
||||
|
||||
// make sure we are not trying to connect during a close or stop event.
|
||||
|
@ -277,35 +294,49 @@ public class Client extends EndPointClient {
|
|||
/**
|
||||
* Expose methods to send objects to a destination when the connection has become idle.
|
||||
*/
|
||||
public IdleBridge sendOnIdle(IdleSender<?, ?> sender) {
|
||||
return this.connectionManager.getConnection0().sendOnIdle(sender);
|
||||
public
|
||||
IdleBridge sendOnIdle(IdleSender<?, ?> sender) {
|
||||
return this.connectionManager.getConnection0()
|
||||
.sendOnIdle(sender);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose methods to send objects to a destination when the connection has become idle.
|
||||
*/
|
||||
public IdleBridge sendOnIdle(Object message) {
|
||||
return this.connectionManager.getConnection0().sendOnIdle(message);
|
||||
public
|
||||
IdleBridge sendOnIdle(Object message) {
|
||||
return this.connectionManager.getConnection0()
|
||||
.sendOnIdle(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the connection used by the client.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Make <b>sure</b> that you only call this <b>after</b> the client connects!
|
||||
* <p>
|
||||
* This is preferred to {@link getConnections}, as it properly does some error checking
|
||||
* <p/>
|
||||
* This is preferred to {@link EndPoint#getConnections()} getConnections()}, as it properly does some error checking
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
public
|
||||
Connection getConnection() {
|
||||
return this.connectionManager.getConnection0();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the host (server) name for this client.
|
||||
*/
|
||||
public
|
||||
String getHost() {
|
||||
return this.options.host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all connections ONLY (keeps the server/client running).
|
||||
* <p>
|
||||
* <p/>
|
||||
* This is used, for example, when reconnecting to a server.
|
||||
*/
|
||||
@Override
|
||||
public void close() {
|
||||
public
|
||||
void close() {
|
||||
synchronized (this.registrationLock) {
|
||||
this.registrationLock.notify();
|
||||
}
|
||||
|
|
|
@ -1,49 +1,61 @@
|
|||
package dorkbox.network;
|
||||
|
||||
import dorkbox.network.rmi.RemoteObject;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.store.SettingsStore;
|
||||
|
||||
public class ConnectionOptions {
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
public
|
||||
class Configuration {
|
||||
public String host = null;
|
||||
public int tcpPort = -1;
|
||||
|
||||
/** UDP requires TCP to handshake */
|
||||
/**
|
||||
* UDP requires TCP to handshake
|
||||
*/
|
||||
public int udpPort = -1;
|
||||
|
||||
/** UDT requires TCP to handshake */
|
||||
/**
|
||||
* UDT requires TCP to handshake
|
||||
*/
|
||||
public int udtPort = -1;
|
||||
|
||||
public String localChannelName = null;
|
||||
|
||||
public SerializationManager serializationManager = null;
|
||||
public SettingsStore settingsStore = null;
|
||||
|
||||
/**
|
||||
* Enable remote method invocation (RMI) for this connection. This is additional overhead to using RMI.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Specifically, It costs at least 2 bytes more to use remote method invocation than just
|
||||
* sending the parameters. If the method has a return value which is not
|
||||
* {@link RemoteObject#setNonBlocking(boolean) ignored}, an extra byte is
|
||||
* {@link dorkbox.network.rmi.RemoteObject#setNonBlocking(boolean) ignored}, an extra byte is
|
||||
* written. If the type of a parameter is not final (note primitives are final)
|
||||
* then an extra byte is written for that parameter.
|
||||
*/
|
||||
public boolean enableRmi = false;
|
||||
public boolean rmiEnabled = false;
|
||||
|
||||
/**
|
||||
* Sets the executor used to invoke methods when an invocation is received
|
||||
* from a remote endpoint. By default, no executor is set and invocations
|
||||
* occur on the network thread, which should not be blocked for long.
|
||||
*/
|
||||
public Executor rmiExecutor = null;
|
||||
|
||||
public ConnectionOptions() {
|
||||
public
|
||||
Configuration() {
|
||||
}
|
||||
|
||||
public ConnectionOptions(String localChannelName) {
|
||||
public
|
||||
Configuration(String localChannelName) {
|
||||
this.localChannelName = localChannelName;
|
||||
}
|
||||
|
||||
public ConnectionOptions(String host, int tcpPort, int udpPort, int udtPort, String localChannelName, SerializationManager serializationManager) {
|
||||
public
|
||||
Configuration(String host, int tcpPort, int udpPort, int udtPort, String localChannelName) {
|
||||
this.host = host;
|
||||
this.tcpPort = tcpPort;
|
||||
this.udpPort = udpPort;
|
||||
this.udtPort = udtPort;
|
||||
this.localChannelName = localChannelName;
|
||||
this.serializationManager = serializationManager;
|
||||
}
|
||||
}
|
|
@ -1,25 +1,5 @@
|
|||
package dorkbox.network;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.DefaultEventLoopGroup;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import io.netty.channel.local.LocalServerChannel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.oio.OioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.oio.OioDatagramChannel;
|
||||
import io.netty.channel.socket.oio.OioServerSocketChannel;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.connection.EndPointServer;
|
||||
import dorkbox.network.connection.registration.local.RegistrationLocalHandlerServer;
|
||||
import dorkbox.network.connection.registration.remote.RegistrationRemoteHandlerServerTCP;
|
||||
|
@ -31,15 +11,38 @@ import dorkbox.network.util.udt.UdtEndpointProxy;
|
|||
import dorkbox.util.NamedThreadFactory;
|
||||
import dorkbox.util.OS;
|
||||
import dorkbox.util.Sys;
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.DefaultEventLoopGroup;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollChannelOption;
|
||||
import io.netty.channel.epoll.EpollDatagramChannel;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||
import io.netty.channel.local.LocalAddress;
|
||||
import io.netty.channel.local.LocalServerChannel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.oio.OioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.oio.OioDatagramChannel;
|
||||
import io.netty.channel.socket.oio.OioServerSocketChannel;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
/**
|
||||
* The server can only be accessed in an ASYNC manner. This means that the server can only be used in RESPONSE
|
||||
* to events. If you access the server OUTSIDE of events, you will get inaccurate information from the server (such as getConnections())
|
||||
* <p>
|
||||
* To put it bluntly, ONLY have the server do work inside of a listener!
|
||||
* <p/>
|
||||
* To put it bluntly, ONLY have the server do work inside of a listener!
|
||||
*/
|
||||
public class Server extends EndPointServer {
|
||||
public
|
||||
class Server extends EndPointServer {
|
||||
|
||||
/**
|
||||
* The maximum queue length for incoming connection indications (a request to connect). If a connection indication arrives when
|
||||
|
@ -61,17 +64,19 @@ public class Server extends EndPointServer {
|
|||
/**
|
||||
* Starts a LOCAL <b>only</b> server, with the default serialization scheme
|
||||
*/
|
||||
public Server() throws InitializationException, SecurityException {
|
||||
this(new ConnectionOptions(LOCAL_CHANNEL));
|
||||
public
|
||||
Server() throws InitializationException, SecurityException, IOException {
|
||||
this(new Configuration(LOCAL_CHANNEL));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to starts a server with the specified Connection Options
|
||||
*/
|
||||
public Server(ConnectionOptions options) throws InitializationException, SecurityException {
|
||||
public
|
||||
Server(Configuration options) throws InitializationException, SecurityException, IOException {
|
||||
// watch-out for serialization... it can be NULL incoming. The EndPoint (superclass) sets it, if null, so
|
||||
// you have to make sure to use this.serializatino
|
||||
super("Server", options);
|
||||
// you have to make sure to use this.serialization
|
||||
super(options);
|
||||
|
||||
Logger logger2 = this.logger;
|
||||
if (Sys.isAndroid && options.udtPort > 0) {
|
||||
|
@ -88,24 +93,30 @@ public class Server extends EndPointServer {
|
|||
|
||||
this.localChannelName = options.localChannelName;
|
||||
|
||||
if (this.localChannelName != null ) {
|
||||
if (this.localChannelName != null) {
|
||||
this.localBootstrap = new ServerBootstrap();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.localBootstrap = null;
|
||||
}
|
||||
|
||||
if (this.tcpPort > 0) {
|
||||
this.tcpBootstrap = new ServerBootstrap();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.tcpBootstrap = null;
|
||||
}
|
||||
|
||||
if (this.udpPort > 0) {
|
||||
this.udpBootstrap = new Bootstrap();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.udpBootstrap = null;
|
||||
}
|
||||
|
||||
|
||||
String threadName = Server.class.getSimpleName();
|
||||
|
||||
if (this.udtPort > 0) {
|
||||
// check to see if we have UDT available!
|
||||
boolean udtAvailable = false;
|
||||
|
@ -118,10 +129,12 @@ public class Server extends EndPointServer {
|
|||
|
||||
if (udtAvailable) {
|
||||
this.udtBootstrap = new ServerBootstrap();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.udtBootstrap = null;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.udtBootstrap = null;
|
||||
}
|
||||
|
||||
|
@ -131,7 +144,10 @@ public class Server extends EndPointServer {
|
|||
|
||||
// setup the thread group to easily ID what the following threads belong to (and their spawned threads...)
|
||||
SecurityManager s = System.getSecurityManager();
|
||||
ThreadGroup nettyGroup = new ThreadGroup(s != null ? s.getThreadGroup() : Thread.currentThread().getThreadGroup(), this.name + " (Netty)");
|
||||
ThreadGroup nettyGroup = new ThreadGroup(s != null
|
||||
? s.getThreadGroup()
|
||||
: Thread.currentThread()
|
||||
.getThreadGroup(), threadName + " (Netty)");
|
||||
|
||||
|
||||
// always use local channels on the server.
|
||||
|
@ -140,15 +156,15 @@ public class Server extends EndPointServer {
|
|||
EventLoopGroup worker;
|
||||
|
||||
if (this.localBootstrap != null) {
|
||||
boss = new DefaultEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-boss-LOCAL", nettyGroup));
|
||||
worker = new DefaultEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-worker-LOCAL", nettyGroup));
|
||||
boss = new DefaultEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss-LOCAL", nettyGroup));
|
||||
worker = new DefaultEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-worker-LOCAL",
|
||||
nettyGroup));
|
||||
|
||||
this.localBootstrap.group(boss, worker)
|
||||
.channel(LocalServerChannel.class)
|
||||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.localAddress(new LocalAddress(this.localChannelName))
|
||||
.childHandler(new RegistrationLocalHandlerServer(this.name,
|
||||
this.registrationWrapper));
|
||||
.childHandler(new RegistrationLocalHandlerServer(threadName, this.registrationWrapper));
|
||||
|
||||
manageForShutdown(boss);
|
||||
manageForShutdown(worker);
|
||||
|
@ -161,22 +177,22 @@ public class Server extends EndPointServer {
|
|||
|
||||
if (Sys.isAndroid) {
|
||||
// android ONLY supports OIO (not NIO)
|
||||
boss = new OioEventLoopGroup(0, new NamedThreadFactory(this.name + "-boss-TCP", nettyGroup));
|
||||
worker = new OioEventLoopGroup(0, new NamedThreadFactory(this.name + "-worker-TCP", nettyGroup));
|
||||
boss = new OioEventLoopGroup(0, new NamedThreadFactory(threadName + "-boss-TCP", nettyGroup));
|
||||
worker = new OioEventLoopGroup(0, new NamedThreadFactory(threadName + "-worker-TCP", nettyGroup));
|
||||
this.tcpBootstrap.channel(OioServerSocketChannel.class);
|
||||
} else {
|
||||
if (OS.isLinux()) {
|
||||
// JNI network stack is MUCH faster (but only on linux)
|
||||
boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-boss-TCP", nettyGroup));
|
||||
worker = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-worker-TCP", nettyGroup));
|
||||
}
|
||||
else if (OS.isLinux()) {
|
||||
// JNI network stack is MUCH faster (but only on linux)
|
||||
boss = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss-TCP", nettyGroup));
|
||||
worker = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-worker-TCP", nettyGroup));
|
||||
|
||||
this.tcpBootstrap.channel(EpollServerSocketChannel.class);
|
||||
} else {
|
||||
boss = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-boss-TCP", nettyGroup));
|
||||
worker = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-worker-TCP", nettyGroup));
|
||||
this.tcpBootstrap.channel(EpollServerSocketChannel.class);
|
||||
}
|
||||
else {
|
||||
boss = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-boss-TCP", nettyGroup));
|
||||
worker = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-worker-TCP", nettyGroup));
|
||||
|
||||
this.tcpBootstrap.channel(NioServerSocketChannel.class);
|
||||
}
|
||||
this.tcpBootstrap.channel(NioServerSocketChannel.class);
|
||||
}
|
||||
|
||||
// TODO: If we use netty for an HTTP server,
|
||||
|
@ -189,13 +205,14 @@ public class Server extends EndPointServer {
|
|||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.option(ChannelOption.SO_BACKLOG, backlogConnectionCount)
|
||||
.option(ChannelOption.SO_REUSEADDR, true)
|
||||
.childHandler(new RegistrationRemoteHandlerServerTCP(this.name,
|
||||
.childHandler(new RegistrationRemoteHandlerServerTCP(threadName,
|
||||
this.registrationWrapper,
|
||||
this.serializationManager));
|
||||
|
||||
if (options.host != null) {
|
||||
this.tcpBootstrap.localAddress(options.host, this.tcpPort);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.tcpBootstrap.localAddress(this.tcpPort);
|
||||
}
|
||||
|
||||
|
@ -212,29 +229,30 @@ public class Server extends EndPointServer {
|
|||
|
||||
if (Sys.isAndroid) {
|
||||
// android ONLY supports OIO (not NIO)
|
||||
worker = new OioEventLoopGroup(0, new NamedThreadFactory(this.name + "-worker-UDP", nettyGroup));
|
||||
worker = new OioEventLoopGroup(0, new NamedThreadFactory(threadName + "-worker-UDP", nettyGroup));
|
||||
this.udpBootstrap.channel(OioDatagramChannel.class);
|
||||
} else {
|
||||
// if (OS.isLinux()) {
|
||||
// // JNI network stack is MUCH faster (but only on linux)
|
||||
// worker = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-worker-UDP", nettyGroup));
|
||||
//
|
||||
// this.udpBootstrap.channel(EpollDatagramChannel.class)
|
||||
// .option(EpollChannelOption.SO_REUSEPORT, true);
|
||||
// } else {
|
||||
worker = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(this.name + "-worker-UDP", nettyGroup));
|
||||
}
|
||||
else if (OS.isLinux()) {
|
||||
// JNI network stack is MUCH faster (but only on linux)
|
||||
worker = new EpollEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-worker-UDP", nettyGroup));
|
||||
|
||||
this.udpBootstrap.channel(NioDatagramChannel.class);
|
||||
// }
|
||||
this.udpBootstrap.channel(EpollDatagramChannel.class)
|
||||
.option(EpollChannelOption.SO_REUSEPORT, true);
|
||||
}
|
||||
else {
|
||||
worker = new NioEventLoopGroup(DEFAULT_THREAD_POOL_SIZE, new NamedThreadFactory(threadName + "-worker-UDP", nettyGroup));
|
||||
|
||||
this.udpBootstrap.channel(NioDatagramChannel.class);
|
||||
}
|
||||
|
||||
manageForShutdown(worker);
|
||||
|
||||
this.udpBootstrap.group(worker)
|
||||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
// not binding to specific address, since it's driven by TCP, and that can be bound to a specific address
|
||||
.localAddress(this.udpPort) // if you bind to a specific interface, Linux will be unable to receive broadcast packets!
|
||||
.handler(new RegistrationRemoteHandlerServerUDP(this.name, this.registrationWrapper, this.serializationManager));
|
||||
this.udpBootstrap.group(worker).option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
// not binding to specific address, since it's driven by TCP, and that can be bound to a specific address
|
||||
.localAddress(this.udpPort) // if you bind to a specific interface, Linux will be unable to receive broadcast packets!
|
||||
.handler(new RegistrationRemoteHandlerServerUDP(threadName,
|
||||
this.registrationWrapper,
|
||||
this.serializationManager));
|
||||
|
||||
|
||||
// Enable to READ from MULTICAST data (ie, 192.168.1.0)
|
||||
|
@ -256,18 +274,18 @@ public class Server extends EndPointServer {
|
|||
|
||||
// all of this must be proxied to another class, so THIS class doesn't have unmet dependencies.
|
||||
// Annoying and abusing the classloader, but it works well.
|
||||
boss = UdtEndpointProxy.getServerBoss(DEFAULT_THREAD_POOL_SIZE, this.name, nettyGroup);
|
||||
worker = UdtEndpointProxy.getServerWorker(DEFAULT_THREAD_POOL_SIZE, this.name, nettyGroup);
|
||||
boss = UdtEndpointProxy.getServerBoss(DEFAULT_THREAD_POOL_SIZE, threadName, nettyGroup);
|
||||
worker = UdtEndpointProxy.getServerWorker(DEFAULT_THREAD_POOL_SIZE, threadName, nettyGroup);
|
||||
|
||||
UdtEndpointProxy.setChannelFactory(this.udtBootstrap);
|
||||
this.udtBootstrap.group(boss, worker)
|
||||
.option(ChannelOption.SO_BACKLOG, backlogConnectionCount)
|
||||
.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
// not binding to specific address, since it's driven by TCP, and that can be bound to a specific address
|
||||
.localAddress(this.udtPort)
|
||||
.childHandler(new RegistrationRemoteHandlerServerUDT(this.name,
|
||||
this.registrationWrapper,
|
||||
this.serializationManager));
|
||||
.option(ChannelOption.SO_BACKLOG, backlogConnectionCount).option(ChannelOption.ALLOCATOR,
|
||||
PooledByteBufAllocator.DEFAULT)
|
||||
// not binding to specific address, since it's driven by TCP, and that can be bound to a specific address
|
||||
.localAddress(this.udtPort)
|
||||
.childHandler(new RegistrationRemoteHandlerServerUDT(threadName,
|
||||
this.registrationWrapper,
|
||||
this.serializationManager));
|
||||
|
||||
manageForShutdown(boss);
|
||||
manageForShutdown(worker);
|
||||
|
@ -276,33 +294,34 @@ public class Server extends EndPointServer {
|
|||
|
||||
/**
|
||||
* Binds the server to the configured, underlying protocols.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This method will also BLOCK until the stop method is called, and if
|
||||
* you want to continue running code after this method invocation, bind should be called in a separate, non-daemon thread.
|
||||
*/
|
||||
public void bind() {
|
||||
public
|
||||
void bind() {
|
||||
bind(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the server to the configured, underlying protocols.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This is a more advanced method, and you should consider calling <code>bind()</code> instead.
|
||||
*
|
||||
* @param blockUntilTerminate will BLOCK until the server stop method is called, and if
|
||||
* you want to continue running code after this method invocation, bind should be called in a separate,
|
||||
* non-daemon thread - or with false as the parameter.
|
||||
* you want to continue running code after this method invocation, bind should be called in a separate,
|
||||
* non-daemon thread - or with false as the parameter.
|
||||
*/
|
||||
public void bind(boolean blockUntilTerminate) {
|
||||
public
|
||||
void bind(boolean blockUntilTerminate) {
|
||||
// make sure we are not trying to connect during a close or stop event.
|
||||
// This will wait until we have finished starting up/shutting down.
|
||||
synchronized (this.shutdownInProgress) {
|
||||
}
|
||||
|
||||
|
||||
// Note: The bootstraps will be accessed ONE AT A TIME, in this order!
|
||||
|
||||
ChannelFuture future = null;
|
||||
// The bootstraps will be accessed ONE AT A TIME, in this order!
|
||||
ChannelFuture future;
|
||||
|
||||
// LOCAL
|
||||
Logger logger2 = this.logger;
|
||||
|
@ -337,7 +356,9 @@ public class Server extends EndPointServer {
|
|||
}
|
||||
|
||||
if (!future.isSuccess()) {
|
||||
String errorMessage = stopWithErrorMessage(logger2, "Could not bind to TCP port " + this.tcpPort + " on the server.", future.cause());
|
||||
String errorMessage = stopWithErrorMessage(logger2,
|
||||
"Could not bind to TCP port " + this.tcpPort + " on the server.",
|
||||
future.cause());
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
|
||||
|
@ -357,7 +378,9 @@ public class Server extends EndPointServer {
|
|||
}
|
||||
|
||||
if (!future.isSuccess()) {
|
||||
String errorMessage = stopWithErrorMessage(logger2, "Could not bind to UDP port " + this.udpPort + " on the server.", future.cause());
|
||||
String errorMessage = stopWithErrorMessage(logger2,
|
||||
"Could not bind to UDP port " + this.udpPort + " on the server.",
|
||||
future.cause());
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
|
||||
|
@ -377,7 +400,9 @@ public class Server extends EndPointServer {
|
|||
}
|
||||
|
||||
if (!future.isSuccess()) {
|
||||
String errorMessage = stopWithErrorMessage(logger2, "Could not bind to UDT port " + this.udtPort + " on the server.", future.cause());
|
||||
String errorMessage = stopWithErrorMessage(logger2,
|
||||
"Could not bind to UDT port " + this.udtPort + " on the server.",
|
||||
future.cause());
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
|
||||
|
@ -387,6 +412,8 @@ public class Server extends EndPointServer {
|
|||
|
||||
// we now BLOCK until the stop method is called.
|
||||
// if we want to continue running code in the server, bind should be called in a separate, non-daemon thread.
|
||||
waitForStop(blockUntilTerminate);
|
||||
if (blockUntilTerminate) {
|
||||
waitForShutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,134 +1,121 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
|
||||
import org.bouncycastle.crypto.params.ParametersWithIV;
|
||||
|
||||
import dorkbox.network.connection.bridge.ConnectionBridge;
|
||||
import dorkbox.network.connection.idle.IdleBridge;
|
||||
import dorkbox.network.connection.idle.IdleSender;
|
||||
import dorkbox.network.rmi.RemoteObject;
|
||||
import dorkbox.network.rmi.TimeoutException;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import org.bouncycastle.crypto.params.ParametersWithIV;
|
||||
|
||||
public interface Connection {
|
||||
public static final String connection = "connection";
|
||||
|
||||
public
|
||||
interface Connection {
|
||||
/**
|
||||
* Initialize the connection with any extra info that is needed but was unavailable at the channel construction.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This happens BEFORE prep.
|
||||
*/
|
||||
public void init(EndPoint endPoint, Bridge bridge);
|
||||
void init(Bridge bridge);
|
||||
|
||||
/**
|
||||
* Prepare the channel wrapper, since it doesn't have access to certain fields during it's initialization.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This happens AFTER init.
|
||||
*/
|
||||
public void prep();
|
||||
void prep();
|
||||
|
||||
/**
|
||||
* @return the AES key/IV, etc associated with this connection
|
||||
*/
|
||||
public ParametersWithIV getCryptoParameters();
|
||||
ParametersWithIV getCryptoParameters();
|
||||
|
||||
/**
|
||||
* Has the remote ECC public key changed. This can be useful if specific actions are necessary when the key has changed.
|
||||
*/
|
||||
public boolean hasRemoteKeyChanged();
|
||||
boolean hasRemoteKeyChanged();
|
||||
|
||||
/**
|
||||
* @return the remote address, as a string.
|
||||
*/
|
||||
public String getRemoteHost();
|
||||
|
||||
/**
|
||||
* @return the name used by the connection
|
||||
*/
|
||||
public String getName();
|
||||
String getRemoteHost();
|
||||
|
||||
|
||||
/**
|
||||
* @return the endpoint associated with this connection
|
||||
*/
|
||||
public EndPoint getEndPoint();
|
||||
EndPoint getEndPoint();
|
||||
|
||||
|
||||
/**
|
||||
* @return the connection (TCP or LOCAL) id of this connection.
|
||||
*/
|
||||
public int id();
|
||||
int id();
|
||||
|
||||
/**
|
||||
* @return the connection (TCP or LOCAL) id of this connection as a HEX
|
||||
* string.
|
||||
* string.
|
||||
*/
|
||||
public String idAsHex();
|
||||
String idAsHex();
|
||||
|
||||
/**
|
||||
* @return true if this connection is also configured to use UDP
|
||||
*/
|
||||
public boolean hasUDP();
|
||||
boolean hasUDP();
|
||||
|
||||
/**
|
||||
* @return true if this connection is also configured to use UDT
|
||||
*/
|
||||
public boolean hasUDT();
|
||||
boolean hasUDT();
|
||||
|
||||
/**
|
||||
* Expose methods to send objects to a destination (such as a custom object or a standard ping)
|
||||
*/
|
||||
public ConnectionBridge send();
|
||||
ConnectionBridge send();
|
||||
|
||||
/**
|
||||
* Expose methods to send objects to a destination when the connection has become idle.
|
||||
*/
|
||||
public IdleBridge sendOnIdle(IdleSender<?, ?> sender);
|
||||
IdleBridge sendOnIdle(IdleSender<?, ?> sender);
|
||||
|
||||
/**
|
||||
* Expose methods to send objects to a destination when the connection has become idle.
|
||||
*/
|
||||
public IdleBridge sendOnIdle(Object message);
|
||||
IdleBridge sendOnIdle(Object message);
|
||||
|
||||
/**
|
||||
* Expose methods to modify the connection listeners.
|
||||
*/
|
||||
public ListenerBridge listeners();
|
||||
ListenerBridge listeners();
|
||||
|
||||
/**
|
||||
* Closes the connection
|
||||
*/
|
||||
public void close();
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Identical to {@link #getRemoteObject(C, int, Class...)} except returns
|
||||
* the object cast to the specified interface type. The returned object
|
||||
* still implements {@link RemoteObject}.
|
||||
*/
|
||||
public <T> T getRemoteObject(int objectID, Class<T> iface);
|
||||
|
||||
/**
|
||||
* Returns a proxy object that implements the specified interfaces. Methods
|
||||
* invoked on the proxy object will be invoked remotely on the object with
|
||||
* the specified ID in the ObjectSpace for the specified connection. If the
|
||||
* remote end of the connection has not {@link #addConnection(Connection)
|
||||
* added} the connection to the ObjectSpace, the remote method invocations
|
||||
* will be ignored.
|
||||
* <p>
|
||||
* Returns a new proxy object implements the specified interface. Methods invoked on the proxy object will be
|
||||
* invoked remotely on the object with the specified ID in the ObjectSpace for the current connection.
|
||||
* <p/>
|
||||
* This will request a registration ID from the remote endpoint, <b>and will block</b> until the registration
|
||||
* ID has been returned.
|
||||
* <p/>
|
||||
* Methods that return a value will throw {@link TimeoutException} if the
|
||||
* response is not received with the
|
||||
* {@link RemoteObject#setResponseTimeout(int) response timeout}.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If {@link RemoteObject#setNonBlocking(boolean) non-blocking} is false
|
||||
* (the default), then methods that return a value must not be called from
|
||||
* the update thread for the connection. An exception will be thrown if this
|
||||
* occurs. Methods with a void return value can be called on the update
|
||||
* thread.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If a proxy returned from this method is part of an object graph sent over
|
||||
* the network, the object graph on the receiving side will have the proxy
|
||||
* object replaced with the registered object.
|
||||
* object replaced with the registered (non-proxy) object.
|
||||
*
|
||||
* @see RemoteObject
|
||||
*/
|
||||
public RemoteObject getRemoteObject(int objectID, Class<?>... ifaces);
|
||||
<Iface, Impl extends Iface> Iface createRemoteObject(final Class<Iface> remoteImplementationInterface,
|
||||
final Class<Impl> remoteImplementationClass) throws NetException;
|
||||
}
|
||||
|
|
|
@ -16,7 +16,13 @@ import io.netty.util.ReferenceCountUtil;
|
|||
import io.netty.util.concurrent.Promise;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.LinkedList;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.bouncycastle.crypto.params.ParametersWithIV;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -28,30 +34,30 @@ import dorkbox.network.connection.idle.IdleSender;
|
|||
import dorkbox.network.connection.wrapper.ChannelNetworkWrapper;
|
||||
import dorkbox.network.connection.wrapper.ChannelNull;
|
||||
import dorkbox.network.connection.wrapper.ChannelWrapper;
|
||||
import dorkbox.network.rmi.RemoteObject;
|
||||
import dorkbox.network.rmi.TimeoutException;
|
||||
import dorkbox.network.rmi.RemoteProxy;
|
||||
import dorkbox.network.rmi.RmiBridge;
|
||||
import dorkbox.network.rmi.RmiRegistration;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
|
||||
|
||||
/**
|
||||
* The "network connection" is established once the registration is validated for TCP/UDP/UDT
|
||||
*/
|
||||
@Sharable
|
||||
public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
||||
implements Connection, ListenerBridge, ConnectionBridge {
|
||||
public
|
||||
class ConnectionImpl extends ChannelInboundHandlerAdapter implements Connection, ListenerBridge, ConnectionBridge {
|
||||
|
||||
private final org.slf4j.Logger logger;
|
||||
private final String name;
|
||||
|
||||
private AtomicBoolean closeInProgress = new AtomicBoolean(false);
|
||||
private AtomicBoolean alreadyClosed = new AtomicBoolean(false);
|
||||
private final AtomicBoolean closeInProgress = new AtomicBoolean(false);
|
||||
private final AtomicBoolean alreadyClosed = new AtomicBoolean(false);
|
||||
private final Object closeInProgressLock = new Object();
|
||||
|
||||
private final Object messageInProgressLock = new Object();
|
||||
private AtomicBoolean messageInProgress = new AtomicBoolean(false);
|
||||
private final AtomicBoolean messageInProgress = new AtomicBoolean(false);
|
||||
|
||||
private ISessionManager sessionManager;
|
||||
private ChannelWrapper channelWrapper;
|
||||
private EndPoint endPoint;
|
||||
|
||||
private volatile PingFuture pingFuture = null;
|
||||
|
||||
|
@ -62,28 +68,42 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
private boolean remoteKeyChanged;
|
||||
|
||||
|
||||
public ConnectionImpl(String name) {
|
||||
this.name = name;
|
||||
this.logger = org.slf4j.LoggerFactory.getLogger(name);
|
||||
private final EndPoint endPoint;
|
||||
public final RmiBridge rmiBridge;
|
||||
|
||||
|
||||
/**
|
||||
* All of the parameters can be null, when metaChannel want's to get the base class type
|
||||
* @param logger
|
||||
* @param endPoint
|
||||
* @param rmiBridge
|
||||
*/
|
||||
public
|
||||
ConnectionImpl(final Logger logger, final EndPoint endPoint, final RmiBridge rmiBridge) {
|
||||
this.logger = logger;
|
||||
this.endPoint = endPoint;
|
||||
this.rmiBridge = rmiBridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the connection with any extra info that is needed but was unavailable at the channel construction.
|
||||
*/
|
||||
@Override
|
||||
public void init(EndPoint endPoint, Bridge bridge) {
|
||||
this.endPoint = endPoint;
|
||||
public
|
||||
void init(final Bridge bridge) {
|
||||
if (bridge != null) {
|
||||
this.sessionManager = bridge.sessionManager;
|
||||
this.channelWrapper = bridge.channelWrapper;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.sessionManager = null;
|
||||
this.channelWrapper = null;
|
||||
}
|
||||
|
||||
if (this.channelWrapper instanceof ChannelNetworkWrapper) {
|
||||
this.remoteKeyChanged = ((ChannelNetworkWrapper)this.channelWrapper).remoteKeyChanged();
|
||||
} else {
|
||||
this.remoteKeyChanged = ((ChannelNetworkWrapper) this.channelWrapper).remoteKeyChanged();
|
||||
}
|
||||
else {
|
||||
this.remoteKeyChanged = false;
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +112,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Prepare the channel wrapper, since it doesn't have access to certain fields during it's construction.
|
||||
*/
|
||||
@Override
|
||||
public void prep() {
|
||||
public
|
||||
void prep() {
|
||||
if (this.channelWrapper != null) {
|
||||
this.channelWrapper.init();
|
||||
}
|
||||
|
@ -103,7 +124,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* @return the AES key/IV, etc associated with this connection
|
||||
*/
|
||||
@Override
|
||||
public final ParametersWithIV getCryptoParameters() {
|
||||
public final
|
||||
ParametersWithIV getCryptoParameters() {
|
||||
return this.channelWrapper.cryptoParameters();
|
||||
}
|
||||
|
||||
|
@ -111,7 +133,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Has the remote ECC public key changed. This can be useful if specific actions are necessary when the key has changed.
|
||||
*/
|
||||
@Override
|
||||
public boolean hasRemoteKeyChanged() {
|
||||
public
|
||||
boolean hasRemoteKeyChanged() {
|
||||
return this.remoteKeyChanged;
|
||||
}
|
||||
|
||||
|
@ -119,23 +142,18 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* @return the remote address, as a string.
|
||||
*/
|
||||
@Override
|
||||
public String getRemoteHost() {
|
||||
public
|
||||
String getRemoteHost() {
|
||||
return this.channelWrapper.getRemoteHost();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name used by the connection
|
||||
*/
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the endpoint associated with this connection
|
||||
*/
|
||||
@Override
|
||||
public EndPoint getEndPoint() {
|
||||
public
|
||||
EndPoint getEndPoint() {
|
||||
return this.endPoint;
|
||||
}
|
||||
|
||||
|
@ -143,7 +161,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* @return the connection (TCP or LOCAL) id of this connection.
|
||||
*/
|
||||
@Override
|
||||
public int id() {
|
||||
public
|
||||
int id() {
|
||||
return this.channelWrapper.id();
|
||||
}
|
||||
|
||||
|
@ -151,14 +170,16 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* @return the connection (TCP or LOCAL) id of this connection as a HEX string.
|
||||
*/
|
||||
@Override
|
||||
public String idAsHex() {
|
||||
public
|
||||
String idAsHex() {
|
||||
return Integer.toHexString(this.channelWrapper.id());
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the ping times for this connection (called when this connection gets a REPLY ping message).
|
||||
*/
|
||||
public final void updatePingResponse(PingMessage ping) {
|
||||
public final
|
||||
void updatePingResponse(PingMessage ping) {
|
||||
if (this.pingFuture != null) {
|
||||
this.pingFuture.setSuccess(this, ping);
|
||||
}
|
||||
|
@ -170,13 +191,15 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* @return Ping can have a listener attached, which will get called when the ping returns.
|
||||
*/
|
||||
@Override
|
||||
public final Ping ping() {
|
||||
public final
|
||||
Ping ping() {
|
||||
PingFuture pingFuture2 = this.pingFuture;
|
||||
if (pingFuture2 != null && !pingFuture2.isSuccess()) {
|
||||
pingFuture2.cancel();
|
||||
}
|
||||
|
||||
Promise<PingTuple<? extends Connection>> newPromise = this.channelWrapper.getEventLoop().newPromise();
|
||||
Promise<PingTuple<? extends Connection>> newPromise = this.channelWrapper.getEventLoop()
|
||||
.newPromise();
|
||||
this.pingFuture = new PingFuture(newPromise);
|
||||
|
||||
PingMessage ping = new PingMessage();
|
||||
|
@ -190,12 +213,15 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* INTERNAL USE ONLY. Used to initiate a ping, and to return a ping.
|
||||
* Sends a ping message attempted in the following order: UDP, UDT, TCP
|
||||
*/
|
||||
final void ping0(PingMessage ping) {
|
||||
final
|
||||
void ping0(PingMessage ping) {
|
||||
if (this.channelWrapper.udp() != null) {
|
||||
UDP(ping).flush();
|
||||
} else if (this.channelWrapper.udt() != null) {
|
||||
}
|
||||
else if (this.channelWrapper.udt() != null) {
|
||||
UDT(ping).flush();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
TCP(ping).flush();
|
||||
}
|
||||
}
|
||||
|
@ -203,11 +229,13 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
/**
|
||||
* Returns the last calculated TCP return trip time, or -1 if or the {@link PingMessage} response has not yet been received.
|
||||
*/
|
||||
public final int getLastRoundTripTime() {
|
||||
public final
|
||||
int getLastRoundTripTime() {
|
||||
PingFuture pingFuture2 = this.pingFuture;
|
||||
if (pingFuture2 != null) {
|
||||
return pingFuture2.getResponse();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -216,7 +244,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* @return true if this connection is also configured to use UDP
|
||||
*/
|
||||
@Override
|
||||
public final boolean hasUDP() {
|
||||
public final
|
||||
boolean hasUDP() {
|
||||
return this.channelWrapper.udp() != null;
|
||||
}
|
||||
|
||||
|
@ -224,7 +253,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* @return true if this connection is also configured to use UDT
|
||||
*/
|
||||
@Override
|
||||
public final boolean hasUDT() {
|
||||
public final
|
||||
boolean hasUDT() {
|
||||
return this.channelWrapper.udt() != null;
|
||||
}
|
||||
|
||||
|
@ -232,7 +262,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Expose methods to send objects to a destination.
|
||||
*/
|
||||
@Override
|
||||
public final ConnectionBridge send() {
|
||||
public final
|
||||
ConnectionBridge send() {
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -240,7 +271,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Sends the object to other listeners INSIDE this endpoint. It does not send it to a remote address.
|
||||
*/
|
||||
@Override
|
||||
public final void self(Object message) {
|
||||
public final
|
||||
void self(Object message) {
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("Sending LOCAL {}", message);
|
||||
|
@ -252,7 +284,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Sends the object over the network using TCP. (LOCAL channels do not care if its TCP or UDP)
|
||||
*/
|
||||
@Override
|
||||
public final ConnectionPoint TCP(Object message) {
|
||||
public final
|
||||
ConnectionPoint TCP(Object message) {
|
||||
Logger logger2 = this.logger;
|
||||
if (!this.closeInProgress.get()) {
|
||||
if (logger2.isTraceEnabled()) {
|
||||
|
@ -261,7 +294,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
ConnectionPointWriter tcp = this.channelWrapper.tcp();
|
||||
tcp.write(message);
|
||||
return tcp;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (logger2.isDebugEnabled()) {
|
||||
logger2.debug("writing TCP while closed: {}", message);
|
||||
}
|
||||
|
@ -275,7 +309,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Sends the object over the network using UDP (or via LOCAL when it's a local channel).
|
||||
*/
|
||||
@Override
|
||||
public ConnectionPoint UDP(Object message) {
|
||||
public
|
||||
ConnectionPoint UDP(Object message) {
|
||||
Logger logger2 = this.logger;
|
||||
if (!this.closeInProgress.get()) {
|
||||
if (logger2.isTraceEnabled()) {
|
||||
|
@ -284,7 +319,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
ConnectionPointWriter udp = this.channelWrapper.udp();
|
||||
udp.write(message);
|
||||
return udp;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (logger2.isDebugEnabled()) {
|
||||
logger2.debug("writing UDP while closed: {}", message);
|
||||
}
|
||||
|
@ -297,7 +333,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Sends the object over the network using TCP. (LOCAL channels do not care if its TCP or UDP)
|
||||
*/
|
||||
@Override
|
||||
public final ConnectionPoint UDT(Object message) {
|
||||
public final
|
||||
ConnectionPoint UDT(Object message) {
|
||||
Logger logger2 = this.logger;
|
||||
if (!this.closeInProgress.get()) {
|
||||
if (logger2.isTraceEnabled()) {
|
||||
|
@ -306,7 +343,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
ConnectionPointWriter udt = this.channelWrapper.udt();
|
||||
udt.write(message);
|
||||
return udt;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (logger2.isDebugEnabled()) {
|
||||
logger2.debug("writing UDT while closed: {}", message);
|
||||
}
|
||||
|
@ -320,7 +358,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Flushes the contents of the TCP/UDP/UDT/etc pipes to the actual transport.
|
||||
*/
|
||||
@Override
|
||||
public final void flush() {
|
||||
public final
|
||||
void flush() {
|
||||
this.channelWrapper.flush();
|
||||
}
|
||||
|
||||
|
@ -328,7 +367,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Expose methods to modify the connection listeners.
|
||||
*/
|
||||
@Override
|
||||
public final IdleBridge sendOnIdle(@SuppressWarnings("rawtypes") IdleSender sender) {
|
||||
public final
|
||||
IdleBridge sendOnIdle(@SuppressWarnings("rawtypes") IdleSender sender) {
|
||||
listeners().add(sender);
|
||||
return sender;
|
||||
}
|
||||
|
@ -338,59 +378,20 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Expose methods to modify the connection listeners.
|
||||
*/
|
||||
@Override
|
||||
public final IdleBridge sendOnIdle(Object message) {
|
||||
@SuppressWarnings({"rawtypes","unchecked"})
|
||||
public final
|
||||
IdleBridge sendOnIdle(Object message) {
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
IdleObjectSender sender = new IdleObjectSender(message);
|
||||
listeners().add(sender);
|
||||
return sender;
|
||||
}
|
||||
|
||||
/**
|
||||
* Identical to {@link #getRemoteObject(C, int, Class...)} except returns
|
||||
* the object cast to the specified interface type. The returned object
|
||||
* still implements {@link RemoteObject}.
|
||||
*/
|
||||
@Override
|
||||
public <T> T getRemoteObject(int objectID, Class<T> iface) {
|
||||
@SuppressWarnings({"unchecked"})
|
||||
T remoteObject = (T) this.endPoint.getRemoteObject(this, objectID, new Class<?>[] {iface});
|
||||
return remoteObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proxy object that implements the specified interfaces. Methods
|
||||
* invoked on the proxy object will be invoked remotely on the object with
|
||||
* the specified ID in the ObjectSpace for the specified connection. If the
|
||||
* remote end of the connection has not {@link #addConnection(Connection)
|
||||
* added} the connection to the ObjectSpace, the remote method invocations
|
||||
* will be ignored.
|
||||
* <p>
|
||||
* Methods that return a value will throw {@link TimeoutException} if the
|
||||
* response is not received with the
|
||||
* {@link RemoteObject#setResponseTimeout(int) response timeout}.
|
||||
* <p>
|
||||
* If {@link RemoteObject#setNonBlocking(boolean) non-blocking} is false
|
||||
* (the default), then methods that return a value must not be called from
|
||||
* the update thread for the connection. An exception will be thrown if this
|
||||
* occurs. Methods with a void return value can be called on the update
|
||||
* thread.
|
||||
* <p>
|
||||
* If a proxy returned from this method is part of an object graph sent over
|
||||
* the network, the object graph on the receiving side will have the proxy
|
||||
* object replaced with the registered object.
|
||||
*
|
||||
* @see RemoteObject
|
||||
*/
|
||||
@Override
|
||||
public RemoteObject getRemoteObject(int objectID, Class<?>... ifaces) {
|
||||
return this.endPoint.getRemoteObject(this, objectID, ifaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a {@link Channel} has been idle for a while.
|
||||
*/
|
||||
@Override
|
||||
public void userEventTriggered(ChannelHandlerContext context, Object event) throws Exception {
|
||||
public
|
||||
void userEventTriggered(ChannelHandlerContext context, Object event) throws Exception {
|
||||
// if (e.getState() == IdleState.READER_IDLE) {
|
||||
// e.getChannel().close();
|
||||
// } else if (e.getState() == IdleState.WRITER_IDLE) {
|
||||
|
@ -406,12 +407,14 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
public
|
||||
void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
channelRead(message);
|
||||
ReferenceCountUtil.release(message);
|
||||
}
|
||||
|
||||
public void channelRead(Object object) throws Exception {
|
||||
public
|
||||
void channelRead(Object object) throws Exception {
|
||||
|
||||
// prevent close from occurring SMACK in the middle of a message in progress.
|
||||
// delay close until it's finished.
|
||||
|
@ -430,13 +433,14 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
}
|
||||
|
||||
@Override
|
||||
public void channelInactive(ChannelHandlerContext context) throws Exception {
|
||||
public
|
||||
void channelInactive(ChannelHandlerContext context) throws Exception {
|
||||
// if we are in the middle of a message, hold off.
|
||||
if (this.messageInProgress.get()) {
|
||||
synchronized (this.messageInProgressLock) {
|
||||
try {
|
||||
this.messageInProgressLock.wait();
|
||||
} catch (InterruptedException e) {
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -451,19 +455,24 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
|
||||
if (isTCP) {
|
||||
type = "TCP";
|
||||
} else if (channelClass == NioDatagramChannel.class) {
|
||||
}
|
||||
else if (channelClass == NioDatagramChannel.class || channelClass == EpollDatagramChannel.class) {
|
||||
type = "UDP";
|
||||
} else if (channelClass == EpollDatagramChannel.class) {
|
||||
type = "UDP";
|
||||
} else if (channelClass == NioUdtByteConnectorChannel.class) {
|
||||
}
|
||||
else if (channelClass == NioUdtByteConnectorChannel.class) {
|
||||
type = "UDT";
|
||||
} else if (channelClass == LocalChannel.class) {
|
||||
}
|
||||
else if (channelClass == LocalChannel.class) {
|
||||
type = "LOCAL";
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
type = "UNKNOWN";
|
||||
}
|
||||
|
||||
this.logger.info("Closed remote {} connection: {}", type, channel.remoteAddress().toString());
|
||||
this.logger.info("Closed remote {} connection: {}",
|
||||
type,
|
||||
channel.remoteAddress()
|
||||
.toString());
|
||||
}
|
||||
|
||||
// our master channels are TCP/LOCAL (which are mutually exclusive). Only key disconnect events based on the status of them.
|
||||
|
@ -486,21 +495,22 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Closes the connection
|
||||
*/
|
||||
@Override
|
||||
public final void close() {
|
||||
public final
|
||||
void close() {
|
||||
// only close if we aren't already in the middle of closing.
|
||||
if (this.closeInProgress.compareAndSet(false, true)) {
|
||||
int idleTimeout = this.endPoint.getIdleTimeout();
|
||||
if (idleTimeout == 0) {
|
||||
int idleTimeoutMs = this.endPoint.getIdleTimeout();
|
||||
if (idleTimeoutMs == 0) {
|
||||
// default is 2 second timeout, in milliseconds.
|
||||
idleTimeout = 2000;
|
||||
idleTimeoutMs = 2000;
|
||||
}
|
||||
|
||||
// if we are in the middle of a message, hold off.
|
||||
synchronized (this.messageInProgressLock) {
|
||||
if (this.messageInProgress.get()) {
|
||||
try {
|
||||
this.messageInProgressLock.wait(idleTimeout);
|
||||
} catch (InterruptedException e) {
|
||||
this.messageInProgressLock.wait(idleTimeoutMs);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -521,8 +531,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
synchronized (this.closeInProgressLock) {
|
||||
if (!this.alreadyClosed.get()) {
|
||||
try {
|
||||
this.closeInProgressLock.wait(idleTimeout);
|
||||
} catch (Exception e) {
|
||||
this.closeInProgressLock.wait(idleTimeoutMs);
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -530,7 +540,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
|
||||
public
|
||||
void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
|
||||
if (!(cause instanceof IOException)) {
|
||||
Channel channel = context.channel();
|
||||
|
||||
|
@ -553,27 +564,29 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* Expose methods to modify the connection listeners.
|
||||
*/
|
||||
@Override
|
||||
public final ListenerBridge listeners() {
|
||||
public final
|
||||
ListenerBridge listeners() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to this connection/endpoint to be notified of
|
||||
* connect/disconnect/idle/receive(object) events.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If the listener already exists, it is not added again.
|
||||
* <p>
|
||||
* <p/>
|
||||
* When called by a server, NORMALLY listeners are added at the GLOBAL level
|
||||
* (meaning, I add one listener, and ALL connections are notified of that
|
||||
* listener.
|
||||
* <p>
|
||||
* <p/>
|
||||
* It is POSSIBLE to add a server connection ONLY (ie, not global) listener
|
||||
* (via connection.addListener), meaning that ONLY that listener attached to
|
||||
* the connection is notified on that event (ie, admin type listeners)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public final void add(ListenerRaw listener) {
|
||||
public final
|
||||
void add(ListenerRaw listener) {
|
||||
if (this.endPoint instanceof EndPointServer) {
|
||||
// when we are a server, NORMALLY listeners are added at the GLOBAL level
|
||||
// meaning --
|
||||
|
@ -586,31 +599,34 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
// is empty, we can remove it from this connection.
|
||||
synchronized (this) {
|
||||
if (this.localListenerManager == null) {
|
||||
this.localListenerManager = ((EndPointServer)this.endPoint).addListenerManager(this);
|
||||
this.localListenerManager = ((EndPointServer) this.endPoint).addListenerManager(this);
|
||||
}
|
||||
this.localListenerManager.add(listener);
|
||||
}
|
||||
|
||||
} else {
|
||||
this.endPoint.listeners().add(listener);
|
||||
}
|
||||
else {
|
||||
this.endPoint.listeners()
|
||||
.add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from this connection/endpoint to NO LONGER be notified
|
||||
* of connect/disconnect/idle/receive(object) events.
|
||||
* <p>
|
||||
* <p/>
|
||||
* When called by a server, NORMALLY listeners are added at the GLOBAL level
|
||||
* (meaning, I add one listener, and ALL connections are notified of that
|
||||
* listener.
|
||||
* <p>
|
||||
* <p/>
|
||||
* It is POSSIBLE to remove a server-connection 'non-global' listener (via
|
||||
* connection.removeListener), meaning that ONLY that listener attached to
|
||||
* the connection is removed
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public final void remove(ListenerRaw listener) {
|
||||
public final
|
||||
void remove(ListenerRaw listener) {
|
||||
if (this.endPoint instanceof EndPointServer) {
|
||||
// when we are a server, NORMALLY listeners are added at the GLOBAL level
|
||||
// meaning --
|
||||
|
@ -626,12 +642,14 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
this.localListenerManager.remove(listener);
|
||||
|
||||
if (!this.localListenerManager.hasListeners()) {
|
||||
((EndPointServer)this.endPoint).removeListenerManager(this);
|
||||
((EndPointServer) this.endPoint).removeListenerManager(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.endPoint.listeners().remove(listener);
|
||||
}
|
||||
else {
|
||||
this.endPoint.listeners()
|
||||
.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -640,7 +658,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* LONGER be notified of connect/disconnect/idle/receive(object) events.
|
||||
*/
|
||||
@Override
|
||||
public final void removeAll() {
|
||||
public final
|
||||
void removeAll() {
|
||||
if (this.endPoint instanceof EndPointServer) {
|
||||
// when we are a server, NORMALLY listeners are added at the GLOBAL level
|
||||
// meaning --
|
||||
|
@ -656,11 +675,13 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
this.localListenerManager.removeAll();
|
||||
this.localListenerManager = null;
|
||||
|
||||
((EndPointServer)this.endPoint).removeListenerManager(this);
|
||||
((EndPointServer) this.endPoint).removeListenerManager(this);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.endPoint.listeners().removeAll();
|
||||
}
|
||||
else {
|
||||
this.endPoint.listeners()
|
||||
.removeAll();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -670,7 +691,8 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
* connect/disconnect/idle/receive(object) events.
|
||||
*/
|
||||
@Override
|
||||
public final void removeAll(Class<?> classType) {
|
||||
public final
|
||||
void removeAll(Class<?> classType) {
|
||||
if (this.endPoint instanceof EndPointServer) {
|
||||
// when we are a server, NORMALLY listeners are added at the GLOBAL level
|
||||
// meaning --
|
||||
|
@ -687,27 +709,32 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
|
||||
if (!this.localListenerManager.hasListeners()) {
|
||||
this.localListenerManager = null;
|
||||
((EndPointServer)this.endPoint).removeListenerManager(this);
|
||||
((EndPointServer) this.endPoint).removeListenerManager(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.endPoint.listeners().removeAll(classType);
|
||||
}
|
||||
else {
|
||||
this.endPoint.listeners()
|
||||
.removeAll(classType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
public
|
||||
String toString() {
|
||||
return this.channelWrapper.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public
|
||||
int hashCode() {
|
||||
return id();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
public
|
||||
boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
|
@ -723,16 +750,154 @@ public class ConnectionImpl extends ChannelInboundHandlerAdapter
|
|||
if (other.channelWrapper != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!this.channelWrapper.equals(other.channelWrapper)) {
|
||||
return false;
|
||||
}
|
||||
if (this.name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!this.name.equals(other.name)) {
|
||||
}
|
||||
else if (!this.channelWrapper.equals(other.channelWrapper)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
//
|
||||
// RMI methods
|
||||
//
|
||||
|
||||
volatile RegistrationLatch registrationLatch;
|
||||
|
||||
class RegistrationLatch {
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
Object remoteObject;
|
||||
boolean hasError = false;
|
||||
}
|
||||
|
||||
|
||||
private final AtomicInteger rmiObjectIdCounter = new AtomicInteger(0);
|
||||
|
||||
|
||||
@SuppressWarnings({"UnnecessaryLocalVariable", "unchecked"})
|
||||
@Override
|
||||
public
|
||||
<Iface, Impl extends Iface> Iface createRemoteObject(final Class<Iface> remoteImplementationInterface,
|
||||
final Class<Impl> remoteImplementationClass) throws NetException {
|
||||
|
||||
// only one register can happen at a time
|
||||
synchronized (rmiObjectIdCounter) {
|
||||
registrationLatch = new RegistrationLatch();
|
||||
|
||||
// since this synchronous, we want to wait for the response before we continue
|
||||
TCP(new RmiRegistration(remoteImplementationClass.getName())).flush();
|
||||
|
||||
try {
|
||||
if (!registrationLatch.latch.await(2, TimeUnit.SECONDS)) {
|
||||
final String errorMessage = "Timed out getting registration ID for: " + remoteImplementationClass;
|
||||
logger.error(errorMessage);
|
||||
throw new NetException(errorMessage);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
final String errorMessage = "Error getting registration ID for: " + remoteImplementationClass;
|
||||
logger.error(errorMessage, e);
|
||||
throw new NetException(errorMessage, e);
|
||||
}
|
||||
|
||||
// local var to prevent double hit on volatile field
|
||||
final RegistrationLatch latch = registrationLatch;
|
||||
if (latch.hasError) {
|
||||
final String errorMessage = "Error getting registration ID for: " + remoteImplementationClass;
|
||||
logger.error(errorMessage);
|
||||
throw new NetException(errorMessage);
|
||||
}
|
||||
|
||||
return (Iface) latch.remoteObject;
|
||||
}
|
||||
}
|
||||
|
||||
void registerInternal(final ConnectionImpl connection, final RmiRegistration remoteRegistration) {
|
||||
final String implementationClassName = remoteRegistration.remoteImplementationClass;
|
||||
|
||||
|
||||
if (implementationClassName != null) {
|
||||
// THIS IS ON THE SERVER SIDE
|
||||
// create a new ID, and register the ID and new object (must create a new one) in the object maps
|
||||
|
||||
Class<?> implementationClass;
|
||||
|
||||
try {
|
||||
implementationClass = Class.forName(implementationClassName);
|
||||
} catch (Exception e) {
|
||||
logger.error("Error registering RMI class " + implementationClassName, e);
|
||||
connection.TCP(new RmiRegistration()).flush();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final Object remotePrimaryObject = implementationClass.newInstance();
|
||||
rmiBridge.register(rmiObjectIdCounter.getAndIncrement(), remotePrimaryObject);
|
||||
|
||||
LinkedList<ClassObject> remoteClasses = new LinkedList<ClassObject>();
|
||||
remoteClasses.add(new ClassObject(implementationClass, remotePrimaryObject));
|
||||
|
||||
ClassObject remoteClassObject;
|
||||
while ((remoteClassObject = remoteClasses.pollFirst()) != null) {
|
||||
// we have to check the class that is being registered for any additional proxy information
|
||||
for (Field field : remoteClassObject.clazz.getDeclaredFields()) {
|
||||
Annotation[] annotations = field.getDeclaredAnnotations();
|
||||
|
||||
if (annotations != null) {
|
||||
for (Annotation annotation : annotations) {
|
||||
if (annotation.annotationType().equals(RemoteProxy.class)) {
|
||||
boolean prev = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
final Object o = field.get(remoteClassObject.object);
|
||||
field.setAccessible(prev);
|
||||
final Class<?> type = field.getType();
|
||||
|
||||
rmiBridge.register(rmiObjectIdCounter.getAndIncrement(), o);
|
||||
|
||||
remoteClasses.offerLast(new ClassObject(type, o));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// connection.TCP(new RmiRegistration()).flush();
|
||||
connection.TCP(new RmiRegistration(remotePrimaryObject)).flush();
|
||||
} catch (Exception e) {
|
||||
logger.error("Error registering RMI class " + implementationClassName, e);
|
||||
connection.TCP(new RmiRegistration()).flush();
|
||||
}
|
||||
} else {
|
||||
// THIS IS ON THE CLIENT SIDE
|
||||
|
||||
// the next two use a local var, so that there isn't a double hit for volatile access
|
||||
final RegistrationLatch latch = this.registrationLatch;
|
||||
latch.hasError = remoteRegistration.hasError;
|
||||
|
||||
if (!remoteRegistration.hasError) {
|
||||
latch.remoteObject = remoteRegistration.remoteObject;
|
||||
}
|
||||
|
||||
// notify the original register that it may continue. We access the volatile field directly, so that it's members are updated
|
||||
registrationLatch.latch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the object registered with the specified ID.
|
||||
*/
|
||||
public
|
||||
Object getRegisteredObject(final int objectID) {
|
||||
return rmiBridge.getRegisteredObject(objectID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID registered for the specified object, or Integer.MAX_VALUE if not found.
|
||||
*/
|
||||
public
|
||||
int getRegisteredId(final Object object) {
|
||||
return rmiBridge.getRegisteredId(object);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.rmi.RmiMessages;
|
||||
import dorkbox.network.util.ConcurrentHashMapFactory;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import dorkbox.util.ClassHelper;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
//note that we specifically DO NOT implement equals/hashCode, because we cannot create two separate
|
||||
// objects that are somehow equal to each other.
|
||||
public class ConnectionManager implements ListenerBridge, ISessionManager {
|
||||
public
|
||||
class ConnectionManager implements ListenerBridge, ISessionManager {
|
||||
|
||||
public static Listener<?> unRegisteredType_Listener = null;
|
||||
|
||||
|
@ -28,15 +24,16 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
private final ConcurrentHashMapFactory<Connection, ConnectionManager> localManagers;
|
||||
private final CopyOnWriteArrayList<Connection> connections = new CopyOnWriteArrayList<Connection>();
|
||||
|
||||
/** Used by the listener subsystem to determine types. */
|
||||
/**
|
||||
* Used by the listener subsystem to determine types.
|
||||
*/
|
||||
private final Class<?> baseClass;
|
||||
protected final org.slf4j.Logger logger;
|
||||
private final String name;
|
||||
volatile boolean shutdown = false;
|
||||
|
||||
public ConnectionManager(String name, Class<?> baseClass) {
|
||||
this.name = name;
|
||||
this.logger = org.slf4j.LoggerFactory.getLogger(name);
|
||||
public
|
||||
ConnectionManager(final String loggerName, final Class<?> baseClass) {
|
||||
this.logger = org.slf4j.LoggerFactory.getLogger(loggerName);
|
||||
|
||||
this.baseClass = baseClass;
|
||||
|
||||
|
@ -44,7 +41,8 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public CopyOnWriteArrayList<ListenerRaw<Connection, Object>> createNewOject(Object... args) {
|
||||
public
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection, Object>> createNewOject(Object... args) {
|
||||
return new CopyOnWriteArrayList<ListenerRaw<Connection, Object>>();
|
||||
}
|
||||
};
|
||||
|
@ -53,8 +51,9 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public ConnectionManager createNewOject(Object... args) {
|
||||
return new ConnectionManager(ConnectionManager.this.name + "-" + args[0] + " Specific", ConnectionManager.this.baseClass);
|
||||
public
|
||||
ConnectionManager createNewOject(Object... args) {
|
||||
return new ConnectionManager(loggerName + "-" + args[0] + " Specific", ConnectionManager.this.baseClass);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -62,20 +61,21 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
/**
|
||||
* Adds a listener to this connection/endpoint to be notified of
|
||||
* connect/disconnect/idle/receive(object) events.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If the listener already exists, it is not added again.
|
||||
* <p>
|
||||
* <p/>
|
||||
* When called by a server, NORMALLY listeners are added at the GLOBAL level
|
||||
* (meaning, I add one listener, and ALL connections are notified of that
|
||||
* listener.
|
||||
* <p>
|
||||
* <p/>
|
||||
* It is POSSIBLE to add a server connection ONLY (ie, not global) listener
|
||||
* (via connection.addListener), meaning that ONLY that listener attached to
|
||||
* the connection is notified on that event (ie, admin type listeners)
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public final void add(ListenerRaw listener) {
|
||||
public final
|
||||
void add(ListenerRaw listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null.");
|
||||
}
|
||||
|
@ -95,9 +95,8 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
addListener0(listener);
|
||||
return;
|
||||
|
||||
} else if (ClassHelper.hasInterface(Connection.class, genericClass) &&
|
||||
!ClassHelper.hasParentClass(this.baseClass, genericClass)) {
|
||||
|
||||
}
|
||||
else if (ClassHelper.hasInterface(Connection.class, genericClass) && !ClassHelper.hasParentClass(this.baseClass, genericClass)) {
|
||||
// now we must make sure that the PARENT class is NOT the base class. ONLY the base class is allowed!
|
||||
addListener0(listener);
|
||||
return;
|
||||
|
@ -110,8 +109,9 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
/**
|
||||
* INTERNAL USE ONLY
|
||||
*/
|
||||
@SuppressWarnings({"unchecked","rawtypes"})
|
||||
private final void addListener0(ListenerRaw listener) {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private
|
||||
void addListener0(ListenerRaw listener) {
|
||||
Class<?> type = listener.getObjectType();
|
||||
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection, Object>> list = this.listeners.getOrCreate(type);
|
||||
|
@ -119,25 +119,29 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("listener added: {} <{}>", listener.getClass().getName(), listener.getObjectType());
|
||||
logger2.trace("listener added: {} <{}>",
|
||||
listener.getClass()
|
||||
.getName(),
|
||||
listener.getObjectType());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener from this connection/endpoint to NO LONGER be notified
|
||||
* of connect/disconnect/idle/receive(object) events.
|
||||
* <p>
|
||||
* <p/>
|
||||
* When called by a server, NORMALLY listeners are added at the GLOBAL level
|
||||
* (meaning, I add one listener, and ALL connections are notified of that
|
||||
* listener.
|
||||
* <p>
|
||||
* <p/>
|
||||
* It is POSSIBLE to remove a server-connection 'non-global' listener (via
|
||||
* connection.removeListener), meaning that ONLY that listener attached to
|
||||
* the connection is removed
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public final void remove(ListenerRaw listener) {
|
||||
public final
|
||||
void remove(ListenerRaw listener) {
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null.");
|
||||
}
|
||||
|
@ -151,7 +155,10 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("listener removed: {} <{}>", listener.getClass().getName(), listener.getObjectType());
|
||||
logger2.trace("listener removed: {} <{}>",
|
||||
listener.getClass()
|
||||
.getName(),
|
||||
listener.getObjectType());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,23 +166,25 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
* Removes all registered listeners from this connection/endpoint to NO
|
||||
* LONGER be notified of connect/disconnect/idle/receive(object) events.
|
||||
*/
|
||||
@Override
|
||||
public final void removeAll() {
|
||||
this.listeners.clear();
|
||||
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("all listeners removed !!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all registered listeners (of the object type) from this
|
||||
* connection/endpoint to NO LONGER be notified of
|
||||
* connect/disconnect/idle/receive(object) events.
|
||||
*/
|
||||
@Override
|
||||
public final void removeAll(Class<?> classType) {
|
||||
public final
|
||||
void removeAll() {
|
||||
this.listeners.clear();
|
||||
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("all listeners removed !!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all registered listeners (of the object type) from this
|
||||
* connection/endpoint to NO LONGER be notified of
|
||||
* connect/disconnect/idle/receive(object) events.
|
||||
*/
|
||||
@Override
|
||||
public final
|
||||
void removeAll(Class<?> classType) {
|
||||
if (classType == null) {
|
||||
throw new IllegalArgumentException("classType cannot be null.");
|
||||
}
|
||||
|
@ -184,25 +193,29 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("all listeners removed for type: {}", classType.getClass().getName());
|
||||
logger2.trace("all listeners removed for type: {}",
|
||||
classType.getClass()
|
||||
.getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invoked when a message object was received from a remote peer.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If data is sent in response to this event, the connection data is automatically flushed to the wire. If the data is sent in a separate thread,
|
||||
* {@link connection.send().flush()} must be called manually.
|
||||
*
|
||||
* {@link EndPoint#send().flush()} must be called manually.
|
||||
* <p/>
|
||||
* {@link ISessionManager}
|
||||
*/
|
||||
@Override
|
||||
public final void notifyOnMessage(Connection connection, Object message) {
|
||||
public final
|
||||
void notifyOnMessage(Connection connection, Object message) {
|
||||
notifyOnMessage0(connection, message, false);
|
||||
}
|
||||
|
||||
private final boolean notifyOnMessage0(Connection connection, Object message, boolean foundListener) {
|
||||
private
|
||||
boolean notifyOnMessage0(Connection connection, Object message, boolean foundListener) {
|
||||
Class<?> objectType = message.getClass();
|
||||
|
||||
// this is the GLOBAL version (unless it's the call from below, then it's the connection scoped version)
|
||||
|
@ -265,42 +278,50 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
|
||||
// only run a flush once
|
||||
if (foundListener) {
|
||||
connection.send().flush();
|
||||
} else if (unRegisteredType_Listener != null) {
|
||||
connection.send()
|
||||
.flush();
|
||||
}
|
||||
else if (unRegisteredType_Listener != null) {
|
||||
unRegisteredType_Listener.received(connection, null);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isErrorEnabled()) {
|
||||
this.logger.error("----------- LISTENER NOT REGISTERED FOR TYPE: {}", message.getClass().getSimpleName());
|
||||
this.logger.error("----------- LISTENER NOT REGISTERED FOR TYPE: {}",
|
||||
message.getClass()
|
||||
.getSimpleName());
|
||||
}
|
||||
}
|
||||
return foundListener;
|
||||
}
|
||||
|
||||
public static void setUnregisteredTypeListener(Listener<?> listener) {
|
||||
public static
|
||||
void setUnregisteredTypeListener(Listener<?> listener) {
|
||||
unRegisteredType_Listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked when a Connection has been idle for a while.
|
||||
*
|
||||
* <p/>
|
||||
* {@link ISessionManager}
|
||||
*/
|
||||
@Override
|
||||
public final void notifyOnIdle(Connection connection) {
|
||||
public final
|
||||
void notifyOnIdle(Connection connection) {
|
||||
Set<Entry<Type, CopyOnWriteArrayList<ListenerRaw<Connection, Object>>>> entrySet = this.listeners.entrySet();
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection,Object>> list;
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection, Object>> list;
|
||||
for (Entry<Type, CopyOnWriteArrayList<ListenerRaw<Connection, Object>>> entry : entrySet) {
|
||||
list = entry.getValue();
|
||||
if (list != null) {
|
||||
for (ListenerRaw<Connection,Object> listener : list) {
|
||||
for (ListenerRaw<Connection, Object> listener : list) {
|
||||
if (this.shutdown) {
|
||||
return;
|
||||
}
|
||||
|
||||
listener.idle(connection);
|
||||
}
|
||||
connection.send().flush();
|
||||
connection.send()
|
||||
.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,28 +333,30 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Invoked when a {@link Channel} is open, bound to a local address, and connected to a remote address.
|
||||
*
|
||||
* Invoked when a Channel is open, bound to a local address, and connected to a remote address.
|
||||
* <p/>
|
||||
* {@link ISessionManager}
|
||||
*/
|
||||
@Override
|
||||
public void connectionConnected(Connection connection) {
|
||||
public
|
||||
void connectionConnected(Connection connection) {
|
||||
// create a new connection!
|
||||
this.connections.add(connection);
|
||||
|
||||
try {
|
||||
Set<Entry<Type, CopyOnWriteArrayList<ListenerRaw<Connection, Object>>>> entrySet = this.listeners.entrySet();
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection,Object>> list;
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection, Object>> list;
|
||||
for (Entry<Type, CopyOnWriteArrayList<ListenerRaw<Connection, Object>>> entry : entrySet) {
|
||||
list = entry.getValue();
|
||||
if (list != null) {
|
||||
for (ListenerRaw<Connection,Object> listener : list) {
|
||||
for (ListenerRaw<Connection, Object> listener : list) {
|
||||
if (this.shutdown) {
|
||||
return;
|
||||
}
|
||||
listener.connected(connection);
|
||||
}
|
||||
connection.send().flush();
|
||||
connection.send()
|
||||
.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -348,14 +371,15 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Invoked when a {@link Channel} was disconnected from its remote peer.
|
||||
*
|
||||
* Invoked when a Channel was disconnected from its remote peer.
|
||||
* <p/>
|
||||
* {@link ISessionManager}
|
||||
*/
|
||||
@Override
|
||||
public void connectionDisconnected(Connection connection) {
|
||||
public
|
||||
void connectionDisconnected(Connection connection) {
|
||||
Set<Entry<Type, CopyOnWriteArrayList<ListenerRaw<Connection, Object>>>> entrySet = this.listeners.entrySet();
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection,Object>> list;
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection, Object>> list;
|
||||
for (Entry<Type, CopyOnWriteArrayList<ListenerRaw<Connection, Object>>> entry : entrySet) {
|
||||
list = entry.getValue();
|
||||
if (list != null) {
|
||||
|
@ -384,13 +408,14 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
|
||||
/**
|
||||
* Invoked when there is an error of some kind during the up/down stream process
|
||||
*
|
||||
* <p/>
|
||||
* {@link ISessionManager}
|
||||
*/
|
||||
@Override
|
||||
public void connectionError(Connection connection, Throwable throwable) {
|
||||
public
|
||||
void connectionError(Connection connection, Throwable throwable) {
|
||||
Set<Entry<Type, CopyOnWriteArrayList<ListenerRaw<Connection, Object>>>> entrySet = this.listeners.entrySet();
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection,Object>> list;
|
||||
CopyOnWriteArrayList<ListenerRaw<Connection, Object>> list;
|
||||
for (Entry<Type, CopyOnWriteArrayList<ListenerRaw<Connection, Object>>> entry : entrySet) {
|
||||
list = entry.getValue();
|
||||
if (list != null) {
|
||||
|
@ -401,7 +426,8 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
|
||||
listener.error(connection, throwable);
|
||||
}
|
||||
connection.send().flush();
|
||||
connection.send()
|
||||
.flush();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -414,17 +440,19 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
|
||||
/**
|
||||
* Returns a non-modifiable list of active connections
|
||||
*
|
||||
* <p/>
|
||||
* {@link ISessionManager}
|
||||
*/
|
||||
@Override
|
||||
public List<Connection> getConnections() {
|
||||
public
|
||||
List<Connection> getConnections() {
|
||||
return Collections.unmodifiableList(this.connections);
|
||||
}
|
||||
|
||||
|
||||
|
||||
final ConnectionManager addListenerManager(Connection connection) {
|
||||
final
|
||||
ConnectionManager addListenerManager(Connection connection) {
|
||||
// when we are a server, NORMALLY listeners are added at the GLOBAL level (meaning, I add one listener, and ALL connections
|
||||
// are notified of that listener.
|
||||
// it is POSSIBLE to add a connection-specfic listener (via connection.addListener), meaning that ONLY
|
||||
|
@ -440,7 +468,8 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
return lm;
|
||||
}
|
||||
|
||||
final void removeListenerManager(Connection connection) {
|
||||
final
|
||||
void removeListenerManager(Connection connection) {
|
||||
this.localManagers.remove(connection);
|
||||
}
|
||||
|
||||
|
@ -450,7 +479,8 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
*
|
||||
* @return Returns a FAST list of active connections.
|
||||
*/
|
||||
public final Collection<Connection> getConnections0() {
|
||||
public final
|
||||
Collection<Connection> getConnections0() {
|
||||
return this.connections;
|
||||
}
|
||||
|
||||
|
@ -459,10 +489,14 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
*
|
||||
* @return Returns a FAST first connection (for client!).
|
||||
*/
|
||||
public final Connection getConnection0() {
|
||||
if (this.connections.iterator().hasNext()) {
|
||||
return this.connections.iterator().next();
|
||||
} else {
|
||||
public final
|
||||
Connection getConnection0() {
|
||||
if (this.connections.iterator()
|
||||
.hasNext()) {
|
||||
return this.connections.iterator()
|
||||
.next();
|
||||
}
|
||||
else {
|
||||
throw new NetException("Not connected to a remote computer. Unable to continue!");
|
||||
}
|
||||
}
|
||||
|
@ -472,14 +506,16 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
*
|
||||
* @return a boolean indicating if there are any listeners registered with this manager.
|
||||
*/
|
||||
final boolean hasListeners() {
|
||||
final
|
||||
boolean hasListeners() {
|
||||
return this.listeners.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all associated resources/threads/connections
|
||||
*/
|
||||
final void stop() {
|
||||
final
|
||||
void stop() {
|
||||
this.shutdown = true;
|
||||
|
||||
// disconnect the sessions
|
||||
|
@ -491,7 +527,8 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
/**
|
||||
* Close all connections ONLY
|
||||
*/
|
||||
final void closeConnections() {
|
||||
final
|
||||
void closeConnections() {
|
||||
// close the sessions
|
||||
Iterator<Connection> iterator = this.connections.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
|
@ -503,4 +540,17 @@ public class ConnectionManager implements ListenerBridge, ISessionManager {
|
|||
}
|
||||
this.connections.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
boolean equals(final Object o) {
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +1,25 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
import com.esotericsoftware.kryo.factories.SerializerFactory;
|
||||
import dorkbox.network.ConnectionOptions;
|
||||
import dorkbox.network.Configuration;
|
||||
import dorkbox.network.connection.bridge.ConnectionBridgeBase;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.connection.wrapper.ChannelLocalWrapper;
|
||||
import dorkbox.network.connection.wrapper.ChannelNetworkWrapper;
|
||||
import dorkbox.network.connection.wrapper.ChannelWrapper;
|
||||
import dorkbox.network.pipeline.KryoEncoder;
|
||||
import dorkbox.network.pipeline.KryoEncoderCrypto;
|
||||
import dorkbox.network.rmi.RemoteObject;
|
||||
import dorkbox.network.rmi.Rmi;
|
||||
import dorkbox.network.rmi.RmiBridge;
|
||||
import dorkbox.network.rmi.TimeoutException;
|
||||
import dorkbox.network.util.EndpointTool;
|
||||
import dorkbox.network.util.KryoSerializationManager;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.EndPointTool;
|
||||
import dorkbox.network.util.entropy.Entropy;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import dorkbox.network.util.serializers.FieldAnnotationAwareSerializer;
|
||||
import dorkbox.network.util.serializers.IgnoreSerialization;
|
||||
import dorkbox.network.util.store.NullSettingsStore;
|
||||
import dorkbox.network.util.store.SettingsStore;
|
||||
import dorkbox.util.Sys;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.collections.IntMap.Entries;
|
||||
import dorkbox.util.crypto.Crypto;
|
||||
import dorkbox.util.crypto.serialization.EccPrivateKeySerializer;
|
||||
import dorkbox.util.crypto.serialization.EccPublicKeySerializer;
|
||||
import dorkbox.util.crypto.serialization.IesParametersSerializer;
|
||||
import dorkbox.util.crypto.serialization.IesWithCipherParametersSerializer;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
|
@ -41,18 +28,20 @@ import io.netty.util.concurrent.Future;
|
|||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.bouncycastle.crypto.params.IESParameters;
|
||||
import org.bouncycastle.crypto.params.IESWithCipherParameters;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.security.AccessControlException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
|
@ -72,17 +61,20 @@ class EndPoint {
|
|||
// TODO: maybe some sort of STUN-like connection keep-alive??
|
||||
|
||||
|
||||
// TODO: do we really need this? Maybe?
|
||||
public static final String LOCAL_CHANNEL = "local_channel";
|
||||
protected static final String shutdownHookName = "::SHUTDOWN_HOOK::";
|
||||
protected static final String stopTreadName = "::STOP_THREAD::";
|
||||
|
||||
|
||||
/**
|
||||
* Shall we REALLY use a valid lan IP address instead of the loopback address? A common mistake is to listen on localhost, which does
|
||||
* not accept external network connections
|
||||
*/
|
||||
public static boolean useLanIpInsteadOfLoopback = true;
|
||||
/**
|
||||
* this can be changed to a more specialized value, if necessary
|
||||
*/
|
||||
public static int DEFAULT_THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors() * 2;
|
||||
public static final String LOCAL_CHANNEL = "local_channel";
|
||||
|
||||
|
||||
public static int DEFAULT_THREAD_POOL_SIZE = Runtime.getRuntime()
|
||||
.availableProcessors() * 2;
|
||||
/**
|
||||
* The amount of time in milli-seconds to wait for this endpoint to close all
|
||||
* {@link Channel}s and shutdown gracefully.
|
||||
|
@ -114,60 +106,58 @@ class EndPoint {
|
|||
// Needed for NIO selectors on Android 2.2, and to force IPv4.
|
||||
System.setProperty("java.net.preferIPv4Stack", Boolean.TRUE.toString());
|
||||
System.setProperty("java.net.preferIPv6Addresses", Boolean.FALSE.toString());
|
||||
} catch (AccessControlException ignored) {
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
protected final org.slf4j.Logger logger;
|
||||
protected final String name;
|
||||
|
||||
// make sure that the endpoint is closed on JVM shutdown (if it's still open at that point in time)
|
||||
protected Thread shutdownHook;
|
||||
protected final Class<? extends EndPoint> type;
|
||||
|
||||
protected final ConnectionManager connectionManager;
|
||||
protected final SerializationManager serializationManager;
|
||||
|
||||
protected final CryptoSerializationManager serializationManager;
|
||||
protected final RegistrationWrapper registrationWrapper;
|
||||
|
||||
// The remote object space is used by RMI.
|
||||
private final RmiBridge rmiBridge;
|
||||
|
||||
// the eventLoop groups are used to track and manage the event loops for startup/shutdown
|
||||
private List<EventLoopGroup> eventLoopGroups = new ArrayList<EventLoopGroup>(8);
|
||||
private List<ChannelFuture> shutdownChannelList = new ArrayList<ChannelFuture>();
|
||||
protected final Object shutdownInProgress = new Object();
|
||||
final ECPrivateKeyParameters privateKey;
|
||||
final ECPublicKeyParameters publicKey;
|
||||
final SecureRandom secureRandom;
|
||||
|
||||
private final CountDownLatch blockUntilDone = new CountDownLatch(1);
|
||||
private final CountDownLatch blockWhileShutdown = new CountDownLatch(1);
|
||||
|
||||
protected final Object shutdownInProgress = new Object();
|
||||
private final Executor rmiExecutor;
|
||||
private final boolean rmiEnabled;
|
||||
// the eventLoop groups are used to track and manage the event loops for startup/shutdown
|
||||
private final List<EventLoopGroup> eventLoopGroups = new ArrayList<EventLoopGroup>(8);
|
||||
private final List<ChannelFuture> shutdownChannelList = new ArrayList<ChannelFuture>();
|
||||
private final ConcurrentHashMap<Class<?>, EndPointTool> toolMap = new ConcurrentHashMap<Class<?>, EndPointTool>();
|
||||
// make sure that the endpoint is closed on JVM shutdown (if it's still open at that point in time)
|
||||
protected Thread shutdownHook;
|
||||
protected AtomicBoolean stopCalled = new AtomicBoolean(false);
|
||||
|
||||
protected AtomicBoolean isConnected = new AtomicBoolean(false);
|
||||
|
||||
SettingsStore propertyStore;
|
||||
boolean disableRemoteKeyValidation;
|
||||
/**
|
||||
* in milliseconds. default is disabled!
|
||||
*/
|
||||
private volatile int idleTimeout = 0;
|
||||
|
||||
private ConcurrentHashMap<Class<?>, EndpointTool> toolMap = new ConcurrentHashMap<Class<?>, EndpointTool>();
|
||||
|
||||
final ECPrivateKeyParameters privateKey;
|
||||
final ECPublicKeyParameters publicKey;
|
||||
|
||||
final SecureRandom secureRandom;
|
||||
SettingsStore propertyStore;
|
||||
boolean disableRemoteKeyValidation;
|
||||
private volatile int idleTimeoutMs = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @param type this is either "Client" or "Server", depending on who is creating this endpoint.
|
||||
* @param options these are the specific connection options
|
||||
* @throws InitializationException
|
||||
* @throws SecurityException
|
||||
*/
|
||||
public
|
||||
EndPoint(String name, ConnectionOptions options) throws InitializationException, SecurityException {
|
||||
this.name = name;
|
||||
this.logger = org.slf4j.LoggerFactory.getLogger(name);
|
||||
EndPoint(Class<? extends EndPoint> type, final Configuration options) throws InitializationException, SecurityException, IOException {
|
||||
this.type = type;
|
||||
|
||||
this.registrationWrapper = new RegistrationWrapper(this, this.logger);
|
||||
this.logger = org.slf4j.LoggerFactory.getLogger(type);
|
||||
|
||||
this.registrationWrapper = new RegistrationWrapper(this,
|
||||
this.logger); // TODO - get rid of the wrapper, since it just loops back on itself
|
||||
|
||||
// make sure that 'localhost' is REALLY our specific IP address
|
||||
if (options.host != null && (options.host.equals("localhost") || options.host.startsWith("127."))) {
|
||||
if (useLanIpInsteadOfLoopback && options.host != null && (options.host.equals("localhost") || options.host.startsWith("127."))) {
|
||||
try {
|
||||
InetAddress localHostLanAddress = Sys.getLocalHostLanAddress();
|
||||
options.host = localHostLanAddress.getHostAddress();
|
||||
|
@ -177,14 +167,33 @@ class EndPoint {
|
|||
}
|
||||
}
|
||||
|
||||
// serialization stuff
|
||||
this.serializationManager = KryoCryptoSerializationManager.DEFAULT;
|
||||
|
||||
rmiEnabled = options.rmiEnabled;
|
||||
if (rmiEnabled) {
|
||||
// setup our RMI serialization managers. Can only be called once
|
||||
serializationManager.initRmiSerialization();
|
||||
}
|
||||
|
||||
rmiExecutor = options.rmiExecutor;
|
||||
|
||||
|
||||
// setup our TCP kryo encoders
|
||||
this.registrationWrapper.setKryoTcpEncoder(new KryoEncoder(this.serializationManager));
|
||||
this.registrationWrapper.setKryoTcpCryptoEncoder(new KryoEncoderCrypto(this.serializationManager));
|
||||
|
||||
|
||||
// we have to be able to specify WHAT property store we want to use, since it can change!
|
||||
if (options.settingsStore == null) {
|
||||
this.propertyStore = new PropertyStore(name);
|
||||
this.propertyStore = new PropertyStore();
|
||||
}
|
||||
else {
|
||||
this.propertyStore = options.settingsStore;
|
||||
}
|
||||
|
||||
this.propertyStore.init(type, this.serializationManager, null);
|
||||
|
||||
// null it out, since it is sensitive!
|
||||
options.settingsStore = null;
|
||||
|
||||
|
@ -198,7 +207,7 @@ class EndPoint {
|
|||
if (privateKey == null || publicKey == null) {
|
||||
try {
|
||||
// seed our RNG based off of this and create our ECC keys
|
||||
byte[] seedBytes = Entropy.get("There are no ECC keys for the " + name + " yet");
|
||||
byte[] seedBytes = Entropy.get("There are no ECC keys for the " + type.getSimpleName() + " yet");
|
||||
SecureRandom secureRandom = new SecureRandom(seedBytes);
|
||||
secureRandom.nextBytes(seedBytes);
|
||||
|
||||
|
@ -242,57 +251,23 @@ class EndPoint {
|
|||
}
|
||||
};
|
||||
this.shutdownHook.setName(shutdownHookName);
|
||||
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
|
||||
|
||||
|
||||
// serialization stuff
|
||||
if (options.serializationManager != null) {
|
||||
this.serializationManager = options.serializationManager;
|
||||
}
|
||||
else {
|
||||
this.serializationManager = new KryoSerializationManager();
|
||||
try {
|
||||
Runtime.getRuntime()
|
||||
.addShutdownHook(this.shutdownHook);
|
||||
} catch (Throwable ignored) {
|
||||
// if we are in the middle of shutdown, we cannot do this.
|
||||
}
|
||||
|
||||
|
||||
// we don't care about un-instantiated/constructed members, since the class type is the only interest.
|
||||
this.connectionManager = new ConnectionManager(name, connection0(null).getClass());
|
||||
|
||||
// setup our TCP kryo encoders
|
||||
this.registrationWrapper.setKryoTcpEncoder(new KryoEncoder(this.serializationManager));
|
||||
this.registrationWrapper.setKryoTcpCryptoEncoder(new KryoEncoderCrypto(this.serializationManager));
|
||||
|
||||
|
||||
this.serializationManager.setReferences(false);
|
||||
this.serializationManager.setRegistrationRequired(true);
|
||||
|
||||
this.serializationManager.register(PingMessage.class);
|
||||
this.serializationManager.register(byte[].class);
|
||||
this.serializationManager.register(IESParameters.class, new IesParametersSerializer());
|
||||
this.serializationManager.register(IESWithCipherParameters.class, new IesWithCipherParametersSerializer());
|
||||
this.serializationManager.register(ECPublicKeyParameters.class, new EccPublicKeySerializer());
|
||||
this.serializationManager.register(ECPrivateKeyParameters.class, new EccPrivateKeySerializer());
|
||||
this.serializationManager.register(Registration.class);
|
||||
|
||||
|
||||
// ignore fields that have the "IgnoreSerialization" annotation.
|
||||
Set<Class<? extends Annotation>> marks = new HashSet<Class<? extends Annotation>>();
|
||||
marks.add(IgnoreSerialization.class);
|
||||
SerializerFactory disregardingFactory = new FieldAnnotationAwareSerializer.Factory(marks, true);
|
||||
this.serializationManager.setDefaultSerializer(disregardingFactory);
|
||||
|
||||
this.connectionManager = new ConnectionManager(type.getSimpleName(), connection0(null).getClass());
|
||||
|
||||
// add the ping listener (internal use only!)
|
||||
this.connectionManager.add(new PingSystemListener());
|
||||
|
||||
/*
|
||||
* Creates the remote method invocation (RMI) bridge for this endpoint.
|
||||
* <p>
|
||||
* there is some housekeeping that is necessary BEFORE a connection is actually connected..
|
||||
*/
|
||||
if (options.enableRmi) {
|
||||
this.rmiBridge = new RmiBridge(this.logger, this.serializationManager);
|
||||
}
|
||||
else {
|
||||
this.rmiBridge = null;
|
||||
if (this.rmiEnabled) {
|
||||
// these register the listener for registering a class implementation for RMI (internal use only)
|
||||
this.connectionManager.add(new RegisterRmiSystemListener());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,6 +325,15 @@ class EndPoint {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of milli-seconds that must elapse with no read or write before {@link ListenerRaw#idle(Connection)} }
|
||||
* will be triggered
|
||||
*/
|
||||
public
|
||||
int getIdleTimeout() {
|
||||
return this.idleTimeoutMs;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Listener:idle()} will be triggered when neither read nor write
|
||||
* has happened for the specified period of time (in milli-seconds)
|
||||
|
@ -357,17 +341,8 @@ class EndPoint {
|
|||
* Specify {@code 0} to disable (default).
|
||||
*/
|
||||
public
|
||||
void setIdleTimeout(int idleTimeout) {
|
||||
this.idleTimeout = idleTimeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of milli-seconds that must elapse with no read or write before {@link Listener.idle()}
|
||||
* will be triggered
|
||||
*/
|
||||
public
|
||||
int getIdleTimeout() {
|
||||
return this.idleTimeout;
|
||||
void setIdleTimeout(int idleTimeoutMs) {
|
||||
this.idleTimeoutMs = idleTimeoutMs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -400,39 +375,26 @@ class EndPoint {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the serialization wrapper if there is an object type that needs to be added outside of the basics.
|
||||
*/
|
||||
public
|
||||
SerializationManager getSerialization() {
|
||||
CryptoSerializationManager getSerialization() {
|
||||
return this.serializationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the remote method invocation (RMI) bridge for this endpoint.
|
||||
*/
|
||||
public
|
||||
Rmi rmi() {
|
||||
if (this.rmiBridge == null) {
|
||||
throw new NetException("Cannot use a remote object space that has NOT been created first! Configure the ConnectionOptions!");
|
||||
}
|
||||
|
||||
return this.rmiBridge;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method allows the connections used by the client/server to be subclassed (custom implementations).
|
||||
* <p/>
|
||||
* As this is for the network stack, the new connection type MUST subclass {@link Connection}
|
||||
* As this is for the network stack, the new connection MUST subclass {@link Connection}
|
||||
* <p/>
|
||||
* The parameters are ALL NULL when getting the base class, as this instance is just thrown away.
|
||||
*
|
||||
* @param bridge null when retrieving the subclass type (internal use only). Non-null when creating a new (and real) connection.
|
||||
* @return a new network connection
|
||||
*/
|
||||
public
|
||||
Connection newConnection(String name) {
|
||||
return new ConnectionImpl(name);
|
||||
Connection newConnection(final Logger logger, final EndPoint endPoint, final RmiBridge rmiBridge) {
|
||||
return new ConnectionImpl(logger, endPoint, rmiBridge);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -446,8 +408,13 @@ class EndPoint {
|
|||
Connection connection0(MetaChannel metaChannel) {
|
||||
Connection connection;
|
||||
|
||||
RmiBridge rmiBridge = null;
|
||||
if (metaChannel != null && rmiEnabled) {
|
||||
rmiBridge = new RmiBridge(logger, rmiExecutor);
|
||||
}
|
||||
|
||||
// setup the extras needed by the network connection.
|
||||
// These properties are ASSGINED in the same thread that CREATED the object. Only the AES info needs to be
|
||||
// These properties are ASSIGNED in the same thread that CREATED the object. Only the AES info needs to be
|
||||
// volatile since it is the only thing that changes.
|
||||
if (metaChannel != null) {
|
||||
ChannelWrapper wrapper;
|
||||
|
@ -464,23 +431,22 @@ class EndPoint {
|
|||
}
|
||||
}
|
||||
|
||||
connection = newConnection(this.name);
|
||||
|
||||
// now initialize the connection channels with whatever extra info they might need.
|
||||
connection.init(this, new Bridge(wrapper, this.connectionManager));
|
||||
|
||||
connection = newConnection(logger, this, rmiBridge);
|
||||
metaChannel.connection = connection;
|
||||
|
||||
// notify our remote object space that it is able to receive method calls.
|
||||
if (this.rmiBridge != null) {
|
||||
connection.listeners().add(this.rmiBridge.getListener());
|
||||
// now initialize the connection channels with whatever extra info they might need.
|
||||
connection.init(new Bridge(wrapper, this.connectionManager));
|
||||
|
||||
if (rmiBridge != null) {
|
||||
// notify our remote object space that it is able to receive method calls.
|
||||
connection.listeners().add(rmiBridge.getListener());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// getting the baseClass
|
||||
// getting the connection baseClass
|
||||
|
||||
// have to add the networkAssociate to a map of "connected" computers
|
||||
connection = newConnection(this.name);
|
||||
connection = newConnection(null, null, null);
|
||||
}
|
||||
|
||||
return connection;
|
||||
|
@ -532,51 +498,23 @@ class EndPoint {
|
|||
public abstract
|
||||
ConnectionBridgeBase send();
|
||||
|
||||
/**
|
||||
* Returns a proxy object that implements the specified interfaces. Methods
|
||||
* invoked on the proxy object will be invoked remotely on the object with
|
||||
* the specified ID in the ObjectSpace for the specified connection. If the
|
||||
* remote end of the connection has not {@link #addConnection(Connection)
|
||||
* added} the connection to the ObjectSpace, the remote method invocations
|
||||
* will be ignored.
|
||||
* <p/>
|
||||
* Methods that return a value will throw {@link TimeoutException} if the
|
||||
* response is not received with the
|
||||
* {@link RemoteObject#setResponseTimeout(int) response timeout}.
|
||||
* <p/>
|
||||
* If {@link RemoteObject#setNonBlocking(boolean) non-blocking} is false
|
||||
* (the default), then methods that return a value must not be called from
|
||||
* the update thread for the connection. An exception will be thrown if this
|
||||
* occurs. Methods with a void return value can be called on the update
|
||||
* thread.
|
||||
* <p/>
|
||||
* If a proxy returned from this method is part of an object graph sent over
|
||||
* the network, the object graph on the receiving side will have the proxy
|
||||
* object replaced with the registered object.
|
||||
*
|
||||
* @see RemoteObject
|
||||
*/
|
||||
public
|
||||
RemoteObject getRemoteObject(Connection connection, int objectID, Class<?>[] ifaces) {
|
||||
return this.rmiBridge.getRemoteObject(connection, objectID, ifaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a tool with the server, to be used by other services.
|
||||
*/
|
||||
public
|
||||
void registerTool(EndpointTool toolClass) {
|
||||
void registerTool(EndPointTool toolClass) {
|
||||
if (toolClass == null) {
|
||||
throw new IllegalArgumentException("Tool must not be null! Unable to add tool");
|
||||
}
|
||||
|
||||
Class<?>[] interfaces = toolClass.getClass().getInterfaces();
|
||||
Class<?>[] interfaces = toolClass.getClass()
|
||||
.getInterfaces();
|
||||
int length = interfaces.length;
|
||||
int index = -1;
|
||||
|
||||
if (length > 1) {
|
||||
Class<?> clazz2;
|
||||
Class<EndpointTool> cls = EndpointTool.class;
|
||||
Class<EndPointTool> cls = EndPointTool.class;
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
clazz2 = interfaces[i];
|
||||
|
@ -595,7 +533,7 @@ class EndPoint {
|
|||
}
|
||||
|
||||
Class<?> clazz = interfaces[index];
|
||||
EndpointTool put = this.toolMap.put(clazz, toolClass);
|
||||
EndPointTool put = this.toolMap.put(clazz, toolClass);
|
||||
if (put != null) {
|
||||
throw new IllegalArgumentException("Tool must be unique! Unable to add tool");
|
||||
}
|
||||
|
@ -605,7 +543,7 @@ class EndPoint {
|
|||
* Only get the tools in the ModuleStart (ie: load) methods. If done in the constructor, the tool might not be available yet
|
||||
*/
|
||||
public
|
||||
<T extends EndpointTool> T getTool(Class<?> toolClass) {
|
||||
<T extends EndPointTool> T getTool(Class<?> toolClass) {
|
||||
if (toolClass == null) {
|
||||
throw new IllegalArgumentException("Tool must not be null! Unable to add tool");
|
||||
}
|
||||
|
@ -615,11 +553,6 @@ class EndPoint {
|
|||
return tool;
|
||||
}
|
||||
|
||||
public
|
||||
String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes all connections ONLY (keeps the server/client running).
|
||||
* <p/>
|
||||
|
@ -651,9 +584,11 @@ class EndPoint {
|
|||
}
|
||||
|
||||
/**
|
||||
* Safely closes all associated resources/threads/connections
|
||||
* Safely closes all associated resources/threads/connections.
|
||||
* <p/>
|
||||
* Override stopExtraActions() if you want to provide extra behavior to stopping the endpoint
|
||||
* If we want to WAIT for this endpoint to shutdown, we must explicitly call waitForShutdown()
|
||||
* <p/>
|
||||
* Override stopExtraActions() if you want to provide extra behavior while stopping the endpoint
|
||||
*/
|
||||
public final
|
||||
void stop() {
|
||||
|
@ -667,52 +602,66 @@ class EndPoint {
|
|||
// This occurs when calling stop from within a listener callback.
|
||||
Thread currentThread = Thread.currentThread();
|
||||
String threadName = currentThread.getName();
|
||||
boolean inEventThread = !threadName.equals(shutdownHookName) && !threadName.equals(stopTreadName);
|
||||
boolean inShutdownThread = !threadName.equals(shutdownHookName) && !threadName.equals(stopTreadName);
|
||||
|
||||
// used to check the event groups to see if we are running from one of them. NOW we force to
|
||||
// ALWAYS shutdown inside a NEW thread
|
||||
|
||||
if (!inEventThread) {
|
||||
if (!inShutdownThread) {
|
||||
stopInThread();
|
||||
}
|
||||
else {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
EndPoint.this.stopInThread();
|
||||
EndPoint.this.blockWhileShutdown.countDown();
|
||||
// we have to make sure always run this from within it's OWN thread -- because if it's run from within
|
||||
// a client/server thread executor, it will deadlock while waiting for the threadpool to terminate.
|
||||
boolean isInEventLoop = false;
|
||||
for (EventLoopGroup loopGroup : this.eventLoopGroups) {
|
||||
for (EventExecutor child : loopGroup.children()) {
|
||||
if (child.inEventLoop()) {
|
||||
isInEventLoop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.setDaemon(false);
|
||||
thread.setName(stopTreadName);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
// we want to wait for this to finish before we continue
|
||||
try {
|
||||
this.blockWhileShutdown.await();
|
||||
} catch (InterruptedException e) {
|
||||
this.logger.error("Thread interrupted while waiting for shutdown to finish!");
|
||||
if (!isInEventLoop) {
|
||||
EndPoint.this.stopInThread();
|
||||
}
|
||||
else {
|
||||
Thread thread = new Thread(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
EndPoint.this.stopInThread();
|
||||
}
|
||||
});
|
||||
thread.setDaemon(false);
|
||||
thread.setName(stopTreadName);
|
||||
thread.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This actually does the "stopping", since there is some logic to making sure we don't deadlock, this is important
|
||||
private final
|
||||
private
|
||||
void stopInThread() {
|
||||
// make sure we are not trying to stop during a startup procedure.
|
||||
// This will wait until we have finished starting up/shutting down.
|
||||
synchronized (this.shutdownInProgress) {
|
||||
close();
|
||||
|
||||
this.logger.info("Stopping endpoint");
|
||||
|
||||
// there is no need to call "stop" again if we close the connection.
|
||||
// however, if this is called WHILE from the shutdown hook, blammo! problems!
|
||||
|
||||
// Also, you can call client/server.stop from another thread, which is run when the JVM is shutting down
|
||||
// (as there is nothing left to do), and also have problems.
|
||||
if (!Thread.currentThread().getName().equals(shutdownHookName)) {
|
||||
if (!Thread.currentThread()
|
||||
.getName()
|
||||
.equals(shutdownHookName)) {
|
||||
try {
|
||||
Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
|
||||
Runtime.getRuntime()
|
||||
.removeShutdownHook(this.shutdownHook);
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
@ -737,13 +686,15 @@ class EndPoint {
|
|||
registrationWrapper2.releaseChannelMap();
|
||||
}
|
||||
|
||||
|
||||
// shutdown the database store
|
||||
this.propertyStore.shutdown();
|
||||
|
||||
// now we stop all of our channels
|
||||
for (ChannelFuture f : this.shutdownChannelList) {
|
||||
Channel channel = f.channel();
|
||||
channel.close().awaitUninterruptibly(maxShutdownWaitTimeInMilliSeconds);
|
||||
channel.close()
|
||||
.awaitUninterruptibly(maxShutdownWaitTimeInMilliSeconds);
|
||||
}
|
||||
|
||||
// we have to clear the shutdown list.
|
||||
|
@ -753,16 +704,18 @@ class EndPoint {
|
|||
List<Future<?>> shutdownThreadList = new LinkedList<Future<?>>();
|
||||
|
||||
for (EventLoopGroup loopGroup : this.eventLoopGroups) {
|
||||
shutdownThreadList.add(loopGroup.shutdownGracefully());
|
||||
shutdownThreadList.add(loopGroup.shutdownGracefully(maxShutdownWaitTimeInMilliSeconds,
|
||||
maxShutdownWaitTimeInMilliSeconds * 4,
|
||||
TimeUnit.MILLISECONDS));
|
||||
}
|
||||
|
||||
// now wait for them to finish!
|
||||
// It can take a few seconds to shut down the executor. This will affect unit testing, where connections are quickly created/stopped
|
||||
for (Future<?> f : shutdownThreadList) {
|
||||
f.awaitUninterruptibly(maxShutdownWaitTimeInMilliSeconds);
|
||||
f.syncUninterruptibly();
|
||||
}
|
||||
|
||||
// when the eventloop closes, the associated selectors are ALSO closed!
|
||||
|
||||
stopExtraActions();
|
||||
}
|
||||
|
||||
|
@ -778,50 +731,23 @@ class EndPoint {
|
|||
}
|
||||
|
||||
/**
|
||||
* Determines if the specified thread (usually the current thread) is a member of a group's threads.
|
||||
*/
|
||||
protected static final
|
||||
boolean checkInEventGroup(Thread currentThread, EventLoopGroup group) {
|
||||
if (group != null) {
|
||||
Set<EventExecutor> children = group.children();
|
||||
for (EventExecutor e : children) {
|
||||
if (e.inEventLoop(currentThread)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks the current thread until the endpoint has been stopped.
|
||||
*
|
||||
* @param blockUntilTerminate if TRUE, then this endpoint will block until STOP is called, otherwise it will not block
|
||||
* Blocks the current thread until the endpoint has been stopped. If the endpoint is already stopped, this do nothing.
|
||||
*/
|
||||
public final
|
||||
void waitForStop(boolean blockUntilTerminate) {
|
||||
if (blockUntilTerminate) {
|
||||
// we now BLOCK until the stop method is called.
|
||||
try {
|
||||
this.blockUntilDone.await();
|
||||
} catch (InterruptedException e) {
|
||||
this.logger.error("Thread interrupted while waiting for stop!");
|
||||
}
|
||||
void waitForShutdown() {
|
||||
// we now BLOCK until the stop method is called.
|
||||
try {
|
||||
this.blockUntilDone.await();
|
||||
} catch (InterruptedException e) {
|
||||
this.logger.error("Thread interrupted while waiting for stop!");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
String toString() {
|
||||
return "EndPoint [" + this.name + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (this.name == null ? 0 : this.name.hashCode());
|
||||
result = prime * result + (this.privateKey == null ? 0 : this.privateKey.hashCode());
|
||||
result = prime * result + (this.publicKey == null ? 0 : this.publicKey.hashCode());
|
||||
return result;
|
||||
|
@ -840,14 +766,7 @@ class EndPoint {
|
|||
return false;
|
||||
}
|
||||
EndPoint other = (EndPoint) obj;
|
||||
if (this.name == null) {
|
||||
if (other.name != null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!this.name.equals(other.name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.privateKey == null) {
|
||||
if (other.privateKey != null) {
|
||||
return false;
|
||||
|
@ -866,4 +785,15 @@ class EndPoint {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
String toString() {
|
||||
return "EndPoint [" + getName() + "]";
|
||||
}
|
||||
|
||||
public
|
||||
String getName() {
|
||||
return this.type.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,46 +1,49 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelOption;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.ConnectionOptions;
|
||||
import dorkbox.network.Client;
|
||||
import dorkbox.network.Configuration;
|
||||
import dorkbox.network.connection.bridge.ConnectionBridge;
|
||||
import dorkbox.network.connection.bridge.ConnectionBridgeFlushAlways;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* This serves the purpose of making sure that specific methods are not available to the end user.
|
||||
*/
|
||||
public class EndPointClient extends EndPoint implements Runnable {
|
||||
public
|
||||
class EndPointClient extends EndPoint implements Runnable {
|
||||
|
||||
protected List<BootstrapWrapper> bootstraps = new LinkedList<BootstrapWrapper>();
|
||||
protected AtomicInteger connectingBootstrap = new AtomicInteger(0);
|
||||
protected final Object registrationLock = new Object();
|
||||
|
||||
protected List<BootstrapWrapper> bootstraps = new LinkedList<BootstrapWrapper>();
|
||||
protected final AtomicInteger connectingBootstrap = new AtomicInteger(0);
|
||||
protected volatile int connectionTimeout = 5000; // default
|
||||
protected volatile boolean registrationComplete = false;
|
||||
|
||||
private volatile ConnectionBridgeFlushAlways connectionBridgeFlushAlways;
|
||||
|
||||
|
||||
public EndPointClient(String name, ConnectionOptions options) throws InitializationException, SecurityException {
|
||||
super(name, options);
|
||||
public
|
||||
EndPointClient(Configuration options) throws InitializationException, SecurityException, IOException {
|
||||
super(Client.class, options);
|
||||
}
|
||||
|
||||
protected void registerNextProtocol() {
|
||||
protected
|
||||
void registerNextProtocol() {
|
||||
new Thread(this, "Bootstrap registration").start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized(this.connectingBootstrap) {
|
||||
public
|
||||
void run() {
|
||||
synchronized (this.connectingBootstrap) {
|
||||
int bootstrapToRegister = this.connectingBootstrap.getAndIncrement();
|
||||
|
||||
BootstrapWrapper bootstrapWrapper = this.bootstraps.get(bootstrapToRegister);
|
||||
|
@ -60,12 +63,18 @@ public class EndPointClient extends EndPoint implements Runnable {
|
|||
future = bootstrapWrapper.bootstrap.connect();
|
||||
future.await();
|
||||
} catch (Exception e) {
|
||||
String errorMessage = stopWithErrorMessage(logger2, "Could not connect to the " + bootstrapWrapper.type + " server on port: " + bootstrapWrapper.port, e);
|
||||
String errorMessage = stopWithErrorMessage(logger2,
|
||||
"Could not connect to the " + bootstrapWrapper.type + " server on port: " +
|
||||
bootstrapWrapper.port,
|
||||
e);
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
|
||||
if (!future.isSuccess()) {
|
||||
String errorMessage = stopWithErrorMessage(logger2, "Could not connect to the " + bootstrapWrapper.type + " server on port: " + bootstrapWrapper.port, future.cause());
|
||||
String errorMessage = stopWithErrorMessage(logger2,
|
||||
"Could not connect to the " + bootstrapWrapper.type + " server on port: " +
|
||||
bootstrapWrapper.port,
|
||||
future.cause());
|
||||
throw new IllegalArgumentException(errorMessage);
|
||||
}
|
||||
|
||||
|
@ -78,11 +87,13 @@ public class EndPointClient extends EndPoint implements Runnable {
|
|||
|
||||
/**
|
||||
* Internal call by the pipeline to notify the client to continue registering the different session protocols.
|
||||
*
|
||||
* @return true if we are done registering bootstraps
|
||||
*/
|
||||
@Override
|
||||
protected boolean registerNextProtocol0() {
|
||||
synchronized(this.connectingBootstrap) {
|
||||
protected
|
||||
boolean registerNextProtocol0() {
|
||||
synchronized (this.connectingBootstrap) {
|
||||
this.registrationComplete = this.connectingBootstrap.get() == this.bootstraps.size();
|
||||
if (!this.registrationComplete) {
|
||||
registerNextProtocol();
|
||||
|
@ -104,7 +115,8 @@ public class EndPointClient extends EndPoint implements Runnable {
|
|||
* will BLOCK until it has successfully registered it's connections.
|
||||
*/
|
||||
@Override
|
||||
final void connectionConnected0(Connection connection) {
|
||||
final
|
||||
void connectionConnected0(Connection connection) {
|
||||
// invokes the listener.connection() method, and initialize the connection channels with whatever extra info they might need.
|
||||
super.connectionConnected0(connection);
|
||||
|
||||
|
@ -114,6 +126,25 @@ public class EndPointClient extends EndPoint implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose methods to send objects to a destination.
|
||||
* <p/>
|
||||
* This returns a bridge that will flush after EVERY send! This is because sending data can occur on the client, outside
|
||||
* of the normal eventloop patterns, and it is confusing to the user to have to manually flush the channel each time.
|
||||
*/
|
||||
@Override
|
||||
public
|
||||
ConnectionBridge send() {
|
||||
ConnectionBridgeFlushAlways connectionBridgeFlushAlways2 = this.connectionBridgeFlushAlways;
|
||||
if (connectionBridgeFlushAlways2 == null) {
|
||||
ConnectionBridge clientBridge = this.connectionManager.getConnection0()
|
||||
.send();
|
||||
this.connectionBridgeFlushAlways = new ConnectionBridgeFlushAlways(clientBridge);
|
||||
}
|
||||
|
||||
return this.connectionBridgeFlushAlways;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal call to abort registration if the shutdown command is issued during channel registration.
|
||||
*/
|
||||
|
@ -123,21 +154,4 @@ public class EndPointClient extends EndPoint implements Runnable {
|
|||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose methods to send objects to a destination.
|
||||
* <p>
|
||||
* This returns a bridge that will flush after EVERY send! This is because sending data can occur on the client, outside
|
||||
* of the normal eventloop patterns, and it is confusing to the user to have to manually flush the channel each time.
|
||||
*/
|
||||
@Override
|
||||
public ConnectionBridge send() {
|
||||
ConnectionBridgeFlushAlways connectionBridgeFlushAlways2 = this.connectionBridgeFlushAlways;
|
||||
if (connectionBridgeFlushAlways2 == null) {
|
||||
ConnectionBridge clientBridge = this.connectionManager.getConnection0().send();
|
||||
this.connectionBridgeFlushAlways = new ConnectionBridgeFlushAlways(clientBridge);
|
||||
}
|
||||
|
||||
return this.connectionBridgeFlushAlways;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,24 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
import dorkbox.network.ConnectionOptions;
|
||||
import dorkbox.network.Configuration;
|
||||
import dorkbox.network.Server;
|
||||
import dorkbox.network.connection.bridge.ConnectionBridgeServer;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This serves the purpose of making sure that specific methods are not available to the end user.
|
||||
*/
|
||||
public class EndPointServer extends EndPoint {
|
||||
public
|
||||
class EndPointServer extends EndPoint {
|
||||
|
||||
private ServerConnectionBridge serverConnections;
|
||||
private final ServerConnectionBridge serverConnections;
|
||||
|
||||
public EndPointServer(String name, ConnectionOptions options) throws InitializationException, SecurityException {
|
||||
super(name, options);
|
||||
public
|
||||
EndPointServer(Configuration options) throws InitializationException, SecurityException, IOException {
|
||||
super(Server.class, options);
|
||||
|
||||
this.serverConnections = new ServerConnectionBridge(this.connectionManager);
|
||||
}
|
||||
|
@ -22,7 +27,8 @@ public class EndPointServer extends EndPoint {
|
|||
* Expose methods to send objects to a destination.
|
||||
*/
|
||||
@Override
|
||||
public ConnectionBridgeServer send() {
|
||||
public
|
||||
ConnectionBridgeServer send() {
|
||||
return this.serverConnections;
|
||||
}
|
||||
|
||||
|
@ -35,7 +41,8 @@ public class EndPointServer extends EndPoint {
|
|||
*
|
||||
* @return a newly created listener manager for the connection
|
||||
*/
|
||||
final ConnectionManager addListenerManager(Connection connection) {
|
||||
final
|
||||
ConnectionManager addListenerManager(Connection connection) {
|
||||
return this.connectionManager.addListenerManager(connection);
|
||||
}
|
||||
|
||||
|
@ -45,10 +52,11 @@ public class EndPointServer extends EndPoint {
|
|||
* <br>
|
||||
* It is POSSIBLE to remove a server-connection 'local' listener (via connection.removeListener), meaning that ONLY
|
||||
* that listener attached to the connection is removed
|
||||
*
|
||||
* <p/>
|
||||
* This removes the listener manager for that specific connection
|
||||
*/
|
||||
final void removeListenerManager(Connection connection) {
|
||||
final
|
||||
void removeListenerManager(Connection connection) {
|
||||
this.connectionManager.removeListenerManager(connection);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
167
Dorkbox-Network/src/dorkbox/network/connection/KryoExtra.java
Normal file
167
Dorkbox-Network/src/dorkbox/network/connection/KryoExtra.java
Normal file
|
@ -0,0 +1,167 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.Serializer;
|
||||
import com.esotericsoftware.kryo.util.Util;
|
||||
import com.esotericsoftware.reflectasm.MethodAccess;
|
||||
import dorkbox.network.pipeline.ByteBufInput;
|
||||
import dorkbox.network.pipeline.ByteBufOutput;
|
||||
import dorkbox.network.rmi.AsmCachedMethod;
|
||||
import dorkbox.network.rmi.CachedMethod;
|
||||
import dorkbox.util.crypto.bouncycastle.GCMBlockCipher_ByteBuf;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.handler.codec.compression.SnappyAccess;
|
||||
import org.bouncycastle.crypto.engines.AESFastEngine;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.zip.Deflater;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
public
|
||||
class KryoExtra extends Kryo {
|
||||
final ByteBufOutput output;
|
||||
final ByteBufInput input;
|
||||
|
||||
final Inflater inflater;
|
||||
final Deflater deflater;
|
||||
final SnappyAccess snappy;
|
||||
|
||||
final ByteBuf tmpBuffer1;
|
||||
final ByteBuf tmpBuffer2;
|
||||
final GCMBlockCipher_ByteBuf aesEngine;
|
||||
|
||||
private final Map<Class<?>, CachedMethod[]> methodCache = new ConcurrentHashMap<Class<?>, CachedMethod[]>();
|
||||
private final boolean asmEnabled = !KryoCryptoSerializationManager.useUnsafeMemory;
|
||||
|
||||
// not thread safe
|
||||
public ConnectionImpl connection;
|
||||
|
||||
|
||||
public
|
||||
KryoExtra() {
|
||||
this.snappy = new SnappyAccess();
|
||||
this.deflater = new Deflater(KryoCryptoSerializationManager.compressionLevel, true);
|
||||
this.inflater = new Inflater(true);
|
||||
|
||||
this.input = new ByteBufInput();
|
||||
this.output = new ByteBufOutput();
|
||||
|
||||
this.tmpBuffer1 = Unpooled.buffer(1024);
|
||||
this.tmpBuffer2 = Unpooled.buffer(1024);
|
||||
this.aesEngine = new GCMBlockCipher_ByteBuf(new AESFastEngine());
|
||||
}
|
||||
|
||||
public
|
||||
CachedMethod[] getMethods(Class<?> type) {
|
||||
CachedMethod[] cachedMethods = this.methodCache.get(type); // Maybe should cache per Kryo instance?
|
||||
if (cachedMethods != null) {
|
||||
return cachedMethods;
|
||||
}
|
||||
|
||||
ArrayList<Method> allMethods = new ArrayList<Method>();
|
||||
|
||||
Class<?> nextClass = type;
|
||||
while (nextClass != null) {
|
||||
Collections.addAll(allMethods, nextClass.getDeclaredMethods());
|
||||
nextClass = nextClass.getSuperclass();
|
||||
if (nextClass == Object.class) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Method> methods = new ArrayList<Method>(Math.max(1, allMethods.size()));
|
||||
for (int i = 0, n = allMethods.size(); i < n; i++) {
|
||||
Method method = allMethods.get(i);
|
||||
int modifiers = method.getModifiers();
|
||||
if (Modifier.isStatic(modifiers)) {
|
||||
continue;
|
||||
}
|
||||
if (Modifier.isPrivate(modifiers)) {
|
||||
continue;
|
||||
}
|
||||
if (method.isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
methods.add(method);
|
||||
}
|
||||
Collections.sort(methods, new Comparator<Method>() {
|
||||
@Override
|
||||
public
|
||||
int compare(Method o1, Method o2) {
|
||||
// Methods are sorted so they can be represented as an index.
|
||||
int diff = o1.getName()
|
||||
.compareTo(o2.getName());
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
Class<?>[] argTypes1 = o1.getParameterTypes();
|
||||
Class<?>[] argTypes2 = o2.getParameterTypes();
|
||||
if (argTypes1.length > argTypes2.length) {
|
||||
return 1;
|
||||
}
|
||||
if (argTypes1.length < argTypes2.length) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < argTypes1.length; i++) {
|
||||
diff = argTypes1[i].getName()
|
||||
.compareTo(argTypes2[i].getName());
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Two methods with same signature!"); // Impossible.
|
||||
}
|
||||
});
|
||||
|
||||
Object methodAccess = null;
|
||||
if (asmEnabled && !Util.isAndroid && Modifier.isPublic(type.getModifiers())) {
|
||||
methodAccess = MethodAccess.get(type);
|
||||
}
|
||||
|
||||
|
||||
int n = methods.size();
|
||||
cachedMethods = new CachedMethod[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
Method method = methods.get(i);
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
|
||||
CachedMethod cachedMethod = null;
|
||||
if (methodAccess != null) {
|
||||
try {
|
||||
AsmCachedMethod asmCachedMethod = new AsmCachedMethod();
|
||||
asmCachedMethod.methodAccessIndex = ((MethodAccess) methodAccess).getIndex(method.getName(), parameterTypes);
|
||||
asmCachedMethod.methodAccess = (MethodAccess) methodAccess;
|
||||
cachedMethod = asmCachedMethod;
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedMethod == null) {
|
||||
cachedMethod = new CachedMethod();
|
||||
}
|
||||
cachedMethod.method = method;
|
||||
cachedMethod.methodClassID = getRegistration(method.getDeclaringClass()).getId();
|
||||
cachedMethod.methodIndex = i;
|
||||
|
||||
// Store the serializer for each final parameter.
|
||||
cachedMethod.serializers = new Serializer<?>[parameterTypes.length];
|
||||
for (int ii = 0, nn = parameterTypes.length; ii < nn; ii++) {
|
||||
if (isFinal(parameterTypes[ii])) {
|
||||
cachedMethod.serializers[ii] = getSerializer(parameterTypes[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
cachedMethods[i] = cachedMethod;
|
||||
}
|
||||
|
||||
this.methodCache.put(type, cachedMethods);
|
||||
return cachedMethods;
|
||||
}
|
||||
}
|
|
@ -1,197 +1,203 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
|
||||
import java.io.File;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import dorkbox.network.util.store.SettingsStore;
|
||||
import dorkbox.util.SerializationManager;
|
||||
import dorkbox.util.bytes.ByteArrayWrapper;
|
||||
import dorkbox.util.database.DB_Server;
|
||||
import dorkbox.util.database.DatabaseStorage;
|
||||
import dorkbox.util.storage.Storage;
|
||||
import dorkbox.util.storage.Store;
|
||||
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
|
||||
import dorkbox.network.util.store.SettingsStore;
|
||||
import dorkbox.util.FileUtil;
|
||||
import dorkbox.util.bytes.ByteArrayWrapper;
|
||||
import dorkbox.util.storage.Storage;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The property store is the DEFAULT type of store for the network stack.
|
||||
* This is package private.
|
||||
*/
|
||||
public
|
||||
class PropertyStore extends SettingsStore {
|
||||
|
||||
private static class Props {
|
||||
private volatile ECPrivateKeyParameters serverPrivateKey = null;
|
||||
private volatile ECPublicKeyParameters serverPublicKey = null;
|
||||
private volatile byte[] salt = null;
|
||||
protected Storage storage;
|
||||
protected Map<ByteArrayWrapper, DB_Server> servers;
|
||||
|
||||
private volatile Map<ByteArrayWrapper, ECPublicKeyParameters> registeredServer = new HashMap<ByteArrayWrapper, ECPublicKeyParameters>(16);
|
||||
|
||||
private Props() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + (this.serverPrivateKey == null ? 0 : this.serverPrivateKey.getD().hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null) {
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
Props other = (Props) obj;
|
||||
if (this.serverPrivateKey == null) {
|
||||
if (other.serverPrivateKey != null) {
|
||||
return false;
|
||||
}
|
||||
} else if (!this.serverPrivateKey.getD().equals(other.serverPrivateKey.getD())) {
|
||||
return false;
|
||||
} else if (!this.serverPrivateKey.getParameters().getCurve().equals(other.serverPrivateKey.getParameters().getCurve())) {
|
||||
return false;
|
||||
} else if (!this.serverPrivateKey.getParameters().getG().equals(other.serverPrivateKey.getParameters().getG())) {
|
||||
return false;
|
||||
} else if (!this.serverPrivateKey.getParameters().getH().equals(other.serverPrivateKey.getParameters().getH())) {
|
||||
return false;
|
||||
} else if (!this.serverPrivateKey.getParameters().getN().equals(other.serverPrivateKey.getParameters().getN())) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public
|
||||
PropertyStore() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Method of preference for creating/getting this connection store. package only since only the ConnectionStoreProxy calls this
|
||||
*
|
||||
* @param type this is either "Client" or "Server", depending on who is creating this endpoint.
|
||||
* @param serializationManager this is the serialization used for saving objects into the storage database
|
||||
*/
|
||||
@Override
|
||||
public
|
||||
void init(Class<? extends EndPoint> type, final SerializationManager serializationManager, Storage storage) throws IOException {
|
||||
// make sure our custom types are registered
|
||||
serializationManager.register(HashMap.class);
|
||||
serializationManager.register(ByteArrayWrapper.class);
|
||||
serializationManager.register(DB_Server.class);
|
||||
|
||||
// the name of the file that contains the saved properties
|
||||
private static final String SETTINGS_FILE_NAME = "settings.db";
|
||||
if (storage == null) {
|
||||
this.storage = Store.Memory()
|
||||
.make();
|
||||
}
|
||||
else {
|
||||
this.storage = storage;
|
||||
}
|
||||
|
||||
private String name;
|
||||
private final Storage storage;
|
||||
private Props props = new Props();
|
||||
servers = this.storage.load(DatabaseStorage.SERVERS, new HashMap<ByteArrayWrapper, DB_Server>(16));
|
||||
|
||||
//use map to keep track of recid, so we can get record info during restarts.
|
||||
DB_Server localServer = servers.get(DB_Server.IP_0_0_0_0);
|
||||
if (localServer == null) {
|
||||
localServer = new DB_Server();
|
||||
servers.put(DB_Server.IP_0_0_0_0, localServer);
|
||||
|
||||
// Method of preference for creating/getting this connection store. Private since only the ConnectionStoreProxy calls this
|
||||
public PropertyStore(String name) {
|
||||
this.name = name;
|
||||
|
||||
// DEFAULT location! (if it's for testing, etc)
|
||||
File propertiesFile = new File(SETTINGS_FILE_NAME);
|
||||
propertiesFile = FileUtil.normalize(propertiesFile);
|
||||
|
||||
propertiesFile = propertiesFile.getAbsoluteFile();
|
||||
|
||||
// loads the saved data into the props
|
||||
this.storage = Storage.open(propertiesFile);
|
||||
this.storage.load(name, this.props);
|
||||
// have to always specify what we are saving
|
||||
this.storage.commit(DatabaseStorage.SERVERS, servers);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple, property based method to getting the private key of the server
|
||||
*/
|
||||
@Override
|
||||
public synchronized ECPrivateKeyParameters getPrivateKey() throws dorkbox.network.util.exceptions.SecurityException {
|
||||
public synchronized
|
||||
ECPrivateKeyParameters getPrivateKey() throws dorkbox.network.util.exceptions.SecurityException {
|
||||
checkAccess(EndPoint.class);
|
||||
|
||||
return this.props.serverPrivateKey;
|
||||
return servers.get(DB_Server.IP_0_0_0_0)
|
||||
.getPrivateKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple, property based method for saving the private key of the server
|
||||
*/
|
||||
@Override
|
||||
public synchronized void savePrivateKey(ECPrivateKeyParameters serverPrivateKey) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
public synchronized
|
||||
void savePrivateKey(ECPrivateKeyParameters serverPrivateKey) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
checkAccess(EndPoint.class);
|
||||
|
||||
this.props.serverPrivateKey = serverPrivateKey;
|
||||
servers.get(DB_Server.IP_0_0_0_0)
|
||||
.setPrivateKey(serverPrivateKey);
|
||||
|
||||
this.storage.save(this.name);
|
||||
// have to always specify what we are saving
|
||||
storage.commit(DatabaseStorage.SERVERS, servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple, property based method to getting the public key of the server
|
||||
*/
|
||||
@Override
|
||||
public synchronized ECPublicKeyParameters getPublicKey() throws dorkbox.network.util.exceptions.SecurityException {
|
||||
public synchronized
|
||||
ECPublicKeyParameters getPublicKey() throws dorkbox.network.util.exceptions.SecurityException {
|
||||
checkAccess(EndPoint.class);
|
||||
|
||||
return this.props.serverPublicKey;
|
||||
return servers.get(DB_Server.IP_0_0_0_0)
|
||||
.getPublicKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple, property based method for saving the public key of the server
|
||||
*/
|
||||
@Override
|
||||
public synchronized void savePublicKey(ECPublicKeyParameters serverPublicKey) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
public synchronized
|
||||
void savePublicKey(ECPublicKeyParameters serverPublicKey) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
checkAccess(EndPoint.class);
|
||||
|
||||
this.props.serverPublicKey = serverPublicKey;
|
||||
this.storage.save(this.name, this.props);
|
||||
servers.get(DB_Server.IP_0_0_0_0)
|
||||
.setPublicKey(serverPublicKey);
|
||||
|
||||
// have to always specify what we are saving
|
||||
storage.commit(DatabaseStorage.SERVERS, servers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple, property based method to getting the server salt
|
||||
*/
|
||||
@Override
|
||||
public synchronized byte[] getSalt() {
|
||||
public synchronized
|
||||
byte[] getSalt() {
|
||||
final DB_Server localServer = servers.get(DB_Server.IP_0_0_0_0);
|
||||
byte[] salt = localServer.getSalt();
|
||||
|
||||
// we don't care who gets the server salt
|
||||
if (this.props.salt == null) {
|
||||
if (salt == null) {
|
||||
SecureRandom secureRandom = new SecureRandom();
|
||||
|
||||
// server salt is used to salt usernames and other various connection handshake parameters
|
||||
this.props.salt = new byte[256];
|
||||
secureRandom.nextBytes(this.props.salt);
|
||||
byte[] bytes = new byte[256];
|
||||
secureRandom.nextBytes(bytes);
|
||||
|
||||
this.storage.save(this.name, this.props);
|
||||
return this.props.salt;
|
||||
salt = bytes;
|
||||
|
||||
localServer.setSalt(bytes);
|
||||
|
||||
// have to always specify what we are saving
|
||||
storage.commit(DatabaseStorage.SERVERS, servers);
|
||||
}
|
||||
|
||||
return this.props.salt;
|
||||
return salt;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple, property based method to getting a connected computer by host IP address
|
||||
*/
|
||||
@Override
|
||||
public synchronized ECPublicKeyParameters getRegisteredServerKey(byte[] hostAddress) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
public synchronized
|
||||
ECPublicKeyParameters getRegisteredServerKey(byte[] hostAddress) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
checkAccess(RegistrationWrapper.class);
|
||||
|
||||
return this.props.registeredServer.get(ByteArrayWrapper.wrap(hostAddress));
|
||||
final DB_Server db_server = this.servers.get(ByteArrayWrapper.wrap(hostAddress));
|
||||
if (db_server != null) {
|
||||
return db_server.getPublicKey();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a connected computer by host IP address and public key
|
||||
*/
|
||||
@Override
|
||||
public synchronized void addRegisteredServerKey(byte[] hostAddress, ECPublicKeyParameters publicKey) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
public synchronized
|
||||
void addRegisteredServerKey(byte[] hostAddress, ECPublicKeyParameters publicKey)
|
||||
throws dorkbox.network.util.exceptions.SecurityException {
|
||||
checkAccess(RegistrationWrapper.class);
|
||||
|
||||
this.props.registeredServer.put(ByteArrayWrapper.wrap(hostAddress), publicKey);
|
||||
this.storage.save(this.name, this.props);
|
||||
final ByteArrayWrapper wrap = ByteArrayWrapper.wrap(hostAddress);
|
||||
DB_Server db_server = this.servers.get(wrap);
|
||||
if (db_server == null) {
|
||||
db_server = new DB_Server();
|
||||
}
|
||||
|
||||
db_server.setPublicKey(publicKey);
|
||||
servers.put(wrap, db_server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a registered computer by host IP address
|
||||
*/
|
||||
@Override
|
||||
public synchronized boolean removeRegisteredServerKey(byte[] hostAddress) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
public synchronized
|
||||
boolean removeRegisteredServerKey(byte[] hostAddress) throws dorkbox.network.util.exceptions.SecurityException {
|
||||
checkAccess(RegistrationWrapper.class);
|
||||
|
||||
ECPublicKeyParameters remove = this.props.registeredServer.remove(ByteArrayWrapper.wrap(hostAddress));
|
||||
this.storage.save(this.name, this.props);
|
||||
final ByteArrayWrapper wrap = ByteArrayWrapper.wrap(hostAddress);
|
||||
DB_Server db_server = this.servers.remove(wrap);
|
||||
|
||||
return remove != null;
|
||||
return db_server != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
Storage.close(this.storage);
|
||||
public
|
||||
void shutdown() {
|
||||
Store.close(storage);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
|
||||
import dorkbox.network.rmi.RmiRegistration;
|
||||
|
||||
class RegisterRmiSystemListener extends ListenerRaw<ConnectionImpl, RmiRegistration> {
|
||||
|
||||
RegisterRmiSystemListener() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void received(ConnectionImpl connection, RmiRegistration registerClass) {
|
||||
// register this into the RmiBridge
|
||||
connection.registerInternal(connection, registerClass);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,16 @@
|
|||
package dorkbox.network.connection;
|
||||
|
||||
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.pipeline.KryoEncoder;
|
||||
import dorkbox.network.pipeline.KryoEncoderCrypto;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.crypto.Crypto;
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
@ -9,30 +19,20 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import org.bouncycastle.crypto.CipherParameters;
|
||||
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.pipeline.KryoEncoder;
|
||||
import dorkbox.network.pipeline.KryoEncoderCrypto;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.crypto.Crypto;
|
||||
|
||||
/**
|
||||
* Just wraps common/needed methods of the client/server endpoint by the registration stage/handshake.
|
||||
*
|
||||
* <p/>
|
||||
* This is in the connection package, so it can access the endpoint methods that it needs to.
|
||||
*/
|
||||
public class RegistrationWrapper implements UdpServer {
|
||||
public
|
||||
class RegistrationWrapper implements UdpServer {
|
||||
private final org.slf4j.Logger logger;
|
||||
|
||||
private final EndPoint endPoint;
|
||||
|
||||
// keeps track of connections (TCP/UDT/UDP-client)
|
||||
private final ReentrantLock channelMapLock = new ReentrantLock();
|
||||
private IntMap<MetaChannel> channelMap = new IntMap<MetaChannel>();
|
||||
private final IntMap<MetaChannel> channelMap = new IntMap<MetaChannel>();
|
||||
|
||||
// keeps track of connections (UDP-server)
|
||||
// this is final, because the REFERENCE to these will never change. They ARE NOT immutable objects (meaning their content can change)
|
||||
|
@ -41,66 +41,78 @@ public class RegistrationWrapper implements UdpServer {
|
|||
private KryoEncoder kryoTcpEncoder;
|
||||
private KryoEncoderCrypto kryoTcpCryptoEncoder;
|
||||
|
||||
public RegistrationWrapper(EndPoint endPoint, Logger logger) {
|
||||
public
|
||||
RegistrationWrapper(final EndPoint endPoint, final Logger logger) {
|
||||
this.endPoint = endPoint;
|
||||
this.logger = logger;
|
||||
|
||||
if (endPoint instanceof EndPointServer) {
|
||||
this.udpRemoteMap = new ConcurrentHashMap<InetSocketAddress, ConnectionImpl>();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
this.udpRemoteMap = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setKryoTcpEncoder(KryoEncoder kryoTcpEncoder) {
|
||||
public
|
||||
void setKryoTcpEncoder(KryoEncoder kryoTcpEncoder) {
|
||||
this.kryoTcpEncoder = kryoTcpEncoder;
|
||||
}
|
||||
|
||||
public void setKryoTcpCryptoEncoder(KryoEncoderCrypto kryoTcpCryptoEncoder) {
|
||||
public
|
||||
void setKryoTcpCryptoEncoder(KryoEncoderCrypto kryoTcpCryptoEncoder) {
|
||||
this.kryoTcpCryptoEncoder = kryoTcpCryptoEncoder;
|
||||
}
|
||||
|
||||
public KryoEncoder getKryoTcpEncoder() {
|
||||
public
|
||||
KryoEncoder getKryoTcpEncoder() {
|
||||
return this.kryoTcpEncoder;
|
||||
}
|
||||
|
||||
public KryoEncoderCrypto getKryoTcpCryptoEncoder() {
|
||||
public
|
||||
KryoEncoderCrypto getKryoTcpCryptoEncoder() {
|
||||
return this.kryoTcpCryptoEncoder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks, and then returns the channelMap used by the registration process.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Make SURE to use this in a try/finally block with releaseChannelMap in the finally block!
|
||||
*/
|
||||
public IntMap<MetaChannel> getAndLockChannelMap() {
|
||||
public
|
||||
IntMap<MetaChannel> getAndLockChannelMap() {
|
||||
// try to lock access
|
||||
this.channelMapLock.lock();
|
||||
|
||||
// guarantee that the contents of this map are visible across threads
|
||||
synchronized (this.channelMap) {}
|
||||
synchronized (this.channelMap) {
|
||||
}
|
||||
return this.channelMap;
|
||||
}
|
||||
|
||||
public void releaseChannelMap() {
|
||||
public
|
||||
void releaseChannelMap() {
|
||||
// try to unlock access
|
||||
this.channelMapLock.unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of milli-seconds that must elapse with no read or write before {@link Listener:idle()}
|
||||
* will be triggered
|
||||
* The amount of milli-seconds that must elapse with no read or write before {@link Listener:idle()}
|
||||
* will be triggered
|
||||
*/
|
||||
public int getIdleTimeout() {
|
||||
public
|
||||
int getIdleTimeout() {
|
||||
return this.endPoint.getIdleTimeout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal call by the pipeline to notify the client to continue registering the different session protocols.
|
||||
* The server does not use this.
|
||||
*
|
||||
* @return true if we are done registering bootstraps
|
||||
*/
|
||||
public boolean registerNextProtocol0() {
|
||||
public
|
||||
boolean registerNextProtocol0() {
|
||||
return this.endPoint.registerNextProtocol0();
|
||||
}
|
||||
|
||||
|
@ -108,7 +120,8 @@ public class RegistrationWrapper implements UdpServer {
|
|||
* Internal call by the pipeline to notify the "Connection" object that it has "connected", meaning that modifications
|
||||
* to the pipeline are finished.
|
||||
*/
|
||||
public void connectionConnected0(Connection networkConnection) {
|
||||
public
|
||||
void connectionConnected0(Connection networkConnection) {
|
||||
this.endPoint.connectionConnected0(networkConnection);
|
||||
}
|
||||
|
||||
|
@ -119,23 +132,28 @@ public class RegistrationWrapper implements UdpServer {
|
|||
*
|
||||
* @param metaChannel can be NULL (when getting the baseClass)
|
||||
*/
|
||||
public Connection connection0(MetaChannel metaChannel) {
|
||||
public
|
||||
Connection connection0(MetaChannel metaChannel) {
|
||||
return this.endPoint.connection0(metaChannel);
|
||||
}
|
||||
|
||||
public SecureRandom getSecureRandom() {
|
||||
public
|
||||
SecureRandom getSecureRandom() {
|
||||
return this.endPoint.secureRandom;
|
||||
}
|
||||
|
||||
public ECPublicKeyParameters getPublicKey() {
|
||||
public
|
||||
ECPublicKeyParameters getPublicKey() {
|
||||
return this.endPoint.publicKey;
|
||||
}
|
||||
|
||||
public CipherParameters getPrivateKey() {
|
||||
public
|
||||
CipherParameters getPrivateKey() {
|
||||
return this.endPoint.privateKey;
|
||||
}
|
||||
|
||||
public boolean validateRemoteServerAddress(InetSocketAddress tcpRemoteServer, ECPublicKeyParameters publicKey) throws SecurityException {
|
||||
public
|
||||
boolean validateRemoteServerAddress(InetSocketAddress tcpRemoteServer, ECPublicKeyParameters publicKey) throws SecurityException {
|
||||
if (this.endPoint.disableRemoteKeyValidation) {
|
||||
return true;
|
||||
}
|
||||
|
@ -150,18 +168,21 @@ public class RegistrationWrapper implements UdpServer {
|
|||
logger2.debug("Adding new remote IP address key for {}", address.getHostAddress());
|
||||
}
|
||||
this.endPoint.propertyStore.addRegisteredServerKey(hostAddress, publicKey);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// COMPARE!
|
||||
if (!Crypto.ECC.compare(publicKey, savedPublicKey)) {
|
||||
String byAddress;
|
||||
try {
|
||||
byAddress = InetAddress.getByAddress(hostAddress).getHostAddress();
|
||||
byAddress = InetAddress.getByAddress(hostAddress)
|
||||
.getHostAddress();
|
||||
} catch (UnknownHostException e) {
|
||||
byAddress = "Unknown";
|
||||
}
|
||||
|
||||
//whoa! abort since something messed up!
|
||||
logger2.error("Invalid or non-matching public key from remote server. Their public key has changed. To fix, remove entry for: {}", byAddress);
|
||||
logger2.error("Invalid or non-matching public key from remote server. Their public key has changed. To fix, remove entry for: {}",
|
||||
byAddress);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -169,12 +190,17 @@ public class RegistrationWrapper implements UdpServer {
|
|||
return true;
|
||||
}
|
||||
|
||||
public void removeRegisteredServerKey(byte[] hostAddress) throws SecurityException {
|
||||
public
|
||||
void removeRegisteredServerKey(byte[] hostAddress) throws SecurityException {
|
||||
ECPublicKeyParameters savedPublicKey = this.endPoint.propertyStore.getRegisteredServerKey(hostAddress);
|
||||
if (savedPublicKey != null) {
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isDebugEnabled()) {
|
||||
logger2.debug("Deleteing remote IP address key {}.{}.{}.{}", hostAddress[0], hostAddress[1], hostAddress[2], hostAddress[3]);
|
||||
logger2.debug("Deleteing remote IP address key {}.{}.{}.{}",
|
||||
hostAddress[0],
|
||||
hostAddress[1],
|
||||
hostAddress[2],
|
||||
hostAddress[3]);
|
||||
}
|
||||
this.endPoint.propertyStore.removeRegisteredServerKey(hostAddress);
|
||||
}
|
||||
|
@ -186,15 +212,17 @@ public class RegistrationWrapper implements UdpServer {
|
|||
* Only called if we have a UDP channel
|
||||
*/
|
||||
@Override
|
||||
public final void registerServerUDP(MetaChannel metaChannel) {
|
||||
public final
|
||||
void registerServerUDP(MetaChannel metaChannel) {
|
||||
if (metaChannel != null && metaChannel.udpRemoteAddress != null) {
|
||||
this.udpRemoteMap.put(metaChannel.udpRemoteAddress, (ConnectionImpl) metaChannel.connection);
|
||||
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isDebugEnabled()) {
|
||||
logger2.debug("Connected to remote UDP connection. [{} <== {}]",
|
||||
metaChannel.udpChannel.localAddress().toString(),
|
||||
metaChannel.udpRemoteAddress.toString());
|
||||
metaChannel.udpChannel.localAddress()
|
||||
.toString(),
|
||||
metaChannel.udpRemoteAddress.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -204,7 +232,8 @@ public class RegistrationWrapper implements UdpServer {
|
|||
* Called when closing a connection.
|
||||
*/
|
||||
@Override
|
||||
public final void unRegisterServerUDP(InetSocketAddress udpRemoteAddress) {
|
||||
public final
|
||||
void unRegisterServerUDP(InetSocketAddress udpRemoteAddress) {
|
||||
if (udpRemoteAddress != null) {
|
||||
this.udpRemoteMap.remove(udpRemoteAddress);
|
||||
Logger logger2 = this.logger;
|
||||
|
@ -218,17 +247,20 @@ public class RegistrationWrapper implements UdpServer {
|
|||
* ONLY SERVER SIDE CALLS THIS
|
||||
*/
|
||||
@Override
|
||||
public ConnectionImpl getServerUDP(InetSocketAddress udpRemoteAddress) {
|
||||
public
|
||||
ConnectionImpl getServerUDP(InetSocketAddress udpRemoteAddress) {
|
||||
if (udpRemoteAddress != null) {
|
||||
return this.udpRemoteMap.get(udpRemoteAddress);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void abortRegistrationIfClient() {
|
||||
public
|
||||
void abortRegistrationIfClient() {
|
||||
if (this.endPoint instanceof EndPointClient) {
|
||||
((EndPointClient)this.endPoint).abortRegistration();
|
||||
((EndPointClient) this.endPoint).abortRegistration();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
package dorkbox.network.connection.idle;
|
||||
|
||||
|
||||
public interface IdleBridge {
|
||||
public
|
||||
interface IdleBridge {
|
||||
/**
|
||||
* Sends the object over the network using TCP (or via LOCAL when it's a
|
||||
* local channel) when the socket is in an "idle" state.
|
||||
*/
|
||||
public void TCP();
|
||||
void TCP();
|
||||
|
||||
/**
|
||||
* Sends the object over the network using UDP when the socket is in an
|
||||
* "idle" state.
|
||||
*/
|
||||
public void UDP();
|
||||
void UDP();
|
||||
|
||||
/**
|
||||
* Sends the object over the network using UDT (or via LOCAL when it's a
|
||||
* local channel) when the socket is in an "idle" state.
|
||||
*/
|
||||
public void UDT();
|
||||
void UDT();
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package dorkbox.network.connection.idle;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
|
@ -6,12 +5,14 @@ import dorkbox.network.connection.ListenerRaw;
|
|||
import dorkbox.network.util.exceptions.NetException;
|
||||
|
||||
|
||||
abstract public class IdleSender<C extends Connection, M> extends ListenerRaw<C, M> implements IdleBridge {
|
||||
public abstract
|
||||
class IdleSender<C extends Connection, M> extends ListenerRaw<C, M> implements IdleBridge {
|
||||
volatile boolean started;
|
||||
IdleListener<C, M> idleListener;
|
||||
|
||||
@Override
|
||||
public void idle(C connection) {
|
||||
public
|
||||
void idle(C connection) {
|
||||
if (!this.started) {
|
||||
this.started = true;
|
||||
start();
|
||||
|
@ -19,38 +20,50 @@ abstract public class IdleSender<C extends Connection, M> extends ListenerRaw<C,
|
|||
|
||||
M message = next();
|
||||
if (message == null) {
|
||||
connection.listeners().remove(this);
|
||||
} else {
|
||||
connection.listeners()
|
||||
.remove(this);
|
||||
}
|
||||
else {
|
||||
if (this.idleListener != null) {
|
||||
this.idleListener.send(connection, message);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
throw new NetException("Invalid idle listener. Please specify .TCP(), .UDP(), or .UDT()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void TCP() {
|
||||
public
|
||||
void TCP() {
|
||||
this.idleListener = new IdleListenerTCP<C, M>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void UDP() {
|
||||
public
|
||||
void UDP() {
|
||||
this.idleListener = new IdleListenerUDP<C, M>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void UDT() {
|
||||
public
|
||||
void UDT() {
|
||||
this.idleListener = new IdleListenerUDT<C, M>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Called once, before the first send. Subclasses can override this method to send something so the receiving side expects
|
||||
* subsequent objects. */
|
||||
protected void start () {
|
||||
/**
|
||||
* Called once, before the first send. Subclasses can override this method to send something so the receiving side expects
|
||||
* subsequent objects.
|
||||
*/
|
||||
protected
|
||||
void start() {
|
||||
}
|
||||
|
||||
/** Returns the next object to send, or null if no more objects will be sent. */
|
||||
abstract protected M next ();
|
||||
/**
|
||||
* Returns the next object to send, or null if no more objects will be sent.
|
||||
*/
|
||||
protected abstract
|
||||
M next();
|
||||
}
|
||||
|
|
|
@ -1,11 +1,5 @@
|
|||
package dorkbox.network.connection.registration.local;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.connection.ConnectionImpl;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
|
@ -13,6 +7,10 @@ import dorkbox.network.connection.registration.MetaChannel;
|
|||
import dorkbox.network.connection.registration.RegistrationHandler;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.collections.IntMap.Entries;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public abstract class RegistrationLocalHandler extends RegistrationHandler {
|
||||
|
||||
|
@ -52,18 +50,16 @@ public abstract class RegistrationLocalHandler extends RegistrationHandler {
|
|||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
Channel channel = context.channel();
|
||||
Channel channel = context.channel();
|
||||
|
||||
StringBuilder builder = new StringBuilder(76);
|
||||
builder.append("Connected to LOCAL connection. [");
|
||||
builder.append(context.channel().localAddress());
|
||||
builder.append(getConnectionDirection());
|
||||
builder.append(channel.remoteAddress());
|
||||
builder.append("]");
|
||||
StringBuilder builder = new StringBuilder(76);
|
||||
builder.append("Connected to LOCAL connection. [");
|
||||
builder.append(context.channel().localAddress());
|
||||
builder.append(getConnectionDirection());
|
||||
builder.append(channel.remoteAddress());
|
||||
builder.append("]");
|
||||
|
||||
this.logger.debug(builder.toString());
|
||||
}
|
||||
this.logger.debug(builder.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,17 +1,19 @@
|
|||
package dorkbox.network.connection.registration.local;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
public class RegistrationLocalHandlerClient extends RegistrationLocalHandler {
|
||||
public
|
||||
class RegistrationLocalHandlerClient extends RegistrationLocalHandler {
|
||||
|
||||
public RegistrationLocalHandlerClient(String name, RegistrationWrapper registrationWrapper) {
|
||||
public
|
||||
RegistrationLocalHandlerClient(String name, RegistrationWrapper registrationWrapper) {
|
||||
super(name, registrationWrapper);
|
||||
}
|
||||
|
||||
|
@ -27,11 +29,13 @@ public class RegistrationLocalHandlerClient extends RegistrationLocalHandler {
|
|||
* STEP 2: Channel is now active. Start the registration process
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
public
|
||||
void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
super.channelActive(context);
|
||||
}
|
||||
|
||||
// client starts the registration process
|
||||
Channel channel = context.channel();
|
||||
channel.writeAndFlush(new Registration());
|
||||
}
|
||||
|
@ -40,12 +44,14 @@ public class RegistrationLocalHandlerClient extends RegistrationLocalHandler {
|
|||
* @return the direction that traffic is going to this handler (" <== " or " ==> ")
|
||||
*/
|
||||
@Override
|
||||
protected String getConnectionDirection() {
|
||||
protected
|
||||
String getConnectionDirection() {
|
||||
return " ==> ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
public
|
||||
void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
ReferenceCountUtil.release(message);
|
||||
|
||||
Channel channel = context.channel();
|
||||
|
@ -60,14 +66,16 @@ public class RegistrationLocalHandlerClient extends RegistrationLocalHandler {
|
|||
|
||||
// have to setup new listeners
|
||||
if (metaChannel != null) {
|
||||
channel.pipeline().remove(this);
|
||||
channel.pipeline()
|
||||
.remove(this);
|
||||
|
||||
// Event though a local channel is XOR with everything else, we still have to make the client clean up it's state.
|
||||
registrationWrapper.registerNextProtocol0();
|
||||
|
||||
Connection connection = metaChannel.connection;
|
||||
registrationWrapper.connectionConnected0(connection);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// this should NEVER happen!
|
||||
logger.error("Error registering LOCAL channel! MetaChannel is null!");
|
||||
shutdown(registrationWrapper, channel);
|
||||
|
|
|
@ -1,25 +1,54 @@
|
|||
package dorkbox.network.connection.registration.local;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class RegistrationLocalHandlerServer extends RegistrationLocalHandler {
|
||||
public
|
||||
class RegistrationLocalHandlerServer extends RegistrationLocalHandler {
|
||||
|
||||
public RegistrationLocalHandlerServer(String name, RegistrationWrapper registrationWrapper) {
|
||||
public
|
||||
RegistrationLocalHandlerServer(String name, RegistrationWrapper registrationWrapper) {
|
||||
super(name, registrationWrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 1: Channel is first created
|
||||
*/
|
||||
// Calls the super class to init the local channel
|
||||
// @Override
|
||||
// protected void initChannel(Channel channel) {
|
||||
// }
|
||||
|
||||
/**
|
||||
* STEP 2: Channel is now active. Start the registration process (Client starts the process)
|
||||
*/
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
public
|
||||
void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
super.channelActive(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the direction that traffic is going to this handler (" <== " or " ==> ")
|
||||
*/
|
||||
@Override
|
||||
protected
|
||||
String getConnectionDirection() {
|
||||
return " <== ";
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
Channel channel = context.channel();
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
|
||||
|
@ -50,13 +79,5 @@ public class RegistrationLocalHandlerServer extends RegistrationLocalHandler {
|
|||
this.registrationWrapper.connectionConnected0(connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the direction that traffic is going to this handler (" <== " or " ==> ")
|
||||
*/
|
||||
@Override
|
||||
protected String getConnectionDirection() {
|
||||
return " <== ";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
package dorkbox.network.connection.registration.remote;
|
||||
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.epoll.EpollDatagramChannel;
|
||||
import io.netty.channel.epoll.EpollSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.channel.udt.nio.NioUdtByteConnectorChannel;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bouncycastle.crypto.engines.AESFastEngine;
|
||||
import org.bouncycastle.crypto.modes.GCMBlockCipher;
|
||||
|
||||
import dorkbox.network.connection.ConnectionImpl;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
|
@ -27,16 +9,32 @@ import dorkbox.network.pipeline.KryoDecoder;
|
|||
import dorkbox.network.pipeline.KryoDecoderCrypto;
|
||||
import dorkbox.network.pipeline.udp.KryoDecoderUdpCrypto;
|
||||
import dorkbox.network.pipeline.udp.KryoEncoderUdpCrypto;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.collections.IntMap.Entries;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.channel.epoll.EpollDatagramChannel;
|
||||
import io.netty.channel.epoll.EpollSocketChannel;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.channel.socket.nio.NioSocketChannel;
|
||||
import io.netty.channel.udt.nio.NioUdtByteConnectorChannel;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import org.bouncycastle.crypto.engines.AESFastEngine;
|
||||
import org.bouncycastle.crypto.modes.GCMBlockCipher;
|
||||
|
||||
public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
||||
private static final String IDLE_HANDLER_FULL = "idleHandlerFull";
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public abstract
|
||||
class RegistrationRemoteHandler extends RegistrationHandler {
|
||||
protected static final String KRYO_ENCODER = "kryoEncoder";
|
||||
protected static final String KRYO_DECODER = "kryoDecoder";
|
||||
|
||||
private static final String IDLE_HANDLER_FULL = "idleHandlerFull";
|
||||
private static final String FRAME_AND_KRYO_ENCODER = "frameAndKryoEncoder";
|
||||
private static final String FRAME_AND_KRYO_DECODER = "frameAndKryoDecoder";
|
||||
|
||||
|
@ -47,18 +45,10 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
private static final String KRYO_CRYPTO_DECODER = "kryoCryptoDecoder";
|
||||
|
||||
private static final String IDLE_HANDLER = "idleHandler";
|
||||
private static final ThreadLocal<GCMBlockCipher> aesEngineLocal = new ThreadLocal<GCMBlockCipher>();
|
||||
|
||||
protected final SerializationManager serializationManager;
|
||||
|
||||
private static ThreadLocal<GCMBlockCipher> aesEngineLocal = new ThreadLocal<GCMBlockCipher>();
|
||||
|
||||
public RegistrationRemoteHandler(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
super(name, registrationWrapper);
|
||||
|
||||
this.serializationManager = serializationManager;
|
||||
}
|
||||
|
||||
protected static final GCMBlockCipher getAesEngine() {
|
||||
protected static
|
||||
GCMBlockCipher getAesEngine() {
|
||||
GCMBlockCipher aesEngine = aesEngineLocal.get();
|
||||
if (aesEngine == null) {
|
||||
aesEngine = new GCMBlockCipher(new AESFastEngine());
|
||||
|
@ -67,11 +57,33 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
return aesEngine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to verify if two InetAdresses are equal, by comparing the underlying byte arrays.
|
||||
*/
|
||||
public static
|
||||
boolean checkEqual(InetAddress serverA, InetAddress serverB) {
|
||||
if (serverA == null || serverB == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.equals(serverA.getAddress(), serverB.getAddress());
|
||||
}
|
||||
|
||||
protected final CryptoSerializationManager serializationManager;
|
||||
|
||||
public
|
||||
RegistrationRemoteHandler(String name, RegistrationWrapper registrationWrapper, CryptoSerializationManager serializationManager) {
|
||||
super(name, registrationWrapper);
|
||||
|
||||
this.serializationManager = serializationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 1: Channel is first created
|
||||
*/
|
||||
@Override
|
||||
protected void initChannel(Channel channel) {
|
||||
protected
|
||||
void initChannel(Channel channel) {
|
||||
ChannelPipeline pipeline = channel.pipeline();
|
||||
|
||||
///////////////////////
|
||||
|
@ -79,10 +91,12 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
///////////////////////
|
||||
pipeline.addFirst(FRAME_AND_KRYO_DECODER, new KryoDecoder(this.serializationManager)); // cannot be shared because of possible fragmentation.
|
||||
|
||||
// this makes the proper event get raised in the registrationHandler to kill NEW idle connections. Once "connected" they last a lot longer.
|
||||
// we ALWAYS have this initial IDLE handler, so we don't have to worry about a slow-loris attack against the server.
|
||||
pipeline.addFirst(IDLE_HANDLER, new IdleStateHandler(4, 0, 0)); // timer must be shared.
|
||||
|
||||
int idleTimeout = this.registrationWrapper.getIdleTimeout();
|
||||
if (idleTimeout > 0) {
|
||||
// this makes the proper event get raised in the registrationHandler to kill NEW idle connections. Once "connected" they last a lot longer.
|
||||
// we ALWAYS have this initial IDLE handler, so we don't have to worry about a slow-loris attack against the server.
|
||||
pipeline.addFirst(IDLE_HANDLER, new IdleStateHandler(4, 0, 0)); // in Seconds -- not shared, because it is per-connection
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// ENCODE (or downstream)
|
||||
|
@ -95,7 +109,8 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
* Debug output, so we can tell what direction the connection is in the log
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
public
|
||||
void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
// add the channel so we can access it later.
|
||||
// do NOT want to add UDP channels, since they are tracked differently.
|
||||
|
||||
|
@ -110,13 +125,17 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
stringBuilder.append("Connected to remote ");
|
||||
if (channelClass == NioSocketChannel.class) {
|
||||
stringBuilder.append("TCP");
|
||||
} else if (channelClass == EpollSocketChannel.class) {
|
||||
}
|
||||
else if (channelClass == EpollSocketChannel.class) {
|
||||
stringBuilder.append("TCP");
|
||||
} else if (channelClass == NioDatagramChannel.class) {
|
||||
}
|
||||
else if (channelClass == NioDatagramChannel.class) {
|
||||
stringBuilder.append("UDP");
|
||||
} else if (channelClass == EpollDatagramChannel.class) {
|
||||
}
|
||||
else if (channelClass == EpollDatagramChannel.class) {
|
||||
stringBuilder.append("UDP");
|
||||
} else if (channelClass == NioUdtByteConnectorChannel.class) {
|
||||
}
|
||||
else if (channelClass == NioUdtByteConnectorChannel.class) {
|
||||
stringBuilder.append("UDT");
|
||||
}
|
||||
else {
|
||||
|
@ -130,12 +149,14 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
if (channel.remoteAddress() != null) {
|
||||
stringBuilder.append(" ==> ");
|
||||
stringBuilder.append(channel.remoteAddress());
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// this means we are LISTENING.
|
||||
stringBuilder.append(" <== ");
|
||||
stringBuilder.append("?????");
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
stringBuilder.append(getConnectionDirection());
|
||||
stringBuilder.append(channel.remoteAddress());
|
||||
}
|
||||
|
@ -144,26 +165,27 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
this.logger.debug(stringBuilder.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
|
||||
Channel channel = context.channel();
|
||||
|
||||
this.logger.error("Unexpected exception while trying to send/receive data on Client remote (network) channel. ({})" +
|
||||
System.getProperty("line.separator"), channel.remoteAddress(), cause);
|
||||
if (channel.isOpen()) {
|
||||
channel.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the direction that traffic is going to this handler (" <== " or " ==> ")
|
||||
*/
|
||||
protected abstract String getConnectionDirection();
|
||||
|
||||
|
||||
/**
|
||||
* Check to verify if two InetAdresses are equal, by comparing the underlying byte arrays.
|
||||
*/
|
||||
public static boolean checkEqual(InetAddress serverA, InetAddress serverB) {
|
||||
if (serverA == null || serverB == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Arrays.equals(serverA.getAddress(), serverB.getAddress());
|
||||
}
|
||||
|
||||
protected abstract
|
||||
String getConnectionDirection();
|
||||
|
||||
// have to setup AFTER establish connection, data, as we don't want to enable AES until we're ready.
|
||||
protected final void setupConnectionCrypto(MetaChannel metaChannel) {
|
||||
protected final
|
||||
void setupConnectionCrypto(MetaChannel metaChannel) {
|
||||
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
String type = "TCP";
|
||||
|
@ -175,7 +197,7 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
}
|
||||
|
||||
|
||||
InetSocketAddress address = (InetSocketAddress)metaChannel.tcpChannel.remoteAddress();
|
||||
InetSocketAddress address = (InetSocketAddress) metaChannel.tcpChannel.remoteAddress();
|
||||
this.logger.debug("Encrypting {} session with {}", type, address.getAddress());
|
||||
}
|
||||
|
||||
|
@ -183,13 +205,20 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
int idleTimeout = this.registrationWrapper.getIdleTimeout();
|
||||
|
||||
// add the new handlers (FORCE encryption and longer IDLE handler)
|
||||
pipeline.replace(FRAME_AND_KRYO_DECODER, FRAME_AND_KRYO_CRYPTO_DECODER, new KryoDecoderCrypto(this.serializationManager)); // cannot be shared because of possible fragmentation.
|
||||
pipeline.replace(FRAME_AND_KRYO_DECODER,
|
||||
FRAME_AND_KRYO_CRYPTO_DECODER,
|
||||
new KryoDecoderCrypto(this.serializationManager)); // cannot be shared because of possible fragmentation.
|
||||
|
||||
if (idleTimeout > 0) {
|
||||
pipeline.replace(IDLE_HANDLER, IDLE_HANDLER_FULL, new IdleStateHandler(0, 0, this.registrationWrapper.getIdleTimeout(), TimeUnit.MILLISECONDS));
|
||||
} else {
|
||||
pipeline.remove(IDLE_HANDLER);
|
||||
pipeline.replace(IDLE_HANDLER, IDLE_HANDLER_FULL, new IdleStateHandler(0,
|
||||
0,
|
||||
idleTimeout,
|
||||
TimeUnit.MILLISECONDS));
|
||||
}
|
||||
pipeline.replace(FRAME_AND_KRYO_ENCODER, FRAME_AND_KRYO_CRYPTO_ENCODER, this.registrationWrapper.getKryoTcpCryptoEncoder()); // this is shared
|
||||
|
||||
pipeline.replace(FRAME_AND_KRYO_ENCODER,
|
||||
FRAME_AND_KRYO_CRYPTO_ENCODER,
|
||||
this.registrationWrapper.getKryoTcpCryptoEncoder()); // this is shared
|
||||
|
||||
|
||||
if (metaChannel.udpChannel != null && metaChannel.udpRemoteAddress == null) {
|
||||
|
@ -201,11 +230,12 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
|
||||
if (metaChannel.udtChannel != null) {
|
||||
pipeline = metaChannel.udtChannel.pipeline();
|
||||
pipeline.replace(FRAME_AND_KRYO_DECODER, FRAME_AND_KRYO_CRYPTO_DECODER, new KryoDecoderCrypto(this.serializationManager)); // cannot be shared because of possible fragmentation.
|
||||
pipeline.replace(FRAME_AND_KRYO_DECODER,
|
||||
FRAME_AND_KRYO_CRYPTO_DECODER,
|
||||
new KryoDecoderCrypto(this.serializationManager)); // cannot be shared because of possible fragmentation.
|
||||
|
||||
if (idleTimeout > 0) {
|
||||
pipeline.replace(IDLE_HANDLER, IDLE_HANDLER_FULL, new IdleStateHandler(0, 0, idleTimeout, TimeUnit.MILLISECONDS));
|
||||
} else {
|
||||
pipeline.remove(IDLE_HANDLER);
|
||||
}
|
||||
pipeline.replace(FRAME_AND_KRYO_ENCODER, FRAME_AND_KRYO_CRYPTO_ENCODER, this.registrationWrapper.getKryoTcpCryptoEncoder());
|
||||
}
|
||||
|
@ -214,7 +244,8 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
/**
|
||||
* Setup our meta-channel to migrate to the correct connection handler for all regular data.
|
||||
*/
|
||||
protected final void establishConnection(MetaChannel metaChannel) {
|
||||
protected final
|
||||
void establishConnection(MetaChannel metaChannel) {
|
||||
ChannelPipeline tcpPipe = metaChannel.tcpChannel.pipeline();
|
||||
ChannelPipeline udpPipe;
|
||||
ChannelPipeline udtPipe;
|
||||
|
@ -223,13 +254,15 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
// don't want to muck with the SERVER udp pipeline, as it NEVER CHANGES.
|
||||
// only the client will have the udp remote address
|
||||
udpPipe = metaChannel.udpChannel.pipeline();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
udpPipe = null;
|
||||
}
|
||||
|
||||
if (metaChannel.udtChannel != null) {
|
||||
udtPipe = metaChannel.udtChannel.pipeline();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
udtPipe = null;
|
||||
}
|
||||
|
||||
|
@ -254,9 +287,9 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
// have to setup AFTER establish connection, data, as we don't want to enable AES until we're ready.
|
||||
protected final void setupConnection(MetaChannel metaChannel) {
|
||||
protected final
|
||||
void setupConnection(MetaChannel metaChannel) {
|
||||
boolean registerServer = false;
|
||||
|
||||
// now that we are CONNECTED, we want to remove ourselves (and channel ID's) from the map.
|
||||
|
@ -281,7 +314,8 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
// to keep track of UDP connections. This is very different than how the client works
|
||||
// only the client will have the udp remote address
|
||||
channelMap.remove(metaChannel.udpChannel.hashCode());
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// SERVER RUNS THIS
|
||||
// don't ALWAYS have UDP on SERVER...
|
||||
registerServer = true;
|
||||
|
@ -305,31 +339,34 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
type += "/UDT";
|
||||
}
|
||||
|
||||
InetSocketAddress address = (InetSocketAddress)metaChannel.tcpChannel.remoteAddress();
|
||||
InetSocketAddress address = (InetSocketAddress) metaChannel.tcpChannel.remoteAddress();
|
||||
this.logger.info("Created a {} connection with {}", type, address.getAddress());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the metachannel for the UDP server. Default is to do nothing.
|
||||
*
|
||||
* <p/>
|
||||
* The server will override this.
|
||||
* Only called if we have a UDP channel when we finalize the setup of the TCP connection
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
protected void setupServerUdpConnection(MetaChannel metaChannel) {
|
||||
protected
|
||||
void setupServerUdpConnection(MetaChannel metaChannel) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal call by the pipeline to notify the "Connection" object that it has "connected", meaning that modifications
|
||||
* to the pipeline are finished.
|
||||
*/
|
||||
protected final void notifyConnection(MetaChannel metaChannel) {
|
||||
protected final
|
||||
void notifyConnection(MetaChannel metaChannel) {
|
||||
this.registrationWrapper.connectionConnected0(metaChannel.connection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void channelInactive(ChannelHandlerContext context) throws Exception {
|
||||
public final
|
||||
void channelInactive(ChannelHandlerContext context) throws Exception {
|
||||
Channel channel = context.channel();
|
||||
|
||||
this.logger.info("Closed connection: {}", channel.remoteAddress());
|
||||
|
@ -358,14 +395,4 @@ public abstract class RegistrationRemoteHandler extends RegistrationHandler {
|
|||
|
||||
super.channelInactive(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception {
|
||||
Channel channel = context.channel();
|
||||
|
||||
this.logger.error("Unexpected exception while trying to send/receive data on Client remote (network) channel. ({})" + System.getProperty("line.separator"), channel.remoteAddress(), cause);
|
||||
if (channel.isOpen()) {
|
||||
channel.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package dorkbox.network.connection.registration.remote;
|
||||
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
|
||||
public class RegistrationRemoteHandlerClient extends RegistrationRemoteHandler {
|
||||
|
||||
public RegistrationRemoteHandlerClient(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
public RegistrationRemoteHandlerClient(String name, RegistrationWrapper registrationWrapper, CryptoSerializationManager serializationManager) {
|
||||
super(name, registrationWrapper, serializationManager);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
package dorkbox.network.connection.registration.remote;
|
||||
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteArray;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.crypto.Crypto;
|
||||
import dorkbox.util.crypto.serialization.EccPublicKeySerializer;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bouncycastle.crypto.BasicAgreement;
|
||||
import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||
import org.bouncycastle.crypto.digests.SHA384Digest;
|
||||
|
@ -19,27 +23,23 @@ import org.bouncycastle.jce.ECNamedCurveTable;
|
|||
import org.bouncycastle.jce.spec.ECParameterSpec;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteArray;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.crypto.Crypto;
|
||||
import dorkbox.util.crypto.serialization.EccPublicKeySerializer;
|
||||
|
||||
public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandlerClient {
|
||||
public
|
||||
class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandlerClient {
|
||||
|
||||
private static final String DELETE_IP = "eleteIP"; // purposefully missing the "D", since that is a system parameter, which starts with "-D"
|
||||
|
||||
private ThreadLocal<IESEngine> eccEngineLocal = new ThreadLocal<IESEngine>();
|
||||
private final static ECParameterSpec eccSpec = ECNamedCurveTable.getParameterSpec(Crypto.ECC.p521_curve);
|
||||
private ThreadLocal<IESEngine> eccEngineLocal = new ThreadLocal<IESEngine>();
|
||||
|
||||
public RegistrationRemoteHandlerClientTCP(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
public
|
||||
RegistrationRemoteHandlerClientTCP(String name,
|
||||
RegistrationWrapper registrationWrapper,
|
||||
CryptoSerializationManager serializationManager) {
|
||||
super(name, registrationWrapper, serializationManager);
|
||||
|
||||
// check to see if we need to delete an IP address as commanded from the user prompt
|
||||
|
@ -51,11 +51,12 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
String[] split = ipAsString.split("\\.");
|
||||
if (split.length == 4) {
|
||||
address = new byte[4];
|
||||
for (int i=0;i<split.length;i++) {
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
int asInt = Integer.parseInt(split[i]);
|
||||
if (asInt >= 0 && asInt <= 255) {
|
||||
address[i] = (byte) Integer.parseInt(split[i]);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
address = null;
|
||||
break;
|
||||
}
|
||||
|
@ -77,7 +78,8 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
// end command
|
||||
}
|
||||
|
||||
private final IESEngine getEccEngine() {
|
||||
private final
|
||||
IESEngine getEccEngine() {
|
||||
IESEngine iesEngine = this.eccEngineLocal.get();
|
||||
if (iesEngine == null) {
|
||||
iesEngine = Crypto.ECC.createEngine();
|
||||
|
@ -90,8 +92,11 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
* STEP 1: Channel is first created
|
||||
*/
|
||||
@Override
|
||||
protected void initChannel(Channel channel) {
|
||||
this.logger.trace("Channel registered: {}", channel.getClass().getSimpleName());
|
||||
protected
|
||||
void initChannel(Channel channel) {
|
||||
this.logger.trace("Channel registered: {}",
|
||||
channel.getClass()
|
||||
.getSimpleName());
|
||||
|
||||
|
||||
// TCP & UDT
|
||||
|
@ -104,10 +109,11 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
* STEP 2: Channel is now active. Start the registration process
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
public
|
||||
void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isDebugEnabled()) {
|
||||
super.channelActive(context);
|
||||
super.channelActive(context);
|
||||
}
|
||||
|
||||
Channel channel = context.channel();
|
||||
|
@ -139,7 +145,8 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
public
|
||||
void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
Channel channel = context.channel();
|
||||
|
||||
RegistrationWrapper registrationWrapper2 = this.registrationWrapper;
|
||||
|
@ -168,8 +175,10 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
|
||||
if (!valid) {
|
||||
//whoa! abort since something messed up! (log happens inside of validate method)
|
||||
String hostAddress = tcpRemoteServer.getAddress().getHostAddress();
|
||||
logger2.error("Invalid ECC public key for server IP {} during handshake. WARNING. The server has changed!", hostAddress);
|
||||
String hostAddress = tcpRemoteServer.getAddress()
|
||||
.getHostAddress();
|
||||
logger2.error("Invalid ECC public key for server IP {} during handshake. WARNING. The server has changed!",
|
||||
hostAddress);
|
||||
logger2.error("Fix by adding the argument -D{} {} when starting the client.", DELETE_IP, hostAddress);
|
||||
metaChannel.changedRemoteKey = true;
|
||||
|
||||
|
@ -182,7 +191,10 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
// setup crypto state
|
||||
IESEngine decrypt = getEccEngine();
|
||||
|
||||
byte[] aesKeyBytes = Crypto.ECC.decrypt(decrypt, registrationWrapper2.getPrivateKey(), registration.publicKey, registration.eccParameters,
|
||||
byte[] aesKeyBytes = Crypto.ECC.decrypt(decrypt,
|
||||
registrationWrapper2.getPrivateKey(),
|
||||
registration.publicKey,
|
||||
registration.eccParameters,
|
||||
registration.aesKey);
|
||||
|
||||
if (aesKeyBytes.length != 32) {
|
||||
|
@ -262,7 +274,7 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
metaChannel.aesKey = Arrays.copyOfRange(digest, 0, 32); // 256bit keysize (32 bytes)
|
||||
metaChannel.aesIV = Arrays.copyOfRange(digest, 32, 48); // 128bit blocksize (16 bytes)
|
||||
|
||||
// abort if something messed up!
|
||||
// abort if something messed up!
|
||||
if (metaChannel.aesKey.length != 32) {
|
||||
logger2.error("Fatal error trying to use AES key (wrong key length).");
|
||||
shutdown(registrationWrapper2, channel);
|
||||
|
@ -280,7 +292,7 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
byte[] pubKeyAsBytes = output.toBytes();
|
||||
register.payload = Crypto.AES.encrypt(getAesEngine(), aesKeyBytes, registration.aesIV, pubKeyAsBytes);
|
||||
|
||||
channel.write(register);
|
||||
channel.writeAndFlush(register);
|
||||
|
||||
ReferenceCountUtil.release(message);
|
||||
return;
|
||||
|
@ -300,7 +312,7 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
|
||||
// tell the server we are done, and to setup crypto on it's side
|
||||
if (isDoneWithRegistration) {
|
||||
channel.write(registration);
|
||||
channel.writeAndFlush(registration);
|
||||
|
||||
// re-sync the TCP delta round trip time
|
||||
metaChannel.updateTcpRoundTripTime();
|
||||
|
@ -311,7 +323,7 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
// we only get this when we are 100% done with the registration of all connection types.
|
||||
else {
|
||||
setupConnectionCrypto(metaChannel);
|
||||
// AES ENCRPYTION NOW USED
|
||||
// AES ENCRYPTION NOW USED
|
||||
|
||||
// this sets up the pipeline for the client, so all the necessary handlers are ready to go
|
||||
establishConnection(metaChannel);
|
||||
|
@ -319,19 +331,23 @@ public class RegistrationRemoteHandlerClientTCP extends RegistrationRemoteHandle
|
|||
|
||||
final MetaChannel metaChannel2 = metaChannel;
|
||||
// wait for a "round trip" amount of time, then notify the APP!
|
||||
channel.eventLoop().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Logger logger2 = RegistrationRemoteHandlerClientTCP.this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("Notify Connection");
|
||||
}
|
||||
notifyConnection(metaChannel2);
|
||||
}}, metaChannel.getNanoSecBetweenTCP() * 2, TimeUnit.NANOSECONDS);
|
||||
channel.eventLoop()
|
||||
.schedule(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
Logger logger2 = RegistrationRemoteHandlerClientTCP.this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("Notify Connection");
|
||||
}
|
||||
notifyConnection(metaChannel2);
|
||||
}
|
||||
}, metaChannel.getNanoSecBetweenTCP() * 2, TimeUnit.NANOSECONDS);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// this means that UDP beat us to the "punch", and notified before we did. (notify removes all the entries from the map)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import dorkbox.network.connection.registration.MetaChannel;
|
|||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.pipeline.udp.KryoDecoderUdp;
|
||||
import dorkbox.network.pipeline.udp.KryoEncoderUdp;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteArray;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
|
@ -24,7 +24,7 @@ import dorkbox.util.crypto.Crypto;
|
|||
|
||||
public class RegistrationRemoteHandlerClientUDP extends RegistrationRemoteHandlerClient {
|
||||
|
||||
public RegistrationRemoteHandlerClientUDP(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
public RegistrationRemoteHandlerClientUDP(String name, RegistrationWrapper registrationWrapper, CryptoSerializationManager serializationManager) {
|
||||
super(name, registrationWrapper, serializationManager);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.slf4j.Logger;
|
|||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteArray;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
|
@ -22,7 +22,7 @@ import dorkbox.util.crypto.Crypto;
|
|||
|
||||
public class RegistrationRemoteHandlerClientUDT extends RegistrationRemoteHandlerClient {
|
||||
|
||||
public RegistrationRemoteHandlerClientUDT(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
public RegistrationRemoteHandlerClientUDT(String name, RegistrationWrapper registrationWrapper, CryptoSerializationManager serializationManager) {
|
||||
super(name, registrationWrapper, serializationManager);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,28 +2,33 @@ package dorkbox.network.connection.registration.remote;
|
|||
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
|
||||
public class RegistrationRemoteHandlerServer extends RegistrationRemoteHandler {
|
||||
public
|
||||
class RegistrationRemoteHandlerServer extends RegistrationRemoteHandler {
|
||||
|
||||
public RegistrationRemoteHandlerServer(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
public
|
||||
RegistrationRemoteHandlerServer(String name,
|
||||
RegistrationWrapper registrationWrapper,
|
||||
CryptoSerializationManager serializationManager) {
|
||||
super(name, registrationWrapper, serializationManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the direction that traffic is going to this handler (" <== " or " ==> ")
|
||||
*/
|
||||
@Override
|
||||
protected
|
||||
String getConnectionDirection() {
|
||||
return " <== ";
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the metachannel for the UDP server (For the TCP/UDT streams)
|
||||
*/
|
||||
@Override
|
||||
protected void setupServerUdpConnection(MetaChannel metaChannel) {
|
||||
protected
|
||||
void setupServerUdpConnection(MetaChannel metaChannel) {
|
||||
registrationWrapper.registerServerUDP(metaChannel);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the direction that traffic is going to this handler (" <== " or " ==> ")
|
||||
*/
|
||||
@Override
|
||||
protected String getConnectionDirection() {
|
||||
return " <== ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
package dorkbox.network.connection.registration.remote;
|
||||
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.util.MathUtils;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteArray;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.crypto.Crypto;
|
||||
import dorkbox.util.crypto.serialization.EccPublicKeySerializer;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
|
||||
import org.bouncycastle.crypto.BasicAgreement;
|
||||
import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
|
||||
|
@ -20,37 +25,32 @@ import org.bouncycastle.jce.spec.ECParameterSpec;
|
|||
import org.bouncycastle.util.Arrays;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import java.math.BigInteger;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.util.MathUtils;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteArray;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.crypto.Crypto;
|
||||
import dorkbox.util.crypto.serialization.EccPublicKeySerializer;
|
||||
public
|
||||
class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandlerServer {
|
||||
|
||||
public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandlerServer {
|
||||
|
||||
private static final long ECDH_TIMEOUT = 10*60*60*1000*1000*1000; // 10 minutes in nanoseconds
|
||||
|
||||
private final static ECParameterSpec eccSpec = ECNamedCurveTable.getParameterSpec(Crypto.ECC.p521_curve);
|
||||
|
||||
private ThreadLocal<IESEngine> eccEngineLocal = new ThreadLocal<IESEngine>();
|
||||
private static final long ECDH_TIMEOUT = 10 * 60 * 60 * 1000 * 1000 * 1000; // 10 minutes in nanoseconds
|
||||
|
||||
private static final ECParameterSpec eccSpec = ECNamedCurveTable.getParameterSpec(Crypto.ECC.p521_curve);
|
||||
private final Object ecdhKeyLock = new Object();
|
||||
private AsymmetricCipherKeyPair ecdhKeyPair = Crypto.ECC.generateKeyPair(eccSpec, new SecureRandom());
|
||||
private ThreadLocal<IESEngine> eccEngineLocal = new ThreadLocal<IESEngine>();
|
||||
private AsymmetricCipherKeyPair ecdhKeyPair = Crypto.ECC.generateKeyPair(eccSpec, new SecureRandom());
|
||||
private volatile long ecdhTimeout = System.nanoTime();
|
||||
|
||||
|
||||
public RegistrationRemoteHandlerServerTCP(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
public
|
||||
RegistrationRemoteHandlerServerTCP(String name,
|
||||
RegistrationWrapper registrationWrapper,
|
||||
CryptoSerializationManager serializationManager) {
|
||||
super(name, registrationWrapper, serializationManager);
|
||||
}
|
||||
|
||||
private final IESEngine getEccEngine() {
|
||||
private final
|
||||
IESEngine getEccEngine() {
|
||||
IESEngine iesEngine = this.eccEngineLocal.get();
|
||||
if (iesEngine == null) {
|
||||
iesEngine = Crypto.ECC.createEngine();
|
||||
|
@ -62,7 +62,8 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
/**
|
||||
* Rotates the ECDH key every 10 minutes, as this is a VERY expensive calculation to keep on doing for every connection.
|
||||
*/
|
||||
private AsymmetricCipherKeyPair getEchdKeyOnRotate(SecureRandom secureRandom) {
|
||||
private
|
||||
AsymmetricCipherKeyPair getEchdKeyOnRotate(SecureRandom secureRandom) {
|
||||
if (System.nanoTime() - this.ecdhTimeout > ECDH_TIMEOUT) {
|
||||
synchronized (this.ecdhKeyLock) {
|
||||
this.ecdhTimeout = System.nanoTime();
|
||||
|
@ -77,7 +78,8 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
* STEP 1: Channel is first created (This is TCP/UDT only, as such it differs from the client which is TCP/UDP)
|
||||
*/
|
||||
@Override
|
||||
protected void initChannel(Channel channel) {
|
||||
protected
|
||||
void initChannel(Channel channel) {
|
||||
super.initChannel(channel);
|
||||
}
|
||||
|
||||
|
@ -85,9 +87,10 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
* STEP 2: Channel is now active. Prepare the meta channel to listen for the registration process
|
||||
*/
|
||||
@Override
|
||||
public void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
public
|
||||
void channelActive(ChannelHandlerContext context) throws Exception {
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
super.channelActive(context);
|
||||
super.channelActive(context);
|
||||
}
|
||||
|
||||
Channel channel = context.channel();
|
||||
|
@ -105,14 +108,17 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
this.registrationWrapper.releaseChannelMap();
|
||||
}
|
||||
|
||||
this.logger.trace(this.name, "New TCP connection. Saving TCP channel info.");
|
||||
if (this.logger.isTraceEnabled()) {
|
||||
this.logger.trace(this.name, "New TCP connection. Saving TCP channel info.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 3-XXXXX: We pass registration messages around until we the registration handshake is complete!
|
||||
*/
|
||||
@Override
|
||||
public void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
public
|
||||
void channelRead(ChannelHandlerContext context, Object message) throws Exception {
|
||||
Channel channel = context.channel();
|
||||
|
||||
// only TCP will come across here for the server. (UDP here is called by the UDP handler/wrapper)
|
||||
|
@ -155,7 +161,9 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
if (!valid) {
|
||||
//whoa! abort since something messed up! (log happens inside of validate method)
|
||||
if (logger2.isInfoEnabled()) {
|
||||
logger2.info("Invalid ECC public key for IP {} during handshake with client. Toggling extra flag in channel to indicate this.", tcpRemoteClient.getAddress().getHostAddress());
|
||||
logger2.info("Invalid ECC public key for IP {} during handshake with client. Toggling extra flag in channel to indicate this.",
|
||||
tcpRemoteClient.getAddress()
|
||||
.getHostAddress());
|
||||
}
|
||||
metaChannel.changedRemoteKey = true;
|
||||
}
|
||||
|
@ -221,13 +229,17 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
// now we have to ENCRYPT the AES key!
|
||||
register.eccParameters = Crypto.ECC.generateSharedParameters(secureRandom);
|
||||
register.aesIV = metaChannel.aesIV;
|
||||
register.aesKey = Crypto.ECC.encrypt(encrypt, registrationWrapper2.getPrivateKey(), metaChannel.publicKey, register.eccParameters, metaChannel.aesKey);
|
||||
register.aesKey = Crypto.ECC.encrypt(encrypt,
|
||||
registrationWrapper2.getPrivateKey(),
|
||||
metaChannel.publicKey,
|
||||
register.eccParameters,
|
||||
metaChannel.aesKey);
|
||||
|
||||
|
||||
// now encrypt payload via AES
|
||||
register.payload = Crypto.AES.encrypt(getAesEngine(), metaChannel.aesKey, register.aesIV, combinedBytes);
|
||||
|
||||
channel.write(register);
|
||||
channel.writeAndFlush(register);
|
||||
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("Assigning new random connection ID for TCP and performing ECDH.");
|
||||
|
@ -248,7 +260,10 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
if (metaChannel.ecdhKey != null) {
|
||||
// now we have to decrypt the ECDH key using our TEMP AES keys
|
||||
|
||||
byte[] payload = Crypto.AES.decrypt(getAesEngine(), metaChannel.aesKey, metaChannel.aesIV, registration.payload);
|
||||
byte[] payload = Crypto.AES.decrypt(getAesEngine(),
|
||||
metaChannel.aesKey,
|
||||
metaChannel.aesIV,
|
||||
registration.payload);
|
||||
|
||||
if (payload.length == 0) {
|
||||
logger2.error("Invalid decryption of payload. Aborting.");
|
||||
|
@ -290,12 +305,12 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
metaChannel.aesIV = Arrays.copyOfRange(digest, 32, 48); // 128bit blocksize (16 bytes)
|
||||
|
||||
// tell the client to continue it's registration process.
|
||||
channel.write(new Registration());
|
||||
channel.writeAndFlush(new Registration());
|
||||
}
|
||||
|
||||
// we only get this when we are 100% done with the registration of all connection types.
|
||||
else {
|
||||
channel.write(registration); // causes client to setup network connection & AES
|
||||
channel.writeAndFlush(registration); // causes client to setup network connection & AES
|
||||
|
||||
setupConnectionCrypto(metaChannel);
|
||||
// AES ENCRPYTION NOW USED
|
||||
|
@ -306,15 +321,18 @@ public class RegistrationRemoteHandlerServerTCP extends RegistrationRemoteHandle
|
|||
|
||||
final MetaChannel chan2 = metaChannel;
|
||||
// wait for a "round trip" amount of time, then notify the APP!
|
||||
channel.eventLoop().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Logger logger2 = RegistrationRemoteHandlerServerTCP.this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("Notify Connection");
|
||||
}
|
||||
notifyConnection(chan2);
|
||||
}}, metaChannel.getNanoSecBetweenTCP() * 2, TimeUnit.NANOSECONDS);
|
||||
channel.eventLoop()
|
||||
.schedule(new Runnable() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
Logger logger2 = RegistrationRemoteHandlerServerTCP.this.logger;
|
||||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("Notify Connection");
|
||||
}
|
||||
notifyConnection(chan2);
|
||||
}
|
||||
}, metaChannel.getNanoSecBetweenTCP() * 2, TimeUnit.NANOSECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,5 @@
|
|||
package dorkbox.network.connection.registration.remote;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.handler.codec.MessageToMessageCodec;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.Broadcast;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.ConnectionImpl;
|
||||
|
@ -21,12 +7,24 @@ import dorkbox.network.connection.RegistrationWrapper;
|
|||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.connection.wrapper.UdpWrapper;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteArray;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.collections.IntMap.Entries;
|
||||
import dorkbox.util.crypto.Crypto;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.socket.DatagramPacket;
|
||||
import io.netty.handler.codec.MessageToMessageCodec;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
@Sharable
|
||||
public class RegistrationRemoteHandlerServerUDP extends MessageToMessageCodec<DatagramPacket, UdpWrapper> {
|
||||
|
@ -37,10 +35,10 @@ public class RegistrationRemoteHandlerServerUDP extends MessageToMessageCodec<Da
|
|||
private final String name;
|
||||
private final ByteBuf discoverResponseBuffer;
|
||||
private final RegistrationWrapper registrationWrapper;
|
||||
private final SerializationManager serializationManager;
|
||||
private final CryptoSerializationManager serializationManager;
|
||||
|
||||
|
||||
public RegistrationRemoteHandlerServerUDP(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
public RegistrationRemoteHandlerServerUDP(String name, RegistrationWrapper registrationWrapper, CryptoSerializationManager serializationManager) {
|
||||
this.name = name + " Registration-UDP-Server";
|
||||
this.logger = org.slf4j.LoggerFactory.getLogger(this.name);
|
||||
this.registrationWrapper = registrationWrapper;
|
||||
|
@ -141,7 +139,7 @@ public class RegistrationRemoteHandlerServerUDP extends MessageToMessageCodec<Da
|
|||
// registration is the ONLY thing NOT encrypted
|
||||
Logger logger2 = this.logger;
|
||||
RegistrationWrapper registrationWrapper2 = this.registrationWrapper;
|
||||
SerializationManager serializationManager2 = this.serializationManager;
|
||||
CryptoSerializationManager serializationManager2 = this.serializationManager;
|
||||
|
||||
if (serializationManager2.isEncrypted(data)) {
|
||||
// we need to FORWARD this message "down the pipeline".
|
||||
|
|
|
@ -12,7 +12,7 @@ import org.slf4j.Logger;
|
|||
import dorkbox.network.connection.RegistrationWrapper;
|
||||
import dorkbox.network.connection.registration.MetaChannel;
|
||||
import dorkbox.network.connection.registration.Registration;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteArray;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.collections.IntMap.Entries;
|
||||
|
@ -20,7 +20,7 @@ import dorkbox.util.crypto.Crypto;
|
|||
|
||||
public class RegistrationRemoteHandlerServerUDT extends RegistrationRemoteHandlerServer {
|
||||
|
||||
public RegistrationRemoteHandlerServerUDT(String name, RegistrationWrapper registrationWrapper, SerializationManager serializationManager) {
|
||||
public RegistrationRemoteHandlerServerUDT(String name, RegistrationWrapper registrationWrapper, CryptoSerializationManager serializationManager) {
|
||||
super(name, registrationWrapper, serializationManager);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,166 +1,218 @@
|
|||
package dorkbox.network.pipeline;
|
||||
|
||||
import com.esotericsoftware.kryo.KryoException;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.InputStream;
|
||||
|
||||
import com.esotericsoftware.kryo.KryoException;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
|
||||
|
||||
/**
|
||||
* An {@link InputStream} which reads data from a {@link ChannelBuffer}.
|
||||
* <p>
|
||||
* <p/>
|
||||
* A read operation against this stream will occur at the {@code readerIndex}
|
||||
* of its underlying buffer and the {@code readerIndex} will increase during
|
||||
* the read operation.
|
||||
* <p>
|
||||
* <p/>
|
||||
* This stream implements {@link DataInput} for your convenience.
|
||||
* The endianness of the stream is not always big endian but depends on
|
||||
* the endianness of the underlying buffer.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Utility methods are provided for efficiently reading primitive types and strings.
|
||||
*
|
||||
* <p/>
|
||||
* Modified from KRYO to use ByteBuf.
|
||||
*/
|
||||
|
||||
public class ByteBufInput extends Input {
|
||||
public
|
||||
class ByteBufInput extends Input {
|
||||
private char[] inputChars = new char[32];
|
||||
private ByteBuf byteBuf;
|
||||
private int startIndex;
|
||||
|
||||
/** Creates an uninitialized Input. {@link #setBuffer(ByteBuf)} must be called before the Input is used. */
|
||||
public ByteBufInput () {
|
||||
/**
|
||||
* Creates an uninitialized Input. {@link #setBuffer(ByteBuf)} must be called before the Input is used.
|
||||
*/
|
||||
public
|
||||
ByteBufInput() {
|
||||
}
|
||||
|
||||
public ByteBufInput(ByteBuf buffer) {
|
||||
public
|
||||
ByteBufInput(ByteBuf buffer) {
|
||||
setBuffer(buffer);
|
||||
}
|
||||
|
||||
public final void setBuffer(ByteBuf byteBuf) {
|
||||
public final
|
||||
void setBuffer(ByteBuf byteBuf) {
|
||||
this.byteBuf = byteBuf;
|
||||
|
||||
if (byteBuf != null) {
|
||||
startIndex = byteBuf.readerIndex(); // where the object starts...
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
startIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public ByteBuf getByteBuf() {
|
||||
public
|
||||
ByteBuf getByteBuf() {
|
||||
return byteBuf;
|
||||
}
|
||||
|
||||
/** Sets a new buffer. The position and total are reset, discarding any buffered bytes. */
|
||||
/**
|
||||
* Sets a new buffer. The position and total are reset, discarding any buffered bytes.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setBuffer (byte[] bytes) {
|
||||
public
|
||||
void setBuffer(byte[] bytes) {
|
||||
throw new RuntimeException("Cannot access this method!");
|
||||
}
|
||||
|
||||
/** Sets a new buffer. The position and total are reset, discarding any buffered bytes. */
|
||||
/**
|
||||
* Sets a new buffer. The position and total are reset, discarding any buffered bytes.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setBuffer (byte[] bytes, int offset, int count) {
|
||||
public
|
||||
void setBuffer(byte[] bytes, int offset, int count) {
|
||||
throw new RuntimeException("Cannot access this method!");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public byte[] getBuffer () {
|
||||
public
|
||||
byte[] getBuffer() {
|
||||
throw new RuntimeException("Cannot access this method!");
|
||||
}
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public InputStream getInputStream () {
|
||||
public
|
||||
InputStream getInputStream() {
|
||||
throw new RuntimeException("Cannot access this method!");
|
||||
}
|
||||
|
||||
/** Sets a new InputStream. The position and total are reset, discarding any buffered bytes.
|
||||
* @param inputStream May be null. */
|
||||
/**
|
||||
* Sets a new InputStream. The position and total are reset, discarding any buffered bytes.
|
||||
*
|
||||
* @param inputStream May be null.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setInputStream (InputStream inputStream) {
|
||||
public
|
||||
void setInputStream(InputStream inputStream) {
|
||||
throw new RuntimeException("Cannot access this method!");
|
||||
}
|
||||
|
||||
/** Returns the number of bytes read. */
|
||||
/**
|
||||
* Returns the number of bytes read.
|
||||
*/
|
||||
@Override
|
||||
public long total () {
|
||||
public
|
||||
long total() {
|
||||
return byteBuf.readerIndex() - startIndex;
|
||||
}
|
||||
|
||||
/** Returns the current position in the buffer. */
|
||||
/**
|
||||
* Returns the current position in the buffer.
|
||||
*/
|
||||
@Override
|
||||
public int position () {
|
||||
public
|
||||
int position() {
|
||||
return byteBuf.readerIndex();
|
||||
}
|
||||
|
||||
/** Sets the current position in the buffer. */
|
||||
/**
|
||||
* Sets the current position in the buffer.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setPosition (int position) {
|
||||
public
|
||||
void setPosition(int position) {
|
||||
throw new RuntimeException("Cannot access this method!");
|
||||
}
|
||||
|
||||
/** Returns the limit for the buffer. */
|
||||
/**
|
||||
* Returns the limit for the buffer.
|
||||
*/
|
||||
@Override
|
||||
public int limit () {
|
||||
public
|
||||
int limit() {
|
||||
return byteBuf.writerIndex();
|
||||
}
|
||||
|
||||
/** Sets the limit in the buffer. */
|
||||
/**
|
||||
* Sets the limit in the buffer.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void setLimit (int limit) {
|
||||
public
|
||||
void setLimit(int limit) {
|
||||
throw new RuntimeException("Cannot access this method!");
|
||||
}
|
||||
|
||||
/** Sets the position and total to zero. */
|
||||
/**
|
||||
* Sets the position and total to zero.
|
||||
*/
|
||||
@Override
|
||||
public void rewind () {
|
||||
public
|
||||
void rewind() {
|
||||
byteBuf.readerIndex(startIndex);
|
||||
}
|
||||
|
||||
/** Discards the specified number of bytes. */
|
||||
/**
|
||||
* Discards the specified number of bytes.
|
||||
*/
|
||||
@Override
|
||||
public void skip (int count) throws KryoException {
|
||||
public
|
||||
void skip(int count) throws KryoException {
|
||||
byteBuf.skipBytes(count);
|
||||
}
|
||||
|
||||
/** Fills the buffer with more bytes. Can be overridden to fill the bytes from a source other than the InputStream. */
|
||||
/**
|
||||
* Fills the buffer with more bytes. Can be overridden to fill the bytes from a source other than the InputStream.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
protected int fill (byte[] buffer, int offset, int count) throws KryoException {
|
||||
protected
|
||||
int fill(byte[] buffer, int offset, int count) throws KryoException {
|
||||
throw new RuntimeException("Cannot access this method!");
|
||||
}
|
||||
|
||||
|
||||
// InputStream
|
||||
|
||||
/** Reads a single byte as an int from 0 to 255, or -1 if there are no more bytes are available. */
|
||||
/**
|
||||
* Reads a single byte as an int from 0 to 255, or -1 if there are no more bytes are available.
|
||||
*/
|
||||
@Override
|
||||
public int read () throws KryoException {
|
||||
public
|
||||
int read() throws KryoException {
|
||||
return byteBuf.readByte() & 0xFF;
|
||||
}
|
||||
|
||||
/** Reads bytes.length bytes or less and writes them to the specified byte[], starting at 0, and returns the number of bytes
|
||||
* read. */
|
||||
/**
|
||||
* Reads bytes.length bytes or less and writes them to the specified byte[], starting at 0, and returns the number of bytes
|
||||
* read.
|
||||
*/
|
||||
@Override
|
||||
public int read (byte[] bytes) throws KryoException {
|
||||
public
|
||||
int read(byte[] bytes) throws KryoException {
|
||||
int start = byteBuf.readerIndex();
|
||||
byteBuf.readBytes(bytes);
|
||||
int end = byteBuf.readerIndex();
|
||||
return end - start; // return how many bytes were actually read.
|
||||
}
|
||||
|
||||
/** Reads count bytes or less and writes them to the specified byte[], starting at offset, and returns the number of bytes read
|
||||
* or -1 if no more bytes are available. */
|
||||
/**
|
||||
* Reads count bytes or less and writes them to the specified byte[], starting at offset, and returns the number of bytes read
|
||||
* or -1 if no more bytes are available.
|
||||
*/
|
||||
@Override
|
||||
public int read (byte[] bytes, int offset, int count) throws KryoException {
|
||||
public
|
||||
int read(byte[] bytes, int offset, int count) throws KryoException {
|
||||
if (bytes == null) {
|
||||
throw new IllegalArgumentException("bytes cannot be null.");
|
||||
}
|
||||
|
@ -171,56 +223,77 @@ public class ByteBufInput extends Input {
|
|||
return end - start; // return how many bytes were actually read.
|
||||
}
|
||||
|
||||
/** Discards the specified number of bytes. */
|
||||
/**
|
||||
* Discards the specified number of bytes.
|
||||
*/
|
||||
@Override
|
||||
public long skip (long count) throws KryoException {
|
||||
public
|
||||
long skip(long count) throws KryoException {
|
||||
long remaining = count;
|
||||
while (remaining > 0) {
|
||||
int skip = Math.max(Integer.MAX_VALUE, (int)remaining);
|
||||
int skip = Math.max(Integer.MAX_VALUE, (int) remaining);
|
||||
skip(skip);
|
||||
remaining -= skip;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/** Closes the underlying InputStream, if any. */
|
||||
/**
|
||||
* Closes the underlying InputStream, if any.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
public void close () throws KryoException {
|
||||
public
|
||||
void close() throws KryoException {
|
||||
// does nothing.
|
||||
}
|
||||
|
||||
// byte
|
||||
|
||||
/** Reads a single byte. */
|
||||
/**
|
||||
* Reads a single byte.
|
||||
*/
|
||||
@Override
|
||||
public byte readByte () throws KryoException {
|
||||
public
|
||||
byte readByte() throws KryoException {
|
||||
return byteBuf.readByte();
|
||||
}
|
||||
|
||||
/** Reads a byte as an int from 0 to 255. */
|
||||
/**
|
||||
* Reads a byte as an int from 0 to 255.
|
||||
*/
|
||||
@Override
|
||||
public int readByteUnsigned () throws KryoException {
|
||||
public
|
||||
int readByteUnsigned() throws KryoException {
|
||||
return byteBuf.readUnsignedByte();
|
||||
}
|
||||
|
||||
/** Reads the specified number of bytes into a new byte[]. */
|
||||
/**
|
||||
* Reads the specified number of bytes into a new byte[].
|
||||
*/
|
||||
@Override
|
||||
public byte[] readBytes (int length) throws KryoException {
|
||||
public
|
||||
byte[] readBytes(int length) throws KryoException {
|
||||
byte[] bytes = new byte[length];
|
||||
readBytes(bytes, 0, length);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/** Reads bytes.length bytes and writes them to the specified byte[], starting at index 0. */
|
||||
/**
|
||||
* Reads bytes.length bytes and writes them to the specified byte[], starting at index 0.
|
||||
*/
|
||||
@Override
|
||||
public void readBytes (byte[] bytes) throws KryoException {
|
||||
public
|
||||
void readBytes(byte[] bytes) throws KryoException {
|
||||
readBytes(bytes, 0, bytes.length);
|
||||
}
|
||||
|
||||
/** Reads count bytes and writes them to the specified byte[], starting at offset. */
|
||||
/**
|
||||
* Reads count bytes and writes them to the specified byte[], starting at offset.
|
||||
*/
|
||||
@Override
|
||||
public void readBytes (byte[] bytes, int offset, int count) throws KryoException {
|
||||
public
|
||||
void readBytes(byte[] bytes, int offset, int count) throws KryoException {
|
||||
if (bytes == null) {
|
||||
throw new IllegalArgumentException("bytes cannot be null.");
|
||||
}
|
||||
|
@ -230,24 +303,33 @@ public class ByteBufInput extends Input {
|
|||
|
||||
// int
|
||||
|
||||
/** Reads a 4 byte int. */
|
||||
/**
|
||||
* Reads a 4 byte int.
|
||||
*/
|
||||
@Override
|
||||
public int readInt () throws KryoException {
|
||||
public
|
||||
int readInt() throws KryoException {
|
||||
return byteBuf.readInt();
|
||||
}
|
||||
|
||||
|
||||
/** Reads a 1-5 byte int. This stream may consider such a variable length encoding request as a hint. It is not guaranteed that
|
||||
/**
|
||||
* Reads a 1-5 byte int. This stream may consider such a variable length encoding request as a hint. It is not guaranteed that
|
||||
* a variable length encoding will be really used. The stream may decide to use native-sized integer representation for
|
||||
* efficiency reasons. **/
|
||||
* efficiency reasons.
|
||||
**/
|
||||
@Override
|
||||
public int readInt (boolean optimizePositive) throws KryoException {
|
||||
public
|
||||
int readInt(boolean optimizePositive) throws KryoException {
|
||||
return readVarInt(optimizePositive);
|
||||
}
|
||||
|
||||
/** Reads a 1-5 byte int. It is guaranteed that a varible length encoding will be used. */
|
||||
/**
|
||||
* Reads a 1-5 byte int. It is guaranteed that a variable length encoding will be used.
|
||||
*/
|
||||
@Override
|
||||
public int readVarInt (boolean optimizePositive) throws KryoException {
|
||||
public
|
||||
int readVarInt(boolean optimizePositive) throws KryoException {
|
||||
ByteBuf buffer = byteBuf;
|
||||
|
||||
int b = buffer.readByte();
|
||||
|
@ -272,9 +354,12 @@ public class ByteBufInput extends Input {
|
|||
}
|
||||
|
||||
|
||||
/** Returns true if enough bytes are available to read an int with {@link #readInt(boolean)}. */
|
||||
/**
|
||||
* Returns true if enough bytes are available to read an int with {@link #readInt(boolean)}.
|
||||
*/
|
||||
@Override
|
||||
public boolean canReadInt () throws KryoException {
|
||||
public
|
||||
boolean canReadInt() throws KryoException {
|
||||
ByteBuf buffer = byteBuf;
|
||||
int limit = buffer.writerIndex();
|
||||
|
||||
|
@ -309,9 +394,12 @@ public class ByteBufInput extends Input {
|
|||
return true;
|
||||
}
|
||||
|
||||
/** Returns true if enough bytes are available to read a long with {@link #readLong(boolean)}. */
|
||||
/**
|
||||
* Returns true if enough bytes are available to read a long with {@link #readLong(boolean)}.
|
||||
*/
|
||||
@Override
|
||||
public boolean canReadLong () throws KryoException {
|
||||
public
|
||||
boolean canReadLong() throws KryoException {
|
||||
ByteBuf buffer = byteBuf;
|
||||
int limit = buffer.writerIndex();
|
||||
|
||||
|
@ -372,25 +460,28 @@ public class ByteBufInput extends Input {
|
|||
|
||||
// string
|
||||
|
||||
/** Reads the length and string of UTF8 characters, or null. This can read strings written by {@link Output#writeString(String)}
|
||||
* , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}.
|
||||
* @return May be null. */
|
||||
/**
|
||||
* Reads the length and string of UTF8 characters, or null. This can read strings written by {@link Output#writeString(String)}
|
||||
* , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}.
|
||||
*
|
||||
* @return May be null.
|
||||
*/
|
||||
@Override
|
||||
public String readString () {
|
||||
public
|
||||
String readString() {
|
||||
ByteBuf buffer = byteBuf;
|
||||
|
||||
int b = buffer.readByte();
|
||||
if ((b & 0x80) == 0)
|
||||
{
|
||||
if ((b & 0x80) == 0) {
|
||||
return readAscii(); // ASCII.
|
||||
}
|
||||
// Null, empty, or UTF8.
|
||||
int charCount = readUtf8Length(b);
|
||||
switch (charCount) {
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return "";
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return "";
|
||||
}
|
||||
charCount--;
|
||||
if (inputChars.length < charCount) {
|
||||
|
@ -400,7 +491,8 @@ public class ByteBufInput extends Input {
|
|||
return new String(inputChars, 0, charCount);
|
||||
}
|
||||
|
||||
private int readUtf8Length (int b) {
|
||||
private
|
||||
int readUtf8Length(int b) {
|
||||
int result = b & 0x3F; // Mask all but first 6 bits.
|
||||
if ((b & 0x40) != 0) { // Bit 7 means another byte, bit 8 means UTF8.
|
||||
ByteBuf buffer = byteBuf;
|
||||
|
@ -422,7 +514,8 @@ public class ByteBufInput extends Input {
|
|||
return result;
|
||||
}
|
||||
|
||||
private void readUtf8 (int charCount) {
|
||||
private
|
||||
void readUtf8(int charCount) {
|
||||
ByteBuf buffer = byteBuf;
|
||||
char[] chars = this.inputChars; // will be the correct size.
|
||||
// Try to read 7 bit ASCII chars.
|
||||
|
@ -432,10 +525,10 @@ public class ByteBufInput extends Input {
|
|||
while (charIndex < count) {
|
||||
b = buffer.readByte();
|
||||
if (b < 0) {
|
||||
buffer.readerIndex(buffer.readerIndex()-1);
|
||||
buffer.readerIndex(buffer.readerIndex() - 1);
|
||||
break;
|
||||
}
|
||||
chars[charIndex++] = (char)b;
|
||||
chars[charIndex++] = (char) b;
|
||||
}
|
||||
// If buffer didn't hold all chars or any were not ASCII, use slow path for remainder.
|
||||
if (charIndex < charCount) {
|
||||
|
@ -443,35 +536,37 @@ public class ByteBufInput extends Input {
|
|||
}
|
||||
}
|
||||
|
||||
private void readUtf8_slow (int charCount, int charIndex) {
|
||||
private
|
||||
void readUtf8_slow(int charCount, int charIndex) {
|
||||
ByteBuf buffer = byteBuf;
|
||||
char[] chars = this.inputChars;
|
||||
while (charIndex < charCount) {
|
||||
int b = buffer.readByte() & 0xFF;
|
||||
switch (b >> 4) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
chars[charIndex] = (char)b;
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
chars[charIndex] = (char)((b & 0x1F) << 6 | buffer.readByte() & 0x3F);
|
||||
break;
|
||||
case 14:
|
||||
chars[charIndex] = (char)((b & 0x0F) << 12 | (buffer.readByte() & 0x3F) << 6 | buffer.readByte() & 0x3F);
|
||||
break;
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
chars[charIndex] = (char) b;
|
||||
break;
|
||||
case 12:
|
||||
case 13:
|
||||
chars[charIndex] = (char) ((b & 0x1F) << 6 | buffer.readByte() & 0x3F);
|
||||
break;
|
||||
case 14:
|
||||
chars[charIndex] = (char) ((b & 0x0F) << 12 | (buffer.readByte() & 0x3F) << 6 | buffer.readByte() & 0x3F);
|
||||
break;
|
||||
}
|
||||
charIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
private String readAscii () {
|
||||
private
|
||||
String readAscii() {
|
||||
ByteBuf buffer = byteBuf;
|
||||
|
||||
int start = buffer.readerIndex() - 1;
|
||||
|
@ -479,7 +574,7 @@ public class ByteBufInput extends Input {
|
|||
do {
|
||||
b = buffer.readByte();
|
||||
} while ((b & 0x80) == 0);
|
||||
int i = buffer.readerIndex()-1;
|
||||
int i = buffer.readerIndex() - 1;
|
||||
buffer.setByte(i, buffer.getByte(i) & 0x7F); // Mask end of ascii bit.
|
||||
|
||||
int capp = buffer.readerIndex() - start;
|
||||
|
@ -495,25 +590,28 @@ public class ByteBufInput extends Input {
|
|||
}
|
||||
|
||||
|
||||
/** Reads the length and string of UTF8 characters, or null. This can read strings written by {@link Output#writeString(String)}
|
||||
* , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}.
|
||||
* @return May be null. */
|
||||
/**
|
||||
* Reads the length and string of UTF8 characters, or null. This can read strings written by {@link Output#writeString(String)}
|
||||
* , {@link Output#writeString(CharSequence)}, and {@link Output#writeAscii(String)}.
|
||||
*
|
||||
* @return May be null.
|
||||
*/
|
||||
@Override
|
||||
public StringBuilder readStringBuilder () {
|
||||
public
|
||||
StringBuilder readStringBuilder() {
|
||||
ByteBuf buffer = byteBuf;
|
||||
|
||||
int b = buffer.readByte();
|
||||
if ((b & 0x80) == 0)
|
||||
{
|
||||
if ((b & 0x80) == 0) {
|
||||
return new StringBuilder(readAscii()); // ASCII.
|
||||
}
|
||||
// Null, empty, or UTF8.
|
||||
int charCount = readUtf8Length(b);
|
||||
switch (charCount) {
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return new StringBuilder("");
|
||||
case 0:
|
||||
return null;
|
||||
case 1:
|
||||
return new StringBuilder("");
|
||||
}
|
||||
charCount--;
|
||||
if (inputChars.length < charCount) {
|
||||
|
@ -528,43 +626,61 @@ public class ByteBufInput extends Input {
|
|||
|
||||
// float
|
||||
|
||||
/** Reads a 4 byte float. */
|
||||
/**
|
||||
* Reads a 4 byte float.
|
||||
*/
|
||||
@Override
|
||||
public float readFloat () throws KryoException {
|
||||
public
|
||||
float readFloat() throws KryoException {
|
||||
return Float.intBitsToFloat(readInt());
|
||||
}
|
||||
|
||||
/** Reads a 1-5 byte float with reduced precision. */
|
||||
/**
|
||||
* Reads a 1-5 byte float with reduced precision.
|
||||
*/
|
||||
@Override
|
||||
public float readFloat (float precision, boolean optimizePositive) throws KryoException {
|
||||
public
|
||||
float readFloat(float precision, boolean optimizePositive) throws KryoException {
|
||||
return readInt(optimizePositive) / precision;
|
||||
}
|
||||
|
||||
// short
|
||||
|
||||
/** Reads a 2 byte short. */
|
||||
/**
|
||||
* Reads a 2 byte short.
|
||||
*/
|
||||
@Override
|
||||
public short readShort () throws KryoException {
|
||||
public
|
||||
short readShort() throws KryoException {
|
||||
return byteBuf.readShort();
|
||||
}
|
||||
|
||||
/** Reads a 2 byte short as an int from 0 to 65535. */
|
||||
/**
|
||||
* Reads a 2 byte short as an int from 0 to 65535.
|
||||
*/
|
||||
@Override
|
||||
public int readShortUnsigned () throws KryoException {
|
||||
public
|
||||
int readShortUnsigned() throws KryoException {
|
||||
return byteBuf.readUnsignedShort();
|
||||
}
|
||||
|
||||
// long
|
||||
|
||||
/** Reads an 8 byte long. */
|
||||
/**
|
||||
* Reads an 8 byte long.
|
||||
*/
|
||||
@Override
|
||||
public long readLong () throws KryoException {
|
||||
public
|
||||
long readLong() throws KryoException {
|
||||
return byteBuf.readLong();
|
||||
}
|
||||
|
||||
/** Reads a 1-9 byte long. */
|
||||
/**
|
||||
* Reads a 1-9 byte long.
|
||||
*/
|
||||
@Override
|
||||
public long readLong (boolean optimizePositive) throws KryoException {
|
||||
public
|
||||
long readLong(boolean optimizePositive) throws KryoException {
|
||||
ByteBuf buffer = byteBuf;
|
||||
int b = buffer.readByte();
|
||||
long result = b & 0x7F;
|
||||
|
@ -579,19 +695,19 @@ public class ByteBufInput extends Input {
|
|||
result |= (b & 0x7F) << 21;
|
||||
if ((b & 0x80) != 0) {
|
||||
b = buffer.readByte();
|
||||
result |= (long)(b & 0x7F) << 28;
|
||||
result |= (long) (b & 0x7F) << 28;
|
||||
if ((b & 0x80) != 0) {
|
||||
b = buffer.readByte();
|
||||
result |= (long)(b & 0x7F) << 35;
|
||||
result |= (long) (b & 0x7F) << 35;
|
||||
if ((b & 0x80) != 0) {
|
||||
b = buffer.readByte();
|
||||
result |= (long)(b & 0x7F) << 42;
|
||||
result |= (long) (b & 0x7F) << 42;
|
||||
if ((b & 0x80) != 0) {
|
||||
b = buffer.readByte();
|
||||
result |= (long)(b & 0x7F) << 49;
|
||||
result |= (long) (b & 0x7F) << 49;
|
||||
if ((b & 0x80) != 0) {
|
||||
b = buffer.readByte();
|
||||
result |= (long)b << 56;
|
||||
result |= (long) b << 56;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -609,31 +725,43 @@ public class ByteBufInput extends Input {
|
|||
|
||||
// boolean
|
||||
|
||||
/** Reads a 1 byte boolean. */
|
||||
/**
|
||||
* Reads a 1 byte boolean.
|
||||
*/
|
||||
@Override
|
||||
public boolean readBoolean () throws KryoException {
|
||||
public
|
||||
boolean readBoolean() throws KryoException {
|
||||
return byteBuf.readBoolean();
|
||||
}
|
||||
|
||||
// char
|
||||
|
||||
/** Reads a 2 byte char. */
|
||||
/**
|
||||
* Reads a 2 byte char.
|
||||
*/
|
||||
@Override
|
||||
public char readChar () throws KryoException {
|
||||
public
|
||||
char readChar() throws KryoException {
|
||||
return byteBuf.readChar();
|
||||
}
|
||||
|
||||
// double
|
||||
|
||||
/** Reads an 8 bytes double. */
|
||||
/**
|
||||
* Reads an 8 bytes double.
|
||||
*/
|
||||
@Override
|
||||
public double readDouble () throws KryoException {
|
||||
public
|
||||
double readDouble() throws KryoException {
|
||||
return Double.longBitsToDouble(readLong());
|
||||
}
|
||||
|
||||
/** Reads a 1-9 byte double with reduced precision. */
|
||||
/**
|
||||
* Reads a 1-9 byte double with reduced precision.
|
||||
*/
|
||||
@Override
|
||||
public double readDouble (double precision, boolean optimizePositive) throws KryoException {
|
||||
public
|
||||
double readDouble(double precision, boolean optimizePositive) throws KryoException {
|
||||
return readLong(optimizePositive) / precision;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
package dorkbox.network.pipeline;
|
||||
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteBuf;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteBuf;
|
||||
|
||||
public class KryoDecoder extends ByteToMessageDecoder {
|
||||
private final OptimizeUtilsByteBuf optimize;
|
||||
private final SerializationManager kryoWrapper;
|
||||
private final CryptoSerializationManager kryoWrapper;
|
||||
|
||||
public KryoDecoder(SerializationManager kryoWrapper) {
|
||||
public KryoDecoder(CryptoSerializationManager kryoWrapper) {
|
||||
super();
|
||||
this.kryoWrapper = kryoWrapper;
|
||||
this.optimize = OptimizeUtilsByteBuf.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
protected Object readObject(SerializationManager kryoWrapper, ChannelHandlerContext context, ByteBuf in, int length) {
|
||||
protected Object readObject(CryptoSerializationManager kryoWrapper, ChannelHandlerContext context, ByteBuf in, int length) {
|
||||
// no connection here because we haven't created one yet. When we do, we replace this handler with a new one.
|
||||
return kryoWrapper.read(in, length);
|
||||
}
|
||||
|
@ -96,13 +95,11 @@ public class KryoDecoder extends ByteToMessageDecoder {
|
|||
// how many more objects?? The first time, it can be off, because we already KNOW it's > 0.
|
||||
// (That's how we got here to begin with)
|
||||
while (readableBytes > 0) {
|
||||
objectCount++;
|
||||
if (optimize.canReadInt(in) > 0) {
|
||||
length = optimize.readInt(in, true);
|
||||
|
||||
if (length <= 0) {
|
||||
// throw new IllegalStateException("Kryo DecoderTCP had a read length of 0");
|
||||
objectCount--;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -112,12 +109,11 @@ public class KryoDecoder extends ByteToMessageDecoder {
|
|||
if (endOfObjectPosition <= writerIndex) {
|
||||
in.readerIndex(endOfObjectPosition);
|
||||
readableBytes = in.readableBytes();
|
||||
objectCount++;
|
||||
} else {
|
||||
objectCount--;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
objectCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package dorkbox.network.pipeline;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
@ -11,12 +11,12 @@ import io.netty.channel.ChannelHandlerContext;
|
|||
|
||||
public class KryoDecoderCrypto extends KryoDecoder {
|
||||
|
||||
public KryoDecoderCrypto(SerializationManager kryoWrapper) {
|
||||
public KryoDecoderCrypto(CryptoSerializationManager kryoWrapper) {
|
||||
super(kryoWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object readObject(SerializationManager kryoWrapper, ChannelHandlerContext ctx, ByteBuf in, int length) {
|
||||
protected Object readObject(CryptoSerializationManager kryoWrapper, ChannelHandlerContext ctx, ByteBuf in, int length) {
|
||||
ChannelHandler last = ctx.pipeline().last();
|
||||
if (last instanceof Connection) {
|
||||
return kryoWrapper.readWithCryptoTcp((Connection) last, in, length);
|
||||
|
|
|
@ -7,18 +7,18 @@ import io.netty.handler.codec.MessageToByteEncoder;
|
|||
|
||||
import com.esotericsoftware.kryo.KryoException;
|
||||
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import dorkbox.util.bytes.OptimizeUtilsByteBuf;
|
||||
|
||||
@Sharable
|
||||
public class KryoEncoder extends MessageToByteEncoder<Object> {
|
||||
private static final int reservedLengthIndex = 4;
|
||||
private final SerializationManager kryoWrapper;
|
||||
private final CryptoSerializationManager kryoWrapper;
|
||||
private final OptimizeUtilsByteBuf optimize;
|
||||
|
||||
|
||||
public KryoEncoder(SerializationManager kryoWrapper) {
|
||||
public KryoEncoder(CryptoSerializationManager kryoWrapper) {
|
||||
super();
|
||||
this.kryoWrapper = kryoWrapper;
|
||||
this.optimize = OptimizeUtilsByteBuf.get();
|
||||
|
@ -26,7 +26,7 @@ public class KryoEncoder extends MessageToByteEncoder<Object> {
|
|||
|
||||
// the crypto writer will override this
|
||||
@SuppressWarnings("unused")
|
||||
protected void writeObject(SerializationManager kryoWrapper, ChannelHandlerContext context, Object msg, ByteBuf buffer) {
|
||||
protected void writeObject(CryptoSerializationManager kryoWrapper, ChannelHandlerContext context, Object msg, ByteBuf buffer) {
|
||||
// no connection here because we haven't created one yet. When we do, we replace this handler with a new one.
|
||||
kryoWrapper.write(buffer, msg);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package dorkbox.network.pipeline;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
@ -11,12 +11,12 @@ import io.netty.channel.ChannelHandlerContext;
|
|||
@Sharable
|
||||
public class KryoEncoderCrypto extends KryoEncoder {
|
||||
|
||||
public KryoEncoderCrypto(SerializationManager kryoWrapper) {
|
||||
public KryoEncoderCrypto(CryptoSerializationManager kryoWrapper) {
|
||||
super(kryoWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeObject(SerializationManager kryoWrapper, ChannelHandlerContext ctx, Object msg, ByteBuf buffer) {
|
||||
protected void writeObject(CryptoSerializationManager kryoWrapper, ChannelHandlerContext ctx, Object msg, ByteBuf buffer) {
|
||||
ChannelHandler last = ctx.pipeline().last();
|
||||
if (last instanceof Connection) {
|
||||
kryoWrapper.writeWithCryptoTcp((Connection) last, buffer, msg);
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package dorkbox.network.pipeline.udp;
|
||||
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
@ -8,20 +10,20 @@ import io.netty.handler.codec.MessageToMessageDecoder;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
|
||||
@Sharable
|
||||
public class KryoDecoderUdp extends MessageToMessageDecoder<DatagramPacket> {
|
||||
public
|
||||
class KryoDecoderUdp extends MessageToMessageDecoder<DatagramPacket> {
|
||||
|
||||
private final SerializationManager kryoWrapper;
|
||||
private final CryptoSerializationManager kryoWrapper;
|
||||
|
||||
public KryoDecoderUdp(SerializationManager kryoWrapper) {
|
||||
public
|
||||
KryoDecoderUdp(CryptoSerializationManager kryoWrapper) {
|
||||
this.kryoWrapper = kryoWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {
|
||||
protected
|
||||
void decode(ChannelHandlerContext ctx, DatagramPacket msg, List<Object> out) throws Exception {
|
||||
if (msg != null) {
|
||||
ByteBuf data = msg.content();
|
||||
|
||||
|
@ -30,7 +32,7 @@ public class KryoDecoderUdp extends MessageToMessageDecoder<DatagramPacket> {
|
|||
// COULD be encrypted!
|
||||
|
||||
if (kryoWrapper.isEncrypted(data)) {
|
||||
throw new NetException("Encrypted UDP packet recieved before registration complete. WHOOPS!");
|
||||
throw new NetException("Encrypted UDP packet received before registration complete. WHOOPS!");
|
||||
}
|
||||
|
||||
// no connection here because we haven't created one yet. When we do, we replace this handler with a new one.
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package dorkbox.network.pipeline.udp;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
|
@ -9,27 +12,29 @@ import io.netty.handler.codec.MessageToMessageDecoder;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
|
||||
@Sharable
|
||||
public class KryoDecoderUdpCrypto extends MessageToMessageDecoder<DatagramPacket> {
|
||||
public
|
||||
class KryoDecoderUdpCrypto extends MessageToMessageDecoder<DatagramPacket> {
|
||||
|
||||
private final SerializationManager kryoWrapper;
|
||||
private final CryptoSerializationManager kryoWrapper;
|
||||
|
||||
public KryoDecoderUdpCrypto(SerializationManager kryoWrapper) {
|
||||
public
|
||||
KryoDecoderUdpCrypto(CryptoSerializationManager kryoWrapper) {
|
||||
this.kryoWrapper = kryoWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void decode(ChannelHandlerContext ctx, DatagramPacket in, List<Object> out) throws Exception {
|
||||
ChannelHandler last = ctx.pipeline().last();
|
||||
public
|
||||
void decode(ChannelHandlerContext ctx, DatagramPacket in, List<Object> out) throws Exception {
|
||||
ChannelHandler last = ctx.pipeline()
|
||||
.last();
|
||||
|
||||
if (last instanceof Connection) {
|
||||
ByteBuf data = in.content();
|
||||
Object object = kryoWrapper.readWithCryptoUdp((Connection) last, data, data.readableBytes());
|
||||
out.add(object);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// SHOULD NEVER HAPPEN!
|
||||
throw new NetException("Tried to use kryo to READ an object with NO network connection!");
|
||||
}
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
package dorkbox.network.pipeline.udp;
|
||||
|
||||
import com.esotericsoftware.kryo.KryoException;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandler.Sharable;
|
||||
|
@ -10,35 +14,33 @@ import io.netty.handler.codec.MessageToMessageEncoder;
|
|||
import java.net.InetSocketAddress;
|
||||
import java.util.List;
|
||||
|
||||
import com.esotericsoftware.kryo.KryoException;
|
||||
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
|
||||
@Sharable
|
||||
// UDP uses messages --- NOT bytebuf!
|
||||
// ONLY USED BY THE CLIENT (the server has it's own handler!)
|
||||
public class KryoEncoderUdp extends MessageToMessageEncoder<Object> {
|
||||
public
|
||||
class KryoEncoderUdp extends MessageToMessageEncoder<Object> {
|
||||
|
||||
private final static int maxSize = EndPoint.udpMaxSize;
|
||||
private SerializationManager kryoWrapper;
|
||||
private static final int maxSize = EndPoint.udpMaxSize;
|
||||
private final CryptoSerializationManager kryoWrapper;
|
||||
|
||||
|
||||
public KryoEncoderUdp(SerializationManager kryoWrapper) {
|
||||
public
|
||||
KryoEncoderUdp(CryptoSerializationManager kryoWrapper) {
|
||||
super();
|
||||
this.kryoWrapper = kryoWrapper;
|
||||
}
|
||||
|
||||
// the crypto writer will override this
|
||||
@SuppressWarnings("unused")
|
||||
protected void writeObject(SerializationManager kryoWrapper, ChannelHandlerContext context, Object msg, ByteBuf buffer) {
|
||||
protected
|
||||
void writeObject(CryptoSerializationManager kryoWrapper, ChannelHandlerContext context, Object msg, ByteBuf buffer) {
|
||||
// no connection here because we haven't created one yet. When we do, we replace this handler with a new one.
|
||||
kryoWrapper.write(buffer, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
|
||||
protected
|
||||
void encode(ChannelHandlerContext ctx, Object msg, List<Object> out) throws Exception {
|
||||
if (msg != null) {
|
||||
try {
|
||||
ByteBuf outBuffer = Unpooled.buffer(maxSize);
|
||||
|
@ -50,13 +52,17 @@ public class KryoEncoderUdp extends MessageToMessageEncoder<Object> {
|
|||
// have to check to see if we are too big for UDP!
|
||||
if (outBuffer.readableBytes() > EndPoint.udpMaxSize) {
|
||||
System.err.println("Object larger than MAX udp size! " + EndPoint.udpMaxSize + "/" + outBuffer.readableBytes());
|
||||
throw new NetException("Object is TOO BIG FOR UDP! " + msg.toString() + " (" + EndPoint.udpMaxSize + "/" + outBuffer.readableBytes() + ")");
|
||||
throw new NetException("Object is TOO BIG FOR UDP! " + msg.toString() + " (" + EndPoint.udpMaxSize + "/" +
|
||||
outBuffer.readableBytes() + ")");
|
||||
}
|
||||
|
||||
DatagramPacket packet = new DatagramPacket(outBuffer, (InetSocketAddress) ctx.channel().remoteAddress());
|
||||
DatagramPacket packet = new DatagramPacket(outBuffer,
|
||||
(InetSocketAddress) ctx.channel()
|
||||
.remoteAddress());
|
||||
out.add(packet);
|
||||
} catch (KryoException ex) {
|
||||
throw new NetException("Unable to serialize object of type: " + msg.getClass().getName(), ex);
|
||||
throw new NetException("Unable to serialize object of type: " + msg.getClass()
|
||||
.getName(), ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package dorkbox.network.pipeline.udp;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
|
@ -9,18 +9,24 @@ import io.netty.channel.ChannelHandler.Sharable;
|
|||
import io.netty.channel.ChannelHandlerContext;
|
||||
|
||||
@Sharable
|
||||
public class KryoEncoderUdpCrypto extends KryoEncoderUdp {
|
||||
public
|
||||
class KryoEncoderUdpCrypto extends KryoEncoderUdp {
|
||||
|
||||
public KryoEncoderUdpCrypto(SerializationManager kryoWrapper) {
|
||||
public
|
||||
KryoEncoderUdpCrypto(CryptoSerializationManager kryoWrapper) {
|
||||
super(kryoWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeObject(SerializationManager kryoWrapper, ChannelHandlerContext ctx, Object msg, ByteBuf buffer) {
|
||||
ChannelHandler last = ctx.pipeline().last();
|
||||
protected
|
||||
void writeObject(CryptoSerializationManager kryoWrapper, ChannelHandlerContext ctx, Object msg, ByteBuf buffer) {
|
||||
ChannelHandler last = ctx.pipeline()
|
||||
.last();
|
||||
|
||||
if (last instanceof Connection) {
|
||||
kryoWrapper.writeWithCryptoUdp((Connection) last, buffer, msg);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
// SHOULD NEVER HAPPEN!
|
||||
throw new NetException("Tried to use kryo to WRITE an object with NO network connection!");
|
||||
}
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
import com.esotericsoftware.reflectasm.MethodAccess;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public
|
||||
class AsmCachedMethod extends CachedMethod {
|
||||
MethodAccess methodAccess;
|
||||
int methodAccessIndex = -1;
|
||||
public MethodAccess methodAccess;
|
||||
public int methodAccessIndex = -1;
|
||||
|
||||
@Override
|
||||
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
import com.esotericsoftware.kryo.Serializer;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import com.esotericsoftware.kryo.Serializer;
|
||||
|
||||
public
|
||||
class CachedMethod {
|
||||
Method method;
|
||||
int methodClassID;
|
||||
int methodIndex;
|
||||
public Method method;
|
||||
public int methodClassID;
|
||||
public int methodIndex;
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Serializer[] serializers;
|
||||
public Serializer[] serializers;
|
||||
|
||||
public Object invoke(Object target, Object[] args) throws IllegalAccessException, InvocationTargetException {
|
||||
return this.method.invoke(target, args);
|
||||
|
|
|
@ -2,6 +2,7 @@ package dorkbox.network.rmi;
|
|||
|
||||
|
||||
/** Internal message to invoke methods remotely. */
|
||||
public
|
||||
class InvokeMethod implements RmiMessages {
|
||||
public int objectID;
|
||||
public CachedMethod cachedMethod;
|
||||
|
|
|
@ -5,8 +5,10 @@ import com.esotericsoftware.kryo.KryoException;
|
|||
import com.esotericsoftware.kryo.Serializer;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import dorkbox.network.connection.KryoExtra;
|
||||
|
||||
/** Internal message to invoke methods remotely. */
|
||||
public
|
||||
class InvokeMethodSerializer extends Serializer<InvokeMethod> {
|
||||
private RmiBridge rmi;
|
||||
|
||||
|
@ -46,7 +48,9 @@ class InvokeMethodSerializer extends Serializer<InvokeMethod> {
|
|||
|
||||
byte methodIndex = input.readByte();
|
||||
try {
|
||||
invokeMethod.cachedMethod = this.rmi.getMethods(kryo, methodClass)[methodIndex];
|
||||
KryoExtra kryoExtra = (KryoExtra) kryo;
|
||||
|
||||
invokeMethod.cachedMethod = kryoExtra.getMethods(methodClass)[methodIndex];
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new KryoException("Invalid method index " + methodIndex + " for class: " + methodClass.getName());
|
||||
}
|
||||
|
|
|
@ -3,7 +3,9 @@ package dorkbox.network.rmi;
|
|||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.KryoExtra;
|
||||
import dorkbox.network.connection.ListenerRaw;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import dorkbox.util.objectPool.ObjectPool;
|
||||
|
||||
|
@ -15,11 +17,12 @@ import java.util.concurrent.locks.Condition;
|
|||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
/** Handles network communication when methods are invoked on a proxy. */
|
||||
public
|
||||
class RemoteInvocationHandler implements InvocationHandler {
|
||||
private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(RemoteInvocationHandler.class);
|
||||
private final Connection connection;
|
||||
|
||||
final int objectID;
|
||||
public final int objectID;
|
||||
private int timeoutMillis = 3000;
|
||||
|
||||
private boolean nonBlocking = false;
|
||||
|
@ -137,14 +140,24 @@ class RemoteInvocationHandler implements InvocationHandler {
|
|||
return "<proxy>";
|
||||
}
|
||||
|
||||
|
||||
EndPoint endPoint = this.connection.getEndPoint();
|
||||
RmiBridge rmi = (RmiBridge) endPoint.rmi();
|
||||
|
||||
InvokeMethod invokeMethod = this.invokeMethodPool.take();
|
||||
invokeMethod.objectID = this.objectID;
|
||||
invokeMethod.args = args;
|
||||
|
||||
CachedMethod[] cachedMethods = rmi.getMethods(endPoint.getSerialization().getSingleInstanceUnsafe(), method.getDeclaringClass());
|
||||
final CryptoSerializationManager serializationManager = endPoint.getSerialization();
|
||||
// thread safe access.
|
||||
final KryoExtra kryo = (KryoExtra) serializationManager.take();
|
||||
if (kryo == null) {
|
||||
String msg = "Interrupted during kryo pool.take()";
|
||||
logger.error(msg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
CachedMethod[] cachedMethods = kryo.getMethods(method.getDeclaringClass());
|
||||
serializationManager.release(kryo);
|
||||
for (int i = 0, n = cachedMethods.length; i < n; i++) {
|
||||
CachedMethod cachedMethod = cachedMethods[i];
|
||||
if (cachedMethod.method.equals(method)) {
|
||||
|
|
|
@ -4,9 +4,8 @@ import com.esotericsoftware.kryo.Kryo;
|
|||
import com.esotericsoftware.kryo.Serializer;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.ConnectionImpl;
|
||||
import dorkbox.network.connection.KryoExtra;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
|
||||
/**
|
||||
|
@ -18,15 +17,13 @@ import dorkbox.network.util.exceptions.NetException;
|
|||
*/
|
||||
public class RemoteObjectSerializer<T> extends Serializer<T> {
|
||||
|
||||
private final RmiBridge rmi;
|
||||
|
||||
public RemoteObjectSerializer(EndPoint endpoint) {
|
||||
this.rmi = (RmiBridge) endpoint.rmi();
|
||||
public RemoteObjectSerializer() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Kryo kryo, Output output, T object) {
|
||||
int id = this.rmi.getRegisteredId(object);
|
||||
KryoExtra kryoExtra = (KryoExtra) kryo;
|
||||
int id = kryoExtra.connection.getRegisteredId(object);
|
||||
if (id == Integer.MAX_VALUE) {
|
||||
throw new NetException("Object not found in an ObjectSpace: " + object);
|
||||
}
|
||||
|
@ -37,8 +34,9 @@ public class RemoteObjectSerializer<T> extends Serializer<T> {
|
|||
@SuppressWarnings({"rawtypes","unchecked"})
|
||||
@Override
|
||||
public T read(Kryo kryo, Input input, Class type) {
|
||||
KryoExtra kryoExtra = (KryoExtra) kryo;
|
||||
int objectID = input.readInt(true);
|
||||
Connection connection = (Connection) kryo.getContext().get(Connection.connection);
|
||||
return (T) this.rmi.getRemoteObject(connection, objectID, type);
|
||||
final ConnectionImpl connection = kryoExtra.connection;
|
||||
return (T) connection.rmiBridge.getRemoteObject(connection, objectID, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
14
Dorkbox-Network/src/dorkbox/network/rmi/RemoteProxy.java
Normal file
14
Dorkbox-Network/src/dorkbox/network/rmi/RemoteProxy.java
Normal file
|
@ -0,0 +1,14 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(value = RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Target(value = {ElementType.FIELD, ElementType.ANNOTATION_TYPE})
|
||||
public
|
||||
@interface RemoteProxy {
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
|
||||
|
||||
public interface Rmi {
|
||||
/**
|
||||
* Registers an object to allow the remote end of the RmiBridge connections to access it using the specified ID.
|
||||
*
|
||||
* @param objectID Must not be Integer.MAX_VALUE.
|
||||
*/
|
||||
public void register(int objectID, Object object);
|
||||
|
||||
/**
|
||||
* Removes an object. The remote end of the RmiBridge connection will no longer be able to access it.
|
||||
*/
|
||||
public void remove(int objectID);
|
||||
|
||||
/**
|
||||
* Removes an object. The remote end of the RmiBridge connection will no longer be able to access it.
|
||||
*/
|
||||
public void remove(Object object);
|
||||
}
|
|
@ -1,14 +1,7 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
|
@ -16,19 +9,11 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
|||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.Serializer;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import com.esotericsoftware.kryo.serializers.FieldSerializer;
|
||||
import com.esotericsoftware.kryo.util.IntMap;
|
||||
import com.esotericsoftware.kryo.util.Util;
|
||||
import com.esotericsoftware.reflectasm.MethodAccess;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.ListenerRaw;
|
||||
import dorkbox.network.util.SerializationManager;
|
||||
import dorkbox.network.util.exceptions.NetException;
|
||||
import dorkbox.util.collections.ObjectIntMap;
|
||||
import dorkbox.util.objectPool.ObjectPool;
|
||||
|
@ -36,11 +21,10 @@ import dorkbox.util.objectPool.ObjectPoolFactory;
|
|||
|
||||
/**
|
||||
* Allows methods on objects to be invoked remotely over TCP, UDP, or UDT. Objects are
|
||||
* {@link #register(int, Object) registered} with an ID. The remote end of
|
||||
* connections that have been {@link #addConnection(Connection) added} are
|
||||
* allowed to {@link #getRemoteObject(Connection, int, Class) access} registered
|
||||
* {@link #register(int, Object)} registered with an ID, and endpoint connections
|
||||
* can then {@link Connection#getRemoteObject(int, Class)} for registered
|
||||
* objects.
|
||||
* <p>
|
||||
* <p/>
|
||||
* It costs at least 2 bytes more to use remote method invocation than just
|
||||
* sending the parameters. If the method has a return value which is not
|
||||
* {@link RemoteObject#setNonBlocking(boolean) ignored}, an extra byte is
|
||||
|
@ -49,9 +33,8 @@ import dorkbox.util.objectPool.ObjectPoolFactory;
|
|||
*
|
||||
* @author Nathan Sweet <misc@n4te.com>, Nathan Robinson
|
||||
*/
|
||||
public class RmiBridge implements Rmi {
|
||||
private static final String OBJECT_ID = "objectID";
|
||||
|
||||
public
|
||||
class RmiBridge {
|
||||
static final int returnValueMask = 1 << 7;
|
||||
static final int returnExceptionMask = 1 << 6;
|
||||
static final int responseIdMask = 0xFF & ~returnValueMask & ~returnExceptionMask;
|
||||
|
@ -60,23 +43,20 @@ public class RmiBridge implements Rmi {
|
|||
// the name of who created this RmiBridge
|
||||
private final org.slf4j.Logger logger;
|
||||
|
||||
private HashMap<Class<?>, CachedMethod[]> methodCache = new HashMap<Class<?>, CachedMethod[]>();
|
||||
|
||||
private final boolean asm;
|
||||
|
||||
// can be accessed by DIFFERENT threads.
|
||||
private ReentrantReadWriteLock objectLock = new ReentrantReadWriteLock();
|
||||
private IntMap<Object> idToObject = new IntMap<Object>();
|
||||
private ObjectIntMap<Object> objectToID = new ObjectIntMap<Object>();
|
||||
private final ReentrantReadWriteLock objectLock = new ReentrantReadWriteLock();
|
||||
private final IntMap<Object> idToObject = new IntMap<Object>();
|
||||
private final ObjectIntMap<Object> objectToID = new ObjectIntMap<Object>();
|
||||
|
||||
private Executor executor;
|
||||
private final Executor executor;
|
||||
|
||||
// 4096 concurrent method invocations max
|
||||
private final ObjectPool<InvokeMethod> invokeMethodPool = ObjectPoolFactory.create(new InvokeMethodPoolable(), 4096);
|
||||
|
||||
private final ListenerRaw<Connection, InvokeMethod> invokeListener = new ListenerRaw<Connection, InvokeMethod>() {
|
||||
@Override
|
||||
public void received(final Connection connection, final InvokeMethod invokeMethod) {
|
||||
public
|
||||
void received(final Connection connection, final InvokeMethod invokeMethod) {
|
||||
ReadLock readLock = RmiBridge.this.objectLock.readLock();
|
||||
readLock.lock();
|
||||
|
||||
|
@ -96,16 +76,14 @@ public class RmiBridge implements Rmi {
|
|||
|
||||
Executor executor2 = RmiBridge.this.executor;
|
||||
if (executor2 == null) {
|
||||
invoke(connection,
|
||||
target,
|
||||
invokeMethod);
|
||||
} else {
|
||||
invoke(connection, target, invokeMethod);
|
||||
}
|
||||
else {
|
||||
executor2.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
invoke(connection,
|
||||
target,
|
||||
invokeMethod);
|
||||
public
|
||||
void run() {
|
||||
invoke(connection, target, invokeMethod);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -114,35 +92,128 @@ public class RmiBridge implements Rmi {
|
|||
|
||||
/**
|
||||
* Creates an RmiBridge with no connections. Connections must be
|
||||
* {@link #connectionConnected(Connection) added} to allow the remote end of
|
||||
* {@link RmiBridge#register(int, Object)} added to allow the remote end of
|
||||
* the connections to access objects in this ObjectSpace.
|
||||
*
|
||||
* @param executor Sets the executor used to invoke methods when an invocation is received
|
||||
* from a remote endpoint. By default, no executor is set and invocations
|
||||
* occur on the network thread, which should not be blocked for long, May be null.
|
||||
*/
|
||||
public RmiBridge(Logger logger, SerializationManager serializationManager) {
|
||||
public
|
||||
RmiBridge(final org.slf4j.Logger logger, final Executor executor) {
|
||||
this.logger = logger;
|
||||
this.asm = serializationManager.getSingleInstanceUnsafe().getAsmEnabled();
|
||||
|
||||
registerClasses(serializationManager);
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the executor used to invoke methods when an invocation is received
|
||||
* from a remote endpoint. By default, no executor is set and invocations
|
||||
* occur on the network thread, which should not be blocked for long.
|
||||
*
|
||||
* @param executor
|
||||
* May be null.
|
||||
* @return the invocation listener
|
||||
*/
|
||||
public void setExecutor(Executor executor) {
|
||||
this.executor = executor;
|
||||
@SuppressWarnings("rawtypes")
|
||||
public
|
||||
ListenerRaw getListener() {
|
||||
return this.invokeListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the method on the object and, if necessary, sends the result back
|
||||
* to the connection that made the invocation request. This method is
|
||||
* invoked on the update thread of the {@link EndPoint} for this RmiBridge
|
||||
* and unless an executor has been set.
|
||||
*
|
||||
* @param connection The remote side of this connection requested the invocation.
|
||||
*/
|
||||
protected
|
||||
void invoke(Connection connection, Object target, InvokeMethod invokeMethod) {
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isDebugEnabled()) {
|
||||
String argString = "";
|
||||
if (invokeMethod.args != null) {
|
||||
argString = Arrays.deepToString(invokeMethod.args);
|
||||
argString = argString.substring(1, argString.length() - 1);
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder(128);
|
||||
stringBuilder.append(connection.toString())
|
||||
.append(" received: ")
|
||||
.append(target.getClass()
|
||||
.getSimpleName());
|
||||
stringBuilder.append(":")
|
||||
.append(invokeMethod.objectID);
|
||||
stringBuilder.append("#")
|
||||
.append(invokeMethod.cachedMethod.method.getName());
|
||||
stringBuilder.append("(")
|
||||
.append(argString)
|
||||
.append(")");
|
||||
logger2.debug(stringBuilder.toString());
|
||||
}
|
||||
|
||||
|
||||
byte responseData = invokeMethod.responseData;
|
||||
boolean transmitReturnVal = (responseData & returnValueMask) == returnValueMask;
|
||||
boolean transmitExceptions = (responseData & returnExceptionMask) == returnExceptionMask;
|
||||
int responseID = responseData & responseIdMask;
|
||||
|
||||
Object result;
|
||||
CachedMethod cachedMethod = invokeMethod.cachedMethod;
|
||||
|
||||
try {
|
||||
result = cachedMethod.invoke(target, invokeMethod.args);
|
||||
} catch (Exception ex) {
|
||||
if (transmitExceptions) {
|
||||
Throwable cause = ex.getCause();
|
||||
// added to prevent a stack overflow when references is false
|
||||
// (because cause = "this").
|
||||
// See:
|
||||
// https://groups.google.com/forum/?fromgroups=#!topic/kryo-users/6PDs71M1e9Y
|
||||
if (cause == null) {
|
||||
cause = ex;
|
||||
}
|
||||
else {
|
||||
cause.initCause(null);
|
||||
}
|
||||
result = cause;
|
||||
}
|
||||
else {
|
||||
throw new NetException("Error invoking method: " + cachedMethod.method.getDeclaringClass()
|
||||
.getName() + "." + cachedMethod.method.getName(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (responseID == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
InvokeMethodResult invokeMethodResult = new InvokeMethodResult();
|
||||
invokeMethodResult.objectID = invokeMethod.objectID;
|
||||
invokeMethodResult.responseID = (byte) responseID;
|
||||
|
||||
|
||||
// Do not return non-primitives if transmitReturnVal is false
|
||||
if (!transmitReturnVal && !invokeMethod.cachedMethod.method.getReturnType()
|
||||
.isPrimitive()) {
|
||||
invokeMethodResult.result = null;
|
||||
}
|
||||
else {
|
||||
invokeMethodResult.result = result;
|
||||
}
|
||||
|
||||
// System.err.println("Sending: " + invokeMethod.responseID);
|
||||
connection.send()
|
||||
.TCP(invokeMethodResult)
|
||||
.flush();
|
||||
|
||||
// logger.error("{} sent data: {} with id ({})", connection, result, invokeMethod.responseID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an object to allow the remote end of the RmiBridge connections to access it using the specified ID.
|
||||
*
|
||||
* @param objectID Must not be Integer.MAX_VALUE.
|
||||
*
|
||||
* @return true if the new ID has not been previously registered. False if the ID was already registered
|
||||
*/
|
||||
@Override
|
||||
public void register(int objectID, Object object) {
|
||||
public
|
||||
void register(int objectID, Object object) {
|
||||
if (objectID == Integer.MAX_VALUE) {
|
||||
throw new IllegalArgumentException("objectID cannot be Integer.MAX_VALUE.");
|
||||
}
|
||||
|
@ -162,13 +233,14 @@ public class RmiBridge implements Rmi {
|
|||
if (logger2.isTraceEnabled()) {
|
||||
logger2.trace("Object registered with ObjectSpace as {}:{}", objectID, object);
|
||||
}
|
||||
logger2.info("Object registered with ObjectSpace as {}:{}", objectID, object);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an object. The remote end of the RmiBridge connection will no longer be able to access it.
|
||||
*/
|
||||
@Override
|
||||
public void remove(int objectID) {
|
||||
public
|
||||
void remove(int objectID) {
|
||||
WriteLock writeLock = RmiBridge.this.objectLock.writeLock();
|
||||
writeLock.lock();
|
||||
|
||||
|
@ -188,8 +260,8 @@ public class RmiBridge implements Rmi {
|
|||
/**
|
||||
* Removes an object. The remote end of the RmiBridge connection will no longer be able to access it.
|
||||
*/
|
||||
@Override
|
||||
public void remove(Object object) {
|
||||
public
|
||||
void remove(Object object) {
|
||||
WriteLock writeLock = RmiBridge.this.objectLock.writeLock();
|
||||
writeLock.lock();
|
||||
|
||||
|
@ -210,241 +282,55 @@ public class RmiBridge implements Rmi {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the invocation listener
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
public ListenerRaw getListener() {
|
||||
return this.invokeListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the method on the object and, if necessary, sends the result back
|
||||
* to the connection that made the invocation request. This method is
|
||||
* invoked on the update thread of the {@link EndPoint} for this RmiBridge
|
||||
* and unless an {@link #setExecutor(Executor) executor} has been set.
|
||||
*
|
||||
* @param connection
|
||||
* The remote side of this connection requested the invocation.
|
||||
*/
|
||||
protected void invoke(Connection connection, Object target, InvokeMethod invokeMethod) {
|
||||
Logger logger2 = this.logger;
|
||||
if (logger2.isDebugEnabled()) {
|
||||
String argString = "";
|
||||
if (invokeMethod.args != null) {
|
||||
argString = Arrays.deepToString(invokeMethod.args);
|
||||
argString = argString.substring(1, argString.length() - 1);
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
stringBuilder.append(connection.toString()).append(" received: ").append(target.getClass().getSimpleName());
|
||||
stringBuilder.append(":").append(invokeMethod.objectID);
|
||||
stringBuilder.append("#").append(invokeMethod.cachedMethod.method.getName());
|
||||
stringBuilder.append("(").append(argString).append(")");
|
||||
logger2.debug(stringBuilder.toString());
|
||||
}
|
||||
|
||||
|
||||
byte responseData = invokeMethod.responseData;
|
||||
boolean transmitReturnVal = (responseData & returnValueMask) == returnValueMask;
|
||||
boolean transmitExceptions = (responseData & returnExceptionMask) == returnExceptionMask;
|
||||
int responseID = responseData & responseIdMask;
|
||||
|
||||
Object result = null;
|
||||
CachedMethod cachedMethod = invokeMethod.cachedMethod;
|
||||
|
||||
try {
|
||||
result = cachedMethod.invoke(target, invokeMethod.args);
|
||||
} catch (Exception ex) {
|
||||
if (transmitExceptions) {
|
||||
Throwable cause = ex.getCause();
|
||||
// added to prevent a stack overflow when references is false
|
||||
// (because cause = "this").
|
||||
// See:
|
||||
// https://groups.google.com/forum/?fromgroups=#!topic/kryo-users/6PDs71M1e9Y
|
||||
if (cause == null) {
|
||||
cause = ex;
|
||||
} else {
|
||||
cause.initCause(null);
|
||||
}
|
||||
result = cause;
|
||||
} else {
|
||||
throw new NetException("Error invoking method: " + cachedMethod.method.getDeclaringClass().getName() + "."
|
||||
+ cachedMethod.method.getName(), ex);
|
||||
}
|
||||
}
|
||||
|
||||
if (responseID == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
InvokeMethodResult invokeMethodResult = new InvokeMethodResult();
|
||||
invokeMethodResult.objectID = invokeMethod.objectID;
|
||||
invokeMethodResult.responseID = (byte) responseID;
|
||||
|
||||
|
||||
// Do not return non-primitives if transmitReturnVal is false
|
||||
if (!transmitReturnVal && !invokeMethod.cachedMethod.method.getReturnType().isPrimitive()) {
|
||||
invokeMethodResult.result = null;
|
||||
} else {
|
||||
invokeMethodResult.result = result;
|
||||
}
|
||||
|
||||
// System.err.println("Sending: " + invokeMethod.responseID);
|
||||
connection.send().TCP(invokeMethodResult).flush();
|
||||
|
||||
// logger.error("{} sent data: {} with id ({})", connection, result, invokeMethod.responseID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a proxy object that implements the specified interfaces. Methods
|
||||
* invoked on the proxy object will be invoked remotely on the object with
|
||||
* the specified ID in the ObjectSpace for the specified connection. If the
|
||||
* remote end of the connection has not {@link #addConnection(Connection)
|
||||
* added} the connection to the ObjectSpace, the remote method invocations
|
||||
* remote end of the connection has not {@link RmiBridge#register(int, Object)}
|
||||
* added the connection to the ObjectSpace, the remote method invocations
|
||||
* will be ignored.
|
||||
* <p>
|
||||
* <p/>
|
||||
* Methods that return a value will throw {@link TimeoutException} if the
|
||||
* response is not received with the
|
||||
* {@link RemoteObject#setResponseTimeout(int) response timeout}.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If {@link RemoteObject#setNonBlocking(boolean) non-blocking} is false
|
||||
* (the default), then methods that return a value must not be called from
|
||||
* the update thread for the connection. An exception will be thrown if this
|
||||
* occurs. Methods with a void return value can be called on the update
|
||||
* thread.
|
||||
* <p>
|
||||
* <p/>
|
||||
* If a proxy returned from this method is part of an object graph sent over
|
||||
* the network, the object graph on the receiving side will have the proxy
|
||||
* object replaced with the registered object.
|
||||
*
|
||||
* @see RemoteObject
|
||||
*/
|
||||
public RemoteObject getRemoteObject(Connection connection, int objectID, Class<?>... ifaces) {
|
||||
public
|
||||
RemoteObject getRemoteObject(Connection connection, int objectID, Class<?> iface) {
|
||||
if (connection == null) {
|
||||
throw new IllegalArgumentException("connection cannot be null.");
|
||||
}
|
||||
if (ifaces == null) {
|
||||
throw new IllegalArgumentException("ifaces cannot be null.");
|
||||
if (iface == null) {
|
||||
throw new IllegalArgumentException("iface cannot be null.");
|
||||
}
|
||||
|
||||
Class<?>[] temp = new Class<?>[ifaces.length + 1];
|
||||
Class<?>[] temp = new Class<?>[2];
|
||||
temp[0] = RemoteObject.class;
|
||||
System.arraycopy(ifaces, 0, temp, 1, ifaces.length);
|
||||
temp[1] = iface;
|
||||
|
||||
return (RemoteObject) Proxy.newProxyInstance(RmiBridge.class.getClassLoader(),
|
||||
temp,
|
||||
new RemoteInvocationHandler(this.invokeMethodPool, connection, objectID));
|
||||
}
|
||||
|
||||
public CachedMethod[] getMethods(Kryo kryo, Class<?> type) {
|
||||
CachedMethod[] cachedMethods = this.methodCache.get(type); // Maybe should cache per Kryo instance?
|
||||
if (cachedMethods != null) {
|
||||
return cachedMethods;
|
||||
}
|
||||
|
||||
ArrayList<Method> allMethods = new ArrayList<Method>();
|
||||
|
||||
Class<?> nextClass = type;
|
||||
while (nextClass != null) {
|
||||
Collections.addAll(allMethods, nextClass.getDeclaredMethods());
|
||||
nextClass = nextClass.getSuperclass();
|
||||
if (nextClass == Object.class) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<Method> methods = new ArrayList<Method>(Math.max(1, allMethods.size()));
|
||||
for (int i = 0, n = allMethods.size(); i < n; i++) {
|
||||
Method method = allMethods.get(i);
|
||||
int modifiers = method.getModifiers();
|
||||
if (Modifier.isStatic(modifiers)) {
|
||||
continue;
|
||||
}
|
||||
if (Modifier.isPrivate(modifiers)) {
|
||||
continue;
|
||||
}
|
||||
if (method.isSynthetic()) {
|
||||
continue;
|
||||
}
|
||||
methods.add(method);
|
||||
}
|
||||
Collections.sort(methods, new Comparator<Method>() {
|
||||
@Override
|
||||
public int compare(Method o1, Method o2) {
|
||||
// Methods are sorted so they can be represented as an index.
|
||||
int diff = o1.getName().compareTo(o2.getName());
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
Class<?>[] argTypes1 = o1.getParameterTypes();
|
||||
Class<?>[] argTypes2 = o2.getParameterTypes();
|
||||
if (argTypes1.length > argTypes2.length) {
|
||||
return 1;
|
||||
}
|
||||
if (argTypes1.length < argTypes2.length) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < argTypes1.length; i++) {
|
||||
diff = argTypes1[i].getName().compareTo(argTypes2[i].getName());
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Two methods with same signature!"); // Impossible.
|
||||
}
|
||||
});
|
||||
|
||||
Object methodAccess = null;
|
||||
if (this.asm && !Util.isAndroid && Modifier.isPublic(type.getModifiers())) {
|
||||
methodAccess = MethodAccess.get(type);
|
||||
}
|
||||
|
||||
|
||||
int n = methods.size();
|
||||
cachedMethods = new CachedMethod[n];
|
||||
for (int i = 0; i < n; i++) {
|
||||
Method method = methods.get(i);
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
|
||||
CachedMethod cachedMethod = null;
|
||||
if (methodAccess != null) {
|
||||
try {
|
||||
AsmCachedMethod asmCachedMethod = new AsmCachedMethod();
|
||||
asmCachedMethod.methodAccessIndex = ((MethodAccess)methodAccess).getIndex(method.getName(), parameterTypes);
|
||||
asmCachedMethod.methodAccess = (MethodAccess)methodAccess;
|
||||
cachedMethod = asmCachedMethod;
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
if (cachedMethod == null) {
|
||||
cachedMethod = new CachedMethod();
|
||||
}
|
||||
cachedMethod.method = method;
|
||||
cachedMethod.methodClassID = kryo.getRegistration(method.getDeclaringClass()).getId();
|
||||
cachedMethod.methodIndex = i;
|
||||
|
||||
// Store the serializer for each final parameter.
|
||||
cachedMethod.serializers = new Serializer<?>[parameterTypes.length];
|
||||
for (int ii = 0, nn = parameterTypes.length; ii < nn; ii++) {
|
||||
if (kryo.isFinal(parameterTypes[ii])) {
|
||||
cachedMethod.serializers[ii] = kryo.getSerializer(parameterTypes[ii]);
|
||||
}
|
||||
}
|
||||
|
||||
cachedMethods[i] = cachedMethod;
|
||||
}
|
||||
|
||||
this.methodCache.put(type, cachedMethods);
|
||||
return cachedMethods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object registered with the specified ID.
|
||||
*/
|
||||
Object getRegisteredObject(int objectID) {
|
||||
public
|
||||
Object getRegisteredObject(final int objectID) {
|
||||
ReadLock readLock = this.objectLock.readLock();
|
||||
readLock.lock();
|
||||
|
||||
|
@ -458,7 +344,8 @@ public class RmiBridge implements Rmi {
|
|||
/**
|
||||
* Returns the ID registered for the specified object, or Integer.MAX_VALUE if not found.
|
||||
*/
|
||||
public int getRegisteredId(Object object) {
|
||||
public
|
||||
int getRegisteredId(final Object object) {
|
||||
// Find an ID with the object.
|
||||
ReadLock readLock = this.objectLock.readLock();
|
||||
|
||||
|
@ -468,55 +355,4 @@ public class RmiBridge implements Rmi {
|
|||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the classes needed to use RMI. This should be called before any connections are opened.
|
||||
*/
|
||||
private void registerClasses(final SerializationManager manager) {
|
||||
manager.registerForRmiClasses(new RmiRegisterClassesCallback() {
|
||||
@Override
|
||||
public void registerForClasses(Kryo kryo) {
|
||||
kryo.register(Object[].class);
|
||||
kryo.register(InvokeMethod.class, new InvokeMethodSerializer(RmiBridge.this));
|
||||
|
||||
FieldSerializer<InvokeMethodResult> resultSerializer = new FieldSerializer<InvokeMethodResult>(kryo, InvokeMethodResult.class) {
|
||||
@Override
|
||||
public void write(Kryo kryo, Output output, InvokeMethodResult result) {
|
||||
super.write(kryo, output, result);
|
||||
output.writeInt(result.objectID, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InvokeMethodResult read(Kryo kryo, Input input, Class<InvokeMethodResult> type) {
|
||||
InvokeMethodResult result = super.read(kryo, input, type);
|
||||
result.objectID = input.readInt(true);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
resultSerializer.removeField(OBJECT_ID);
|
||||
kryo.register(InvokeMethodResult.class, resultSerializer);
|
||||
|
||||
kryo.register(InvocationHandler.class, new Serializer<Object>() {
|
||||
@Override
|
||||
public void write(Kryo kryo, Output output, Object object) {
|
||||
RemoteInvocationHandler handler = (RemoteInvocationHandler) Proxy.getInvocationHandler(object);
|
||||
output.writeInt(handler.objectID, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked"})
|
||||
public Object read(Kryo kryo, Input input, Class<Object> type) {
|
||||
int objectID = input.readInt(true);
|
||||
Connection connection = (Connection) kryo.getContext().get(Connection.connection);
|
||||
Object object = getRegisteredObject(objectID);
|
||||
if (object == null) {
|
||||
final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(RmiBridge.class);
|
||||
logger.warn("Unknown object ID {} for connection: {}", objectID, connection);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
|
||||
public interface RmiRegisterClassesCallback {
|
||||
public void registerForClasses(Kryo kryo);
|
||||
}
|
28
Dorkbox-Network/src/dorkbox/network/rmi/RmiRegistration.java
Normal file
28
Dorkbox-Network/src/dorkbox/network/rmi/RmiRegistration.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
/**
|
||||
* Message specifically to register a class implementation for RMI
|
||||
*/
|
||||
public
|
||||
class RmiRegistration {
|
||||
public Object remoteObject;
|
||||
public String remoteImplementationClass;
|
||||
public boolean hasError;
|
||||
|
||||
public
|
||||
RmiRegistration() {
|
||||
hasError = true;
|
||||
}
|
||||
|
||||
public
|
||||
RmiRegistration(final String remoteImplementationClass) {
|
||||
this.remoteImplementationClass = remoteImplementationClass;
|
||||
hasError = false;
|
||||
}
|
||||
|
||||
public
|
||||
RmiRegistration(final Object remoteObject) {
|
||||
this.remoteObject = remoteObject;
|
||||
hasError = false;
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
import com.esotericsoftware.kryo.Serializer;
|
||||
|
||||
public interface SerializerRegistration<T extends Serializer<?>> {
|
||||
public void register(T serializer);
|
||||
}
|
|
@ -1,201 +0,0 @@
|
|||
package dorkbox.network.util;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.util.Util;
|
||||
|
||||
public class BinaryListReferenceResolver implements com.esotericsoftware.kryo.ReferenceResolver {
|
||||
protected final List<Object> readObjects = new ArrayList<Object>();
|
||||
|
||||
private int[] writtenObjectsHashes = new int[10];
|
||||
// objects, then values
|
||||
private Object[] writtenObjectsAndValues = new Object[20];
|
||||
|
||||
private int size = 0;
|
||||
private int primaryArraySize = 0;
|
||||
|
||||
@Override
|
||||
public void setKryo(Kryo kryo) {
|
||||
}
|
||||
|
||||
private static int binarySearch(int[] array, int startIndex, int endIndex, int value) {
|
||||
int low = startIndex, mid = -1, high = endIndex - 1;
|
||||
while (low <= high) {
|
||||
mid = low + high >>> 1;
|
||||
if (value > array[mid]) {
|
||||
low = mid + 1;
|
||||
} else if (value == array[mid]) {
|
||||
return mid;
|
||||
} else {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
if (mid < 0) {
|
||||
int insertPoint = endIndex;
|
||||
for (int index = startIndex; index < endIndex; index++) {
|
||||
if (value < array[index]) {
|
||||
insertPoint = index;
|
||||
}
|
||||
}
|
||||
return -insertPoint - 1;
|
||||
}
|
||||
return -mid - (value < array[mid] ? 1 : 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int addWrittenObject(Object object) {
|
||||
int id = size;
|
||||
int hash = System.identityHashCode(object);
|
||||
int idx = binarySearch(writtenObjectsHashes, 0, primaryArraySize, hash);
|
||||
if (idx < 0) {
|
||||
idx = -(idx + 1);
|
||||
if (primaryArraySize == writtenObjectsHashes.length) {
|
||||
int[] newHashArray = new int[writtenObjectsHashes.length * 3 / 2];
|
||||
System.arraycopy(writtenObjectsHashes, 0, newHashArray, 0, writtenObjectsHashes.length);
|
||||
writtenObjectsHashes = newHashArray;
|
||||
Object[] newObjectArray = new Object[newHashArray.length * 2];
|
||||
System.arraycopy(writtenObjectsAndValues, 0, newObjectArray, 0, writtenObjectsAndValues.length);
|
||||
writtenObjectsAndValues = newObjectArray;
|
||||
}
|
||||
for (int i = writtenObjectsHashes.length - 1; i > idx; i--) {
|
||||
int j = 2 * i;
|
||||
writtenObjectsHashes[i] = writtenObjectsHashes[i - 1];
|
||||
writtenObjectsAndValues[j] = writtenObjectsAndValues[j - 2];
|
||||
writtenObjectsAndValues[j + 1] = writtenObjectsAndValues[j - 1];
|
||||
}
|
||||
writtenObjectsHashes[idx] = hash;
|
||||
writtenObjectsAndValues[2 * idx] = object;
|
||||
writtenObjectsAndValues[2 * idx + 1] = id;
|
||||
primaryArraySize++;
|
||||
size++;
|
||||
return id;
|
||||
} else {
|
||||
idx = 2 * idx; // objects and values array has bigger indexes
|
||||
if (writtenObjectsAndValues[idx + 1] instanceof Integer) {
|
||||
// single slot
|
||||
if (writtenObjectsAndValues[idx] == object) {
|
||||
return (Integer) writtenObjectsAndValues[idx + 1];
|
||||
} else {
|
||||
Object[] keys = new Object[4];
|
||||
int[] values = new int[4];
|
||||
keys[0] = writtenObjectsAndValues[idx];
|
||||
values[0] = (Integer) writtenObjectsAndValues[idx + 1];
|
||||
keys[1] = object;
|
||||
values[1] = id;
|
||||
writtenObjectsAndValues[idx] = keys;
|
||||
writtenObjectsAndValues[idx + 1] = values;
|
||||
size++;
|
||||
return id;
|
||||
}
|
||||
} else {
|
||||
// multiple entry slot
|
||||
Object[] keys = (Object[]) writtenObjectsAndValues[idx];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
if (keys[i] == object) {
|
||||
return ((int[]) writtenObjectsAndValues[idx + 1])[i];
|
||||
}
|
||||
if (keys[i] == null) {
|
||||
keys[i] = object;
|
||||
((int[]) writtenObjectsAndValues[idx + 1])[i] = id;
|
||||
size++;
|
||||
return id;
|
||||
}
|
||||
}
|
||||
// expand
|
||||
Object[] newKeys = new Object[keys.length * 3 / 2];
|
||||
System.arraycopy(keys, 0, newKeys, 0, keys.length);
|
||||
newKeys[keys.length] = object;
|
||||
int[] newValues = new int[keys.length * 3 / 2];
|
||||
System.arraycopy(writtenObjectsAndValues[idx + 1], 0, newValues, 0, keys.length);
|
||||
writtenObjectsAndValues[idx] = newKeys;
|
||||
writtenObjectsAndValues[idx + 1] = newValues;
|
||||
size++;
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWrittenId(Object object) {
|
||||
int hash = System.identityHashCode(object);
|
||||
int idx = binarySearch(writtenObjectsHashes, 0, primaryArraySize, hash);
|
||||
if (idx < 0) {
|
||||
return -1;
|
||||
} else {
|
||||
idx = 2 * idx; // objects and values array has bigger indexes
|
||||
if (writtenObjectsAndValues[idx + 1] instanceof Integer) {
|
||||
// single slot
|
||||
if (writtenObjectsAndValues[idx] == object) {
|
||||
return (Integer) writtenObjectsAndValues[idx + 1];
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
// multiple entry slot
|
||||
Object[] keys = (Object[]) writtenObjectsAndValues[idx];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
if (keys[i] == object) {
|
||||
return ((int[]) writtenObjectsAndValues[idx + 1])[i];
|
||||
}
|
||||
if (keys[i] == null) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public int nextReadId(Class type) {
|
||||
int id = readObjects.size();
|
||||
readObjects.add(null);
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadObject(int id, Object object) {
|
||||
readObjects.set(id, object);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public Object getReadObject(Class type, int id) {
|
||||
return readObjects.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
readObjects.clear();
|
||||
size = 0;
|
||||
primaryArraySize = 0;
|
||||
writtenObjectsAndValues = new Object[20];
|
||||
writtenObjectsHashes = new int[10];
|
||||
}
|
||||
|
||||
/** Returns false for all primitive wrappers. */
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public boolean useReferences(Class type) {
|
||||
return !Util.isWrapperClass(type) &&
|
||||
!type.equals(String.class) &&
|
||||
!type.equals(Date.class) &&
|
||||
!type.equals(BigDecimal.class) &&
|
||||
!type.equals(BigInteger.class);
|
||||
}
|
||||
|
||||
public void addReadObject(int id, Object object) {
|
||||
while (id >= readObjects.size()) {
|
||||
readObjects.add(null);
|
||||
}
|
||||
readObjects.set(id, object);
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ import io.netty.buffer.ByteBuf;
|
|||
* it is possible to use a single kryo with the use of synchronize, however - that defeats the point of multi-threaded
|
||||
*/
|
||||
public
|
||||
interface ConnectionSerializationManager extends SerializationManager, RMISerializationManager {
|
||||
interface CryptoSerializationManager extends SerializationManager, RMISerializationManager {
|
||||
|
||||
/**
|
||||
* Determines if this buffer is encrypted or not.
|
||||
|
@ -53,4 +53,6 @@ interface ConnectionSerializationManager extends SerializationManager, RMISerial
|
|||
* @param length should ALWAYS be the length of the expected object!
|
||||
*/
|
||||
Object readWithCryptoUdp(Connection connection, ByteBuf buffer, int length);
|
||||
|
||||
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
package dorkbox.network.util;
|
||||
|
||||
public interface EndpointTool {
|
||||
public interface EndPointTool {
|
||||
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
package dorkbox.network.util;
|
||||
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.Registration;
|
||||
import com.esotericsoftware.kryo.Serializer;
|
||||
import com.esotericsoftware.kryo.io.Input;
|
||||
import com.esotericsoftware.kryo.io.Output;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.rmi.RmiRegisterClassesCallback;
|
||||
import dorkbox.network.rmi.SerializerRegistration;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
|
||||
public
|
||||
class NullConnectionSerializationManager implements ConnectionSerializationManager {
|
||||
|
||||
@Override
|
||||
public
|
||||
void register(Class<?> clazz) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void register(Class<?> clazz, Serializer<?> serializer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void register(Class<?> type, Serializer<?> serializer, int id) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void write(ByteBuf buffer, Object message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
Object read(ByteBuf buffer, int length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void writeFullClassAndObject(final Output output, final Object value) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
Object readFullClassAndObject(final Input input) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
Kryo borrow() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void release(final Kryo kryo) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("rawtypes")
|
||||
public
|
||||
void registerSerializer(Class<?> clazz, SerializerRegistration registration) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void registerForRmiClasses(RmiRegisterClassesCallback callback) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
Registration getRegistration(Class<?> clazz) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
boolean isEncrypted(ByteBuf buffer) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void writeWithCryptoTcp(Connection connection, ByteBuf buffer, Object message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void writeWithCryptoUdp(Connection connection, ByteBuf buffer, Object message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
Object readWithCryptoTcp(Connection connection, ByteBuf buffer, int length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
Object readWithCryptoUdp(Connection connection, ByteBuf buffer, int length) {
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -3,11 +3,6 @@ package dorkbox.network.util;
|
|||
import com.esotericsoftware.kryo.ClassResolver;
|
||||
import com.esotericsoftware.kryo.Kryo;
|
||||
import com.esotericsoftware.kryo.Registration;
|
||||
import com.esotericsoftware.kryo.factories.ReflectionSerializerFactory;
|
||||
import com.esotericsoftware.kryo.factories.SerializerFactory;
|
||||
import com.esotericsoftware.kryo.serializers.FieldSerializer;
|
||||
import dorkbox.network.rmi.RmiRegisterClassesCallback;
|
||||
import dorkbox.network.rmi.SerializerRegistration;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -15,33 +10,27 @@ import dorkbox.network.rmi.SerializerRegistration;
|
|||
public
|
||||
interface RMISerializationManager {
|
||||
|
||||
/**
|
||||
* <b>primarily used by RMI</b> It is not common to call this method!
|
||||
* <p/>
|
||||
* Registers the class using the lowest, next available integer ID and the
|
||||
* {@link SerializerRegistration (Class) serializer}. If the class
|
||||
* is already registered, the existing entry is updated with the new
|
||||
* serializer. Registering a primitive also affects the corresponding
|
||||
* primitive wrapper.
|
||||
* <p/>
|
||||
* Because the ID assigned is affected by the IDs registered before it, the
|
||||
* order classes are registered is important when using this method. The
|
||||
* order must be the same at deserialization as it was for serialization.
|
||||
*/
|
||||
@SuppressWarnings({"rawtypes"})
|
||||
void registerSerializer(Class<?> clazz, SerializerRegistration registration);
|
||||
|
||||
/**
|
||||
* Necessary to register classes for RMI, only called once when the RMI bridge is created.
|
||||
*/
|
||||
void registerForRmiClasses(RmiRegisterClassesCallback callback);
|
||||
void initRmiSerialization();
|
||||
|
||||
/**
|
||||
* If the class is not registered and {@link Kryo#setRegistrationRequired(boolean)} is false, it is
|
||||
* automatically registered using the {@link Kryo#addDefaultSerializer(Class, Class) default serializer}.
|
||||
*
|
||||
* @throws IllegalArgumentException if the class is not registered and {@link ConnectionSerializationManager#setRegistrationRequired(boolean)} is true.
|
||||
* @throws IllegalArgumentException if the class is not registered and registration is required.
|
||||
* @see ClassResolver#getRegistration(Class)
|
||||
*/
|
||||
Registration getRegistration(Class<?> clazz);
|
||||
|
||||
/**
|
||||
* Objects that we want to use RMI with, must be accessed via an interface. This method configures the serialization of an
|
||||
* implementation to be serialized via the defined interface, as a RemoteObject (ie: proxy object). If the implementation
|
||||
* class is ALREADY registered, then it's registration will be overwritten by this one
|
||||
*
|
||||
* @param ifaceClass The interface used to access the remote object
|
||||
* @param implClass The implementation class of the interface
|
||||
*/
|
||||
<Iface, Impl extends Iface> void registerRemote(Class<Iface> ifaceClass, Class<Impl> implClass);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
|||
import ch.qos.logback.classic.joran.JoranConfigurator;
|
||||
import ch.qos.logback.classic.spi.ILoggingEvent;
|
||||
import ch.qos.logback.core.ConsoleAppender;
|
||||
import com.esotericsoftware.minlog.Log;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.util.entropy.Entropy;
|
||||
import dorkbox.network.util.entropy.SimpleEntropy;
|
||||
|
@ -45,6 +46,9 @@ class BaseTest {
|
|||
BaseTest() {
|
||||
System.out.println("---- " + getClass().getSimpleName());
|
||||
|
||||
// set the minlog logging level
|
||||
Log.DEBUG();
|
||||
|
||||
// assume SLF4J is bound to logback in the current environment
|
||||
Logger rootLogger = (Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
|
||||
LoggerContext context = rootLogger.getLoggerContext();
|
||||
|
@ -95,40 +99,41 @@ class BaseTest {
|
|||
this.endPoints.add(endPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Immediately stop the endpoints
|
||||
*/
|
||||
public
|
||||
void stopEndPoints() {
|
||||
stopEndPoints(0);
|
||||
stopEndPoints(1);
|
||||
}
|
||||
|
||||
public
|
||||
void stopEndPoints(int stopAfterMillis) {
|
||||
if (stopAfterMillis <= 0) {
|
||||
stopAfterMillis = 1;
|
||||
}
|
||||
|
||||
if (this.timer == null) {
|
||||
this.timer = new Timer("UnitTest timeout timer");
|
||||
}
|
||||
|
||||
// We have to ALWAYS run this in a new timer, BECAUSE if stopEndPoints() is called from a client/server thread, it will DEADLOCK
|
||||
this.timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
synchronized (BaseTest.this.endPoints) {
|
||||
for (EndPoint endPoint : BaseTest.this.endPoints) {
|
||||
endPoint.stop();
|
||||
endPoint.waitForShutdown();
|
||||
}
|
||||
BaseTest.this.endPoints.clear();
|
||||
}
|
||||
if (BaseTest.this.timer != null) {
|
||||
BaseTest.this.timer.cancel();
|
||||
BaseTest.this.timer.purge();
|
||||
BaseTest.this.timer = null;
|
||||
}
|
||||
if (stopAfterMillis > 0) {
|
||||
if (this.timer == null) {
|
||||
this.timer = new Timer("UnitTest timeout timer");
|
||||
}
|
||||
}, stopAfterMillis);
|
||||
|
||||
// We have to ALWAYS run this in a new timer, BECAUSE if stopEndPoints() is called from a client/server thread, it will DEADLOCK
|
||||
this.timer.schedule(new TimerTask() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
synchronized (BaseTest.this.endPoints) {
|
||||
for (EndPoint endPoint : BaseTest.this.endPoints) {
|
||||
endPoint.stop();
|
||||
endPoint.waitForShutdown();
|
||||
}
|
||||
BaseTest.this.endPoints.clear();
|
||||
}
|
||||
if (BaseTest.this.timer != null) {
|
||||
BaseTest.this.timer.cancel();
|
||||
BaseTest.this.timer.purge();
|
||||
BaseTest.this.timer = null;
|
||||
}
|
||||
}
|
||||
}, stopAfterMillis);
|
||||
}
|
||||
}
|
||||
|
||||
public
|
||||
|
@ -140,6 +145,9 @@ class BaseTest {
|
|||
waitForThreads0(stopAfterSecondsOrMillis);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for threads until they are done (no timeout)
|
||||
*/
|
||||
public
|
||||
void waitForThreads() {
|
||||
waitForThreads0(0);
|
||||
|
@ -186,7 +194,7 @@ class BaseTest {
|
|||
|
||||
// Give sockets a chance to close before starting the next test.
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
Thread.sleep(1000);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,10 @@ package dorkbox.network;
|
|||
|
||||
import dorkbox.network.PingPongTest.TYPE;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.connection.idle.IdleBridge;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -17,7 +17,7 @@ import java.util.Arrays;
|
|||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ChunkedDataTest extends BaseTest {
|
||||
public class ChunkedDataIdleTest extends BaseTest {
|
||||
private volatile boolean success = false;
|
||||
|
||||
enum ConnectionType {
|
||||
|
@ -29,51 +29,45 @@ public class ChunkedDataTest extends BaseTest {
|
|||
// have to test sending objects
|
||||
@Test
|
||||
public void ObjectSender() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
final Data mainData = new Data();
|
||||
populateData(mainData);
|
||||
|
||||
|
||||
System.err.println("-- TCP");
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
|
||||
sendObject(mainData, connectionOptions, ConnectionType.TCP);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
sendObject(mainData, configuration, ConnectionType.TCP);
|
||||
|
||||
|
||||
System.err.println("-- UDP");
|
||||
connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
|
||||
sendObject(mainData, connectionOptions, ConnectionType.UDP);
|
||||
configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
sendObject(mainData, configuration, ConnectionType.UDP);
|
||||
|
||||
|
||||
System.err.println("-- UDT");
|
||||
connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udtPort = udtPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
|
||||
sendObject(mainData, connectionOptions, ConnectionType.UDT);
|
||||
configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udtPort = udtPort;
|
||||
configuration.host = host;
|
||||
sendObject(mainData, configuration, ConnectionType.UDT);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void sendObject(final Data mainData, ConnectionOptions connectionOptions, final ConnectionType type)
|
||||
private void sendObject(final Data mainData, Configuration configuration, final ConnectionType type)
|
||||
throws InitializationException, SecurityException, IOException {
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
|
||||
addEndPoint(server);
|
||||
server.setIdleTimeout(100);
|
||||
server.setIdleTimeout(10);
|
||||
server.bind(false);
|
||||
server.listeners().add(new Listener<Data>() {
|
||||
|
||||
|
@ -94,14 +88,15 @@ public class ChunkedDataTest extends BaseTest {
|
|||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
client.setIdleTimeout(10);
|
||||
addEndPoint(client);
|
||||
client.listeners().add(new Listener<Data>() {
|
||||
@Override
|
||||
public void received(Connection connection, Data object) {
|
||||
if (mainData.equals(object)) {
|
||||
ChunkedDataTest.this.success = true;
|
||||
ChunkedDataIdleTest.this.success = true;
|
||||
}
|
||||
|
||||
System.err.println("finished!");
|
||||
|
@ -146,7 +141,7 @@ public class ChunkedDataTest extends BaseTest {
|
|||
data.Booleans = new Boolean[] {true, false};
|
||||
}
|
||||
|
||||
private void register (ConnectionSerializationManager kryoMT) {
|
||||
private void register (CryptoSerializationManager kryoMT) {
|
||||
kryoMT.register(int[].class);
|
||||
kryoMT.register(short[].class);
|
||||
kryoMT.register(float[].class);
|
|
@ -2,9 +2,9 @@ package dorkbox.network;
|
|||
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -22,13 +22,14 @@ class ClientSendTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void sendDataFromClientClass() throws InitializationException, SecurityException, IOException {
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
@ -44,7 +45,7 @@ class ClientSendTest extends BaseTest {
|
|||
}
|
||||
});
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
client.connect(5000);
|
||||
|
@ -70,7 +71,7 @@ class ClientSendTest extends BaseTest {
|
|||
}
|
||||
|
||||
private static
|
||||
void register(ConnectionSerializationManager kryoMT) {
|
||||
void register(CryptoSerializationManager kryoMT) {
|
||||
kryoMT.register(AMessage.class);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@ package dorkbox.network;
|
|||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -21,14 +21,16 @@ class ConnectionTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void connectLocal() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
System.out.println("---- " + "Local");
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions(EndPoint.LOCAL_CHANNEL);
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.localChannelName = EndPoint.LOCAL_CHANNEL;
|
||||
|
||||
startServer(connectionOptions);
|
||||
startClient(connectionOptions);
|
||||
startServer(configuration);
|
||||
startClient(configuration);
|
||||
|
||||
waitForThreads(10);
|
||||
}
|
||||
|
@ -36,17 +38,18 @@ class ConnectionTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void connectTcp() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
System.out.println("---- " + "TCP");
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
|
||||
startServer(connectionOptions);
|
||||
startServer(configuration);
|
||||
|
||||
connectionOptions.host = host;
|
||||
startClient(connectionOptions);
|
||||
configuration.host = host;
|
||||
startClient(configuration);
|
||||
|
||||
waitForThreads(10);
|
||||
}
|
||||
|
@ -54,18 +57,19 @@ class ConnectionTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void connectTcpUdp() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
System.out.println("---- " + "TCP UDP");
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
|
||||
startServer(connectionOptions);
|
||||
startServer(configuration);
|
||||
|
||||
connectionOptions.host = host;
|
||||
startClient(connectionOptions);
|
||||
configuration.host = host;
|
||||
startClient(configuration);
|
||||
|
||||
waitForThreads(10);
|
||||
}
|
||||
|
@ -73,18 +77,19 @@ class ConnectionTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void connectTcpUdt() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
System.out.println("---- " + "TCP UDT");
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udtPort = udtPort;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udtPort = udtPort;
|
||||
|
||||
startServer(connectionOptions);
|
||||
startServer(configuration);
|
||||
|
||||
connectionOptions.host = host;
|
||||
startClient(connectionOptions);
|
||||
configuration.host = host;
|
||||
startClient(configuration);
|
||||
|
||||
waitForThreads(10);
|
||||
}
|
||||
|
@ -92,27 +97,28 @@ class ConnectionTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void connectTcpUdpUdt() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
System.out.println("---- " + "TCP UDP UDT");
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.udtPort = udtPort;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.udtPort = udtPort;
|
||||
|
||||
startServer(connectionOptions);
|
||||
startServer(configuration);
|
||||
|
||||
connectionOptions.host = host;
|
||||
configuration.host = host;
|
||||
|
||||
startClient(connectionOptions);
|
||||
startClient(configuration);
|
||||
|
||||
waitForThreads(10);
|
||||
}
|
||||
|
||||
private
|
||||
Server startServer(ConnectionOptions connectionOptions) throws InitializationException, SecurityException, IOException {
|
||||
Server server = new Server(connectionOptions);
|
||||
Server startServer(Configuration configuration) throws InitializationException, SecurityException, IOException {
|
||||
Server server = new Server(configuration);
|
||||
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
|
@ -145,10 +151,10 @@ class ConnectionTest extends BaseTest {
|
|||
}
|
||||
|
||||
private
|
||||
Client startClient(ConnectionOptions connectionOptions) throws InitializationException, SecurityException, IOException {
|
||||
Client startClient(Configuration configuration) throws InitializationException, SecurityException, IOException {
|
||||
Client client;
|
||||
if (connectionOptions != null) {
|
||||
client = new Client(connectionOptions);
|
||||
if (configuration != null) {
|
||||
client = new Client(configuration);
|
||||
}
|
||||
else {
|
||||
client = new Client();
|
||||
|
@ -174,7 +180,7 @@ class ConnectionTest extends BaseTest {
|
|||
}
|
||||
|
||||
private
|
||||
void register(ConnectionSerializationManager kryoMT) {
|
||||
void register(CryptoSerializationManager kryoMT) {
|
||||
kryoMT.register(BMessage.class);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,12 @@ class DiscoverHostTest extends BaseTest {
|
|||
public
|
||||
void broadcast() throws InitializationException, SecurityException, IOException {
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
@ -38,7 +38,7 @@ class DiscoverHostTest extends BaseTest {
|
|||
return;
|
||||
}
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
client.listeners()
|
||||
|
|
|
@ -3,11 +3,11 @@ package dorkbox.network;
|
|||
|
||||
import dorkbox.network.PingPongTest.TYPE;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.connection.idle.IdleBridge;
|
||||
import dorkbox.network.connection.idle.InputStreamSender;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -34,32 +34,31 @@ class IdleTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void InputStreamSender() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT(false, false);
|
||||
|
||||
final int largeDataSize = 12345;
|
||||
|
||||
System.err.println("-- TCP");
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT(false, false);
|
||||
streamSpecificType(largeDataSize, connectionOptions, ConnectionType.TCP);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
streamSpecificType(largeDataSize, configuration, ConnectionType.TCP);
|
||||
|
||||
|
||||
System.err.println("-- UDP");
|
||||
connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT(false, false);
|
||||
streamSpecificType(largeDataSize, connectionOptions, ConnectionType.UDP);
|
||||
configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
streamSpecificType(largeDataSize, configuration, ConnectionType.UDP);
|
||||
|
||||
|
||||
System.err.println("-- UDT");
|
||||
connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udtPort = udtPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT(false, false);
|
||||
streamSpecificType(largeDataSize, connectionOptions, ConnectionType.UDT);
|
||||
configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udtPort = udtPort;
|
||||
configuration.host = host;
|
||||
streamSpecificType(largeDataSize, configuration, ConnectionType.UDT);
|
||||
}
|
||||
|
||||
|
||||
|
@ -68,45 +67,42 @@ class IdleTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void ObjectSender() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
final Data mainData = new Data();
|
||||
populateData(mainData);
|
||||
|
||||
|
||||
System.err.println("-- TCP");
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
sendObject(mainData, connectionOptions, ConnectionType.TCP);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
sendObject(mainData, configuration, ConnectionType.TCP);
|
||||
|
||||
|
||||
System.err.println("-- UDP");
|
||||
connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
sendObject(mainData, connectionOptions, ConnectionType.TCP);
|
||||
configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
sendObject(mainData, configuration, ConnectionType.TCP);
|
||||
|
||||
|
||||
System.err.println("-- UDT");
|
||||
connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udtPort = udtPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
sendObject(mainData, connectionOptions, ConnectionType.TCP);
|
||||
configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udtPort = udtPort;
|
||||
configuration.host = host;
|
||||
sendObject(mainData, configuration, ConnectionType.TCP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private
|
||||
void sendObject(final Data mainData, ConnectionOptions connectionOptions, final ConnectionType type)
|
||||
void sendObject(final Data mainData, Configuration configuration, final ConnectionType type)
|
||||
throws InitializationException, SecurityException, IOException {
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.setIdleTimeout(100);
|
||||
|
@ -135,7 +131,7 @@ class IdleTest extends BaseTest {
|
|||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
client.listeners()
|
||||
|
@ -163,9 +159,9 @@ class IdleTest extends BaseTest {
|
|||
|
||||
|
||||
private
|
||||
void streamSpecificType(final int largeDataSize, ConnectionOptions connectionOptions, final ConnectionType type)
|
||||
void streamSpecificType(final int largeDataSize, Configuration configuration, final ConnectionType type)
|
||||
throws InitializationException, SecurityException, IOException {
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.setIdleTimeout(100);
|
||||
|
@ -215,7 +211,7 @@ class IdleTest extends BaseTest {
|
|||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
client.listeners()
|
||||
|
@ -276,7 +272,7 @@ class IdleTest extends BaseTest {
|
|||
}
|
||||
|
||||
private
|
||||
void register(ConnectionSerializationManager kryoMT) {
|
||||
void register(CryptoSerializationManager kryoMT) {
|
||||
kryoMT.register(int[].class);
|
||||
kryoMT.register(short[].class);
|
||||
kryoMT.register(float[].class);
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
|
||||
package dorkbox.network;
|
||||
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -16,7 +15,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class LargeBufferTest extends BaseTest {
|
||||
public
|
||||
class LargeBufferTest extends BaseTest {
|
||||
private static final int OBJ_SIZE = 1024 * 10;
|
||||
|
||||
private volatile int finalCheckAmount = 0;
|
||||
|
@ -24,74 +24,84 @@ public class LargeBufferTest extends BaseTest {
|
|||
private volatile int clientCheck = -1;
|
||||
|
||||
@Test
|
||||
public void manyLargeMessages () throws InitializationException, SecurityException, IOException {
|
||||
public
|
||||
void manyLargeMessages() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
final int messageCount = 1024;
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
server.listeners().add(new Listener<LargeMessage>() {
|
||||
AtomicInteger received = new AtomicInteger();
|
||||
AtomicInteger receivedBytes = new AtomicInteger();
|
||||
server.listeners()
|
||||
.add(new Listener<LargeMessage>() {
|
||||
AtomicInteger received = new AtomicInteger();
|
||||
AtomicInteger receivedBytes = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public void received (Connection connection, LargeMessage object) {
|
||||
// System.err.println("Server ack message: " + received.get());
|
||||
connection.send().TCP(object);
|
||||
this.receivedBytes.addAndGet(object.bytes.length);
|
||||
@Override
|
||||
public
|
||||
void received(Connection connection, LargeMessage object) {
|
||||
//System.err.println("Server ack message: " + received.get());
|
||||
|
||||
if (this.received.incrementAndGet() == messageCount) {
|
||||
System.out.println("Server received all " + messageCount + " messages!");
|
||||
System.out.println("Server received and sent " + this.receivedBytes.get() + " bytes.");
|
||||
LargeBufferTest.this.serverCheck = LargeBufferTest.this.finalCheckAmount - this.receivedBytes.get();
|
||||
System.out.println("Server missed " + LargeBufferTest.this.serverCheck + " bytes.");
|
||||
stopEndPoints();
|
||||
}
|
||||
}
|
||||
});
|
||||
connection.send()
|
||||
.TCP(object);
|
||||
this.receivedBytes.addAndGet(object.bytes.length);
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
if (this.received.incrementAndGet() == messageCount) {
|
||||
System.out.println("Server received all " + messageCount + " messages!");
|
||||
System.out.println("Server received and sent " + this.receivedBytes.get() + " bytes.");
|
||||
LargeBufferTest.this.serverCheck = LargeBufferTest.this.finalCheckAmount - this.receivedBytes.get();
|
||||
System.out.println("Server missed " + LargeBufferTest.this.serverCheck + " bytes.");
|
||||
stopEndPoints();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
|
||||
client.listeners()
|
||||
.add(new Listener<LargeMessage>() {
|
||||
AtomicInteger received = new AtomicInteger();
|
||||
AtomicInteger receivedBytes = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public
|
||||
void received(Connection connection, LargeMessage object) {
|
||||
this.receivedBytes.addAndGet(object.bytes.length);
|
||||
|
||||
int count = this.received.incrementAndGet();
|
||||
//System.out.println("Client received " + count + " messages.");
|
||||
|
||||
if (count == messageCount) {
|
||||
System.out.println("Client received all " + messageCount + " messages!");
|
||||
System.out.println("Client received and sent " + this.receivedBytes.get() + " bytes.");
|
||||
LargeBufferTest.this.clientCheck = LargeBufferTest.this.finalCheckAmount - this.receivedBytes.get();
|
||||
System.out.println("Client missed " + LargeBufferTest.this.clientCheck + " bytes.");
|
||||
}
|
||||
}
|
||||
});
|
||||
client.connect(5000);
|
||||
|
||||
client.listeners().add(new Listener<LargeMessage>() {
|
||||
AtomicInteger received = new AtomicInteger();
|
||||
AtomicInteger receivedBytes = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public void received (Connection connection, LargeMessage object) {
|
||||
this.receivedBytes.addAndGet(object.bytes.length);
|
||||
|
||||
int count = this.received.incrementAndGet();
|
||||
//System.out.println("Client received " + count + " messages.");
|
||||
|
||||
if (count == messageCount) {
|
||||
System.out.println("Client received all " + messageCount + " messages!");
|
||||
System.out.println("Client received and sent " + this.receivedBytes.get() + " bytes.");
|
||||
LargeBufferTest.this.clientCheck = LargeBufferTest.this.finalCheckAmount - this.receivedBytes.get();
|
||||
System.out.println("Client missed " + LargeBufferTest.this.clientCheck + " bytes.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] b = new byte[OBJ_SIZE];
|
||||
random.nextBytes(b);
|
||||
|
||||
System.err.println(" Client sending " + messageCount + " messages");
|
||||
for (int i = 0; i < messageCount; i++) {
|
||||
this.finalCheckAmount += OBJ_SIZE;
|
||||
System.err.println(" Client sending number: " + i);
|
||||
client.send().TCP(new LargeMessage(b));
|
||||
|
||||
byte[] b = new byte[OBJ_SIZE];
|
||||
random.nextBytes(b);
|
||||
client.send()
|
||||
.TCP(new LargeMessage(b));
|
||||
}
|
||||
System.err.println("Client has queued " + messageCount + " messages.");
|
||||
|
||||
|
@ -106,18 +116,22 @@ public class LargeBufferTest extends BaseTest {
|
|||
}
|
||||
}
|
||||
|
||||
private void register (ConnectionSerializationManager kryoMT) {
|
||||
private
|
||||
void register(CryptoSerializationManager kryoMT) {
|
||||
kryoMT.register(byte[].class);
|
||||
kryoMT.register(LargeMessage.class);
|
||||
}
|
||||
|
||||
public static class LargeMessage {
|
||||
public static
|
||||
class LargeMessage {
|
||||
public byte[] bytes;
|
||||
|
||||
public LargeMessage () {
|
||||
public
|
||||
LargeMessage() {
|
||||
}
|
||||
|
||||
public LargeMessage (byte[] bytes) {
|
||||
public
|
||||
LargeMessage(byte[] bytes) {
|
||||
this.bytes = bytes;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,11 @@ package dorkbox.network;
|
|||
|
||||
|
||||
import dorkbox.network.connection.*;
|
||||
import dorkbox.network.rmi.RmiBridge;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
@ -30,8 +32,8 @@ class ListenerTest extends BaseTest {
|
|||
// quick and dirty test to also test connection sub-classing
|
||||
class TestConnectionA extends ConnectionImpl {
|
||||
public
|
||||
TestConnectionA(Class<? extends EndPoint> type) {
|
||||
super(type);
|
||||
TestConnectionA(final Logger logger, final EndPoint endPoint, final RmiBridge rmiBridge) {
|
||||
super(logger, endPoint, rmiBridge);
|
||||
}
|
||||
|
||||
public
|
||||
|
@ -43,8 +45,8 @@ class ListenerTest extends BaseTest {
|
|||
|
||||
class TestConnectionB extends TestConnectionA {
|
||||
public
|
||||
TestConnectionB(Class<? extends EndPoint> type) {
|
||||
super(type);
|
||||
TestConnectionB(final Logger logger, final EndPoint endPoint, final RmiBridge rmiBridge) {
|
||||
super(logger, endPoint, rmiBridge);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -60,15 +62,15 @@ class ListenerTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void listener() throws SecurityException, InitializationException, IOException {
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(connectionOptions) {
|
||||
Server server = new Server(configuration) {
|
||||
@Override
|
||||
public
|
||||
TestConnectionA newConnection(Class<? extends EndPoint> type) {
|
||||
return new TestConnectionA(type);
|
||||
TestConnectionA newConnection(final Logger logger, final EndPoint endPoint, final RmiBridge rmiBridge) {
|
||||
return new TestConnectionA(logger, endPoint, rmiBridge);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -152,7 +154,7 @@ class ListenerTest extends BaseTest {
|
|||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package dorkbox.network;
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -19,15 +19,15 @@ class MultipleServerTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void multipleServers() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
KryoCryptoSerializationManager.DEFAULT.register(String[].class);
|
||||
|
||||
ConnectionOptions connectionOptions1 = new ConnectionOptions();
|
||||
connectionOptions1.tcpPort = tcpPort;
|
||||
connectionOptions1.udpPort = udpPort;
|
||||
connectionOptions1.localChannelName = "chan1";
|
||||
connectionOptions1.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
connectionOptions1.serializationManager.register(String[].class);
|
||||
Configuration configuration1 = new Configuration();
|
||||
configuration1.tcpPort = tcpPort;
|
||||
configuration1.udpPort = udpPort;
|
||||
configuration1.localChannelName = "chan1";
|
||||
|
||||
Server server1 = new Server(connectionOptions1);
|
||||
Server server1 = new Server(configuration1);
|
||||
server1.disableRemoteKeyValidation();
|
||||
addEndPoint(server1);
|
||||
|
||||
|
@ -46,14 +46,12 @@ class MultipleServerTest extends BaseTest {
|
|||
}
|
||||
});
|
||||
|
||||
ConnectionOptions connectionOptions2 = new ConnectionOptions();
|
||||
connectionOptions2.tcpPort = tcpPort + 1;
|
||||
connectionOptions2.udpPort = udpPort + 1;
|
||||
connectionOptions2.localChannelName = "chan2";
|
||||
connectionOptions2.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
connectionOptions2.serializationManager.register(String[].class);
|
||||
Configuration configuration2 = new Configuration();
|
||||
configuration2.tcpPort = tcpPort + 1;
|
||||
configuration2.udpPort = udpPort + 1;
|
||||
configuration2.localChannelName = "chan2";
|
||||
|
||||
Server server2 = new Server(connectionOptions2);
|
||||
Server server2 = new Server(configuration2);
|
||||
server2.disableRemoteKeyValidation();
|
||||
|
||||
addEndPoint(server2);
|
||||
|
@ -74,10 +72,10 @@ class MultipleServerTest extends BaseTest {
|
|||
|
||||
// ----
|
||||
|
||||
connectionOptions1.localChannelName = null;
|
||||
connectionOptions1.host = host;
|
||||
configuration1.localChannelName = null;
|
||||
configuration1.host = host;
|
||||
|
||||
Client client1 = new Client(connectionOptions1);
|
||||
Client client1 = new Client(configuration1);
|
||||
client1.disableRemoteKeyValidation();
|
||||
addEndPoint(client1);
|
||||
client1.listeners()
|
||||
|
@ -92,10 +90,10 @@ class MultipleServerTest extends BaseTest {
|
|||
client1.connect(5000);
|
||||
|
||||
|
||||
connectionOptions2.localChannelName = null;
|
||||
connectionOptions2.host = host;
|
||||
configuration2.localChannelName = null;
|
||||
configuration2.host = host;
|
||||
|
||||
Client client2 = new Client(connectionOptions2);
|
||||
Client client2 = new Client(configuration2);
|
||||
client2.disableRemoteKeyValidation();
|
||||
addEndPoint(client2);
|
||||
client2.listeners()
|
||||
|
|
|
@ -2,8 +2,8 @@ package dorkbox.network;
|
|||
|
||||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -19,40 +19,38 @@ import static org.junit.Assert.assertEquals;
|
|||
|
||||
public
|
||||
class MultipleThreadTest extends BaseTest {
|
||||
AtomicInteger sent = new AtomicInteger(0);
|
||||
AtomicInteger totalClientCounter = new AtomicInteger(1);
|
||||
AtomicInteger receivedServer = new AtomicInteger(1);
|
||||
|
||||
private final Object lock = new Object();
|
||||
ConcurrentHashMap<Integer, DataClass> sentStringsToClientDebug = new ConcurrentHashMap<Integer, DataClass>();
|
||||
|
||||
private final int messageCount = 150;
|
||||
private final int threadCount = 15;
|
||||
private final int clientCount = 13;
|
||||
|
||||
private final List<Client> clients = new ArrayList<Client>(this.clientCount);
|
||||
|
||||
AtomicInteger sent = new AtomicInteger(0);
|
||||
AtomicInteger totalClientCounter = new AtomicInteger(1);
|
||||
AtomicInteger receivedServer = new AtomicInteger(1);
|
||||
ConcurrentHashMap<Integer, DataClass> sentStringsToClientDebug = new ConcurrentHashMap<Integer, DataClass>();
|
||||
|
||||
@Test
|
||||
public
|
||||
void multipleThreads() throws InitializationException, SecurityException, IOException {
|
||||
final KryoConnectionSerializationManager serializationManager = KryoConnectionSerializationManager.DEFAULT(false, true);
|
||||
serializationManager.register(String[].class);
|
||||
serializationManager.register(DataClass.class);
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
KryoCryptoSerializationManager.DEFAULT.register(String[].class);
|
||||
KryoCryptoSerializationManager.DEFAULT.register(DataClass.class);
|
||||
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = serializationManager;
|
||||
|
||||
|
||||
final Server server = new Server(connectionOptions);
|
||||
final Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
server.listeners()
|
||||
.add(new Listener<DataClass>() {
|
||||
private final int total = MultipleThreadTest.this.messageCount * MultipleThreadTest.this.clientCount;
|
||||
|
||||
@Override
|
||||
public
|
||||
void connected(final Connection connection) {
|
||||
|
@ -70,6 +68,7 @@ class MultipleThreadTest extends BaseTest {
|
|||
DataClass dataClass = new DataClass(
|
||||
"Server -> client. Thread #" + index + " message# " + incrementAndGet,
|
||||
incrementAndGet);
|
||||
|
||||
MultipleThreadTest.this.sentStringsToClientDebug.put(incrementAndGet, dataClass);
|
||||
connection.send()
|
||||
.TCP(dataClass)
|
||||
|
@ -87,7 +86,7 @@ class MultipleThreadTest extends BaseTest {
|
|||
int incrementAndGet = MultipleThreadTest.this.receivedServer.getAndIncrement();
|
||||
|
||||
|
||||
if (incrementAndGet == MultipleThreadTest.this.messageCount * MultipleThreadTest.this.clientCount) {
|
||||
if (incrementAndGet == total) {
|
||||
System.err.println("Server DONE " + incrementAndGet);
|
||||
// note. this is getting called BEFORE it's ready?
|
||||
stopEndPoints();
|
||||
|
@ -103,7 +102,7 @@ class MultipleThreadTest extends BaseTest {
|
|||
for (int i = 1; i <= this.clientCount; i++) {
|
||||
final int index = i;
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
|
||||
this.clients.add(client);
|
||||
|
@ -112,6 +111,7 @@ class MultipleThreadTest extends BaseTest {
|
|||
client.listeners()
|
||||
.add(new Listener<DataClass>() {
|
||||
AtomicInteger received = new AtomicInteger(1);
|
||||
private final int total = MultipleThreadTest.this.messageCount * MultipleThreadTest.this.clientCount;
|
||||
|
||||
@Override
|
||||
public
|
||||
|
@ -126,10 +126,11 @@ class MultipleThreadTest extends BaseTest {
|
|||
MultipleThreadTest.this.sentStringsToClientDebug.remove(object.index);
|
||||
|
||||
// we finished!!
|
||||
if (clientLocalCounter == MultipleThreadTest.this.messageCount * MultipleThreadTest.this.threadCount) {
|
||||
if (clientLocalCounter == total) {
|
||||
System.err.println("Client #" + index + " received " + clientLocalCounter + " (" +
|
||||
MultipleThreadTest.this.totalClientCounter.getAndIncrement() + ") Sending back " +
|
||||
MultipleThreadTest.this.messageCount + " messages.");
|
||||
|
||||
// now spam back messages!
|
||||
for (int i = 0; i < MultipleThreadTest.this.messageCount; i++) {
|
||||
connection.send()
|
||||
|
|
|
@ -3,7 +3,7 @@ package dorkbox.network;
|
|||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -126,7 +126,7 @@ public class PingPongLocalTest extends BaseTest {
|
|||
data.Booleans = new Boolean[] {true,false};
|
||||
}
|
||||
|
||||
private void register(ConnectionSerializationManager kryoMT) {
|
||||
private void register(CryptoSerializationManager kryoMT) {
|
||||
kryoMT.register(int[].class);
|
||||
kryoMT.register(short[].class);
|
||||
kryoMT.register(float[].class);
|
||||
|
|
|
@ -3,9 +3,9 @@ package dorkbox.network;
|
|||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -31,19 +31,21 @@ class PingPongTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void pingPong() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
// UDP data is kinda big. Make sure it fits into one packet.
|
||||
int origSize = EndPoint.udpMaxSize;
|
||||
EndPoint.udpMaxSize = 2048;
|
||||
|
||||
this.fail = "Data not received.";
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.udtPort = udtPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT();
|
||||
register(connectionOptions.serializationManager);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.udtPort = udtPort;
|
||||
configuration.host = host;
|
||||
|
||||
|
||||
final Data dataTCP = new Data();
|
||||
populateData(dataTCP, TYPE.TCP);
|
||||
|
@ -52,7 +54,7 @@ class PingPongTest extends BaseTest {
|
|||
final Data dataUDT = new Data();
|
||||
populateData(dataUDT, TYPE.UDT);
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
@ -100,7 +102,7 @@ class PingPongTest extends BaseTest {
|
|||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
client.listeners()
|
||||
|
@ -199,11 +201,11 @@ class PingPongTest extends BaseTest {
|
|||
EndPoint.udpMaxSize = origSize;
|
||||
}
|
||||
|
||||
private
|
||||
private static
|
||||
void populateData(Data data, TYPE type) {
|
||||
data.type = type;
|
||||
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
StringBuilder buffer = new StringBuilder(3001);
|
||||
for (int i = 0; i < 3000; i++) {
|
||||
buffer.append('a');
|
||||
}
|
||||
|
@ -232,7 +234,7 @@ class PingPongTest extends BaseTest {
|
|||
}
|
||||
|
||||
private
|
||||
void register(ConnectionSerializationManager kryoMT) {
|
||||
void register(CryptoSerializationManager kryoMT) {
|
||||
kryoMT.register(int[].class);
|
||||
kryoMT.register(short[].class);
|
||||
kryoMT.register(float[].class);
|
||||
|
@ -254,7 +256,7 @@ class PingPongTest extends BaseTest {
|
|||
kryoMT.register(TYPE.class);
|
||||
}
|
||||
|
||||
static public
|
||||
public static
|
||||
class Data {
|
||||
public TYPE type;
|
||||
public String string;
|
||||
|
|
|
@ -24,18 +24,18 @@ class PingTest extends BaseTest {
|
|||
void pingTCP() throws InitializationException, SecurityException, IOException {
|
||||
this.response = -1;
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
|
||||
|
@ -60,18 +60,18 @@ class PingTest extends BaseTest {
|
|||
void pingTCP_testListeners1() throws InitializationException, SecurityException, IOException {
|
||||
this.response = -1;
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
|
||||
|
@ -117,18 +117,18 @@ class PingTest extends BaseTest {
|
|||
void pingTCP_testListeners2() throws InitializationException, SecurityException, IOException {
|
||||
this.response = -1;
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
|
||||
|
@ -166,20 +166,20 @@ class PingTest extends BaseTest {
|
|||
void pingUDP() throws InitializationException, SecurityException, IOException {
|
||||
this.response = -1;
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
|
||||
|
@ -207,19 +207,19 @@ class PingTest extends BaseTest {
|
|||
void pingUDT() throws InitializationException, SecurityException, IOException {
|
||||
this.response = -1;
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udtPort = udtPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udtPort = udtPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
|
||||
|
|
|
@ -22,12 +22,12 @@ class ReconnectTest extends BaseTest {
|
|||
void reconnect() throws InitializationException, SecurityException, IOException {
|
||||
final Timer timer = new Timer();
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
|
||||
|
||||
final Server server = new Server(connectionOptions);
|
||||
final Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
@ -50,7 +50,7 @@ class ReconnectTest extends BaseTest {
|
|||
// ----
|
||||
|
||||
final AtomicInteger reconnectCount = new AtomicInteger();
|
||||
final Client client = new Client(connectionOptions);
|
||||
final Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
client.listeners()
|
||||
|
|
|
@ -23,12 +23,12 @@ class ReuseTest extends BaseTest {
|
|||
this.serverCount = new AtomicInteger(0);
|
||||
this.clientCount = new AtomicInteger(0);
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.listeners()
|
||||
|
@ -52,7 +52,7 @@ class ReuseTest extends BaseTest {
|
|||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
client.listeners()
|
||||
|
|
|
@ -3,8 +3,8 @@ package dorkbox.network;
|
|||
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -27,14 +27,15 @@ class UnregisteredClassTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void unregisteredClasses() throws InitializationException, SecurityException, IOException {
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT(false, false);
|
||||
|
||||
int origSize = EndPoint.udpMaxSize;
|
||||
EndPoint.udpMaxSize = 2048;
|
||||
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT(false, false);
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
|
||||
System.err.println("Running test " + this.tries + " times, please wait for it to finish.");
|
||||
|
||||
|
@ -43,7 +44,7 @@ class UnregisteredClassTest extends BaseTest {
|
|||
final Data dataUDP = new Data();
|
||||
populateData(dataUDP, false);
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
@ -81,7 +82,7 @@ class UnregisteredClassTest extends BaseTest {
|
|||
|
||||
// ----
|
||||
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
addEndPoint(client);
|
||||
client.listeners()
|
||||
|
|
|
@ -2,12 +2,11 @@ package dorkbox.network.rmi;
|
|||
|
||||
import dorkbox.network.BaseTest;
|
||||
import dorkbox.network.Client;
|
||||
import dorkbox.network.ConnectionOptions;
|
||||
import dorkbox.network.Configuration;
|
||||
import dorkbox.network.Server;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Test;
|
||||
|
@ -15,10 +14,10 @@ import org.junit.Test;
|
|||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public
|
||||
class RmiSendObjectTest extends BaseTest {
|
||||
private Rmi serverRMI;
|
||||
|
||||
/**
|
||||
* In this test the server has two objects in an object space. The client
|
||||
|
@ -27,52 +26,44 @@ class RmiSendObjectTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void rmi() throws InitializationException, SecurityException, IOException {
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.enableRmi = true;
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
KryoCryptoSerializationManager.DEFAULT.registerRemote(TestObject.class, TestObjectImpl.class);
|
||||
KryoCryptoSerializationManager.DEFAULT.registerRemote(OtherObject.class, OtherObjectImpl.class);
|
||||
|
||||
Server server = new Server(connectionOptions);
|
||||
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.host = host;
|
||||
configuration.rmiEnabled = true;
|
||||
|
||||
Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
ConnectionSerializationManager serverSerializationManager = server.getSerialization();
|
||||
register(server, serverSerializationManager);
|
||||
server.setIdleTimeout(0);
|
||||
|
||||
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
|
||||
// After all common registrations, register OtherObjectImpl only on the server using the remote object interface ID.
|
||||
// This causes OtherObjectImpl to be serialized as OtherObject.
|
||||
int otherObjectID = serverSerializationManager.getRegistration(OtherObject.class)
|
||||
.getId();
|
||||
serverSerializationManager.register(OtherObjectImpl.class, new RemoteObjectSerializer<OtherObjectImpl>(server), otherObjectID);
|
||||
|
||||
|
||||
// TestObjectImpl has a reference to an OtherObjectImpl.
|
||||
final TestObjectImpl serverTestObject = new TestObjectImpl();
|
||||
serverTestObject.otherObject = new OtherObjectImpl();
|
||||
|
||||
// Both objects must be registered with the ObjectSpace.
|
||||
this.serverRMI = server.rmi();
|
||||
this.serverRMI.register(42, serverTestObject);
|
||||
this.serverRMI.register(777, serverTestObject.getOtherObject());
|
||||
|
||||
server.listeners()
|
||||
.add(new Listener<OtherObjectImpl>() {
|
||||
@Override
|
||||
public
|
||||
void received(Connection connection, OtherObjectImpl object) {
|
||||
// The test is complete when the client sends the OtherObject instance.
|
||||
if (object == serverTestObject.getOtherObject()) {
|
||||
if (object.value() == 12.34f) {
|
||||
stopEndPoints();
|
||||
} else {
|
||||
fail("Incorrect object value");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// ----
|
||||
Client client = new Client(connectionOptions);
|
||||
Client client = new Client(configuration);
|
||||
client.disableRemoteKeyValidation();
|
||||
register(client, client.getSerialization());
|
||||
client.setIdleTimeout(0);
|
||||
|
||||
addEndPoint(client);
|
||||
client.listeners()
|
||||
|
@ -84,13 +75,15 @@ class RmiSendObjectTest extends BaseTest {
|
|||
@Override
|
||||
public
|
||||
void run() {
|
||||
TestObject test = connection.getRemoteObject(42, TestObject.class);
|
||||
TestObject test = connection.createRemoteObject(TestObject.class, TestObjectImpl.class);
|
||||
test.setOther(43.21f);
|
||||
// Normal remote method call.
|
||||
assertEquals(43.21f, test.other(), .0001f);
|
||||
|
||||
// Make a remote method call that returns another remote proxy object.
|
||||
OtherObject otherObject = test.getOtherObject();
|
||||
// Normal remote method call on the second object.
|
||||
otherObject.setValue(12.34f);
|
||||
float value = otherObject.value();
|
||||
assertEquals(12.34f, value, .0001f);
|
||||
|
||||
|
@ -109,31 +102,40 @@ class RmiSendObjectTest extends BaseTest {
|
|||
waitForThreads(20);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the same classes in the same order on both the client and server.
|
||||
*/
|
||||
public static
|
||||
void register(EndPoint endpoint, ConnectionSerializationManager kryoMT) {
|
||||
kryoMT.register(TestObject.class);
|
||||
kryoMT.register(OtherObject.class, new RemoteObjectSerializer<OtherObject>(endpoint));
|
||||
}
|
||||
|
||||
public
|
||||
interface TestObject {
|
||||
void setOther(float aFloat);
|
||||
|
||||
float other();
|
||||
|
||||
OtherObject getOtherObject();
|
||||
}
|
||||
|
||||
|
||||
public
|
||||
interface OtherObject {
|
||||
void setValue(float aFloat);
|
||||
float value();
|
||||
}
|
||||
|
||||
|
||||
public static
|
||||
class TestObjectImpl implements TestObject {
|
||||
public OtherObject otherObject;
|
||||
@RemoteProxy
|
||||
private OtherObject otherObject = new OtherObjectImpl();
|
||||
private float aFloat;
|
||||
|
||||
|
||||
@Override
|
||||
public
|
||||
void setOther(final float aFloat) {
|
||||
this.aFloat = aFloat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
float other() {
|
||||
return 43.21f;
|
||||
return aFloat;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -144,18 +146,20 @@ class RmiSendObjectTest extends BaseTest {
|
|||
}
|
||||
|
||||
|
||||
public
|
||||
interface OtherObject {
|
||||
float value();
|
||||
}
|
||||
|
||||
|
||||
public static
|
||||
class OtherObjectImpl implements OtherObject {
|
||||
private float aFloat;
|
||||
|
||||
@Override
|
||||
public
|
||||
void setValue(final float aFloat) {
|
||||
this.aFloat = aFloat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
float value() {
|
||||
return 12.34f;
|
||||
return aFloat;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,42 +1,39 @@
|
|||
package dorkbox.network.rmi;
|
||||
|
||||
|
||||
import dorkbox.network.BaseTest;
|
||||
import dorkbox.network.Client;
|
||||
import dorkbox.network.ConnectionOptions;
|
||||
import dorkbox.network.Server;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.ConnectionSerializationManager;
|
||||
import dorkbox.network.util.KryoConnectionSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import dorkbox.network.BaseTest;
|
||||
import dorkbox.network.Client;
|
||||
import dorkbox.network.Configuration;
|
||||
import dorkbox.network.Server;
|
||||
import dorkbox.network.connection.Connection;
|
||||
import dorkbox.network.connection.KryoCryptoSerializationManager;
|
||||
import dorkbox.network.connection.Listener;
|
||||
import dorkbox.network.util.CryptoSerializationManager;
|
||||
import dorkbox.network.util.exceptions.InitializationException;
|
||||
import dorkbox.network.util.exceptions.SecurityException;
|
||||
|
||||
public
|
||||
class RmiTest extends BaseTest {
|
||||
|
||||
private static final int CLIENT_ID = 4321;
|
||||
private static final int SERVER_ID = 1234;
|
||||
|
||||
private static final int CLIENT_REMOTE_ID = 42;
|
||||
private static final int SERVER_REMOTE_ID = 12;
|
||||
|
||||
private static
|
||||
void runTest(final Connection connection, final int id, final int otherID) {
|
||||
void runTest(final Connection connection, final int remoteObjectID) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
System.err.println("Starting test for: " + id);
|
||||
TestObject test = connection.createRemoteObject(TestObject.class, TestObjectImpl.class);
|
||||
|
||||
TestObject test = connection.getRemoteObject(id, TestObject.class);
|
||||
System.err.println("Starting test for: " + remoteObjectID);
|
||||
|
||||
//TestObject test = connection.getRemoteObject(id, TestObject.class);
|
||||
RemoteObject remoteObject = (RemoteObject) test;
|
||||
|
||||
// Default behavior. RMI is transparent, method calls behave like normal
|
||||
|
@ -45,7 +42,7 @@ class RmiTest extends BaseTest {
|
|||
System.err.println("toString: " + test);
|
||||
test.moo();
|
||||
test.moo("Cow");
|
||||
assertEquals(otherID, test.id());
|
||||
assertEquals(remoteObjectID, test.id());
|
||||
|
||||
|
||||
// UDP calls that ignore the return value
|
||||
|
@ -95,11 +92,13 @@ class RmiTest extends BaseTest {
|
|||
test.moo("Foo");
|
||||
|
||||
assertEquals(0, test.id());
|
||||
assertEquals(otherID, remoteObject.waitForLastResponse());
|
||||
// wait for the response to id()
|
||||
assertEquals(remoteObjectID, remoteObject.waitForLastResponse());
|
||||
|
||||
assertEquals(0, test.id());
|
||||
byte responseID = remoteObject.getLastResponseID();
|
||||
assertEquals(otherID, remoteObject.waitForResponse(responseID));
|
||||
// wait for the response to id()
|
||||
assertEquals(remoteObjectID, remoteObject.waitForResponse(responseID));
|
||||
|
||||
// Non-blocking call that errors out
|
||||
remoteObject.setTransmitReturnValue(false);
|
||||
|
@ -115,7 +114,7 @@ class RmiTest extends BaseTest {
|
|||
MessageWithTestObject m = new MessageWithTestObject();
|
||||
m.number = 678;
|
||||
m.text = "sometext";
|
||||
m.testObject = connection.getRemoteObject(id, TestObject.class);
|
||||
m.testObject = test;
|
||||
connection.send()
|
||||
.TCP(m)
|
||||
.flush();
|
||||
|
@ -124,12 +123,11 @@ class RmiTest extends BaseTest {
|
|||
}
|
||||
|
||||
public static
|
||||
void register(ConnectionSerializationManager kryoMT) {
|
||||
void register(CryptoSerializationManager kryoMT) {
|
||||
kryoMT.register(Object.class); // Needed for Object#toString, hashCode, etc.
|
||||
kryoMT.register(TestObject.class);
|
||||
|
||||
kryoMT.registerRemote(TestObject.class, TestObjectImpl.class);
|
||||
kryoMT.register(MessageWithTestObject.class);
|
||||
kryoMT.register(StackTraceElement.class);
|
||||
kryoMT.register(StackTraceElement[].class);
|
||||
|
||||
kryoMT.register(UnsupportedOperationException.class);
|
||||
}
|
||||
|
@ -137,222 +135,73 @@ class RmiTest extends BaseTest {
|
|||
@Test
|
||||
public
|
||||
void rmi() throws InitializationException, SecurityException, IOException {
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.udpPort = udpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.enableRmi = true;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT(true, true);
|
||||
register(connectionOptions.serializationManager);
|
||||
KryoCryptoSerializationManager.DEFAULT = KryoCryptoSerializationManager.DEFAULT();
|
||||
register(KryoCryptoSerializationManager.DEFAULT);
|
||||
|
||||
final Server server = new Server(connectionOptions);
|
||||
|
||||
Configuration configuration = new Configuration();
|
||||
configuration.tcpPort = tcpPort;
|
||||
configuration.udpPort = udpPort;
|
||||
configuration.host = host;
|
||||
configuration.rmiEnabled = true;
|
||||
|
||||
final Server server = new Server(configuration);
|
||||
server.disableRemoteKeyValidation();
|
||||
server.setIdleTimeout(0);
|
||||
|
||||
register(server.getSerialization());
|
||||
|
||||
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
// have to have this happen BEFORE any connections are made.
|
||||
server.rmi()
|
||||
.register(SERVER_REMOTE_ID, new TestObjectImpl(SERVER_ID));
|
||||
|
||||
server.listeners()
|
||||
.add(new Listener<MessageWithTestObject>() {
|
||||
@Override
|
||||
public
|
||||
void connected(final Connection connection) {
|
||||
RmiTest.runTest(connection, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void received(Connection connection, MessageWithTestObject m) {
|
||||
assertEquals(SERVER_ID, m.testObject.id());
|
||||
System.err.println("Client Finished!");
|
||||
TestObject object = m.testObject;
|
||||
final int id = object.id();
|
||||
assertEquals(2, id);
|
||||
System.err.println("Client/Server Finished!");
|
||||
|
||||
stopEndPoints(2000);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
final Client client = new Client(configuration);
|
||||
client.setIdleTimeout(0);
|
||||
client.disableRemoteKeyValidation();
|
||||
|
||||
addEndPoint(client);
|
||||
|
||||
client.listeners()
|
||||
.add(new Listener<MessageWithTestObject>() {
|
||||
@Override
|
||||
public
|
||||
void received(Connection connection, MessageWithTestObject m) {
|
||||
TestObject object = m.testObject;
|
||||
final int id = object.id();
|
||||
assertEquals(1, id);
|
||||
System.err.println("Server/Client Finished!");
|
||||
|
||||
// normally this is in the 'connected', but we do it here, so that it's more linear and easier to debug
|
||||
runTest(connection, CLIENT_REMOTE_ID, CLIENT_ID);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
final Client client = new Client(connectionOptions);
|
||||
client.disableRemoteKeyValidation();
|
||||
|
||||
addEndPoint(client);
|
||||
|
||||
// have to have this happen BEFORE any connections are made.
|
||||
client.rmi()
|
||||
.register(CLIENT_REMOTE_ID, new TestObjectImpl(CLIENT_ID));
|
||||
|
||||
client.listeners()
|
||||
.add(new Listener<MessageWithTestObject>() {
|
||||
@Override
|
||||
public
|
||||
void connected(final Connection connection) {
|
||||
RmiTest.runTest(connection, SERVER_REMOTE_ID, SERVER_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
public
|
||||
void received(Connection connection, MessageWithTestObject m) {
|
||||
assertEquals(CLIENT_ID, m.testObject.id());
|
||||
System.err.println("Server Finished!");
|
||||
|
||||
stopEndPoints(2000);
|
||||
runTest(connection, 2);
|
||||
}
|
||||
});
|
||||
|
||||
client.connect(5000);
|
||||
waitForThreads(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
public
|
||||
void rmiMany() throws InitializationException, SecurityException, IOException {
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.enableRmi = true;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT(true, true);
|
||||
register(connectionOptions.serializationManager);
|
||||
|
||||
|
||||
final Server server = new Server(connectionOptions);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
// have to have this happen BEFORE any connections are made.
|
||||
final TestObjectImpl serverTestObject = new TestObjectImpl(CLIENT_ID);
|
||||
server.rmi()
|
||||
.register(CLIENT_REMOTE_ID, serverTestObject);
|
||||
|
||||
server.listeners()
|
||||
.add(new Listener<MessageWithTestObject>() {
|
||||
@Override
|
||||
public
|
||||
void received(Connection connection, MessageWithTestObject m) {
|
||||
assertEquals(256 + 512 + 1024, serverTestObject.moos);
|
||||
System.err.println("Client Finished!");
|
||||
stopEndPoints(2000);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
final Client client = new Client(connectionOptions);
|
||||
client.disableRemoteKeyValidation();
|
||||
|
||||
addEndPoint(client);
|
||||
|
||||
client.listeners()
|
||||
.add(new Listener<MessageWithTestObject>() {
|
||||
@Override
|
||||
public
|
||||
void connected(final Connection connection) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
TestObject test = connection.getRemoteObject(CLIENT_REMOTE_ID, TestObject.class);
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
assertEquals(CLIENT_ID, test.id());
|
||||
}
|
||||
|
||||
for (int i = 0; i < 256; i++) {
|
||||
test.moo();
|
||||
}
|
||||
for (int i = 0; i < 256; i++) {
|
||||
test.moo("" + i);
|
||||
}
|
||||
for (int i = 0; i < 256; i++) {
|
||||
test.moo("" + i, 0);
|
||||
}
|
||||
|
||||
connection.send()
|
||||
.TCP(new MessageWithTestObject())
|
||||
.flush();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
});
|
||||
|
||||
client.connect(5000);
|
||||
waitForThreads(30);
|
||||
}
|
||||
|
||||
@Test
|
||||
public
|
||||
void rmiSlow() throws InitializationException, SecurityException, IOException {
|
||||
ConnectionOptions connectionOptions = new ConnectionOptions();
|
||||
connectionOptions.tcpPort = tcpPort;
|
||||
connectionOptions.host = host;
|
||||
connectionOptions.enableRmi = true;
|
||||
connectionOptions.serializationManager = KryoConnectionSerializationManager.DEFAULT(true, true);
|
||||
register(connectionOptions.serializationManager);
|
||||
|
||||
|
||||
final Server server = new Server(connectionOptions);
|
||||
server.disableRemoteKeyValidation();
|
||||
addEndPoint(server);
|
||||
server.bind(false);
|
||||
|
||||
// have to have this happen BEFORE any connections are made.
|
||||
final TestObjectImpl serverTestObject = new TestObjectImpl(CLIENT_ID);
|
||||
server.rmi()
|
||||
.register(CLIENT_REMOTE_ID, serverTestObject);
|
||||
|
||||
server.listeners()
|
||||
.add(new Listener<MessageWithTestObject>() {
|
||||
@Override
|
||||
public
|
||||
void received(Connection connection, MessageWithTestObject m) {
|
||||
System.err.println("Client Finished!");
|
||||
stopEndPoints(2000);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
// ----
|
||||
|
||||
final Client client = new Client(connectionOptions);
|
||||
client.disableRemoteKeyValidation();
|
||||
|
||||
addEndPoint(client);
|
||||
|
||||
client.listeners()
|
||||
.add(new Listener<MessageWithTestObject>() {
|
||||
@Override
|
||||
public
|
||||
void connected(final Connection connection) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public
|
||||
void run() {
|
||||
TestObject test = connection.getRemoteObject(CLIENT_REMOTE_ID, TestObject.class);
|
||||
test.id();
|
||||
// Timeout on purpose.
|
||||
try {
|
||||
((RemoteObject) test).setResponseTimeout(200);
|
||||
test.slow();
|
||||
Assert.fail();
|
||||
} catch (TimeoutException ignored) {
|
||||
}
|
||||
try {
|
||||
Thread.sleep(300);
|
||||
} catch (InterruptedException ignored) {
|
||||
}
|
||||
|
||||
((RemoteObject) test).setResponseTimeout(3000);
|
||||
connection.send()
|
||||
.TCP(new MessageWithTestObject())
|
||||
.flush();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
client.connect(5000);
|
||||
waitForThreads(30);
|
||||
waitForThreads();
|
||||
}
|
||||
|
||||
public
|
||||
|
@ -373,13 +222,15 @@ class RmiTest extends BaseTest {
|
|||
|
||||
public static
|
||||
class TestObjectImpl implements TestObject {
|
||||
private final int id;
|
||||
// has to start at 1, because UDP/UDT method invocations ignore return values
|
||||
static final AtomicInteger ID_COUNTER = new AtomicInteger(1);
|
||||
|
||||
public long value = System.currentTimeMillis();
|
||||
public int moos;
|
||||
private final int id = ID_COUNTER.getAndIncrement();
|
||||
|
||||
public
|
||||
TestObjectImpl(int id) {
|
||||
this.id = id;
|
||||
TestObjectImpl() {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -417,7 +268,7 @@ class RmiTest extends BaseTest {
|
|||
@Override
|
||||
public
|
||||
int id() {
|
||||
return this.id;
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue
Block a user