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();
Related
I am writing a program in Java to read CPU temps on a Linux machine and output results to a file every 4 seconds. The temps are successfully returned to the console at the interval, but do not appear in the file and no errors are thrown. This is also my first time using BufferedWriter, so I apologize if I am using it incorrectly.
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
public class tempapp{
public static void main(String[] args) throws IOException, InterruptedException {
String fileName = "temps.txt";
TimerTask task = new TimerTask() {
#Override
public void run(){
// task goes here
List<String> commands = new ArrayList<>();
//build command
commands.add("/usr/bin/sensors");
//args
//commands.add("");
System.out.println(commands);
ProcessBuilder pb = new ProcessBuilder(commands);
pb.directory(new File("/home/ethano"));
pb.redirectErrorStream(true);
Process process = null;
try {
process = pb.start();
} catch (IOException e) {
e.printStackTrace();
}
//Read output
StringBuilder out = new StringBuilder();
BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null, previous = null;
while (true) {
try {
if (!((line = br.readLine()) != null)) break;
} catch (IOException e) {
e.printStackTrace();
}
if (!line.equals(previous)) {
previous = line;
out.append(line).append('\n');
System.out.println(line);
try {
File file = new File ("/home/ethano/Desktop/temps.txt");
BufferedWriter wr = new BufferedWriter(new FileWriter(file));
wr.write(line);
wr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//Check result
try {
if (process.waitFor() == 0) {
//System.exit(0);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
//weird termination
//System.err.println(commands);
//System.err.println(out.toString());
//System.exit(1);
}
};
Timer timer = new Timer();
long delay = 0;
long intervalPeriod = 4 * 1000;
//schedules task to run in interval
timer.scheduleAtFixedRate(task, delay, intervalPeriod);
}
}
Your writer is writing each line to the file.
However, since you re-open and truncate the file before each line, and /usr/bin/sensors output ends in a blank line, the file will only contain that last blank line at the end.
The easiest way to see this is to tell your FileWriter to append instead of truncate:
BufferedWriter wr = new BufferedWriter(new FileWriter(file, true));
If you want the file to contain all the output of the command, but only if it's different from the last run, then obviously you can't make this determination on a line-by-line basis. You instead have to read all the lines into a String, and compare that to the previous run.
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 have a code that starts a java process (i.e.: executing a compiled java code) via
ProcessBuilder builder = new ProcessBuilder("java", "Sample", "arg1", "arg2");
builder.redirectErrorStream(true);
Process process = builder.start();
Through this, I can basically process the output and errors
OutputStream stdin = process.getOutputStream(); // <- Eh?
InputStream stdout = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
// reader.readLine() blah blah
Now, how can I send input to the stdin? That is, if the code executed by the process has a line that waits for an input as in:
Scanner scan = new Scanner(System.in);
String val = scan.nextLine();
System.out.println(val);
I tried this:
writer.write("I'm from the stdin!.");
writer.flush();
Though nothing happened. The console still waited for an input.
Any thoughts?
EDIT: The question was answered, as accepted below. I'm editing to show the faulty code (which I failed to include btw. Lol).
Before the writer.write() part, I had a
String line;
line = reader.readLine();
while (line != null) {
System.out.println(line);
line = reader.readLine();
}
The Process OutputStream (our point of view) is the STDIN from the process point of view
OutputStream stdin = process.getOutputStream(); // write to this
So what you have should be correct.
My driver (apply your own best practices with try-with-resources statements)
public class ProcessWriter {
public static void main(String[] args) throws Exception {
ProcessBuilder builder = new ProcessBuilder("java", "Test");
builder.directory(new File("C:\\Users\\sotirios.delimanolis\\Downloads"));
Process process = builder.start();
OutputStream stdin = process.getOutputStream(); // <- Eh?
InputStream stdout = process.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
writer.write("Sup buddy");
writer.flush();
writer.close();
Scanner scanner = new Scanner(stdout);
while (scanner.hasNextLine()) {
System.out.println(scanner.nextLine());
}
}
}
My application
public class Test {
public static void main(String[] args) throws Exception {
Scanner console = new Scanner(System.in);
System.out.println("heello World");
while(console.hasNextLine()) {
System.out.println(console.nextLine());
}
}
}
Running the driver prints
heello World
Sup buddy
For some reason I need the close(). The flush() alone won't do it.
Edit It also works if instead of the close() you provide a \n.
So with
writer.write("Sup buddy");
writer.write("\n");
writer.write("this is more\n");
writer.flush();
the driver prints
heello World
Sup buddy
this is more
This is an example which maybe can helps someone
import java.io.IOException;
import java.io.File;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws IOException {
String[] commands = {"C:/windows/system32/cmd.exe"};
ProcessBuilder builder = new ProcessBuilder(commands);
builder.directory(new File("C:/windows/system32"));
Process process = builder.start();
OutputStream stdin = process.getOutputStream();
InputStream stdout = process.getInputStream();
InputStream stderr = process.getErrorStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
BufferedReader error = new BufferedReader(new InputStreamReader(stderr));
new Thread(() -> {
String read;
try {
while ((read = reader.readLine()) != null) {
System.out.println(read);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
String read;
try {
while ((read = error.readLine()) != null) {
System.out.println(read);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
while (true) {
try {
Scanner scanner = new Scanner(System.in);
writer.write(scanner.nextLine());
writer.newLine();
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
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
}
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.