Automatic Service Replacement

This page describes how to use Java reflection to wrap a River service to provide automatic fail over and replacements. The exact fail over behaviour, including the number of retries, is completely customisable. The code offered here is simply for demonstration only.

The River Service

The River service consists of a very simple interface;

package org.apache.river.samples.wrapper;

import java.rmi.RemoteException;

public interface HelloService {

    String sayHello() throws RemoteException;

}

And a very simple implementation;

package org.apache.river.samples.wrapper;

import java.io.Serializable;
import java.rmi.RemoteException;
import java.util.logging.Level;
import java.util.logging.Logger;

public class HelloServiceImpl implements HelloService, Serializable {

    private static final long serialVersionUID = 944525970032024164L;

    private static final Logger logger = Logger.getLogger(HelloServiceImpl.class.getSimpleName());

    public String sayHello() throws RemoteException {
        logger.log(Level.FINE, "Proxy is saying hello");
        return "Hello";
    }

}

Providing Fail Over

The following uses standard Java reflection to provide lazy service discovery and the fail over strategy. The "invoke" method below is very simple and is obviously tied to the HelloService implementation. An implementation fit for real world use would obviously be generic enough to allow it's use with any given service interface (and also use real service discovery mechanisms).

package org.apache.river.samples.wrapper;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.logging.Logger;
import net.jini.core.lookup.ServiceTemplate;

public class ServiceWrapper implements InvocationHandler {

    private static final Logger logger = Logger.getLogger(ServiceWrapper.class.getSimpleName());

    private Object serviceProxy;
    private ServiceTemplate template;

    public ServiceWrapper(ServiceTemplate template) {
        this.template = template;
    }

    private boolean hasService() {
        return null != this.serviceProxy;
    }

    private void findService() throws RemoteException {
        /*
         *  user supplied serviceType and template to River-lookup a new service
         *  this needs to be clever enough to *not* find any "broken" service
         *  that we're trying to replace
         * 
         *  e.g. return MyStaticServiceLookuper.lookup(template);
         */

        //dummy code which "finds" a Rivers service
        this.serviceProxy = new HelloServiceImpl();
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        logger.fine("InvocationHandler: invoking method on a proxy");

        if(!hasService()) {
            //might throw RE if no service can be found, allow this to bubble up to caller
            findService();
        }

        try {
            return ((HelloService)this.serviceProxy).sayHello();
        } catch (RemoteException re) {
            //service threw an exception, maybe it's ill?  Let's try again.
            this.serviceProxy = null;
            findService();
            return ((HelloService)this.serviceProxy).sayHello();
            /*
             * if we throw a second exception from sayHello(), then we could opt
             * to keep looking a set number of times, or let subsequent exceptions
             * bubble back to the caller
             */
        }
    }

}

Putting It All Together

A simple main which puts it all together;

package org.apache.river.samples.wrapper;

import java.lang.reflect.Proxy;
import java.rmi.RemoteException;
import java.util.logging.Logger;

public class Main {

    private static final Logger logger = Logger.getLogger(Main.class.getSimpleName());

    /**
     * @param args
     * @throws RemoteException
     */
    public static void main(String[] args) throws RemoteException {

        HelloService service = (HelloService) Proxy.newProxyInstance(HelloService.class.getClassLoader(),
                new Class[] { HelloService.class },
                new ServiceWrapper());

        logger.fine("calling service: "+service.sayHello());

        System.exit(0);
    }

}

Executing this gives the output;

InvocationHandler: invoking method on a proxy
Proxy is saying hello
calling service: Hello

AutomaticServiceReplacement (last edited 2010-02-09 10:15:53 by TomHobbs)