org.jboss.dashboard.database.hibernate.HibernateInitializer.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.dashboard.database.hibernate.HibernateInitializer.java

Source

/**
 * Copyright (C) 2012 JBoss Inc
 *
 * Licensed 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.
 */
package org.jboss.dashboard.database.hibernate;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Environment;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.jboss.dashboard.Application;
import org.jboss.dashboard.annotation.Priority;
import org.jboss.dashboard.annotation.Startable;
import org.jboss.dashboard.annotation.config.Config;
import org.jboss.dashboard.commons.misc.ReflectionUtils;
import org.jboss.dashboard.database.DataSourceManager;
import org.jboss.dashboard.database.DatabaseAutoSynchronizer;
import org.jboss.dashboard.database.JNDIDataSourceEntry;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.hibernate.*;
import org.hibernate.cfg.Configuration;
import org.hibernate.persister.entity.AbstractEntityPersister;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.sql.DataSource;
import java.io.*;
import java.sql.Connection;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

/**
 * Class that initializes the Hibernate framework. It reads all the *.hbm.xml files and push them as part of the
 * Hibernate configuration. Furthermore, initializes a SessionFactory object that will be used further by transactions. 
 * To do so it reads first the configuration stored into the HibernateProperties factory bean.
 */
@ApplicationScoped
public class HibernateInitializer implements Startable {

    private static transient Logger log = LoggerFactory.getLogger(HibernateInitializer.class.getName());

    public static final String DB_H2 = "h2";
    public static final String DB_POSTGRES = "postgres";
    public static final String DB_MYSQL = "mysql";
    public static final String DB_ORACLE = "oracle";
    public static final String DB_SQLSERVER = "sqlserver";
    public static final String DB_DB2 = "db2";
    public static final String DB_TEIID = "teiid";
    public static final String DB_SYBASE = "sybase";

    @Inject
    protected HibernateSessionFactoryProvider hibernateSessionFactoryProvider;

    @Inject
    protected DatabaseAutoSynchronizer databaseAutoSynchronizer;

    @Inject
    protected DataSourceManager databaseSourceManager;

    @Inject
    @Config(DB_H2 + "=org.hibernate.dialect.H2Dialect," + DB_POSTGRES + "=org.hibernate.dialect.PostgreSQLDialect,"
            + DB_ORACLE + "=org.hibernate.dialect.Oracle10gDialect," + DB_MYSQL
            + "=org.hibernate.dialect.MySQLDialect," + DB_SQLSERVER + "=org.hibernate.dialect.SQLServerDialect,"
            + DB_DB2 + "=org.hibernate.dialect.DB2Dialect," + DB_TEIID + "=org.teiid.dialect.TeiidDialect,"
            + DB_SYBASE + "=org.hibernate.dialect.SybaseASE157Dialect")
    protected Map<String, String> supportedDialects;

    @Inject
    @Config("org.hibernate.dialect.H2Dialect," + "org.hibernate.dialect.DB2Dialect,"
            + "org.teiid.dialect.TeiidDialect")
    protected String[] nativeToSequenceReplaceableDialects;

    @Inject
    @Config("org.hibernate.dialect.MySQLDialect," + "org.hibernate.dialect.SQLServerDialect,"
            + "org.hibernate.dialect.SybaseASE157Dialect")
    protected String[] nativeToHiloReplaceableDialects;

    @Inject
    @Config("org.jboss.dashboard.ui.resources.Envelope," + "org.jboss.dashboard.ui.resources.Skin,"
            + "org.jboss.dashboard.ui.resources.Layout," + "org.jboss.dashboard.ui.resources.GraphicElement")
    protected String[] verificationExcludedClassNames;

    protected Configuration hbmConfig;
    protected String databaseName;
    protected DataSource localDataSource;

    @Inject
    @Config
    protected String defaultSchema;

    public SessionFactory getSessionFactory() {
        return hibernateSessionFactoryProvider.getSessionFactory();
    }

    public Priority getPriority() {
        return Priority.URGENT;
    }

    public void start() throws Exception {

        // Configure Hibernate using the hibernate.cfg.xml.
        String hbnCfgPath = Application.lookup().getBaseCfgDirectory() + File.separator + "hibernate.cfg.xml";
        hbmConfig = new Configuration().configure(new File(hbnCfgPath));

        // Infer the underlying database name.
        databaseName = inferDatabaseName();
        log.info("The underlying database is: " + databaseName);

        // Set the proper dialect according to the database detected.
        String hbnDialect = supportedDialects.get(databaseName);
        hbmConfig.setProperty("hibernate.dialect", hbnDialect);

        // Fetch and pre-process the Hibernate mapping descriptors.
        loadHibernateDescriptors(hbmConfig);

        // Initialize the Hibernate session factory.
        ServiceRegistryBuilder serviceRegistryBuilder = new ServiceRegistryBuilder()
                .applySettings(hbmConfig.getProperties());
        ServiceRegistry serviceRegistry = (ServiceRegistry) ReflectionUtils.invokeMethod(serviceRegistryBuilder,
                "buildServiceRegistry", null);
        if (serviceRegistry == null)
            serviceRegistry = (ServiceRegistry) ReflectionUtils.invokeMethod(serviceRegistryBuilder, "build", null);
        SessionFactory factory = hbmConfig.buildSessionFactory(serviceRegistry);
        hibernateSessionFactoryProvider.setSessionFactory(factory);

        // Set the default schema if specified via system property or via hibernate configuration.
        // NOTE: To set the default schema via hibernate configuration, the hibernate.cfg.xml file must be modified.
        //       This file is located inside the generated webapp. So, if the user wants to change the working schema,
        //       the fastest way is to add a System property when starting the application server to modify the
        //       defaultSchema bean property, instead of opening the war, modifying the file and reassembling it.
        if (StringUtils.isBlank(defaultSchema)) {
            String _defaultSchema = hbmConfig.getProperty(Environment.DEFAULT_SCHEMA);
            if (!StringUtils.isBlank(_defaultSchema))
                defaultSchema = _defaultSchema;
        } else {
            hbmConfig.setProperty(Environment.DEFAULT_SCHEMA, defaultSchema);
        }

        // Synchronize the database schema.
        if (databaseAutoSynchronizer != null) {
            databaseAutoSynchronizer.synchronize(this);
        }

        // Verify the Hibernate mappings.
        verifyHibernateConfig();
    }

    public String getDatabaseName() {
        return databaseName;
    }

    public void setDefaultSchema(String defaultSchema) {
        this.defaultSchema = defaultSchema;
    }

    public String getDefaultSchema() {
        return defaultSchema;
    }

    public boolean isOracleDatabase() {
        return isDatabase(DB_ORACLE);
    }

    public boolean isPostgresDatabase() {
        return isDatabase(DB_POSTGRES);
    }

    public boolean isSQLServerDatabase() {
        return isDatabase(DB_SQLSERVER);
    }

    public boolean isMySQLDatabase() {
        return isDatabase(DB_MYSQL);
    }

    public boolean isH2Database() {
        return isDatabase(DB_H2);
    }

    public boolean isDB2Database() {
        return isDatabase(DB_DB2);
    }

    public boolean isTeiidDatabase() {
        return isDatabase(DB_TEIID);
    }

    public boolean isSybaseDatabase() {
        return isDatabase(DB_SYBASE);
    }

    protected boolean isDatabase(String dbId) {
        return dbId.equals(databaseName);
    }

    public DataSource getLocalDataSource() throws Exception {
        if (localDataSource != null)
            return localDataSource;

        String jndiName = hbmConfig.getProperty("hibernate.connection.datasource");
        if (StringUtils.isBlank(jndiName))
            throw new Exception("Property 'hibernate.connection.datasource' not specified.");

        // Obtain the application data source.
        JNDIDataSourceEntry jndiDS = new JNDIDataSourceEntry();
        jndiDS.setJndiPath(jndiName);
        return localDataSource = jndiDS.getDataSource();
    }

    protected String inferDatabaseName() throws Exception {
        String databaseName = inferDatabaseName(getLocalDataSource());
        if (StringUtils.isBlank(databaseName)) {
            throw new Exception("The underlying database is unknown or the system is unable to recognize it.");
        } else {
            return databaseName;
        }
    }

    public String inferDatabaseName(DataSource ds) throws Exception {
        if (ds == null)
            return null;
        Connection connection = null;
        String dbProductName = null;
        try {
            connection = ds.getConnection();
            dbProductName = connection.getMetaData().getDatabaseProductName().toLowerCase();
            if (dbProductName.contains("h2"))
                return DB_H2;
            if (dbProductName.contains("postgre") || dbProductName.contains("enterprisedb"))
                return DB_POSTGRES;
            if (dbProductName.contains("mysql"))
                return DB_MYSQL;
            if (dbProductName.contains("oracle"))
                return DB_ORACLE;
            if (dbProductName.contains("microsoft") || dbProductName.contains("sqlserver")
                    || dbProductName.contains("sql server"))
                return DB_SQLSERVER;
            if (dbProductName.contains("db2"))
                return DB_DB2;
            if (dbProductName.contains("teiid"))
                return DB_TEIID;
            if (dbProductName.contains("ase") || dbProductName.contains("adaptive"))
                return DB_SYBASE;
        } finally {
            if (connection != null) {
                connection.close();
            }
        }
        log.error("The underlying database code [" + dbProductName + "] is not supported.");
        return null;
    }

    protected void verifyHibernateConfig() throws Exception {
        new HibernateTxFragment() {
            protected void txFragment(Session session) throws Exception {
                Map metadata = session.getSessionFactory().getAllClassMetadata();
                for (Iterator i = metadata.values().iterator(); i.hasNext();) {
                    final AbstractEntityPersister persister = (AbstractEntityPersister) i.next();
                    final String className = persister.getName();
                    if (!ArrayUtils.contains(verificationExcludedClassNames, className)) {
                        log.debug("Verifying: " + className);
                        new HibernateTxFragment(true) {
                            protected void txFragment(Session session) throws Exception {
                                try {
                                    boolean usingOracle = isOracleDatabase();
                                    Query query = session.createQuery(
                                            "from " + className + " c " + (usingOracle ? " where rownum < 6" : ""));
                                    if (!usingOracle)
                                        query.setMaxResults(5);
                                    query.list();
                                } catch (Exception e) {
                                    log.error("Structure verification error for class " + className);
                                    log.error("Error seems to affect table named " + persister.getTableName());
                                    log.error(
                                            "The following stack trace may help you to determine the current error cause: ",
                                            e);
                                }
                            }
                        }.execute();
                    }
                }
            }
        }.execute();
    }

    protected void loadHibernateDescriptors(Configuration hbmConfig) throws IOException {
        Set<File> jars = Application.lookup().getJarFiles();
        for (File jar : jars) {
            ZipFile zf = new ZipFile(jar);
            for (Enumeration en = zf.entries(); en.hasMoreElements();) {
                ZipEntry entry = (ZipEntry) en.nextElement();
                String entryName = entry.getName();
                if (entryName.endsWith("hbm.xml") && !entry.isDirectory()) {
                    InputStream is = zf.getInputStream(entry);
                    String xml = readXMLForFile(entryName, is);
                    xml = processXMLContents(xml);
                    hbmConfig.addXML(xml);
                }
            }
        }
    }

    protected String readXMLForFile(String fileName, InputStream is) throws IOException {
        try {
            BufferedReader reader = null;
            try {
                reader = new BufferedReader(new InputStreamReader(is));
                StringBuffer fileContents = new StringBuffer();
                String lineRead;
                while ((lineRead = reader.readLine()) != null) {
                    fileContents.append(lineRead);
                }
                return fileContents.toString();
            } finally {
                if (reader != null)
                    reader.close();
            }
        } catch (IOException e) {
            log.error("Error processing the Hibernate XML mapping descriptor: " + fileName, e);
            return null;
        }
    }

    protected String processXMLContents(String fileContent) {
        if (ArrayUtils.contains(nativeToSequenceReplaceableDialects, hbmConfig.getProperty("hibernate.dialect"))) {
            String line = "class=\"sequence\" />";
            fileContent = StringUtils.replace(fileContent, "class=\"native\"/>", line);
            fileContent = StringUtils.replace(fileContent, "class=\"native\" />", line);
        }
        if (ArrayUtils.contains(nativeToHiloReplaceableDialects, hbmConfig.getProperty("hibernate.dialect"))) {
            String line = "class=\"hilo\"><param name=\"table\">hibernate_unique_key</param><param name=\"column\">next_hi</param><param name=\"max_lo\">0</param></generator>";
            fileContent = StringUtils.replace(fileContent, "class=\"native\"/>", line);
            fileContent = StringUtils.replace(fileContent, "class=\"native\" />", line);
        }
        return fileContent;
    }

    /**
     * Evict all cache information.
     */
    public synchronized void evictAllCaches() {
        Cache cache = getSessionFactory().getCache();
        cache.evictQueryRegions();
        cache.evictEntityRegions();
        cache.evictCollectionRegions();
    }
}