ohair@286: /* alanb@368: * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. ohair@286: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. ohair@286: * ohair@286: * This code is free software; you can redistribute it and/or modify it ohair@286: * under the terms of the GNU General Public License version 2 only, as ohair@286: * published by the Free Software Foundation. Oracle designates this ohair@286: * particular file as subject to the "Classpath" exception as provided ohair@286: * by Oracle in the LICENSE file that accompanied this code. ohair@286: * ohair@286: * This code is distributed in the hope that it will be useful, but WITHOUT ohair@286: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ohair@286: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ohair@286: * version 2 for more details (a copy is included in the LICENSE file that ohair@286: * accompanied this code). ohair@286: * ohair@286: * You should have received a copy of the GNU General Public License version ohair@286: * 2 along with this work; if not, write to the Free Software Foundation, ohair@286: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. ohair@286: * ohair@286: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@286: * or visit www.oracle.com if you need additional information or have any ohair@286: * questions. ohair@286: */ ohair@286: ohair@286: package com.sun.xml.internal.ws.util; ohair@286: ohair@286: import com.sun.istack.internal.NotNull; ohair@286: import com.sun.istack.internal.Nullable; ohair@286: ohair@286: import java.io.File; ohair@286: import java.io.FileInputStream; ohair@286: import java.io.FileOutputStream; ohair@286: import java.io.IOException; ohair@286: import java.io.InputStream; alanb@368: import java.util.logging.Level; alanb@368: import java.util.logging.Logger; ohair@286: ohair@286: /** ohair@286: * Reads a input stream completely and creates a new stream ohair@286: * by keeping some data in memory and the rest on the file system. ohair@286: * ohair@286: * @author Jitendra Kotamraju ohair@286: */ ohair@286: public class ReadAllStream extends InputStream { ohair@286: ohair@286: private final @NotNull MemoryStream memStream; ohair@286: private final @NotNull FileStream fileStream; ohair@286: ohair@286: private boolean readAll; ohair@286: private boolean closed; ohair@286: alanb@368: private static final Logger LOGGER = Logger.getLogger(ReadAllStream.class.getName()); alanb@368: ohair@286: public ReadAllStream() { ohair@286: memStream = new MemoryStream(); ohair@286: fileStream = new FileStream(); ohair@286: } ohair@286: ohair@286: /** ohair@286: * Reads the data from input stream completely. It keeps ohair@286: * inMemory size in the memory, and the rest on the file ohair@286: * system. ohair@286: * ohair@286: * Caller's responsibility to close the InputStream. This ohair@286: * method can be called only once. ohair@286: * ohair@286: * @param in from which to be read ohair@286: * @param inMemory this much data is kept in the memory ohair@286: * @throws IOException in case of exception ohair@286: */ ohair@286: public void readAll(InputStream in, long inMemory) throws IOException { ohair@286: assert !readAll; ohair@286: readAll = true; ohair@286: ohair@286: boolean eof = memStream.readAll(in, inMemory); ohair@286: if (!eof) { ohair@286: fileStream.readAll(in); ohair@286: } ohair@286: } ohair@286: alanb@368: @Override ohair@286: public int read() throws IOException { ohair@286: int ch = memStream.read(); ohair@286: if (ch == -1) { ohair@286: ch = fileStream.read(); ohair@286: } ohair@286: return ch; ohair@286: } ohair@286: ohair@286: @Override ohair@286: public int read(byte b[], int off, int sz) throws IOException { ohair@286: int len = memStream.read(b, off, sz); ohair@286: if (len == -1) { ohair@286: len = fileStream.read(b, off, sz); ohair@286: } ohair@286: return len; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public void close() throws IOException { ohair@286: if (!closed) { ohair@286: memStream.close(); ohair@286: fileStream.close(); ohair@286: closed = true; ohair@286: } ohair@286: } ohair@286: ohair@286: // Keeps the rest of the data on the file system ohair@286: private static class FileStream extends InputStream { ohair@286: private @Nullable File tempFile; ohair@286: private @Nullable FileInputStream fin; ohair@286: ohair@286: void readAll(InputStream in) throws IOException { ohair@286: tempFile = File.createTempFile("jaxws",".bin"); ohair@286: FileOutputStream fileOut = new FileOutputStream(tempFile); ohair@286: try { ohair@286: byte[] buf = new byte[8192]; ohair@286: int len; ohair@286: while((len=in.read(buf)) != -1) { ohair@286: fileOut.write(buf, 0, len); ohair@286: } ohair@286: } finally { ohair@286: fileOut.close(); ohair@286: } ohair@286: fin = new FileInputStream(tempFile); ohair@286: } ohair@286: alanb@368: @Override ohair@286: public int read() throws IOException { ohair@286: return (fin != null) ? fin.read() : -1; ohair@286: } ohair@286: ohair@286: @Override ohair@286: public int read(byte b[], int off, int sz) throws IOException { ohair@286: return (fin != null) ? fin.read(b, off, sz) : -1; ohair@286: } ohair@286: ohair@286: @Override ohair@286: public void close() throws IOException { ohair@286: if (fin != null) { ohair@286: fin.close(); ohair@286: } ohair@286: if (tempFile != null) { alanb@368: boolean success = tempFile.delete(); alanb@368: if (!success) { alanb@368: LOGGER.log(Level.INFO, "File {0} could not be deleted", tempFile); alanb@368: } ohair@286: } ohair@286: } ohair@286: } ohair@286: ohair@286: // Keeps data in memory until certain size ohair@286: private static class MemoryStream extends InputStream { ohair@286: private Chunk head, tail; ohair@286: private int curOff; ohair@286: ohair@286: private void add(byte[] buf, int len) { ohair@286: if (tail != null) { ohair@286: tail = tail.createNext(buf, 0, len); ohair@286: } else { ohair@286: head = tail = new Chunk(buf, 0, len); ohair@286: } ohair@286: } ohair@286: ohair@286: /** ohair@286: * Reads until the size specified ohair@286: * ohair@286: * @param in stream from which to be read ohair@286: * @param inMemory reads until this size ohair@286: * @return true if eof ohair@286: * false otherwise ohair@286: * @throws IOException in case of exception ohair@286: */ ohair@286: boolean readAll(InputStream in, long inMemory) throws IOException { ohair@286: long total = 0; ohair@286: while(true) { ohair@286: byte[] buf = new byte[8192]; ohair@286: int read = fill(in, buf); ohair@286: total += read; alanb@368: if (read != 0) { ohair@286: add(buf, read); alanb@368: } alanb@368: if (read != buf.length) { alanb@368: return true; alanb@368: } // EOF alanb@368: if (total > inMemory) { alanb@368: return false; // Reached in-memory size alanb@368: } ohair@286: } ohair@286: } ohair@286: ohair@286: private int fill(InputStream in, byte[] buf) throws IOException { ohair@286: int read; ohair@286: int total = 0; ohair@286: while(total < buf.length && (read=in.read(buf, total, buf.length-total)) != -1) { ohair@286: total += read; ohair@286: } ohair@286: return total; ohair@286: } ohair@286: alanb@368: @Override ohair@286: public int read() throws IOException { ohair@286: if (!fetch()) { ohair@286: return -1; ohair@286: } ohair@286: return (head.buf[curOff++] & 0xff); ohair@286: } ohair@286: ohair@286: @Override ohair@286: public int read(byte b[], int off, int sz) throws IOException { ohair@286: if (!fetch()) { ohair@286: return -1; ohair@286: } ohair@286: sz = Math.min(sz, head.len-(curOff-head.off)); ohair@286: System.arraycopy(head.buf,curOff,b,off,sz); ohair@286: curOff += sz; ohair@286: return sz; ohair@286: } ohair@286: ohair@286: // if eof, return false else true ohair@286: private boolean fetch() { ohair@286: if (head == null) { ohair@286: return false; ohair@286: } ohair@286: if (curOff == head.off+head.len) { ohair@286: head = head.next; ohair@286: if (head == null) { ohair@286: return false; ohair@286: } ohair@286: curOff = head.off; ohair@286: } ohair@286: return true; ohair@286: } ohair@286: ohair@286: private static final class Chunk { ohair@286: Chunk next; ohair@286: final byte[] buf; ohair@286: final int off; ohair@286: final int len; ohair@286: ohair@286: public Chunk(byte[] buf, int off, int len) { ohair@286: this.buf = buf; ohair@286: this.off = off; ohair@286: this.len = len; ohair@286: } ohair@286: ohair@286: public Chunk createNext(byte[] buf, int off, int len) { ohair@286: return next = new Chunk(buf, off, len); ohair@286: } ohair@286: } ohair@286: } ohair@286: }