/* ALMA - Atacama Large Millimiter Array * Copyright (c) European Southern Observatory, 2012 * * 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.alarmsystem.dump; import java.sql.Timestamp; import java.util.Date; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; import cern.laser.source.alarmsysteminterface.FaultState; import alma.acs.component.client.AdvancedComponentClient; import alma.acs.component.client.ComponentClient; import alma.acs.logging.ClientLogManager; import alma.acs.shutdown.ShutdownHookBase; import alma.acs.util.IsoDateFormat; import alma.alarmsystem.clients.SourceClient; import alma.alarmsystem.clients.source.SourceListener; /** * The base class of the alarm dumper. It groups common code for the dumpers that write * XML alarms, from sources or categories, in the stdout. * <P> * The pattern for using a dumper is: * <OL> * <LI>Instantiate the dumper that extends this class * <LI>call {@link #receiveAlarms()} * </OL>call {@link #tearDown()} * <P> * {@link #receiveAlarms()} blocks until the user presses CTRL-C a signal is received * or {@link #tearDown()} is called.<BR> * {@link #receiveAlarms(long, TimeUnit)} can be used instead of {@link #receiveAlarms()} * if the receiving has to terminate after the specified timeout. * <P> * {@link #tearDown()} must be called before terminating to cleanly exit. * * @author acaproni * @since ACS-10.1 */ public abstract class AlarmDumperBase extends AdvancedComponentClient { /** * Detects when the user issued a CTRL+C and disconnects the {@link SourceClient} * and the {@link AdvancedComponentClient} before exiting. * * @author acaproni * */ private final class ShutdownHook extends ShutdownHookBase { /** * Constructor */ public ShutdownHook() { super(AlarmDumperBase.this.m_logger,AlarmDumperBase.class.getName()); } /** * Close the {@link SourceClient} and tearDown the {@link AdvancedComponentClient} */ @Override protected void interruptDetected() { tearDown(); } @Override protected void regularTermination() { // Inhibit logging generated by super class. } } /** * We use this synchronization aid to signal that the user pressed CTRL-C so that * the object stops receiving XML strings. */ private final CountDownLatch m_latch = new CountDownLatch(1); /** * <code>true</code> if the dumper has been closed */ private final AtomicBoolean m_closed = new AtomicBoolean(false); /** * The formatter for the timestamp */ private final IsoDateFormat m_dateFormat = new IsoDateFormat(); /** * Constructor * * @param theLogger The logger * @param mangerLoc The CORBA loc of the manager * @param clientname The name of the client * @throws Exception if an error happens instantiating the {@link AdvancedComponentClient} */ public AlarmDumperBase(Logger theLogger, String mangerLoc,String clientname ) throws Exception { super(theLogger,mangerLoc,clientname); } /** * Connects to the NC and start receiving alarms. * <P> * This must be implemented to connect to the right NC. * If the connection failed, {@link #startReceivingAlarms()} must thrown an exception */ public abstract void startReceivingAlarms() throws Exception ; /** * Connect the client * * This method never stops. * * @throws Exception In case of error connecting the {@link SourceClient} */ public void receiveAlarms() throws Exception { if (m_closed.get()) { m_logger.log(Level.SEVERE, "AlarmSourceDump is closed."); } startReceivingAlarms(); m_latch.await(); } /** * Connect the client until the timeout elapses or the user presses CTRL-C * * This method never when the timeout elapse or the user presses CTRL-C * * @throws Exception In case of error connecting the {@link SourceClient} */ public void receiveAlarms(long timeout, TimeUnit timeUnit) throws Exception { if (m_closed.get()) { m_logger.log(Level.SEVERE, "AlarmSourceDump is closed."); } if (timeUnit==null) { throw new IllegalArgumentException("Invalid NULL TimeUnit"); } startReceivingAlarms(); m_latch.await(timeout,timeUnit); } /** * Close the resources of the alarm client */ public abstract void close() throws Exception ; /** * Cleanly tear down */ public synchronized void tearDown() { if (m_closed.getAndSet(true)) { // Already called // // tearDown is, in fact, called twice:the shutdown hook and in by main return; } // Stop receiving XMLs m_latch.countDown(); // Close client try { close(); } catch (Throwable t) { // Nothing to do but we log a message.. m_logger.log(Level.SEVERE, "Error shutting closing the alarm client", t); } // Close the component client try { super.tearDown(); } catch (Throwable t) { // Nothing to do but we log a message.. m_logger.log(Level.SEVERE, "Error shutting down the component client", t); } } /** * {@link AlarmDumperBase} registers its own shutdown hook in fact what is provided * by {@link ComponentClient} logs a few warnings if the process is interrupted * but in this case the situation is absolutely normal. */ @Override protected void registerShutdownHook() { m_shutdownHook = new ShutdownHook(); Runtime.getRuntime().addShutdownHook(m_shutdownHook); } /** * Return the timestamp in ISO format. * @param timestamp * @return */ public String formatTimestamp(Timestamp timestamp) { if (timestamp==null) { throw new IllegalArgumentException("The timestamp can't be null"); } synchronized (m_dateFormat) { return m_dateFormat.format(new Date(timestamp.getTime())); } } /** * This block of code avoid code replication. * * @param args Command line args * @param isSourceClient <code>true</code> if a source client must be instantiated, * <code>false</code> for category client * @param timeout If greater then <code>0</code> the process waits until the timeout elapses * @param timeUnit The TimeUnit for the timeout (must be not <code>null</code> if the timeout is greater then <code>0</code>) */ public static void clientRunner(String[] args, boolean isSourceClient, long timeout, TimeUnit timeUnit) { if (timeout>0 && timeUnit==null) { throw new IllegalArgumentException("Invalid parameters for timeout"); } Logger logger = ClientLogManager.getAcsLogManager().getLoggerForApplication("AlarmSourceDump",true); String managerLoc = System.getProperty("ACS.manager"); if (managerLoc == null) { System.err.println("Java property 'ACS.manager' must be set to the corbaloc of the ACS manager!"); System.exit(-1); } AlarmDumperBase dumper=null; try { if (isSourceClient) { dumper = new AlarmSourceDumper(logger, managerLoc); } else { dumper = new AlarmCategoryDumper(logger,managerLoc); } } catch (Throwable t) { System.err.println("Exception caught while creating the AlarmSourceDump: "+t.getMessage()); t.printStackTrace(System.err); System.exit(-1); } try { if (timeout>0) { dumper.receiveAlarms(timeout,timeUnit); } else { // No timeout dumper.receiveAlarms(); } } catch (Throwable t) { logger.log(Level.SEVERE, "Error connecting the alarm source client", t); } dumper.tearDown(); } }