[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