package liquibase.test; import liquibase.database.*; import liquibase.database.core.DB2Database; import liquibase.database.example.ExampleCustomDatabase; import liquibase.database.core.SQLiteDatabase; import liquibase.database.core.MockDatabase; import liquibase.database.jvm.JdbcConnection; import liquibase.dbtest.AbstractIntegrationTest; import liquibase.resource.ResourceAccessor; import liquibase.exception.DatabaseException; import java.util.*; import java.sql.SQLException; import java.sql.Driver; import java.sql.Connection; public class DatabaseTestContext { private static DatabaseTestContext instance = new DatabaseTestContext(); private Set<Database> availableDatabases = new HashSet<Database>(); private Set<Database> allDatabases; private Set<DatabaseConnection> availableConnections; private final DatabaseTestURL[] DEFAULT_TEST_DATABASES = new DatabaseTestURL[]{ new DatabaseTestURL("Cache","jdbc:Cache://"+AbstractIntegrationTest.getDatabaseServerHostname("Cache")+":1972/liquibase"), new DatabaseTestURL("DB2","jdbc:db2://"+AbstractIntegrationTest.getDatabaseServerHostname("DB2")+":50000/liquibas"), new DatabaseTestURL("Derby","jdbc:derby:liquibase;create=true"), new DatabaseTestURL("FireBird","jdbc:firebirdsql:"+AbstractIntegrationTest.getDatabaseServerHostname("Firebird")+"/3050:c:\\firebird\\liquibase.fdb"), new DatabaseTestURL("H2","jdbc:h2:mem:liquibase"), new DatabaseTestURL("Hsql","jdbc:hsqldb:mem:liquibase"), new DatabaseTestURL("MssqlJtds","jdbc:jtds:sqlserver://"+AbstractIntegrationTest.getDatabaseServerHostname("MSSQL")+";databaseName=liquibase"), // "jdbc:sqlserver://localhost;databaseName=liquibase", new DatabaseTestURL("MySQL","jdbc:mysql://"+AbstractIntegrationTest.getDatabaseServerHostname("mysql")+"/liquibase"), new DatabaseTestURL("Oracle","jdbc:oracle:thin:@"+AbstractIntegrationTest.getDatabaseServerHostname("oracle")+"/XE"), // "jdbc:jtds:sybase://localhost/nathan:5000", // "jdbc:sybase:Tds:"+ InetAddress.getLocalHost().getHostName()+":5000/liquibase", new DatabaseTestURL("SAPDB","jdbc:sapdb://"+AbstractIntegrationTest.getDatabaseServerHostname("sapdb")+"/liquibas"), new DatabaseTestURL("SQLite","jdbc:sqlite:/liquibase.db"), new DatabaseTestURL("SybaseJtds","jdbc:sybase:Tds:"+AbstractIntegrationTest.getDatabaseServerHostname("sybase")+":9810/servicename=prior") }; private Map<String, DatabaseConnection> connectionsByUrl = new HashMap<String, DatabaseConnection>(); private Map<String, Boolean> connectionsAttempted = new HashMap<String, Boolean>(); public static final String ALT_SCHEMA = "LIQUIBASEB"; public static final String ALT_TABLESPACE = "LIQUIBASE2"; private static final String TEST_DATABASES_PROPERTY = "test.databases"; private ResourceAccessor resourceAccessor; private DatabaseConnection openConnection(final String url) throws Exception { if (connectionsAttempted.containsKey(url)) { JdbcConnection connection = (JdbcConnection) connectionsByUrl.get(url); if (connection == null) { return null; } else if (connection.getUnderlyingConnection().isClosed()){ connectionsByUrl.put(url, openDatabaseConnection(url)); } return connectionsByUrl.get(url); } connectionsAttempted.put(url, Boolean.TRUE); if (System.getProperty(TEST_DATABASES_PROPERTY) != null) { boolean shouldTest = false; String[] databasesToTest = System.getProperty(TEST_DATABASES_PROPERTY).split("\\s*,\\s*"); for (String database : databasesToTest) { if (url.indexOf(database) >= 0) { shouldTest = true; } } if (!shouldTest) { System.out.println("test.databases system property forbids testing against " + url); return null; } else { System.out.println("Will be tested against " + url); } } DatabaseConnection connection = openDatabaseConnection(url); if (connection == null) { return null; } Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection); final DatabaseConnection databaseConnection = database.getConnection(); if (databaseConnection.getAutoCommit()) { databaseConnection.setAutoCommit(false); } try { if (url.startsWith("jdbc:hsql")) { ((JdbcConnection) databaseConnection).getUnderlyingConnection().createStatement().execute("CREATE SCHEMA " + ALT_SCHEMA + " AUTHORIZATION DBA"); } else if (url.startsWith("jdbc:sqlserver") || url.startsWith("jdbc:postgresql") || url.startsWith("jdbc:h2")) { ((JdbcConnection) databaseConnection).getUnderlyingConnection().createStatement().execute("CREATE SCHEMA " + ALT_SCHEMA); } if (!databaseConnection.getAutoCommit()) { databaseConnection.commit(); } } catch (SQLException e) { // e.printStackTrace(); ; //schema already exists } finally { try { databaseConnection.rollback(); } catch (DatabaseException e) { if (database instanceof DB2Database) { // expected, there is a problem with it } else { throw e; } } } connectionsByUrl.put(url, databaseConnection); Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { public void run() { try { try { if (!((JdbcConnection) databaseConnection).getUnderlyingConnection().getAutoCommit()) { ((JdbcConnection) databaseConnection).getUnderlyingConnection().rollback(); } } catch (SQLException e) { ; } ((JdbcConnection) databaseConnection).getUnderlyingConnection().close(); } catch (SQLException e) { System.out.println("Could not close " + url); e.printStackTrace(); } } })); return databaseConnection; } public DatabaseConnection openDatabaseConnection(String url) throws Exception { String username = getUsername(url); String password = getPassword(url); JUnitJDBCDriverClassLoader jdbcDriverLoader = JUnitJDBCDriverClassLoader.getInstance(); final Driver driver; try { driver = (Driver) Class.forName(DatabaseFactory.getInstance().findDefaultDriver(url), true, jdbcDriverLoader).newInstance(); } catch (Exception e) { System.out.println("Could not connect to " + url + ": Will not test against. " + e.getMessage()); return null; //could not connect } Properties info = new Properties(); info.put("user", username); if (password != null) { info.put("password", password); } info.put("retrieveMessagesFromServerOnGetMessage", "true"); //for db2 Connection connection; try { connection = driver.connect(url, info); } catch (SQLException e) { System.out.println("Could not connect to " + url + ": Will not test against. " + e.getMessage()); return null; //could not connect } if (connection == null) { throw new DatabaseException("Connection could not be created to " + url + " with driver " + driver.getClass().getName() + ". Possibly the wrong driver for the given database URL"); } return new JdbcConnection(connection); } private String getUsername(String url) { if (url.startsWith("jdbc:hsqldb")) { return "sa"; } return "liquibase"; } private String getPassword(String url) { if (url.startsWith("jdbc:hsqldb")) { return ""; } return "liquibase"; } public static DatabaseTestContext getInstance() { return instance; } public DatabaseTestURL[] getTestUrls() { return DEFAULT_TEST_DATABASES; } public Set<Database> getAllDatabases() { if (allDatabases == null) { allDatabases = new HashSet<Database>(); allDatabases.addAll(DatabaseFactory.getInstance().getImplementedDatabases()); List<Database> toRemove = new ArrayList<Database>(); for (Database database : allDatabases) { if (database instanceof SQLiteDatabase //todo: re-enable sqlite testing || database instanceof MockDatabase || database instanceof ExampleCustomDatabase) { toRemove.add(database); } } allDatabases.removeAll(toRemove); } return allDatabases; } public Set<Database> getAvailableDatabases() throws Exception { if (availableDatabases.size() == 0) { for (DatabaseConnection conn : getAvailableConnections()) { availableDatabases.add(DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn)); } } //Check to don't return closed databases Iterator<Database> iter=availableDatabases.iterator(); while(iter.hasNext()) { Database database=iter.next(); if(database.getConnection().isClosed()) iter.remove(); } return availableDatabases; } public Set<DatabaseConnection> getAvailableConnections() throws Exception { if (availableConnections == null) { availableConnections = new HashSet<DatabaseConnection>(); for (DatabaseTestURL url : getTestUrls()) { // if (url.indexOf("jtds") >= 0) { // continue; // } DatabaseConnection connection = openConnection(adaptTestURLWithConfiguredHost(url)); if (connection != null) { availableConnections.add(connection); } } } //Check to don't return closed connections Iterator<DatabaseConnection> iter=availableConnections.iterator(); while(iter.hasNext()) { DatabaseConnection connection=iter.next(); if(connection.isClosed()) iter.remove(); } return availableConnections; } protected String adaptTestURLWithConfiguredHost(DatabaseTestURL url) throws Exception { return url.getUrl().replaceAll("localhost", AbstractIntegrationTest.getDatabaseServerHostname(url.getDatabaseManager())); } public DatabaseConnection getConnection(String url) throws Exception { return openConnection(url); } public String getTestUrl(Database database) throws Exception { for (DatabaseTestURL turl : getTestUrls()) { String url=adaptTestURLWithConfiguredHost(turl); if (database.getDefaultDriver(url) != null) { return url; } } throw new RuntimeException("Could not find url for " + database); } }