javaspringrmisoaspring-remoting

Dynamic Remote Service Location - How to inject with Spring?


I'm currently working on a distributed services architecture for a project at work. Essentially, we are managing upwards of 200 machines. Each of these machines has an instance of a service running on them that allows us to interface with the machine in a specific way.

At the center I have a control application which needs to talk to these 200 identical services. I was hoping to use RMI via Spring Remoting to make this happen, allowing me to @Autowire my remote service into my @Controller and treat it like a local service with exception propagation and maybe in the future propagation of transactions / security context via hooks.

This works great for a single service on a single machine where I can hardcode the remote service in my Spring configuration, but what I'm not able to figure out is how to dynamically choose which service (aka, which machine) I want to talk to at runtime and make that remote service available the "Spring" way.

I would like to be able to configure this dynamically from a database table and use the same table information to do a service lookup, while still taking advantage of dependency injection.

I thought of maybe injecting some kind of service manager that could do a service lookup of some sort, but was hoping someone else has managed to solve this (or a similar) problem elegantly.

An example of a hardcoded, single service instance would be like so:

The first XML snippet is on the machine service itself, telling Spring to expose it via RMI

<!-- Expose DeviceService via RMI -->
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
    <property name="serviceName" value="DeviceService" />
    <property name="service" ref="deviceService" />
    <property name="serviceInterface"
        value="com.example.service.DeviceService" />
    <property name="registryPort" value="1199" />
</bean>

The second XML snippet is on the client (control application) which lets me access the exposed service

<!-- Proxy our remote DeviceService via RMI -->
<bean id="remoteDeviceService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://machineurl:1199/DeviceService"/>
    <property name="serviceInterface" value="com.example.service.DeviceService"/>
</bean>

It's that second bit of configuration that I'm trying to make dynamic. As you can see, to create this service proxy, I need to know at bean creation time the service URL. The Service URL can be 1 of 200+ variations, depending on what machine I want to talk to. The service I'm talking to is the same interface, but I won't know which machine until runtime depending on the current request context.


Solution

  • You could create connections to your servers dynamically with an additional service and remove "remoteDeviceService" from your client/controll app, i.e.:

    public class RMIConnectionService {
    
        public DeviceService connect(String serverUrl) {
            RmiProxyFactoryBean factory = new RmiProxyFactoryBean();
            factory.setServiceInterface(DeviceService.class);
            factory.setServiceUrl("rmi://" + serverUrl + ":1099/SERVICE_URL");
            factory.afterPropertiesSet();
            //...
            return (DeviceService) factory.getObject();
        }
    }
    

    Then add this service to you service layer:

    <bean id="rmiService" class="...RMIConnectionService" >
        //...
    </bean>
    

    In your logic autowire the service and use it like:

    DeviceService server1 = rmiService.connect("127.0.0.1");
    

    For Database config add your DAO to this service, to load the right url. Port and url, or even the interface class could be configured this way too.

    I have no RMI service to test this, but it worked with Hessian too. I hope this helps you.