I have two services first frontend_service and second backend_service and I'm getting the large file from backend_service and trying to forward to user via frontend_service using
response.getBodyAsStream() but this is causing "java.lang.OutOfMemoryError: GC overhead limit exceeded" in frontend_service.
code for backend_service:
`
public static Result downloadLargeFile(String filePath){
File file = new File(filePath);
InputStream inputStream = new FileInputStream(file);
return ok(inputStream);
}
`
code for frontend_service:
`
public static F.Promise<Result> downloadLargeFile(String filePath) {
//this will call backend_service downloadLargeFile method.
String backEndUrl = getBackEndUrl(filePath);
return getInputStream(backEndUrl);
}
`
`
public static Promise<Result> getInputStream(String url) {
return WS.url(url).get().map(
response -> {
InputStream inputStream = response.getBodyAsStream();
return ok(inputStream);
}
);
}
`
I tried the solution suggested here by reading few bytes at a time from inputStream and creating tmp file in frontend_service and sending the tmp file as output from frontend_service.
`
public static Promise<Result> getInputStream(String url) {
return WS.url(url).get().map(
response -> {
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = response.getBodyAsStream();
//write input stream to tmp file
final File tmpFile = new File("/tmp/tmp.txt");
outputStream = new FileOutputStream(tmpFile);
int read = 0;
byte[] buffer = new byte[500];
while((read = inputStream.read(buffer)) != -1){
outputStream.write(buffer, 0 , read);
}
return ok(tmpFile);
} catch (IOException e) {
e.printStackTrace();
return badRequest();
} finally {
if (inputStream != null) {inputStream.close();}
if (outputStream != null) {outputStream.close();}
}
}
);
`
Above code also throwing java.lang.OutOfMemoryError. I'm trying 1 GB file.
I do not have the implementation "under the hand", so I will write the algorithm.
1. Play uses the AsyncHttpClient under the WS. You need to get it, or create it as described in the https://www.playframework.com/documentation/2.3.x/JavaWS#Using-WSClient
2. Then, you need to implement the AsyncCompletionHandler, like in the description of the class https://static.javadoc.io/org.asynchttpclient/async-http-client/2.0.0/org/asynchttpclient/AsyncHttpClient.html
3. In the onBodyPartReceived method of the AsyncCompletionHandler class, you need to push the body part to the chunked play response. Chanked responses described here: https://www.playframework.com/documentation/2.3.x/JavaStream#Chunked-responses
P.S.
The discussion of the similar solution but in oposite direction - streaming uploading to the "backend" (Amazon) service through the "frontend" (play 2) service:
https://groups.google.com/d/msg/asynchttpclient/EpNKLSG9ymM/BAGvwl0Wby8J
Related
is it possible to write/create an exe file in Java?
I can successfully read it but writing the exact same data that has been read to a new file seems to create some trouble because Windows tell's me it's not supported for my pc anymore.
This is the code I'm using to read the file where path is a String given with the actual path (it's in the .jar itself that's why I'm using ResourceAsStream()):
try {
InputStream inputStream = FileIO.class.getResourceAsStream(path);
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
ArrayList<String> _final = new ArrayList<String>();
String line;
while ((line = reader.readLine()) != null) {
_final.add(line);
}
inputStream.close();
return _final.toArray(new String[_final.size()]);
}catch(Exception e) {
return null;
}
This is the code I'm using to write the file:
public static void writeFileArray(String path, String[] data) {
String filename = path;
try{
FileWriter fileWriter = new FileWriter(filename);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
for(String d : data) {
bufferedWriter.write(d + "\n");
}
bufferedWriter.close();
}
catch(IOException ex){
System.out.println("FileIO failed to write file, IO exception");
}
}
So it doesn't give me any error's or something and the file size of the original .exe and the 'transferred' .exe stays the same, but it doesn't work anymore. Am I just doing it wrong? Did I forget something? Can u even do this with Java?
Btw I'm not that experienced with reading/writing files..
Thanks for considering my request.
I'm going to guess that you're using a Reader when you should be using a raw input stream. Use BufferedInputStream instead of BufferedReader.
BufferedInputStream in = new BufferedInputStream( inputStream );
The problem is that Reader interprets the binary as your local character set instead of the data you want.
Edit: if you need a bigger hint start with this. I just noticed you're using a BufferedWriter too, that won't work either.
try {
InputStream inputStream = FileIO.class.getResourceAsStream(path);
BufferedInputStream in = new BufferedInputStream( inputStream );
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] bytes = new byte[ 1024 ];
for( int length; ( length = ins.read( bytes ) ) != -1; )
bos.write( bytes, 0, length );
}
inputStream.close();
return bos;
When you are using Java 7 or newer, you should copy a resource to a file using
public static void copyResourceToFile(String resourcePath, String filePath) {
try(InputStream inputStream = FileIO.class.getResourceAsStream(resourcePath)) {
Files.copy(inputStream, Paths.get(filePath));
}
catch(IOException ex){
System.out.println("Copying failed. "+ex.getMessage());
}
}
This construct ensures correct closing of the resources even in the exceptional case and the JRE method ensures correct and efficient copying of the data.
It accepts additional options, e.g. to specify that the target file should be overwritten in case it already exists, you would use
public static void copyResourceToFile(String resourcePath, String filePath) {
try(InputStream inputStream = FileIO.class.getResourceAsStream(resourcePath)) {
Files.copy(inputStream, Paths.get(filePath), StandardCopyOption.REPLACE_EXISTING);
}
catch(IOException ex){
System.out.println("Copying failed. "+ex.getMessage());
}
}
You are using InputStreams for strings, .exe files are bytes!
Try using a ByteArrayInputStream and ByteArrayOutputStream.
Edit: completing with markspace's answer:
new BufferedInputStream(new ByteArrayInputStream( ... ) )
I'm writing a program to download a PDF file from server. I'm using some program given here Download file by passing URL using java code, this solution works fine for the sample URL provided in the first answer, but not for PDF, I'm replacing just the URL. Below is my code.
import java.io.*;
import java.net.*;
public class FileDownloadTest {
final static int size = 1024;
public static void fileUrl(String fAddress, String localFileName, String destinationDir) {
// localFileName = "Hello World";
OutputStream outStream = null;
URLConnection uCon = null;
InputStream is = null;
try {
URL url;
byte[] buf;
int byteRead, byteWritten = 0;
url = new URL(fAddress);
outStream = new BufferedOutputStream(new FileOutputStream(destinationDir + "\\" + localFileName));
uCon = url.openConnection();
is = uCon.getInputStream();
buf = new byte[size];
while ((byteRead = is.read(buf)) != -1) {
outStream.write(buf, 0, byteRead);
byteWritten += byteRead;
}
System.out.println("Downloaded Successfully.");
System.out.println("File name:\"" + localFileName + "\"\nNo ofbytes :" + byteWritten);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
is.close();
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void fileDownload(String fAddress, String destinationDir) {
int slashIndex = fAddress.lastIndexOf('/');
int periodIndex = fAddress.lastIndexOf('.');
String fileName = fAddress.substring(slashIndex + 1);
if (periodIndex >= 1 && slashIndex >= 0 && slashIndex < fAddress.length() - 1) {
fileUrl(fAddress, fileName, destinationDir);
} else {
System.err.println("path or file name.");
}
}
public static void main(String[] args) {
String fAddress = "http://singztechmusings.files.wordpress.com/2011/09/maven_eclipse_and_osgi_working_together.pdf";
String destinationDir = "D:\\FileDownload";
fileDownload(fAddress, destinationDir);
}
}
Here, This pdf has 73 pages, and in my folder, it is download as a PDF of 1KB, when opened in Acrobat Reader, it says that the file might be corrupted.
I've also tried the code provided here https://dzone.com/articles/java-how-save-download-file, but the result is same.
please let me know how can I fix this.
Thanks
If you check the downloaded file content, you can see it is html. The server is redirecting the original request to https url. Use url https://singztechmusings.files.wordpress.com/2011/09/maven_eclipse_and_osgi_working_together.pdf instead.
Or use http client with automatic redirect handling, ala http-commons
You define a Variable size = 1024 and use this to define your Buffer.
So logically you can only write 1 KB into it.
But if the input Stream reads more at once it will be lost ... So change your Buffer size to a value which would be able to contain most documents or try to determine the necessary size
I need some help on below problem. I am working on a project where I need to deal with files.
I get the handle of input stream from the user from which before writing it to disk I need to perform certain steps.
calculate the file digest
check for only 1 zip file present, unzip the data if zipped
dos 2 unix conversion
record length validation
and encrypt and save the file to disk
Also need to break the flow if there is any exception in the process
I tried to use piped output and input stream, but the constraint is Java recommends it to run in 2 separate threads. Once I read from input stream I am not able to use it from other processing steps. Files can be very big so cannot cache all the data in buffer.
Please provide your suggestions or is there any third party lib I can use for same.
The biggest issue is that you'll need to peek ahead in the provided InputStream to decide if you received a zipfile or not.
private boolean isZipped(InputStream is) throws IOException {
try {
return new ZipInputStream(is).getNextEntry() != null;
} catch (final ZipException ze) {
return false;
}
}
After this you need to reset the inputstream to the initial position before setting up a DigestInputStream.
Then read a ZipInputstream or the DigestInputstream directly.
After you've done your processing, read the DigestInputStream to the end so you can obtain the digest.
Below code has been validated through a wrapping "CountingInputstream" that keeps track of the total number of bytes read from the provided FileInputStream.
final FileInputStream fis = new FileInputStream(filename);
final CountingInputStream countIs = new CountingInputStream(fis);
final boolean isZipped = isZipped(countIs);
// make sure we reset the inputstream before calculating the digest
fis.getChannel().position(0);
final DigestInputStream dis = new DigestInputStream(countIs, MessageDigest.getInstance("SHA-256"));
// decide which inputStream to use
InputStream is = null;
ZipInputStream zis = null;
if (isZipped) {
zis = new ZipInputStream(dis);
zis.getNextEntry();
is = zis;
} else {
is = dis;
}
final File tmpFile = File.createTempFile("Encrypted_", ".tmp");
final OutputStream os = new CipherOutputStream(new FileOutputStream(tmpFile), obtainCipher());
try {
readValidateAndWriteRecords(is, os);
failIf2ndZipEntryExists(zis);
} catch (final Exception e) {
os.close();
tmpFile.delete();
throw e;
}
System.out.println("Digest: " + obtainDigest(dis));
dis.close();
System.out.println("\nValidating bytes read and calculated digest");
final DigestInputStream dis2 = new DigestInputStream(new CountingInputStream(new FileInputStream(filename)), MessageDigest.getInstance("SHA-256"));
System.out.println("Digest: " + obtainDigest(dis2));
dis2.close();
Not really relevant, but these are the helper methods:
private String obtainDigest(DigestInputStream dis) throws IOException {
final byte[] buff = new byte[1024];
while (dis.read(buff) > 0) {
dis.read(buff);
}
return DatatypeConverter.printBase64Binary(dis.getMessageDigest().digest());
}
private void readValidateAndWriteRecords(InputStream is, final OutputStream os) throws IOException {
final BufferedReader br = new BufferedReader(new InputStreamReader(is));
// do2unix is done automatically by readline
for (String line = br.readLine(); line != null; line = br.readLine()) {
// record length validation
if (line.length() < 1) {
throw new RuntimeException("RecordLengthValidationFailed");
}
os.write((line + "\n").getBytes());
}
}
private void failIf2ndZipEntryExists(ZipInputStream zis) throws IOException {
if (zis != null && zis.getNextEntry() != null) {
throw new RuntimeException("Zip File contains multiple entries");
}
}
==> output:
Digest: jIisvDleAttKiPkyU/hDvbzzottAMn6n7inh4RKxPOc=
CountingInputStream closed. Total number of bytes read: 1100
Validating bytes read and calculated digest
Digest: jIisvDleAttKiPkyU/hDvbzzottAMn6n7inh4RKxPOc=
CountingInputStream closed. Total number of bytes read: 1072
Fun question, I may have gone overboard with my answer :)
I would like to know what I'm supposed to do in the case of a gzip-encoded response. this is the method handling my responses :
private InputStream getResultStream(Response response) throws IOException
{
InputStream resultStream = null;
if(response != null)
{
String encoding = response.getHeader("Content-Encoding");
if((encoding != null) && (encoding.equalsIgnoreCase("gzip")))
{
// What to do here ?
Log.d("Stream :", "Read GZIP");
} else if ((encoding != null) && encoding.equalsIgnoreCase("deflate")) {
resultStream = new InflaterInputStream(response.getStream(), new Inflater(true));
Log.d("Stream :", "Read Deflated.");
} else {
resultStream = response.getStream();
Log.d("Stream :","Read Normal.");
}
}
return resultStream;
}
How do I approach this ?
Wrap your stream in a GZIPInputStream and read from that.
resultStream = new GZIPInputStream(resultStream);
//proceed reading as usual
Disclaimer: I have not had a chance to test this.
According to what Android’s HTTP Clients blog post says if you are on Android 2.3+, then HttpURLConnection can do it for you automatically.
If you just want to know how to read a gziped stream, just wrap your inputstream with a GZIPInputStream
InputStream is = ...
GZIPInputStream zis = new GZIPInputStream(new BufferedInputStream(is));
try {
// Reading from 'zis' gets you the uncompressed bytes...
processStream(zis);
} finally {
zis.close();
}
Do the following :
resultStream = new java.util.zip.GZIPInputStream(response.getStream());
I have a InputStream that I pass to a method to do some processing. I will use the same InputStream in other method, but after the first processing, the InputStream appears be closed inside the method.
How I can clone the InputStream to send to the method that closes him? There is another solution?
EDIT: the methods that closes the InputStream is an external method from a lib. I dont have control about closing or not.
private String getContent(HttpURLConnection con) {
InputStream content = null;
String charset = "";
try {
content = con.getInputStream();
CloseShieldInputStream csContent = new CloseShieldInputStream(content);
charset = getCharset(csContent);
return IOUtils.toString(content,charset);
} catch (Exception e) {
System.out.println("Error downloading page: " + e);
return null;
}
}
private String getCharset(InputStream content) {
try {
Source parser = new Source(content);
return parser.getEncoding();
} catch (Exception e) {
System.out.println("Error determining charset: " + e);
return "UTF-8";
}
}
If all you want to do is read the same information more than once, and the input data is small enough to fit into memory, you can copy the data from your InputStream to a ByteArrayOutputStream.
Then you can obtain the associated array of bytes and open as many "cloned" ByteArrayInputStreams as you like.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Code simulating the copy
// You could alternatively use NIO
// And please, unlike me, do something about the Exceptions :D
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1 ) {
baos.write(buffer, 0, len);
}
baos.flush();
// Open new InputStreams using recorded bytes
// Can be repeated as many times as you wish
InputStream is1 = new ByteArrayInputStream(baos.toByteArray());
InputStream is2 = new ByteArrayInputStream(baos.toByteArray());
But if you really need to keep the original stream open to receive new data, then you will need to track the external call to close(). You will need to prevent close() from being called somehow.
UPDATE (2019):
Since Java 9 the the middle bits can be replaced with InputStream.transferTo:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
input.transferTo(baos);
InputStream firstClone = new ByteArrayInputStream(baos.toByteArray());
InputStream secondClone = new ByteArrayInputStream(baos.toByteArray());
You want to use Apache's CloseShieldInputStream:
This is a wrapper that will prevent the stream from being closed. You'd do something like this.
InputStream is = null;
is = getStream(); //obtain the stream
CloseShieldInputStream csis = new CloseShieldInputStream(is);
// call the bad function that does things it shouldn't
badFunction(csis);
// happiness follows: do something with the original input stream
is.read();
You can't clone it, and how you are going to solve your problem depends on what the source of the data is.
One solution is to read all data from the InputStream into a byte array, and then create a ByteArrayInputStream around that byte array, and pass that input stream into your method.
Edit 1:
That is, if the other method also needs to read the same data. I.e you want to "reset" the stream.
If the data read from the stream is large, I would recommend using a TeeInputStream from Apache Commons IO. That way you can essentially replicate the input and pass a t'd pipe as your clone.
This might not work in all situations, but here is what I did: I extended the FilterInputStream class and do the required processing of the bytes as the external lib reads the data.
public class StreamBytesWithExtraProcessingInputStream extends FilterInputStream {
protected StreamBytesWithExtraProcessingInputStream(InputStream in) {
super(in);
}
#Override
public int read() throws IOException {
int readByte = super.read();
processByte(readByte);
return readByte;
}
#Override
public int read(byte[] buffer, int offset, int count) throws IOException {
int readBytes = super.read(buffer, offset, count);
processBytes(buffer, offset, readBytes);
return readBytes;
}
private void processBytes(byte[] buffer, int offset, int readBytes) {
for (int i = 0; i < readBytes; i++) {
processByte(buffer[i + offset]);
}
}
private void processByte(int readByte) {
// TODO do processing here
}
}
Then you simply pass an instance of StreamBytesWithExtraProcessingInputStream where you would have passed in the input stream. With the original input stream as constructor parameter.
It should be noted that this works byte for byte, so don't use this if high performance is a requirement.
UPD.
Check the comment before. It isn't exactly what was asked.
If you are using apache.commons you may copy streams using IOUtils .
You can use following code:
InputStream = IOUtils.toBufferedInputStream(toCopy);
Here is the full example suitable for your situation:
public void cloneStream() throws IOException{
InputStream toCopy=IOUtils.toInputStream("aaa");
InputStream dest= null;
dest=IOUtils.toBufferedInputStream(toCopy);
toCopy.close();
String result = new String(IOUtils.toByteArray(dest));
System.out.println(result);
}
This code requires some dependencies:
MAVEN
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
GRADLE
'commons-io:commons-io:2.4'
Here is the DOC reference for this method:
Fetches entire contents of an InputStream and represent same data as
result InputStream. This method is useful where,
Source InputStream is slow. It has network resources associated, so we
cannot keep it open for long time. It has network timeout associated.
You can find more about IOUtils here:
http://commons.apache.org/proper/commons-io/javadocs/api-2.4/org/apache/commons/io/IOUtils.html#toBufferedInputStream(java.io.InputStream)
Below is the solution with Kotlin.
You can copy your InputStream into ByteArray
val inputStream = ...
val byteOutputStream = ByteArrayOutputStream()
inputStream.use { input ->
byteOutputStream.use { output ->
input.copyTo(output)
}
}
val byteInputStream = ByteArrayInputStream(byteOutputStream.toByteArray())
If you need to read the byteInputStream multiple times, call byteInputStream.reset() before reading again.
https://code.luasoftware.com/tutorials/kotlin/how-to-clone-inputstream/
Cloning an input stream might not be a good idea, because this requires deep knowledge about the details of the input stream being cloned. A workaround for this is to create a new input stream that reads from the same source again.
So using some Java 8 features this would look like this:
public class Foo {
private Supplier<InputStream> inputStreamSupplier;
public void bar() {
procesDataThisWay(inputStreamSupplier.get());
procesDataTheOtherWay(inputStreamSupplier.get());
}
private void procesDataThisWay(InputStream) {
// ...
}
private void procesDataTheOtherWay(InputStream) {
// ...
}
}
This method has the positive effect that it will reuse code that is already in place - the creation of the input stream encapsulated in inputStreamSupplier. And there is no need to maintain a second code path for the cloning of the stream.
On the other hand, if reading from the stream is expensive (because a it's done over a low bandwith connection), then this method will double the costs. This could be circumvented by using a specific supplier that will store the stream content locally first and provide an InputStream for that now local resource.
The class below should do the trick. Just create an instance, call the "multiply" method, and provide the source input stream and the amount of duplicates you need.
Important: you must consume all cloned streams simultaneously in separate threads.
package foo.bar;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class InputStreamMultiplier {
protected static final int BUFFER_SIZE = 1024;
private ExecutorService executorService = Executors.newCachedThreadPool();
public InputStream[] multiply(final InputStream source, int count) throws IOException {
PipedInputStream[] ins = new PipedInputStream[count];
final PipedOutputStream[] outs = new PipedOutputStream[count];
for (int i = 0; i < count; i++)
{
ins[i] = new PipedInputStream();
outs[i] = new PipedOutputStream(ins[i]);
}
executorService.execute(new Runnable() {
public void run() {
try {
copy(source, outs);
} catch (IOException e) {
e.printStackTrace();
}
}
});
return ins;
}
protected void copy(final InputStream source, final PipedOutputStream[] outs) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
int n = 0;
try {
while (-1 != (n = source.read(buffer))) {
//write each chunk to all output streams
for (PipedOutputStream out : outs) {
out.write(buffer, 0, n);
}
}
} finally {
//close all output streams
for (PipedOutputStream out : outs) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
Enhancing the #Anthony Accioly with the example.
InputStream: Clones the bytes-Stream and provides number of copies as a List Collection.
public static List<InputStream> multiplyBytes(InputStream input, int cloneCount) throws IOException {
List<InputStream> copies = new ArrayList<InputStream>();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
copy(input, baos);
for (int i = 0; i < cloneCount; i++) {
copies.add(new ByteArrayInputStream(baos.toByteArray()));
}
return copies;
}
// IOException - If reading the Reader or Writing into the Writer goes wrong.
public static void copy(Reader in, Writer out) throws IOException {
try {
char[] buffer = new char[1024];
int nrOfBytes = -1;
while ((nrOfBytes = in.read(buffer)) != -1) {
out.write(buffer, 0, nrOfBytes);
}
out.flush();
} finally {
close(in);
close(out);
}
}
Reader: Clones the chars-Stream and provides number of copies as a List Collection.
public static List<Reader> multiplyChars(Reader reader, int cloneCOunt) throws IOException {
List<Reader> copies = new ArrayList<Reader>();
BufferedReader bufferedInput = new BufferedReader(reader);
StringBuffer buffer = new StringBuffer();
String delimiter = System.getProperty("line.separator");
String line;
while ((line = bufferedInput.readLine()) != null) {
if (!buffer.toString().equals(""))
buffer.append(delimiter);
buffer.append(line);
}
close(bufferedInput);
for (int i = 0; i < cloneCOunt; i++) {
copies.add(new StringReader(buffer.toString()));
}
return copies;
}
public static void copy(InputStream in, OutputStream out) throws IOException {
try {
byte[] buffer = new byte[1024];
int nrOfBytes = -1;
while ((nrOfBytes = in.read(buffer)) != -1) {
out.write(buffer, 0, nrOfBytes);
}
out.flush();
} finally {
close(in);
close(out);
}
}
Full Example:
public class SampleTest {
public static void main(String[] args) throws IOException {
String filePath = "C:/Yash/StackoverflowSSL.cer";
InputStream fileStream = new FileInputStream(new File(filePath) );
List<InputStream> bytesCopy = multiplyBytes(fileStream, 3);
for (Iterator<InputStream> iterator = bytesCopy.iterator(); iterator.hasNext();) {
InputStream inputStream = (InputStream) iterator.next();
System.out.println("Byte Stream:"+ inputStream.available()); // Byte Stream:1784
}
printInputStream(bytesCopy.get(0));
//java.sql.Clob clob = ((Clob) getValue(sql)); - clob.getCharacterStream();
Reader stringReader = new StringReader("StringReader that reads Characters from the specified string.");
List<Reader> charsCopy = multiplyChars(stringReader, 3);
for (Iterator<Reader> iterator = charsCopy.iterator(); iterator.hasNext();) {
Reader reader = (Reader) iterator.next();
System.out.println("Chars Stream:"+reader.read()); // Chars Stream:83
}
printReader(charsCopy.get(0));
}
// Reader, InputStream - Prints the contents of the reader to System.out.
public static void printReader(Reader reader) throws IOException {
BufferedReader br = new BufferedReader(reader);
String s;
while ((s = br.readLine()) != null) {
System.out.println(s);
}
}
public static void printInputStream(InputStream inputStream) throws IOException {
printReader(new InputStreamReader(inputStream));
}
// Closes an opened resource, catching any exceptions.
public static void close(Closeable resource) {
if (resource != null) {
try {
resource.close();
} catch (IOException e) {
System.err.println(e);
}
}
}
}