/* * ALMA - Atacama Large Millimiter Array * (c) European Southern Observatory, 2002 * Copyright by ESO (in the framework of the ALMA collaboration), * All rights reserved * * This library 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 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ package alma.acs.component.dynwrapper; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.logging.Level; import java.util.logging.Logger; import alma.acs.container.ComponentHelper; import alma.acs.util.StopWatch; /** * @author hsommer Nov 22, 2002 4:48:25 PM * $Id$ */ public class DynamicProxyFactory { private static DynamicProxyFactory s_instance; /** * Singleton accessor. * The logger must be provided because this class is used both inside the container as well * as outside in tests and applications that are clients to container/components. * The latter cases might not want to use central ACS logging. * * @param logger * @return DynamicProxyFactory */ public static DynamicProxyFactory getDynamicProxyFactory(Logger logger) { if (s_instance == null) { s_instance = new DynamicProxyFactory(logger); } return s_instance; } private Logger m_logger = null; private DynamicProxyFactory(Logger logger) { m_logger = logger; } /** * Creates a proxy object for a corba stub for a component. * The proxy object will delegate calls to the corba stub, and will translate parameters as necessary. * * @param componentInterface the component interface * @param corbaStub (should be a Corba object, but this is currently not enforced) * @return Object * @throws DynWrapperException */ public <T, F> T createClientProxy(Class<T> componentInterface, F corbaStub, Class<F> corbaOperationsIF) throws DynWrapperException { T proxy = null; try { ComponentInvocationHandler handler = new ComponentInvocationHandler(corbaStub, corbaOperationsIF, m_logger); // TODO: use the result checkMethodMatching(componentInterface, handler); proxy = componentInterface.cast(Proxy.newProxyInstance(componentInterface.getClassLoader(), new Class[] { componentInterface }, handler)); } catch (DynWrapperException e) { throw e; } catch (RuntimeException e2) { String msg = "failed to create a binding-class aware client proxy implementing " + componentInterface.getName(); m_logger.log(Level.SEVERE, msg, e2); throw new DynWrapperException(msg, e2); } return proxy; } /** * Creates a proxy object for a component implementation. * The proxy object will delegate calls to the component, and will translate parameters as necessary. * * @param corbaIF the IDL-generated xxOperations interface. * @param componentImpl the component implementation class that implements <code>componentIF</code>. * This may be identical with <code>corbaIF</code>, or could be * some other similar interface, e.g. one that uses XML binding classes. * @param componentIF the interface of the component to which calls will be delegated. * @return the proxy object that implements <code>corbaIF</code> and delegates to <code>componentImpl</code>. * @throws DynWrapperException */ public Object createServerProxy(Class<?> corbaIF, Object componentImpl, Class<?> componentIF) throws DynWrapperException { ComponentInvocationHandler handler = new ComponentInvocationHandler(componentImpl, componentIF, m_logger); checkMethodMatching(corbaIF, handler); Object proxy = Proxy.newProxyInstance(corbaIF.getClassLoader(), new Class[] { corbaIF }, handler); return proxy; } /** * Checks if all methods in <code>facade</code> can be mapped to a corresponding method * in <code>delegate</code> using the various mappers for argument translation. * <p> * TODO: make this check optional (property etc.) since it's not needed for stable * and already verified interfaces. Perhaps move from runtime to buildtime? * * @param facade the interface that is presented to the client * (serverside: the container; clientside: the client application). * @param invHandler the invocation handler that can delegate the facade method calls * (serverside: to the component implementation; clientside: to the CORBA stub). * @return true if all parameters can be mapped automatically both ways. * false if at least one pair of parameters requires manual mapping. * @throws DynWrapperException if <code>delegate</code> does not provide * a peer method for at least one method in <code>facade</code>. * Currently two methods are considered peers if they have the same name and the same * number of parameters. */ @SuppressWarnings("unchecked") private boolean checkMethodMatching(Class facade, ComponentInvocationHandler invHandler) throws DynWrapperException { StopWatch methodMatchCheckTimer = new StopWatch(m_logger); boolean autonomousMapping = true; Method[] facadeMethods = facade.getMethods(); for (int i = 0; i < facadeMethods.length; i++) { Method delegateMethod = invHandler.findDelegateMethod(facadeMethods[i]); Class[] facadeParams = facadeMethods[i].getParameterTypes(); Class[] delegParams = delegateMethod.getParameterTypes(); // number of parameters must be equal if (facadeParams.length != delegParams.length) { String msg = "unmatching numbers of parameters in method " + delegateMethod.getName(); throw new DynWrapperException(msg); } // check calling parameters for (int pIndex = 0; pIndex < facadeParams.length; pIndex++) { // todo: use in/inout/out info from IR for more specific check; currently assumes worst case 'inout' if (!invHandler.canTranslate(facadeParams[pIndex], delegParams[pIndex]) || !invHandler.canTranslate(delegParams[pIndex], facadeParams[pIndex]) ) { String msg = "unable to map automatically between parameter type '" + facadeParams[pIndex].getName() + "' and '" + delegParams[pIndex].getName() + "' in method '" + facadeMethods[i].getName() + "'. This functionality must therefore be provided " + " by the component's associated " + ComponentHelper.class.getName() + " class."; m_logger.info(msg); autonomousMapping = false; } } // check return value Class delegateRet = delegateMethod.getReturnType(); Class facadeRet = facadeMethods[i].getReturnType(); if (!invHandler.canTranslate(delegateRet, facadeRet)) { String msg = "unable to map automatically from return type '" + delegateRet.getName() + "' to type '" + facadeRet.getName() + "' in method '" + facadeMethods[i].getName() + "'."; m_logger.info(msg); autonomousMapping = false; } } methodMatchCheckTimer.logLapTime("verify automatic translation for methods in " + facade.getName()); return autonomousMapping; } }