/* 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. *
* The pattern for using a dumper is: *
* {@link #receiveAlarms()} blocks until the user presses CTRL-C a signal is received
* or {@link #tearDown()} is called.
* {@link #receiveAlarms(long, TimeUnit)} can be used instead of {@link #receiveAlarms()}
* if the receiving has to terminate after the specified timeout.
*
* {@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);
/**
* true
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.
*
* 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 true
if a source client must be instantiated,
* false
for category client
* @param timeout If greater then 0
the process waits until the timeout elapses
* @param timeUnit The TimeUnit for the timeout (must be not null
if the timeout is greater then 0
)
*/
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();
}
}