using java to manipulate a minecraft server input/output - java

I'm trying to manage my minecraft server through java but even though i can read outputs easily I can't manage to get commands or even text in:
ProcessBuilder builder = new ProcessBuilder(
"cmd.exe", "/c", "cd C:\\my\\path\\ && java -jar server.jar nogui");
builder.redirectErrorStream(true);
Process p;
p = builder.start();
this.p = p;
BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while (loop) {
line = r.readLine();
if (line == null) { break; }
System.out.println(line);
}
This works just fine but when I try to send commands it doesn't work at all:
OutputStream os = BotData.minecraftServer.getOutputStream();
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(os));
String stop = "stop";
try {
out.write(stop + "\n");
out.write("\n");
out.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
I've tried with "Command:>>" + stop + "\n"
with or without / before stop etc.
Killing the process, forcibly or not, starting it in a thread I'd then stop...
I can get neither text nor commands to work.

document says commands from cmd should come with a leading /.
Try send /stop instead of stop.
Also mind, the line line = r.readLine(); may never return a null while the process is alive(by default).

OK found it, it is needed to use write() newline() then flush() to send anything to the console.
My second problem was a dead reference to my process.
destroying the process doesn't work, but the stop command does.
using / is useless, \n doesn't replace newline()

Related

Grab CloudCompare Command Line Output with Java ProcessBuilder

I'm playing a bit around with a small JAVA gui for the command line mode of cloudcompare.
Therefore I'm using a short snippet like these:
var processBuilder = new ProcessBuilder();
try {
var process = processBuilder
.command("open", "-a", "CloudCompare.app", "-n",
"--args", "-NO_TIMESTAMP", "-C_EXPORT_FMT", "LAS",
"-O", "/Users/se/pcl_1.las",
"-O", "/Users/se/pcl_2.las",
"-MERGE_CLOUDS")
.start();
String error, line;
BufferedReader inputStream = new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((line = inputStream.readLine()) != null) {
System.out.println("line = " + line);
}
BufferedReader errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
while ((error = errorStream.readLine()) != null) {
System.out.println("error = " + error);
}
var ret = process.waitFor();
System.out.printf("Program exited with code: %d", ret);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
But if I run it on macOS the command line window opens, the process runs normal, but I can't grab any of the informations in it. There is an option to write log files from cloudcompare. That works - the log file shows that all cloud processing steps are done.
Does anybody knows, how to grab the command line output?
As mentioned here, the /usr/bin/open command is not an option to grab the stdinput stream.
I change the command to /Applications/CloudCompare.app/Contents/MacOS/CloudCompare and it works.
The next question is, how to grab the InputStream with a thread. I tried some stack overflow topics, but it doesn't work at the moment, to get the output stream in realtime. It is flushed at the end of the CloudCompare process.

Java runtime exec getInputStream when process pauses

I am creating a wrapper for a executable that runs on the windows command line. The executable takes a few commands then attempts to connect to another device. then it outputs and ERROR! or Ready For "Device Name" i do not get this message until the app exits. The problem is this app is a tunnel allowing me to run telnet on the external box but i need to make sure the Device is ready this is my code.
public void startUDPTunnel() {
//TODO Pull Amino serial number from webportal
Properties prop = new Properties();
InputStream inConfig = getClass().getClassLoader().getResourceAsStream("config.properties");
try {
prop.load(inConfig);
} catch (IOException e) {
System.out.println(e.getMessage());
}
String server = prop.getProperty("server");//config.GetProp("server");
System.out.println(server);
String port = prop.getProperty("port");//config.GetProp("port");
System.out.println(port);
String location = prop.getProperty("location");//config.GetProp("location");
System.out.println(location);
String url = prop.getProperty("URL");
System.out.println(url);
String input = "";
try {
input = getSerial(url);
System.out.println(input);
Process p = Runtime.getRuntime().exec(location+"udptunnel.exe -c 127.0.0.1 23 "+input+" "+server+" "+port+" 127.0.0.1 23");
threadSleep();
BufferedReader in = new BufferedReader(
new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = in.readLine()) != null) {
if(line.equals("ERROR!")){
System.out.println("There was an ERROR");
}
if(line.equals("Ready for \""+input+"\"")){
System.out.println("Load Telnet");
}
}
p.destroy();
} catch (IOException e) {
e.printStackTrace();
}
}
Sorry there is a lot of debug code left in this function.
EDIT
OK I am pretty sure know what the issue is bufferReader.readLine() requires a \n or \r or just hangs is there anyway to watch the stream with out the buffer?
You should use a ProcessBuilder, and then use redirectErrorStream(). I think this will cause stdout of the process to be unbuffered. And even if it doesn't, you'll only have to read from one InputStream to get both stdout and stderr.
I have figured out my problem the applications that i am executing with java do not have a EOL at the end of the line in fact they just hang on the line For example telnet waits for the username then the password. i am not sure this is proper but it works and is what i am going to use for now
while((i=br.read())!=-1){
ch += (char)i;
}
This outputs every char as they come in when then i just make sure the string contains what i am looking for!

User input to the command line when using Runtime.getRuntime().exec(command);

I dont think this is possible, but I have been using:
Process p = Runtime.getRuntime().exec(command);
to run commands on the command line, but now I have come accross a situation where the command I am running part way through will ask for some user input, for example a username.
This can not be resolved by a argument to the command that is being exec, is there any way I can pass the username to the same command line instance and continue?
---EDIT---
I still cant get this to work. These are the steps on the command line:
C:\someProgram.exe
Login:
Passowrd:
So I need to pass the login and password when it prompts at runtime. The code I've got that doesnt work:
try {
String CMD = "\"C:\\someProgram\"";
Scanner scan = new Scanner(System.in);
ProcessBuilder builder = new ProcessBuilder(CMD);
builder.redirectErrorStream(true);
Process process = builder.start();
InputStream is = process.getInputStream();
BufferedReader reader = new BufferedReader (new InputStreamReader(is));
OutputStream out = process.getOutputStream();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
String line;
try {
while (scan.hasNext()) {
String input = scan.nextLine();
if (input.toLowerCase().startsWith("login")) {
writer.write("myUsername");
} else if(input.toLowerCase().startsWith("password")){
writer.write("myPassword");
}
writer.flush();
line = reader.readLine();
while (line != null) {
System.out.println ("Stdout: " + line);
line = reader.readLine();
}
if (line == null) {
break;
}
}
process.waitFor();
}
finally {;
writer.close();
reader.close();
}
}
catch (Exception err) {
System.err.println("some message");
}
Ive tried things like:
writer.write("myUsername\n");
Any help, i can see that someProgram.exe is called and running in the processes, but it just hangs.
Just write to p.getOutputStream(). That'll send the username to the process's standard input, which should do what you want.
out = p.getOutputStream();
out.write("fooUsername\n".getBytes());
out.flush();
You should redirect the command input and send your parameters there. Use process.setInputStream(), the write into this stream.
As part your command String, if you are running on Unix/Linux/OSX and maybe PowerShell, you could prepend the cat shell command to have the shell dump the contents of a file into the input stream for your intended executable to read as input.
A command something like cat user_input.txt | myAppWantsInput.pl.
This will take the content of user_input.txt, dump it into standard-in, so when "myAppWantsInput.pl" in your command executes, and reads from standard-in, it will be reading the contents of the file and taking that as input as if entered from the keyboard.
Of course, it you don't know a priori what values you intend to pass, you could generate the files you need dynamically before invoke the command. This won't work if you can't determine all the input you'll want before you run the command.

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

process.waitFor() never returns

Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
There are many reasons that waitFor() doesn't return.
But it usually boils down to the fact that the executed command doesn't quit.
This, again, can have many reasons.
One common reason is that the process produces some output and you don't read from the appropriate streams. This means that the process is blocked as soon as the buffer is full and waits for your process to continue reading. Your process in turn waits for the other process to finish (which it won't because it waits for your process, ...). This is a classical deadlock situation.
You need to continually read from the processes input stream to ensure that it doesn't block.
There's a nice article that explains all the pitfalls of Runtime.exec() and shows ways around them called "When Runtime.exec() won't" (yes, the article is from 2000, but the content still applies!)
It appears you are not reading the output before waiting for it to finish. This is fine only if the output doesn't fill the buffer. If it does, it will wait until you read the output, catch-22.
Perhaps you have some errors which you are not reading. This would case the application to stop and waitFor to wait forever. A simple way around this is to re-direct the errors to the regular output.
ProcessBuilder pb = new ProcessBuilder("tasklist");
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
System.out.println("tasklist: " + line);
process.waitFor();
Also from Java doc:
java.lang
Class Process
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.
Fail to clear the buffer of input stream (which pipes to the output stream of subprocess)
from Process may lead to a subprocess blocking.
Try this:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
I would like to add something to the previous answers but since I don't have the rep to comment, I will just add an answer. This is directed towards android users which are programming in Java.
Per the post from RollingBoy, this code almost worked for me:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
In my case, the waitFor() was not releasing because I was executing a statement with no return ("ip adddr flush eth0"). An easy way to fix this is to simply ensure you always return something in your statement. For me, that meant executing the following: "ip adddr flush eth0 && echo done". You can read the buffer all day, but if there is nothing ever returned, your thread will never release its wait.
Hope that helps someone!
There are several possibilities:
You haven't consumed all the output on the process's stdout.
You haven't consumed all the output on the process's stderr.
The process is waiting for input from you and you haven't provided it, or you haven't closed the process's stdin.
The process is spinning in a hard loop.
As others have mentioned you have to consume stderr and stdout.
Compared to the other answers, since Java 1.7 it is even more easy. You do not have to create threads yourself anymore to read stderr and stdout.
Just use the ProcessBuilder and use the methods redirectOutput in combination with either redirectError or redirectErrorStream.
String directory = "/working/dir";
File out = new File(...); // File to write stdout to
File err = new File(...); // File to write stderr to
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(directory));
builder.command(command);
builder.redirectOutput(out); // Redirect stdout to file
if(out == err) {
builder.redirectErrorStream(true); // Combine stderr into stdout
} else {
builder.redirectError(err); // Redirect stderr to file
}
Process process = builder.start();
For the same reason you can also use inheritIO() to map Java console with external app console like:
ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
You should try consume output and error in the same while
private void runCMD(String CMD) throws IOException, InterruptedException {
System.out.println("Standard output: " + CMD);
Process process = Runtime.getRuntime().exec(CMD);
// Get input streams
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = "";
String newLineCharacter = System.getProperty("line.separator");
boolean isOutReady = false;
boolean isErrorReady = false;
boolean isProcessAlive = false;
boolean isErrorOut = true;
boolean isErrorError = true;
System.out.println("Read command ");
while (process.isAlive()) {
//Read the stdOut
do {
isOutReady = stdInput.ready();
//System.out.println("OUT READY " + isOutReady);
isErrorOut = true;
isErrorError = true;
if (isOutReady) {
line = stdInput.readLine();
isErrorOut = false;
System.out.println("=====================================================================================" + line + newLineCharacter);
}
isErrorReady = stdError.ready();
//System.out.println("ERROR READY " + isErrorReady);
if (isErrorReady) {
line = stdError.readLine();
isErrorError = false;
System.out.println("ERROR::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" + line + newLineCharacter);
}
isProcessAlive = process.isAlive();
//System.out.println("Process Alive " + isProcessAlive);
if (!isProcessAlive) {
System.out.println(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Process DIE " + line + newLineCharacter);
line = null;
isErrorError = false;
process.waitFor(1000, TimeUnit.MILLISECONDS);
}
} while (line != null);
//Nothing else to read, lets pause for a bit before trying again
System.out.println("PROCESS WAIT FOR");
process.waitFor(100, TimeUnit.MILLISECONDS);
}
System.out.println("Command finished");
}
I think I observed a similar problem: some processes started, seemed to run successfully but never completed. The function waitFor() was waiting forever except if I killed the process in Task Manager.
However, everything worked well in cases the length of the command line was 127 characters or shorter. If long file names are inevitable you may want to use environmental variables, which may allow you keeping the command line string short. You can generate a batch file (using FileWriter) in which you set your environmental variables before calling the program you actually want to run.
The content of such a batch could look like:
set INPUTFILE="C:\Directory 0\Subdirectory 1\AnyFileName"
set OUTPUTFILE="C:\Directory 2\Subdirectory 3\AnotherFileName"
set MYPROG="C:\Directory 4\Subdirectory 5\ExecutableFileName.exe"
%MYPROG% %INPUTFILE% %OUTPUTFILE%
Last step is running this batch file using Runtime.
Here is a method that works for me.
NOTE: There is some code within this method that may not apply to you, so try and ignore it. For example "logStandardOut(...), git-bash, etc".
private String exeShellCommand(String doCommand, String inDir, boolean ignoreErrors) {
logStandardOut("> %s", doCommand);
ProcessBuilder builder = new ProcessBuilder();
StringBuilder stdOut = new StringBuilder();
StringBuilder stdErr = new StringBuilder();
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
if (isWindows) {
String gitBashPathForWindows = "C:\\Program Files\\Git\\bin\\bash";
builder.command(gitBashPathForWindows, "-c", doCommand);
} else {
builder.command("bash", "-c", doCommand);
}
//Do we need to change dirs?
if (inDir != null) {
builder.directory(new File(inDir));
}
//Execute it
Process process = null;
BufferedReader brStdOut;
BufferedReader brStdErr;
try {
//Start the command line process
process = builder.start();
//This hangs on a large file
// https://stackoverflow.com/questions/5483830/process-waitfor-never-returns
//exitCode = process.waitFor();
//This will have both StdIn and StdErr
brStdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
brStdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
//Get the process output
String line = null;
String newLineCharacter = System.getProperty("line.separator");
while (process.isAlive()) {
//Read the stdOut
while ((line = brStdOut.readLine()) != null) {
stdOut.append(line + newLineCharacter);
}
//Read the stdErr
while ((line = brStdErr.readLine()) != null) {
stdErr.append(line + newLineCharacter);
}
//Nothing else to read, lets pause for a bit before trying again
process.waitFor(100, TimeUnit.MILLISECONDS);
}
//Read anything left, after the process exited
while ((line = brStdOut.readLine()) != null) {
stdOut.append(line + newLineCharacter);
}
//Read anything left, after the process exited
while ((line = brStdErr.readLine()) != null) {
stdErr.append(line + newLineCharacter);
}
//cleanup
if (brStdOut != null) {
brStdOut.close();
}
if (brStdErr != null) {
brStdOut.close();
}
//Log non-zero exit values
if (!ignoreErrors && process.exitValue() != 0) {
String exMsg = String.format("%s%nprocess.exitValue=%s", stdErr, process.exitValue());
throw new ExecuteCommandException(exMsg);
}
} catch (ExecuteCommandException e) {
throw e;
} catch (Exception e) {
throw new ExecuteCommandException(stdErr.toString(), e);
} finally {
//Log the results
logStandardOut(stdOut.toString());
logStandardError(stdErr.toString());
}
return stdOut.toString();
}
Asynchronous reading of stream combined with avoiding Wait with a timeout will solve the problem.
You can find a page explaining this here http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/
public static void main(String[] args) throws PyException, IOException, InterruptedException
these should be the exceptions thrown

Categories