Java - Strange behaviour on PrintStream with custom OutputStream - java

I am trying to write a program that redirects System.out to a JTextArea (it doesn't have to be a JTextArea), but when I call System.out.println("Test!") the output to the text area is like so:
\n
st!
\n
The code for my OutputStream:
package gui;
import java.awt.*;
import java.io.*;
import javax.swing.text.*;
public class LogOutputStream extends OutputStream
{
public void write(final int b) throws IOException
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
write0(b);
}
});
}
public void write(final byte[] b, final int off, final int len)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
write0(b, off, len);
}
});
}
public void write(final byte[] b)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
write0(b);
}
});
}
private void write0(int b)
{
Document doc = FernflowerGUI.frame.textArea.getDocument();
try
{
doc.insertString(doc.getLength(), String.valueOf((char)b), null);
}
catch(BadLocationException impossible)
{
}
}
private void write0(byte[] b, int off, int len)
{
Document doc = FernflowerGUI.frame.textArea.getDocument();
try
{
doc.insertString(doc.getLength(), new String(b, off, len), null);
}
catch(BadLocationException impossible)
{
}
}
private void write0(byte[] b)
{
write0(b, 0, b.length);
}
}
The code that creates the PrintStream:
PrintStream ps = new PrintStream(new LogOutputStream(), true);
Can anyone please tell me what on Earth is going on?

Your code isn't thread-safe, basically.
You're accepting a synchronous call accepting a byte array - and then you're using that byte array later, and assuming it will still have the same content. What if the caller to write() overwrites the data in the byte array immediately after the method returns? By the time you get to use it, you won't have the right data.
I would extract the String from the byte array in your write call, and then use that String in the call to write0.
(I'd also personally use a Writer rather than an OutputStream - fundamentally you want to deal with text data, not binary data.)

Related

Netty TCP Socket InputStream

Netty TCP Server is running at port 8000 receiving NMEA format data. It uses Marine API library to convert the gibberish to a meaningful information which needs input stream from the socket.
SentenceReader sentenceReader = new SentenceReader(socket.getInputStream());
sentenceReader.addSentenceListener(new MultiSentenceListener());
sentenceReader.start();
How can i get inputstream for netty server port being used?
SentenceReader does not have any method to accept "streamed in" data, however with subclassing, it can be made to accept the data.
The core of SentenceReader uses a DataReader for its data, normally this datareader is polled from a seperate thread SentenceReader itself, and we can modify this structure to get what we need.
First, we subclass SentenceReader with our own class, give it the proper constructor and methods we want, and remove the effect of the start and stop methods. We provide null as the file for now (and hope future versions provide a method to pass a datareader in directly)
public class NettySentenceReader extends SentenceReader {
public NettySentenceReader () {
super((InputStream)null);
}
#Override
public void start() {
}
#Override
public void stop() {
}
}
We now need to implement all functionality of the internal class DataReader inside our own Netty handler, to replicate the same behaviour
public class SentenceReaderHandler extends
SimpleChannelInboundHandler<String> {
private SentenceFactory factory;
private SentenceReader parent;
public SentenceReaderHandler (SentenceReader parent) {
this.parent = parent;
}
#Override
public void channelRegistered(ChannelHandlerContext ctx) {
if(!ctx.channel().isActive())
return;
//ActivityMonitor monitor = new ActivityMonitor(parent);
this.factory = SentenceFactory.getInstance();
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
//ActivityMonitor monitor = new ActivityMonitor(parent);
this.factory = SentenceFactory.getInstance();
}
#Override
// This method will be renamed to `messageReceived` in Netty 5.0.0
protected void channelRead0(ChannelHandlerContext ctx, String data)
throws Exception {
if (SentenceValidator.isValid(data)) {
monitor.refresh();
Sentence s = factory.createParser(data);
parent.fireSentenceEvent(s);
} else if (!SentenceValidator.isSentence(data)) {
parent.fireDataEvent(data);
}
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
//monitor.reset();
parent.fireReadingStopped();
}
#Override
public void channelUnregistered(ChannelHandlerContext ctx) {
if(!ctx.channel().isActive())
return;
//monitor.reset();
parent.fireReadingStopped();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
parent.handleException("Data read failed", e);
}
}
Finally, we need to integrate this into a Netty pipeline:
SentenceReader reader = new NettySentenceReader();
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
private static final StringDecoder DECODER = new StringDecoder();
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(DECODER);
pipeline.addLast(new SentenceReaderHandler(reader));
}
});
You can't easily as InputStream is blocking and netty is an async - non blocking API.

JAVA Synchronize cycle with callbacks

I have this function that downloads all files in the ArrayList and I want to make this "synchronous" , I mean that I want to download only one file at time.
How can I make the FOR cycle to wait until a file is downloaded and than take an other file to download?
public void downloadFiles(ArrayList<String> files, final String destinationFolder){
for(String file:files){
GoogleDrive.getInstance().readFile(file, GoogleDrive.FolderLocation.ROOT_FOLDER, new GoogleDrive.GoogleDriveReadFileCallback() {
#Override
public void successful(String title, byte[] content) {
try {
FileUtils.writeByteArrayToFile(new File(destinationFolder+File.pathSeparator+title), content);
} catch (IOException e) {
Log.e(TAG,"ERROR FileManager.downloadFiles: "+e.toString());
}
}
#Override
public void notFound() { }
#Override
public void error(String error) { }
});
}
}
The question sounds pretty simple; but turns out to be hard. Why is that? Because the given code is doing things in a wrong way. What do I mean with that?
I assume that
GoogleDrive.getInstance().readFile(file,
GoogleDrive.FolderLocation.ROOT_FOLDER,
new GoogleDrive.GoogleDriveReadFileCallback()
triggers an asynchronous read from Google Drive; and upon competition, that callback instance will be called. But when we have a closer look into that callback code - we find that it is missing essential parts:
it is not doing any kind of error handling (hint: you have no idea when something went wrong with this approach)
the callback has no means to "signal" to the outside world "i am done".
Thus: the solution is to rework that thing completely. You could create a real class implementing the required interface; and that callback implementation could have methods that tell you whether file reading is still ongoing, completed successfully or failed.
In other words: you build a wrapper around GoogleDrive readFile(); and that wrapper offers synchronous reading (probably successfull() gets called when the readFile() is done - so your wrapper can simply wait for that callback); or the wrapper could return some sort of Future.
24 hours later the answear was too easy, just implemented a listener that start a new download every time an old one is terminated(with success or not) and remove it from the list. I don't know if this is the correct way to do it but it works
interface FileManagerDownloadEvent{
void downloadSuccessful(String fileName);
void downloadNotFound(String fileName);
void downloadError(String fileName,String error);
}
public class FileManager implements FileManagerDownloadEvent{
private FileManagerDownloadEvent downloadEvent;
private ArrayList<String> filesToDownload;
private String destinationFolder;
public FileManager(){
this.downloadEvent=this;
}
private void download(){
if(filesToDownload.size()!=0) {
final String file=filesToDownload.get(0);
filesToDownload.remove(0);
GoogleDrive.getInstance().readFile(file, GoogleDrive.FolderLocation.ROOT_FOLDER, new GoogleDrive.GoogleDriveReadFileCallback() {
#Override
public void successful(String title, byte[] content) {
try {
FileUtils.writeByteArrayToFile(new File(destinationFolder+File.separator+title), content);
downloadEvent.downloadSuccessful(destinationFolder+File.separator+title);
} catch (Exception e) {
Log.e(TAG,"ERROR FileManager.downloadFiles: "+e.toString());
}
}
#Override
public void notFound() {
downloadEvent.downloadNotFound(file);
}
#Override
public void error(String error) {
downloadEvent.downloadError(file,error);
}
});
}
}
#Override
public void downloadSuccessful(String filePath) {
Log.d(TAG,"downloadSuccessful: "+filePath);
download();
}
#Override
public void downloadNotFound(String fileName) {
Log.e(TAG,"downloadNotFound: "+fileName);
download();
}
#Override
public void downloadError(String fileName,String error) {
Log.e(TAG,"downloadError: "+fileName+" --> "+error);
download();
}
}

ByteBuffer and partial write

If the ByteBuffer is written partially, the position is updated and the next _channel.write call will resume from last position, yep?
compact() is not necessary?
private AsynchronousSocketChannel _channel;
private ByteBuffer _buffer;
final CompletionHandler<Integer, LogstashClientStream> _writeCompletionHandler = new CompletionHandler<Integer, LogstashClientStream>(){
#Override
public void completed(Integer sent, LogstashClientStream self) {
if( _buffer.remaining() == 0 ){
_buffer.clear();
//...
}
else {
// partial write
self.send();
}
}
#Override
public void failed(Throwable exc, LogstashClientStream self) {
//...
}
};
private void send(){
try{
_channel.write( _buffer, this, _writeCompletionHandler);
} catch(Throwable e){
//...
}
}
Yes, it will resume, and no, compact() is not necessary here. It's useful mainly in cases when you want to fill the rest of the buffer from some input stream before invoking write() again.

How to specify the type of threads between which an exchange takes place using an exchanger?

I have two Runnable classes, Reader and Writer.
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.concurrent.Exchanger;
public class Reader implements Runnable {
private static final int THRESHHOLD = 1000;
private final int START, STOP;
private Exchanger<ByteBuffer> exch;
private RandomAccessFile file;
private ByteBuffer buffer;
public Reader(Exchanger<ByteBuffer> ex, RandomAccessFile f, int start, int stop) {
START = start;
STOP = stop;
exch = ex;
file = f;
buffer = ByteBuffer.allocate(THRESHHOLD);
buffer.mark();
}
#Override
public void run() {
for(int i = START; i < STOP; i++)
try {
buffer.put((byte)file.read());
} catch(IOException e) {
e.printStackTrace();
}
try {
exch.exchange(buffer);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.util.concurrent.Exchanger;
import java.util.concurrent.locks.ReentrantLock;
public class Writer implements Runnable {
private static final int THRESHHOLD = 1000;
private final int START, STOP;
private ReentrantLock lock;
private Exchanger<ByteBuffer> exch;
private RandomAccessFile file;
private ByteBuffer buffer;
public Writer(Exchanger<ByteBuffer> e, ReentrantLock l, RandomAccessFile f, int start, int stop) {
lock = l;
START = start;
STOP = stop;
exch = e;
file = f;
}
#Override
public void run() {
try {
buffer = exch.exchange(ByteBuffer.allocate(THRESHHOLD));
} catch(InterruptedException e) {
e.printStackTrace();
}
lock.lock();
for(int i = START; i < STOP; i++)
try {
file.write(buffer.get());
} catch(IOException e) {
e.printStackTrace();
}
lock.unlock();
}
}
Both these threads use an Exchanger to exchange the same kind of data. How can I ensure that the exchange takes place between Reader and Writer threads only, and not between two threads of the smae kind?
Your question is little unclear. Since you are sharing the same exchanger instance between these reader and writer no other thread will be able to participate in this exchange.
If you are afraid that two threads "of the same kind" could invoke the exchange method of the same Exchanger instance (it's difficult to guess how it's possible from the example you brought, but you know your working mix better), then you can intercept (either by sub-classing or by delegation, whichever you like more) this method and check whether the combination of threads is right or wrong and what should done in every case.
Here is an example by sub-classing, was tested only for a simplest case
public class ThreadStrictExchanger<V> extends Exchanger<V> {
private Thread waitingThread;
#Override
public V exchange(V x) throws InterruptedException {
Thread currentThread = Thread.currentThread();
if (waitingThread == null){
waitingThread = currentThread;
} else {
checkThreads(waitingThread, currentThread);
waitingThread = null;
}
return super.exchange(x);
}
private void checkThreads(Thread waitingThread, Thread currentThread) {
//TODO add here your logic/change
}
}
You could compare the two threads by type or by name (use Thread.setName()/getName()). It's up to you what action to take if the combination of two threads in checkThreads method is wrong - either throw an exception or even return false to indicate that the actual invocation of Exchanger.exchange should be skipped (if it doesn't break the caller's logic).
Just replace Exchanger with your class on Reader and Writer ctors' invocations. Please note that another exchange method is not overridden but, if you need to, the logic will be the same.

How to handle a variable being used by 2 threads?

One thread keeps reading bytes received from a BufferedReader. The data comes from a SerialPort.
On the main thread, there is a JMenuItem when it's clicked the serial port is closed and the BufferedReader should stop receiving the messages.
The problem is:
If I try to close while messages are being read, the application will stuck and the serial port won't be closed until the port stops sending messages.
So basically, I should close the reader before closing the serial port. If I do this, sometimes I get a null pointer exception because I close the buffered reader while he is being read.
How can I solve this issue?
It sounds like you can fix this with a stop method in your reader class (called from the menu item's click event)
private boolean isStopped = false;
public void stop() {
isStopped = true;
}
while(bufferedReader.isReady()) {
bufferedReader.read();
if(isStopped) {
bufferedReader.close();
}
}
This way you ensure that you don't call close until all read calls have completed.
The simplest thing to do would be to create a SynchronizedReader class which would wrap your BufferedReader. But without more context, I cannot guarantee this will work, especially if you have calling code which makes multiple interdependent calls to the Reader (you would then need to ensure that all calls are made in a single synchronized(reader) block).
import java.io.IOException;
import java.io.Reader;
import java.nio.CharBuffer;
public class SynchronizedReader extends Reader {
private Reader reader;
public SynchronizedReader(Reader reader) {
super();
this.reader = reader;
}
#Override
public synchronized int read(char[] cbuf, int off, int len) throws IOException {
return reader.read(cbuf, off, len);
}
#Override
public synchronized void close() throws IOException {
reader.close();
}
#Override
public synchronized int hashCode() {
return reader.hashCode();
}
#Override
public synchronized int read(CharBuffer target) throws IOException {
return reader.read(target);
}
#Override
public synchronized int read() throws IOException {
return reader.read();
}
#Override
public synchronized int read(char[] cbuf) throws IOException {
return reader.read(cbuf);
}
#Override
public synchronized boolean equals(Object obj) {
return reader.equals(obj);
}
#Override
public synchronized long skip(long n) throws IOException {
return reader.skip(n);
}
#Override
public synchronized boolean ready() throws IOException {
return reader.ready();
}
#Override
public synchronized boolean markSupported() {
return reader.markSupported();
}
#Override
public synchronized void mark(int readAheadLimit) throws IOException {
reader.mark(readAheadLimit);
}
#Override
public synchronized void reset() throws IOException {
reader.reset();
}
#Override
public synchronized String toString() {
return reader.toString();
}
}

Categories