/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.vertical.engine.handlers;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import com.enonic.esl.sql.model.DatabaseSchemaTool;
import com.enonic.vertical.engine.dbmodel.VerticalDatabase;
import com.enonic.cms.core.jdbc.DatabaseValuesInitializer;
import com.enonic.cms.store.DatabaseAccessor;
/**
* This class implements the system handler that takes care of creating database schema and populating version numbers.
*/
@Component
public final class SystemHandler
extends BaseHandler
{
/**
* Logger.
*/
private final static Logger LOG = LoggerFactory.getLogger( SystemHandler.class );
/**
* Return true if it has the table.
*/
private boolean hasTable( Connection conn, String tableName )
{
Statement stmt = null;
try
{
stmt = conn.createStatement();
close( stmt.executeQuery( "SELECT * FROM " + tableName ) );
return true;
}
catch ( SQLException e )
{
try
{
conn.rollback();
}
catch ( Exception e2 )
{
// Do nothing
}
return false;
}
finally
{
close( stmt );
}
}
/**
* Return the model version.
*/
private int getModelNumber( Connection conn )
throws Exception
{
if ( hasTable( conn, this.db.tModelVersion.getName() ) )
{
return selectModelNumber( conn );
}
else
{
return -1;
}
}
/**
* Select the model version.
*/
private int selectModelNumber( Connection conn )
throws Exception
{
PreparedStatement stmt =
conn.prepareStatement( "SELECT " + this.db.tModelVersion.mve_lVersion.getName() + " FROM " + this.db.tModelVersion.getName() +
" WHERE " + this.db.tModelVersion.mve_sKey.getName() + " = ?" );
ResultSet result = null;
try
{
stmt.setString( 1, "model" );
result = stmt.executeQuery();
if ( result.next() )
{
return result.getInt( 1 );
}
else
{
return 0;
}
}
finally
{
close( result );
close( stmt );
}
}
/**
* Return version number.
*/
private String getVerticalVersion( Connection conn )
throws Exception
{
PreparedStatement stmt = conn.prepareStatement( "SELECT vve_sVersionName FROM tVerticalVersion" );
ResultSet result = null;
try
{
result = stmt.executeQuery();
if ( result.next() )
{
return result.getString( 1 );
}
else
{
return "";
}
}
finally
{
close( result );
close( stmt );
}
}
/**
* Set model version.
*/
private void setModelNumber( int version )
throws Exception
{
Connection conn = getConnection();
if ( !updateModelNumber( conn, version ) )
{
insertModelNumber( conn, version );
}
}
/**
* Update model version.
*/
private boolean updateModelNumber( Connection conn, int version )
throws Exception
{
PreparedStatement stmt =
conn.prepareStatement( "UPDATE " + this.db.tModelVersion.getName() + " SET " + this.db.tModelVersion.mve_lVersion.getName() +
" = ? WHERE " + this.db.tModelVersion.mve_sKey.getName() + " = ?" );
try
{
stmt.setInt( 1, version );
stmt.setString( 2, "model" );
return stmt.executeUpdate() > 0;
}
finally
{
close( stmt );
}
}
/**
* Insert model version.
*/
private void insertModelNumber( Connection conn, int version )
throws Exception
{
PreparedStatement stmt = conn.prepareStatement( "INSERT INTO " + this.db.tModelVersion.getName() + " VALUES (?, ?)" );
try
{
stmt.setString( 1, "model" );
stmt.setInt( 2, version );
stmt.executeUpdate();
}
finally
{
close( stmt );
}
}
/**
* Initialize the schema.
*/
public boolean initializeDatabaseSchema()
throws Exception
{
Connection conn = getConnection();
return initializeDatabaseSchema( conn );
}
/**
* Initialize the database.
*/
public boolean initializeDatabaseValues()
throws Exception
{
Connection conn = getConnection();
return initializeDatabaseValues( conn );
}
/**
* Creating schema if not created.
*/
private boolean initializeDatabaseSchema( Connection conn )
throws Exception
{
if ( isSchemaCreated( conn ) )
{
return false;
}
else
{
long tm = System.currentTimeMillis();
LOG.info( "Database schema does not exist. Creating schema..." );
List<?> sqlList = DatabaseSchemaTool.generateDatabaseSchema( DatabaseAccessor.getLatestDatabase() );
initializeDatabaseSchema( conn, sqlList );
LOG.info( "Database schema created in " + ( System.currentTimeMillis() - tm ) + " ms" );
return true;
}
}
/**
* Return true if schema is created.
*/
private boolean isSchemaCreated( Connection conn )
{
return hasTable( conn, this.db.tModelVersion.getName() ) || hasTable( conn, "tVerticalVersion" );
}
/**
* Execute database schema sqls.
*/
private void initializeDatabaseSchema( Connection conn, List<?> schema )
throws Exception
{
Statement stmt = null;
String currentSql = null;
try
{
stmt = conn.createStatement();
for ( Object sql : schema )
{
currentSql = sql.toString();
LOG.debug( "Executing statement: " + currentSql );
stmt.execute( currentSql );
}
}
catch ( SQLException e )
{
if ( currentSql != null )
{
LOG.debug( "Failed to execute: " + currentSql );
}
throw e;
}
finally
{
close( stmt );
}
}
/**
* Initialize the database.
*/
private boolean initializeDatabaseValues( Connection conn )
throws Exception
{
if ( !isDatabaseValuesInitialized( conn ) )
{
LOG.info( "Populating database with initial values..." );
final int modelNumber = VerticalDatabase.getInstance().getVersion();
new DatabaseValuesInitializer().initializeDatabaseValues( conn );
setModelNumber( modelNumber );
return true;
}
else
{
return false;
}
}
/**
* Return true if database values is initialized.
*/
private boolean isDatabaseValuesInitialized( Connection conn )
throws Exception
{
if ( hasTable( conn, this.db.tModelVersion.getName() ) )
{
return getModelNumber( conn ) > 0;
}
else if ( hasTable( conn, "tVerticalVersion" ) )
{
return getVerticalVersion( conn ) != null;
}
else
{
return true;
}
}
}