/*******************************************************************************
* ALMA - Atacama Large Millimeter Array
* Copyright (c) ESO - European Southern Observatory, 2011
* (in the framework of the ALMA collaboration).
* All rights reserved.
*
* 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.source;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import com.cosylab.CDB.DAL;
import alma.JavaContainerError.wrappers.AcsJContainerServicesEx;
import alma.acs.alarmsystem.source.AlarmSource;
import alma.acs.alarmsystem.source.AlarmSourceFactory;
import alma.acs.container.ContainerServicesBase;
import alma.acs.logging.AcsLogLevel;
import alma.acsErrTypeAlarmSourceFactory.ACSASFactoryNotInitedEx;
import alma.acsErrTypeAlarmSourceFactory.FaultStateCreationErrorEx;
import alma.acsErrTypeAlarmSourceFactory.SourceCreationErrorEx;
import alma.acsErrTypeAlarmSourceFactory.wrappers.AcsJACSASFactoryNotInitedEx;
import alma.acsErrTypeAlarmSourceFactory.wrappers.AcsJFaultStateCreationErrorEx;
import alma.acsErrTypeAlarmSourceFactory.wrappers.AcsJSourceCreationErrorEx;
/**
* <code>ACSAlarmSystemInterfaceFactory</code> extends the CERN AlarmSystemInterfaceFactory
* to create sources with different implementations depending on the actual
* configuration in the CDB.
* <P>
* The type of implementation is in AlarmSystemConfiguration.xml: a property named
* Implementation.
* CERN implementation is used only if the value of such property is CERN.
* If the property is not found, is ACS or the CDB record does not exist then the
* ACS implementation for sources is used.
* <P>
* <code>ACSAlarmSystemInterfaceFactory</code> owns the instances of the {@link AlarmSource}
* created by the component or containers. in this way it is possible to control the cleanup
* of the alarm sources when a component is unloaded or the <code>ACSAlarmSystemInterfaceFactory</code>
* terminates.
*
* @author acaproni
*
*/
public class ACSAlarmSystemInterfaceFactory {
/**
* The path in the CDB of the AS configuration
*/
private static final String CONFIGURATION_PATH="Alarms/Administrative/AlarmSystemConfiguration";
/**
* It is <code>true</code> if ACS implementation for sources must be used;
* <code>false</code> means CERN implementation.
* <P>
* It is <code>null</code> if it has not yet been initialized.
*/
private static Boolean useACSAlarmSystem = null;
/**
* At the present, while using the CERN implementation,
* we use the same source for sending all the alarms
* so we can use a singleton and return the same object
* to all the clients.
*
* @see AlarmSystemInterfaceProxy
*/
private static ACSAlarmSystemInterface source=null;
/**
* The logger
*/
private static Logger logger=null;
/**
* Container services
*/
private static ContainerServicesBase containerServices;
/**
* The factory to keep track of all the existing {@link AlarmSource} objects.
*/
private static AlarmSourceFactory alarmSourceFactory;
/**
* Init the static variables of the class
* This method has to be called before executing any other
* method.
*
* @param logger The logger
* @param dal The DAL to init the AS with CERN or ACS implementation
* @throws AcsJContainerServicesEx
*/
public static void init(ContainerServicesBase containerServices) throws AcsJContainerServicesEx {
if (containerServices==null) {
throw new AcsJContainerServicesEx(new Exception("Invalid null ContainerServicesBase"));
}
ACSAlarmSystemInterfaceFactory.containerServices=containerServices;
ACSAlarmSystemInterfaceFactory.logger = containerServices.getLogger();
DAL dal = containerServices.getCDB();
if (logger==null || dal==null) {
throw new IllegalArgumentException("Invalid DAL or Logger from ContainerServicesBase");
}
alarmSourceFactory = new AlarmSourceFactory(containerServices);
useACSAlarmSystem = retrieveImplementationType(dal);
if (logger!=null) {
if (useACSAlarmSystem) {
logger.log(AcsLogLevel.DEBUG,"Alarm system type: ACS");
} else {
logger.log(AcsLogLevel.DEBUG,"Alarm system type: CERN");
try {
initCmwMom();
} catch (Throwable t) {
throw new AcsJContainerServicesEx(new Exception("Error initing cmw-mom",t));
}
}
}
}
/**
* Initialize cmw-mom (that in turn inits acs-jms) by setting the container services.
*
* The container services must be set in a cmw-mom static variable in order the
* messages are published into the NC.
* The container services will be passed from this class down to the acs-jms classes.
*
* @see cern.cmw.mom.pubsub.impl.ACSJMSTopicConnectionImpl
*/
private static void initCmwMom() throws Exception {
if (containerServices==null) {
throw new IllegalStateException("Trying to init cmw-mom with null ContainerServicesBase");
}
try {
Thread t = Thread.currentThread();
ClassLoader loader = t.getContextClassLoader();
Class cl =loader.loadClass("cern.cmw.mom.pubsub.impl.ACSJMSTopicConnectionImpl");
Field var = cl.getField("containerServices");
var.set(null, containerServices);
logger.log(AcsLogLevel.DEBUG,"cmw-mom/acs-jms initialized");
} catch (Throwable t) {
throw new Exception("Error setting ContainerServices into cmw-mom",t);
}
}
/**
* Cleanup the class.
* This method has to be called outside of the class and performs all the necessary
* clean up
*
*/
public static void done() {
if (containerServices==null) {
throw new IllegalStateException("Trying close with null ContainerServicesBase");
}
alarmSourceFactory.tearDown();
if (useACSAlarmSystem!=null) {
if (source!=null) {
source.close();
source=null;
}
useACSAlarmSystem=null;
}
}
/**
* Read the Implementation property from the Alarm System Configuration
*
* @param dal The DAL
*
* @return false if the Implementation property is CERN
* true otherwise
*/
private static boolean retrieveImplementationType(DAL dal) {
if (dal==null) {
return true;
}
String dao;
try {
dao = dal.get_DAO(CONFIGURATION_PATH);
} catch (Exception e) {
return true;
}
String implementation = getProperty(dao,"Implementation");
return implementation==null || !implementation.equals("CERN");
}
/**
* Get the value of a property from the DAO.
*
* @param dao The dao (XML string)
* @param propName The name of the property
* @return The value of the property with the given name
* null if the property doesn't exist
*/
private static String getProperty(String dao, String propName) {
if (dao==null || propName==null) {
return null;
}
DocumentBuilder builder = null;
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
try {
builder = factory.newDocumentBuilder();
} catch (Exception e) {
System.out.println("Error instantiating the document builder");
System.out.println(e.getMessage());
e.printStackTrace();
return null;
}
Document doc;
try {
doc = builder.parse(new InputSource(new StringReader(dao)));
} catch (Exception e) {
System.out.println("Error parsing the DAO: ["+dao+"]");
System.out.println(e.getMessage());
e.printStackTrace();
return null;
}
NodeList propNodeList = doc.getElementsByTagName("configuration-property");
String val = null; // The value of the property to return
for (int t=0; t<propNodeList.getLength(); t++) {
Node node = propNodeList.item(t);
// Get the attributes
NamedNodeMap attributes = node.getAttributes();
if (attributes==null) {
continue;
}
// Get the attribute "name"
Node attrNodeName=attributes.getNamedItem("name");
if (attrNodeName==null) {
continue;
}
String nameNode = attrNodeName.getNodeValue();
if (nameNode==null) {
continue;
}
if (nameNode.equals(propName)) {
// We have found the property!
Node child = node.getFirstChild();
val = child.getNodeValue();
break;
}
}
return val;
}
/**
* Create a new instance of an alarm system interface.
* @param sourceName the source name.
* @return the interface instance.
* @throws ASIException if the AlarmSystemInterface instance can not be created.
*/
public synchronized static ACSAlarmSystemInterface createSource(String sourceName) throws ACSASFactoryNotInitedEx, SourceCreationErrorEx {
if (useACSAlarmSystem==null) {
Exception e = new IllegalStateException("Factory not initialised");
throw new AcsJACSASFactoryNotInitedEx(e).toACSASFactoryNotInitedEx();
}
if (useACSAlarmSystem) {
return new ACSAlarmSystemInterfaceProxy(sourceName,logger);
} else {
if (source!=null) {
return source;
}
try {
Thread t = Thread.currentThread();
ClassLoader loader = t.getContextClassLoader();
Class cl =loader.loadClass("alma.acs.alarmsystem.binding.ACSLaserSource");
Class[] classes = {String.class , Logger.class };
Constructor constructor = cl.getConstructor(classes);
// TODO: take ContainerServicesBase object received in the init method,
// and set it on ACSJMSTopicConnectionImpl.containerServices
// Of course in after the ACS 7.0 release rush, this static field communication
// must be replaced with with real calls, or at least the absence of such an object must be detected early.
source=(ACSAlarmSystemInterface)constructor.newInstance(sourceName,logger);
return source;
} catch (Throwable t) {
System.out.println("ERROR: "+t.getMessage());
t.printStackTrace();
throw new AcsJSourceCreationErrorEx(t).toSourceCreationErrorEx();
}
}
}
/**
* Create a new instance of an alarm system interface without binding it to any source.
* @return the interface instance.
* @throws ASIException if the AlarmSystemInterface instance can not be created.
*/
public static ACSAlarmSystemInterface createSource() throws ACSASFactoryNotInitedEx, SourceCreationErrorEx {
return createSource("UNDEFINED");
}
/**
* Factory method for creating ACSFaultState instances.
* @return a new ACSFaultState instance.
*
*/
public synchronized static ACSFaultState createFaultState() throws ACSASFactoryNotInitedEx, FaultStateCreationErrorEx{
if (useACSAlarmSystem==null) {
Exception e = new IllegalStateException("Factory not initialised");
throw new AcsJACSASFactoryNotInitedEx(e).toACSASFactoryNotInitedEx();
}
if (useACSAlarmSystem) {
return new ACSFaultStateImpl();
} else {
try {
Thread t = Thread.currentThread();
ClassLoader loader = t.getContextClassLoader();
Class cl =loader.loadClass("alma.acs.alarmsystem.binding.ACSLaserFaultStateImpl");
Class[] classes = {};
Constructor constructor = cl.getConstructor(classes);
return (ACSFaultState)constructor.newInstance();
} catch (Exception e) {
throw new AcsJFaultStateCreationErrorEx(e).toFaultStateCreationErrorEx();
}
}
}
/**
* Factory method for creating ACSFaultState instances.
* @return a new ACSFaultState instance.
* @param family the fault family.
* @param member the fault member.
* @param code the fault code.
*/
public static ACSFaultState createFaultState(String family, String member, int code) throws ACSASFactoryNotInitedEx, FaultStateCreationErrorEx {
ACSFaultState state = createFaultState();
state.setFamily(family);
state.setMember(member);
state.setCode(code);
return state;
}
/**
* Return the type of AS used
*
* @return True if ACS AS is used, false otherwise
* @throws IllegalStateException If the factory has not been initialized yet
*/
public static boolean usingACSAlarmSystem() throws ACSASFactoryNotInitedEx {
if (useACSAlarmSystem==null) {
Exception e = new IllegalStateException("Factory not initialised");
throw new AcsJACSASFactoryNotInitedEx(e).toACSASFactoryNotInitedEx();
}
return ACSAlarmSystemInterfaceFactory.useACSAlarmSystem;
}
/**
* Return the AlarmSource for the container whose name is retrieved from the passed
* {@link ContainerServicesBase} by delegating to the {@link AlarmSourceFactory}.
*
* @param contSvcs The ContainerServices
* @return the AlarmSource
*/
public static AlarmSource getAlarmSource(ContainerServicesBase contSvcs) {
return alarmSourceFactory.getAlarmSource(contSvcs);
}
/**
* Release the {@link AlarmSource} object of the component whose name is read from
* passed the {@link ContainerServicesBase} by delegating to the {@link AlarmSourceFactory}.
*
* @param containerServices The ContainerServices
*/
public static void releaseAlarmSource(ContainerServicesBase contSvcs) {
alarmSourceFactory.releaseAlarmSource(contSvcs);
}
}