Skip to content

Commit

Permalink
iss993: added test for default ignore exceptions
Browse files Browse the repository at this point in the history
  • Loading branch information
dmgcodevil committed Jun 30, 2016
1 parent 9a56749 commit e902a38
Show file tree
Hide file tree
Showing 5 changed files with 165 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public Builder collapsedRequests(Collection<HystrixCollapser.CollapsedRequest<Ob
* @param pIgnoreExceptions the exceptions to be ignored
* @return this {@link HystrixCommandBuilder.Builder}
*/
public Builder ignoreExceptions(Class<? extends Throwable>[] pIgnoreExceptions) {
public Builder ignoreExceptions(List<Class<? extends Throwable>> pIgnoreExceptions) {
this.ignoreExceptions = ImmutableList.copyOf(pIgnoreExceptions);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@
*/
package com.netflix.hystrix.contrib.javanica.command;

import com.google.common.base.Function;
import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.utils.FallbackMethod;
import com.netflix.hystrix.contrib.javanica.utils.MethodProvider;
Expand Down Expand Up @@ -62,12 +60,7 @@ public HystrixCommandBuilder create(MetaHolder metaHolder, Collection<HystrixCol
.collapsedRequests(collapsedRequests)
.cacheResultInvocationContext(createCacheResultInvocationContext(metaHolder))
.cacheRemoveInvocationContext(createCacheRemoveInvocationContext(metaHolder))
.ignoreExceptions(metaHolder.getDefaultProperties().transform(new Function<DefaultProperties, Class<? extends Throwable>[]>() {
@Override
public Class<? extends Throwable>[] apply(DefaultProperties input) {
return input.ignoreExceptions();
}
}).or(metaHolder.getHystrixCommand().ignoreExceptions()))
.ignoreExceptions(metaHolder.getCommandIgnoreExceptions())
.executionType(metaHolder.getExecutionType())
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.netflix.hystrix.contrib.javanica.command;

import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Supplier;
Expand All @@ -26,7 +27,6 @@
import com.netflix.hystrix.contrib.javanica.annotation.ObservableExecutionMode;
import com.netflix.hystrix.contrib.javanica.command.closure.Closure;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.aspectj.lang.JoinPoint;

import javax.annotation.Nullable;
Expand All @@ -40,7 +40,7 @@
* Simple immutable holder to keep all necessary information about current method to build Hystrix command.
*/
@Immutable
public class MetaHolder {
public final class MetaHolder {

private final HystrixCollapser hystrixCollapser;
private final HystrixCommand hystrixCommand;
Expand Down Expand Up @@ -68,6 +68,14 @@ public class MetaHolder {
private final boolean observable;
private final ObservableExecutionMode observableExecutionMode;

private static final Function identityFun = new Function<Object, Object>() {
@Nullable
@Override
public Object apply(@Nullable Object input) {
return input;
}
};

private MetaHolder(Builder builder) {
this.hystrixCommand = builder.hystrixCommand;
this.method = builder.method;
Expand Down Expand Up @@ -220,23 +228,43 @@ public boolean isExtendedFallback() {
return extendedFallback;
}

@SuppressWarnings("unchecked")
public List<Class<? extends Throwable>> getCommandIgnoreExceptions() {
if (!isCommandAnnotationPresent()) return Collections.emptyList();
return getOrDefault(new Supplier<List<Class<? extends Throwable>>>() {
@Override
public List<Class<? extends Throwable>> get() {
return ImmutableList.<Class<? extends Throwable>>copyOf(hystrixCommand.ignoreExceptions());
}
}, new Supplier<List<Class<? extends Throwable>>>() {
@Override
public List<Class<? extends Throwable>> get() {
return hasDefaultProperties()
? ImmutableList.<Class<? extends Throwable>>copyOf(defaultProperties.ignoreExceptions())
: Collections.<Class<? extends Throwable>>emptyList();
}
}, this.<Class<? extends Throwable>>nonEmptyList());
}

public ExecutionType getFallbackExecutionType() {
return fallbackExecutionType;
}

public List<HystrixProperty> getCommandProperties() {
if (!isCommandAnnotationPresent()) return Collections.emptyList();
return getProperties(new Supplier<HystrixProperty[]>() {
return getOrDefault(new Supplier<List<HystrixProperty>>() {
@Override
public HystrixProperty[] get() {
return hystrixCommand.commandProperties();
public List<HystrixProperty> get() {
return ImmutableList.copyOf(hystrixCommand.commandProperties());
}
}, new Supplier<HystrixProperty[]>() {
}, new Supplier<List<HystrixProperty>>() {
@Override
public HystrixProperty[] get() {
return hasDefaultProperties() ? defaultProperties.commandProperties() : new HystrixProperty[0];
public List<HystrixProperty> get() {
return hasDefaultProperties()
? ImmutableList.copyOf(defaultProperties.commandProperties())
: Collections.<HystrixProperty>emptyList();
}
});
}, this.<HystrixProperty>nonEmptyList());
}

public List<HystrixProperty> getCollapserProperties() {
Expand All @@ -245,17 +273,19 @@ public List<HystrixProperty> getCollapserProperties() {

public List<HystrixProperty> getThreadPoolProperties() {
if (!isCommandAnnotationPresent()) return Collections.emptyList();
return getProperties(new Supplier<HystrixProperty[]>() {
return getOrDefault(new Supplier<List<HystrixProperty>>() {
@Override
public HystrixProperty[] get() {
return hystrixCommand.threadPoolProperties();
public List<HystrixProperty> get() {
return ImmutableList.copyOf(hystrixCommand.threadPoolProperties());
}
}, new Supplier<HystrixProperty[]>() {
}, new Supplier<List<HystrixProperty>>() {
@Override
public HystrixProperty[] get() {
return hasDefaultProperties() ? defaultProperties.threadPoolProperties() : new HystrixProperty[0];
public List<HystrixProperty> get() {
return hasDefaultProperties()
? ImmutableList.copyOf(defaultProperties.threadPoolProperties())
: Collections.<HystrixProperty>emptyList();
}
});
}, this.<HystrixProperty>nonEmptyList());
}

public boolean isObservable() {
Expand All @@ -270,12 +300,26 @@ private String get(String key, String defaultKey) {
return StringUtils.isNotBlank(key) ? key : defaultKey;
}

private List<HystrixProperty> getProperties(Supplier<HystrixProperty[]> props, Supplier<HystrixProperty[]> defaultProps) {
HystrixProperty[] p = props.get();
if (p.length > 0) {
return ImmutableList.copyOf(p);
private <T> Predicate<List<T>> nonEmptyList() {
return new Predicate<List<T>>() {
@Override
public boolean apply(@Nullable List<T> input) {
return input != null && !input.isEmpty();
}
};
}

@SuppressWarnings("unchecked")
private <T> T getOrDefault(Supplier<T> source, Supplier<T> defaultChoice, Predicate<T> isDefined) {
return getOrDefault(source, defaultChoice, isDefined, (Function<T, T>) identityFun);
}

private <T> T getOrDefault(Supplier<T> source, Supplier<T> defaultChoice, Predicate<T> isDefined, Function<T, T> map) {
T res = source.get();
if (!isDefined.apply(res)) {
res = defaultChoice.get();
}
return ImmutableList.copyOf(defaultProps.get());
return map.apply(res);
}

public static final class Builder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,45 +2,122 @@

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;

/**
* todo [dmgcodevil]: add docs
* Test for {@link DefaultProperties#ignoreExceptions()} feature.
*
* <p>
* Created by dmgcodevil.
*/
public abstract class BasicDefaultIgnoreExceptionsTest {
private UserService userService;
private Service service;

@Before
public void setUp() throws Exception {
userService = createUserService();
service = createService();
}

protected abstract UserService createUserService();
protected abstract Service createService();

@Test(expected = IllegalArgumentException.class)
@Test(expected = BadRequestException.class)
public void testDefaultIgnoreException() {
userService.getById("");
service.commandInheritsDefaultIgnoreExceptions();
}

@DefaultProperties(ignoreExceptions = IllegalArgumentException.class)
public static class UserService {
@Test(expected = SpecificException.class)
public void testCommandOverridesDefaultIgnoreExceptions() {
service.commandOverridesDefaultIgnoreExceptions(SpecificException.class);
}

@Test(expected = HystrixRuntimeException.class)
public void testCommandOverridesDefaultIgnoreExceptions_nonIgnoreExceptionShouldBePropagated() {
// method throws BadRequestException that isn't ignored
service.commandOverridesDefaultIgnoreExceptions(BadRequestException.class);
}

@Ignore // https://github.com/Netflix/Hystrix/issues/993#issuecomment-229542203
@Test(expected = BadRequestException.class)
public void testFallbackCommandInheritsDefaultIgnoreException() {
service.commandWithFallbackInheritsDefaultIgnoreExceptions();
}

@Ignore // https://github.com/Netflix/Hystrix/issues/993#issuecomment-229542203
@Test(expected = SpecificException.class)
public void testFallbackCommandOverridesDefaultIgnoreExceptions() {
service.commandWithFallbackOverridesDefaultIgnoreExceptions(SpecificException.class);
}

@Test(expected = HystrixRuntimeException.class)
public void testFallbackCommandOverridesDefaultIgnoreExceptions_nonIgnoreExceptionShouldBePropagated() {
service.commandWithFallbackOverridesDefaultIgnoreExceptions(BadRequestException.class);
}

@DefaultProperties(ignoreExceptions = BadRequestException.class)
public static class Service {
@HystrixCommand
public Object commandInheritsDefaultIgnoreExceptions() throws BadRequestException {
// this exception will be ignored (wrapped in HystrixBadRequestException) because specified in default ignore exceptions
throw new BadRequestException("from 'commandInheritsIgnoreExceptionsFromDefault'");
}

@HystrixCommand(ignoreExceptions = SpecificException.class)
public Object commandOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) throws BadRequestException, SpecificException {
if(errorType.equals(BadRequestException.class)){
// isn't ignored because command doesn't specify this exception type in 'ignoreExceptions'
throw new BadRequestException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
}
// something went wrong, this error is ignored because specified in the command's ignoreExceptions
throw new SpecificException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
}

@HystrixCommand(fallbackMethod = "fallbackInheritsDefaultIgnoreExceptions")
public Object commandWithFallbackInheritsDefaultIgnoreExceptions() throws SpecificException {
// isn't ignored, need to trigger fallback
throw new SpecificException("from 'commandWithFallbackInheritsDefaultIgnoreExceptions'");
}

@HystrixCommand
public User getById(String id) throws IllegalArgumentException {
if (id == null || "".equals(id)) {
throw new IllegalArgumentException("bad id");
private Object fallbackInheritsDefaultIgnoreExceptions() throws BadRequestException {
// should be ignored because specified in global ignore exception, fallback command inherits default ignore exceptions
throw new BadRequestException("from 'fallbackInheritsDefaultIgnoreExceptions'");
}

@HystrixCommand(fallbackMethod = "fallbackOverridesDefaultIgnoreExceptions")
public Object commandWithFallbackOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) {
// isn't ignored, need to trigger fallback
throw new SpecificException();
}

@HystrixCommand(ignoreExceptions = SpecificException.class)
private Object fallbackOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) {
if(errorType.equals(BadRequestException.class)){
// isn't ignored because fallback doesn't specify this exception type in 'ignoreExceptions'
throw new BadRequestException("from 'fallbackOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
}
return new User(id);
// something went wrong, this error is ignored because specified in the fallback's ignoreExceptions
throw new SpecificException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
}
}

public static class User {
private String id;
public static final class BadRequestException extends RuntimeException {
public BadRequestException() {
}

public BadRequestException(String message) {
super(message);
}
}

public static final class SpecificException extends RuntimeException {
public SpecificException() {
}

public User(String id) {
this.id = id;
public SpecificException(String message) {
super(message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@ public class DefaultIgnoreExceptionsTest extends BasicDefaultIgnoreExceptionsTes


@Autowired
private BasicDefaultIgnoreExceptionsTest.UserService userService;
private BasicDefaultIgnoreExceptionsTest.Service service;

@Override
protected BasicDefaultIgnoreExceptionsTest.UserService createUserService() {
return userService;
protected BasicDefaultIgnoreExceptionsTest.Service createService() {
return service;
}

@Configurable
public static class DefaultIgnoreExceptionsTestConfig {

@Bean
public BasicDefaultIgnoreExceptionsTest.UserService userService() {
return new BasicDefaultIgnoreExceptionsTest.UserService();
public BasicDefaultIgnoreExceptionsTest.Service userService() {
return new BasicDefaultIgnoreExceptionsTest.Service();
}
}
}

0 comments on commit e902a38

Please sign in to comment.