Environment.java

  1. /*
  2.  *    Copyright 2010-2023 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.migration;

  17. import java.io.File;
  18. import java.io.FileInputStream;
  19. import java.io.FileNotFoundException;
  20. import java.io.IOException;
  21. import java.nio.charset.Charset;
  22. import java.util.ArrayList;
  23. import java.util.Arrays;
  24. import java.util.Collections;
  25. import java.util.List;
  26. import java.util.Locale;
  27. import java.util.Map;
  28. import java.util.Properties;

  29. public class Environment {

  30.   public static final String CHANGELOG = "changelog";

  31.   private enum SETTING_KEY {
  32.     TIME_ZONE,

  33.     DELIMITER,

  34.     SCRIPT_CHAR_SET,

  35.     FULL_LINE_DELIMITER,

  36.     SEND_FULL_SCRIPT,

  37.     AUTO_COMMIT,

  38.     REMOVE_CRS,

  39.     IGNORE_WARNINGS,

  40.     DRIVER_PATH,

  41.     DRIVER,

  42.     URL,

  43.     USERNAME,

  44.     PASSWORD,

  45.     HOOK_BEFORE_UP,

  46.     HOOK_BEFORE_EACH_UP,

  47.     HOOK_AFTER_EACH_UP,

  48.     HOOK_AFTER_UP,

  49.     HOOK_BEFORE_DOWN,

  50.     HOOK_BEFORE_EACH_DOWN,

  51.     HOOK_AFTER_EACH_DOWN,

  52.     HOOK_AFTER_DOWN,

  53.     HOOK_BEFORE_NEW,

  54.     HOOK_AFTER_NEW,

  55.     HOOK_BEFORE_SCRIPT,

  56.     HOOK_BEFORE_EACH_SCRIPT,

  57.     HOOK_AFTER_EACH_SCRIPT,

  58.     HOOK_AFTER_SCRIPT;

  59.     @Override
  60.     public String toString() {
  61.       return this.name().toLowerCase(Locale.ENGLISH);
  62.     }
  63.   }

  64.   private static final List<String> SETTING_KEYS;

  65.   static {
  66.     ArrayList<String> list = new ArrayList<>();
  67.     SETTING_KEY[] keys = SETTING_KEY.values();
  68.     for (SETTING_KEY key : keys) {
  69.       list.add(key.toString());
  70.     }
  71.     SETTING_KEYS = Collections.unmodifiableList(list);
  72.   }

  73.   private final String timeZone;
  74.   private final String delimiter;
  75.   private final String scriptCharset;
  76.   private final boolean fullLineDelimiter;
  77.   private final boolean sendFullScript;
  78.   private final boolean autoCommit;
  79.   private final boolean removeCrs;
  80.   private final boolean ignoreWarnings;
  81.   private final String driverPath;
  82.   private final String driver;
  83.   private final String url;
  84.   private final String username;
  85.   private final String password;

  86.   private final String hookBeforeUp;
  87.   private final String hookBeforeEachUp;
  88.   private final String hookAfterEachUp;
  89.   private final String hookAfterUp;
  90.   private final String hookBeforeDown;
  91.   private final String hookBeforeEachDown;
  92.   private final String hookAfterEachDown;
  93.   private final String hookAfterDown;

  94.   private final String hookBeforeNew;
  95.   private final String hookAfterNew;

  96.   private final String hookBeforeScript;
  97.   private final String hookBeforeEachScript;
  98.   private final String hookAfterEachScript;
  99.   private final String hookAfterScript;

  100.   /**
  101.    * Prefix used to lookup environment variable or system property.
  102.    */
  103.   private static final String PREFIX = "MIGRATIONS_";
  104.   private final Map<String, String> envVars = System.getenv();
  105.   private final Properties sysProps = System.getProperties();
  106.   private final Properties variables = new Properties();

  107.   private final VariableReplacer parser = new VariableReplacer(Arrays.asList(sysProps, envVars));

  108.   public Environment(File file) {
  109.     Properties prop = mergeProperties(file);

  110.     this.timeZone = readProperty(prop, SETTING_KEY.TIME_ZONE.toString(), "GMT+0:00");
  111.     this.delimiter = readProperty(prop, SETTING_KEY.DELIMITER.toString(), ";");
  112.     this.scriptCharset = readProperty(prop, SETTING_KEY.SCRIPT_CHAR_SET.toString(),
  113.         Charset.defaultCharset().toString());
  114.     this.fullLineDelimiter = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.FULL_LINE_DELIMITER.toString()));
  115.     this.sendFullScript = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.SEND_FULL_SCRIPT.toString()));
  116.     this.autoCommit = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.AUTO_COMMIT.toString()));
  117.     this.removeCrs = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.REMOVE_CRS.toString()));
  118.     this.ignoreWarnings = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.IGNORE_WARNINGS.toString(), "true"));

  119.     this.driverPath = readProperty(prop, SETTING_KEY.DRIVER_PATH.toString());
  120.     this.driver = readProperty(prop, SETTING_KEY.DRIVER.toString());
  121.     this.url = readProperty(prop, SETTING_KEY.URL.toString());
  122.     this.username = readProperty(prop, SETTING_KEY.USERNAME.toString());
  123.     this.password = readProperty(prop, SETTING_KEY.PASSWORD.toString());

  124.     this.hookBeforeUp = readProperty(prop, SETTING_KEY.HOOK_BEFORE_UP.toString());
  125.     this.hookBeforeEachUp = readProperty(prop, SETTING_KEY.HOOK_BEFORE_EACH_UP.toString());
  126.     this.hookAfterEachUp = readProperty(prop, SETTING_KEY.HOOK_AFTER_EACH_UP.toString());
  127.     this.hookAfterUp = readProperty(prop, SETTING_KEY.HOOK_AFTER_UP.toString());
  128.     this.hookBeforeDown = readProperty(prop, SETTING_KEY.HOOK_BEFORE_DOWN.toString());
  129.     this.hookBeforeEachDown = readProperty(prop, SETTING_KEY.HOOK_BEFORE_EACH_DOWN.toString());
  130.     this.hookAfterEachDown = readProperty(prop, SETTING_KEY.HOOK_AFTER_EACH_DOWN.toString());
  131.     this.hookAfterDown = readProperty(prop, SETTING_KEY.HOOK_AFTER_DOWN.toString());

  132.     this.hookBeforeNew = readProperty(prop, SETTING_KEY.HOOK_BEFORE_NEW.toString());
  133.     this.hookAfterNew = readProperty(prop, SETTING_KEY.HOOK_AFTER_NEW.toString());

  134.     this.hookBeforeScript = readProperty(prop, SETTING_KEY.HOOK_BEFORE_SCRIPT.toString());
  135.     this.hookBeforeEachScript = readProperty(prop, SETTING_KEY.HOOK_BEFORE_EACH_SCRIPT.toString());
  136.     this.hookAfterEachScript = readProperty(prop, SETTING_KEY.HOOK_AFTER_EACH_SCRIPT.toString());
  137.     this.hookAfterScript = readProperty(prop, SETTING_KEY.HOOK_AFTER_SCRIPT.toString());

  138.     // User defined variables.
  139.     prop.entrySet().stream().filter(e -> !SETTING_KEYS.contains(e.getKey()))
  140.         .forEach(e -> variables.put(e.getKey(), parser.replace((String) e.getValue())));
  141.   }

  142.   private Properties mergeProperties(File file) {
  143.     // 1. Load from file.
  144.     Properties prop = loadPropertiesFromFile(file);
  145.     // 2. Read environment variables (existing entries are overwritten).
  146.     envVars.entrySet().stream().filter(e -> isMigrationsKey(e.getKey()))
  147.         .forEach(e -> prop.put(normalizeKey(e.getKey()), e.getValue()));
  148.     // 3. Read system properties (existing entries are overwritten).
  149.     sysProps.entrySet().stream().filter(e -> isMigrationsKey((String) e.getKey()))
  150.         .forEach(e -> prop.put(normalizeKey((String) e.getKey()), e.getValue()));
  151.     return prop;
  152.   }

  153.   private String normalizeKey(String key) {
  154.     return key.substring(PREFIX.length()).toLowerCase(Locale.ENGLISH);
  155.   }

  156.   private boolean isMigrationsKey(String key) {
  157.     return key.length() > PREFIX.length() && key.toUpperCase(Locale.ENGLISH).startsWith(PREFIX);
  158.   }

  159.   private Properties loadPropertiesFromFile(File file) {
  160.     Properties properties = new Properties();
  161.     try (FileInputStream inputStream = new FileInputStream(file)) {
  162.       properties.load(inputStream);
  163.       return properties;
  164.     } catch (FileNotFoundException e) {
  165.       throw new MigrationException("Environment file missing: " + file.getAbsolutePath());
  166.     } catch (IOException e) {
  167.       throw new MigrationException("Error loading environment properties.  Cause: " + e, e);
  168.     }
  169.   }

  170.   private String readProperty(Properties properties, String propertyKey) {
  171.     return readProperty(properties, propertyKey, null);
  172.   }

  173.   private String readProperty(Properties properties, String propertyKey, String defaultValue) {
  174.     String property = properties.getProperty(propertyKey, defaultValue);
  175.     return property == null ? null : parser.replace(property);
  176.   }

  177.   public String getTimeZone() {
  178.     return timeZone;
  179.   }

  180.   public String getDelimiter() {
  181.     return delimiter;
  182.   }

  183.   public String getScriptCharset() {
  184.     return scriptCharset;
  185.   }

  186.   public boolean isFullLineDelimiter() {
  187.     return fullLineDelimiter;
  188.   }

  189.   public boolean isSendFullScript() {
  190.     return sendFullScript;
  191.   }

  192.   public boolean isAutoCommit() {
  193.     return autoCommit;
  194.   }

  195.   public boolean isRemoveCrs() {
  196.     return removeCrs;
  197.   }

  198.   public boolean isIgnoreWarnings() {
  199.     return ignoreWarnings;
  200.   }

  201.   public String getDriverPath() {
  202.     return driverPath;
  203.   }

  204.   public String getDriver() {
  205.     return driver;
  206.   }

  207.   public String getUrl() {
  208.     return url;
  209.   }

  210.   public String getUsername() {
  211.     return username;
  212.   }

  213.   public String getPassword() {
  214.     return password;
  215.   }

  216.   public String getHookBeforeUp() {
  217.     return hookBeforeUp;
  218.   }

  219.   public String getHookBeforeEachUp() {
  220.     return hookBeforeEachUp;
  221.   }

  222.   public String getHookAfterEachUp() {
  223.     return hookAfterEachUp;
  224.   }

  225.   public String getHookAfterUp() {
  226.     return hookAfterUp;
  227.   }

  228.   public String getHookBeforeDown() {
  229.     return hookBeforeDown;
  230.   }

  231.   public String getHookBeforeEachDown() {
  232.     return hookBeforeEachDown;
  233.   }

  234.   public String getHookAfterEachDown() {
  235.     return hookAfterEachDown;
  236.   }

  237.   public String getHookAfterDown() {
  238.     return hookAfterDown;
  239.   }

  240.   public String getHookBeforeNew() {
  241.     return hookBeforeNew;
  242.   }

  243.   public String getHookAfterNew() {
  244.     return hookAfterNew;
  245.   }

  246.   public String getHookBeforeScript() {
  247.     return hookBeforeScript;
  248.   }

  249.   public String getHookBeforeEachScript() {
  250.     return hookBeforeEachScript;
  251.   }

  252.   public String getHookAfterEachScript() {
  253.     return hookAfterEachScript;
  254.   }

  255.   public String getHookAfterScript() {
  256.     return hookAfterScript;
  257.   }

  258.   public Properties getVariables() {
  259.     return variables;
  260.   }
  261. }