Java tutorial
/* * RHQ Management Platform * Copyright (C) 2005-2008 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 as published by * the Free Software Foundation version 2 of the License. * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ package org.rhq.enterprise.installer; import java.lang.reflect.Method; import java.net.InetAddress; import java.sql.Connection; import java.sql.Statement; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.faces.context.FacesContext; import javax.faces.model.SelectItem; import mazz.i18n.Msg; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jboss.resource.security.SecureIdentityLoginModule; import org.rhq.core.db.DatabaseTypeFactory; import org.rhq.core.util.exception.ThrowableUtil; import org.rhq.enterprise.installer.i18n.InstallerI18NResourceKeys; /** * Responsible for taking the settings the user selects in the installer window and saves them * as the server's initial configuration. * * @author John Mazzitelli * @author Jay Shaughnessy */ public class ConfigurationBean { private static final Log LOG = LogFactory.getLog(ConfigurationBean.class); private static Set<String> JON_SUPPORTED_DATABASE_TYPES = new HashSet<String>(2); static { JON_SUPPORTED_DATABASE_TYPES.add("PostgreSQL"); JON_SUPPORTED_DATABASE_TYPES.add("Oracle"); } private enum ExistingSchemaOption { OVERWRITE, KEEP, SKIP }; private Msg I18Nmsg; private List<SelectItem> existingSchemaOptions; private ServerInformation serverInfo; private Boolean showAdvancedSettings; private List<PropertyItemWithValue> configuration; private String lastError; private String lastTest; private String lastCreate; private String existingSchemaOption; private String adminConnectionUrl; private String adminUsername; private String adminPassword; private ServerInformation.Server haServer; private String haServerName; private String selectedRegisteredServerName; public ConfigurationBean() { FacesContext currentInstance = FacesContext.getCurrentInstance(); if (currentInstance != null) { I18Nmsg = new Msg(InstallerI18NResourceKeys.BUNDLE_BASE_NAME, currentInstance.getViewRoot().getLocale()); } else { I18Nmsg = new Msg(InstallerI18NResourceKeys.BUNDLE_BASE_NAME, Locale.getDefault()); } serverInfo = new ServerInformation(); showAdvancedSettings = Boolean.FALSE; existingSchemaOption = ExistingSchemaOption.KEEP.name(); // this will initialize 'configuration' initConfiguration(); if (!isAutoinstallEnabled()) { setHaServerName(getDefaultServerName()); } } private String getDefaultServerName() { String defaultServerName = ""; try { defaultServerName = InetAddress.getLocalHost().getCanonicalHostName(); } catch (Exception e) { LOG.info("Could not determine default server name: ", e); } return defaultServerName; } /** * Loads in the server's current configuration and returns all the settings. * This only returns the property if it is a basic setting or the UI is showing advanced, too. * * @return the requested server setting */ public PropertyItemWithValue getConfigurationProperty(String propertyName) { return getConfigurationProperty(getConfiguration(), propertyName); } /** * Loads in the server's current configuration and returns all the settings. * This returns any property, basic or advanced, no matter what the "show advanced" setting is. * * @return the requested server setting */ private PropertyItemWithValue getConfigurationPropertyFromAll(String propertyName) { return getConfigurationProperty(this.configuration, propertyName); } private PropertyItemWithValue getConfigurationProperty(List<PropertyItemWithValue> config, String propertyName) { PropertyItemWithValue result = null; for (PropertyItemWithValue item : config) { if (item.getItemDefinition().getPropertyName().equalsIgnoreCase(propertyName)) { result = item; break; } } return result; } public void initConfiguration() { if (configuration == null) { Properties properties = serverInfo.getServerProperties(); List<PropertyItem> itemDefs = new ServerProperties().getPropertyItems(); if ((serverInfo.getProduct() == ServerInformation.Product.JON) && !serverInfo.isUnsupportedJonFeaturesEnabled()) { LOG.debug("Hiding the embedded agent props and the unsupported DB types..."); for (PropertyItem itemDef : itemDefs) { if (itemDef.getPropertyName().startsWith(ServerProperties.PREFIX_PROP_EMBEDDED_AGENT)) { itemDef.setHidden(true); } if (itemDef.getPropertyName().equals(ServerProperties.PROP_DATABASE_TYPE)) { List<SelectItem> options = itemDef.getOptions(); for (Iterator<SelectItem> iterator = options.iterator(); iterator.hasNext();) { SelectItem option = iterator.next(); String databaseType = (String) option.getValue(); if (!JON_SUPPORTED_DATABASE_TYPES.contains(databaseType)) { iterator.remove(); } } } } } configuration = new ArrayList<PropertyItemWithValue>(); for (PropertyItem itemDef : itemDefs) { String property = properties.getProperty(itemDef.getPropertyName()); PropertyItemWithValue value = new PropertyItemWithValue(itemDef, property); configuration.add(value); } // force some hardcoded defaults for IBM JVMs that must have specific values boolean isIBM = System.getProperty("java.vendor", "").contains("IBM"); if (isIBM) { String[] algPropNames = { ServerProperties.PROP_TOMCAT_SECURITY_ALGORITHM, ServerProperties.PROP_SECURITY_CLIENT_KEYSTORE_ALGORITHM, ServerProperties.PROP_SECURITY_CLIENT_TRUSTSTORE_ALGORITHM, ServerProperties.PROP_SECURITY_SERVER_KEYSTORE_ALGORITHM, ServerProperties.PROP_SECURITY_SERVER_TRUSTSTORE_ALGORITHM }; for (String algPropName : algPropNames) { PropertyItemWithValue prop = getConfigurationProperty(this.configuration, algPropName); if (prop != null) { prop.setValue("IbmX509"); } } } } return; } /** * Loads in the server's current configuration and returns all the settings. * * @return current server settings */ public List<PropertyItemWithValue> getConfiguration() { // prepare the items to return - only return the basic settings unless showing advanced, too List<PropertyItemWithValue> retConfig; if (showAdvancedSettings.booleanValue()) { retConfig = configuration; } else { retConfig = new ArrayList<PropertyItemWithValue>(); for (PropertyItemWithValue item : configuration) { if (!item.getItemDefinition().isAdvanced()) { retConfig.add(item); } } } return retConfig; } /** * Loads in the server's current configuration and returns all the settings except * database related properties. This additionally strips out hidden properties too, * so the caller will not see any of the hidden properties in the returned list. * * @return current server settings, minus database related settings and hidden settings. * * @see #getDatabaseConfiguration() * @see #getConfiguration() */ public List<PropertyItemWithValue> getNonDatabaseConfiguration() { List<PropertyItemWithValue> allConfig = getConfiguration(); List<PropertyItemWithValue> retConfig = new ArrayList<PropertyItemWithValue>(); for (PropertyItemWithValue item : allConfig) { if (!(item.getItemDefinition().getPropertyName().startsWith(ServerProperties.PREFIX_PROP_DATABASE) || item.getItemDefinition().isHidden())) { retConfig.add(item); } } return retConfig; } /** * Loads in the server's current configuration and returns only the database related properties. * * @return current database settings * * @see #getNonDatabaseConfiguration() * @see #getConfiguration() */ public List<PropertyItemWithValue> getDatabaseConfiguration() { List<PropertyItemWithValue> allConfig = getConfiguration(); List<PropertyItemWithValue> retConfig = new ArrayList<PropertyItemWithValue>(); for (PropertyItemWithValue item : allConfig) { if (item.getItemDefinition().getPropertyName().startsWith(ServerProperties.PREFIX_PROP_DATABASE) && !item.getItemDefinition().isHidden()) { retConfig.add(item); } } return retConfig; } /** * Checks to see if the server has been preconfigured and should be auto-installed. If <code>true</code> * is returned, the installer webapp should not be needed to install the server and the installer should * immediately begin the installation process. * * @return <code>true</code> if auto-install should occur; <code>false</code> means the user needs to use * the installer GUI before the installation can begin */ public boolean isAutoinstallEnabled() { PropertyItemWithValue enableProp = getConfigurationPropertyFromAll( ServerProperties.PROP_AUTOINSTALL_ENABLE); if (enableProp != null) { return Boolean.parseBoolean(enableProp.getValue()); } return false; } public void setConfiguration(List<PropertyItemWithValue> newConfig) { PropertyItemWithValue oldValue; // we can't just store the entire thing because newConfig may be missing items (if not viewing advanced) // just loop through the new config and overwrite the old values with the new values for (PropertyItemWithValue newValue : newConfig) { oldValue = findPropertyItemWithValue(newValue.getItemDefinition().getPropertyName()); oldValue.setValue(newValue.getValue()); } return; } public Boolean isShowAdvancedSettings() { return showAdvancedSettings; } public Boolean getShowAdvancedSettings() { return showAdvancedSettings; } public void setShowAdvancedSettings(Boolean showAdvancedSettings) { if (showAdvancedSettings == null) { showAdvancedSettings = Boolean.FALSE; } this.showAdvancedSettings = showAdvancedSettings; } public String getLastError() { return (lastError != null) ? lastError.replaceAll("'", "\\\\'") : null; } public String getLastTest() { return (lastTest != null) ? lastTest.replaceAll("'", "\\\\'") : null; } public String getLastCreate() { return (lastCreate != null) ? lastCreate.replaceAll("'", "\\\\'") : null; } public String getAdminConnectionUrl() { if (adminConnectionUrl == null) { adminConnectionUrl = getConfigurationAsProperties(configuration) .getProperty(ServerProperties.PROP_DATABASE_CONNECTION_URL); } return adminConnectionUrl; } public void setAdminConnectionUrl(String adminUrl) { this.adminConnectionUrl = adminUrl; } public String getAdminUsername() { if (adminUsername == null) { Properties config = getConfigurationAsProperties(configuration); String dbtype = config.getProperty(ServerProperties.PROP_DATABASE_TYPE); if (dbtype != null) { if (dbtype.toLowerCase().indexOf("oracle") > -1) { adminUsername = "sys"; } else if (dbtype.toLowerCase().indexOf("postgres") > -1) { adminUsername = "postgres"; } else if (dbtype.toLowerCase().indexOf("h2") > -1) { adminUsername = "sa"; } else if (dbtype.toLowerCase().indexOf("sqlserver") > -1) { adminUsername = "sa"; } else if (dbtype.toLowerCase().indexOf("mysql") > -1) { adminUsername = "mysqladmin"; } } } return adminUsername; } public void setAdminUsername(String adminUsername) { this.adminUsername = adminUsername; } public String getAdminPassword() { return adminPassword; } public void setAdminPassword(String adminPassword) { this.adminPassword = adminPassword; } public StartPageResults testConnection() { Properties configurationAsProperties = getConfigurationAsProperties(configuration); // its possible the JDBC URL was changed, clear the factory cache in case the DB version is different now DatabaseTypeFactory.clearDatabaseTypeCache(); try { serverInfo.ensureDatabaseIsSupported(configurationAsProperties); lastTest = "OK"; } catch (Exception e) { LOG.warn("Installer failed to test connection", e); lastTest = e.toString(); } return StartPageResults.STAY; } public StartPageResults showCreateDatabasePage() { adminConnectionUrl = null; adminUsername = null; adminPassword = null; return StartPageResults.CREATEDB; } public StartPageResults createDatabase() { Properties config = getConfigurationAsProperties(configuration); String dbType = config.getProperty(ServerProperties.PROP_DATABASE_TYPE, "-unknown-"); Properties adminConfig = new Properties(); adminConfig.put(ServerProperties.PROP_DATABASE_CONNECTION_URL, adminConnectionUrl); adminConfig.put(ServerProperties.PROP_DATABASE_USERNAME, adminUsername); adminConfig.put(ServerProperties.PROP_DATABASE_PASSWORD, adminPassword); Connection conn = null; Statement stmt = null; // If we successfully add the user/database, we'll change the values in the UI // by modifying the configuration property items that this bean manages. PropertyItemWithValue propertyItemUsername = null; PropertyItemWithValue propertyItemPassword = null; PropertyItemWithValue propertyItemUrl = null; for (PropertyItemWithValue item : configuration) { String propName = item.getItemDefinition().getPropertyName(); if (propName.equals(ServerProperties.PROP_DATABASE_USERNAME)) { propertyItemUsername = item; } else if (propName.equals(ServerProperties.PROP_DATABASE_PASSWORD)) { propertyItemPassword = item; } else if (propName.equals(ServerProperties.PROP_DATABASE_CONNECTION_URL)) { propertyItemUrl = item; } } if (propertyItemUsername == null || propertyItemPassword == null || propertyItemUrl == null) { throw new NullPointerException("Missing a property item - this is a bug please report it"); } LOG.info("Will attempt to create user/database 'rhqadmin' using URL [" + getAdminConnectionUrl() + "] and admin user [" + getAdminUsername() + "]. Admin password was" + (getAdminPassword().length() > 0 ? " not " : " ") + "empty"); try { String sql1, sql2; conn = serverInfo.getDatabaseConnection(adminConfig); conn.setAutoCommit(true); stmt = conn.createStatement(); if (dbType.equalsIgnoreCase("postgresql")) { sql1 = "CREATE ROLE rhqadmin LOGIN ENCRYPTED PASSWORD 'rhqadmin' NOSUPERUSER NOINHERIT CREATEDB NOCREATEROLE"; sql2 = "CREATE DATABASE rhq WITH OWNER = rhqadmin ENCODING = 'SQL_ASCII' TABLESPACE = pg_default"; } else if (dbType.equalsIgnoreCase("oracle10g")) { sql1 = "CREATE USER rhqadmin IDENTIFIED BY rhqadmin"; sql2 = "GRANT connect, resource TO rhqadmin"; } else if (dbType.equalsIgnoreCase("h2")) { // I have no idea if these are correct for H2 - I just copied oracle's sql sql1 = "CREATE USER rhqadmin IDENTIFIED BY rhqadmin"; sql2 = "GRANT connect, resource TO rhqadmin"; } else if (dbType.equalsIgnoreCase("sqlserver")) { // I have no idea if these are correct for sql server - I just copied oracle's sql sql1 = "CREATE USER rhqadmin IDENTIFIED BY rhqadmin"; sql2 = "GRANT connect, resource TO rhqadmin"; } else { throw new Exception("Unknown database type: " + dbType); } stmt.addBatch(sql1); stmt.addBatch(sql2); int[] results = stmt.executeBatch(); if (results[0] == Statement.EXECUTE_FAILED) throw new Exception("Failed to execute: " + sql1); if (results[1] == Statement.EXECUTE_FAILED) throw new Exception("Failed to execute: " + sql2); // success! let's set our properties to the values we just created propertyItemUsername.setValue("rhqadmin"); propertyItemPassword.setValue("rhqadmin"); if (dbType.equalsIgnoreCase("postgresql") || dbType.equalsIgnoreCase("mysql")) { if (!propertyItemUrl.getValue().endsWith("/rhq")) { propertyItemUrl.setValue(propertyItemUrl.getValue() + "/rhq"); } } testConnection(); lastCreate = "OK"; } catch (Exception e) { LOG.warn("Installer failed to create database", e); lastCreate = ThrowableUtil.getAllMessages(e); } finally { adminConnectionUrl = null; adminUsername = null; adminPassword = null; if (stmt != null) try { stmt.close(); } catch (Exception e) { } if (conn != null) try { conn.close(); } catch (Exception e) { } } return StartPageResults.STAY; } public String getDataDirectory() { try { String path = this.serverInfo.getDataDirectory().getCanonicalPath(); path = path.replace('\\', '/'); // in case we are on windows, we still want forward slashes return path; } catch (Exception e) { throw new RuntimeException(e); // this should never happen unless the file system is out of wack } } public boolean isDatabaseSchemaExist() { try { Properties configurationAsProperties = getConfigurationAsProperties(configuration); return serverInfo.isDatabaseSchemaExist(configurationAsProperties); } catch (Exception e) { LOG.info("Could not determine database existence: " + e); return false; } } public String getExistingSchemaOption() { return this.existingSchemaOption; } public void setExistingSchemaOption(String existingSchemaOption) { // this is allowed to be null to support auto-installer this.existingSchemaOption = existingSchemaOption; } public boolean isKeepExistingSchema() { return ExistingSchemaOption.KEEP.name().equals(existingSchemaOption) || ExistingSchemaOption.SKIP.name().equals(existingSchemaOption); } public StartPageResults saveEmbeddedMode() { // embedded mode simply means use our embedded database and the embedded agent - used mainly for demo purposes // set embedded agent enabled to true PropertyItemWithValue prop = getConfigurationPropertyFromAll(ServerProperties.PROP_EMBEDDED_AGENT_ENABLED); prop.setValue(Boolean.TRUE.toString()); List<PropertyItemWithValue> newConfig = new ArrayList<PropertyItemWithValue>(1); newConfig.add(prop); setConfiguration(newConfig); // use the H2 database, which is our embedded DB List<PropertyItemWithValue> dbConfig = getDatabaseConfiguration(); for (PropertyItemWithValue dbProp : dbConfig) { if (dbProp.getItemDefinition().getPropertyName() .equals(ServerProperties.PROP_DATABASE_CONNECTION_URL)) { dbProp.setValue("jdbc:h2:" + getDataDirectory() + "/rhq;MVCC=TRUE;DB_CLOSE_ON_EXIT=FALSE;LOG=2"); } else if (dbProp.getItemDefinition().getPropertyName().equals(ServerProperties.PROP_DATABASE_TYPE)) { dbProp.setValue("H2"); } else if (dbProp.getItemDefinition().getPropertyName() .equals(ServerProperties.PROP_DATABASE_DRIVER_CLASS)) { dbProp.setValue("org.h2.Driver"); } else if (dbProp.getItemDefinition().getPropertyName() .equals(ServerProperties.PROP_DATABASE_XA_DS_CLASS)) { dbProp.setValue("org.h2.jdbcx.JdbcDataSource"); } else if (dbProp.getItemDefinition().getPropertyName() .equals(ServerProperties.PROP_DATABASE_USERNAME)) { dbProp.setValue("rhqadmin"); } else if (dbProp.getItemDefinition().getPropertyName() .equals(ServerProperties.PROP_DATABASE_PASSWORD)) { dbProp.setValue("rhqadmin"); } } setExistingSchemaOption(ExistingSchemaOption.OVERWRITE.name()); return save(); } public StartPageResults save() { LOG.info("Installer raw values: " + configuration); // if auto-install is enabled, the db password will be encrypted - decrypt it internally and we'll re-encrypt later if (isAutoinstallEnabled()) { try { PropertyItemWithValue prop = getConfigurationPropertyFromAll( ServerProperties.PROP_DATABASE_PASSWORD); String pass = prop.getValue(); pass = decodePassword(pass); prop.setValue(pass); // log the unencrypted pw, but only at the trace level so it isn't put in the log file // unless someone explicitly enables the trace level so they can see the pass that is to be used for debugging LOG.trace(">" + pass); } catch (Exception e) { LOG.fatal("Could not decrypt the password for some reason - auto-installation failed", e); lastError = I18Nmsg.getMsg(InstallerI18NResourceKeys.SAVE_ERROR, ThrowableUtil.getAllMessages(e)); return StartPageResults.ERROR; } } // its possible the JDBC URL was changed, clear the factory cache in case the DB version is different now DatabaseTypeFactory.clearDatabaseTypeCache(); try { // update server properties with the latest ha info to keep the form and server properties file up to date getConfigurationPropertyFromAll(ServerProperties.PROP_HIGH_AVAILABILITY_NAME) .setValue(getHaServer().getName()); getConfigurationPropertyFromAll(ServerProperties.PROP_HTTP_PORT) .setValue(getHaServer().getEndpointPortString()); getConfigurationPropertyFromAll(ServerProperties.PROP_HTTPS_PORT) .setValue(getHaServer().getEndpointSecurePortString()); // the comm bind port is a special setting - it is allowed to be blank; // if it was originally blank, it will have been set to 0 because its an integer property; // but we do not want it to be 0, so make sure we switch it back to empty PropertyItemWithValue portConfig = getConfigurationPropertyFromAll( ServerProperties.PROP_CONNECTOR_BIND_PORT); if ("0".equals(portConfig.getValue())) { portConfig.setRawValue(""); } } catch (Exception e) { LOG.fatal("Could not save the settings for some reason", e); lastError = I18Nmsg.getMsg(InstallerI18NResourceKeys.SAVE_ERROR, ThrowableUtil.getAllMessages(e)); return StartPageResults.ERROR; } try { String url = getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_CONNECTION_URL).getValue(); String db = getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_TYPE).getValue(); Pattern pattern = null; if (db.toLowerCase().indexOf("postgres") > -1) { pattern = Pattern.compile(".*://(.*):([0123456789]+)/(.*)"); // jdbc:postgresql://host.name:5432/rhq } else if (db.toLowerCase().indexOf("oracle") > -1) { LOG.info("Oracle does not need to have server-name, port and db-name individually set, skipping"); // if we ever find that we'll need these props set, uncomment below and it should all work //pattern = Pattern.compile(".*@(.*):([0123456789]+):(.*)"); // jdbc:oracle:thin:@host.name:1521:rhq } else if (db.toLowerCase().indexOf("h2") > -1) { LOG.info("H2 does not need to have server-name, port and db-name individually set, skipping"); } else if (db.toLowerCase().indexOf("sqlserver") > -1) { pattern = Pattern.compile("(?i).*://(.*):([0123456789]+).*databaseName=([^;]*)"); // jdbc:jtds:sqlserver://localhost:7777;databaseName=rhq } else { LOG.info("Unknown database type - will not set server-name, port and db-name"); // don't bother throwing error; these three extra settings may not be necessary anyway } if (pattern != null) { Matcher match = pattern.matcher(url); if (match.find() && (match.groupCount() == 3)) { String serverName = match.group(1); String port = match.group(2); String dbName = match.group(3); getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_SERVER_NAME) .setValue(serverName); getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_PORT).setValue(port); getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_DB_NAME).setValue(dbName); } else { throw new Exception("Cannot get server, port or db name from connection URL: " + url); } } else { getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_SERVER_NAME).setValue(""); getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_PORT).setValue(""); getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_DB_NAME).setValue(""); } } catch (Exception e) { LOG.fatal("JDBC connection URL seems to be invalid", e); lastError = I18Nmsg.getMsg(InstallerI18NResourceKeys.SAVE_ERROR, ThrowableUtil.getAllMessages(e)); return StartPageResults.ERROR; } try { String db = getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_TYPE).getValue(); String dialect; String quartzDriverDelegateClass = "org.quartz.impl.jdbcjobstore.StdJDBCDelegate"; String quartzSelectWithLockSQL = "SELECT * FROM {0}LOCKS ROWLOCK WHERE LOCK_NAME = ? FOR UPDATE"; String quartzLockHandlerClass = "org.quartz.impl.jdbcjobstore.StdRowLockSemaphore"; if (db.toLowerCase().indexOf("postgres") > -1) { dialect = "org.hibernate.dialect.PostgreSQLDialect"; quartzDriverDelegateClass = "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"; } else if (db.toLowerCase().indexOf("oracle") > -1) { dialect = "org.hibernate.dialect.Oracle10gDialect"; quartzDriverDelegateClass = "org.quartz.impl.jdbcjobstore.oracle.OracleDelegate"; } else if (db.toLowerCase().indexOf("h2") > -1) { dialect = "org.rhq.core.server.H2CustomDialect"; } else if (db.toLowerCase().indexOf("sqlserver") > -1) { dialect = "org.hibernate.dialect.SQLServerDialect"; quartzDriverDelegateClass = "org.quartz.impl.jdbcjobstore.MSSQLDelegate"; quartzSelectWithLockSQL = "SELECT * FROM {0}LOCKS ROWLOCK WITH (HOLDLOCK,XLOCK) WHERE LOCK_NAME = ?"; quartzLockHandlerClass = "org.quartz.impl.jdbcjobstore.UpdateLockRowSemaphore"; } else if (db.toLowerCase().indexOf("mysql") > -1) { dialect = "org.hibernate.dialect.MySQL5InnoDBDialect"; quartzDriverDelegateClass = "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"; } else { throw new Exception("Unknown db type: " + db); } getConfigurationPropertyFromAll(ServerProperties.PROP_DATABASE_HIBERNATE_DIALECT).setValue(dialect); getConfigurationPropertyFromAll(ServerProperties.PROP_QUARTZ_DRIVER_DELEGATE_CLASS) .setValue(quartzDriverDelegateClass); getConfigurationPropertyFromAll(ServerProperties.PROP_QUARTZ_SELECT_WITH_LOCK_SQL) .setValue(quartzSelectWithLockSQL); getConfigurationPropertyFromAll(ServerProperties.PROP_QUARTZ_LOCK_HANDLER_CLASS) .setValue(quartzLockHandlerClass); } catch (Exception e) { LOG.fatal("Invalid database type", e); lastError = I18Nmsg.getMsg(InstallerI18NResourceKeys.SAVE_ERROR, ThrowableUtil.getAllMessages(e)); return StartPageResults.ERROR; } Properties configurationAsProperties = getConfigurationAsProperties(configuration); testConnection(); // so our lastTest gets set and the user will be able to get the error in the UI if (lastTest == null || !lastTest.equals("OK")) { lastError = lastTest; return StartPageResults.ERROR; } // Ensure server info has been set if ((null == haServer) || (null == haServer.getName()) || "".equals(haServer.getName().trim())) { lastError = I18Nmsg.getMsg(InstallerI18NResourceKeys.INVALID_STRING, I18Nmsg.getMsg(InstallerI18NResourceKeys.PROP_HIGH_AVAILABILITY_NAME)); return StartPageResults.ERROR; } for (PropertyItemWithValue newValue : configuration) { if (Integer.class.isAssignableFrom(newValue.getItemDefinition().getPropertyType())) { try { Integer.parseInt(newValue.getValue()); } catch (Exception e) { // there is one special property - the connector bind port - that is allowed to be empty // ignore this error if we are looking at that property and its empty; otherwise, this is an error if (!(newValue.getItemDefinition().getPropertyName().equals( ServerProperties.PROP_CONNECTOR_BIND_PORT) && newValue.getValue().length() == 0)) { lastError = I18Nmsg.getMsg(InstallerI18NResourceKeys.INVALID_NUMBER, newValue.getItemDefinition().getPropertyLabel(), newValue.getValue()); return StartPageResults.ERROR; } } } else if (Boolean.class.isAssignableFrom(newValue.getItemDefinition().getPropertyType())) { try { if (newValue.getValue() == null) { newValue.setValue(Boolean.FALSE.toString()); } Boolean.parseBoolean(newValue.getValue()); } catch (Exception e) { lastError = I18Nmsg.getMsg(InstallerI18NResourceKeys.INVALID_BOOLEAN, newValue.getItemDefinition().getPropertyLabel(), newValue.getValue()); return StartPageResults.ERROR; } } } try { // indicate that no errors occurred lastError = null; // save the properties serverInfo.setServerProperties(configurationAsProperties); // prepare the db schema if (!ExistingSchemaOption.SKIP.name().equals(existingSchemaOption)) { if (serverInfo.isDatabaseSchemaExist(configurationAsProperties)) { if (existingSchemaOption == null) { if (!isAutoinstallEnabled()) { return StartPageResults.STAY; // user didn't tell us what to do, re-display the page with the question } // we are supposed to auto-install but wasn't explicitly told what to do - the default is "auto" // and since we know the database schema exists, that means we upgrade it } if (ExistingSchemaOption.OVERWRITE.name().equals(existingSchemaOption)) { serverInfo.createNewDatabaseSchema(configurationAsProperties); } else { serverInfo.upgradeExistingDatabaseSchema(configurationAsProperties); } } else { serverInfo.createNewDatabaseSchema(configurationAsProperties); } } // Ensure the install server info is up to date and stored in the DB serverInfo.storeServer(configurationAsProperties, haServer); // encode database password and set updated properties String pass = configurationAsProperties.getProperty(ServerProperties.PROP_DATABASE_PASSWORD); pass = encryptPassword(pass); configurationAsProperties.setProperty(ServerProperties.PROP_DATABASE_PASSWORD, pass); serverInfo.setServerProperties(configurationAsProperties); // We have changed the password of the database connection, so we need to // tell the login config about it serverInfo.restartLoginConfig(); // build a keystore whose cert has a CN of this server's public endpoint address serverInfo.createKeystore(haServer); // now deploy RHQ Server fully serverInfo.moveDeploymentArtifacts(true); } catch (Exception e) { LOG.fatal("Failed to update properties and fully deploy - " + serverInfo.getProduct() + " Server will not function properly.", e); lastError = I18Nmsg.getMsg(InstallerI18NResourceKeys.SAVE_FAILURE, ThrowableUtil.getAllMessages(e)); return StartPageResults.ERROR; } LOG.info("Installer: final submitted values: " + configurationAsProperties); return StartPageResults.SUCCESS; } private String encryptPassword(String password) throws Exception { // We need to do some mumbo jumbo, as the interesting method is private // in SecureIdentityLoginModule try { SecureIdentityLoginModule lm = new SecureIdentityLoginModule(); Class<?> clazz = SecureIdentityLoginModule.class; Method m = clazz.getDeclaredMethod("encode", String.class); m.setAccessible(true); String res = (String) m.invoke(lm, password); return res; } catch (Exception e) { throw new Exception("Encoding db password failed: ", e); } } private String decodePassword(String encrypedPassword) throws Exception { // We need to do some mumbo jumbo, as the interesting method is private // in SecureIdentityLoginModule try { SecureIdentityLoginModule lm = new SecureIdentityLoginModule(); Class<?> clazz = SecureIdentityLoginModule.class; Method m = clazz.getDeclaredMethod("decode", String.class); m.setAccessible(true); char[] res = (char[]) m.invoke(lm, encrypedPassword); return new String(res); } catch (Exception e) { throw new Exception("Decoding db password failed: ", e); } } private Properties getConfigurationAsProperties(List<PropertyItemWithValue> config) { Properties props = new Properties(); for (PropertyItemWithValue itemWithValue : config) { props.setProperty(itemWithValue.getItemDefinition().getPropertyName(), itemWithValue.getValue()); } return props; } private PropertyItemWithValue findPropertyItemWithValue(String propertyName) { for (PropertyItemWithValue value : configuration) { if (value.getItemDefinition().getPropertyName().equals(propertyName)) { return value; } } return null; } public List<SelectItem> getExistingSchemaOptions() { if (existingSchemaOptions == null) { existingSchemaOptions = new ArrayList<SelectItem>(); existingSchemaOptions.add(new SelectItem(ExistingSchemaOption.OVERWRITE.name(), I18Nmsg.getMsg(InstallerI18NResourceKeys.EXISTING_SCHEMA_OPTION_OVERWRITE))); existingSchemaOptions.add(new SelectItem(ExistingSchemaOption.KEEP.name(), I18Nmsg.getMsg(InstallerI18NResourceKeys.EXISTING_SCHEMA_OPTION_KEEP))); existingSchemaOptions.add(new SelectItem(ExistingSchemaOption.SKIP.name(), I18Nmsg.getMsg(InstallerI18NResourceKeys.EXISTING_SCHEMA_OPTION_SKIP))); } return existingSchemaOptions; } /** To set the server name use setServerName() */ public PropertyItemWithValue getPropHaServerName() { return getConfigurationPropertyFromAll(ServerProperties.PROP_HIGH_AVAILABILITY_NAME); } public PropertyItemWithValue getPropHaEndpointPort() { return getConfigurationPropertyFromAll(ServerProperties.PROP_HTTP_PORT); } public PropertyItemWithValue getPropHaEndpointSecurePort() { return getConfigurationPropertyFromAll(ServerProperties.PROP_HTTPS_PORT); } public boolean isRegisteredServers() { if (!this.isKeepExistingSchema()) return false; List<SelectItem> registeredServerNames = getRegisteredServerNames(); return ((null != registeredServerNames) && !registeredServerNames.isEmpty()); } public List<SelectItem> getRegisteredServerNames() { List<SelectItem> result = new ArrayList<SelectItem>(0); if (!isDatabaseSchemaExist()) return result; try { Properties configurationAsProperties = getConfigurationAsProperties(configuration); for (String serverName : serverInfo.getServerNames(configurationAsProperties)) { result.add(new SelectItem(serverName)); } if (!result.isEmpty()) { result.add(0, new SelectItem(I18Nmsg.getMsg(InstallerI18NResourceKeys.NEW_SERVER_SELECT_ITEM))); } } catch (Exception e) { // Should not be able to get here since we checked for schema above LOG.warn("Unexpected Exception getting registered server info: ", e); } return result; } public String getSelectedRegisteredServerName() { return selectedRegisteredServerName; } // If an existing server name is selected from the list set haServerName to the selection. Note, this function // should not call getServerConfiguration.setValue() public void setSelectedRegisteredServerName(String selectedRegisteredServerName) { this.selectedRegisteredServerName = selectedRegisteredServerName; } public String getHaServerName() { return haServerName; } public void setHaServerName(String serverName) { // handle the case where the user selected the dummy entry in the registered servers drop down if (I18Nmsg.getMsg(InstallerI18NResourceKeys.NEW_SERVER_SELECT_ITEM).equals(serverName)) { serverName = this.getDefaultServerName(); } this.haServerName = serverName; // try pulling info from the database for this server name if (isRegisteredServers()) { Properties configurationAsProperties = getConfigurationAsProperties(configuration); setHaServer(serverInfo.getServerDetail(configurationAsProperties, serverName)); } // if the server was not registered in the database then populate the ha server info with proper defaults if (null == getHaServer()) { String endpointAddress = ""; try { endpointAddress = InetAddress.getLocalHost().getCanonicalHostName(); } catch (Exception e) { LOG.info("Could not determine default server address: ", e); } setHaServer(new ServerInformation.Server(serverName, endpointAddress, ServerInformation.Server.DEFAULT_ENDPOINT_PORT, ServerInformation.Server.DEFAULT_ENDPOINT_SECURE_PORT, ServerInformation.Server.DEFAULT_AFFINITY_GROUP)); // override default settings with current property values try { getHaServer().setEndpointPortString( getConfigurationPropertyFromAll(ServerProperties.PROP_HTTP_PORT).getValue()); } catch (Exception e) { LOG.debug("Could not determine default port: ", e); } try { getHaServer().setEndpointSecurePortString( getConfigurationPropertyFromAll(ServerProperties.PROP_HTTPS_PORT).getValue()); } catch (Exception e) { LOG.debug("Could not determine default secure port: ", e); } } else { getHaServer().setName(serverName); } } /** * This method will set the HA Server information based solely on the server configuration * properties. It does not rely on any database access. * * This is used by the auto-installation process - see {@link AutoInstallServlet}. * * @throws Exception */ public void setHaServerFromPropertiesOnly() throws Exception { PropertyItemWithValue preconfigDb = getConfigurationPropertyFromAll(ServerProperties.PROP_AUTOINSTALL_DB); if (preconfigDb != null && preconfigDb.getValue() != null) { if ("overwrite".equals(preconfigDb.getValue().toLowerCase())) { setExistingSchemaOption(ExistingSchemaOption.OVERWRITE.name()); } else if ("skip".equals(preconfigDb.getValue().toLowerCase())) { setExistingSchemaOption(ExistingSchemaOption.SKIP.name()); } else if ("auto".equals(preconfigDb.getValue().toLowerCase())) { setExistingSchemaOption(null); } else { LOG.warn("An invalid setting for [" + ServerProperties.PROP_AUTOINSTALL_DB + "] was provided: [" + preconfigDb.getValue() + "]. Valid values are 'auto', 'overwrite' or 'skip'. Defaulting to 'auto'"); preconfigDb.setRawValue("auto"); setExistingSchemaOption(null); } } else { LOG.debug("[" + ServerProperties.PROP_AUTOINSTALL_DB + "] was not provided. Defaulting to 'auto'"); setExistingSchemaOption(null); } // create a ha server stub with some defaults - we'll fill this in based on our property settings ServerInformation.Server preconfiguredHaServer = new ServerInformation.Server("", "", ServerInformation.Server.DEFAULT_ENDPOINT_PORT, ServerInformation.Server.DEFAULT_ENDPOINT_SECURE_PORT, ServerInformation.Server.DEFAULT_AFFINITY_GROUP); // determine the name of the server - its either preconfigured as the high availability name, or // we auto-determine it by using the machine's canonical hostname PropertyItemWithValue haNameProp = getPropHaServerName(); if (haNameProp != null) { preconfiguredHaServer.setName(haNameProp.getValue()); // this leaves it alone if value is null or empty } if (preconfiguredHaServer.getName().equals("")) { String serverName = getDefaultServerName(); // gets hostname if (serverName == null || "".equals(serverName)) { throw new Exception("Server name is not preconfigured and could not be determined automatically"); } preconfiguredHaServer.setName(serverName); } // the public endpoint address is one that can be preconfigured in the special autoinstall property. // if that is not specified, then we use either the connector's bind address or the server bind address. // if nothing was specified, we'll default to the canonical host name. String publicEndpointAddress; PropertyItemWithValue preconfigAddr = getConfigurationPropertyFromAll( ServerProperties.PROP_AUTOINSTALL_PUBLIC_ENDPOINT); if (preconfigAddr != null && preconfigAddr.getValue() != null && !"".equals(preconfigAddr.getValue().trim())) { // its preconfigured using the autoinstall setting, use that and ignore the other settings publicEndpointAddress = preconfigAddr.getValue().trim(); } else { PropertyItemWithValue connBindAddress = getConfigurationPropertyFromAll( ServerProperties.PROP_CONNECTOR_BIND_ADDRESS); if (connBindAddress != null && connBindAddress.getValue() != null && !"".equals(connBindAddress.getValue().trim()) && !"0.0.0.0".equals(connBindAddress.getValue().trim())) { // the server-side connector bind address is explicitly set, use that publicEndpointAddress = connBindAddress.getValue().trim(); } else { PropertyItemWithValue serverBindAddress = getConfigurationPropertyFromAll( ServerProperties.PROP_SERVER_BIND_ADDRESS); if (serverBindAddress != null && serverBindAddress.getValue() != null && !"".equals(serverBindAddress.getValue().trim()) && !"0.0.0.0".equals(serverBindAddress.getValue().trim())) { // the main JBossAS server bind address is set and it isn't 0.0.0.0, use that publicEndpointAddress = serverBindAddress.getValue().trim(); } else { publicEndpointAddress = InetAddress.getLocalHost().getCanonicalHostName(); } } } preconfiguredHaServer.setEndpointAddress(publicEndpointAddress); // define the public endpoint ports. // note that if using a different transport other than (ssl)servlet, we'll // take the connector's bind port and use it for both ports. This is to support a special deployment // use-case - 99% of the time, the agents will go through the web/tomcat connector and thus we'll use // the http/https ports for the public endpoints. PropertyItemWithValue connectorTransport = getConfigurationPropertyFromAll( ServerProperties.PROP_CONNECTOR_TRANSPORT); if (connectorTransport != null && connectorTransport.getValue() != null && connectorTransport.getValue().contains("socket")) { // we aren't using the (ssl)servlet protocol, take the connector bind port and use it for the public endpoint ports PropertyItemWithValue connectorBindPort = getConfigurationPropertyFromAll( ServerProperties.PROP_CONNECTOR_BIND_PORT); if (connectorBindPort == null || connectorBindPort.getValue() == null || "".equals(connectorBindPort.getValue().trim()) || "0".equals(connectorBindPort.getValue().trim())) { throw new Exception( "Using non-servlet transport [" + connectorTransport + "] but didn't define a port"); } preconfiguredHaServer.setEndpointPort(Integer.parseInt(connectorBindPort.getValue())); preconfiguredHaServer.setEndpointSecurePort(Integer.parseInt(connectorBindPort.getValue())); } else { // this is the typical use-case - the transport is probably (ssl)servlet so use the web http/https ports try { PropertyItemWithValue httpPort = getPropHaEndpointPort(); preconfiguredHaServer.setEndpointPortString(httpPort.getValue()); } catch (Exception e) { LOG.warn("Could not determine port, will use default: " + e); } try { PropertyItemWithValue httpsPort = getPropHaEndpointSecurePort(); preconfiguredHaServer.setEndpointSecurePortString(httpsPort.getValue()); } catch (Exception e) { LOG.warn("Could not determine secure port, will use default: " + e); } } // everything looks good - remember these setHaServer(preconfiguredHaServer); this.haServerName = preconfiguredHaServer.getName(); } public ServerInformation.Server getHaServer() { return haServer; } public void setHaServer(ServerInformation.Server haServer) { this.haServer = haServer; } }