/*
* This program is copyright (c) 2007 Hortis-GRC SA.
*
* This file is part of Sonar.
* Sonar is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Sonar 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package ch.hortis.sonar.jpa;
import ch.hortis.sonar.model.JdbcData;
import ch.hortis.sonar.model.SchemaInfo;
import org.hibernate.stat.EntityStatistics;
import org.hibernate.stat.QueryStatistics;
import org.hibernate.stat.SecondLevelCacheStatistics;
import org.hibernate.stat.Statistics;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.NoResultException;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
/**
* Contains all functions to setup the database.
*/
public class Persistence {
public static final int MODEL_VERSION = 7;
protected final JdbcData jdbcData;
protected final Logger log;
private EntityManagerFactory factory = null;
protected Persistence( JdbcData jdbcData ) {
this.jdbcData = jdbcData;
this.log = LoggerFactory.getLogger( this.getClass().getName() );
}
public static Persistence create( JdbcData jdbcData ) throws PersistenceException {
Persistence persistence = new Persistence( jdbcData );
persistence.install();
return persistence;
}
public static boolean databaseExists( JdbcData jdbcData ) throws SQLException, WrongDatabaseVersionException {
Connection jdbcConn;
if ( jdbcData.getDatasource() != null ) {
DataSource ds;
try {
Context ctx = new InitialContext();
ds = (DataSource) ctx.lookup( jdbcData.getDatasource() );
} catch (Exception ex) {
SQLException sqlEx = new SQLException( "Error during datasource JNDI lookup" );
sqlEx.initCause( ex );
throw sqlEx;
}
jdbcConn = ds.getConnection();
} else {
try {
Class.forName( jdbcData.getDriverClassName() );
} catch (ClassNotFoundException e) {
throw new SQLException( "Cannot found sql driver " + jdbcData.getDriverClassName() );
}
jdbcConn = DriverManager.getConnection( jdbcData.getUrl(), jdbcData.getUsername(), jdbcData.getPassword() );
}
Statement stmt = null;
ResultSet rs = null;
try {
stmt = jdbcConn.createStatement();
rs = stmt.executeQuery( "SELECT version FROM " + SchemaInfo.TABLE_NAME );
while ( rs.next() ) {
int version = rs.getInt( 1 );
if ( version == MODEL_VERSION ) {
return true;
}
throw new WrongDatabaseVersionException( "Database required model version (" + MODEL_VERSION + ") does not match current version (" + version + ")" );
}
} catch (SQLException ex) {
// don't care
} finally {
if ( rs != null ) rs.close();
if ( stmt != null ) stmt.close();
jdbcConn.close();
}
return false;
}
private void install() throws PersistenceException {
try {
factory = JPAUtil.getEntityManagerFactory( jdbcData, null );
} catch (javax.persistence.PersistenceException ex) {
log.debug( "Persistence exception " + ex.getMessage() );
// if an error exists trace should look like : org.hibernate.HibernateException: Missing table:
throw new PersistenceException( ex );
//createDatabase("create");
}
EntityManager manager = factory.createEntityManager();
try {
SchemaInfo schemaInfo = manager.find( SchemaInfo.class, MODEL_VERSION );
if ( schemaInfo == null ) {
throw new WrongDatabaseVersionException( "Database required model version (" + MODEL_VERSION + ") not found" );
}
} catch (NoResultException exception) {
throw new PersistenceException( exception.getMessage() );
} finally {
manager.close();
}
}
protected final void createDatabase( String creationMode, int version ) throws PersistenceException {
SchemaInfo schemaInfo = new SchemaInfo();
schemaInfo.setVersion( version );
Map<String, String> configuration = new HashMap<String, String>();
configuration.put( "hibernate.hbm2ddl.auto", creationMode );
try {
factory = JPAUtil.getEntityManagerFactory( jdbcData, configuration );
EntityManager manager = factory.createEntityManager();
manager.getTransaction().begin();
manager.persist( schemaInfo );
manager.getTransaction().commit();
manager.close();
log.info( "Database created" );
} catch (javax.persistence.PersistenceException exception) {
throw new PersistenceException( exception );
}
}
public void dumpStats( Logger logger ) {
if ( factory instanceof org.hibernate.ejb.EntityManagerFactoryImpl ) {
org.hibernate.ejb.EntityManagerFactoryImpl hibernateFactory = (org.hibernate.ejb.EntityManagerFactoryImpl) factory;
Statistics stats = hibernateFactory.getSessionFactory().getStatistics();
if ( !stats.isStatisticsEnabled() ) {
logger.info( "DB statistics computation are disabled, enable them using sonar.db.statistics=true system property" );
return;
}
double cacheHitCount = stats.getSecondLevelCacheHitCount();
Date statsDate = new Date( stats.getStartTime() );
logger.info( "DB Statistics computed since " + statsDate );
logger.info( "" );
logger.info( "Cache hit count:" + cacheHitCount );
double cacheMissCount = stats.getSecondLevelCacheMissCount();
logger.info( "Cache miss count:" + cacheMissCount );
double cachePutCount = stats.getSecondLevelCachePutCount();
logger.info( "Cache put count:" + cachePutCount );
double cacheHitRatio = cacheHitCount / ( cacheHitCount + cacheMissCount );
logger.info( "Cache Hit ratio:" + cacheHitRatio );
logger.info( "" );
logger.info( "Cache regions statistics :" );
logger.info( "" );
String[] cacheRegions = stats.getSecondLevelCacheRegionNames();
for (String region : cacheRegions) {
SecondLevelCacheStatistics cacheStats = stats.getSecondLevelCacheStatistics( region );
logger.info( "Cache region:" + cacheStats.getCategoryName() );
logger.info( "Elements in memory:" + cacheStats.getElementCountInMemory() );
logger.info( "Hits count:" + cacheStats.getHitCount() );
logger.info( "Miss count:" + cacheStats.getMissCount() );
logger.info( "Put count:" + cacheStats.getPutCount() );
logger.info( "" );
}
logger.info( "Entites statistics :" );
logger.info( "" );
String[] entities = stats.getEntityNames();
for (String entity : entities) {
EntityStatistics stat = stats.getEntityStatistics( entity );
logger.info( "Entity name:" + stat.getCategoryName() );
logger.info( "Entity delete count:" + stat.getDeleteCount() );
logger.info( "Entity fetch count:" + stat.getFetchCount() );
logger.info( "Entity insert count:" + stat.getInsertCount() );
logger.info( "Entity load count:" + stat.getLoadCount() );
logger.info( "Entity update count:" + stat.getUpdateCount() );
logger.info( "" );
}
logger.info( "Queries statistics :\n" );
logger.info( "Queries execution count :" + stats.getQueryExecutionCount() );
logger.info( "Queries max time :" + stats.getQueryExecutionMaxTime() );
logger.info( "Queries max time sql:" + stats.getQueryExecutionMaxTimeQueryString() );
logger.info( "" );
String[] queries = stats.getQueries();
for (String query : queries) {
QueryStatistics stat = stats.getQueryStatistics( query );
logger.info( "Query name:" + stat.getCategoryName() );
logger.info( "Query cache hit count:" + stat.getCacheHitCount() );
logger.info( "Query cache miss count:" + stat.getCacheMissCount() );
logger.info( "Query cache put count:" + stat.getCachePutCount() );
logger.info( "Query exec count:" + stat.getExecutionCount() );
logger.info( "Query avg time:" + stat.getExecutionAvgTime() );
logger.info( "Query max time:" + stat.getExecutionMaxTime() );
logger.info( "Query min time:" + stat.getExecutionMinTime() );
logger.info( "Query row count:" + stat.getExecutionRowCount() );
logger.info( "" );
}
stats.clear();
}
}
public EntityManager getNewEntityManager() {
return factory.createEntityManager();
}
public void close() {
if ( factory.isOpen() ) {
factory.close();
}
}
}