/*
* Copyright 2014 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.artificer.repository.hibernate;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import java.io.Serializable;
import java.net.URL;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.artificer.common.ArtificerConfig;
import org.artificer.common.ArtificerException;
import org.artificer.common.error.ArtificerNotFoundException;
import org.artificer.common.error.ArtificerServerException;
import org.artificer.common.ontology.ArtificerOntology;
import org.artificer.repository.hibernate.entity.ArtificerArtifact;
import org.artificer.repository.hibernate.entity.ArtificerStoredQuery;
import org.hibernate.Hibernate;
import org.hibernate.Session;
import org.hibernate.ejb.HibernatePersistence;
import org.hibernate.engine.spi.SessionImplementor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Brett Meyer.
*/
public class HibernateUtil {
private static String persistenceUnit = "Artificer";
private static EntityManagerFactory entityManagerFactory = null;
private static Logger LOG = LoggerFactory.getLogger(HibernateUtil.class);
/**
* A worker pattern, used for *all* integration with the EntityManager. This should be the only public means to use it.
* @param <T>
*/
public static abstract class HibernateTask<T> {
public T execute() throws ArtificerException {
EntityManager entityManager = null;
try {
entityManager = entityManager();
entityManager.getTransaction().begin();
T rtn = doExecute(entityManager);
entityManager.getTransaction().commit();
return rtn;
} catch (ArtificerException ae) {
if (entityManager != null) {
try {
entityManager.getTransaction().rollback();
} catch (Throwable t1) {
// eat it
}
}
throw ae;
} catch (Throwable t) {
if (entityManager != null) {
try {
entityManager.getTransaction().rollback();
} catch (Throwable t1) {
// eat it
}
}
throw new ArtificerServerException(t);
} finally {
// entityManager.unwrap(Session.class).getSessionFactory().getStatistics().logSummary();
if (entityManager != null) {
entityManager.close();
}
}
}
protected abstract T doExecute(EntityManager entityManager) throws Exception;
}
private synchronized static EntityManager entityManager() throws Exception {
if (entityManagerFactory == null) {
// Pass in all hibernate.* settings from artificer.properties
Map<String, Object> properties = ArtificerConfig.getConfigProperties("hibernate");
if (properties.containsKey("hibernate.connection.url")) {
// If a connection is used, we *cannot* rely on Hibernate's built-in connection pool. Instead,
// automatically set up HikariCP.
initHikariCP(properties);
}
entityManagerFactory = new HibernatePersistence().createEntityManagerFactory(persistenceUnit, properties);
EntityManager entityManager = entityManagerFactory.createEntityManager();
initDDL(entityManager, properties);
return entityManager;
} else {
return entityManagerFactory.createEntityManager();
}
}
private static void initHikariCP(Map<String, Object> properties) {
String connectionUrl = (String) properties.remove("hibernate.connection.url");
String username = (String) properties.remove("hibernate.connection.username");
String password = (String) properties.remove("hibernate.connection.password");
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setJdbcUrl(connectionUrl);
hikariConfig.setUsername(username);
hikariConfig.setPassword(password);
// In case we're using MySQL, these settings are recommended by HikariCP:
hikariConfig.addDataSourceProperty("cachePrepStmts", "true");
hikariConfig.addDataSourceProperty("prepStmtCacheSize", "250");
hikariConfig.addDataSourceProperty("prepStmtCacheSqlLimit", "2048");
hikariConfig.addDataSourceProperty("useServerPrepStmts", "true");
String dialect = (String) properties.get("hibernate.dialect");
if (dialect != null && dialect.contains("PostgreSQL")) {
// The JDBC jar verion in the IP BOM does not support Connection.isValid(), so need to use this:
hikariConfig.setConnectionTestQuery("SELECT 1");
}
HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);
properties.put("hibernate.connection.datasource", hikariDataSource);
}
private static void initDDL(EntityManager entityManager, Map<String, Object> properties) throws Exception {
// If the DDL is not already installed in the DB, automatically do it on first use.
SessionImplementor session = (SessionImplementor) entityManager.getDelegate();
Connection connection = session.connection();
String schema = (String)properties.get("hibernate.default_schema");
if (!hasTables(connection, schema)) {
// our tables don't exist -- create them
String dialect = (String)properties.get("hibernate.dialect");
if (dialect != null) {
String ddlFile;
boolean isOracle = false;
boolean isDB2 = false;
if (dialect.contains("PostgreSQL")) {
ddlFile = "postgres9.sql";
} else if (dialect.contains("MySQL")) {
ddlFile = "mysql5.sql";
} else if (dialect.contains("Oracle")) {
ddlFile = "oracle10.sql";
isOracle = true;
} else if (dialect.contains("SQLServer")) {
ddlFile = "mssql2012.sql";
} else if (dialect.contains("DB2")) {
ddlFile = "db2.sql";
isDB2 = true;
} else {
ddlFile = "h2.sql";
}
Statement statement = null;
LOG.info("INITIALIZING DATABASE WITH SCRIPT: " + ddlFile);
URL url = HibernateUtil.class.getClassLoader().getResource("ddl/" + ddlFile);
String ddl = IOUtils.toString(url);
String[] queries = StringUtils.split(ddl, ";");
if (queries != null && queries.length > 0) {
for (String query : queries) {
if (query != null && !query.trim().equals("")) {
try {
statement = connection.createStatement();
if(query!=null && !query.trim().equals("")){
if (!isOracle && !isDB2) {
query += ";";
} else if (isDB2) {
if (query.endsWith(";")) {
query = query.substring(0, query.length() - 1);
}
}
statement.executeUpdate(query);
}
} catch (Exception e) {
LOG.error("Exception executing Query:" + query, e);
throw e;
} finally {
if (statement != null) {
statement.close();
}
// do *not* close the connection -- it will
// still be
// used by this instance of the EntityManager
}
}
}
}
LOG.info("END INITIALIZING DATABASE WITH SCRIPT");
}
}
}
private static boolean hasTables(Connection connection, String schema) throws Exception {
DatabaseMetaData metadata = connection.getMetaData();
// check if "ArtificerArtifact" table exists
ResultSet tables = metadata.getTables(null, schema, "Artifact", null);
if (tables.next()) {
return true;
}
// also need to check all caps (thanks, Oracle)
tables = metadata.getTables(null, schema, "ARTIFACT", null);
if (tables.next()) {
return true;
}
// otherwise, nope
return false;
}
public static ArtificerArtifact getArtifact(String uuid, EntityManager entityManager, boolean fullFetch) throws ArtificerException {
Query q = entityManager.createQuery("FROM ArtificerArtifact a WHERE a.trashed = false AND a.uuid = :uuid");
q.setParameter("uuid", uuid);
q.unwrap(org.hibernate.Query.class).setCacheable(true);
ArtificerArtifact artifact;
try {
artifact = (ArtificerArtifact) q.getSingleResult();
} catch (NoResultException e) {
throw ArtificerNotFoundException.artifactNotFound(uuid);
}
if (fullFetch) {
Hibernate.initialize(artifact.getClassifiers());
Hibernate.initialize(artifact.getComments());
Hibernate.initialize(artifact.getNormalizedClassifiers());
Hibernate.initialize(artifact.getRelationships());
}
return artifact;
}
public static ArtificerOntology getOntology(String uuid, EntityManager entityManager) throws ArtificerException {
Query q = entityManager.createQuery("FROM ArtificerOntology a WHERE a.uuid = :uuid");
q.setParameter("uuid", uuid);
q.unwrap(org.hibernate.Query.class).setCacheable(true);
ArtificerOntology ontology;
try {
ontology = (ArtificerOntology) q.getSingleResult();
} catch (NoResultException e) {
throw ArtificerNotFoundException.ontologyNotFound(uuid);
}
Hibernate.initialize(ontology.getRootClasses());
return ontology;
}
public static ArtificerStoredQuery getStoredQuery(String queryName, EntityManager entityManager) throws ArtificerException {
ArtificerStoredQuery storedQuery = entityManager.find(ArtificerStoredQuery.class, queryName);
if (storedQuery == null) {
throw ArtificerNotFoundException.storedQueryNotFound(queryName);
}
return storedQuery;
}
public static void evict(Class clazz, Serializable id, EntityManager entityManager) {
entityManager.unwrap(Session.class).getSessionFactory().getCache().evictEntity(clazz, id);
}
/**
* Override the name of the persistence unit used to build the EMF. Mainly used for testing.
* @param name
*/
public static void setPersistenceUnit(String name) {
persistenceUnit = name;
}
}