/* * 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 gobblin.util.limiter.broker; import java.util.Collection; import com.typesafe.config.Config; import gobblin.broker.ResourceInstance; import gobblin.broker.iface.ConfigView; import gobblin.broker.iface.NoSuchScopeException; import gobblin.broker.iface.NotConfiguredException; import gobblin.broker.iface.ScopeType; import gobblin.broker.iface.ScopedConfigView; import gobblin.broker.iface.SharedResourceFactory; import gobblin.broker.iface.SharedResourcesBroker; import gobblin.broker.iface.SharedResourceFactoryResponse; import gobblin.broker.ResourceCoordinate; import gobblin.util.ClassAliasResolver; import gobblin.util.limiter.Limiter; import gobblin.util.limiter.LimiterFactory; import gobblin.util.limiter.MultiLimiter; import gobblin.util.limiter.NoopLimiter; import lombok.extern.slf4j.Slf4j; /** * A {@link SharedResourceFactory} to create shared {@link Limiter}s using a {@link SharedResourcesBroker}. * * <p> * The factory creates a {@link MultiLimiter} combining a {@link Limiter} at the indicated scope with the * {@link Limiter} at all immediate parent scopes (obtained from the broker itself). The factory reads the property * {@link #LIMITER_CLASS_KEY} from the input {@link ConfigView} to determine the class of {@link Limiter} to create * at the indicated scope. If the key is not found, a {@link NoopLimiter} is generated instead (to combine with * parent {@link Limiter}s). * </p> */ @Slf4j public class SharedLimiterFactory<S extends ScopeType<S>> implements SharedResourceFactory<Limiter, SharedLimiterKey, S> { public static final String NAME = "limiter"; public static final String LIMITER_CLASS_KEY = "class"; public static final String FAIL_IF_NO_GLOBAL_LIMITER_KEY = "failIfNoGlobalLimiter"; public static final String FAIL_ON_UNKNOWN_RESOURCE_ID = "faiOnUnknownResourceId"; private static final ClassAliasResolver<LimiterFactory> RESOLVER = new ClassAliasResolver<>(LimiterFactory.class); @Override public String getName() { return NAME; } @Override public SharedResourceFactoryResponse<Limiter> createResource(SharedResourcesBroker<S> broker, ScopedConfigView<S, SharedLimiterKey> configView) throws NotConfiguredException{ Config config = configView.getConfig(); SharedLimiterKey.GlobalLimiterPolicy globalLimiterPolicy = configView.getKey().getGlobalLimiterPolicy(); if (config.hasPath(FAIL_IF_NO_GLOBAL_LIMITER_KEY) && config.getBoolean(FAIL_IF_NO_GLOBAL_LIMITER_KEY) && globalLimiterPolicy != SharedLimiterKey.GlobalLimiterPolicy.USE_GLOBAL) { // if user has specified FAIL_IF_NO_GLOBAL_LIMITER_KEY, promote the policy from USE_GLOBAL_IF_CONFIGURED to USE_GLOBAL // e.g. fail if no GLOBAL configuration is present SharedLimiterKey modifiedKey = new SharedLimiterKey(configView.getKey().getResourceLimitedPath(), SharedLimiterKey.GlobalLimiterPolicy.USE_GLOBAL); return new ResourceCoordinate<>(this, modifiedKey, (S) configView.getScope()); } Limiter limiter; if (!configView.getScope().isLocal() && !globalLimiterPolicy.equals(SharedLimiterKey.GlobalLimiterPolicy.LOCAL_ONLY)) { try { Class<?> klazz = Class.forName("gobblin.util.limiter.RestliLimiterFactory"); return new ResourceCoordinate<>((SharedResourceFactory<Limiter, SharedLimiterKey, S>) klazz.newInstance(), configView.getKey(), (S) configView.getScope()); } catch (ReflectiveOperationException roe) { if (globalLimiterPolicy.equals(SharedLimiterKey.GlobalLimiterPolicy.USE_GLOBAL)) { throw new RuntimeException("There is no Global limiter factory in the classpath."); } } } if (config.hasPath(LIMITER_CLASS_KEY)) { try { LimiterFactory factory = RESOLVER.resolveClass(config.getString(LIMITER_CLASS_KEY)).newInstance(); limiter = factory.buildLimiter(config); } catch (ReflectiveOperationException roe) { throw new RuntimeException(roe); } } else { if (config.hasPath(FAIL_ON_UNKNOWN_RESOURCE_ID) && config.getBoolean(FAIL_ON_UNKNOWN_RESOURCE_ID)) { throw new NotConfiguredException(); } limiter = new NoopLimiter(); } ScopeType<S> scope = configView.getScope(); Collection<S> parentScopes = scope.parentScopes(); if (parentScopes != null) { try { for (S parentScope : parentScopes) { limiter = new MultiLimiter(limiter, broker.getSharedResourceAtScope(this, configView.getKey(), parentScope)); } } catch (NoSuchScopeException nsse) { throw new RuntimeException("Could not get higher scope limiter. This is an error in code.", nsse); } } return new ResourceInstance<>(limiter); } /** * @return brokers self scope. */ @Override public S getAutoScope(SharedResourcesBroker<S> broker, ConfigView<S, SharedLimiterKey> config) { return broker.selfScope().getType(); } }