/** * Copyright (c) 2009 - 2010 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org> * * This file is part of org.appwork.utils.net.throttledconnection * * This software is licensed under the Artistic License 2.0, * see the LICENSE file or http://www.opensource.org/licenses/artistic-license-2.0.php * for details */ package org.appwork.utils.net.throttledconnection; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; /** * @author daniel * */ public class ThrottledConnectionManager { private final ArrayList<ThrottledConnection> managedIn = new ArrayList<ThrottledConnection>(); private final ArrayList<ThrottledConnection> managedOut = new ArrayList<ThrottledConnection>(); private final Object LOCK = new Object(); /** * how fast do we want to update and check current status */ private final static int updateSpeed = 2000; private Thread watchDog = null; private volatile int IncommingBandwidthLimit = 0; private volatile int IncommingBandwidthUsage = 0; private volatile int OutgoingBandwidthLimit = 0; private volatile int OutgoingBandwidthUsage = 0; public ThrottledConnectionManager() { } /** * adds ThrottledInputStream to this manager * * @param tin */ public void addManagedThrottledInputStream(final ThrottledInputStream tin) { synchronized (this.LOCK) { if (this.managedIn.contains(tin)) { return; } this.managedIn.add(tin); tin.setManager(this); tin.setManagedLimit(this.IncommingBandwidthLimit / this.managedIn.size()); this.startWatchDog(); } } /** * adds ThrottledOutputStream to this manager * * @param tout */ public void addManagedThrottledOutputStream(final ThrottledOutputStream tout) { synchronized (this.LOCK) { if (this.managedOut.contains(tout)) { return; } this.managedOut.add(tout); tout.setManager(this); tout.setManagedLimit(this.OutgoingBandwidthLimit / this.managedOut.size()); this.startWatchDog(); } } /** * returns incomming bandwidth limit * * @return */ public int getIncommingBandwidthLimit() { return this.IncommingBandwidthLimit; } /** * returns current incomming bandwidth * * @return */ public int getIncommingBandwidthUsage() { return this.IncommingBandwidthUsage; } /** * returns a managed ThrottledInputStream for given InputStream * * @param in * @return */ public ThrottledInputStream getManagedThrottledInputStream(final InputStream in) { final ThrottledInputStream ret = new ThrottledInputStream(in, this); this.addManagedThrottledInputStream(ret); return ret; } /** * returns a managed ThrottledOutputStream for given OutputStream * * @param out * @return */ public ThrottledOutputStream getManagedThrottledOutputStream(final OutputStream out) { final ThrottledOutputStream ret = new ThrottledOutputStream(out, this); this.addManagedThrottledOutputStream(ret); return ret; } /** * returns outgoing bandwidth limit * * @return */ public int getOutgoingBandwidthLimit() { return this.OutgoingBandwidthLimit; } /** * returns current outgoing bandwidth * * @return */ public int getOutgoingBandwidthUsage() { return this.OutgoingBandwidthUsage; } private int manageConnections(final ArrayList<ThrottledConnection> managed, final int limit) { synchronized (this.LOCK) { int managedConnections = 0; int currentRealSpeed = 0; long ret; for (final ThrottledConnection in : managed) { ret = in.transferedSinceLastCall(); currentRealSpeed += ret; if (in.getCustomLimit() == 0) { /* this connection is managed */ /* * dont count connections with no bandwidth usage, eg lost * ones */ if (ret != 0) { managedConnections++; } } } /* * calculate new input limit based on current input bandwidth usage */ currentRealSpeed = (int) ((long) currentRealSpeed * 1000 / Math.max(1000, ThrottledConnectionManager.updateSpeed)); int newLimit = 0; if (managedConnections == 0) { newLimit = limit; } else { newLimit = limit / managedConnections; } for (final ThrottledConnection in : managed) { if (newLimit == 0) { /* we do not have a limit set */ in.setManagedLimit(0); } else { /* set new limit */ in.setManagedLimit(newLimit); } } return currentRealSpeed; } } /** * removes ThrottledInputStream from this manager * * @param tin * @return */ public boolean removeManagedThrottledInputStream(final ThrottledInputStream tin) { synchronized (this.LOCK) { final boolean ret = this.managedIn.remove(tin); if (ret) { tin.setManager(null); tin.setManagedLimit(0); } return ret; } } /** * removes ThrottledOutputStream from this manager * * @param tin * @return */ public boolean removeManagedThrottledOutputStream(final ThrottledOutputStream tout) { synchronized (this.LOCK) { final boolean ret = this.managedOut.remove(tout); if (ret) { tout.setManager(null); tout.setManagedLimit(0); } return ret; } } /** * set incomming bandwidth limit * * @param kpsLimit */ public void setIncommingBandwidthLimit(final int kpsLimit) { this.IncommingBandwidthLimit = Math.max(0, kpsLimit); } /** * set outgoing bandwidth limit * * @param kpsLimit */ public void setOutgoingBandwidthLimit(final int kpsLimit) { this.OutgoingBandwidthLimit = Math.max(0, kpsLimit); } private synchronized void startWatchDog() { if (this.watchDog != null) { return; } this.watchDog = new Thread() { @Override public void run() { this.setName("ThrottlecConnectionManager"); while (true) { try { Thread.sleep(Math.max(1000, ThrottledConnectionManager.updateSpeed)); } catch (final InterruptedException e) { org.appwork.utils.logging.Log.exception(e); } ThrottledConnectionManager.this.IncommingBandwidthUsage = ThrottledConnectionManager.this.manageConnections(ThrottledConnectionManager.this.managedIn, ThrottledConnectionManager.this.IncommingBandwidthLimit); ThrottledConnectionManager.this.OutgoingBandwidthUsage = ThrottledConnectionManager.this.manageConnections(ThrottledConnectionManager.this.managedOut, ThrottledConnectionManager.this.OutgoingBandwidthLimit); } } }; this.watchDog.start(); } }