/**
   * Helper method for handling concurrent throttling
   *
   * @param concurrentAccessController ConcurrentAccessController
   * @param messageContext MessageContext - message level states
   * @return true if access is allowed through concurrent throttling ,o.w false
   */
  private boolean doConcurrentThrottling(
      ConcurrentAccessController concurrentAccessController, MessageContext messageContext) {

    boolean canAccess = true;
    int available;

    if (concurrentAccessController != null) {
      if (messageContext.getFLOW() == MessageContext.IN_FLOW) {
        available = concurrentAccessController.getAndDecrement();
        canAccess = available > 0;
        if (debugOn) {
          log.debug(
              "Concurrency Throttle : Access "
                  + (canAccess ? "allowed" : "denied")
                  + " :: "
                  + available
                  + " of available of "
                  + concurrentAccessController.getLimit()
                  + " connections");
        }
        if (debugOn) {
          if (!canAccess) {
            log.debug(
                "Concurrency Throttle : Access has currently been denied since allowed"
                    + " maximum concurrent access have exceeded");
          }
        }
      } else if (messageContext.getFLOW() == MessageContext.OUT_FLOW) {
        available = concurrentAccessController.incrementAndGet();
        if (debugOn) {
          log.debug(
              "Concurrency Throttle : Connection returned"
                  + " :: "
                  + available
                  + " of available of "
                  + concurrentAccessController.getLimit()
                  + " connections");
        }
      }
    }
    return canAccess;
  }
  /**
   * Helper method for handling role based Access throttling
   *
   * @param messageContext MessageContext - message level states
   * @return true if access is allowed through concurrent throttling ,o.w false
   */
  private boolean doRoleBasedAccessThrottling(
      Throttle throttle, MessageContext messageContext, boolean isClusteringEnable)
      throws AxisFault, ThrottleException {

    boolean canAccess = true;
    ConfigurationContext cc = messageContext.getConfigurationContext();
    String throttleId = throttle.getId();

    String key = null;
    ConcurrentAccessController cac = null;
    if (isClusteringEnable) {
      // for clustered  env.,gets it from axis configuration context
      key = ThrottleConstants.THROTTLE_PROPERTY_PREFIX + throttleId + ThrottleConstants.CAC_SUFFIX;
      cac = (ConcurrentAccessController) cc.getProperty(key);
    }

    if (messageContext.getFLOW() == MessageContext.IN_FLOW) {
      // gets the remote caller role name
      String consumerKey = null;
      boolean isAuthenticated = false;
      String roleID = null;
      HttpServletRequest request =
          (HttpServletRequest)
              messageContext.getPropertyNonReplicable(HTTPConstants.MC_HTTP_SERVLETREQUEST);
      if (request != null) {
        String oAuthHeader = request.getHeader("OAuth");
        //                consumerKey = Utils.extractCustomerKeyFromAuthHeader(oAuthHeader);
        //                roleID = Utils.extractCustomerKeyFromAuthHeader(oAuthHeader);
        DummyAuthenticator authFuture = new DummyAuthenticator(oAuthHeader);
        consumerKey = authFuture.getAPIKey();
        new DummyHandler().authenticateUser(authFuture);
        roleID = (String) authFuture.getAuthorizedRoles().get(0);
        isAuthenticated = authFuture.isAuthenticated();
      }

      if (!isAuthenticated) {
        throw new AxisFault(
            " Access deny for a "
                + "caller with consumer Key: "
                + consumerKey
                + " "
                + " : Reason : Authentication failure");
      }
      // Domain name based throttling
      // check whether a configuration has been defined for this role name or not
      String consumerRoleID = null;
      if (consumerKey != null && isAuthenticated) {
        // loads the ThrottleContext
        ThrottleContext context =
            throttle.getThrottleContext(ThrottleConstants.ROLE_BASED_THROTTLE_KEY);
        if (context != null) {
          // Loads the ThrottleConfiguration
          ThrottleConfiguration config = context.getThrottleConfiguration();
          if (config != null) {
            // check for configuration for this caller
            consumerRoleID = config.getConfigurationKeyOfCaller(roleID);
            if (consumerRoleID != null) {
              // If this is a clustered env.
              if (isClusteringEnable) {
                context.setConfigurationContext(cc);
                context.setThrottleId(throttleId);
              }
              AccessInformation infor =
                  roleBasedAccessController.canAccess(context, consumerKey, consumerRoleID);
              StatCollector.collect(infor, consumerKey, ThrottleConstants.ROLE_BASE);
              // check for the permission for access
              if (!infor.isAccessAllowed()) {

                // In the case of both of concurrency throttling and
                // rate based throttling have enabled ,
                // if the access rate less than maximum concurrent access ,
                // then it is possible to occur death situation.To avoid that reset,
                // if the access has denied by rate based throttling
                if (cac != null) {
                  cac.incrementAndGet();
                  // set back if this is a clustered env
                  if (isClusteringEnable) {
                    cc.setProperty(key, cac);
                    // replicate the current state of ConcurrentAccessController
                    try {
                      if (debugOn) {
                        log.debug(
                            "Going to replicates the "
                                + "states of the ConcurrentAccessController"
                                + " with key : "
                                + key);
                      }
                      Replicator.replicate(cc, new String[] {key});
                    } catch (ClusteringFault clusteringFault) {
                      log.error("Error during replicating states ", clusteringFault);
                    }
                  }
                }
                throw new AxisFault(
                    " Access deny for a "
                        + "caller with Domain "
                        + consumerKey
                        + " "
                        + " : Reason : "
                        + infor.getFaultReason());
              }
            } else {
              if (debugOn) {
                log.debug(
                    "Could not find the Throttle Context for role-Based "
                        + "Throttling for role name "
                        + consumerKey
                        + " Throttling for this "
                        + "role name may not be configured from policy");
              }
            }
          }
        }
      } else {
        if (debugOn) {
          log.debug("Could not find the role of the caller - role based throttling NOT applied");
        }
      }
    }
    return canAccess;
  }
  /**
   * processing through the throttle 1) concurrent throttling 2) access rate based throttling -
   * domain or ip
   *
   * @param throttle The Throttle object - holds all configuration and state data of the throttle
   * @param messageContext The MessageContext , that holds all data per message basis
   * @throws AxisFault Throws when access must deny for caller
   * @throws ThrottleException ThrottleException
   */
  public void process(Throttle throttle, MessageContext messageContext)
      throws ThrottleException, AxisFault {

    String throttleId = throttle.getId();
    ConfigurationContext cc = messageContext.getConfigurationContext();

    // check the env - whether clustered  or not
    boolean isClusteringEnable = false;
    ClusteringAgent clusteringAgent = null;
    if (cc != null) {
      clusteringAgent = cc.getAxisConfiguration().getClusteringAgent();
    }
    if (clusteringAgent != null && clusteringAgent.getStateManager() != null) {
      isClusteringEnable = true;
    }

    // Get the concurrent access controller
    ConcurrentAccessController cac;
    String key = null;
    if (isClusteringEnable) {
      // for clustered  env.,gets it from axis configuration context
      key = ThrottleConstants.THROTTLE_PROPERTY_PREFIX + throttleId + ThrottleConstants.CAC_SUFFIX;
      cac = (ConcurrentAccessController) cc.getProperty(key);
    } else {
      // for non-clustered  env.,gets it from axis configuration context
      cac = throttle.getConcurrentAccessController();
    }

    // check for concurrent access
    boolean canAccess = doConcurrentThrottling(cac, messageContext);

    if (canAccess) {
      // if the concurrent access is success then
      // do the access rate based throttling

      if (messageContext.getFLOW() == MessageContext.IN_FLOW) {
        // gets the remote caller domain name
        String domain = null;
        HttpServletRequest request =
            (HttpServletRequest)
                messageContext.getPropertyNonReplicable(HTTPConstants.MC_HTTP_SERVLETREQUEST);
        if (request != null) {
          domain = request.getRemoteHost();
        }

        // Domain name based throttling
        // check whether a configuration has been defined for this domain name or not
        String callerId = null;
        if (domain != null) {
          // loads the ThrottleContext
          ThrottleContext context =
              throttle.getThrottleContext(ThrottleConstants.DOMAIN_BASED_THROTTLE_KEY);
          if (context != null) {
            // Loads the ThrottleConfiguration
            ThrottleConfiguration config = context.getThrottleConfiguration();
            if (config != null) {
              // check for configuration for this caller
              callerId = config.getConfigurationKeyOfCaller(domain);
              if (callerId != null) {
                // If this is a clustered env.
                if (isClusteringEnable) {
                  context.setConfigurationContext(cc);
                  context.setThrottleId(throttleId);
                }
                AccessInformation infor =
                    accessRateController.canAccess(
                        context, callerId, ThrottleConstants.DOMAIN_BASE);
                StatCollector.collect(infor, domain, ThrottleConstants.DOMAIN_BASE);

                // check for the permission for access
                if (!infor.isAccessAllowed()) {

                  // In the case of both of concurrency throttling and
                  // rate based throttling have enabled ,
                  // if the access rate less than maximum concurrent access ,
                  // then it is possible to occur death situation.To avoid that reset,
                  // if the access has denied by rate based throttling
                  if (cac != null) {
                    cac.incrementAndGet();
                    // set back if this is a clustered env
                    if (isClusteringEnable) {
                      cc.setProperty(key, cac);
                      // replicate the current state of ConcurrentAccessController
                      try {
                        if (debugOn) {
                          log.debug(
                              "Going to replicates the "
                                  + "states of the ConcurrentAccessController"
                                  + " with key : "
                                  + key);
                        }
                        Replicator.replicate(cc, new String[] {key});
                      } catch (ClusteringFault clusteringFault) {
                        log.error("Error during replicating states ", clusteringFault);
                      }
                    }
                  }
                  throw new AxisFault(
                      " Access deny for a "
                          + "caller with Domain "
                          + domain
                          + " "
                          + " : Reason : "
                          + infor.getFaultReason());
                }
              } else {
                if (debugOn) {
                  log.debug(
                      "Could not find the Throttle Context for domain-Based "
                          + "Throttling for domain name "
                          + domain
                          + " Throttling for this "
                          + "domain name may not be configured from policy");
                }
              }
            }
          }
        } else {
          if (debugOn) {
            log.debug("Could not find the domain of the caller - IP-based throttling may occur");
          }
        }

        // IP based throttling - Only if there is no configuration for caller domain name

        if (callerId == null) {
          String ip = (String) messageContext.getProperty(MessageContext.REMOTE_ADDR);
          if (ip != null) {
            // loads IP based throttle context
            ThrottleContext context =
                throttle.getThrottleContext(ThrottleConstants.IP_BASED_THROTTLE_KEY);
            if (context != null) {
              // Loads the ThrottleConfiguration
              ThrottleConfiguration config = context.getThrottleConfiguration();
              if (config != null) {
                // check for configuration for this ip
                callerId = config.getConfigurationKeyOfCaller(ip);
                if (callerId != null) {
                  // for clustered env.
                  if (isClusteringEnable) {
                    context.setConfigurationContext(cc);
                    context.setThrottleId(throttleId);
                  }
                  AccessInformation infor =
                      accessRateController.canAccess(context, callerId, ThrottleConstants.IP_BASE);
                  // check for the permission for access
                  StatCollector.collect(infor, ip, ThrottleConstants.IP_BASE);
                  if (!infor.isAccessAllowed()) {

                    // In the case of both of concurrency throttling and
                    // rate based throttling have enabled ,
                    // if the access rate less than maximum concurrent access ,
                    // then it is possible to occur death situation.To avoid that reset,
                    // if the access has denied by rate based throttling
                    if (cac != null) {
                      cac.incrementAndGet();
                      // set back if this is a clustered env
                      if (isClusteringEnable) {
                        cc.setProperty(key, cac);
                        // replicate the current state of ConcurrentAccessController
                        try {
                          if (debugOn) {
                            log.debug(
                                "Going to replicates the "
                                    + "states of the ConcurrentAccessController"
                                    + " with key : "
                                    + key);
                          }
                          Replicator.replicate(cc, new String[] {key});
                        } catch (ClusteringFault clusteringFault) {
                          log.error("Error during replicating states ", clusteringFault);
                        }
                      }
                    }
                    throw new AxisFault(
                        " Access deny for a "
                            + "caller with IP "
                            + ip
                            + " "
                            + " : Reason : "
                            + infor.getFaultReason());
                  }
                }
              }
            } else {
              if (debugOn) {
                log.debug("Could not find the throttle Context for IP-Based throttling");
              }
            }
          } else {
            if (debugOn) {
              log.debug(
                  "Could not find the IP address of the caller " + "- throttling will not occur");
            }
          }
        }
      }
      // all the replication functionality of the access rate based throttling handles by itself
      // just replicate the current state of ConcurrentAccessController
      if (isClusteringEnable && cac != null) {
        try {
          if (debugOn) {
            log.debug(
                "Going to replicates the states of the ConcurrentAccessController"
                    + " with key : "
                    + key);
          }
          Replicator.replicate(cc, new String[] {key});
        } catch (ClusteringFault clusteringFault) {
          log.error("Error during replicating states ", clusteringFault);
        }
      }

      // finally engage rolebased access throttling if available
      doRoleBasedAccessThrottling(throttle, messageContext, isClusteringEnable);
    } else {
      // replicate the current state of ConcurrentAccessController
      if (isClusteringEnable) {
        try {
          if (debugOn) {
            log.debug(
                "Going to replicates the states of the ConcurrentAccessController"
                    + " with key : "
                    + key);
          }
          Replicator.replicate(cc, new String[] {key});
        } catch (ClusteringFault clusteringFault) {
          log.error("Error during replicating states ", clusteringFault);
        }
      }
      throw new AxisFault(
          "Access has currently been denied since " + " maximum concurrent access have exceeded");
    }
  }