Skip to content

Package: KapuaLiquibaseClient

KapuaLiquibaseClient

nameinstructionbranchcomplexitylinemethod
KapuaLiquibaseClient(String, String, String)
M: 8 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
KapuaLiquibaseClient(String, String, String, Optional)
M: 10 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 2 C: 0
0%
M: 1 C: 0
0%
KapuaLiquibaseClient(String, String, String, String)
M: 77 C: 0
0%
M: 4 C: 0
0%
M: 3 C: 0
0%
M: 26 C: 0
0%
M: 1 C: 0
0%
executeMasters(Connection, String, File, List)
M: 37 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
executeMasters(Connection, String, File, String, List)
M: 76 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 15 C: 0
0%
M: 1 C: 0
0%
forceReleaseChangelogLock()
M: 46 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 10 C: 0
0%
M: 1 C: 0
0%
lambda$executeMasters$0(String, File, String)
M: 4 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 1 C: 0
0%
M: 1 C: 0
0%
loadChangelogs()
M: 116 C: 0
0%
M: 12 C: 0
0%
M: 7 C: 0
0%
M: 19 C: 0
0%
M: 1 C: 0
0%
static {...}
M: 11 C: 0
0%
M: 0 C: 0
100%
M: 1 C: 0
0%
M: 3 C: 0
0%
M: 1 C: 0
0%
update()
M: 62 C: 0
0%
M: 6 C: 0
0%
M: 4 C: 0
0%
M: 15 C: 0
0%
M: 1 C: 0
0%

Coverage

1: /*******************************************************************************
2: * Copyright (c) 2017, 2022 Red Hat Inc and others.
3: *
4: * This program and the accompanying materials are made
5: * available under the terms of the Eclipse Public License 2.0
6: * which is available at https://www.eclipse.org/legal/epl-2.0/
7: *
8: * SPDX-License-Identifier: EPL-2.0
9: *
10: * Contributors:
11: * Red Hat Inc - initial API and implementation
12: * Eurotech
13: *******************************************************************************/
14: package org.eclipse.kapua.commons.liquibase;
15:
16: import com.google.common.base.Strings;
17: import liquibase.Liquibase;
18: import liquibase.database.Database;
19: import liquibase.database.DatabaseFactory;
20: import liquibase.database.jvm.JdbcConnection;
21: import liquibase.exception.LiquibaseException;
22: import liquibase.resource.FileSystemResourceAccessor;
23: import org.apache.commons.io.FileUtils;
24: import org.apache.commons.io.IOUtils;
25: import org.apache.commons.lang3.SystemUtils;
26: import org.eclipse.kapua.commons.liquibase.settings.LiquibaseClientSettingKeys;
27: import org.eclipse.kapua.commons.liquibase.settings.LiquibaseClientSettings;
28: import org.eclipse.kapua.commons.util.SemanticVersion;
29: import org.eclipse.kapua.commons.util.log.ConfigurationPrinter;
30: import org.reflections.Reflections;
31: import org.reflections.scanners.ResourcesScanner;
32: import org.slf4j.Logger;
33: import org.slf4j.LoggerFactory;
34:
35: import java.io.File;
36: import java.io.FileOutputStream;
37: import java.io.IOException;
38: import java.net.URL;
39: import java.sql.Connection;
40: import java.sql.DriverManager;
41: import java.sql.SQLException;
42: import java.util.ArrayList;
43: import java.util.Arrays;
44: import java.util.Comparator;
45: import java.util.List;
46: import java.util.Optional;
47: import java.util.Set;
48: import java.util.regex.Pattern;
49:
50: /**
51: * Client that execute {@link Liquibase} scripts.
52: * <p>
53: * It looks available scripts in {@code .xml} or {@code .sql} in the classpath.
54: *
55: * @since 1.0.0
56: */
57: public class KapuaLiquibaseClient {
58:
59: private static final Logger LOG = LoggerFactory.getLogger(KapuaLiquibaseClient.class);
60:
61: private static final SemanticVersion LIQUIBASE_TIMESTAMP_FIX_VERSION = new SemanticVersion("3.3.3"); // https://liquibase.jira.com/browse/CORE-1958
62:
63: private static final LiquibaseClientSettings LIQUIBASE_CLIENT_SETTINGS = LiquibaseClientSettings.getInstance();
64:
65: private final String jdbcUrl;
66: private final String username;
67: private final String password;
68: private final String schema;
69: private final boolean runTimestampsFix;
70:
71: /**
72: * Constructor.
73: *
74: * @param jdbcUrl The JDBC connection string.
75: * @param username The username to connect to to the database.
76: * @param password The password to connect to to the database.
77: * @since 1.0.0
78: */
79: public KapuaLiquibaseClient(String jdbcUrl, String username, String password) {
80: this(jdbcUrl, username, password, (String) null);
81: }
82:
83: /**
84: * Constructor.
85: *
86: * @param jdbcUrl The JDBC connection string.
87: * @param username The username to connect to to the database.
88: * @param password The password to connect to to the database.
89: * @param schema The {@link java.util.Optional} schema name.
90: * @since 1.0.0
91: * @deprecated Since 1.2.0. Removed usage of {@link Optional} as parameter.
92: */
93: @Deprecated
94: public KapuaLiquibaseClient(String jdbcUrl, String username, String password, Optional<String> schema) {
95: this(jdbcUrl, username, password, schema.orElse(null));
96: }
97:
98: /**
99: * Constructor.
100: *
101: * @param jdbcUrl The JDBC connection string.
102: * @param username The username to connect to to the database.
103: * @param password The password to connect to to the database.
104: * @param schema The schema name.
105: * @since 1.2.0
106: */
107: public KapuaLiquibaseClient(String jdbcUrl, String username, String password, String schema) {
108: this.jdbcUrl = jdbcUrl;
109: this.username = username;
110: this.password = password;
111: this.schema = schema;
112:
113: // Check wether or not fix the timestamp based on Liquibase version
114: boolean forceTimestampFix = LIQUIBASE_CLIENT_SETTINGS.getBoolean(LiquibaseClientSettingKeys.FORCE_TIMESTAMPS_FIX);
115: String currentLiquibaseVersionString = LIQUIBASE_CLIENT_SETTINGS.getString(LiquibaseClientSettingKeys.LIQUIBASE_VERSION);
116: SemanticVersion currentLiquibaseVersion = new SemanticVersion(currentLiquibaseVersionString);
117:
118:• runTimestampsFix = (currentLiquibaseVersion.afterOrMatches(LIQUIBASE_TIMESTAMP_FIX_VERSION) || forceTimestampFix);
119:
120: // Print Configurations
121: ConfigurationPrinter
122: .create()
123: .withLogger(LOG)
124: .withLogLevel(ConfigurationPrinter.LogLevel.INFO)
125: .withTitle("KapuaLiquibaseClient Configuration")
126: .addParameter("Liquibase Version", currentLiquibaseVersionString)
127: .openSection("DB connection info")
128: .addParameter("JDBC URL", jdbcUrl)
129: .addParameter("Username", username)
130: .addParameter("Password", "******")
131: .addParameter("Schema", schema)
132: .closeSection()
133: .openSection("Timestamp(3) fix info (eclipse/kapua#2889)")
134: .addParameter("Force timestamp fix", forceTimestampFix)
135: .addParameter("Apply timestamp fix", runTimestampsFix)
136: .closeSection()
137: .printLog();
138: }
139:
140: /**
141: * Starts the looking and execution of the Liquibase Scripts.
142: *
143: * @since 1.0.0
144: */
145: public void update() {
146: try {
147: LOG.info("Running Liquibase scripts...");
148:• if (Boolean.parseBoolean(System.getProperty("LIQUIBASE_ENABLED", "true")) || Boolean.parseBoolean(System.getenv("LIQUIBASE_ENABLED"))) {
149:
150: try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
151: File changelogDir = loadChangelogs();
152:
153: List<String> contexts = new ArrayList<>();
154:• if (!runTimestampsFix) {
155: contexts.add("!fixTimestamps");
156: }
157:
158: executeMasters(connection, schema, changelogDir, contexts);
159: }
160:
161: LOG.info("Running Liquibase scripts... DONE!");
162: } else {
163: LOG.info("Running Liquibase scripts... SKIPPED! Liquibase disabled by System property 'LIQUIBASE_ENABLED'...");
164: }
165: } catch (LiquibaseException | SQLException | IOException e) {
166: LOG.error("Running Liquibase scripts... ERROR! Error: {}", e.getMessage(), e);
167: throw new RuntimeException(e); // TODO: throw an appropriate exception!
168: }
169: }
170:
171: public void forceReleaseChangelogLock() {
172: LOG.info("Trying to release changelog lock...");
173: try (Connection connection = DriverManager.getConnection(jdbcUrl, username, password)) {
174: Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
175: Liquibase liquibase = new Liquibase((String) null, new FileSystemResourceAccessor(), database);
176: liquibase.forceReleaseLocks();
177: } catch (LiquibaseException | SQLException e) {
178: LOG.error("Running release changelog lock... ERROR! Error: {}", e.getMessage(), e);
179: throw new RuntimeException(e); // TODO: throw an appropriate exception!
180: }
181: }
182:
183: protected static synchronized File loadChangelogs() throws IOException {
184: String tmpDirectory = SystemUtils.getJavaIoTmpDir().getAbsolutePath();
185:
186: File changelogTempDirectory = new File(tmpDirectory, "kapua-liquibase");
187:
188:• if (changelogTempDirectory.exists()) {
189: FileUtils.deleteDirectory(changelogTempDirectory);
190: }
191:
192: boolean createdTmp = changelogTempDirectory.mkdirs();
193:• LOG.trace("{} Tmp dir: {}", createdTmp ? "Created" : "Using", changelogTempDirectory.getAbsolutePath());
194:
195: Reflections reflections = new Reflections("liquibase", new ResourcesScanner());
196: Set<String> changeLogs = reflections.getResources(Pattern.compile(".*\\.xml|.*\\.sql"));
197:• for (String script : changeLogs) {
198: URL scriptUrl = KapuaLiquibaseClient.class.getResource("/" + script);
199: File changelogFile = new File(changelogTempDirectory, script.replaceFirst("liquibase/", ""));
200:• if (changelogFile.getParentFile() != null && !changelogFile.getParentFile().exists()) {
201: boolean createdParent = changelogFile.getParentFile().mkdirs();
202:• LOG.trace("{} parent dir: {}", createdParent ? "Created" : "Using", changelogFile.getParentFile().getAbsolutePath());
203: }
204: try (FileOutputStream tmpStream = new FileOutputStream(changelogFile)) {
205: IOUtils.write(IOUtils.toString(scriptUrl), tmpStream);
206: }
207: LOG.trace("Copied file: {}", changelogFile.getAbsolutePath());
208: }
209:
210: return changelogTempDirectory;
211: }
212:
213: protected static void executeMasters(Connection connection, String schema, File changelogDir, List<String> contexts) throws LiquibaseException {
214: //
215: // Find and execute all master scripts
216: LOG.info("Executing pre master files...");
217: executeMasters(connection, schema, changelogDir, "-master.pre.xml", contexts);
218: LOG.info("Executing pre master files... DONE!");
219:
220: LOG.info("Executing master files...");
221: executeMasters(connection, schema, changelogDir, "-master.xml", contexts);
222: LOG.info("Executing master files... DONE!");
223:
224: LOG.info("Executing post master files...");
225: executeMasters(connection, schema, changelogDir, "-master.post.xml", contexts);
226: LOG.info("Executing post master files... DONE!");
227: }
228:
229: protected static void executeMasters(Connection connection, String schema, File changelogTempDirectory, String preMaster, List<String> contexts) throws LiquibaseException {
230: List<File> masterChangelogs = Arrays.asList(changelogTempDirectory.listFiles((dir, name) -> name.endsWith(preMaster)));
231:
232: LOG.info("\tMaster Liquibase files found: {}", masterChangelogs.size());
233:
234: LOG.trace("\tSorting master Liquibase files found.");
235: masterChangelogs.sort(Comparator.comparing(File::getAbsolutePath));
236:
237:• String ctx = contexts.isEmpty() ? null : String.join(",", contexts);
238:• for (File masterChangelog : masterChangelogs) {
239: LOG.info("\t\tExecuting liquibase script: {}...", masterChangelog.getAbsolutePath());
240: Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(new JdbcConnection(connection));
241:• if (!Strings.isNullOrEmpty(schema)) {
242: database.setDefaultSchemaName(schema);
243: }
244: Liquibase liquibase = new Liquibase(masterChangelog.getAbsolutePath(), new FileSystemResourceAccessor(), database);
245: liquibase.update(ctx);
246:
247: LOG.debug("\t\tExecuting liquibase script: {}... DONE!", masterChangelog.getAbsolutePath());
248: }
249: }
250:
251: }