Java tutorial
/* * Copyright 2013 Sigurd Randoll <srandoll@digiway.de>. * * 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 de.digiway.rapidbreeze.server.model.download; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import org.apache.commons.lang3.Validate; /** * A {@linkplain InputStream} implementation which can be throttled with a given * speed. A source {@linkplain InputStream} must by passed which will be used as * the underlaying source. All read operations return -1 if the inputstream has * to wait. * * @author Sigurd */ public class ThrottledInputStream extends FilterInputStream { private float throttleBytesPerMillis = 0; private float currentBytesPerMillis = 0; private long lastRead = 0; private long startReading = 0; private long bytesRead = 0; private int autoCorrection = 0; public ThrottledInputStream(InputStream is) { super(is); Validate.notNull(is); } /** * Specifies the speed in "bytes per second". The value 0 represents an * unlimited speed. * * @param bytesPerSecond */ public void setThrottle(long bytesPerSecond) { this.throttleBytesPerMillis = (float) bytesPerSecond / 1000f; this.currentBytesPerMillis = 0; this.startReading = 0; this.bytesRead = 0; this.lastRead = 0; this.autoCorrection = 0; } /** * Returns the duration in ms when the next transfer can be executed for the * given chunksize. * * @return */ public int nextTransfer(long chunksize) { Float idle = (1 / throttleBytesPerMillis) * (float) chunksize; long alreadyWaited = System.currentTimeMillis() - lastRead; long toWait = idle.longValue() - alreadyWaited + autoCorrection; return (int) (toWait < 0 ? 0 : toWait); } private boolean needToWait() { if (startReading == 0) { startReading = System.currentTimeMillis(); } long elapsed = System.currentTimeMillis() - startReading; if (elapsed == 0) { elapsed = 1; } currentBytesPerMillis = (float) bytesRead / (float) elapsed; return (throttleBytesPerMillis > 0 && currentBytesPerMillis > throttleBytesPerMillis); } @Override public int read() throws IOException { if (needToWait()) { autoCorrection++; return -1; } bytesRead++; lastRead = System.currentTimeMillis(); return super.read(); } @Override public int read(byte[] b, int off, int len) throws IOException { if (needToWait()) { autoCorrection++; return -1; } int read = super.read(b, off, len); bytesRead += read; lastRead = System.currentTimeMillis(); return read; } /** * Returns the current speed in bytes per second. * * @return */ public long getCurrentBytesPerSecond() { return Math.round(currentBytesPerMillis * 1000); } }