Skip to content

Commit

Permalink
Merge pull request #1412 from michaelcowan/javanica/raise-runtime-exc…
Browse files Browse the repository at this point in the history
…eption-for-observable

Javanica/Support raiseHystrixExceptions for Observables
  • Loading branch information
mattrjacobs authored Dec 6, 2016
2 parents 3f7ad96 + b1280ea commit 03dac0c
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 10 deletions.
19 changes: 16 additions & 3 deletions hystrix-contrib/hystrix-javanica/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<Throwable, Observable>() {
@Override
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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<Void> testSubscriber = new TestSubscriber<Void>();
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<Void> 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'");
}
Expand Down

0 comments on commit 03dac0c

Please sign in to comment.