Summary: Async Remoting allows you to use remote invocations of POJO services, with the space as the transport layer.
![]() OverviewAsync remoting is characterized by the nature of how the client communicates with the server. Under the wires, async remoting uses the space write and take capabilities. The client writes an internal invocation Entry, that holds the different invocation information (such as service name, method name, and arguments) and then waits for a response (by using blocking take on an expected response). The server, meanwhile, waits (by using OpenSpaces event containers) for internal invocation Entries, and once one arrives, it executes the requested service and writes back a response. Defining the ContractIn order to support remoting, the first step is to define the contract between the client and the server. In our case, the contract is a simple Java interface. Here is an example: public interface IDataProcessor { /** * Process a given Data object, returning the processed Data object. */ Data processData(Data data); }
Implementing the ContractNext, an implementation of this contract needs to be provided. This implementation will "live" on the server side. Here is a sample implementation:
Annotation
@RemotingService public class DataProcessor implements IDataProcessor { public Data processData(Data data) { data.setProcessor(true); return data; } } XML public class DataProcessor implements IDataProcessor { public Data processData(Data data) { data.setProcessor(true); return data; } } The XML tab corresponds to exporting the service using xml configuration. The Annotation tab corresponds to exporting the service using annotations. Exporting the Service Over the SpaceThe next step is exporting the service over the space. Exporting the service is done on the server side. As with other Spring remoting support, exporting the service and the method of exporting the service is a configuration time decision. Here is an example of a Spring XML configuration:
Annotation
<!-- Support @RemotingService component scanning --> <context:component-scan base-package="com.mycompany"/> <!-- Support the @RemotingService annotation on a service--> <os-remoting:annotation-support /> <os-core:space id="space" url="/./space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:service-exporter id="serviceExporter" /> <os-events:polling-container id="eventContainer" giga-space="gigaSpace"> <os-events:listener ref="serviceExporter"/> </os-events:polling-container> Namespace <os-core:space id="space" url="/./space" /> <os-core:giga-space id="gigaSpace" space="space"/> <bean id="dataProcessor" class="DataProcessor" /> <os-remoting:service-exporter id="serviceExporter"> <os-remoting:service ref="dataProcessor"/> </os-remoting:service-exporter> <os-events:polling-container id="eventContainer" giga-space="gigaSpace"> <os-events:listener ref="serviceExporter"/> </os-events:polling-container> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="/./space" /> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space" /> </bean> <bean id="dataProcessor" class="DataProcessor" /> <bean id="serviceExporter" class="org.openspaces.remoting.SpaceRemotingServiceExporter"> <property name="services"> <list> <ref bean="dataProcessor" /> </list> </property> </bean> <bean id="eventContainer" class="org.openspaces.events.polling.SimplePollingEventListenerContainer"> <property name="eventListener" ref="serviceExporter"> </bean> Exporting the service as a remote space service uses the event polling container. The polling container automatically listens to internal AsyncSpaceRemotingEntry objects, and writes back an updated AsyncSpaceRemotingEntry to the space. All polling event container features, such as transactions and different receive handlers can be used with Space remoting.
Using the Service on the Client-SideIn order to use the exported IDataProcessor on the client side, beans should use the IDataProcessor interface directly: public class DataRemoting { private IDataProcessor dataProcessor; public void setDataProcessor(IDataProcessor dataProcessor) { this.dataProcessor = dataProcessor; } } Configuring the IDataProcessor proxy can be done in the following manner:
Namespace
<os-core:space id="space" url="jini://*/*/space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:async-proxy id="dataProcessor" giga-space="gigaSpace" interface="org.openspaces.example.data.common.IDataProcessor" timeout="15000"> </os-remoting:async-proxy> <bean id="dataRemoting" class="DataRemoting"> <property name="dataProcessor" ref="dataProcessor" /> </bean> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="jini://*/*/space" /> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space" /> </bean> <bean id="dataProcessor" class="org.openspaces.remoting.AsyncSpaceRemotingProxyFactoryBean"> <property name="gigaSpace" ref="gigaSpace" /> <property name="serviceInterface" value="org.openspaces.example.data.common.IDataProcessor" /> <property name="timeout" value="15000" /> </bean> <bean id="dataRemoting" class="DataRemoting"> <property name="dataProcessor" ref="dataProcessor" /> </bean> Code IJSpace space = new UrlSpaceConfigurer("jini://*/*/space").space(); GigaSpace gigaSpace = new GigaSpaceConfigurer(space).gigaSpace(); IDataProcessor dataProcessor = new AsyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .timeout(15000) .asyncProxy(); DataRemoting dataRemoting = new DataRemoting(); dataRemoting.setDataProcessor(dataProcessor); The above example uses the async-proxy bean in order to create the remoting proxy, which can then be injected into the DataRemoting object. As of 6.5, OpenSpaces remoting also allows you to inject the remoting proxy directly into the remoting service property using annotations. Here is an example using the DataRemoting class: public class DataRemoting { @AsyncProxy(timeout=15000) private IDataProcessor dataProcessor; // ... } If there are more than one GigaSpace beans defined within the application context, the ID of the giga-space bean needs to be defined on the annotations. In order to enable this feature, the following element needs to be added to the application context XML:
Namespace
<os-remoting:annotation-support />
Plain XML <bean id="space" class="org.openspaces.remoting.RemotingAnnotationBeanPostProcessor" />
Remote Routing HandlerMany times, space remoting is done by exporting services in a space with a partitioned cluster topology. The service is exported when working directly with a cluster member (and not against the whole space cluster). When working with such a topology, the client side remoting automatically generates a random routing index value (using the newly created remote invocation object hashcode). In order to control the routing index, the following interface can be implemented: public interface RemoteRoutingHandler<T> { /** * Returns the routing field value based on the remoting invocation. If <code>null</code> * is returned, will use internal calcualtion of the routing index. */ T computeRouting(SpaceRemotingInvocation remotingEntry); } Here is a sample implementation which uses the first parameter Data object type as the routing index. public class DataRemoteRoutingHandler implements RemoteRoutingHandler<Long> { public Long computeRouting(SpaceRemotingInvocation remotingEntry) { if (remotingEntry.getMethodName().equals("processData")) { Data data = (Data) remotingEntry.getArguments()[0]; return data.getType(); } return null; } } Finally, the wiring is done in the following manner:
Namespace
<os-core:space id="space" url="jini://*/*/space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:async-proxy id="dataProcessor" giga-space="gigaSpace" interface="org.openspaces.example.data.common.IDataProcessor" timeout="15000"> <os-remoting:routing-handler> <bean class="org.openspaces.example.data.feeder.support.DataRemoteRoutingHandler"/> </os-remoting:routing-handler> </os-remoting:async-proxy> <bean id="dataRemoting" class="DataRemoting"> <property name="dataProcessor" ref="dataProcessor" /> </bean> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="jini://*/*/space" /> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space" /> </bean> <bean id="dataProcessor" class="org.openspaces.remoting.AsyncSpaceRemotingProxyFactoryBean"> <property name="gigaSpace" ref="gigaSpace" /> <property name="serviceInterface" value="org.openspaces.example.data.common.IDataProcessor" /> <property name="timeout" value="15000" /> <property name="remoteRoutingHandler"> <bean class="org.openspaces.example.data.feeder.support.DataRemoteRoutingHandler"/> </property> </bean> <bean id="dataRemoting" class="DataRemoting"> <property name="dataProcessor" ref="dataProcessor" /> </bean> Code IJSpace space = new UrlSpaceConfigurer("jini://*/*/space").space(); GigaSpace gigaSpace = new GigaSpaceConfigurer(space).gigaSpace(); IDataProcessor dataProcessor = new AsyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .timeout(15000) .remoteRoutingHandler(new DataRemoteRoutingHandler()) .asyncProxy(); DataRemoting dataRemoting = new DataRemoting(); dataRemoting.setDataProcessor(dataProcessor); Routing AnnotationThe above option of using the remote routing handler is very handy when not using annotations. OpenSpaces remoting supports the Routing annotation in order to define which of the parameters controls the routing. Here is an example: public interface MyService { void doSomething(@Routing int param1, int param2); } In the above example, routing is done using the param1 value. When using complex objects, a method name can be specified, which is invoked on the parameter in order to get the actual routing index. Here is an example: public interface MyService { void doSomething(@Routing("getProperty") Value param1, int param2); } In the above example, the getProperty method is called on the Value object and its return value is used to extract the routing index. Server Side Services Injection
@AutowireArguments public interface MyService { void doSomething(Value param1); } The above service exposes a service which accepts a Value as a parameter. The Value parameter, if needed, can be injected dynamically with server side resources and state. For example, if the server side execution needs to be aware of the ClusterInfo, the Value parameter can implement the ClusterInfoAware interface. Here is what it looks like: public class Value implements ClusterInfoAware { private transient ClusterInfo clusterInfo; public void setClusterInfo(ClusterInfo clusterInfo) { this.clusterInfo = clusterInfo; } }
Another example is injecting another service that is defined in the server side directly into the parameter, for example, using Spring @Autowired annotation. public class Value implements ClusterInfoAware { @Autowired private transient AnotherService anotherService; } Execution AspectsOpenSpaces remoting allows you to inject "aspects" that can wrap the invocation of a remote method on the client side, as well as wrapping the execution of an invocation on the server side. The Client Invocation AspectThe client invocation aspect interface is shown below: public interface RemoteInvocationAspect<T> { /** * The aspect is called instead of the actual remote invocation. The methodInvocation can * be used to access any method related information. The remoting invoker passed can be used * to actually invoke the remote invocaiton. */ T invoke(MethodInvocation methodInvocation, RemotingInvoker remotingInvoker) throws Throwable; } An implementation of this interface can be configured as follows:
Namespace
<os-core:space id="space" url="jini://*/*/space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:async-proxy id="dataProcessor" giga-space="gigaSpace" interface="org.openspaces.example.data.common.IDataProcessor"> <os-remoting:aspect> <bean class="eg.MyRemoteInvocationAspect" /> </os-remoting:aspect> </os-remoting:async-proxy> <bean id="dataRemoting" class="DataRemoting"> <property name="dataProcessor" ref="dataProcessor" /> </bean> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="jini://*/*/space" /> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space" /> </bean> <bean id="dataProcessor" class="org.openspaces.remoting.AsyncSpaceRemotingProxyFactoryBean"> <property name="gigaSpace" ref="gigaSpace" /> <property name="serviceInterface" value="org.openspaces.example.data.common.IDataProcessor" /> <property name="remoteInvocationAspect"> <bean class="eg.MyRemoteInvocationAspect" /> </property> </bean> <bean id="dataRemoting" class="DataRemoting"> <property name="dataProcessor" ref="dataProcessor" /> </bean> Code IJSpace space = new UrlSpaceConfigurer("jini://*/*/space").space(); GigaSpace gigaSpace = new GigaSpaceConfigurer(space).gigaSpace(); IDataProcessor dataProcessor = new AsyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .timeout(15000) .remoteInvocationAspect(new MyRemoteInvocationAspect()) .asyncProxy(); DataRemoting dataRemoting = new DataRemoting(); dataRemoting.setDataProcessor(dataProcessor); The Server Invocation AspectThe server invocation aspect interface is shown below: public interface ServiceExecutionAspect { /** * A service execution callback allows you to wrap the execution of "server side" service. If * actual execution of the service is needed, the <code>invoke</code> method will need to * be called on the passed <code>Method</code>, using the service as the actual service to * invoke it on, and {@link SpaceRemotingInvocation#getArguments()} as the method arguments. * * <p>As an example: <code>method.invoke(service, invocation.getArguments())</code>. */ Object invoke(SpaceRemotingInvocation invocation, Method method, Object service) throws InvocationTargetException, IllegalAccessException; } An implementation of such an aspect can be configured as follows:
Namespace
<os-core:space id="space" url="/./space" /> <os-core:giga-space id="gigaSpace" space="space"/> <bean id="dataProcessor" class="DataProcessor" /> <os-remoting:service-exporter id="serviceExporter"> <os-remoting:aspect> <bean class="eg.MyServiceExecutionAspect" /> </os-remoting:aspect> <os-remoting:service ref="dataProcessor"/> </os-remoting:service-exporter> <os-events:polling-container id="eventContainer" giga-space="gigaSpace"> <os-events:listener ref="serviceExporter"/> </os-events:polling-container> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="/./space" /> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space" /> </bean> <bean id="dataProcessor" class="DataProcessor" /> <bean id="serviceExporter" class="org.openspaces.remoting.SpaceRemotingServiceExporter"> <property name="serviceExecutionAspect"> <bean class="eg.MyServiceExecutionAspect" /> </property> <property name="services"> <list> <ref bean="dataProcessor" /> </list> </property> </bean> <bean id="eventContainer" class="org.openspaces.events.polling.SimplePollingEventListenerContainer"> <property name="eventListener" ref="serviceExporter"> </bean> The Metadata Handler
When executing a service using OpenSpaces remoting, a set of one or more meta data arguments can be passed within the remote invocation. This feature if very handy to execute "aspect" behavior and usually works nicely using the server side aspect feature of remoting. A prime example using meta arguments is security, by passing the security credentials as meta arguments, and using the server side aspect to authorize the execution. The following interface allows you to plug in a custom handler that will create the set of metadata parameters that will "piggyback" the invocation: public interface MetaArgumentsHandler { /** * Meta argument handler can control the meta data objects that will be used * for the remote invocation. */ Object[] obtainMetaArguments(SpaceRemotingInvocation remotingEntry); } And here is how this can be configured:
Namespace
<os-core:space id="space" url="jini://*/*/space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:async-proxy id="dataProcessor" giga-space="gigaSpace" interface="org.openspaces.example.data.common.IDataProcessor"> <os-remoting:meta-arguments-handler> <bean class="eg.MyMetaArgumentsHandler" /> </os-remoting:meta-arguments-handler> </os-remoting:async-proxy> <bean id="dataRemoting" class="DataRemoting"> <property name="dataProcessor" ref="dataProcessor" /> </bean> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="jini://*/*/space" /> </bean> <bean id="gigaSpace" class="org.openspaces.core.GigaSpaceFactoryBean"> <property name="space" ref="space" /> </bean> <bean id="dataProcessor" class="org.openspaces.remoting.AsyncSpaceRemotingProxyFactoryBean"> <property name="gigaSpace" ref="gigaSpace" /> <property name="serviceInterface" value="org.openspaces.example.data.common.IDataProcessor" /> <property name="metaArgumentsHandler"> <bean class="eg.MyMetaArgumentsHandler" /> </property> </bean> <bean id="dataRemoting" class="DataRemoting"> <property name="dataProcessor" ref="dataProcessor" /> </bean> Code IJSpace space = new UrlSpaceConfigurer("jini://*/*/space").space(); GigaSpace gigaSpace = new GigaSpaceConfigurer(space).gigaSpace(); IDataProcessor dataProcessor = new AsyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .timeout(15000) .metaArgumentsHandler(new MyMetaArgumentsHandler()) .asyncProxy(); DataRemoting dataRemoting = new DataRemoting(); dataRemoting.setDataProcessor(dataProcessor); Asynchronous ExecutionOpenSpaces async remoting supports asynchronous execution of remote services using the java.util.concurrent Future interface. Since the remoting implementation is asynchronous by nature, a Future can be used to execute remote services asynchronously. Since the definition of the interface acts as the contract between the client and the server, asynchronous invocation requires the interface to also support the same business method, returning a Future instead of its actual return value. The asynchronous nature is only relevant on the client side, provided by the asynchronous nature of OpenSpaces remoting. There are two options to implement such behavior: Different Service InterfacesThe client and the server can have a different service interface definition under the same package and under the same name. The server includes the actual implementation: public interface SimpleService { String say(String message); int calc(int value1, int value2); } The client has the same method except it returns a future: public interface SimpleService { Future<String> say(String message); int calc(int value1, int value2); } In the above example, the say() method uses a Future in order to receive the result, while the calc method remains synchronous.
Single Service InterfaceBoth the client and server share the same interface, with the interface holding both synchronous and asynchronous services. Here is an example: public interface SimpleService { String say(String message); Future<String> asyncSay(String message); } In the above case, the server implementation does not need to implement the asyncSay method (simply return null for the asyncSay method). The client side can choose which service to invoke. |
![]() |
GigaSpaces.com - Legal Notice - 3rd Party Licenses - Site Map - API Docs - Forum - Downloads - Blog - White Papers - Contact Tech Writing - Gen. by Atlassian Confluence |