Adding an initial implementation for a service manager module

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
Paul Speed 10 years ago
parent 9abedf284e
commit 1eb2ba7276
  1. 51
      jme3-networking/src/main/java/com/jme3/network/service/AbstractClientService.java
  2. 70
      jme3-networking/src/main/java/com/jme3/network/service/AbstractHostedService.java
  3. 111
      jme3-networking/src/main/java/com/jme3/network/service/AbstractService.java
  4. 72
      jme3-networking/src/main/java/com/jme3/network/service/ClientService.java
  5. 87
      jme3-networking/src/main/java/com/jme3/network/service/ClientServiceManager.java
  6. 74
      jme3-networking/src/main/java/com/jme3/network/service/HostedService.java
  7. 127
      jme3-networking/src/main/java/com/jme3/network/service/HostedServiceManager.java
  8. 67
      jme3-networking/src/main/java/com/jme3/network/service/Service.java
  9. 160
      jme3-networking/src/main/java/com/jme3/network/service/ServiceManager.java
  10. 123
      jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcClientService.java
  11. 247
      jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcConnection.java
  12. 52
      jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHandler.java
  13. 227
      jme3-networking/src/main/java/com/jme3/network/service/rpc/RpcHostedService.java
  14. 98
      jme3-networking/src/main/java/com/jme3/network/service/rpc/msg/RpcCallMessage.java
  15. 89
      jme3-networking/src/main/java/com/jme3/network/service/rpc/msg/RpcResponseMessage.java

@ -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…
Cancel
Save