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

iss1446: Ready for review #1482

Merged
merged 3 commits into from
Mar 1, 2017
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
82 changes: 80 additions & 2 deletions hystrix-contrib/hystrix-javanica/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# hystrix-javanica

**Could you please spend 5 sec and answer the [questionnaire](https://docs.google.com/forms/d/1NEeWxtL_PleX0H9GqTvKxxHqwUryJ9L048j8D3T45fs/viewform). Thank you !**

Java language has a great advantages over other languages such as reflection and annotations.
All modern frameworks such as Spring, Hibernate, myBatis and etc. seek to use this advantages to the maximum.
The idea of introduction annotations in Hystrix is obvious solution for improvement. Currently using Hystrix involves writing a lot of code that is a barrier to rapid development. You likely be spending a lot of time on writing a Hystrix commands. Idea of the Javanica project is make easier using of Hystrix by the introduction of support annotations.
Expand Down Expand Up @@ -316,6 +314,86 @@ case 2: sync command, async fallback. This case isn't supported for the same rea

Same restrictions are imposed on using observable feature in javanica.

## Default fallback for class or concrete command
This feature allows to define default fallback for the whole class or concrete command. If you have a batch of commands with exactly the same fallback logic you still have to define a fallback method for every command because fallback method should have exactly the same signature as command does, consider the following code:

```java
public class Service {
@RequestMapping(value = "/test1")
@HystrixCommand(fallbackMethod = "fallback")
public APIResponse test1(String param1) {
// some codes here
return APIResponse.success("success");
}

@RequestMapping(value = "/test2")
@HystrixCommand(fallbackMethod = "fallback")
public APIResponse test2() {
// some codes here
return APIResponse.success("success");
}

@RequestMapping(value = "/test3")
@HystrixCommand(fallbackMethod = "fallback")
public APIResponse test3(ObjectRequest obj) {
// some codes here
return APIResponse.success("success");
}

private APIResponse fallback(String param1) {
return APIResponse.failed("Server is busy");
}

private APIResponse fallback() {
return APIResponse.failed("Server is busy");
}

private APIResponse fallback(ObjectRequest obj) {
return APIResponse.failed("Server is busy");
}
}
```

Default fallback feature allows to engage DRY principle and get rid of redundancy:

```java
@DefaultProperties(defaultFallback = "fallback")
public class Service {
@RequestMapping(value = "/test1")
@HystrixCommand
public APIResponse test1(String param1) {
// some codes here
return APIResponse.success("success");
}

@RequestMapping(value = "/test2")
@HystrixCommand
public APIResponse test2() {
// some codes here
return APIResponse.success("success");
}

@RequestMapping(value = "/test3")
@HystrixCommand
public APIResponse test3(ObjectRequest obj) {
// some codes here
return APIResponse.success("success");
}

private APIResponse fallback() {
return APIResponse.failed("Server is busy");
}
}
```

Default fallback method should not have any parameters except extra one to get execution exception and shouldn't throw any exceptions.
Below fallbacks listed in descending order of priority:

1. command fallback defined using `fallbackMethod` property of `@HystrixCommand`
2. command default fallback defined using `defaultFallback` property of `@HystrixCommand`
3. class default fallback defined using `defaultFallback` property of `@DefaultProperties`


## Error Propagation
Based on [this](https://github.com/Netflix/Hystrix/wiki/How-To-Use#ErrorPropagation) description, `@HystrixCommand` has an ability to specify exceptions types which should be ignored.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,13 @@
* @return exceptions to wrap
*/
HystrixException[] raiseHystrixExceptions() default {};

/**
* Specifies default fallback method for each command in the given class. Every command within the class should
* have a return type which is compatible with default fallback method return type.
* note: default fallback method cannot have parameters.
*
* @return the name of default fallback method
*/
String defaultFallback() default "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,14 @@
* @return exceptions to wrap
*/
HystrixException[] raiseHystrixExceptions() default {};

/**
* Specifies default fallback method for the command. If both {@link #fallbackMethod} and {@link #defaultFallback}
* methods are specified then specific one is used.
* note: default fallback method cannot have parameters, return type should be compatible with command return type.
*
* @return the name of default fallback method
*/
String defaultFallback() default "";
}

Original file line number Diff line number Diff line change
Expand Up @@ -105,15 +105,17 @@ private CommandAction createFallbackAction(MetaHolder metaHolder) {
if (fallbackMethod.isPresent()) {

Method fMethod = fallbackMethod.getMethod();
Object[] args = fallbackMethod.isDefault() ? new Object[0] : metaHolder.getArgs();
if (fallbackMethod.isCommand()) {
fMethod.setAccessible(true);
HystrixCommand hystrixCommand = fMethod.getAnnotation(HystrixCommand.class);
MetaHolder fmMetaHolder = MetaHolder.builder()
.obj(metaHolder.getObj())
.method(fMethod)
.ajcMethod(getAjcMethod(metaHolder.getObj(), fMethod))
.args(metaHolder.getArgs())
.args(args)
.fallback(true)
.defaultFallback(fallbackMethod.isDefault())
.defaultCollapserKey(metaHolder.getDefaultCollapserKey())
.fallbackMethod(fMethod)
.extendedFallback(fallbackMethod.isExtended())
Expand All @@ -131,12 +133,13 @@ private CommandAction createFallbackAction(MetaHolder metaHolder) {
} else {
MetaHolder fmMetaHolder = MetaHolder.builder()
.obj(metaHolder.getObj())
.defaultFallback(fallbackMethod.isDefault())
.method(fMethod)
.fallbackExecutionType(ExecutionType.SYNCHRONOUS)
.extendedFallback(fallbackMethod.isExtended())
.extendedParentFallback(metaHolder.isExtendedFallback())
.ajcMethod(null) // if fallback method isn't annotated with command annotation then we don't need to get ajc method for this
.args(metaHolder.getArgs()).build();
.args(args).build();

fallbackAction = new MethodExecutionAction(fmMetaHolder.getObj(), fMethod, fmMetaHolder.getArgs(), fmMetaHolder);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
/**
* Simple immutable holder to keep all necessary information about current method to build Hystrix command.
*/
// todo: replace fallback related flags with FallbackMethod class
@Immutable
public final class MetaHolder {

Expand All @@ -60,6 +61,7 @@ public final class MetaHolder {
private final ExecutionType fallbackExecutionType;
private final boolean fallback;
private boolean extendedParentFallback;
private final boolean defaultFallback;
private final JoinPoint joinPoint;
private final boolean observable;
private final ObservableExecutionMode observableExecutionMode;
Expand Down Expand Up @@ -93,6 +95,7 @@ private MetaHolder(Builder builder) {
this.fallbackExecutionType = builder.fallbackExecutionType;
this.joinPoint = builder.joinPoint;
this.extendedFallback = builder.extendedFallback;
this.defaultFallback = builder.defaultFallback;
this.fallback = builder.fallback;
this.extendedParentFallback = builder.extendedParentFallback;
this.observable = builder.observable;
Expand Down Expand Up @@ -227,6 +230,10 @@ public boolean isExtendedFallback() {
return extendedFallback;
}

public boolean isDefaultFallback() {
return defaultFallback;
}

@SuppressWarnings("unchecked")
public List<Class<? extends Throwable>> getCommandIgnoreExceptions() {
if (!isCommandAnnotationPresent()) return Collections.emptyList();
Expand Down Expand Up @@ -367,6 +374,7 @@ public static final class Builder {
private boolean extendedFallback;
private boolean fallback;
private boolean extendedParentFallback;
private boolean defaultFallback;
private boolean observable;
private JoinPoint joinPoint;
private ObservableExecutionMode observableExecutionMode;
Expand Down Expand Up @@ -411,6 +419,11 @@ public Builder extendedParentFallback(boolean extendedParentFallback) {
return this;
}

public Builder defaultFallback(boolean defaultFallback) {
this.defaultFallback = defaultFallback;
return this;
}

public Builder ajcMethod(Method ajcMethod) {
this.ajcMethod = ajcMethod;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,19 @@ public class FallbackMethod {

private final Method method;
private final boolean extended;
private final boolean defaultFallback;
private ExecutionType executionType;

public static final FallbackMethod ABSENT = new FallbackMethod(null, false);
public static final FallbackMethod ABSENT = new FallbackMethod(null, false, false);

public FallbackMethod(Method method) {
this(method, false);
this(method, false, false);
}

public FallbackMethod(Method method, boolean extended) {
public FallbackMethod(Method method, boolean extended, boolean defaultFallback) {
this.method = method;
this.extended = extended;
this.defaultFallback = defaultFallback;
if (method != null) {
this.executionType = ExecutionType.getExecutionType(method.getReturnType());
}
Expand All @@ -86,7 +88,11 @@ public boolean isExtended() {
return extended;
}

public void validateReturnType(Method commandMethod) {
public boolean isDefault() {
return defaultFallback;
}

public void validateReturnType(Method commandMethod) throws FallbackDefinitionException {
if (isPresent()) {
Class<?> commandReturnType = commandMethod.getReturnType();
if (ExecutionType.OBSERVABLE == ExecutionType.getExecutionType(commandReturnType)) {
Expand Down
Loading