Java tutorial
/* * Copyright (C) 2000 - 2010 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ /* * BlueDragon only tag. (Alan Williamson) * * This tag maintains a list of TOKENs and the time between * successive access of those TOKENs to allow for a throttle * mechanism. * * ACTION = THROTTLE (default) * - TOKEN = string of the token (defaults to the client IP) * - HITTHRESHOLD = number of times the token should be hit in a time period * - HITTIMEPERIOD = the time period in which the max hits can appear in * - MINHITTIME = the time where if a request comes in quicker its throttled * * returns CFTHROTTLE structure with a flag to say whether or not it should throttle * * * ACTION = STATUS * - returns a structure CFTHROTTLE of all the tokens * * * ACTION = SET * - HISTORY the size of the list (defaults to 100) * */ package com.naryx.tagfusion.cfm.tag.ext; import java.io.Serializable; import org.apache.commons.collections.map.LRUMap; import com.naryx.tagfusion.cfm.engine.cfArrayData; import com.naryx.tagfusion.cfm.engine.cfBooleanData; import com.naryx.tagfusion.cfm.engine.cfDateData; import com.naryx.tagfusion.cfm.engine.cfNumberData; import com.naryx.tagfusion.cfm.engine.cfSession; import com.naryx.tagfusion.cfm.engine.cfStringData; import com.naryx.tagfusion.cfm.engine.cfStructData; import com.naryx.tagfusion.cfm.engine.cfmBadFileException; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; import com.naryx.tagfusion.cfm.tag.cfTag; import com.naryx.tagfusion.cfm.tag.cfTagReturnType; public class cfTHROTTLE extends cfTag implements Serializable { static final long serialVersionUID = 1; transient static LRUMap throttleHistory = null; static int totalHit = 0, totalThrottled = 0, totalThrottledQuick = 0; static long startTime; private int actionType = 0; static int ACTION_THROTTLE = 0; static int ACTION_FLUSH = 1; static int ACTION_STATUS = 2; static int ACTION_SET = 3; protected void defaultParameters(String _tag) throws cfmBadFileException { //-- If not initialised, initialise it if (throttleHistory == null) { throttleHistory = new LRUMap(100); startTime = System.currentTimeMillis(); } defaultAttribute("ACTION", "THROTTLE"); defaultAttribute("HITTHRESHOLD", "5"); defaultAttribute("HITTIMEPERIOD", "10000"); defaultAttribute("MINHITTIME", "500"); parseTagHeader(_tag); if (getConstant("ACTION").equalsIgnoreCase("FLUSH")) { actionType = ACTION_FLUSH; } else if (getConstant("ACTION").equalsIgnoreCase("STATUS")) { actionType = ACTION_STATUS; } else if (getConstant("ACTION").equalsIgnoreCase("SET")) { actionType = ACTION_SET; } else { actionType = ACTION_THROTTLE; } } public cfTagReturnType render(cfSession _Session) throws cfmRunTimeException { if (actionType == ACTION_THROTTLE) { //-- Determine the token; if not present then use the clients remote IP address String token; if (containsAttribute("TOKEN")) token = getDynamic(_Session, "TOKEN").getString(); else token = _Session.REQ.getRemoteAddr(); int hitThresHold = getDynamic(_Session, "HITTHRESHOLD").getInt(); long hitTimePeriodMs = getDynamic(_Session, "HITTIMEPERIOD").getLong(); long hitMinTimeMs = getDynamic(_Session, "MINHITTIME").getLong(); throttleClient client; cfStructData result = new cfStructData(); synchronized (throttleHistory) { client = (throttleClient) throttleHistory.get(token); if (client == null) { client = new throttleClient(token); throttleHistory.put(token, client); } } //-- Synchronize on this client synchronized (client) { long lasthit = client.lastHit(); long age = client.age(); totalHit++; client.hit(); //-- Register this hit if (age <= hitTimePeriodMs && client.hitCount >= hitThresHold) { //- checks to make sure they are within their allocated amount result.put("THROTTLE", cfBooleanData.TRUE); client.throttled(); totalThrottled++; } else if (lasthit > 10 && lasthit < hitMinTimeMs) { //-- checks for too fast accesses between requests result.put("THROTTLE", cfBooleanData.TRUE); client.throttled(); totalThrottled++; totalThrottledQuick++; } else result.put("THROTTLE", cfBooleanData.FALSE); //-- If the age of this entry has expired then reset it. if (age > hitTimePeriodMs) client.reset(); //-- Set the data into the session result.setData("HITCOUNT", new cfNumberData(client.hitCount)); result.setData("TOTALHITS", new cfNumberData(client.totalHits)); result.setData("LASTHIT", new cfNumberData(lasthit)); result.setData("AGE", new cfNumberData(age)); } _Session.setData("CFTHROTTLE", result); } else if (actionType == ACTION_SET) { //-- Check to see if the history length has changed or been passed in if (containsAttribute("HISTORY")) { throttleHistory = new LRUMap(getDynamic(_Session, "HISTORY").getInt()); } } else if (actionType == ACTION_FLUSH) { //-- Clears out the history synchronized (throttleHistory) { throttleHistory.clear(); } } else if (actionType == ACTION_STATUS) { cfArrayData array = cfArrayData.createArray(1); synchronized (throttleHistory) { org.apache.commons.collections.OrderedMapIterator it = throttleHistory.orderedMapIterator(); throttleClient client; cfStructData clientData; while (it.hasNext()) { it.next(); client = (throttleClient) it.getValue(); clientData = new cfStructData(); clientData.setData("TOKEN", new cfStringData(client.token)); clientData.setData("HIT", new cfNumberData(client.hitCount)); clientData.setData("TOTALHITS", new cfNumberData(client.totalHits)); clientData.setData("TOTALTHROTTLE", new cfNumberData(client.totalThrottled)); clientData.setData("THROTTLE", new cfNumberData(client.throttled)); clientData.setData("LASTHIT", new cfDateData(client.lastUsed)); clientData.setData("AGE", new cfNumberData(client.age())); array.addElement(clientData); } } cfStructData throttle = new cfStructData(); throttle.setData("CLIENTS", array); throttle.setData("HITS", new cfNumberData(totalHit)); throttle.setData("THROTTLE", new cfNumberData(totalThrottled)); throttle.setData("QUICKTHROTTLE", new cfNumberData(totalThrottledQuick)); throttle.setData("STARTTIME", new cfDateData(startTime)); _Session.setData("CFTHROTTLE", throttle); } return cfTagReturnType.NORMAL; } //----------------------- class throttleClient extends Object { public String token; public int hitCount; public long lastUsed; public long created; public int throttled; public int totalHits, totalThrottled; public throttleClient(String _token) { token = _token; hitCount = 0; throttled = 0; totalHits = 0; totalThrottled = 0; lastUsed = System.currentTimeMillis(); created = lastUsed; } public final void throttled() { throttled++; totalThrottled++; } public final void hit() { hitCount++; totalHits++; lastUsed = System.currentTimeMillis(); } public final long age() { return System.currentTimeMillis() - created; } public final long lastHit() { return System.currentTimeMillis() - lastUsed; } public void reset() { hitCount = 0; lastUsed = System.currentTimeMillis(); created = lastUsed; throttled = 0; } } }