1 | |
package liquibase.integration.ant; |
2 | |
|
3 | |
import java.io.File; |
4 | |
import java.io.FileWriter; |
5 | |
import java.io.IOException; |
6 | |
import java.io.PrintStream; |
7 | |
import java.io.Writer; |
8 | |
import java.net.URL; |
9 | |
import java.net.URLClassLoader; |
10 | |
import java.security.AccessController; |
11 | |
import java.security.PrivilegedAction; |
12 | |
import java.sql.Connection; |
13 | |
import java.sql.Driver; |
14 | |
import java.util.ArrayList; |
15 | |
import java.util.HashMap; |
16 | |
import java.util.List; |
17 | |
import java.util.Map; |
18 | |
import java.util.Properties; |
19 | |
import java.util.logging.Handler; |
20 | |
import java.util.logging.Level; |
21 | |
import java.util.logging.LogRecord; |
22 | |
|
23 | |
import liquibase.Liquibase; |
24 | |
import liquibase.database.Database; |
25 | |
import liquibase.database.DatabaseFactory; |
26 | |
import liquibase.database.jvm.JdbcConnection; |
27 | |
import liquibase.exception.DatabaseException; |
28 | |
import liquibase.logging.LogFactory; |
29 | |
import liquibase.logging.Logger; |
30 | |
import liquibase.resource.CompositeResourceAccessor; |
31 | |
import liquibase.resource.FileSystemResourceAccessor; |
32 | |
import liquibase.resource.ResourceAccessor; |
33 | |
|
34 | |
import org.apache.tools.ant.AntClassLoader; |
35 | |
import org.apache.tools.ant.BuildException; |
36 | |
import org.apache.tools.ant.Project; |
37 | |
import org.apache.tools.ant.Task; |
38 | |
import org.apache.tools.ant.types.Path; |
39 | |
import org.apache.tools.ant.types.Reference; |
40 | |
|
41 | |
|
42 | |
|
43 | |
|
44 | |
|
45 | |
public class BaseLiquibaseTask extends Task { |
46 | |
private String changeLogFile; |
47 | |
private String driver; |
48 | |
private String url; |
49 | |
private String username; |
50 | |
private String password; |
51 | |
protected Path classpath; |
52 | 1 | private boolean promptOnNonLocalDatabase = false; |
53 | |
private String currentDateTimeFunction; |
54 | |
private String contexts; |
55 | |
private String outputFile; |
56 | |
private String defaultSchemaName; |
57 | |
private String databaseClass; |
58 | |
private String databaseChangeLogTableName; |
59 | |
private String databaseChangeLogLockTableName; |
60 | |
|
61 | 1 | private Map<String, Object> changeLogProperties = new HashMap<String, Object>(); |
62 | |
|
63 | |
public BaseLiquibaseTask() { |
64 | 1 | super(); |
65 | 1 | new LogRedirector(this).redirectLogger(); |
66 | 1 | } |
67 | |
|
68 | |
@Override |
69 | |
public void execute() throws BuildException { |
70 | 0 | super.execute(); |
71 | |
|
72 | 0 | AntClassLoader loader = getProject().createClassLoader(classpath); |
73 | 0 | loader.setParent(this.getClass().getClassLoader()); |
74 | 0 | loader.setThreadContextLoader(); |
75 | |
|
76 | 0 | } |
77 | |
|
78 | |
public boolean isPromptOnNonLocalDatabase() { |
79 | 0 | return promptOnNonLocalDatabase; |
80 | |
} |
81 | |
|
82 | |
public void setPromptOnNonLocalDatabase(boolean promptOnNonLocalDatabase) { |
83 | 0 | this.promptOnNonLocalDatabase = promptOnNonLocalDatabase; |
84 | 0 | } |
85 | |
|
86 | |
public String getDriver() { |
87 | 0 | return driver; |
88 | |
} |
89 | |
|
90 | |
public void setDriver(String driver) { |
91 | 0 | this.driver = driver.trim(); |
92 | 0 | } |
93 | |
|
94 | |
public String getUrl() { |
95 | 0 | return url; |
96 | |
} |
97 | |
|
98 | |
public void setUrl(String url) { |
99 | 0 | this.url = url.trim(); |
100 | 0 | } |
101 | |
|
102 | |
public String getUsername() { |
103 | 0 | return username; |
104 | |
} |
105 | |
|
106 | |
public void setUsername(String username) { |
107 | 0 | this.username = username.trim(); |
108 | 0 | } |
109 | |
|
110 | |
public String getPassword() { |
111 | 0 | return password; |
112 | |
} |
113 | |
|
114 | |
public void setPassword(String password) { |
115 | 0 | this.password = password.trim(); |
116 | 0 | } |
117 | |
|
118 | |
public String getChangeLogFile() { |
119 | 0 | return changeLogFile; |
120 | |
} |
121 | |
|
122 | |
public void setChangeLogFile(String changeLogFile) { |
123 | 0 | this.changeLogFile = changeLogFile.trim(); |
124 | 0 | } |
125 | |
|
126 | |
public Path createClasspath() { |
127 | 1 | if (this.classpath == null) { |
128 | 1 | this.classpath = new Path(getProject()); |
129 | |
} |
130 | 1 | return this.classpath.createPath(); |
131 | |
} |
132 | |
|
133 | |
public void setClasspathRef(Reference r) { |
134 | 0 | createClasspath().setRefid(r); |
135 | 0 | } |
136 | |
|
137 | |
public String getCurrentDateTimeFunction() { |
138 | 0 | return currentDateTimeFunction; |
139 | |
} |
140 | |
|
141 | |
public void setCurrentDateTimeFunction(String currentDateTimeFunction) { |
142 | 0 | this.currentDateTimeFunction = currentDateTimeFunction.trim(); |
143 | 0 | } |
144 | |
|
145 | |
public String getOutputFile() { |
146 | 0 | return outputFile; |
147 | |
} |
148 | |
|
149 | |
public void setOutputFile(String outputFile) { |
150 | 0 | this.outputFile = outputFile.trim(); |
151 | 0 | } |
152 | |
|
153 | |
public Writer createOutputWriter() throws IOException { |
154 | 0 | if (outputFile == null) { |
155 | 0 | return null; |
156 | |
} |
157 | 0 | return new FileWriter(new File(getOutputFile())); |
158 | |
} |
159 | |
|
160 | |
public PrintStream createPrintStream() throws IOException { |
161 | 0 | if (outputFile == null) { |
162 | 0 | return null; |
163 | |
} |
164 | 0 | return new PrintStream(new File(getOutputFile())); |
165 | |
} |
166 | |
|
167 | |
public String getDefaultSchemaName() { |
168 | 0 | return defaultSchemaName; |
169 | |
} |
170 | |
|
171 | |
public void setDefaultSchemaName(String defaultSchemaName) { |
172 | 0 | this.defaultSchemaName = defaultSchemaName.trim(); |
173 | 0 | } |
174 | |
|
175 | |
public void addConfiguredChangeLogProperty(ChangeLogProperty changeLogProperty) { |
176 | 0 | changeLogProperties.put(changeLogProperty.getName(), changeLogProperty.getValue()); |
177 | 0 | } |
178 | |
|
179 | |
protected Liquibase createLiquibase() throws Exception { |
180 | 0 | ResourceAccessor antFO = new AntResourceAccessor(getProject(), classpath); |
181 | 0 | ResourceAccessor fsFO = new FileSystemResourceAccessor(); |
182 | |
|
183 | 0 | Database database = createDatabaseObject(getDriver(), getUrl(), getUsername(), getPassword(), |
184 | |
getDefaultSchemaName(), getDatabaseClass()); |
185 | |
|
186 | 0 | String changeLogFile = null; |
187 | 0 | if (getChangeLogFile() != null) { |
188 | 0 | changeLogFile = getChangeLogFile().trim(); |
189 | |
} |
190 | 0 | Liquibase liquibase = new Liquibase(changeLogFile, new CompositeResourceAccessor(antFO, fsFO), database); |
191 | 0 | liquibase.setCurrentDateTimeFunction(currentDateTimeFunction); |
192 | 0 | for (Map.Entry<String, Object> entry : changeLogProperties.entrySet()) { |
193 | 0 | liquibase.setChangeLogParameter(entry.getKey(), entry.getValue()); |
194 | |
} |
195 | |
|
196 | 0 | return liquibase; |
197 | |
} |
198 | |
|
199 | |
protected Database createDatabaseObject(String driverClassName, String databaseUrl, String username, |
200 | |
String password, String defaultSchemaName, String databaseClass) throws Exception { |
201 | 0 | String[] strings = classpath.list(); |
202 | |
|
203 | 0 | final List<URL> taskClassPath = new ArrayList<URL>(); |
204 | 0 | for (String string : strings) { |
205 | 0 | URL url = new File(string).toURL(); |
206 | 0 | taskClassPath.add(url); |
207 | |
} |
208 | |
|
209 | 0 | URLClassLoader loader = AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() { |
210 | |
@Override |
211 | |
public URLClassLoader run() { |
212 | 0 | return new URLClassLoader(taskClassPath.toArray(new URL[taskClassPath.size()]), Database.class |
213 | |
.getClassLoader()); |
214 | |
} |
215 | |
}); |
216 | |
|
217 | |
Database database; |
218 | |
|
219 | 0 | if (databaseClass != null) { |
220 | |
try { |
221 | 0 | DatabaseFactory.getInstance().register( |
222 | |
(Database) Class.forName(databaseClass, true, loader).newInstance()); |
223 | 0 | } catch (ClassCastException e) { |
224 | 0 | DatabaseFactory.getInstance().register((Database) Class.forName(databaseClass).newInstance()); |
225 | 0 | } |
226 | |
} |
227 | |
|
228 | 0 | if (driverClassName == null) { |
229 | 0 | driverClassName = DatabaseFactory.getInstance().findDefaultDriver(databaseUrl); |
230 | |
} |
231 | |
|
232 | 0 | if (driverClassName == null) { |
233 | 0 | throw new DatabaseException("driver not specified and no default could be found for " + databaseUrl); |
234 | |
} |
235 | |
|
236 | 0 | Driver driver = (Driver) Class.forName(driverClassName, true, loader).newInstance(); |
237 | |
|
238 | 0 | Properties info = new Properties(); |
239 | 0 | if (username != null) { |
240 | 0 | info.put("user", username); |
241 | |
} |
242 | 0 | if (password != null) { |
243 | 0 | info.put("password", password); |
244 | |
} |
245 | 0 | Connection connection = driver.connect(databaseUrl, info); |
246 | |
|
247 | 0 | if (connection == null) { |
248 | 0 | throw new DatabaseException("Connection could not be created to " + databaseUrl + " with driver " |
249 | |
+ driver.getClass().getName() + ". Possibly the wrong driver for the given database URL"); |
250 | |
} |
251 | |
|
252 | 0 | database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection)); |
253 | 0 | database.setDefaultSchemaName(defaultSchemaName); |
254 | |
|
255 | 0 | if (getDatabaseChangeLogTableName() != null) |
256 | 0 | database.setDatabaseChangeLogTableName(getDatabaseChangeLogTableName()); |
257 | |
|
258 | 0 | if (getDatabaseChangeLogLockTableName() != null) |
259 | 0 | database.setDatabaseChangeLogLockTableName(getDatabaseChangeLogLockTableName()); |
260 | |
|
261 | 0 | return database; |
262 | |
} |
263 | |
|
264 | |
public String getContexts() { |
265 | 0 | return contexts; |
266 | |
} |
267 | |
|
268 | |
public void setContexts(String cntx) { |
269 | 0 | this.contexts = cntx.trim(); |
270 | 0 | } |
271 | |
|
272 | |
|
273 | |
|
274 | |
|
275 | 0 | protected static class LogRedirector { |
276 | |
|
277 | |
private final Task task; |
278 | |
|
279 | |
|
280 | |
|
281 | |
|
282 | |
|
283 | |
|
284 | |
protected LogRedirector(Task task) { |
285 | 1 | super(); |
286 | 1 | this.task = task; |
287 | 1 | } |
288 | |
|
289 | |
protected void redirectLogger() { |
290 | 1 | registerHandler(createHandler()); |
291 | 1 | } |
292 | |
|
293 | |
protected void registerHandler(Handler theHandler) { |
294 | 1 | Logger logger = LogFactory.getLogger(); |
295 | 1 | } |
296 | |
|
297 | |
protected Handler createHandler() { |
298 | 1 | return new Handler() { |
299 | |
@Override |
300 | |
public void publish(LogRecord logRecord) { |
301 | 0 | task.log(logRecord.getMessage(), mapLevelToAntLevel(logRecord.getLevel())); |
302 | 0 | } |
303 | |
|
304 | |
@Override |
305 | |
public void close() throws SecurityException { |
306 | 0 | } |
307 | |
|
308 | |
@Override |
309 | |
public void flush() { |
310 | 0 | } |
311 | |
|
312 | |
protected int mapLevelToAntLevel(Level level) { |
313 | 0 | if (Level.ALL == level) { |
314 | 0 | return Project.MSG_INFO; |
315 | 0 | } else if (Level.SEVERE == level) { |
316 | 0 | return Project.MSG_ERR; |
317 | 0 | } else if (Level.WARNING == level) { |
318 | 0 | return Project.MSG_WARN; |
319 | 0 | } else if (Level.INFO == level) { |
320 | 0 | return Project.MSG_INFO; |
321 | |
} else { |
322 | 0 | return Project.MSG_VERBOSE; |
323 | |
} |
324 | |
} |
325 | |
}; |
326 | |
} |
327 | |
|
328 | |
} |
329 | |
|
330 | |
protected boolean shouldRun() { |
331 | 0 | String shouldRunProperty = System.getProperty(Liquibase.SHOULD_RUN_SYSTEM_PROPERTY); |
332 | 0 | if (shouldRunProperty != null && !Boolean.valueOf(shouldRunProperty)) { |
333 | 0 | log("Liquibase did not run because '" + Liquibase.SHOULD_RUN_SYSTEM_PROPERTY |
334 | |
+ "' system property was set to false"); |
335 | 0 | return false; |
336 | |
} |
337 | 0 | return true; |
338 | |
} |
339 | |
|
340 | |
protected void closeDatabase(Liquibase liquibase) { |
341 | 0 | if (liquibase != null && liquibase.getDatabase() != null && liquibase.getDatabase().getConnection() != null) { |
342 | |
try { |
343 | 0 | liquibase.getDatabase().close(); |
344 | 0 | } catch (DatabaseException e) { |
345 | 0 | log("Error closing database: " + e.getMessage()); |
346 | 0 | } |
347 | |
} |
348 | 0 | } |
349 | |
|
350 | |
public String getDatabaseClass() { |
351 | 0 | return databaseClass; |
352 | |
} |
353 | |
|
354 | |
public void setDatabaseClass(String databaseClass) { |
355 | 0 | this.databaseClass = databaseClass; |
356 | 0 | } |
357 | |
|
358 | |
public String getDatabaseChangeLogTableName() { |
359 | 0 | return databaseChangeLogTableName; |
360 | |
} |
361 | |
|
362 | |
public void setDatabaseChangeLogTableName(String tableName) { |
363 | 0 | this.databaseChangeLogTableName = tableName; |
364 | 0 | } |
365 | |
|
366 | |
public String getDatabaseChangeLogLockTableName() { |
367 | 0 | return databaseChangeLogLockTableName; |
368 | |
} |
369 | |
|
370 | |
public void setDatabaseChangeLogLockTableName(String tableName) { |
371 | 0 | this.databaseChangeLogLockTableName = tableName; |
372 | 0 | } |
373 | |
|
374 | |
public String getLogLevel() { |
375 | 0 | return LogFactory.getLogger().getLogLevel().name(); |
376 | |
} |
377 | |
|
378 | |
public void setLogLevel(String level) { |
379 | 0 | LogFactory.getLogger().setLogLevel(level); |
380 | 0 | } |
381 | |
|
382 | 0 | public static class ChangeLogProperty { |
383 | |
private String name; |
384 | |
private String value; |
385 | |
|
386 | |
public String getName() { |
387 | 0 | return name; |
388 | |
} |
389 | |
|
390 | |
public void setName(String name) { |
391 | 0 | this.name = name; |
392 | 0 | } |
393 | |
|
394 | |
public String getValue() { |
395 | 0 | return value; |
396 | |
} |
397 | |
|
398 | |
public void setValue(String value) { |
399 | 0 | this.value = value; |
400 | 0 | } |
401 | |
} |
402 | |
} |