/*
* Copyright 2014-2015 Cel Skeggs
*
* This file is part of the CCRE, the Common Chicken Runtime Engine.
*
* The CCRE is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the CCRE. If not, see <http://www.gnu.org/licenses/>.
*/
package ccre.net;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicLong;
import ccre.timers.Ticker;
/**
* You can enable Traffic Counting and query traffic analytics via this class.
*
* @author skeggsc
*/
public class TrafficCounting {
/**
* Calculates the number of bytes sent or received in a recent one-second
* interval.
*
* @return the rate of data transfer, in bytes per second.
*/
public static long getRateBytesPerSecond() {
return TrafficCounting.lastDelta;
}
/**
* Calculates the total number of bytes sent or received.
*
* @return the total number of bytes.
*/
public static long getTotalBytes() {
return TrafficCounting.totalBytesReceived.get() + TrafficCounting.totalBytesSent.get();
}
/**
* Enables or disables traffic counting. Counting has to be on when a socket
* is created for the analytics to work.
*
* @param enabled true if traffic counting should be on, false if it should
* be off.
*/
public static synchronized void setCountingEnabled(boolean enabled) {
TrafficCounting.countingEnabled = enabled;
if (enabled && !TrafficCounting.countingEverEnabled) {
TrafficCounting.countingEverEnabled = true;
// TODO: get rid of this ticker
new Ticker(1000).send(() -> {
long newTotal = getTotalBytes();
TrafficCounting.lastDelta = newTotal - TrafficCounting.lastTotal;
TrafficCounting.lastTotal = newTotal;
});
}
}
private static boolean countingEnabled = false;
private static boolean countingEverEnabled = false;
/**
* The total number of bytes sent through the CountingNetworkProvider.
*/
private static final AtomicLong totalBytesSent = new AtomicLong();
/**
* The total number of bytes received through the CountingNetworkProvider.
*/
private static final AtomicLong totalBytesReceived = new AtomicLong();
private static long lastDelta;
private static long lastTotal;
static InputStream wrap(InputStream inputStream) {
return countingEnabled ? new CountingInputStream(inputStream) : inputStream;
}
static OutputStream wrap(OutputStream outputStream) {
return countingEnabled ? new CountingOutputStream(outputStream) : outputStream;
}
private static class CountingInputStream extends InputStream {
private final InputStream base;
CountingInputStream(InputStream base) {
this.base = base;
}
@Override
public int read() throws IOException {
int out = base.read();
if (out != -1) {
TrafficCounting.totalBytesReceived.getAndIncrement();
}
return out;
}
@Override
public int read(byte[] b) throws IOException {
int count = base.read(b);
if (count > 0) {
TrafficCounting.totalBytesReceived.getAndAdd(count);
}
return count;
}
@Override
public int read(byte b[], int off, int len) throws IOException {
int count = base.read(b, off, len);
if (count > 0) {
TrafficCounting.totalBytesReceived.getAndAdd(count);
}
return count;
}
@Override
public long skip(long n) throws IOException {
long count = base.skip(n);
if (count > 0) {
TrafficCounting.totalBytesReceived.getAndAdd(count);
}
return count;
}
@Override
public int available() throws IOException {
return base.available();
}
@Override
public void close() throws IOException {
base.close();
}
@Override
public synchronized void mark(int readlimit) {
base.mark(readlimit);
}
@Override
public synchronized void reset() throws IOException {
base.reset();
}
@Override
public boolean markSupported() {
return base.markSupported();
}
}
private static class CountingOutputStream extends OutputStream {
private final OutputStream base;
CountingOutputStream(OutputStream base) {
this.base = base;
}
@Override
public void write(int b) throws IOException {
base.write(b);
TrafficCounting.totalBytesSent.getAndIncrement();
}
@Override
public void write(byte[] b) throws IOException {
base.write(b);
TrafficCounting.totalBytesSent.getAndAdd(b.length);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
base.write(b, off, len);
TrafficCounting.totalBytesSent.getAndAdd(b.length);
}
@Override
public void flush() throws IOException {
base.flush();
}
@Override
public void close() throws IOException {
base.close();
}
}
}