package org.limewire.io; import java.io.FilterOutputStream; import java.io.IOException; import java.io.OutputStream; /** * Wraps a stream to ensure that the caller can write no more than N bytes/sec. * The current implementation of <code>ThrottledOutputStream</code> delegates * to a {@link BandwidthThrottle} class to control throughput. By sharing a single * <code>BandwidthThrottle</code> among multiple <code>ThrottledOutputStream</code> * instances, you can approximate fair global bandwidth sharing. * <p> * This implementation is based on the <a href="http://freenetproject.org/"> * ThrottledOutputStream</a> class from the Freenet project. This implementation * differs with the Freenet project in that the bandwidth throttle is no longer * static and it no longer subclasses {@link FilterOutputStream}. */ /* * This implementation differs with the Freenet project in that the bandwidth * throttle is no longer static and it no longer subclasses * {@link FilterOutputStream}, since the temptation to call * <code>super.write()</code> introduced bugs. */ public class ThrottledOutputStream extends OutputStream { /** The delegate. */ private OutputStream _delegate; /** Limits throughput. */ private BandwidthThrottle _throttle; /** * Wraps the delegate stream with the given throttle. * @param delegate the underlying stream for all IO * @param throttle limits throughput. May be shared with other streams. */ public ThrottledOutputStream(OutputStream delegate, BandwidthThrottle throttle) { this._delegate=delegate; this._throttle=throttle; } /** * Write a single byte to the delegate stream, possibly blocking if * necessary to ensure that throughput doesn't exceed the limits. * * @param b the byte to write. * @exception IOException if an I/O error occurs on the OutputStream. */ @Override public void write(final int b) throws IOException { int allow=_throttle.request(1); //Note that _request never returns zero. assert (allow==1); _delegate.write(b); } /** * Write bytes[offset...offset+totalLength-1] to the delegate stream, * possibly blocking if necessary to ensure that throughput doesn't exceed * the limits. * * @param data the bytes to write. * @param offset the index in the array to start at. * @param totalLength the number of bytes to write. * @exception IOException if an I/O error occurs on the OutputStream. */ @Override public void write(byte[] data, int offset, int totalLength) throws IOException { //Note that we delegate directly to out. Do NOT call super.write(); //that calls this.write() resulting in HALF the throughput. while (totalLength > 0) { int length = _throttle.request(totalLength); assert (length+offset<=data.length); _delegate.write(data, offset, length); totalLength -= length; offset += length; } } /** * Write the given bytes to the delegate stream, possibly blocking if * necessary to ensure that throughput doesn't exceed the limits. */ @Override public void write(byte[] data) throws IOException { write(data, 0, data.length); } @Override public void flush() throws IOException { _delegate.flush(); } @Override public void close() throws IOException { _delegate.flush(); } //Tests: see core/com/.../tests/BandwidthThrottleTest }