/* * * * RHQ Management Platform * * Copyright (C) 2005-2012 Red Hat, Inc. * * All rights reserved. * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License, version 2, as * * published by the Free Software Foundation, and/or the GNU Lesser * * General Public License, version 2.1, also 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 and the GNU Lesser General Public License * * for more details. * * * * You should have received a copy of the GNU General Public License * * and the GNU Lesser General Public License along with this program; * * if not, write to the Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ package org.rhq.cassandra; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.util.Map; import java.util.Properties; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.rhq.core.util.PropertiesFileUpdate; import org.rhq.core.util.StringUtil; import org.rhq.core.util.TokenReplacingReader; import org.rhq.core.util.ZipUtil; import org.rhq.core.util.stream.StreamUtil; /** * Deployment consists of a few steps. * * <ol> * <li>Unzip Cassandra to disk.</li> * <li>Update configuration files like casssandra.yaml. This involves performing variable substitution.</li> * <li>Update file permissions to make scripts in the bin directory executable.</li> * </ol> * * The values used in the variable substitution are supplied by an instance of {@link DeploymentOptions}. * * @author John Sanda */ public class Deployer { private final Log log = LogFactory.getLog(Deployer.class); private DeploymentOptions deploymentOptions; public void setDeploymentOptions(DeploymentOptions deploymentOptions) { this.deploymentOptions = deploymentOptions; } public void unzipDistro() throws DeploymentException { InputStream inputStream = getClass().getResourceAsStream("/cassandra.zip"); File deployDir = new File(deploymentOptions.getBasedir()); deployDir.mkdir(); try { log.info("Unzipping storage node to " + deployDir); ZipUtil.unzipFile(inputStream, deployDir); } catch (IOException e) { log.error("An error occurred while unzipping the storage zip file", e); throw new DeploymentException("An error occurred while unzipping the storage zip file", e); } } public void applyConfigChanges() throws DeploymentException { File deployDir = new File(deploymentOptions.getBasedir()); File confDir = new File(deployDir, "conf"); Map<String, String> tokens = deploymentOptions.toMap(); tokens.put("cluster.name", "rhq"); applyConfigChanges(confDir, "cassandra.yaml", tokens); applyConfigChanges(confDir, "log4j-server.properties", tokens); applyChangesToCassandraJvmProps(confDir, deploymentOptions); // For windows, update the service wrapper env. It may not ne necessary to have updated cassandra-jvm.properties // as well as this file, but for now we'll update both, leaving the former as a dependably set file. if (File.separatorChar == '\\') { applyChangesToWindowsServiceWrapper(deployDir); } // applyConfigChanges(confDir, "cassandra-env.sh", tokens); } private void applyConfigChanges(File confDir, String fileName, Map<String, String> tokens) throws DeploymentException { File filteredFile = new File(confDir, fileName); try { if (log.isInfoEnabled()) { log.info("Applying configuration changes to " + filteredFile); } File rhqFile = new File(confDir, "rhq." + fileName); TokenReplacingReader reader = new TokenReplacingReader(new FileReader(rhqFile), tokens); StreamUtil.copy(reader, new FileWriter(filteredFile)); rhqFile.delete(); } catch (IOException e) { log.error("An unexpected error occurred while apply configuration changes to " + filteredFile, e); throw new DeploymentException("An unexpected error occurred while apply configuration changes to " + filteredFile, e); } } private void applyChangesToCassandraJvmProps(File confDir, DeploymentOptions deploymentOptions) throws DeploymentException { File jvmPropsFile = new File(confDir, "cassandra-jvm.properties"); try { log.info("Applying configuration changes to " + jvmPropsFile); PropertiesFileUpdate propertiesUpdater = new PropertiesFileUpdate(jvmPropsFile.getAbsolutePath()); Properties properties = propertiesUpdater.loadExistingProperties(); properties.setProperty("heap_min", "-Xms" + deploymentOptions.getHeapSize()); properties.setProperty("heap_max", "-Xmx" + deploymentOptions.getHeapSize()); properties.setProperty("heap_new", "-Xmn" + deploymentOptions.getHeapNewSize()); properties.setProperty("thread_stack_size", "-Xss" + deploymentOptions.getStackSize()); properties.setProperty("jmx_port", deploymentOptions.getJmxPort().toString()); String javaVersion = System.getProperty("java.version"); // The check here is taken right from cassandra-env.sh if ((!isOpenJDK() || javaVersion.compareTo("1.6.0") > 0) || (javaVersion.equals("1.6.0") && getJavaPatchVersion() > 23)) { properties.put("java_agent", "-javaagent:$CASSANDRA_HOME/lib/jamm-0.2.5.jar"); } propertiesUpdater.update(properties); } catch (IOException e) { log.error("An error occurred while updating " + jvmPropsFile, e); throw new DeploymentException("An error occurred while updating " + jvmPropsFile, e); } } private boolean isOpenJDK() { String javaVMName = System.getProperty("java.vm.name"); return javaVMName.startsWith("OpenJDK"); } private boolean isJava1_6() { String javaVersion = System.getProperty("java.version"); return javaVersion.startsWith("1.6.0"); } private int getJavaPatchVersion() { String javaVersion = System.getProperty("java.version"); int startIndex = javaVersion.indexOf('_'); if (startIndex == -1) { return 0; } return Integer.parseInt(javaVersion.substring(startIndex + 1, javaVersion.length())); } public void applyChangesToWindowsServiceWrapper(File deployDir) throws DeploymentException { File wrapperDir = new File(deployDir, "../bin/wrapper"); File wrapperEnvFile = new File(wrapperDir, "rhq-storage-wrapper.env"); try { log.info("Applying configuration changes to " + wrapperEnvFile); PropertiesFileUpdate propertiesUpdater = new PropertiesFileUpdate(wrapperEnvFile.getAbsolutePath()); Properties wrapperEnvProps = propertiesUpdater.loadExistingProperties(); wrapperEnvProps.setProperty("set.heap_min", "-Xms" + deploymentOptions.getHeapSize()); wrapperEnvProps.setProperty("set.heap_max", "-Xmx" + deploymentOptions.getHeapSize()); wrapperEnvProps.setProperty("set.heap_new", "-Xmn" + deploymentOptions.getHeapNewSize()); wrapperEnvProps.setProperty("set.thread_stack_size", "-Xss" + deploymentOptions.getStackSize()); wrapperEnvProps.setProperty("set.jmx_port", deploymentOptions.getJmxPort().toString()); // This is always on by default, just set it literally wrapperEnvProps.setProperty("set.heap_dump_on_OOMError", "-XX:+HeapDumpOnOutOfMemoryError"); // This is always set to the bin directory initially wrapperEnvProps.setProperty("set.heap_dump_dir", "-XX:HeapDumpPath=" + useForwardSlash(new File(deployDir, "bin").getAbsolutePath())); propertiesUpdater.update(wrapperEnvProps); } catch (IOException e) { log.error("An error occurred while updating " + wrapperEnvFile, e); throw new DeploymentException("An error occurred while updating " + wrapperEnvFile, e); } } /** * Ensure that the path uses only forward slash. * @param path * @return forward-slashed path, or null if path is null */ private static String useForwardSlash(String path) { return (null != path) ? path.replace('\\', '/') : null; } public void updateFilePerms() { File deployDir = new File(deploymentOptions.getBasedir()); File binDir = new File(deployDir, "bin"); log.info("Updating file permissions in " + binDir); for (File f : binDir.listFiles()) { f.setExecutable(true); } } public void updateStorageAuthConf(Set<String> addresses) { File confDir = new File(deploymentOptions.getBasedir(), "conf"); File authFile = new File(confDir, "rhq-storage-auth.conf"); try { authFile.delete(); StreamUtil.copy(new StringReader(StringUtil.collectionToString(addresses, "\n")), new FileWriter(authFile), true); } catch (IOException e) { throw new RuntimeException("Failed to update " + authFile); } } }