[rhq] Branch 'rhq-on-as7' - modules/enterprise
mazz
mazz at fedoraproject.org
Sat Aug 4 04:22:19 UTC 2012
modules/enterprise/gui/installer/pom.xml | 7
modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/Installer.java | 19
modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/gwt/InstallerGWTService.java | 10
modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerDetails.java | 16
modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerProperties.java | 1
modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/InstallerGWTServiceImpl.java | 186 ++++++-
modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/LoggerAntBuildListener.java | 103 ++++
modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/ServerInstallUtil.java | 255 +++++++++-
8 files changed, 559 insertions(+), 38 deletions(-)
New commits:
commit e85f349ab11cfb5fb0bf7c18396c8023724ff813
Author: John Mazzitelli <mazz at redhat.com>
Date: Sat Aug 4 00:22:13 2012 -0400
gwt installer is getting close to doing dbsetup/dbupgrade
diff --git a/modules/enterprise/gui/installer/pom.xml b/modules/enterprise/gui/installer/pom.xml
index 98e7b07..b784cb6 100644
--- a/modules/enterprise/gui/installer/pom.xml
+++ b/modules/enterprise/gui/installer/pom.xml
@@ -66,6 +66,13 @@
<version>${project.version}</version>
</dependency>
+ <!-- dbutils has transitive dep on ant-launcher but with test scope - installer needs it for real -->
+ <dependency>
+ <groupId>ant</groupId>
+ <artifactId>ant-launcher</artifactId>
+ <version>1.6.5</version>
+ </dependency>
+
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-servlet</artifactId>
diff --git a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/Installer.java b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/Installer.java
index 3df3460..571f61b 100644
--- a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/Installer.java
+++ b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/Installer.java
@@ -202,7 +202,15 @@ public class Installer implements EntryPoint {
installButton.setDisabled(true); // we can't allow the user to install yet
installButton.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
- installerService.install(serverProperties.getMap(), new AsyncCallback<Void>() {
+ String highAvailabilityName = serverSettingServerName.getValueAsString();
+ String publicEndpoint = serverSettingPublicAddress.getValueAsString();
+ int httpPort = Integer.parseInt(serverSettingWebHttpPort.getValueAsString());
+ int httpsPort = Integer.parseInt(serverSettingWebSecureHttpPort.getValueAsString());
+ String existingSchemaOption = dbExistingSchemaOption.getValueAsString();
+
+ HashMap<String, String> propMap = serverProperties.getMap();
+ ServerDetails svrDetails = new ServerDetails(highAvailabilityName, publicEndpoint, httpPort, httpsPort);
+ installerService.install(propMap, svrDetails, existingSchemaOption, new AsyncCallback<Void>() {
@Override
public void onSuccess(Void result) {
SC.say("TODO: the installer should do something");
@@ -359,10 +367,11 @@ public class Installer implements EntryPoint {
});
dbExistingSchemaOption = new SelectItem("existingSchemaOption", MSG.schema_update_question());
+ // keys must match ServerInstallUtil.ExistingSchemaOption enum names
final LinkedHashMap<String, String> schemaOpt = new LinkedHashMap<String, String>();
- schemaOpt.put("keep", MSG.schema_update_keep());
- schemaOpt.put("overwrite", MSG.schema_update_overwrite());
- schemaOpt.put("skip", MSG.schema_update_skip());
+ schemaOpt.put("KEEP", MSG.schema_update_keep());
+ schemaOpt.put("OVERWRITE", MSG.schema_update_overwrite());
+ schemaOpt.put("SKIP", MSG.schema_update_skip());
dbExistingSchemaOption.setValueMap(schemaOpt);
dbExistingSchemaOption.setDefaultToFirstOption(true);
dbExistingSchemaOption.setVisible(false);
@@ -486,6 +495,7 @@ public class Installer implements EntryPoint {
serverSettingWebHttpPort.setWidth(fieldWidth);
serverSettingWebHttpPort.setMin(1);
serverSettingWebHttpPort.setMax(65535);
+ serverSettingWebHttpPort.setDefaultValue(ServerDetails.DEFAULT_ENDPOINT_PORT);
serverSettingWebHttpPort.addChangedHandler(new ChangedHandler() {
public void onChanged(ChangedEvent event) {
updateServerProperty(ServerProperties.PROP_WEB_HTTP_PORT, String.valueOf(event.getValue()));
@@ -497,6 +507,7 @@ public class Installer implements EntryPoint {
serverSettingWebSecureHttpPort.setWidth(fieldWidth);
serverSettingWebSecureHttpPort.setMin(1);
serverSettingWebSecureHttpPort.setMax(65535);
+ serverSettingWebSecureHttpPort.setDefaultValue(ServerDetails.DEFAULT_ENDPOINT_SECURE_PORT);
serverSettingWebSecureHttpPort.addChangedHandler(new ChangedHandler() {
public void onChanged(ChangedEvent event) {
updateServerProperty(ServerProperties.PROP_WEB_HTTPS_PORT, String.valueOf(event.getValue()));
diff --git a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/gwt/InstallerGWTService.java b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/gwt/InstallerGWTService.java
index 56c9684..97e776e 100644
--- a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/gwt/InstallerGWTService.java
+++ b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/gwt/InstallerGWTService.java
@@ -24,6 +24,7 @@ import java.util.HashMap;
import com.google.gwt.user.client.rpc.RemoteService;
import org.rhq.enterprise.gui.installer.client.shared.ServerDetails;
+import org.rhq.enterprise.gui.installer.server.servlet.ServerInstallUtil;
/**
* Remote RPC API for the GWT Installer.
@@ -37,9 +38,16 @@ public interface InstallerGWTService extends RemoteService {
*
* @param serverProperties the server's settings to use. These will be persisted to
* the server's .properties file.
+ * @param serverDetails details on the server being installed.
+ * If in auto-install mode, this value is ignored and can be anything.
+ * @param existingSchemaOption if not in auto-install mode, this tells the installer what to do with any
+ * existing schema. Must be one of the names of the
+ * {@link ServerInstallUtil.ExistingSchemaOption} enum.
+ * If in auto-install mode, this value is ignored and can be anything.
* @throws Exception
*/
- void install(HashMap<String, String> serverProperties) throws Exception;
+ void install(HashMap<String, String> serverProperties, ServerDetails serverDetails, String existingSchemaOption)
+ throws Exception;
/**
* Returns a list of all registered servers in the database.
diff --git a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerDetails.java b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerDetails.java
index 7e07073..44bfb6a 100644
--- a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerDetails.java
+++ b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerDetails.java
@@ -37,18 +37,16 @@ public class ServerDetails implements Serializable {
private String endpointAddress;
private int endpointPort;
private int endpointSecurePort;
- private String affinityGroup;
protected ServerDetails() {
// for GWT
}
- public ServerDetails(String name, String endpointAddress, int port, int securePort, String affinityGroup) {
+ public ServerDetails(String name, String endpointAddress, int port, int securePort) {
this.name = name;
this.endpointAddress = endpointAddress;
this.endpointPort = port;
this.endpointSecurePort = securePort;
- this.affinityGroup = affinityGroup;
}
public String getName() {
@@ -103,19 +101,9 @@ public class ServerDetails implements Serializable {
this.endpointSecurePort = Integer.valueOf(endpointSecurePort).intValue();
}
- public String getAffinityGroup() {
- return affinityGroup;
- }
-
- public void setAffinityGroup(String affinityGroup) {
- if ((null != affinityGroup) && (!"".equals(affinityGroup.trim()))) {
- this.affinityGroup = affinityGroup;
- }
- }
-
@Override
public String toString() {
return "[name=" + name + ", address=" + endpointAddress + ", port=" + endpointPort + ", secureport="
- + endpointSecurePort + ", affinitygroup=" + affinityGroup + "]";
+ + endpointSecurePort + "]";
}
}
diff --git a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerProperties.java b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerProperties.java
index 0ea750a..1236024 100644
--- a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerProperties.java
+++ b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/client/shared/ServerProperties.java
@@ -113,6 +113,7 @@ public class ServerProperties {
public static final String PROP_CONCURRENCY_LIMIT_MEAS_REPORT = "rhq.server.concurrency-limit.measurement-report";
public static final String PROP_CONCURRENCY_LIMIT_MEASSCHED_REQ = "rhq.server.concurrency-limit.measurement-schedule-request";
+ public static final String PROP_JBOSS_BIND_ADDRESS = "jboss.bind.address";
public static final String PROP_HIGH_AVAILABILITY_NAME = "rhq.server.high-availability.name";
public static final String PROP_MM_AT_START = "rhq.server.maintenance-mode-at-startup";
public static final String PROP_OPERATION_TIMEOUT = "rhq.server.operation-timeout";
diff --git a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/InstallerGWTServiceImpl.java b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/InstallerGWTServiceImpl.java
index b390987..4b16898 100644
--- a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/InstallerGWTServiceImpl.java
+++ b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/InstallerGWTServiceImpl.java
@@ -19,6 +19,7 @@
package org.rhq.enterprise.gui.installer.server.servlet;
import java.io.File;
+import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
@@ -42,6 +43,7 @@ import org.rhq.enterprise.gui.installer.client.gwt.InstallerGWTService;
import org.rhq.enterprise.gui.installer.client.shared.ServerDetails;
import org.rhq.enterprise.gui.installer.client.shared.ServerProperties;
import org.rhq.enterprise.gui.installer.server.service.ManagementService;
+import org.rhq.enterprise.gui.installer.server.servlet.ServerInstallUtil.ExistingSchemaOption;
/**
* Remote RPC API implementation for the GWT Installer.
@@ -56,13 +58,45 @@ public class InstallerGWTServiceImpl extends RemoteServiceServlet implements Ins
private static final String RHQ_SECURITY_DOMAIN = "RHQDSSecurityDomain";
@Override
- public void install(HashMap<String, String> serverProperties) throws Exception {
+ public void install(HashMap<String, String> serverProperties, ServerDetails serverDetails, String existingSchemaOption) throws Exception {
+ // make sure the data is at least in the correct format (booleans are true/false, integers are valid numbers)
+ StringBuilder dataErrors = new StringBuilder();
+ for (Map.Entry<String, String> entry : serverProperties.entrySet()) {
+ String name = entry.getKey();
+ if (ServerProperties.BOOLEAN_PROPERTIES.contains(name)) {
+ String newValue = entry.getValue();
+ if (!(newValue.equals("true") || newValue.equals("false"))) {
+ dataErrors.append("[" + name + "] must be 'true' or 'false' : [" + newValue + "]\n");
+ }
+ } else if (ServerProperties.INTEGER_PROPERTIES.contains(name)) {
+ String newValue = entry.getValue();
+ try {
+ Integer.parseInt(newValue);
+ } catch (NumberFormatException e) {
+ if (ServerInstallUtil.isEmpty(newValue) && name.equals(ServerProperties.PROP_CONNECTOR_BIND_PORT)) {
+ // this is a special setting and is allowed to be empty
+ } else {
+ dataErrors.append("[" + name + "] must be a number : [" + newValue + "]\n");
+ }
+ }
+ }
+ }
+ if (dataErrors.length() > 0) {
+ throw new Exception("Cannot install due to data errors:\n" + dataErrors.toString());
+ }
+
+ // if we are in auto-install mode, ignore the server details passed in and build our own using the given server properties
+ boolean autoInstallMode = ServerInstallUtil.isAutoinstallEnabled(serverProperties);
+ if (autoInstallMode) {
+ serverDetails = getServerDetailsFromPropertiesOnly(serverProperties);
+ }
+
// its possible the JDBC URL was changed, clear the factory cache in case the DB version is different now
DatabaseTypeFactory.clearDatabaseTypeCache();
// determine the type of database to connect to
String databaseType = serverProperties.get(ServerProperties.PROP_DATABASE_TYPE);
- if (databaseType == null || databaseType.length() == 0) {
+ if (ServerInstallUtil.isEmpty(databaseType)) {
throw new Exception("Please indicate the type of database to connect to");
}
@@ -131,18 +165,54 @@ public class InstallerGWTServiceImpl extends RemoteServiceServlet implements Ins
// test the connection to make sure everything is OK - note that if we are in auto-install mode,
// the password will have been obfuscated, so we need to de-obfucate it in order to use it.
- String url = serverProperties.get(ServerProperties.PROP_DATABASE_CONNECTION_URL);
- String user = serverProperties.get(ServerProperties.PROP_DATABASE_USERNAME);
- String pw = serverProperties.get(ServerProperties.PROP_DATABASE_PASSWORD);
- if (ServerInstallUtil.isAutoinstallEnabled(serverProperties)) {
- pw = ServerInstallUtil.deobfuscatePassword(pw);
+ String dbUrl = serverProperties.get(ServerProperties.PROP_DATABASE_CONNECTION_URL);
+ String dbUsername = serverProperties.get(ServerProperties.PROP_DATABASE_USERNAME);
+ String dbPassword = serverProperties.get(ServerProperties.PROP_DATABASE_PASSWORD);
+ if (autoInstallMode) {
+ dbPassword = ServerInstallUtil.deobfuscatePassword(dbPassword);
}
- String testConnectionErrorMessage = testConnection(url, user, pw);
+ String testConnectionErrorMessage = testConnection(dbUrl, dbUsername, dbPassword);
if (testConnectionErrorMessage != null) {
throw new Exception("Cannot connect to the database: " + testConnectionErrorMessage);
}
- // TODO: CONTINUE WITH CONFIGURATION BEAN LINE 712
+ // write the new properties to the rhq-server.properties file
+ saveServerProperties(serverProperties);
+
+ // Prepare the db schema.
+ // existingSchemaOption is either overwrite, keep or skip.
+ // If in auto-install mode, we can be told to overwrite, skip or auto (meaning "keep" if schema exists)
+ ServerInstallUtil.ExistingSchemaOption existingSchemaOptionEnum;
+ if (autoInstallMode) {
+ String s = serverProperties.get(ServerProperties.PROP_AUTOINSTALL_DATABASE);
+ if (s == null || s.equalsIgnoreCase("auto")) {
+ existingSchemaOptionEnum = ServerInstallUtil.ExistingSchemaOption.KEEP;
+ } else {
+ existingSchemaOptionEnum = ServerInstallUtil.ExistingSchemaOption.valueOf(s);
+ }
+ } else {
+ if (existingSchemaOption == null) {
+ throw new Exception("Don't know what to do with the database schema");
+ }
+ existingSchemaOptionEnum = ServerInstallUtil.ExistingSchemaOption.valueOf(existingSchemaOption);
+ }
+
+ if (ServerInstallUtil.ExistingSchemaOption.SKIP != existingSchemaOptionEnum) {
+ if (isDatabaseSchemaExist(dbUrl, dbUsername, dbPassword)) {
+ if (ExistingSchemaOption.OVERWRITE == existingSchemaOptionEnum) {
+ ServerInstallUtil.createNewDatabaseSchema(serverProperties, serverDetails, dbPassword, getLogDir());
+ } else {
+ ServerInstallUtil.upgradeExistingDatabaseSchema(serverProperties, serverDetails, dbPassword,
+ getLogDir());
+ }
+ } else {
+ ServerInstallUtil.createNewDatabaseSchema(serverProperties, serverDetails, dbPassword, getLogDir());
+ }
+ }
+
+ // TODO: CONTINUE WITH CONFIGURATION BEAN LINE 777
+ if (true)
+ throw new IllegalArgumentException("testing");
return;
}
@@ -227,6 +297,12 @@ public class InstallerGWTServiceImpl extends RemoteServiceServlet implements Ins
propsFile.update(props);
+ // we need to put them as system properties now so when we hot deploy,
+ // the replacement variables in the config files pick up the new values
+ for (Map.Entry<String, String> entry : serverProperties.entrySet()) {
+ System.setProperty(entry.getKey(), entry.getValue());
+ }
+
return;
}
@@ -252,12 +328,104 @@ public class InstallerGWTServiceImpl extends RemoteServiceServlet implements Ins
return dir;
}
+ private String getLogDir() throws Exception {
+ File asHomeDir = new File(getAppServerHomeDir());
+ File logDir = new File(asHomeDir, "../logs"); // this is RHQ's log dir, not JBossAS's log dir
+ logDir.mkdirs(); // create it in case it doesn't yet exist
+ return logDir.getAbsolutePath();
+ }
+
private File getServerPropertiesFile() throws Exception {
File appServerHomeDir = new File(getAppServerHomeDir());
File serverPropertiesFile = new File(appServerHomeDir, "../bin/rhq-server.properties");
return serverPropertiesFile;
}
+ /**
+ * Returns server details based on information found solely in the given server properties.
+ * It does not rely on any database access.
+ *
+ * This is used by the auto-installation process.
+ *
+ * @param serverProperties the server properties
+ * @throws Exception
+ */
+ public ServerDetails getServerDetailsFromPropertiesOnly(HashMap<String, String> serverProperties) throws Exception {
+
+ String highAvailabilityName = serverProperties.get(ServerProperties.PROP_HIGH_AVAILABILITY_NAME);
+ String publicEndpoint = serverProperties.get(ServerProperties.PROP_AUTOINSTALL_PUBLIC_ADDR);
+ int port;
+ int securePort;
+
+ if (ServerInstallUtil.isEmpty(highAvailabilityName)) {
+ try {
+ highAvailabilityName = InetAddress.getLocalHost().getCanonicalHostName();
+ } catch (Exception e) {
+ log("Could not determine default server name: ", e);
+ throw new Exception("Server name is not preconfigured and could not be determined automatically");
+ }
+ }
+
+ // 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.
+ if (ServerInstallUtil.isEmpty(publicEndpoint)) {
+ String connBindAddress = serverProperties.get(ServerProperties.PROP_CONNECTOR_BIND_ADDRESS);
+ if ((!ServerInstallUtil.isEmpty(connBindAddress)) && (!"0.0.0.0".equals(connBindAddress.trim()))) {
+ // the server-side connector bind address is explicitly set, use that
+ publicEndpoint = connBindAddress.trim();
+ } else {
+ String serverBindAddress = serverProperties.get(ServerProperties.PROP_JBOSS_BIND_ADDRESS);
+ if ((!ServerInstallUtil.isEmpty(serverBindAddress)) && (!"0.0.0.0".equals(serverBindAddress.trim()))) {
+ // the main JBossAS server bind address is set and it isn't 0.0.0.0, use that
+ publicEndpoint = serverBindAddress.trim();
+ } else {
+ try {
+ publicEndpoint = InetAddress.getLocalHost().getCanonicalHostName();
+ } catch (Exception e) {
+ log("Could not determine default public endpoint address: ", e);
+ throw new Exception(
+ "Public endpoint address not preconfigured and could not be determined automatically");
+ }
+ }
+ }
+ }
+
+ // 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.
+ String connectorTransport = serverProperties.get(ServerProperties.PROP_CONNECTOR_TRANSPORT);
+ if (connectorTransport != null && connectorTransport.contains("socket")) {
+ // we aren't using the (ssl)servlet protocol, take the connector bind port and use it for the public endpoint ports
+ String connectorBindPort = serverProperties.get(ServerProperties.PROP_CONNECTOR_BIND_PORT);
+ if (ServerInstallUtil.isEmpty(connectorBindPort) || "0".equals(connectorBindPort.trim())) {
+ throw new Exception("Using non-servlet transport [" + connectorTransport + "] but didn't define a port");
+ }
+ port = Integer.parseInt(connectorBindPort);
+ securePort = Integer.parseInt(connectorBindPort);
+ } else {
+ // this is the typical use-case - the transport is probably (ssl)servlet so use the web http/https ports
+ try {
+ port = Integer.parseInt(serverProperties.get(ServerProperties.PROP_WEB_HTTP_PORT));
+ } catch (Exception e) {
+ log("Could not determine port, will use default: " + e);
+ port = ServerDetails.DEFAULT_ENDPOINT_PORT;
+ }
+ try {
+ securePort = Integer.parseInt(serverProperties.get(ServerProperties.PROP_WEB_HTTPS_PORT));
+ } catch (Exception e) {
+ log("Could not determine secure port, will use default: " + e);
+ securePort = ServerDetails.DEFAULT_ENDPOINT_SECURE_PORT;
+ }
+ }
+
+ // everything looks good
+ ServerDetails serverDetails = new ServerDetails(highAvailabilityName, publicEndpoint, port, securePort);
+ return serverDetails;
+ }
+
private ModelControllerClient getClient() {
ModelControllerClient client = ManagementService.getClient();
return client;
diff --git a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/LoggerAntBuildListener.java b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/LoggerAntBuildListener.java
new file mode 100644
index 0000000..822eb34
--- /dev/null
+++ b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/LoggerAntBuildListener.java
@@ -0,0 +1,103 @@
+/*
+ * 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.gui.installer.server.servlet;
+
+import java.io.PrintWriter;
+import java.util.Date;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+
+/**
+ * Listens for ANT build events and logs them to a log stream.
+ *
+ * @author John Mazzitelli
+ */
+public class LoggerAntBuildListener implements BuildListener {
+ private PrintWriter output;
+
+ public LoggerAntBuildListener(PrintWriter logFile) {
+ output = logFile;
+
+ // just output our first line with the date this was started
+ output.println(new Date().toString());
+ }
+
+ public void buildFinished(BuildEvent event) {
+ logEvent(event, "FINISHED!");
+ }
+
+ public void buildStarted(BuildEvent event) {
+ logEvent(event, "STARTED!");
+ }
+
+ public void messageLogged(BuildEvent event) {
+ logEvent(event, null);
+ }
+
+ public void targetFinished(BuildEvent event) {
+ logEvent(event, null);
+ }
+
+ public void targetStarted(BuildEvent event) {
+ logEvent(event, null);
+ }
+
+ public void taskFinished(BuildEvent event) {
+ logEvent(event, null);
+ }
+
+ public void taskStarted(BuildEvent event) {
+ logEvent(event, null);
+ }
+
+ private void logEvent(BuildEvent event, String additionalMessage) {
+ String message = event.getMessage();
+ Throwable exception = event.getException();
+ Target target = event.getTarget();
+ Task task = event.getTask();
+
+ if (additionalMessage != null) {
+ output.println(additionalMessage);
+ }
+
+ if (target != null) {
+ output.print("[" + target.getName() + "] ");
+ }
+
+ if (task != null) {
+ output.print("[" + task.getTaskName() + "] ");
+ }
+
+ if (message != null) {
+ output.print(message);
+ }
+
+ if (exception != null) {
+ output.println();
+ exception.printStackTrace(output);
+ }
+
+ output.println();
+
+ return;
+ }
+}
\ No newline at end of file
diff --git a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/ServerInstallUtil.java b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/ServerInstallUtil.java
index 1bbeb81..8952ef7 100644
--- a/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/ServerInstallUtil.java
+++ b/modules/enterprise/gui/installer/src/main/java/org/rhq/enterprise/gui/installer/server/servlet/ServerInstallUtil.java
@@ -18,7 +18,15 @@
*/
package org.rhq.enterprise.gui.installer.server.servlet;
+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.lang.reflect.Method;
+import java.net.InetAddress;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -26,14 +34,20 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
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.rhq.core.db.DatabaseType;
import org.rhq.core.db.DatabaseTypeFactory;
import org.rhq.core.db.DbUtil;
+import org.rhq.core.db.setup.DBSetup;
import org.rhq.core.util.exception.ThrowableUtil;
+import org.rhq.core.util.stream.StreamUtil;
import org.rhq.enterprise.gui.installer.client.shared.ServerDetails;
import org.rhq.enterprise.gui.installer.client.shared.ServerProperties;
@@ -49,6 +63,16 @@ public class ServerInstallUtil {
OVERWRITE, KEEP, SKIP
};
+ /**
+ * 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<String, String> serverProperties) {
String enableProp = serverProperties.get(ServerProperties.PROP_AUTOINSTALL_ENABLE);
if (enableProp != null) {
@@ -57,11 +81,6 @@ public class ServerInstallUtil {
return false;
}
- public static boolean isKeepExistingSchema(ExistingSchemaOption existingSchemaOption) {
- return ExistingSchemaOption.KEEP.name().equals(existingSchemaOption)
- || ExistingSchemaOption.SKIP.name().equals(existingSchemaOption);
- }
-
/**
* Returns <code>true</code> 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.
@@ -176,19 +195,18 @@ public class ServerInstallUtil {
try {
stm = conn.prepareStatement("" //
- + "SELECT s.address, s.port, s.secure_port, ag.name " //
- + " FROM rhq_server s LEFT JOIN rhq_affinity_group ag ON ag.id = s.affinity_group_id " //
+ + "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), rs.getString(4));
+ result = new ServerDetails(serverName, rs.getString(1), rs.getInt(2), rs.getInt(3));
}
-
} catch (SQLException e) {
- LOG.info("Unable to get affinity group name for server: " + e.getMessage());
+ LOG.info("Unable to get server details for server [" + serverName + "]: " + e.getMessage());
} finally {
if (null != db) {
db.closeResultSet(rs);
@@ -331,4 +349,221 @@ public class ServerInstallUtil {
throw new RuntimeException("de-obfuscating db password failed: ", e);
}
}
+
+ /**
+ * This will create the database schema in the database. <code>props</code> define the connection to the database -
+ *
+ * <p>Note that if the {@link #isDatabaseSchemaExist(Properties) schema already exists}, it will be purged of all
+ * data/tables and recreated.</p>
+ *
+ * @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<String, String> 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.
+ *
+ * <p>Note that if the {@link #isDatabaseSchemaExist(Properties) schema does not already exist}, errors will
+ * occur.</p>
+ *
+ * @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<String, String> 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<String, String> 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 at 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<Object, Object> property : properties.entrySet()) {
+ project.setProperty(property.getKey().toString(), property.getValue().toString());
+ }
+
+ // notice we add our listener after we set the properties - we do not want the password to be in the log file
+ // our dbupgrade script will echo the property settings, so we can still get the other values
+ project.addBuildListener(new LoggerAntBuildListener(logFileOutput));
+
+ for (Map.Entry<Object, Object> taskDef : taskDefs.entrySet()) {
+ project.addTaskDefinition(taskDef.getKey().toString(),
+ Class.forName(taskDef.getValue().toString(), true, classLoader));
+ }
+
+ new ProjectHelper2().parse(project, buildFile);
+ project.executeTarget(project.getDefaultTarget());
+
+ } catch (Exception e) {
+ throw new RuntimeException("Cannot run ANT on script [" + buildFile + "]. Cause: " + e, e);
+ } finally {
+ if (logFileOutput != null) {
+ logFileOutput.close();
+ }
+ }
+ }
}
More information about the rhq-commits
mailing list