with custom subclasses and service interfaces for client services and server-side services (HostedServices). This code is a copy and refactoring of code I developed for Mythruna... it worked there but I haven't tested it yet in its new form. Things may change as I integrate this more closely with the core Client and Server classes. I wanted to get it into source control first. Also included an RPC service implementation which can serve as the underpinning for other things. Coming soon: serializer registration service and a simple RMI service based on the RPC layer.experimental
parent
9abedf284e
commit
1eb2ba7276
@ -0,0 +1,51 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
|
||||
/** |
||||
* Convenient base class for ClientServices providing some default ClientService |
||||
* interface implementations as well as a few convenience methods |
||||
* such as getServiceManager() and getService(type). Subclasses |
||||
* must at least override the onInitialize() method to handle |
||||
* service initialization. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public abstract class AbstractClientService extends AbstractService<ClientServiceManager> |
||||
implements ClientService { |
||||
|
||||
protected AbstractClientService() { |
||||
} |
||||
|
||||
} |
@ -0,0 +1,70 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
import com.jme3.network.HostedConnection; |
||||
import com.jme3.network.Server; |
||||
|
||||
|
||||
/** |
||||
* Convenient base class for HostedServices providing some default HostedService |
||||
* interface implementations as well as a few convenience methods |
||||
* such as getServiceManager() and getService(type). Subclasses |
||||
* must at least override the onInitialize() method to handle |
||||
* service initialization. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public abstract class AbstractHostedService extends AbstractService<HostedServiceManager> |
||||
implements HostedService { |
||||
|
||||
protected AbstractHostedService() { |
||||
} |
||||
|
||||
/** |
||||
* Default implementation does nothing. Implementations can |
||||
* override this to peform custom new connection behavior. |
||||
*/ |
||||
@Override |
||||
public void connectionAdded(Server server, HostedConnection hc) { |
||||
} |
||||
|
||||
/** |
||||
* Default implementation does nothing. Implementations can |
||||
* override this to peform custom leaving connection behavior. |
||||
*/ |
||||
@Override |
||||
public void connectionRemoved(Server server, HostedConnection hc) { |
||||
} |
||||
|
||||
} |
@ -0,0 +1,111 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
|
||||
/** |
||||
* Base class providing some default Service interface implementations |
||||
* as well as a few convenience methods such as getServiceManager() |
||||
* and getService(type). Subclasses must at least override the |
||||
* onInitialize() method to handle service initialization. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public abstract class AbstractService<S extends ServiceManager> implements Service<S> { |
||||
|
||||
private S serviceManager; |
||||
|
||||
protected AbstractService() { |
||||
} |
||||
|
||||
/** |
||||
* Returns the ServiceManager that was passed to |
||||
* initialize() during service initialization. |
||||
*/ |
||||
protected S getServiceManager() { |
||||
return serviceManager; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the first sibling service of the specified |
||||
* type. |
||||
*/ |
||||
protected <T extends Service<S>> T getService( Class<T> type ) { |
||||
return type.cast(serviceManager.getService(type)); |
||||
} |
||||
|
||||
/** |
||||
* Initializes this service by keeping a reference to |
||||
* the service manager and calling onInitialize(). |
||||
*/ |
||||
@Override |
||||
public final void initialize( S serviceManager ) { |
||||
this.serviceManager = serviceManager; |
||||
onInitialize(serviceManager); |
||||
} |
||||
|
||||
/** |
||||
* Called during initialize() for the subclass to perform |
||||
* implementation specific initialization. |
||||
*/ |
||||
protected abstract void onInitialize( S serviceManager ); |
||||
|
||||
/** |
||||
* Default implementation does nothing. Implementations can |
||||
* override this to peform custom startup behavior. |
||||
*/ |
||||
@Override |
||||
public void start() { |
||||
} |
||||
|
||||
/** |
||||
* Default implementation does nothing. Implementations can |
||||
* override this to peform custom stop behavior. |
||||
*/ |
||||
@Override |
||||
public void stop() { |
||||
} |
||||
|
||||
/** |
||||
* Default implementation does nothing. Implementations can |
||||
* override this to peform custom termination behavior. |
||||
*/ |
||||
@Override |
||||
public void terminate( S serviceManager ) { |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getName() + "[serviceManager=" + serviceManager + "]"; |
||||
} |
||||
} |
@ -0,0 +1,72 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
|
||||
/** |
||||
* Interface implemented by Client-side services that augment |
||||
* a network Client's functionality. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface ClientService extends Service<ClientServiceManager> { |
||||
|
||||
/** |
||||
* Called when the service is first attached to the service |
||||
* manager. |
||||
*/ |
||||
@Override |
||||
public void initialize( ClientServiceManager serviceManager ); |
||||
|
||||
/** |
||||
* Called when the service manager is started or if the |
||||
* service is added to an already started service manager. |
||||
*/ |
||||
@Override |
||||
public void start(); |
||||
|
||||
/** |
||||
* Called when the service is shutting down. All services |
||||
* are stopped and any service manager resources are closed |
||||
* before the services are terminated. |
||||
*/ |
||||
@Override |
||||
public void stop(); |
||||
|
||||
/** |
||||
* The service manager is fully shutting down. All services |
||||
* have been stopped and related connections closed. |
||||
*/ |
||||
@Override |
||||
public void terminate( ClientServiceManager serviceManager ); |
||||
} |
@ -0,0 +1,87 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
import com.jme3.network.Client; |
||||
|
||||
|
||||
/** |
||||
* Manages ClientServices on behalf of a network Client object. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class ClientServiceManager extends ServiceManager<ClientServiceManager> { |
||||
|
||||
private Client client; |
||||
|
||||
/** |
||||
* Creates a new ClientServiceManager for the specified network Client. |
||||
*/ |
||||
public ClientServiceManager( Client client ) { |
||||
this.client = client; |
||||
} |
||||
|
||||
/** |
||||
* Returns the network Client associated with this ClientServiceManager. |
||||
*/ |
||||
public Client getClient() { |
||||
return client; |
||||
} |
||||
|
||||
/** |
||||
* Returns 'this' and is what is passed to ClientService.initialize() |
||||
* and ClientService.termnate(); |
||||
*/ |
||||
@Override |
||||
protected final ClientServiceManager getParent() { |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Adds the specified ClientService and initializes it. If the service manager |
||||
* has already been started then the service will also be started. |
||||
*/ |
||||
public void addService( ClientService s ) { |
||||
super.addService(s); |
||||
} |
||||
|
||||
/** |
||||
* Removes the specified ClientService from this service manager, stopping |
||||
* and terminating it as required. If this service manager is in a |
||||
* started state then the service will be stopped. After removal, |
||||
* the service will be terminated. |
||||
*/ |
||||
public void removeService( ClientService s ) { |
||||
super.removeService(s); |
||||
} |
||||
} |
@ -0,0 +1,74 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
import com.jme3.network.ConnectionListener; |
||||
|
||||
|
||||
/** |
||||
* Interface implemented by Server-side services that augment |
||||
* a network Server's functionality. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface HostedService extends Service<HostedServiceManager>, ConnectionListener { |
||||
|
||||
/** |
||||
* Called when the service is first attached to the service |
||||
* manager. |
||||
*/ |
||||
@Override |
||||
public void initialize( HostedServiceManager serviceManager ); |
||||
|
||||
/** |
||||
* Called when the service manager is started or if the |
||||
* service is added to an already started service manager. |
||||
*/ |
||||
@Override |
||||
public void start(); |
||||
|
||||
/** |
||||
* Called when the service is shutting down. All services |
||||
* are stopped and any service manager resources are closed |
||||
* before the services are terminated. |
||||
*/ |
||||
@Override |
||||
public void stop(); |
||||
|
||||
/** |
||||
* The service manager is fully shutting down. All services |
||||
* have been stopped and related connections closed. |
||||
*/ |
||||
@Override |
||||
public void terminate( HostedServiceManager serviceManager ); |
||||
} |
@ -0,0 +1,127 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
import com.jme3.network.ConnectionListener; |
||||
import com.jme3.network.HostedConnection; |
||||
import com.jme3.network.Server; |
||||
|
||||
|
||||
/** |
||||
* Manages HostedServices on behalf of a network Server object. |
||||
* All HostedServices are automatically informed about new and |
||||
* leaving connections. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class HostedServiceManager extends ServiceManager<HostedServiceManager> { |
||||
|
||||
private Server server; |
||||
private ConnectionObserver connectionObserver; |
||||
|
||||
/** |
||||
* Creates a HostedServiceManager for the specified network Server. |
||||
*/ |
||||
public HostedServiceManager( Server server ) { |
||||
this.server = server; |
||||
this.connectionObserver = new ConnectionObserver(); |
||||
server.addConnectionListener(connectionObserver); |
||||
} |
||||
|
||||
/** |
||||
* Returns the network Server associated with this HostedServiceManager. |
||||
*/ |
||||
public Server getServer() { |
||||
return server; |
||||
} |
||||
|
||||
/** |
||||
* Returns 'this' and is what is passed to HostedService.initialize() |
||||
* and HostedService.termnate(); |
||||
*/ |
||||
@Override |
||||
protected final HostedServiceManager getParent() { |
||||
return this; |
||||
} |
||||
|
||||
/** |
||||
* Adds the specified HostedService and initializes it. If the service manager |
||||
* has already been started then the service will also be started. |
||||
*/ |
||||
public void addService( HostedService s ) { |
||||
super.addService(s); |
||||
} |
||||
|
||||
/** |
||||
* Removes the specified HostedService from this service manager, stopping |
||||
* and terminating it as required. If this service manager is in a |
||||
* started state then the service will be stopped. After removal, |
||||
* the service will be terminated. |
||||
*/ |
||||
public void removeService( HostedService s ) { |
||||
super.removeService(s); |
||||
} |
||||
|
||||
/** |
||||
* Called internally when a new connection has been added so that the |
||||
* services can be notified. |
||||
*/ |
||||
protected void addConnection( HostedConnection hc ) { |
||||
for( Service s : getServices() ) { |
||||
((HostedService)s).connectionAdded(server, hc); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Called internally when a connection has been removed so that the |
||||
* services can be notified. |
||||
*/ |
||||
protected void removeConnection( HostedConnection hc ) { |
||||
for( Service s : getServices() ) { |
||||
((HostedService)s).connectionRemoved(server, hc); |
||||
} |
||||
} |
||||
|
||||
protected class ConnectionObserver implements ConnectionListener { |
||||
|
||||
@Override |
||||
public void connectionAdded(Server server, HostedConnection hc) { |
||||
addConnection(hc); |
||||
} |
||||
|
||||
@Override |
||||
public void connectionRemoved(Server server, HostedConnection hc) { |
||||
removeConnection(hc); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,67 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
|
||||
/** |
||||
* The base interface for managed services. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface Service<S> { |
||||
|
||||
/** |
||||
* Called when the service is first attached to the service |
||||
* manager. |
||||
*/ |
||||
public void initialize( S serviceManager ); |
||||
|
||||
/** |
||||
* Called when the service manager is started or if the |
||||
* service is added to an already started service manager. |
||||
*/ |
||||
public void start(); |
||||
|
||||
/** |
||||
* Called when the service is shutting down. All services |
||||
* are stopped and any service manager resources are closed |
||||
* before the services are terminated. |
||||
*/ |
||||
public void stop(); |
||||
|
||||
/** |
||||
* The service manager is fully shutting down. All services |
||||
* have been stopped and related connections closed. |
||||
*/ |
||||
public void terminate( S serviceManager ); |
||||
} |
@ -0,0 +1,160 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service; |
||||
|
||||
import java.util.List; |
||||
import java.util.concurrent.CopyOnWriteArrayList; |
||||
|
||||
/** |
||||
* The base service manager class from which the HostedServiceManager |
||||
* and ClientServiceManager classes are derived. This manages the |
||||
* the underlying services and their life cycles. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public abstract class ServiceManager<T> { |
||||
|
||||
private List<Service<T>> services = new CopyOnWriteArrayList<Service<T>>(); |
||||
private volatile boolean started = false; |
||||
|
||||
protected ServiceManager() { |
||||
} |
||||
|
||||
/** |
||||
* Retreives the 'parent' of this service manager, usually |
||||
* a more specifically typed version of 'this' but it can be |
||||
* anything the seervices are expecting. |
||||
*/ |
||||
protected abstract T getParent(); |
||||
|
||||
/** |
||||
* Returns the complete list of services managed by this |
||||
* service manager. This list is thread safe following the |
||||
* CopyOnWriteArrayList semantics. |
||||
*/ |
||||
protected List<Service<T>> getServices() { |
||||
return services; |
||||
} |
||||
|
||||
/** |
||||
* Starts this service manager and all services that it contains. |
||||
* Any services added after the service manager has started will have |
||||
* their start() methods called. |
||||
*/ |
||||
public void start() { |
||||
if( started ) { |
||||
return; |
||||
} |
||||
for( Service<T> s : services ) { |
||||
s.start(); |
||||
} |
||||
started = true; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this service manager has been started. |
||||
*/ |
||||
public boolean isStarted() { |
||||
return started; |
||||
} |
||||
|
||||
/** |
||||
* Stops all services and puts the service manager into a stopped state. |
||||
*/ |
||||
public void stop() { |
||||
if( !started ) { |
||||
throw new IllegalStateException(getClass().getSimpleName() + " not started."); |
||||
} |
||||
for( Service<T> s : services ) { |
||||
s.stop(); |
||||
} |
||||
started = false; |
||||
} |
||||
|
||||
/** |
||||
* Adds the specified service and initializes it. If the service manager |
||||
* has already been started then the service will also be started. |
||||
*/ |
||||
public <S extends Service<T>> void addService( S s ) { |
||||
services.add(s); |
||||
s.initialize(getParent()); |
||||
if( started ) { |
||||
s.start(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Removes the specified service from this service manager, stopping |
||||
* and terminating it as required. If this service manager is in a |
||||
* started state then the service will be stopped. After removal, |
||||
* the service will be terminated. |
||||
*/ |
||||
public <S extends Service<T>> void removeService( S s ) { |
||||
if( started ) { |
||||
s.stop(); |
||||
} |
||||
services.remove(s); |
||||
s.terminate(getParent()); |
||||
} |
||||
|
||||
/** |
||||
* Terminates all services. If the service manager has not been |
||||
* stopped yet then it will be stopped. |
||||
*/ |
||||
public void terminate() { |
||||
if( started ) { |
||||
stop(); |
||||
} |
||||
for( Service<T> s : services ) { |
||||
s.terminate(getParent()); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the first service of the specified type. |
||||
*/ |
||||
public <S extends Service<T>> S getService( Class<S> type ) { |
||||
for( Service s : services ) { |
||||
if( type.isInstance(s) ) { |
||||
return type.cast(s); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getName() + "[services=" + services + "]"; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,123 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service.rpc; |
||||
|
||||
import com.jme3.network.Client; |
||||
import com.jme3.network.util.ObjectMessageDelegator; |
||||
import com.jme3.network.service.AbstractClientService; |
||||
import com.jme3.network.service.ClientServiceManager; |
||||
|
||||
|
||||
/** |
||||
* RPC service that can be added to a network Client to |
||||
* add RPC send/receive capabilities. Remote procedure |
||||
* calls can be made to the server and responses retrieved. |
||||
* Any remote procedure calls that the server performs for |
||||
* this connection will be received by this service and delegated |
||||
* to the appropriate RpcHandlers. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class RpcClientService extends AbstractClientService { |
||||
|
||||
private RpcConnection rpc; |
||||
private ObjectMessageDelegator delegator; |
||||
|
||||
/** |
||||
* Creates a new RpcClientService that can be registered |
||||
* with the network Client object. |
||||
*/ |
||||
public RpcClientService() { |
||||
} |
||||
|
||||
/** |
||||
* Used internally to setup the RpcConnection and MessageDelegator. |
||||
*/ |
||||
@Override |
||||
protected void onInitialize( ClientServiceManager serviceManager ) { |
||||
Client client = serviceManager.getClient(); |
||||
this.rpc = new RpcConnection(client); |
||||
|
||||
delegator = new ObjectMessageDelegator(rpc, true); |
||||
client.addMessageListener(delegator, delegator.getMessageTypes()); |
||||
} |
||||
|
||||
/** |
||||
* Used internally to unregister the RPC MessageDelegator that |
||||
* was previously added to the network Client. |
||||
*/ |
||||
@Override |
||||
public void terminate( ClientServiceManager serviceManager ) { |
||||
Client client = serviceManager.getClient(); |
||||
client.removeMessageListener(delegator, delegator.getMessageTypes()); |
||||
} |
||||
|
||||
/** |
||||
* Performs a synchronous call on the server against the specified |
||||
* object using the specified procedure ID. Both inboud and outbound |
||||
* communication is done on the specified channel. |
||||
*/ |
||||
public Object callAndWait( byte channel, short objId, short procId, Object... args ) { |
||||
return rpc.callAndWait(channel, objId, procId, args); |
||||
} |
||||
|
||||
/** |
||||
* Performs an asynchronous call on the server against the specified |
||||
* object using the specified procedure ID. Communication is done |
||||
* over the specified channel. No responses are received and none |
||||
* are waited for. |
||||
*/ |
||||
public void callAsync( byte channel, short objId, short procId, Object... args ) { |
||||
rpc.callAsync(channel, objId, procId, args); |
||||
} |
||||
|
||||
/** |
||||
* Register a handler that will be called when the server |
||||
* performs a remove procedure call against this client. |
||||
* Only one handler per object ID can be registered at any given time, |
||||
* though the same handler can be registered for multiple object |
||||
* IDs. |
||||
*/ |
||||
public void registerHandler( short objId, RpcHandler handler ) { |
||||
rpc.registerHandler(objId, handler); |
||||
} |
||||
|
||||
/** |
||||
* Removes a previously registered handler for the specified |
||||
* object ID. |
||||
*/ |
||||
public void removeHandler( short objId, RpcHandler handler ) { |
||||
rpc.removeHandler(objId, handler); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,247 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service.rpc; |
||||
|
||||
import com.jme3.network.MessageConnection; |
||||
import com.jme3.network.service.rpc.msg.RpcCallMessage; |
||||
import com.jme3.network.service.rpc.msg.RpcResponseMessage; |
||||
import java.util.Map; |
||||
import java.util.concurrent.ConcurrentHashMap; |
||||
import java.util.concurrent.atomic.AtomicLong; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
|
||||
/** |
||||
* Wraps a message connection to provide RPC call support. This |
||||
* is used internally by the RpcClientService and RpcHostedService to manage |
||||
* network messaging. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class RpcConnection { |
||||
|
||||
static final Logger log = Logger.getLogger(RpcConnection.class.getName()); |
||||
|
||||
/** |
||||
* The underlying connection upon which RPC call messages are sent |
||||
* and RPC response messages are received. It can be a Client or |
||||
* a HostedConnection depending on the mode of the RPC service. |
||||
*/ |
||||
private MessageConnection connection; |
||||
|
||||
/** |
||||
* The objectId index of RpcHandler objects that are used to perform the |
||||
* RPC calls for a particular object. |
||||
*/ |
||||
private Map<Short, RpcHandler> handlers = new ConcurrentHashMap<Short, RpcHandler>(); |
||||
|
||||
/** |
||||
* Provides unique messages IDs for outbound synchronous call |
||||
* messages. These are then used in the responses index to |
||||
* locate the proper ResponseHolder objects. |
||||
*/ |
||||
private AtomicLong sequenceNumber = new AtomicLong(); |
||||
|
||||
/** |
||||
* Tracks the ResponseHolder objects for sent message IDs. When the |
||||
* response is received, the appropriate handler is found here and the |
||||
* response or error set, thus releasing the waiting caller. |
||||
*/ |
||||
private Map<Long, ResponseHolder> responses = new ConcurrentHashMap<Long, ResponseHolder>(); |
||||
|
||||
/** |
||||
* Creates a new RpcConnection for the specified network connection. |
||||
*/ |
||||
public RpcConnection( MessageConnection connection ) { |
||||
this.connection = connection; |
||||
} |
||||
|
||||
/** |
||||
* Clears any pending synchronous calls causing them to |
||||
* throw an exception with the message "Closing connection". |
||||
*/ |
||||
public void close() { |
||||
// Let any pending waits go free
|
||||
for( ResponseHolder holder : responses.values() ) { |
||||
holder.release(); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Performs a remote procedure call with the specified arguments and waits |
||||
* for the response. Both the outbound message and inbound response will |
||||
* be sent on the specified channel. |
||||
*/ |
||||
public Object callAndWait( byte channel, short objId, short procId, Object... args ) { |
||||
|
||||
RpcCallMessage msg = new RpcCallMessage(sequenceNumber.getAndIncrement(), |
||||
channel, objId, procId, args); |
||||
|
||||
// Need to register an object so we can wait for the response.
|
||||
// ...before we send it. Just in case.
|
||||
ResponseHolder holder = new ResponseHolder(msg); |
||||
responses.put(msg.getMessageId(), holder); |
||||
|
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "Sending:{0} on channel:{1}", new Object[]{msg, channel}); |
||||
} |
||||
connection.send(channel, msg); |
||||
|
||||
return holder.getResponse(); |
||||
} |
||||
|
||||
/** |
||||
* Performs a remote procedure call with the specified arguments but does |
||||
* not wait for a response. The outbound message is sent on the specified channel. |
||||
* There is no inbound response message. |
||||
*/ |
||||
public void callAsync( byte channel, short objId, short procId, Object... args ) { |
||||
|
||||
RpcCallMessage msg = new RpcCallMessage(-1, channel, objId, procId, args); |
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "Sending:{0} on channel:{1}", new Object[]{msg, channel}); |
||||
} |
||||
connection.send(channel, msg); |
||||
} |
||||
|
||||
/** |
||||
* Register a handler that can be called by the other end |
||||
* of the connection using the specified object ID. Only one |
||||
* handler per object ID can be registered at any given time, |
||||
* though the same handler can be registered for multiple object |
||||
* IDs. |
||||
*/ |
||||
public void registerHandler( short objId, RpcHandler handler ) { |
||||
handlers.put(objId, handler); |
||||
} |
||||
|
||||
/** |
||||
* Removes a previously registered handler for the specified |
||||
* object ID. |
||||
*/ |
||||
public void removeHandler( short objId, RpcHandler handler ) { |
||||
RpcHandler removing = handlers.get(objId); |
||||
if( handler != removing ) { |
||||
throw new IllegalArgumentException("Handler not registered for object ID:" |
||||
+ objId + ", handler:" + handler ); |
||||
} |
||||
handlers.remove(objId); |
||||
} |
||||
|
||||
/** |
||||
* Called internally when an RpcCallMessage is received from |
||||
* the remote connection. |
||||
*/ |
||||
public void handleMessage( RpcCallMessage msg ) { |
||||
|
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "handleMessage({0})", msg); |
||||
} |
||||
RpcHandler handler = handlers.get(msg.getObjectId()); |
||||
try { |
||||
Object result = handler.call(this, msg.getObjectId(), msg.getProcedureId(), msg.getArguments()); |
||||
if( !msg.isAsync() ) { |
||||
RpcResponseMessage response = new RpcResponseMessage(msg.getMessageId(), result); |
||||
connection.send(msg.getChannel(), response); |
||||
} |
||||
} catch( Exception e ) { |
||||
if( !msg.isAsync() ) { |
||||
RpcResponseMessage response = new RpcResponseMessage(msg.getMessageId(), e); |
||||
connection.send(msg.getChannel(), response); |
||||
} else { |
||||
log.log(Level.SEVERE, "Error invoking async call for:" + msg, e); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Called internally when an RpcResponseMessage is received from |
||||
* the remote connection. |
||||
*/ |
||||
public void handleMessage( RpcResponseMessage msg ) { |
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "handleMessage({0})", msg); |
||||
} |
||||
ResponseHolder holder = responses.remove(msg.getMessageId()); |
||||
if( holder == null ) { |
||||
return; |
||||
} |
||||
holder.setResponse(msg); |
||||
} |
||||
|
||||
/** |
||||
* Sort of like a Future, holds a locked reference to a response |
||||
* until the remote call has completed and returned a response. |
||||
*/ |
||||
private class ResponseHolder { |
||||
private Object response; |
||||
private String error; |
||||
private RpcCallMessage msg; |
||||
boolean received = false; |
||||
|
||||
public ResponseHolder( RpcCallMessage msg ) { |
||||
this.msg = msg; |
||||
} |
||||
|
||||
public synchronized void setResponse( RpcResponseMessage msg ) { |
||||
this.response = msg.getResult(); |
||||
this.error = msg.getError(); |
||||
this.received = true; |
||||
notifyAll(); |
||||
} |
||||
|
||||
public synchronized Object getResponse() { |
||||
try { |
||||
while(!received) { |
||||
wait(); |
||||
} |
||||
} catch( InterruptedException e ) { |
||||
throw new RuntimeException("Interrupted waiting for respone to:" + msg, e); |
||||
} |
||||
if( error != null ) { |
||||
throw new RuntimeException("Error calling remote procedure:" + msg + "\n" + error); |
||||
} |
||||
return response; |
||||
} |
||||
|
||||
public synchronized void release() { |
||||
if( received ) { |
||||
return; |
||||
} |
||||
// Else signal an error for the callers
|
||||
this.error = "Closing connection"; |
||||
this.received = true; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service.rpc; |
||||
|
||||
|
||||
/** |
||||
* Implementations of this interface can be registered with |
||||
* the RpcClientService or RpcHostService to handle the |
||||
* remote procedure calls for a given object or objects. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public interface RpcHandler { |
||||
|
||||
/** |
||||
* Called when a remote procedure call request is received for a particular |
||||
* object from the other end of the network connection. |
||||
*/ |
||||
public Object call( RpcConnection conn, short objectId, short procId, Object... args ); |
||||
} |
||||
|
||||
|
@ -0,0 +1,227 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service.rpc; |
||||
|
||||
import com.jme3.network.HostedConnection; |
||||
import com.jme3.network.Server; |
||||
import com.jme3.network.serializing.Serializer; |
||||
import com.jme3.network.util.SessionDataDelegator; |
||||
import com.jme3.network.service.AbstractHostedService; |
||||
import com.jme3.network.service.HostedServiceManager; |
||||
import com.jme3.network.service.rpc.msg.RpcCallMessage; |
||||
import com.jme3.network.service.rpc.msg.RpcResponseMessage; |
||||
import java.util.Arrays; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
|
||||
/** |
||||
* RPC service that can be added to a network Server to |
||||
* add RPC send/receive capabilities. For a particular |
||||
* HostedConnection, Remote procedure calls can be made to the |
||||
* associated Client and responses retrieved. Any remote procedure |
||||
* calls that the Client performs for this connection will be |
||||
* received by this service and delegated to the appropriate RpcHandlers. |
||||
* |
||||
* Note: it can be dangerous for a server to perform synchronous |
||||
* RPC calls to a client but especially so if not done as part |
||||
* of the response to some other message. ie: iterating over all |
||||
* or some HostedConnections to perform synchronous RPC calls |
||||
* will be slow and potentially block the server's threads in ways |
||||
* that can cause deadlocks or odd contention. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
public class RpcHostedService extends AbstractHostedService { |
||||
|
||||
private static final String ATTRIBUTE_NAME = "rpcSession"; |
||||
|
||||
static final Logger log = Logger.getLogger(RpcHostedService.class.getName()); |
||||
|
||||
private boolean autoHost; |
||||
private SessionDataDelegator delegator; |
||||
|
||||
/** |
||||
* Creates a new RPC host service that can be registered |
||||
* with the Network server and will automatically 'host' |
||||
* RPC services and each new network connection. |
||||
*/ |
||||
public RpcHostedService() { |
||||
this(true); |
||||
} |
||||
|
||||
/** |
||||
* Creates a new RPC host service that can be registered |
||||
* with the Network server and will optionally 'host' |
||||
* RPC services and each new network connection depending |
||||
* on the specified 'autoHost' flag. |
||||
*/ |
||||
public RpcHostedService( boolean autoHost ) { |
||||
this.autoHost = autoHost; |
||||
|
||||
// This works for me... has to be different in
|
||||
// the general case
|
||||
Serializer.registerClasses(RpcCallMessage.class, RpcResponseMessage.class); |
||||
} |
||||
|
||||
/** |
||||
* Used internally to setup the message delegator that will |
||||
* handle HostedConnection specific messages and forward them |
||||
* to that connection's RpcConnection. |
||||
*/ |
||||
@Override |
||||
protected void onInitialize( HostedServiceManager serviceManager ) { |
||||
Server server = serviceManager.getServer(); |
||||
|
||||
// A general listener for forwarding the messages
|
||||
// to the client-specific handler
|
||||
this.delegator = new SessionDataDelegator(RpcConnection.class, |
||||
ATTRIBUTE_NAME, |
||||
true); |
||||
server.addMessageListener(delegator, delegator.getMessageTypes()); |
||||
|
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "Registered delegator for message types:{0}", Arrays.asList(delegator.getMessageTypes())); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* When set to true, all new connections will automatically have |
||||
* RPC hosting services attached to them, meaning they can send |
||||
* and receive RPC calls. If this is set to false then it is up |
||||
* to other services to eventually call startHostingOnConnection(). |
||||
* |
||||
* <p>Reasons for doing this vary but usually would be because |
||||
* the client shouldn't be allowed to perform any RPC calls until |
||||
* it has provided more information. In general, this is unnecessary |
||||
* because the RpcHandler registries are not shared. Each client |
||||
* gets their own and RPC calls will fail until the appropriate |
||||
* objects have been registtered.</p> |
||||
*/ |
||||
public void setAutoHost( boolean b ) { |
||||
this.autoHost = b; |
||||
} |
||||
|
||||
/** |
||||
* Returns true if this service automatically attaches RPC |
||||
* hosting capabilities to new connections. |
||||
*/ |
||||
public boolean getAutoHost() { |
||||
return autoHost; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the RpcConnection for the specified HostedConnection |
||||
* if that HostedConnection has had RPC services started using |
||||
* startHostingOnConnection() (or via autohosting). Returns null |
||||
* if the connection currently doesn't have RPC hosting services |
||||
* attached. |
||||
*/ |
||||
public RpcConnection getRpcConnection( HostedConnection hc ) { |
||||
return hc.getAttribute(ATTRIBUTE_NAME); |
||||
} |
||||
|
||||
/** |
||||
* Sets up RPC hosting services for the hosted connection allowing |
||||
* getRpcConnection() to return a valid RPC connection object. |
||||
* This method is called automatically for all new connections if |
||||
* autohost is set to true. |
||||
*/ |
||||
public void startHostingOnConnection( HostedConnection hc ) { |
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "startHostingOnConnection:{0}", hc); |
||||
} |
||||
hc.setAttribute(ATTRIBUTE_NAME, new RpcConnection(hc)); |
||||
} |
||||
|
||||
/** |
||||
* Removes any RPC hosting services associated with the specified |
||||
* connection. Calls to getRpcConnection() will return null for |
||||
* this connection. The connection's RpcConnection is also closed, |
||||
* releasing any waiting synchronous calls with a "Connection closing" |
||||
* error. |
||||
* This method is called automatically for all leaving connections if |
||||
* autohost is set to true. |
||||
*/ |
||||
public void stopHostingOnConnection( HostedConnection hc ) { |
||||
RpcConnection rpc = hc.getAttribute(ATTRIBUTE_NAME); |
||||
if( rpc == null ) { |
||||
return; |
||||
} |
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "stopHostingOnConnection:{0}", hc); |
||||
} |
||||
hc.setAttribute(ATTRIBUTE_NAME, null); |
||||
rpc.close(); |
||||
} |
||||
|
||||
/** |
||||
* Used internally to remove the message delegator from the |
||||
* server. |
||||
*/ |
||||
@Override |
||||
public void terminate(HostedServiceManager serviceManager) { |
||||
Server server = serviceManager.getServer(); |
||||
server.removeMessageListener(delegator, delegator.getMessageTypes()); |
||||
} |
||||
|
||||
/** |
||||
* Called internally when a new connection is detected for |
||||
* the server. If the current autoHost property is true then |
||||
* startHostingOnConnection(hc) is called. |
||||
*/ |
||||
@Override |
||||
public void connectionAdded(Server server, HostedConnection hc) { |
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "connectionAdded({0}, {1})", new Object[]{server, hc}); |
||||
} |
||||
if( autoHost ) { |
||||
startHostingOnConnection(hc); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Called internally when an existing connection is leaving |
||||
* the server. If the current autoHost property is true then |
||||
* stopHostingOnConnection(hc) is called. |
||||
*/ |
||||
@Override |
||||
public void connectionRemoved(Server server, HostedConnection hc) { |
||||
if( log.isLoggable(Level.FINEST) ) { |
||||
log.log(Level.FINEST, "connectionRemoved({0}, {1})", new Object[]{server, hc}); |
||||
} |
||||
stopHostingOnConnection(hc); |
||||
} |
||||
|
||||
} |
||||
|
@ -0,0 +1,98 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service.rpc.msg; |
||||
|
||||
import com.jme3.network.AbstractMessage; |
||||
import com.jme3.network.serializing.Serializable; |
||||
|
||||
|
||||
/** |
||||
* Used internally to send RPC call information to |
||||
* the other end of a connection for execution. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
@Serializable |
||||
public class RpcCallMessage extends AbstractMessage { |
||||
|
||||
private long msgId; |
||||
private byte channel; |
||||
private short objId; |
||||
private short procId; |
||||
private Object[] args; |
||||
|
||||
public RpcCallMessage() { |
||||
} |
||||
|
||||
public RpcCallMessage( long msgId, byte channel, short objId, short procId, Object... args ) { |
||||
this.msgId = msgId; |
||||
this.channel = channel; |
||||
this.objId = objId; |
||||
this.procId = procId; |
||||
this.args = args; |
||||
} |
||||
|
||||
public long getMessageId() { |
||||
return msgId; |
||||
} |
||||
|
||||
public byte getChannel() { |
||||
return channel; |
||||
} |
||||
|
||||
public boolean isAsync() { |
||||
return msgId == -1; |
||||
} |
||||
|
||||
public short getObjectId() { |
||||
return objId; |
||||
} |
||||
|
||||
public short getProcedureId() { |
||||
return procId; |
||||
} |
||||
|
||||
public Object[] getArguments() { |
||||
return args; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName() + "[#" + msgId + ", channel=" + channel |
||||
+ (isAsync() ? ", async" : ", sync") |
||||
+ ", objId=" + objId |
||||
+ ", procId=" + procId |
||||
+ ", args.length=" + args.length |
||||
+ "]"; |
||||
} |
||||
} |
@ -0,0 +1,89 @@ |
||||
/* |
||||
* Copyright (c) 2015 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package com.jme3.network.service.rpc.msg; |
||||
|
||||
import com.jme3.network.AbstractMessage; |
||||
import com.jme3.network.serializing.Serializable; |
||||
import java.io.PrintWriter; |
||||
import java.io.StringWriter; |
||||
|
||||
|
||||
/** |
||||
* Used internally to send an RPC call's response back to |
||||
* the caller. |
||||
* |
||||
* @author Paul Speed |
||||
*/ |
||||
@Serializable |
||||
public class RpcResponseMessage extends AbstractMessage { |
||||
|
||||
private long msgId; |
||||
private Object result; |
||||
private String error; |
||||
|
||||
public RpcResponseMessage() { |
||||
} |
||||
|
||||
public RpcResponseMessage( long msgId, Object result ) { |
||||
this.msgId = msgId; |
||||
this.result = result; |
||||
} |
||||
|
||||
public RpcResponseMessage( long msgId, Throwable t ) { |
||||
this.msgId = msgId; |
||||
|
||||
StringWriter sOut = new StringWriter(); |
||||
PrintWriter out = new PrintWriter(sOut); |
||||
t.printStackTrace(out); |
||||
out.close(); |
||||
this.error = sOut.toString(); |
||||
} |
||||
|
||||
public long getMessageId() { |
||||
return msgId; |
||||
} |
||||
|
||||
public Object getResult() { |
||||
return result; |
||||
} |
||||
|
||||
public String getError() { |
||||
return error; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return getClass().getSimpleName() + "[#" + msgId + ", result=" + result |
||||
+ "]"; |
||||
} |
||||
} |
Loading…
Reference in new issue