Updated Netty, passes all unit tests

This commit is contained in:
nathan 2015-07-20 14:18:34 +02:00
parent 9a58cc52f4
commit cbb4a1942c
75 changed files with 3550 additions and 3408 deletions

View File

@ -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>

View File

@ -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">

View File

@ -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.

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View 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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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());
}
/**

View File

@ -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);

View File

@ -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 " <== ";
}
}

View File

@ -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();
}
}
}

View File

@ -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);
}

View File

@ -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)
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 " <== ";
}
}
}

View File

@ -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);
}
}

View File

@ -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".

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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.

View File

@ -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!");
}

View File

@ -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);
}
}
}

View File

@ -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!");
}

View File

@ -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 {

View File

@ -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);

View File

@ -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;

View File

@ -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());
}

View File

@ -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)) {

View File

@ -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);
}
}
}

View 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 {
}

View File

@ -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);
}

View File

@ -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;
}
});
}
});
}
}

View File

@ -1,7 +0,0 @@
package dorkbox.network.rmi;
import com.esotericsoftware.kryo.Kryo;
public interface RmiRegisterClassesCallback {
public void registerForClasses(Kryo kryo);
}

View 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;
}
}

View File

@ -1,7 +0,0 @@
package dorkbox.network.rmi;
import com.esotericsoftware.kryo.Serializer;
public interface SerializerRegistration<T extends Serializer<?>> {
public void register(T serializer);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -1,5 +1,5 @@
package dorkbox.network.util;
public interface EndpointTool {
public interface EndPointTool {
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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) {
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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()

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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()

View File

@ -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()

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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;
}
}
}

View File

@ -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