Java interception standard output (1)

Source: Internet
Author: User
Java interception standard output (1)-Linux general technology-Linux programming and kernel information. The following is a detailed description. Capture console output in Java program
Content:

1. Java pipeline stream
1.1 Note 1
1.2 Note 2
1.3 Note 3
1.4 solve the problem
Ii. Capture Java console output
3. Capture console output of other programs
References
About the author




Yu LiangSong (javaman@163.net)
Software engineers, independent consultants and freelance writers
October 2001

In Java Development, console output is still an important tool, but the default console output has various limitations. This article describes how to use the Java pipeline stream to intercept console output, analyzes the issues that should be paid attention to in the pipeline stream application, and provides examples of intercepting Java programs and non-Java program console output.
Even if the graphic user interface is dominant today, console output still plays an important role in Java programs. The console is not only the default stack trace and error message output window for Java programs, but also a practical debugging tool (especially for those who are used to using println ). However, console windows have many limitations. For example, on Windows 9x, the DOS console can only accommodate 50 rows of output. If a Java program outputs a large amount of content to the console at a time, it is very difficult to view the content.

The console window is especially valuable for developers who use the javaw Startup Program. Because when you start a java program using java W, there is no console window. If the program encounters a problem and throws an exception, you cannot view the call stack trace information written to System. out or System. err in the Java Runtime Environment. To capture stack information, some people use try/catch () block to encapsulate main (), but this method is not always effective. At some time points during Java runtime, some descriptive error messages will be written to System before an exception is thrown. out and System. err; this information is invisible unless the two console streams can be monitored.

Therefore, it is necessary to check the data written to the console stream in the Java Runtime Environment (or a third-party Program) and take appropriate operations. One of the topics discussed in this article is to create an input stream from which you can read data previously written into the Java console stream (or any other program's output stream. We can imagine that the data written to the output stream is immediately "reflux" to the Java program in the form of input.

The goal of this article is to design a Swing-based text window display console output. During this period, we will also discuss important considerations related to Java pipeline streams (PipedInputStream and PipedOutputStream. Shows the Java program used to intercept and Display Console text output. The core of the user interface is a JTextArea. Finally, we need to create a simple program that can capture and display other programs (which can be non-Java programs) output on the console.


: Multi-threaded console output truncation Program



1. Java pipeline stream
To display console output in the text box, we must "intercept" the console stream in some way. In other words, we need a method to efficiently read all the content written to System. out and System. err. If you are familiar with the Java pipeline stream PipedInputStream and PipedOutputStream, we believe that we already have the most effective tool.

Data written to the PipedOutputStream output stream can be read from the corresponding PipedInputStream input stream. The Java pipeline stream greatly facilitates the capture of console output. Listing 1 shows a very simple screenshot console output solution.

[Listing 1: Console output with pipeline stream capture]
PipedInputStream pipedIS = new PipedInputStream ();
PipedOutputStream pipedOS = new PipedOutputStream ();
Try {
PipedOS. connect (pipedIS );
}
Catch (IOException e ){
System. err. println ("connection failed ");
System. exit (1 );
}
PrintStream ps = new PrintStream (pipedOS );
System. setOut (ps );
System. setErr (ps );






As you can see, the code here is extremely simple. We just created a PipedInputStream and set it as the final destination of all data written to the console stream. All data written to the console stream is transferred to PipedOutputStream. In this way, data written to the console stream can be quickly intercepted by reading from the corresponding PipedInputStream. The next thing seems to be that only the data read from the pipedIS stream is displayed in Swing JTextArea to get a program that can display the console output in the text box. Unfortunately, there are some important notes when using Java pipeline streams. Only by taking all these precautions seriously can the Listing 1 code run stably. Next let's take a look at the first note.

1.1 Note 1
PipedInputStream uses a circular buffer with a fixed size of 1024 bytes. The data written to PipedOutputStream is actually saved to the internal buffer of the corresponding PipedInputStream. When a read operation is performed from PipedInputStream, the data read actually comes from this internal buffer zone. If the corresponding PipedInputStream input buffer is full, any thread attempting to write PipedOutputStream will be blocked. The write operation thread will be blocked until the operation to read PipedInputStream is executed to delete data from the buffer zone.

This means that the thread writing data to PipedOutputStream should not be the only thread responsible for reading data from the corresponding PipedInputStream. We can clearly see the problem here: Assume that thread t is the only thread responsible for reading data from PipedInputStream; in addition, assume that t attempts to write () to PipedOutputStream at a time () the method is called to write 2000 bytes of data to the corresponding PipedOutputStream. Before the t thread is congested, it can write up to 1024 bytes of data (the size of the internal buffer of PipedInputStream ). However, once t is blocked, the operation to read PipedInputStream will no longer appear, because t is the only thread to read PipedInputStream. In this way, the t thread is completely blocked. At the same time, all other threads trying to write data to PipedOutputStream will also encounter the same situation.


: Pipeline Flow Process



This does not mean that more than 1024 bytes of data cannot be written in a write () call. However, it should be ensured that while writing data, another thread reads data from PipedInputStream.

Listing 2 demonstrates this problem. This program uses a thread to alternately read PipedInputStream and write PipedOutputStream. Each call to write () writes 20 bytes to the buffer zone of PipedInputStream. Each call to read () only reads and deletes 10 bytes from the buffer zone. The internal buffer is eventually full, leading to write blocking. Since we use the same thread to perform read and write operations, once the write operation is blocked, we cannot read data from PipedInputStream.

[Listing 2: using the same thread to execute read/write operations causes thread blocking]

Import java. io .*;
Public class Listing2 {
Static PipedInputStream pipedIS = new PipedInputStream ();
Static PipedOutputStream pipedOS =
New PipedOutputStream ();

Public static void main (String [] ){
Try {
PipedIS. connect (pipedOS );
}
Catch (IOException e ){
System. err. println ("connection failed ");
System. exit (1 );
}

Byte [] inArray = new byte [10];
Byte [] outArray = new byte [20];
Int bytesRead = 0;

Try {
// Send 20 bytes of data to pipedOS
PipedOS. write (outArray, 0, 20 );
System. out. println ("20 Bytes Sent ...");

// Read 10 bytes in each loop iteration
// Send 20 bytes
BytesRead = pipedIS. read (inArray, 0, 10 );
Int I = 0;
While (bytesRead! =-1 ){
PipedOS. write (outArray, 0, 20 );
System. out. println ("20 Bytes Sent..." + I );
I ++;
BytesRead = pipedIS. read (inArray, 0, 10 );
}
}
Catch (IOException e ){
System. err. println ("error when reading pipedIS:" + e );
System. exit (1 );
}
} // Main ()
}





As long as the read/write operations are separated to different threads, The Listing 2 problem can be easily solved. Listing 3 is the modified version of Listing 2. It writes PipedOutputStream in a separate thread (different from the reading thread ). To prove that the data written at a time can exceed 1024 bytes, the write operation thread writes 2000 bytes each time it calls the PipedOutputStream's write () method. Is the thread created in the startWriterThread () method blocked? According to the thread scheduling mechanism during Java runtime, it will certainly be blocked. Before blocking, write operations can write a maximum of 1024 bytes of payload (the size of the PipedInputStream buffer ). But this will not be a problem, because the main thread will soon read data from the loop buffer of PipedInputStream and empty the buffer space. In the end, the write operation thread starts from the last stop and writes the remaining part of the 2000-byte payload.

[Listing 3: Separate read/write operations to different threads]
Import java. io .*;

Public class Listing3 {
Static PipedInputStream pipedIS =
New PipedInputStream ();
Static PipedOutputStream pipedOS =
New PipedOutputStream ();

Public static void main (String [] args ){
Try {
PipedIS. connect (pipedOS );
}
Catch (IOException e ){
System. err. println ("connection failed ");
System. exit (1 );
}

Byte [] inArray = new byte [10];
Int bytesRead = 0;

// Start the write operation thread
StartWriterThread ();

Try {
BytesRead = pipedIS. read (inArray, 0, 10 );
While (bytesRead! =-1 ){
System. out. println ("read" +
BytesRead + "Byte ...");
BytesRead = pipedIS. read (inArray, 0, 10 );
}
}
Catch (IOException e ){
System. err. println ("read input error .");
System. exit (1 );
}
} // Main ()

// Create an independent thread
// Write to PipedOutputStream
Private static void startWriterThread (){
New Thread (new Runnable (){
Public void run (){
Byte [] outArray = new byte [2000];

While (true) {// loop without termination conditions
Try {
// Before the thread is blocked, a maximum of 1024 bytes of data are written.
PipedOS. write (outArray, 0, 2000 );
}
Catch (IOException e ){
System. err. println ("write operation error ");
System. exit (1 );
}
System. out. println ("2000 bytes sent ...");
}
}
}). Start ();
} // StartWriterThread ()
} // Listing3





Maybe we cannot say that this problem is a defect in Java pipeline stream design, but it must be paid close attention to when applying pipeline stream. Next let's take a look at the second more important (more dangerous) problem.

1.2 Note 2
When reading data from PipedInputStream, if the following three conditions are met, an IOException occurs:

Trying to read data from PipedInputStream,
The buffer zone of PipedInputStream is "null" (that is, there is no readable data ),
The last Thread that writes data to PipedOutputStream is no longer active (detected by Thread. isAlive ).



This is a very delicate moment and an extremely important moment. Assume that one thread w writes data to PipedOutputStream, And the other thread r reads data from the corresponding PipedInputStream. The following events cause the r thread to encounter an IOException when attempting to read PipedInputStream:

W writes data to PipedOutputStream.
W (w. isAlive () returns false ).
R reads data written by w from PipedInputStream and clears the buffer zone of PipedInputStream.
R tries to read data from PipedInputStream again. At this time, the buffer of PipedInputStream is empty and w is over, which leads to an IOException during read operation execution.



It is not difficult to construct a program to demonstrate this problem. You only need to delete the while (true) condition from the startWriterThread () method of Listing 3. This change prevents the write operation method from being executed cyclically, so that the write operation execution method ends after a write operation. As mentioned above, when the main thread tries to read PipedInputStraem, it will encounter an IOException.

This is a rare case and there is no direct correction method. Please do not solve this problem by using the method of the Child class from the pipeline genre ?? It is totally inappropriate to use inheritance here. In addition, if Sun later changes the pipeline stream implementation method, the modifications made now will not be valid.

The last problem is very similar to the second one. The difference is that it generates an IOException when the reading thread ends (rather than the writing thread.

1.3 Note 3
If a write operation is executed on PipedOutputStream and the Thread recently read from the corresponding PipedInputStream is no longer active (detected by Thread. isAlive (), the write operation throws an IOException. Assume that there are two threads w and r, w writes data to PipedOutputStream, and r reads data from the corresponding PipedInputStream. The following series of events will cause the w thread to encounter an IOException when attempting to write PipedOutputStream:

The write operation thread w has been created, but the r thread does not exist.
W writes data to PipedOutputStream.
The read thread r is created and reads data from PipedInputStream.
The r thread ends.
W attempted to write data to PipedOutputStream and found that r has ended, throwing an IOException.



In fact, this problem is not as tricky as the second one. Compared with multiple read threads/single write threads, it may be more common to have one read thread (as the server responding to the request) and multiple write threads (sending requests) in the application.

1.4 solve the problem
To prevent problems caused by the first two limitations of the pipeline stream, one method is to use a ByteArrayOutputStream as a proxy or replace PipedOutputStream. Listing 4 shows a LoopedStreams class. It uses a ByteArrayOutputStream to provide similar functions as a Java pipeline stream, but does not encounter deadlocks or IOException exceptions. This class still uses pipeline streams internally, but isolates the first two problems described in this article. Let's take a look at the public methods of this class (see ). The constructor is simple. It connects to the pipeline stream and then calls the startByteArrayReaderThread () method (this method will be discussed later ). The getOutputStream () method returns an OutputStream (specifically, a ByteArrayOutputStream) to replace PipedOutputStream. Data written to this OutputStream will eventually appear as input in the stream returned by the getInputStream () method. Unlike PipedOutputStream, activation, data writing, and termination of the thread that writes data to ByteArrayOutputStream do not bring negative effects.


: ByteArrayOutputStream Principle


[Listing 4: prevent common problems in pipeline flow applications]
Import java. io .*;

Public class LoopedStreams {
Private PipedOutputStream pipedOS =
New PipedOutputStream ();
Private boolean keepRunning = true;
Private ByteArrayOutputStream byteArrayOS =
New ByteArrayOutputStream (){
Public void close (){
KeepRunning = false;
Try {
Super. close ();
PipedOS. close ();
}
Catch (IOException e ){
// Record errors or other handling
// For simple calculation, we will end it here
System. exit (1 );
}
}
};


Private PipedInputStream pipedIS = new PipedInputStream (){
Public void close (){
KeepRunning = false;
Try {
Super. close ();
}
Catch (IOException e ){
// Record errors or other handling
// For simple calculation, we will end it here
System. exit (1 );
}
}
};


Public LoopedStreams () throws IOException {
PipedOS. connect (pipedIS );
StartByteArrayReaderThread ();
} // LoopedStreams ()


Public InputStream getInputStream (){
Return pipedIS;
} // GetInputStream ()


Public OutputStream getOutputStream (){
Return byteArrayOS;
} // GetOutputStream ()


Private void startByteArrayReaderThread (){
New Thread (new Runnable (){
Public void run (){
While (keepRunning ){
// Check the number of bytes in the stream
If (byteArrayOS. size ()> 0 ){
Byte [] buffer = null;
Synchronized (byteArrayOS ){
Buffer = byteArrayOS. toByteArray ();
ByteArrayOS. reset (); // clear the buffer
}
Try {
// Send the extracted data to PipedOutputStream
PipedOS. write (buffer, 0, buffer. length );
}
Catch (IOException e ){
// Record errors or other handling
// For simple calculation, we will end it here
System. exit (1 );
}
}
Else // No data is available and the thread enters sleep state
Try {
// View ByteArrayOutputStream every 1 second to check new data
Thread. sleep (1000 );
}
Catch (InterruptedException e ){}
}
}
}). Start ();
} // StartByteArrayReaderThread ()
} // LoopedStreams
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.