Skip to content

Commit a735c80

Browse files
authored
Merge pull request #1397 from michaelcowan/feature/optionally-raise-hystrix-runtime-exception
Add option to raise HystrixRuntimeException
2 parents 8b5a075 + 478c211 commit a735c80

File tree

7 files changed

+211
-9
lines changed

7 files changed

+211
-9
lines changed

hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/DefaultProperties.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,17 @@
6767
HystrixProperty[] threadPoolProperties() default {};
6868

6969
/**
70-
* Defines exceptions which should be ignored and wrapped to throw in HystrixBadRequestException.
71-
* All methods annotated with @HystrixCommand will automatically inherit this property.
72-
*
70+
* Defines exceptions which should be ignored.
71+
* Optionally these can be wrapped in HystrixRuntimeException if raiseHystrixExceptions contains RUNTIME_EXCEPTION.
7372
*
7473
* @return exceptions to ignore
7574
*/
7675
Class<? extends Throwable>[] ignoreExceptions() default {};
76+
77+
/**
78+
* When includes RUNTIME_EXCEPTION, any exceptions that are not ignored are wrapped in HystrixRuntimeException.
79+
*
80+
* @return exceptions to wrap
81+
*/
82+
HystrixException[] raiseHystrixExceptions() default {};
7783
}

hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCommand.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,8 @@
100100
HystrixProperty[] threadPoolProperties() default {};
101101

102102
/**
103-
* Defines exceptions which should be ignored and wrapped to throw in HystrixBadRequestException.
103+
* Defines exceptions which should be ignored.
104+
* Optionally these can be wrapped in HystrixRuntimeException if raiseHystrixExceptions contains RUNTIME_EXCEPTION.
104105
*
105106
* @return exceptions to ignore
106107
*/
@@ -113,5 +114,12 @@
113114
* @return observable execution mode
114115
*/
115116
ObservableExecutionMode observableExecutionMode() default ObservableExecutionMode.EAGER;
117+
118+
/**
119+
* When includes RUNTIME_EXCEPTION, any exceptions that are not ignored are wrapped in HystrixRuntimeException.
120+
*
121+
* @return exceptions to wrap
122+
*/
123+
HystrixException[] raiseHystrixExceptions() default {};
116124
}
117125

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.netflix.hystrix.contrib.javanica.annotation;
2+
3+
/**
4+
* Created by Mike Cowan
5+
*/
6+
public enum HystrixException {
7+
RUNTIME_EXCEPTION,
8+
}

hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
2222
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
2323
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
24+
import com.netflix.hystrix.contrib.javanica.annotation.HystrixException;
2425
import com.netflix.hystrix.contrib.javanica.command.CommandExecutor;
2526
import com.netflix.hystrix.contrib.javanica.command.ExecutionType;
2627
import com.netflix.hystrix.contrib.javanica.command.HystrixCommandFactory;
@@ -103,6 +104,9 @@ public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinP
103104
} catch (HystrixBadRequestException e) {
104105
throw e.getCause();
105106
} catch (HystrixRuntimeException e) {
107+
if (metaHolder.raiseHystrixExceptionsContains(HystrixException.RUNTIME_EXCEPTION)) {
108+
throw e;
109+
}
106110
throw getCause(e);
107111
}
108112
return result;

hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/MetaHolder.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,7 @@
2020
import com.google.common.base.Predicate;
2121
import com.google.common.base.Supplier;
2222
import com.google.common.collect.ImmutableList;
23-
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
24-
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
25-
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
26-
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
27-
import com.netflix.hystrix.contrib.javanica.annotation.ObservableExecutionMode;
23+
import com.netflix.hystrix.contrib.javanica.annotation.*;
2824
import com.netflix.hystrix.contrib.javanica.command.closure.Closure;
2925
import org.apache.commons.lang3.StringUtils;
3026
import org.aspectj.lang.JoinPoint;
@@ -299,6 +295,27 @@ public ObservableExecutionMode getObservableExecutionMode() {
299295
return observableExecutionMode;
300296
}
301297

298+
public boolean raiseHystrixExceptionsContains(HystrixException hystrixException) {
299+
return getRaiseHystrixExceptions().contains(hystrixException);
300+
}
301+
302+
public List<HystrixException> getRaiseHystrixExceptions() {
303+
return getOrDefault(new Supplier<List<HystrixException>>() {
304+
@Override
305+
public List<HystrixException> get() {
306+
return ImmutableList.copyOf(hystrixCommand.raiseHystrixExceptions());
307+
}
308+
}, new Supplier<List<HystrixException>>() {
309+
@Override
310+
public List<HystrixException> get() {
311+
return hasDefaultProperties()
312+
? ImmutableList.copyOf(defaultProperties.raiseHystrixExceptions())
313+
: Collections.<HystrixException>emptyList();
314+
315+
}
316+
}, this.<HystrixException>nonEmptyList());
317+
}
318+
302319
private String get(String key, String defaultKey) {
303320
return StringUtils.isNotBlank(key) ? key : defaultKey;
304321
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package com.netflix.hystrix.contrib.javanica.test.common.error;
2+
3+
import org.junit.Before;
4+
import org.junit.Ignore;
5+
import org.junit.Test;
6+
7+
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
8+
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
9+
import com.netflix.hystrix.contrib.javanica.annotation.HystrixException;
10+
import com.netflix.hystrix.exception.HystrixRuntimeException;
11+
12+
/**
13+
* Created by Mike Cowan
14+
*/
15+
public abstract class BasicDefaultRaiseHystrixExceptionsTest {
16+
17+
private Service service;
18+
19+
@Before
20+
public void setUp() throws Exception {
21+
service = createService();
22+
}
23+
24+
protected abstract Service createService();
25+
26+
@Test(expected = BadRequestException.class)
27+
public void testDefaultIgnoreException() {
28+
service.commandInheritsDefaultIgnoreExceptions();
29+
}
30+
31+
@Test(expected = SpecificException.class)
32+
public void testCommandOverridesDefaultIgnoreExceptions() {
33+
service.commandOverridesDefaultIgnoreExceptions(SpecificException.class);
34+
}
35+
36+
@Test(expected = HystrixRuntimeException.class)
37+
public void testCommandOverridesDefaultIgnoreExceptions_nonIgnoreExceptionShouldBePropagated() {
38+
// method throws BadRequestException that isn't ignored
39+
service.commandOverridesDefaultIgnoreExceptions(BadRequestException.class);
40+
}
41+
42+
@Ignore // https://github.com/Netflix/Hystrix/issues/993#issuecomment-229542203
43+
@Test(expected = BadRequestException.class)
44+
public void testFallbackCommandInheritsDefaultIgnoreException() {
45+
service.commandWithFallbackInheritsDefaultIgnoreExceptions();
46+
}
47+
48+
@Ignore // https://github.com/Netflix/Hystrix/issues/993#issuecomment-229542203
49+
@Test(expected = SpecificException.class)
50+
public void testFallbackCommandOverridesDefaultIgnoreExceptions() {
51+
service.commandWithFallbackOverridesDefaultIgnoreExceptions(SpecificException.class);
52+
}
53+
54+
@Test(expected = HystrixRuntimeException.class)
55+
public void testFallbackCommandOverridesDefaultIgnoreExceptions_nonIgnoreExceptionShouldBePropagated() {
56+
service.commandWithFallbackOverridesDefaultIgnoreExceptions(BadRequestException.class);
57+
}
58+
59+
@DefaultProperties(ignoreExceptions = BadRequestException.class, raiseHystrixExceptions = {HystrixException.RUNTIME_EXCEPTION})
60+
public static class Service {
61+
@HystrixCommand
62+
public Object commandInheritsDefaultIgnoreExceptions() throws BadRequestException {
63+
// this exception will be ignored (wrapped in HystrixBadRequestException) because specified in default ignore exceptions
64+
throw new BadRequestException("from 'commandInheritsIgnoreExceptionsFromDefault'");
65+
}
66+
67+
@HystrixCommand(ignoreExceptions = SpecificException.class)
68+
public Object commandOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) throws BadRequestException, SpecificException {
69+
if(errorType.equals(BadRequestException.class)){
70+
// isn't ignored because command doesn't specify this exception type in 'ignoreExceptions'
71+
throw new BadRequestException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
72+
}
73+
// something went wrong, this error is ignored because specified in the command's ignoreExceptions
74+
throw new SpecificException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
75+
}
76+
77+
@HystrixCommand(fallbackMethod = "fallbackInheritsDefaultIgnoreExceptions")
78+
public Object commandWithFallbackInheritsDefaultIgnoreExceptions() throws SpecificException {
79+
// isn't ignored, need to trigger fallback
80+
throw new SpecificException("from 'commandWithFallbackInheritsDefaultIgnoreExceptions'");
81+
}
82+
83+
@HystrixCommand
84+
private Object fallbackInheritsDefaultIgnoreExceptions() throws BadRequestException {
85+
// should be ignored because specified in global ignore exception, fallback command inherits default ignore exceptions
86+
throw new BadRequestException("from 'fallbackInheritsDefaultIgnoreExceptions'");
87+
}
88+
89+
@HystrixCommand(fallbackMethod = "fallbackOverridesDefaultIgnoreExceptions")
90+
public Object commandWithFallbackOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) {
91+
// isn't ignored, need to trigger fallback
92+
throw new SpecificException();
93+
}
94+
95+
@HystrixCommand(ignoreExceptions = SpecificException.class)
96+
private Object fallbackOverridesDefaultIgnoreExceptions(Class<? extends Throwable> errorType) {
97+
if(errorType.equals(BadRequestException.class)){
98+
// isn't ignored because fallback doesn't specify this exception type in 'ignoreExceptions'
99+
throw new BadRequestException("from 'fallbackOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
100+
}
101+
// something went wrong, this error is ignored because specified in the fallback's ignoreExceptions
102+
throw new SpecificException("from 'commandOverridesDefaultIgnoreExceptions', cause: " + errorType.getSimpleName());
103+
}
104+
}
105+
106+
public static final class BadRequestException extends RuntimeException {
107+
public BadRequestException() {
108+
}
109+
110+
public BadRequestException(String message) {
111+
super(message);
112+
}
113+
}
114+
115+
public static final class SpecificException extends RuntimeException {
116+
public SpecificException() {
117+
}
118+
119+
public SpecificException(String message) {
120+
super(message);
121+
}
122+
}
123+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.netflix.hystrix.contrib.javanica.test.spring.error;
2+
3+
import org.junit.runner.RunWith;
4+
import org.springframework.beans.factory.annotation.Autowired;
5+
import org.springframework.beans.factory.annotation.Configurable;
6+
import org.springframework.context.annotation.Bean;
7+
import org.springframework.test.context.ContextConfiguration;
8+
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
9+
10+
import com.netflix.hystrix.contrib.javanica.test.common.error.BasicDefaultRaiseHystrixExceptionsTest;
11+
import com.netflix.hystrix.contrib.javanica.test.spring.conf.AopCglibConfig;
12+
13+
/**
14+
* Created by Mike Cowan
15+
*/
16+
@RunWith(SpringJUnit4ClassRunner.class)
17+
@ContextConfiguration(classes = {AopCglibConfig.class, DefaultRaiseHystrixExceptionsTest.DefaultRaiseHystrixExceptionsTestConfig.class})
18+
public class DefaultRaiseHystrixExceptionsTest extends BasicDefaultRaiseHystrixExceptionsTest {
19+
20+
@Autowired
21+
private BasicDefaultRaiseHystrixExceptionsTest.Service service;
22+
23+
@Override
24+
protected BasicDefaultRaiseHystrixExceptionsTest.Service createService() {
25+
return service;
26+
}
27+
28+
@Configurable
29+
public static class DefaultRaiseHystrixExceptionsTestConfig {
30+
31+
@Bean
32+
public BasicDefaultRaiseHystrixExceptionsTest.Service userService() {
33+
return new BasicDefaultRaiseHystrixExceptionsTest.Service();
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)