/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.store.hibernate.cache.invalidation;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import com.enonic.esl.sql.model.Column;
import com.enonic.esl.sql.model.Database;
import com.enonic.esl.sql.model.Table;
import com.enonic.esl.sql.model.datatypes.CharType;
import com.enonic.esl.sql.model.datatypes.DataType;
import com.enonic.esl.sql.model.datatypes.IntegerType;
import com.enonic.cms.framework.cache.CacheFacade;
import com.enonic.cms.framework.cache.CacheManager;
import com.enonic.cms.store.DatabaseAccessor;
/**
* This class implements the cache invalidator.
*/
public class CacheInvalidator
{
/**
* Session factory.
*/
private final SessionFactory sessionFactory;
/**
* Cache manager.
*/
private final CacheManager cacheMananger;
/**
* Invalidation rules.
*/
private final InvalidationRules invalidationRules;
private final PrimaryKeyResolver primaryKeyResolver = new PrimaryKeyResolver();
private Map<String, Column[]> tableMap = new HashMap<String, Column[]>();
/**
* Construct the invalidator.
*/
public CacheInvalidator( Configuration configuration, SessionFactory sessionFactory, CacheManager cacheMananger )
{
this.sessionFactory = sessionFactory;
this.cacheMananger = cacheMananger;
InvalidationRulesBuilder builder = new InvalidationRulesBuilder( configuration );
this.invalidationRules = builder.build();
Database db = DatabaseAccessor.getLatestDatabase();
Table[] tables = db.getTables();
for ( Table table : tables )
{
tableMap.put( table.getName().toLowerCase(), table.getPrimaryKeys() );
}
}
/**
* Analyze the SQL. If it's not a select, find the affected table and invalidate it.
*/
public void invalidateSql( String sql )
{
invalidateSql( sql, null );
}
/**
* Analyze the SQL. If it's not a select, find the affected table and invalidate it.
*/
public void invalidateSql( String sql, List paramList )
{
SqlAnalyzer analyzer = new SqlAnalyzer( sql.trim().toLowerCase() );
if ( analyzer.resolveTableName() != null )
{
invalidateTable( analyzer, paramList );
}
}
/**
* Find primary key. Return null if not found.
*/
private Serializable findPrimaryKeyValue( String tableName, String sql, List paramList )
{
Column[] primaryKeyColumns = tableMap.get( tableName );
if ( primaryKeyColumns == null )
{
return null;
}
if ( primaryKeyColumns.length == 1 )
{
String columnName = primaryKeyColumns[0].getName().toLowerCase();
DataType dataType = primaryKeyColumns[0].getType();
if ( dataType instanceof IntegerType )
{
return primaryKeyResolver.resolveIntegerValue( sql, paramList, columnName );
}
else if ( dataType instanceof CharType )
{
return primaryKeyResolver.resolveStringValue( sql, paramList, columnName );
}
}
return null;
}
/**
* Find the right table to invalidate and make calls to invalidate the necessary domain objects and collections that are affected by the
* table change.
*/
private void invalidateTable( SqlAnalyzer analyzer, List paramList )
{
TableInvalidation rule = this.invalidationRules.getTableRuleByName( analyzer.resolveTableName() );
if ( rule != null )
{
Serializable primaryKey = null;
if ( !analyzer.isInsertType() )
{
primaryKey = findPrimaryKeyValue( analyzer.resolveTableName(), analyzer.getSql(), paramList );
}
invalidateTable( rule, primaryKey, analyzer.isInsertType() );
}
}
/**
* Invalidate on table.
*/
private void invalidateTable( TableInvalidation rule, Serializable primaryKey, boolean insertType )
{
invalidateNamedQueries();
invalidateCollectionCaches( rule );
if ( !insertType )
{
invalidateEntityCache( rule, primaryKey );
}
}
/**
* Invalidate queries.
*/
private void invalidateNamedQueries()
{
this.sessionFactory.evictQueries();
for ( String regionName : this.invalidationRules.getQueryCacheRegions() )
{
this.sessionFactory.evictQueries( regionName );
}
}
/**
* Invalidate entity cache.
*/
private void invalidateEntityCache( TableInvalidation rule, Serializable primaryKey )
{
Class entityClass = rule.getEntityClass();
if ( entityClass != null )
{
if ( primaryKey != null )
{
this.sessionFactory.evict( entityClass, primaryKey );
}
else
{
this.sessionFactory.evict( entityClass );
}
}
}
/**
* Invalidate collection cache.
*/
private void invalidateCollectionCaches( TableInvalidation rule )
{
for ( String roleName : rule.getCollectionRoles() )
{
this.sessionFactory.getCache().evictCollectionRegion( roleName );
}
}
}