Summary: Sync Remoting allows you to use remote invocations of POJO services, with the space as the transport layer.
OverviewSync remoting is characterized by the nature of how the client communicates with the server. Under the wires, sync remoting uses the GigaSpaces space filter capabilities.
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 an 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:filter-provider ref="serviceExporter" /> </os-core:space> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:service-exporter id="serviceExporter" /> Namespace <os-core:space id="space" url="/./space"> <os-core:filter-provider ref="serviceExporter" /> </os-core: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> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="/./space" /> <property name="filterProviders"> <list> <ref bean="serviceExporter" /> </list> </property> </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> Exporting the service as a remote space service uses the space support for filters.
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 done in the following manner:
Namespace
<os-core:space id="space" url="jini://*/*/space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:sync-proxy id="dataProcessor" giga-space="gigaSpace" interface="org.openspaces.example.data.common.IDataProcessor"> </os-remoting:sync-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.SyncSpaceRemotingProxyFactoryBean"> <property name="gigaSpace" ref="gigaSpace" /> <property name="serviceInterface" value="org.openspaces.example.data.common.IDataProcessor" /> </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 SyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .syncProxy(); DataRemoting dataRemoting = new DataRemoting(); dataRemoting.setDataProcessor(dataProcessor); The above example uses the sync-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 on the remoting service property using annotations. Here is an example using the DataRemoting class: public class DataRemoting { @SyncProxy 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. 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:sync-proxy id="dataProcessor" giga-space="gigaSpace" interface="org.openspaces.example.data.common.IDataProcessor"> <os-remoting:routing-handler> <bean class="org.openspaces.example.data.feeder.support.DataRemoteRoutingHandler"/> </os-remoting:routing-handler> </os-remoting:sync-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.SyncSpaceRemotingProxyFactoryBean"> <property name="gigaSpace" ref="gigaSpace" /> <property name="serviceInterface" value="org.openspaces.example.data.common.IDataProcessor" /> <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 SyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .remoteRoutingHandler(new DataRemoteRoutingHandler()) .syncProxy(); 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 control the routing. Here is an example: public interface MyService { void doSomething(@Routing int param1, int param2); } In the above example, the routing will be done using the param1 value. When using complex objects, a method name can be specified to be 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 InjectionEach parameter of a remote service can be dynamically injected as if it were a bean defined within the server side (where the service is actually executed) context. This allows you to utilize server side resources and state, within the actual invocation. In order to enable such injection the service interface should either be annotated with AutowireArguments annotation, or implement the marker interface AutowireArgumentsMarker. Let's see an example: @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; } Transactional ExecutionSync remoting supports transactional execution of services. On the client side, if there is an ongoing declarative transaction during the service invocation (a Space based transaction), the service will be executed under the same transaction. The transaction itself is passed to the server and any Space related operations (performed using GigaSpace) will be executed under the same transaction. The transaction lifecycle itself is controlled on the client side (declaratively) and is committed / rolled back only on the client side. (Note, exceptions on the server side will simply propagate to the client side, and will cause a rollback only if the client "decides" so.) When using broadcast with sync remoting, a declarative distributed transaction must be used and not a local (or JTA one). Also note, the GigaSpace instance that is used internally by the sync remoting invocation must be configured with a transaction manager (as is the case for enabling transaction execution with any other GigaSpace based operation). Execution AspectsSpace based 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. You should implement this interface and wire and instance of the implementing class to the client side remote proxy, as explained 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 invocation. */ T invoke(MethodInvocation methodInvocation, RemotingInvoker remotingInvoker) throws Throwable; } An implementation of such an aspect can be confuigured as follows:
Namespace
<os-core:space id="space" url="jini://*/*/space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:sync-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:sync-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.SyncSpaceRemotingProxyFactoryBean"> <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 SyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .remoteInvocationAspect(new MyRemoteInvocationAspect()) .syncProxy(); DataRemoting dataRemoting = new DataRemoting(); dataRemoting.setDataProcessor(dataProcessor); The instance of the class that implements the RemoteInvocationAspect interface will be called by the framework prior to the invocation on the client side. Note that it is up to the aspect implementation to decide whether or not the call should proceed. The Server Invocation AspectThe server side invocation aspect interface is shown below. You should implement this interface and wire and instance of the implementing class to the server side service exporter (this is the component that is responsible for exposing your service bean to remote clients): 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:filter-provider ref="serviceExporter" /> </os-core: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> Plain XML <bean id="space" class="org.openspaces.core.space.UrlSpaceFactoryBean"> <property name="url" value="/./space" /> <property name="filterProviders"> <list> <ref bean="serviceExporter" /> </list> </property> </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> The instance of the class that implements the ServiceExecutionAspect interface will be called by the framework prior to the invocation of the service bean on the server side. Note that it is up to the aspect implementation to decide whether or not the call should proceed. The Metadata HandlerWhen executing a service using Space based remoting, a set of one or more metadata arguments (in the form of a java.lang.Object array) can be passed to the server with the remote invocation. This feature is very handy when you want to pass certain metadata with every remote call that is not part of the arguments of the method you call. A prime example for using meta arguments is security – i.e. passing security credentials as meta arguments, and using a server side aspect to authorize the execution or to log who actually called the method. To create the meta arguments on the client side you should implement the following interface and inject an instance of the implementing class to the client side proxy: public interface MetaArgumentsHandler { /** * Meta argument handler can control the meta data objects that will be used * for the remote invocation. */ Object[] obtainMetaArguments(SpaceRemotingInvocation remotingEntry); } The following snippets show how to plug a custom meta arguments handler to the client side remote proxy. The Object array returned by the implementation of the MetaArgumentsHandler interface will be sent along with the invocation to server side.
Namespace
<os-core:space id="space" url="jini://*/*/space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:sync-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:sync-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.SyncSpaceRemotingProxyFactoryBean"> <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 SyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .metaArgumentsHandler(new MyMetaArgumentsHandler()) .syncProxy(); DataRemoting dataRemoting = new DataRemoting(); dataRemoting.setDataProcessor(dataProcessor); The way to access the meta arguments on the server side is to configure a server side execution aspect by implementing the ServiceExecutionAspect and wiring it on the server side as shown above. To access the meta arguments, you should call SpaceRemotingInvocation.getMetaArguments() on the invocation argument provided to the server side aspect. Broadcast RemotingWhen using sync remoting (since filters are used), a remote invocation can be broadcasted to all active (primary) cluster members. The configuration of enabling broadcasting is done on the client level, by setting the broadcast flag to true:
Namespace
<os-core:space id="space" url="jini://*/*/space" /> <os-core:giga-space id="gigaSpace" space="space"/> <os-remoting:sync-proxy id="dataProcessor" giga-space="gigaSpace" interface="org.openspaces.example.data.common.IDataProcessor" broadcast="true"> </os-remoting:sync-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.SyncSpaceRemotingProxyFactoryBean"> <property name="gigaSpace" ref="gigaSpace" /> <property name="serviceInterface" value="org.openspaces.example.data.common.IDataProcessor" /> <property name="broadcast" value="true" /> </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 SyncRemotingProxyConfigurer<IDataProcessor>(gigaSpace, IDataProcessor.class) .broadcast(true) .syncProxy(); DataRemoting dataRemoting = new DataRemoting(); dataRemoting.setDataProcessor(dataProcessor); The Remote Result ReducerWhen broadcasting remote invocations to all active cluster members, and the remote method returns a result, on the client side an array of all remote results needs to be processed. By default, the first result is returned, and it can be overriden using the return-first-result flag (which will then return the array of responses). The sync remoting proxy allows for a pluggable remote result reducer that can reduce a collection of remoting results into a single one. Here is the defined interface: public interface RemoteResultReducer<T, Y> { /** * Reduces a list of Space remoting invocation results to an Object value. Can use * the provided remote invocation to perform different reduce operations, for example * based on the {@link SpaceRemotingInvocation#getMethodName()}. * * <p>An exception thrown from the reduce operation will be propagated to the client. * * @param results A list of results from (usually) broadcast sync remote invocation. * @param remotingInvocation The remote invocation * @return A reduced return value (to the calling client) * @throws Exception An exception that will be propagated to the client */ T reduce(SpaceRemotingResult<Y>[] results, SpaceRemotingInvocation remotingInvocation) throws Exception; } |
![]() |
GigaSpaces.com - Legal Notice - 3rd Party Licenses - Site Map - API Docs - Forum - Downloads - Blog - White Papers - Contact Tech Writing - Gen. by Atlassian Confluence |