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