2 * Copyright (C) 2001, 2002 The Mir-coders group
\r
4 * This file is part of Mir.
\r
6 * Mir is free software; you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation; either version 2 of the License, or
\r
9 * (at your option) any later version.
\r
11 * Mir is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with Mir; if not, write to the Free Software
\r
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
20 * In addition, as a special exception, The Mir-coders gives permission to link
\r
21 * the code of this program with any library licensed under the Apache Software License,
\r
22 * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
\r
23 * (or with modified versions of the above that use the same license as the above),
\r
24 * and distribute linked combinations including the two. You must obey the
\r
25 * GNU General Public License in all respects for all of the code used other than
\r
26 * the above mentioned libraries. If you modify this file, you may extend this
\r
27 * exception to your version of the file, but you are not obligated to do so.
\r
28 * If you do not wish to do so, delete this exception statement from your version.
\r
30 package mircoders.localizer.basic.filters;
\r
32 import mircoders.localizer.basic.MirBasicAntiAbuseFilterTypes;
\r
33 import mircoders.entity.EntityComment;
\r
37 import mir.util.StringRoutines;
\r
38 import mir.entity.Entity;
\r
39 import mir.session.Request;
\r
42 * A ip-based throttling filter.
\r
45 * Expressions have the form <time in minutes>:<posting limit>
\r
47 public class ThrottleFilter extends MirBasicAntiAbuseFilterTypes.BasicFilterType {
\r
48 private long overallHorizon;
\r
50 private ThrottleManager throttleManager;
\r
52 public ThrottleFilter(String aName, long anOverallHorizon) {
\r
55 overallHorizon = anOverallHorizon;
\r
56 throttleManager = new ThrottleManager(overallHorizon);
\r
59 /** * {@inheritDoc} */
\r
60 public boolean validate(String anExpression) {
\r
61 List parts = StringRoutines.splitString(anExpression.trim(), ":");
\r
64 if (parts.size()==2) {
\r
65 Integer.parseInt((String) parts.get(0));
\r
66 Integer.parseInt((String) parts.get(1));
\r
71 catch (Throwable t) {
\r
80 public boolean test(String anExpression, Entity anEntity, Request aRequest) {
\r
81 String ip = aRequest.getHeader("ip");
\r
85 List parts = StringRoutines.splitString(anExpression, ":");
\r
88 period = Integer.parseInt((String) parts.get(0))*1000*60;
\r
89 limit = Integer.parseInt((String) parts.get(1));
\r
91 catch (Throwable t) {
\r
95 return throttleManager.addMessage(ip, anEntity, limit, period);
\r
98 private class ThrottleManager {
\r
99 private Map throttles;
\r
100 private long overallHorizon;
\r
101 private Thread cleanUpThread;
\r
103 public ThrottleManager(long anOverallHorizon) {
\r
104 throttles = new HashMap();
\r
105 overallHorizon = anOverallHorizon;
\r
107 cleanUpThread = new Thread() {
\r
108 public void run() {
\r
110 synchronized(throttles) {
\r
111 List toDelete = new ArrayList();
\r
112 Iterator i = throttles.entrySet().iterator();
\r
114 while (i.hasNext()) {
\r
115 Map.Entry entry = (Map.Entry) i.next();
\r
117 if (((Throttle) entry.getValue()).flush()) {
\r
118 toDelete.add(entry.getKey());
\r
121 catch (Throwable t) {
\r
122 toDelete.add(entry.getKey());
\r
126 i = toDelete.iterator();
\r
127 while (i.hasNext()) {
\r
128 throttles.remove(i.next());
\r
132 Thread.sleep(60*10*1000);
\r
134 catch (InterruptedException e) {
\r
141 cleanUpThread.setDaemon(true);
\r
142 cleanUpThread.start();
\r
145 public boolean addMessage(String anIP, Entity anEntity, int aLimit, long aPeriod) {
\r
146 synchronized (throttles) {
\r
147 Throttle throttle = (Throttle) throttles.get(anIP);
\r
149 if (throttle==null) {
\r
150 throttle = new Throttle(overallHorizon);
\r
151 throttles.put(anIP, throttle);
\r
153 return throttle.addMessage(anEntity, aLimit, aPeriod);
\r
157 private class Throttle {
\r
158 private List messages;
\r
159 private long horizon;
\r
161 public Throttle(long aHorizon) {
\r
162 messages = new ArrayList();
\r
163 horizon = aHorizon;
\r
166 public boolean flush() {
\r
167 long limit = System.currentTimeMillis() - horizon;
\r
169 while (messages.size()>0 && ((Message) messages.get(0)).getTime()<=limit) {
\r
170 messages.remove(0);
\r
173 return messages.size()==0;
\r
176 public boolean addMessage(Entity anEntity, int aLimit, long aPeriod) {
\r
177 Message lastMessage=null;
\r
178 if (messages.size()>0) {
\r
179 lastMessage = (Message) messages.get(messages.size()-1);
\r
182 Message newMessage = new Message(anEntity.getId(),
\r
183 anEntity instanceof EntityComment, System.currentTimeMillis());
\r
185 if (!newMessage.equals(lastMessage))
\r
186 messages.add(newMessage);
\r
188 if (messages.size()>=aLimit) {
\r
189 Message message = (Message) messages.get(messages.size()-aLimit);
\r
190 return (System.currentTimeMillis()-message.getTime())<aPeriod;
\r
196 private class Message {
\r
198 private boolean isComment;
\r
201 public Message(String anId, boolean anIsComment, long aTime) {
\r
203 isComment = anIsComment;
\r
207 public String getId() {
\r
211 public boolean getIsComment() {
\r
215 public long getTime() {
\r
219 public int hashCode() {
\r
220 return getId().hashCode();
\r
223 public boolean equals(Object anObject) {
\r
224 if (anObject instanceof Message) {
\r
225 Message that = (Message) anObject;
\r
227 if (that.getId().equals(getId()) && that.getIsComment() == getIsComment()) {
\r