/*
* Copyright 2014 Avanza Bank AB
*
* 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.avanza.astrix.ft;
import static org.junit.Assert.assertEquals;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.avanza.astrix.beans.core.AstrixBeanKey;
import com.avanza.astrix.beans.core.AstrixBeanSettings;
import com.avanza.astrix.beans.core.AstrixSettings;
import com.avanza.astrix.beans.ft.BeanFaultTolerance;
import com.avanza.astrix.beans.ft.BeanFaultToleranceFactorySpi;
import com.avanza.astrix.beans.registry.InMemoryServiceRegistry;
import com.avanza.astrix.context.AstrixContext;
import com.avanza.astrix.context.TestAstrixConfigurer;
import com.avanza.astrix.core.AstrixFaultToleranceProxy;
import com.avanza.astrix.core.function.CheckedCommand;
import com.avanza.astrix.provider.core.AstrixApiProvider;
import com.avanza.astrix.provider.core.AstrixQualifier;
import com.avanza.astrix.provider.core.Library;
import com.avanza.astrix.provider.core.Service;
import com.google.common.base.Optional;
import rx.Observable;
public class FaultToleranceTest {
private InMemoryServiceRegistry serviceRegistry = new InMemoryServiceRegistry();
private FakeFaultToleranceFactory faultTolerance = new FakeFaultToleranceFactory();
private AstrixContext astrixContext;
private Ping ping;
private AstrixBeanKey<?> pingKey = AstrixBeanKey.create(Ping.class);
private TestAstrixConfigurer astrixConfigurer;
private Ping anotherPing;
private AstrixBeanKey<?> anotherPingKey = AstrixBeanKey.create(Ping.class, "another-ping");
@Before
public void setup() {
serviceRegistry.registerProvider(Ping.class, "configured-ping", new PingImpl());
astrixConfigurer = new TestAstrixConfigurer();
astrixConfigurer.registerApiProvider(PingApi.class);
astrixConfigurer.set(AstrixSettings.SERVICE_REGISTRY_URI, serviceRegistry.getServiceUri());
astrixConfigurer.registerStrategy(BeanFaultToleranceFactorySpi.class, faultTolerance);
astrixConfigurer.enableFaultTolerance(true);
astrixContext = astrixConfigurer.configure();
ping = astrixContext.getBean(Ping.class);
anotherPing = astrixContext.getBean(Ping.class, anotherPingKey.getQualifier());
}
@After
public void destroy() {
astrixContext.destroy();
}
@Test
public void itShouldBePossibleToDisableFaultToleranceGloballyAtRuntime() throws Throwable {
assertEquals(0, getAppliedFaultToleranceCount(pingKey));
assertEquals("foo", ping.ping("foo"));
assertEquals(1, getAppliedFaultToleranceCount(pingKey));
astrixConfigurer.set(AstrixSettings.ENABLE_FAULT_TOLERANCE, false);
assertEquals("bar", ping.ping("bar"));
assertEquals(1, getAppliedFaultToleranceCount(pingKey));
}
private int getAppliedFaultToleranceCount(AstrixBeanKey<?> pingKey) {
return this.faultTolerance.getAppliedFaultToleranceCount(pingKey);
}
@Test
public void itShouldBePossibleToDisableFaultToleranceAtRuntimeForAGivenBean() throws Throwable {
astrixConfigurer.set(AstrixSettings.ENABLE_FAULT_TOLERANCE, true);
assertEquals(0, getAppliedFaultToleranceCount(pingKey));
assertEquals("foo", ping.ping("foo"));
assertEquals(1, getAppliedFaultToleranceCount(pingKey));
astrixConfigurer.set(AstrixBeanSettings.FAULT_TOLERANCE_ENABLED.nameFor(AstrixBeanKey.create(Ping.class)), "false");
assertEquals("bar", ping.ping("bar"));
assertEquals(1, getAppliedFaultToleranceCount(pingKey));
assertEquals(0, getAppliedFaultToleranceCount(anotherPingKey));
assertEquals("bar", anotherPing.ping("bar"));
assertEquals(1, getAppliedFaultToleranceCount(anotherPingKey));
}
public interface Ping {
String ping(String msg);
}
public interface ConfiguredPing {
String ping(String msg);
}
@AstrixApiProvider
public static class PingApi {
@AstrixFaultToleranceProxy
@Library
public Ping ping() {
return new PingImpl();
}
@AstrixFaultToleranceProxy
@Library
@AstrixQualifier("another-ping")
public Ping anotherPing() {
return new PingImpl();
}
@Service
@AstrixQualifier("configured-ping")
public Ping configurerdPing() {
return null;
}
}
private static class PingImpl implements Ping {
@Override
public String ping(String msg) {
return msg;
}
}
private static class FakeFaultToleranceFactory implements BeanFaultToleranceFactorySpi {
private Map<AstrixBeanKey<?>, FakeBeanFaultTolerance> ftByBeanKey = new HashMap<>();
@Override
public BeanFaultTolerance create(AstrixBeanKey<?> beanKey) {
return this.ftByBeanKey.computeIfAbsent(beanKey, bk -> new FakeBeanFaultTolerance());
}
public int getAppliedFaultToleranceCount(AstrixBeanKey<?> pingKey) {
return java.util.Optional.ofNullable(this.ftByBeanKey.get(pingKey))
.map(FakeBeanFaultTolerance::getAppliedFaultToleranceCount)
.orElse(0);
}
}
private static class FakeBeanFaultTolerance implements BeanFaultTolerance {
private final AtomicInteger appliedFaultToleranceCount = new AtomicInteger(0);
@Override
public <T> Observable<T> observe(Supplier<Observable<T>> observable) {
appliedFaultToleranceCount.incrementAndGet();
return observable.get();
}
@Override
public <T> T execute(CheckedCommand<T> command) throws Throwable {
appliedFaultToleranceCount.incrementAndGet();
return command.call();
}
public int getAppliedFaultToleranceCount() {
return this.appliedFaultToleranceCount.get();
}
}
}