25 |
25 |
import org.apache.log4j.Level;
|
26 |
26 |
import org.apache.log4j.Logger;
|
27 |
27 |
|
|
28 |
import com.fasterxml.jackson.core.type.TypeReference;
|
|
29 |
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
30 |
|
28 |
31 |
/**
|
29 |
32 |
* Java executor for drush (https://www.drush.org/).
|
30 |
33 |
*
|
... | ... | |
55 |
58 |
}
|
56 |
59 |
|
57 |
60 |
/**
|
58 |
|
* List indexes returned from
|
59 |
|
* <code>DrushExecuter.execute(DrushCommand cmd, String... value)</code>:
|
|
61 |
* The execution of this command via
|
|
62 |
* <code>DrushExecuter.execute({@linkplain DrushCommand#version})</code> results in
|
|
63 |
* a {@code List<String>} return variable with the following elements:
|
60 |
64 |
*
|
61 |
65 |
* <ol>
|
62 |
66 |
* <li>major</li>
|
... | ... | |
72 |
76 |
public static DrushCommand coreStatus = new DrushCommand(Arrays.asList("core-status"), null, null);
|
73 |
77 |
|
74 |
78 |
/**
|
75 |
|
* List indexes returned from
|
76 |
|
* <code>DrushExecuter.execute(DrushCommand cmd, String... value)</code>:
|
77 |
|
* Multiple matches are possible:
|
|
79 |
* Executes {@code drush vget --exact <variable-key>}
|
|
80 |
* <p>
|
|
81 |
* The execution of this command via
|
|
82 |
* <code>DrushExecuter.execute({@linkplain DrushCommand#variableSet})</code> results in
|
|
83 |
* a {@code List<String>} return variable with the following elements:
|
|
84 |
*
|
78 |
85 |
* <ol>
|
79 |
86 |
* <li>value</li>
|
80 |
87 |
* <li>value</li>
|
81 |
88 |
* <li>value</li>
|
82 |
89 |
* <ol>
|
83 |
90 |
*/
|
84 |
|
public static DrushCommand variableGet = new DrushCommand(Arrays.asList("vget", "%s"), ".*:\\s+'(?<value>.*)'",
|
85 |
|
null);
|
86 |
|
|
|
91 |
public static DrushCommand variableGet = new DrushCommand(Arrays.asList("vget", "--exact", "--format=json", "%s"));
|
87 |
92 |
/**
|
88 |
|
* List indexes returned from
|
89 |
|
* <code>DrushExecuter.execute(DrushCommand cmd, String... value)</code>:
|
90 |
|
* Multiple matches are possible:
|
|
93 |
* Executes {@code drush vset --exact <variable-key> <variable-value>}
|
|
94 |
* <p>
|
|
95 |
* The execution of this command via
|
|
96 |
* <code>DrushExecuter.execute({@linkplain DrushCommand#variableSet})</code> results in
|
|
97 |
* a {@code List<String>} return variable with the following elements:
|
|
98 |
*
|
91 |
99 |
* <ol>
|
92 |
100 |
* <li>value</li>
|
93 |
101 |
* <li>status</li>
|
94 |
102 |
* <ol>
|
95 |
103 |
*/
|
96 |
104 |
public static DrushCommand variableSet = new DrushCommand(Arrays.asList("--yes", "vset", "%s", "%s"), null,
|
97 |
|
"[^\\\"]*\\\"(?<value>.*)\\\"\\.\\s+\\[(?<status>\\w+)\\]");
|
|
105 |
"[^\\\"]*\\\"(.*)\\\".*\\[(\\w+)\\]"
|
|
106 |
);
|
98 |
107 |
|
99 |
108 |
/**
|
100 |
109 |
* @throws IOException
|
... | ... | |
108 |
117 |
throw new RuntimeException("not yet implmented for Windows");
|
109 |
118 |
}
|
110 |
119 |
|
111 |
|
List<String> matches = execute(version);
|
112 |
|
assert !matches.get(0).isEmpty() : "No suitable drush command found in the system";
|
113 |
|
String majorVersion = matches.get(0);
|
|
120 |
List<Object> matches = execute(version);
|
|
121 |
assert !((String) matches.get(0)).isEmpty() : "No suitable drush command found in the system";
|
|
122 |
String majorVersion = (String) matches.get(0);
|
114 |
123 |
if (Integer.valueOf(majorVersion) < 8) {
|
115 |
124 |
throw new RuntimeException("drush version >= 8 required");
|
116 |
125 |
}
|
... | ... | |
123 |
132 |
* @throws InterruptedException
|
124 |
133 |
* if the Process was interrupted
|
125 |
134 |
*/
|
126 |
|
public List<String> execute(DrushCommand cmd, String... value) throws IOException, InterruptedException {
|
|
135 |
public List<Object> execute(DrushCommand cmd, String... value) throws IOException, InterruptedException {
|
127 |
136 |
|
128 |
137 |
List<String> executableWithArgs = new ArrayList<>();
|
129 |
138 |
|
... | ... | |
154 |
163 |
}
|
155 |
164 |
}
|
156 |
165 |
|
157 |
|
List<String> matches = new ArrayList<>();
|
|
166 |
List<Object> matches = new ArrayList<>();
|
158 |
167 |
|
159 |
168 |
ProcessBuilder pb = new ProcessBuilder(executableWithArgs);
|
160 |
169 |
logger.debug("Command: " + pb.command().toString());
|
... | ... | |
162 |
171 |
int exitCode = process.waitFor();
|
163 |
172 |
|
164 |
173 |
if (exitCode == 0) {
|
165 |
|
String out = readExecutionResponse(matches, process.getInputStream(), cmd.outRegex);
|
166 |
|
String error = readExecutionResponse(matches, process.getErrorStream(), cmd.errRegex);
|
|
174 |
String out, error;
|
|
175 |
if(cmd.jsonResult) {
|
|
176 |
out = readExecutionResponse(matches, process.getInputStream());
|
|
177 |
error = readExecutionResponse(matches, process.getErrorStream());
|
|
178 |
} else {
|
|
179 |
out = readExecutionResponse(matches, process.getInputStream(), cmd.outRegex);
|
|
180 |
error = readExecutionResponse(matches, process.getErrorStream(), cmd.errRegex);
|
|
181 |
}
|
167 |
182 |
if (out != null && !out.isEmpty()) {
|
168 |
183 |
logger.error(error);
|
169 |
184 |
}
|
... | ... | |
176 |
191 |
return matches;
|
177 |
192 |
}
|
178 |
193 |
|
179 |
|
protected String readExecutionResponse(List<String> matches, InputStream stream, Pattern regex) throws IOException {
|
|
194 |
protected String readExecutionResponse(List<Object> matches, InputStream stream, Pattern regex) throws IOException {
|
180 |
195 |
String out;
|
181 |
196 |
if (regex != null) {
|
182 |
197 |
Scanner scanner = new Scanner(stream);
|
... | ... | |
209 |
224 |
logger.debug(out);
|
210 |
225 |
return out;
|
211 |
226 |
}
|
|
227 |
}
|
212 |
228 |
|
|
229 |
/**
|
|
230 |
* @param matches
|
|
231 |
* @param stream
|
|
232 |
* @return depending on the drupal variable type different return types are possible:
|
|
233 |
* <ul>
|
|
234 |
* <li>Object</li>
|
|
235 |
* <li>List</li>
|
|
236 |
* <li>List</li>
|
|
237 |
* <li>String</li>
|
|
238 |
* <li>Double</li>
|
|
239 |
* <li>Integer</li>
|
|
240 |
* </ul>
|
|
241 |
*
|
|
242 |
* @throws IOException
|
|
243 |
*/
|
|
244 |
protected String readExecutionResponse(List<Object> matches, InputStream stream) throws IOException {
|
|
245 |
String out = IOUtils.toString(stream);
|
|
246 |
if(out != null) {
|
|
247 |
out = out.trim();
|
|
248 |
if(!out.isEmpty()) {
|
|
249 |
ObjectMapper mapper = new ObjectMapper();
|
|
250 |
if(out.startsWith("[")) {
|
|
251 |
matches.add(mapper.readValue(out, new TypeReference<List<Object>>(){}));
|
|
252 |
} else {
|
|
253 |
matches.add(mapper.readValue(out, Object.class));
|
|
254 |
}
|
|
255 |
if(matches.isEmpty()) {
|
|
256 |
logger.debug("no result");
|
|
257 |
} else {
|
|
258 |
logger.debug("result object: " + matches.get(0));
|
|
259 |
}
|
|
260 |
}
|
|
261 |
|
|
262 |
}
|
|
263 |
return out;
|
213 |
264 |
}
|
214 |
265 |
|
215 |
266 |
public static class DrushCommand {
|
216 |
267 |
|
217 |
268 |
Pattern outRegex;
|
218 |
269 |
Pattern errRegex;
|
|
270 |
boolean jsonResult = false;
|
219 |
271 |
List<String> args = new ArrayList<>();
|
220 |
272 |
|
221 |
273 |
public DrushCommand(List<String> args, String outRegex, String errRegex) {
|
... | ... | |
227 |
279 |
this.errRegex = Pattern.compile(errRegex, Pattern.MULTILINE);
|
228 |
280 |
}
|
229 |
281 |
}
|
|
282 |
|
|
283 |
/**
|
|
284 |
* For drush commands suopporting the {@code --format=json} option.
|
|
285 |
* @param args
|
|
286 |
*/
|
|
287 |
public DrushCommand(List<String> args) {
|
|
288 |
this.args = args;
|
|
289 |
this.jsonResult = true;
|
|
290 |
}
|
230 |
291 |
}
|
231 |
292 |
|
232 |
293 |
/**
|
... | ... | |
239 |
300 |
DrushExecuter.logger.setLevel(Level.DEBUG);
|
240 |
301 |
try {
|
241 |
302 |
DrushExecuter dex = new DrushExecuter();
|
|
303 |
List<Object> results;
|
242 |
304 |
dex.setDrupalRoot(new File("/home/andreas/workspaces/www/drupal-7"));
|
243 |
305 |
dex.setSiteURI(new URI("http://edit.test/d7/caryophyllales/"));
|
244 |
|
dex.execute(coreStatus);
|
245 |
|
dex.execute(help);
|
246 |
|
List<String> results = dex.execute(variableSet, "cdm_webservice_url",
|
|
306 |
// dex.execute(coreStatus);
|
|
307 |
// dex.execute(help);
|
|
308 |
results = dex.execute(variableSet, "cdm_webservice_url",
|
247 |
309 |
"http://api.cybertaxonomy.org/cyprus/");
|
248 |
310 |
if (!results.get(0).equals("http://api.cybertaxonomy.org/cyprus/")) {
|
249 |
311 |
throw new RuntimeException("unexpected result item 0: " + results.get(0));
|
... | ... | |
251 |
313 |
if (!results.get(1).equals("success")) {
|
252 |
314 |
throw new RuntimeException("unexpected result item 1: " + results.get(0));
|
253 |
315 |
}
|
|
316 |
results = dex.execute(variableGet, "cdm_webservice_url");
|
|
317 |
if (!results.get(0).equals("http://api.cybertaxonomy.org/cyprus/")) {
|
|
318 |
throw new RuntimeException("unexpected result item 0: " + results.get(0));
|
|
319 |
}
|
254 |
320 |
// testing remote execution via ssh
|
255 |
321 |
dex.sshHost = "edit-int";
|
256 |
322 |
dex.setDrupalRoot(new File("/var/www/drupal-7"));
|
ref #9181 DrushExecuter reading json values