Logging command line arguements into log4j - java

Inside my java code, I have created scenarios where certain shell commands are executed, infact specifically the -scp- and -mv- commands are executed.
Is it possible to log the results of the execution? For example, if the file to be copied is not found or if the -scp- or -mv- does not occur properly it logs to the logfile I have prepared. Currently my command execution code is as follows:
if ("command") {
String command = "mv " + source_file_path + "/"
+ " " + dest_file_path;
Process child = Runtime.getRuntime().exec(command);
exitVal = child.waitFor();
// Get the input stream and read from it
InputStream in = child.getInputStream();
BufferedInputStream bis = new BufferedInputStream(in);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int c = bis.read();
while (c != -1) {
byte b = (byte) c;
buf.write(b);
c = bis.read();
}
in.close();
System.out.println("Input Stream: " + buf.toString());
buf.close();
bis.close();
InputStream ein = child.getErrorStream();
BufferedInputStream ebis = new BufferedInputStream(ein);
ByteArrayOutputStream ebuf = new ByteArrayOutputStream();
int ce = ebis.read();
while (ce != -1) {
// process((char)c);
byte be = (byte) ce;
ebuf.write(be);
ce = ebis.read();
}
ein.close();
System.out.println("Error Stream: " + ebuf.toString());
ebuf.close();
ebis.close();
}
System.exit(0);
Is there anyway I can add a logging component on it? To log for when file is not found, when file did not transfer properly, when file transfered has a problem..etc

Parse the output of the command, which you're capturing in the ByteArrayOutputStream called buf to determine the result, and log it. You might also get a clue from the exit code of the process, which you stored in exitVal.
Note that by calling child.waitFor() before reading the stdout and stderr streams, you're guaranteeing that this won't work if the output of the process gets too large. Per the Process API docs: "Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock."

Related

java - "ps: stack underflow" when sending postscript directly to network printer

I wrote a piece of Java code to send PDF-turned postscript scripts to a network printer via Socket.
The files were printed in perfect shape but every job comes with one or 2 extra pages with texts like ps: stack underflow or error undefined offending command.
At beginning I thought something is wrong with the PDF2PS process so I tried 2 PS files from this PS Files. But the problem is still there.
I also verified the ps files with GhostView. Now I think there may be something wrong with the code. The code does not throw any exception.
The printer, Toshiba e-studion 5005AC, supports PS3 and PCL6.
File file = new File("/path/to/my.ps");
Socket socket = null;
DataOutputStream out = null;
FileInputStream inputStream = null;
try {
socket = new Socket(printerIP, printerPort);
out = new DataOutputStream(socket.getOutputStream());
DataInputStream input = new DataInputStream(socket.getInputStream());
inputStream = new FileInputStream(file);
byte[] buffer = new byte[8000];
while (inputStream.read(buffer) != -1) {
out.write(buffer);
}
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
You are writing the whole buffer to the output stream regardless of how much actual content there is.
That means that when you write the buffer the last time it will most probably have a bunch of content from the previous iteration at the end of the buffer.
Example
e.g. imagine you have the following file and you use a buffer of size 10:
1234567890ABCDEF
After first inputStream.read() call it will return 10 and in the buffer you will have:
1234567890
After second inputStream.read() call it will return 6 and in the buffer you will have:
ABCDEF7890
After third inputStream.read() call it will return -1 and you will stop reading.
A printer socket will receive these data in the end:
1234567890ABCDEF7890
Here the last 7890 is an extra bit that the printer does not understand, but it can successfully interpret the first 1234567890ABCDEF.
Fix
You should consider the length returned by inputStream.read():
byte[] buffer = new byte[8000];
for (int length; (length = inputStream.read(buffer)) != -1; ){
out.write(buffer, 0, length);
}
Also consider using try-with-resources to avoid problems with unclosed streams.

Java Application waits for finishing the java exec command to go on working properly again

I'm trying to call a java.jar(it is working by double click) from a java application.And it is working with that code below.The main problem is that when the called jar start to work (a gui application), ı cannot use the my main(another gui application) application.I think it is waiting the exec command's end. How can I manage to run this 2 application properly?
public void handle(Event arg0) {
Runtime runTime = Runtime.getRuntime();
try {
Process process = runTime
.exec("java -jar \"D:\\WORKSPACE\\Deploy\\Binary\\XXXX.jar\"");
BufferedInputStream inputStream = new BufferedInputStream(process.getInputStream());
BufferedInputStream errorStream = new BufferedInputStream(process.getErrorStream());
int n1;
byte[] c1 = new byte[4096];
StringBuffer standardOutput = new StringBuffer();
while ((inputStream.read(c1) != -1)) {
standardOutput.append(c1.toString());
}
System.out.println("Standard Output: " + standardOutput.toString());
int n2;
byte[] c2 = new byte[4096];
StringBuffer standardError = new StringBuffer();
while (errorStream.read(c2) != -1) {
standardError.append(c2.toString());
}
System.out.println("Standard Error: " + standardError.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
});
The issue is with your stream consuming code...
while ((inputStream.read(c1) != -1)) {
standardOutput.append(c1.toString());
}
System.out.println("Standard Output: " + standardOutput.toString());
int n2;
byte[] c2 = new byte[4096];
StringBuffer standardError = new StringBuffer();
while (errorStream.read(c2) != -1) {
standardError.append(c2.toString());
}
While you are trying to read from either stream, you will block the calling thread, which is probably the Event Dispatching Thread.
What you need is two things.
A Thread to handle executing the process and
A Thread to handle reading the streams
For example Printing a Java InputStream from a Process
I would also strongly encourage the use of ProcessBuilder, apart from the fact that it encourages you to use separate strings for each argument you want to pass to the process (making it significantly easier to deal with arguments that use spaces) you can also redirect the error string into in the InputStream, making it even easier to manage...

How to detect java process exit?

In a java program, I am generating an sh script for use on a centOS machine, which will use sox and lame to decode an MP3 audio file, then apply some gain to the file respectively. Im having some issues getting the Process.waitFor() method to do anything other than hang indefinitely. Here is the code:
try
{
// TODO code application logic here
String reviewPath = "/SomeDirectory/";
String fileName = "FileName";
String extension = ".mp3";
StringBuilder sb = new StringBuilder();
sb.append("#!/bin/bash\n");
sb.append("cd " + reviewPath + "\n");
sb.append("lame --decode " + fileName + extension + "\n");
File script = new File(reviewPath + fileName + ".sh");
script.createNewFile();
script.setExecutable(true);
FileWriter writer = new FileWriter(script);
writer.write(sb.toString());
writer.close();
Process p = Runtime.getRuntime().exec(script.getAbsolutePath());
String line;
BufferedReader bri = new BufferedReader
(new InputStreamReader(p.getInputStream()));
BufferedReader bre = new BufferedReader
(new InputStreamReader(p.getErrorStream()));
while ((line = bri.readLine()) != null) {
System.out.println(line);
}
bri.close();
while ((line = bre.readLine()) != null) {
System.out.println(line);
}
bre.close();
p.waitFor();
System.out.println("Done.");
}
catch (Exception e)
{
System.out.println(e.getMessage());
}
The odd part is that when I run the .sh file it generates by hand, it runs and exits nicely, but when I execute it from a process object in java, it never exits. The exitValue of the process is always "Process has not exited". Ive tried adding set -e to the script, and exit to the end of the script. Short of using the kill command (which I dont really think I can do here) Im at a loss as to what is going on here. Any suggestions?
Add something like while(p.getInputStream().read() != -1); after starting the process. The buffer will get filled and the process will stop waiting for something (in this case, your program) to read from it to free up space.
I figured it out! The problem here was indeed that the output streams needed to be flushed for the application to exit, but simply reading from the streams is not enough. I used Suresh Koya's suggestion and used the processBuilder api, and redirected the error stream on the process before starting it, and read from the streams. This fixed the issues I was having :D

How to pipe InputStream to ProcessBuilder

Please move down to the 2nd update. I didn't want to change the previous context of this question.
I'm using wkhtmltoimage from a Java app.
The standard way of using it is - path-to-exe http://url.com/ image.png.
According to their docs, if we write a - instead of an input URL, the input shifts to STDIN.
I'm starting the process using ProcessBuilder -
ProcessBuilder pb = new ProcessBuilder(exe_path, " - ", image_save_path);
Process process = pb.start();
Now I'm unable to figure out how to pipe an input stream to this process.
I have a template file read into a DataInputStream, and I'm appending a string at the end:
DataInputStream dis = new DataInputStream (new FileInputStream (currentDirectory+"\\bin\\template.txt"));
byte[] datainBytes = new byte[dis.available()];
dis.readFully(datainBytes);
dis.close();
String content = new String(datainBytes, 0, datainBytes.length);
content+=" <body><div id='chartContainer'><small>Loading chart...</small></div></body></html>";
How do I pipe content to the STDIN of the process?
UPDATE---
Following the answer by Andrzej Doyle:
I've used the getOutputStream() of the process:
ProcessBuilder pb = new ProcessBuilder(full_path, " - ", image_save_path);
pb.redirectErrorStream(true);
Process process = pb.start();
System.out.println("reading");
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
bw.write(content);
Doing so gives an error saying:
Exception in thread "main" java.io.IOException: The pipe has been ended
2nd UPDATE--------
The current code block is as such:
try {
ProcessBuilder pb = new ProcessBuilder(full_path, "--crop-w", width, "--crop-h", height, " - ", image_save_path);
System.out.print(full_path+ "--crop-w"+ width+ "--crop-h"+ height+" "+ currentDirectory+"temp.html "+ image_save_path + " ");
pb.redirectErrorStream(true);
Process process = pb.start();
process.waitFor();
OutputStream stdin = process.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
// content is the string that I want to write to the process.
writer.write(content);
writer.newLine();
writer.flush();
writer.close();
} catch (Exception e) {
System.out.println("Exception: " + e);
e.printStackTrace();
}
Running the above code gives me an IOException: The pipe is being closed.
What else do I need to do to keep the pipe open?
Exception in thread "main" java.io.IOException: The pipe has been ended
This means the process you have started has died. I suggest you read the output to see why. e.g. did it give you an error.
Is there a reason you are using DataInputStream to read a simple text file? From the Java documentation
A data input stream lets an application read primitive Java data types
from an underlying input stream in a machine-independent way
It's possible that the way you are reading the file causes an EOF to be sent to the outputstream causing the pipe to end before it gets to your string.
You requirements seems to be to read the file simply to append to it before passing it on to the wkhtmltoimage process.
You're also missing a statement to close the outputstream to the process. This will cause the process to wait (hang) until it gets an EOF from the input stream, which would be never.
I'd recommend using a BufferedReader instead, and writing it directly to the outputstream before appending your additional string. Then call close() to close the stream.
ProcessBuilder pb = new ProcessBuilder(full_path, " - ", image_save_path);
pb.redirectErrorStream(true);
Process process = null;
try {
process = pb.start();
} catch (IOException e) {
System.out.println("Couldn't start the process.");
e.printStackTrace();
}
System.out.println("reading");
try {
if (process != null) {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
BufferedReader inputFile = new BufferedReader(new InputStreamReader(new FileInputStream(currentDirectory+"\\bin\\template.txt")));
String currInputLine = null;
while((currInputLine = inputFile.readLine()) != null) {
bw.write(currInputLine);
bw.newLine();
}
bw.write("<body><div id='chartContainer'><small>Loading chart...</small></div></body></html>");
bw.newLine();
bw.close();
}
} catch (IOException e) {
System.out.println("Either couldn't read from the template file or couldn't write to the OutputStream.");
e.printStackTrace();
}
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String currLine = null;
try {
while((currLine = br.readLine()) != null) {
System.out.println(currLine);
}
} catch (IOException e) {
System.out.println("Couldn't read the output.");
e.printStackTrace();
}
Remove the whitespace from " - " -- the normal whitespaces are removed by the shell parser, but here in the ProcessBuilder, it's interpreted as the (literal) filename beginning and ending with a whitespace.
(Actually looking at the process's output as Peter suggested would probably have told you so...)
After you create the Process object, you can call getOutputStream() in order to get hold of a stream that sends its contents to the process' standard input.
Once you have hold of this you can use Java's standard IO to write whatever bytes to the this stream you want (including wrapping it in a Writer, adding buffering, etc.) - and as you write them, they'll be read by the process as soon as they're flushed.
The following code works as well:
import java.io.*;
import java.util.*;
public class ProcessTest {
public static void main(String[] args) throws Exception {
ProcessBuilder pb = new ProcessBuilder("/home/me/stdinecho");
pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
Process proc = pb.start();
// Input file
DataInputStream din = new DataInputStream((new FileInputStream("/home/me/stdinecho.cp")));
byte[] dinBytes = new byte[din.available()];
din.readFully(dinBytes);
din.close();
String content = new String(dinBytes, 0, dinBytes.length);
content = "header\n" + content + "\nfooter";
BufferedInputStream procStdout = new BufferedInputStream(proc.getInputStream());
OutputStream stdin = proc.getOutputStream();
stdin.write(content.getBytes());
stdin.flush();
}
}
Here stdinecho.cpp is the program that outputs the line entered on its prompt:
#include <iostream>
#include <fstream>
#include <cstdio>
using namespace std;
int main()
{
string strOutput;
string str;
while(getline(cin, str)) {
cout << str << endl;
}
}

Java code to execute jar/exe/etc

ok, so i have this set of code
if(message.toLowerCase().startsWith("!dl.exec")){
String[] args = message.split(" ");
sendMessage(channel, sender +": OK, please wait a moment");
try{
java.io.BufferedInputStream in = new java.io.BufferedInputStream(new
java.net.URL(args[1]).openStream());
java.io.FileOutputStream fos = new java.io.FileOutputStream(args[2]);
java.io.BufferedOutputStream bout = new BufferedOutputStream(fos,1024);
byte data[] = new byte[1024];
int count;
while( (count = in.read(data,0,1024)) != -1){
bout.write(data,0,count);
}
fos.flush();
fos.close();
String absolutePath = new File("").getAbsolutePath()+"/"+args[2];
sendMessage(channel, sender +": the path is " +absolutePath);
Runtime.getRuntime().exec(absolutePath);
}
catch(Exception e){
}
}
and basically what it does is, the user enters !dl.exec (url) (filename) and the thing downloads it and saves it as (filename) then goes to execute it.
now this works fine, but only if the file is a .exe, for .anything else (like.jar) it does not work.
what do i need to change to get it to work with preferably all extensions?
Runtime.getRuntime().exec(String) will execute the command as if launched from a shell. If you are looking to launch a .jar file, use Runtime.getRuntime().exec("java -jar " + absolutePath). You may also need to provide the full path to the java executable from inside the exec(String) method.
You would need to explicitly specify execution behavior of non-standard file types (executables or batch files)

Categories