diff --git a/liquibase-cdi/src/main/java/liquibase/integration/cdi/CDILiquibase.java b/liquibase-cdi/src/main/java/liquibase/integration/cdi/CDILiquibase.java
index 6daaaed69d7..5ed40bd091f 100644
--- a/liquibase-cdi/src/main/java/liquibase/integration/cdi/CDILiquibase.java
+++ b/liquibase-cdi/src/main/java/liquibase/integration/cdi/CDILiquibase.java
@@ -4,8 +4,7 @@
import liquibase.LabelExpression;
import liquibase.Liquibase;
import liquibase.Scope;
-import liquibase.configuration.GlobalConfiguration;
-import liquibase.configuration.LiquibaseConfiguration;
+import liquibase.GlobalConfiguration;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
@@ -107,12 +106,10 @@ public void onStartup() {
return;
}
- LiquibaseConfiguration liquibaseConfiguration = LiquibaseConfiguration.getInstance();
- if (!liquibaseConfiguration.getConfiguration(GlobalConfiguration.class).getShouldRun()) {
+ if (!GlobalConfiguration.SHOULD_RUN.getCurrentValue()) {
log.info(String.format("Liquibase did not run on %s because %s was set to false.",
hostName,
- liquibaseConfiguration.describeValueLookupLogic(
- GlobalConfiguration.class, GlobalConfiguration.SHOULD_RUN)
+ GlobalConfiguration.SHOULD_RUN.getKey()
));
return;
}
diff --git a/liquibase-cdi/src/test/java/liquibase/integration/cdi/CDILiquibaseTest.java b/liquibase-cdi/src/test/java/liquibase/integration/cdi/CDILiquibaseTest.java
index e6e8a7e5b48..c88d9d1bcf7 100644
--- a/liquibase-cdi/src/test/java/liquibase/integration/cdi/CDILiquibaseTest.java
+++ b/liquibase-cdi/src/test/java/liquibase/integration/cdi/CDILiquibaseTest.java
@@ -21,7 +21,6 @@ public class CDILiquibaseTest {
public void clearProperty() {
System.clearProperty("liquibase.shouldRun");
System.clearProperty("liquibase.config.shouldRun");
- LiquibaseConfiguration.getInstance().reset();
}
private void validateRunningState(boolean shouldBeRunning) {
diff --git a/liquibase-cli/pom.xml b/liquibase-cli/pom.xml
new file mode 100644
index 00000000000..d9d19909b4f
--- /dev/null
+++ b/liquibase-cli/pom.xml
@@ -0,0 +1,20 @@
+
+ 4.0.0
+
+
+ org.liquibase
+ liquibase-base-module
+ ${liquibase.version}
+ ../base-module.pom.xml
+
+
+ Liquibase CLI interface
+
+ liquibase-cli
+
+
+
+
+
+
diff --git a/liquibase-cli/src/main/java/liquibase/integration/commandline/NewMain.java b/liquibase-cli/src/main/java/liquibase/integration/commandline/NewMain.java
new file mode 100644
index 00000000000..4fd136cab30
--- /dev/null
+++ b/liquibase-cli/src/main/java/liquibase/integration/commandline/NewMain.java
@@ -0,0 +1,18 @@
+package liquibase.integration.commandline;
+
+import liquibase.Scope;
+import liquibase.configuration.ConfigurationDefinition;
+import liquibase.configuration.LiquibaseConfiguration;
+
+import java.util.SortedSet;
+
+public class NewMain {
+ public static void main(String[] args) {
+ System.out.println("New CLI!");
+
+ final SortedSet definitions = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class).getRegisteredDefinitions();
+ for (ConfigurationDefinition def : definitions) {
+ System.out.println("See " + def.getKey() + " = " + def.getCurrentValue() + " -- " + def.getDescription());
+ }
+ }
+}
diff --git a/liquibase-core/src/main/java/liquibase/GlobalConfiguration.java b/liquibase-core/src/main/java/liquibase/GlobalConfiguration.java
new file mode 100644
index 00000000000..efd547b8fcb
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/GlobalConfiguration.java
@@ -0,0 +1,145 @@
+package liquibase;
+
+import liquibase.configuration.ConfigurationDefinition;
+import liquibase.configuration.AutoloadedConfigurations;
+
+/**
+ * Configuration container for global properties.
+ */
+public class GlobalConfiguration implements AutoloadedConfigurations {
+
+ public static final ConfigurationDefinition SHOULD_RUN;
+ public static final ConfigurationDefinition DATABASECHANGELOG_TABLE_NAME;
+ public static final ConfigurationDefinition DATABASECHANGELOGLOCK_TABLE_NAME;
+ public static final ConfigurationDefinition LIQUIBASE_TABLESPACE_NAME;
+ public static final ConfigurationDefinition LIQUIBASE_CATALOG_NAME;
+ public static final ConfigurationDefinition LIQUIBASE_SCHEMA_NAME;
+ public static final ConfigurationDefinition OUTPUT_LINE_SEPARATOR;
+ public static final ConfigurationDefinition OUTPUT_ENCODING;
+ public static final ConfigurationDefinition CHANGELOGLOCK_WAIT_TIME;
+ public static final ConfigurationDefinition CHANGELOGLOCK_POLL_RATE;
+ public static final ConfigurationDefinition CONVERT_DATA_TYPES;
+ public static final ConfigurationDefinition GENERATE_CHANGESET_CREATED_VALUES;
+ public static final ConfigurationDefinition AUTO_REORG;
+ public static final ConfigurationDefinition DIFF_COLUMN_ORDER;
+ public static final ConfigurationDefinition ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA;
+ public static final ConfigurationDefinition GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION;
+ public static final ConfigurationDefinition INCLUDE_CATALOG_IN_SPECIFICATION;
+ public static final ConfigurationDefinition SHOULD_SNAPSHOT_DATA;
+ public static final ConfigurationDefinition FILTER_LOG_MESSAGES;
+ public static final ConfigurationDefinition HEADLESS;
+
+ static {
+ ConfigurationDefinition.Builder builder = new ConfigurationDefinition.Builder("liquibase");
+
+ SHOULD_RUN = builder.define("shouldRun", Boolean.class)
+ .setDescription("Should Liquibase commands execute")
+ .setDefaultValue(true)
+ .addAliasKey("should.run")
+ .build();
+
+ DATABASECHANGELOG_TABLE_NAME = builder.define("databaseChangelogTableName", String.class)
+ .addAliasKey("liquibase.databaseChangeLogTableName")
+ .setDescription("Name of table to use for tracking change history")
+ .setDefaultValue("DATABASECHANGELOG")
+ .build();
+
+ DATABASECHANGELOGLOCK_TABLE_NAME = builder.define("databaseChangelogLockTableName", String.class)
+ .addAliasKey("liquibase.databaseChangeLogLockTableName")
+ .setDescription("Name of table to use for tracking concurrent Liquibase usage")
+ .setDefaultValue("DATABASECHANGELOGLOCK")
+ .build();
+
+ CHANGELOGLOCK_WAIT_TIME = builder.define("changelogLockWaitTimeInMinutes", Long.class)
+ .addAliasKey("liquibase.changeLogLockWaitTimeInMinutes")
+ .setDescription("Number of minutes to wait for the changelog lock to be available before giving up")
+ .setDefaultValue(5L)
+ .build();
+
+ CHANGELOGLOCK_POLL_RATE = builder.define("changelogLockPollRate", Long.class)
+ .addAliasKey("liquibase.changeLogLockPollRate")
+ .setDescription("Number of seconds wait between checks to the changelog lock when it is locked")
+ .setDefaultValue(10L)
+ .build();
+
+ LIQUIBASE_TABLESPACE_NAME = builder.define("tablespaceName", String.class)
+ .setDescription("Tablespace to use for Liquibase objects")
+ .build();
+
+ LIQUIBASE_CATALOG_NAME = builder.define("catalogName", String.class)
+ .setDescription("Catalog to use for Liquibase objects")
+ .build();
+
+ LIQUIBASE_SCHEMA_NAME = builder.define("schemaName", String.class)
+ .setDescription("Schema to use for Liquibase objects")
+ .build();
+
+ OUTPUT_LINE_SEPARATOR = builder.define("outputLineSeparator", String.class)
+ .setDescription("Line separator for output. Defaults to OS default")
+ .setDefaultValue(System.getProperty("line.separator"))
+ .build();
+
+ OUTPUT_ENCODING = builder.define("outputFileEncoding", String.class)
+ .setDescription("Encoding to output text in. Defaults to file.encoding system property or UTF-8")
+ .setDefaultValue("UTF-8")
+ .addAliasKey("file.encoding")
+ .setCommonlyUsed(true)
+ .build();
+
+ CONVERT_DATA_TYPES = builder.define("convertDataTypes", Boolean.class)
+ .setDescription("Should Liquibase convert to/from STANDARD data types. Applies to both snapshot and " +
+ "update commands.")
+ .setDefaultValue(true)
+ .build();
+
+ GENERATE_CHANGESET_CREATED_VALUES = builder.define("generateChangesetCreatedValues", Boolean.class)
+ .addAliasKey("liquibase.generateChangeSetCreatedValues")
+ .setDescription("Should Liquibase include a 'created' attribute in diff/generateChangelog changesets with" +
+ " the current datetime")
+ .setDefaultValue(false)
+ .build();
+
+ AUTO_REORG = builder.define("autoReorg", Boolean.class)
+ .setDescription("Should Liquibase automatically include REORG TABLE commands when needed?")
+ .setDefaultValue(true)
+ .build();
+
+ DIFF_COLUMN_ORDER = builder.define("diffColumnOrder", Boolean.class)
+ .setDescription("Should Liquibase compare column order in diff operation?")
+ .setDefaultValue(true)
+ .build();
+
+ ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA = builder.define("alwaysOverrideStoredLogicSchema", Boolean.class)
+ .setDescription("When generating SQL for createProcedure, should the procedure schema be forced to the default schema if no schemaName attribute is set?")
+ .setDefaultValue(false)
+ .build();
+
+
+ GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION = builder.define("generatedChangesetIdsContainsDescription", Boolean.class)
+ .addAliasKey("liquibase.generatedChangeSetIdsContainsDescription")
+ .setDescription("Should Liquibase include the change description in the id when generating changesets?")
+ .setDefaultValue(false)
+ .build();
+
+ INCLUDE_CATALOG_IN_SPECIFICATION = builder.define("includeCatalogInSpecification", Boolean.class)
+ .setDescription("Should Liquibase include the catalog name when determining equality?")
+ .setDefaultValue(false)
+ .build();
+
+ SHOULD_SNAPSHOT_DATA = builder.define("shouldSnapshotData", Boolean.class)
+ .setDescription("Should Liquibase snapshot data by default?")
+ .setDefaultValue(false)
+ .build();
+
+ FILTER_LOG_MESSAGES = builder.define("filterLogMessages", Boolean.class)
+ .setDescription("Should Liquibase filter log messages for potentially insecure data?")
+ .setDefaultValue(true)
+ .build();
+
+ HEADLESS = builder.define("headless", Boolean.class)
+ .setDescription("Force liquibase think it has no access to a keyboard?")
+ .setDefaultValue(false)
+ .setCommonlyUsed(true)
+ .build();
+ }
+}
diff --git a/liquibase-core/src/main/java/liquibase/Liquibase.java b/liquibase-core/src/main/java/liquibase/Liquibase.java
index a55f0f53455..c258271fb8d 100644
--- a/liquibase-core/src/main/java/liquibase/Liquibase.java
+++ b/liquibase-core/src/main/java/liquibase/Liquibase.java
@@ -8,7 +8,6 @@
import liquibase.command.CommandExecutionException;
import liquibase.command.CommandFactory;
import liquibase.command.core.DropAllCommand;
-import liquibase.configuration.HubConfiguration;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
@@ -26,10 +25,7 @@
import liquibase.executor.Executor;
import liquibase.executor.ExecutorService;
import liquibase.executor.LoggingExecutor;
-import liquibase.hub.HubService;
-import liquibase.hub.HubServiceFactory;
-import liquibase.hub.HubUpdater;
-import liquibase.hub.LiquibaseHubException;
+import liquibase.hub.*;
import liquibase.hub.listener.HubChangeExecListener;
import liquibase.hub.model.Connection;
import liquibase.hub.model.HubChangeLog;
@@ -311,13 +307,12 @@ public Connection getConnection(DatabaseChangeLog changeLog) throws LiquibaseHub
if (executor instanceof LoggingExecutor) {
return null;
}
- HubConfiguration hubConfiguration = LiquibaseConfiguration.getInstance().getConfiguration(HubConfiguration.class);
String changeLogId = changeLog.getChangeLogId();
HubUpdater hubUpdater = new HubUpdater(new Date(), changeLog, database);
if (hubUpdater.hubIsNotAvailable(changeLogId)) {
- if (StringUtil.isNotEmpty(hubConfiguration.getLiquibaseHubApiKey()) && changeLogId == null) {
+ if (StringUtil.isNotEmpty(HubConfiguration.LIQUIBASE_HUB_API_KEY.getCurrentValue()) && changeLogId == null) {
String message =
- "The API key '" + hubConfiguration.getLiquibaseHubApiKey() + "' was found, but no changelog ID exists.\n" +
+ "The API key '" + HubConfiguration.LIQUIBASE_HUB_API_KEY.getCurrentValue() + "' was found, but no changelog ID exists.\n" +
"No operations will be reported. Register this changelog with Liquibase Hub to generate free deployment reports.\n" +
"Learn more at https://hub.liquibase.com.";
Scope.getCurrentScope().getUI().sendMessage("WARNING: " + message);
@@ -329,7 +324,7 @@ public Connection getConnection(DatabaseChangeLog changeLog) throws LiquibaseHub
//
// Warn about the situation where there is a changeLog ID, but no API key
//
- if (StringUtil.isEmpty(hubConfiguration.getLiquibaseHubApiKey()) && changeLogId != null) {
+ if (StringUtil.isEmpty(HubConfiguration.LIQUIBASE_HUB_API_KEY.getCurrentValue()) && changeLogId != null) {
String message = "The changelog ID '" + changeLogId + "' was found, but no API Key exists.\n" +
"No operations will be reported. Simply add a liquibase.hub.apiKey setting to generate free deployment reports.\n" +
"Learn more at https://hub.liquibase.com.";
@@ -1783,15 +1778,16 @@ public final void dropAll(CatalogAndSchema... schemas) throws DatabaseException
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
+ final CommandFactory commandFactory = Scope.getCurrentScope().getSingleton(CommandFactory.class);
- DropAllCommand dropAll = (DropAllCommand) CommandFactory.getInstance().getCommand("dropAll");
+ DropAllCommand dropAll = (DropAllCommand) commandFactory.getCommand("dropAll");
dropAll.setDatabase(Liquibase.this.getDatabase());
dropAll.setSchemas(finalSchemas);
dropAll.setLiquibase(Liquibase.this);
dropAll.setChangeLogFile(changeLogFile);
try {
- dropAll.execute();
+ commandFactory.execute(dropAll);
} catch (CommandExecutionException e) {
throw new DatabaseException(e);
}
diff --git a/liquibase-core/src/main/java/liquibase/Scope.java b/liquibase-core/src/main/java/liquibase/Scope.java
index 1190605bc06..7439869d667 100644
--- a/liquibase-core/src/main/java/liquibase/Scope.java
+++ b/liquibase-core/src/main/java/liquibase/Scope.java
@@ -1,5 +1,6 @@
package liquibase;
+import liquibase.configuration.LiquibaseConfiguration;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.OfflineConnection;
@@ -66,6 +67,13 @@ public static Scope getCurrentScope() {
Scope rootScope = new Scope();
scopeManager.setCurrentScope(rootScope);
+ rootScope.values.put(Attr.logService.name(), new JavaLogService());
+ rootScope.values.put(Attr.resourceAccessor.name(), new ClassLoaderResourceAccessor());
+ rootScope.values.put(Attr.serviceLocator.name(), new StandardServiceLocator());
+
+ rootScope.values.put(Attr.ui.name(), new ConsoleUIService());
+ rootScope.getSingleton(LiquibaseConfiguration.class).init(rootScope);
+
LogService overrideLogService = rootScope.getSingleton(LogServiceFactory.class).getDefaultLogService();
if (overrideLogService == null) {
throw new UnexpectedLiquibaseException("Cannot find default log service");
@@ -109,10 +117,6 @@ public static void setScopeManager(ScopeManager scopeManager) {
* Defaults serviceLocator to {@link StandardServiceLocator}
*/
private Scope() {
- values.put(Attr.logService.name(), new JavaLogService());
- values.put(Attr.resourceAccessor.name(), new ClassLoaderResourceAccessor());
- values.put(Attr.serviceLocator.name(), new StandardServiceLocator());
- values.put(Attr.ui.name(), new ConsoleUIService());
}
protected Scope(Scope parent, Map scopeValues) {
diff --git a/liquibase-core/src/main/java/liquibase/ScopeManager.java b/liquibase-core/src/main/java/liquibase/ScopeManager.java
index af9e7b87252..c497ca8969c 100644
--- a/liquibase-core/src/main/java/liquibase/ScopeManager.java
+++ b/liquibase-core/src/main/java/liquibase/ScopeManager.java
@@ -2,7 +2,6 @@
public abstract class ScopeManager {
-
public abstract Scope getCurrentScope();
protected abstract void setCurrentScope(Scope scope);
diff --git a/liquibase-core/src/main/java/liquibase/change/AbstractSQLChange.java b/liquibase-core/src/main/java/liquibase/change/AbstractSQLChange.java
index 82c63b2d200..9cf405f8b72 100644
--- a/liquibase-core/src/main/java/liquibase/change/AbstractSQLChange.java
+++ b/liquibase-core/src/main/java/liquibase/change/AbstractSQLChange.java
@@ -2,8 +2,7 @@
import liquibase.change.core.RawSQLChange;
import liquibase.Scope;
-import liquibase.configuration.GlobalConfiguration;
-import liquibase.configuration.LiquibaseConfiguration;
+import liquibase.GlobalConfiguration;
import liquibase.database.Database;
import liquibase.database.core.MSSQLDatabase;
import liquibase.database.core.PostgresDatabase;
@@ -194,11 +193,7 @@ public CheckSum generateCheckSum() {
}
if (sql != null) {
- stream = new ByteArrayInputStream(
- sql.getBytes(
- LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class)
- .getOutputEncoding()
- )
+ stream = new ByteArrayInputStream(sql.getBytes(GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue())
);
}
@@ -295,7 +290,7 @@ public static class NormalizingStream extends InputStream {
public NormalizingStream(String endDelimiter, Boolean splitStatements, Boolean stripComments, InputStream stream) {
this.stream = new PushbackInputStream(stream, 2048);
try {
- this.headerStream = new ByteArrayInputStream((endDelimiter+":"+splitStatements+":"+stripComments+":").getBytes(LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding()));
+ this.headerStream = new ByteArrayInputStream((endDelimiter+":"+splitStatements+":"+stripComments+":").getBytes(GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue()));
} catch (UnsupportedEncodingException e) {
throw new UnexpectedLiquibaseException(e);
}
diff --git a/liquibase-core/src/main/java/liquibase/change/core/CreateProcedureChange.java b/liquibase-core/src/main/java/liquibase/change/core/CreateProcedureChange.java
index 0dec8574a2a..5e99f161830 100644
--- a/liquibase-core/src/main/java/liquibase/change/core/CreateProcedureChange.java
+++ b/liquibase-core/src/main/java/liquibase/change/core/CreateProcedureChange.java
@@ -3,9 +3,7 @@
import liquibase.Scope;
import liquibase.change.*;
import liquibase.changelog.ChangeLogParameters;
-import liquibase.changelog.ChangeSet;
-import liquibase.configuration.GlobalConfiguration;
-import liquibase.configuration.LiquibaseConfiguration;
+import liquibase.GlobalConfiguration;
import liquibase.database.Database;
import liquibase.database.DatabaseList;
import liquibase.database.core.*;
@@ -251,8 +249,7 @@ public CheckSum generateCheckSum() {
procedureText = "";
}
- String encoding =
- LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding();
+ String encoding = GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue();
if (procedureText != null) {
try {
stream = new ByteArrayInputStream(procedureText.getBytes(encoding));
diff --git a/liquibase-core/src/main/java/liquibase/change/core/CreateViewChange.java b/liquibase-core/src/main/java/liquibase/change/core/CreateViewChange.java
index 907ec808e7d..3f9309962d1 100644
--- a/liquibase-core/src/main/java/liquibase/change/core/CreateViewChange.java
+++ b/liquibase-core/src/main/java/liquibase/change/core/CreateViewChange.java
@@ -3,8 +3,7 @@
import liquibase.Scope;
import liquibase.change.*;
import liquibase.changelog.ChangeLogParameters;
-import liquibase.configuration.GlobalConfiguration;
-import liquibase.configuration.LiquibaseConfiguration;
+import liquibase.GlobalConfiguration;
import liquibase.database.Database;
import liquibase.database.core.OracleDatabase;
import liquibase.database.core.SQLiteDatabase;
@@ -199,7 +198,7 @@ public CheckSum generateCheckSum() {
selectQuery = "";
}
- String encoding = LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding();
+ String encoding = GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue();
if (selectQuery != null) {
try {
stream = new ByteArrayInputStream(selectQuery.getBytes(encoding));
diff --git a/liquibase-core/src/main/java/liquibase/change/core/ExecuteShellCommandChange.java b/liquibase-core/src/main/java/liquibase/change/core/ExecuteShellCommandChange.java
index 4c78cb6f7b2..6b09985df8a 100644
--- a/liquibase-core/src/main/java/liquibase/change/core/ExecuteShellCommandChange.java
+++ b/liquibase-core/src/main/java/liquibase/change/core/ExecuteShellCommandChange.java
@@ -5,8 +5,7 @@
import liquibase.change.ChangeMetaData;
import liquibase.change.DatabaseChange;
import liquibase.change.DatabaseChangeProperty;
-import liquibase.configuration.GlobalConfiguration;
-import liquibase.configuration.LiquibaseConfiguration;
+import liquibase.GlobalConfiguration;
import liquibase.database.Database;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.exception.ValidationErrors;
@@ -25,7 +24,6 @@
import java.io.*;
import java.util.*;
-import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
@@ -216,10 +214,8 @@ protected void executeCommand(Database database) throws Exception {
Thread.currentThread().interrupt();
}
- String errorStreamOut = errorStream.toString(LiquibaseConfiguration.getInstance().getConfiguration
- (GlobalConfiguration.class).getOutputEncoding());
- String infoStreamOut = inputStream.toString(LiquibaseConfiguration.getInstance().getConfiguration
- (GlobalConfiguration.class).getOutputEncoding());
+ String errorStreamOut = errorStream.toString(GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue());
+ String infoStreamOut = inputStream.toString(GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue());
if (errorStreamOut != null && !errorStreamOut.isEmpty()) {
Scope.getCurrentScope().getLog(getClass()).severe(errorStreamOut);
diff --git a/liquibase-core/src/main/java/liquibase/changelog/ChangeLogParameters.java b/liquibase-core/src/main/java/liquibase/changelog/ChangeLogParameters.java
index 43923be15fa..32b18c14b5c 100644
--- a/liquibase-core/src/main/java/liquibase/changelog/ChangeLogParameters.java
+++ b/liquibase-core/src/main/java/liquibase/changelog/ChangeLogParameters.java
@@ -8,7 +8,7 @@
import liquibase.database.Database;
import liquibase.database.DatabaseList;
import liquibase.exception.DatabaseException;
-import liquibase.parser.ChangeLogParserCofiguration;
+import liquibase.parser.ChangeLogParserConfiguration;
import liquibase.util.StringUtil;
import java.util.*;
@@ -210,8 +210,7 @@ protected static class ExpressionExpander {
public ExpressionExpander(ChangeLogParameters changeLogParameters) {
this.changeLogParameters = changeLogParameters;
- this.enableEscaping = LiquibaseConfiguration.getInstance()
- .getConfiguration(ChangeLogParserCofiguration.class).getSupportPropertyEscaping();
+ this.enableEscaping = ChangeLogParserConfiguration.SUPPORT_PROPERTY_ESCAPING.getCurrentValue();
}
public String expandExpressions(String text, DatabaseChangeLog changeLog) {
diff --git a/liquibase-core/src/main/java/liquibase/changelog/ChangelogRewriter.java b/liquibase-core/src/main/java/liquibase/changelog/ChangelogRewriter.java
index ef5c356f154..fb98253fe40 100644
--- a/liquibase-core/src/main/java/liquibase/changelog/ChangelogRewriter.java
+++ b/liquibase-core/src/main/java/liquibase/changelog/ChangelogRewriter.java
@@ -1,7 +1,7 @@
package liquibase.changelog;
import liquibase.Scope;
-import liquibase.configuration.GlobalConfiguration;
+import liquibase.GlobalConfiguration;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.parser.core.xml.XMLChangeLogSAXParser;
import liquibase.resource.InputStreamList;
@@ -38,7 +38,7 @@ public static ChangeLogRewriterResult removeChangeLogId(String changeLogFile, St
list = resourceAccessor.openStreams("", changeLogFile);
List uris = list.getURIs();
InputStream is = list.iterator().next();
- String encoding = LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding();
+ String encoding = GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue();
String changeLogString = StreamUtil.readStreamAsString(is, encoding);
if (changeLogFile.toLowerCase().endsWith(".xml")) {
//
@@ -133,7 +133,7 @@ public static ChangeLogRewriterResult addChangeLogId(String changeLogFile, Strin
list = resourceAccessor.openStreams("", changeLogFile);
List uris = list.getURIs();
InputStream is = list.iterator().next();
- String encoding = LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding();
+ String encoding = GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue();
String changeLogString = StreamUtil.readStreamAsString(is, encoding);
if (changeLogFile.toLowerCase().endsWith(".xml")) {
String patternString = "(?ms).*]*>";
@@ -229,4 +229,4 @@ public ChangeLogRewriterResult(String message, boolean success) {
public String message;
public boolean success;
}
-}
\ No newline at end of file
+}
diff --git a/liquibase-core/src/main/java/liquibase/changelog/OfflineChangeLogHistoryService.java b/liquibase-core/src/main/java/liquibase/changelog/OfflineChangeLogHistoryService.java
index a895d5bc110..719098d51e7 100644
--- a/liquibase-core/src/main/java/liquibase/changelog/OfflineChangeLogHistoryService.java
+++ b/liquibase-core/src/main/java/liquibase/changelog/OfflineChangeLogHistoryService.java
@@ -5,8 +5,7 @@
import liquibase.Scope;
import liquibase.change.CheckSum;
import liquibase.changelog.ChangeSet.ExecType;
-import liquibase.configuration.GlobalConfiguration;
-import liquibase.configuration.LiquibaseConfiguration;
+import liquibase.GlobalConfiguration;
import liquibase.database.Database;
import liquibase.database.OfflineConnection;
import liquibase.exception.DatabaseException;
@@ -18,7 +17,6 @@
import liquibase.statement.core.MarkChangeSetRanStatement;
import liquibase.statement.core.RemoveChangeSetRanStatusStatement;
import liquibase.statement.core.UpdateChangeSetChecksumStatement;
-import liquibase.structure.core.Column;
import liquibase.util.ISODateFormat;
import liquibase.util.LiquibaseUtil;
import liquibase.util.csv.CSVReader;
@@ -26,7 +24,6 @@
import java.io.*;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Date;
import java.util.List;
@@ -122,7 +119,7 @@ public void init() throws DatabaseException {
protected void writeHeader(File file) throws IOException {
try (FileOutputStream outputStream = new FileOutputStream(file);
Writer writer = new OutputStreamWriter(outputStream,
- LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding())
+ GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue())
) {
CSVWriter csvWriter = new CSVWriter(writer);
String[] columns = new String[Columns.values().length];
@@ -151,7 +148,7 @@ public String[] execute(String[] line) {
@Override
public List getRanChangeSets() throws DatabaseException {
try (
- Reader reader = new InputStreamReader(new FileInputStream(this.changeLogFile), LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding());
+ Reader reader = new InputStreamReader(new FileInputStream(this.changeLogFile), GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue());
)
{
CSVReader csvReader = new CSVReader(reader);
@@ -207,8 +204,8 @@ protected void replaceChangeSet(ChangeSet changeSet, ReplaceChangeSetLogic repla
File newFile = new File(oldFile.getParentFile(), oldFile.getName()+".new");
try (
- Reader reader = new InputStreamReader(new FileInputStream(oldFile), LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding());
- Writer writer = new OutputStreamWriter(new FileOutputStream(newFile), LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding());
+ Reader reader = new InputStreamReader(new FileInputStream(oldFile), GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue());
+ Writer writer = new OutputStreamWriter(new FileOutputStream(newFile), GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue());
CSVReader csvReader = new CSVReader(reader);
CSVWriter csvWriter = new CSVWriter(writer);
)
@@ -235,8 +232,8 @@ protected void appendChangeSet(ChangeSet changeSet, ChangeSet.ExecType execType)
File newFile = new File(oldFile.getParentFile(), oldFile.getName()+".new");
try (
- Reader reader = new InputStreamReader(new FileInputStream(oldFile), LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding());
- Writer writer = new OutputStreamWriter(new FileOutputStream(newFile), LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding());
+ Reader reader = new InputStreamReader(new FileInputStream(oldFile), GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue());
+ Writer writer = new OutputStreamWriter(new FileOutputStream(newFile), GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue());
CSVReader csvReader = new CSVReader(reader);
CSVWriter csvWriter = new CSVWriter(writer);
)
@@ -319,7 +316,7 @@ public int getNextSequenceValue() throws LiquibaseException {
lastChangeSetSequenceValue = 0;
try (
- Reader reader = new InputStreamReader(new FileInputStream(this.changeLogFile), LiquibaseConfiguration.getInstance().getConfiguration(GlobalConfiguration.class).getOutputEncoding());
+ Reader reader = new InputStreamReader(new FileInputStream(this.changeLogFile), GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue());
)
{
diff --git a/liquibase-core/src/main/java/liquibase/command/AbstractCommand.java b/liquibase-core/src/main/java/liquibase/command/AbstractCommand.java
index a5c78f31e0e..2be7820f055 100644
--- a/liquibase-core/src/main/java/liquibase/command/AbstractCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/AbstractCommand.java
@@ -1,30 +1,21 @@
package liquibase.command;
-import liquibase.servicelocator.PrioritizedService;
+import java.util.SortedSet;
+import java.util.TreeSet;
public abstract class AbstractCommand implements LiquibaseCommand {
@Override
public int getPriority(String commandName) {
if ((commandName != null) && commandName.equalsIgnoreCase(getName())) {
- return PrioritizedService.PRIORITY_DEFAULT;
+ return PRIORITY_DEFAULT;
} else {
- return -1;
+ return PRIORITY_NOT_APPLICABLE;
}
}
- public final T execute() throws CommandExecutionException {
- this.validate();
- try {
- return this.run();
- } catch (Exception e) {
- if (e instanceof CommandExecutionException) {
- throw (CommandExecutionException) e;
- } else {
- throw new CommandExecutionException(e);
- }
- }
+ @Override
+ public SortedSet getArguments() {
+ return new TreeSet<>();
}
-
- protected abstract T run() throws Exception;
}
diff --git a/liquibase-core/src/main/java/liquibase/command/CommandArgument.java b/liquibase-core/src/main/java/liquibase/command/CommandArgument.java
new file mode 100644
index 00000000000..ac0b7760644
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/command/CommandArgument.java
@@ -0,0 +1,45 @@
+package liquibase.command;
+
+import java.util.Objects;
+
+public class CommandArgument implements Comparable {
+
+ private String name;
+ private String description;
+ private Class dataType;
+ private boolean required;
+
+ public String getName() {
+ return name;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public Class getDataType() {
+ return dataType;
+ }
+
+ public boolean isRequired() {
+ return required;
+ }
+
+ @Override
+ public int compareTo(Object o) {
+ return this.getName().compareTo(((CommandArgument) o).getName());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CommandArgument that = (CommandArgument) o;
+ return Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name);
+ }
+}
diff --git a/liquibase-core/src/main/java/liquibase/command/CommandFactory.java b/liquibase-core/src/main/java/liquibase/command/CommandFactory.java
index af9a7937901..03e2d911548 100644
--- a/liquibase-core/src/main/java/liquibase/command/CommandFactory.java
+++ b/liquibase-core/src/main/java/liquibase/command/CommandFactory.java
@@ -1,79 +1,42 @@
package liquibase.command;
-import liquibase.Scope;
-import liquibase.exception.UnexpectedLiquibaseException;
-import liquibase.servicelocator.ServiceLocator;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
+import liquibase.plugin.AbstractPluginFactory;
/**
* Manages {@link LiquibaseCommand} implementations.
*/
-public class CommandFactory {
-
- private static CommandFactory instance;
-
- private List commands;
+public class CommandFactory extends AbstractPluginFactory {
- private CommandFactory() {
- commands = new ArrayList<>();
- try {
- for (LiquibaseCommand command : Scope.getCurrentScope().getServiceLocator().findInstances(LiquibaseCommand.class)) {
- register(command);
- }
- } catch (Exception e) {
- throw new UnexpectedLiquibaseException(e);
- }
+ protected CommandFactory() {
}
- public static synchronized CommandFactory getInstance() {
- if (instance == null) {
- instance = new CommandFactory();
- }
- return instance;
+ @Override
+ protected Class getPluginClass() {
+ return LiquibaseCommand.class;
}
- public static synchronized void reset() {
- instance = new CommandFactory();
+ @Override
+ protected int getPriority(LiquibaseCommand obj, Object... args) {
+ return obj.getPriority((String) args[0]);
}
+ public LiquibaseCommand getCommand(String commandName) {
+ return getPlugin(commandName);
+ }
- public LiquibaseCommand getCommand(final String commandName) {
-
- Comparator commandComparator = new Comparator() {
- @Override
- public int compare(LiquibaseCommand o1, LiquibaseCommand o2) {
- return Integer.valueOf(o2.getPriority(commandName)).compareTo(o1.getPriority(commandName));
- }
- };
-
-
- List sortedCommands = new ArrayList<>(commands);
- Collections.sort(sortedCommands, commandComparator);
- if (sortedCommands.isEmpty()) {
- throw new UnexpectedLiquibaseException("Could not find command class for "+commandName);
- }
+ public T execute(LiquibaseCommand command) throws CommandExecutionException {
+ command.validate();
try {
- LiquibaseCommand command = sortedCommands.iterator().next().getClass().getConstructor().newInstance();
-
- if (command.getPriority(commandName) <= 0) {
- throw new UnexpectedLiquibaseException("Could not find command class for "+commandName);
- }
- return command;
+ return command.run();
} catch (Exception e) {
- throw new UnexpectedLiquibaseException(e);
+ if (e instanceof CommandExecutionException) {
+ throw (CommandExecutionException) e;
+ } else {
+ throw new CommandExecutionException(e);
+ }
}
- }
- public void register(LiquibaseCommand command) {
- commands.add(command);
}
- public void unregister(LiquibaseCommand command) {
- commands.remove(command);
- }
}
diff --git a/liquibase-core/src/main/java/liquibase/command/LiquibaseCommand.java b/liquibase-core/src/main/java/liquibase/command/LiquibaseCommand.java
index 3627208a775..bab1d21652b 100644
--- a/liquibase-core/src/main/java/liquibase/command/LiquibaseCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/LiquibaseCommand.java
@@ -1,20 +1,27 @@
package liquibase.command;
+import liquibase.plugin.Plugin;
+
+import java.util.SortedSet;
+
/**
* Commands are higher-level functions. They may execute multiple {@link liquibase.change.Change}s and usually interact with the outside world.
* Commands are different from Actions in that they implement end-user functionality rather than small pieces of logic.
* We package functionaly as commands so that the command line interface as well as other integrations can all use the same business logic.
*/
-public interface LiquibaseCommand {
+public interface LiquibaseCommand extends Plugin {
String getName();
+ int getPriority(String commandName);
+
+ SortedSet getArguments();
+
CommandValidationErrors validate();
/**
- * Executes the command. Should call {@link #validate()} as part of this method and throw {@link CommandExecutionException} if validation fails or there are any errors executing the command.
+ * Function that performs the actual logic. This should not be called directly by any code,
+ * only by {@link CommandFactory#execute(LiquibaseCommand)}
*/
- T execute() throws CommandExecutionException;
-
- int getPriority(String commandName);
-}
\ No newline at end of file
+ T run() throws Exception;
+}
diff --git a/liquibase-core/src/main/java/liquibase/command/core/DeactivateChangeLogCommand.java b/liquibase-core/src/main/java/liquibase/command/core/DeactivateChangeLogCommand.java
index b5059ae6983..ec127f243cc 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/DeactivateChangeLogCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/DeactivateChangeLogCommand.java
@@ -56,7 +56,7 @@ public void setOutputStream(PrintStream outputStream) {
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
//
// Access the HubService
// Stop if we do no have a key
diff --git a/liquibase-core/src/main/java/liquibase/command/core/DiffCommand.java b/liquibase-core/src/main/java/liquibase/command/core/DiffCommand.java
index a765f7f6638..225a9124c40 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/DiffCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/DiffCommand.java
@@ -130,7 +130,7 @@ public DiffCommand setCompareControl(CompareControl compareControl) {
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
SnapshotCommand.logUnsupportedDatabase(this.getReferenceDatabase(), this.getClass());
DiffResult diffResult = createDiffResult();
diff --git a/liquibase-core/src/main/java/liquibase/command/core/DiffToChangeLogCommand.java b/liquibase-core/src/main/java/liquibase/command/core/DiffToChangeLogCommand.java
index 548f1db0d94..b58bba748b6 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/DiffToChangeLogCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/DiffToChangeLogCommand.java
@@ -51,7 +51,7 @@ public DiffToChangeLogCommand setDiffOutputControl(DiffOutputControl diffOutputC
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
SnapshotCommand.logUnsupportedDatabase(this.getReferenceDatabase(), this.getClass());
DiffResult diffResult = createDiffResult();
diff --git a/liquibase-core/src/main/java/liquibase/command/core/DropAllCommand.java b/liquibase-core/src/main/java/liquibase/command/core/DropAllCommand.java
index ffbe6d8fba0..e63b7565628 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/DropAllCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/DropAllCommand.java
@@ -7,8 +7,7 @@
import liquibase.command.AbstractCommand;
import liquibase.command.CommandResult;
import liquibase.command.CommandValidationErrors;
-import liquibase.configuration.HubConfiguration;
-import liquibase.configuration.LiquibaseConfiguration;
+import liquibase.hub.HubConfiguration;
import liquibase.database.Database;
import liquibase.exception.DatabaseException;
import liquibase.exception.LiquibaseException;
@@ -101,7 +100,7 @@ public void setHubConnectionId(String hubConnectionIdString) {
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
Logger log = Scope.getCurrentScope().getLog(getClass());
HubUpdater hubUpdater = null;
@@ -159,9 +158,8 @@ protected CommandResult run() throws Exception {
// a LiquibaseHubException
//
private HubChangeLog getHubChangeLog(DatabaseChangeLog changeLog) throws LiquibaseHubException {
- HubConfiguration hubConfiguration = LiquibaseConfiguration.getInstance().getConfiguration(HubConfiguration.class);
- String apiKey = StringUtil.trimToNull(hubConfiguration.getLiquibaseHubApiKey());
- String hubMode = StringUtil.trimToNull(hubConfiguration.getLiquibaseHubMode());
+ String apiKey = StringUtil.trimToNull(HubConfiguration.LIQUIBASE_HUB_API_KEY.getCurrentValue());
+ String hubMode = StringUtil.trimToNull(HubConfiguration.LIQUIBASE_HUB_MODE.getCurrentValue());
String changeLogId = changeLog.getChangeLogId();
final HubServiceFactory hubServiceFactory = Scope.getCurrentScope().getSingleton(HubServiceFactory.class);
if (apiKey == null || hubMode.equals("off") || !hubServiceFactory.isOnline()) {
diff --git a/liquibase-core/src/main/java/liquibase/command/core/ExecuteSqlCommand.java b/liquibase-core/src/main/java/liquibase/command/core/ExecuteSqlCommand.java
index 07f6fa414c5..cf9a7b38b04 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/ExecuteSqlCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/ExecuteSqlCommand.java
@@ -65,7 +65,7 @@ public CommandValidationErrors validate() {
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
Executor executor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", database);
String sqlText;
if (sqlFile == null) {
diff --git a/liquibase-core/src/main/java/liquibase/command/core/GenerateChangeLogCommand.java b/liquibase-core/src/main/java/liquibase/command/core/GenerateChangeLogCommand.java
index d2f28fac0de..169815e60e0 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/GenerateChangeLogCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/GenerateChangeLogCommand.java
@@ -48,7 +48,7 @@ public GenerateChangeLogCommand setContext(String context) {
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
outputBestPracticeMessage();
String changeLogFile = StringUtil.trimToNull(getChangeLogFile());
diff --git a/liquibase-core/src/main/java/liquibase/command/core/HistoryCommand.java b/liquibase-core/src/main/java/liquibase/command/core/HistoryCommand.java
index c5faeccafbd..35bd90859da 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/HistoryCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/HistoryCommand.java
@@ -52,7 +52,7 @@ public void setOutputStream(PrintStream outputStream) {
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
ChangeLogHistoryService historyService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database);
outputStream.println("Liquibase History for " + database.getConnection().getURL());
diff --git a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangeLogCommand.java b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangeLogCommand.java
index bf536d13dbf..5b507dd85e8 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/RegisterChangeLogCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/RegisterChangeLogCommand.java
@@ -6,8 +6,8 @@
import liquibase.command.AbstractSelfConfiguratingCommand;
import liquibase.command.CommandResult;
import liquibase.command.CommandValidationErrors;
-import liquibase.configuration.HubConfiguration;
-import liquibase.configuration.LiquibaseConfiguration;
+import liquibase.GlobalConfiguration;
+import liquibase.hub.HubConfiguration;
import liquibase.exception.CommandLineParsingException;
import liquibase.exception.LiquibaseException;
import liquibase.hub.HubService;
@@ -71,7 +71,7 @@ public void setOutputStream(PrintStream outputStream) {
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
//
// Access the HubService
// Stop if we do no have a key
@@ -212,8 +212,7 @@ public int compare(Project o1, Project o2) {
private String readProjectNameFromConsole() throws CommandLineParsingException {
final UIService ui = Scope.getCurrentScope().getUI();
- HubConfiguration hubConfiguration = LiquibaseConfiguration.getInstance().getConfiguration(HubConfiguration.class);
- String hubUrl = hubConfiguration.getLiquibaseHubUrl();
+ String hubUrl = HubConfiguration.LIQUIBASE_HUB_URL.getCurrentValue();
String input = ui.prompt("Please enter your Project name and press [enter]. This is editable in your Liquibase Hub account at " + hubUrl, null, null, String.class);
return StringUtil.trimToEmpty(input);
}
diff --git a/liquibase-core/src/main/java/liquibase/command/core/SnapshotCommand.java b/liquibase-core/src/main/java/liquibase/command/core/SnapshotCommand.java
index 01d879e5a73..d69f10751b0 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/SnapshotCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/SnapshotCommand.java
@@ -94,7 +94,7 @@ public void setSnapshotMetadata(Map snapshotMetadata) {
}
@Override
- protected SnapshotCommandResult run() throws Exception {
+ public SnapshotCommandResult run() throws Exception {
SnapshotCommand.logUnsupportedDatabase(database, this.getClass());
SnapshotControl snapshotControl = new SnapshotControl(database);
snapshotControl.setSnapshotListener(snapshotListener);
diff --git a/liquibase-core/src/main/java/liquibase/command/core/SyncHubCommand.java b/liquibase-core/src/main/java/liquibase/command/core/SyncHubCommand.java
index 1440b3eeb6d..04041b1a448 100644
--- a/liquibase-core/src/main/java/liquibase/command/core/SyncHubCommand.java
+++ b/liquibase-core/src/main/java/liquibase/command/core/SyncHubCommand.java
@@ -80,7 +80,7 @@ public CommandValidationErrors validate() {
}
@Override
- protected CommandResult run() throws Exception {
+ public CommandResult run() throws Exception {
final HubServiceFactory hubServiceFactory = Scope.getCurrentScope().getSingleton(HubServiceFactory.class);
if (! hubServiceFactory.isOnline()) {
if (failIfOnline) {
diff --git a/liquibase-core/src/main/java/liquibase/configuration/AbstractConfigurationContainer.java b/liquibase-core/src/main/java/liquibase/configuration/AbstractConfigurationContainer.java
index 4e52cd9a8c5..d089e13bc40 100644
--- a/liquibase-core/src/main/java/liquibase/configuration/AbstractConfigurationContainer.java
+++ b/liquibase-core/src/main/java/liquibase/configuration/AbstractConfigurationContainer.java
@@ -1,161 +1,109 @@
package liquibase.configuration;
-import liquibase.exception.UnexpectedLiquibaseException;
+import liquibase.Scope;
+import liquibase.configuration.core.DeprecatedConfigurationValueProvider;
-import java.util.*;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
/**
- * Base class for configuration classes used by {@link liquibase.configuration.LiquibaseConfiguration}.
- * Implementations must have a no-arg constructor for LiquibaseConfiguration to initialize them as needed.
- *
- * AbstractConfigurationContainer implementations contain a "namespace" which can be used as the prefix to system properties or cases where there may be name conflicts.
- *
- * Properties can be accessed by name using the {@link #getValue(String, Class)} method, but implementation should implement standard get/set methods for easier use.
+ * @deprecated Use new {@link ConfigurationDefinition} style
*/
public abstract class AbstractConfigurationContainer implements ConfigurationContainer {
- private ConfigurationContainer configurationContainer;
+ private final String namespace;
+ private final ConfigurationContainer container;
+ private Map properties = new HashMap<>();
- /**
- * Subclasses must call this constructor passing the namespace, but must themselves provide a no-arg public constructor.
- */
- protected AbstractConfigurationContainer(String namespace) {
- this.configurationContainer = new ConfigurationContainer(namespace);
+ public AbstractConfigurationContainer(String namespace) {
+ this.namespace = namespace;
+ this.container = new ConfigurationContainer();
}
+ /**
+ * @deprecated
+ */
protected ConfigurationContainer getContainer() {
- return configurationContainer;
+ return container;
}
/**
- * Return the ConfigurationProperty object for the given property name.
- * Normally {@link #getValue(String, Class)} is the easiest method to call.
+ * @deprecated
*/
@Override
public ConfigurationProperty getProperty(String propertyName) {
- return getContainer().getProperty(propertyName);
+ return properties.get(propertyName);
}
/**
- * Return all available properties.
+ * @deprecated
*/
@Override
public Set getProperties() {
- return new HashSet<>(getContainer().properties.values());
+ return new HashSet<>(properties.values());
}
/**
- * Returns the value for the given property cast to the passed returnType.
- * If the type of the property and the given return type are not compatible an exception will be thrown.
- * If the passed propertyName is not a defined property, an exception is thrown.
+ * @deprecated
*/
@Override
public T getValue(String propertyName, Class returnType) {
- return getContainer().getValue(propertyName, returnType);
+ final ConfiguredValue currentValue = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class).getCurrentConfiguredValue(namespace + "." + propertyName);
+ return (T) currentValue.getValue();
}
-
/**
- * Override default values for properties with the given ConfigurationProviders.
+ * @deprecated
*/
- @Override
- public void init(ConfigurationValueProvider... configurationValueProviders) {
- if (configurationValueProviders != null) {
- for (ConfigurationProperty property : getContainer().properties.values()) {
- property.init(configurationValueProviders);
- }
- }
- }
-
@Override
public void setValue(String propertyName, Object value) {
- getContainer().setValue(propertyName, value);
+ DeprecatedConfigurationValueProvider.setData(namespace + "." + propertyName, value);
}
+ /**
+ * @deprecated
+ */
@Override
public String getNamespace() {
- return getContainer().getNamespace();
+ return namespace;
}
/**
- * Like a java.util.Map, but with extra logic for working with ConfigurationProperties.
- * Used to define and hold available properties. Methods return "this" to allow easy chaining.
+ * @deprecated
*/
- protected static class ConfigurationContainer {
-
- private final String namespace;
- private final Map properties = new HashMap<>();
-
- protected ConfigurationContainer(String namespace) {
- this.namespace = namespace;
- }
-
- public String getNamespace() {
- return namespace;
- }
+ protected class ConfigurationContainer {
/**
- * Adds a property definition to this configuration.
+ * @deprecated
*/
public ConfigurationProperty addProperty(String propertyName, Class type) {
- ConfigurationProperty property = new ConfigurationProperty(namespace, propertyName, type);
- properties.put(propertyName, property);
+ final ConfigurationDefinition.Builder builder = new ConfigurationDefinition.Builder(namespace);
+ final ConfigurationDefinition.Builder.NewDefinition newDefinition = builder.define(propertyName, type);
+ final ConfigurationProperty property = new ConfigurationProperty(namespace, newDefinition);
+
+ properties.put(propertyName, property);
return property;
}
- /**
- * Returns the ConfigurationProperty object with the given name. If the property was not defined, an exception is thrown.
- */
public ConfigurationProperty getProperty(String propertyName) {
- ConfigurationProperty property = properties.get(propertyName);
- if (property != null) {
- return property;
- }
-
- //
- // Not matching with the actual key then try case insensitive
- //
- for (Map.Entry entry : properties.entrySet()) {
- if (entry.getKey().equalsIgnoreCase(propertyName)) {
- return entry.getValue();
- }
- }
-
- //
- // No match so throw the exception
- //
- throw new UnexpectedLiquibaseException("Unknown property on "+getClass().getName()+": "+propertyName);
+ return AbstractConfigurationContainer.this.getProperty(propertyName);
}
- /**
- * Returns the value for the given property. If the property was not defined, an exception is thrown.
- */
public T getValue(String propertyName, Class returnType) {
- ConfigurationProperty property = getProperty(propertyName);
-
- if (!property.getType().isAssignableFrom(returnType)) {
- throw new UnexpectedLiquibaseException("Property "+propertyName+" on "+getClass().getName()+" is of type "+property.getType().getName()+", not "+returnType.getName());
- }
-
- return (T) property.getValue();
+ return AbstractConfigurationContainer.this.getValue(propertyName, returnType);
}
- /**
- * Sets the value for the given property.
- * Any value set through this method will overwrite any default values found by the configured ConfigurationPropertyProviders.
- * If the property was not defined, an exception is thrown.
- */
public void setValue(String propertyName, Object value) {
- ConfigurationProperty property = getProperty(propertyName); // properties.get(propertyName);
-
- if (property == null) {
- throw new UnexpectedLiquibaseException("Unknown property on "+getClass().getName()+": "+propertyName);
- }
-
- property.setValue(value);
-
+ AbstractConfigurationContainer.this.setValue(propertyName, value);
}
}
+ protected static class DelegatedConfigurationContainer extends AbstractConfigurationContainer {
+ public DelegatedConfigurationContainer(String namespace) {
+ super(namespace);
+ }
+ }
}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/AbstractConfigurationValueProvider.java b/liquibase-core/src/main/java/liquibase/configuration/AbstractConfigurationValueProvider.java
new file mode 100644
index 00000000000..1fd7cb7f48c
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/configuration/AbstractConfigurationValueProvider.java
@@ -0,0 +1,7 @@
+package liquibase.configuration;
+
+/**
+ * Convenience base class for {@link ConfigurationValueProvider} implementations
+ */
+public abstract class AbstractConfigurationValueProvider implements ConfigurationValueProvider {
+}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/AbstractMapConfigurationValueProvider.java b/liquibase-core/src/main/java/liquibase/configuration/AbstractMapConfigurationValueProvider.java
new file mode 100644
index 00000000000..527f169cee0
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/configuration/AbstractMapConfigurationValueProvider.java
@@ -0,0 +1,88 @@
+package liquibase.configuration;
+
+import liquibase.util.StringUtil;
+
+import java.util.Map;
+
+/**
+ * Convenience class for {@link ConfigurationValueProvider}s that can collect the possible values into a Map.
+ * By default, it will follow standardized fuzzy-matching rules including being case insensitive, checking camelCase and kabob-case, etc.
+ *
+ * @see #keyMatches(String, String)
+ */
+public abstract class AbstractMapConfigurationValueProvider extends AbstractConfigurationValueProvider {
+
+ protected abstract Map, ?> getMap();
+
+ protected abstract String getSourceDescription();
+
+ /**
+ * Finds the given key in the result of {@link #getMap()} using {@link #keyMatches(String, String)} to determine key equality
+ */
+ @Override
+ public ProvidedValue getProvidedValue(String key) {
+ if (key == null) {
+ return null;
+ }
+
+ final Map, ?> sourceData = getMap();
+
+ //try direct lookup first, for performance:
+ if (sourceData.containsKey(key)) {
+ final Object foundValue = sourceData.get(key);
+ if (isValueSet(foundValue)) {
+ return new ProvidedValue(key, key, foundValue, getSourceDescription(), this);
+ }
+ }
+
+
+ for (Map.Entry, ?> entry : sourceData.entrySet()) {
+ final String actualKey = String.valueOf(entry.getKey());
+
+ if (keyMatches(key, actualKey)) {
+ final Object value = entry.getValue();
+ if (isValueSet(value)) {
+ return new ProvidedValue(key, actualKey, value, getSourceDescription(), this);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Used by {@link #getProvidedValue(String)} to determine if the given value is a "real" value.
+ * This implementation returns false if value is null or if it is an empty string
+ */
+ protected boolean isValueSet(Object value) {
+ if (value == null) {
+ return false;
+ }
+ if (value instanceof String) {
+ return StringUtil.isNotEmpty((String) value);
+ }
+
+ return false;
+ }
+
+ /**
+ * Used by {@link #getProvidedValue(String)} to determine of a given map entry matches the wanted key.
+ * This implementation compares the values case-insensitively, and will replace camelCase words with kabob-case
+ * @param wantedKey the configuration key requested
+ * @param storedKey the key stored in the map
+ */
+ protected boolean keyMatches(String wantedKey, String storedKey) {
+ if (storedKey.equalsIgnoreCase(wantedKey)) {
+ return true;
+ }
+
+ //convert camelCase to kabob-case
+ wantedKey = wantedKey.replaceAll("([A-Z])", "-$1");
+ if (storedKey.equalsIgnoreCase(wantedKey)) {
+ return true;
+ }
+
+ return false;
+ }
+
+}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/AutoloadedConfigurations.java b/liquibase-core/src/main/java/liquibase/configuration/AutoloadedConfigurations.java
new file mode 100644
index 00000000000..223ea9b783f
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/configuration/AutoloadedConfigurations.java
@@ -0,0 +1,9 @@
+package liquibase.configuration;
+
+/**
+ * Marker interface for a class containing {@link ConfigurationDefinition} which should be auto-loaded at Liquibase startup.
+ * All classes that implement this interface must still be registered in the META-INF/services/liquibase.configuration.AutoloadedConfigurations file.
+ */
+public interface AutoloadedConfigurations {
+
+}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationContainer.java b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationContainer.java
index 3cc529f6f5d..36186531940 100644
--- a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationContainer.java
+++ b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationContainer.java
@@ -2,16 +2,33 @@
import java.util.Set;
+/**
+ * @deprecated interface from old style configuration code. Use {@link ConfigurationDefinition} and {@link AutoloadedConfigurations} now.
+ */
public interface ConfigurationContainer {
+
+ /**
+ * @deprecated
+ */
ConfigurationProperty getProperty(String propertyName);
+ /**
+ * @deprecated
+ */
Set getProperties();
+ /**
+ * @deprecated
+ */
T getValue(String propertyName, Class returnType);
-
- void setValue(String propertyName, Object value);
- void init(ConfigurationValueProvider... configurationValueProviders);
+ /**
+ * @deprecated
+ */
+ void setValue(String propertyName, Object value);
+ /**
+ * @deprecated
+ */
String getNamespace();
}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationDefinition.java b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationDefinition.java
new file mode 100644
index 00000000000..831eb692fb0
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationDefinition.java
@@ -0,0 +1,354 @@
+package liquibase.configuration;
+
+import liquibase.Scope;
+import liquibase.util.ObjectUtil;
+
+import java.util.Objects;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.regex.Pattern;
+
+/**
+ * A higher-level/detailed definition to provide type-safety, metadata, default values, etc..
+ * Any code that is working with configurations should be using an instance of this class, rather than the lower-level, generic {@link LiquibaseConfiguration}
+ *
+ * ConfigurationDefinitions that are registered with {@link LiquibaseConfiguration#registerDefinition(ConfigurationDefinition)} will
+ * be available in generated help etc.
+ *
+ * These objects are immutable, so to construct definitions, use {@link Builder}
+ *
+ * The definition keys should be dot-separated, camelCased names, using a unique "namespace" as part of it.
+ * For example:
yourCorp.yourProperty
or
yourCorp.sub.otherProperty
.
+ * Liquibase uses "liquibase" as the base namespace like
liquibase.shouldRun
+ *
+ */
+public class ConfigurationDefinition implements Comparable> {
+
+ private final String key;
+ private final Set aliasKeys = new TreeSet<>();
+ private final Class type;
+ private String description;
+ private DataType defaultValue;
+ private boolean commonlyUsed;
+ private ConfigurationValueConverter valueHandler;
+ private ConfigurationValueObfuscator valueObfuscator;
+
+ private static final Pattern ALLOWED_KEY_PATTERN = Pattern.compile("[a-zA-Z0-9.]+");
+
+ /**
+ * @return if the given {@link ConfiguredValue} was set by a default value
+ */
+ public static boolean wasDefaultValueUsed(ConfiguredValue> configuredValue) {
+ for (ProvidedValue providedValue : configuredValue.getProvidedValues()) {
+ if (providedValue.getProvider() != null && providedValue.getProvider() instanceof ConfigurationDefinition.DefaultValueProvider) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Constructor private to force {@link Builder} usage
+ */
+ private ConfigurationDefinition(String key, Class type) {
+ if (!ALLOWED_KEY_PATTERN.matcher(key).matches()) {
+ throw new IllegalArgumentException("Invalid key format: "+key);
+ }
+
+ this.key = key;
+ this.type = type;
+ this.valueHandler = value -> ObjectUtil.convert(value, type);
+ }
+
+ /**
+ * Convenience method around {@link #getCurrentConfiguredValue()} to return the value.
+ */
+ public DataType getCurrentValue() {
+ final Object value = getCurrentConfiguredValue().getProvidedValue().getValue();
+ try {
+ return (DataType) value;
+ } catch (ClassCastException e) {
+ throw new IllegalArgumentException("The current value of "+key+" not the expected type: "+ e.getMessage(), e);
+ }
+ }
+
+ /**
+ * Convenience method around {@link #getCurrentConfiguredValue()} to return the obfuscated version of the value.
+ * @return the obfuscated value, or the plain-text value if no obfuscator is defined for this definition.
+ */
+ public DataType getCurrentValueObfuscated() {
+ final DataType currentValue = getCurrentValue();
+
+ if (this.valueObfuscator == null) {
+ return currentValue;
+ }
+
+ return this.valueObfuscator.obfuscate(currentValue);
+ }
+
+ /**
+ * @return Full details on the current value for this definition.
+ * Will always return a {@link ConfiguredValue},
+ */
+ public ConfiguredValue getCurrentConfiguredValue() {
+ final LiquibaseConfiguration liquibaseConfiguration = Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class);
+
+ ConfiguredValue configurationValue = liquibaseConfiguration.getCurrentConfiguredValue(this.getKey());
+ ConfiguredValue initialConfigurationValue = configurationValue;
+
+ for (String alias : this.aliasKeys) {
+ if (configurationValue.found()) {
+ break;
+ }
+ configurationValue = liquibaseConfiguration.getCurrentConfiguredValue(alias);
+ }
+
+ if (!configurationValue.found()) {
+ //set back to initial version, not the alias
+ configurationValue = initialConfigurationValue;
+ }
+
+ if (!configurationValue.found()) {
+ defaultValue = this.getDefaultValue();
+ if (defaultValue != null) {
+ configurationValue.override(new DefaultValueProvider(this.getDefaultValue()).getProvidedValue(key));
+ }
+ }
+
+ final ProvidedValue providedValue = configurationValue.getProvidedValue();
+ final DataType finalValue = valueHandler.convert(providedValue.getValue());
+ if (providedValue != finalValue) {
+ configurationValue.override(new ConvertedValueProvider(finalValue, providedValue).getProvidedValue(key));
+ }
+
+ return (ConfiguredValue) configurationValue;
+ }
+
+ /**
+ * The standard configuration key for this definition. See the {@link ConfigurationDefinition} class-level docs on key format.
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * @return alternate configuration keys to check for values. Used for backwards compatibility.
+ */
+ public Set getAliasKeys() {
+ return aliasKeys;
+ }
+
+ /**
+ * @return the type of data this definition returns.
+ */
+ public Class getType() {
+ return type;
+ }
+
+ /**
+ * A user-friendly description of this definition.
+ * This will be exposed to end-users in generated help.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * The default value used by this definition if no value is currently configured.
+ *
+ * NOTE: this is only used if none of the {@link ConfigurationValueProvider}s have a configuration for the property.
+ * Even if some return "null", that is still considered a provided value to use rather than this default.
+ */
+ public DataType getDefaultValue() {
+ return defaultValue;
+ }
+
+ /**
+ * Returns true if this is configuration users are often interested in setting.
+ * Used to simplify generated help by hiding less commonly used settings.
+ */
+ public boolean getCommonlyUsed() {
+ return commonlyUsed;
+ }
+
+ @Override
+ public int compareTo(ConfigurationDefinition o) {
+ return this.getKey().compareTo(o.getKey());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ConfigurationDefinition> that = (ConfigurationDefinition>) o;
+ return Objects.equals(key, that.key);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(key);
+ }
+
+ /**
+ * Return true if the given key matches this definition.
+ */
+ public boolean equalsKey(String key) {
+ if (key == null) {
+ return false;
+ }
+
+ if (getKey().equalsIgnoreCase(key)) {
+ return true;
+ }
+
+ for (String alias : getAliasKeys()) {
+ if (alias.equalsIgnoreCase(key)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Used to construct new {@link ConfigurationDefinition} instances.
+ */
+ public static class Builder {
+ private final String defaultKeyPrefix;
+
+ /**
+ * @param defaultKeyPrefix the prefix to add to new keys that are not fully qualified
+ */
+ public Builder(String defaultKeyPrefix) {
+ if (!ALLOWED_KEY_PATTERN.matcher(defaultKeyPrefix).matches()) {
+ throw new IllegalArgumentException("Invalid prefix format: "+defaultKeyPrefix);
+ }
+
+ this.defaultKeyPrefix = defaultKeyPrefix;
+ }
+
+ /**
+ * Starts a new definition with the given key. Always adds the defaultKeyPrefix.
+ */
+ public NewDefinition define(String key, Class dataType) {
+ final ConfigurationDefinition definition = new ConfigurationDefinition<>(defaultKeyPrefix + "." + key, dataType);
+
+ return new NewDefinition<>(definition);
+ }
+
+ public static class NewDefinition {
+
+ private final ConfigurationDefinition definition;
+
+ private NewDefinition(ConfigurationDefinition definition) {
+ this.definition = definition;
+ }
+
+ public NewDefinition addAliasKey(String alias) {
+ if (!ALLOWED_KEY_PATTERN.matcher(alias).matches()) {
+ throw new IllegalArgumentException("Invalid alias format: "+alias);
+ }
+
+ definition.aliasKeys.add(alias);
+
+ return this;
+ }
+
+ public NewDefinition setDescription(String description) {
+ definition.description = description;
+ return this;
+ }
+
+ public NewDefinition setDefaultValue(DataType defaultValue) {
+ definition.defaultValue = defaultValue;
+ return this;
+ }
+
+ public NewDefinition setValueHandler(ConfigurationValueConverter handler) {
+ definition.valueHandler = handler;
+
+ return this;
+ }
+
+ public NewDefinition setValueObfuscator(ConfigurationValueObfuscator handler) {
+ definition.valueObfuscator = handler;
+
+ return this;
+ }
+
+ public NewDefinition setCommonlyUsed(boolean commonlyUsed) {
+ definition.commonlyUsed = commonlyUsed;
+
+ return this;
+ }
+
+ /**
+ * Finishes building this definition AND registers it with {@link LiquibaseConfiguration#registerDefinition(ConfigurationDefinition)}.
+ * To not register this definition, use {@link #buildTemporary()}
+ */
+ public ConfigurationDefinition build() {
+ Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class).registerDefinition(definition);
+
+ return definition;
+ }
+
+ /**
+ * Finishes building this definition WITHOUT registering it with {@link LiquibaseConfiguration#registerDefinition(ConfigurationDefinition)}.
+ * To automatically register this definition, use {@link #build()}
+ */
+ public ConfigurationDefinition buildTemporary() {
+ return definition;
+ }
+ }
+ }
+
+ /**
+ * Used to track configuration values set by a default
+ */
+ private static final class DefaultValueProvider implements ConfigurationValueProvider {
+
+ private final Object value;
+
+ public DefaultValueProvider(Object value) {
+ this.value = value;
+ }
+
+ @Override
+ public int getPrecedence() {
+ return -1;
+ }
+
+ @Override
+ public ProvidedValue getProvidedValue(String key) {
+ return new ProvidedValue(key, key, value, "Default value", this);
+ }
+ }
+
+ /**
+ * Used to track configuration values converted by a handler
+ */
+ private static final class ConvertedValueProvider implements ConfigurationValueProvider {
+
+ private final DataType value;
+ private final String originalSource;
+ private final String actualKey;
+
+ public ConvertedValueProvider(DataType value, ProvidedValue originalProvidedValue) {
+ this.value = value;
+ this.actualKey = originalProvidedValue.getActualKey();
+ this.originalSource = originalProvidedValue.getSourceDescription();
+ }
+
+ @Override
+ public int getPrecedence() {
+ return -1;
+ }
+
+ @Override
+ public ProvidedValue getProvidedValue(String key) {
+ return new ProvidedValue(key, actualKey, value, originalSource, this);
+ }
+ }
+}
+
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationProperty.java b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationProperty.java
index 51e89b2a6ad..5f215ef3ebd 100644
--- a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationProperty.java
+++ b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationProperty.java
@@ -1,77 +1,39 @@
package liquibase.configuration;
+import liquibase.configuration.core.DeprecatedConfigurationValueProvider;
import liquibase.exception.UnexpectedLiquibaseException;
-import liquibase.util.ObjectUtil;
import liquibase.util.StringUtil;
import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
- * Contains the definition and current value of a given configuration property.
+ * @deprecated
*/
public class ConfigurationProperty {
+ private final ConfigurationDefinition.Builder.NewDefinition definitionBuilder;
+ private ConfigurationDefinition definition;
private final String namespace;
- private final String name;
- private final Class type;
- private List aliases = new ArrayList<>();
- private Object value;
- private String description;
- private Object defaultValue;
- private boolean wasOverridden;
-
- private ConfigurationValueHandler valueHandler;
-
- public ConfigurationProperty(String namespace, String propertyName, Class type) {
+ public ConfigurationProperty(String namespace, ConfigurationDefinition.Builder.NewDefinition definitionBuilder) {
this.namespace = namespace;
- this.name = propertyName;
- this.type = type;
+ this.definitionBuilder = definitionBuilder;
+ this.definition = definitionBuilder.buildTemporary();
}
- /**
- * Initialize this property with values in the given ConfigurationProvers. If the configurationValueProviders do not contain
- * a default value, the property is initialized with the value set by {@link #setDefaultValue(Object)}.
- * If multiple configurationValueProviders contain values, the first in the list wins.
- */
- protected void init(ConfigurationValueProvider[] configurationValueProviders) {
- Object containerValue = null;
-
- for (ConfigurationValueProvider container : configurationValueProviders) {
- containerValue = container.getValue(namespace, name);
- for (String alias : aliases) {
- if (containerValue != null) {
- break;
- }
- containerValue = container.getValue(namespace, alias);
- }
- }
-
- if (containerValue == null) {
- value = defaultValue;
- } else {
- try {
- value = valueOf(containerValue);
- wasOverridden = true;
- } catch (NumberFormatException e) {
- throw new UnexpectedLiquibaseException("Error parsing "+containerValue+" as a "+type.getSimpleName());
- }
- }
- }
-
-
/**
* Returns the property name.
+ *
+ * @deprecated
*/
public String getName() {
- return name;
+ return definition.getKey().replace(namespace+".", "");
}
/**
* Returns the namespace used by this property's {@link ConfigurationContainer}
+ * @deprecated
*/
public String getNamespace() {
return namespace;
@@ -79,15 +41,18 @@ public String getNamespace() {
/**
* Returns the type of value stored in this property
+ * @deprecated
*/
public Class getType() {
- return type;
+ return definition.getType();
}
/**
* Converts an object of a different type to the type used by this property. If types are not convertible, an exception is thrown.
+ * @deprecated
*/
protected Object valueOf(Object value) {
+ Class type = definition.getType();
if (value == null) {
return value;
} else if (type.isAssignableFrom(value.getClass())) {
@@ -100,7 +65,7 @@ protected Object valueOf(Object value) {
} else if (type.equals(BigDecimal.class)) {
return new BigDecimal((String) value);
} else if (type.equals(Long.class)) {
- return Long.valueOf((String) value);
+ return Long.valueOf((String) value);
} else if (type.equals(List.class)) {
return StringUtil.splitAndTrim((String) value, ",");
} else {
@@ -113,45 +78,43 @@ protected Object valueOf(Object value) {
/**
* Returns the value currently stored in this property without any casting.
+ * @deprecated
*/
public Object getValue() {
- return value;
+ return definition.getCurrentValue();
}
/**
* Returns the value currently stored in this property cast to the given type.
+ * @deprecated
*/
public T getValue(Class type) {
- if (!this.type.isAssignableFrom(type)) {
- throw new UnexpectedLiquibaseException("Property "+name+" on is of type "+this.type.getSimpleName()+", not "+type.getSimpleName());
+ if (!this.definition.getType().isAssignableFrom(type)) {
+ throw new UnexpectedLiquibaseException("Property "+definition.getType()+" on is of type "+this.definition.getType().getSimpleName()+", not "+type.getSimpleName());
}
- return (T) value;
+ return (T) definition.getCurrentValue();
}
/**
* Overwrites the value currently stored in this property. It he passed type is not compatible with the defined type, an exception is thrown.
+ * @deprecated
*/
public void setValue(Object value) {
- if (valueHandler == null) {
- value = ObjectUtil.convert(value, type);
- } else {
- value = valueHandler.convert(value);
- }
- if ((value != null) && !type.isAssignableFrom(value.getClass())) {
- throw new UnexpectedLiquibaseException("Property "+name+" is of type "+type.getSimpleName()+", not "+value.getClass().getSimpleName());
- }
-
- this.value = value;
- wasOverridden = true;
+ DeprecatedConfigurationValueProvider.setData(definition, value);
}
/**
* Adds an alias for this property. An alias is an alternate to the "name" field that can be used by the ConfigurationProvers to look up starting values.
+ * @deprecated
*/
public ConfigurationProperty addAlias(String... aliases) {
if (aliases != null) {
- this.aliases.addAll(Arrays.asList(aliases));
+ for (String alias : aliases) {
+ definitionBuilder.addAliasKey(alias);
+ }
+
+ definition = definitionBuilder.buildTemporary();
}
return this;
@@ -159,48 +122,55 @@ public ConfigurationProperty addAlias(String... aliases) {
/**
* Returns a human-readable definition of this property
+ * @deprecated
*/
public String getDescription() {
- return description;
+ return definition.getDescription();
}
+ /**
+ * @deprecated
+ */
public ConfigurationProperty setDescription(String description) {
- this.description = description;
+ this.definitionBuilder.setDescription(description);
+ this.definition = definitionBuilder.buildTemporary();
+
return this;
}
/**
* Returns the default value to use if no ConfigurationProviders override it.
+ * @deprecated
*/
public Object getDefaultValue() {
- return defaultValue;
+ return definition.getDefaultValue();
}
/**
* Sets the default value to use if no ConfigurationProviders override it. Throws an exception if the given object is not compatible with the defined type.
+ * @deprecated
*/
public ConfigurationProperty setDefaultValue(Object defaultValue) {
- if ((defaultValue != null) && !type.isAssignableFrom(defaultValue.getClass())) {
- if ((type == Long.class) && (defaultValue instanceof Integer)) {
- return setDefaultValue(((Integer) defaultValue).longValue());
- }
- throw new UnexpectedLiquibaseException("Property "+name+" on is of type "+type.getSimpleName()+", not "+defaultValue.getClass().getSimpleName());
- }
-
- this.defaultValue = defaultValue;
+ this.definitionBuilder.setDefaultValue(defaultValue);
+ this.definition = definitionBuilder.buildTemporary();
return this;
}
/**
* Returns true if the value has been set by a ConfigurationValueProvider or by {@link #setValue(Object)}
+ * @deprecated
*/
public boolean getWasOverridden() {
- return wasOverridden;
+ return !ConfigurationDefinition.wasDefaultValueUsed(this.definition.getCurrentConfiguredValue());
}
+ /**
+ * @deprecated
+ */
public ConfigurationProperty setValueHandler(ConfigurationValueHandler handler) {
- this.valueHandler = handler;
+ this.definitionBuilder.setValueHandler(value -> handler.convert(value));
+ this.definition = definitionBuilder.buildTemporary();
return this;
}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueConverter.java b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueConverter.java
new file mode 100644
index 00000000000..c69c5244276
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueConverter.java
@@ -0,0 +1,17 @@
+package liquibase.configuration;
+
+/**
+ * Used by {@link ConfigurationDefinition#getCurrentConfiguredValue()} to translate whatever object type a {@link ConfigurationValueProvider} is returning
+ * into the object type the definition uses.
+ */
+public interface ConfigurationValueConverter {
+
+ /**
+ * Converts an arbitrary object into the correct type.
+ * Implementations should be able to handle any type passed them, often types by calling toString() on the incoming value and parsing the string.
+ * Normally, a null value will be returned as a null value, but that is up to the implementation.
+ *
+ * @throws IllegalArgumentException if the value cannot be parsed or is an invalid value.
+ */
+ DataType convert(Object value) throws IllegalArgumentException;
+}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueHandler.java b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueHandler.java
index 6c086177763..d6d322d7a77 100644
--- a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueHandler.java
+++ b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueHandler.java
@@ -1,9 +1,8 @@
package liquibase.configuration;
/**
- * Implementations can convert specified {@link ConfigurationProperty} values
+ * @deprecated
*/
public interface ConfigurationValueHandler {
-
Object convert(Object value);
}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueObfuscator.java b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueObfuscator.java
new file mode 100644
index 00000000000..533fd531d44
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueObfuscator.java
@@ -0,0 +1,12 @@
+package liquibase.configuration;
+
+/**
+ * Used by {@link ConfigurationDefinition#getCurrentValueObfuscated()} to obfuscate the current value.
+ */
+public interface ConfigurationValueObfuscator {
+
+ /**
+ * Return an "obfuscated" version of the given value, suitable for logging or storing in non-secure environments.
+ */
+ DataType obfuscate(DataType value);
+}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueProvider.java b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueProvider.java
index 18df84963cd..6ad4f468ba9 100644
--- a/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueProvider.java
+++ b/liquibase-core/src/main/java/liquibase/configuration/ConfigurationValueProvider.java
@@ -1,19 +1,33 @@
package liquibase.configuration;
/**
- * Interface for classes that are able to lookup overriding default LiquibaseConfiguration values.
- * For example, {@link liquibase.configuration.SystemPropertyProvider} can look up property values in system properties.
+ * Defines a way for {@link LiquibaseConfiguration} to find configured values.
*/
public interface ConfigurationValueProvider {
/**
- * Return the value for a given namespace and property. Returns null if this provider does not have a value for this property.
+ * Returns the precedence of values returned by this provider. Higher a provider with higher precedence overrides values from lower precedence providers.
+ *
100: TODO profile/context specific properties files
+ *
50: TODO default properties files
+ *
*/
- Object getValue(String namespace, String property);
+ int getPrecedence();
/**
- * Generates a human consumable description of how the configured ConfigurationValueProvider(s) will attempt to set a default value.
- * See {@link LiquibaseConfiguration#describeValueLookupLogic(ConfigurationProperty)}
+ * Lookup the given key in this source.
+ * It is up to the implementation to provide any "smoothing" or translation of key names.
+ * For example, a SystemEnvironmentValueProvider will look check environment variables containing _'s rather than .'s.
+ *
+ * @return null if the key is not defined in this provider.
*/
- String describeValueLookupLogic(ConfigurationProperty property);
+ ProvidedValue getProvidedValue(String key);
}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java b/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java
new file mode 100644
index 00000000000..e2aadc4ed25
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/configuration/ConfiguredValue.java
@@ -0,0 +1,82 @@
+package liquibase.configuration;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This wraps all the {@link ProvidedValue}s to return the overall value returned from the collection of {@link ConfigurationValueProvider}s.
+ * Returned by {@link LiquibaseConfiguration#getCurrentConfiguredValue(String)}
+ */
+public class ConfiguredValue {
+
+ private static final NoValueProvider NO_VALUE_PROVIDER = new NoValueProvider();
+
+ private final List providedValues = new ArrayList<>();
+ private final String key;
+
+ protected ConfiguredValue(String key) {
+ this.key = key;
+ }
+
+ public DataType getValue() {
+ final ProvidedValue providedValue = getProvidedValue();
+ if (providedValue == null) {
+ return null;
+ }
+
+ return (DataType) providedValue.getValue();
+ }
+
+
+ /**
+ * Returns the "winning" value across all the possible {@link ConfigurationValueProvider}.
+ * A {@link ProvidedValue} is always returned, even if the value was not configured.
+ *
+ * @see #found()
+ */
+ public ProvidedValue getProvidedValue() {
+ return getProvidedValues().get(0);
+ }
+
+
+ /**
+ * Replaces the current configured value with a higher-precedence one.
+ */
+ protected void override(ProvidedValue details) {
+ this.providedValues.add(0, details);
+ }
+
+ /**
+ * @return a full list of where the configuration value was set and/or overridden.
+ */
+ public List getProvidedValues() {
+ if (providedValues.size() == 0) {
+ return Collections.singletonList(NO_VALUE_PROVIDER.getProvidedValue(key));
+ }
+
+ return Collections.unmodifiableList(providedValues);
+ }
+
+ /**
+ * @return true if a value was found across the providers.
+ */
+ public boolean found() {
+ return providedValues.size() > 0;
+ }
+
+ /**
+ * Used to track configuration with no value set
+ */
+ private static final class NoValueProvider implements ConfigurationValueProvider {
+ @Override
+ public int getPrecedence() {
+ return -1;
+ }
+
+ @Override
+ public ProvidedValue getProvidedValue(String key) {
+ return new ProvidedValue(key, key, null, "No configuration or default value found", this);
+ }
+ }
+}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/GlobalConfiguration.java b/liquibase-core/src/main/java/liquibase/configuration/GlobalConfiguration.java
index ec0789dc1ea..128fd7e1a75 100644
--- a/liquibase-core/src/main/java/liquibase/configuration/GlobalConfiguration.java
+++ b/liquibase-core/src/main/java/liquibase/configuration/GlobalConfiguration.java
@@ -1,219 +1,258 @@
package liquibase.configuration;
+import liquibase.configuration.core.DeprecatedConfigurationValueProvider;
+
+import java.util.Set;
+
/**
- * Configuration container for global properties.
+ * @deprecated use {@link liquibase.GlobalConfiguration}
*/
-public class GlobalConfiguration extends AbstractConfigurationContainer {
-
- public static final String SHOULD_RUN = "shouldRun";
- public static final String DATABASECHANGELOG_TABLE_NAME = "databaseChangeLogTableName";
- public static final String DATABASECHANGELOGLOCK_TABLE_NAME = "databaseChangeLogLockTableName";
- public static final String LIQUIBASE_TABLESPACE_NAME = "tablespaceName";
- public static final String LIQUIBASE_CATALOG_NAME = "catalogName";
- public static final String LIQUIBASE_SCHEMA_NAME = "schemaName";
- public static final String OUTPUT_LINE_SEPARATOR = "outputLineSeparator";
- public static final String OUTPUT_ENCODING = "outputFileEncoding";
- public static final String CHANGELOGLOCK_WAIT_TIME = "changeLogLockWaitTimeInMinutes";
- public static final String CHANGELOGLOCK_POLL_RATE = "changeLogLockPollRate";
- public static final String CONVERT_DATA_TYPES = "convertDataTypes";
- public static final String GENERATE_CHANGESET_CREATED_VALUES = "generateChangeSetCreatedValues";
- public static final String AUTO_REORG = "autoReorg";
- public static final String DIFF_COLUMN_ORDER = "diffColumnOrder";
- public static final String ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA = "alwaysOverrideStoredLogicSchema";
- public static final String GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION = "generatedChangeSetIdsContainsDescription";
- public static final String INCLUDE_CATALOG_IN_SPECIFICATION = "includeCatalogInSpecification";
- public static final String SHOULD_SNAPSHOT_DATA = "shouldSnapshotData";
- public static final String FILTER_LOG_MESSAGES = "filterLogMessages";
- public static final String HEADLESS = "headless";
-
- public GlobalConfiguration() {
- super("liquibase");
+public class GlobalConfiguration extends liquibase.GlobalConfiguration implements ConfigurationContainer {
- getContainer().addProperty(SHOULD_RUN, Boolean.class)
- .setDescription("Should Liquibase commands execute")
- .setDefaultValue(true)
- .addAlias("should.run");
+ /**
+ * @deprecated
+ */
+ public static final String SHOULD_RUN = liquibase.GlobalConfiguration.SHOULD_RUN.getKey();
- getContainer().addProperty(DATABASECHANGELOG_TABLE_NAME, String.class)
- .setDescription("Name of table to use for tracking change history")
- .setDefaultValue("DATABASECHANGELOG");
+ /**
+ * @deprecated
+ */
+ public static final String DATABASECHANGELOG_TABLE_NAME = liquibase.GlobalConfiguration.DATABASECHANGELOG_TABLE_NAME.getKey();
- getContainer().addProperty(DATABASECHANGELOGLOCK_TABLE_NAME, String.class)
- .setDescription("Name of table to use for tracking concurrent Liquibase usage")
- .setDefaultValue("DATABASECHANGELOGLOCK");
+ /**
+ * @deprecated
+ */
+ public static final String DATABASECHANGELOGLOCK_TABLE_NAME = liquibase.GlobalConfiguration.DATABASECHANGELOGLOCK_TABLE_NAME.getKey();
- getContainer().addProperty(CHANGELOGLOCK_WAIT_TIME, Long.class)
- .setDescription("Number of minutes to wait for the changelog lock to be available before giving up")
- .setDefaultValue(5);
+ /**
+ * @deprecated
+ */
+ public static final String LIQUIBASE_TABLESPACE_NAME = liquibase.GlobalConfiguration.LIQUIBASE_TABLESPACE_NAME.getKey();
- getContainer().addProperty(CHANGELOGLOCK_POLL_RATE, Long.class)
- .setDescription("Number of seconds wait between checks to the changelog lock when it is locked")
- .setDefaultValue(10);
+ /**
+ * @deprecated
+ */
+ public static final String LIQUIBASE_CATALOG_NAME = liquibase.GlobalConfiguration.LIQUIBASE_CATALOG_NAME.getKey();
- getContainer().addProperty(LIQUIBASE_TABLESPACE_NAME, String.class)
- .setDescription("Tablespace to use for Liquibase objects");
+ /**
+ * @deprecated
+ */
+ public static final String LIQUIBASE_SCHEMA_NAME = liquibase.GlobalConfiguration.LIQUIBASE_SCHEMA_NAME.getKey();
- getContainer().addProperty(LIQUIBASE_CATALOG_NAME, String.class)
- .setDescription("Catalog to use for Liquibase objects");
+ /**
+ * @deprecated
+ */
+ public static final String OUTPUT_LINE_SEPARATOR = liquibase.GlobalConfiguration.OUTPUT_LINE_SEPARATOR.getKey();
- getContainer().addProperty(LIQUIBASE_SCHEMA_NAME, String.class)
- .setDescription("Schema to use for Liquibase objects");
+ /**
+ * @deprecated
+ */
+ public static final String OUTPUT_ENCODING = liquibase.GlobalConfiguration.OUTPUT_ENCODING.getKey();
- getContainer().addProperty(OUTPUT_LINE_SEPARATOR, String.class)
- .setDescription("Line separator for output. Defaults to OS default")
- .setDefaultValue(System.getProperty("line.separator"));
+ /**
+ * @deprecated
+ */
+ public static final String CHANGELOGLOCK_WAIT_TIME = liquibase.GlobalConfiguration.CHANGELOGLOCK_WAIT_TIME.getKey();
- getContainer().addProperty(OUTPUT_ENCODING, String.class)
- .setDescription("Encoding to output text in. Defaults to file.encoding system property or UTF-8")
- .setDefaultValue("UTF-8")
- .addAlias("file.encoding");
+ /**
+ * @deprecated
+ */
+ public static final String CHANGELOGLOCK_POLL_RATE = liquibase.GlobalConfiguration.CHANGELOGLOCK_POLL_RATE.getKey();
- getContainer().addProperty(CONVERT_DATA_TYPES, Boolean.class)
- .setDescription("Should Liquibase convert to/from STANDARD data types. Applies to both snapshot and " +
- "update commands.")
- .setDefaultValue(true);
+ /**
+ * @deprecated
+ */
+ public static final String CONVERT_DATA_TYPES = liquibase.GlobalConfiguration.CONVERT_DATA_TYPES.getKey();
- getContainer().addProperty(GENERATE_CHANGESET_CREATED_VALUES, Boolean.class)
- .setDescription("Should Liquibase include a 'created' attribute in diff/generateChangeLog changeSets with" +
- " the current datetime")
- .setDefaultValue(false);
+ /**
+ * @deprecated
+ */
+ public static final String GENERATE_CHANGESET_CREATED_VALUES = liquibase.GlobalConfiguration.GENERATE_CHANGESET_CREATED_VALUES.getKey();
- getContainer().addProperty(AUTO_REORG, Boolean.class)
- .setDescription("Should Liquibase automatically include REORG TABLE commands when needed?")
- .setDefaultValue(true);
+ /**
+ * @deprecated
+ */
+ public static final String AUTO_REORG = liquibase.GlobalConfiguration.AUTO_REORG.getKey();
- getContainer().addProperty(DIFF_COLUMN_ORDER, Boolean.class)
- .setDescription("Should Liquibase compare column order in diff operation?")
- .setDefaultValue(true);
+ /**
+ * @deprecated
+ */
+ public static final String DIFF_COLUMN_ORDER = liquibase.GlobalConfiguration.DIFF_COLUMN_ORDER.getKey();
- getContainer().addProperty(ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA, Boolean.class)
- .setDescription("When generating SQL for createProcedure, should the procedure schema be forced to the default schema if no schemaName attribute is set?")
- .setDefaultValue(false);
+ /**
+ * @deprecated
+ */
+ public static final String ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA = liquibase.GlobalConfiguration.ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA.getKey();
+ /**
+ * @deprecated
+ */
+ public static final String GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION = liquibase.GlobalConfiguration.GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION.getKey();
- getContainer().addProperty(GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION, Boolean.class)
- .setDescription("Should Liquibase include the change description in the id when generating changeSets?")
- .setDefaultValue(false);
+ /**
+ * @deprecated
+ */
+ public static final String INCLUDE_CATALOG_IN_SPECIFICATION = liquibase.GlobalConfiguration.INCLUDE_CATALOG_IN_SPECIFICATION.getKey();
- getContainer().addProperty(INCLUDE_CATALOG_IN_SPECIFICATION, Boolean.class)
- .setDescription("Should Liquibase include the catalog name when determining equality?")
- .setDefaultValue(false);
+ /**
+ * @deprecated
+ */
+ public static final String SHOULD_SNAPSHOT_DATA = liquibase.GlobalConfiguration.SHOULD_SNAPSHOT_DATA.getKey();
- getContainer().addProperty(SHOULD_SNAPSHOT_DATA, Boolean.class)
- .setDescription("Should Liquibase snapshot data by default?")
- .setDefaultValue(false);
+ /**
+ * @deprecated
+ */
+ public static final String FILTER_LOG_MESSAGES = liquibase.GlobalConfiguration.FILTER_LOG_MESSAGES.getKey();
- getContainer().addProperty(FILTER_LOG_MESSAGES, Boolean.class)
- .setDescription("Should Liquibase filter log messages for potentially insecure data?")
- .setDefaultValue(true);
+ /**
+ * @deprecated
+ */
+ public static final String HEADLESS = liquibase.GlobalConfiguration.HEADLESS.getKey();
- getContainer().addProperty(HEADLESS, Boolean.class)
- .setDescription("Force liquibase think it has no access to a keyboard?")
- .setDefaultValue(false);
+ private static final AbstractConfigurationContainer.DelegatedConfigurationContainer containerDelegate = new AbstractConfigurationContainer.DelegatedConfigurationContainer("liquibase");
+ public GlobalConfiguration() {
}
/**
* Should Liquibase execute
+ * @deprecated
*/
public boolean getShouldRun() {
- return getContainer().getValue(SHOULD_RUN, Boolean.class);
+ return liquibase.GlobalConfiguration.SHOULD_RUN.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setShouldRun(boolean shouldRun) {
- getContainer().setValue(SHOULD_RUN, shouldRun);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.SHOULD_RUN, shouldRun);
return this;
}
/**
* Table name to use for DATABASECHANGELOG
+ * @deprecated
*/
public String getDatabaseChangeLogTableName() {
- return getContainer().getValue(DATABASECHANGELOG_TABLE_NAME, String.class);
+ return liquibase.GlobalConfiguration.DATABASECHANGELOG_TABLE_NAME.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setDatabaseChangeLogTableName(String name) {
- getContainer().setValue(DATABASECHANGELOG_TABLE_NAME, name);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.DATABASECHANGELOG_TABLE_NAME, name);
return this;
}
/**
* Table name to use for DATABASECHANGELOGLOCK
+ * @deprecated
*/
public String getDatabaseChangeLogLockTableName() {
- return getContainer().getValue(DATABASECHANGELOGLOCK_TABLE_NAME, String.class);
+ return liquibase.GlobalConfiguration.DATABASECHANGELOGLOCK_TABLE_NAME.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setDatabaseChangeLogLockTableName(String name) {
- getContainer().setValue(DATABASECHANGELOGLOCK_TABLE_NAME, name);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.DATABASECHANGELOGLOCK_TABLE_NAME, name);
return this;
}
/**
* Wait time (in minutes) to wait to receive the changelog lock before giving up.
+ * @deprecated
*/
public Long getDatabaseChangeLogLockWaitTime() {
- return getContainer().getValue(CHANGELOGLOCK_WAIT_TIME, Long.class);
+ return liquibase.GlobalConfiguration.CHANGELOGLOCK_WAIT_TIME.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setDatabaseChangeLogLockWaitTime(Long minutes) {
- getContainer().setValue(CHANGELOGLOCK_WAIT_TIME, minutes);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.CHANGELOGLOCK_WAIT_TIME, minutes);
return this;
}
/**
* Wait time (in seconds) between polling requests to the changelog lock system.
+ * @deprecated
*/
public Long getDatabaseChangeLogLockPollRate() {
- return getContainer().getValue(CHANGELOGLOCK_POLL_RATE, Long.class);
+ return liquibase.GlobalConfiguration.CHANGELOGLOCK_POLL_RATE.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setDatabaseChangeLogLockPollRate(Long seconds) {
- getContainer().setValue(CHANGELOGLOCK_POLL_RATE, seconds);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.CHANGELOGLOCK_POLL_RATE, seconds);
return this;
}
/**
* Name of the tablespace to use for liquibase database objects
+ * @deprecated
*/
public String getLiquibaseTablespaceName() {
- return getContainer().getValue(LIQUIBASE_TABLESPACE_NAME, String.class);
+ return liquibase.GlobalConfiguration.LIQUIBASE_TABLESPACE_NAME.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setLiquibaseTablespaceName(String name) {
- getContainer().setValue(LIQUIBASE_TABLESPACE_NAME, name);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.LIQUIBASE_TABLESPACE_NAME, name);
return this;
}
/**
- *
* Should Liquibase snapshot data for table by default
- *
+ * @deprecated
*/
public boolean getShouldSnapshotData() {
- return getContainer().getValue(SHOULD_SNAPSHOT_DATA, Boolean.class);
+ return liquibase.GlobalConfiguration.SHOULD_SNAPSHOT_DATA.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setShouldSnapshotData(boolean shouldSnapshotData) {
- getContainer().setValue(SHOULD_SNAPSHOT_DATA, shouldSnapshotData);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.SHOULD_SNAPSHOT_DATA, shouldSnapshotData);
return this;
}
+ /**
+ * @deprecated
+ */
public boolean getShouldFilterLogMessages() {
- return getContainer().getValue(FILTER_LOG_MESSAGES, Boolean.class);
+ return liquibase.GlobalConfiguration.FILTER_LOG_MESSAGES.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setShouldFilterLogMessages(boolean filter) {
- getContainer().setValue(FILTER_LOG_MESSAGES, filter);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.FILTER_LOG_MESSAGES, filter);
return this;
}
+ /**
+ * @deprecated
+ */
public boolean getHeadless() {
- return getContainer().getValue(HEADLESS, Boolean.class);
+ return liquibase.GlobalConfiguration.HEADLESS.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setHeadless(boolean headless) {
- getContainer().setValue(HEADLESS, headless);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.HEADLESS, headless);
return this;
}
@@ -221,11 +260,12 @@ public GlobalConfiguration setHeadless(boolean headless) {
* Name of the catalog to use for liquibase database objects
*/
public String getLiquibaseCatalogName() {
- return getContainer().getValue(LIQUIBASE_CATALOG_NAME, String.class);
+ return liquibase.GlobalConfiguration.LIQUIBASE_CATALOG_NAME.getCurrentValue();
+
}
public GlobalConfiguration setLiquibaseCatalogName(String name) {
- getContainer().setValue(LIQUIBASE_CATALOG_NAME, name);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.LIQUIBASE_CATALOG_NAME, name);
return this;
}
@@ -233,64 +273,119 @@ public GlobalConfiguration setLiquibaseCatalogName(String name) {
* Name of the schema to use for liquibase database objects
*/
public String getLiquibaseSchemaName() {
- return getContainer().getValue(LIQUIBASE_SCHEMA_NAME, String.class);
+ return liquibase.GlobalConfiguration.LIQUIBASE_SCHEMA_NAME.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setLiquibaseSchemaName(String name) {
- getContainer().setValue(LIQUIBASE_SCHEMA_NAME, name);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.LIQUIBASE_SCHEMA_NAME, name);
return this;
}
/**
* Line separator to use in output
+ * @deprecated
*/
public String getOutputLineSeparator() {
- return getContainer().getValue(OUTPUT_LINE_SEPARATOR, String.class);
+ return liquibase.GlobalConfiguration.OUTPUT_LINE_SEPARATOR.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setOutputLineSeparator(String name) {
- getContainer().setValue(OUTPUT_LINE_SEPARATOR, name);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.OUTPUT_LINE_SEPARATOR, name);
return this;
}
/**
* String encoding to use in output.
+ * @deprecated
*/
public String getOutputEncoding() {
- return getContainer().getValue(OUTPUT_ENCODING, String.class);
+ return liquibase.GlobalConfiguration.OUTPUT_ENCODING.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setOutputEncoding(String name) {
- getContainer().setValue(OUTPUT_ENCODING, name);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.OUTPUT_ENCODING, name);
return this;
}
+ /**
+ * @deprecated
+ */
public Boolean getDiffColumnOrder() {
- return getContainer().getValue(DIFF_COLUMN_ORDER, Boolean.class);
+ return liquibase.GlobalConfiguration.DIFF_COLUMN_ORDER.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setDiffColumnOrder(boolean diff) {
- getContainer().setValue(DIFF_COLUMN_ORDER, diff);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.DIFF_COLUMN_ORDER, diff);
return this;
}
+ /**
+ * @deprecated
+ */
public Boolean getAlwaysOverrideStoredLogicSchema() {
- return getContainer().getValue(ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA, Boolean.class);
+ return liquibase.GlobalConfiguration.ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA.getCurrentValue();
+
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setAlwaysOverrideStoredLogicSchema(boolean override) {
- getContainer().setValue(ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA, override);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.ALWAYS_OVERRIDE_STORED_LOGIC_SCHEMA, override);
return this;
}
+ /**
+ * @deprecated
+ */
public Boolean getGeneratedChangeSetIdsContainDescription() {
- return getContainer().getValue(GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION, Boolean.class);
+ return liquibase.GlobalConfiguration.GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION.getCurrentValue();
}
+ /**
+ * @deprecated
+ */
public GlobalConfiguration setGeneratedChangeSetIdsContainDescription(boolean containDescription) {
- getContainer().setValue(GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION, containDescription);
+ DeprecatedConfigurationValueProvider.setData(liquibase.GlobalConfiguration.GENERATED_CHANGESET_IDS_INCLUDE_DESCRIPTION, containDescription);
return this;
}
+
+ @Override
+ public ConfigurationProperty getProperty(String propertyName) {
+ return containerDelegate.getProperty(propertyName);
+ }
+
+ @Override
+ public Set getProperties() {
+ return containerDelegate.getProperties();
+ }
+
+ @Override
+ public T getValue(String propertyName, Class returnType) {
+ return containerDelegate.getValue(propertyName, returnType);
+ }
+
+ @Override
+ public void setValue(String propertyName, Object value) {
+ containerDelegate.setValue(propertyName, value);
+ }
+
+ @Override
+ public String getNamespace() {
+ return containerDelegate.getNamespace();
+ }
}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/HubConfiguration.java b/liquibase-core/src/main/java/liquibase/configuration/HubConfiguration.java
deleted file mode 100644
index e7027737cc1..00000000000
--- a/liquibase-core/src/main/java/liquibase/configuration/HubConfiguration.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package liquibase.configuration;
-
-import liquibase.Scope;
-import liquibase.util.StringUtil;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.logging.Level;
-
-/**
- * Configuration container for global properties.
- */
-public class HubConfiguration extends AbstractConfigurationContainer {
-
- public static final String LIQUIBASE_HUB_API_KEY = "apiKey";
- public static final String LIQUIBASE_HUB_URL = "url";
- public static final String LIQUIBASE_HUB_MODE = "mode";
- public static final String LIQUIBASE_HUB_LOGLEVEL = "logLevel";
-
- public HubConfiguration() {
- super("liquibase.hub");
-
- getContainer().addProperty(LIQUIBASE_HUB_API_KEY, String.class)
- .setDescription("Liquibase Hub API key for operations");
- getContainer().addProperty(LIQUIBASE_HUB_URL, String.class)
- .setDescription("Liquibase Hub URL for operations")
- .setValueHandler(value -> {
- if (value == null) {
- return null;
- }
- return value.toString().replaceFirst("(https?://[^/]+).*", "$1");
- });
- getContainer().addProperty(LIQUIBASE_HUB_MODE, String.class)
- .setDescription("Content to send to Liquibase Hub during operations. Values can be 'all', 'meta', or 'off'")
- .setDefaultValue("all");
- getContainer().addProperty(LIQUIBASE_HUB_LOGLEVEL, String.class)
- .setDescription("Log level for filtering log messages to send to Liquibase Hub during operations. Values can be any acceptable log level.")
- .setDefaultValue("INFO");
- }
-
- @Override
- public void setValue(String propertyName, Object value) {
- super.setValue(propertyName, value);
- }
-
- /**
- * Output {@link #getLiquibaseHubApiKey()} but in a way that is secure for message output.
- *
- */
- public String getLiquibaseHubApiKeySecureDescription() {
- final String key = getContainer().getValue(LIQUIBASE_HUB_API_KEY, String.class);
- if (key == null) {
- return null;
- }
- return key.substring(0,6) + "************";
- }
-
- public String getLiquibaseHubApiKey() {
- return getContainer().getValue(LIQUIBASE_HUB_API_KEY, String.class);
- }
-
- public HubConfiguration setLiquibaseHubApiKey(String liquibaseHubApiKey) {
- getContainer().setValue(LIQUIBASE_HUB_API_KEY, liquibaseHubApiKey);
- return this;
- }
-
- public HubConfiguration setLiquibaseHubUrl(String liquibaseHubUrl) {
- getContainer().setValue(LIQUIBASE_HUB_URL, liquibaseHubUrl);
- return this;
- }
-
- public String getLiquibaseHubUrl() {
- String hubUrl = getContainer().getValue(LIQUIBASE_HUB_URL, String.class);
- if (hubUrl == null || hubUrl.isEmpty()) {
- return "https://hub.liquibase.com";
- }
- return hubUrl;
- }
-
- public HubConfiguration setLiquibaseHubMode(String liquibaseHubMode) {
- getContainer().setValue(LIQUIBASE_HUB_MODE, liquibaseHubMode);
- return this;
- }
-
- public String getLiquibaseHubMode() {
- final String value = getContainer().getValue(LIQUIBASE_HUB_MODE, String.class);
-
- final List validValues = Arrays.asList("off", "meta", "all");
- if (!validValues.contains(value.toLowerCase())) {
- throw new RuntimeException(" An invalid liquibase.hub.mode value of "+value+" detected. Acceptable values are "+StringUtil.join(validValues, ", "));
- }
- return value;
- }
-
- public HubConfiguration setLiquibaseHubLogLevel(String liquibaseHubLogLevel) {
- getContainer().setValue(LIQUIBASE_HUB_LOGLEVEL, liquibaseHubLogLevel);
- return this;
- }
-
- public String getLiquibaseHubLogLevel() {
- String value = getContainer().getValue(LIQUIBASE_HUB_LOGLEVEL, String.class);
-
- final List validValues = Arrays.asList("OFF", "FINE", "WARN", "ERROR", "INFO");
- if (!validValues.contains(value.toUpperCase())) {
- Level logLevel = Level.INFO;
- try {
- logLevel = Level.parse(value.toUpperCase());
- } catch (IllegalArgumentException e) {
- String message = "An invalid liquibase.hub.logLevel value of " + value + " detected. Acceptable values are " + StringUtil.join(validValues, ",");
- Scope.getCurrentScope().getLog(HubConfiguration.class).warning(message);
- }
- value = logLevel.toString();
- }
- return value;
- }
-
-}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/LiquibaseConfiguration.java b/liquibase-core/src/main/java/liquibase/configuration/LiquibaseConfiguration.java
index b623258de18..ffe4b2c3c2f 100644
--- a/liquibase-core/src/main/java/liquibase/configuration/LiquibaseConfiguration.java
+++ b/liquibase-core/src/main/java/liquibase/configuration/LiquibaseConfiguration.java
@@ -1,184 +1,167 @@
package liquibase.configuration;
+import liquibase.Scope;
+import liquibase.SingletonObject;
import liquibase.exception.UnexpectedLiquibaseException;
-import liquibase.util.StringUtil;
+import liquibase.servicelocator.ServiceLocator;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
/**
* Provides unified management of configuration properties within Liquibase core and in extensions.
*
- * This class is the top level container used to access {@link ConfigurationContainer} implementations which contain
- * the actual configuration properties.
- * Normal use is to call
- * LiquibaseConfiguration.getInstance().getConfiguration(NEEDED_CONFIGURATION.class).getYOUR_PROPERTY()
+ * Because this class focuses on raw/untyped access to what is actually configured, it is usually best to interact with {@link ConfigurationDefinition} instances
+ * which provide type safety, standardized key naming, default values, and more.
+ *
+ * "Registered" configuration definitions will be available for generated help.
+ *
+ * This class will search through the configured {@link ConfigurationValueProvider}s. Standard value providers are auto-loaded on startup, but more can be added/removed at runtime.
*
- * This class is implemented as a singleton with a single global set of configuration objects, but the
- * {@link #setInstance(LiquibaseConfiguration)} method can be used to replace
- * the singleton with an alternate implementation that uses ThreadLocal objects or any other way of managing
- * configurations.
*/
-public class LiquibaseConfiguration {
-
- private Map configurations;
+public class LiquibaseConfiguration implements SingletonObject {
- private ConfigurationValueProvider[] configurationValueProviders;
-
- private static LiquibaseConfiguration instance;
+ private final SortedSet configurationValueProviders;
+ private final SortedSet definitions = new TreeSet<>();
/**
- * Returns the singleton instance, creating it if necessary. On creation, the configuration is initialized with {@link liquibase.configuration.SystemPropertyProvider}
+ * Track looked up values we have logged to avoid infinite loops between this and the log system using configurations
+ * and to limit logged messages.
+ * Only re-log when values changed from the last time they were logged.
*/
- public static synchronized LiquibaseConfiguration getInstance() {
- if (instance == null) {
- instance = new LiquibaseConfiguration();
- instance.init(new SystemPropertyProvider());
- }
+ private final Map lastLoggedKeyValues = new HashMap<>();
- return instance;
- }
+ protected LiquibaseConfiguration() {
+ configurationValueProviders = new TreeSet<>((o1, o2) -> {
+ if (o1.getPrecedence() < o2.getPrecedence()) {
+ return -1;
+ } else if (o1.getPrecedence() > o2.getPrecedence()) {
+ return 1;
+ }
- /**
- * Overrides the standard singleton instance created by getInstance().
- * Useful for alternate implementations with more complex AbstractConfigurationContainer lookup logic such
- * as different configurations per thread.
- */
- public static synchronized void setInstance(LiquibaseConfiguration instance) {
- LiquibaseConfiguration.instance = instance;
- }
+ return o1.getClass().getName().compareTo(o2.getClass().getName());
+ });
+ }
/**
- * Constructor protected to prevent construction outside getInstance()
+ * @deprecated use {@link Scope#getSingleton(Class)}
*/
- protected LiquibaseConfiguration() {
+ public static LiquibaseConfiguration getInstance() {
+ return Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class);
}
-
/**
- * Re-initialize the configuration with the given ConfigurationProviders. Any existing
- * AbstractConfigurationContainer instances are reset to defaults.
+ * Finishes configuration of this service. Called as the root scope is set up, should not be called elsewhere.
*/
- public void init(ConfigurationValueProvider... configurationValueProviders) {
- if (configurationValueProviders == null) {
- configurationValueProviders = new ConfigurationValueProvider[0];
+ public void init(Scope scope) {
+ configurationValueProviders.clear();
+ ServiceLocator serviceLocator = scope.getServiceLocator();
+ final List containers = serviceLocator.findInstances(AutoloadedConfigurations.class);
+ for (AutoloadedConfigurations container : containers) {
+ Scope.getCurrentScope().getLog(getClass()).fine("Found ConfigurationDefinitions in " + container.getClass().getName());
}
- this.configurationValueProviders = configurationValueProviders;
- this.reset();
+ configurationValueProviders.addAll(serviceLocator.findInstances(ConfigurationValueProvider.class));
}
/**
- * Resets existing AbstractConfigurationContainer instances to their default values.
+ * Adds a new {@link ConfigurationValueProvider} to the active collection of providers.
*/
- public void reset() {
- this.configurations = new HashMap<>();
+ public void registerProvider(ConfigurationValueProvider valueProvider) {
+ this.configurationValueProviders.add(valueProvider);
}
+ /**
+ * Removes a specific {@link ConfigurationValueProvider} from the active collection of providers.
+ *
+ * @return true if the provider was removed.
+ */
+ public boolean removeProvider(ConfigurationValueProvider provider) {
+ return this.configurationValueProviders.remove(provider);
+ }
/**
- * Return an instance of the passed AbstractConfigurationContainer type.
- * The same instance is returned from every call to getConfiguration()
+ * @deprecated use {@link ConfigurationDefinition} instances directly
*/
public T getConfiguration(Class type) {
- if (!configurations.containsKey(type)) {
- configurations.put(type, createConfiguration(type));
+ try {
+ return type.newInstance();
+ } catch (Throwable e) {
+ throw new UnexpectedLiquibaseException(e);
}
-
- return (T) configurations.get(type);
}
+
/**
+ * Searches for the given key in the current providers.
*
- * Return an instance of the passed ConfigurationContainer type
- * The typeName can be the name of a class or the namespace associated with the container
- *
- * @param typeName
- * @return ConfigurationContainer
- *
+ * @return the value for the key, or null if not configured.
*/
- public ConfigurationContainer getConfiguration(String typeName) {
- //
- // Lookup by class name
- //
- for (Map.Entry entry : configurations.entrySet()) {
- if (entry.getKey().getName().equals(typeName)) {
- return entry.getValue();
+ public ConfiguredValue> getCurrentConfiguredValue(String key) {
+ ConfiguredValue> details = new ConfiguredValue<>(key);
+
+ for (ConfigurationValueProvider provider : configurationValueProviders) {
+ final ProvidedValue providerValue = provider.getProvidedValue(key);
+
+ if (providerValue != null) {
+ details.override(providerValue);
}
}
- //
- // Lookup by namespace
- //
- for (ConfigurationContainer container : configurations.values()) {
- String namespace = container.getNamespace();
- if (namespace.equalsIgnoreCase(typeName)) {
- return container;
+ final String foundValue = String.valueOf(details.getValue());
+ if (!foundValue.equals(lastLoggedKeyValues.get(key))) {
+ lastLoggedKeyValues.put(key, foundValue);
+
+ //avoid infinite loop when logging is getting set up
+ StringBuilder logMessage = new StringBuilder("Found '" + key + "' configuration of '"+foundValue+"'");
+ boolean foundFirstValue = false;
+ for (ProvidedValue providedValue : details.getProvidedValues()) {
+ logMessage.append("\n ");
+ if (foundFirstValue) {
+ logMessage.append("Overrides ");
+ }
+ logMessage.append(providedValue.describe());
+ final Object value = providedValue.getValue();
+ if (value != null) {
+ logMessage.append(" of '").append(providedValue.getValue()).append("'");
+ }
+ foundFirstValue = true;
}
- }
- //
- // Instantiate and put in the map
- //
- try {
- Class typeClass = Class.forName(typeName);
- configurations.put(typeClass, createConfiguration(typeClass));
- return configurations.get(typeClass);
- } catch (Exception e) {
- throw new UnexpectedLiquibaseException(e);
+ Scope.getCurrentScope().getLog(getClass()).fine(logMessage.toString());
}
+
+ return details;
}
/**
- * Convenience method for liquibaseConfiguration.getConfiguration(type).getProperty(property)
+ * Registers a {@link ConfigurationDefinition} so it will be returned by {@link #getRegisteredDefinitions()}
*/
- public ConfigurationProperty getProperty(Class extends ConfigurationContainer> type, String property) {
- ConfigurationContainer configuration = getConfiguration(type);
- return configuration.getProperty(property);
- }
-
- protected T createConfiguration(Class type) {
- try {
- T configuration = type.getConstructor().newInstance();
- configuration.init(configurationValueProviders);
- return configuration;
- } catch (Exception e) {
- throw new UnexpectedLiquibaseException("Cannot create default configuration "+type.getName(), e);
- }
+ public void registerDefinition(ConfigurationDefinition definition) {
+ this.definitions.add(definition);
}
/**
- * Convenience method for {@link #describeValueLookupLogic(ConfigurationProperty)}
+ * Returns all registered {@link ConfigurationDefinition}s. Registered definitions are used for generated help documentation.
*/
- public String describeValueLookupLogic(Class extends ConfigurationContainer> config, String property) {
- return describeValueLookupLogic(getProperty(config, property));
+ public SortedSet getRegisteredDefinitions() {
+ return Collections.unmodifiableSortedSet(this.definitions);
}
/**
- * Generates a human consumable description of how the configured ConfigurationValueProvider(s) will
- * attempt to set a default value.
+ * @return the registered {@link ConfigurationDefinition} asssociated with this key. Null if none match.
*/
- public String describeValueLookupLogic(ConfigurationProperty property) {
- List reasons = new ArrayList<>();
- for (ConfigurationValueProvider container : configurationValueProviders) {
- reasons.add(container.describeValueLookupLogic(property));
+ public ConfigurationDefinition getRegisteredDefinition(String key) {
+ for (ConfigurationDefinition def : getRegisteredDefinitions()) {
+ if (def.getKey().equalsIgnoreCase(key)) {
+ return def;
+ }
+ final Set aliasKeys = def.getAliasKeys();
+ if (aliasKeys != null && aliasKeys.contains(def.getKey())) {
+ return def;
+ }
}
- return StringUtil.join(reasons, " AND ");
- }
-
- /**
- * Convenience method to check if the object types should consider catalog name
- * also during comparision (equals(), hashcode() and compareTo())
- *
- * @return
- */
- public boolean shouldIncludeCatalogInSpecification() {
- Boolean includeCatalog = getConfiguration(GlobalConfiguration.class)
- .getValue(GlobalConfiguration.INCLUDE_CATALOG_IN_SPECIFICATION, Boolean.class);
- return includeCatalog != null ? includeCatalog : false;
+ return null;
}
}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/ProvidedValue.java b/liquibase-core/src/main/java/liquibase/configuration/ProvidedValue.java
new file mode 100644
index 00000000000..35426dd8f2b
--- /dev/null
+++ b/liquibase-core/src/main/java/liquibase/configuration/ProvidedValue.java
@@ -0,0 +1,61 @@
+package liquibase.configuration;
+
+/**
+ * Describes a value found from a provider. This is the most basic level at which a configuration value is defined.
+ */
+public class ProvidedValue {
+ private final String requestedKey;
+ private final String actualKey;
+ private final String sourceDescription;
+ private final ConfigurationValueProvider provider;
+ private final Object value;
+
+ public ProvidedValue(String requestedKey, String actualKey, Object value, String sourceDescription, ConfigurationValueProvider provider) {
+ this.requestedKey = requestedKey;
+ this.actualKey = actualKey;
+ this.value = value;
+ this.sourceDescription = sourceDescription;
+ this.provider = provider;
+ }
+
+ /**
+ * The value found by the provider.
+ */
+ public Object getValue() {
+ return value;
+ }
+
+ /**
+ * The configuration key the code asked the provider for.
+ * May be different than {@link #getActualKey()} if the provider does fuzzy matching such as case-insensitive lookups or . -> _ conversions etc.
+ */
+ public String getRequestedKey() {
+ return requestedKey;
+ }
+
+ /**
+ * The actual key/source for the value.
+ * This may be different than {@link #getRequestedKey()} if the provider does fuzzy matching such as case-insensitive lookups or . -> _ conversions etc.
+ */
+ public String getActualKey() {
+ return actualKey;
+ }
+
+ /**
+ * A description of where the value came from.
+ */
+ public String getSourceDescription() {
+ return sourceDescription;
+ }
+
+ /**
+ * The provider for this value
+ */
+ public ConfigurationValueProvider getProvider() {
+ return provider;
+ }
+
+ public String describe() {
+ return sourceDescription + " '" + actualKey + "'";
+ }
+}
diff --git a/liquibase-core/src/main/java/liquibase/configuration/SystemPropertyProvider.java b/liquibase-core/src/main/java/liquibase/configuration/SystemPropertyProvider.java
deleted file mode 100644
index 91ffc63a74e..00000000000
--- a/liquibase-core/src/main/java/liquibase/configuration/SystemPropertyProvider.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package liquibase.configuration;
-
-import liquibase.util.StringUtil;
-
-import java.util.Map;
-import java.util.Properties;
-
-/**
- * A ConfigurationValueProvider implementation that looks for overriding values in system properties.
- * Looks for system properties in the format "NAMESPACE.PROPERTY_NAME".
- */
-public class SystemPropertyProvider implements ConfigurationValueProvider {
-
- @Override
- public Object getValue(String namespace, String property) {
- String propValue = System.getProperty(namespace +"."+property);
- if (StringUtil.isNotEmpty(propValue)) {
- return propValue;
- }
-
- //
- // Not matching with the actual key then try case insensitive
- //
- Properties properties = System.getProperties();
- for (Map.Entry
Generates a changelog based on the current database schema. Typically used when
* beginning to use Liquibase on an existing project and database schema.