-
Notifications
You must be signed in to change notification settings - Fork 160
/
KapuaLiquibaseClient.java
201 lines (171 loc) · 8.65 KB
/
KapuaLiquibaseClient.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*******************************************************************************
* Copyright (c) 2017, 2020 Red Hat Inc and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Red Hat Inc - initial API and implementation
* Eurotech
*******************************************************************************/
package org.eclipse.kapua.commons.liquibase;
import com.google.common.base.Strings;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.LiquibaseException;
import liquibase.resource.FileSystemResourceAccessor;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.SystemUtils;
import org.eclipse.kapua.commons.liquibase.settings.LiquibaseClientSettingKeys;
import org.eclipse.kapua.commons.liquibase.settings.LiquibaseClientSettings;
import org.eclipse.kapua.commons.util.SemanticVersion;
import org.reflections.Reflections;
import org.reflections.scanners.ResourcesScanner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
/**
* Client that execute {@link Liquibase} scripts.
* <p>
* It looks available scripts in {@code .xml} or {@code .sql} in the classpath.
*
* @since 1.0.0
*/
public class KapuaLiquibaseClient {
private static final Logger LOG = LoggerFactory.getLogger(KapuaLiquibaseClient.class);
private static final SemanticVersion LIQUIBASE_TIMESTAMP_FIX_VERSION = new SemanticVersion("3.3.3"); // https://liquibase.jira.com/browse/CORE-1958
private static final LiquibaseClientSettings LIQUIBASE_CLIENT_SETTINGS = LiquibaseClientSettings.getInstance();
private final String jdbcUrl;
private final String username;
private final String password;
private final String schema;
private final boolean runTimestampsFix;
/**
* Constructor.
*
* @param jdbcUrl The JDBC connection string.
* @param username The username to connect to to the database.
* @param password The password to connect to to the database.
* @since 1.0.0
*/
public KapuaLiquibaseClient(String jdbcUrl, String username, String password) {
this(jdbcUrl, username, password, null);
}
/**
* Constructor.
*
* @param jdbcUrl The JDBC connection string.
* @param username The username to connect to to the database.
* @param password The password to connect to to the database.
* @param schema The schema name.
* @since 1.0.0
*/
public KapuaLiquibaseClient(String jdbcUrl, String username, String password, String schema) {
this.jdbcUrl = jdbcUrl;
this.username = username;
this.password = password;
this.schema = schema;
// Check wether or not fix the timestamp based on Liquibase version
boolean forceTimestampFix = LIQUIBASE_CLIENT_SETTINGS.getBoolean(LiquibaseClientSettingKeys.FORCE_TIMESTAMPS_FIX);
String currentLiquibaseVersionString = LIQUIBASE_CLIENT_SETTINGS.getString(LiquibaseClientSettingKeys.LIQUIBASE_VERSION);
SemanticVersion currentLiquibaseVersion = new SemanticVersion(currentLiquibaseVersionString);
runTimestampsFix = (currentLiquibaseVersion.afterOrMatches(LIQUIBASE_TIMESTAMP_FIX_VERSION) || forceTimestampFix);
LOG.info("Liquibase Version: {}", currentLiquibaseVersionString);
LOG.info("Force timestamp fix: {}", forceTimestampFix);
LOG.info("Apply timestamp fix: {}", runTimestampsFix);
}
/**
* Starts the looking and execution of the Liquibase Scripts.
*
* @since 1.0.0
*/
public void update() {
try {
if (Boolean.parseBoolean(System.getProperty("LIQUIBASE_ENABLED", "true")) || Boolean.parseBoolean(System.getenv("LIQUIBASE_ENABLED"))) {
LOG.info("Running Liquibase update with schema: {}", schema);
try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
File changelogDir = loadChangelogs();
List<String> contexts = new ArrayList<>();
if (!runTimestampsFix) {
contexts.add("!fixTimestamps");
}
executeMasters(connection, schema, changelogDir, contexts);
}
}
} catch (LiquibaseException | SQLException | IOException e) {
LOG.error("Error while running Liquibase scripts: {}", e.getMessage(), e);
throw new RuntimeException(e);
}
}
protected static synchronized File loadChangelogs() throws IOException {
String tmpDirectory = SystemUtils.getJavaIoTmpDir().getAbsolutePath();
File changelogTempDirectory = new File(tmpDirectory, "kapua-liquibase");
if (changelogTempDirectory.exists()) {
FileUtils.deleteDirectory(changelogTempDirectory);
}
boolean createdTmp = changelogTempDirectory.mkdirs();
LOG.trace("{} Tmp dir: {}", createdTmp ? "Created" : "Using", changelogTempDirectory.getAbsolutePath());
Reflections reflections = new Reflections("liquibase", new ResourcesScanner());
Set<String> changeLogs = reflections.getResources(Pattern.compile(".*\\.xml|.*\\.sql"));
for (String script : changeLogs) {
URL scriptUrl = KapuaLiquibaseClient.class.getResource("/" + script);
File changelogFile = new File(changelogTempDirectory, script.replaceFirst("liquibase/", ""));
if (changelogFile.getParentFile() != null && !changelogFile.getParentFile().exists()) {
boolean createdParent = changelogFile.getParentFile().mkdirs();
LOG.trace("{} parent dir: {}", createdParent ? "Created" : "Using", changelogFile.getParentFile().getAbsolutePath());
}
try (FileOutputStream tmpStream = new FileOutputStream(changelogFile)) {
IOUtils.write(IOUtils.toString(scriptUrl), tmpStream);
}
LOG.trace("Copied file: {}", changelogFile.getAbsolutePath());
}
return changelogTempDirectory;
}
protected static void executeMasters(Connection connection, String schema, File changelogDir, List<String> contexts) throws LiquibaseException {
//
// Find and execute all master scripts
LOG.info("Executing pre master files...");
executeMasters(connection, schema, changelogDir, "-master.pre.xml", contexts);
LOG.info("Executing pre master files... DONE!");
LOG.info("Executing master files...");
executeMasters(connection, schema, changelogDir, "-master.xml", contexts);
LOG.info("Executing master files... DONE!");
LOG.info("Executing post master files...");
executeMasters(connection, schema, changelogDir, "-master.post.xml", contexts);
LOG.info("Executing post master files... DONE!");
}
protected static void executeMasters(Connection connection, String schema, File changelogTempDirectory, String preMaster, List<String> contexts) throws LiquibaseException {
List<File> masterChangelogs = Arrays.asList(changelogTempDirectory.listFiles((dir, name) -> name.endsWith(preMaster)));
LOG.info("\tMaster Liquibase files found: {}", masterChangelogs.size());
LOG.trace("\tSorting master Liquibase files found.");
masterChangelogs.sort(Comparator.comparing(File::getAbsolutePath));
String ctx = contexts.isEmpty() ? null : String.join(",", contexts);
for (File masterChangelog : masterChangelogs) {
LOG.info("\t\tExecuting liquibase script: {}...", masterChangelog.getAbsolutePath());
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
if (!Strings.isNullOrEmpty(schema)) {
database.setDefaultSchemaName(schema);
}
Liquibase liquibase = new Liquibase(masterChangelog.getAbsolutePath(), new FileSystemResourceAccessor(), database);
liquibase.update(ctx);
LOG.debug("\t\tExecuting liquibase script: {}... DONE!", masterChangelog.getAbsolutePath());
}
}
}