/*
* Copyright 2000-2013 Enonic AS
* http://www.enonic.com/license
*/
package com.enonic.cms.upgrade.service;
import java.util.Collections;
import java.util.List;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import com.enonic.cms.framework.jdbc.dialect.Dialect;
import com.enonic.cms.store.DatabaseAccessor;
import com.enonic.cms.store.support.ConnectionFactory;
import com.enonic.cms.upgrade.UpgradeContext;
import com.enonic.cms.upgrade.UpgradeException;
import com.enonic.cms.upgrade.UpgradeService;
import com.enonic.cms.upgrade.log.UpgradeLog;
import com.enonic.cms.upgrade.runner.UpgradeTaskRunner;
import com.enonic.cms.upgrade.runner.UpgradeTaskRunnerImpl;
import com.enonic.cms.upgrade.task.UpgradeTask;
import com.enonic.cms.upgrade.task.UpgradeTaskLocator;
@Transactional
public final class UpgradeServiceImpl
implements UpgradeService, InitializingBean
{
private SqlOperationHelper sqlHelper;
private int currentModelNumber = -1;
private int targetModelNumber = -1;
private Dialect dialect;
private ConnectionFactory connectionFactory;
private List<UpgradeTask> upgradeTasks;
private PropertyResolver propertyResolver;
private UpgradeTaskRunner upgradeTaskRunner;
private TransactionTemplate transactionTemplate;
public void setDialect( Dialect dialect )
{
this.dialect = dialect;
}
public void setConnectionFactory( ConnectionFactory connectionFactory )
{
this.connectionFactory = connectionFactory;
}
public void setPropertyResolver( PropertyResolver propertyResolver )
{
this.propertyResolver = propertyResolver;
}
public void setTransactionTemplate( TransactionTemplate transactionTemplate )
{
this.transactionTemplate = transactionTemplate;
}
public void afterPropertiesSet()
throws Exception
{
this.upgradeTaskRunner = new UpgradeTaskRunnerImpl( this.transactionTemplate );
this.sqlHelper = new SqlOperationHelper( this.dialect, this.connectionFactory, this.transactionTemplate );
this.upgradeTasks = new UpgradeTaskLocator().getTasks();
Collections.sort( this.upgradeTasks );
}
public boolean needsUpgrade()
{
final int current = getCurrentModelNumber();
final int target = getTargetModelNumber();
return current < target;
}
public boolean needsSoftwareUpgrade()
{
return getTargetModelNumber() < getCurrentModelNumber();
}
public boolean needsOldUpgradeSystem()
{
return getCurrentModelNumber() < 0;
}
public int getCurrentModelNumber()
{
try
{
if ( this.currentModelNumber < 0 )
{
this.currentModelNumber = this.sqlHelper.getModelNumber();
}
return this.currentModelNumber;
}
catch ( Exception e )
{
throw new IllegalStateException( "Failed to find target model number", e );
}
}
public int getTargetModelNumber()
{
if ( this.targetModelNumber < 0 )
{
this.targetModelNumber = DatabaseAccessor.getLatestDatabase().getVersion();
}
return this.targetModelNumber;
}
public boolean upgrade( UpgradeLog log )
throws UpgradeException
{
return doUpgrade( log, false );
}
public boolean upgradeStep( UpgradeLog log )
throws UpgradeException
{
return doUpgrade( log, true );
}
private boolean doUpgrade( UpgradeLog log, boolean single )
throws UpgradeException
{
try
{
if ( needsUpgrade() && !needsOldUpgradeSystem() )
{
int targetModel = single ? getNextModelNumber() : getTargetModelNumber();
boolean canUpgrade = canUpgrade( log, getCurrentModelNumber(), getTargetModelNumber() );
if ( canUpgrade )
{
doUpgrade( log, getCurrentModelNumber(), targetModel );
return true;
}
}
return false;
}
finally
{
this.currentModelNumber = -1;
this.targetModelNumber = -1;
}
}
private int getNextModelNumber()
{
int currentModelNumber = getCurrentModelNumber();
int lowestNewModelNumber = getTargetModelNumber();
for ( UpgradeTask task : this.upgradeTasks )
{
int modelNumber = task.getModelNumber();
if ( ( modelNumber > currentModelNumber ) && ( modelNumber < lowestNewModelNumber ) )
{
lowestNewModelNumber = modelNumber;
}
}
return lowestNewModelNumber;
}
private void doUpgrade( UpgradeLog log, int fromModel, int toModel )
throws UpgradeException
{
try
{
doUpgrade( createContext( log ), fromModel, toModel );
}
catch ( Throwable e )
{
throw new UpgradeException( "Upgrade from " + fromModel + " to " + toModel + " failed", e );
}
}
private void doUpgrade( UpgradeContext context, int fromModel, int toModel )
throws Throwable
{
context.logInfo( "Upgrading from " + fromModel + " to " + toModel );
for ( UpgradeTask task : this.upgradeTasks )
{
int modelNumber = task.getModelNumber();
if ( ( modelNumber > fromModel ) && ( modelNumber <= toModel ) )
{
doUpgradeStep( context, task );
}
}
context.logInfo( "Upgrade was successful" );
}
private void doUpgradeStep( UpgradeContext context, UpgradeTask task )
throws Throwable
{
context.setCurrentModelNumber( task.getModelNumber() );
context.logInfo( "Upgrading to model " + task.getModelNumber() );
long start = System.currentTimeMillis();
try
{
if ( task.isRunTransactional() )
{
this.upgradeTaskRunner.runUpgradeTaskInTx( context, task );
}
else
{
this.upgradeTaskRunner.runUpgradeTask( context, task );
}
}
catch ( Throwable e )
{
context.logError(
"Upgrade to " + task.getModelNumber() + " failed. Time spent: " + ( System.currentTimeMillis() - start ) + " ms.", e );
throw e;
}
context.logInfo(
"Upgrade to " + task.getModelNumber() + " was successful. Time spent: " + ( System.currentTimeMillis() - start + " ms." ) );
}
private UpgradeContext createContext( UpgradeLog log )
{
return new UpgradeContextImpl( log, this.propertyResolver, this.sqlHelper, getCurrentModelNumber() );
}
/**
* Check if you can upgrade.
*/
public boolean canUpgrade( UpgradeLog log )
{
return canUpgrade( log, getCurrentModelNumber(), getTargetModelNumber() );
}
private boolean canUpgrade( UpgradeLog log, int fromModel, int toModel )
{
UpgradeContext context = createContext( log );
context.logInfo( "Checking if your current model #" + fromModel + " is upgradeable to model #" + toModel + " ..." );
boolean canUpgrade = true;
for ( UpgradeTask task : this.upgradeTasks )
{
int modelNumber = task.getModelNumber();
if ( ( modelNumber > fromModel ) && ( modelNumber <= toModel ) )
{
context.setCurrentModelNumber( modelNumber );
if ( !task.canUpgrade( context ) )
{
context.logError( "Upgrade check NOT OK" );
canUpgrade = false;
}
else
{
context.logInfo( "Upgrade check OK" );
}
}
}
context.setCurrentModelNumber( -1 );
if ( canUpgrade )
{
context.logInfo( "Upgrade check completed. No issues found." );
}
else
{
context.logError( "Cannot upgrade. Please resolve the upgrade issues listed by each model and try again." );
}
return canUpgrade;
}
}