/** * Check whether that caller can access or not ,based on current state and pre-defined policy * * @param throttleContext -The Context for this caller - runtime state * @param configuration -The Configuration for this caller - data from policy * @param currentTime - The current system time * @return boolean -The boolean value which say access will allow or not * @throws ThrottleException throws for invalid throttle configuration */ public boolean canAccess( ThrottleContext throttleContext, CallerConfiguration configuration, long currentTime) throws ThrottleException { boolean canAccess; if (configuration == null) { if (log.isDebugEnabled()) { log.debug("Couldn't find the configuration ."); } return true; } if (configuration.getMaximumRequestPerUnitTime() < 0 || configuration.getUnitTime() <= 0 || configuration.getProhibitTimePeriod() < 0) { throw new ThrottleException("Invalid Throttle Configuration"); } // if caller access first time in his new session if (this.firstAccessTime == 0) { initAccess(configuration, throttleContext, currentTime); } // if unit time period (session time) is not over if (this.nextTimeWindow > currentTime) { canAccess = canAccessIfUnitTimeNotOver(configuration, throttleContext, currentTime); } else { canAccess = canAccessIfUnitTimeOver(configuration, throttleContext, currentTime); } return canAccess; }
/** * Init the access for a particular caller , caller will registered with context * * @param configuration -The Configuration for this caller * @param throttleContext -The Throttle Context * @param currentTime -The system current time in milliseconds */ private void initAccess( CallerConfiguration configuration, ThrottleContext throttleContext, long currentTime) { // This block should be synchronize by key as multiple threads can access this. this.unitTime = configuration.getUnitTime(); this.firstAccessTime = currentTime; this.nextTimeWindow = this.firstAccessTime + this.unitTime; // Also we need to pick counter value associated with time window. throttleContext.addCallerContext(this, this.id); throttleContext.replicateTimeWindow(this.id); }
/** * To verify access if unit time has already over * * @param configuration -The Configuration for this caller * @param throttleContext -The Throttle that caller having pass * @param currentTime -The system current time * @return boolean -The boolean value which say access will allow or not */ private boolean canAccessIfUnitTimeOver( CallerConfiguration configuration, ThrottleContext throttleContext, long currentTime) { boolean canAccess = false; // if number of access for a unit time is less than MAX and // if the unit time period (session time) has just over int maxRequest = configuration.getMaximumRequestPerUnitTime(); if (maxRequest != 0) { if ((this.globalCount.get() + this.localCount.get()) < maxRequest) { if (this.nextTimeWindow != 0) { // Removes and sends the current state to others (clustered env) throttleContext.removeAndFlushCaller(this.id); } if (log.isDebugEnabled()) { log.debug( "CallerContext Checking access if unit time over next time window>> Access allowed=" + maxRequest + " available=" + (maxRequest - (this.globalCount.get() + this.localCount.get())) + " key=" + this.getId() + " currentGlobalCount=" + globalCount + " currentTime=" + currentTime + " nextTimeWindow=" + this.nextTimeWindow + " currentLocalCount=" + localCount + " Tier=" + configuration.getID() + " nextAccessTime=" + this.nextAccessTime); } canAccess = true; // this is bonus access // next time callers can access as a new one } else { // if number of access for a unit time has just been greater than MAX now same as a new // session // OR if caller in prohibit session and prohibit period has just over if ((this.nextAccessTime == 0) || (this.nextAccessTime <= currentTime)) { if (log.isDebugEnabled()) { log.debug( "CallerContext Checking access if unit time over>> Access allowed=" + maxRequest + " available=" + (maxRequest - (this.globalCount.get() + this.localCount.get())) + " key=" + this.getId() + " currentGlobalCount=" + globalCount + " currentTime=" + currentTime + " nextTimeWindow=" + this.nextTimeWindow + " currentLocalCount=" + localCount + " Tier=" + configuration.getID() + " nextAccessTime=" + this.nextAccessTime); } // remove previous callercontext instance if (this.nextTimeWindow != 0) { throttleContext.removeCallerContext(id); } // reset the states so that, this is the first access this.nextAccessTime = 0; canAccess = true; this.globalCount.set(0); // can access the system and this is same as first access this.localCount.set(1); this.firstAccessTime = currentTime; this.nextTimeWindow = currentTime + configuration.getUnitTime(); // registers caller and send the current state to others (clustered env) throttleContext.addAndFlushCallerContext(this, id); throttleContext.replicateTimeWindow(this.id); if (log.isDebugEnabled()) { log.debug( "Caller=" + this.getId() + " has reset counters and added for replication when unit " + "time is over"); } } else { // if caller in prohibit session and prohibit period has not over if (log.isDebugEnabled()) { String type = ThrottleConstants.IP_BASE == configuration.getType() ? "IP address" : "domain"; log.debug( "Even unit time has over , CallerContext in prohibit state :" + type + " - " + this.id); } } } } return canAccess; }
/** * To verify access if the unit time has already not over * * @param configuration - -The Configuration for this caller * @param throttleContext -The Throttle Context * @param currentTime -The system current time * @return boolean -The boolean value which say access will allow or not */ private boolean canAccessIfUnitTimeNotOver( CallerConfiguration configuration, ThrottleContext throttleContext, long currentTime) { boolean canAccess = false; int maxRequest = configuration.getMaximumRequestPerUnitTime(); if (maxRequest != 0) { if ((this.globalCount.get() + this.localCount.get()) < maxRequest) { // If the globalCount is less than max request if (log.isDebugEnabled()) { log.debug( "CallerContext Checking access if unit time is not over and less than max count>> Access " + "allowed=" + maxRequest + " available=" + (maxRequest - (this.globalCount.get() + this.localCount.get())) + " key=" + this.getId() + " currentGlobalCount=" + globalCount + " currentTime=" + currentTime + " " + "nextTimeWindow=" + this.nextTimeWindow + " currentLocalCount=" + localCount + " Tier=" + configuration.getID() + " nextAccessTime=" + this.nextAccessTime); } canAccess = true; // can continue access this.localCount.incrementAndGet(); // Send the current state to others (clustered env) throttleContext.flushCallerContext(this, id); // can complete access } else { // else , if caller has not already prohibit if (this.nextAccessTime == 0) { // and if there is no prohibit time period in configuration long prohibitTime = configuration.getProhibitTimePeriod(); if (prohibitTime == 0) { // prohibit access until unit time period is over this.nextAccessTime = this.firstAccessTime + configuration.getUnitTime(); } else { // if there is a prohibit time period in configuration ,then // set it as prohibit period this.nextAccessTime = currentTime + prohibitTime; } if (log.isDebugEnabled()) { String type = ThrottleConstants.IP_BASE == configuration.getType() ? "IP address" : "domain"; log.debug( "Maximum Number of requests are reached for caller with " + type + " - " + this.id); } // Send the current state to others (clustered env) throttleContext.flushCallerContext(this, id); } else { // else , if the caller has already prohibit and prohibit // time period has already over if (this.nextAccessTime <= currentTime) { if (log.isDebugEnabled()) { log.debug( "CallerContext Checking access if unit time is not over before time window exceed >> " + "Access allowed=" + maxRequest + " available=" + (maxRequest - (this.globalCount.get() + this.localCount.get())) + " key=" + this.getId() + " currentGlobalCount=" + globalCount + " currentTime=" + currentTime + " " + "nextTimeWindow=" + this.nextTimeWindow + " currentLocalCount=" + localCount + " " + "Tier=" + configuration.getID() + " nextAccessTime=" + this.nextAccessTime); } // remove previous caller context if (this.nextTimeWindow != 0) { throttleContext.removeCallerContext(id); } // reset the states so that, this is the first access this.nextAccessTime = 0; canAccess = true; this.globalCount.set(0); // can access the system and this is same as first access this.localCount.set(1); this.firstAccessTime = currentTime; this.nextTimeWindow = currentTime + configuration.getUnitTime(); throttleContext.addAndFlushCallerContext(this, this.id); throttleContext.replicateTimeWindow(this.id); if (log.isDebugEnabled()) { log.debug( "Caller=" + this.getId() + " has reset counters and added for replication when unit " + "time is not over"); } } else { if (log.isDebugEnabled()) { String type = ThrottleConstants.IP_BASE == configuration.getType() ? "IP address" : "domain"; log.debug( "Prohibit period is not yet over for caller with " + type + " - " + this.id); } } } } } return canAccess; }