Author: athimel Date: 2011-01-12 15:31:01 +0100 (Wed, 12 Jan 2011) New Revision: 2010 Url: http://nuiton.org/repositories/revision/nuiton-utils/2010 Log: [FEATURE] Add classes to easily export and reuse services to a RMI registry Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/ trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutor.java trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutorImpl.java trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteProxyFactory.java trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/ServiceExporter.java trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/package-info.java Modified: trunk/ trunk/nuiton-utils/changelog.txt Property changes on: trunk ___________________________________________________________________ Modified: svn:ignore - target velocity.log maven.log .eclipse .classpath *.iml *.ipr *.iws .settings .project + target velocity.log maven.log .eclipse .classpath *.iml *.ipr *.iws .settings .project testdir Modified: trunk/nuiton-utils/changelog.txt =================================================================== --- trunk/nuiton-utils/changelog.txt 2011-01-07 13:05:05 UTC (rev 2009) +++ trunk/nuiton-utils/changelog.txt 2011-01-12 14:31:01 UTC (rev 2010) @@ -1,13 +1,17 @@ -1.1.3 xxx 201001xx +ver 2.0 xxx 201101xx + + * [FEATURE] Add classes to easily export and reuse services to a RMI registry + +ver 1.1.3 xxx 201001xx * Use display-name from web.xml file as server name -1.1.2 desbois 20091223 +ver 1.1.2 desbois 20091223 * Add generic war launcher (winstone & jetty based) * Add new uncompress method with exclusion filters * [FEATURE] Force application configuration properties to be written sorted * [FEATURE] Add sed and grep method on FileUtil -1.1.1 chemit 20090903 +ver 1.1.1 chemit 20090903 * [FEATURE] #39 add a filterVersions method in VersionUtil Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutor.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutor.java (rev 0) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutor.java 2011-01-12 14:31:01 UTC (rev 2010) @@ -0,0 +1,50 @@ +/* + * #%L + * Nuiton Utils + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.util.rmi; + +import java.rmi.Remote; +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 { + + /** + * 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; + +} Property changes on: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutor.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutorImpl.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutorImpl.java (rev 0) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutorImpl.java 2011-01-12 14:31:01 UTC (rev 2010) @@ -0,0 +1,86 @@ +/* + * #%L + * Nuiton Utils + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.util.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"); + } + this.service = service; + } + + @Override + public Object execute( + String methodName, Class<?>[] parametersType, Object[] args) + throws RemoteException { + + Object result; + try { + + // Get the method on the target service then invoke it + Method method = service.getClass().getMethod( + methodName, parametersType); + result = method.invoke(service, args); + + } 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; + } + +} Property changes on: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteMethodExecutorImpl.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteProxyFactory.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteProxyFactory.java (rev 0) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteProxyFactory.java 2011-01-12 14:31:01 UTC (rev 2010) @@ -0,0 +1,132 @@ +/* + * #%L + * Nuiton Utils + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.util.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"; + + /** + * 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; + } + + /** + * 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 { + + // 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 { + + // Get parameters types and values to prepare delegate call + String methodName = method.getName(); + Class<?>[] parametersType = method.getParameterTypes(); + + // 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; + } + }; + + // Invocation handler is ready, now create the proxy + T proxy = (T) Proxy.newProxyInstance( + ServiceExporter.class.getClassLoader(), + new Class<?>[]{serviceInterface}, + handler); + + return proxy; + } + +} Property changes on: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/RemoteProxyFactory.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/ServiceExporter.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/ServiceExporter.java (rev 0) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/ServiceExporter.java 2011-01-12 14:31:01 UTC (rev 2010) @@ -0,0 +1,124 @@ +/* + * #%L + * Nuiton Utils + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.util.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; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +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())) + && 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); + // To test that registry has been created. An exception will be + // thrown if registry cannot be called + result.list(); + } catch (ConnectException ce) { + if (log.isWarnEnabled()) { + log.warn("Registry not found, creating a new one"); + } + result = LocateRegistry.createRegistry(PORT); + } + return result; + } + + /** + * 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); + } + + /** + * 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); + Remote stub = UnicastRemoteObject.exportObject(executor, 0); + + // Bind into the registry + Registry registry = getRegistry(); + registry.rebind(rmiName, stub); + } + +} Property changes on: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/ServiceExporter.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL Added: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/package-info.java =================================================================== --- trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/package-info.java (rev 0) +++ trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/package-info.java 2011-01-12 14:31:01 UTC (rev 2010) @@ -0,0 +1,37 @@ +/* + * #%L + * Nuiton Utils + * + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2010 CodeLutin + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * This package contains classes to easily export services to a RMI registry + * then get a proxy to access them. The provided classes will hide RMI + * complexity. + * <p/> + * Use the {@link ServiceExporter} to register an service to the RMI registry. + * <p/> + * Use the {@link RemoteProxyFactory} to get a proxy an call the RMI exported + * service. + * + * @author Arnaud Thimel <thimel@codelutin.com> + */ +package org.nuiton.util.rmi; \ No newline at end of file Property changes on: trunk/nuiton-utils/src/main/java/org/nuiton/util/rmi/package-info.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision HeadURL