PrintWriter to JTextArea, nothing displays until called method closes - java

Context: I am reading data from a serial port at 115.2 Kbaud. The read data is printed using a PrintWriter that I then have appending to a JTextArea.
Everything works well, but the text in the JTextArea does not appear until the method sending the stream from the serial port to my PrintWriter finishes. I'd like it to display closer to real-time, as I will at times be receiving upwards of 20-30 MB of text at a time, and how the general flow of text changes as the program executes would be valuable.
I am using the PrintWriter to JTextArea method here. I think the solution probably has to do with Threads and PipedWriter/PipedReader, but every attempt I've made to implement that has failed miserably.
Thank you for your help.
//code calling method; VerifierReader does not inherit from Reader
//or any such class. it's wholly homegrown. I send it the PrintWriter
//as out, telling it to output there
verifierInstance=new VerifierReader("COM3", verifierOutputLocString.getText());
verifierInstance.setSysOutWriter(out);
verifierInstance.readVerifierStream();
// and the relevant code from VerifierReader
public void setSysOutWriter (PrintWriter outWriter) {
sysOutWriter=new PrintWriter(outWriter);
}
public void readVerifierStream() throws SerialPortException,
InterruptedException{
try{
sysOutWriter.println("Listening for verifier...");
//sysOutWriter.flush();
verifierPort.addEventListener(new verifierListener());
lastReadTimer=System.currentTimeMillis();
while(verifierPort.isOpened()) {
Thread.sleep(1000);
//System.out.println(timeOut);
if( ((long)(System.currentTimeMillis()-lastReadTimer))>timeOut){
sysOutWriter.println("Finished");
verifierPort.removeEventListener();
verifierPort.closePort();
}
}
}
finally {
if (verifierPort.isOpened()) {
verifierPort.closePort();
}
bfrFile.close();
}
}
private class verifierListener implements SerialPortEventListener{
String outBuffer;
public void serialEvent(SerialPortEvent event) {
if(event.isRXCHAR()){//If data is available
timeOut=200;
lastReadTimer=System.currentTimeMillis();
if(event.getEventValue() > 0){//Check bytes count in the input buffer
try {
byte[] buffer = verifierPort.readBytes(event.getEventValue());
outBuffer=new String(buffer);
bfrFile.print(outBuffer);
sysOutWriter.print(outBuffer);
//bfrFile.flush();
//sysOutWriter.flush();
}
catch (SerialPortException ex) {
sysOutWriter.println(ex);
}
}
}
}
}
Edit:
I've attempted what was recommended below, and have made the following changes:
private class VerifierTask extends SwingWorker<Void, String> {
public VerifierTask() throws IOException, SerialPortException, InterruptedException{
verifierInstance= new VerifierReader(streamReader);
verifierInstance.setReaderIO("COM3", verifierOutputLocString.getText());
verifierInstance.readVerifierStream();
}
#Override
protected Void doInBackground() throws IOException{
int charItem;
char[] charBuff = new char[10];
String passString;
while ((charItem = streamReader.read(charBuff, 0, 10)) !=-1) {
passString = new String(charBuff);
publish(passString);
}
return null;
}
#Override
protected void process(List<String> outList) {
for (String output : outList) {
outputArea.append(output);
}
}
}
was added, and I changed my button to immediately invoke a new instance of the VerifierTask class, in addition to making VerifierReader implement a PipedWriter for output (with all of that being Strings).
I'm not sure what I'm doing wrong here. When this code is executed the Java process just freezes indefinitely.
Am I assuming correctly that a VerifierReader created in any VerifierTask thread is tied to that thread, and thus my thread.sleep and while(true) statements no longer pose a problem?

Don't call Thread.sleep or do while (true) on the main Swing event thread, the EDT. Ever. Instead do this sort of thing in a background thread such as one provided via a SwingWorker. You would use the publish/process method pair to get intermediate results to your JTextArea.
For more on this, please check out the tutorial: Concurrency in Swing.

Related

can't get sequential behavior Java Swing [duplicate]

basically, I have this code which was initially working with console i/o now I have to connect it to UI. It may be completely wrong, I've tried multiple things although it still ends up with freezing the GUI.
I've tried to redirect console I/O to GUI scrollpane, but the GUI freezes anyway. Probably it has to do something with threads, but I have limited knowledge on it so I need the deeper explanation how to implement it in this current situation.
This is the button on GUI class containing the method that needs to change this GUI.
public class GUI {
...
btnNext.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
controller.startTest(index, idUser);
}
});
}
This is the method startTest from another class which contains instance of Question class.
public int startTest() {
for (int i = 0; i < this.numberofQuestions; i++) {
Question qt = this.q[i];
qt.askQuestion(); <--- This needs to change Label in GUI
if(!qt.userAnswer()) <--- This needs to get string from TextField
decreaseScore(1);
}
return actScore();
}
askQuestion method:
public void askQuestion() {
System.out.println(getQuestion());
/* I've tried to change staticaly declared frame in GUI from there */
}
userAnswer method:
public boolean userAnswer() {
#SuppressWarnings("resource")
Scanner scanner = new Scanner(System.in);
if( Objects.equals(getAnswer(),userInput) ) {
System.out.println("Correct");
return true;
}
System.out.println("False");
return false;
}
Thanks for help.
You're correct in thinking that it related to threads.
When you try executing code that will take a long time to process (eg. downloading a large file) in the swing thread, the swing thread will pause to complete execution and cause the GUI to freeze. This is solved by executing the long running code in a separate thread.
As Sergiy Medvynskyy pointed out in his comment, you need to implement the long running code in the SwingWorker class.
A good way to implement it would be this:
public class TestWorker extends SwingWorker<Integer, String> {
#Override
protected Integer doInBackground() throws Exception {
//This is where you execute the long running
//code
controller.startTest(index, idUser);
publish("Finish");
}
#Override
protected void process(List<String> chunks) {
//Called when the task has finished executing.
//This is where you can update your GUI when
//the task is complete or when you want to
//notify the user of a change.
}
}
Use TestWorker.execute() to start the worker.
This website provides a good example on how to use
the SwingWorker class.
As other answers pointed out, doing heavy work on the GUI thread will freeze the GUI. You can use a SwingWorker for that, but in many cases a simple Thread does the job:
Thread t = new Thread(){
#Override
public void run(){
// do stuff
}
};
t.start();
Or if you use Java 8+:
Thread t = new Thread(() -> {
// do stuff
});
t.start();

Creating a standalone gui console in java that extends prompt based application

I have several little applications which uses the standard console for retrieving user input and for showing messages troughtout System.in and System.out.
Now i would like to realize some Swing based class which called from these applications, it shows a frame with 2 text area, one for input (so associated to System.in) and another one (not editable) that shows the messages (hence associated to System.out). Actually i have implemented all, (actually creating a simple swing based gui and launching it from the event dispatcher thread is not so complex, the same for exporting all as a jar and including it as a library in the original project). The only problem I have so far, which took me here is about the swapping of the standard System.in and System.out to some custom ones which are associated with the 2 JTextArea. Actually checking some solutions online, I ended up with this few lines of code:
I use 2 PipedInputStream and a PrintWriter:
private final PipedInputStream inPipe = new PipedInputStream();
private final PipedInputStream outPipe = new PipedInputStream();
private PrintWriter inWriter;
then i swap the streams
System.setIn(inPipe);
System.setOut(new PrintStream(new PipedOutputStream(outPipe), true));
inWriter = new PrintWriter(new PipedOutputStream(inPipe), true);
for retrieving the data from the outPipe, I use a SwingWorker whose doInBackgroud method troughtout a Scanner reads the lines from the outPipe and publish them in order to append these string of lines in a not editable JTextArea.
Meanwhile a keyListener checks for VK_ENTER click in order to get the text from the JTextField used as prompt, once this happens, the text is displayed using the System.out itself, and it effectively appears in the previous JTextArea, hence the SwingWorker described above works, and then I wrote the same line of text in the inWriter (the PrintStream object associated to the pipe related to the System.in) so the line should be available to be read from Reader objects which are present in the original application.
Unfortunately this is the only part of the code which does not work. Indeed, once i launch the new gui console, then change the streams, the original application will show only the text it prints on System.out, but when it wants to read the text the user writes, for instance troughtout either a BufferedReader or a Scanner object, nothing happens, as if the the in stream was empty.
I think this is due to the Scanner in the SwingWorker doInBackground method since when it reads the next line on the outPipe, it cleans the stream itself too. Any idea to get rid of this problem? I know i could write new methods for handling input and output but i would like to keep this not-intrusive approach, so without editing the original code, a part the creation of the gui Console object in the original application main method. Thanks in advance.
Update 1
This is the Console class, all is done here
public class Console extends JFrame implements KeyListener
{
private JTextField prompt;
private JTextArea log;
private final PipedInputStream inPipe = new PipedInputStream();
private final PipedInputStream outPipe = new PipedInputStream();
private PrintWriter inWriter;
public Console(String title)
{
super(title);
System.setIn(inPipe);
try
{
System.setOut(new PrintStream(new PipedOutputStream(outPipe), true));
inWriter = new PrintWriter(new PipedOutputStream(inPipe), true);
}
catch(IOException e)
{
System.out.println("Error: " + e);
return;
}
JPanel p = new JPanel();
p.setLayout(null);
log = new JTextArea();
log.setEditable(false);
log.setBounds(10, 10, 345, 250);
p.add(log);
prompt = new JTextField();
prompt.setBounds(10, 270, 356, 80);
prompt.addKeyListener(this);
p.add(prompt);
getContentPane().add(p);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setSize(392, 400);
setLocationRelativeTo(null);
(new SwingWorker<Void, String>()
{
protected Void doInBackground() throws Exception
{
Scanner s = new Scanner(outPipe);
while (s.hasNextLine())
{
String line = s.nextLine();
publish(line);
}
return null;
}
#Override
protected void process(java.util.List<String> chunks)
{
for (String line : chunks)
{
if (line.length() < 1)
continue;
log.append(line.trim() + "\n");
}
}
}).execute();
}
public void execute()
{
String text = prompt.getText();
prompt.setText("");
System.out.println(text);
inWriter.print(text.trim().replaceAll("\r\n", ""));
}
#Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ENTER)
execute();
}
#Override
public void keyReleased(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ENTER)
execute();
}
#Override
public void keyTyped(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_ENTER)
execute();
}
// this is the method called from the original application
public static void setConsole(final String title)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
new Console(title);
//System.out.println("somewhat");
}
});
}
}
I found out the solution by myself, actually all the code posted in the 1st post is almost correct, the problem is with the reading from the System.in the original program, since it was done with a Scanner object (but i tested it with a BufferedReader too) and it seems it's something related to the string termination because if i read by using a simple System.in.read() call, i got the right data, of course char by char. Since both Scanner methods and read one are I/O blocking, I think it's something related to the termination of the string. Because when i wrote it on the InputStream, i cleared the string from any LF and CF char, as you can see in the above code, so when println is invoked only a \r\n should be appended. But reading the result char by char I see \r\n\r\n at the end, so i guess it should be something related to the ending of the string, even if i couldnt figured out the right way to handle it. I will keep you informated by updates tought, but however if you have suggestions and/or feedbacks, they will be obviously welcome.

Android: Thread Runnable and Handler.post issues - (Handler.postXX does not run on time when expected) within the Run method

I have a TCP Socket used as a TCP client which is used to read incoming data from a server constantly -- until the client or the server drops the connection. I don't know of any other way but use a while (true) loop in a different Runnable thread and in it (in the while loop) check for incoming data. The received data needs to be printed in an EditText.
The problem I am having is updating the text from Handler.post(...).
I know that:
In order to create TCP connections with Android 3.0 and above, I am required to place the TCP code either in a new Thread(...) or an AsyncTask because Strict mode has been enabled by default and I do not want to disable it.
I know that in order to update the UI from a new Thread in Android, I need to use Handler.post() if I use a Thread or the onProgressUpdate via publichProgress if I use AsyncTask and I know how to use all these but I am experiencing weird frustrating issues with both of them.
So, all I want to do is:
Listen to the server permanently
As soon as the server sends me a message, example: 'w' or 'a' or n', I immidiately display it on the EditText. You can think of it as a telnet session but I need "more"
precision than telnet as I want to process every single byte, even non-printable ones so I do not want to use readLine in anyway. I must read a byte at a time OR get a buffer of bytes and then process them separately by iterating through the buffer. I went with a byte at a time.
Here is the code I have and please pay attention to my comment above handle.response to see the problem I am having. I hope you can clear this out.
The code is very briefly coded and I have removed a lot of the error checking sections for this sample.
I have a new class called: ThreadClientInput:
public class ThreadClientInput implements Runnable {
InputStream inputStream;
MainActivity mainActivity;
Handler handler = new Handler();
int NextByte = 0;
public ThreadClientInput(MainActivity ma)
{
mainActivity = ma;
}
#Override
public void run()
{
////////////////////////////////////////////////////////////////
// Run the sensitive code that requires us to create this thread
try {
mainActivity.tcp_Client = new Socket("192.168.1.90", 23);
}
catch (Exception e){Log.e("EXEPTION:", e.getMessage().toString());return;}
////////////////////////////////////////////////////////////////
// Only try to get the inputStream if we have a successful connection
if (mainActivity.tcp_Client != null)
{
try
{
inputStream = mainActivity.tcp_Client.getInputStream();
}
catch (Exception e){Log.e("EXEPTION:", e.getMessage().toString()); return;}
}
/////////////////////////////////////////////
/////////////////////////////////////////////
// Update the text on the "Connect" button
handler.post(new Runnable()
{
#Override
public void run()
{ mainActivity.btn_Connect.setText("Connected");}
});
/////////////////////////////////////////////
/////////////////////////////////////////////
try
{
// I need to constantly read the data until we manually disconnect or
// the server drops the connection
while (true)
{
// Get the next byte
// I do not want to use "readline()" from a BufferedReader etc because I need to know exactly what's coming in
// I need to process every single byte
NextByte = inputStream.read();
if (NextByte > -1)
{
Log.e("in (While loop):", Character.toString((char)NextByte));
*** Here is the problem ****
// Update the EditText and this is where the problem starts
// if the server sends "1234", the Log.e() above will display everything correctly: "1234"
// however the handler.post below will run at the end of the last byte so the
// the EditText as well as the second Log.e below within the handle.post will display: "1444" or "4444" or "2444"
// So the handler.post does *not* run immediately even if I try handle.postAtFrontOfQueue()
// If the server sends "12345678", again, Log.e() above will display everything correctly: "12345678"
// however the handler.post below will run at the end of the last byte again
// and I will get "88888888" (most of the time) or "18888888"
//
handler.post(new Runnable()
{
#Override
public void run()
{
mainActivity.et_Response.setText(mainActivity.et_Response.getText() + Character.toString((char)NextByte));
Log.e("In handler.post:", Character.toString((char)NextByte));
}
});
}
}
}
catch (Exception e){Log.e("EXEPTION:", e.getMessage().toString());}
}}
I tried various variations including one with runOnUiThread and AsyncTask, with all I am getting same results. I am out, I have nothing. At this point of time I am just reading some documentation about Handle.post method to see if I can make sense.
I hope you have a solution and I know that "while (true)" isn't a good practice but I can break the loop from outside the thread with setting a flag and I don't know of any other way how to do this.
I am not sure how you are able to access NextByte from public void run() without defining it as final !?
If you want the handler to post message to the Activity UI thread, yo should create it within the activity class so it can access its Looper.
I can think of two solutions for your problem as follows:
1- To use a custom Runnable class where you pass to it the NextByte value as variable e.g.
handler.post(new MyRunnable(NextByte));
And MyRunnable can be something like:
class MyRunnable implements Runnable{
int byteData;
public MyRunnable(int byteData){
this.byteData = byteData;
}
public void run(){ // update the EditText}
}
2- To use handler.sendMessage(); and add the NextByte as the message argument e.g.
Message msg = new Message();
msg.arg1 = NextBye
handler.sendMessage(msg);
And your handler should be defined as following:
handler = new Handler() {
#Override
public void handleMessage(Message msg) {
int nextByte = msg.arg1;
// update the EditText
}
};
Problem has been resolved.
This is what I did this.
In MainActivity.java which is the main file, in class MainActivity
public static class MyRunnable implements Runnable {
int currentByte = 0;
public MyRunnable(int b){
currentByte = b;
}
#Override
public void run()
{
mainActivity.et_Response.setText(mainActivity.et_Response.getText() + Character.toString((char)currentByte));
}
}
I have a statement mainActivity = this; in onCreate and then in ThreadClientInput.run
try {
while (true)
{
NextByte = inputStream.read();
// if the server drops the connection, break out the loop
if (NextByte < 0) break;
handler.post(new MainActivity.MyRunnable(NextByte));
}
}
catch (Exception e) {
Log.e("EXCEPTION", e.getMessage());
}
After this, handler.post is getting called correctly and at the correct and expected time. Full credit goes to iTech.

Synchronisation on java.io.File Object

Is it good to use synchronised on java.io.File Object. When you want to alternatively read and write that File Object using two threads: one for reading and one for writing.
public class PrintChar {
File fileObj;
public void read() {
while (true) {
synchronized (this) {
readFile();
notifyAll();
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()
+ " throws Exception");
e.printStackTrace();
}
}
}
}
public void write(String temp) {
while (true) {
synchronized (this) {
writeFile(temp);
notifyAll();
try {
wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()
+ " throws Exception");
e.printStackTrace();
}
}
}
}
public void setFileObj(File fileObj) {
this.fileObj = fileObj;
}
public void readFile() {
InputStream inputStream;
try {
inputStream = new FileInputStream(fileObj);
// Get the object of DataInputStream
DataInputStream in = new DataInputStream(inputStream);
BufferedReader br = new BufferedReader(new InputStreamReader(in));
String strLine;
// Read File Line By Line
while ((strLine = br.readLine()) != null) {
// Print the content on the console
System.out.println(strLine);
}
in.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public void writeFile(String temp) {
BufferedWriter bw;
try {
bw = new BufferedWriter(new FileWriter(fileObj, true));
bw.write(temp);
bw.newLine();
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String args[]) {
final PrintChar p = new PrintChar();
p.setFileObj(new File("C:\\sunny.txt"));
Thread readingThread = new Thread(new Runnable() {
#Override
public void run() {
p.read();
}
});
Thread writingThread = new Thread(new Runnable() {
#Override
public void run() {
p.write("hello");
}
});
Thread Randomizer = new Thread(new Runnable() {
#Override
public void run() {
while (true)
try {
Thread.sleep(500000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()
+ " throws Exception");
e.printStackTrace();
}
}
});
readingThread.start();
writingThread.start();
Randomizer.start();
}
}
In the code above I have used Synchronised(this), Can i use Synchronise(fileObj)??
One More solution I have got from one of my professors is to encapsulate the read and write in objects and push them in a fifo after every operation, if anybody elaborate on this
Edit:
Now that you have added your code, you can lock on fileObj but only if it is not changed. I would move it to the constructor and make it final to make sure that someone doesn't call setFileObj inappropriately. Either that or throw an exception if this.fileObj is not null.
Couple other comments:
Don't use notifyAll() unless you really need to notify multiple threads.
If you catch InterruptedException, I'd quit the thread instead of looping. Always make good decisions around catching InterruptedException and don't just print and loop.
Your in.close(); should be in a finally block.
You can lock on any object you want as long as both threads are locking on the same constant object. It is typical to use a private final object for example:
private final File sharedFile = new File(...);
// reader
synchronized (sharedFile) {
// read from file
}
...
// writer
synchronized (sharedFile) {
// write to file
}
What you can't do is lock on two different File objects, even if they both point to the same file. The following will not work for example:
private static final String SHARED_FILE_NAME = "/tmp/some-file";
// reader
File readFile = new File(SHARED_FILE_NAME);
synchronized (readFile) {
...
}
// writer
File writeFile = new File(SHARED_FILE_NAME);
synchronized (writeFile) {
...
}
Also, just because you are locking on the same File object does not mean that the reading and writing code will work between the threads. You will need to make sure that in the writer that all updates are flushed in the synchronized block. In the reader you probably do not want to use buffered streams otherwise you will have stale data.
In general, locking across I/O is not a great idea. It's better to construct your program such that you guarantee by design that usually a given section of the file is not being concurrently written and read, and only lock if you absolutely must mediate between reads and writes of a given piece of the file.
Usually not. There are much better ways: Use a ReentrantLock
This class already offers the "lock for reading/writing" metaphor. It also correctly handles the case that many threads can read at the same time but only one thread can write.
As other people already mentioned, locking will only work if all threads use the same File instance.
Make sure you flush the output buffers after each write; this will cost some performance but otherwise, you'll get stale reads (read thread won't find data that you expect to be there).
If you want to simplify the code, add a third thread which accepts commands from the other two. The commands are READ and WRITE. Put the commands in a queue and let the 3rd thread wait for entries in the queue. Each command should have a callback method (like success()) which the 3rd thread will call when the command has been executed.
This way, you don't need any locking at all. The code for each thread will be much more simple and easy to test.
[EDIT] Answer based on your code: It would work in your case because everyone uses the same instance of fileObj but it would mix several things into one field. People reading your code would expect the file object to be just the path to the file to read. So the solution would violate the principle of least astonishment.
If you'd argue that it would save memory, then I'd reply with "premature optimization".
Try to find a solution which clearly communicates your intent. "Clever" coding is good for your ego but that's about the only positive thing that one can say about it (and it's not good for your ego to learn what people will say about you after they see your "clever" code for the first time...) ;-)
Queueing off read/write objects to one thread that then performs the operation is a valid approach to something, but I'm not sure what.
Wha it would not do, for example, is to enforce read/write/read/write order as you specified in your earlier question. There is nothing to stop the read thread queueing up 100 read requests.
That could be prevented by making the thread that submits an object wait on it until it is signaled by the read/write thread, but this seems a very complex way of just enforcing read/write order, (assuming that's what you still want).
I'm getting to the state now where I'm not sure what it is you need/want.

Always blocking input stream for testing?

I'm doing some unit tests where essentially I need the input stream to block forever. Right now I'm using this to construct the input stream
InputStream in = new ByteArrayInputStream("".getBytes());
While it works some of the time, other times the input stream is read before the output stream (what I'm testing) is finished, causing all sorts of havoc.
Essentially I need this input stream to block forever when read. The only solution I can think of is to setup the InputStream with a massive buffer so that the other threads finish, but thats a really hackish and brittle solution. I do have mockito but I'm very new at it and not sure if I can get away with only mocking read without mocking anything else.
Does anyone know of a better solution?
EDIT:
This is my new attempt. It works most of the time, but other times the input thread dies early which causes the Output Thread to die (that behavior is intentional). I can't seem to figure out though why this would sometimes fail.
This is the general test under TestNG simplified for clarity.
protected CountDownLatch inputLatch;
#BeforeMethod
public void botSetup() throws Exception {
//Setup streams for bot
PipedOutputStream out = new PipedOutputStream();
//Create an input stream that we'll kill later
inputLatch = new CountDownLatch(1);
in = new AutoCloseInputStream(new ByteArrayInputStream("".getBytes()) {
#Override
public synchronized int read() {
try {
//Block until were killed
inputLatch.await();
} catch (InterruptedException ex) {
//Wrap in an RuntimeException so whatever was using this fails
throw new RuntimeException("Interrupted while waiting for input", ex);
}
//No more input
return -1;
}
});
Socket socket = mock(Socket.class);
when(socket.getInputStream()).thenReturn(in);
when(socket.getOutputStream()).thenReturn(out);
//Setup ability to read from bots output
botOut = new BufferedReader(new InputStreamReader(new PipedInputStream(out)));
...
}
#AfterMethod
public void cleanUp() {
inputLatch.countDown();
bot.dispose();
}
For the test I use readLine() from botOut to get the appropriate number of lines. The issue though is that when the output thread dies, readLine() blocks forever which hangs up TestNG. I've tried a timeout with mixed results: most of the time it would work but others it would kill tests that just took a little longer than normal to test.
My only other option is to just not use streams for this kind of work. The output thread relies on an output queue, so I could just run off of that. The issue though is that I'm not actually testing writing to the stream, just what is going to be sent, which does bother me.
Mockito is great- I am personally a huge fan!
With Mockito, you can do something like the code below. You basically set up a stream mock, and you tell it to sleep for a very long time when the "read" method is invoked on it. You can then pass this mock into the code you want to test when the stream hangs.
import static org.mockito.Mockito.*;
//...
#Test
public void testMockitoSleepOnInputStreamRead() throws Exception{
InputStream is = mock(InputStream.class);
when(is.read()).thenAnswer(new Answer() {
#Override
public Object answer(InvocationOnMock invocation) {
try {
Thread.sleep(10000000000L);
return null;
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
}
});
//then use this input stream for your testing.
}
I'd make an InputStream that, when read(), does a wait() on something that's held locked till you're done with the rest of the test. Subclass from FilterInputStream to get everything else for free.
There doesn't seem to be any reliable way to do this. My code in the question only works sometimes, #Moe's doesn't work at all, #Ed's suggesting is what I was origionally doing, and #SJuan's is sort of what I'm already doing.
There just seems to be too much stuff going on. The input stream I give to the class is wrapped in a InputStreamReader, then a Buffered reader. Suggestions for other streams inside of other streams just further complicate the issue.
To fix the problem I did what I should of done origionally: Create a factory method for the InputThread (the thread that actually does the reading), then override in my testing. Simple, effective, and 100% reliable.
I suggest anyone that runs into this problem to first try and override the part of your program that does the reading. If you can't then the code I posted is the only semi-reliable code that works in my situation.
Then you need another InputStream flavour. Read block when no more bytes are available, but with ByteArrayOutputStream they are always available until the end of stream is found.
I would extend BAOS by changing read() so it checks for a certain boolean value (if true read, if false wait a second and loop). Then change that variable from your unit code when it is the right time.
Hope that helps
I created a helper class that extends ByteArrayInputStream for my unit tests. It pipes a given byte[] through, but at the end of the stream instead of returning -1 it waits until close() is called. If ten seconds passes it gives up and throws an exception.
If you want it to close earlier you can call latch.countdown() yourself.
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class BlockingByteArrayInputStream extends ByteArrayInputStream {
private CountDownLatch latch;
public BlockingByteArrayInputStream(byte[] buf) {
super(buf);
latch = new CountDownLatch(1);
}
#Override
public synchronized int read() {
int read = super.read();
if (read == -1) {
waitForUnblock();
}
return read;
}
#Override
public int read(byte[] b) throws IOException {
int read = super.read(b);
if (read == -1) {
waitForUnblock();
}
return read;
}
#Override
public synchronized int read(byte[] b, int off, int len) {
int read = super.read(b, off, len);
if (read == -1) {
waitForUnblock();
}
return read;
}
private void waitForUnblock() {
try {
latch.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
throw new RuntimeException("safeAwait interrupted");
}
}
#Override
public void close() throws IOException {
super.close();
latch.countDown();
}
}

Categories