Reuse APIChecker adapter interface for APi Rate limit checking and optimize 
ApiRateLimitService interface.

Signed-off-by: Min Chen <min.c...@citrix.com>


Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/4355d06a
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/4355d06a
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/4355d06a

Branch: refs/heads/add_remove_nics
Commit: 4355d06a869ce1a745bfc483f7bd0fc246cdf2c5
Parents: ec3dd71 7f1486e
Author: Min Chen <min.c...@citrix.com>
Authored: Tue Jan 15 15:53:19 2013 -0800
Committer: Min Chen <min.c...@citrix.com>
Committed: Tue Jan 15 15:53:19 2013 -0800

----------------------------------------------------------------------
 .../com/cloud/exception/RequestLimitException.java |   43 ++++
 api/src/com/cloud/network/NetworkService.java      |    2 +
 api/src/com/cloud/user/DomainService.java          |    2 +
 api/src/org/apache/cloudstack/acl/APIChecker.java  |   11 +-
 .../apache/cloudstack/api/ResponseGenerator.java   |    7 -
 .../api/command/user/vm/DeployVMCmd.java           |   17 ++-
 .../api/command/user/volume/ExtractVolumeCmd.java  |    2 +-
 client/bindir/cloud-setup-management.in            |   10 +-
 client/tomcatconf/api-limit_commands.properties.in |   24 ---
 .../cisconexusvsm_commands.properties.in           |   25 ---
 client/tomcatconf/commands-ext.properties.in       |   28 ---
 client/tomcatconf/commands.properties.in           |   89 ++++++++
 client/tomcatconf/components.xml.in                |    8 +-
 client/tomcatconf/f5bigip_commands.properties.in   |   32 ---
 .../tomcatconf/junipersrx_commands.properties.in   |   30 ---
 client/tomcatconf/netapp_commands.properties.in    |   33 ---
 .../netscalerloadbalancer_commands.properties.in   |   26 ---
 .../tomcatconf/nicira-nvp_commands.properties.in   |   29 ---
 client/tomcatconf/simulator_commands.properties.in |   19 --
 .../virtualrouter_commands.properties.in           |   24 ---
 .../acl/StaticRoleBasedAPIAccessChecker.java       |   52 +++---
 .../api/command/user/discovery/ListApisCmd.java    |   10 +-
 .../cloudstack/discovery/ApiDiscoveryService.java  |    4 +-
 .../discovery/ApiDiscoveryServiceImpl.java         |  116 +++++------
 .../command/admin/ratelimit/ResetApiLimitCmd.java  |    2 +-
 .../api/command/user/ratelimit/GetApiLimitCmd.java |    3 +-
 .../cloudstack/ratelimit/ApiRateLimitService.java  |    9 +-
 .../ratelimit/ApiRateLimitServiceImpl.java         |   65 ++++---
 .../cloudstack/ratelimit/ApiRateLimitTest.java     |   72 ++++---
 .../hypervisor/kvm/resource/KVMGuestOsMapper.java  |    2 +
 .../kvm/resource/LibvirtComputingResource.java     |    5 +-
 .../server/ManagementServerSimulatorImpl.java      |   11 +-
 .../network/element/CiscoNexusVSMElement.java      |   11 +-
 .../xen/resource/CitrixResourceBase.java           |    6 +-
 .../element/F5ExternalLoadBalancerElement.java     |   15 +-
 .../element/JuniperSRXExternalFirewallElement.java |   14 +-
 .../network/element/MidokuraMidonetElement.java    |    9 +-
 .../cloud/network/element/NetscalerElement.java    |   12 +-
 .../cloud/network/element/NiciraNvpElement.java    |   10 +-
 pom.xml                                            |    2 +
 python/lib/cloudutils/utilities.py                 |    6 +-
 server/src/com/cloud/api/ApiDispatcher.java        |    6 -
 server/src/com/cloud/api/ApiResponseHelper.java    |    5 -
 server/src/com/cloud/api/ApiServer.java            |   90 +++------
 server/src/com/cloud/api/ApiServlet.java           |   45 ++---
 server/src/com/cloud/configuration/Config.java     |    5 +-
 .../configuration/ConfigurationManagerImpl.java    |   54 +++---
 .../consoleproxy/ConsoleProxyManagerImpl.java      |   35 ++--
 .../com/cloud/hypervisor/HypervisorGuruBase.java   |    1 +
 .../src/com/cloud/network/NetworkManagerImpl.java  |    6 +
 .../network/element/VirtualRouterElement.java      |   10 +-
 .../router/VirtualNetworkApplianceManagerImpl.java |    4 +-
 .../com/cloud/server/ManagementServerExtImpl.java  |    6 +-
 .../src/com/cloud/server/ManagementServerImpl.java |    7 +-
 server/src/com/cloud/user/DomainManagerImpl.java   |    5 +
 server/test/com/cloud/api/ListPerfTest.java        |   22 ++-
 .../com/cloud/network/MockNetworkManagerImpl.java  |    6 +
 .../test/com/cloud/user/MockDomainManagerImpl.java |    6 +
 .../test/com/cloud/vpc/MockNetworkManagerImpl.java |    6 +
 setup/db/db/schema-40to410.sql                     |    2 -
 setup/db/templates.kvm.sql                         |   11 +-
 setup/db/templates.sql                             |    7 +
 tools/apidoc/gen_toc.py                            |    1 +
 tools/apidoc/pom.xml                               |    2 +-
 tools/devcloud-kvm/devcloud-kvm-advanced.cfg       |  115 +++++++++++
 tools/devcloud-kvm/devcloud-kvm.py                 |  157 +++++++++++++++
 tools/devcloud-kvm/kvm.properties                  |   62 ++++++
 tools/devcloud/devcloud-advanced.cfg               |  118 +++++++++++
 tools/devcloud/devcloud-advanced_internal-mgt.cfg  |  124 ++++++++++++
 tools/marvin/marvin/configGenerator.py             |    3 +-
 tools/marvin/marvin/deployDataCenter.py            |   10 +-
 tools/marvin/marvin/integration/lib/base.py        |    3 +
 .../marvin/marvin/sandbox/advanced/advanced_env.py |   18 ++-
 .../marvin/sandbox/advanced/setup.properties       |   16 +-
 ui/scripts/network.js                              |   10 +-
 ui/scripts/sharedFunctions.js                      |    3 +-
 .../cloud/utils/component/PluggableService.java    |    3 +-
 utils/src/com/cloud/utils/db/GenericDao.java       |    2 +-
 utils/src/com/cloud/utils/db/GenericDaoBase.java   |    2 +-
 .../utils/exception/CSExceptionErrorCode.java      |    1 +
 80 files changed, 1209 insertions(+), 677 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/api/src/com/cloud/exception/RequestLimitException.java
----------------------------------------------------------------------
diff --cc api/src/com/cloud/exception/RequestLimitException.java
index 0000000,0000000..0142f8e
new file mode 100644
--- /dev/null
+++ b/api/src/com/cloud/exception/RequestLimitException.java
@@@ -1,0 -1,0 +1,43 @@@
++// Licensed to the Apache Software Foundation (ASF) under one
++// or more contributor license agreements.  See the NOTICE file
++// distributed with this work for additional information
++// regarding copyright ownership.  The ASF licenses this file
++// to you 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 com.cloud.exception;
++
++import com.cloud.utils.SerialVersionUID;
++import com.cloud.utils.exception.CloudRuntimeException;
++
++/**
++ * Exception thrown if number of requests is over api rate limit set.
++ * @author minc
++ *
++ */
++public class RequestLimitException extends CloudRuntimeException {
++
++    private static final long serialVersionUID = 
SerialVersionUID.AccountLimitException;
++
++    protected RequestLimitException() {
++        super();
++    }
++
++    public RequestLimitException(String msg) {
++        super(msg);
++    }
++
++    public RequestLimitException(String msg, Throwable cause) {
++        super(msg, cause);
++    }
++
++}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/api/src/org/apache/cloudstack/acl/APIChecker.java
----------------------------------------------------------------------
diff --cc api/src/org/apache/cloudstack/acl/APIChecker.java
index 61dd7de,0d0dfd1..2e2b73b
--- a/api/src/org/apache/cloudstack/acl/APIChecker.java
+++ b/api/src/org/apache/cloudstack/acl/APIChecker.java
@@@ -16,13 -16,15 +16,16 @@@
  // under the License.
  package org.apache.cloudstack.acl;
  
- import org.apache.cloudstack.acl.RoleType;
+ import com.cloud.exception.PermissionDeniedException;
++import com.cloud.exception.RequestLimitException;
+ import com.cloud.user.User;
  import com.cloud.utils.component.Adapter;
  
  // APIChecker checks the ownership and access control to API requests
  public interface APIChecker extends Adapter {
      // Interface for checking access for a role using apiname
-     boolean checkAccess(RoleType roleType, String apiCommandName);
-     // Interface for checking existence of an api by name
-     boolean checkExistence(String apiCommandName);
+     // If true, apiChecker has checked the operation
+     // If false, apiChecker is unable to handle the operation or not 
implemented
+     // On exception, checkAccess failed don't allow
 -    boolean checkAccess(User user, String apiCommandName) throws 
PermissionDeniedException;
++    boolean checkAccess(User user, String apiCommandName) throws 
PermissionDeniedException, RequestLimitException;
  }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/client/tomcatconf/commands.properties.in
----------------------------------------------------------------------
diff --cc client/tomcatconf/commands.properties.in
index 3872f2a,99cb874..db722ad
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@@ -424,3 -424,87 +424,92 @@@ resetVpnConnection=1
  listVpnCustomerGateways=15
  listVpnGateways=15
  listVpnConnections=15
+ 
+ #### router commands
+ createVirtualRouterElement=7
+ configureVirtualRouterElement=7
+ listVirtualRouterElements=7
+ 
+ #### usage commands
+ generateUsageRecords=1
+ listUsageRecords=1
+ listUsageTypes=1
+ 
+ #### traffic monitor commands
+ addTrafficMonitor=1
+ deleteTrafficMonitor=1
+ listTrafficMonitors=1
+ 
+ #### Cisco Nexus 1000v Virtual Supervisor Module (VSM) commands
+ deleteCiscoNexusVSM=1
+ enableCiscoNexusVSM=1
+ disableCiscoNexusVSM=1
+ listCiscoNexusVSMs=1
+ 
+ #### f5 big ip load balancer commands
+ 
+ #Deprecated commands
+ addExternalLoadBalancer=1
+ deleteExternalLoadBalancer=1
+ listExternalLoadBalancers=1
+ 
+ addF5LoadBalancer=1
+ configureF5LoadBalancer=1
+ deleteF5LoadBalancer=1
+ listF5LoadBalancers=1
+ listF5LoadBalancerNetworks=1
+ 
+ #### juniper srx firewall commands
+ addExternalFirewall=1
+ deleteExternalFirewall=1
+ listExternalFirewalls=1
+ 
+ addSrxFirewall=1
+ deleteSrxFirewall=1
+ configureSrxFirewall=1
+ listSrxFirewalls=1
+ listSrxFirewallNetworks=1
+ 
+ ####Netapp integration commands
+ createVolumeOnFiler=15
+ destroyVolumeOnFiler=15
+ listVolumesOnFiler=15
+ createLunOnFiler=15
+ destroyLunOnFiler=15
+ listLunsOnFiler=15
+ associateLun=15
+ dissociateLun=15
+ createPool=15
+ deletePool=15
+ modifyPool=15
+ listPools=15
+ 
+ #### netscaler load balancer commands
+ addNetscalerLoadBalancer=1
+ deleteNetscalerLoadBalancer=1
+ configureNetscalerLoadBalancer=1
+ listNetscalerLoadBalancers=1
+ listNetscalerLoadBalancerNetworks=1
+ 
+ #### nicira nvp commands
+ 
+ addNiciraNvpDevice=1
+ deleteNiciraNvpDevice=1
+ listNiciraNvpDevices=1
+ listNiciraNvpDeviceNetworks=1
+ 
+ # Not implemented (yet)
+ #configureNiciraNvpDevice=1
+ 
+ #### host simulator commands
+ 
+ configureSimulator=1
+ 
+ #### api discovery commands
+ 
+ listApis=15
++
++#### API Rate Limit service command
++
++getApiLimit=15
++resetApiLimit=1

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/client/tomcatconf/components.xml.in
----------------------------------------------------------------------
diff --cc client/tomcatconf/components.xml.in
index 630bd97,bb39839..e19b418
--- a/client/tomcatconf/components.xml.in
+++ b/client/tomcatconf/components.xml.in
@@@ -54,15 -54,8 +54,13 @@@ under the License
               <param name="premium">true</param>
          </dao>
          <adapters key="org.apache.cloudstack.acl.APIChecker">
-             <adapter name="StaticRoleBasedAPIAccessChecker" 
class="org.apache.cloudstack.acl.StaticRoleBasedAPIAccessChecker"/>
-         </adapters>
-         <adapters key="org.apache.cloudstack.acl.APILimitChecker">
 +            <adapter name="AccountBasedAPIRateLimit" 
class="org.apache.cloudstack.ratelimit.ApiRateLimitServiceImpl" 
singleton="true">
 +                 <param name="api.throttling.interval">1</param>
 +                 <param name="api.throttling.max">25</param>
 +                 <param name="api.throttling.cachesize">50000</param>
-             </adapter>
-         </adapters>        
++            </adapter>        
+             <adapter name="StaticRoleBasedAPIAccessChecker" 
class="org.apache.cloudstack.acl.StaticRoleBasedAPIAccessChecker"/>
+         </adapters>
          <adapters key="com.cloud.agent.manager.allocator.HostAllocator">
              <adapter name="FirstFitRouting" 
class="com.cloud.agent.manager.allocator.impl.FirstFitRoutingAllocator"/>
              <!--adapter name="FirstFitRouting" 
class="com.cloud.agent.manager.allocator.impl.RecreateHostAllocator"/-->

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/admin/ratelimit/ResetApiLimitCmd.java
----------------------------------------------------------------------
diff --cc 
plugins/api/rate-limit/src/org/apache/cloudstack/api/command/admin/ratelimit/ResetApiLimitCmd.java
index 3c612fa,0000000..771b63a
mode 100644,000000..100644
--- 
a/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/admin/ratelimit/ResetApiLimitCmd.java
+++ 
b/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/admin/ratelimit/ResetApiLimitCmd.java
@@@ -1,94 -1,0 +1,94 @@@
 +// Licensed to the Apache Software Foundation (ASF) under one
 +// or more contributor license agreements.  See the NOTICE file
 +// distributed with this work for additional information
 +// regarding copyright ownership.  The ASF licenses this file
 +// to you 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.cloudstack.api.command.admin.ratelimit;
 +
 +import org.apache.cloudstack.api.*;
 +import org.apache.cloudstack.api.response.AccountResponse;
 +import org.apache.cloudstack.api.response.ApiLimitResponse;
 +import org.apache.cloudstack.api.response.SuccessResponse;
 +import org.apache.log4j.Logger;
 +
 +import org.apache.cloudstack.api.APICommand;
 +import org.apache.cloudstack.ratelimit.ApiRateLimitService;
 +
 +import com.cloud.user.Account;
 +import com.cloud.user.UserContext;
 +
 +@APICommand(name = "resetApiLimit", responseObject=ApiLimitResponse.class, 
description="Reset api count")
 +public class ResetApiLimitCmd extends BaseCmd {
 +    private static final Logger s_logger = 
Logger.getLogger(ResetApiLimitCmd.class.getName());
 +
 +    private static final String s_name = "resetapilimitresponse";
 +
 +    @PlugService
 +    ApiRateLimitService _apiLimitService;
 +
 +    /////////////////////////////////////////////////////
 +    //////////////// API parameters /////////////////////
 +    /////////////////////////////////////////////////////
 +
 +    @ACL
 +    @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.UUID, 
entityType=AccountResponse.class,
 +            description="the ID of the acount whose limit to be reset")
 +    private Long accountId;
 +
 +    /////////////////////////////////////////////////////
 +    /////////////////// Accessors ///////////////////////
 +    /////////////////////////////////////////////////////
 +
 +
 +    public Long getAccountId() {
 +        return accountId;
 +    }
 +
 +
 +    public void setAccountId(Long accountId) {
 +        this.accountId = accountId;
 +    }
 +
 +
 +    /////////////////////////////////////////////////////
 +    /////////////// API Implementation///////////////////
 +    /////////////////////////////////////////////////////
 +
 +
 +    @Override
 +    public String getCommandName() {
 +        return s_name;
 +    }
 +
 +    @Override
 +    public long getEntityOwnerId() {
 +        Account account = UserContext.current().getCaller();
 +        if (account != null) {
 +            return account.getId();
 +        }
 +
 +        return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent 
this command to SYSTEM so ERROR events are tracked
 +    }
 +
 +    @Override
 +    public void execute(){
-         boolean result = _apiLimitService.resetApiLimit(this);
++        boolean result = _apiLimitService.resetApiLimit(this.accountId);
 +        if (result) {
 +            SuccessResponse response = new SuccessResponse(getCommandName());
 +            this.setResponseObject(response);
 +        } else {
 +            throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to 
reset api limit counter");
 +        }
 +    }
 +}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java
----------------------------------------------------------------------
diff --cc 
plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java
index 0397fa8,0000000..ad1fb28
mode 100644,000000..100644
--- 
a/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java
+++ 
b/plugins/api/rate-limit/src/org/apache/cloudstack/api/command/user/ratelimit/GetApiLimitCmd.java
@@@ -1,87 -1,0 +1,88 @@@
 +// Licensed to the Apache Software Foundation (ASF) under one
 +// or more contributor license agreements.  See the NOTICE file
 +// distributed with this work for additional information
 +// regarding copyright ownership.  The ASF licenses this file
 +// to you 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.cloudstack.api.command.user.ratelimit;
 +
 +import java.util.ArrayList;
 +import java.util.List;
 +
 +import org.apache.cloudstack.api.ACL;
 +import org.apache.cloudstack.api.ApiConstants;
 +import org.apache.cloudstack.api.BaseCmd;
 +import org.apache.cloudstack.api.BaseListCmd;
 +import org.apache.cloudstack.api.Parameter;
 +import org.apache.cloudstack.api.PlugService;
 +import org.apache.cloudstack.api.ServerApiException;
 +import org.apache.cloudstack.api.BaseCmd.CommandType;
 +import org.apache.cloudstack.api.command.admin.ratelimit.ResetApiLimitCmd;
 +import org.apache.cloudstack.api.response.AccountResponse;
 +import org.apache.cloudstack.api.response.ApiLimitResponse;
 +import org.apache.cloudstack.api.response.PhysicalNetworkResponse;
 +import org.apache.log4j.Logger;
 +
 +import org.apache.cloudstack.api.APICommand;
 +import org.apache.cloudstack.api.response.ListResponse;
 +import org.apache.cloudstack.ratelimit.ApiRateLimitService;
 +import com.cloud.exception.ConcurrentOperationException;
 +import com.cloud.exception.InsufficientCapacityException;
 +import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.exception.ResourceAllocationException;
 +import com.cloud.exception.ResourceUnavailableException;
 +import com.cloud.user.Account;
 +import com.cloud.user.UserContext;
 +import com.cloud.utils.exception.CloudRuntimeException;
 +
 +@APICommand(name = "getApiLimit", responseObject=ApiLimitResponse.class, 
description="Get API limit count for the caller")
 +public class GetApiLimitCmd extends BaseListCmd {
 +    private static final Logger s_logger = 
Logger.getLogger(GetApiLimitCmd.class.getName());
 +
 +    private static final String s_name = "getapilimitresponse";
 +
 +    @PlugService
 +    ApiRateLimitService _apiLimitService;
 +
 +
 +
 +
 +    /////////////////////////////////////////////////////
 +    /////////////// API Implementation///////////////////
 +    /////////////////////////////////////////////////////
 +
 +    @Override
 +    public String getCommandName() {
 +        return s_name;
 +    }
 +
 +    @Override
 +    public long getEntityOwnerId() {
 +        Account account = UserContext.current().getCaller();
 +        if (account != null) {
 +            return account.getId();
 +        }
 +
 +        return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent 
this command to SYSTEM so ERROR events are tracked
 +    }
 +
 +    @Override
 +    public void execute(){
-         ApiLimitResponse response = _apiLimitService.searchApiLimit(this);
++        Account caller = UserContext.current().getCaller();
++        ApiLimitResponse response = _apiLimitService.searchApiLimit(caller);
 +        response.setResponseName(getCommandName());
 +        this.setResponseObject(response);
 +    }
 +}
 +
 +

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitService.java
----------------------------------------------------------------------
diff --cc 
plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitService.java
index 8c9d49b,0000000..c5b7150
mode 100644,000000..100644
--- 
a/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitService.java
+++ 
b/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitService.java
@@@ -1,40 -1,0 +1,37 @@@
 +// Licensed to the Apache Software Foundation (ASF) under one
 +// or more contributor license agreements.  See the NOTICE file
 +// distributed with this work for additional information
 +// regarding copyright ownership.  The ASF licenses this file
 +// to you 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.cloudstack.ratelimit;
 +
- import org.apache.cloudstack.api.command.admin.ratelimit.ResetApiLimitCmd;
- import org.apache.cloudstack.api.command.user.ratelimit.GetApiLimitCmd;
 +import org.apache.cloudstack.api.response.ApiLimitResponse;
- import org.apache.cloudstack.api.response.ListResponse;
- 
++import com.cloud.user.Account;
 +import com.cloud.utils.component.PluggableService;
 +
 +/**
 + * Provide API rate limit service
 + * @author minc
 + *
 + */
 +public interface ApiRateLimitService extends PluggableService{
 +
-     public ApiLimitResponse searchApiLimit(GetApiLimitCmd cmd);
++    public ApiLimitResponse searchApiLimit(Account caller);
 +
-     public boolean resetApiLimit(ResetApiLimitCmd cmd);
++    public boolean resetApiLimit(Long accountId);
 +
 +    public void setTimeToLive(int timeToLive);
 +
 +    public void setMaxAllowed(int max);
 +}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java
----------------------------------------------------------------------
diff --cc 
plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java
index 00f39af,0000000..1e9b9ad
mode 100644,000000..100644
--- 
a/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java
+++ 
b/plugins/api/rate-limit/src/org/apache/cloudstack/ratelimit/ApiRateLimitServiceImpl.java
@@@ -1,181 -1,0 +1,196 @@@
 +// Licensed to the Apache Software Foundation (ASF) under one
 +// or more contributor license agreements.  See the NOTICE file
 +// distributed with this work for additional information
 +// regarding copyright ownership.  The ASF licenses this file
 +// to you 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.cloudstack.ratelimit;
 +
++import java.util.ArrayList;
++import java.util.List;
 +import java.util.Map;
 +import javax.ejb.Local;
 +import javax.naming.ConfigurationException;
 +
 +import net.sf.ehcache.Cache;
 +import net.sf.ehcache.CacheManager;
 +
 +import org.apache.log4j.Logger;
 +
- import org.apache.cloudstack.acl.APILimitChecker;
- import org.apache.cloudstack.api.BaseCmd;
- import org.apache.cloudstack.api.ServerApiException;
++import org.apache.cloudstack.acl.APIChecker;
 +import org.apache.cloudstack.api.command.admin.ratelimit.ResetApiLimitCmd;
 +import org.apache.cloudstack.api.command.user.ratelimit.GetApiLimitCmd;
 +import org.apache.cloudstack.api.response.ApiLimitResponse;
++
++import com.cloud.exception.PermissionDeniedException;
++import com.cloud.exception.RequestLimitException;
 +import com.cloud.user.Account;
- import com.cloud.user.UserContext;
- import com.cloud.utils.PropertiesUtil;
++import com.cloud.user.AccountService;
++import com.cloud.user.User;
 +import com.cloud.utils.component.AdapterBase;
++import com.cloud.utils.component.Inject;
 +
- @Local(value = APILimitChecker.class)
- public class ApiRateLimitServiceImpl extends AdapterBase implements 
APILimitChecker, ApiRateLimitService {
++@Local(value = APIChecker.class)
++public class ApiRateLimitServiceImpl extends AdapterBase implements 
APIChecker, ApiRateLimitService {
 +      private static final Logger s_logger = 
Logger.getLogger(ApiRateLimitServiceImpl.class);
 +
 +      /**
 +       * Fixed time duration where api rate limit is set, in seconds
 +       */
 +      private int timeToLive = 1;
 +
 +      /**
 +       * Max number of api requests during timeToLive duration.
 +       */
 +      private int maxAllowed = 30;
 +
 +      private LimitStore _store = null;
 +
++      @Inject
++      AccountService _accountService;
++
++
 +
 +      @Override
 +    public boolean configure(String name, Map<String, Object> params) throws 
ConfigurationException {
 +        super.configure(name, params);
 +
 +        if (_store == null) {
 +            // not configured yet, note that since this class is both adapter
 +            // and pluggableService, so this method
 +            // may be invoked twice in ComponentLocator.
 +            // get global configured duration and max values
 +            Object duration = params.get("api.throttling.interval");
 +            if (duration != null) {
 +                timeToLive = Integer.parseInt((String) duration);
 +            }
 +            Object maxReqs = params.get("api.throttling.max");
 +            if (maxReqs != null) {
 +                maxAllowed = Integer.parseInt((String) maxReqs);
 +            }
 +            // create limit store
 +            EhcacheLimitStore cacheStore = new EhcacheLimitStore();
 +            int maxElements = 10000;
 +            Object cachesize = params.get("api.throttling.cachesize");
 +            if ( cachesize != null ){
 +                maxElements = Integer.parseInt((String)cachesize);
 +            }
 +            CacheManager cm = CacheManager.create();
 +            Cache cache = new Cache("api-limit-cache", maxElements, false, 
false, timeToLive, timeToLive);
 +            cm.addCache(cache);
-             s_logger.info("Limit Cache created: " + cache.toString());
++            s_logger.info("Limit Cache created with timeToLive=" + timeToLive 
+ ", maxAllowed=" + maxAllowed + ", maxElements=" + maxElements );
 +            cacheStore.setCache(cache);
 +            _store = cacheStore;
++
 +        }
 +
 +        return true;
 +
 +    }
 +
 +
 +
 +    @Override
-     public ApiLimitResponse searchApiLimit(GetApiLimitCmd cmd) {
-         Account caller = UserContext.current().getCaller();
++    public ApiLimitResponse searchApiLimit(Account caller) {
 +        ApiLimitResponse response = new ApiLimitResponse();
 +        response.setAccountId(caller.getUuid());
 +        response.setAccountName(caller.getAccountName());
 +        StoreEntry entry = _store.get(caller.getId());
 +        if (entry == null) {
 +
 +            /* Populate the entry, thus unlocking any underlying mutex */
 +            entry = _store.create(caller.getId(), timeToLive);
 +            response.setApiIssued(0);
 +            response.setApiAllowed(maxAllowed);
 +            response.setExpireAfter(timeToLive);
 +        }
 +        else{
 +            response.setApiIssued(entry.getCounter());
 +            response.setApiAllowed(maxAllowed - entry.getCounter());
 +            response.setExpireAfter(entry.getExpireDuration());
 +        }
 +
 +        return response;
 +    }
 +
 +
 +
 +    @Override
-     public boolean resetApiLimit(ResetApiLimitCmd cmd) {
-         if ( cmd.getAccountId() != null ){
-             _store.create(cmd.getAccountId(), timeToLive);
++    public boolean resetApiLimit(Long accountId) {
++        if ( accountId != null ){
++            _store.create(accountId, timeToLive);
 +        }
 +        else{
 +            _store.resetCounters();
 +        }
 +        return true;
 +    }
 +
 +
-     @Override
-     public void checkLimit(Account account) throws ServerApiException {
 +
-         Long accountId = account.getId();
++    @Override
++    public boolean checkAccess(User user, String apiCommandName) throws 
PermissionDeniedException, RequestLimitException {
++        Long accountId = user.getAccountId();
++        Account account = _accountService.getAccount(accountId);
++        if ( _accountService.isRootAdmin(account.getType())){
++            // no API throttling on root admin
++            return true;
++        }
 +        StoreEntry entry = _store.get(accountId);
 +
 +        if (entry == null) {
 +
 +            /* Populate the entry, thus unlocking any underlying mutex */
 +            entry = _store.create(accountId, timeToLive);
 +        }
 +
 +        /* Increment the client count and see whether we have hit the maximum 
allowed clients yet. */
 +        int current = entry.incrementAndGet();
 +
 +        if (current <= maxAllowed) {
-             return;
++            s_logger.info("current count = " + current);
++            return true;
 +        } else {
 +            long expireAfter = entry.getExpireDuration();
-             s_logger.warn("The given user has reached his/her account api 
limit, please retry after " + expireAfter + " ms.");
-             throw new ServerApiException(BaseCmd.API_LIMIT_EXCEED, "The given 
user has reached his/her account api limit, please retry after " +
-                     expireAfter + " ms.");
++            // for this exception, we can just show the same message to user 
and admin users.
++            String msg = "The given user has reached his/her account api 
limit, please retry after " + expireAfter + " ms.";
++            s_logger.warn(msg);
++            throw new RequestLimitException(msg);
 +        }
 +    }
 +
 +
- 
 +    @Override
-     public Map<String, String> getProperties() {
-         return PropertiesUtil.processConfigFile(new String[]
-                 { "api-limit_commands.properties" });
++    public List<Class<?>> getCommands() {
++        List<Class<?>> cmdList = new ArrayList<Class<?>>();
++        cmdList.add(ResetApiLimitCmd.class);
++        cmdList.add(GetApiLimitCmd.class);
++        return cmdList;
 +    }
 +
 +
- 
 +    @Override
 +    public void setTimeToLive(int timeToLive) {
 +        this.timeToLive = timeToLive;
 +    }
 +
 +
 +
 +    @Override
 +    public void setMaxAllowed(int max) {
 +        this.maxAllowed = max;
 +
 +    }
 +
 +
 +}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/ApiRateLimitTest.java
----------------------------------------------------------------------
diff --cc 
plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/ApiRateLimitTest.java
index ef3cf6d,0000000..850182d
mode 100644,000000..100644
--- 
a/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/ApiRateLimitTest.java
+++ 
b/plugins/api/rate-limit/test/org/apache/cloudstack/ratelimit/ApiRateLimitTest.java
@@@ -1,214 -1,0 +1,226 @@@
 +// Licensed to the Apache Software Foundation (ASF) under one
 +// or more contributor license agreements.  See the NOTICE file
 +// distributed with this work for additional information
 +// regarding copyright ownership.  The ASF licenses this file
 +// to you 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.cloudstack.ratelimit;
 +
 +import java.util.Collections;
 +import java.util.concurrent.CountDownLatch;
 +import java.util.concurrent.ExecutorService;
 +import java.util.concurrent.Executors;
 +
 +import javax.naming.ConfigurationException;
 +
- import org.apache.cloudstack.api.ServerApiException;
- import org.apache.cloudstack.api.command.admin.ratelimit.ResetApiLimitCmd;
- import org.apache.cloudstack.api.command.user.ratelimit.GetApiLimitCmd;
 +import org.apache.cloudstack.api.response.ApiLimitResponse;
 +import org.apache.cloudstack.ratelimit.ApiRateLimitServiceImpl;
 +import org.junit.Before;
 +import org.junit.BeforeClass;
 +import org.junit.Test;
 +
- import com.cloud.configuration.Config;
- import com.cloud.configuration.dao.ConfigurationDao;
++import com.cloud.exception.RequestLimitException;
 +import com.cloud.user.Account;
++import com.cloud.user.AccountService;
 +import com.cloud.user.AccountVO;
- import com.cloud.user.UserContext;
++import com.cloud.user.User;
++import com.cloud.user.UserVO;
 +
 +import static org.junit.Assert.*;
 +import static org.mockito.Mockito.*;
 +
 +public class ApiRateLimitTest {
 +
 +      static ApiRateLimitServiceImpl _limitService = new 
ApiRateLimitServiceImpl();
-       private static long acctIdSeq = 0L;
++      static AccountService _accountService = mock(AccountService.class);
++      private static long acctIdSeq = 5L;
++      private static Account testAccount;
 +
 +      @BeforeClass
 +      public static void setUp() throws ConfigurationException {
 +
 +              _limitService.configure("ApiRateLimitTest", 
Collections.<String, Object> emptyMap());
++
++          _limitService._accountService = _accountService;
++
++          // Standard responses
++          AccountVO acct = new AccountVO(acctIdSeq);
++          acct.setType(Account.ACCOUNT_TYPE_NORMAL);
++          acct.setAccountName("demo");
++          testAccount = acct;
++
++          when(_accountService.getAccount(5L)).thenReturn(testAccount);
++          
when(_accountService.isRootAdmin(Account.ACCOUNT_TYPE_NORMAL)).thenReturn(false);
 +      }
 +
++    @Before
++    public void testSetUp() {
++        // reset counter for each test
++        _limitService.resetApiLimit(null);
++    }
 +
-       private Account createFakeAccount(){
-           return new AccountVO(acctIdSeq++);
++      private User createFakeUser(){
++          UserVO user = new UserVO();
++          user.setAccountId(acctIdSeq);
++          return user;
 +      }
 +
-       private boolean isUnderLimit(Account key){
++      private boolean isUnderLimit(User key){
 +          try{
-               _limitService.checkLimit(key);
++             _limitService.checkAccess(key, null);
 +              return true;
 +          }
-           catch (ServerApiException ex){
++          catch (RequestLimitException ex){
 +              return false;
 +          }
 +      }
 +
 +    @Test
 +    public void sequentialApiAccess() {
 +        int allowedRequests = 1;
 +        _limitService.setMaxAllowed(allowedRequests);
 +        _limitService.setTimeToLive(1);
 +
-         Account key = createFakeAccount();
++        User key = createFakeUser();
 +        assertTrue("Allow for the first request", isUnderLimit(key));
 +
 +        assertFalse("Second request should be blocked, since we assume that 
the two api "
 +                + " accesses take less than a second to perform", 
isUnderLimit(key));
 +    }
 +
 +    @Test
 +    public void canDoReasonableNumberOfApiAccessPerSecond() throws Exception {
 +        int allowedRequests = 50000;
 +        _limitService.setMaxAllowed(allowedRequests);
 +        _limitService.setTimeToLive(1);
 +
-         Account key = createFakeAccount();
++        User key = createFakeUser();
 +
 +        for (int i = 0; i < allowedRequests; i++) {
-             assertTrue("We should allow " + allowedRequests + " requests per 
second", isUnderLimit(key));
++            assertTrue("We should allow " + allowedRequests + " requests per 
second, but failed at request " + i, isUnderLimit(key));
 +        }
 +
 +
 +        assertFalse("We should block >" + allowedRequests + " requests per 
second", isUnderLimit(key));
 +    }
 +
 +    @Test
 +    public void multipleClientsCanAccessWithoutBlocking() throws Exception {
 +        int allowedRequests = 200;
 +        _limitService.setMaxAllowed(allowedRequests);
 +        _limitService.setTimeToLive(1);
 +
 +
-         final Account key = createFakeAccount();
++        final User key = createFakeUser();
 +
 +        int clientCount = allowedRequests;
 +        Runnable[] clients = new Runnable[clientCount];
 +        final boolean[] isUsable = new boolean[clientCount];
 +
 +        final CountDownLatch startGate = new CountDownLatch(1);
 +
 +        final CountDownLatch endGate = new CountDownLatch(clientCount);
 +
 +
 +        for (int i = 0; i < isUsable.length; ++i) {
 +            final int j = i;
 +            clients[j] = new Runnable() {
 +
 +                /**
 +                 * {@inheritDoc}
 +                 */
 +                @Override
 +                public void run() {
 +                    try {
 +                        startGate.await();
 +
 +                        isUsable[j] = isUnderLimit(key);
 +
 +                    } catch (InterruptedException e) {
 +                        e.printStackTrace();
 +                    } finally {
 +                        endGate.countDown();
 +                    }
 +                }
 +            };
 +        }
 +
 +        ExecutorService executor = Executors.newFixedThreadPool(clientCount);
 +
 +        for (Runnable runnable : clients) {
 +            executor.execute(runnable);
 +        }
 +
 +        startGate.countDown();
 +
 +        endGate.await();
 +
 +        for (boolean b : isUsable) {
 +            assertTrue("Concurrent client request should be allowed within 
limit", b);
 +        }
 +    }
 +
 +    @Test
 +    public void expiryOfCounterIsSupported() throws Exception {
 +        int allowedRequests = 1;
 +        _limitService.setMaxAllowed(allowedRequests);
 +        _limitService.setTimeToLive(1);
 +
-         Account key = this.createFakeAccount();
++        User key = this.createFakeUser();
 +
 +        assertTrue("The first request should be allowed", isUnderLimit(key));
 +
 +        // Allow the token to expire
 +        Thread.sleep(1001);
 +
 +        assertTrue("Another request after interval should be allowed as 
well", isUnderLimit(key));
 +    }
 +
 +    @Test
 +    public void verifyResetCounters() throws Exception {
 +        int allowedRequests = 1;
 +        _limitService.setMaxAllowed(allowedRequests);
 +        _limitService.setTimeToLive(1);
 +
-         Account key = this.createFakeAccount();
++        User key = this.createFakeUser();
 +
 +        assertTrue("The first request should be allowed", isUnderLimit(key));
 +
 +        assertFalse("Another request should be blocked", isUnderLimit(key));
 +
-         ResetApiLimitCmd cmd = new ResetApiLimitCmd();
-         cmd.setAccountId(key.getId());
- 
-         _limitService.resetApiLimit(cmd);
++        _limitService.resetApiLimit(key.getAccountId());
 +
 +        assertTrue("Another request should be allowed after reset counter", 
isUnderLimit(key));
 +    }
 +
-     /* Disable this since I cannot mock Static method UserContext.current()
++
 +    @Test
 +    public void verifySearchCounter() throws Exception {
 +        int allowedRequests = 10;
 +        _limitService.setMaxAllowed(allowedRequests);
 +        _limitService.setTimeToLive(1);
 +
-         Account key = this.createFakeAccount();
++        User key = this.createFakeUser();
 +
 +        for ( int i = 0; i < 5; i++ ){
 +            assertTrue("Issued 5 requests", isUnderLimit(key));
 +        }
 +
-         GetApiLimitCmd cmd = new GetApiLimitCmd();
-         UserContext ctx = mock(UserContext.class);
-         when(UserContext.current().getCaller()).thenReturn(key);
-         ApiLimitResponse response = _limitService.searchApiLimit(cmd);
++        ApiLimitResponse response = _limitService.searchApiLimit(testAccount);
 +        assertEquals("apiIssued is incorrect", 5, response.getApiIssued());
 +        assertEquals("apiAllowed is incorrect", 5, response.getApiAllowed());
-         assertTrue("expiredAfter is incorrect", response.getExpireAfter() < 
1);
++        assertTrue("expiredAfter is incorrect", response.getExpireAfter() < 
1000);
 +
 +    }
-     */
++
 +}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/server/src/com/cloud/api/ApiServer.java
----------------------------------------------------------------------
diff --cc server/src/com/cloud/api/ApiServer.java
index 1d15acf,e106f03..72bed44
--- a/server/src/com/cloud/api/ApiServer.java
+++ b/server/src/com/cloud/api/ApiServer.java
@@@ -115,6 -114,6 +115,7 @@@ import com.cloud.event.EventUtils
  import com.cloud.exception.CloudAuthenticationException;
  import com.cloud.exception.InvalidParameterValueException;
  import com.cloud.exception.PermissionDeniedException;
++import com.cloud.exception.RequestLimitException;
  import com.cloud.server.ManagementServer;
  import com.cloud.user.Account;
  import com.cloud.user.AccountManager;
@@@ -555,15 -549,13 +553,18 @@@ public class ApiServer implements HttpR
              // if userId not null, that mean that user is logged in
              if (userId != null) {
                User user = ApiDBUtils.findUserById(userId);
-               if (apiThrottlingEnabled){
-                   // go through each API limit checker, throw exception 
inside adapter implementation so that message
-                   // can contain some detailed information only known for 
each adapter implementation.
-                   checkRequestLimit(user);
++
+               try{
+                   checkCommandAvailable(user, commandName);
                }
-                 if (!isCommandAvailable(user, commandName)) {
+               catch (PermissionDeniedException ex){
                      s_logger.debug("The given command:" + commandName + " 
does not exist or it is not available for user with id:" + userId);
                      throw new 
ServerApiException(BaseCmd.UNSUPPORTED_ACTION_ERROR, "The given command does 
not exist or it is not available for user");
                  }
++                catch (RequestLimitException ex){
++                    s_logger.debug(ex.getMessage());
++                    throw new ServerApiException(BaseCmd.API_LIMIT_EXCEED, 
ex.getMessage());
++                }
                  return true;
              } else {
                  // check against every available command to see if the 
command exists or not
@@@ -686,11 -680,11 +689,10 @@@
                  s_logger.info("User signature: " + signature + " is not 
equaled to computed signature: " + computedSignature);
              }
              return equalSig;
--        } catch (Exception ex) {
--            if (ex instanceof ServerApiException && ((ServerApiException) 
ex).getErrorCode() == BaseCmd.UNSUPPORTED_ACTION_ERROR) {
--                throw (ServerApiException) ex;
--            }
-             s_logger.error("unable to verifty request signature", ex);
 -            s_logger.error("unable to verify request signature", ex);
++        } catch (ServerApiException ex){
++            throw ex;
++        } catch (Exception ex){
++            s_logger.error("unable to verify request signature");
          }
          return false;
      }
@@@ -799,42 -786,14 +794,15 @@@
          return true;
      }
  
 +
-     private void checkRequestLimit(User user) throws ServerApiException {
-         Account account = ApiDBUtils.findAccountById(user.getAccountId());
-         if ( _accountMgr.isRootAdmin(account.getType()) ){
-             // no api throttling for root admin
-             return;
-         }
-         for (APILimitChecker apiChecker : _apiLimitCheckers) {
-             // Fail the checking if any checker fails to verify
-             apiChecker.checkLimit(account);
-          }
-     }
- 
- 
-     private boolean doesCommandExist(String apiName) {
-         for (APIChecker apiChecker : _apiAccessCheckers) {
-             // If any checker has api info on the command, return true
-             if (apiChecker.checkExistence(apiName))
-                 return true;
-         }
-         return false;
-     }
- 
-     private boolean isCommandAvailable(User user, String commandName) {
+     private void checkCommandAvailable(User user, String commandName) throws 
PermissionDeniedException {
          if (user == null) {
-             return false;
+             throw new PermissionDeniedException("User is null for role based 
API access check for command" + commandName);
          }
  
-         Account account = _accountMgr.getAccount(user.getAccountId());
-         RoleType roleType = _accountMgr.getRoleType(account);
          for (APIChecker apiChecker : _apiAccessCheckers) {
-             // Fail the checking if any checker fails to verify
-             if (!apiChecker.checkAccess(roleType, commandName))
-                 return false;
+             apiChecker.checkAccess(user, commandName);
          }
-         return true;
      }
  
      private Class<?> getCmdClass(String cmdName) {

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/server/src/com/cloud/api/ApiServlet.java
----------------------------------------------------------------------
diff --cc server/src/com/cloud/api/ApiServlet.java
index 19091f2,19091f2..21373cd
--- a/server/src/com/cloud/api/ApiServlet.java
+++ b/server/src/com/cloud/api/ApiServlet.java
@@@ -128,7 -128,7 +128,7 @@@ public class ApiServlet extends HttpSer
              reqStr = auditTrailSb.toString() + " " + req.getQueryString();
              s_logger.debug("===START=== " + StringUtils.cleanString(reqStr));
          }
--        
++
          try {
              HttpSession session = req.getSession(false);
              Object[] responseTypeParam = params.get("response");
@@@ -298,24 -298,24 +298,16 @@@
                   * params.put(BaseCmd.Properties.ACCOUNT_OBJ.getName(), new 
Object[] { accountObj }); } else {
                   * params.put(BaseCmd.Properties.USER_ID.getName(), new 
String[] { userId });
                   * params.put(BaseCmd.Properties.ACCOUNT_OBJ.getName(), new 
Object[] { accountObj }); } }
--                 * 
++                 *
                   * // update user context info here so that we can take 
information if the request is authenticated // via api
                   * key mechanism updateUserContext(params, session != null ? 
session.getId() : null);
                   */
  
--                auditTrailSb.insert(0,
--                        "(userId=" + UserContext.current().getCallerUserId() 
+ " accountId=" + UserContext.current().getCaller().getId() + " sessionId=" + 
(session != null ? session.getId() : null)
--                                + ")");
--
--                try {
--                    String response = _apiServer.handleRequest(params, false, 
responseType, auditTrailSb);
--                    writeResponse(resp, response != null ? response : "", 
HttpServletResponse.SC_OK, responseType);
--                } catch (ServerApiException se) {
--                    String serializedResponseText = 
_apiServer.getSerializedApiError(se.getErrorCode(), se.getDescription(), 
params, responseType, null);
--                    resp.setHeader("X-Description", se.getDescription());
--                    writeResponse(resp, serializedResponseText, 
se.getErrorCode(), responseType);
--                    auditTrailSb.append(" " + se.getErrorCode() + " " + 
se.getDescription());
--                }
++                auditTrailSb.insert(0, "(userId=" + 
UserContext.current().getCallerUserId() + " accountId="
++                        + UserContext.current().getCaller().getId() + " 
sessionId=" + (session != null ? session.getId() : null) + ")");
++
++                String response = _apiServer.handleRequest(params, false, 
responseType, auditTrailSb);
++                writeResponse(resp, response != null ? response : "", 
HttpServletResponse.SC_OK, responseType);
              } else {
                  if (session != null) {
                      try {
@@@ -329,17 -329,17 +321,14 @@@
                  writeResponse(resp, serializedResponse, 
HttpServletResponse.SC_UNAUTHORIZED, responseType);
  
              }
++        } catch (ServerApiException se) {
++            String serializedResponseText = 
_apiServer.getSerializedApiError(se.getErrorCode(), se.getDescription(), 
params, responseType, null);
++            resp.setHeader("X-Description", se.getDescription());
++            writeResponse(resp, serializedResponseText, se.getErrorCode(), 
responseType);
++            auditTrailSb.append(" " + se.getErrorCode() + " " + 
se.getDescription());
          } catch (Exception ex) {
--            if (ex instanceof ServerApiException && ((ServerApiException) 
ex).getErrorCode() == BaseCmd.UNSUPPORTED_ACTION_ERROR) {
--                ServerApiException se = (ServerApiException) ex;
--                String serializedResponseText = 
_apiServer.getSerializedApiError(se.getErrorCode(), se.getDescription(), 
params, responseType, null);
--                resp.setHeader("X-Description", se.getDescription());
--                writeResponse(resp, serializedResponseText, 
se.getErrorCode(), responseType);
--                auditTrailSb.append(" " + se.getErrorCode() + " " + 
se.getDescription());
--            } else {
--                s_logger.error("unknown exception writing api response", ex);
--                auditTrailSb.append(" unknown exception writing api 
response");
--            }
++            s_logger.error("unknown exception writing api response", ex);
++            auditTrailSb.append(" unknown exception writing api response");
          } finally {
              s_accessLogger.info(auditTrailSb.toString());
              if (s_logger.isDebugEnabled()) {
@@@ -354,9 -354,9 +343,9 @@@
       * private void updateUserContext(Map<String, Object[]> 
requestParameters, String sessionId) { String userIdStr =
       * 
(String)(requestParameters.get(BaseCmd.Properties.USER_ID.getName())[0]); 
Account accountObj =
       * 
(Account)(requestParameters.get(BaseCmd.Properties.ACCOUNT_OBJ.getName())[0]);
--     * 
++     *
       * Long userId = null; Long accountId = null; if(userIdStr != null) 
userId = Long.parseLong(userIdStr);
--     * 
++     *
       * if(accountObj != null) accountId = accountObj.getId(); 
UserContext.updateContext(userId, accountId, sessionId); }
       */
  
@@@ -386,7 -386,7 +375,7 @@@
      private String getLoginSuccessResponse(HttpSession session, String 
responseType) {
          StringBuffer sb = new StringBuffer();
          int inactiveInterval = session.getMaxInactiveInterval();
--        
++
          String user_UUID = (String)session.getAttribute("user_UUID");
          session.removeAttribute("user_UUID");
  

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --cc server/src/com/cloud/configuration/Config.java
index e6bf3d5,b91fbdd..59abd99
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@@ -358,11 -358,9 +358,8 @@@ public enum Config 
      DetailBatchQuerySize("Advanced", ManagementServer.class, Integer.class, 
"detail.batch.query.size", "2000", "Default entity detail batch query size for 
listing", null),
  
        ConcurrentSnapshotsThresholdPerHost("Advanced", ManagementServer.class, 
Long.class, "concurrent.snapshots.threshold.perhost",
-                       null, "Limits number of snapshots that can be handled 
by the host concurrently; default is NULL - unlimited", null),
- 
-       // API throttling
-     ApiLimitEnabled("Advanced", ManagementServer.class, Boolean.class, 
"api.throttling.enable", "true", "If true, api throttline feature is enabled", 
"true,false");
+                       null, "Limits number of snapshots that can be handled 
by the host concurrently; default is NULL - unlimited", null);
  
 -
        private final String _category;
        private final Class<?> _componentClass;
        private final Class<?> _type;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/server/test/com/cloud/api/ListPerfTest.java
----------------------------------------------------------------------
diff --cc server/test/com/cloud/api/ListPerfTest.java
index e5d277a,eb98d91..350dde8
--- a/server/test/com/cloud/api/ListPerfTest.java
+++ b/server/test/com/cloud/api/ListPerfTest.java
@@@ -16,14 -16,11 +16,18 @@@
  // under the License.
  package com.cloud.api;
  
++import static org.junit.Assert.*;
++
  import java.util.HashMap;
 +import java.util.concurrent.CountDownLatch;
 +import java.util.concurrent.ExecutorService;
 +import java.util.concurrent.Executors;
  
  import org.junit.Before;
  import org.junit.Test;
  
++import com.cloud.utils.exception.CloudRuntimeException;
++
  
  /**
   * Test fixture to do performance test for list command
@@@ -166,57 -163,6 +170,71 @@@ public class ListPerfTest extends APITe
  
      }
  
 +    @Test
 +    public void testMultiListAccounts() throws Exception {
 +        // log in using normal user
 +        login("demo", "password");
 +        // issue list Accounts calls
 +        final HashMap<String, String> params = new HashMap<String, String>();
 +        params.put("response", "json");
 +        params.put("listAll", "true");
 +        params.put("sessionkey", sessionKey);
-         int clientCount = 6;
++        // assuming ApiRateLimitService set api.throttling.max = 25
++        int clientCount = 26;
 +        Runnable[] clients = new Runnable[clientCount];
 +        final boolean[] isUsable = new boolean[clientCount];
 +
 +        final CountDownLatch startGate = new CountDownLatch(1);
 +
 +        final CountDownLatch endGate = new CountDownLatch(clientCount);
 +
 +
 +        for (int i = 0; i < isUsable.length; ++i) {
 +            final int j = i;
 +            clients[j] = new Runnable() {
  
 +                /**
 +                 * {@inheritDoc}
 +                 */
 +                @Override
 +                public void run() {
 +                    try {
 +                        startGate.await();
 +
-                         System.out.println(sendRequest("listAccounts", 
params));
++                        sendRequest("listAccounts", params);
 +
++                        isUsable[j] = true;
++
++                    } catch (CloudRuntimeException e){
++                        isUsable[j] = false;
++                        e.printStackTrace();
 +                    } catch (InterruptedException e) {
 +                        e.printStackTrace();
 +                    } finally {
 +                        endGate.countDown();
 +                    }
 +                }
 +            };
 +        }
 +
 +        ExecutorService executor = Executors.newFixedThreadPool(clientCount);
 +
 +        for (Runnable runnable : clients) {
 +            executor.execute(runnable);
 +        }
 +
 +        startGate.countDown();
 +
 +        endGate.await();
 +
++        int rejectCount = 0;
++        for ( int i = 0; i < isUsable.length; ++i){
++            if ( !isUsable[i])
++                rejectCount++;
++        }
++
++        assertEquals("Only one request should be rejected!", 1, rejectCount);
++
 +    }
  
  }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/4355d06a/utils/src/com/cloud/utils/exception/CSExceptionErrorCode.java
----------------------------------------------------------------------
diff --cc utils/src/com/cloud/utils/exception/CSExceptionErrorCode.java
index 303e0d6,303e0d6..8f40233
--- a/utils/src/com/cloud/utils/exception/CSExceptionErrorCode.java
+++ b/utils/src/com/cloud/utils/exception/CSExceptionErrorCode.java
@@@ -96,6 -96,6 +96,7 @@@ public class CSExceptionErrorCode 
              
ExceptionErrorCodeMap.put("com.cloud.exception.UnsupportedServiceException", 
4530);
              
ExceptionErrorCodeMap.put("com.cloud.exception.VirtualMachineMigrationException",
 4535);
              ExceptionErrorCodeMap.put("com.cloud.async.AsyncCommandQueued", 
4540);
++            
ExceptionErrorCodeMap.put("com.cloud.exception.RequestLimitException", 4545);
  
              // Have a special error code for ServerApiException when it is
              // thrown in a standalone manner when failing to detect any of 
the above

Reply via email to