/* * ConcourseConnect * Copyright 2009 Concursive Corporation * http://www.concursive.com * * This file is part of ConcourseConnect, an open source social business * software and community platform. * * Concursive ConcourseConnect is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License as published * by the Free Software Foundation, version 3 of the License. * * Under the terms of the GNU Affero General Public License you must release the * complete source code for any application that uses any part of ConcourseConnect * (system header files and libraries used by the operating system are excluded). * These terms must be included in any work that has ConcourseConnect components. * If you are developing and distributing open source applications under the * GNU Affero General Public License, then you are free to use ConcourseConnect * under the GNU Affero General Public License. * * If you are deploying a web site in which users interact with any portion of * ConcourseConnect over a network, the complete source code changes must be made * available. For example, include a link to the source archive directly from * your web site. * * For OEMs, ISVs, SIs and VARs who distribute ConcourseConnect with their * products, and do not license and distribute their source code under the GNU * Affero General Public License, Concursive provides a flexible commercial * license. * * To anyone in doubt, we recommend the commercial license. Our commercial license * is competitively priced and will eliminate any confusion about how * ConcourseConnect can be used and distributed. * * ConcourseConnect 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 Affero General Public License for more * details. * * You should have received a copy of the GNU Affero General Public License * along with ConcourseConnect. If not, see <http://www.gnu.org/licenses/>. * * Attribution Notice: ConcourseConnect is an Original Work of software created * by Concursive Corporation */ package com.concursive.connect.ant.tasks; import bsh.Interpreter; import com.concursive.commons.db.ConnectionElement; import com.concursive.commons.db.ConnectionPool; import com.concursive.commons.db.DatabaseUtils; import com.concursive.commons.text.StringUtils; import com.concursive.connect.web.modules.upgrade.utils.UpgradeUtils; import com.concursive.connect.config.ApplicationPrefs; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; import java.io.File; import java.io.InputStream; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; /** * Ant task to upgrade the database * * @author matt rajkowski * @version $Id$ * @created August 26, 2003 */ public class UpgradeDatabaseTask extends Task { /** * Description of the Field */ public final static String fs = System.getProperty("file.separator"); private String driver = null; private String url = null; private String user = null; private String password = null; private String baseFilePath = null; private String baseFile = null; private String servletJar = null; private String libPath = null; private String fileLibrary = null; private String specificDatabase = null; private ApplicationPrefs prefs = new ApplicationPrefs(); /** * Sets the driver attribute of the UpgradeDatabaseTask object * * @param tmp The new driver value */ public void setDriver(String tmp) { this.driver = tmp; } /** * Sets the url attribute of the UpgradeDatabaseTask object * * @param tmp The new url value */ public void setUrl(String tmp) { this.url = tmp; } /** * Sets the user attribute of the UpgradeDatabaseTask object * * @param tmp The new user value */ public void setUser(String tmp) { this.user = tmp; } /** * Sets the password attribute of the UpgradeDatabaseTask object * * @param tmp The new password value */ public void setPassword(String tmp) { this.password = tmp; } public void setBaseFilePath(String baseFilePath) { this.baseFilePath = baseFilePath; } /** * Sets the source attribute of the UpgradeDatabaseTask object * * @param tmp The new source value */ public void setBaseFile(String tmp) { this.baseFile = tmp; } /** * Sets the servletJar attribute of the UpgradeDatabaseTask object * * @param tmp The new servletJar value */ public void setServletJar(String tmp) { this.servletJar = tmp; } public void setLibPath(String libPath) { this.libPath = libPath; } /** * Sets the fileLibrary attribute of the UpgradeDatabaseTask object * * @param tmp The new fileLibrary value */ public void setFileLibrary(String tmp) { this.fileLibrary = tmp; } /** * Sets the specificDatabase attribute of the UpgradeDatabaseTask object * * @param tmp The new specificDatabase value */ public void setSpecificDatabase(String tmp) { this.specificDatabase = tmp; } /** * This method is called by Ant when the upgradeDatabaseTask is used * * @throws BuildException Description of the Exception */ public void execute() throws BuildException { String fsEval = System.getProperty("file.separator"); if ("\\".equals(fsEval)) { fsEval = "\\\\"; servletJar = StringUtils.replace(servletJar, "\\", "\\\\"); fileLibrary = StringUtils.replace(fileLibrary, "\\", "\\\\"); } System.out.println("Beginning database task..."); try { // Load the application prefs prefs.loadProperties(fileLibrary); //Create a Connection Pool to facilitate connections ConnectionPool sqlDriver = new ConnectionPool(); sqlDriver.setDebug(true); sqlDriver.setTestConnections(false); sqlDriver.setAllowShrinking(true); sqlDriver.setMaxConnections(10); sqlDriver.setMaxIdleTime(60000); sqlDriver.setMaxDeadTime(300000); //Cache a list of databases to upgrade ArrayList<HashMap> siteList = new ArrayList<HashMap>(); //Prepare the database ConnectionElement ce = new ConnectionElement(url, user, password); ce.setDriver(driver); //Set the database to update if (1 == 1) { HashMap<String, String> siteInfo = new HashMap<String, String>(); siteInfo.put("url", url); siteInfo.put("dbName", specificDatabase); siteInfo.put("user", user); siteInfo.put("password", password); siteInfo.put("driver", driver); if ((specificDatabase == null || "".equals(specificDatabase)) || (specificDatabase.equals(siteInfo.get("dbName")))) { siteList.add(siteInfo); } } //Iterate over the databases to upgrade and run the correct //sql code and bean shell scripts Iterator i = siteList.iterator(); while (i.hasNext()) { HashMap siteInfo = (HashMap) i.next(); ce = new ConnectionElement( (String) siteInfo.get("url"), (String) siteInfo.get("user"), (String) siteInfo.get("password")); ce.setDriver((String) siteInfo.get("driver")); System.out.println(""); if (baseFilePath == null) { baseFilePath = ""; } String thisDbName = (String) siteInfo.get("dbName"); // Connect to the database Connection db = sqlDriver.getConnection(ce, true); // Run any scripts if ("all".equals(baseFile)) { // Look for scripts to run InputStream is = new File("src/main/webapp/WEB-INF/database/database_versions.txt").toURL().openStream(); ArrayList<String> versionList = UpgradeUtils.retrieveDatabaseVersions(is); for (String version : versionList) { if (!UpgradeUtils.isInstalled(db, version)) { System.out.println("Executing database version: " + version); executeTxtFile(db, version + ".txt", fsEval, thisDbName); UpgradeUtils.addVersion(db, version); } } } else if (baseFile.indexOf(".txt") > -1) { // try a single .txt executeTxtFile(db, baseFile, fsEval, thisDbName); } else { // assume it is a .bsh or .sql executeFile(db, baseFile, fsEval, thisDbName); } sqlDriver.free(db); } // Close the connections sqlDriver.closeAllConnections(); } catch (Exception e) { throw new BuildException("Script Error: " + e.getMessage()); } } private void executeTxtFile(Connection db, String baseFile, String fsEval, String dbName) throws Exception { //Connection db, String path, String thisScriptFile, String fsEval, String dbName ArrayList<String> files = new ArrayList<String>(); if (baseFile.startsWith("upgrade_")) { baseFile = baseFile.substring(8); } // Try the coded path String yearString = (new File(baseFile)).getName().substring(0, 4); String upgradeFile = baseFilePath + "upgrade" + fs + yearString + fs + "upgrade_" + baseFile; File testFile = new File(upgradeFile); if (testFile.exists()) { StringUtils.loadText(upgradeFile, files, true); } else { // Try the specified file StringUtils.loadText(baseFile, files, true); } System.out.println("Scripts to process: " + files.size()); for (String thisFile : files) { executeFile(db, thisFile, fsEval, dbName); } } private void executeFile(Connection db, String thisFile, String fsEval, String dbName) throws Exception { if (thisFile.endsWith(".bsh")) { // Try to run a specified bean shell script if found executeScript(db, thisFile, fsEval, dbName); } else if (thisFile.endsWith(".sql")) { // Try to run the specified sql file executeSql(db, baseFilePath, thisFile); } else { throw new Exception("UpgradeDatabaseTask-> File type not understood: " + thisFile); } } /** * Executes the specified BeanShell script on the given database connection * * @param db Description of the Parameter * @param thisScriptFile Description of the Parameter * @param fsEval Description of the Parameter * @param dbName Description of the Parameter * @throws Exception Description of the Exception */ private void executeScript(Connection db, String thisScriptFile, String fsEval, String dbName) throws Exception { String scriptFile = thisScriptFile; if (scriptFile.endsWith(".bsh")) { if (!(new File(scriptFile).exists())) { // look for script stored in a common directory String yearString = (new File(scriptFile)).getName().substring(0, 4); scriptFile = baseFilePath + "common" + fs + yearString + fs + (new File(scriptFile)).getName(); } if (!(new File(scriptFile).exists())) { throw new Exception("BSH FILE NOT FOUND: " + thisScriptFile); } // Setup the classpath and environment System.out.println("\nSetting up BSH environment..."); Interpreter script = new Interpreter(); // Dynamically add all libraries if (libPath != null) { File directory = new File(libPath); if (directory.isDirectory()) { System.out.println("\nAdding libraries from... " + libPath); String[] libraries = directory.list(); for (String library : libraries) { if (library.endsWith(".jar")) { script.eval("addClassPath(bsh.cwd + \"" + fsEval + "lib" + fsEval + library + "\")"); } } } } script.eval("addClassPath(bsh.cwd + \"" + fsEval + "src" + fsEval + "main" + fsEval + "resources\")"); script.eval("addClassPath(\"" + servletJar + "\")"); script.set("prefs", prefs); script.set("db", db); script.set("fileLibraryPath", fileLibrary); // Some classes use the cache so make it available during upgrades System.out.println("\nAdding caches..."); script.eval("import com.concursive.connect.cache.CacheContext;"); script.eval("import com.concursive.connect.cache.Caches;"); script.eval("import net.sf.ehcache.CacheManager;"); script.eval("CacheManager.create();"); script.eval("CacheContext cacheContext = new CacheContext();"); script.eval("cacheContext.setUpgradeConnection(db);"); script.eval("Caches.addCaches(cacheContext);"); // Execute the script System.out.println("\nExecuting: " + thisScriptFile); script.source(scriptFile); System.out.println(""); // Cleanup... System.out.println("\nCleaning up BSH environment..."); script.eval("CacheManager.getInstance().shutdown();"); } } /** * Executes the specified sql file on the given database connection * * @param db Description of the Parameter * @param path Description of the Parameter * @param sqlFileName Description of the Parameter * @throws Exception Description of the Exception */ private void executeSql(Connection db, String path, String sqlFileName) throws Exception { String dbType = null; switch (DatabaseUtils.getType(db)) { case DatabaseUtils.POSTGRESQL: dbType = "postgresql"; break; case DatabaseUtils.MSSQL: dbType = "mssql"; break; default: throw new Exception("Upgrade-> * Database could not be determined: " + DatabaseUtils.getType(db)); } String yearString = sqlFileName.substring(0, 4); String sqlFile = path + dbType + fs + "upgrade" + fs + yearString + fs + sqlFileName; if (sqlFile.endsWith(".sql")) { if (!(new File(sqlFile).exists())) { throw new Exception("SQL FILE NOT FOUND: " + sqlFile); } System.out.println("\nExecuting: " + sqlFile); try { db.setAutoCommit(false); Statement st = db.createStatement(); st.execute(StringUtils.loadText(sqlFile)); st.close(); db.commit(); System.out.println(""); } catch (SQLException sq) { db.rollback(); System.out.println(" SQL ERROR: " + sq.getMessage()); throw new Exception(sq); } finally { db.setAutoCommit(true); } } } }