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

iss993: added DefaultProperties annotation [ready for review] #1259

Merged
merged 12 commits into from
Jul 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
49 changes: 47 additions & 2 deletions hystrix-contrib/hystrix-javanica/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# 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 @@ -324,7 +326,32 @@ Based on [this](https://github.com/Netflix/Hystrix/wiki/How-To-Use#ErrorPropagat
}
```

If `userResource.getUserById(id);` throws an exception which type is _BadRequestException_ then this exception will be thrown without triggering fallback logic.
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.

*Note*: If command has a fallback then only first exception that trigers fallback logic will be propagated to caller. Example:

```java
class Service {
@HystrixCommand(fallbackMethod = "fallback")
Object command(Object o) throws CommandException {
throw new CommandException();
}

@HystrixCommand
Object fallback(Object o) throws FallbackException {
throw new FallbackException();
}
}

// in client code
{
try {
service.command(null);
} catch (Exception e) {
assert CommandException.class.equals(e.getClass())
}
}
```

## Request Cache

Expand Down Expand Up @@ -529,6 +556,24 @@ 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.
Example:

```java
@DefaultProperties(groupKey = "DefaultGroupKey")
class Service {
@HystrixCommand // hystrix command group key is 'DefaultGroupKey'
public Object commandInheritsDefaultProperties() {
return null;
}
@HystrixCommand(groupKey = "SpecificGroupKey") // command overrides default group key
public Object commandOverridesGroupKey() {
return null;
}
}
```

## Hystrix collapser

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

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

**It's recommended to use Javaniva 1.4.+**
**It's recommended to use Javanica 1.4.+**
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/**
* Copyright 2016 Netflix, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.netflix.hystrix.contrib.javanica.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* This annotation is used to specify default parameters for
* hystrix commands (methods annotated with {@code @HystrixCommand} annotation).
*
* @author dmgcodevil
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface DefaultProperties {

/**
* Specifies default group key used for each hystrix command by default unless a command specifies group key explicitly.
* For additional info about this property see {@link HystrixCommand#groupKey()}.
*
* @return default group key
*/
String groupKey() default "";

/**
* Specifies default thread pool key used for each hystrix command by default unless a command specifies thread pool key explicitly.
* For additional info about this property see {@link HystrixCommand#threadPoolKey()}
*
* @return default thread pool
*/
String threadPoolKey() default "";

/**
* Specifies command properties that will be used for
* each hystrix command be default unless command properties explicitly specified in @HystrixCommand.
*
* @return command properties
*/
HystrixProperty[] commandProperties() default {};

/**
* Specifies thread pool properties that will be used for
* each hystrix command be default unless thread pool properties explicitly specified in @HystrixCommand.
*
* @return thread pool properties
*/
HystrixProperty[] threadPoolProperties() default {};

/**
* Defines exceptions which should be ignored and wrapped to throw in HystrixBadRequestException.
* All methods annotated with @HystrixCommand will automatically inherit this property.
*
*
* @return exceptions to ignore
*/
Class<? extends Throwable>[] ignoreExceptions() default {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,24 @@
*/
package com.netflix.hystrix.contrib.javanica.aop.aspectj;

import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.netflix.hystrix.HystrixExecutable;
import com.netflix.hystrix.HystrixInvokable;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.command.CommandExecutor;
import com.netflix.hystrix.contrib.javanica.command.ExecutionType;
import com.netflix.hystrix.contrib.javanica.command.HystrixCommandFactory;
import com.netflix.hystrix.contrib.javanica.command.MetaHolder;
import com.netflix.hystrix.contrib.javanica.exception.CommandActionExecutionException;
import com.netflix.hystrix.contrib.javanica.utils.AopUtils;
import com.netflix.hystrix.contrib.javanica.utils.FallbackMethod;
import com.netflix.hystrix.contrib.javanica.utils.MethodProvider;
import com.netflix.hystrix.exception.HystrixBadRequestException;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
Expand Down Expand Up @@ -90,10 +95,21 @@ public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinP
result = CommandExecutor.execute(invokable, executionType, metaHolder);
} catch (HystrixBadRequestException e) {
throw e.getCause();
} catch (HystrixRuntimeException e) {
throw getCauseOrDefault(e, e);
}
return result;
}

private Throwable getCauseOrDefault(RuntimeException e, RuntimeException defaultException) {
if (e.getCause() == null) return defaultException;
if (e.getCause() instanceof CommandActionExecutionException) {
CommandActionExecutionException commandActionExecutionException = (CommandActionExecutionException) e.getCause();
return Optional.fromNullable(commandActionExecutionException.getCause()).or(defaultException);
}
return e.getCause();
}

/**
* A factory to create MetaHolder depending on {@link HystrixPointcutType}.
*/
Expand All @@ -111,19 +127,10 @@ public MetaHolder create(final ProceedingJoinPoint joinPoint) {
MetaHolder.Builder metaHolderBuilder(Object proxy, Method method, Object obj, Object[] args, final ProceedingJoinPoint joinPoint) {
MetaHolder.Builder builder = MetaHolder.builder()
.args(args).method(method).obj(obj).proxyObj(proxy)
.defaultGroupKey(obj.getClass().getSimpleName())
.joinPoint(joinPoint);
if (isCompileWeaving()) {
builder.ajcMethod(getAjcMethodFromTarget(joinPoint));
}

FallbackMethod fallbackMethod = MethodProvider.getInstance().getFallbackMethod(obj.getClass(), method);
if (fallbackMethod.isPresent()) {
fallbackMethod.validateReturnType(method);
builder
.fallbackMethod(fallbackMethod.getMethod())
.fallbackExecutionType(ExecutionType.getExecutionType(fallbackMethod.getMethod().getReturnType()));
}
setFallbackMethod(builder, obj.getClass(), method);
builder = setDefaultProperties(builder, obj.getClass(), joinPoint);
return builder;
}
}
Expand Down Expand Up @@ -173,10 +180,8 @@ public MetaHolder create(Object proxy, Method collapserMethod, Object obj, Objec
}
// method of batch hystrix command must be passed to metaholder because basically collapser doesn't have any actions
// that should be invoked upon intercepted method, it's required only for underlying batch command
MetaHolder.Builder builder = MetaHolder.builder()
.args(args).method(batchCommandMethod).obj(obj).proxyObj(proxy)
.defaultGroupKey(obj.getClass().getSimpleName())
.joinPoint(joinPoint);

MetaHolder.Builder builder = metaHolderBuilder(proxy, batchCommandMethod, obj, args, joinPoint);

if (isCompileWeaving()) {
builder.ajcMethod(getAjcMethodAroundAdvice(obj.getClass(), batchCommandMethod.getName(), List.class));
Expand Down Expand Up @@ -206,6 +211,9 @@ public MetaHolder create(Object proxy, Method method, Object obj, Object[] args,
HystrixCommand hystrixCommand = method.getAnnotation(HystrixCommand.class);
ExecutionType executionType = ExecutionType.getExecutionType(method.getReturnType());
MetaHolder.Builder builder = metaHolderBuilder(proxy, method, obj, args, joinPoint);
if (isCompileWeaving()) {
builder.ajcMethod(getAjcMethodFromTarget(joinPoint));
}
return builder.defaultCommandKey(method.getName())
.hystrixCommand(hystrixCommand)
.observableExecutionMode(hystrixCommand.observableExecutionMode())
Expand Down Expand Up @@ -239,4 +247,31 @@ private static Class<?> getGenericParameter(Type type) {
}
}

private static MetaHolder.Builder setDefaultProperties(MetaHolder.Builder builder, Class<?> declaringClass, final ProceedingJoinPoint joinPoint) {
Optional<DefaultProperties> defaultPropertiesOpt = AopUtils.getAnnotation(joinPoint, DefaultProperties.class);
builder.defaultGroupKey(declaringClass.getSimpleName());
if (defaultPropertiesOpt.isPresent()) {
DefaultProperties defaultProperties = defaultPropertiesOpt.get();
builder.defaultProperties(defaultProperties);
if (StringUtils.isNotBlank(defaultProperties.groupKey())) {
builder.defaultGroupKey(defaultProperties.groupKey());
}
if (StringUtils.isNotBlank(defaultProperties.threadPoolKey())) {
builder.defaultThreadPoolKey(defaultProperties.threadPoolKey());
}
}
return builder;
}

private static MetaHolder.Builder setFallbackMethod(MetaHolder.Builder builder, Class<?> declaringClass, Method commandMethod) {
FallbackMethod fallbackMethod = MethodProvider.getInstance().getFallbackMethod(declaringClass, commandMethod);
if (fallbackMethod.isPresent()) {
fallbackMethod.validateReturnType(commandMethod);
builder
.fallbackMethod(fallbackMethod.getMethod())
.fallbackExecutionType(ExecutionType.getExecutionType(fallbackMethod.getMethod().getReturnType()));
}
return builder;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,13 @@ Object process(Action action) throws Exception {
if (isIgnorable(cause)) {
throw new HystrixBadRequestException(cause.getMessage(), cause);
}
if (cause instanceof Exception) {
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Exception) {
throw (Exception) cause;
} else {
throw Throwables.propagate(cause);
// instance of Throwable
throw new CommandActionExecutionException(cause);
}
}
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public Builder collapsedRequests(Collection<HystrixCollapser.CollapsedRequest<Ob
* @param pIgnoreExceptions the exceptions to be ignored
* @return this {@link HystrixCommandBuilder.Builder}
*/
public Builder ignoreExceptions(Class<? extends Throwable>[] pIgnoreExceptions) {
public Builder ignoreExceptions(List<Class<? extends Throwable>> pIgnoreExceptions) {
this.ignoreExceptions = ImmutableList.copyOf(pIgnoreExceptions);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.utils.FallbackMethod;
import com.netflix.hystrix.contrib.javanica.utils.MethodProvider;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;

import java.lang.reflect.Method;
Expand Down Expand Up @@ -61,7 +60,7 @@ public HystrixCommandBuilder create(MetaHolder metaHolder, Collection<HystrixCol
.collapsedRequests(collapsedRequests)
.cacheResultInvocationContext(createCacheResultInvocationContext(metaHolder))
.cacheRemoveInvocationContext(createCacheRemoveInvocationContext(metaHolder))
.ignoreExceptions(metaHolder.getHystrixCommand().ignoreExceptions())
.ignoreExceptions(metaHolder.getCommandIgnoreExceptions())
.executionType(metaHolder.getExecutionType())
.build();
}
Expand All @@ -73,10 +72,10 @@ private void validateMetaHolder(MetaHolder metaHolder) {

private GenericSetterBuilder createGenericSetterBuilder(MetaHolder metaHolder) {
GenericSetterBuilder.Builder setterBuilder = GenericSetterBuilder.builder()
.groupKey(createGroupKey(metaHolder))
.threadPoolKey(createThreadPoolKey(metaHolder))
.commandKey(createCommandKey(metaHolder))
.collapserKey(createCollapserKey(metaHolder))
.groupKey(metaHolder.getCommandGroupKey())
.threadPoolKey(metaHolder.getThreadPoolKey())
.commandKey(metaHolder.getCommandKey())
.collapserKey(metaHolder.getCollapserKey())
.commandProperties(metaHolder.getCommandProperties())
.threadPoolProperties(metaHolder.getThreadPoolProperties())
.collapserProperties(metaHolder.getCollapserProperties());
Expand All @@ -86,31 +85,6 @@ private GenericSetterBuilder createGenericSetterBuilder(MetaHolder metaHolder) {
return setterBuilder.build();
}

private String createGroupKey(MetaHolder metaHolder) {
return createKey(metaHolder.getHystrixCommand().groupKey(), metaHolder.getDefaultGroupKey());
}

private String createThreadPoolKey(MetaHolder metaHolder) {
// this key is created without default value because intrinsically Hystrix knows how to derive this key properly if it's absent
return metaHolder.getHystrixCommand().threadPoolKey();
}

private String createCommandKey(MetaHolder metaHolder) {
return createKey(metaHolder.getHystrixCommand().commandKey(), metaHolder.getDefaultCommandKey());
}

private String createCollapserKey(MetaHolder metaHolder) {
if (metaHolder.isCollapserAnnotationPresent()) {
return createKey(metaHolder.getHystrixCollapser().collapserKey(), metaHolder.getDefaultCollapserKey());
}
return null;
}

private String createKey(String key, String defKey) {
return StringUtils.isNotBlank(key) ? key : defKey;
}


private CommandActions createCommandActions(MetaHolder metaHolder) {
CommandAction commandAction = createCommandAction(metaHolder);
CommandAction fallbackAction = createFallbackAction(metaHolder);
Expand Down Expand Up @@ -148,6 +122,8 @@ private CommandAction createFallbackAction(MetaHolder metaHolder) {
.observable(ExecutionType.OBSERVABLE == fallbackMethod.getExecutionType())
.defaultCommandKey(fMethod.getName())
.defaultGroupKey(metaHolder.getDefaultGroupKey())
.defaultThreadPoolKey(metaHolder.getDefaultThreadPoolKey())
.defaultProperties(metaHolder.getDefaultProperties().orNull())
.hystrixCollapser(metaHolder.getHystrixCollapser())
.observableExecutionMode(hystrixCommand.observableExecutionMode())
.hystrixCommand(hystrixCommand).build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ private MetaHolder createCopy(MetaHolder source, ExecutionType executionType) {
.defaultCollapserKey(source.getDefaultCollapserKey())
.defaultCommandKey(source.getDefaultCommandKey())
.defaultGroupKey(source.getDefaultGroupKey())
.defaultThreadPoolKey(source.getDefaultThreadPoolKey())
.defaultProperties(source.getDefaultProperties().orNull())
.hystrixCollapser(source.getHystrixCollapser())
.hystrixCommand(source.getHystrixCommand()).build();
}
Expand All @@ -100,6 +102,8 @@ private MetaHolder createCopy(MetaHolder source, ExecutionType executionType, Ob
.defaultCollapserKey(source.getDefaultCollapserKey())
.defaultCommandKey(source.getDefaultCommandKey())
.defaultGroupKey(source.getDefaultGroupKey())
.defaultThreadPoolKey(source.getDefaultThreadPoolKey())
.defaultProperties(source.getDefaultProperties().orNull())
.hystrixCollapser(source.getHystrixCollapser())
.hystrixCommand(source.getHystrixCommand()).build();
}
Expand Down
Loading