All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.hystrix.strategy.HystrixPlugins Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2012 Netflix, Inc.
 * 
 * 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.netflix.hystrix.strategy;

import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;
import java.util.concurrent.atomic.AtomicReference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy;
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifierDefault;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHookDefault;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisherDefault;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisherFactory;
import com.netflix.hystrix.strategy.properties.HystrixDynamicProperties;
import com.netflix.hystrix.strategy.properties.HystrixDynamicPropertiesSystemProperties;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategyDefault;

/**
 * Registry for plugin implementations that allows global override and handles the retrieval of correct implementation based on order of precedence:
 * 
    *
  1. plugin registered globally via register methods in this class
  2. *
  3. plugin registered and retrieved using the resolved {@link HystrixDynamicProperties} (usually Archaius, see get methods for property names)
  4. *
  5. plugin registered and retrieved using the JDK {@link ServiceLoader}
  6. *
  7. default implementation
  8. *
* * The exception to the above order is the {@link HystrixDynamicProperties} implementation * which is only loaded through System.properties or the ServiceLoader (see the {@link HystrixPlugins#getDynamicProperties() getter} for more details). *

* See the Hystrix GitHub Wiki for more information: https://github.com/Netflix/Hystrix/wiki/Plugins. */ public class HystrixPlugins { //We should not load unless we are requested to. This avoids accidental initialization. @agentgt //See https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom private static class LazyHolder { private static final HystrixPlugins INSTANCE = HystrixPlugins.create(); } private final ClassLoader classLoader; /* package */ final AtomicReference notifier = new AtomicReference(); /* package */ final AtomicReference concurrencyStrategy = new AtomicReference(); /* package */ final AtomicReference metricsPublisher = new AtomicReference(); /* package */ final AtomicReference propertiesFactory = new AtomicReference(); /* package */ final AtomicReference commandExecutionHook = new AtomicReference(); private final HystrixDynamicProperties dynamicProperties; private HystrixPlugins(ClassLoader classLoader, LoggerSupplier logSupplier) { //This will load Archaius if its in the classpath. this.classLoader = classLoader; //N.B. Do not use a logger before this is loaded as it will most likely load the configuration system. //The configuration system may need to do something prior to loading logging. @agentgt dynamicProperties = resolveDynamicProperties(classLoader, logSupplier); } /** * For unit test purposes. * @ExcludeFromJavadoc */ /* private */ static HystrixPlugins create(ClassLoader classLoader, LoggerSupplier logSupplier) { return new HystrixPlugins(classLoader, logSupplier); } /** * For unit test purposes. * @ExcludeFromJavadoc */ /* private */ static HystrixPlugins create(ClassLoader classLoader) { return new HystrixPlugins(classLoader, new LoggerSupplier() { @Override public Logger getLogger() { return LoggerFactory.getLogger(HystrixPlugins.class); } }); } /** * @ExcludeFromJavadoc */ /* private */ static HystrixPlugins create() { return create(HystrixPlugins.class.getClassLoader()); } public static HystrixPlugins getInstance() { return LazyHolder.INSTANCE; } /** * Reset all of the HystrixPlugins to null. You may invoke this directly, or it also gets invoked via Hystrix.reset() */ public static void reset() { getInstance().notifier.set(null); getInstance().concurrencyStrategy.set(null); getInstance().metricsPublisher.set(null); getInstance().propertiesFactory.set(null); getInstance().commandExecutionHook.set(null); HystrixMetricsPublisherFactory.reset(); } /** * Retrieve instance of {@link HystrixEventNotifier} to use based on order of precedence as defined in {@link HystrixPlugins} class header. *

* Override default by using {@link #registerEventNotifier(HystrixEventNotifier)} or setting property (via Archaius): hystrix.plugin.HystrixEventNotifier.implementation with the full classname to * load. * * @return {@link HystrixEventNotifier} implementation to use */ public HystrixEventNotifier getEventNotifier() { if (notifier.get() == null) { // check for an implementation from Archaius first Object impl = getPluginImplementation(HystrixEventNotifier.class); if (impl == null) { // nothing set via Archaius so initialize with default notifier.compareAndSet(null, HystrixEventNotifierDefault.getInstance()); // we don't return from here but call get() again in case of thread-race so the winner will always get returned } else { // we received an implementation from Archaius so use it notifier.compareAndSet(null, (HystrixEventNotifier) impl); } } return notifier.get(); } /** * Register a {@link HystrixEventNotifier} implementation as a global override of any injected or default implementations. * * @param impl * {@link HystrixEventNotifier} implementation * @throws IllegalStateException * if called more than once or after the default was initialized (if usage occurs before trying to register) */ public void registerEventNotifier(HystrixEventNotifier impl) { if (!notifier.compareAndSet(null, impl)) { throw new IllegalStateException("Another strategy was already registered."); } } /** * Retrieve instance of {@link HystrixConcurrencyStrategy} to use based on order of precedence as defined in {@link HystrixPlugins} class header. *

* Override default by using {@link #registerConcurrencyStrategy(HystrixConcurrencyStrategy)} or setting property (via Archaius): hystrix.plugin.HystrixConcurrencyStrategy.implementation with the * full classname to load. * * @return {@link HystrixConcurrencyStrategy} implementation to use */ public HystrixConcurrencyStrategy getConcurrencyStrategy() { if (concurrencyStrategy.get() == null) { // check for an implementation from Archaius first Object impl = getPluginImplementation(HystrixConcurrencyStrategy.class); if (impl == null) { // nothing set via Archaius so initialize with default concurrencyStrategy.compareAndSet(null, HystrixConcurrencyStrategyDefault.getInstance()); // we don't return from here but call get() again in case of thread-race so the winner will always get returned } else { // we received an implementation from Archaius so use it concurrencyStrategy.compareAndSet(null, (HystrixConcurrencyStrategy) impl); } } return concurrencyStrategy.get(); } /** * Register a {@link HystrixConcurrencyStrategy} implementation as a global override of any injected or default implementations. * * @param impl * {@link HystrixConcurrencyStrategy} implementation * @throws IllegalStateException * if called more than once or after the default was initialized (if usage occurs before trying to register) */ public void registerConcurrencyStrategy(HystrixConcurrencyStrategy impl) { if (!concurrencyStrategy.compareAndSet(null, impl)) { throw new IllegalStateException("Another strategy was already registered."); } } /** * Retrieve instance of {@link HystrixMetricsPublisher} to use based on order of precedence as defined in {@link HystrixPlugins} class header. *

* Override default by using {@link #registerMetricsPublisher(HystrixMetricsPublisher)} or setting property (via Archaius): hystrix.plugin.HystrixMetricsPublisher.implementation with the full * classname to load. * * @return {@link HystrixMetricsPublisher} implementation to use */ public HystrixMetricsPublisher getMetricsPublisher() { if (metricsPublisher.get() == null) { // check for an implementation from Archaius first Object impl = getPluginImplementation(HystrixMetricsPublisher.class); if (impl == null) { // nothing set via Archaius so initialize with default metricsPublisher.compareAndSet(null, HystrixMetricsPublisherDefault.getInstance()); // we don't return from here but call get() again in case of thread-race so the winner will always get returned } else { // we received an implementation from Archaius so use it metricsPublisher.compareAndSet(null, (HystrixMetricsPublisher) impl); } } return metricsPublisher.get(); } /** * Register a {@link HystrixMetricsPublisher} implementation as a global override of any injected or default implementations. * * @param impl * {@link HystrixMetricsPublisher} implementation * @throws IllegalStateException * if called more than once or after the default was initialized (if usage occurs before trying to register) */ public void registerMetricsPublisher(HystrixMetricsPublisher impl) { if (!metricsPublisher.compareAndSet(null, impl)) { throw new IllegalStateException("Another strategy was already registered."); } } /** * Retrieve instance of {@link HystrixPropertiesStrategy} to use based on order of precedence as defined in {@link HystrixPlugins} class header. *

* Override default by using {@link #registerPropertiesStrategy(HystrixPropertiesStrategy)} or setting property (via Archaius): hystrix.plugin.HystrixPropertiesStrategy.implementation with the full * classname to load. * * @return {@link HystrixPropertiesStrategy} implementation to use */ public HystrixPropertiesStrategy getPropertiesStrategy() { if (propertiesFactory.get() == null) { // check for an implementation from Archaius first Object impl = getPluginImplementation(HystrixPropertiesStrategy.class); if (impl == null) { // nothing set via Archaius so initialize with default propertiesFactory.compareAndSet(null, HystrixPropertiesStrategyDefault.getInstance()); // we don't return from here but call get() again in case of thread-race so the winner will always get returned } else { // we received an implementation from Archaius so use it propertiesFactory.compareAndSet(null, (HystrixPropertiesStrategy) impl); } } return propertiesFactory.get(); } /** * Retrieves the instance of {@link HystrixDynamicProperties} to use. *

* Unlike the other plugins this plugin cannot be re-registered and is only loaded at creation * of the {@link HystrixPlugins} singleton. *

* The order of precedence for loading implementations is: *

    *
  1. System property of key: hystrix.plugin.HystrixDynamicProperties.implementation with the class as a value.
  2. *
  3. The {@link ServiceLoader}.
  4. *
  5. An implementation based on Archaius if it is found in the classpath is used.
  6. *
  7. A fallback implementation based on the {@link System#getProperties()}
  8. *
* @return never null */ public HystrixDynamicProperties getDynamicProperties() { return dynamicProperties; } /** * Register a {@link HystrixPropertiesStrategy} implementation as a global override of any injected or default implementations. * * @param impl * {@link HystrixPropertiesStrategy} implementation * @throws IllegalStateException * if called more than once or after the default was initialized (if usage occurs before trying to register) */ public void registerPropertiesStrategy(HystrixPropertiesStrategy impl) { if (!propertiesFactory.compareAndSet(null, impl)) { throw new IllegalStateException("Another strategy was already registered."); } } /** * Retrieve instance of {@link HystrixCommandExecutionHook} to use based on order of precedence as defined in {@link HystrixPlugins} class header. *

* Override default by using {@link #registerCommandExecutionHook(HystrixCommandExecutionHook)} or setting property (via Archaius): hystrix.plugin.HystrixCommandExecutionHook.implementation with the * full classname to * load. * * @return {@link HystrixCommandExecutionHook} implementation to use * * @since 1.2 */ public HystrixCommandExecutionHook getCommandExecutionHook() { if (commandExecutionHook.get() == null) { // check for an implementation from Archaius first Object impl = getPluginImplementation(HystrixCommandExecutionHook.class); if (impl == null) { // nothing set via Archaius so initialize with default commandExecutionHook.compareAndSet(null, HystrixCommandExecutionHookDefault.getInstance()); // we don't return from here but call get() again in case of thread-race so the winner will always get returned } else { // we received an implementation from Archaius so use it commandExecutionHook.compareAndSet(null, (HystrixCommandExecutionHook) impl); } } return commandExecutionHook.get(); } /** * Register a {@link HystrixCommandExecutionHook} implementation as a global override of any injected or default implementations. * * @param impl * {@link HystrixCommandExecutionHook} implementation * @throws IllegalStateException * if called more than once or after the default was initialized (if usage occurs before trying to register) * * @since 1.2 */ public void registerCommandExecutionHook(HystrixCommandExecutionHook impl) { if (!commandExecutionHook.compareAndSet(null, impl)) { throw new IllegalStateException("Another strategy was already registered."); } } private T getPluginImplementation(Class pluginClass) { T p = getPluginImplementationViaProperties(pluginClass, dynamicProperties); if (p != null) return p; return findService(pluginClass, classLoader); } @SuppressWarnings("unchecked") private static T getPluginImplementationViaProperties(Class pluginClass, HystrixDynamicProperties dynamicProperties) { String classSimpleName = pluginClass.getSimpleName(); // Check Archaius for plugin class. String propertyName = "hystrix.plugin." + classSimpleName + ".implementation"; String implementingClass = dynamicProperties.getString(propertyName, null).get(); if (implementingClass != null) { try { Class cls = Class.forName(implementingClass); // narrow the scope (cast) to the type we're expecting cls = cls.asSubclass(pluginClass); return (T) cls.newInstance(); } catch (ClassCastException e) { throw new RuntimeException(classSimpleName + " implementation is not an instance of " + classSimpleName + ": " + implementingClass); } catch (ClassNotFoundException e) { throw new RuntimeException(classSimpleName + " implementation class not found: " + implementingClass, e); } catch (InstantiationException e) { throw new RuntimeException(classSimpleName + " implementation not able to be instantiated: " + implementingClass, e); } catch (IllegalAccessException e) { throw new RuntimeException(classSimpleName + " implementation not able to be accessed: " + implementingClass, e); } } else { return null; } } private static HystrixDynamicProperties resolveDynamicProperties(ClassLoader classLoader, LoggerSupplier logSupplier) { HystrixDynamicProperties hp = getPluginImplementationViaProperties(HystrixDynamicProperties.class, HystrixDynamicPropertiesSystemProperties.getInstance()); if (hp != null) { logSupplier.getLogger().debug( "Created HystrixDynamicProperties instance from System property named " + "\"hystrix.plugin.HystrixDynamicProperties.implementation\". Using class: {}", hp.getClass().getCanonicalName()); return hp; } hp = findService(HystrixDynamicProperties.class, classLoader); if (hp != null) { logSupplier.getLogger() .debug("Created HystrixDynamicProperties instance by loading from ServiceLoader. Using class: {}", hp.getClass().getCanonicalName()); return hp; } hp = HystrixArchaiusHelper.createArchaiusDynamicProperties(); if (hp != null) { logSupplier.getLogger().debug("Created HystrixDynamicProperties. Using class : {}", hp.getClass().getCanonicalName()); return hp; } hp = HystrixDynamicPropertiesSystemProperties.getInstance(); logSupplier.getLogger().info("Using System Properties for HystrixDynamicProperties! Using class: {}", hp.getClass().getCanonicalName()); return hp; } private static T findService( Class spi, ClassLoader classLoader) throws ServiceConfigurationError { ServiceLoader sl = ServiceLoader.load(spi, classLoader); for (T s : sl) { if (s != null) return s; } return null; } interface LoggerSupplier { Logger getLogger(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy