Author: athimel Date: 2011-01-12 14:52:52 +0100 (Wed, 12 Jan 2011) New Revision: 460 Url: http://nuiton.org/repositories/revision/sandbox/460 Log: Prepare implementation for another project's integration Added: testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceImpl.java testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceInterface.java Removed: testStandaloneRmi/src/main/java/org/nuiton/sandbox/business/ testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceInterface.java Modified: testStandaloneRmi/pom.xml testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteMethodExecutor.java testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteMethodExecutorImpl.java testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteProxyFactory.java testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/ServiceExporter.java testStandaloneRmi/src/test/java/org/nuiton/sandbox/rmi/ClientExporter.java Modified: testStandaloneRmi/pom.xml =================================================================== --- testStandaloneRmi/pom.xml 2011-01-12 09:52:53 UTC (rev 459) +++ testStandaloneRmi/pom.xml 2011-01-12 13:52:52 UTC (rev 460) @@ -8,5 +8,20 @@ <artifactId>sandbox-rmi</artifactId> <version>1.0</version> - + <dependencies> + + <dependency> + <groupId>org.nuiton.wikitty</groupId> + <artifactId>wikitty-api</artifactId> + <version>3.0.3-SNAPSHOT</version> + </dependency> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>1.1</version> + </dependency> + + </dependencies> + </project> \ No newline at end of file Modified: testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteMethodExecutor.java =================================================================== --- testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteMethodExecutor.java 2011-01-12 09:52:53 UTC (rev 459) +++ testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteMethodExecutor.java 2011-01-12 13:52:52 UTC (rev 460) @@ -4,10 +4,23 @@ import java.rmi.RemoteException; /** + * This class will act as an InvocationHandler except that it is distributed. + * * @author Arnaud Thimel <thimel@codelutin.com> */ public interface RemoteMethodExecutor extends Remote { - public abstract Object execute(String methodName, Class<?>[] parametersType, Object[] args) throws RemoteException; + /** + * Acts like an InvocationHandler. + * + * @param methodName name of the method to invoke + * @param parametersType parameters type to reliably identify the method + * @param args method arguments to process the effective call + * @return the result of the delegate method + * @throws RemoteException for any error. Business exceptions will be + * wrapped. + */ + Object execute(String methodName, Class<?>[] parametersType, Object[] args) + throws RemoteException; } Modified: testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteMethodExecutorImpl.java =================================================================== --- testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteMethodExecutorImpl.java 2011-01-12 09:52:53 UTC (rev 459) +++ testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteMethodExecutorImpl.java 2011-01-12 13:52:52 UTC (rev 460) @@ -1,15 +1,30 @@ package org.nuiton.sandbox.rmi; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.rmi.RemoteException; /** + * RMI implementation of an invocation handler. This object will be exported to + * a RMI registry and will delegate method calls to some business service. The + * service is provided in the constructor. + * * @author Arnaud Thimel <thimel@codelutin.com> */ public class RemoteMethodExecutorImpl<T> implements RemoteMethodExecutor { + /** + * The target service on which calls will be made + */ protected T service; + /** + * This is the only available constructor. It is mandatory to specify a + * target service on which call will be + * delegated. + * + * @param service the mandatory service which calls will be delegated on + */ public RemoteMethodExecutorImpl(T service) { if (service == null) { throw new NullPointerException("Service cannot be null"); @@ -18,14 +33,29 @@ } @Override - public Object execute(String methodName, Class<?>[] parametersType, Object[] args) throws RemoteException { + public Object execute( + String methodName, Class<?>[] parametersType, Object[] args) + throws RemoteException { + Object result; try { - Method method = service.getClass().getMethod(methodName, parametersType); + + // Get the method on the target service then invoke it + Method method = service.getClass().getMethod( + methodName, parametersType); result = method.invoke(service, args); - } catch (Exception eee) { - throw new RemoteException("Unable to delegate method call to service", eee); + + } catch (InvocationTargetException ite) { + // This is the normal behaviour if a business exception is thrown + Throwable targetException = ite.getTargetException(); + throw new RemoteException( + "Business exception occurred", targetException); + } catch (NoSuchMethodException nsme) { + throw new RemoteException("Delegate method not found", nsme); + } catch (IllegalAccessException iae) { + throw new RemoteException("Delegate method not accessible", iae); } + return result; } Modified: testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteProxyFactory.java =================================================================== --- testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteProxyFactory.java 2011-01-12 09:52:53 UTC (rev 459) +++ testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/RemoteProxyFactory.java 2011-01-12 13:52:52 UTC (rev 460) @@ -1,44 +1,108 @@ package org.nuiton.sandbox.rmi; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.ServerException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; /** + * Factory to create RMI proxies to some given services. + * * @author Arnaud Thimel <thimel@codelutin.com> */ public class RemoteProxyFactory { + private final static Log log = LogFactory.getLog(RemoteProxyFactory.class); + + // TODO AThimel 12/01/2011 This settings has to be externalized protected final static int PORT = 12345; + protected final static String REGISTRY_IP = "10.1.1.85"; - protected static <T> T createProxy(final Class<T> serviceInterface) throws Exception { + /** + * Create a RMI proxy on the wanted service interface. The default RMI name + * will be used to find this service in the Registry. + * + * @param serviceInterface The class of the service proxy to create + * @param <T> some interface class + * @return A newly created proxy which interface is <T> + * @throws RemoteException in case the registry is not reachable + * @throws NotBoundException if the default RMI name cannot be found in the + * registry + */ + public static <T> T createProxy(final Class<T> serviceInterface) + throws RemoteException, NotBoundException { + + // The default RMI name will be the FQN of the service interface String rmiName = serviceInterface.getName(); T result = createProxy(rmiName, serviceInterface); + return result; } - protected static <T> T createProxy(String rmiName, final Class<T> serviceInterface) throws Exception { + /** + * Create a RMI proxy on the wanted service interface. The specific given + * RMI name will be used to find this service in the Registry. + * + * @param rmiName The specific RMI name to use to find the service + * in the registry + * @param serviceInterface The class of the service proxy to create + * @param <T> some interface class + * @return A newly created proxy which interface is <T> + * @throws RemoteException in case the registry is not reachable + * @throws NotBoundException if the default RMI name cannot be found in the + * registry + */ + public static <T> T createProxy(String rmiName, Class<T> serviceInterface) + throws RemoteException, NotBoundException { - Registry registry = LocateRegistry.getRegistry("10.1.1.85", PORT); - final RemoteMethodExecutor stub = (RemoteMethodExecutor) registry.lookup(rmiName); + // Lookup the registry and the remote executor from the registry + Registry registry = LocateRegistry.getRegistry(REGISTRY_IP, PORT); + final RemoteMethodExecutor stub = + (RemoteMethodExecutor) registry.lookup(rmiName); InvocationHandler handler = new InvocationHandler() { @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + // Get parameters types and values to prepare delegate call String methodName = method.getName(); Class<?>[] parametersType = method.getParameterTypes(); - Object result = stub.execute(methodName, parametersType, args); + // Delegate the execution and manage business exception cases + Object result; + try { + result = stub.execute(methodName, parametersType, args); + } catch (ServerException se) { + if (log.isInfoEnabled()) { + log.info("Server exception: " + se.getMessage()); + } + Throwable cause = se.getCause(); + if (cause instanceof RemoteException) { + RemoteException re = (RemoteException) cause; + cause = re.getCause(); + } + throw cause; + } return result; } }; - T proxy = (T) Proxy.newProxyInstance(ServiceExporter.class.getClassLoader(), new Class<?>[]{serviceInterface}, handler); + + // Invocation handler is ready, now create the proxy + T proxy = (T) Proxy.newProxyInstance( + ServiceExporter.class.getClassLoader(), + new Class<?>[]{serviceInterface}, + handler); + return proxy; } - } Modified: testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/ServiceExporter.java =================================================================== --- testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/ServiceExporter.java 2011-01-12 09:52:53 UTC (rev 459) +++ testStandaloneRmi/src/main/java/org/nuiton/sandbox/rmi/ServiceExporter.java 2011-01-12 13:52:52 UTC (rev 460) @@ -1,5 +1,8 @@ package org.nuiton.sandbox.rmi; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + import java.rmi.ConnectException; import java.rmi.Remote; import java.rmi.RemoteException; @@ -8,42 +11,85 @@ import java.rmi.server.UnicastRemoteObject; /** + * This class allows to make some service available throw RMI. For each service, + * a wrapper will be created which will be put in the RMI registry. This wrapper + * will intercept calls to the service and delegate them to it. + * * @author Arnaud Thimel <thimel@codelutin.com> */ public class ServiceExporter { + private final static Log log = LogFactory.getLog(ServiceExporter.class); + + // TODO AThimel 12/01/2011 This settings has to be externalized private static final int PORT = 12345; + /** + * Does some checks on RMI configuration + */ protected void testRmiConfig() { String rmiHost = System.getProperty("java.rmi.server.hostname"); - if (rmiHost == null || "".equals(rmiHost.trim())) { - System.err.println("Warning, server might not have been initialized properly, please specify '-Djava.rmi.server.hostname=<IP-address>'"); + if ((rmiHost == null || "".equals(rmiHost.trim())) + && log.isWarnEnabled()) { + log.warn("Server might not have been initialized properly, " + + "please specify '-Djava.rmi.server.hostname=<IP-address>'"); } } + /** + * Will look for the RMI registry. It an external registry cannot be found, + * a new one will be created. + * + * @return the registry found or created + * @throws RemoteException in case it is not possible to get the registry + */ protected Registry getRegistry() throws RemoteException { Registry result; try { result = LocateRegistry.getRegistry(PORT); - result.list(); // To test that registry has been created. Exception will be thrown is registry cannot be called + // To test that registry has been created. An exception will be + // thrown if registry cannot be called + result.list(); } catch (ConnectException ce) { - System.err.println("Registry not found, creating a new one"); + if (log.isWarnEnabled()) { + log.warn("Registry not found, creating a new one"); + } result = LocateRegistry.createRegistry(PORT); } return result; } - public <E> void registerService(Class<E> serviceInterface, E instance) throws RemoteException { + /** + * Will register a service using the default name. + * + * @param serviceInterface the interface used to bind the service. The RMI + * name will be generated from this class name + * @param instance the service instance to bind + * @param <E> some interface class + * @throws RemoteException in case the registry is not reachable + */ + public <E> void registerService(Class<E> serviceInterface, E instance) + throws RemoteException { String rmiName = serviceInterface.getName(); registerService(rmiName, instance); } - public <E> void registerService(String rmiName, E instance) throws RemoteException { + /** + * Will register a service using the given RMI name. + * + * @param rmiName the RMI name used to bind the service in the registry + * @param instance the service instance to bind + * @param <E> some interface class + * @throws RemoteException in case the registry is not reachable + */ + public <E> void registerService(String rmiName, E instance) + throws RemoteException { testRmiConfig(); // Create the proxy and let him be a stub - RemoteMethodExecutorImpl<E> executor = new RemoteMethodExecutorImpl<E>(instance); + RemoteMethodExecutorImpl<E> executor = + new RemoteMethodExecutorImpl<E>(instance); Remote stub = UnicastRemoteObject.exportObject(executor, 0); // Bind into the registry Copied: testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceImpl.java (from rev 459, testStandaloneRmi/src/main/java/org/nuiton/sandbox/business/ServiceImpl.java) =================================================================== --- testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceImpl.java (rev 0) +++ testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceImpl.java 2011-01-12 13:52:52 UTC (rev 460) @@ -0,0 +1,37 @@ +package org.nuiton.sandbox.business; + +import org.nuiton.wikitty.entities.Wikitty; + +/** + * @author Arnaud Thimel <thimel@codelutin.com> + */ +public class ServiceImpl implements ServiceInterface { + + protected static final int number = (int) (Math.random() * 100); + + @Override + public String testExchange(String whoIsCalling, int intValue) { + System.out.println(String.format("Incoming call from %s with value: (%d). To guess: (%d)", whoIsCalling, intValue, number)); + String result; + if (intValue == number) { + result = String.format("Congrats %s, you found the correct number : '%d' !", whoIsCalling, intValue); + } else if (intValue < number) { + result = String.format("%s, your number is too low : '%d'", whoIsCalling, intValue); + } else { + result = String.format("%s, your number is too high : '%d'", whoIsCalling, intValue); + } + return result; + } + + @Override + public void testException() throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + + @Override + public Wikitty testComplexObject(Wikitty in) throws CloneNotSupportedException { + Wikitty out = in.clone(); + return out; + } + +} Deleted: testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceInterface.java =================================================================== --- testStandaloneRmi/src/main/java/org/nuiton/sandbox/business/ServiceInterface.java 2011-01-11 10:52:23 UTC (rev 455) +++ testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceInterface.java 2011-01-12 13:52:52 UTC (rev 460) @@ -1,7 +0,0 @@ -package org.nuiton.sandbox.business; - -public interface ServiceInterface { - - public abstract String testExchange(Integer intValue); - -} Copied: testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceInterface.java (from rev 457, testStandaloneRmi/src/main/java/org/nuiton/sandbox/business/ServiceInterface.java) =================================================================== --- testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceInterface.java (rev 0) +++ testStandaloneRmi/src/test/java/org/nuiton/sandbox/business/ServiceInterface.java 2011-01-12 13:52:52 UTC (rev 460) @@ -0,0 +1,16 @@ +package org.nuiton.sandbox.business; + +import org.nuiton.wikitty.entities.Wikitty; + +/** + * @author Arnaud Thimel <thimel@codelutin.com> + */ +public interface ServiceInterface { + + String testExchange(String whoIsCalling, int intValue); + + void testException() throws CloneNotSupportedException; + + Wikitty testComplexObject(Wikitty in) throws CloneNotSupportedException; + +} Modified: testStandaloneRmi/src/test/java/org/nuiton/sandbox/rmi/ClientExporter.java =================================================================== --- testStandaloneRmi/src/test/java/org/nuiton/sandbox/rmi/ClientExporter.java 2011-01-12 09:52:53 UTC (rev 459) +++ testStandaloneRmi/src/test/java/org/nuiton/sandbox/rmi/ClientExporter.java 2011-01-12 13:52:52 UTC (rev 460) @@ -1,6 +1,8 @@ package org.nuiton.sandbox.rmi; import org.nuiton.sandbox.business.ServiceInterface; +import org.nuiton.wikitty.entities.Wikitty; +import org.nuiton.wikitty.entities.WikittyImpl; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; @@ -20,6 +22,18 @@ ServiceInterface service = RemoteProxyFactory.createProxy(ServiceInterface.class); String result = service.testExchange(System.getProperty("user.name"), 50); System.out.println(result); + + try { + service.testException(); + } catch (CloneNotSupportedException cnse) { + System.err.println("Ok!"); + } + + Wikitty in = new WikittyImpl(); + in.setFqField("toto.titi", 999L); + Wikitty out = service.testComplexObject(in); + assert out.equals(in); + } }