/** * NOTE: This copyright does *not* cover user programs that use HQ * program services by normal system calls through the application * program interfaces provided as part of the Hyperic Plug-in Development * Kit or the Hyperic Client Development Kit - this is merely considered * normal use of the program, and does *not* fall under the heading of * "derived work". * * Copyright (C) [2010], VMware, Inc. * This file is part of HQ. * * HQ is free software; you can redistribute it and/or modify * it under the terms version 2 of the GNU General Public License as * published by the Free Software Foundation. This program 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 General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA. * */ package org.hyperic.tools.ant; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashSet; import java.util.Properties; import java.util.Set; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import org.jasypt.encryption.pbe.StandardPBEStringEncryptor; import org.jasypt.properties.PropertyValueEncryptionUtils; import org.springframework.util.StringUtils; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Ant task responsible for applying any necessary transformations to an * existing hq-server.conf file during a server upgrade * @author jhickey * */ public class ServerConfigUpgrader extends Task { private String existingConfigFile; private String newConfigFile; private String upgradeDir; /** * Mail server config properties to grab from the old * jboss-service-events.xml file and add to hq-server.conf */ private static final Set<String> UPGRADE_MAIL_PROPERTIES = new HashSet<String>(); static { // server.mail.host is already in hq-server.conf. Grab anything else // that may have been configured for mail UPGRADE_MAIL_PROPERTIES.add("mail.smtp.auth"); UPGRADE_MAIL_PROPERTIES.add("mail.smtp.port"); UPGRADE_MAIL_PROPERTIES.add("mail.smtp.starttls.enable"); UPGRADE_MAIL_PROPERTIES.add("mail.smtp.socketFactory.port"); UPGRADE_MAIL_PROPERTIES.add("mail.smtp.socketFactory.fallback"); UPGRADE_MAIL_PROPERTIES.add("mail.smtp.socketFactory.class"); } /** * hq-server.conf properties no longer used. While not harmful to leave them * in, we take them out to avoid clutter */ private static final Set<String> UNUSED_PROPS = new HashSet<String>(); static { UNUSED_PROPS.add("hq-engine.jnp.port"); UNUSED_PROPS.add("hq-engine.server.port"); } @Override public void execute() throws BuildException { Properties config = upgradeServerConfig(); parseMailConfig(config); exportConfig(config); } void exportConfig(Properties config) { FileOutputStream fo = null; try { fo = new FileOutputStream(newConfigFile); config.store(fo, null); } catch (IOException e) { throw new BuildException("Error storing server config to " + newConfigFile, e); } finally { if (fo != null) { try { fo.close(); } catch (IOException e) { } } } } void parseMailConfig(Properties existingConfig) { FileInputStream mailConfigInputStream = null; File mailServiceConfig = new File(upgradeDir + "/conf/templates/jboss-service-events.xml"); try { mailConfigInputStream = new FileInputStream(mailServiceConfig); } catch (FileNotFoundException e) { // if we are upgrading a server after 4.3, the JBoss MailService // config will not be present return; } log("Parsing jboss mail service configuration=" + mailServiceConfig.getAbsolutePath()); try { parseMailConfig(mailConfigInputStream, existingConfig); } finally { if (mailConfigInputStream != null) { try { mailConfigInputStream.close(); } catch (IOException e) { } } } } void parseMailConfig(InputStream mailConfigInputStream, Properties serverConfig) { Document mailDocument = loadDocument(mailConfigInputStream); NodeList mbeans = mailDocument.getElementsByTagName("mbean"); for (int i = 0; i < mbeans.getLength(); i++) { Node mbean = mbeans.item(i); String name = mbean.getAttributes().getNamedItem("name").getNodeValue(); if ("jboss:service=SpiderMail".equals(name)) { NodeList mbeanElements = mbean.getChildNodes(); for (int j = 0; j < mbeanElements.getLength(); j++) { Node mbeanElement = mbeanElements.item(j); if (mbeanElement.getAttributes() == null) { continue; } String childName = mbeanElement.getAttributes().getNamedItem("name") .getNodeValue(); if ("User".equals(childName)) { String username = mbeanElement.getFirstChild().getNodeValue(); serverConfig.put("mail.user", username); } else if ("Password".equals(childName)) { String password = mbeanElement.getFirstChild().getNodeValue(); serverConfig.put("mail.password", password); } else if ("Configuration".equals(childName)) { parseMailConfigurationNode(mbeanElement, serverConfig); } } } } } private void parseMailConfigurationNode(Node mbeanElement, Properties serverConfig) { NodeList childNodes = mbeanElement.getChildNodes(); for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); if ("configuration".equals(childNode.getNodeName())) { NodeList configNodes = childNode.getChildNodes(); for (int k = 0; k < configNodes.getLength(); k++) { Node configNode = configNodes.item(k); if ("property".equals(configNode.getNodeName())) { String propName = configNode.getAttributes().getNamedItem("name") .getNodeValue(); String propValue = configNode.getAttributes().getNamedItem("value") .getNodeValue(); if (UPGRADE_MAIL_PROPERTIES.contains(propName)) { serverConfig.put(propName, propValue); } } } } } } private Document loadDocument(InputStream input) throws BuildException { try { DocumentBuilder dom = DocumentBuilderFactory.newInstance().newDocumentBuilder(); return dom.parse(input); } catch (Exception e) { throw new BuildException("Error parsing document", e); } } Properties upgradeServerConfig(InputStream confFile) throws IOException { Properties serverProps = new Properties(); serverProps.load(confFile); removeOldProps(serverProps); addNewProps(serverProps); return serverProps; } private void removeOldProps(Properties serverProps) { // remove props no longer needed for (String unusedProp : UNUSED_PROPS) { if (serverProps.containsKey(unusedProp)) { serverProps.remove(unusedProp); } } } private void addNewProps(Properties serverProps) { // Add new properties that we've placed in hq-server.conf // Add protocol version to upgraded servers that use the embedded // database String jdbcUrl = serverProps.getProperty("server.database-url"); if (jdbcUrl.startsWith("jdbc:postgresql:") && !jdbcUrl.endsWith("?protocolVersion=2")) { serverProps.setProperty("server.database-url", jdbcUrl + "?protocolVersion=2"); } String dbProp = serverProps.getProperty("server.database"); if (dbProp == null) { // this shouldn't happen, but return to avoid NPEs if it does return; } // Add new DB connection validation sql if (serverProps.getProperty("server.connection-validation-sql") == null) { String validationSQL = "select 1"; if (dbProp.startsWith("Oracle")) { validationSQL += " from dual"; } serverProps.setProperty("server.connection-validation-sql", validationSQL); } // Add new hibernate dialect property if (serverProps.getProperty("server.hibernate.dialect") == null) { if (dbProp.startsWith("Oracle")) { serverProps.setProperty("server.hibernate.dialect", "org.hyperic.hibernate.dialect.Oracle9Dialect"); } else if (dbProp.equals("MySQL")) { serverProps.setProperty("server.hibernate.dialect", "org.hyperic.hibernate.dialect.MySQL5InnoDBDialect"); } else { serverProps.setProperty("server.hibernate.dialect", "org.hyperic.hibernate.dialect.PostgreSQLDialect"); } } if (serverProps.getProperty("server.encryption-key") == null) { serverProps.setProperty("server.encryption-key", "defaultkey"); serverProps.setProperty("server.database-password", encryptPassword("defaultkey", serverProps.getProperty("server.database-password"))); } // Add new SSL properties for upgrade if (!StringUtils.hasText(serverProps.getProperty("accept.unverified.certificates"))) { serverProps.setProperty("accept.unverified.certificates", "true"); } if (!StringUtils.hasText(serverProps.getProperty("server.keystore.path"))) { serverProps.setProperty("server.keystore.path", "../../conf/hyperic.keystore"); } if (!StringUtils.hasText(serverProps.getProperty("server.keystore.password"))) { serverProps.setProperty("server.keystore.password", "hyperic"); } if (!StringUtils.hasText(serverProps.getProperty("tomcat.maxthreads"))) { serverProps.setProperty("tomcat.maxthreads", "500"); } if (!StringUtils.hasText(serverProps.getProperty("tomcat.minsparethreads"))) { serverProps.setProperty("tomcat.minsparethreads", "50"); } if (!StringUtils.hasText(serverProps.getProperty("server.jms.usejmx"))) { serverProps.setProperty("server.jms.usejmx", "false"); } if (!StringUtils.hasText(serverProps.getProperty("server.jms.jmxport"))) { serverProps.setProperty("server.jms.jmxport", "1099"); } } private String encryptPassword(String encryptionKey, String clearTextPassword) { // TODO: This needs to be refactored into a security utility class StandardPBEStringEncryptor encryptor = new StandardPBEStringEncryptor(); encryptor.setPassword(encryptionKey); encryptor.setAlgorithm("PBEWithMD5AndDES"); return PropertyValueEncryptionUtils.encrypt(clearTextPassword, encryptor); } Properties upgradeServerConfig() { Properties config; FileInputStream confFileInputStream = null; try { confFileInputStream = new FileInputStream(new File(existingConfigFile)); config = upgradeServerConfig(confFileInputStream); } catch (IOException e) { throw new BuildException("Error loading existing config from " + existingConfigFile, e); } finally { if (confFileInputStream != null) { try { confFileInputStream.close(); } catch (IOException e) { } } } return config; } public void setExisting(String existingConfigFile) { this.existingConfigFile = existingConfigFile; } public void setNew(String newConfigFile) { this.newConfigFile = newConfigFile; } public void setUpgradeDir(String upgradeDir) { this.upgradeDir = upgradeDir; } }