com.naryx.tagfusion.cfm.tag.ext.cfTHROTTLE.java Source code

Java tutorial

Introduction

Here is the source code for com.naryx.tagfusion.cfm.tag.ext.cfTHROTTLE.java

Source

/* 
 *  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;
        }
    }

}