/* * ALMA - Atacama Large Millimiter Array (c) European Southern Observatory, 2006 * * 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.alarm.tests; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; import static org.junit.Assert.assertThat; import java.sql.Timestamp; import java.util.Date; import java.util.Vector; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import junit.framework.JUnit4TestAdapter; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; import cern.laser.business.pojo.AlarmMessageProcessorImpl; import cern.laser.client.data.Alarm; import cern.laser.client.services.selection.AlarmSelectionListener; import cern.laser.client.services.selection.LaserSelectionException; import cern.laser.source.alarmsysteminterface.FaultState; import cern.laser.source.alarmsysteminterface.impl.ASIMessageHelper; import cern.laser.source.alarmsysteminterface.impl.FaultStateImpl; import cern.laser.source.alarmsysteminterface.impl.XMLMessageHelper; import cern.laser.source.alarmsysteminterface.impl.message.ASIMessage; import com.cosylab.acs.jms.ACSJMSMessageEntity; import com.cosylab.acs.jms.ACSJMSTextMessage; import alma.acs.component.client.ComponentClient; import alma.acs.container.ContainerServices; import alma.acs.logging.AcsLogLevel; import alma.acs.nc.AcsEventPublisher; import alma.acs.util.AcsLocations; import alma.acs.util.IsoDateFormat; import alma.acscommon.ACS_NC_DOMAIN_ALARMSYSTEM; import alma.alarmsystem.clients.CategoryClient; import alma.alarmsystem.core.alarms.LaserCoreFaultState; import alma.alarmsystem.core.alarms.LaserCoreFaultState.LaserCoreFaultCodes; import alma.alarmsystem.source.ACSAlarmSystemInterface; import alma.alarmsystem.source.ACSAlarmSystemInterfaceFactory; /** * Test if the alarm server sends/clears an alarm when the difference between the actual * time and the timestamp of the alarms received by the sources differs of more then a threshold. * <P> * The alarm that this test want to check is {@link LaserCoreFaultCodes#ALARMS_TOO_OLD}. * <BR> * All other alarms received by {@link #onAlarm(Alarm)} that do not match with {@link LaserCoreFaultCodes#ALARMS_TOO_OLD} * are discarded. * <P> * The test {@link #testAlarmsTooOld()} waits for a signal when the alarms are received in {@link #onAlarm(Alarm)}. * If the alarms are not received a timeout occurs and the relative assert fails. * * @author acaproni * @since ACS 11.1 */ public class AlarmsDelayCoreAlarmTest extends ComponentClient implements AlarmSelectionListener { /** * TODO: Check if this rule and the getMethodName() call in setUp() can be moved up to ComponentClient, * if that adds a runtime dependency on junit, and how bad that would be. * Probably we should add a class ComponentClientTestCaseJUnit4 that extends ComponentClient * and only adds this testname business. */ @Rule public TestName testName = new TestName(); /** * The category client */ private CategoryClient categoryClient; /** * Container services */ private ContainerServices contSvcs; /** * The logger */ private Logger logger; /** * The name of the source NCs */ private final String sourceChannelName="CMW.ALARM_SYSTEM.ALARMS.SOURCES.ALARM_SYSTEM_SOURCES"; /** * The channel to publish alarms */ private AcsEventPublisher<ACSJMSMessageEntity> sourceNC; /** * The ID of the ALARMS_TOO_OLD alarm that we want to receive */ private final String tooOldAlarmId=LaserCoreFaultState.FaultFamily+":"+LaserCoreFaultState.FaultMember+":"+LaserCoreFaultCodes.ALARMS_TOO_OLD.faultCode; /** * The countdown to wait for the arrival of the active core alarm */ private CountDownLatch activeAlarmCL = new CountDownLatch(1); /** * The countdown to wait for the arrival of the active core alarm */ private CountDownLatch terminateAlarmCL = new CountDownLatch(1); /** * Monitor if the activate core alarm has been received more then once */ private volatile boolean coreActivatedReceived=false; /** * Monitor if the activate core alarm has been received more then once */ private volatile boolean coreTerminatedReceived=false; /** * The source to send alarms */ private ACSAlarmSystemInterface alarmSource; /** * For compatibility with JUnit3 based TATJUnitRunner */ public static junit.framework.Test suite() { return new JUnit4TestAdapter(AlarmsDelayCoreAlarmTest.class); } /** * Constructor * * @throws Exception */ public AlarmsDelayCoreAlarmTest() throws Exception { super(null, AcsLocations.figureOutManagerLocation(), AlarmsDelayCoreAlarmTest.class.getSimpleName()); } @After public void tearDown() throws Exception { sourceNC.disconnect(); categoryClient.close(); super.tearDown(); } @Before public void setUp() throws Exception { contSvcs = getContainerServices(); assertThat("Invalid null container services",contSvcs,notNullValue()); logger=contSvcs.getLogger(); assertThat("Invalid null logger",logger,notNullValue()); // Connect the categories categoryClient= new CategoryClient(contSvcs); assertThat("Invalid null CategoryClient",categoryClient,notNullValue()); categoryClient.connect(this); alarmSource = ACSAlarmSystemInterfaceFactory.createSource(); assertThat("The source is null!",alarmSource,notNullValue()); // Connect to the source NC sourceNC=contSvcs.createNotificationChannelPublisher(sourceChannelName, ACS_NC_DOMAIN_ALARMSYSTEM.value, ACSJMSMessageEntity.class); assertThat("The source NC is null!",sourceNC,notNullValue()); } /** * Create a ASI message with a old timestamp and checks if the alarm server detects the error and publishes the alarm. * <P> * If the previous part passed, then generate another alarm with the correct timestamp to check if the server clears the alarm * * @throws Exception */ @Test public void testAlarmsTooOld() throws Exception { // Set a alarm with a old time stamp long timestamp = System.currentTimeMillis()-AlarmMessageProcessorImpl.delayThreashold-10; sendAlarm("TheFamily", "TheMember", 1, true, timestamp); // Leave the alarm server enough time to detect the problem and send the alarm logger.log(AcsLogLevel.DEBUG,"Waiting for the alarm server to set the core alarm"); boolean timedOut=activeAlarmCL.await(AlarmMessageProcessorImpl.alarmTimestampsCheckInterval+60000, TimeUnit.MILLISECONDS); assertThat("Active core alarm NOT received", timedOut, is(true)); // Set another alarm with a correct time stamp timestamp = System.currentTimeMillis(); logger.log(AcsLogLevel.DEBUG,"Waiting for the alarm server to clear the core alarm"); sendAlarm("TheFamily", "TheMember", 1, false, timestamp); // Leave the alarm server enough time to detect that the problem has been solved and clear the alarm timedOut=terminateAlarmCL.await(AlarmMessageProcessorImpl.alarmTimestampsCheckInterval+60000, TimeUnit.MILLISECONDS); assertThat("Terminate core alarm NOT received", timedOut, is(true)); } /** * Send a alarm to the alarm NC bypassing the ACS API because we want to simulate a delay! * * @param faultFamily * @param faultMember * @param code * @param active * @param timeStamp * @throws Exception */ private void sendAlarm(String faultFamily, String faultMember, int code, boolean active, long timeStamp) throws Exception { FaultState fs = new FaultStateImpl(faultFamily, faultMember, code); if (active) { fs.setDescriptor(FaultState.ACTIVE); } else { fs.setDescriptor(FaultState.TERMINATE); } Timestamp timestamp = new Timestamp(timeStamp); fs.setUserTimestamp(timestamp); Vector<FaultState> states = new Vector<FaultState>(); states.add(fs); ASIMessage asiMessage = ASIMessageHelper.marshal(states); // Set the timestamp by adding a dealy greater then AlarmMessageProcessorImpl.delayThreashold String nowStr = IsoDateFormat.formatDate(new Date(timeStamp)); asiMessage.setSourceTimestamp(nowStr); // Set a fake hostname asiMessage.setSourceHostname("alma"); asiMessage.setSourceName("ALARM_SYSTEM_SOURCES"); ACSJMSTextMessage textMessage = new ACSJMSTextMessage(contSvcs); textMessage.setText(XMLMessageHelper.marshal(asiMessage)); // Finally send the alarm sourceNC.publishEvent(textMessage.getEntity()); } /** * * @see cern.laser.client.services.selection.AlarmSelectionListener#onAlarm(cern.laser.client.data.Alarm) */ @Override public void onAlarm(Alarm alarm) { if (!tooOldAlarmId.equals(alarm.getAlarmId())) { // Only want to check for tooOldAlarmId return; } if (alarm.getStatus().isActive()) { if (!coreActivatedReceived) { activeAlarmCL.countDown(); coreActivatedReceived=true; } else { logger.log(AcsLogLevel.ERROR,alarm.getAlarmId()+" already received"); } } else { if (!coreTerminatedReceived) { terminateAlarmCL.countDown(); coreTerminatedReceived=true; } else { logger.log(AcsLogLevel.ERROR,alarm.getAlarmId()+" already received"); } } } /** * @see cern.laser.client.services.selection.AlarmSelectionListener#onException(cern.laser.client.services.selection.LaserSelectionException) */ @Override public void onException(LaserSelectionException e) { super.getContainerServices().getLogger().log(AcsLogLevel.ERROR, "Error from the alarm server", e); } }