/*
* RHQ Management Platform
* Copyright (C) 2005-2010 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.helpers.perftest.support.dbsetup;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Target;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.UnknownElement;
import org.apache.tools.ant.helper.ProjectHelper2;
import org.rhq.core.db.ant.dbupgrade.DBUpgrader;
import org.rhq.core.db.setup.DBSetup;
/**
* This class is a utility wrapper around the actual {@link DBSetup} and {@link DBUpgrader} classes defined in the
* rhq-core-dbutils module.
*
* @author Lukas Krejci
*/
public class DbSetup {
private static final String MINIMAL_VERSION_OF_DATA = "db-data-combined.2.94.xml";
private static final String MINIMAL_VERSION_OF_SCHEMA = "db-schema-combined.2.94.xml";
private static final Map<String, String> REPLACEMENTS;
static {
REPLACEMENTS = new HashMap<String, String>();
// these are all the replacements from the dbsetup-build.xml
REPLACEMENTS.put("@@@SERVER_VERSION@@@", "project.version");
REPLACEMENTS.put("@@@DB_SCHEMA_VERSION@@@", "db.schema.version");
REPLACEMENTS.put("@@@ADMINUSERNAME@@@", "server.admin.username");
REPLACEMENTS.put("@@@ADMINPASSWORD@@@", "server.admin.password.encrypted");
REPLACEMENTS.put("@@@ADMINEMAIL@@@", "server.admin.email");
REPLACEMENTS.put("@@@BASEURL@@@", "server.webapp.baseurl");
REPLACEMENTS.put("@@@JAASPROVIDER@@@", "server.jaas.provider");
REPLACEMENTS.put("@@@LDAPURL@@@", "server.ldap.url");
REPLACEMENTS.put("@@@LDAPPROTOCOL@@@", "server.ldap.protocol");
REPLACEMENTS.put("@@@LDAPLOGINPROP@@@", "server.ldap.loginProperty");
REPLACEMENTS.put("@@@LDAPBASEDN@@@", "server.ldap.baseDN");
REPLACEMENTS.put("@@@LDAPSEARCHFILTER@@@", "server.ldap.searchFilter");
REPLACEMENTS.put("@@@LDAPBINDDN@@@", "server.ldap.bindDN");
REPLACEMENTS.put("@@@LDAPBINDPW@@@", "server.ldap.bindPW");
REPLACEMENTS.put("@@@MULTICAST_ADDR@@@", "server.highavail.address");
REPLACEMENTS.put("@@@MULTICAST_PORT@@@", "server.highavail.port");
}
private Connection connection;
public DbSetup(Connection connection) throws Exception {
this.connection = connection;
}
public void setup(String targetVersion) throws Exception {
setup();
upgrade(targetVersion);
}
public void upgrade(String targetVersion) throws Exception {
Project project = new Project();
File upgradeFile = getFileFromDbUtils("db-upgrade.xml");
try {
project.setCoreLoader(getClass().getClassLoader());
project.init();
project.setProperty("target.schema.version", targetVersion == null ? "LATEST" : targetVersion);
loadDbSetupAntTasksProperties(project);
new ProjectHelper2().parse(project, upgradeFile);
Target defaultTarget = (Target) project.getTargets().get(project.getDefaultTarget());
for (Task t : defaultTarget.getTasks()) {
DBUpgrader upgrader = null;
if (t instanceof DBUpgrader) {
upgrader = (DBUpgrader) t;
} else if (t instanceof UnknownElement) {
if ("dbupgrade".equals(t.getTaskType())) {
UnknownElement u = (UnknownElement)t;
u.maybeConfigure();
if (u.getTask() instanceof DBUpgrader) {
upgrader = (DBUpgrader) u.getTask();
}
}
}
if (upgrader != null) {
upgrader.setConnection(connection);
break;
}
}
project.executeTarget(project.getDefaultTarget());
} catch (BuildException e) {
throw new RuntimeException("Cannot run ANT on script [" + upgradeFile + "]. Cause: " + e, e);
} finally {
upgradeFile.delete();
}
}
private void setup() throws Exception {
DBSetup dbSetup = new DBSetup(connection);
File minimalSchema = getFileFromResource(MINIMAL_VERSION_OF_SCHEMA, getClass().getClassLoader());
File currentSchema = getFileFromDbUtils("db-schema-combined.xml");
try {
replaceTokensInFile(minimalSchema);
replaceTokensInFile(currentSchema);
dbSetup.uninstall(currentSchema.getAbsolutePath());
dbSetup.setup(minimalSchema.getAbsolutePath());
} finally {
minimalSchema.delete();
currentSchema.delete();
}
File data = getFileFromResource(MINIMAL_VERSION_OF_DATA, getClass().getClassLoader());
try {
replaceTokensInFile(data);
dbSetup.setup(data.getAbsolutePath(), null, true, false);
} finally {
data.delete();
}
}
private void replaceTokensInFile(File f) throws IOException {
Properties properties = getDbSetupProperties();
String contents = readIntoString(new FileInputStream(f));
for(Map.Entry<String, String> entry : REPLACEMENTS.entrySet()) {
String token = entry.getKey();
String value = properties.getProperty(token);
if (value != null) {
contents = contents.replaceAll(token, value);
}
}
FileWriter wrt = new FileWriter(f);
try {
wrt.write(contents.toCharArray());
} finally {
safeClose(wrt);
}
}
private static Properties getDbSetupProperties() throws IOException {
Properties dbSetupProperties = loadPropertiesFromDbUtils("dbsetup.properties");
//add the project.version manually because that has to be found out in a different way
dbSetupProperties.put("project.version", DbSetup.class.getPackage().getImplementationVersion());
return dbSetupProperties;
}
private static void loadDbSetupAntTasksProperties(Project project) throws Exception {
Properties taskDefs = loadPropertiesFromDbUtils("db-ant-tasks.properties");
for(Map.Entry<Object, Object> entry : taskDefs.entrySet()) {
String taskName = (String) entry.getKey();
String taskClassName = (String) entry.getValue();
project.addTaskDefinition(taskName, Class.forName(taskClassName));
}
}
private static Properties loadPropertiesFromDbUtils(String resourceName) throws IOException {
InputStream propertiesStream = DBSetup.class.getClassLoader().getResourceAsStream(resourceName);
try {
Properties properties = new Properties();
properties.load(propertiesStream);
return properties;
} finally {
safeClose(propertiesStream);
}
}
private static File getFileFromDbUtils(String fileName) throws IOException {
return getFileFromResource(fileName, DBSetup.class.getClassLoader());
}
private static File getFileFromResource(String resourceName, ClassLoader cl) throws IOException {
InputStream stream = cl.getResourceAsStream(resourceName);
if (stream == null) {
throw new FileNotFoundException("Could not find " + resourceName + " in the classloader.");
}
File tmpFile = File.createTempFile("DbSetup", null);
OutputStream out = new BufferedOutputStream(new FileOutputStream(tmpFile));
try {
copy(stream, out);
return tmpFile;
} finally {
safeClose(stream, out);
}
}
private String readIntoString(InputStream s) throws IOException {
char[] buffer = new char[32768];
StringBuilder bld = new StringBuilder();
BufferedReader rdr = new BufferedReader(new InputStreamReader(s));
try {
int cnt = 0;
while ((cnt = rdr.read(buffer, 0, buffer.length)) != -1) {
bld.append(buffer, 0, cnt);
}
return bld.toString();
} finally {
rdr.close();
}
}
private static void copy(InputStream source, OutputStream target) throws IOException {
byte[] buffer = new byte[32768];
int cnt = 0;
while ((cnt = source.read(buffer, 0, buffer.length)) != -1) {
target.write(buffer, 0, cnt);
}
target.flush();
}
private static void safeClose(Closeable... streams) {
for(Closeable stream : streams) {
try {
if (stream!=null)
stream.close();
} catch (IOException e) {
//ignore
}
}
}
}