1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.migration;
17
18 import java.io.File;
19 import java.io.FileInputStream;
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.nio.charset.Charset;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Locale;
28 import java.util.Map;
29 import java.util.Properties;
30
31 public class Environment {
32
33 public static final String CHANGELOG = "changelog";
34
35 private enum SETTING_KEY {
36 TIME_ZONE,
37
38 DELIMITER,
39
40 SCRIPT_CHAR_SET,
41
42 FULL_LINE_DELIMITER,
43
44 SEND_FULL_SCRIPT,
45
46 AUTO_COMMIT,
47
48 REMOVE_CRS,
49
50 IGNORE_WARNINGS,
51
52 DRIVER_PATH,
53
54 DRIVER,
55
56 URL,
57
58 USERNAME,
59
60 PASSWORD,
61
62 HOOK_BEFORE_UP,
63
64 HOOK_BEFORE_EACH_UP,
65
66 HOOK_AFTER_EACH_UP,
67
68 HOOK_AFTER_UP,
69
70 HOOK_BEFORE_DOWN,
71
72 HOOK_BEFORE_EACH_DOWN,
73
74 HOOK_AFTER_EACH_DOWN,
75
76 HOOK_AFTER_DOWN,
77
78 HOOK_BEFORE_NEW,
79
80 HOOK_AFTER_NEW,
81
82 HOOK_BEFORE_SCRIPT,
83
84 HOOK_BEFORE_EACH_SCRIPT,
85
86 HOOK_AFTER_EACH_SCRIPT,
87
88 HOOK_AFTER_SCRIPT;
89
90 @Override
91 public String toString() {
92 return this.name().toLowerCase(Locale.ENGLISH);
93 }
94 }
95
96 private static final List<String> SETTING_KEYS;
97
98 static {
99 ArrayList<String> list = new ArrayList<>();
100 SETTING_KEY[] keys = SETTING_KEY.values();
101 for (SETTING_KEY key : keys) {
102 list.add(key.toString());
103 }
104 SETTING_KEYS = Collections.unmodifiableList(list);
105 }
106
107 private final String timeZone;
108 private final String delimiter;
109 private final String scriptCharset;
110 private final boolean fullLineDelimiter;
111 private final boolean sendFullScript;
112 private final boolean autoCommit;
113 private final boolean removeCrs;
114 private final boolean ignoreWarnings;
115 private final String driverPath;
116 private final String driver;
117 private final String url;
118 private final String username;
119 private final String password;
120
121 private final String hookBeforeUp;
122 private final String hookBeforeEachUp;
123 private final String hookAfterEachUp;
124 private final String hookAfterUp;
125 private final String hookBeforeDown;
126 private final String hookBeforeEachDown;
127 private final String hookAfterEachDown;
128 private final String hookAfterDown;
129
130 private final String hookBeforeNew;
131 private final String hookAfterNew;
132
133 private final String hookBeforeScript;
134 private final String hookBeforeEachScript;
135 private final String hookAfterEachScript;
136 private final String hookAfterScript;
137
138
139
140
141 private static final String PREFIX = "MIGRATIONS_";
142 private final Map<String, String> envVars = System.getenv();
143 private final Properties sysProps = System.getProperties();
144 private final Properties variables = new Properties();
145
146 private final VariableReplacer parser = new VariableReplacer(Arrays.asList(sysProps, envVars));
147
148 public Environment(File file) {
149 Properties prop = mergeProperties(file);
150
151 this.timeZone = readProperty(prop, SETTING_KEY.TIME_ZONE.toString(), "GMT+0:00");
152 this.delimiter = readProperty(prop, SETTING_KEY.DELIMITER.toString(), ";");
153 this.scriptCharset = readProperty(prop, SETTING_KEY.SCRIPT_CHAR_SET.toString(),
154 Charset.defaultCharset().toString());
155 this.fullLineDelimiter = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.FULL_LINE_DELIMITER.toString()));
156 this.sendFullScript = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.SEND_FULL_SCRIPT.toString()));
157 this.autoCommit = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.AUTO_COMMIT.toString()));
158 this.removeCrs = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.REMOVE_CRS.toString()));
159 this.ignoreWarnings = Boolean.parseBoolean(readProperty(prop, SETTING_KEY.IGNORE_WARNINGS.toString(), "true"));
160
161 this.driverPath = readProperty(prop, SETTING_KEY.DRIVER_PATH.toString());
162 this.driver = readProperty(prop, SETTING_KEY.DRIVER.toString());
163 this.url = readProperty(prop, SETTING_KEY.URL.toString());
164 this.username = readProperty(prop, SETTING_KEY.USERNAME.toString());
165 this.password = readProperty(prop, SETTING_KEY.PASSWORD.toString());
166
167 this.hookBeforeUp = readProperty(prop, SETTING_KEY.HOOK_BEFORE_UP.toString());
168 this.hookBeforeEachUp = readProperty(prop, SETTING_KEY.HOOK_BEFORE_EACH_UP.toString());
169 this.hookAfterEachUp = readProperty(prop, SETTING_KEY.HOOK_AFTER_EACH_UP.toString());
170 this.hookAfterUp = readProperty(prop, SETTING_KEY.HOOK_AFTER_UP.toString());
171 this.hookBeforeDown = readProperty(prop, SETTING_KEY.HOOK_BEFORE_DOWN.toString());
172 this.hookBeforeEachDown = readProperty(prop, SETTING_KEY.HOOK_BEFORE_EACH_DOWN.toString());
173 this.hookAfterEachDown = readProperty(prop, SETTING_KEY.HOOK_AFTER_EACH_DOWN.toString());
174 this.hookAfterDown = readProperty(prop, SETTING_KEY.HOOK_AFTER_DOWN.toString());
175
176 this.hookBeforeNew = readProperty(prop, SETTING_KEY.HOOK_BEFORE_NEW.toString());
177 this.hookAfterNew = readProperty(prop, SETTING_KEY.HOOK_AFTER_NEW.toString());
178
179 this.hookBeforeScript = readProperty(prop, SETTING_KEY.HOOK_BEFORE_SCRIPT.toString());
180 this.hookBeforeEachScript = readProperty(prop, SETTING_KEY.HOOK_BEFORE_EACH_SCRIPT.toString());
181 this.hookAfterEachScript = readProperty(prop, SETTING_KEY.HOOK_AFTER_EACH_SCRIPT.toString());
182 this.hookAfterScript = readProperty(prop, SETTING_KEY.HOOK_AFTER_SCRIPT.toString());
183
184
185 prop.entrySet().stream().filter(e -> !SETTING_KEYS.contains(e.getKey()))
186 .forEach(e -> variables.put(e.getKey(), parser.replace((String) e.getValue())));
187 }
188
189 private Properties mergeProperties(File file) {
190
191 Properties prop = loadPropertiesFromFile(file);
192
193 envVars.entrySet().stream().filter(e -> isMigrationsKey(e.getKey()))
194 .forEach(e -> prop.put(normalizeKey(e.getKey()), e.getValue()));
195
196 sysProps.entrySet().stream().filter(e -> isMigrationsKey((String) e.getKey()))
197 .forEach(e -> prop.put(normalizeKey((String) e.getKey()), e.getValue()));
198 return prop;
199 }
200
201 private String normalizeKey(String key) {
202 return key.substring(PREFIX.length()).toLowerCase(Locale.ENGLISH);
203 }
204
205 private boolean isMigrationsKey(String key) {
206 return key.length() > PREFIX.length() && key.toUpperCase(Locale.ENGLISH).startsWith(PREFIX);
207 }
208
209 private Properties loadPropertiesFromFile(File file) {
210 Properties properties = new Properties();
211 try (FileInputStream inputStream = new FileInputStream(file)) {
212 properties.load(inputStream);
213 return properties;
214 } catch (FileNotFoundException e) {
215 throw new MigrationException("Environment file missing: " + file.getAbsolutePath());
216 } catch (IOException e) {
217 throw new MigrationException("Error loading environment properties. Cause: " + e, e);
218 }
219 }
220
221 private String readProperty(Properties properties, String propertyKey) {
222 return readProperty(properties, propertyKey, null);
223 }
224
225 private String readProperty(Properties properties, String propertyKey, String defaultValue) {
226 String property = properties.getProperty(propertyKey, defaultValue);
227 return property == null ? null : parser.replace(property);
228 }
229
230 public String getTimeZone() {
231 return timeZone;
232 }
233
234 public String getDelimiter() {
235 return delimiter;
236 }
237
238 public String getScriptCharset() {
239 return scriptCharset;
240 }
241
242 public boolean isFullLineDelimiter() {
243 return fullLineDelimiter;
244 }
245
246 public boolean isSendFullScript() {
247 return sendFullScript;
248 }
249
250 public boolean isAutoCommit() {
251 return autoCommit;
252 }
253
254 public boolean isRemoveCrs() {
255 return removeCrs;
256 }
257
258 public boolean isIgnoreWarnings() {
259 return ignoreWarnings;
260 }
261
262 public String getDriverPath() {
263 return driverPath;
264 }
265
266 public String getDriver() {
267 return driver;
268 }
269
270 public String getUrl() {
271 return url;
272 }
273
274 public String getUsername() {
275 return username;
276 }
277
278 public String getPassword() {
279 return password;
280 }
281
282 public String getHookBeforeUp() {
283 return hookBeforeUp;
284 }
285
286 public String getHookBeforeEachUp() {
287 return hookBeforeEachUp;
288 }
289
290 public String getHookAfterEachUp() {
291 return hookAfterEachUp;
292 }
293
294 public String getHookAfterUp() {
295 return hookAfterUp;
296 }
297
298 public String getHookBeforeDown() {
299 return hookBeforeDown;
300 }
301
302 public String getHookBeforeEachDown() {
303 return hookBeforeEachDown;
304 }
305
306 public String getHookAfterEachDown() {
307 return hookAfterEachDown;
308 }
309
310 public String getHookAfterDown() {
311 return hookAfterDown;
312 }
313
314 public String getHookBeforeNew() {
315 return hookBeforeNew;
316 }
317
318 public String getHookAfterNew() {
319 return hookAfterNew;
320 }
321
322 public String getHookBeforeScript() {
323 return hookBeforeScript;
324 }
325
326 public String getHookBeforeEachScript() {
327 return hookBeforeEachScript;
328 }
329
330 public String getHookAfterEachScript() {
331 return hookAfterEachScript;
332 }
333
334 public String getHookAfterScript() {
335 return hookAfterScript;
336 }
337
338 public Properties getVariables() {
339 return variables;
340 }
341 }