| // Copyright (C) 2014 The Android Open Source Project |
| // |
| // 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 com.googlesource.gerrit.plugins.quota; |
| |
| import static com.google.gerrit.server.config.ConfigResource.CONFIG_KIND; |
| import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS; |
| import static com.google.gerrit.server.project.ProjectResource.PROJECT_KIND; |
| import static com.googlesource.gerrit.plugins.quota.QuotaResource.QUOTA_KIND; |
| |
| import com.google.common.cache.CacheLoader; |
| import com.google.common.util.concurrent.RateLimiter; |
| import com.google.gerrit.extensions.events.GarbageCollectorListener; |
| import com.google.gerrit.extensions.events.LifecycleListener; |
| import com.google.gerrit.extensions.events.ProjectDeletedListener; |
| import com.google.gerrit.extensions.registration.DynamicMap; |
| import com.google.gerrit.extensions.registration.DynamicSet; |
| import com.google.gerrit.extensions.restapi.RestApiModule; |
| import com.google.gerrit.reviewdb.client.Account; |
| import com.google.gerrit.server.IdentifiedUser; |
| import com.google.gerrit.server.IdentifiedUser.GenericFactory; |
| import com.google.gerrit.server.cache.CacheModule; |
| import com.google.gerrit.server.git.ReceivePackInitializer; |
| import com.google.gerrit.server.git.validators.UploadValidationListener; |
| import com.google.gerrit.server.group.SystemGroupBackend; |
| import com.google.gerrit.server.validators.ProjectCreationValidationListener; |
| import com.google.inject.Inject; |
| import com.google.inject.Scopes; |
| import com.google.inject.internal.UniqueAnnotations; |
| import com.googlesource.gerrit.plugins.quota.AccountLimitsConfig.RateLimit; |
| import java.util.Optional; |
| import org.eclipse.jgit.transport.PostReceiveHook; |
| |
| class Module extends CacheModule { |
| static final String CACHE_NAME_ACCOUNTID = "rate_limits_by_account"; |
| static final String CACHE_NAME_REMOTEHOST = "rate_limits_by_ip"; |
| |
| @Override |
| protected void configure() { |
| DynamicSet.bind(binder(), ProjectCreationValidationListener.class) |
| .to(MaxRepositoriesQuotaValidator.class); |
| DynamicSet.bind(binder(), ReceivePackInitializer.class) |
| .to(MaxRepositorySizeQuota.class); |
| DynamicSet.bind(binder(), PostReceiveHook.class) |
| .to(MaxRepositorySizeQuota.class); |
| DynamicSet.bind(binder(), ProjectDeletedListener.class).to( |
| DeletionListener.class); |
| DynamicSet.bind(binder(), GarbageCollectorListener.class).to( |
| GCListener.class); |
| DynamicSet.setOf(binder(), UsageDataEventCreator.class); |
| install(MaxRepositorySizeQuota.module()); |
| install(new RestApiModule() { |
| @Override |
| protected void configure() { |
| DynamicMap.mapOf(binder(), QUOTA_KIND); |
| get(PROJECT_KIND, "quota").to(GetQuota.class); |
| child(CONFIG_KIND, "quota").to(GetQuotas.class); |
| } |
| }); |
| bind(Publisher.class).in(Scopes.SINGLETON); |
| bind(PublisherScheduler.class).in(Scopes.SINGLETON); |
| bind(ProjectNameResolver.class).in(Scopes.SINGLETON); |
| bind(LifecycleListener.class) |
| .annotatedWith(UniqueAnnotations.create()) |
| .to(PublisherScheduler.class); |
| |
| DynamicSet.bind(binder(), UploadValidationListener.class) |
| .to(RateLimitUploadListener.class); |
| cache(CACHE_NAME_ACCOUNTID, Account.Id.class, Holder.class) |
| .loader(LoaderAccountId.class); |
| cache(CACHE_NAME_REMOTEHOST, String.class, Holder.class) |
| .loader(LoaderRemoteHost.class); |
| } |
| |
| static class Holder { |
| public static final Holder EMPTY = new Holder(null); |
| private RateLimiter l; |
| |
| public Holder(RateLimiter l) { |
| this.l = l; |
| } |
| |
| public RateLimiter get() { |
| return l; |
| } |
| } |
| |
| private static class LoaderAccountId extends CacheLoader<Account.Id, Holder> { |
| private GenericFactory userFactory; |
| private AccountLimitsFinder finder; |
| |
| @Inject |
| LoaderAccountId(IdentifiedUser.GenericFactory userFactory, |
| AccountLimitsFinder finder) { |
| this.userFactory = userFactory; |
| this.finder = finder; |
| } |
| |
| @Override |
| public Holder load(Account.Id key) throws Exception { |
| IdentifiedUser user = userFactory.create(key); |
| Optional<RateLimit> limit = |
| finder.firstMatching(AccountLimitsConfig.Type.UPLOADPACK, user); |
| if (limit.isPresent()) { |
| return new Holder(RateLimitUploadListener.createSmoothBurstyRateLimiter( |
| limit.get().getRatePerSecond(), limit.get().getMaxBurstSeconds())); |
| } |
| return Holder.EMPTY; |
| } |
| } |
| |
| private static class LoaderRemoteHost extends CacheLoader<String, Holder> { |
| private AccountLimitsFinder finder; |
| private String anonymous; |
| |
| @Inject |
| LoaderRemoteHost(SystemGroupBackend systemGroupBackend, |
| AccountLimitsFinder finder) { |
| this.finder = finder; |
| this.anonymous = systemGroupBackend.get(ANONYMOUS_USERS).getName(); |
| } |
| |
| @Override |
| public Holder load(String key) throws Exception { |
| Optional<RateLimit> limit = |
| finder.getRateLimit(AccountLimitsConfig.Type.UPLOADPACK, anonymous); |
| if (limit.isPresent()) { |
| return new Holder(RateLimitUploadListener.createSmoothBurstyRateLimiter( |
| limit.get().getRatePerSecond(), limit.get().getMaxBurstSeconds())); |
| } |
| return Holder.EMPTY; |
| } |
| } |
| } |