1 package liquibase.change.core;
2
3 import liquibase.change.AbstractChange;
4 import liquibase.change.ChangeMetaData;
5 import liquibase.database.Database;
6 import liquibase.exception.UnexpectedLiquibaseException;
7 import liquibase.exception.ValidationErrors;
8 import liquibase.exception.Warnings;
9 import liquibase.executor.Executor;
10 import liquibase.executor.ExecutorService;
11 import liquibase.executor.LoggingExecutor;
12 import liquibase.logging.LogFactory;
13 import liquibase.sql.Sql;
14 import liquibase.statement.SqlStatement;
15 import liquibase.statement.core.CommentStatement;
16 import liquibase.statement.core.RuntimeStatement;
17 import liquibase.util.StreamUtil;
18 import liquibase.util.StringUtils;
19
20 import java.io.ByteArrayOutputStream;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.List;
24
25
26
27
28 public class ExecuteShellCommandChange extends AbstractChange {
29
30 private String executable;
31 private List<String> os;
32 private List<String> args = new ArrayList<String>();
33
34 public ExecuteShellCommandChange() {
35 super("executeCommand", "Execute Shell Command", ChangeMetaData.PRIORITY_DEFAULT);
36 }
37
38 public String getExecutable() {
39 return executable;
40 }
41
42 public void setExecutable(String executable) {
43 this.executable = executable;
44 }
45
46 public void addArg(String arg) {
47 this.args.add(arg);
48 }
49
50 public void setOs(String os) {
51 this.os = StringUtils.splitAndTrim(os, ",");
52 }
53
54 public List<String> getOs() {
55 return os;
56 }
57
58 @Override
59 public ValidationErrors validate(Database database) {
60 return new ValidationErrors();
61 }
62
63 @Override
64 public Warnings warn(Database database) {
65 return new Warnings();
66 }
67
68 public SqlStatement[] generateStatements(final Database database) {
69 boolean shouldRun = true;
70 if (os != null && os.size() > 0) {
71 String currentOS = System.getProperty("os.name");
72 if (!os.contains(currentOS)) {
73 shouldRun = false;
74 LogFactory.getLogger().info("Not executing on os " + currentOS + " when " + os + " was specified");
75 }
76 }
77
78
79 boolean nonExecutedMode = false;
80 Executor executor = ExecutorService.getInstance().getExecutor(database);
81 if (executor instanceof LoggingExecutor) {
82 nonExecutedMode = true;
83 }
84
85 if (shouldRun && !nonExecutedMode) {
86
87 return new SqlStatement[] { new RuntimeStatement() {
88
89 @Override
90 public Sql[] generate(Database database) {
91 List<String> commandArray = new ArrayList<String>();
92 commandArray.add(executable);
93 commandArray.addAll(args);
94
95 try {
96 ProcessBuilder pb = new ProcessBuilder(commandArray);
97 pb.redirectErrorStream(true);
98 Process p = pb.start();
99 int returnCode = 0;
100 try {
101 returnCode = p.waitFor();
102 } catch (InterruptedException e) {
103 ;
104 }
105
106 ByteArrayOutputStream errorStream = new ByteArrayOutputStream();
107 ByteArrayOutputStream inputStream = new ByteArrayOutputStream();
108 StreamUtil.copy(p.getErrorStream(), errorStream);
109 StreamUtil.copy(p.getInputStream(), inputStream);
110
111 LogFactory.getLogger().severe(errorStream.toString());
112 LogFactory.getLogger().info(inputStream.toString());
113
114 if (returnCode != 0) {
115 throw new RuntimeException(getCommandString() + " returned an code of " + returnCode);
116 }
117 } catch (IOException e) {
118 throw new UnexpectedLiquibaseException("Error executing command: " + e);
119 }
120
121 return null;
122 }
123 } };
124 }
125
126 if (nonExecutedMode) {
127 return new SqlStatement[] { new CommentStatement(getCommandString()) };
128 }
129
130 return new SqlStatement[0];
131 }
132
133 public String getConfirmationMessage() {
134 return "Shell command '" + getCommandString() + "' executed";
135 }
136
137 private String getCommandString() {
138 return executable + " " + StringUtils.join(args, " ");
139 }
140 }