/** * Copyright 2016 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 static java.util.Arrays.asList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.ServiceConfigurationError; import java.util.Vector; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentLinkedQueue; import org.junit.After; import org.junit.Test; import org.slf4j.Logger; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.strategy.HystrixPlugins.LoggerSupplier; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook; import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher; import com.netflix.hystrix.strategy.properties.HystrixDynamicProperties; import com.netflix.hystrix.strategy.properties.HystrixDynamicPropertiesSystemProperties; import com.netflix.hystrix.strategy.properties.HystrixDynamicProperty; import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; public class HystrixPluginsTest { @After public void reset() { //HystrixPlugins.reset(); dynamicPropertyEvents.clear(); System.clearProperty("hystrix.plugin.HystrixDynamicProperties.implementation"); } private static ConcurrentLinkedQueue<String> dynamicPropertyEvents = new ConcurrentLinkedQueue<String>(); @Test public void testDynamicProperties() throws Exception { fakeServiceLoaderResource = "FAKE_META_INF_SERVICES/com.netflix.hystrix.strategy.properties.HystrixDynamicProperties"; HystrixPlugins plugins = setupMockServiceLoader(); HystrixDynamicProperties properties = plugins.getDynamicProperties(); plugins.getCommandExecutionHook(); plugins.getPropertiesStrategy(); assertTrue(properties instanceof MockHystrixDynamicPropertiesTest); assertEvents( "[serviceloader: META-INF/services/com.netflix.hystrix.strategy.properties.HystrixDynamicProperties" + ", debug: [Created HystrixDynamicProperties instance by loading from ServiceLoader. Using class: {}, com.netflix.hystrix.strategy.HystrixPluginsTest.MockHystrixDynamicPropertiesTest]" + ", property: hystrix.plugin.HystrixCommandExecutionHook.implementation" + ", serviceloader: META-INF/services/com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook" + ", property: hystrix.plugin.HystrixPropertiesStrategy.implementation" + ", serviceloader: META-INF/services/com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy]"); } void assertEvents(String expect) throws Exception { List<String> keys = getEvents(); String actual = keys.toString(); if (! actual.equals(expect)) { javaPrintList(System.out, keys); } assertEquals(expect, actual); } static List<String> getEvents() { return new ArrayList<String>(dynamicPropertyEvents); } static void javaPrintList(Appendable a, Iterable<String> list) throws IOException { boolean first = true; for (String o : list) { if (first) { a.append("\"["); first = false; } else { a.append("\""); a.append("\n+ \", "); } a.append(o); } a.append("]\""); } @Test(expected=ServiceConfigurationError.class) public void testDynamicPropertiesFailure() throws Exception { /* * James Bond: Do you expect me to talk? * Auric Goldfinger: No, Mr. Bond, I expect you to die! */ fakeServiceLoaderResource = "FAKE_META_INF_SERVICES/com.netflix.hystrix.strategy.properties.HystrixDynamicPropertiesFail"; HystrixPlugins plugins = setupMockServiceLoader(); plugins.getDynamicProperties(); } @Test public void testDynamicSystemProperties() throws Exception { //On the off chance this is the first test lets not screw up all the other tests HystrixPlugins.getInstance(); System.setProperty("hystrix.plugin.HystrixDynamicProperties.implementation", "com.netflix.hystrix.strategy.properties.HystrixDynamicPropertiesSystemProperties"); HystrixPlugins plugins = setupMockServiceLoader(); assertTrue(plugins.getDynamicProperties() instanceof HystrixDynamicPropertiesSystemProperties); HystrixDynamicProperties p = plugins.getDynamicProperties(); //Some minimum testing of system properties wrapper //this probably should be in its own test class. assertTrue(p.getBoolean("USE_DEFAULT", true).get()); assertEquals("string", p.getString("USE_DEFAULT", "string").get()); assertEquals(1L, p.getLong("USE_DEFAULT", 1L).get().longValue()); assertEquals(1, p.getInteger("USE_DEFAULT", 1).get().intValue()); assertNotNull(p.getString("path.separator", null).get()); assertEvents("[debug: [Created HystrixDynamicProperties instance from System property named \"hystrix.plugin.HystrixDynamicProperties.implementation\". Using class: {}, com.netflix.hystrix.strategy.properties.HystrixDynamicPropertiesSystemProperties]]"); System.clearProperty("hystrix.plugin.HystrixDynamicProperties.implementation"); } static String fakeServiceLoaderResource = "FAKE_META_INF_SERVICES/com.netflix.hystrix.strategy.properties.HystrixDynamicProperties"; private HystrixPlugins setupMockServiceLoader() throws Exception { final ClassLoader realLoader = HystrixPlugins.class.getClassLoader(); ClassLoader loader = new WrappedClassLoader(realLoader) { @Override public Enumeration<URL> getResources(String name) throws IOException { dynamicPropertyEvents.add("serviceloader: " + name); final Enumeration<URL> r; if (name.endsWith("META-INF/services/com.netflix.hystrix.strategy.properties.HystrixDynamicProperties")) { Vector<URL> vs = new Vector<URL>(); URL u = super.getResource(fakeServiceLoaderResource); vs.add(u); return vs.elements(); } else { r = super.getResources(name); } return r; } }; final Logger mockLogger = (Logger) Proxy.newProxyInstance(realLoader, new Class<?>[] {Logger.class}, new MockLoggerInvocationHandler()); return HystrixPlugins.create(loader, new LoggerSupplier() { @Override public Logger getLogger() { return mockLogger; } }); } static class MockLoggerInvocationHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { dynamicPropertyEvents.offer(method.getName() + ": " + asList(args)); return null; } } static class WrappedClassLoader extends ClassLoader { final ClassLoader delegate; public WrappedClassLoader(ClassLoader delegate) { super(); this.delegate = delegate; } public Class<?> loadClass(String name) throws ClassNotFoundException { return delegate.loadClass(name); } public URL getResource(String name) { return delegate.getResource(name); } public Enumeration<URL> getResources(String name) throws IOException { return delegate.getResources(name); } public InputStream getResourceAsStream(String name) { return delegate.getResourceAsStream(name); } public void setDefaultAssertionStatus(boolean enabled) { delegate.setDefaultAssertionStatus(enabled); } public void setPackageAssertionStatus(String packageName, boolean enabled) { delegate.setPackageAssertionStatus(packageName, enabled); } public void setClassAssertionStatus(String className, boolean enabled) { delegate.setClassAssertionStatus(className, enabled); } public void clearAssertionStatus() { delegate.clearAssertionStatus(); } } private static class NoOpProperty<T> implements HystrixDynamicProperty<T> { @Override public T get() { return null; } @Override public void addCallback(Runnable callback) { } @Override public String getName() { return "NOP"; } } public static class MockHystrixDynamicPropertiesTest implements HystrixDynamicProperties { @Override public HystrixDynamicProperty<String> getString(String name, String fallback) { dynamicPropertyEvents.offer("property: " + name); return new NoOpProperty<String>(); } @Override public HystrixDynamicProperty<Integer> getInteger(String name, Integer fallback) { dynamicPropertyEvents.offer("property: " + name); return new NoOpProperty<Integer>(); } @Override public HystrixDynamicProperty<Long> getLong(String name, Long fallback) { dynamicPropertyEvents.offer("property: " + name); return new NoOpProperty<Long>(); } @Override public HystrixDynamicProperty<Boolean> getBoolean(String name, Boolean fallback) { dynamicPropertyEvents.offer("property: " + name); return new NoOpProperty<Boolean>(); } } /* @Test public void testCommandExecutionHookDefaultImpl() { HystrixCommandExecutionHook impl = HystrixPlugins.getInstance().getCommandExecutionHook(); assertTrue(impl instanceof HystrixCommandExecutionHookDefault); } @Test public void testCommandExecutionHookViaRegisterMethod() { HystrixPlugins.getInstance().registerCommandExecutionHook(new HystrixCommandExecutionHookTestImpl()); HystrixCommandExecutionHook impl = HystrixPlugins.getInstance().getCommandExecutionHook(); assertTrue(impl instanceof HystrixCommandExecutionHookTestImpl); }*/ public static class HystrixCommandExecutionHookTestImpl extends HystrixCommandExecutionHook { } /*@Test public void testEventNotifierDefaultImpl() { HystrixEventNotifier impl = HystrixPlugins.getInstance().getEventNotifier(); assertTrue(impl instanceof HystrixEventNotifierDefault); } @Test public void testEventNotifierViaRegisterMethod() { HystrixPlugins.getInstance().registerEventNotifier(new HystrixEventNotifierTestImpl()); HystrixEventNotifier impl = HystrixPlugins.getInstance().getEventNotifier(); assertTrue(impl instanceof HystrixEventNotifierTestImpl); } @Test public void testEventNotifierViaProperty() { try { String fullClass = HystrixEventNotifierTestImpl.class.getName(); System.setProperty("hystrix.plugin.HystrixEventNotifier.implementation", fullClass); HystrixEventNotifier impl = HystrixPlugins.getInstance().getEventNotifier(); assertTrue(impl instanceof HystrixEventNotifierTestImpl); } finally { System.clearProperty("hystrix.plugin.HystrixEventNotifier.implementation"); } }*/ // inside UnitTest so it is stripped from Javadocs public static class HystrixEventNotifierTestImpl extends HystrixEventNotifier { // just use defaults } /*@Test public void testConcurrencyStrategyDefaultImpl() { HystrixConcurrencyStrategy impl = HystrixPlugins.getInstance().getConcurrencyStrategy(); assertTrue(impl instanceof HystrixConcurrencyStrategyDefault); } @Test public void testConcurrencyStrategyViaRegisterMethod() { HystrixPlugins.getInstance().registerConcurrencyStrategy(new HystrixConcurrencyStrategyTestImpl()); HystrixConcurrencyStrategy impl = HystrixPlugins.getInstance().getConcurrencyStrategy(); assertTrue(impl instanceof HystrixConcurrencyStrategyTestImpl); } @Test public void testConcurrencyStrategyViaProperty() { try { String fullClass = HystrixConcurrencyStrategyTestImpl.class.getName(); System.setProperty("hystrix.plugin.HystrixConcurrencyStrategy.implementation", fullClass); HystrixConcurrencyStrategy impl = HystrixPlugins.getInstance().getConcurrencyStrategy(); assertTrue(impl instanceof HystrixConcurrencyStrategyTestImpl); } finally { System.clearProperty("hystrix.plugin.HystrixConcurrencyStrategy.implementation"); } }*/ // inside UnitTest so it is stripped from Javadocs public static class HystrixConcurrencyStrategyTestImpl extends HystrixConcurrencyStrategy { // just use defaults } /*@Test public void testMetricsPublisherDefaultImpl() { HystrixMetricsPublisher impl = HystrixPlugins.getInstance().getMetricsPublisher(); assertTrue(impl instanceof HystrixMetricsPublisherDefault); } @Test public void testMetricsPublisherViaRegisterMethod() { HystrixPlugins.getInstance().registerMetricsPublisher(new HystrixMetricsPublisherTestImpl()); HystrixMetricsPublisher impl = HystrixPlugins.getInstance().getMetricsPublisher(); assertTrue(impl instanceof HystrixMetricsPublisherTestImpl); } @Test public void testMetricsPublisherViaProperty() { try { String fullClass = HystrixMetricsPublisherTestImpl.class.getName(); System.setProperty("hystrix.plugin.HystrixMetricsPublisher.implementation", fullClass); HystrixMetricsPublisher impl = HystrixPlugins.getInstance().getMetricsPublisher(); assertTrue(impl instanceof HystrixMetricsPublisherTestImpl); } finally { System.clearProperty("hystrix.plugin.HystrixMetricsPublisher.implementation"); } }*/ // inside UnitTest so it is stripped from Javadocs public static class HystrixMetricsPublisherTestImpl extends HystrixMetricsPublisher { // just use defaults } /*@Test public void testPropertiesStrategyDefaultImpl() { HystrixPropertiesStrategy impl = HystrixPlugins.getInstance().getPropertiesStrategy(); assertTrue(impl instanceof HystrixPropertiesStrategyDefault); } @Test public void testPropertiesStrategyViaRegisterMethod() { HystrixPlugins.getInstance().registerPropertiesStrategy(new HystrixPropertiesStrategyTestImpl()); HystrixPropertiesStrategy impl = HystrixPlugins.getInstance().getPropertiesStrategy(); assertTrue(impl instanceof HystrixPropertiesStrategyTestImpl); } @Test public void testPropertiesStrategyViaProperty() { try { String fullClass = HystrixPropertiesStrategyTestImpl.class.getName(); System.setProperty("hystrix.plugin.HystrixPropertiesStrategy.implementation", fullClass); HystrixPropertiesStrategy impl = HystrixPlugins.getInstance().getPropertiesStrategy(); assertTrue(impl instanceof HystrixPropertiesStrategyTestImpl); } finally { System.clearProperty("hystrix.plugin.HystrixPropertiesStrategy.implementation"); } }*/ // inside UnitTest so it is stripped from Javadocs public static class HystrixPropertiesStrategyTestImpl extends HystrixPropertiesStrategy { // just use defaults } /*@Test public void testRequestContextViaPluginInTimeout() { HystrixPlugins.getInstance().registerConcurrencyStrategy(new HystrixConcurrencyStrategy() { @Override public <T> Callable<T> wrapCallable(final Callable<T> callable) { return new RequestIdCallable<T>(callable); } }); HystrixRequestContext context = HystrixRequestContext.initializeContext(); testRequestIdThreadLocal.set("foobar"); final AtomicReference<String> valueInTimeout = new AtomicReference<String>(); new DummyCommand().toObservable() .doOnError(new Action1<Throwable>() { @Override public void call(Throwable throwable) { System.out.println("initialized = " + HystrixRequestContext.isCurrentThreadInitialized()); System.out.println("requestId (timeout) = " + testRequestIdThreadLocal.get()); valueInTimeout.set(testRequestIdThreadLocal.get()); } }) .materialize() .toBlocking().single(); context.shutdown(); Hystrix.reset(); assertEquals("foobar", valueInTimeout.get()); }*/ private static class RequestIdCallable<T> implements Callable<T> { private final Callable<T> callable; private final String requestId; public RequestIdCallable(Callable<T> callable) { this.callable = callable; this.requestId = testRequestIdThreadLocal.get(); } @Override public T call() throws Exception { String original = testRequestIdThreadLocal.get(); testRequestIdThreadLocal.set(requestId); try { return callable.call(); } finally { testRequestIdThreadLocal.set(original); } } } private static final ThreadLocal<String> testRequestIdThreadLocal = new ThreadLocal<String>(); public static class DummyCommand extends HystrixCommand<Void> { public DummyCommand() { super(HystrixCommandGroupKey.Factory.asKey("Dummy")); } @Override protected Void run() throws Exception { System.out.println("requestId (run) = " + testRequestIdThreadLocal.get()); Thread.sleep(2000); return null; } } }