I'm trying to make program which runs some executable program(call it p), given time limit t ms. It does following tasks:
If program p has executed normally, print it's output to console.
If program p couldn't execute completely within time limit, print "Sorry, needs more time!" and then terminate execution of p.
If program p has terminated abnormally (e.g. RuntimeError), print "Can I've some debugger?"
I'm using ProcessResultReader class in the following program from here. My program is working as long as p finishes it's execution normally or terminate abnormally. But, it doesn't terminate if p itself doesn't terminate after timeout.(Try p with simple while(true) loop with no exit condition). It seems that thread stdout is alive even after execution of stdout.stop(). What am I doing wrong in this code?
Thanks.
import java.util.concurrent.TimeUnit;
import java.io.*;
class ProcessResultReader extends Thread
{
final InputStream is;
final StringBuilder sb;
ProcessResultReader(final InputStream is)
{
this.is = is;
this.sb = new StringBuilder();
}
public void run()
{
try
{
final InputStreamReader isr = new InputStreamReader(is);
final BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
{
this.sb.append(line).append("\n");
}
}
catch (final IOException ioe)
{
System.err.println(ioe.getMessage());
throw new RuntimeException(ioe);
}
}
#Override
public String toString()
{
return this.sb.toString();
}
public static void main(String[] args) throws Exception
{
int t = 1000;
Process p = Runtime.getRuntime().exec(cmd); //cmd is command to execute program p
ProcessResultReader stdout = new ProcessResultReader(p.getInputStream());
stdout.start();
if(!p.waitFor(t, TimeUnit.MILLISECONDS))
{
stdout.stop();
p.destroy();
System.out.println("Sorry, needs more time!");
}
else
{
if(p.exitValue()==0) System.out.println(stdout.toString());
else System.out.println("Can I've some debugger?");
}
}
}
According to java docs,
stdout.stop() was deprecated and even stdout.destroy() is never implemented.
For more information, see Why are Thread.stop, Thread.suspend and Thread.resume Deprecated?.
you could try this instead.
String cmd="cmd /c sleep 5";
int timeout = 1;
Process p = Runtime.getRuntime().exec(cmd); //cmd is command to execute program p
ProcessResultReader stdout = new ProcessResultReader(p.getInputStream());
stdout.start();
if(!p.waitFor(timeout, TimeUnit.MILLISECONDS))
{
stdout.stop();
p.destroy();
System.out.println("Sorry, needs more time!");
System.out.flush();
}
else
{
if(p.exitValue()==0) System.out.println(stdout.toString());
else System.out.println("Can I've some debugger?");
}
Related
I have a Java project which help to find all videos information in youtube playlist using youtube-dl. Here is the Main.java
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
String command1 = "/usr/local/bin/youtube-dl --flat-playlist --dump-single-json https://www.youtube.com/playlist?list=PLFcOH1YaRqUo1yEjY5ly09RFbIpUePF7G";
String command2 = "/usr/local/bin/youtube-dl --flat-playlist --dump-single-json https://www.youtube.com/playlist?list=PLC6A0625DCA9AAE2D";
System.out.println(executeCommand(command1));
}
private static String executeCommand(String command) throws IOException, InterruptedException {
int exitCode = 0;
String result = "";
Process process;
ProcessBuilder builder = new ProcessBuilder(command.replaceAll("[ ]+", " ").split(" "));
builder.directory(new File("/tmp/test"));
process = builder.start();
exitCode = process.waitFor();
return getStringFromInputStream(process.getInputStream());
}
private static String getStringFromInputStream(InputStream is) {
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
br = new BufferedReader(new InputStreamReader(is));
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return sb.toString();
}
}
The command1 and command2 are identical except for the Youbute playlist parameter. The playlist in command2 has 409 videos and has 200 videos in command1. I can get result successfully for both command in terminal. Just the command2 takes more time, but only several seconds. But when I run the Main.java (javac Main.java; java Main), for command1 it prints result successfully, but for command2 it hangs there for several minutes without any result. Here is the jstack for this process
"main" #1 prio=5 os_prio=0 tid=0x00007f828c009800 nid=0xce7 in Object.wait() [0x00007f8293cf7000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x0000000771df9fe0> (a java.lang.UNIXProcess)
at java.lang.Object.wait(Object.java:502)
at java.lang.UNIXProcess.waitFor(UNIXProcess.java:396)
- locked <0x0000000771df9fe0> (a java.lang.UNIXProcess)
at Main.executeCommand(Main.java:18)
at Main.main(Main.java:8)
It hangs at exitCode = process.waitFor();. I have no idea about it. Can anyone help me? Many thanks.
As mentioned in the comments, by default the output of the subprocess is sent to a pipe which can be read using Process.getInputStream(). If the subprocess generates lots of output and the Java program doesn't consume it, the pipe's buffer will fill up and the subprocess will block on writing.
The easiest solution is to call .inheritIO() on the ProcessBuilder. That will send the output to the console instead of buffering it in memory (same for input and error streams).
I need a function whose argument is bat's fileName and a float means timeOut. I've used Process in java to do it. But if I want to stop it, I find the p.destroy() cannot stop the exe file which be called by bat file and still runs.So ,how can I stop it like "Ctrl"+"C" in cmd?
public void exec(String path, float timeOutFloat) throws IOException, InterruptedException {
Runtime r = Runtime.getRuntime();
Process p = r.exec(new String[] { path });
ThreadReadExec2 thread = new ThreadReadExec2(p.getInputStream());
thread.start();
long timeOut = (long) ((float) timeOutFloat) * 1000;
long time = 0;
long onceTime = 100;
while (thread.isAlive()) {
Thread.sleep(onceTime);
time += onceTime;
if (time > timeOut) {
p.destroy();
Thread.sleep(onceTime);
}
}
int res = p.waitFor();
System.out.println("res:" + res);
}
class ThreadReadExec2 extends Thread {
InputStream input;
public ThreadReadExec2(InputStream input) {
this.input = input;
}
#Override
public void run() {
BufferedReader ir = new BufferedReader(new InputStreamReader(input));
try {
String line;
while ((line = ir.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
}
}
}
The reason that this is happening is likely because the destroy method is killing the command shell you invoked via the batch file, but not the process that that batch file in turn invoked.
You essentially need to kill the process tree of the batch file.
There are some methods in this question that suggest ways to accomplish this in Java, although it's somewhat roundabout (link to the one that seems the most promising):
https://stackoverflow.com/a/7627648/3583500
this is my first time posting here, so I'm not really sure what to say/ask.
Anyways, I am trying to make a simple java program that runs command prompt commands from the java program, mainly used for ping flood (ping flooding myself).
Here is my current code
public class Core extends JFrame {
JTextField ipTextField;
int packets = 0;
boolean running = false;
public Core() {
super("Fatique");
Container container = getContentPane();
JButton bAttack = new JButton("Start Attack");
JButton bStop = new JButton("Stop Attack");
JPanel jPanel = new JPanel();
container.setLayout(new FlowLayout());
ipTextField = new JTextField("IP Address", 30);
container.add(ipTextField);
bAttack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String input = ipTextField.getText();
String[] value = input.split(":");
int amountOfPackets = Integer.parseInt(value[1]);
exec("cmd /c" + input + " -t -n " + amountOfPackets);
running = true;
}
});
bStop.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
stop();
}
});
if(!running) {
jPanel.add(bAttack);
} else {
jPanel.add(bStop);
}
add(jPanel);
}
public void exec(String cmd) {
try {
Process p = Runtime.getRuntime().exec(cmd);
System.out.println(getOutput(p) + " - " + getPacketsSent());
} catch (IOException e) {
e.printStackTrace();
}
}
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output = line;
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
public int getPacketsSent() {
return packets;
}
public void stop() {
exec("cmd /c break");
running = false;
}
public static void main(String[] args) {
Core c = new Core();
c.setSize(500, 300);
c.setVisible(true);
c.setResizable(false);
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.setLocationRelativeTo(null);
}
I'm quite new at java, so that might not do what I want it to do.
What I want it to do is I enter an ip address in the textfield, and split it with ":", and after that the amount of packets, for instance
127.0.0.1:100
Though now when I try to use that ip and packet amount, it returns "null - 0" (from exec method), and I'm not even sure if it did anything related to ping.
What I am trying to accomplish is as I already said, ping flood myself, and then output whatever I get as response, though I have no idea if this code does anything even related to that, I mostly use logic when coding java.
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output = line;
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
Could someone explain me why my code code is not working how I want it to work? Please don't judge, as I already said, I'm quite new to java programming.
EDIT: Here is a quick "informative" explanation of what I am trying to accomplish.
I type in an ip address and how many packets I want to send. In this explanation, I am using localhost ip, and 5 packets.
I start the attack. At this part, I want the program to run cmd prompt command
ping 127.0.0.1 -t -n 5
127.0.0.1 being the ip that I put in the textfield in my program, and 5 is the amount of packets I put in the textfield.
I started the attack, so this is what should happen in the command prompt:
The language is Finnish, but still the same thing.
This is the basic explanation of what I am trying to accomplish, hopefully someone understood and can help/tell why my code is not working, or is working but not printing the proper lines in eclipse console.
There is a problem with your getOutput method. It looks like you intend to collect every line of output. But in fact, since you are assigning line to output, you will only return the last line before the end of stream.
To fix this, change
output = line;
to
output += line + "\n";
Or to be more correct:
output += line + LINE_SEPARATOR;
where you previously declared the latter as:
final String LINE_SEPARATOR = System.getProperty("line.separator");
That doesn't directly explain why you are getting null, but that might be because the command you are running is writing output to the 'error' stream rather than the 'output' stream.
Try something like this:
try {
Runtime rt = Runtime.getRuntime();
Process p = rt.exec("ping 192.168.16.67");
InputStream in = p.getInputStream();
OutputStream out = p.getOutputStream ();
InputStream err = p.getErrorStream();
p.destroy();
} catch(Exception exc) {}
Then, you'll have to read the out variable to parse the ping command output continuously.
bAttack.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String input = ipTextField.getText();
String[] value = input.split(":");
int amountOfPackets = Integer.parseInt(value[1]);
try {
p=Runtime.getRuntime().exec("ping -n "+amountOfPackets+" "+value[0]);
} catch (IOException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
running = true;
}
Just a small modification of your code. get output is as:
public String getOutput(Process p) {
String output = null;
try {
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
output =output+ line+"\n";
packets++;
}
return output;
} catch (IOException e) {
System.err.println(e.getStackTrace());
}
return null;
}
Here output is JTextArea I have taken to display the output of PING process. I cannot show you the output because I lack reputation.
I don't know why first line is null. Anyway, it works.
Hope this help you. Have good time coding.
I am trying to run some shell scripts for Java by using commons exec package and clear the STDOUT & STDERR buffers by using PumpStreamHandler. Most of the scripts run fine without any problems but some of them hangs.
Particularly those scripts that takes some time to return. My guess is that the PumpStramHandle might be reading end of stream as there is nothing put on the stream for a while and after that the buffers fill up.
Is there any better way to get across this problem?
Extract the script/command being executed and run it yourself in a shell. When running things that are 'exec'd through some other language(c,c++, python java etc) and things start going 'wrong' this should be the first step.
You find all sorts of things going on. Scripts that stop and prompt for input(big source of hangups) errors that don't parse correctly, seg faults, files not found.
To expand on the first answer about running the commands directly to test, you can test your hypothesis with a simple script that sleeps for a while before returning output. If you
can't test your command, test your idea.
#!/bin/bash
sleep 60;
echo "if you are patient, here is your response"
Not the best solution. But does what I need. :)
class OSCommandLogger extends Thread {
private static final Logger logger = Logger.getLogger(OSCommandLogger.class);
private volatile boolean done = false;
private final String name;
// Each process is associated with an error and output stream
private final BufferedReader outputReader;
private final BufferedReader errorReader;
private final Logger log;
/**
* Reads the output & error streams of the processes and writes them to
* specified log
*
* #param p
* #param name
* #param log
*/
OSCommandLogger(Process p, String name, Logger log) {
// Create readers
outputReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
errorReader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
this.log = log;
if (name != null)
this.name = name;
else
this.name = "OSCommandStreamsLogger";
}
private void logLine(BufferedReader reader, boolean isError) {
try {
String line = null;
while ((line = reader.readLine()) != null) {
if (log != null && log.isDebugEnabled()) {
if (!isError)
log.debug("[OuputStream] " + line);
else
log.warn("[ErrorStream] " + line);
} else
logger.debug(line);
}
} catch (Exception ex) {
if (log != null)
log.error(name + ":" + "Error while reading command process stream", ex);
}
}
public void run() {
while (!done) {
logLine(outputReader, false);
logLine(errorReader, true);
try {
// Sleep for a while before reading the next lines
Thread.sleep(100);
} catch (InterruptedException e) {
log.debug("Done with command");
}
}
// Process is done. Close all the streams
try {
logLine(outputReader, false);
outputReader.close();
logLine(errorReader, true);
errorReader.close();
if (log != null && log.isDebugEnabled())
log.debug(name + ": Closed output/ error Streams.");
} catch (IOException ie) {
if (log != null)
log.error(name + ":" + "Error while reading command process stream", ie);
}
}
public void stopLoggers() {
if (log != null && log.isDebugEnabled())
log.debug(name + ":Stop loggers");
this.done = true;
}
}
Usage:
Process p = Runtime.getRuntime().exec("Command");
OSCommandLogger logger = new OSCommandLogger(p, "Command", log);
// Start the thread using thread pool
threadExec.executeRunnable(logger);
int exitValue = p.waitFor(); // Wait till the process is finished
// Required to stop the logger threads
logger.stopLoggers();
logger.interrupt();
I have some issues regarding ProcessBuilder.
The program is basically a simple wrapper invoking a command line script.
When running the script on its own via the terminal, the memory consumption stays below 2G.
When running the script via the java wrapper, the memory consumption explodes and even 8G is quickly filled up, resulting in out-of-memory errors.
The code to launch the process is simply:
public static int execute(String command) throws IOException
{
System.out.println("Executing: " + command);
ProcessBuilder pb = new ProcessBuilder(command.split(" +"));
Process p = pb.start();
// display any output in stderr or stdout
StreamConsumer stderr = new StreamConsumer(p.getErrorStream(), "stderr");
StreamConsumer stdout = new StreamConsumer(p.getInputStream(), "stdout");
new Thread(stderr).start();
new Thread(stdout).start();
try {
return p.waitFor();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
The StreamConsumer class is simply a class which consumes the stdout/stderr streams and display them on the console.
...the question is: why on earth does the memory consumption explode?
Regards,
Arnaud
Edit:
Whether I use ProcessBuilder or
Runtime.getRuntime.exec(...), the
result is the same.
The memory bursts tend to appear during unix 'sort' invoked by the
shell script called:
sort big-text-file > big-text-file.sorted
Edit 2 on request of Jim Garrison:
Ok, here is the StreamConsumer class which I omitted because it is rather simple:
class StreamConsumer implements Runnable
{
InputStream stream;
String descr;
StreamConsumer(InputStream stream, String descr) {
this.stream = stream;
this.descr = descr;
}
#Override
public void run()
{
String line;
BufferedReader brCleanUp =
new BufferedReader (new InputStreamReader (stream));
try {
while ((line = brCleanUp.readLine ()) != null)
System.out.println ("[" + descr + "] " + line);
brCleanUp.close();
} catch (IOException e) {
// TODO: handle exception
}
}
}
if you change your command like this :
sort -o big-text-file.sorted big-text-file
is it always the same ?
Maybe its because those StreamConsumer threads are not daemons so they don't die and get garbage collected when your processes return? You could try:
//...
final StreamConsumer stderr = new StreamConsumer(p.getErrorStream(), "stderr");
final StreamConsumer stdout = new StreamConsumer(p.getInputStream(), "stdout");
final Thread stderrThread = new Thread(stderr);
final Thread stdoutThread = new Thread(stdout);
stderrThread.setDaemon(true);
stdoutThread.setDaemon(true);
stderrThread.start();
stdoutThread.start();
//...
Is this behavior happening for single invocation or after doing this many times?