package org.epics.archiverappliance.config;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import javax.servlet.ServletContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.log4j.Logger;
import org.epics.archiverappliance.config.exception.ConfigException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Parses the appliances.xml file and loads the appliances
* @author mshankar
*
*/
public class AppliancesList {
private static Logger logger = Logger.getLogger(AppliancesList.class.getName());
/**
* Parses the appliances.xml file and loads the appliances into the specified appliancesList
* @param servletContext ServletContext
* @return appliancesList
* @throws IOException
* @throws ConfigException
*/
public static HashMap<String, ApplianceInfo> loadAppliancesXML(ServletContext servletContext) throws IOException, ConfigException {
HashMap<String, ApplianceInfo> appliancesList = new HashMap<String, ApplianceInfo>();
try(InputStream appliancesXMLInputStream = determineApplianceXMLFileAndReturnStream(servletContext)) {
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
Document doc = docBuilder.parse (appliancesXMLInputStream);
NodeList applianceList = doc.getElementsByTagName("appliance");
int totalAppliances = applianceList.getLength();
logger.debug("Found " + totalAppliances + " appliances in appliances.xml");
HashSet<String> allInetPorts = new HashSet<String>();
for(int i = 0; i < totalAppliances; i++) {
Node applianceNode = applianceList.item(i);
String identity = getChildNodeTextContent(applianceNode, "identity", i);
String cluster_inetport = getChildNodeTextContent(applianceNode, "cluster_inetport", i);
String mgmt_url = getChildNodeTextContent(applianceNode, "mgmt_url", i);
String engine_url = getChildNodeTextContent(applianceNode, "engine_url", i);
String etl_url = getChildNodeTextContent(applianceNode, "etl_url", i);
String retrieval_url = getChildNodeTextContent(applianceNode, "retrieval_url", i);
String data_retrieval_url = getChildNodeTextContent(applianceNode, "data_retrieval_url", i);
ApplianceInfo applianceInfo = new ApplianceInfo(identity, mgmt_url, engine_url, retrieval_url, etl_url, cluster_inetport, data_retrieval_url);
if(appliancesList.containsKey(identity)) {
String msg = "We have more than one appliance with identity " + identity + ". This is probably a cut and paste typo; please fix this.";
logger.fatal(msg);
throw new ConfigException(msg);
}
appliancesList.put(identity, applianceInfo);
if(!cluster_inetport.startsWith("localhost")) {
if(allInetPorts.contains(cluster_inetport)) {
String msg = "When adding appliance with identity " + identity + ", we already have another appliance with the same cluster_inetport " + cluster_inetport + ". This is probably a cut and paste typo.";
logger.fatal(msg);
throw new ConfigException(msg);
} else {
allInetPorts.add(cluster_inetport);
}
}
}
} catch(Exception ex) {
throw new IOException("Exception parsing appliance.xml", ex);
}
return appliancesList;
}
private static InputStream determineApplianceXMLFileAndReturnStream(ServletContext servletContext) throws IOException, FileNotFoundException {
String applianceFileFromEnvVar = System.getenv(ConfigService.ARCHAPPL_APPLIANCES);
if(applianceFileFromEnvVar == null || applianceFileFromEnvVar.equals("")) {
applianceFileFromEnvVar = System.getProperty(ConfigService.ARCHAPPL_APPLIANCES);
}
if(applianceFileFromEnvVar != null) {
logger.info("appliances.xml file specified in the environment as " + applianceFileFromEnvVar);
File appliancesXMLFile = new File(applianceFileFromEnvVar);
if(!appliancesXMLFile.exists()) {
String msg = "Specified appliances.xml file " + applianceFileFromEnvVar + " does not seem to exist. This is a fatal error; cannot continue";
logger.fatal(msg);
throw new IOException(msg);
}
return new FileInputStream(appliancesXMLFile);
} else {
logger.info("Environment variable " + ConfigService.ARCHAPPL_APPLIANCES + " not specified. Using appliances.xml as found in classpath");
InputStream appliancesXMLInputStream = servletContext.getResourceAsStream("/WEB-INF/classes/appliances.xml");
if(appliancesXMLInputStream == null) {
String msg = "Unable to find appliances.xml file " + servletContext.getRealPath("/WEB-INF/classes/appliances.xml") + " in the servlet classpath. Please copy into WEB-INF/classes/appliances.xml. This is a fatal error; cannot continue";
logger.fatal(msg);
throw new IOException(msg);
}
return appliancesXMLInputStream;
}
}
/**
* Gets the test content of the specified element.
* Not super efficient, but serves the purpose for this usecase.
* @param node
* @param elementName The name of the specified element
* @param applianceNum The number of appliances
* @return ChildNodeTextContent
* @throws IOException
*/
private static String getChildNodeTextContent(Node node, String elementName, int applianceNum) throws IOException {
NodeList childNodes = node.getChildNodes();
for(int i = 0; i < childNodes.getLength(); i++) {
Node childNode = childNodes.item(i);
if(childNode.getNodeName().equals(elementName)) {
return childNode.getTextContent();
}
}
throw new IOException("Cannot determine " + elementName + " for appliance " + applianceNum + " in appliances.xml ");
}
}