diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/Asynchronous.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/Asynchronous.java index 8deada70b..a26d09635 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/Asynchronous.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/Asynchronous.java @@ -41,7 +41,9 @@ import static java.lang.annotation.RetentionPolicy.*; /** * Indicates that a given method should be executed asynchronously * through the RMI service. This must annotate the method on the - * shared interface for it to have an effect. + * shared interface for it to have an effect. If reliable=false + * is specified then remote method invocation is done over UDP + * instead of TCP, ie: unreliably... but faster. * * @author Paul Speed */ diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/CallType.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/CallType.java index 6b218891d..5167bd7eb 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/CallType.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/CallType.java @@ -34,10 +34,27 @@ package com.jme3.network.service.rmi; /** - * + * Internal type denoting the type of call to make when remotely + * invoking methods. * * @author Paul Speed */ public enum CallType { - Synchronous, Asynchronous, Unreliable + /** + * Caller will block until a response is received and returned. + */ + Synchronous, + + /** + * Caller does not block or wait for a response. The other end + * of the connection will also not send one. + */ + Asynchronous, + + /** + * Similar to asynchronous in that no response is expected or sent + * but differs in that the call will be sent over UDP and so may + * not make it to the other end. + */ + Unreliable } diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfo.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfo.java index 88d91a686..4f7f7de32 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfo.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfo.java @@ -39,7 +39,8 @@ import java.util.List; /** - * + * Internal information about a shared class. This is the information + * that is sent over the wire for shared types. * * @author Paul Speed */ diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfoRegistry.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfoRegistry.java index 557948042..addc03593 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfoRegistry.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/ClassInfoRegistry.java @@ -40,7 +40,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; /** - * + * Internal registry of shared types and their ClassInfo and MethodInfo + * objects. * * @author Paul Speed */ diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/MethodInfo.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/MethodInfo.java index a0138556d..9b324a6c3 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/MethodInfo.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/MethodInfo.java @@ -39,7 +39,8 @@ import javax.jws.Oneway; /** - * + * Internal information about shared methods. This is part of the data that + * is passed over the wire when an object is shared. * * @author Paul Speed */ diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RemoteObjectHandler.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RemoteObjectHandler.java index 03309a4fa..f42ada84f 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RemoteObjectHandler.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RemoteObjectHandler.java @@ -39,7 +39,7 @@ import java.util.concurrent.ConcurrentHashMap; /** - * + * Used internally to remotely invoke methods on RMI shared objects. * * @author Paul Speed */ diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiClientService.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiClientService.java index 437a697f2..ea1506f72 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiClientService.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiClientService.java @@ -41,7 +41,23 @@ import java.util.List; /** + * A service that can be added to the client to support a simple + * shared objects protocol. * + *

Objects are shared by adding them to the RmiRegistry with one of the + * share() methods. Shared objects must have a separate interface and implementation. + * The interface is what the other end of the connection will use to interact + * with the object and that interface class must be available on both ends of + * the connection. The implementing class need only be on the sharing end.

+ * + *

Shared objects can be accessed on the other end of the connection by + * using one of the RmiRegistry's getRemoteObject() methods. These can be + * used to lookup an object by class if it is a shared singleton or by name + * if it was registered with a name.

+ * + *

Note: This RMI implementation is not as advanced as Java's regular + * RMI as it won't marshall shared references, ie: you can't pass + * a shared objects as an argument to another shared object's method.

* * @author Paul Speed */ @@ -64,18 +80,42 @@ public class RmiClientService extends AbstractClientService { this.rmiObjectId = rmiObjectId; } + /** + * Shares the specified object with the server and associates it with the + * specified type. Objects shared in this way are available in the connection-specific + * RMI registry on the server and are not available to other connections. + */ public void share( T object, Class type ) { share(defaultChannel, object, type); } + /** + * Shares the specified object with the server and associates it with the + * specified type. Objects shared in this way are available in the connection-specific + * RMI registry on the server and are not available to other connections. + * All object related communication will be done over the specified connection + * channel. + */ public void share( byte channel, T object, Class type ) { share(channel, type.getName(), object, type); } + /** + * Shares the specified object with the server and associates it with the + * specified name. Objects shared in this way are available in the connection-specific + * RMI registry on the server and are not available to other connections. + */ public void share( String name, T object, Class type ) { share(defaultChannel, name, object, type); } + /** + * Shares the specified object with the server and associates it with the + * specified name. Objects shared in this way are available in the connection-specific + * RMI registry on the server and are not available to other connections. + * All object related communication will be done over the specified connection + * channel. + */ public void share( byte channel, String name, T object, Class type ) { if( !isStarted ) { synchronized(pending) { @@ -90,10 +130,18 @@ public class RmiClientService extends AbstractClientService { rmi.share(channel, name, object, type); } + /** + * Looks up a remote object on the server by type and returns a local proxy to the + * remote object that was shared on the other end of the network connection. + */ public T getRemoteObject( Class type ) { return rmi.getRemoteObject(type); } + /** + * Looks up a remote object on the server by name and returns a local proxy to the + * remote object that was shared on the other end of the network connection. + */ public T getRemoteObject( String name, Class type ) { return rmi.getRemoteObject(name, type); } diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiContext.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiContext.java index 47d115d5b..547e83dd4 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiContext.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiContext.java @@ -45,7 +45,11 @@ import com.jme3.network.HostedConnection; */ public class RmiContext { private static final ThreadLocal connection = new ThreadLocal(); - + + /** + * Returns the HostedConnection that is responsible for any + * RMI-related calls on this thread. + */ public static HostedConnection getRmiConnection() { return connection.get(); } diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiHostedService.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiHostedService.java index 647fe4fb6..997658a77 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiHostedService.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiHostedService.java @@ -46,7 +46,30 @@ import java.util.logging.Logger; /** + * A service that can be added to the host to support a simple + * shared objects protocol. * + *

Objects are shared by adding them to the RmiRegistry with one of the + * share() methods. Shared objects must have a separate interface and implementation. + * The interface is what the other end of the connection will use to interact + * with the object and that interface class must be available on both ends of + * the connection. The implementing class need only be on the sharing end.

+ * + *

Shared objects can be accessed on the other end of the connection by + * using one of the RmiRegistry's getRemoteObject() methods. These can be + * used to lookup an object by class if it is a shared singleton or by name + * if it was registered with a name.

+ * + *

On the hosting side, a special shardGlobal() method is provided that + * will register shared objects that will automatically be provided to every + * new joining client and they will all be calling the same server-side instance. + * Normally, shared objects themselves are connection specific and handled + * at the connection layer. The shareGlobal() space is a way to have global + * resources passed directly though the need is relatively rare.

+ * + *

Note: This RMI implementation is not as advanced as Java's regular + * RMI as it won't marshall shared references, ie: you can't pass + * a shared objects as an argument to another shared object's method.

* * @author Paul Speed */ @@ -74,14 +97,34 @@ public class RmiHostedService extends AbstractHostedService { Serializer.registerClasses(ClassInfo.class, MethodInfo.class); } + /** + * Shares a server-wide object associated with the specified type. All connections + * with RMI hosting started will have access to this shared object as soon as they + * connect and they will all share the same instance. It is up to the shared object + * to handle any multithreading that might be required. + */ public void shareGlobal( T object, Class type ) { shareGlobal(defaultChannel, type.getName(), object, type); } + /** + * Shares a server-wide object associated with the specified name. All connections + * with RMI hosting started will have access to this shared object as soon as they + * connect and they will all share the same instance. It is up to the shared object + * to handle any multithreading that might be required. + */ public void shareGlobal( String name, T object, Class type ) { shareGlobal(defaultChannel, name, object, type); } + /** + * Shares a server-wide object associated with the specified name over the specified + * channel. All connections with RMI hosting started will have access to this shared + * object as soon as they connect and they will all share the same instance. It is up + * to the shared object to handle any multithreading that might be required. + * All network communcation associated with the shared object will be done over + * the specified channel. + */ public void shareGlobal( byte channel, String name, T object, Class type ) { GlobalShare share = new GlobalShare(channel, object, type); GlobalShare existing = globalShares.put(name, share); @@ -99,14 +142,30 @@ public class RmiHostedService extends AbstractHostedService { } } + /** + * Set to true if all new connections should automatically have RMI hosting started. + * Set to false if the game-specific connection setup will call startHostingOnConnection() + * after some connection setup is done (for example, logging in). Note: generally + * is is safe to autohost RMI as long as callers are careful about what they've added + * using shareGlobal(). One reasonable use-case is to shareGlobal() some kind of login + * service and nothing else. All other shared objects would then be added as connection + * specific objects during successful login processing. + */ public void setAutoHost( boolean b ) { this.autoHost = b; } + /** + * Returns true if RMI hosting is automatically started for all new connections. + */ public boolean getAutoHost() { return autoHost; } + /** + * Returns the RMI registry for the specific HostedConection. Each connection + * has its own registry with its own connection-specific shared objects. + */ public RmiRegistry getRmiRegistry( HostedConnection hc ) { return hc.getAttribute(ATTRIBUTE_NAME); } diff --git a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java index 6d1722307..c04a8c7b3 100644 --- a/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java +++ b/jme3-networking/src/main/java/com/jme3/network/service/rmi/RmiRegistry.java @@ -87,18 +87,45 @@ public class RmiRegistry { rpc.registerHandler(rmiId, rmiHandler); } + /** + * Exposes the specified object to the other end of the connection as + * the specified interface type. The object can be looked up by type + * on the other end. + */ public void share( T object, Class type ) { share(defaultChannel, object, type); } + /** + * Exposes, through a specific connection channel, the specified object + * to the other end of the connection as the specified interface type. + * The object can be looked up by type on the other end. + * The specified channel will be used for all network communication + * specific to this object. + */ public void share( byte channel, T object, Class type ) { share(channel, type.getName(), object, type); } + /** + * Exposes the specified object to the other end of the connection as + * the specified interface type and associates it with the specified name. + * The object can be looked up by the associated name on the other end of + * the connection. + */ public void share( String name, T object, Class type ) { share(defaultChannel, name, object, type); } + /** + * Exposes, through a specific connection channel, the specified object to + * the other end of the connection as the specified interface type and associates + * it with the specified name. + * The object can be looked up by the associated name on the other end of + * the connection. + * The specified channel will be used for all network communication + * specific to this object. + */ public void share( byte channel, String name, T object, Class type ) { ClassInfo typeInfo = classCache.getClassInfo(type); @@ -153,16 +180,18 @@ public class RmiRegistry { } /** - * Returns an object that was previously registered with share(). + * Returns a local object that was previously registered with share() using + * just type registration. */ - public T getSharedObject( Class type ) { - return getSharedObject(type.getName(), type); + public T getLocalObject( Class type ) { + return getLocalObject(type.getName(), type); } /** - * Returns an object that was previously registered with share(). + * Returns a local object that was previously registered with share() using + * name registration. */ - public T getSharedObject( String name, Class type ) { + public T getLocalObject( String name, Class type ) { local.lock.readLock().lock(); try { return type.cast(local.byName.get(name)); @@ -171,10 +200,24 @@ public class RmiRegistry { } } + /** + * Looks up a remote object by type and returns a local proxy to the remote object + * that was shared on the other end of the network connection. If this is called + * from a client then it is accessing a shared object registered on the server. + * If this is called from the server then it is accessing a shared object registered + * on the client. + */ public T getRemoteObject( Class type ) { return getRemoteObject(type.getName(), type); } + /** + * Looks up a remote object by name and returns a local proxy to the remote object + * that was shared on the other end of the network connection. If this is called + * from a client then it is accessing a shared object registered on the server. + * If this is called from the server then it is accessing a shared object registered + * on the client. + */ public T getRemoteObject( String name, Class type ) { remote.lock.readLock().lock(); try {