/* * Copyright [2013] [Cloud4SOA, www.cloud4soa.eu] * * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Copyright 2009-2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.cloudfoundry.client.lib.io; import java.io.ByteArrayOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import org.springframework.util.Assert; /** * InputStream that dynamically loads data on demand as the stream is read. Subclasses must implement the * {@link #writeMoreData()} method. * * @author Phillip Webb */ public abstract class DynamicInputStream extends InputStream { private BufferedOutputStream outputStream = new BufferedOutputStream(); private byte[] singleByte = new byte[1]; @Override public int read() throws IOException { int s = read(singleByte); if (s == 1) { return (int) singleByte[0] & 0xFF; } return -1; } @Override public int read(byte[] b, int off, int len) throws IOException { if (b == null) { throw new NullPointerException(); } if (off < 0 || len < 0 || len > (b.length - off)) { throw new IndexOutOfBoundsException(); } if (len == 0) { return 0; } return doRead(b, off, len, true); } private int doRead(byte[] b, int off, int len, boolean lastWriteWasSuccessful) throws IOException { if (outputStream.getAvailable() > 0) { return outputStream.read(b, off, len); } if (!lastWriteWasSuccessful) { return -1; } outputStream.clear(); boolean writeSuccess = writeMoreData(); return doRead(b, off, len, writeSuccess); } /** * Returns the {@link OutputStream} that should be used when {@link #writeMoreData() writing} data. The output * stream instance will not change during the life of the object and so can be used as the source to a a * {@link FilterInputStream}. This is an in-memory stream so care should be taken to not write large amounts of * data. * * @return the output stream * @see #writeMoreData() */ protected final OutputStream getOutputStream() { return outputStream; } /** * Called when more data should be written to the {@link #getOutputStream() output stream}. This method can be * called many times, implementations should write zero or more bytes to {@link #getOutputStream() output stream} on * each call. Generally it is recommended that not more that 4096 bytes are written in a single call. * * @return <tt>false</tt> when no more data is available to write. * @throws IOException */ protected abstract boolean writeMoreData() throws IOException; /** * Internal buffered {@link OutputStream} implementation. */ private static class BufferedOutputStream extends ByteArrayOutputStream { private int offset; public int getAvailable() { return count - offset; } public void clear() { this.count = 0; this.offset = 0; } public int read(byte[] b, int off, int len) { int length = Math.min(getAvailable(), len); Assert.state(length > 0, "No data available in buffer"); System.arraycopy(this.buf, this.offset, b, off, length); offset += length; return length; } } }