@MinDoc(copyright="Copyright 2001 - 2002, 2009 A. Weinert", author="Albrecht Weinert", version="V.44", lastModified="6.05.2021", usage="use where one Writer is handled but two are needed", purpose="a Writer that outputs the content to two branches") public class TeeWriter extends Writer
Writer
, that
branches the output it receives to up to two other
Writer
s.
out1 -- < ----X-[ Thread ]----------- > -- out2
(flush1()) block1() / (flush2())
unBlock1() /
/
(write()) /
-- > ------[ Buffer ]-------/
:
:.....- > (getContent())
The TeeWriter's structureWriter
s may be done by
cascading / decorating. Extra abilities like line structure and formating
(by PrintWriter
) etc. will be put once and
for all in front of the (first) TeeWriter (decorator pattern). For a
decorating PrintWriter
, almost always needed,
there is a method getPrintWriter()
. And the
buffering (usually used in line oriented output with good reason) is done
in the TeeWriter.BufferedWriter
is never needed.out1
by a Thread. This (plus the internal buffering) the delivering /
outputting / working thread is effectively shielded from the
"taking qualities" of the first Writer out1
.TeeWriter
a de-coupling, non-blocking and
in that sense "better" BufferedWriter
.TeeWriter
(log writer) as final, so solving a lot of concurrency
problems without loosing the hinted flexibility.SwitchedReader
brings that switchabilty /
flexibility, being in this respect only TeeWriter
's
complement.out1
or out2
impede the writing to the TeeWriter
as whole. So they are caught internally and the methods
write(..)
and others are free from menaced exceptions.
About problems arisen one may extract information via exc1
,
exc2
and lostChars1
.flush1()
, flush2()
or flush()
). A prepended
PrintWriter
with autoFlush (see
getPrintWriter(boolean)
) automatically calls flush()
at
line boundaries.autoFlushThr
out1
every
6.8 s (if something is ready to be output there) respectively as
late as every 121 s is noExplFlush1
is set.System.out
or
System.err
) may block. As a matter of fact a
(some Window's at least) console output is blocked for ever by just
marking text. (And this quite often happens inadvertently e.g. by mouse
movements, may be cause on error about the focus.)out2
) and to the console (out1
). The latter
is just done for the seldom case of a human controller / observer
watching. Of course here it would be unwanted to catastrophic if a
night-watchman on his round would block the console (which may not be
avoided) and hence the server application.out1
) of the both output
Writers. The rationale behind is on one hand an expected technical
asymmetry of the branches, like console and file in the above example.
On the other hand it is an usual asymmetry of the requirements. One of the
outputs (the log file in the above example) has to have the guarantee to
get all outputs without loosing a single character. If it blocks due to
speed problems beyond what buffering can level the outputting thread has
to be blocked. It is the responsibility of the user / programmer /
start-up operator to use performing and reliable resources (local file on
fast and big drive or RAID e.g.) for those purposes.out1
allows to use those blocking by will. This is done by the
methods (block1()
and unBlock1()
.Modifier and Type | Field and Description |
---|---|
int |
autoFlushThr
The threshold for automatic flushing.
|
int |
buffLen
The buffer's length.
|
IOException |
exc1
The first Writer's last caught exception.
|
IOException |
exc2
The second Writer's last caught exception.
|
protected Thread |
flush1Thread
First Writer's flush thread.
|
protected int |
lostChars1
Number of characters lost by (blocked) first Writer.
|
protected int |
maxBlockTime
Maximum block or jamming time by the first Writer / ms .
|
boolean |
noExplFlush1
The first Writer ignores explicit flush().
|
boolean |
noExplFlush2
The second Writer ignores explicit flush().
|
protected Writer |
out1
The first Writer.
|
protected Writer |
out2
The second Writer.
|
protected PrintWriter |
vPw
The PrintWriter "before" this TeeWriter.
|
Constructor and Description |
---|
TeeWriter(int buffLen,
int maxBlockTime)
Make a TeeWriter with no outputs.
|
TeeWriter(OutputStream out)
Make a TeeWriter for an OutputStream.
|
TeeWriter(Writer writer1)
Make a TeeWriter with one output.
|
TeeWriter(Writer writer1,
OutputStream os2)
Make a TeeWriter with two outputs.
|
TeeWriter(Writer writer1,
Writer writer2)
Make a TeeWriter with two outputs.
|
Modifier and Type | Method and Description |
---|---|
void |
block1()
Blocking the first Writer.
|
void |
close()
Close the TeeWriter.
|
void |
close1()
Closing the first Writer.
|
void |
close2()
Closing the second Writer.
|
void |
flush()
Flushing the TeeWriter.
|
void |
flush1()
Flushing the first Writer.
|
void |
flush2()
Flushing the second Writer.
|
void |
forceFlush1()
Flushing the first Writer (forcefully).
|
int |
getBuffLen()
This TeeWriter's buffer length.
|
String |
getContent(int maxLen,
boolean early,
int maxNLsearch)
The buffer's current content as String.
|
int |
getContentLen()
The buffer's current length / the number of available characters.
|
int |
getLostChars1(boolean doClear)
Number of characters lost by (blocked) first Writer.
|
int |
getMaxBlockTime()
Maximum blocking / jamming time by the first Writer in ms .
|
Writer |
getOut1()
The first Writer.
|
Writer |
getOut2()
The second Writer.
|
PrintWriter |
getPrintWriter(boolean autoflush)
Get the (one) PrintWriter connected to this TeeWriter.
|
boolean |
isClosed()
Closed and dead.
|
boolean |
isOpen()
Not yet closed / still usable.
|
void |
setMaxBlockTime(int maxBlockTime)
Set maximum Block or jamming time by the first Writer / ms .
|
TeeWriter |
setOut1(OutputStream os1)
Setting / connecting the first Writer as OutputStream.
|
TeeWriter |
setOut1(OutputStream os1,
CharSequence cp1)
Setting / connecting the first Writer.
|
TeeWriter |
setOut1(Writer wr1)
Setting / connecting the first Writer.
|
TeeWriter |
setOut2(OutputStream os2)
Setting the second Writer as OutputStream.
|
TeeWriter |
setOut2(OutputStream os2,
CharSequence cp2)
Setting the second Writer.
|
TeeWriter |
setOut2(Writer wr2)
Setting the second Writer.
|
TeeWriter |
switchOut1(Writer wr1)
Switching the first Writer.
|
void |
unBlock1()
Unblocking the first Writer.
|
void |
write(char c)
Output a character (char) to the TeeWriter.
|
void |
write(char[] cbuf,
int off,
int len)
Output a char[] to the TeeWriter.
|
void |
write(int ic)
Output a character (char provided as int) to the TeeWriter.
|
protected volatile PrintWriter vPw
getPrintWriter()
public final int buffLen
getBuffLen()
public final int autoFlushThr
autoFlushThr
characters are held back in the buffer
from one of the output writers it will be flushed.buffLen
).protected volatile Writer out1
public volatile IOException exc1
public volatile boolean noExplFlush1
noExplFlush1
is true, explicit calls of flush()
(flush1()
) will not be executed any more for out1
.
Hence the output behind will receive any calls quite seldom using
TeeWriter's and out1
's buffering (if given) much more
efficiently.out2
.out1
is normally taken for the
visible branch blockable by humans (like standard out).protected Thread flush1Thread
protected int maxBlockTime
getMaxBlockTime()
protected int lostChars1
getLostChars1(boolean)
protected volatile Writer out2
public volatile IOException exc2
public volatile boolean noExplFlush2
noExplFlush2
is true, explicit calls of flush()
(flush2()
) will not be executed any more for out2
.
The effect will be out2
's not receiving further calls.noExplFlush1
this noExplFlush2
will be
(automatic) reset by the following events:close2()
close()
setOut2()
autoFlushThr
i(in the buffer)out2
has no decoupling thread this auto reset avoids a
jamming of the TeeWriter due to noExplFlush2
.public TeeWriter(OutputStream out)
out
supplied as parameter using the default encoding.TeeWriter
(new
OutputStreamWriter
(out)).out
- the stream to be used as one T outputNullPointerException
- if out is nullpublic TeeWriter(Writer writer1)
writer1
.buffLen
is set to 20480 characters and
the maximal blocking time maxBlockTime
to 50 ms.writer1
- the Writer to be used as one T outputpublic TeeWriter(Writer writer1, Writer writer2)
writer1
and writer2
.writer1
- the Writer to be used as one T outputwriter2
- the Writer to be used as second T outputpublic TeeWriter(Writer writer1, OutputStream os2)
writer1
and OutputStream os2
, the latter
using default encoding.writer1
- the Writer to be used as one T outputos2
- the stream to be used as second T outputpublic TeeWriter(int buffLen, int maxBlockTime)
buffLen
and the maximal blocking time
are set according to the supplied parameter values.maxBlockTime
- new maximal blocking time in ms; limited to 4...9800buffLen
- the buffer size (1025 .. 400001)setMaxBlockTime(int)
public PrintWriter getPrintWriter(boolean autoflush)
PrintWriter
set as decorator in
front of this TeeWriter. It is made on the first call.autoflush
- if true, the println, printf, or
format methods will flush the output bufferpublic boolean isClosed()
getContent()
and
getContentLen()
.close()
public boolean isOpen()
close()
,
isClosed()
,
Channel.isOpen()
public void close()
isClosed()
.getPrintWriter()
this
is not necessary.close
in interface Closeable
close
in interface AutoCloseable
close
in class Writer
isClosed()
public final int getBuffLen()
flush1()
,
flush2()
,
flush()
,
getPrintWriter()
public final int getContentLen()
write()
,
close()
,
getContent()
public final String getContent(int maxLen, boolean early, int maxNLsearch)
maxLen
more than the actual buffer content
size (see getContentLen()
) it will be reduced to it. This method
returns up to maxLen buffered characters as String. Is early false, the
end of the buffer is chosen (the latest output) otherwise the start of the
buffer (the oldest output not overwritten yet).maxNLsearch
is greater than 0 and less than 1/3 of the (may be
reduced) maxLen
the start and end of chosen buffer range is
shortened by at most maxNLsearch
character to let the String
returned start and end at line borders. (80 .. 150 are a good
choices). no such clipping occurs on very first or very last (before
close) output.maxLen
is <= 0, the
empty String is returned.write()
,
getContentLen()
public Writer getOut1()
public void block1()
out1
of the TeeWriter shall be blocked by will.
If this blocking is transitional (what it normally should be) it is
dismissed by unBlock1()
.out1
for other purposes (like e.g. an input dialogue
via console).public void unBlock1()
public void close1()
public final int getMaxBlockTime()
out1
blocks and as consequence no further
buffering is possible without overriding output not yet forwarded to
out1
, the whole TeeWriter is blocked at first. This blocking
is (forcefully) removed after maxBlockTime
milliseconds, even if
that would mean loss of (part of) output to out1
.out2
's speed also) is faster than out1
or
if out1
is simply jammed.lostChars1
)
they can to a certain extend be reduced by larger buffers or longer
blocking times. But they are a symptom for out1
being slower
than the application requires (and out2
allows).setMaxBlockTime(int)
public final void setMaxBlockTime(int maxBlockTime)
maxBlockTime
- new maximal blocking time in ms; limited to 4...9800getMaxBlockTime()
public int getLostChars1(boolean doClear)
close1()
) or switching
(setOut1()
) the first Writer the sum is
reset to zero as well as by this method if doClear
is true.public void flush1()
public void forceFlush1()
flush1()
this method's effect is not suspended
by noExplFlush1
.public TeeWriter setOut1(Writer wr1)
wr1
equals an already set Writer (be it 1 or 2)
nothing happens.flush1()
(if not blocked)
and closed by close1()
. If the parameter wr1
is null
that was all.wr1
will be set as new Writer 1. It starts not
blocked (unBlock1()
), even if its predecessor switched from
was. The setting noExplFlush1
will be kept unchanged.public TeeWriter switchOut1(Writer wr1)
wr1
equals an already set Writer (be it 1 or 2)
nothing happens. If there is no set Writer 1 yet this method acts like
setOut1
.setOut1
— the former
first Writer is just disconnected, i.e neither flushed nor closed. It is
replaced on the spot by the Writer wr1
supplied as parameter. All
settings remain.public TeeWriter setOut1(OutputStream os1)
os1
will be decorated by a Writer (default
encoding) and connected by
setOut1(Writer)
as first Writer.os1
- the OutputStream to use as TeeWriter's first branchpublic TeeWriter setOut1(OutputStream os1, CharSequence cp1)
os1
will be decorated by a Writer with the
character encoding supplied by cp1
and connected by
setOut1(Writer)
as first Writer.os1
- the OutputStream to use as TeeWriter's first branchcp1
- the encoding. null will be taken Cp850 (Western Windows).
A non implemented encoding will be replaced by the default
encodingpublic Writer getOut2()
public void flush2()
public void close2()
public TeeWriter setOut2(Writer wr2)
wr2
equals an already set Writer (be it 1 or 2)
nothing happens.close2()
. If the parameter
wr2
is null that was all.wr2
will be set as new Writer 2.public TeeWriter setOut2(OutputStream os2)
os2
will be decorated by a Writer (default
encoding) and connected by
setOut2(decoOs2)
as second Writer.os2
- the OutputStream to use as TeeWriter's second branchpublic TeeWriter setOut2(OutputStream os2, CharSequence cp2)
os2
will be decorated by a Writer with the
character encoding supplied by cp2
and connected by
setOut2(Writer)
as second Writer.os2
- the OutputStream to use as TeeWriter's second branchcp2
- the encoding. null will be taken Cp850 (Western Windows).
A not implemented encoding will be replaced by the default
encodingpublic void flush()
public void write(char[] cbuf, int off, int len)
write(char)
and write(int)
.public void write(int ic)
ic
as character (char) to
this TeeWriter. The effect is like
write
((char) ic)
public void write(char c)