Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.flywaydb.core.FlywayExecutor Maven / Gradle / Ivy
/*
* Copyright (C) Red Gate Software Ltd 2010-2024
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.flywaydb.core;
import lombok.CustomLog;
import org.flywaydb.core.api.ClassProvider;
import org.flywaydb.core.api.ResourceProvider;
import org.flywaydb.core.api.callback.Callback;
import org.flywaydb.core.api.configuration.Configuration;
import org.flywaydb.core.api.migration.JavaMigration;
import org.flywaydb.core.extensibility.LicenseGuard;
import org.flywaydb.core.extensibility.RootTelemetryModel;
import org.flywaydb.core.extensibility.Tier;
import org.flywaydb.core.internal.callback.*;
import org.flywaydb.core.internal.clazz.NoopClassProvider;
import org.flywaydb.core.internal.configuration.ConfigurationValidator;
import org.flywaydb.core.internal.database.DatabaseType;
import org.flywaydb.core.internal.database.base.CommunityDatabaseType;
import org.flywaydb.core.internal.database.base.Database;
import org.flywaydb.core.internal.database.base.Schema;
import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory;
import org.flywaydb.core.internal.jdbc.StatementInterceptor;
import org.flywaydb.core.internal.parser.ParsingContext;
import org.flywaydb.core.internal.resolver.CompositeMigrationResolver;
import org.flywaydb.core.internal.resolver.script.ScriptMigrationResolver;
import org.flywaydb.core.internal.resource.NoopResourceProvider;
import org.flywaydb.core.internal.resource.ResourceNameValidator;
import org.flywaydb.core.internal.resource.StringResource;
import org.flywaydb.core.internal.scanner.LocationScannerCache;
import org.flywaydb.core.internal.scanner.ResourceNameCache;
import org.flywaydb.core.internal.scanner.Scanner;
import org.flywaydb.core.internal.schemahistory.SchemaHistory;
import org.flywaydb.core.internal.schemahistory.SchemaHistoryFactory;
import org.flywaydb.core.internal.sqlscript.SqlScript;
import org.flywaydb.core.internal.sqlscript.SqlScriptExecutorFactory;
import org.flywaydb.core.internal.sqlscript.SqlScriptFactory;
import org.flywaydb.core.internal.strategy.RetryStrategy;
import org.flywaydb.core.internal.util.FileUtils;
import org.flywaydb.core.internal.util.FlywayDbWebsiteLinks;
import org.flywaydb.core.internal.util.IOUtils;
import org.flywaydb.core.internal.util.Pair;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.flywaydb.core.internal.database.DatabaseTypeRegister.redactJdbcUrl;
import static org.flywaydb.core.internal.util.DataUnits.MEGABYTE;
import org.flywaydb.core.internal.license.FlywayExpiredLicenseKeyException;
@CustomLog
public class FlywayExecutor {
public interface Command {
T execute(CompositeMigrationResolver migrationResolver, SchemaHistory schemaHistory, Database database,
Schema defaultSchema, Schema[] schemas, CallbackExecutor callbackExecutor, StatementInterceptor statementInterceptor);
}
/**
* Designed so we can fail fast if the configuration is invalid
*/
private final ConfigurationValidator configurationValidator;
/**
* Designed so we can fail fast if the SQL file resources are invalid
*/
private final ResourceNameValidator resourceNameValidator;
/**
* Used to cache resource names for classpath scanning between commands
*/
private final ResourceNameCache resourceNameCache;
/**
* Used to cache LocationScanners between commands
*/
private final LocationScannerCache locationScannerCache;
/**
* Whether the database connection info has already been printed in the logs
*/
private boolean dbConnectionInfoPrinted;
private final Configuration configuration;
public FlywayExecutor(Configuration configuration) {
this.configurationValidator = new ConfigurationValidator();
this.resourceNameValidator = new ResourceNameValidator();
this.resourceNameCache = new ResourceNameCache();
this.locationScannerCache = new LocationScannerCache();
this.configuration = configuration;
}
/**
* Executes this command with proper resource handling and cleanup.
*
* @param command The command to execute.
* @param The type of the result.
* @return The result of the command.
*/
public T execute(Command command, boolean scannerRequired, FlywayTelemetryManager flywayTelemetryManager) {
T result;
configurationValidator.validate(configuration);
StatementInterceptor statementInterceptor = configuration.getPluginRegister().getPlugins(StatementInterceptor.class).stream()
.filter(i -> i.isConfigured(configuration))
.findFirst()
.orElse(null);
final Pair> resourceProviderClassProviderPair = createResourceAndClassProviders(scannerRequired);
final ResourceProvider resourceProvider = resourceProviderClassProviderPair.getLeft();
final ClassProvider classProvider = resourceProviderClassProviderPair.getRight();
final ParsingContext parsingContext = new ParsingContext();
resourceNameValidator.validateSQLMigrationNaming(resourceProvider, configuration);
JdbcConnectionFactory jdbcConnectionFactory = new JdbcConnectionFactory(configuration.getDataSource(), configuration, statementInterceptor);
final DatabaseType databaseType = jdbcConnectionFactory.getDatabaseType();
final SqlScriptFactory sqlScriptFactory = databaseType.createSqlScriptFactory(configuration, parsingContext);
RetryStrategy.setNumberOfRetries(configuration.getLockRetryCount());
final SqlScriptExecutorFactory noCallbackSqlScriptExecutorFactory = databaseType.createSqlScriptExecutorFactory(
jdbcConnectionFactory, NoopCallbackExecutor.INSTANCE, null);
jdbcConnectionFactory.setConnectionInitializer((jdbcConnectionFactory1, connection) -> {
if (configuration.getInitSql() == null) {
return;
}
StringResource resource = new StringResource(configuration.getInitSql());
SqlScript sqlScript = sqlScriptFactory.createSqlScript(resource, true, resourceProvider);
boolean outputQueryResults = configuration.isOutputQueryResults();
noCallbackSqlScriptExecutorFactory.createSqlScriptExecutor(connection, false, false, outputQueryResults).execute(sqlScript, configuration);
});
Database database = null;
try {
database = databaseType.createDatabase(configuration, jdbcConnectionFactory, statementInterceptor);
if (!dbConnectionInfoPrinted) {
dbConnectionInfoPrinted = true;
if (database.getDatabaseType() instanceof CommunityDatabaseType) {
LOG.info(((CommunityDatabaseType) database.getDatabaseType()).announcementForCommunitySupport());
}
LOG.info("Database: " + redactJdbcUrl(jdbcConnectionFactory.getJdbcUrl()) + " (" + jdbcConnectionFactory.getProductName() + ")");
LOG.debug("Driver: " + jdbcConnectionFactory.getDriverInfo());
if (flywayTelemetryManager != null) {
RootTelemetryModel rootTelemetryModel = flywayTelemetryManager.getRootTelemetryModel();
if (rootTelemetryModel != null) {
rootTelemetryModel.setDatabaseEngine(database.getDatabaseType().getName());
rootTelemetryModel.setDatabaseVersion(database.getVersion().toString());
}
}
}
LOG.debug("DDL Transactions Supported: " + database.supportsDdlTransactions());
Pair> schemas = SchemaHistoryFactory.prepareSchemas(configuration, database);
Schema defaultSchema = schemas.getLeft();
if (statementInterceptor != null) {
statementInterceptor.init(configuration, database, defaultSchema.getTable(configuration.getTable()));
}
parsingContext.populate(database, configuration);
database.ensureSupported(configuration);
DefaultCallbackExecutor callbackExecutor = new DefaultCallbackExecutor(configuration, database, defaultSchema, prepareCallbacks(
database, resourceProvider, jdbcConnectionFactory, sqlScriptFactory, statementInterceptor, defaultSchema, parsingContext));
SqlScriptExecutorFactory sqlScriptExecutorFactory = databaseType.createSqlScriptExecutorFactory(jdbcConnectionFactory, callbackExecutor, statementInterceptor);
SchemaHistory schemaHistory = SchemaHistoryFactory.getSchemaHistory(
configuration,
noCallbackSqlScriptExecutorFactory,
sqlScriptFactory,
database,
defaultSchema,
statementInterceptor);
result = command.execute(
createMigrationResolver(resourceProvider, classProvider, sqlScriptExecutorFactory, sqlScriptFactory, parsingContext, statementInterceptor),
schemaHistory,
database,
defaultSchema,
schemas.getRight().toArray(new Schema[0]),
callbackExecutor,
statementInterceptor);
} finally {
IOUtils.close(database);
if (statementInterceptor instanceof AutoCloseable) {
IOUtils.close((AutoCloseable) statementInterceptor);
}
showMemoryUsage();
}
File permit_file = new File(FileUtils.getAppDataFlywayCLILocation(), "permit");
if (LicenseGuard.getTier(configuration) == Tier.COMMUNITY && !permit_file.exists()) {
LOG.info("");
LOG.info("You are not signed in to Flyway, to sign in please run auth");
}
return result;
}
private Pair> createResourceAndClassProviders(boolean scannerRequired) {
ResourceProvider resourceProvider;
ClassProvider classProvider;
if (!scannerRequired && configuration.isSkipDefaultResolvers() && configuration.isSkipDefaultCallbacks()) {
resourceProvider = NoopResourceProvider.INSTANCE;
//noinspection unchecked
classProvider = NoopClassProvider.INSTANCE;
} else {
if (configuration.getResourceProvider() != null && configuration.getJavaMigrationClassProvider() != null) {
// don't create the scanner at all in this case
resourceProvider = configuration.getResourceProvider();
classProvider = configuration.getJavaMigrationClassProvider();
} else {
boolean stream = false;
Scanner scanner = new Scanner<>(
JavaMigration.class,
stream,
resourceNameCache,
locationScannerCache,
configuration);
// set the defaults
resourceProvider = scanner;
classProvider = scanner;
if (configuration.getResourceProvider() != null) {
resourceProvider = configuration.getResourceProvider();
}
if (configuration.getJavaMigrationClassProvider() != null) {
classProvider = configuration.getJavaMigrationClassProvider();
}
}
}
return Pair.of(resourceProvider, classProvider);
}
private List prepareCallbacks(Database database, ResourceProvider resourceProvider,
JdbcConnectionFactory jdbcConnectionFactory,
SqlScriptFactory sqlScriptFactory, StatementInterceptor statementInterceptor,
Schema schema, ParsingContext parsingContext) {
List effectiveCallbacks = new ArrayList<>();
CallbackExecutor callbackExecutor = NoopCallbackExecutor.INSTANCE;
if (statementInterceptor != null) {
effectiveCallbacks.addAll(statementInterceptor.getCallbacks());
}
effectiveCallbacks.addAll(Arrays.asList(configuration.getCallbacks()));
if (!configuration.isSkipDefaultCallbacks()) {
SqlScriptExecutorFactory sqlScriptExecutorFactory = jdbcConnectionFactory.getDatabaseType().createSqlScriptExecutorFactory(
jdbcConnectionFactory, callbackExecutor, statementInterceptor);
effectiveCallbacks.addAll(new SqlScriptCallbackFactory(resourceProvider, sqlScriptExecutorFactory, sqlScriptFactory, configuration).getCallbacks());
}
return effectiveCallbacks;
}
private CompositeMigrationResolver createMigrationResolver(ResourceProvider resourceProvider,
ClassProvider classProvider,
SqlScriptExecutorFactory sqlScriptExecutorFactory,
SqlScriptFactory sqlScriptFactory,
ParsingContext parsingContext,
StatementInterceptor statementInterceptor) {
return new CompositeMigrationResolver(resourceProvider, classProvider, configuration, sqlScriptExecutorFactory, sqlScriptFactory, parsingContext, statementInterceptor, configuration.getResolvers());
}
private void showMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
long free = runtime.freeMemory();
long total = runtime.totalMemory();
long used = total - free;
long totalMB = MEGABYTE.fromBytes(total);
long usedMB = MEGABYTE.fromBytes(used);
LOG.debug("Memory usage: " + usedMB + " of " + totalMB + "M");
}
}