So I'm starting a Bukkit (Minecraft) server from a GUI.
ProcessBuilder builder = new ProcessBuilder();
builder.redirectErrorStream(true);
builder.command("java", "-jar", file.getAbsolutePath());
try {
p = builder.start();
input = new BufferedReader(new InputStreamReader(p.getInputStream()));
output = new DataOutputStream(p.getOutputStream());
} catch (IOException e) {
Logger.logError(e);
return;
}
There are no errors, and the server itself starts correctly. The input stream works correctly too, as I get all the input as I should. Now, I have this method to send a command to the server.
public void send(String message) {
try {
output.writeUTF(message + "\n");
output.flush();
} catch (IOException e) {
Logger.logError(e);
}
}
For some reason though, it doesn't work. I'm not sure if I missed a step, or am looking over something, etc. Any help would be greatly appreciated!
I suspect the DataOutputStream is writing data in a non-conventional way towards the OutputStream, try using a PrintWriter object instead.
Consider this:
try {
p = builder.start();
input = new BufferedReader(new InputStreamReader(p.getInputStream()));
output = new PrintWriter(p.getOutputStream());
} catch (IOException e) {
Logger.logError(e);
return;
}
The Send method:
public void send(String message) {
output.println(message);
output.flush();
}
P.S You no longer need the try-catch around the output.println() as PrintWriter's print and println methods don't throw IOException.
From bukkit's plugin perspective (read my comment if you have no clue what this is):
final JavaPlugin Inst = ... //This plugin's object
try(BufferedReader Reader = new BufferedReader(new InputStreamReader(System.in, Charset.forName("UTF-8")))){
while((Line = Reader.readLine()) != null){
final String L = Line;
Bukkit.getScheduler().runTask(Inst, new Runnable(){
#Override
public void run() {
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), L);
}
});
}
}catch(IOException ex){
//Handle this
}
Related
I'm trying to forward the input to the process. It's a minecraft server so I need to send commands to it. I try in this way but it doesn't send anything. (I've also checked the log file)
public static void main(String[] args) throws IOException {
File file = new File("spigot.jar");
if(file.exists()) {
ProcessBuilder builder = new ProcessBuilder("java", "-jar", "spigot.jar");
Process p = builder.start();
new Thread(() -> {
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String s = "";
try {
while((s = br.readLine()) != null) {
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(()-> {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
String cmd = "";
try {
while((cmd = input.readLine()) != null) {
bw.write(cmd);
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
while(true) {
if(!p.isAlive()) {
System.out.println("Server closed.");
System.exit(0);
}
}
}).start();
} else {
System.out.println("spigot.jar not found.");
System.exit(0);
}
}
EDIT:
I rewrote the code using threads but i'm getting the same problem
Solved.
I need to add "bw.newline()" to send ENTER, and make the server execute my command
I am able to print the output of an external program in Eclipse via the code below (found online, not mine). I would like to be able to pass a command to the program and THEN print the output of the program but I don't know how to pass commands to the program. Any suggestions would be appreciated, thanks!
Code:
String line;
Process p = Runtime.getRuntime().exec("Z:/LPCXpresso/test10/Debug/arm-none-eabi-readelf.exe");
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();
First I would recommend using ProcessBuilder instead of Runtime.getRuntime().exec(...)
Second to be able to interact with the different streams of the process started you need to do it concurrently, i.e. for each stream create an own Thread where you interact with it.
Here is a sample code wihch illustrates interaction with the command line prompt in windows cmd.exe
public static void main(String... args) throws Exception {
ProcessBuilder pb = new ProcessBuilder("cmd.exe");
Process proc = pb.start();
// handle each of proc's streams in a separate thread
ExecutorService handlerThreadPool = Executors.newFixedThreadPool(3);
handlerThreadPool.submit(new Runnable() {
#Override
public void run() {
// we want to write to the stdin of the process
BufferedWriter stdin = new BufferedWriter(
new OutputStreamWriter(proc.getOutputStream()));
// read from our own stdin so we can write it to proc's stdin
BufferedReader myStdin =
new BufferedReader(new InputStreamReader(System.in));
String line = null;
try {
do {
line = myStdin.readLine();
stdin.write(String.format("%s%n", line));
stdin.flush();
} while(! "exit".equalsIgnoreCase(line));
} catch(IOException e) {
e.printStackTrace();
}
}
});
handlerThreadPool.submit(new Runnable() {
#Override
public void run() {
// we want to read the stdout of the process
BufferedReader stdout = new BufferedReader(
new InputStreamReader(proc.getInputStream()));
String line;
try {
while(null != (line = stdout.readLine())) {
System.out.printf("[stdout] %s%n", line);
}
} catch(IOException e) {
e.printStackTrace();
}
}
});
handlerThreadPool.submit(new Runnable() {
#Override
public void run() {
// we want to read the stderr of the process
BufferedReader stderr = new BufferedReader(
new InputStreamReader(proc.getErrorStream()));
String line;
try {
while(null != (line = stderr.readLine())) {
System.err.printf("[stderr] %s%n", line);
}
} catch(IOException e) {
e.printStackTrace();
}
}
});
// wait for the process to terminate
int exitCode = proc.waitFor();
System.out.printf("Process terminated with exit code %d%n", exitCode);
handlerThreadPool.shutdown();
}
You could make it simple and redirect of the process stderr to stdout using ProcessBuilder#redirectErrorStream(true) before starting the process and then have only 2 threads, one for input and one for output
Here is my sample code, I want to handle the command from standard input while running a new sub process. However, the exec method never returns if I read the system.in. The command in the exec() is very simple and has nothing to do with the stdin.
I'm wondering about is there any way to solve this? How can I start a new sub process while start another thread reading stdin?
public static void main(String[] args){
new Thread(new Runnable(){
public void run(){
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String command = null;
try{
while((command = reader.readLine()) != null){
System.out.println("Command Received:" + command);
}
}catch(Exception ex){
ex.printStackTrace();
//failed to listening command
}
}
}).start();
Process process = null;
try {
process = Runtime.getRuntime().exec("java -cp C:/agenttest Test");
System.out.println("never returns");
process.waitFor();
} catch (IOException e) {
throw new RuntimeException( e );
} catch (InterruptedException e) {
throw new RuntimeException( e );
}
}
The Test class is very simple, here is the Test.java
public static void main(String[] args){
System.out.println("Standard out");
System.out.println("Standard out");
System.err.println("Standard err");
System.out.println("Standard out");
try{
Thread.sleep(10000);
}catch(InterruptedException ex){}
}
The problem could be that you're not handling the error stream and input stream and are overrunning the platform's buffers. Try handling that output as per the famous article, When Runtime.exec() won't.
For example:
import java.io.*;
public class TestMain {
private static final String JAVA_CMD = "java";
private static final String CP = "-cp";
// *** your CLASS_PATH and PROG Strings will of course be different ***
private static final String CLASS_PATH = "C:/Users/hovercraft/Documents/workspace/Yr 2012A/bin";
private static final String PROG = "yr12.m07.b.Test2";
private static final String[] CMD_ARRAY = { JAVA_CMD, CP, CLASS_PATH, PROG };
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in));
String command = null;
try {
while ((command = reader.readLine()) != null) {
System.out.println("Command Received:" + command);
}
} catch (Exception ex) {
ex.printStackTrace();
// failed to listening command
}
}
}).start();
Process process = null;
try {
ProcessBuilder processBuilder = new ProcessBuilder(CMD_ARRAY);
process = processBuilder.start();
InputStream inputStream = process.getInputStream();
setUpStreamGobbler(inputStream, System.out);
InputStream errorStream = process.getErrorStream();
setUpStreamGobbler(errorStream, System.err);
System.out.println("never returns");
process.waitFor();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public static void setUpStreamGobbler(final InputStream is, final PrintStream ps) {
final InputStreamReader streamReader = new InputStreamReader(is);
new Thread(new Runnable() {
public void run() {
BufferedReader br = new BufferedReader(streamReader);
String line = null;
try {
while ((line = br.readLine()) != null) {
ps.println("process stream: " + line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
}
You should keep reading the input stream, otherwise it will get blocked. It has nothing to do with JVM but the underyling operating system.
I want to write a Java program that runs an external "java myprog < input.txt > output.txt" command. The eventual goal is to run this command on two different programs and compare their output similarity from their respective output files.
I think I've read just about every relevant article about using ProcessBuilder to run an external program, and the few entries about handling user input in that external program, but I still can't get things working. From what I have read, I think the best approach is to not run the exact command above, but instead read the input.txt file and feed it byte-by-byte into the Process object, then collect the output and write it to output.txt ... I am 100% open to other options.
I put together the code below based on my readings. It seems to correctly feed input from input.txt into myprog, but when I try to print the external program's output to the console to verify, the program hangs at the point where (surprise) user input is expected in myprog.
I get the same issues with and without the redirectErrorStream(true) line.
I really want this to be in Java since I plan to share the source code with the people whose program outputs I will compare, and they are primarily only familiar with Java.
import java.io.*;
import java.util.*;
public class test7 {
public static void main(String args[]) {
try {
// WANT: "java myprog < input.txt > output.txt"
String inputFile = "input.txt";
String outputFile = "output.txt";
ProcessBuilder pb = new ProcessBuilder("java","myprog");
pb.redirectErrorStream(true); // merge stdout, stderr of process
Process p = pb.start();
// write input to the running program
OutputStream pos = p.getOutputStream();
InputStream fis = new FileInputStream(inputFile);
int read = 0;
while ( (read = fis.read()) != -1) {
pos.write(read);
}
fis.close();
// get output of running program
InputStreamReader isr = new InputStreamReader(p.getInputStream());
BufferedReader br = new BufferedReader(isr);
// HANGS HERE WHEN USER INPUT REQUIRED
String lineRead;
while ((lineRead = br.readLine()) != null) {
System.out.println(lineRead);
}
}
catch (IOException e) {
e.printStackTrace();
}
} // end main
}
Here is the content of myprog.java:
import java.io.*;
public class myprog {
public static void main(String args[]) throws IOException {
System.out.println("Hello world!");
System.out.println("Enter something:");
BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));
// the readLine() command causes ProcessBuilder to hang
cin.readLine();
}
}
And the input.txt file is just
p
The output.txt file should be
Hello world!
Enter something:
I wonder if your problem is partly to do with not using separate threads for reading input and writing output. For instance:
public static void main(String args[]) {
try {
// WANT: "java myprog < input.txt > output.txt"
String inputFile = "input.txt";
String outputFile = "output.txt";
// my ProcessBuilder Strings will be different from yours
ProcessBuilder pb = new ProcessBuilder("java", "-cp", ".;bin;",
"yr12.m04.a.MyProg");
pb.redirectErrorStream(true);
Process p = pb.start();
final OutputStream pos = p.getOutputStream();
final PrintWriter pw = new PrintWriter(pos);
final InputStream fis = new FileInputStream(inputFile);
final BufferedReader fileBr = new BufferedReader(new InputStreamReader(fis));
InputStreamReader isr = new InputStreamReader(p.getInputStream());
final BufferedReader br = new BufferedReader(isr);
new Thread(new Runnable() {
public void run() {
String lineRead;
try {
while ((lineRead = br.readLine()) != null) {
System.out.println(lineRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
try {
String lineRead;
while ((lineRead = fileBr.readLine()) != null) {
pw.println(lineRead);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (pw != null) {
pw.close();
}
if (fileBr != null) {
try {
fileBr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
} // end main
Have you thought about using Runtime.getRuntime().exec() instead?
Process proc = Runtime.getRuntime().exec("java myprog "+inputFile+" "+outputFile);
You could include the jar of the 'myprog' and call the main() method yourself. Even more so if myprog is in your domain you could get rid of the main method altogether.
I am trying to write a small Java application that will let me run a Bukkit server off-screen using the Java Process/ProcessBuilder API.
I am able to get the output from the server fine, but the server doesn't respond to commands written by the output stream returned by Process.getOutputStream() (chained to the process input stream).
I tried doing this with my own test code, and it worked. The separate process reading from System.in received the text written to the output stream.
Does Bukkit not listen to System.in or something?
If not, how can that be?
Any ideas?
try {
ProcessBuilder pb = new ProcessBuilder();
File dir = new File("C:/Users/Brian/Desktop/MC-Server/Bukkit-Testing");
pb.directory(dir);
pb.command(new String[] {"java", "-Xincgc", "-Xmx1G", "-jar", "craftbukkit-1.0.1-R1.jar"});
pb.redirectErrorStream(true);
final Process p = pb.start();
InputStream out = p.getInputStream();
BufferedReader r1 = new BufferedReader(new InputStreamReader(out));
String s = null;
new Thread(new Runnable() {
#Override
public void run() {
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));
Scanner scan = new Scanner(System.in);
String input = null;
while((input=scan.nextLine()) != null) {
if(input.equals("exit")) {
p.destroy();
break;
}
try {
bw.write(input);
bw.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
while((s=r1.readLine()) !=null)
System.out.println(s);
} catch (IOException e) {
e.printStackTrace();
}
I don't think Bukkit uses its System.in, so we have to make a workaround.
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
public class ConsolePlugin extends JavaPlugin {
public Logger log;
public void onEnable(){
log = this.getLogger();
log.info("BufferedReader has been enabled!");
new Thread(new Runnable(){
public void run(){
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String line = null;
while (true){
try {
line=br.readLine();
} catch (Exception e) {e.printStackTrace();}
if (line!=null){
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), line);
System.out.println(line);
}
}
}
}).start();
}
public void onDisable(){
log.info("BufferedReader has been disabled.");
}
}
To send commands:
bw.write(input);
bw.nextLine();
bw.flush();