/* 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 com.cosylab.acs.laser; import java.util.Collection; import java.util.Collections; import java.util.Vector; import java.util.logging.Logger; import com.cosylab.acs.jms.ACSJMSMessageEntity; import alma.ACSErrTypeCommon.wrappers.AcsJCouldntPerformActionEx; import alma.ACSErrTypeCommon.wrappers.AcsJIllegalStateEventEx; import alma.JavaContainerError.wrappers.AcsJContainerServicesEx; import alma.acs.container.ContainerServicesBase; import alma.acs.logging.AcsLogLevel; import alma.acs.nc.AcsEventSubscriber; import alma.acsErrTypeLifeCycle.wrappers.AcsJEventSubscriptionEx; import alma.acscommon.ACS_NC_DOMAIN_ALARMSYSTEM; import alma.acsnc.EventDescription; /** * Connects to all the alarm source NC and listen for alarms. * <P> * {@link #shutdown()} must be called to disconnect from the NC when terminated * using objects of this class. * * @author acaproni * @version $Id: AlarmSourcesListener.java,v 1.5 2013/02/05 17:15:15 acaproni Exp $ * @since ACS 11.0 */ public class AlarmSourcesListener implements AcsEventSubscriber.Callback<ACSJMSMessageEntity> { /** * The listener of messages from sources * * @author acaproni * */ public interface SourceListener { /** * The xml recieved from the source (to be unmarshalled * into e <code>ASIMessage</code>). * @param xml */ public void onMessage(String xml); } /** * Container Services */ protected final ContainerServicesBase contSvcs; /** * The logger */ protected final Logger logger; /** * The alarm system prepend the name of each source NCs with the same string */ private final String channelGroupName="CMW.ALARM_SYSTEM.ALARMS.SOURCES."; /** * The listeners to send messages to */ private final Collection<SourceListener> listeners = Collections.synchronizedCollection(new Vector<SourceListener>()); /** * Signal that the object has been shutdown */ private volatile boolean closed=false; /** * The consumers each of which listens to a source NC */ private final Collection<AcsEventSubscriber<ACSJMSMessageEntity>> consumers = Collections.synchronizedCollection(new Vector<AcsEventSubscriber<ACSJMSMessageEntity>>()); /** * Constructor. * * @param contSvcs Alarm service container services * @param logger The logger */ public AlarmSourcesListener(ContainerServicesBase contSvcs, Logger logger) { if (contSvcs==null) { throw new IllegalArgumentException("Alarm container services can't be null!"); } this.contSvcs=contSvcs; if (logger==null) { throw new IllegalArgumentException("The logger can't be null!"); } this.logger=logger; } /** * Constructor. * * @param contSvcs Alarm service container services * @param logger The logger * @param listener the listener to notify messages to */ public AlarmSourcesListener(ContainerServicesBase contSvcs, Logger logger, SourceListener listener) { this(contSvcs,logger); if (listener==null) { throw new IllegalArgumentException("The listener can't be null!"); } addListener(listener); } /** * Add a listener to notify messages to * * @return <code>true</code> if the collection of message listeners changed as a result of the call */ public boolean addListener(SourceListener listener) { if (listener==null) { throw new IllegalArgumentException("The listener can't be null!"); } if (closed) { return false; } return listeners.add(listener); } /** * Add a listener to notify messages to * * @return <code>true</code> if the listener was removed as a result of this call */ public boolean removeListener(SourceListener listener) { if (listener==null) { throw new IllegalArgumentException("The listener can't be null!"); } if (closed) { return false; } return listeners.remove(listener); } /** * Connect to all the passed sources NC * * @param sourceNcNames The names of the source NCs to connect to * @throws AcsJContainerServicesEx In case of error subscribing to the NC * @throws AcsJIllegalStateEventEx If already subscribed to one of the consumers * @throws AcsJEventSubscriptionEx * @throws AcsJCouldntPerformActionEx */ public void connectSources(String[] sourceNcNames) throws AcsJContainerServicesEx, AcsJIllegalStateEventEx, AcsJEventSubscriptionEx, AcsJCouldntPerformActionEx { if (sourceNcNames==null || sourceNcNames.length==0) { throw new IllegalArgumentException("No source channel names to connect to!"); } for (String name: sourceNcNames) { connectSource(name); } } /** * Connect to all the passed sources NC * * @param sourceNcNames * @throws AcsJContainerServicesEx In case of error subscribing to the NC * @throws AcsJIllegalStateEventEx If already subscribed to the consumer * @throws AcsJEventSubscriptionEx * @throws AcsJCouldntPerformActionEx */ public void connectSource(String ncName) throws AcsJContainerServicesEx, AcsJIllegalStateEventEx, AcsJEventSubscriptionEx, AcsJCouldntPerformActionEx { if (ncName==null || ncName.isEmpty()) { throw new IllegalArgumentException("Invalid name of source channel!"); } String name=channelGroupName+ncName; logger.log(AcsLogLevel.DEBUG,"Connecting to source NC: "+name); AcsEventSubscriber<ACSJMSMessageEntity> consumer; consumer = contSvcs.createNotificationChannelSubscriber(name, ACS_NC_DOMAIN_ALARMSYSTEM.value, ACSJMSMessageEntity.class); consumer.addSubscription(this); logger.log(AcsLogLevel.DEBUG,"Start receiving alarms from source NC: "+name); consumer.startReceivingEvents(); consumers.add(consumer); } /** * Closes all the NC and remove all the listeners. * */ public void shutdown() { closed=true; // Disconnects the NCs logger.log(AcsLogLevel.DEBUG,"Sources listener is about to disconnect from "+consumers.size()+" source channels."); synchronized (consumers) { for (AcsEventSubscriber<ACSJMSMessageEntity> consumer: consumers) { try { disconnect(consumer); } catch (Throwable t) { logger.log(AcsLogLevel.ERROR,"Error disconnecting from the source NC: "+t.getMessage()); t.printStackTrace(System.err); } } consumers.clear(); } listeners.clear(); logger.log(AcsLogLevel.DEBUG,"Sources listener is shutdown."); } /** * Disconnect a consumer. * * @throws AcsJIllegalStateEventEx If the subscriber has been already disconnected * @throws AcsJCouldntPerformActionEx */ private void disconnect(AcsEventSubscriber<ACSJMSMessageEntity> consumer) throws AcsJIllegalStateEventEx, AcsJCouldntPerformActionEx { consumer.disconnect(); } /** * The NC subscriber callback method. */ @Override public void receive(ACSJMSMessageEntity message, EventDescription eventDescrip) { if (message==null) { throw new NullPointerException("The message received is null"); } // Discard messages if closed if (closed) { return; } if (message.type.compareTo("com.cosylab.acs.jms.ACSJMSObjectMessage")==0){ logger.log(AcsLogLevel.WARNING,"Unknown message type received from source "+message.type); } else { notifyListeners(message.text); } } /** * Notify the listeners of a new message * * @param xml The xml (ASIMessage) to notify */ protected void notifyListeners(String xml) { if (xml==null) { throw new NullPointerException("The XML source message to notify is null"); } if (closed) { return; } synchronized (listeners) { for (SourceListener listener: listeners) { try { listener.onMessage(xml); } catch (Throwable t) { logger.log(AcsLogLevel.ERROR,"Error notifying the source listener: "+t.getMessage(),t); } } } } @Override public Class<ACSJMSMessageEntity> getEventType() { return ACSJMSMessageEntity.class; } }