/* 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.acs.alarmsystem.source; import java.util.HashMap; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import alma.acs.container.ContainerServicesBase; import alma.acs.logging.AcsLogLevel; import alma.alarmsystem.source.ACSAlarmSystemInterfaceFactory; /** * <code>AlarmSourceFactory</code> is in charge of instantiating and releasing {@link AlarmSource} objects * avoid dependencies in the {@link ACSAlarmSystemInterfaceFactory}, {@link ComponentClient} and * {@link ContainerServices}. * <P> * <code>AlarmSourceFactory</code> should be used by {@link ComponentClient} and {@link ContainerServices} * to return a {@link AlarmSource} and to release all the {@link AlarmSource} objects before exiting. * <P> * The purpose of <code>AlarmSourceFactory</code> is to store and delivery <code>AlarmSource</code> objects to components, * being sure that a component instantiates at the most one and only one <code>AlarmSource</code>. * The <code>AlarmSourceFactory</code> keep a reference to all the instantiated <code>AlarmSource</code> objects to ensure, during the * shutdown of a component, to flush all the alarms and finally release all the resources used by those * objects. * <P>Life cycle: while there is no special initialization method, * {@link AlarmSourceFactory#tearDown()} must be called when done with this object. * <P>Concurrency: We use a {@link ConcurrentHashMap} that reduces the number of lock outs in respect * of the traditional {@link HashMap} resulting in better performances, especially when the number of get's * is relevant as we expect during normal activities. To avoid using another lock for releasing * all the alarm sources, we preferred a volatile boolean to reject operations when the factory has been closed. * Under some circumstances, it could happen that the tearDown cleans the map when another method is still running. * This should not be a big deal because tearDown runs just before terminating the container (or * component client). * * @author acaproni * @version $Id: AlarmSourceFactory.java,v 1.2 2012/08/02 12:51:47 acaproni Exp $ * @since ACS 10.2 */ public class AlarmSourceFactory { /** * The ContainerServices */ private ContainerServicesBase contSvcs; /** * The map of all the alarm sources created by the factory. * <P> * The key is the name of the component. */ private final Map<String, AlarmSource>alarmSources = new ConcurrentHashMap<String, AlarmSource>(); /** * The flag signals if the factory has been already closed to reject further * incoming requests. * <P> * {@link ConcurrentHashMap} does not support transaction so we want to avoid accepting new * requests especially during the shutdown where the factory must perform a series of operations. */ private volatile boolean closed=false; /** * Constructor * * @param logger The logger * @param closed */ public AlarmSourceFactory(ContainerServicesBase contSvcs) { super(); if (contSvcs==null) { throw new IllegalArgumentException("The ContainerServices can't be null"); } this.contSvcs = contSvcs; } /** * Return the {@link AlarmSource} for the component whose name is read from * passed the {@link ContainerServicesBase}. * <P> * If a <code>AlarmSource</code> already exists for the component then a reference is returned, * otherwise a new <code>AlarmSource</code> is built. * * @param containerServices The not <code>null</code> {@link ContainerServicesBase} * @return the {@link AlarmSource} */ public AlarmSource getAlarmSource(ContainerServicesBase containerServices) { if (containerServices==null) { throw new IllegalArgumentException("The ContainerServices can't be null"); } return getAlarmSource(containerServices.getName()); } /** * Return the {@link AlarmSource} for the component with the passed name. * <P> * If a <code>AlarmSource</code> already exists for the component then a reference is returned, * otherwise a new <code>AlarmSource</code> is built. * <P> * Note that the component name is used as a key to return the same {@link AlarmSource} * for the same component so in principle the parameter <code>componentName</code> * can be freely set. * * @param componentName The not <code>null</code> and not empty name of the component. * @return the {@link AlarmSource} */ public AlarmSource getAlarmSource(String componentName) { if (closed) { throw new IllegalStateException("The AlarmSourceFactory has already been closed!"); } if (componentName==null | componentName.isEmpty()) { throw new IllegalArgumentException("The name can't be null nor empty"); } AlarmSource ret = alarmSources.get(componentName); if (ret==null) { contSvcs.getLogger().log(AcsLogLevel.DELOUSE, "Instantiating the AlarmSource for "+componentName); ret = new AlarmSourceImpl(contSvcs); alarmSources.put(componentName, ret); ret.start(); } return ret; } /** * Release the {@link AlarmSource} object of the component whose name is read from * passed the {@link ContainerServicesBase}. * * @param containerServices */ public void releaseAlarmSource(ContainerServicesBase containerServices) { if (containerServices==null) { throw new IllegalArgumentException("The ContainerServices can't be null"); } releaseAlarmSource(containerServices.getName()); } /** * Release the {@link AlarmSource} object of the component whose name is read from * passed the {@link ContainerServicesBase}. * * @param componentName The name of the component */ public void releaseAlarmSource(String componentName) { if (closed) { throw new IllegalStateException("The AlarmSourceFactory has already been closed!"); } if (componentName==null | componentName.isEmpty()) { throw new IllegalArgumentException("The name can't be null nor empty"); } AlarmSource alarmSrc=alarmSources.remove(componentName); if (alarmSrc!=null) { contSvcs.getLogger().log(AcsLogLevel.DELOUSE, "Releasing the AlarmSource of "+componentName); // Flush all the alarms.. frees resources like threads and so on alarmSrc.tearDown(); } } public void tearDown() { if (closed) { contSvcs.getLogger().log(AcsLogLevel.WARNING, "AlarmSourceFactory already closed"); return; } closed=true; contSvcs.getLogger().log(AcsLogLevel.DELOUSE, "Closing the AlarmSourceFactory"); Set<String>sourceNames=alarmSources.keySet(); for (String sourceName: sourceNames) { AlarmSource source=alarmSources.get(sourceName); if (source!=null) { contSvcs.getLogger().log(AcsLogLevel.DELOUSE, "Closing AlarmSource with name "+sourceName); source.tearDown(); } } alarmSources.clear(); contSvcs.getLogger().log(AcsLogLevel.DEBUG, "AlarmSourceFactory closed"); } }