Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/DAT-7531' into DAT-7447
Browse files Browse the repository at this point in the history
  • Loading branch information
wwillard7800 committed Jan 25, 2022
2 parents 545632e + dbef2bc commit b4fae37
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 21 deletions.
Expand Up @@ -42,7 +42,7 @@ public class ExecuteShellCommandChange extends AbstractChange {
protected List<String> finalCommandArray;
private String executable;
private List<String> os;
private List<String> args = new ArrayList<String>();
private final List<String> args = new ArrayList<>();
private String timeout;
private static final Pattern TIMEOUT_PATTERN = Pattern.compile("^\\s*(\\d+)\\s*([sSmMhH]?)\\s*$");
private static final Long SECS_IN_MILLIS = 1000L;
Expand Down Expand Up @@ -190,8 +190,8 @@ protected void executeCommand(Database database) throws Exception {
int returnCode = 0;
try {
//output both stdout and stderr data from proc to stdout of this process
StreamGobbler errorGobbler = new StreamGobbler(p.getErrorStream(), errorStream);
StreamGobbler outputGobbler = new StreamGobbler(p.getInputStream(), inputStream);
StreamGobbler errorGobbler = createErrorGobbler(p.getErrorStream(), errorStream);
StreamGobbler outputGobbler = createErrorGobbler(p.getInputStream(), inputStream);

errorGobbler.start();
outputGobbler.start();
Expand Down Expand Up @@ -225,6 +225,10 @@ protected void executeCommand(Database database) throws Exception {
processResult(returnCode, errorStreamOut, infoStreamOut, database);
}

protected StreamGobbler createErrorGobbler(InputStream processStream, OutputStream outputStream) {
return new StreamGobbler(processStream, outputStream, Thread.currentThread());
}

/**
* Max bytes to copy from output to {@link #processResult(int, String, String, Database)}. If null, process all output.
* @return
Expand All @@ -238,10 +242,8 @@ protected Integer getMaxStreamGobblerOutput() {
* <p>
* Creates a scheduled task to destroy the process in given timeout milliseconds.
* This killer task will be cancelled if the process returns before the timeout value.
*
* @param process
* @param process
* @param timeoutInMillis waits for specified timeoutInMillis before destroying the process.
* It will wait indefinitely if timeoutInMillis is 0.
*/
@java.lang.SuppressWarnings("squid:S2142")
private int waitForOrKill(final Process process, final long timeoutInMillis) throws TimeoutException {
Expand Down Expand Up @@ -273,6 +275,11 @@ public void run() {
}
} catch (InterruptedException ignore) {
// check again
if (timedOut.get()) {
timer.cancel();
String timeoutStr = timeout != null ? timeout : timeoutInMillis + " ms";
throw new TimeoutException("Process timed out (" + timeoutStr + ")");
}
}
}

Expand Down Expand Up @@ -368,22 +375,24 @@ protected void customLoadLogic(ParsedNode parsedNode, ResourceAccessor resourceA
}
}
}
private class StreamGobbler extends Thread {

public class StreamGobbler extends Thread {
private static final int THREAD_SLEEP_MILLIS = 100;
private final OutputStream outputStream;
private InputStream processStream;
boolean loggedTruncated = false;
long copiedSize = 0;
private final Thread parentThread;

private StreamGobbler(InputStream processStream, ByteArrayOutputStream outputStream) {
public StreamGobbler(InputStream processStream, OutputStream outputStream, Thread parentThread) {
this.processStream = processStream;
this.outputStream = outputStream;
this.parentThread = parentThread;
}

@Override
public void run() {
try {
BufferedInputStream bufferedInputStream = new BufferedInputStream(processStream);
try (BufferedInputStream bufferedInputStream = new BufferedInputStream(processStream)) {
while (processStream != null) {
if (bufferedInputStream.available() > 0) {
copy(bufferedInputStream, outputStream);
Expand All @@ -396,7 +405,10 @@ public void run() {
}
}
} catch (IOException ioe) {
ioe.printStackTrace();
Scope.getCurrentScope().getLog(ExecuteShellCommandChange.class).warning(ioe.getMessage());
if (parentThread != null) {
parentThread.interrupt();
}
}
}

Expand Down
@@ -1,13 +1,11 @@
package liquibase.changelog;

import liquibase.ContextExpression;
import liquibase.Labels;
import liquibase.RuntimeEnvironment;
import liquibase.Scope;
import liquibase.*;
import liquibase.changelog.filter.ChangeSetFilter;
import liquibase.changelog.filter.ChangeSetFilterResult;
import liquibase.changelog.visitor.ChangeSetVisitor;
import liquibase.changelog.visitor.SkippedChangeSetVisitor;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.exception.LiquibaseException;
import liquibase.exception.UnexpectedLiquibaseException;
import liquibase.exception.ValidationErrors;
Expand Down Expand Up @@ -137,7 +135,8 @@ private void validateChangeSetExecutor(ChangeSet changeSet, RuntimeEnvironment e
if (changeSet.getRunWith() == null) {
return;
}
String executorName = changeSet.getRunWith();
String executorName = ChangeSet.lookupExecutor(changeSet.getRunWith());

Executor executor;
try {
executor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(executorName, env.getTargetDatabase());
Expand Down
22 changes: 21 additions & 1 deletion liquibase-core/src/main/java/liquibase/changelog/ChangeSet.java
Expand Up @@ -8,6 +8,7 @@
import liquibase.change.core.EmptyChange;
import liquibase.change.core.RawSQLChange;
import liquibase.changelog.visitor.ChangeExecListener;
import liquibase.configuration.LiquibaseConfiguration;
import liquibase.database.Database;
import liquibase.database.DatabaseList;
import liquibase.database.ObjectQuotingStrategy;
Expand Down Expand Up @@ -719,7 +720,8 @@ private Executor setupCustomExecutorIfNecessary(Database database) {
if (getRunWith() == null || originalExecutor instanceof LoggingExecutor) {
return originalExecutor;
}
Executor customExecutor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(getRunWith(), database);
String executorName = ChangeSet.lookupExecutor(getRunWith());
Executor customExecutor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(executorName, database);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, customExecutor);
List<Change> changes = getChanges();
for (Change change : changes) {
Expand All @@ -735,6 +737,24 @@ private Executor setupCustomExecutorIfNecessary(Database database) {
return originalExecutor;
}

public static String lookupExecutor(String executorName) {
if (StringUtil.isEmpty(executorName)) {
return null;
}
String key = "liquibase." + executorName + ".executor";
String replacementExecutorName =
(String)Scope.getCurrentScope().getSingleton(LiquibaseConfiguration.class).getCurrentConfiguredValue(null, null, key).getValue();
if (replacementExecutorName != null) {
Scope.getCurrentScope().getLog(ChangeSet.class).info("Mapped '" + executorName + "' to executor '" + replacementExecutorName + "'");
return replacementExecutorName;
} else if (executorName.equalsIgnoreCase("native")) {
String message = "Unable to locate an executor for 'runWith=native'. You must specify a valid executor name.";
Scope.getCurrentScope().getLog(ChangeSet.class).warning(message);
Scope.getCurrentScope().getUI().sendErrorMessage("WARNING: " + message);
}
return executorName;
}

public void rollback(Database database) throws RollbackFailedException {
rollback(database, null);
}
Expand Down
@@ -0,0 +1,55 @@
#### _ _ _ _ _____
## | | (_) (_) | | __ \
## | | _ __ _ _ _ _| |__ __ _ ___ ___ | |__) | __ ___
## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ | ___/ '__/ _ \
## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ | | | | | (_) |
## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| |_| |_| \___/
## | |
## |_|
##
## The liquibase.sqlcmd.conf file stores properties which are used during the
## execution of the Microsoft SQLCMD tool.
## Learn more: https://www.liquibase.org/documentation/config_properties.html
####
####
## Note about relative and absolute paths:
## The liquibase.sqlcmd.path must be a valid path to the SQLCMD executable.
## The liquibase.sqlcmd.timeout value can be one of:
## -1 - disable the timeout
## Any integer value > 0 (measured in seconds)
##
####

# The full path to the SQLCMD executable.
# Sample Linux path
# liquibase.sqlcmd.path=/opt/mssql-tools/bin/sqlcmd
# Sample Windows path
# liquibase.sqlcmd.path="C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\170\\Tools\\Binn\\SQLCMD.EXE"

# A valid timeout value for the execution of the SQLCMD tool
liquibase.sqlcmd.timeout=-1

# Flag to indicate whether or not to keep the temporary SQL file after execution of SQLCMD.
# True = keep False = delete (default)
liquibase.sqlcmd.keep.temp=true

# OPTIONAL Flag to designate the location to store temporary SQL file after execution of SQLCMD.
# Liquibase will attempt to use path exactly as entered, so please ensure it complies with your OS requirements.
# liquibase.sqlcmd.keep.temp.path=

# OPTIONAL Flag to designate the name of temporary SQL file after execution of SQLCMD.
# Liquibase will attempt to use the name exactly as entered, so please ensure it complies with your OS requirements.
# liquibase.sqlcmd.keep.temp.name=

# OPTIONAL Args to pass directly to SQLCMD.
# Learn about SQLCMD args at https://<link>
# Note: The delimiter for args is a space eg:" " and not "," or ";" separated.
# liquibase.sqlcmd.args=

# OPTIONAL Path to a log file for the SQLCMD output
# liquibase.sqlcmd.logFile=
#

# OPTIONAL Name of a custom executor to use instead of SQLCMD
# The Executor must be on the Liquibase classpath
# liquibase.sqlcmd.executor=
Expand Up @@ -21,9 +21,9 @@
####

# The full path to the SQLPLUS executable.
# Sample linux path
# Sample Linux path
# liquibase.sqlplus.path=/apps/app/12.2.0.1.0/oracle/product/12.2.0.1.0/client_1/bin/sqlplus
# Sample windows path
# Sample Windows path
# liquibase.sqlplus.path=c:\\oracle\\product\\11.2.0\\client_1\\bin\\sqlplus.exe

# A valid timeout value for the execution of the SQLPLUS tool
Expand All @@ -45,3 +45,7 @@ liquibase.sqlplus.keep.temp=true
# Learn about SQLPLUS args at https://docs.oracle.com/cd/B10501_01/server.920/a90842/ch4.htm
# Note: The delimiter for args is a space eg:" " and not "," or ";" separated.
# liquibase.sqlplus.args=

# OPTIONAL Name of a custom executor to use instead of SQLPLUS
# The Executor must be on the Liquibase classpath
# liquibase.sqlplus.executor=
@@ -0,0 +1,54 @@
#### _ _ _ _ _____
## | | (_) (_) | | __ \
## | | _ __ _ _ _ _| |__ __ _ ___ ___ | |__) | __ ___
## | | | |/ _` | | | | | '_ \ / _` / __|/ _ \ | ___/ '__/ _ \
## | |___| | (_| | |_| | | |_) | (_| \__ \ __/ | | | | | (_) |
## \_____/_|\__, |\__,_|_|_.__/ \__,_|___/\___| |_| |_| \___/
## | |
## |_|
##
## The liquibase.sqlcmd.conf file stores properties which are used during the
## execution of the Microsoft SQLCMD tool.
## Learn more: https://www.liquibase.org/documentation/config_properties.html
####
####
## Note about relative and absolute paths:
## The liquibase.sqlcmd.path must be a valid path to the SQLCMD executable.
## The liquibase.sqlcmd.timeout value can be one of:
## -1 - disable the timeout
## Any integer value > 0 (measured in seconds)
##
####

# The full path to the SQLCMD executable.
# Sample Linux path
# liquibase.sqlcmd.path=/opt/mssql-tools/bin/sqlcmd
# Sample Windows path
# liquibase.sqlcmd.path="C:\\Program Files\\Microsoft SQL Server\\Client SDK\\ODBC\\170\\Tools\\Binn\\SQLCMD.EXE"

# A valid timeout value for the execution of the SQLCMD tool
liquibase.sqlcmd.timeout=-1

# Flag to indicate whether or not to keep the temporary SQL file after execution of SQLCMD.
# True = keep False = delete (default)
liquibase.sqlcmd.keep.temp=true

# OPTIONAL Flag to designate the location to store temporary SQL file after execution of SQLCMD.
# Liquibase will attempt to use path exactly as entered, so please ensure it complies with your OS requirements.
# liquibase.sqlcmd.keep.temp.path=

# OPTIONAL Flag to designate the name of temporary SQL file after execution of SQLCMD.
# Liquibase will attempt to use the name exactly as entered, so please ensure it complies with your OS requirements.
# liquibase.sqlcmd.keep.temp.name=

# OPTIONAL Args to pass directly to SQLCMD.
# Learn about SQLCMD args at https://<link>
# Note: The delimiter for args is a space eg:" " and not "," or ";" separated.
# liquibase.sqlcmd.args=

# OPTIONAL Path to a log file for the SQLCMD output
# liquibase.sqlcmd.logFile=

# OPTIONAL Name of a custom executor to use instead of SQLCMD
# The Executor must be on the Liquibase classpath
# liquibase.sqlcmd.executor=
Expand Up @@ -21,9 +21,9 @@
####

# The full path to the SQLPLUS executable.
# Sample linux path
# Sample Linux path
# liquibase.sqlplus.path=/apps/app/12.2.0.1.0/oracle/product/12.2.0.1.0/client_1/bin/sqlplus
# Sample windows path
# Sample Windows path
# liquibase.sqlplus.path=c:\\oracle\\product\\11.2.0\\client_1\\bin\\sqlplus.exe

# A valid timeout value for the execution of the SQLPLUS tool
Expand All @@ -45,3 +45,7 @@ liquibase.sqlplus.keep.temp=true
# Learn about SQLPLUS args at https://docs.oracle.com/cd/B10501_01/server.920/a90842/ch4.htm
# Note: The delimiter for args is a space eg:" " and not "," or ";" separated.
# liquibase.sqlplus.args=

# OPTIONAL Name of a custom executor to use instead of SQLPLUS
# The Executor must be on the Liquibase classpath
# liquibase.sqlplus.executor=
@@ -0,0 +1 @@
liquibase.executor.jvm.ExampleExecutor

0 comments on commit b4fae37

Please sign in to comment.