org.rhq.enterprise.server.installer.ServerInstallUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rhq-installer-util Show documentation
Show all versions of rhq-installer-util Show documentation
The RHQ Enterprise Installer Utility
/*
* 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 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.server.installer;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.security.auth.login.AppConfigurationEntry;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.helper.ProjectHelper2;
import org.jboss.as.controller.client.ModelControllerClient;
import org.jboss.dmr.ModelNode;
import org.jboss.sasl.util.UsernamePasswordHashUtil;
import org.rhq.common.jbossas.client.controller.Address;
import org.rhq.common.jbossas.client.controller.CoreJBossASClient;
import org.rhq.common.jbossas.client.controller.DatasourceJBossASClient;
import org.rhq.common.jbossas.client.controller.FailureException;
import org.rhq.common.jbossas.client.controller.InfinispanJBossASClient;
import org.rhq.common.jbossas.client.controller.JBossASClient;
import org.rhq.common.jbossas.client.controller.LoggingJBossASClient;
import org.rhq.common.jbossas.client.controller.MessagingJBossASClient;
import org.rhq.common.jbossas.client.controller.SecurityDomainJBossASClient;
import org.rhq.common.jbossas.client.controller.SocketBindingJBossASClient;
import org.rhq.common.jbossas.client.controller.TransactionsJBossASClient;
import org.rhq.common.jbossas.client.controller.WebJBossASClient;
import org.rhq.common.jbossas.client.controller.WebJBossASClient.ConnectorConfiguration;
import org.rhq.common.jbossas.client.controller.WebJBossASClient.SSLConfiguration;
import org.rhq.core.db.DatabaseType;
import org.rhq.core.db.DatabaseTypeFactory;
import org.rhq.core.db.DbUtil;
import org.rhq.core.db.OracleDatabaseType;
import org.rhq.core.db.PostgresqlDatabaseType;
import org.rhq.core.db.setup.DBSetup;
import org.rhq.core.domain.cloud.StorageNode;
import org.rhq.core.domain.cloud.StorageNode.OperationMode;
import org.rhq.core.util.PropertiesFileUpdate;
import org.rhq.core.util.exception.ThrowableUtil;
import org.rhq.core.util.file.FileUtil;
import org.rhq.core.util.stream.StreamUtil;
import org.rhq.enterprise.communications.util.SecurityUtil;
/**
* Provides utility methods necessary to complete the server installation.
*
* @author John Mazzitelli
*/
public class ServerInstallUtil {
private static final Log LOG = LogFactory.getLog(ServerInstallUtil.class);
public enum ExistingSchemaOption {
OVERWRITE, KEEP, SKIP
};
public enum SupportedDatabaseType {
POSTGRES, ORACLE
};
private static class SocketBindingInfo {
public String name;
public String sysprop;
public int port;
public boolean required = true;
public String interfaceName = null; // not null if we know we want it to be changed from its default setting
public SocketBindingInfo(String n, String s, int p) {
this.name = n;
this.sysprop = s;
this.port = p;
}
public SocketBindingInfo(String n, String s, int p, String i) {
this.name = n;
this.sysprop = s;
this.port = p;
this.interfaceName = i;
}
public SocketBindingInfo(String name, String sysprop, int port, String interfaceName, boolean required) {
this.name = name;
this.sysprop = sysprop;
this.port = port;
this.interfaceName = interfaceName;
this.required = required;
}
}
private static final ArrayList defaultSocketBindings;
static {
// all ports are -1000 from out-of-box AS7 defaults
// except for the jboss.management ones - those are -3000 from their out-of-box defaults
defaultSocketBindings = new ArrayList();
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_AJP,
"rhq.server.socket.binding.port.ajp", 7009));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_HTTP,
"rhq.server.socket.binding.port.http", 7080));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_HTTPS,
"rhq.server.socket.binding.port.https", 7443));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_JACORB,
"rhq.server.socket.binding.port.jacorb", 2528));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_JACORB_SSL,
"rhq.server.socket.binding.port.jacorb-ssl", 2529));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_MESSAGING,
"rhq.server.socket.binding.port.messaging", 4449, "management"));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_MESSAGING_THRUPUT,
"rhq.server.socket.binding.port.messaging-throughput", 4455, "management"));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_MGMT_HTTP,
"jboss.management.http.port", 6990));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_MGMT_HTTPS,
"jboss.management.https.port", 6443));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_MGMT_NATIVE,
"jboss.management.native.port", 6999));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_REMOTING,
"rhq.server.socket.binding.port.remoting", 3447, "management"));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_TXN_RECOVERY_ENV,
"rhq.server.socket.binding.port.txn-recovery-environment", 3712));
defaultSocketBindings.add(new SocketBindingInfo(SocketBindingJBossASClient.DEFAULT_BINDING_TXN_STATUS_MGR,
"rhq.server.socket.binding.port.txn-status-manager", 3713));
}
private static final String RHQ_DATASOURCE_NAME_NOTX = "NoTxRHQDS";
private static final String RHQ_DATASOURCE_NAME_XA = "RHQDS";
private static final String RHQ_DS_SECURITY_DOMAIN = "RHQDSSecurityDomain";
private static final String RHQ_USER_SECURITY_DOMAIN = "RHQUserSecurityDomain";
private static final String RHQ_REST_SECURITY_DOMAIN = "RHQRESTSecurityDomain";
private static final String JDBC_LOGIN_MODULE_NAME = "org.rhq.enterprise.server.core.jaas.JDBCLoginModule";
private static final String DELEGATIG_LOGIN_MODULE_NAME = "org.rhq.enterprise.server.core.jaas.DelegatingLoginModule";
private static final String JDBC_DRIVER_POSTGRES = "postgres";
private static final String JDBC_DRIVER_ORACLE = "oracle";
private static final String JMS_ALERT_CONDITION_QUEUE = "AlertConditionQueue";
private static final String JMS_DRIFT_CHANGESET_QUEUE = "DriftChangesetQueue";
private static final String JMS_DRIFT_FILE_QUEUE = "DriftFileQueue";
private static final String RHQ_CACHE_CONTAINER = "rhq";
private static final String RHQ_CACHE = "rhqCache";
private static final String RHQ_MGMT_USER = "rhqadmin";
private static final String RHQ_MGMT_USER_PASSWORD = "rhq.server.management.password";
/**
* Configure the logging subsystem.
* @param mcc JBossAS management client
* @param serverProperties the server properties, which includes the default log level to use
* @throws Exception
*/
public static void configureLogging(ModelControllerClient mcc, HashMap serverProperties)
throws Exception {
LoggingJBossASClient client = new LoggingJBossASClient(mcc);
// we want to create our own category
String val = buildExpression(ServerProperties.PROP_LOG_LEVEL, serverProperties, true);
client.setLoggerLevel("org.rhq", val);
LOG.info("Logging category org.rhq set to [" + val + "]");
client.setLoggerLevel("org.jboss.as.config", "INFO"); // BZ 1004730
}
/**
* Configure the transaction manager.
* @param mcc JBossAS management client
* @throws Exception
*/
public static void configureTransactionManager(ModelControllerClient mcc) throws Exception {
TransactionsJBossASClient client = new TransactionsJBossASClient(mcc);
// we want to bump up the transaction timeout
client.setDefaultTransactionTimeout(600);
LOG.info("Default transaction timeout set to 600 seconds.");
}
/**
* Configure the deployment scanner.
* @param mcc JBossAS management client
* @throws Exception
*/
public static void configureDeploymentScanner(ModelControllerClient mcc) throws Exception {
CoreJBossASClient client = new CoreJBossASClient(mcc);
// we do not want our RHQ Server to support hot deployments via the scanner
client.setAppServerDefaultDeploymentScanEnabled(false);
LOG.info("Deployment scanner turned off.");
}
/**
* Prepares the mail service by configuring the SMTP settings.
*
* @param mcc JBossAS management client
* @param serverProperties the server's properties
* @throws Exception
*/
public static void setupMailService(ModelControllerClient mcc, HashMap serverProperties)
throws Exception {
String fromAddressExpr = buildExpression(ServerProperties.PROP_EMAIL_FROM_ADDRESS, serverProperties, true);
String smtpHostExpr = buildExpression(ServerProperties.PROP_EMAIL_SMTP_HOST, serverProperties, true);
String smtpPortExpr = buildExpression(ServerProperties.PROP_EMAIL_SMTP_PORT, serverProperties, true);
// Tweek the mail configuration that comes out of box. Setup a batch request to write the proper attributes.
// First, the from address (TODO: there is also a "ssl", "username" and "password" attribute we could set for authz)
Address addr = Address.root().add(JBossASClient.SUBSYSTEM, "mail", "mail-session", "java:jboss/mail/Default");
ModelNode writeFromAddr = JBossASClient.createRequest(JBossASClient.WRITE_ATTRIBUTE, addr);
writeFromAddr.get(JBossASClient.NAME).set("from");
writeFromAddr.get(JBossASClient.VALUE).setExpression(fromAddressExpr);
// now the SMTP host
addr = Address.root().add("socket-binding-group", "standard-sockets",
"remote-destination-outbound-socket-binding", "mail-smtp");
ModelNode writeHost = JBossASClient.createRequest(JBossASClient.WRITE_ATTRIBUTE, addr);
writeHost.get(JBossASClient.NAME).set("host");
JBossASClient.setPossibleExpression(writeHost, JBossASClient.VALUE, smtpHostExpr);
// now the SMTP port
addr = Address.root().add("socket-binding-group", "standard-sockets",
"remote-destination-outbound-socket-binding", "mail-smtp");
ModelNode writePort = JBossASClient.createRequest(JBossASClient.WRITE_ATTRIBUTE, addr);
writePort.get(JBossASClient.NAME).set("port");
JBossASClient.setPossibleExpression(writePort, JBossASClient.VALUE, smtpPortExpr);
ModelNode batch = JBossASClient.createBatchRequest(writeFromAddr, writeHost, writePort);
JBossASClient client = new JBossASClient(mcc);
ModelNode response = client.execute(batch);
if (!JBossASClient.isSuccess(response)) {
throw new FailureException(response, "Failed to setup mail service");
}
LOG.info("Mail service has been configured.");
return;
}
/**
* Give the server properties, this returns the type of database that will be connected to.
*
* @param serverProperties
* @return the type of DB
*/
public static SupportedDatabaseType getSupportedDatabaseType(HashMap serverProperties) {
return getSupportedDatabaseType(serverProperties.get(ServerProperties.PROP_DATABASE_TYPE));
}
/**
* Give the database type string, this returns the type of database that it refers to.
*
* @param dbType the database type string
* @return the type of DB
*/
public static SupportedDatabaseType getSupportedDatabaseType(String dbType) {
if (dbType == null) {
return null;
}
if (dbType.toLowerCase().indexOf("postgres") > -1) {
return SupportedDatabaseType.POSTGRES;
} else if (dbType.toLowerCase().indexOf("oracle") > -1) {
return SupportedDatabaseType.ORACLE;
}
return null;
}
/**
* Creates the security domain for the datasources. This is needed to support
* obfuscation of the password in the configuration file.
*
* @param mcc the JBossAS management client
* @param serverProperties contains the obfuscated password to store in the security domain
* @throws Exception
*/
public static void createDatasourceSecurityDomain(ModelControllerClient mcc,
HashMap serverProperties) throws Exception {
final String dbUsername = buildExpression(ServerProperties.PROP_DATABASE_USERNAME, serverProperties, true);
final String obfuscatedPassword = buildExpression(ServerProperties.PROP_DATABASE_PASSWORD, serverProperties,
true);
final SecurityDomainJBossASClient client = new SecurityDomainJBossASClient(mcc);
final String securityDomain = RHQ_DS_SECURITY_DOMAIN;
if (!client.isSecurityDomain(securityDomain)) {
client.createNewSecureIdentitySecurityDomain72(securityDomain, dbUsername, obfuscatedPassword);
LOG.info("Security domain [" + securityDomain + "] created");
} else {
LOG.info("Security domain [" + securityDomain + "] already exists, skipping the creation request");
client.updateSecureIdentitySecurityDomainCredentials(securityDomain, dbUsername, obfuscatedPassword);
LOG.info("Credentials have been updated for security domain [" + securityDomain + "]");
}
}
/**
* Create the standard user security domain with the JDBCLogin module installed
*
* @param mcc ModelControllerClient to talk to the underlying AS
* @throws Exception If anything goes wrong
*/
public static void createUserSecurityDomain(ModelControllerClient mcc) throws Exception {
Map options = new HashMap(2);
options.put("hashAlgorithm", "MD5");
options.put("hashEncoding", "base64");
SecurityDomainJBossASClient.LoginModuleRequest loginModuleRequest = new SecurityDomainJBossASClient.LoginModuleRequest(
JDBC_LOGIN_MODULE_NAME, AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT, options);
SecurityDomainJBossASClient client = new SecurityDomainJBossASClient(mcc);
client.createNewSecurityDomain(RHQ_USER_SECURITY_DOMAIN, loginModuleRequest);
}
/**
* Create a security domain for container managed security used with the rhq-rest.war
* @param mcc ModelControllerClient to talk to the underlying AS.
* @throws Exception If anything goes wrong
*/
public static void createRestSecurityDomain(ModelControllerClient mcc) throws Exception {
Map options = new HashMap(2);
options.put("delegateTo", RHQ_USER_SECURITY_DOMAIN);
options.put("roles", "rest-user");
SecurityDomainJBossASClient.LoginModuleRequest loginModuleRequest = new SecurityDomainJBossASClient.LoginModuleRequest(
DELEGATIG_LOGIN_MODULE_NAME, AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT, options);
SecurityDomainJBossASClient client = new SecurityDomainJBossASClient(mcc);
client.createNewSecurityDomain(RHQ_REST_SECURITY_DOMAIN, loginModuleRequest);
}
/**
* Creates the JMS Queues required for Drift and Alerting.
*
* @param mcc the JBossAS management client
* @param serverProperties contains the obfuscated password to store in the security domain
* @throws Exception
*/
public static void createNewJMSQueues(ModelControllerClient mcc, HashMap serverProperties)
throws Exception {
final MessagingJBossASClient client = new MessagingJBossASClient(mcc);
final List entryNames = new ArrayList();
// TODO (jshaughn): Prior to HornetQ we set recoveryRetries to 0: "don't redeliver messages on failure. It
// just causes more failures. just go straight to the dead messages by setting recoveryRetries to 0.
// This is equivalent to setting the dLQMaxResent property to 0 in the MessageDriven annotation in
// the class definition."
// HornetQ has different semantics, and may behave well with default settings. If not, we'll
// likely need to add specific elements for our queues, which set
// max-delivery-attempts to 0. The documented default is 10.
String queueName = JMS_ALERT_CONDITION_QUEUE;
if (!client.isQueue(queueName)) {
entryNames.clear();
entryNames.add("queue/" + queueName);
ModelNode request = client.createNewQueueRequest(queueName, true, entryNames);
ModelNode results = client.execute(request);
if (!MessagingJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create JMS Queue [" + queueName + "]");
} else {
LOG.info("JMS queue [" + queueName + "] created");
}
} else {
LOG.info("JMS Queue [" + queueName + "] already exists, skipping the creation request");
}
queueName = JMS_DRIFT_CHANGESET_QUEUE;
if (!client.isQueue(queueName)) {
entryNames.clear();
entryNames.add("queue/" + queueName);
ModelNode request = client.createNewQueueRequest(queueName, true, entryNames);
ModelNode results = client.execute(request);
if (!MessagingJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create JMS Queue [" + queueName + "]");
} else {
LOG.info("JMS queue [" + queueName + "] created");
}
} else {
LOG.info("JMS Queue [" + queueName + "] already exists, skipping the creation request");
}
queueName = JMS_DRIFT_FILE_QUEUE;
if (!client.isQueue(queueName)) {
entryNames.clear();
entryNames.add("queue/" + queueName);
ModelNode request = client.createNewQueueRequest(queueName, true, entryNames);
ModelNode results = client.execute(request);
if (!MessagingJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create JMS Queue [" + queueName + "]");
} else {
LOG.info("JMS queue [" + queueName + "] created");
}
} else {
LOG.info("JMS Queue [" + queueName + "] already exists, skipping the creation request");
}
return;
}
/**
* Creates the Infinispan caches for RHQ.
*
* @param mcc the JBossAS management client
* @param serverProperties contains the obfuscated password to store in the security domain
* @throws Exception
*/
public static void createNewCaches(ModelControllerClient mcc, HashMap serverProperties)
throws Exception {
final InfinispanJBossASClient client = new InfinispanJBossASClient(mcc);
final String cacheContainerName = RHQ_CACHE_CONTAINER;
final String localCacheName = RHQ_CACHE;
if (!client.isCacheContainer(cacheContainerName)) {
ModelNode request = client.createNewCacheContainerRequest(cacheContainerName, localCacheName);
ModelNode results = client.execute(request);
if (!MessagingJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create Cache container [" + cacheContainerName + "]");
} else {
LOG.info("Cache container [" + cacheContainerName + "] created");
}
} else {
LOG.info("Cache container [" + cacheContainerName + "] already exists, skipping the creation request");
}
if (!client.isLocalCache(cacheContainerName, localCacheName)) {
ModelNode request = client.createNewLocalCacheRequest(cacheContainerName, localCacheName, null, null, null,
null, null);
ModelNode results = client.execute(request);
if (!MessagingJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create Local Cache [" + localCacheName + "]");
} else {
LOG.info("Local Cache [" + localCacheName + "] created");
}
} else {
LOG.info("Local Cache [" + localCacheName + "] already exists, skipping the creation request");
}
}
/**
* Creates JDBC driver configurations so the datasources can properly connect to the backend databases.
* This will attempt to create drivers for all supported databases, not just for the database type that
* is currently configured.
*
* @param mcc
* @param serverProperties
* @throws Exception
*/
public static void createNewJdbcDrivers(ModelControllerClient mcc, HashMap serverProperties)
throws Exception {
final DatasourceJBossASClient client = new DatasourceJBossASClient(mcc);
final ModelNode postgresDriverRequest = client.createNewJdbcDriverRequest(JDBC_DRIVER_POSTGRES,
"org.rhq.postgres", "org.postgresql.xa.PGXADataSource");
final ModelNode oracleDriverRequest = client.createNewJdbcDriverRequest(JDBC_DRIVER_ORACLE, "org.rhq.oracle",
"oracle.jdbc.xa.client.OracleXADataSource");
// if we are to use Oracle, we throw an exception if we can't create the Oracle datasource. We also try to
// create the Postgres datasource but because it isn't needed, we don't throw exceptions if that fails, we
// just log a warning.
// The reverse is true if we are to use Postgres (that is, we ensure Postgres driver is created, but not Oracle).
ModelNode results;
final SupportedDatabaseType supportedDbType = getSupportedDatabaseType(serverProperties);
switch (supportedDbType) {
case POSTGRES: {
if (client.isJDBCDriver(JDBC_DRIVER_POSTGRES)) {
LOG.info("Postgres JDBC driver is already deployed");
} else {
results = client.execute(postgresDriverRequest);
if (!DatasourceJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create postgres database driver");
} else {
LOG.info("Deployed Postgres JDBC driver");
}
}
if (client.isJDBCDriver(JDBC_DRIVER_ORACLE)) {
LOG.info("Oracle JDBC driver is already deployed");
} else {
results = client.execute(oracleDriverRequest);
if (!DatasourceJBossASClient.isSuccess(results)) {
LOG.warn("Could not create Oracle JDBC Driver - you will not be able to switch to an Oracle DB later: "
+ JBossASClient.getFailureDescription(results));
} else {
LOG.info("Deployed Oracle JDBC driver for future use");
}
}
break;
}
case ORACLE: {
if (client.isJDBCDriver(JDBC_DRIVER_ORACLE)) {
LOG.info("Oracle JDBC driver is already deployed");
} else {
results = client.execute(oracleDriverRequest);
if (!DatasourceJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create oracle database driver");
} else {
LOG.info("Deployed Oracle JDBC driver");
}
}
if (client.isJDBCDriver(JDBC_DRIVER_POSTGRES)) {
LOG.info("Postgres JDBC driver is already deployed");
} else {
results = client.execute(postgresDriverRequest);
if (!DatasourceJBossASClient.isSuccess(results)) {
LOG.warn("Could not create Postgres JDBC Driver - you will not be able to switch to a Postgres DB later: "
+ JBossASClient.getFailureDescription(results));
} else {
LOG.info("Deployed Postgres JDBC driver for future use");
}
}
break;
}
default:
throw new RuntimeException("bad db type"); // this should never happen; should have never gotten to this point with a bad type
}
}
/**
* Creates the datasources needed by the RHQ Server.
*
* @param mcc the JBossAS management client
* @param serverProperties properties to help determine the properties of the datasources to be created
* @throws Exception
*/
public static void createNewDatasources(ModelControllerClient mcc, HashMap serverProperties)
throws Exception {
final SupportedDatabaseType supportedDbType = getSupportedDatabaseType(serverProperties);
switch (supportedDbType) {
case POSTGRES: {
createNewDatasources_Postgres(mcc);
break;
}
case ORACLE: {
createNewDatasources_Oracle(mcc);
break;
}
default:
throw new RuntimeException("bad db type"); // this should never happen; should have never gotten to this point with a bad type
}
LOG.info("Created datasources");
final DatasourceJBossASClient client = new DatasourceJBossASClient(mcc);
client.enableDatasource(RHQ_DATASOURCE_NAME_NOTX);
client.enableXADatasource(RHQ_DATASOURCE_NAME_XA);
LOG.info("Enabled datasources");
}
private static void createNewDatasources_Postgres(ModelControllerClient mcc) throws Exception {
final HashMap props = new HashMap(4);
final DatasourceJBossASClient client = new DatasourceJBossASClient(mcc);
ModelNode noTxDsRequest = null;
ModelNode xaDsRequest = null;
if (!client.isDatasource(RHQ_DATASOURCE_NAME_NOTX)) {
props.put("char.encoding", "UTF-8");
noTxDsRequest = client.createNewDatasourceRequest(RHQ_DATASOURCE_NAME_NOTX, 30000,
"${rhq.server.database.connection-url:jdbc:postgres://127.0.0.1:5432/rhq}", JDBC_DRIVER_POSTGRES,
"org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter", 15, false, 2, 5, 75,
RHQ_DS_SECURITY_DOMAIN, "-unused-stale-conn-checker-", "TRANSACTION_READ_COMMITTED",
"org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker", props);
noTxDsRequest.get("steps").get(0).remove("stale-connection-checker-class-name"); // we don't have one of these for postgres
} else {
LOG.info("Postgres datasource [" + RHQ_DATASOURCE_NAME_NOTX + "] already exists");
}
if (!client.isXADatasource(RHQ_DATASOURCE_NAME_XA)) {
props.clear();
props.put("ServerName", "${rhq.server.database.server-name:127.0.0.1}");
props.put("PortNumber", "${rhq.server.database.port:5432}");
props.put("DatabaseName", "${rhq.server.database.db-name:rhq}");
xaDsRequest = client.createNewXADatasourceRequest(RHQ_DATASOURCE_NAME_XA, 30000, JDBC_DRIVER_POSTGRES,
"org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLExceptionSorter", 15, 5, 50, (Boolean) null,
(Boolean) null, 75, (String) null, RHQ_DS_SECURITY_DOMAIN, (String) null, "TRANSACTION_READ_COMMITTED",
"org.jboss.jca.adapters.jdbc.extensions.postgres.PostgreSQLValidConnectionChecker", props);
} else {
LOG.info("Postgres XA datasource [" + RHQ_DATASOURCE_NAME_XA + "] already exists");
}
if (noTxDsRequest != null || xaDsRequest != null) {
ModelNode batch = DatasourceJBossASClient.createBatchRequest(noTxDsRequest, xaDsRequest);
ModelNode results = client.execute(batch);
if (!DatasourceJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create Postgres datasources");
}
}
}
private static void createNewDatasources_Oracle(ModelControllerClient mcc) throws Exception {
final HashMap props = new HashMap(2);
final DatasourceJBossASClient client = new DatasourceJBossASClient(mcc);
ModelNode noTxDsRequest = null;
ModelNode xaDsRequest = null;
if (!client.isDatasource(RHQ_DATASOURCE_NAME_NOTX)) {
props.put("char.encoding", "UTF-8");
noTxDsRequest = client.createNewDatasourceRequest(RHQ_DATASOURCE_NAME_NOTX, 30000,
"${rhq.server.database.connection-url:jdbc:oracle:thin:@127.0.0.1:1521:rhq}", JDBC_DRIVER_ORACLE,
"org.jboss.jca.adapters.jdbc.extensions.oracle.OracleExceptionSorter", 15, false, 2, 5, 75,
RHQ_DS_SECURITY_DOMAIN, "org.jboss.jca.adapters.jdbc.extensions.oracle.OracleStaleConnectionChecker",
"TRANSACTION_READ_COMMITTED",
"org.jboss.jca.adapters.jdbc.extensions.oracle.OracleValidConnectionChecker", props);
} else {
LOG.info("Oracle datasource [" + RHQ_DATASOURCE_NAME_NOTX + "] already exists");
}
if (!client.isDatasource(RHQ_DATASOURCE_NAME_XA)) {
props.clear();
props.put("URL", "${rhq.server.database.connection-url:jdbc:oracle:thin:@127.0.0.1:1521:rhq}");
xaDsRequest = client.createNewXADatasourceRequest(RHQ_DATASOURCE_NAME_XA, 30000, JDBC_DRIVER_ORACLE,
"org.jboss.jca.adapters.jdbc.extensions.oracle.OracleExceptionSorter", 15, 5, 50, (Boolean) null,
Boolean.TRUE, 75, (String) null, RHQ_DS_SECURITY_DOMAIN,
"org.jboss.jca.adapters.jdbc.extensions.oracle.OracleStaleConnectionChecker",
"TRANSACTION_READ_COMMITTED",
"org.jboss.jca.adapters.jdbc.extensions.oracle.OracleValidConnectionChecker", props);
} else {
LOG.info("Oracle XA datasource [" + RHQ_DATASOURCE_NAME_XA + "] already exists");
}
if (noTxDsRequest != null || xaDsRequest != null) {
ModelNode batch = DatasourceJBossASClient.createBatchRequest(noTxDsRequest, xaDsRequest);
ModelNode results = client.execute(batch);
if (!DatasourceJBossASClient.isSuccess(results)) {
throw new FailureException(results, "Failed to create Oracle datasources");
}
}
}
/**
* Determines if we are in auto-install mode. This means the properties file is
* fully configured and the installation can begin without asking the user
* for more input.
*
* @param serverProperties the full set of server properties
*
* @return true if we are in auto-install mode; false if the user must give us more
* information before we can complete the installation.
*/
public static boolean isAutoinstallEnabled(HashMap serverProperties) {
String enableProp = serverProperties.get(ServerProperties.PROP_AUTOINSTALL_ENABLE);
if (enableProp != null) {
return Boolean.parseBoolean(enableProp);
}
return false;
}
/**
* Returns true
if the database already has the database schema created for it. It will not be known
* what version of schema or if its the latest, all this method tells you is that some RHQ database schema exists.
*
* @param connectionUrl
* @param username
* @param password
* @return true
if the database can be connected to
*
* @throws Exception if failed to communicate with the database
*/
public static boolean isDatabaseSchemaExist(String connectionUrl, String username, String password)
throws Exception {
Connection conn = getDatabaseConnection(connectionUrl, username, password);
DatabaseType db = DatabaseTypeFactory.getDatabaseType(conn);
try {
return db.checkTableExists(conn, "RHQ_PRINCIPAL");
} catch (IllegalStateException e) {
return false;
} finally {
db.closeConnection(conn);
}
}
/**
* Get the list of existing servers from an existing schema.
*
* @param connectionUrl
* @param username
* @param password
* @return List of server names registered in the database. Empty list if the table does not exist or there are no entries in the table.
*
* @throws Exception if failed to communicate with the database
*/
public static ArrayList getServerNames(String connectionUrl, String username, String password)
throws Exception {
DatabaseType db = null;
Connection conn = null;
Statement stm = null;
ResultSet rs = null;
ArrayList result = new ArrayList();
try {
conn = getDatabaseConnection(connectionUrl, username, password);
db = DatabaseTypeFactory.getDatabaseType(conn);
if (db.checkTableExists(conn, "rhq_server")) {
stm = conn.createStatement();
rs = stm.executeQuery("SELECT name FROM rhq_server ORDER BY name asc");
while (rs.next()) {
result.add(rs.getString(1));
}
}
} catch (IllegalStateException e) {
// table does not exist
} catch (SQLException e) {
LOG.info("Unable to fetch existing server info: " + e.getMessage());
} finally {
if (null != db) {
db.closeJDBCObjects(conn, stm, rs);
}
}
return result;
}
/**
* Returns information on the server as found in the database (port numbers, affinity group, etc).
*
* @param connectionUrl
* @param username
* @param password
* @param serverName the server whose details are to be returned
* @return the information on the named server
*/
public static ServerDetails getServerDetails(String connectionUrl, String username, String password,
String serverName) {
DatabaseType db = null;
Connection conn = null;
ServerDetails result = null;
try {
conn = getDatabaseConnection(connectionUrl, username, password);
db = DatabaseTypeFactory.getDatabaseType(conn);
result = getServerDetails(db, conn, serverName);
} catch (Exception e) {
LOG.info("Unable to get server detail: " + e.getMessage());
} finally {
if (null != db) {
db.closeConnection(conn);
}
}
return result;
}
private static ServerDetails getServerDetails(DatabaseType db, Connection conn, String serverName) {
PreparedStatement stm = null;
ResultSet rs = null;
ServerDetails result = null;
if (null == serverName) {
return result;
}
try {
stm = conn.prepareStatement("" //
+ "SELECT s.address, s.port, s.secure_port " //
+ " FROM rhq_server s " //
+ " WHERE s.name = ?");
stm.setString(1, serverName.trim());
rs = stm.executeQuery();
if (rs.next()) {
result = new ServerDetails(serverName, rs.getString(1), rs.getInt(2), rs.getInt(3));
}
} catch (SQLException e) {
LOG.info("Unable to get server details for server [" + serverName + "]: " + e.getMessage());
} finally {
if (null != db) {
db.closeResultSet(rs);
db.closeStatement(stm);
}
}
return result;
}
/**
* Tests to make sure the server can be connected to with the given settings.
* If the test is successful, null
. If the test fails, the returned string
* will be the error message to indicate the problem.
*
* @param connectionUrl
* @param username
* @param password
* @return error message if test failed; null
if test succeeded
*/
public static String testConnection(String connectionUrl, String username, String password) {
// its possible the JDBC URL was changed, clear the factory cache in case the DB version is different now
DatabaseTypeFactory.clearDatabaseTypeCache();
try {
ensureDatabaseIsSupported(connectionUrl, username, password);
return null;
} catch (Exception e) {
LOG.warn("Installer failed to test connection", e);
return ThrowableUtil.getAllMessages(e);
}
}
/**
* Call this when you need to confirm that the database is supported.
*
* @param connectionUrl
* @param username
* @param password
*
* @throws Exception if the database is not supported
*/
public static void ensureDatabaseIsSupported(String connectionUrl, String username, String password)
throws Exception {
Connection conn = null;
DatabaseType db = null;
try {
conn = getDatabaseConnection(connectionUrl, username, password);
db = DatabaseTypeFactory.getDatabaseType(conn);
String version = db.getVersion();
if (DatabaseTypeFactory.isPostgres(db)) {
if (version.startsWith("7") || version.equals("8") || version.startsWith("8.0")
|| version.startsWith("8.1")) {
throw new Exception("Unsupported PostgreSQL [" + db + "]");
}
} else if (DatabaseTypeFactory.isOracle(db)) {
if (version.startsWith("8") || version.startsWith("9")) {
throw new Exception("Unsupported Oracle [" + db + "]");
}
} else {
throw new Exception("Unsupported DB [" + db + "]");
}
LOG.info("Database is supported: " + db);
} finally {
if (db != null) {
db.closeConnection(conn);
}
}
return;
}
/**
* Returns a database connection with the given set of properties providing the settings that allow for a successful
* database connection. If props
is null
, it will use the server properties from
* {@link #getServerProperties}.
*
* @param connectionUrl
* @param userName
* @param password
* @return the database connection
*
* @throws SQLException if cannot successfully connect to the database
*/
public static Connection getDatabaseConnection(String connectionUrl, String userName, String password)
throws SQLException {
return DbUtil.getConnection(connectionUrl, userName, password);
}
/**
* Persists the storage nodes to the database only if no storage node entities already exist. This method is used
* to persist storage nodes created from the rhq.storage.nodes server configuration property. The only time those
* seed nodes should be created is during an initial server installation. After the initial installation storage
* nodes should be created using rhqctl install
. This ensures that any necessary cluster maintenance
* tasks will be performed.
*
* @param serverProperties the server properties
* @param password clear text password to connect to the database
* @param storageNodes the {@link StorageNode storage nodes} to persist
* @throws Exception
*/
public static void persistStorageNodesIfNecessary(HashMap serverProperties, String password,
Set storageNodes) throws Exception {
DatabaseType db = null;
Connection connection = null;
Statement queryStatement = null;
ResultSet resultSet = null;
PreparedStatement insertStorageNode = null;
try {
String dbUrl = serverProperties.get(ServerProperties.PROP_DATABASE_CONNECTION_URL);
String userName = serverProperties.get(ServerProperties.PROP_DATABASE_USERNAME);
connection = getDatabaseConnection(dbUrl, userName, password);
db = DatabaseTypeFactory.getDatabaseType(connection);
if (!(db instanceof PostgresqlDatabaseType || db instanceof OracleDatabaseType)) {
throw new IllegalArgumentException("Unknown database type, can't continue: " + db);
}
queryStatement = connection.createStatement();
resultSet = queryStatement.executeQuery("SELECT count(id) FROM rhq_storage_node");
resultSet.next();
if (resultSet.getInt(1) == 0) {
connection.setAutoCommit(false);
try {
LOG.info("Persisting to database new storage nodes for values specified in server configuration property [rhq.storage.nodes]");
insertStorageNode = connection
.prepareStatement("INSERT INTO rhq_storage_node (id, address, cql_port, operation_mode, ctime, mtime, maintenance_pending) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?)");
int id = 1001;
for (StorageNode storageNode : storageNodes) {
insertStorageNode.setInt(1, id);
insertStorageNode.setString(2, storageNode.getAddress());
insertStorageNode.setInt(3, storageNode.getCqlPort());
insertStorageNode.setString(4, StorageNode.OperationMode.INSTALLED.toString());
insertStorageNode.setLong(5, System.currentTimeMillis());
insertStorageNode.setLong(6, System.currentTimeMillis());
insertStorageNode.setBoolean(7, false);
insertStorageNode.executeUpdate();
id += 1;
}
connection.commit();
} catch (SQLException e) {
LOG.error("Failed to persist to database the storage nodes specified by server configuration "
+ "property [rhq.storage.nodes]. Transaction will be rolled back.", e);
connection.rollback();
throw e;
}
} else {
LOG.info("Storage nodes already exist in database. Server configuration property [rhq.storage.nodes] will be ignored.");
}
} finally {
if (db != null) {
db.closeResultSet(resultSet);
db.closeStatement(queryStatement);
db.closeStatement(insertStorageNode);
db.closeConnection(connection);
}
}
}
public static Map fetchStorageClusterSettings(HashMap serverProperties,
String password) throws Exception {
Map result = new HashMap(5);
DatabaseType db = null;
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
String dbUrl = serverProperties.get(ServerProperties.PROP_DATABASE_CONNECTION_URL);
String userName = serverProperties.get(ServerProperties.PROP_DATABASE_USERNAME);
connection = getDatabaseConnection(dbUrl, userName, password);
db = DatabaseTypeFactory.getDatabaseType(connection);
if (!(db instanceof PostgresqlDatabaseType || db instanceof OracleDatabaseType)) {
throw new IllegalArgumentException("Unknown database type, can't continue: " + db);
}
try {
statement = connection.prepareStatement("" //
+ "SELECT property_key, property_value FROM rhq_system_config " //
+ " WHERE property_key LIKE 'STORAGE%' " //
+ " AND NOT property_value IS NULL ");
resultSet = statement.executeQuery();
while (resultSet.next()) {
String key = resultSet.getString(1);
String value = resultSet.getString(2);
if (key.equals("STORAGE_USERNAME")) {
result.put(ServerProperties.PROP_STORAGE_USERNAME, value);
} else if (key.equals("STORAGE_PASSWORD")) {
result.put(ServerProperties.PROP_STORAGE_PASSWORD, value);
} else if (key.equals("STORAGE_GOSSIP_PORT")) {
result.put(ServerProperties.PROP_STORAGE_GOSSIP_PORT, value);
} else if (key.equals("STORAGE_CQL_PORT")) {
result.put(ServerProperties.PROP_STORAGE_CQL_PORT, value);
}
}
} finally {
db.closeResultSet(resultSet);
db.closeStatement(statement);
}
try {
statement = connection.prepareStatement("" //
+ "SELECT address FROM rhq_storage_node " //
+ " WHERE operation_mode in " + "('" + OperationMode.NORMAL.name()
+ "', '"
+ OperationMode.INSTALLED.name() + "') ");
resultSet = statement.executeQuery();
StringBuffer addressList = new StringBuffer();
while (resultSet.next()) {
String address = resultSet.getString(1);
if(address != null && !address.trim().isEmpty()){
if (addressList.length() != 0) {
addressList.append(',');
}
addressList.append(address);
}
}
if (addressList.length() != 0) {
result.put(ServerProperties.PROP_STORAGE_NODES, addressList.toString());
}
} finally {
db.closeResultSet(resultSet);
db.closeStatement(statement);
}
} catch (SQLException e) {
LOG.error("Failed to fetch storage cluster settings.", e);
throw e;
} finally {
if (db != null) {
db.closeConnection(connection);
}
}
return result;
}
public static void persistStorageClusterSettingsIfNecessary(HashMap serverProperties,
String password) throws Exception {
DatabaseType db = null;
Connection connection = null;
PreparedStatement updateClusterSetting = null;
try {
String dbUrl = serverProperties.get(ServerProperties.PROP_DATABASE_CONNECTION_URL);
String userName = serverProperties.get(ServerProperties.PROP_DATABASE_USERNAME);
connection = getDatabaseConnection(dbUrl, userName, password);
db = DatabaseTypeFactory.getDatabaseType(connection);
if (!(db instanceof PostgresqlDatabaseType || db instanceof OracleDatabaseType)) {
throw new IllegalArgumentException("Unknown database type, can't continue: " + db);
}
connection = getDatabaseConnection(dbUrl, userName, password);
connection.setAutoCommit(false);
updateClusterSetting = connection.prepareStatement("" //
+ "UPDATE rhq_system_config " //
+ " SET property_value = ?, default_property_value = ? " //
+ " WHERE property_key = ? " //
+ " AND ( property_value IS NULL OR property_value = '' OR property_value = 'UNSET' ) ");
updateClusterSetting.setString(1, serverProperties.get(ServerProperties.PROP_STORAGE_USERNAME));
updateClusterSetting.setString(2, serverProperties.get(ServerProperties.PROP_STORAGE_USERNAME));
updateClusterSetting.setString(3, "STORAGE_USERNAME");
updateClusterSetting.executeUpdate();
updateClusterSetting.setString(1, serverProperties.get(ServerProperties.PROP_STORAGE_PASSWORD));
updateClusterSetting.setString(2, serverProperties.get(ServerProperties.PROP_STORAGE_PASSWORD));
updateClusterSetting.setString(3, "STORAGE_PASSWORD");
updateClusterSetting.executeUpdate();
updateClusterSetting.setString(1, serverProperties.get(ServerProperties.PROP_STORAGE_CQL_PORT));
updateClusterSetting.setString(2, serverProperties.get(ServerProperties.PROP_STORAGE_CQL_PORT));
updateClusterSetting.setString(3, "STORAGE_CQL_PORT");
updateClusterSetting.executeUpdate();
updateClusterSetting.setString(1, serverProperties.get(ServerProperties.PROP_STORAGE_GOSSIP_PORT));
updateClusterSetting.setString(2, serverProperties.get(ServerProperties.PROP_STORAGE_GOSSIP_PORT));
updateClusterSetting.setString(3, "STORAGE_GOSSIP_PORT");
updateClusterSetting.executeUpdate();
connection.commit();
} catch (SQLException e) {
LOG.error("Failed to initialize storage cluster settings. Transaction will be rolled back.", e);
connection.rollback();
throw e;
} finally {
if (db != null) {
db.closeStatement(updateClusterSetting);
db.closeConnection(connection);
}
}
}
/**
* Stores the server details (such as the public endpoint) in the database. If the server definition already
* exists, it will be updated; otherwise, a new server will be added to the HA cloud.
*
* @param serverProperties the server properties
* @param password clear text password to connect to the database
* @param serverDetails the details of the server to put into the database
* @throws Exception
*/
public static void storeServerDetails(HashMap serverProperties, String password,
ServerDetails serverDetails) throws Exception {
DatabaseType db = null;
Connection conn = null;
try {
String dbUrl = serverProperties.get(ServerProperties.PROP_DATABASE_CONNECTION_URL);
String userName = serverProperties.get(ServerProperties.PROP_DATABASE_USERNAME);
conn = getDatabaseConnection(dbUrl, userName, password);
db = DatabaseTypeFactory.getDatabaseType(conn);
updateOrInsertServer(db, conn, serverDetails);
} catch (SQLException e) {
// TODO: should we throw an exception here? This would abort the rest of the installation
LOG.info("Unable to store server entry in the database: " + ThrowableUtil.getAllMessages(e));
} finally {
if (null != db) {
db.closeConnection(conn);
}
}
}
private static void updateOrInsertServer(DatabaseType db, Connection conn, ServerDetails serverDetails) {
PreparedStatement stm = null;
ResultSet rs = null;
if (null == serverDetails || isEmpty(serverDetails.getName())) {
return;
}
try {
stm = conn.prepareStatement("UPDATE rhq_server SET address=?, port=?, secure_port=? WHERE name=?");
stm.setString(1, serverDetails.getEndpointAddress());
stm.setInt(2, serverDetails.getEndpointPort());
stm.setInt(3, serverDetails.getEndpointSecurePort());
stm.setString(4, serverDetails.getName());
if (0 == stm.executeUpdate()) {
stm.close();
// set all new servers to operation_mode=INSTALLED
int i = 1;
if (db instanceof PostgresqlDatabaseType || db instanceof OracleDatabaseType) {
stm = conn.prepareStatement("INSERT INTO rhq_server " //
+ " ( id, name, address, port, secure_port, ctime, mtime, operation_mode, compute_power ) " //
+ "VALUES ( ?, ?, ?, ?, ?, ?, ?, 'INSTALLED', 1 )");
stm.setInt(i++, db.getNextSequenceValue(conn, "rhq_server", "id"));
} else {
throw new IllegalArgumentException("Unknown database type, can't continue: " + db);
}
stm.setString(i++, serverDetails.getName());
stm.setString(i++, serverDetails.getEndpointAddress());
stm.setInt(i++, serverDetails.getEndpointPort());
stm.setInt(i++, serverDetails.getEndpointSecurePort());
long now = System.currentTimeMillis();
stm.setLong(i++, now);
stm.setLong(i++, now);
stm.executeUpdate();
}
} catch (SQLException e) {
LOG.info("Unable to put the server details in the database: " + ThrowableUtil.getAllMessages(e));
} finally {
if (null != db) {
db.closeResultSet(rs);
db.closeStatement(stm);
}
}
}
/**
* This will create the database schema in the database. props
define the connection to the database -
*
* Note that if the {@link #isDatabaseSchemaExist schema already exists}, it will be purged of all
* data/tables and recreated.
*
* @param props the full set of server properties
* @param serverDetails additional information about the server being installed
* @param password the database password in clear text
* @param logDir a directory where the db schema upgrade logs can be written
*
* @throws Exception if failed to create the new schema for some reason
*/
public static void createNewDatabaseSchema(HashMap props, ServerDetails serverDetails,
String password, String logDir) throws Exception {
String dbUrl = props.get(ServerProperties.PROP_DATABASE_CONNECTION_URL);
String userName = props.get(ServerProperties.PROP_DATABASE_USERNAME);
try {
// extract the dbsetup files which are located in the dbutils jar
String dbsetupSchemaXmlFile = extractDatabaseXmlFile("db-schema-combined.xml", props, serverDetails, logDir);
String dbsetupDataXmlFile = extractDatabaseXmlFile("db-data-combined.xml", props, serverDetails, logDir);
// first uninstall any old existing schema, then create the tables then insert the data
DBSetup dbsetup = new DBSetup(dbUrl, userName, password);
dbsetup.uninstall(dbsetupSchemaXmlFile);
dbsetup.setup(dbsetupSchemaXmlFile);
dbsetup.setup(dbsetupDataXmlFile, null, true, false);
} catch (Exception e) {
LOG.fatal("Cannot install the database schema - the server will not run properly.", e);
throw e;
}
return;
}
/**
* This will update an existing database schema so it can be upgraded to the latest schema version.
*
* Note that if the {@link #isDatabaseSchemaExist schema does not already exist}, errors will
* occur.
*
* @param props the full set of server properties
* @param serverDetails additional information about the server being installed
* @param password the database password in clear text
* @param logDir a directory where the db schema upgrade logs can be written
*
* @throws Exception if the upgrade failed for some reason
*/
public static void upgradeExistingDatabaseSchema(HashMap props, ServerDetails serverDetails,
String password, String logDir) throws Exception {
String dbUrl = props.get(ServerProperties.PROP_DATABASE_CONNECTION_URL);
String userName = props.get(ServerProperties.PROP_DATABASE_USERNAME);
File logfile = new File(logDir, "rhq-installer-dbupgrade.log");
logfile.delete(); // do not keep logs from previous dbupgrade runs
try {
// extract the dbupgrade ANT script which is located in the dbutils jar
String dbupgradeXmlFile = extractDatabaseXmlFile("db-upgrade.xml", props, serverDetails, logDir);
Properties antProps = new Properties();
antProps.setProperty("jdbc.url", dbUrl);
antProps.setProperty("jdbc.user", userName);
antProps.setProperty("jdbc.password", password);
antProps.setProperty("target.schema.version", "LATEST");
startAnt(new File(dbupgradeXmlFile), "db-ant-tasks.properties", antProps, logfile);
} catch (Exception e) {
LOG.fatal("Cannot upgrade the database schema - the server will not run properly.", e);
throw e;
}
return;
}
/**
* Given a server property value string, returns true if it is not specified.
*
* @param s the property string value
*
* @return true if it is null or empty
*/
public static boolean isEmpty(String s) {
return s == null || s.trim().length() == 0;
}
/**
* Takes the named XML file from the classloader and writes the file to the log directory. This is meant to extract
* the schema/data xml files from the dbutils jar file. It can also be used to extract the db upgrade XML file.
*
* @param xmlFileName the name of the XML file, as found in the classloader
* @param props properties whose values are used to replace the replacement strings found in the XML file
* @param serverDetails additional information about the server being installed
* @param logDir a directory where the db schema upgrade logs can be written
*
* @return the absolute path to the extracted file
*
* @throws IOException if failed to extract the file to the log directory
*/
private static String extractDatabaseXmlFile(String xmlFileName, HashMap props,
ServerDetails serverDetails, String logDir) throws IOException {
// first slurp the file contents in memory
InputStream resourceInStream = ServerInstallUtil.class.getClassLoader().getResourceAsStream(xmlFileName);
ByteArrayOutputStream contentOutStream = new ByteArrayOutputStream();
StreamUtil.copy(resourceInStream, contentOutStream);
// now replace their replacement strings with values from the properties
String emailFromAddress = props.get(ServerProperties.PROP_EMAIL_FROM_ADDRESS);
if (isEmpty(emailFromAddress)) {
emailFromAddress = "rhqadmin@localhost";
}
String httpPort = props.get(ServerProperties.PROP_WEB_HTTP_PORT);
if (isEmpty(httpPort)) {
httpPort = String.valueOf(ServerDetails.DEFAULT_ENDPOINT_PORT);
}
String publicEndpoint = serverDetails.getEndpointAddress();
if (isEmpty(publicEndpoint)) {
try {
publicEndpoint = props.get(ServerProperties.PROP_JBOSS_BIND_ADDRESS);
if (isEmpty(publicEndpoint) || ("0.0.0.0".equals(publicEndpoint))) {
publicEndpoint = InetAddress.getLocalHost().getHostAddress();
}
} catch (Exception e) {
publicEndpoint = "127.0.0.1";
}
}
String content = contentOutStream.toString();
content = content.replaceAll("@@@LARGE_TABLESPACE_FOR_DATA@@@", "DEFAULT");
content = content.replaceAll("@@@LARGE_TABLESPACE_FOR_INDEX@@@", "DEFAULT");
content = content.replaceAll("@@@ADMINUSERNAME@@@", "rhqadmin");
content = content.replaceAll("@@@ADMINPASSWORD@@@", "x1XwrxKuPvYUILiOnOZTLg=="); // rhqadmin
content = content.replaceAll("@@@ADMINEMAIL@@@", emailFromAddress);
content = content.replaceAll("@@@BASEURL@@@", "http://" + publicEndpoint + ":" + httpPort + "/");
content = content.replaceAll("@@@JAASPROVIDER@@@", "JDBC");
content = content.replaceAll("@@@LDAPURL@@@", "ldap://localhost/");
content = content.replaceAll("@@@LDAPPROTOCOL@@@", "");
content = content.replaceAll("@@@LDAPLOGINPROP@@@", "cn");
content = content.replaceAll("@@@LDAPBASEDN@@@", "o=JBoss,c=US");
content = content.replaceAll("@@@LDAPSEARCHFILTER@@@", "");
content = content.replaceAll("@@@LDAPBINDDN@@@", "");
content = content.replaceAll("@@@LDAPBINDPW@@@", "");
content = content.replaceAll("@@@MULTICAST_ADDR@@@", "");
content = content.replaceAll("@@@MULTICAST_PORT@@@", "");
// we now have the finished XML content - write out the file to the log directory
File xmlFile = new File(logDir, xmlFileName);
FileOutputStream xmlFileOutStream = new FileOutputStream(xmlFile);
ByteArrayInputStream contentInStream = new ByteArrayInputStream(content.getBytes());
StreamUtil.copy(contentInStream, xmlFileOutStream);
return xmlFile.getAbsolutePath();
}
/**
* Launches ANT and runs the default target in the given build file.
*
* @param buildFile the build file that ANT will run
* @param customTaskDefs the properties file found in classloader that contains all the taskdef definitions
* @param properties set of properties to set for the ANT task to access
* @param logFile where ANT messages will be logged (in addition to the app server's log file)
*
* @throws RuntimeException
*/
private static void startAnt(File buildFile, String customTaskDefs, Properties properties, File logFile) {
PrintWriter logFileOutput = null;
try {
logFileOutput = new PrintWriter(new FileOutputStream(logFile));
ClassLoader classLoader = ServerInstallUtil.class.getClassLoader();
Properties taskDefs = new Properties();
InputStream taskDefsStream = classLoader.getResourceAsStream(customTaskDefs);
try {
taskDefs.load(taskDefsStream);
} finally {
taskDefsStream.close();
}
Project project = new Project();
project.setCoreLoader(classLoader);
project.init();
for (Map.Entry
© 2015 - 2025 Weber Informatics LLC | Privacy Policy