/* * 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.client; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import org.omg.PortableServer.POA; import com.cosylab.CDB.DAL; import com.cosylab.CDB.DALHelper; import si.ijs.maci.Client; import alma.acs.concurrent.DaemonThreadFactory; import alma.acs.container.AcsManagerProxy; import alma.acs.container.CleaningDaemonThreadFactory; import alma.acs.container.ContainerServices; import alma.acs.container.ContainerServicesImpl; import alma.acs.container.corba.AcsCorba; import alma.acs.logging.AcsLogger; import alma.acs.logging.ClientLogManager; import alma.acs.shutdown.ShutdownHookBase; import alma.alarmsystem.source.ACSAlarmSystemInterfaceFactory; /** * Class that facilitates writing client application that access ACS components. * Takes care of the communication with the ACS manager. * Provides the {@link ContainerServices}. * Can be used either as a base class or as a delegate object. * <p> * Note that this class is only meant to be used within a stand-alone application * that needs to communicate with ACS-based components. * It is definitely not meant to be used by a component running inside an ACS container. * * @author hsommer Nov 21, 2002 5:53:05 PM */ public class ComponentClient { private ContainerServicesImpl m_containerServices; private CleaningDaemonThreadFactory m_threadFactory; protected final AcsLogger m_logger; final String m_clientName; // manager client object private Client m_managerClient; AcsManagerProxy m_acsManagerProxy; /** * The instance of {@link AcsCorba} to be used by this class for all CORBA activities. */ final AcsCorba acsCorba; final boolean ownAcsCorba; protected ShutdownHookBase m_shutdownHook; final private AtomicBoolean m_shuttingDown = new AtomicBoolean(false); ///////////////////////////////////////////////////////////// // initialization stuff ///////////////////////////////////////////////////////////// /** * Normal constructor. * The component client will take care of creating ORB and POAs, * and of setting up remote logging. * * @param logger * @param managerLoc * @param clientName * @throws Exception * @see #initRemoteLogging() */ public ComponentClient(Logger logger, String managerLoc, String clientName) throws Exception { this(logger, managerLoc, clientName, null); } /** * Special c'tor that provides an already initialized instance of <code>AcsCorba</code> which encapsulates the ORB. * This may be useful for an application such as the ALMA executive, which works with different ORBs, tries out * available ports, and so on. * <p> * With this constructor, also initialization and termination of remote ACS logging is left to the caller. * * @param logger * the logger to be used. If <code>null</code>, one will be created. Since ACS 8.0 it is recommended to * supply <code>null</code> or an {@link AcsLogger} instead of a plain JDK Logger because a plain Logger * will have to be wrapped inside this method. * @param managerLoc * the corbaloc for the ACS manager, e.g. "corbaloc::myhost:xxxx/Manager" * @param clientName * name to be used toward the ACS Manager * @param externalAcsCorba * encapsulates initialized ORB and POAs. * @throws Exception * at the slightest provocation... * @see #initRemoteLogging() */ protected ComponentClient(Logger logger, String managerLoc, String clientName, AcsCorba externalAcsCorba) throws Exception { if (logger == null) { m_logger = ClientLogManager.getAcsLogManager().getLoggerForApplication(clientName, true); } else { // wrap logger if necessary m_logger = AcsLogger.fromJdkLogger(logger, null); } m_clientName = clientName; POA myPOA = null; if (externalAcsCorba == null) { try { acsCorba = new AcsCorba(m_logger); myPOA = acsCorba.initCorbaForClient(false); ownAcsCorba = true; } catch (IllegalStateException e) { throw new IllegalStateException("Class '" + ComponentClient.class.getName() + "' can only be used as a singleton and outside of ACS containers. " + e.getMessage()); } } else { acsCorba = externalAcsCorba; myPOA = acsCorba.getRootPOA(); ownAcsCorba = false; } initAcs(managerLoc, myPOA); if (ownAcsCorba) { initRemoteLogging(); } registerShutdownHook(); m_logger.fine("ready to talk with components!"); } /** * Default shutdown hook is used to complain if the application forgot to call tearDown(). * Can overwrite this, e.g. for cmd line tools. */ protected void registerShutdownHook() { m_shutdownHook = new ShutdownHookBase(m_logger, "ClientVM") { protected void interruptDetected() { try { m_logger.severe("*** process is exiting without releasing ACS client - will tearDown() it ***"); tearDown(); } catch(Exception e) { e.printStackTrace(); } System.err.println("*** emergency shutdown complete, will exit... ***"); } protected void regularTermination() { // Don't do or say anything } }; Runtime.getRuntime().addShutdownHook(m_shutdownHook); } private void initAcs(String managerLoc, POA rootPOA) throws Exception { try { ManagerClient clImpl = new ManagerClient(m_clientName, m_logger) { public void disconnect() { m_logger.info("disconnected from manager"); m_acsManagerProxy.logoutFromManager(); m_acsManagerProxy = null; throw new RuntimeException("disconnected from the manager"); } }; m_managerClient = clImpl._this(acsCorba.getORB()); m_acsManagerProxy = new AcsManagerProxy(managerLoc, acsCorba.getORB(), m_logger); m_acsManagerProxy.loginToManager(m_managerClient, 1); DAL cdb = DALHelper.narrow(m_acsManagerProxy.get_service("CDB", false)); m_threadFactory = new CleaningDaemonThreadFactory(m_clientName, m_logger); m_containerServices = new ContainerServicesImpl(m_acsManagerProxy, cdb, rootPOA, acsCorba, m_logger, 0, m_clientName, null, m_threadFactory); clImpl.setContainerServices(m_containerServices); initAlarmSystem(); } catch (Exception ex) { // m_logger.log(Level.SEVERE, "failed to connect to the ACS Manager " + // "or to set up the container services.", ex); if (acsCorba.getORB() != null) { acsCorba.getORB().destroy(); } throw ex; } } /** * Initializes the alarm system, using {@link ACSAlarmSystemInterfaceFactory#init(alma.acs.container.ContainerServicesBase)}. * Override this method only in special cases such as the eventGUI, when a client does not need to access the alarm system * and has special classpath problems with the CERN alarm system jar files. * @see #tearDownAlarmSystem() */ protected void initAlarmSystem() throws Exception { try { ACSAlarmSystemInterfaceFactory.init(m_containerServices); } catch (Throwable thr) { throw new Exception("Error initializing the alarm system factory", thr); } } /** * Stops and cleans up the alarm system, using {@link ACSAlarmSystemInterfaceFactory#done()}. * Override this method only in special cases such as the eventGUI, when a client does not need to access the alarm system * and has special classpath problems with the CERN alarm system jar files. * @see #initAlarmSystem() */ protected void tearDownAlarmSystem() { ACSAlarmSystemInterfaceFactory.done(); } /** * Sets up the client logger(s) to send log records to the remote log service. * This method starts a separate thread and returns immediately. * <p> * Makes repeated attempts to connect to the remote logger. * If they fail unexpectedly, remote logging will be disabled. * <p> * If the <code>ComponentClient</code> has been constructed without an external <code>AcsCorba</code> object * (the normal case), then this method is called automatically. <br> * Otherwise (with an external <code>AcsCorba</code> object provided) it is assumed that also * remote logging is controlled from outside of this class. If nonetheless you want to * initialize remote logging, you may explicitly call this method for convenience. * <p> * Override this method to prevent remote logging (useful only if AcsCorba is is not provided externally). */ public void initRemoteLogging() { Runnable cmd = new Runnable() { public void run() { boolean gotLogService = false; try { gotLogService = ClientLogManager.getAcsLogManager().initRemoteLogging( acsCorba.getORB(), m_acsManagerProxy.getManager(), m_acsManagerProxy.getManagerHandle(), true); } catch (Exception e) { // just log below } if (!gotLogService) { m_logger.log(Level.WARNING, "ACS central logging not available!"); ClientLogManager.getAcsLogManager().suppressRemoteLogging(); } } }; ExecutorService executor = Executors.newSingleThreadExecutor(new DaemonThreadFactory("InitRemoteLogging")); executor.execute(cmd); } ///////////////////////////////////////////////////////////// // service methods ///////////////////////////////////////////////////////////// /** * Gives access to the {@link ContainerServices} interface. * This class plays the part of the role of the Java container that has to do with * providing explicit services to the component, or test case respectively. * <p> * @return ContainerServices */ public ContainerServices getContainerServices() { return m_containerServices; } ///////////////////////////////////////////////////////////// // finalization stuff ///////////////////////////////////////////////////////////// /** * Must be called by the application that uses this class, when it is done using ACS components. * At the latest it should be called when the application itself terminates. * <p> * Releases all previously obtained components (using manager), and logs out from the manager. * It also stops remote logging and terminates the CORBA ORB * unless an <code>AcsCorba</code> instance was provided in the c'tor; * otherwise the application remains responsible to clean up logging and the ORB. */ public void tearDown() throws Exception { if( m_shuttingDown.getAndSet(true) ) { m_logger.fine("duplicate call to tearDown() will be ignored"); return; } m_logger.fine("will disconnect ACS stuff..."); try { m_acsManagerProxy.shutdownNotify(); m_containerServices.releaseAllComponents(); tearDownAlarmSystem(); m_containerServices.cleanUp(); m_acsManagerProxy.logoutFromManager(); if (ownAcsCorba) { ClientLogManager.getAcsLogManager().shutdown(false); acsCorba.shutdownORB(true, false); acsCorba.doneCorba(); } m_threadFactory.cleanUp(); } catch (org.omg.CORBA.OBJECT_NOT_EXIST ex) { // todo: fix the bug that causes this; // since it does not really matter, for now we keep a low profile outputting messages... m_logger.warning("ORB#destroy caused an org.omg.CORBA.OBJECT_NOT_EXIST exception; "); } catch (Exception e) { System.err.println("Exception in " + m_clientName + "#" + "tearDown: "); e.printStackTrace(System.err); throw e; } m_shutdownHook.setRegularShutdownExpected(); } }