1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.jclouds.tools.ant.taskdefs.sshjava;
20
21 import static com.google.common.base.Preconditions.checkNotNull;
22 import static org.jclouds.scriptbuilder.domain.Statements.exec;
23
24 import java.io.BufferedWriter;
25 import java.io.File;
26 import java.io.FileWriter;
27 import java.io.IOException;
28 import java.security.SecureRandom;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Map.Entry;
33 import java.util.concurrent.TimeoutException;
34
35 import org.apache.tools.ant.BuildException;
36 import org.apache.tools.ant.Location;
37 import org.apache.tools.ant.Project;
38 import org.apache.tools.ant.Target;
39 import org.apache.tools.ant.Task;
40 import org.apache.tools.ant.taskdefs.Java;
41 import org.apache.tools.ant.taskdefs.Replace;
42 import org.apache.tools.ant.taskdefs.Replace.Replacefilter;
43 import org.apache.tools.ant.taskdefs.optional.ssh.SSHUserInfo;
44 import org.apache.tools.ant.taskdefs.optional.ssh.Scp;
45 import org.apache.tools.ant.types.CommandlineJava;
46 import org.apache.tools.ant.types.Environment;
47 import org.apache.tools.ant.types.FileSet;
48 import org.apache.tools.ant.types.Path;
49 import org.apache.tools.ant.types.Environment.Variable;
50 import org.jclouds.scriptbuilder.InitBuilder;
51 import org.jclouds.scriptbuilder.domain.OsFamily;
52 import org.jclouds.scriptbuilder.domain.ShellToken;
53 import org.jclouds.scriptbuilder.domain.Statement;
54 import org.jclouds.scriptbuilder.domain.StatementList;
55 import org.jclouds.scriptbuilder.domain.Statements;
56 import org.jclouds.tools.ant.util.SSHExecute;
57
58 import com.google.common.annotations.VisibleForTesting;
59 import com.google.common.base.Function;
60 import com.google.common.base.Joiner;
61 import com.google.common.collect.ImmutableList;
62 import com.google.common.collect.Iterables;
63 import com.google.common.collect.Lists;
64 import com.google.common.collect.Maps;
65 import com.jcraft.jsch.JSchException;
66
67
68
69
70
71
72 public class SSHJava extends Java {
73 private final SSHExecute exec;
74 private final Scp scp;
75 private final SSHUserInfo userInfo;
76 private File localDirectory;
77 File remotebase;
78 @VisibleForTesting
79 File remotedir;
80 @VisibleForTesting
81 Environment env = new Environment();
82
83 private OsFamily osFamily = OsFamily.UNIX;
84 private File errorFile;
85 private String errorProperty;
86 private File outputFile;
87 private String outputProperty;
88 String id = "sshjava" + new SecureRandom().nextLong();
89 private boolean append;
90
91 @VisibleForTesting
92 final LinkedHashMap<String, String> shiftMap = Maps.newLinkedHashMap();
93 @VisibleForTesting
94 final LinkedHashMap<String, String> replace = Maps.newLinkedHashMap();
95
96 public SSHJava() {
97 super();
98 exec = new SSHExecute();
99 exec.setProject(getProject());
100 scp = new Scp();
101 userInfo = new SSHUserInfo();
102 scp.init();
103 setFork(true);
104 setTrust(true);
105 }
106
107 public SSHJava(Task owner) {
108 this();
109 bindToOwner(owner);
110 }
111
112 public void setId(String id) {
113 this.id = id;
114 }
115
116 @Override
117 public int executeJava() throws BuildException {
118 checkNotNull(remotebase, "remotebase must be set");
119
120 if (localDirectory == null) {
121 try {
122 localDirectory = File.createTempFile("sshjava", "dir");
123 localDirectory.delete();
124 localDirectory.mkdirs();
125 } catch (IOException e) {
126 throw new BuildException(e);
127 }
128 }
129
130 if (remotedir == null)
131 remotedir = new File(remotebase, id);
132
133 String command = createInitScript(osFamily, id, remotedir.getAbsolutePath(), env, getCommandLine());
134
135 try {
136 BufferedWriter out = new BufferedWriter(new FileWriter(new File(localDirectory, "init."
137 + ShellToken.SH.to(osFamily))));
138 out.write(command);
139 out.close();
140 } catch (IOException e) {
141 throw new BuildException(e);
142 }
143
144 replaceAllTokensIn(localDirectory);
145
146 FileSet cwd = new FileSet();
147 cwd.setDir(localDirectory);
148 if (osFamily == OsFamily.UNIX) {
149 log("removing old contents: " + remotedir.getAbsolutePath(), Project.MSG_VERBOSE);
150 sshexec(exec("rm -rf " + remotedir.getAbsolutePath()).render(osFamily));
151 } else {
152
153 }
154 mkdirAndCopyTo(remotedir.getAbsolutePath(), ImmutableList.of(cwd));
155
156 for (Entry<String, String> entry : shiftMap.entrySet()) {
157 FileSet set = new FileSet();
158 File source = new File(entry.getKey());
159 if (source.isDirectory()) {
160 set.setDir(new File(entry.getKey()));
161 mkdirAndCopyTo(remotebase.getAbsolutePath() + ShellToken.FS.to(osFamily) + entry.getValue(), ImmutableList
162 .of(set));
163 } else {
164 String destination = remotebase.getAbsolutePath() + ShellToken.FS.to(osFamily)
165 + new File(entry.getValue()).getParent();
166 sshexec(exec("{md} " + destination).render(osFamily));
167 scp.init();
168 String scpDestination = getScpDir(destination);
169 log("staging: " + scpDestination, Project.MSG_VERBOSE);
170 scp.setFile(source.getAbsolutePath());
171 scp.setTodir(scpDestination);
172 scp.execute();
173 }
174 }
175
176 if (getCommandLine().getClasspath() != null) {
177 copyPathTo(getCommandLine().getClasspath(), remotedir.getAbsolutePath() + "/classpath");
178 }
179
180 if (getCommandLine().getBootclasspath() != null) {
181 copyPathTo(getCommandLine().getBootclasspath(), remotedir.getAbsolutePath() + "/bootclasspath");
182 }
183
184 if (osFamily == OsFamily.UNIX) {
185 sshexec(exec("chmod 755 " + remotedir.getAbsolutePath() + "{fs}init.{sh}").render(osFamily));
186 }
187
188 Statement statement = new StatementList(exec("{cd} " + remotedir.getAbsolutePath()), exec(remotedir
189 .getAbsolutePath()
190 + "{fs}init.{sh} init"), exec(remotedir.getAbsolutePath() + "{fs}init.{sh} run"));
191 try {
192 return sshexecRedirectStreams(statement);
193 } catch (IOException e) {
194 throw new BuildException(e, getLocation());
195 }
196 }
197
198 void replaceAllTokensIn(File directory) {
199 Replace replacer = new Replace();
200 replacer.setProject(getProject());
201 replacer.setDir(directory);
202
203 Map<String, String> map = Maps.newLinkedHashMap();
204
205 map.put(directory.getAbsolutePath(), remotedir.getAbsolutePath());
206
207 map.putAll(Maps.transformValues(shiftMap, new Function<String, String>() {
208
209 @Override
210 public String apply(String in) {
211 return remotebase + ShellToken.FS.to(osFamily) + in;
212 }
213
214 }));
215 map.putAll(replace);
216
217 for (Entry<String, String> entry : map.entrySet()) {
218 Replacefilter filter = replacer.createReplacefilter();
219 filter.setToken(entry.getKey());
220 filter.setValue(entry.getValue());
221 }
222 replacer.execute();
223 }
224
225 private int sshexec(String command) {
226 try {
227 return exec.execute(command);
228 } catch (JSchException e) {
229 throw new BuildException(e, getLocation());
230 } catch (IOException e) {
231 throw new BuildException(e, getLocation());
232 } catch (TimeoutException e) {
233 throw new BuildException(e, getLocation());
234 }
235 }
236
237 private int sshexecRedirectStreams(Statement statement) throws IOException {
238 exec.setStreamHandler(redirector.createHandler());
239 log("starting java as:\n" + statement.render(osFamily), Project.MSG_VERBOSE);
240 int rc;
241 try {
242 rc = sshexec(statement.render(osFamily));
243 } finally {
244 redirector.complete();
245 }
246 return rc;
247 }
248
249 private void mkdirAndCopyTo(String destination, Iterable<FileSet> sets) {
250 if (Iterables.size(sets) == 0) {
251 log("no content: " + destination, Project.MSG_DEBUG);
252 return;
253 }
254 if (sshexec(exec("test -d " + destination).render(osFamily)) == 0) {
255 log("already created: " + destination, Project.MSG_VERBOSE);
256 return;
257 }
258 sshexec(exec("{md} " + destination).render(osFamily));
259 scp.init();
260 String scpDestination = getScpDir(destination);
261 log("staging: " + scpDestination, Project.MSG_VERBOSE);
262 for (FileSet set : sets)
263 scp.addFileset(set);
264 scp.setTodir(scpDestination);
265 scp.execute();
266 }
267
268 private String getScpDir(String path) {
269 return String.format("%s:%s@%s:%s", userInfo.getName(), userInfo.getKeyfile() == null ? userInfo.getPassword()
270 : userInfo.getPassphrase(), scp.getHost(), path);
271 }
272
273 void resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(Path path, String prefix, StringBuilder destination) {
274 if (path == null)
275 return;
276 String[] paths = path.list();
277 if (paths != null && paths.length > 0) {
278 for (int i = 0; i < paths.length; i++) {
279 log("converting: " + paths[i], Project.MSG_DEBUG);
280 File file = new File(reprefix(paths[i]));
281 if (file.getAbsolutePath().equals(paths[i]) && file.exists() && file.isFile()) {
282 String newPath = prefix + "{fs}" + file.getName();
283 log("adding new: " + newPath, Project.MSG_DEBUG);
284 destination.append("{ps}").append(prefix + "{fs}" + file.getName());
285 } else {
286
287
288 destination.append("{ps}").append(file.getAbsolutePath());
289 log("adding existing: " + file.getAbsolutePath(), Project.MSG_DEBUG);
290 }
291 }
292 }
293 }
294
295 void copyPathTo(Path path, String destination) {
296 List<FileSet> filesets = Lists.newArrayList();
297 if (path.list() != null && path.list().length > 0) {
298 for (String filepath : path.list()) {
299 if (!filepath.equals(reprefix(filepath)))
300 continue;
301 File file = new File(filepath);
302 if (file.exists()) {
303 FileSet fileset = new FileSet();
304 if (file.isFile()) {
305 fileset.setFile(file);
306 } else {
307 fileset.setDir(file);
308 }
309 filesets.add(fileset);
310 }
311 }
312 }
313 mkdirAndCopyTo(destination, filesets);
314 }
315
316 String reprefix(String in) {
317 log("comparing: " + in, Project.MSG_DEBUG);
318 for (Entry<String, String> entry : shiftMap.entrySet()) {
319 if (in.startsWith(entry.getKey())) {
320 log("match shift map: " + entry.getKey(), Project.MSG_DEBUG);
321 in = remotebase + ShellToken.FS.to(osFamily) + entry.getValue() + in.substring(entry.getKey().length());
322 }
323 }
324 for (Entry<String, String> entry : replace.entrySet()) {
325 if (in.startsWith(entry.getKey())) {
326 log("match replaceMap: " + entry.getKey(), Project.MSG_DEBUG);
327 in = entry.getValue() + in.substring(entry.getKey().length());
328 }
329 }
330 log("now: " + in, Project.MSG_DEBUG);
331 return in;
332 }
333
334 String createInitScript(OsFamily osFamily, String id, String basedir, Environment env,
335 CommandlineJava commandLine) {
336 Map<String, String> envVariables = Maps.newHashMap();
337 String[] environment = env.getVariables();
338 if (environment != null) {
339 for (int i = 0; i < environment.length; i++) {
340 log("Setting environment variable: " + environment[i], Project.MSG_DEBUG);
341 String[] keyValue = environment[i].split("=");
342 envVariables.put(keyValue[0], keyValue[1]);
343 }
344 }
345 StringBuilder commandBuilder = new StringBuilder(commandLine.getVmCommand().getExecutable());
346 if (commandLine.getBootclasspath() != null) {
347 commandBuilder.append(" -Xbootclasspath:bootclasspath");
348 resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(commandLine.getBootclasspath(),
349 "bootclasspath", commandBuilder);
350 }
351
352 if (commandLine.getVmCommand().getArguments() != null
353 && commandLine.getVmCommand().getArguments().length > 0) {
354 commandBuilder.append(" ").append(
355 Joiner.on(' ').join(commandLine.getVmCommand().getArguments()));
356 }
357 commandBuilder.append(" -cp classpath");
358 resetPathToUnderPrefixIfExistsAndIsFileIfNotExistsAddAsIs(commandLine.getClasspath(),
359 "classpath", commandBuilder);
360
361 if (commandLine.getSystemProperties() != null
362 && commandLine.getSystemProperties().getVariables() != null
363 && commandLine.getSystemProperties().getVariables().length > 0) {
364 commandBuilder.append(" ").append(
365 Joiner.on(' ').join(commandLine.getSystemProperties().getVariables()));
366 }
367
368 commandBuilder.append(" ").append(commandLine.getClassname());
369
370 if (commandLine.getJavaCommand().getArguments() != null
371 && commandLine.getJavaCommand().getArguments().length > 0) {
372 commandBuilder.append(" ").append(
373 Joiner.on(' ').join(commandLine.getJavaCommand().getArguments()));
374 }
375
376 InitBuilder testInitBuilder = new InitBuilder(id, basedir, basedir, envVariables,
377 ImmutableList.<Statement> of(Statements.interpret( commandBuilder.toString())));
378 return testInitBuilder.render(osFamily);
379 }
380
381 @Override
382 public void addEnv(Environment.Variable var) {
383 env.addVariable(var);
384 }
385
386
387
388
389
390 @Override
391 public void setDir(File localDir) {
392 this.localDirectory = checkNotNull(localDir, "dir");
393 }
394
395
396
397
398
399 public void setRemotebase(File remotebase) {
400 this.remotebase = checkNotNull(remotebase, "remotebase");
401 }
402
403 @Override
404 public void setFork(boolean fork) {
405 if (!fork)
406 throw new IllegalArgumentException("this only operates when fork is set");
407 }
408
409
410
411
412
413
414
415 public void setHost(String host) {
416 exec.setHost(host);
417 scp.setHost(host);
418 }
419
420
421
422
423
424
425
426 public void setUsername(String username) {
427 exec.setUsername(username);
428 scp.setUsername(username);
429 userInfo.setName(username);
430 }
431
432
433
434
435
436
437
438 public void setPassword(String password) {
439 exec.setPassword(password);
440 scp.setPassword(password);
441 userInfo.setPassword(password);
442 }
443
444
445
446
447
448
449
450 public void setKeyfile(String keyfile) {
451 exec.setKeyfile(keyfile);
452 scp.setKeyfile(keyfile);
453 userInfo.setKeyfile(keyfile);
454 if (userInfo.getPassphrase() == null)
455 userInfo.setPassphrase("");
456 }
457
458
459
460
461
462
463
464 public void setPassphrase(String passphrase) {
465 exec.setPassphrase(passphrase);
466 scp.setPassphrase(passphrase);
467 userInfo.setPassphrase(passphrase);
468 }
469
470
471
472
473
474
475
476
477
478 public void setKnownhosts(String knownHosts) {
479 exec.setKnownhosts(knownHosts);
480 scp.setKnownhosts(knownHosts);
481 }
482
483
484
485
486
487
488
489 public void setTrust(boolean yesOrNo) {
490 exec.setTrust(yesOrNo);
491 scp.setTrust(yesOrNo);
492 userInfo.setTrust(yesOrNo);
493 }
494
495
496
497
498
499
500
501 public void setPort(int port) {
502 exec.setPort(port);
503 scp.setPort(port);
504 }
505
506
507
508
509
510
511
512
513 public void setTimeout(long timeout) {
514 exec.setTimeout(timeout);
515 }
516
517 @Override
518 public void setProject(Project project) {
519 super.setProject(project);
520 exec.setProject(project);
521 scp.setProject(project);
522 }
523
524 @Override
525 public void setOwningTarget(Target target) {
526 super.setOwningTarget(target);
527 scp.setOwningTarget(target);
528 }
529
530 @Override
531 public void setTaskName(String taskName) {
532 super.setTaskName(taskName);
533 scp.setTaskName(taskName);
534 }
535
536 @Override
537 public void setDescription(String description) {
538 super.setDescription(description);
539 scp.setDescription(description);
540 }
541
542 @Override
543 public void setLocation(Location location) {
544 super.setLocation(location);
545 scp.setLocation(location);
546 }
547
548 @Override
549 public void setTaskType(String type) {
550 super.setTaskType(type);
551 scp.setTaskType(type);
552 }
553
554 @Override
555 public String toString() {
556 return "SSHJava [append=" + append + ", env=" + env + ", errorFile=" + errorFile + ", errorProperty="
557 + errorProperty + ", localDirectory=" + localDirectory + ", osFamily=" + osFamily + ", outputFile="
558 + outputFile + ", outputProperty=" + outputProperty + ", remoteDirectory=" + remotebase + ", userInfo="
559 + userInfo + "]";
560 }
561
562 @Override
563 public void addSysproperty(Variable sysp) {
564 if (sysp.getKey().startsWith("sshjava.shift.")) {
565 shiftMap.put(sysp.getKey().replaceFirst("sshjava.shift.", ""), sysp.getValue());
566 } else if (sysp.getKey().startsWith("sshjava.replace.")) {
567 replace.put(sysp.getKey().replaceFirst("sshjava.replace.", ""), sysp.getValue());
568 } else if (sysp.getKey().equals("sshjava.id")) {
569 setId(sysp.getValue());
570 } else if (sysp.getKey().equals("sshjava.host")) {
571 setHost(sysp.getValue());
572 } else if (sysp.getKey().equals("sshjava.port") && !sysp.getValue().equals("")) {
573 setPort(Integer.parseInt(sysp.getValue()));
574 } else if (sysp.getKey().equals("sshjava.username")) {
575 setUsername(sysp.getValue());
576 } else if (sysp.getKey().equals("sshjava.password") && !sysp.getValue().equals("")) {
577 setPassword(sysp.getValue());
578 } else if (sysp.getKey().equals("sshjava.keyfile") && !sysp.getValue().equals("")) {
579 setKeyfile(sysp.getValue());
580 } else if (sysp.getKey().equals("sshjava.remotebase")) {
581 setRemotebase(new File(sysp.getValue()));
582 } else {
583 super.addSysproperty(sysp);
584 }
585 }
586
587 }