org.jumpmind.db.platform.JdbcDatabasePlatformFactory.java Source code

Java tutorial

Introduction

Here is the source code for org.jumpmind.db.platform.JdbcDatabasePlatformFactory.java

Source

package org.jumpmind.db.platform;

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import java.lang.reflect.Constructor;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.lang.StringUtils;
import org.h2.util.JdbcUtils;
import org.jumpmind.db.platform.ase.AseDatabasePlatform;
import org.jumpmind.db.platform.db2.Db2As400DatabasePlatform;
import org.jumpmind.db.platform.db2.Db2DatabasePlatform;
import org.jumpmind.db.platform.db2.Db2zOsDatabasePlatform;
import org.jumpmind.db.platform.derby.DerbyDatabasePlatform;
import org.jumpmind.db.platform.firebird.FirebirdDatabasePlatform;
import org.jumpmind.db.platform.firebird.FirebirdDialect1DatabasePlatform;
import org.jumpmind.db.platform.greenplum.GreenplumPlatform;
import org.jumpmind.db.platform.h2.H2DatabasePlatform;
import org.jumpmind.db.platform.hsqldb.HsqlDbDatabasePlatform;
import org.jumpmind.db.platform.hsqldb2.HsqlDb2DatabasePlatform;
import org.jumpmind.db.platform.informix.InformixDatabasePlatform;
import org.jumpmind.db.platform.interbase.InterbaseDatabasePlatform;
import org.jumpmind.db.platform.mariadb.MariaDBDatabasePlatform;
import org.jumpmind.db.platform.mssql.MsSql2000DatabasePlatform;
import org.jumpmind.db.platform.mssql.MsSql2005DatabasePlatform;
import org.jumpmind.db.platform.mssql.MsSql2008DatabasePlatform;
import org.jumpmind.db.platform.mysql.MySqlDatabasePlatform;
import org.jumpmind.db.platform.oracle.OracleDatabasePlatform;
import org.jumpmind.db.platform.postgresql.PostgreSqlDatabasePlatform;
import org.jumpmind.db.platform.redshift.RedshiftDatabasePlatform;
import org.jumpmind.db.platform.sqlanywhere.SqlAnywhereDatabasePlatform;
import org.jumpmind.db.platform.sqlite.SqliteDatabasePlatform;
import org.jumpmind.db.sql.SqlException;
import org.jumpmind.db.sql.SqlTemplateSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * A factory of {@link IDatabasePlatform} instances based on a case
 * insensitive database name. Note that this is a convenience class as the platforms
 * can also simply be created via their constructors.
 */
public class JdbcDatabasePlatformFactory {

    /* The database name -> platform map. */
    private static Map<String, Class<? extends IDatabasePlatform>> platforms = new HashMap<String, Class<? extends IDatabasePlatform>>();

    /*
     * Maps the sub-protocl part of a jdbc connection url to a OJB platform
     * name.
     */
    private static Map<String, Class<? extends IDatabasePlatform>> jdbcSubProtocolToPlatform = new HashMap<String, Class<? extends IDatabasePlatform>>();

    private static final Logger log = LoggerFactory.getLogger(JdbcDatabasePlatformFactory.class);

    static {

        addPlatform(platforms, "H2", H2DatabasePlatform.class);
        addPlatform(platforms, "H21", H2DatabasePlatform.class);
        addPlatform(platforms, "Informix Dynamic Server11", InformixDatabasePlatform.class);
        addPlatform(platforms, "Informix Dynamic Server", InformixDatabasePlatform.class);
        addPlatform(platforms, "Apache Derby", DerbyDatabasePlatform.class);
        addPlatform(platforms, "Firebird", FirebirdDatabasePlatform.class);
        addPlatform(platforms, DatabaseNamesConstants.GREENPLUM, GreenplumPlatform.class);
        addPlatform(platforms, DatabaseNamesConstants.FIREBIRD_DIALECT1, FirebirdDialect1DatabasePlatform.class);
        addPlatform(platforms, "HsqlDb", HsqlDbDatabasePlatform.class);
        addPlatform(platforms, "HSQL Database Engine2", HsqlDb2DatabasePlatform.class);
        addPlatform(platforms, "Interbase", InterbaseDatabasePlatform.class);
        addPlatform(platforms, "MariaDB", MariaDBDatabasePlatform.class);
        addPlatform(platforms, "microsoft sql server8", MsSql2000DatabasePlatform.class);
        addPlatform(platforms, "microsoft sql server9", MsSql2005DatabasePlatform.class);
        addPlatform(platforms, "microsoft sql server10", MsSql2008DatabasePlatform.class);
        addPlatform(platforms, "microsoft sql server11", MsSql2008DatabasePlatform.class);
        addPlatform(platforms, "microsoft sql server", MsSql2008DatabasePlatform.class);
        addPlatform(platforms, "MySQL", MySqlDatabasePlatform.class);
        addPlatform(platforms, "Oracle", OracleDatabasePlatform.class);
        addPlatform(platforms, "PostgreSql", PostgreSqlDatabasePlatform.class);
        addPlatform(platforms, "Adaptive Server Enterprise", AseDatabasePlatform.class);
        addPlatform(platforms, "Adaptive Server Anywhere", SqlAnywhereDatabasePlatform.class);
        addPlatform(platforms, "SQL Anywhere", SqlAnywhereDatabasePlatform.class);
        addPlatform(platforms, "DB2", Db2DatabasePlatform.class);
        addPlatform(platforms, DatabaseNamesConstants.DB2ZOS, Db2zOsDatabasePlatform.class);
        addPlatform(platforms, DatabaseNamesConstants.DB2AS400, Db2As400DatabasePlatform.class);
        addPlatform(platforms, "SQLite", SqliteDatabasePlatform.class);
        addPlatform(platforms, DatabaseNamesConstants.REDSHIFT, RedshiftDatabasePlatform.class);

        jdbcSubProtocolToPlatform.put(Db2DatabasePlatform.JDBC_SUBPROTOCOL, Db2DatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(DerbyDatabasePlatform.JDBC_SUBPROTOCOL, DerbyDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(FirebirdDatabasePlatform.JDBC_SUBPROTOCOL, FirebirdDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(HsqlDbDatabasePlatform.JDBC_SUBPROTOCOL, HsqlDbDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(InterbaseDatabasePlatform.JDBC_SUBPROTOCOL, InterbaseDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(MsSql2000DatabasePlatform.JDBC_SUBPROTOCOL, MsSql2000DatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(MsSql2005DatabasePlatform.JDBC_SUBPROTOCOL, MsSql2005DatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(MsSql2008DatabasePlatform.JDBC_SUBPROTOCOL, MsSql2008DatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(MySqlDatabasePlatform.JDBC_SUBPROTOCOL, MySqlDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(OracleDatabasePlatform.JDBC_SUBPROTOCOL_THIN, OracleDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(OracleDatabasePlatform.JDBC_SUBPROTOCOL_OCI8, OracleDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(OracleDatabasePlatform.JDBC_SUBPROTOCOL_THIN_OLD,
                OracleDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(PostgreSqlDatabasePlatform.JDBC_SUBPROTOCOL,
                PostgreSqlDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(AseDatabasePlatform.JDBC_SUBPROTOCOL, AseDatabasePlatform.class);
        jdbcSubProtocolToPlatform.put(FirebirdDatabasePlatform.JDBC_SUBPROTOCOL, FirebirdDatabasePlatform.class);
    }

    /*
     * Creates a new platform for the specified database.  Note that this method installs
     * the data source in the returned platform instance.
     *
     * @param dataSource The data source for the database
     * @param log The logger that the platform should use
     *
     * @return The platform or <code>null</code> if the database is not
     * supported
     */
    public static synchronized IDatabasePlatform createNewPlatformInstance(DataSource dataSource,
            SqlTemplateSettings settings, boolean delimitedIdentifierMode) throws DdlException {

        // connects to the database and uses actual metadata info to get db name
        // and version to determine platform
        String[] nameVersion = determineDatabaseNameVersionSubprotocol(dataSource);

        Class<? extends IDatabasePlatform> clazz = findPlatformClass(nameVersion);

        try {
            Constructor<? extends IDatabasePlatform> construtor = clazz.getConstructor(DataSource.class,
                    SqlTemplateSettings.class);
            IDatabasePlatform platform = construtor.newInstance(dataSource, settings);
            log.info("The IDatabasePlatform being used is " + platform.getClass().getCanonicalName());
            platform.getDdlBuilder().setDelimitedIdentifierModeOn(delimitedIdentifierMode);
            return platform;
        } catch (Exception e) {
            throw new DdlException("Could not create a platform of type " + nameVersion[0], e);
        }
    }

    protected static synchronized Class<? extends IDatabasePlatform> findPlatformClass(String[] nameVersion)
            throws DdlException {
        Class<? extends IDatabasePlatform> platformClass = platforms
                .get(String.format("%s%s", nameVersion[0], nameVersion[1]).toLowerCase());

        if (platformClass == null) {
            platformClass = platforms.get(nameVersion[0].toLowerCase());
        }

        if (platformClass == null) {
            platformClass = jdbcSubProtocolToPlatform.get(nameVersion[2]);
        }

        if (platformClass == null) {
            throw new DdlException("Could not find platform for database " + nameVersion[0]);
        } else {
            return platformClass;
        }

    }

    protected static String[] determineDatabaseNameVersionSubprotocol(DataSource dataSource) {
        Connection connection = null;
        String[] nameVersion = new String[3];
        try {
            connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            nameVersion[0] = metaData.getDatabaseProductName();
            nameVersion[1] = Integer.toString(metaData.getDatabaseMajorVersion());
            final String PREFIX = "jdbc:";
            String url = metaData.getURL();
            if (StringUtils.isNotBlank(url) && url.length() > PREFIX.length()) {
                url = url.substring(PREFIX.length());
                if (url.indexOf(":") > 0) {
                    url = url.substring(0, url.indexOf(":"));
                }
            }
            nameVersion[2] = url;

            /*
             * if the productName is PostgreSQL, it could be either PostgreSQL
             * or Greenplum
             */
            /* query the metadata to determine which one it is */
            if (nameVersion[0].equalsIgnoreCase("PostgreSql")) {
                if (isGreenplumDatabase(connection)) {
                    nameVersion[0] = DatabaseNamesConstants.GREENPLUM;
                    nameVersion[1] = Integer.toString(getGreenplumVersion(connection));
                } else if (isRedshiftDatabase(connection)) {
                    nameVersion[0] = DatabaseNamesConstants.REDSHIFT;
                }
            }

            /*
             * if the productName is MySQL, it could be either MysSQL or MariaDB
             * query the metadata to determine which one it is
             */
            if (nameVersion[0].equalsIgnoreCase(DatabaseNamesConstants.MYSQL)) {
                if (isMariaDBDatabase(connection)) {
                    nameVersion[0] = DatabaseNamesConstants.MARIADB;
                }
            }

            if (nameVersion[0].toLowerCase().indexOf(DatabaseNamesConstants.DB2) != -1) {
                if (nameVersion[0].toUpperCase().indexOf("Z") != -1) {
                    nameVersion[0] = DatabaseNamesConstants.DB2ZOS;
                } else if (nameVersion[0].indexOf("400") != -1) {
                    nameVersion[0] = DatabaseNamesConstants.DB2AS400;
                } else {
                    nameVersion[0] = DatabaseNamesConstants.DB2;
                }
            }
            if (nameVersion[0].equalsIgnoreCase("AS") && nameVersion[2].equalsIgnoreCase("db2")) {
                nameVersion[0] = DatabaseNamesConstants.DB2AS400;
            }

            if (nameVersion[0].toLowerCase().startsWith(DatabaseNamesConstants.FIREBIRD)) {
                if (isFirebirdDialect1(connection)) {
                    nameVersion[0] = DatabaseNamesConstants.FIREBIRD_DIALECT1;
                }
            }

            log.info("Detected database '" + nameVersion[0] + "', version '" + nameVersion[1] + "', protocol '"
                    + nameVersion[2] + "'");

            return nameVersion;
        } catch (SQLException ex) {
            throw new SqlException("Error while reading the database metadata: " + ex.getMessage(), ex);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException ex) {
                    // we ignore this one
                }
            }
        }
    }

    private static boolean isGreenplumDatabase(Connection connection) {
        Statement stmt = null;
        ResultSet rs = null;
        String productName = null;
        boolean isGreenplum = false;
        try {
            stmt = connection.createStatement();
            rs = stmt.executeQuery(GreenplumPlatform.SQL_GET_GREENPLUM_NAME);
            while (rs.next()) {
                productName = rs.getString(1);
            }
            if (productName != null && productName.equalsIgnoreCase("Greenplum")) {
                isGreenplum = true;
            }
        } catch (SQLException ex) {
            // ignore the exception, if it is caught, then this is most likely
            // not
            // a greenplum database
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
            } catch (SQLException ex) {
            }
        }
        return isGreenplum;
    }

    private static boolean isRedshiftDatabase(Connection connection) {
        boolean isRedshift = false;
        try {
            DatabaseMetaData dmd = connection.getMetaData();
            dmd.getMaxColumnsInIndex();
        } catch (SQLException ex) {
            if (ex.getSQLState().equals("99999")) {
                isRedshift = true;
            }
        }
        return isRedshift;
    }

    private static boolean isFirebirdDialect1(Connection connection) {
        boolean isDialect1 = false;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            stmt = connection.createStatement();
            rs = stmt.executeQuery("select current_time from rdb$database");
            rs.next();
        } catch (SQLException ex) {
            isDialect1 = true;
            try {
                JdbcUtils.closeSilently(rs);
                rs = stmt.executeQuery("select cast(1 as numeric(10,0)) from rdb$database");
                rs.next();
            } catch (SQLException e) {
                log.error(
                        "The client sql dialect does not match the database, which is not a supported mode.  You must add ?sql_dialect=1 to the end of the JDBC URL.");
            }
        } finally {
            JdbcUtils.closeSilently(rs);
            JdbcUtils.closeSilently(stmt);
        }
        return isDialect1;
    }

    private static boolean isMariaDBDatabase(Connection connection) {
        Statement stmt = null;
        ResultSet rs = null;
        String productName = null;
        boolean isMariaDB = false;
        try {
            stmt = connection.createStatement();
            rs = stmt.executeQuery(MariaDBDatabasePlatform.SQL_GET_MARIADB_NAME);
            while (rs.next()) {
                productName = rs.getString(1);
            }
            if (productName != null
                    && StringUtils.containsIgnoreCase(productName, DatabaseNamesConstants.MARIADB)) {
                isMariaDB = true;
            }
        } catch (SQLException ex) {
            // ignore the exception, if it is caught, then this is most likely
            // not a mariadb database
        } finally {
            try {
                if (rs != null) {
                    rs.close();
                }
                if (stmt != null) {
                    stmt.close();
                }
            } catch (SQLException ex) {
            }
        }
        return isMariaDB;
    }

    private static int getGreenplumVersion(Connection connection) {
        Statement stmt = null;
        ResultSet rs = null;
        String versionName = null;
        int productVersion = 0;
        try {
            stmt = connection.createStatement();
            rs = stmt.executeQuery(GreenplumPlatform.SQL_GET_GREENPLUM_VERSION);
            while (rs.next()) {
                versionName = rs.getString(1);
            }
            // take up to the first "." for version number
            if (versionName.indexOf('.') != -1) {
                versionName = versionName.substring(0, versionName.indexOf('.'));
            }
            try {
                productVersion = Integer.parseInt(versionName);
            } catch (NumberFormatException ex) {
                // if we can't convert this to a version number, leave it 0
            }
        } catch (SQLException ex) {
            // ignore the exception, if it is caught, then this is most likely
            // not
            // a greenplum database
        } finally {
            try {
                rs.close();
                stmt.close();
            } catch (SQLException ex) {
            }
        }
        return productVersion;
    }

    public static String getDatabaseProductVersion(DataSource dataSource) {
        Connection connection = null;

        try {
            connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            return metaData.getDatabaseProductVersion();
        } catch (SQLException ex) {
            throw new SqlException("Error while reading the database metadata: " + ex.getMessage(), ex);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException ex) {
                    // we ignore this one
                }
            }
        }
    }

    public static int getDatabaseMajorVersion(DataSource dataSource) {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            return metaData.getDatabaseMajorVersion();
        } catch (SQLException ex) {
            throw new SqlException("Error while reading the database metadata: " + ex.getMessage(), ex);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException ex) {
                    // we ignore this one
                }
            }
        }
    }

    public static int getDatabaseMinorVersion(DataSource dataSource) {
        Connection connection = null;
        try {
            connection = dataSource.getConnection();
            DatabaseMetaData metaData = connection.getMetaData();
            return metaData.getDatabaseMinorVersion();
        } catch (SQLException ex) {
            throw new SqlException("Error while reading the database metadata: " + ex.getMessage(), ex);
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException ex) {
                    // we ignore this one
                }
            }
        }
    }

    private static synchronized void addPlatform(Map<String, Class<? extends IDatabasePlatform>> platformMap,
            String platformName, Class<? extends IDatabasePlatform> platformClass) {
        if (!IDatabasePlatform.class.isAssignableFrom(platformClass)) {
            throw new IllegalArgumentException("Cannot register class " + platformClass.getName()
                    + " because it does not implement the " + IDatabasePlatform.class.getName() + " interface");
        }
        platformMap.put(platformName.toLowerCase(), platformClass);
    }
}