/* * 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.container; import java.util.logging.Level; import java.util.logging.Logger; import org.omg.PortableServer.Servant; import alma.ACS.ACSComponentOperations; import alma.JavaContainerError.wrappers.AcsJJavaComponentHelperEx; import alma.acs.component.ComponentLifecycle; import alma.acs.component.dynwrapper.DynamicProxyFactory; import alma.acs.logging.ClientLogManager; import alma.maciErrType.wrappers.AcsJComponentCreationEx; /** * Base class for component helper classes that must be provided * by the component developers (for example by copying the template file that gets * generated with the <code>COMPONENT_HELPERS=on</code> Makefile setting). * <p> * The method implementations in this class perform checks on the data returned * from the subclass methods (framework-style design). In that sense, <code>ComponentHelper</code> * is part of the container, whereas its subclass belongs to the component. * <p> * The methods that return a <code>Class</code> * object should be implemented like <code>return XxxPOATie.class;</code> so that the absence * of this class can be discovered at compile time. * (Therefore don't use <code>classForName</code> which evades compile time checking.) * * @author hsommer */ public abstract class ComponentHelper { private ComponentLifecycle m_componentImpl; // must be private since any subclass should get a different logger ("forComponent") private final Logger m_containerLogger; protected String componentInstanceName; /** * Subclasses must override and call this constructor ("<code>super(containerLogger)</code>"). * @param containerLogger logger used by this base class. */ public ComponentHelper(Logger containerLogger) { m_containerLogger = containerLogger; } /** * Allows the container to set the component instance name. * This name is used for the component logger and may also be useful in other ways. * <p> * Note that in a better design this name should be given in the constructor, * but was added here as a separate method to avoid modifying existing component helper classes. * @param name the component instance name (CURL). * @since ACS 5.0 */ final void setComponentInstanceName(String name) { componentInstanceName = name; } /** * Gets the component logger. * @return the logger to be used by subclasses if they have to log anything. */ protected final Logger getComponentLogger() { String loggerName = ( componentInstanceName != null ? componentInstanceName : "ComponentHelper" ); return ClientLogManager.getAcsLogManager().getLoggerForComponent(loggerName); } /** * Gets the component implementation. * Must be the same object that also implements the functional interface * obtained from <code>getInternalInterface</code>. * * @return The component implementation class that implements <code>ComponentLifecycle</code> * and the functional interface. * @throws AcsJJavaComponentHelperEx if the component implementation construction failed or * if the component does not implement its declared functional interface. */ protected final synchronized ComponentLifecycle getComponentImpl() throws AcsJComponentCreationEx, AcsJJavaComponentHelperEx { if (m_componentImpl== null) { Class<?> internalIF = null; try { m_componentImpl = _createComponentImpl(); internalIF = getInternalInterface(); } catch (AcsJComponentCreationEx ex) { throw ex; } catch (AcsJJavaComponentHelperEx ex) { throw ex; } catch (Throwable thr) { // the ex type is slightly misleading if an overloaded 'getInternalInterface' throws something other than // the declared AcsJJavaComponentHelperEx throw new AcsJComponentCreationEx(thr); } if (m_componentImpl == null) { AcsJComponentCreationEx ex = new AcsJComponentCreationEx(); ex.setReason("_createComponentImpl() returned null."); throw ex; } if (!internalIF.isInstance(m_componentImpl)) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(); ex.setContextInfo("component impl class '" + m_componentImpl.getClass().getName() + "' does not implement the specified functional IF " + internalIF.getName()); throw ex; } else if (!ACSComponentOperations.class.isInstance(m_componentImpl)) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(); ex.setContextInfo("component impl class '" + m_componentImpl.getClass().getName() + "' does not implement the mandatory IF " + ACSComponentOperations.class.getName() + ". Check the IDL interface definition, and add ': ACS::ACSComponent'."); throw ex; } m_containerLogger.finer("component '" + componentInstanceName + "' (class '" + m_componentImpl.getClass().getName() + "') instantiated."); } return m_componentImpl; } /** * Method _createComponentImpl to be provided by subclasses. * Must return the same object that also implements the functional component interface. * * @return ComponentLifecycle The component impl class that implements the ComponentLifecycle interface. * @throws AcsJComponentCreationEx if the component implementation class could not be instantiated */ abstract protected ComponentLifecycle _createComponentImpl() throws AcsJComponentCreationEx; /** * Gets the <code>Class</code> object for the POA tie skeleton class. * The POA tie class is generated by the IDL compiler and must have * a constructor that takes the operations interface as its only parameter. * * @return the tie class. */ final Class<? extends Servant> getPOATieClass() throws AcsJJavaComponentHelperEx { Class<? extends Servant> poaTieClass = null; try { poaTieClass = _getPOATieClass(); } catch (Throwable thr) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(thr); ex.setContextInfo("failed to obtain the POATie class from component helper."); throw ex; } if (poaTieClass == null) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(); ex.setContextInfo("received null as the POATie class from the component helper."); throw ex; } // check inheritance stuff (Servant should be checked by compiler under JDK 1.5, but operations IF is unknown at ACS compile time) if (!Servant.class.isAssignableFrom(poaTieClass)) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(); ex.setContextInfo("received invalid POATie class that is not a subclass of org.omg.PortableServer.Servant"); throw ex; } if (!getOperationsInterface().isAssignableFrom(poaTieClass)) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(); ex.setContextInfo("POATie class '" + poaTieClass + "' does not implement the declared operations interface '" + getOperationsInterface().getName() + "'."); throw ex; } return poaTieClass; } /** * This method must be provided by the component helper class. * It must return the Class of the IDL-generated POA-Tie Servant, as in <br> * <code>return DummyComponentPOATie.class;</code>. */ protected abstract Class<? extends Servant> _getPOATieClass(); /** * Gets the xxOperations interface as generated by the IDL compiler by calling {@link #_getOperationsInterface()}. * @throws AcsJJavaComponentHelperEx if the subclass methdod throws something or returns <code>null</code>. */ public final Class<? extends ACSComponentOperations> getOperationsInterface() throws AcsJJavaComponentHelperEx { Class<? extends ACSComponentOperations> opIF = null; try { opIF = _getOperationsInterface(); } catch (Throwable thr) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(thr); ex.setContextInfo("failed to retrieve 'operations' interface from component helper"); throw ex; } if (opIF == null) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(); ex.setContextInfo("received null as the 'operations' IF class from the component helper."); throw ex; } return opIF; } /** * Gets the xxOperations interface as generated by the IDL compiler. * * This method must be provided by the component helper class. * * @return the <code>Class</code> object associated with the operations interface. */ protected abstract Class<? extends ACSComponentOperations> _getOperationsInterface(); /** * Gets the component's implemented functional IF. * For instance, the method signatures can be the same as in the operations interface, * except that the internal interface uses xml binding classes * where the IDL-generated operations interface only contains stringified XML. * <p> * To be overridden by the component helper class only if the InternalInterface differs from * the OperationsInterface. * * @return Class the Java Class of the internal interface. */ protected Class<?> getInternalInterface() throws AcsJJavaComponentHelperEx { return getOperationsInterface(); } /** * Method getInterfaceTranslator. Called by the container framework. * @return Object * @throws AcsJJavaComponentHelperEx */ final Object getInterfaceTranslator() throws AcsJJavaComponentHelperEx { Object interfaceTranslator = null; // the dynamic proxy is the default interface translator Object defaultInterfaceTranslator = null; try { defaultInterfaceTranslator = DynamicProxyFactory.getDynamicProxyFactory(m_containerLogger).createServerProxy( getOperationsInterface(), m_componentImpl, getInternalInterface()); } catch (Throwable thr) { throw new AcsJJavaComponentHelperEx(thr); } try { interfaceTranslator = _getInterfaceTranslator(defaultInterfaceTranslator); } catch (Throwable thr) { AcsJJavaComponentHelperEx ex = new AcsJJavaComponentHelperEx(thr); ex.setContextInfo("failed to obtain the custom interface translator."); throw ex; } if (interfaceTranslator == null) { // use default translator interfaceTranslator = defaultInterfaceTranslator; } return interfaceTranslator; } /** * To be overridden by subclass only if it wants its own interface translator to be used. * <p> * The returned interface translator must implement the component's operations interface, * and must take care of translating <code>in</code>, <code>inout</code> parameters, * forwarding the call to the respective method in the component implementation, * and then translating <code>out</code>, <code>inout</code> parameters, and the return value. * <p> * It is foreseen that the necessary translations for most methods will be done * automatically by the dynamic proxy classes. Some methods may require manual translation, * e.g. if an expression that involves xml entity classes is too complex for the * dynamic proxy. * <br> * On the other end of the spectrum, a manual implementation may * choose to <emph>not</emph> unmarshal a marshalled binding object that * it receives as an <code>in</code> parameter, because the binding object * will only be routed through to another component, so first unmarshalling * and later marshalling it would be an unnecessary performance loss. * In that case, the manual interface translator could call an additional * method in the component implementation which expects the marshalled xml * rather than the binding object. * <p> * To facilitate the implementation of a manual interface translator, * the <code>defaultInterfaceTranslator</code> is provided; it is a dynamic * proxy object that implements the component's operations interface and * forwards all calls to the component implementation created in * <code>_createComponentImpl</code>, performing type translations in between. * The methods that should be handled automatically can then be directly * delegated to that proxy object, and only those methods that require * special care need to be implemented by hand. * * @param defaultInterfaceTranslator the default translator that the custon translator may * use to delegate some or all method invocations to. * @return the custom translator, or null if the default translator should be used. */ protected Object _getInterfaceTranslator(Object defaultInterfaceTranslator) throws AcsJJavaComponentHelperEx { return null; } /** * @see #_getComponentMethodsExcludedFromInvocationLogging */ final String[] getComponentMethodsExcludedFromInvocationLogging() { String[] ret = null; try { ret = _getComponentMethodsExcludedFromInvocationLogging(); } catch (Exception ex) { m_containerLogger.log(Level.WARNING, "failed to obtain the list of component methods to exclude from automatic logging.", ex); } return ret; } /** * Tells the container to not automatically log calls to certain component interface methods. * This method may be overridden by helper subclasses, since the default behavior is * for the container to log all component method invocations except <code>ACSComponentOperations#componentState()</code>. * <p> * Dealing with OffShoots: offshoots that follow the Corba Tie-approach enjoy automatic method invocation logging * just like components do. In the rare event that you explicitly activate offshoots from your component, * and want to suppress automatic logging for certain methods, return <code>String</code>(s) in the * following format: <code>OFFSHOOT::<offshootinterfacename>#<methodname></code>. * Example for ALMA archive: "OFFSHOOT::Operational#retrieveFragment". * <p> * Note that returning <code>String</code>s is fine (compared with more sophisticated <code>Method</code> objects) * because the functional component interface methods cannot have parameter polymorphism thanks to IDL limitations. * Thus there is no ambiguity in the method names. * * @return names of component methods for which the call-intercepting container should not log anything. * Can be null. * @since ACS 5.0 */ protected String[] _getComponentMethodsExcludedFromInvocationLogging() { return null; } /** * <strong>Do not overwrite this method unless you absolutely need to * (currently only for the archive logger component!)</strong> * <p> * If <code>true</code> then the ORB logger will not send any log messages to the central (remote) Log service * after activation of this component, regardless of any kind (env var, CDB, dynamic) log level settings. * Local stdout logging by the ORB is not affected. * Currently the ORB log suppression is not reversible: even when the component which requested this * is unloaded, the ORB logger will not resume sending remote log messages. * This may change in the future though. * <p> * Note that <code>getComponentMethodsExcludedFromInvocationLogging</code> can switch off automatic logging * done by the container on behalf of the component. However, the ORB does not use a logger-per-component * concept, and thus can't be told selectively to not log anything for a particualar component. * The entire process (container and all components) are affected by suppressing the ORB's remote logging. * <p> * This method addresses the special problem of an infrastructural component that receives log messages * from the Log service and writes them to the archive. * Any message logged by the ORB on the receiver side would go back to the Log service and be received * by the infrastructural component, with positive feedback leading to an explosion of log messages. * Therefore we can't rely on log level settings and must categorically rule out such feedback loops. * <p> * @since ACS 7.0 when JacORB logs get sent to the Log service by default. * @return */ protected boolean requiresOrbCentralLogSuppression() { return false; } }