Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javanica/Support raiseHystrixExceptions for Observables #1412

Merged
merged 2 commits into from
Dec 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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