File Source: GenericThrottle.java
/*
P/P * Method: org.apache.roller.weblogger.util.GenericThrottle$ClientInfo__static_init
*/
1 /*
2 * Copyright 2004 Sun Microsystems, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18 package org.apache.roller.weblogger.util;
19
20 import java.util.HashMap;
21 import java.util.Map;
22 import org.apache.commons.logging.Log;
23 import org.apache.commons.logging.LogFactory;
24 import org.apache.roller.weblogger.util.cache.Cache;
25 import org.apache.roller.weblogger.util.cache.CacheManager;
26 import org.apache.roller.weblogger.util.cache.ExpiringCacheEntry;
27
28
29 /**
30 * A tool used to provide throttling support.
31 *
32 * The basic idea is that if the # of hits from a client within a certain
33 * interval of time is greater than the threshold value then the client is
34 * considered to be abusive.
35 */
36 public class GenericThrottle {
37
/*
P/P * Method: org.apache.roller.weblogger.util.GenericThrottle__static_init
*
* Postconditions:
* init'ed(log)
*/
38 private static Log log = LogFactory.getLog(GenericThrottle.class);
39
40 // threshold and interval to determine who is abusive
41 private int threshold = 1;
42 private int interval = 0;
43
44 // a cache to maintain the data
45 Cache clientHistoryCache = null;
46
47
/*
P/P * Method: void org.apache.roller.weblogger.util.GenericThrottle(int, int, int)
*
* Preconditions:
* org/apache/roller/weblogger/util/cache/CacheManager.caches != null
* org/apache/roller/weblogger/util/cache/CacheManager.log != null
* (soft) org/apache/roller/weblogger/util/cache/CacheManager.cacheFactory != null
* (soft) org/apache/roller/weblogger/util/cache/CacheManager.cacheHandlers != null
*
* Postconditions:
* this.clientHistoryCache != null
* this.interval == One-of{0, inter}
* this.interval >= 0
* this.threshold == One-of{1, thresh}
* this.threshold >= 0
* init'ed(new Date(LRUCacheImpl#1) num objects)
* init'ed(new ExpiringLRUCacheImpl(constructCache#1*) num objects)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).cache)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).hits)
* possibly_updated(new ExpiringLRUCacheImpl(constructCache#1*).id)
* ...
*
* Test Vectors:
* inter: {-231..0}, {1..232-1}
* maxEntries: {0..232-1}, {-231..-1}
* thresh: {-231..-1}, {0..232-1}
*/
48 public GenericThrottle(int thresh, int inter, int maxEntries) {
49
50 // threshold can't be negative, that would mean everyone is abusive
51 if(thresh > -1) {
52 this.threshold = thresh;
53 }
54
55 // interval must be a positive value
56 if(inter > 0) {
57 this.interval = inter;
58 }
59
60 // max entries must be a positive value
61 if(maxEntries < 0) {
62 maxEntries = 1;
63 }
64
65 // cache props
66 Map cacheProps = new HashMap();
67 cacheProps.put("id", "throttle");
68 cacheProps.put("size", ""+maxEntries);
69 cacheProps.put("timeout", ""+this.interval);
70
71 // get cache instance. handler is null cuz we don't want to register it
72 this.clientHistoryCache = CacheManager.constructCache(null, cacheProps);
73 }
74
75
76 /**
77 * Process a new hit from the client.
78 *
79 * Each call to this method increments the hit count for the client and
80 * then returns a boolean value indicating if the hit has pushed the client
81 * over the threshold.
82 *
83 * @retuns true if client is abusive, false otherwise
84 */
85 public boolean processHit(String clientId) {
86
/*
P/P * Method: bool processHit(String)
*
* Preconditions:
* (soft) log != null
* (soft) init'ed(this.clientHistoryCache.hits)
* (soft) init'ed(this.clientHistoryCache.misses)
* (soft) init'ed(this.clientHistoryCache.puts)
* (soft) this.clientHistoryCache != null
* (soft) this.clientHistoryCache.cache != null
* (soft) init'ed(this.interval)
* (soft) init'ed(this.threshold)
*
* Presumptions:
* client.hits@93 <= 232-2
*
* Postconditions:
* init'ed(return_value)
* this.clientHistoryCache.hits == One-of{old this.clientHistoryCache.hits, old this.clientHistoryCache.hits + 1, One-of{old this.clientHistoryCache.hits, old this.clientHistoryCache.hits + 1} - 1}
* (soft) init'ed(this.clientHistoryCache.hits)
* this.clientHistoryCache.misses == One-of{old this.clientHistoryCache.misses, old this.clientHistoryCache.misses + 1}
* (soft) init'ed(this.clientHistoryCache.misses)
* this.clientHistoryCache.puts == One-of{old this.clientHistoryCache.puts, old this.clientHistoryCache.puts + 1}
* (soft) init'ed(this.clientHistoryCache.puts)
* this.clientHistoryCache.removes == One-of{old this.clientHistoryCache.removes, old this.clientHistoryCache.removes + 1, One-of{old this.clientHistoryCache.removes, old this.clientHistoryCache.removes + 1} + 1}
*
* Test Vectors:
* clientId: Inverse{null}, Addr_Set{null}
*/
87 if(clientId == null) {
88 return false;
89 }
90
91 // see if we have any info about this client yet
92 ClientInfo client = null;
93 ExpiringCacheEntry cacheEntry = (ExpiringCacheEntry) this.clientHistoryCache.get(clientId);
94 if(cacheEntry != null) {
95 log.debug("HIT "+clientId);
96 client = (ClientInfo) cacheEntry.getValue();
97
98 // this means entry had expired
99 if(client == null) {
100 log.debug("EXPIRED "+clientId);
101 this.clientHistoryCache.remove(clientId);
102 }
103 }
104
105 // if we already know this client then update their hit count and
106 // see if they have surpassed the threshold
107 if(client != null) {
108 client.hits++;
109
110 log.debug("STATUS "+clientId+" - "+client.hits+" hits since "+client.start);
111
112 // abusive client
113 if(client.hits > this.threshold) {
114 return true;
115 }
116
117 } else {
118 log.debug("NEW "+clientId);
119
120 // first timer
121 ClientInfo newClient = new ClientInfo();
122 newClient.hits = 1;
123 newClient.id = clientId;
124
125 ExpiringCacheEntry newEntry = new ExpiringCacheEntry(newClient, this.interval);
126 this.clientHistoryCache.put(clientId, newEntry);
127 }
128
129 return false;
130 }
131
132
133 /**
134 * Check the current status of a client.
135 *
136 * A client is considered abusive if the number of hits from the client
137 * within the configured interval is greater than the set threshold.
138 *
139 * @returns true if client is abusive, false otherwise.
140 */
141 public boolean isAbusive(String clientId) {
142
/*
P/P * Method: bool isAbusive(String)
*
* Preconditions:
* (soft) log != null
* (soft) init'ed(this.clientHistoryCache.hits)
* (soft) init'ed(this.clientHistoryCache.misses)
* (soft) this.clientHistoryCache != null
* (soft) this.clientHistoryCache.cache != null
* (soft) init'ed(this.threshold)
*
* Postconditions:
* init'ed(return_value)
* this.clientHistoryCache.hits == One-of{old this.clientHistoryCache.hits, old this.clientHistoryCache.hits + 1, One-of{old this.clientHistoryCache.hits, old this.clientHistoryCache.hits + 1} - 1}
* (soft) init'ed(this.clientHistoryCache.hits)
* this.clientHistoryCache.misses == One-of{old this.clientHistoryCache.misses, old this.clientHistoryCache.misses + 1}
* (soft) init'ed(this.clientHistoryCache.misses)
* this.clientHistoryCache.removes == One-of{old this.clientHistoryCache.removes, old this.clientHistoryCache.removes + 1, One-of{old this.clientHistoryCache.removes, old this.clientHistoryCache.removes + 1} + 1}
*
* Test Vectors:
* clientId: Inverse{null}, Addr_Set{null}
*/
143 if(clientId == null) {
144 return false;
145 }
146
147 // see if we have any info about this client
148 ClientInfo client = null;
149 ExpiringCacheEntry cacheEntry = (ExpiringCacheEntry) this.clientHistoryCache.get(clientId);
150 if(cacheEntry != null) {
151 log.debug("HIT "+clientId);
152 client = (ClientInfo) cacheEntry.getValue();
153
154 // this means entry had expired
155 if(client == null) {
156 log.debug("EXPIRED "+clientId);
157 this.clientHistoryCache.remove(clientId);
158 }
159 }
160
161 if(client != null) {
162 return (client.hits > this.threshold);
163 } else {
164 return false;
165 }
166 }
167
168
169 // just something to keep a few properties in
/*
P/P * Method: void org.apache.roller.weblogger.util.GenericThrottle$ClientInfo(GenericThrottle, GenericThrottle$1)
*
* Postconditions:
* this.hits == 0
* this.id == null
* this.start == &new Date(GenericThrottle$ClientInfo#1)
* new Date(GenericThrottle$ClientInfo#1) num objects == 1
*/
170 private class ClientInfo {
171
172 public String id = null;
173 public int hits = 0;
174 public java.util.Date start = new java.util.Date();
175
176 }
177
178 }
SofCheck Inspector Build Version : 2.18479
| GenericThrottle.java |
2009-Jan-02 14:24:52 |
| GenericThrottle.class |
2009-Sep-04 03:12:32 |
| GenericThrottle$1.class |
2009-Sep-04 03:12:32 |
| GenericThrottle$ClientInfo.class |
2009-Sep-04 03:12:32 |