001/*-------------------------------------------------------------------------+
002|                                                                          |
003| Copyright 2005-2011 The ConQAT Project                                   |
004|                                                                          |
005| Licensed under the Apache License, Version 2.0 (the "License");          |
006| you may not use this file except in compliance with the License.         |
007| You may obtain a copy of the License at                                  |
008|                                                                          |
009|    http://www.apache.org/licenses/LICENSE-2.0                            |
010|                                                                          |
011| Unless required by applicable law or agreed to in writing, software      |
012| distributed under the License is distributed on an "AS IS" BASIS,        |
013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
014| See the License for the specific language governing permissions and      |
015| limitations under the License.                                           |
016+-------------------------------------------------------------------------*/
017package org.conqat.lib.commons.io;
018
019import java.io.BufferedReader;
020import java.io.IOException;
021import java.io.InputStream;
022import java.io.InputStreamReader;
023import java.nio.charset.Charset;
024import java.nio.charset.StandardCharsets;
025
026/**
027 * A thread to drain an input stream. Storing the content is optional.
028 *
029 * If an exception occurs during draining, the exceptions message is appended to
030 * the content and the exception is also made available via
031 * {@link #getException()}. So if the caller wants to ensure that the content is
032 * really complete, he not only has to wait for the end of the thread via join,
033 * but also check this method.
034 */
035public class StreamReaderThread extends Thread {
036
037        /** Stream the reader reads from. */
038        private final InputStream input;
039
040        /** Content read from the stream. */
041        private final StringBuilder content = new StringBuilder();
042
043        /** Whether to store content or not. */
044        private final boolean storeContent;
045
046        /** Charset to convert streams to strings. */
047        private final Charset charset;
048
049        /** Exception that occurred (or null). */
050        private IOException exception;
051
052        /**
053         * Create a new reader that reads the content of this stream in its own
054         * thread. => This call is non-blocking.
055         * <p>
056         * This constructor causes the content to be stored.
057         *
058         * @param input
059         *            Stream to read from. This stream is not automatically closed,
060         *            but must be closed by the caller (if this is intended).
061         *
062         */
063        public StreamReaderThread(InputStream input) {
064                this(input, StandardCharsets.UTF_8, true);
065        }
066
067        /**
068         * Create a new reader that reads the content of this stream in its own
069         * thread. => This call is non-blocking.
070         * <p>
071         * This constructor causes the content to be stored.
072         *
073         * @param input
074         *            Stream to read from. This stream is not automatically closed,
075         *            but must be closed by the caller (if this is intended).
076         * @param charset
077         *            Character set to be used to convert the stream into a string.
078         */
079        public StreamReaderThread(InputStream input, Charset charset) {
080                this(input, charset, true);
081        }
082
083        /**
084         * Create a new reader that reads the content of this stream in its own
085         * thread. => This call is non-blocking
086         *
087         * @param input
088         *            Stream to read from. This stream is not automatically closed,
089         *            but must be closed by the caller (if this is intended).
090         *
091         */
092        public StreamReaderThread(InputStream input, Charset charset, boolean storeContent) {
093                this.input = input;
094                this.charset = charset;
095                this.storeContent = storeContent;
096                start();
097        }
098
099        /**
100         * Reads content from the stream as long as the stream is not empty.
101         */
102        @Override
103        public synchronized void run() {
104                BufferedReader reader = new BufferedReader(new InputStreamReader(input, charset));
105                char[] buffer = new char[1024];
106
107                try {
108                        int read = 0;
109                        while ((read = reader.read(buffer)) != -1) {
110                                if (storeContent) {
111                                        content.append(buffer, 0, read);
112                                }
113                        }
114                } catch (IOException e) {
115                        exception = e;
116                        content.append(e);
117                }
118        }
119
120        /** Returns the content read from the stream. */
121        public synchronized String getContent() {
122                return content.toString();
123        }
124
125        /**
126         * If everything went ok during reading from the stream, this returns null.
127         * Otherwise the exception can be found here.
128         */
129        public IOException getException() {
130                return exception;
131        }
132}