001/* 002 * #%L 003 * Netarchivesuite - common 004 * %% 005 * Copyright (C) 2005 - 2018 The Royal Danish Library, 006 * the National Library of France and the Austrian National Library. 007 * %% 008 * This program is free software: you can redistribute it and/or modify 009 * it under the terms of the GNU Lesser General Public License as 010 * published by the Free Software Foundation, either version 2.1 of the 011 * License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Lesser Public License for more details. 017 * 018 * You should have received a copy of the GNU General Lesser Public 019 * License along with this program. If not, see 020 * <http://www.gnu.org/licenses/lgpl-2.1.html>. 021 * #L% 022 */ 023 024package dk.netarkivet.common.utils; 025 026import java.io.BufferedReader; 027import java.io.FileInputStream; 028import java.io.FileOutputStream; 029import java.io.FileReader; 030import java.io.IOException; 031import java.io.InputStream; 032import java.io.OutputStream; 033import java.nio.channels.FileChannel; 034 035import javax.servlet.jsp.JspWriter; 036 037import org.dom4j.Document; 038import org.dom4j.io.OutputFormat; 039import org.dom4j.io.XMLWriter; 040import org.slf4j.Logger; 041import org.slf4j.LoggerFactory; 042 043import dk.netarkivet.common.Constants; 044import dk.netarkivet.common.exceptions.ArgumentNotValid; 045import dk.netarkivet.common.exceptions.IOFailure; 046 047/** 048 * Utilities for handling streams. 049 */ 050public class StreamUtils { 051 052 /** logger for this class. */ 053 private static final Logger log = LoggerFactory.getLogger(StreamUtils.class); 054 055 /** Constant for UTF-8. */ 056 private static final String UTF8_CHARSET = "UTF-8"; 057 058 /** 059 * Will copy everything from input stream to jsp writer, closing input stream afterwards. Charset UTF-8 is assumed. 060 * 061 * @param in InputStream to copy from 062 * @param out JspWriter to copy to 063 * @throws ArgumentNotValid if either parameter is null 064 * @throws IOFailure if a read or write error happens during copy 065 */ 066 public static void copyInputStreamToJspWriter(InputStream in, JspWriter out) { 067 ArgumentNotValid.checkNotNull(in, "InputStream in"); 068 ArgumentNotValid.checkNotNull(out, "JspWriter out"); 069 070 byte[] buf = new byte[Constants.IO_BUFFER_SIZE]; 071 int read = 0; 072 try { 073 try { 074 while ((read = in.read(buf)) != -1) { 075 String s = new String(buf, UTF8_CHARSET); 076 if (s.length() < read) { 077 log.debug("String length ({}) < bytes read({})", s.length(), read); 078 out.write(s, 0, s.length()); 079 } else { 080 out.write(s, 0, read); 081 } 082 // Reinitializing the buffer to avoid garbage in buffer 083 buf = new byte[Constants.IO_BUFFER_SIZE]; 084 } 085 } finally { 086 in.close(); 087 } 088 } catch (IOException e) { 089 String errMsg = "Trouble copying inputstream " + in + " to JspWriter " + out; 090 log.warn(errMsg, e); 091 throw new IOFailure(errMsg, e); 092 } 093 } 094 095 /** 096 * Will copy everything from input stream to output stream, closing input stream afterwards. 097 * 098 * @param in Inputstream to copy from 099 * @param out Outputstream to copy to 100 * @throws ArgumentNotValid if either parameter is null 101 * @throws IOFailure if a read or write error happens during copy 102 */ 103 public static void copyInputStreamToOutputStream(InputStream in, OutputStream out) { 104 ArgumentNotValid.checkNotNull(in, "InputStream in"); 105 ArgumentNotValid.checkNotNull(out, "OutputStream out"); 106 107 try { 108 try { 109 if (in instanceof FileInputStream && out instanceof FileOutputStream) { 110 FileChannel inChannel = ((FileInputStream) in).getChannel(); 111 FileChannel outChannel = ((FileOutputStream) out).getChannel(); 112 long transferred = 0; 113 final long fileLength = inChannel.size(); 114 do { 115 transferred += inChannel.transferTo(transferred, 116 Math.min(Constants.IO_CHUNK_SIZE, fileLength - transferred), outChannel); 117 } while (transferred < fileLength); 118 } else { 119 byte[] buf = new byte[Constants.IO_BUFFER_SIZE]; 120 int bytesRead; 121 while ((bytesRead = in.read(buf)) != -1) { 122 out.write(buf, 0, bytesRead); 123 } 124 } 125 out.flush(); 126 } finally { 127 in.close(); 128 } 129 } catch (IOException e) { 130 String errMsg = "Trouble copying inputstream " + in + " to outputstream " + out; 131 log.warn(errMsg, e); 132 throw new IOFailure(errMsg, e); 133 } 134 } 135 136 /** 137 * Write document tree to stream. Note, the stream is flushed, but not closed. 138 * 139 * @param doc the document tree to save. 140 * @param os the stream to write xml to 141 * @throws IOFailure On trouble writing XML to stream. 142 */ 143 public static void writeXmlToStream(Document doc, OutputStream os) { 144 ArgumentNotValid.checkNotNull(doc, "Document doc"); 145 ArgumentNotValid.checkNotNull(doc, "OutputStream os"); 146 XMLWriter xwriter = null; 147 try { 148 try { 149 OutputFormat format = OutputFormat.createPrettyPrint(); 150 format.setEncoding(UTF8_CHARSET); 151 xwriter = new XMLWriter(os, format); 152 xwriter.write(doc); 153 } finally { 154 if (xwriter != null) { 155 xwriter.close(); 156 } 157 os.flush(); 158 } 159 } catch (IOException e) { 160 String errMsg = "Unable to write XML to stream"; 161 log.warn(errMsg, e); 162 throw new IOFailure(errMsg, e); 163 } 164 } 165 166 /** 167 * Reads an input stream and returns it as a string. 168 * 169 * @param in The input stream. 170 * @return The string content of the input stream in the UTF8-charset. 171 * @throws ArgumentNotValid If the input stream is null. 172 * @throws IOFailure If an IOException is caught while reading the inputstream. 173 */ 174 public static String getInputStreamAsString(InputStream in) throws ArgumentNotValid, IOFailure { 175 ArgumentNotValid.checkNotNull(in, "InputStream in"); 176 177 StringBuilder res = new StringBuilder(); 178 byte[] buf = new byte[Constants.IO_BUFFER_SIZE]; 179 int read = 0; 180 try { 181 try { 182 while ((read = in.read(buf)) != -1) { 183 String s = new String(buf, UTF8_CHARSET); 184 if (s.length() < read) { 185 log.debug("String length ({}) < bytes read({})", s.length(), read); 186 res.append(s, 0, s.length()); 187 } else { 188 res.append(s, 0, read); 189 } 190 // Reinitializing the buffer to avoid garbage in buffer 191 buf = new byte[Constants.IO_BUFFER_SIZE]; 192 } 193 } finally { 194 in.close(); 195 } 196 } catch (IOException e) { 197 String errMsg = "Trouble reading inputstream '" + in + "'"; 198 log.warn(errMsg, e); 199 throw new IOFailure(errMsg, e); 200 } 201 202 return res.toString(); 203 } 204 205 /** 206 * Get FileReader as String. 207 * @param fr a given FileReader 208 * @return the FileReader as a String. 209 * @throws ArgumentNotValid If the FileReader is null. 210 * @throws IOFailure If an IOException is caught while reading the FileReader. 211 */ 212 public static String getFileReaderAsString(FileReader fr) throws ArgumentNotValid, IOFailure { 213 ArgumentNotValid.checkNotNull(fr, "FileReader fr"); 214 215 StringBuilder res = new StringBuilder(); 216 String line = null; 217 try { 218 BufferedReader bufferreader = new BufferedReader(fr); 219 line = bufferreader.readLine(); 220 while (line != null) { 221 res.append(line); 222 res.append("\n"); 223 line = bufferreader.readLine(); 224 } 225 } catch (IOException ex) { 226 String errMsg = "Trouble reading FileReader '" + fr + "'"; 227 log.warn(errMsg, ex); 228 throw new IOFailure(errMsg, ex); 229 } 230 return res.toString(); 231 } 232 233 /** 234 * Convert inputStream to byte array. 235 * 236 * @param data a given InputStream 237 * @param dataLength length of the InputStream (must be larger than 0) 238 * @return byte[] containing the data in the given InputStream 239 */ 240 public static byte[] inputStreamToBytes(InputStream data, int dataLength) { 241 ArgumentNotValid.checkNotNull(data, "data"); 242 ArgumentNotValid.checkNotNegative(dataLength, "dataLength"); 243 byte[] contents = new byte[dataLength]; 244 try { 245 int read = data.read(contents, 0, dataLength); 246 if (dataLength != read) { 247 log.debug("Only read {} bytes out of the {} bytes requested", read, dataLength); 248 } 249 } catch (IOException e) { 250 throw new IOFailure("Unable to convert inputstream to byte array", e); 251 } 252 return contents; 253 } 254 255}