package org.hibernate.tool.schema.spi;
import java.util.EnumSet;
import java.util.Map;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.tool.schema.Action;
import org.hibernate.tool.schema.SourceType;
import org.hibernate.tool.schema.TargetType;
import org.hibernate.tool.schema.internal.ExceptionHandlerHaltImpl;
import org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl;
import org.hibernate.tool.schema.internal.Helper;
import org.jboss.logging.Logger;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_CREATE_SCRIPT_SOURCE;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_CREATE_SOURCE;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DATABASE_ACTION;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DROP_SCRIPT_SOURCE;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_DROP_SOURCE;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_SCRIPTS_ACTION;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_SCRIPTS_CREATE_TARGET;
import static org.hibernate.cfg.AvailableSettings.HBM2DDL_SCRIPTS_DROP_TARGET;
public class SchemaManagementToolCoordinator {
private static final Logger log = Logger.getLogger( SchemaManagementToolCoordinator.class );
public static void process(
final Metadata metadata,
final ServiceRegistry serviceRegistry,
final Map configurationValues,
DelayedDropRegistry delayedDropRegistry) {
final ActionGrouping actions = ActionGrouping.interpret( configurationValues );
if ( actions.getDatabaseAction() == Action.NONE && actions.getScriptAction() == Action.NONE ) {
log.debug( "No actions specified; doing nothing" );
return;
}
final SchemaManagementTool tool = serviceRegistry.getService( SchemaManagementTool.class );
final ConfigurationService configService = serviceRegistry.getService( ConfigurationService.class );
boolean haltOnError = configService.getSetting( AvailableSettings.HBM2DDL_HALT_ON_ERROR, Boolean.class, false);
final ExecutionOptions executionOptions = buildExecutionOptions(
configurationValues,
haltOnError ? ExceptionHandlerHaltImpl.INSTANCE :
ExceptionHandlerLoggedImpl.INSTANCE
);
performScriptAction( actions.getScriptAction(), metadata, tool, serviceRegistry, executionOptions );
performDatabaseAction( actions.getDatabaseAction(), metadata, tool, serviceRegistry, executionOptions );
if ( actions.getDatabaseAction() == Action.CREATE_DROP ) {
delayedDropRegistry.registerOnCloseAction(
tool.getSchemaDropper( configurationValues ).buildDelayedAction(
metadata,
executionOptions,
buildDatabaseTargetDescriptor(
configurationValues,
DropSettingSelector.INSTANCE,
serviceRegistry
)
)
);
}
}
public static ExecutionOptions buildExecutionOptions(
final Map configurationValues,
final ExceptionHandler exceptionHandler) {
return new ExecutionOptions() {
@Override
public boolean shouldManageNamespaces() {
return Helper.interpretNamespaceHandling( configurationValues );
}
@Override
public Map getConfigurationValues() {
return configurationValues;
}
@Override
public ExceptionHandler getExceptionHandler() {
return exceptionHandler;
}
};
}
@SuppressWarnings("unchecked")
private static void performDatabaseAction(
final Action action,
Metadata metadata,
SchemaManagementTool tool,
ServiceRegistry serviceRegistry,
final ExecutionOptions executionOptions) {
switch ( action ) {
case CREATE_ONLY: {
final JpaTargetAndSourceDescriptor createDescriptor = buildDatabaseTargetDescriptor(
executionOptions.getConfigurationValues(),
CreateSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaCreator( executionOptions.getConfigurationValues() ).doCreation(
metadata,
executionOptions,
createDescriptor,
createDescriptor
);
break;
}
case CREATE:
case CREATE_DROP: {
final JpaTargetAndSourceDescriptor dropDescriptor = buildDatabaseTargetDescriptor(
executionOptions.getConfigurationValues(),
DropSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaDropper( executionOptions.getConfigurationValues() ).doDrop(
metadata,
executionOptions,
dropDescriptor,
dropDescriptor
);
final JpaTargetAndSourceDescriptor createDescriptor = buildDatabaseTargetDescriptor(
executionOptions.getConfigurationValues(),
CreateSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaCreator( executionOptions.getConfigurationValues() ).doCreation(
metadata,
executionOptions,
createDescriptor,
createDescriptor
);
break;
}
case DROP: {
final JpaTargetAndSourceDescriptor dropDescriptor = buildDatabaseTargetDescriptor(
executionOptions.getConfigurationValues(),
DropSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaDropper( executionOptions.getConfigurationValues() ).doDrop(
metadata,
executionOptions,
dropDescriptor,
dropDescriptor
);
break;
}
case UPDATE: {
final JpaTargetAndSourceDescriptor migrateDescriptor = buildDatabaseTargetDescriptor(
executionOptions.getConfigurationValues(),
MigrateSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaMigrator( executionOptions.getConfigurationValues() ).doMigration(
metadata,
executionOptions,
migrateDescriptor
);
break;
}
case VALIDATE: {
tool.getSchemaValidator( executionOptions.getConfigurationValues() ).doValidation(
metadata,
executionOptions
);
break;
}
}
}
private static JpaTargetAndSourceDescriptor buildDatabaseTargetDescriptor(
Map configurationValues,
SettingSelector settingSelector,
ServiceRegistry serviceRegistry) {
final Object scriptSourceSetting = settingSelector.getScriptSourceSetting( configurationValues );
final SourceType sourceType = SourceType.interpret(
settingSelector.getSourceTypeSetting( configurationValues ),
scriptSourceSetting != null ? SourceType.SCRIPT : SourceType.METADATA
);
final boolean includesScripts = sourceType != SourceType.METADATA;
if ( includesScripts && scriptSourceSetting == null ) {
throw new SchemaManagementException(
"Schema generation configuration indicated to include CREATE scripts, but no script was specified"
);
}
final ScriptSourceInput scriptSourceInput = includesScripts ?
Helper.interpretScriptSourceSetting(
scriptSourceSetting,
serviceRegistry.getService( ClassLoaderService.class ),
(String) configurationValues.get( AvailableSettings.HBM2DDL_CHARSET_NAME )
)
: null;
return new JpaTargetAndSourceDescriptor() {
@Override
public EnumSet<TargetType> getTargetTypes() {
return EnumSet.of( TargetType.DATABASE );
}
@Override
public ScriptTargetOutput getScriptTargetOutput() {
return null;
}
@Override
public SourceType getSourceType() {
return sourceType;
}
@Override
public ScriptSourceInput getScriptSourceInput() {
return scriptSourceInput;
}
};
}
@SuppressWarnings("unchecked")
private static void performScriptAction(
Action scriptAction,
Metadata metadata,
SchemaManagementTool tool,
ServiceRegistry serviceRegistry,
ExecutionOptions executionOptions) {
switch ( scriptAction ) {
case CREATE_ONLY: {
final JpaTargetAndSourceDescriptor createDescriptor = buildScriptTargetDescriptor(
executionOptions.getConfigurationValues(),
CreateSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaCreator( executionOptions.getConfigurationValues() ).doCreation(
metadata,
executionOptions,
createDescriptor,
createDescriptor
);
break;
}
case CREATE:
case CREATE_DROP: {
final JpaTargetAndSourceDescriptor dropDescriptor = buildScriptTargetDescriptor(
executionOptions.getConfigurationValues(),
DropSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaDropper( executionOptions.getConfigurationValues() ).doDrop(
metadata,
executionOptions,
dropDescriptor,
dropDescriptor
);
final JpaTargetAndSourceDescriptor createDescriptor = buildScriptTargetDescriptor(
executionOptions.getConfigurationValues(),
CreateSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaCreator( executionOptions.getConfigurationValues() ).doCreation(
metadata,
executionOptions,
createDescriptor,
createDescriptor
);
break;
}
case DROP: {
final JpaTargetAndSourceDescriptor dropDescriptor = buildScriptTargetDescriptor(
executionOptions.getConfigurationValues(),
DropSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaDropper( executionOptions.getConfigurationValues() ).doDrop(
metadata,
executionOptions,
dropDescriptor,
dropDescriptor
);
break;
}
case UPDATE: {
final JpaTargetAndSourceDescriptor migrateDescriptor = buildScriptTargetDescriptor(
executionOptions.getConfigurationValues(),
MigrateSettingSelector.INSTANCE,
serviceRegistry
);
tool.getSchemaMigrator( executionOptions.getConfigurationValues() ).doMigration(
metadata,
executionOptions,
migrateDescriptor
);
break;
}
case VALIDATE: {
throw new SchemaManagementException( "VALIDATE is not valid SchemaManagementTool action for script output" );
}
}
}
private static JpaTargetAndSourceDescriptor buildScriptTargetDescriptor(
Map configurationValues,
SettingSelector settingSelector,
ServiceRegistry serviceRegistry) {
final Object scriptSourceSetting = settingSelector.getScriptSourceSetting( configurationValues );
final SourceType sourceType = SourceType.interpret(
settingSelector.getSourceTypeSetting( configurationValues ),
scriptSourceSetting != null ? SourceType.SCRIPT : SourceType.METADATA
);
final boolean includesScripts = sourceType != SourceType.METADATA;
if ( includesScripts && scriptSourceSetting == null ) {
throw new SchemaManagementException(
"Schema generation configuration indicated to include CREATE scripts, but no script was specified"
);
}
String charsetName = (String) configurationValues.get( AvailableSettings.HBM2DDL_CHARSET_NAME );
final ScriptSourceInput scriptSourceInput = includesScripts
? Helper.interpretScriptSourceSetting( scriptSourceSetting, serviceRegistry.getService( ClassLoaderService.class ), charsetName )
: null;
final ScriptTargetOutput scriptTargetOutput = Helper.interpretScriptTargetSetting(
settingSelector.getScriptTargetSetting( configurationValues ),
serviceRegistry.getService( ClassLoaderService.class ),
charsetName
);
return new JpaTargetAndSourceDescriptor() {
@Override
public EnumSet<TargetType> getTargetTypes() {
return EnumSet.of( TargetType.SCRIPT );
}
@Override
public ScriptTargetOutput getScriptTargetOutput() {
return scriptTargetOutput;
}
@Override
public SourceType getSourceType() {
return sourceType;
}
@Override
public ScriptSourceInput getScriptSourceInput() {
return scriptSourceInput;
}
};
}
private interface SettingSelector {
Object getSourceTypeSetting(Map configurationValues);
Object getScriptSourceSetting(Map configurationValues);
Object getScriptTargetSetting(Map configurationValues);
}
private static class CreateSettingSelector implements SettingSelector {
public static final CreateSettingSelector INSTANCE = new CreateSettingSelector();
@Override
public Object getSourceTypeSetting(Map configurationValues) {
return configurationValues.get( HBM2DDL_CREATE_SOURCE );
}
@Override
public Object getScriptSourceSetting(Map configurationValues) {
return configurationValues.get( HBM2DDL_CREATE_SCRIPT_SOURCE );
}
@Override
public Object getScriptTargetSetting(Map configurationValues) {
return configurationValues.get( HBM2DDL_SCRIPTS_CREATE_TARGET );
}
}
private static class DropSettingSelector implements SettingSelector {
public static final DropSettingSelector INSTANCE = new DropSettingSelector();
@Override
public Object getSourceTypeSetting(Map configurationValues) {
return configurationValues.get( HBM2DDL_DROP_SOURCE );
}
@Override
public Object getScriptSourceSetting(Map configurationValues) {
return configurationValues.get( HBM2DDL_DROP_SCRIPT_SOURCE );
}
@Override
public Object getScriptTargetSetting(Map configurationValues) {
return configurationValues.get( HBM2DDL_SCRIPTS_DROP_TARGET );
}
}
private static class MigrateSettingSelector implements SettingSelector {
public static final MigrateSettingSelector INSTANCE = new MigrateSettingSelector();
@Override
public Object getSourceTypeSetting(Map configurationValues) {
return SourceType.METADATA;
}
@Override
public Object getScriptSourceSetting(Map configurationValues) {
return null;
}
@Override
public Object getScriptTargetSetting(Map configurationValues) {
return configurationValues.get( HBM2DDL_SCRIPTS_CREATE_TARGET );
}
}
public static class ActionGrouping {
private final Action databaseAction;
private final Action scriptAction;
public ActionGrouping(Action databaseAction, Action scriptAction) {
this.databaseAction = databaseAction;
this.scriptAction = scriptAction;
}
public Action getDatabaseAction() {
return databaseAction;
}
public Action getScriptAction() {
return scriptAction;
}
public static ActionGrouping interpret(Map configurationValues) {
Action databaseAction = Action.interpretJpaSetting( configurationValues.get( HBM2DDL_DATABASE_ACTION ) );
Action scriptAction = Action.interpretJpaSetting( configurationValues.get( HBM2DDL_SCRIPTS_ACTION ) );
if ( databaseAction == Action.NONE && scriptAction == Action.NONE ) {
final Action hbm2ddlAutoAction = Action.interpretHbm2ddlSetting( configurationValues.get( HBM2DDL_AUTO ) );
if ( hbm2ddlAutoAction != Action.NONE ) {
databaseAction = hbm2ddlAutoAction;
}
}
return new ActionGrouping( databaseAction, scriptAction );
}
}
}