package liquibase.configuration; import liquibase.exception.UnexpectedLiquibaseException; import liquibase.util.StringUtils; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * Provides unified management of configuration properties within Liquibase core and in extensions. * <p> * 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() * <p> * 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<Class, ConfigurationContainer> configurations; private ConfigurationValueProvider[] configurationValueProviders; private static LiquibaseConfiguration instance; /** * Returns the singleton instance, creating it if necessary. On creation, the configuration is initialized with {@link liquibase.configuration.SystemPropertyProvider} */ public static synchronized LiquibaseConfiguration getInstance() { if (instance == null) { instance = new LiquibaseConfiguration(); instance.init(new SystemPropertyProvider()); } return instance; } /** * 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 void setInstance(LiquibaseConfiguration instance) { LiquibaseConfiguration.instance = instance; } /** * Constructor protected to prevent construction outside getInstance() */ protected LiquibaseConfiguration() { } /** * Re-initialize the configuration with the given ConfigurationProviders. Any existing AbstractConfigurationContainer instances are reset to * defaults. */ public void init(ConfigurationValueProvider... configurationValueProviders) { if (configurationValueProviders == null) { configurationValueProviders = new ConfigurationValueProvider[0]; } this.configurationValueProviders = configurationValueProviders; this.reset(); } /** * Resets existing AbstractConfigurationContainer instances to their default values. */ public void reset() { this.configurations = new HashMap<Class, ConfigurationContainer>(); } /** * Return an instance of the passed AbstractConfigurationContainer type. * The same instance is returned from every call to getConfiguration() */ public <T extends ConfigurationContainer> T getConfiguration(Class<T> type) { if (!configurations.containsKey(type)) { configurations.put(type, createConfiguration(type)); } return (T) configurations.get(type); } public ConfigurationContainer getConfiguration(String typeName) { for (Map.Entry<Class, ConfigurationContainer> entry : configurations.entrySet()) { if (entry.getKey().getName().equals(typeName)) { return entry.getValue(); } } try { Class typeClass = Class.forName(typeName); configurations.put(typeClass, createConfiguration(typeClass)); return configurations.get(typeClass); } catch (Exception e) { throw new UnexpectedLiquibaseException(e); } } /** * Convenience method for liquibaseConfiguration.getConfiguration(type).getProperty(property) */ public ConfigurationProperty getProperty(Class<? extends ConfigurationContainer> type, String property) { ConfigurationContainer configuration = getConfiguration(type); return configuration.getProperty(property); } protected <T extends ConfigurationContainer> T createConfiguration(Class<T> type) { try { T configuration = type.newInstance(); configuration.init(configurationValueProviders); return configuration; } catch (Exception e) { throw new UnexpectedLiquibaseException("Cannot create default configuration "+type.getName(), e); } } /** * Convenience method for {@link #describeValueLookupLogic(ConfigurationProperty)} */ public String describeValueLookupLogic(Class<? extends ConfigurationContainer> config, String property) { return describeValueLookupLogic(getProperty(config, property)); } /** * Generates a human consumable description of how the configured ConfigurationValueProvider(s) will attempt to set a default value. */ public String describeValueLookupLogic(ConfigurationProperty property) { List<String> reasons = new ArrayList<String>(); for (ConfigurationValueProvider container : configurationValueProviders) { reasons.add(container.describeValueLookupLogic(property)); } return StringUtils.join(reasons, " AND "); } }