/** * Copyright (c) 2009 - 2010 AppWork UG(haftungsbeschränkt) <e-mail@appwork.org> * * This file is part of org.appwork.utils.net * * 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; import java.util.ArrayList; import java.util.Iterator; import org.appwork.utils.event.BasicEvent; import org.appwork.utils.event.BasicEventSender; import org.appwork.utils.logging.Log; /** * @author daniel * */ public class ConnectionLimiter { private static final Object LOCK = new Object(); public static final int CONNECTION_OPENED = 0; public static final int CONNECTION_CLOSED = 1; /** * fired when more than {@link #getMaxConcurrent()} connections are open. * waits until {@link #WAIT_DUETO_MAXSIMULTAN_LIMIT_END} is called. No * parameter:ms waited until noe */ public static final int WAIT_DUETO_MAXSIMULTAN_LIMIT_END = 2; /** * @see #WAIT_DUETO_MAXSIMULTAN_LIMIT_START parameter: ms waited total */ public static final int WAIT_DUETO_MAXSIMULTAN_LIMIT_START = 3; /** * IOs fired when the {@link #getConnectionTimeLimit()} limit is reached. * * parameter: ms to wait * */ public static final int WAIT_DUETO_CONNECTIONSPERMINUTE_LIMIT_START = 4; public static final int WAIT_DUETO_CONNECTIONSPERMINUTE_LIMIT_END = 5; private int connectioncount; private final ArrayList<Long> list = new ArrayList<Long>(); private int maxConcurrent = -1; private int timeConnections = -1; private long timeTime = -1; private final BasicEventSender<Long> eventSender; public ConnectionLimiter() { this.eventSender = new BasicEventSender<Long>(); } /** * DON'T forget to call this after connection is closed */ public void closedConnection() { synchronized (ConnectionLimiter.LOCK) { this.connectioncount--; } this.eventSender.fireEvent(new BasicEvent<Long>(this, ConnectionLimiter.CONNECTION_CLOSED, null, null)); } /** * get current allowed connection in specific time or -1,-1 if none set * * @return */ public long[] getConnectionTimeLimit() { return new long[] { this.timeConnections, this.timeTime }; } /** * @return the eventSender */ public BasicEventSender<Long> getEventSender() { return this.eventSender; } /** * returns current maximum of allowed concurrent connections or -1 for * unlimited * * @return */ public int getMaxConcurrent() { return this.maxConcurrent; } /** * call this if before you open a new connection * * @throws InterruptedException */ public synchronized void openedConnection() throws InterruptedException { if (this.maxConcurrent > 0) { boolean waiting = false; long ms = -1; while (true) { synchronized (ConnectionLimiter.LOCK) { if (this.connectioncount < this.maxConcurrent) { if (waiting) { this.eventSender.fireEvent(new BasicEvent<Long>(this, ConnectionLimiter.WAIT_DUETO_MAXSIMULTAN_LIMIT_END, System.currentTimeMillis() - ms, null)); } break; } } if (ms < 0) { ms = System.currentTimeMillis(); } Log.L.warning("block 250 ms for " + this.maxConcurrent + " connectionlimit"); this.eventSender.fireEvent(new BasicEvent<Long>(this, ConnectionLimiter.WAIT_DUETO_MAXSIMULTAN_LIMIT_START, System.currentTimeMillis() - ms, null)); waiting = true; Thread.sleep(250); } } boolean waiting = false; if (this.timeConnections > 0) { while (true) { final Iterator<Long> it = this.list.iterator(); while (true) { if (it.hasNext()) { if (it.next() + this.timeTime < System.currentTimeMillis()) { it.remove(); } else { break; } } else { break; } } if (this.list.size() >= this.timeConnections) { long wait = this.timeConnections; if (it.hasNext()) { /* calculate how long we have to wait */ wait = Math.max(250, it.next() - (System.currentTimeMillis() - this.timeTime)); } waiting = true; Log.L.warning("wait " + wait + " ms because we got " + this.list.size() + " connections the last minute"); this.eventSender.fireEvent(new BasicEvent<Long>(this, ConnectionLimiter.WAIT_DUETO_CONNECTIONSPERMINUTE_LIMIT_START, wait, null)); Thread.sleep(wait); } else { break; } } this.list.add(System.currentTimeMillis()); // System.out.println(list.size()); } synchronized (ConnectionLimiter.LOCK) { this.connectioncount++; } if (waiting) { this.eventSender.fireEvent(new BasicEvent<Long>(this, ConnectionLimiter.WAIT_DUETO_CONNECTIONSPERMINUTE_LIMIT_END, null, null)); } this.eventSender.fireEvent(new BasicEvent<Long>(this, ConnectionLimiter.CONNECTION_OPENED, null, null)); } /** * set max allowed connections in specific time (ms) * * @param connections * @param time */ public synchronized void setConnectionTimeLimit(final int connections, final long time) { if (connections > 0 && time > 0) { this.timeConnections = connections; this.timeTime = time; } else { this.timeConnections = -1; this.timeTime = -1; } } /** * set max allowed concurrent connections * * @param max */ public synchronized void setMaxConcurrent(int max) { if (max <= 0) { max = -1; } this.maxConcurrent = max; } }