Skip to content

Commit f918595

Browse files
authored
Merge pull request #1259 from dmgcodevil/iss993
iss993: added DefaultProperties annotation [ready for review]
2 parents 3bfe053 + 584deb0 commit f918595

19 files changed

+899
-82
lines changed

hystrix-contrib/hystrix-javanica/README.md

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# hystrix-javanica
22

3+
**Could you please spend 5 sec and answer the [questionnaire](https://docs.google.com/forms/d/1NEeWxtL_PleX0H9GqTvKxxHqwUryJ9L048j8D3T45fs/viewform). Thank you !**
4+
35
Java language has a great advantages over other languages such as reflection and annotations.
46
All modern frameworks such as Spring, Hibernate, myBatis and etc. seek to use this advantages to the maximum.
57
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.
@@ -324,7 +326,32 @@ Based on [this](https://github.com/Netflix/Hystrix/wiki/How-To-Use#ErrorPropagat
324326
}
325327
```
326328

327-
If `userResource.getUserById(id);` throws an exception which type is _BadRequestException_ then this exception will be thrown without triggering fallback logic.
329+
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.
330+
331+
*Note*: If command has a fallback then only first exception that trigers fallback logic will be propagated to caller. Example:
332+
333+
```java
334+
class Service {
335+
@HystrixCommand(fallbackMethod = "fallback")
336+
Object command(Object o) throws CommandException {
337+
throw new CommandException();
338+
}
339+
340+
@HystrixCommand
341+
Object fallback(Object o) throws FallbackException {
342+
throw new FallbackException();
343+
}
344+
}
345+
346+
// in client code
347+
{
348+
try {
349+
service.command(null);
350+
} catch (Exception e) {
351+
assert CommandException.class.equals(e.getClass())
352+
}
353+
}
354+
```
328355

329356
## Request Cache
330357

@@ -529,6 +556,24 @@ ThreadPoolProperties can be set using @HystrixCommand's 'threadPoolProperties' l
529556
}
530557
```
531558

559+
### DefaultProperties
560+
``@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.
561+
Example:
562+
563+
```java
564+
@DefaultProperties(groupKey = "DefaultGroupKey")
565+
class Service {
566+
@HystrixCommand // hystrix command group key is 'DefaultGroupKey'
567+
public Object commandInheritsDefaultProperties() {
568+
return null;
569+
}
570+
@HystrixCommand(groupKey = "SpecificGroupKey") // command overrides default group key
571+
public Object commandOverridesGroupKey() {
572+
return null;
573+
}
574+
}
575+
```
576+
532577
## Hystrix collapser
533578

534579
Suppose you have some command which calls should be collapsed in one backend call. For this goal you can use ```@HystrixCollapser``` annotation.
@@ -664,4 +709,4 @@ Please create an issue if you need a feature or you detected some bugs. Thanks
664709

665710
**Note**: Javanica 1.4.+ is updated more frequently than 1.3.+ hence 1.4+ is more stable.
666711

667-
**It's recommended to use Javaniva 1.4.+**
712+
**It's recommended to use Javanica 1.4.+**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/**
2+
* Copyright 2016 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.netflix.hystrix.contrib.javanica.annotation;
17+
18+
import java.lang.annotation.Documented;
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Inherited;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
/**
26+
* This annotation is used to specify default parameters for
27+
* hystrix commands (methods annotated with {@code @HystrixCommand} annotation).
28+
*
29+
* @author dmgcodevil
30+
*/
31+
@Target({ElementType.TYPE})
32+
@Retention(RetentionPolicy.RUNTIME)
33+
@Inherited
34+
@Documented
35+
public @interface DefaultProperties {
36+
37+
/**
38+
* Specifies default group key used for each hystrix command by default unless a command specifies group key explicitly.
39+
* For additional info about this property see {@link HystrixCommand#groupKey()}.
40+
*
41+
* @return default group key
42+
*/
43+
String groupKey() default "";
44+
45+
/**
46+
* Specifies default thread pool key used for each hystrix command by default unless a command specifies thread pool key explicitly.
47+
* For additional info about this property see {@link HystrixCommand#threadPoolKey()}
48+
*
49+
* @return default thread pool
50+
*/
51+
String threadPoolKey() default "";
52+
53+
/**
54+
* Specifies command properties that will be used for
55+
* each hystrix command be default unless command properties explicitly specified in @HystrixCommand.
56+
*
57+
* @return command properties
58+
*/
59+
HystrixProperty[] commandProperties() default {};
60+
61+
/**
62+
* Specifies thread pool properties that will be used for
63+
* each hystrix command be default unless thread pool properties explicitly specified in @HystrixCommand.
64+
*
65+
* @return thread pool properties
66+
*/
67+
HystrixProperty[] threadPoolProperties() default {};
68+
69+
/**
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+
*
73+
*
74+
* @return exceptions to ignore
75+
*/
76+
Class<? extends Throwable>[] ignoreExceptions() default {};
77+
}

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

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,24 @@
1515
*/
1616
package com.netflix.hystrix.contrib.javanica.aop.aspectj;
1717

18+
import com.google.common.base.Optional;
1819
import com.google.common.base.Throwables;
1920
import com.google.common.collect.ImmutableMap;
20-
import com.netflix.hystrix.HystrixExecutable;
2121
import com.netflix.hystrix.HystrixInvokable;
22+
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
2223
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
2324
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
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;
2728
import com.netflix.hystrix.contrib.javanica.command.MetaHolder;
29+
import com.netflix.hystrix.contrib.javanica.exception.CommandActionExecutionException;
30+
import com.netflix.hystrix.contrib.javanica.utils.AopUtils;
2831
import com.netflix.hystrix.contrib.javanica.utils.FallbackMethod;
2932
import com.netflix.hystrix.contrib.javanica.utils.MethodProvider;
3033
import com.netflix.hystrix.exception.HystrixBadRequestException;
34+
import com.netflix.hystrix.exception.HystrixRuntimeException;
35+
import org.apache.commons.lang3.StringUtils;
3136
import org.apache.commons.lang3.Validate;
3237
import org.aspectj.lang.JoinPoint;
3338
import org.aspectj.lang.ProceedingJoinPoint;
@@ -90,10 +95,21 @@ public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinP
9095
result = CommandExecutor.execute(invokable, executionType, metaHolder);
9196
} catch (HystrixBadRequestException e) {
9297
throw e.getCause();
98+
} catch (HystrixRuntimeException e) {
99+
throw getCauseOrDefault(e, e);
93100
}
94101
return result;
95102
}
96103

104+
private Throwable getCauseOrDefault(RuntimeException e, RuntimeException defaultException) {
105+
if (e.getCause() == null) return defaultException;
106+
if (e.getCause() instanceof CommandActionExecutionException) {
107+
CommandActionExecutionException commandActionExecutionException = (CommandActionExecutionException) e.getCause();
108+
return Optional.fromNullable(commandActionExecutionException.getCause()).or(defaultException);
109+
}
110+
return e.getCause();
111+
}
112+
97113
/**
98114
* A factory to create MetaHolder depending on {@link HystrixPointcutType}.
99115
*/
@@ -111,19 +127,10 @@ public MetaHolder create(final ProceedingJoinPoint joinPoint) {
111127
MetaHolder.Builder metaHolderBuilder(Object proxy, Method method, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) {
112128
MetaHolder.Builder builder = MetaHolder.builder()
113129
.args(args).method(method).obj(obj).proxyObj(proxy)
114-
.defaultGroupKey(obj.getClass().getSimpleName())
115130
.joinPoint(joinPoint);
116-
if (isCompileWeaving()) {
117-
builder.ajcMethod(getAjcMethodFromTarget(joinPoint));
118-
}
119131

120-
FallbackMethod fallbackMethod = MethodProvider.getInstance().getFallbackMethod(obj.getClass(), method);
121-
if (fallbackMethod.isPresent()) {
122-
fallbackMethod.validateReturnType(method);
123-
builder
124-
.fallbackMethod(fallbackMethod.getMethod())
125-
.fallbackExecutionType(ExecutionType.getExecutionType(fallbackMethod.getMethod().getReturnType()));
126-
}
132+
setFallbackMethod(builder, obj.getClass(), method);
133+
builder = setDefaultProperties(builder, obj.getClass(), joinPoint);
127134
return builder;
128135
}
129136
}
@@ -173,10 +180,8 @@ public MetaHolder create(Object proxy, Method collapserMethod, Object obj, Objec
173180
}
174181
// method of batch hystrix command must be passed to metaholder because basically collapser doesn't have any actions
175182
// that should be invoked upon intercepted method, it's required only for underlying batch command
176-
MetaHolder.Builder builder = MetaHolder.builder()
177-
.args(args).method(batchCommandMethod).obj(obj).proxyObj(proxy)
178-
.defaultGroupKey(obj.getClass().getSimpleName())
179-
.joinPoint(joinPoint);
183+
184+
MetaHolder.Builder builder = metaHolderBuilder(proxy, batchCommandMethod, obj, args, joinPoint);
180185

181186
if (isCompileWeaving()) {
182187
builder.ajcMethod(getAjcMethodAroundAdvice(obj.getClass(), batchCommandMethod.getName(), List.class));
@@ -206,6 +211,9 @@ public MetaHolder create(Object proxy, Method method, Object obj, Object[] args,
206211
HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class);
207212
ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType());
208213
MetaHolder.Builder builder = metaHolderBuilder(proxy, method, obj, args, joinPoint);
214+
if (isCompileWeaving()) {
215+
builder.ajcMethod(getAjcMethodFromTarget(joinPoint));
216+
}
209217
return builder.defaultCommandKey(method.getName())
210218
.hystrixCommand(hystrixCommand)
211219
.observableExecutionMode(hystrixCommand.observableExecutionMode())
@@ -239,4 +247,31 @@ private static Class<?> getGenericParameter(Type type) {
239247
}
240248
}
241249

250+
private static MetaHolder.Builder setDefaultProperties(MetaHolder.Builder builder, Class<?> declaringClass, final ProceedingJoinPoint joinPoint) {
251+
Optional<DefaultProperties> defaultPropertiesOpt = AopUtils.getAnnotation(joinPoint, DefaultProperties.class);
252+
builder.defaultGroupKey(declaringClass.getSimpleName());
253+
if (defaultPropertiesOpt.isPresent()) {
254+
DefaultProperties defaultProperties = defaultPropertiesOpt.get();
255+
builder.defaultProperties(defaultProperties);
256+
if (StringUtils.isNotBlank(defaultProperties.groupKey())) {
257+
builder.defaultGroupKey(defaultProperties.groupKey());
258+
}
259+
if (StringUtils.isNotBlank(defaultProperties.threadPoolKey())) {
260+
builder.defaultThreadPoolKey(defaultProperties.threadPoolKey());
261+
}
262+
}
263+
return builder;
264+
}
265+
266+
private static MetaHolder.Builder setFallbackMethod(MetaHolder.Builder builder, Class<?> declaringClass, Method commandMethod) {
267+
FallbackMethod fallbackMethod = MethodProvider.getInstance().getFallbackMethod(declaringClass, commandMethod);
268+
if (fallbackMethod.isPresent()) {
269+
fallbackMethod.validateReturnType(commandMethod);
270+
builder
271+
.fallbackMethod(fallbackMethod.getMethod())
272+
.fallbackExecutionType(ExecutionType.getExecutionType(fallbackMethod.getMethod().getReturnType()));
273+
}
274+
return builder;
275+
}
276+
242277
}

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

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,13 @@ Object process(Action action) throws Exception {
150150
if (isIgnorable(cause)) {
151151
throw new HystrixBadRequestException(cause.getMessage(), cause);
152152
}
153-
if (cause instanceof Exception) {
153+
if (cause instanceof RuntimeException) {
154+
throw (RuntimeException) cause;
155+
} else if (cause instanceof Exception) {
154156
throw (Exception) cause;
155157
} else {
156-
throw Throwables.propagate(cause);
158+
// instance of Throwable
159+
throw new CommandActionExecutionException(cause);
157160
}
158161
}
159162
return result;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ public Builder collapsedRequests(Collection<HystrixCollapser.CollapsedRequest<Ob
155155
* @param pIgnoreExceptions the exceptions to be ignored
156156
* @return this {@link HystrixCommandBuilder.Builder}
157157
*/
158-
public Builder ignoreExceptions(Class<? extends Throwable>[] pIgnoreExceptions) {
158+
public Builder ignoreExceptions(List<Class<? extends Throwable>> pIgnoreExceptions) {
159159
this.ignoreExceptions = ImmutableList.copyOf(pIgnoreExceptions);
160160
return this;
161161
}

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

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
2020
import com.netflix.hystrix.contrib.javanica.utils.FallbackMethod;
2121
import com.netflix.hystrix.contrib.javanica.utils.MethodProvider;
22-
import org.apache.commons.lang3.StringUtils;
2322
import org.apache.commons.lang3.Validate;
2423

2524
import java.lang.reflect.Method;
@@ -61,7 +60,7 @@ public HystrixCommandBuilder create(MetaHolder metaHolder, Collection<HystrixCol
6160
.collapsedRequests(collapsedRequests)
6261
.cacheResultInvocationContext(createCacheResultInvocationContext(metaHolder))
6362
.cacheRemoveInvocationContext(createCacheRemoveInvocationContext(metaHolder))
64-
.ignoreExceptions(metaHolder.getHystrixCommand().ignoreExceptions())
63+
.ignoreExceptions(metaHolder.getCommandIgnoreExceptions())
6564
.executionType(metaHolder.getExecutionType())
6665
.build();
6766
}
@@ -73,10 +72,10 @@ private void validateMetaHolder(MetaHolder metaHolder) {
7372

7473
private GenericSetterBuilder createGenericSetterBuilder(MetaHolder metaHolder) {
7574
GenericSetterBuilder.Builder setterBuilder = GenericSetterBuilder.builder()
76-
.groupKey(createGroupKey(metaHolder))
77-
.threadPoolKey(createThreadPoolKey(metaHolder))
78-
.commandKey(createCommandKey(metaHolder))
79-
.collapserKey(createCollapserKey(metaHolder))
75+
.groupKey(metaHolder.getCommandGroupKey())
76+
.threadPoolKey(metaHolder.getThreadPoolKey())
77+
.commandKey(metaHolder.getCommandKey())
78+
.collapserKey(metaHolder.getCollapserKey())
8079
.commandProperties(metaHolder.getCommandProperties())
8180
.threadPoolProperties(metaHolder.getThreadPoolProperties())
8281
.collapserProperties(metaHolder.getCollapserProperties());
@@ -86,31 +85,6 @@ private GenericSetterBuilder createGenericSetterBuilder(MetaHolder metaHolder) {
8685
return setterBuilder.build();
8786
}
8887

89-
private String createGroupKey(MetaHolder metaHolder) {
90-
return createKey(metaHolder.getHystrixCommand().groupKey(), metaHolder.getDefaultGroupKey());
91-
}
92-
93-
private String createThreadPoolKey(MetaHolder metaHolder) {
94-
// this key is created without default value because intrinsically Hystrix knows how to derive this key properly if it's absent
95-
return metaHolder.getHystrixCommand().threadPoolKey();
96-
}
97-
98-
private String createCommandKey(MetaHolder metaHolder) {
99-
return createKey(metaHolder.getHystrixCommand().commandKey(), metaHolder.getDefaultCommandKey());
100-
}
101-
102-
private String createCollapserKey(MetaHolder metaHolder) {
103-
if (metaHolder.isCollapserAnnotationPresent()) {
104-
return createKey(metaHolder.getHystrixCollapser().collapserKey(), metaHolder.getDefaultCollapserKey());
105-
}
106-
return null;
107-
}
108-
109-
private String createKey(String key, String defKey) {
110-
return StringUtils.isNotBlank(key) ? key : defKey;
111-
}
112-
113-
11488
private CommandActions createCommandActions(MetaHolder metaHolder) {
11589
CommandAction commandAction = createCommandAction(metaHolder);
11690
CommandAction fallbackAction = createFallbackAction(metaHolder);
@@ -148,6 +122,8 @@ private CommandAction createFallbackAction(MetaHolder metaHolder) {
148122
.observable(ExecutionType.OBSERVABLE == fallbackMethod.getExecutionType())
149123
.defaultCommandKey(fMethod.getName())
150124
.defaultGroupKey(metaHolder.getDefaultGroupKey())
125+
.defaultThreadPoolKey(metaHolder.getDefaultThreadPoolKey())
126+
.defaultProperties(metaHolder.getDefaultProperties().orNull())
151127
.hystrixCollapser(metaHolder.getHystrixCollapser())
152128
.observableExecutionMode(hystrixCommand.observableExecutionMode())
153129
.hystrixCommand(hystrixCommand).build();

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ private MetaHolder createCopy(MetaHolder source, ExecutionType executionType) {
8181
.defaultCollapserKey(source.getDefaultCollapserKey())
8282
.defaultCommandKey(source.getDefaultCommandKey())
8383
.defaultGroupKey(source.getDefaultGroupKey())
84+
.defaultThreadPoolKey(source.getDefaultThreadPoolKey())
85+
.defaultProperties(source.getDefaultProperties().orNull())
8486
.hystrixCollapser(source.getHystrixCollapser())
8587
.hystrixCommand(source.getHystrixCommand()).build();
8688
}
@@ -100,6 +102,8 @@ private MetaHolder createCopy(MetaHolder source, ExecutionType executionType, Ob
100102
.defaultCollapserKey(source.getDefaultCollapserKey())
101103
.defaultCommandKey(source.getDefaultCommandKey())
102104
.defaultGroupKey(source.getDefaultGroupKey())
105+
.defaultThreadPoolKey(source.getDefaultThreadPoolKey())
106+
.defaultProperties(source.getDefaultProperties().orNull())
103107
.hystrixCollapser(source.getHystrixCollapser())
104108
.hystrixCommand(source.getHystrixCommand()).build();
105109
}

0 commit comments

Comments
 (0)