It has been a while since I have had to ask upon the experts here, but I think i must be missing something very simple in understanding in what situation ReadLine() is blocking.
I have a controller that launches external classes (other jvm instances) and communicate via std in and out.
These external classes (processes) are not part of the project and are provided without source code.
For easy of development, i have implemented an option where, instead of an external processes, threads for each “process” are created and the Main method of each “process” is called. Since the “processes” expect communication via stdin/out I have swapped out the System.in and System.out streams with custom streams that instead uses sockets to pretend to be std in/out for each thread.
My problem is that whilst the controller can and does receive input from the child process, the child process “hangs” on the ReadLine() call indefinitely. If i do not use a BufferedReader, and instead simply use InputStream.read() then there is no blocking and the message is successfully received by the process.
Note that the “threading” of the “external” processes does not have any effect… the same problem exists if the “process” is run in a seperate JVM.
Here is a cut down example to demonstrate:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
public class BlockingInputStreamReaderTest {
public static void main(String[] args) throws Exception
{
// use custom std streams to allow per thread stream redirection
StandardStreamManager.stdIn=System.in;
StandardStreamManager.stdOut=System.out;
System.setIn(StandardStreamManager.getInputStreamRedirector());
System.setOut(new PrintStream(StandardStreamManager.getOutputStreamRedirector()));
final int port = 9876;
final List<String> command = Arrays.asList("java TestPlayer".split(" "));
Thread thread = new Thread(new Runnable()
{
@Override
public void run() {
ServerSocket ss=null;
try
{
ss = new ServerSocket(port);
Socket socket = ss.accept();
StandardStreamManager.register(socket.getInputStream(),socket.getOutputStream());
Class playerClass;
playerClass = getClass().getClassLoader().loadClass(command.get(1));
String[] commands = command.subList(2, command.size()).toArray(new String[0]);
Method method = playerClass.getMethod("main", new String[commands.length].getClass());
Object[] args = new Object[1];
args[0] = commands;
method.invoke(null,args);
} catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (ss!=null)
try {
ss.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
thread.start();
PrintStream ps = null;
BufferedReader br = null;
int retries=0;
while (retries++<200)
{
try
{
Socket socket = new Socket("127.0.0.1", port);
ps = new PrintStream( socket.getOutputStream());
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
break;
} catch (IOException e)
{
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
}
System.out.println("CONTROLLER INPUT RECEIVED: "+br.readLine());
System.out.println("CONTROLLER INPUT RECEIVED: "+br.readLine());
// write to player...
while(true)
{
ps.println("You can do it!");
Thread.sleep(500);
}
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.HashMap;
public class StandardStreamManager
{
static public PrintStream stdOut;
static public InputStream stdIn;
static private InputStream getInputStream()
{
Streams streams = registry.get(Thread.currentThread());
if (streams == null)
{
return stdIn;
}
else
{
return streams.is;
}
}
static private OutputStream getOutputStream()
{
Streams streams = registry.get(Thread.currentThread());
if (streams == null)
{
return stdOut;
}
else
{
return streams.os;
}
}
static private class Streams
{
public Streams(InputStream inputStreamRedirectWorker,
OutputStream outputStreamRedirectWorker) {
this.is=inputStreamRedirectWorker;
this.os=outputStreamRedirectWorker;
}
InputStream is;
OutputStream os;
}
static HashMap<Thread, Streams> registry = new HashMap<>();
static public void register(InputStream is, OutputStream os)
{
registry.put(Thread.currentThread(), new Streams(is,os));
}
public static InputStream getInputStreamRedirector() {
return new InputStream() {
@Override
public int read() throws IOException {
InputStream is = getInputStream();
return is.read();
}
};
}
public static OutputStream getOutputStreamRedirector() {
return new OutputStream() {
@Override
public void write(int data) throws IOException {
getOutputStream().write(data);
}
};
}
}
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
public class TestPlayer {
public static void main(String[] args) throws Exception
{
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.err.println("TEST PLAYER SHUTDOWN!");
}
}));
System.err.println("TEST PLAYER START!");
InputStream in = System.in;
// send something to the controller
System.out.println("12345,E\n45678,F");
BufferedReader br = new BufferedReader(new InputStreamReader(in));
while (true)
{
String line = br.readLine();
System.err.println("TestPlayer INPUT RECEIVED: "+line);
}
// String line="";
// while (true)
// {
// line+=(char) in.read();
// if (line.endsWith("\n"))
// {
// System.err.print("TestPlayer INPUT RECEIVED: "+line);
// line="";
// }
// }
}
}
To test that the messages are being sent correctly, uncomment the commented section in TestPlayer and comment the code block above it using the BufferedReader.
Can someone give some suggestions on why BufferedReader would be blocking? Thanks