Skip to content

Commit 5848e54

Browse files
harawatah3adache
authored andcommitted
Read user defined variables from environment variables and system properties
Variables must be declared with a name with prefix (e.g. `MIGRATIONS_VAR1`). The name is converted to lower case (e.g. `MIGRATIONS_VAR1` -> `var1`).
1 parent 7eb9e4c commit 5848e54

File tree

4 files changed

+74
-48
lines changed

4 files changed

+74
-48
lines changed

src/main/java/org/apache/ibatis/migration/Environment.java

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -116,65 +116,78 @@ private enum SETTING_KEY {
116116
});
117117

118118
public Environment(File file) {
119+
Properties prop = mergeProperties(file);
120+
121+
this.timeZone = readProperty(prop, SETTING_KEY.time_zone.name(), "GMT+0:00");
122+
this.delimiter = readProperty(prop, SETTING_KEY.delimiter.name(), ";");
123+
this.scriptCharset = readProperty(prop, SETTING_KEY.script_char_set.name(), Charset.defaultCharset().name());
124+
this.fullLineDelimiter = Boolean.valueOf(readProperty(prop, SETTING_KEY.full_line_delimiter.name()));
125+
this.sendFullScript = Boolean.valueOf(readProperty(prop, SETTING_KEY.send_full_script.name()));
126+
this.autoCommit = Boolean.valueOf(readProperty(prop, SETTING_KEY.auto_commit.name()));
127+
this.removeCrs = Boolean.valueOf(readProperty(prop, SETTING_KEY.remove_crs.name()));
128+
this.ignoreWarnings = Boolean.valueOf(readProperty(prop, SETTING_KEY.ignore_warnings.name(), "true"));
129+
130+
this.driverPath = readProperty(prop, SETTING_KEY.driver_path.name());
131+
this.driver = readProperty(prop, SETTING_KEY.driver.name());
132+
this.url = readProperty(prop, SETTING_KEY.url.name());
133+
this.username = readProperty(prop, SETTING_KEY.username.name());
134+
this.password = readProperty(prop, SETTING_KEY.password.name());
135+
136+
this.hookBeforeUp = readProperty(prop, SETTING_KEY.hook_before_up.name());
137+
this.hookBeforeEachUp = readProperty(prop, SETTING_KEY.hook_before_each_up.name());
138+
this.hookAfterEachUp = readProperty(prop, SETTING_KEY.hook_after_each_up.name());
139+
this.hookAfterUp = readProperty(prop, SETTING_KEY.hook_after_up.name());
140+
this.hookBeforeDown = readProperty(prop, SETTING_KEY.hook_before_down.name());
141+
this.hookBeforeEachDown = readProperty(prop, SETTING_KEY.hook_before_each_down.name());
142+
this.hookAfterEachDown = readProperty(prop, SETTING_KEY.hook_after_each_down.name());
143+
this.hookAfterDown = readProperty(prop, SETTING_KEY.hook_after_down.name());
144+
145+
this.hookBeforeNew = readProperty(prop, SETTING_KEY.hook_before_new.name());
146+
this.hookAfterNew = readProperty(prop, SETTING_KEY.hook_after_new.name());
147+
148+
// User defined variables.
149+
prop.entrySet().stream().filter(e -> !SETTING_KEYS.contains(e.getKey()))
150+
.forEach(e -> variables.put(e.getKey(), parser.parse((String) e.getValue())));
151+
}
152+
153+
private Properties mergeProperties(File file) {
154+
// 1. Load from file.
155+
Properties prop = loadPropertiesFromFile(file);
156+
// 2. Read environment variables (existing entries are overwritten).
157+
envVars.entrySet().stream().filter(e -> isMigrationsKey(e.getKey()))
158+
.forEach(e -> prop.put(normalizeKey(e.getKey()), e.getValue()));
159+
// 3. Read system properties (existing entries are overwritten).
160+
sysProps.entrySet().stream().filter(e -> isMigrationsKey((String) e.getKey()))
161+
.forEach(e -> prop.put(normalizeKey((String) e.getKey()), e.getValue()));
162+
return prop;
163+
}
164+
165+
private String normalizeKey(String key) {
166+
return key.substring(PREFIX.length()).toLowerCase(Locale.ENGLISH);
167+
}
168+
169+
private boolean isMigrationsKey(String key) {
170+
return key.length() > PREFIX.length() && key.toUpperCase(Locale.ENGLISH).startsWith(PREFIX);
171+
}
172+
173+
private Properties loadPropertiesFromFile(File file) {
174+
Properties properties = new Properties();
119175
try (FileInputStream inputStream = new FileInputStream(file)) {
120-
Properties prop = new Properties();
121-
prop.load(inputStream);
122-
123-
this.timeZone = readProperty(prop, SETTING_KEY.time_zone.name(), "GMT+0:00");
124-
this.delimiter = readProperty(prop, SETTING_KEY.delimiter.name(), ";");
125-
this.scriptCharset = readProperty(prop, SETTING_KEY.script_char_set.name(), Charset.defaultCharset().name());
126-
this.fullLineDelimiter = Boolean.valueOf(readProperty(prop, SETTING_KEY.full_line_delimiter.name()));
127-
this.sendFullScript = Boolean.valueOf(readProperty(prop, SETTING_KEY.send_full_script.name()));
128-
this.autoCommit = Boolean.valueOf(readProperty(prop, SETTING_KEY.auto_commit.name()));
129-
this.removeCrs = Boolean.valueOf(readProperty(prop, SETTING_KEY.remove_crs.name()));
130-
this.ignoreWarnings = Boolean.valueOf(readProperty(prop, SETTING_KEY.ignore_warnings.name(), "true"));
131-
132-
this.driverPath = readProperty(prop, SETTING_KEY.driver_path.name());
133-
this.driver = readProperty(prop, SETTING_KEY.driver.name());
134-
this.url = readProperty(prop, SETTING_KEY.url.name());
135-
this.username = readProperty(prop, SETTING_KEY.username.name());
136-
this.password = readProperty(prop, SETTING_KEY.password.name());
137-
138-
this.hookBeforeUp = readProperty(prop, SETTING_KEY.hook_before_up.name());
139-
this.hookBeforeEachUp = readProperty(prop, SETTING_KEY.hook_before_each_up.name());
140-
this.hookAfterEachUp = readProperty(prop, SETTING_KEY.hook_after_each_up.name());
141-
this.hookAfterUp = readProperty(prop, SETTING_KEY.hook_after_up.name());
142-
this.hookBeforeDown = readProperty(prop, SETTING_KEY.hook_before_down.name());
143-
this.hookBeforeEachDown = readProperty(prop, SETTING_KEY.hook_before_each_down.name());
144-
this.hookAfterEachDown = readProperty(prop, SETTING_KEY.hook_after_each_down.name());
145-
this.hookAfterDown = readProperty(prop, SETTING_KEY.hook_after_down.name());
146-
147-
this.hookBeforeNew = readProperty(prop, SETTING_KEY.hook_before_new.name());
148-
this.hookAfterNew = readProperty(prop, SETTING_KEY.hook_after_new.name());
149-
150-
// User defined variables.
151-
prop.entrySet().stream().filter(e -> !SETTING_KEYS.contains(e.getKey())).forEach(e -> {
152-
variables.put(e.getKey(), parser.parse((String) e.getValue()));
153-
});
176+
properties.load(inputStream);
177+
return properties;
154178
} catch (FileNotFoundException e) {
155179
throw new MigrationException("Environment file missing: " + file.getAbsolutePath());
156180
} catch (IOException e) {
157181
throw new MigrationException("Error loading environment properties. Cause: " + e, e);
158182
}
159183
}
160184

161-
protected String readProperty(Properties properties, String propertyKey) {
185+
private String readProperty(Properties properties, String propertyKey) {
162186
return readProperty(properties, propertyKey, null);
163187
}
164188

165-
protected String readProperty(Properties properties, String propertyKey, String defaultValue) {
166-
// 1. Check system properties with prefix.
167-
String property = sysProps.getProperty(PREFIX + propertyKey.toUpperCase(Locale.ENGLISH));
168-
if (property != null) {
169-
return property;
170-
}
171-
// 2. Check environment variables with prefix.
172-
property = envVars.get(PREFIX + propertyKey.toUpperCase(Locale.ENGLISH));
173-
if (property != null) {
174-
return property;
175-
}
176-
// 3. Read .properties file with variable replacement.
177-
property = properties.getProperty(propertyKey, defaultValue);
189+
private String readProperty(Properties properties, String propertyKey, String defaultValue) {
190+
String property = properties.getProperty(propertyKey, defaultValue);
178191
return property == null ? null : parser.parse(property);
179192
}
180193

src/test/java/org/apache/ibatis/migration/system_property/SystemPropertyTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ public void checkAssertion() {
5858
System.setProperty("MIGRATIONS_DRIVER", "org.hsqldb.jdbcDriver");
5959
System.setProperty("username", "Pocahontas");
6060
System.setProperty("var1", "Variable 1");
61+
System.setProperty("MIGRATIONS_VAR3", "Variable 3");
62+
System.setProperty("migrations_var4", "Variable 4");
63+
System.setProperty("MIGRATIONS_VAR5", "Variable 5");
6164

6265
Migrator.main(TestUtil.args("--path=" + dir.getAbsolutePath(), "up", "1", "--trace"));
6366

@@ -66,6 +69,10 @@ public void checkAssertion() {
6669
assertTrue(output.contains("username: Pocahontas"));
6770
assertTrue(output.contains("var1: Variable 1"));
6871
assertTrue(output.contains("var2: ${var2}"));
72+
assertTrue(output.contains("var3: Variable 3"));
73+
assertTrue(output.contains("var4: Variable 4"));
74+
assertTrue(output.contains("var5: Variable 5"));
75+
assertTrue(output.contains("Var5: Var5 in properties file"));
6976

7077
out.clearLog();
7178
System.exit(0);

src/test/java/org/apache/ibatis/migration/system_property/testdir/environments/development.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,5 @@ changelog=CHANGELOG
2323

2424
var1=${var1}
2525
var2=${var2}
26+
var4=should be overwritten by system property
27+
Var5=Var5 in properties file

src/test/java/org/apache/ibatis/migration/system_property/testdir/scripts/001_create_changelog.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ PRIMARY KEY (id);
3838
SELECT 'username: ' || USER_NAME FROM INFORMATION_SCHEMA.SYSTEM_USERS;
3939
SELECT 'var1: ' || '${var1}' FROM (VALUES(0));
4040
SELECT 'var2: ' || '${var2}' FROM (VALUES(0));
41+
SELECT 'var3: ' || '${var3}' FROM (VALUES(0));
42+
SELECT 'var4: ' || '${var4}' FROM (VALUES(0));
43+
SELECT 'var5: ' || '${var5}' FROM (VALUES(0));
44+
SELECT 'Var5: ' || '${Var5}' FROM (VALUES(0));
4145

4246

4347
-- //@UNDO

0 commit comments

Comments
 (0)