From 2f04ffad4b747f1b61b3c9ecc46d6e8b2cba2cca Mon Sep 17 00:00:00 2001 From: Mike Cowan Date: Thu, 3 Nov 2016 17:30:40 +0000 Subject: [PATCH 1/2] Add Observable support for option to raise HystrixRuntimeException --- .../aop/aspectj/HystrixCommandAspect.java | 16 +++++++---- ...asicDefaultRaiseHystrixExceptionsTest.java | 28 ++++++++++++++++++- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java index 3f1347fc2..f7d6c0627 100644 --- a/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java +++ b/hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java @@ -104,15 +104,12 @@ public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinP } catch (HystrixBadRequestException e) { throw e.getCause(); } catch (HystrixRuntimeException e) { - if (metaHolder.raiseHystrixExceptionsContains(HystrixException.RUNTIME_EXCEPTION)) { - throw e; - } - throw getCause(e); + throw hystrixRuntimeExceptionToThrowable(metaHolder, e); } return result; } - private Observable executeObservable(HystrixInvokable invokable, ExecutionType executionType, MetaHolder metaHolder) { + private Observable executeObservable(HystrixInvokable invokable, ExecutionType executionType, final MetaHolder metaHolder) { return ((Observable) CommandExecutor.execute(invokable, executionType, metaHolder)) .onErrorResumeNext(new Func1() { @Override @@ -121,13 +118,20 @@ public Observable call(Throwable throwable) { return Observable.error(throwable.getCause()); } else if (throwable instanceof HystrixRuntimeException) { HystrixRuntimeException hystrixRuntimeException = (HystrixRuntimeException) throwable; - return Observable.error(getCause(hystrixRuntimeException)); + return Observable.error(hystrixRuntimeExceptionToThrowable(metaHolder, hystrixRuntimeException)); } return Observable.error(throwable); } }); } + private Throwable hystrixRuntimeExceptionToThrowable(MetaHolder metaHolder, HystrixRuntimeException e) { + if (metaHolder.raiseHystrixExceptionsContains(HystrixException.RUNTIME_EXCEPTION)) { + return e; + } + return getCause(e); + } + private Throwable getCause(HystrixRuntimeException e) { if (e.getFailureType() != HystrixRuntimeException.FailureType.COMMAND_EXCEPTION) { return e; diff --git a/hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/common/error/BasicDefaultRaiseHystrixExceptionsTest.java b/hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/common/error/BasicDefaultRaiseHystrixExceptionsTest.java index d5a6951d0..95c862eae 100644 --- a/hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/common/error/BasicDefaultRaiseHystrixExceptionsTest.java +++ b/hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/common/error/BasicDefaultRaiseHystrixExceptionsTest.java @@ -4,11 +4,15 @@ import org.junit.Ignore; import org.junit.Test; +import com.netflix.hystrix.Hystrix; import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixException; import com.netflix.hystrix.exception.HystrixRuntimeException; +import rx.Observable; +import rx.observers.TestSubscriber; + /** * Created by Mike Cowan */ @@ -56,10 +60,32 @@ public void testFallbackCommandOverridesDefaultIgnoreExceptions_nonIgnoreExcepti service.commandWithFallbackOverridesDefaultIgnoreExceptions(BadRequestException.class); } + @Test(expected = HystrixRuntimeException.class) + public void testRaiseHystrixRuntimeException() { + service.commandShouldRaiseHystrixRuntimeException(); + } + + @Test + public void testObservableRaiseHystrixRuntimeException() { + TestSubscriber testSubscriber = new TestSubscriber(); + service.observableCommandShouldRaiseHystrixRuntimeException().subscribe(testSubscriber); + testSubscriber.assertError(HystrixRuntimeException.class); + } + @DefaultProperties(ignoreExceptions = BadRequestException.class, raiseHystrixExceptions = {HystrixException.RUNTIME_EXCEPTION}) public static class Service { @HystrixCommand - public Object commandInheritsDefaultIgnoreExceptions() throws BadRequestException { + public Object commandShouldRaiseHystrixRuntimeException() throws SpecificException { + throw new SpecificException("from 'commandShouldRaiseHystrixRuntimeException'"); + } + + @HystrixCommand + public Observable observableCommandShouldRaiseHystrixRuntimeException() throws SpecificException { + return Observable.error(new SpecificException("from 'observableCommandShouldRaiseHystrixRuntimeException'")); + } + + @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'"); } From b1280ea137429149a441c9017117a03223bda5d0 Mon Sep 17 00:00:00 2001 From: Mike Cowan Date: Thu, 3 Nov 2016 17:53:37 +0000 Subject: [PATCH 2/2] Adding documentation for raiseHystrixExceptions --- hystrix-contrib/hystrix-javanica/README.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/hystrix-contrib/hystrix-javanica/README.md b/hystrix-contrib/hystrix-javanica/README.md index a09147f3f..74072ad8f 100644 --- a/hystrix-contrib/hystrix-javanica/README.md +++ b/hystrix-contrib/hystrix-javanica/README.md @@ -326,9 +326,22 @@ Based on [this](https://github.com/Netflix/Hystrix/wiki/How-To-Use#ErrorPropagat } ``` -If `userResource.getUserById(id);` throws an exception that type is _BadRequestException_ then this exception will be wrapped in ``HystrixBadRequestException`` and re-thrown without triggering fallback logic. You don't need to do it manually, javanica will do it for you under the hood. It is worth noting that a caller will get root cause exception, i.e. user ``BadRequestException``. A caller always gets root cause exception, never ``HystrixBadRequestException`` or ``HystrixRuntimeException`` except the case when executed code explicitly throws those exceptions. +If `userResource.getUserById(id);` throws an exception that type is _BadRequestException_ then this exception will be wrapped in ``HystrixBadRequestException`` and re-thrown without triggering fallback logic. You don't need to do it manually, javanica will do it for you under the hood. -*Note*: If command has a fallback then only first exception that trigers fallback logic will be propagated to caller. Example: +It is worth noting that by default a caller will always get the root cause exception e.g. ``BadRequestException``, never ``HystrixBadRequestException`` or ``HystrixRuntimeException`` (except the case when executed code explicitly throws those exceptions). + +Optionally this exception un-wraping can be disabled for ``HystrixRuntimeException`` by using ``raiseHystrixExceptions`` i.e. all exceptions that are not ignored are raised as the _cause_ of a ``HystrixRuntimeException``: + +```java + @HystrixCommand( + ignoreExceptions = {BadRequestException.class}, + raiseHystrixExceptions = {HystrixException.RUNTIME_EXCEPTION}) + public User getUserById(String id) { + return userResource.getUserById(id); + } +``` + +*Note*: If command has a fallback then only first exception that triggers fallback logic will be propagated to caller. Example: ```java class Service { @@ -557,7 +570,7 @@ ThreadPoolProperties can be set using @HystrixCommand's 'threadPoolProperties' l ``` ### DefaultProperties -``@DefaultProperties`` is class (type) level annotation that allows to default commands properties such as ``groupKey``, ``threadPoolKey``, ``commandProperties``, ``threadPoolProperties`` and ``ignoreExceptions``. Properties specified using this annotation will be used by default for each hystrix command defined within annotated class unless a command specifies those properties explicitly using corresponding ``@HystrixCommand`` parameters. +``@DefaultProperties`` is class (type) level annotation that allows to default commands properties such as ``groupKey``, ``threadPoolKey``, ``commandProperties``, ``threadPoolProperties``, ``ignoreExceptions`` and ``raiseHystrixExceptions``. Properties specified using this annotation will be used by default for each hystrix command defined within annotated class unless a command specifies those properties explicitly using corresponding ``@HystrixCommand`` parameters. Example: ```java