# AccessRateController.java

    /*
    * Copyright 2005,2006 WSO2, Inc. http://wso2.com
    *
    * Licensed under the Apache License, Version 2.0 (the "License");
    * you may not use this file except in compliance with the License.
    * You may obtain a copy of the License at
    *
    *      http://www.apache.org/licenses/LICENSE-2.0
    *
    * Unless required by applicable law or agreed to in writing, software
    * distributed under the License is distributed on an "AS IS" BASIS,
    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    * See the License for the specific language governing permissions and
    * limitations under the License.
    *
    *
    */

    package org.apache.synapse.commons.throttle.core;

    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.synapse.commons.throttle.core.factory.CallerContextFactory;

    /**
     * Controls the access of remote callers according to the controlling  policy .
     * This provides abstraction that need to control access based on caller IP or caller domain name.
     * This implementation is thread safe.
     */

    public class AccessRateController {

        private static Log log = LogFactory.getLog(AccessRateController.class.getName());

        private static final String ACCESS_DENIED_TEMPORALLY =
                "You cannot access this service since you have exceeded the allocated quota.";

        private static final String ACCESS_DENIED =
                "You cannot access this service since you have been prohibited permanently.";

        /* The Object for used to lock in synchronizing */
        private final Object lock = new Object();

        private boolean debugOn = false;  //is debug enable
        private static final String SYMBOL_UNDERSCORE = "_";

        public AccessRateController() {
            debugOn = log.isDebugEnabled();
        }

        /**
         * To check whether caller can access not not base on the controlling  policy
         *
         * @param throttleContext - current states of throttle - RunTime Data
         * @param callerID        - Identifier for remote caller - ex: ip or domainname
         * @param callerType      - the type of the caller
         * @return access information
         * @throws ThrottleException
         */
        public AccessInformation canAccess(ThrottleContext throttleContext, String callerID,
                                           int callerType) throws ThrottleException {

            String type = ThrottleConstants.IP_BASE == callerType ? "IP address" : "domain";

            ThrottleConfiguration throttleConfigurationBean =
                    throttleContext.getThrottleConfiguration();
            AccessInformation accessInformation = new AccessInformation();

            if (throttleConfigurationBean == null) {
                if (debugOn) {
                    log.debug("Throttle Configuration couldn't find - Throttling will not occur");
                }
                accessInformation.setAccessAllowed(true);
                return accessInformation;
            }

            if (callerID == null) {
                String msg = "Caller host or ip  couldn't find !! - Access will be denied ";
                if (debugOn) {
                    log.debug(msg);
                }
                accessInformation.setAccessAllowed(false);
                accessInformation.setFaultReason(msg);
                return accessInformation;
            }
            // The configs are added without unique key hence removing it while retrieving the configuration.
            String callerAddress = callerID.substring(callerID.lastIndexOf(SYMBOL_UNDERSCORE) + 1);
            CallerConfiguration configuration = throttleConfigurationBean.getCallerConfiguration(callerAddress);
            if (configuration == null) {
                if (debugOn) {
                    log.debug("Caller configuration couldn't find for " + type + " and for caller " +
                            callerID);
                }
                accessInformation.setAccessAllowed(true);
                return accessInformation;
            }
            if (configuration.getAccessState() == ThrottleConstants.ACCESS_DENIED) {
                log.info(ACCESS_DENIED);
                accessInformation.setAccessAllowed(false);
                accessInformation.setFaultReason(ACCESS_DENIED);
                return accessInformation;
            } else if (configuration.getAccessState() == ThrottleConstants.ACCESS_ALLOWED) {
                accessInformation.setAccessAllowed(true);
                return accessInformation;
            } else if (configuration.getAccessState() == ThrottleConstants.ACCESS_CONTROLLED) {
                synchronized (lock) {
                    CallerContext caller = throttleContext.getCallerContext(callerID);
                    if (caller == null) {
                        //if caller has not already registered ,then create new caller description and
                        //set it in throttle
                        caller = CallerContextFactory.createCaller(callerType, callerID);
                    }
                    if (caller != null) {
                        long currentTime = System.currentTimeMillis();

                        if (!caller.canAccess(throttleContext, configuration, currentTime)) {
                            //if current caller cannot access , then perform cleaning
                            log.info(ACCESS_DENIED_TEMPORALLY);
                            throttleContext.processCleanList(currentTime);
                            accessInformation.setAccessAllowed(false);
                            accessInformation.setFaultReason(ACCESS_DENIED_TEMPORALLY);
                            return accessInformation;
                        } else {
                            if (debugOn) {
                                log.debug("Access  from " + type + " " + callerID + " is successful.");
                            }
                            accessInformation.setAccessAllowed(true);
                            return accessInformation;
                        }
                    } else {
                        if (debugOn) {
                            log.debug("Caller " + type + " not found! " + callerID);
                        }
                        accessInformation.setAccessAllowed(true);
                        return accessInformation;
                    }
                }
            }
            accessInformation.setAccessAllowed(true);
            return accessInformation;
        }
    }