Using SSMTP and ProcessBuilder - java

I'm currently working on a project for school and I'm trying to use sSMTP to send emails from java to a user using a text file. Executing from the command line ssmtp email#gmail.com < msg.txt works just fine and sends me the email with the information contained in msg.txt. However, when I try to do it in java using ProcessBuilder it doesn't send an email.
`ProcessBuilder builder = new ProcessBuilder;
builder.command("ssmtp", "email#gmail.com", "<", "msg.txt");
Process p = builder.start();`
I believe that it doesn't like where I try to pipe in msg.txt. If anyone knows a better way to do this that would be great. I haven't been able to find anything yet and am not sure how to do it myself

Instead of trying to rely on the shell's redirect functionality (which as you see doesn't work), you can just read msg.txt and write it to the process' OutputStream. It'll be the same thing, but in code (and it'll be a better solution too).
Something along the lines of
Process p = new ProcessBuilder("ssmtp").start();
PrintStream out = new PrintStream(p.getOutputStream());
String line = null;
BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream("msg.txt")));
while((line = in.readLine()) != null)
out.println(line);
out.close();
in.close();
However if you want to use shell redirection which I wouldn't recommend for anything serious, you need to execute the program which actually does the redirection, i.e. bash. The following should do the trick:
new ProcessBuilder("bash", "ssmtp", "email#gmail.com", "<", "msg.txt").start();
As dave_thompson_085 commented, it's even easier to do programmatic redirection. Things sure are easy these days!
new ProcessBuilder("ssmtp", "email#gmail.com").redirectInput(new File("msg.txt")).start();

Related

Fetching the logs from another Java Application

in a project I need to execute a java application from another java application. So far so good. Now I am stuck at the point, where the former needs to fetch the latters logs from the console.
The execution of the second is handled by the following snipped of code:
ArrayList<String> commands = new ArrayList<>();
commands.add("javaw");
commands.addAll(data.returnPropertiesAsPrefixedStringList());
commands.add("-cp");
commands.add(data.getClasspathJar().toString());
commands.add("[PathToMainClass]");
//data.getArgs for example contains the path to the second java (jnlp) file
commands.addAll(data.getArgs());
ProcessBuilder java = new ProcessBuilder().inheritIO().command(commands);
Process start = java.start();
How can I now fetch the logs "start" writes to the console? As I did not quite know what I had to search for, I did not manage to find fitting answers.
Edit:
I have tried getting the logs like that, but it did not work. The Logs weren't recognised at all.
Process start = java.start();
BufferedReader input = new BufferedReader(new InputStreamReader(start.getInputStream()));
String line;
while ((line = input.readLine()) != null) {
System.out.println("It Did Work: " + line);
}
Thank you in advance for your help.
-IHaveNoClue
Process has a getInputStream() operation, so you could use an inputstream reader to access this data. Also maybe this discussion will help you: printing-runtime-exec-outputstream-to-console

Start and get output of a .jar (E.g. Minecraft Bukkit .jar)

I'm trying to start some .jar inside an .jar using this code: (I'm trying with craftbukkit server right know)
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("java -jar craft.jar");
BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedWriter in = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
this.running = true;
while(this.running){
String line;
if((line = out.readLine()) != null){
System.out.println(line);
}
}
The problem is, it work for a moment, but after a while it stops outputting or just print '>' as show in the image below. How can I fix this? **Plus, Which is the correct way to send commands to the bukkit server?
Thanks!
When using a Process in Java, it is crucial to read stdout and stderr in two independend threads (your while loop). Otherwise the child process can lock up waiting for the filled buffer to be drained.
With Java 7 you can actually redirect stderr to stdout, and possibly redirect the resulting stream to the parents stdout. With older versions you need to attach the seperate threads.
Besides: you should not use the "single string" variant of exec, but specify the args as arrays. This is safer (less vulnerable to parsing problems).
(But I am not sure if this is your actual problem. Maybe you should dump the error stream and see if it helps)
I do it like that:
#Override
public void run(){
// ...
BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream())); //of course proc is an instance of Process, and it's set up
while(true){
String line = stdInput.readLine();
if(line == null) break; //this is essential, when process terminates, line gets null
System.out.println(">"+line);
}
// ...
}
Give it a try.
(Technically it's similiar how you did, but it works for me so i share it)

Problem with copy files using cmd in Java

I'm trying to copy a bunch of files with a specific extension from one folder to another using the copy command, heres wat im doing,
String[] command = new String[3];
command[0] = "cmd";
command[1] = "/c";
command[2] = "copy C:\\output\\html\\*.txt C:\\output\\";
ProcessBuilder copyFiles = new ProcessBuilder(command);
p = copyFiles.start();
p.waitFor();
the thing is, this code works fine for files less than some 5 or so, but just stops responding wen the number of files are more (even for 15 files) !! and the files are not copied either!!
I dont know what the problem is, will be glad if someone could help! :)
You're not reading the output the copy command is generating.
When spawning a child process using ProcessBuilder, output generated by your child process gets written to a buffer. If this buffer isn't read from, it eventually fills up. When it fills up, the copy command can't write any more to it and so is blocked by the operating system. It is then forced to wait until space is made in the buffer by reading from it.
I ran your code with 20 files and I found that it did indeed hang.
One way to solve your problem is to redirect the output from copy to NUL. Most of the output from copy is a list of all the files it has copied, which you probably don't care too much for. To do this redirection, modify the line that assigns to command[2] to the following:
command[2] = "copy C:\\output\\html\\*.txt C:\\output\\ >NUL 2>NUL";
However, if there is a problem copying files, you might not know about it if you do this.
Alternatively, you can read the output that the copy command generates. The following code sends it to System.out, but you can easily send it elsewhere or completely ignore it if you wish:
String[] command = { "cmd", "/c", "copy C:\\output\\html\\*.txt C:\\output\\" };
ProcessBuilder copyFiles = new ProcessBuilder(command);
copyFiles.redirectErrorStream(true);
p = copyFiles.start();
// The InputStream we get from the Process reads from the standard output
// of the process (and also the standard error, by virtue of the line
// copyFiles.redirectErrorStream(true) ).
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
do {
line = reader.readLine();
if (line != null) { System.out.println(line); }
} while (line != null);
reader.close();
p.waitFor();
I gave each approach a quick test with the same 20 files and neither approach hung.
EDIT: You might also want to try a 'hybrid' approach, by throwing away what copy writes to standard output (e.g. the list of files it's copying) but using the second approach to read in what it writes to standard error (e.g. error messages). To do this, you'd add the >NUL, which redirects the standard output of copy to NUL, but you wouldn't add the 2>NUL, since that redirects standard error to NUL.

Call "interactive" Perl script from Java

I want to call an "interactive" Perl script from a Java program. Just for the clarity, the other way around (from Perl to Java) is not good for me.
The script is interactive in the sense that it requires a small configuration dialog with the user. For example, calling the script in cmd.exe would lead to a dialog like:
Do you want to overwrite the old settings? [yes,no (default=no)]
and the user should choose between writing yes, no or nothing at all in the command line.
And depending on the user choice another message would appear: "Do you want to...." and the user will respond etc etc. I think you got the picture.
My question is how can I have the same dialog with the user when the script is called in a Java program? I mean, how can I capture the script's questions to the user, show them to user and then send the user's answer (got in the Java program) to the script?
A simple Runtime.getRuntime().exec() doesn't work in this case.
Hope I expressed clear enough the question.
Thank you for your help!
You must use getInputStream/getOutputStream methods to get access to stdin and stdout of perl stript. You can read and write to these streams to simulate user's behavior
OutputStream stdin = null;
InputStream stderr = null;
InputStream stdout = null;
Process process = Runtime.getRuntime ().exec ("...");
stdin = process.getOutputStream ();
stderr = process.getErrorStream ();
stdout = process.getInputStream ();
// "write" the parms into stdin
String line = "data\n";
stdin.write(line.getBytes());
stdin.flush();
stdin.close();
// clean up if any output in stdout
BufferedReader brCleanUp =
new BufferedReader (new InputStreamReader (stdout));
while ((line = brCleanUp.readLine ()) != null) {
//System.out.println ("[Stdout] " + line);
}
brCleanUp.close();
// clean up if any output in stderr
brCleanUp =
new BufferedReader (new InputStreamReader (stderr));
while ((line = brCleanUp.readLine ()) != null) {
//System.out.println ("[Stderr] " + line);
}
brCleanUp.close();
This is a job for Expect. In Java: ExpectJ, expect4j
If (1) your call is from Java to Perl, and (2) you are not parsing the Perl script itself, why not use a JOptionPane.showConfirmDialog() from the Java code? Shouldn't be a big deal if a Yes/No is all your are getting from the script. Whatever you are printing to display to the user can be included in that confirm dialog as a plain ASCII text, too.

Running other programs from Java

I need to run a couple of other programs from my own Java program, basically I need to run these command line statements.
svn log --xml -v > svn.log
and
java -jar example.jar arg1 arg2
and I need to use the text outputs written to the console from these programs in my own program. I've tried Runtime.getRuntime().exec() with the svn, but it doesn't seem to be doing anything because it doesn't make a svn.log file. Also both programs need to be called in different places, the svn line needs to be called from inside one folder and the java line needs to be called from another.
Any ideas on how to go about this? If this is not possible in Java, is there a way to do it in C#?
Thanks
Here:
ProcessBuilder processbuilder
try
{
processbuilder.directory(file);
processbuilder.redirectErrorStream(true);
process = processbuilder.start();
String readLine;
BufferedReader output = new BufferedReader(new InputStreamReader(process.getInputStream()));
// include this too:
// BufferedReader output = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while((readLine = output.readLine()) != null)
{
m_Logger.info(readLine);
}
process.waitFor();
}
I've used something similar. You'll actually want to do something with the readLine. I just copied and pasted from code where I didn't care what it said.
The redirection > (like the pipe |) is a shell construct and only works when you execute stuff via /bin/sh (or equivalent). So the above isn't really going to work. You could execute
/bin/sh -c "svn log --xml -v > svn.log"
and read svn.log.
Alternatively, you can read the output from the process execution and dump that to a file (if you need to dump it to a file, or just consume it directly as you read it). If you choose this route and consume stdout/stderr separately, note that when you consume the output (stdout), you need to consume stderr as well, and concurrently, otherwise buffers will block (and your spawned process) waiting for your process to consume this. See this answer for more details.
instead of piping in your command, just let it print to standard output and error output. You can access those streams from your process object that is returned from exec.
For the svn stuff use java SVNKit API.
Seeing your two commands, why don't you do it directly from Java, without executing ? You could use SVNKit for the svn part, and include directly the jars in your classpath.
Try this
public static void main(String[] args) {
try {
// Execute a command with an argument that contains a space
System.out.println(args[0]);
String[]commands = new String[]{"svn", "info", args[0]};
Process process = Runtime.getRuntime().exec(commands);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder builder = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
String result = builder.toString();
System.out.println(result);
}catch(Exception e){
System.out.print(e);
}
}

Categories