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}