Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

hystrix-javanica: initial commit

  • Loading branch information...
commit c9a73cd594dd7df6a1fc5a3eea42438ba8f18e8c 1 parent 0de2eb6
@dmgcodevil dmgcodevil authored
Showing with 2,763 additions and 0 deletions.
  1. +169 −0 hystrix-contrib/hystrix-javanica/README.md
  2. +38 −0 hystrix-contrib/hystrix-javanica/build.gradle
  3. +66 −0 ...ontrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCollapser.java
  4. +93 −0 ...-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCommand.java
  5. +47 −0 ...contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixProperty.java
  6. +28 −0 hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/ProxyType.java
  7. +77 −0 ...hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCollapserAspect.java
  8. +94 −0 ...b/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java
  9. +72 −0 ...-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/collapser/CollapserResult.java
  10. +109 −0 ...contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/collapser/CommandCollapser.java
  11. +112 −0 ...rib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/AbstractHystrixCommand.java
  12. +110 −0 ...trix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/AbstractHystrixCommandFactory.java
  13. +59 −0 hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/AsyncCommand.java
  14. +69 −0 ...ontrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/BatchHystrixCommand.java
  15. +44 −0 ...hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/BatchHystrixCommandFactory.java
  16. +70 −0 hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandAction.java
  17. +38 −0 ...rib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandExecutionAction.java
  18. +73 −0 ...ntrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandSetterBuilder.java
  19. +85 −0 ...rix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/GenericCommand.java
  20. +44 −0 ...strix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/GenericHystrixCommandFactory.java
  21. +38 −0 ...trib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/HystrixCommandFactory.java
  22. +184 −0 hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/MetaHolder.java
  23. +99 −0 ...trib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/conf/HystrixPropertiesManager.java
  24. +51 −0 ...-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/exception/CommandActionExecutionException.java
  25. +128 −0 hystrix-contrib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/utils/AopUtils.java
  26. 0  hystrix-contrib/hystrix-javanica/src/main/resources/dummy.txt
  27. +64 −0 hystrix-contrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/CommonUtils.java
  28. +11 −0 ...ib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/conf/AopCglibConfig.java
  29. +11 −0 ...trib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/conf/AopJdkConfig.java
  30. +13 −0 ...-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/conf/AopLoadTimeWeavingConfig.java
  31. +22 −0 ...-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/conf/SpringApplicationContext.java
  32. +27 −0 ...hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/client/RestClient.java
  33. +102 −0 ...x-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/client/SimpleRestClient.java
  34. +110 −0 ...anica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/collapser/BaseRestClientTest.java
  35. +13 −0 ...nica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/collapser/CglibRestClientTest.java
  36. +12 −0 ...vanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/collapser/JdkRestClientTest.java
  37. +166 −0 ...avanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/command/BaseRestClientTest.java
  38. +11 −0 ...vanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/command/CglibRestClientTest.java
  39. +11 −0 ...javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/command/JdkRestClientTest.java
  40. +12 −0 ...vanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/command/LoadTimeWeavingTest.java
  41. +69 −0 ...ntrib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/domain/User.java
  42. +20 −0 ...nica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/exception/BadRequestException.java
  43. +20 −0 ...vanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/exception/NotFoundException.java
  44. +71 −0 ...rix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/resource/UserResource.java
  45. 0  hystrix-contrib/hystrix-javanica/src/test/resources/dummy.txt
  46. +1 −0  settings.gradle
View
169 hystrix-contrib/hystrix-javanica/README.md
@@ -0,0 +1,169 @@
+# hystrix-javanica
+
+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.
+
+First of all in order to use hystrix-javanica you need to add hystrix-javanica dependency in your project.
+
+Example for Maven:
+```xml
+<dependency>
+ <groupId>com.netflix.hystrix</groupId>
+ <artifactId>hystrix-javanica</artifactId>
+ <version>x.y.z</version>
+</dependency>
+```
+
+To implement AOP functionality in the project was used AspectJ library. If in your project already used AspectJ then you need to add hystrix aspect in aop.xml as below:
+```xml
+<aspects>
+ ...
+ <aspect name="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixAspect"/>
+ ...
+</aspects>
+```
+More about AspectJ configuration read [here] (http://www.eclipse.org/aspectj/doc/next/devguide/ltw-configuration.html)
+
+
+If you use Spring AOP in your project then you need to add specific configuration using Spring aop namespace in order to make Spring capable to manage aspects which were written using AspectJ and declare HystrixAspect as Spring bean like below:
+
+```xml
+ <aop:aspectj-autoproxy/>
+ <bean id="hystrixAspect" class="com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixAspect"></bean>
+```
+
+It doesn't matter which approach you use to create proxies in Spring, javanica works fine with JDK and CGLIB proxies. If you use another framework for aop which supports AspectJ and uses other libs (Javassist for instance) to create proxies then let us know what lib you use to create proxies and we'll try to add support for this library in near future.
+
+More about Spring AOP + AspectJ read [here] (http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/aop.html)
+
+# How to use
+
+## Hystrix command
+
+To run method as Hystrix command you need to annotate method with @HystrixCommand annotation, for example
+```java
+ @HystrixCommand(fallbackMethod = "defaultUser")
+ public User getUserById(String id) {
+ return userResource.getUserById(id);
+ }
+
+ private User defaultUser(String id) {
+ return new User();
+ }
+```
+In example above the 'getUserById' method will be processed [synchronously](https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-Synchronous-Execution) as Hystrix command. Fallback method can be private or public. Method 'defaultUser' will be used to process fallback logic in a case of any errors. If you need to run 'defaultUser' as command then you can annotate it with HystrixCommand annotation as below:
+```java
+ @HystrixCommand(fallbackMethod = "defaultUser")
+ public User getUserById(String id) {
+ return userResource.getUserById(id);
+ }
+
+ @HystrixCommand
+ private User defaultUser(String id) {
+ return new User();
+ }
+```
+
+If fallback method was marked with @HystrixCommand then this method also can has own fallback method, as in the example below:
+```java
+ @HystrixCommand(fallbackMethod = "defaultUser")
+ public User getUserById(String id) {
+ return userResource.getUserById(id);
+ }
+
+ @HystrixCommand(fallbackMethod = "defaultUserSecond")
+ private User defaultUser(String id) {
+ return new User();
+ }
+
+ @HystrixCommand
+ private User defaultUserSecond(String id) {
+ return new User("def", "def");
+ }
+```
+
+Its important to remember that Hystrix command and fallback should be placed in the same class.
+
+To process Hystrix command asynchronously you should return an instance of AsyncCommand in your command method as in the exapmple below:
+```java
+ @HystrixCommand
+ public Future<User> getUserByIdAsync(final String id) {
+ return new AsyncCommand<User>() {
+ @Override
+ public User invoke() {
+ return userResource.getUserById(id);
+ }
+ };
+ }
+```
+
+The return type of command method should be Future that indicates that a command should be executed [asynchronously] (https://github.com/Netflix/Hystrix/wiki/How-To-Use#wiki-Asynchronous-Execution).
+
+Command properties can be set using commandProperties field like below:
+```java
+
+ @HystrixCommand(fallbackMethod = "defaultUser",
+ commandProperties = {
+ @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500")
+ })
+ public User getUserById(String id) {
+ return userResource.getUserById(id);
+ }
+```
+
+
+Javanica sets command properties using Hystrix ConfigurationManager.
+For the example above Javanica behind the scenes performs next action:
+```java
+ConfigurationManager.getConfigInstance().setProperty("hystrix.command.getUserById.execution.isolation.thread.timeoutInMilliseconds", "500");
+```
+
+More about Hystrix command properties [command](https://github.com/Netflix/Hystrix/wiki/Configuration#wiki-CommandExecution) and [fallback](https://github.com/Netflix/Hystrix/wiki/Configuration#wiki-CommandFallback)
+
+## Hystrix collapser
+
+Suppose you have some command which calls should be collapsed in one backend call. For this goal you can use HystrixCollapser annotation.
+
+Hystrix command:
+```java
+ @HystrixCommand
+ public User getUserById(String id) {
+ return userResource.getUserById(id);
+ }
+```
+
+Asynchronous call:
+```java
+ @HystrixCollapser(commandMethod = "getUserById",
+ collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "200")})
+ public Future<User> getUserByIdCollapserAsync(String id) {
+ return CollapserResult.async();
+ }
+```
+
+Synchronous call:
+```java
+ @HystrixCollapser(commandMethod = "getUserById",
+ collapserProperties = {@HystrixProperty(name = "maxRequestsInBatch", value = "3")})
+ @Override
+ public User getUserByIdCollapser(String id) {
+ return CollapserResult.sync();
+ }
+```
+
+Lines:
+```java
+ return CollapserResult.async();
+ return CollapserResult.sync();
+```
+In examples above don't affect the result of a collapser call. This just to avoid ```return null;``` statement in code.
+It's important to remember that Hystrix command and сollapser should be placed in the same class.
+
+Read more about Hystrix request collapsing [here] (https://github.com/Netflix/Hystrix/wiki/How-it-Works#wiki-RequestCollapsing)
+
+#Development Status and Future
+
+```java
+todo
+```
View
38 hystrix-contrib/hystrix-javanica/build.gradle
@@ -0,0 +1,38 @@
+apply plugin: 'java'
+apply plugin: 'eclipse'
+apply plugin: 'idea'
+
+aspectjVersion = '1.7.4'
+
+dependencies {
+ compile project(':hystrix-core')
+
+ compile "org.aspectj:aspectjweaver:$aspectjVersion"
+ compile "org.aspectj:aspectjrt:$aspectjVersion"
+
+ compile 'com.google.guava:guava:15.0'
+ compile 'commons-collections:commons-collections:3.2.1'
+ compile 'org.apache.commons:commons-lang3:3.1'
+ testCompile 'junit:junit-dep:4.10'
+ testCompile 'org.springframework:spring-core:4.0.0.RELEASE'
+ testCompile 'org.springframework:spring-context:4.0.0.RELEASE'
+ testCompile 'org.springframework:spring-aop:4.0.0.RELEASE'
+ testCompile 'org.springframework:spring-test:4.0.0.RELEASE'
+ testCompile 'cglib:cglib:3.1'
+}
+
+eclipse {
+ classpath {
+ // include 'provided' dependencies on the classpath
+ plusConfigurations += configurations.provided
+ downloadSources = true
+ downloadJavadoc = true
+ }
+}
+
+idea {
+ module {
+ // include 'provided' dependencies on the classpath
+ scopes.COMPILE.plus += configurations.provided
+ }
+}
View
66 ...anica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCollapser.java
@@ -0,0 +1,66 @@
+/**
+ * Copyright 2012 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 com.netflix.hystrix.HystrixCollapser.Scope;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation is used to mark some methods to collapse some commands into a single backend dependency call.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface HystrixCollapser {
+
+ /**
+ * Specifies a collapser key.
+ * <p/>
+ * default => the name of annotated method.
+ *
+ * @return collapser key.
+ */
+ String collapserKey() default "";
+
+ /**
+ * Defines what scope the collapsing should occur within.
+ * <p/>
+ * default => the {@link Scope#REQUEST}.
+ *
+ * @return {@link Scope}
+ */
+ Scope scope() default Scope.REQUEST;
+
+ /**
+ * Specifies name of a method which has @HystrixCommand annotation and should be collapsed.
+ *
+ * @return method name.
+ */
+ String commandMethod();
+
+ /**
+ * Specifies collapser properties.
+ *
+ * @return collapser properties
+ */
+ HystrixProperty[] collapserProperties() default {};
+
+}
View
93 ...avanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixCommand.java
@@ -0,0 +1,93 @@
+/**
+ * Copyright 2012 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.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+
+/**
+ * This annotation used to specify some methods which should be processes as hystrix commands.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface HystrixCommand {
+
+ /**
+ * The command group key is used for grouping together commands such as for reporting,
+ * alerting, dashboards or team/library ownership.
+ * <p/>
+ * default => the runtime class name of annotated method
+ *
+ * @return group key
+ */
+ String groupKey() default "";
+
+ /**
+ * Hystrix command key.
+ * <p/>
+ * default => the name of annotated method. for example:
+ * <code>
+ * ...
+ * @HystrixCommand
+ * public User getUserById(...)
+ * ...
+ * the command name will be: 'getUserById'
+ * </code>
+ *
+ * @return command key
+ */
+ String commandKey() default "";
+
+ /**
+ * The thread-pool key is used to represent a
+ * HystrixThreadPool for monitoring, metrics publishing, caching and other such uses.
+ *
+ * @return thread pool key
+ */
+ String threadPoolKey() default "";
+
+ /**
+ * Specifies a method to process fallback logic.
+ * A fallback method should be defined in the same class where is HystrixCommand.
+ * Also a fallback method should have same signature to a method which was invoked as hystrix command.
+ * for example:
+ * <code>
+ * @HystrixCommand(fallbackMethod = "getByIdFallback")
+ * public String getById(String id) {...}
+ *
+ * private String getByIdFallback(String id) {...}
+ * </code>
+ * Also a fallback method can be annotated with {@link HystrixCommand}
+ * <p/>
+ * default => see {@link com.netflix.hystrix.contrib.javanica.command.GenericCommand#getFallback()}
+ *
+ * @return method name
+ */
+ String fallbackMethod() default "";
+
+ /**
+ * Specifies command properties.
+ *
+ * @return command properties
+ */
+ HystrixProperty[] commandProperties() default {};
+}
+
View
47 ...vanica/src/main/java/com/netflix/hystrix/contrib/javanica/annotation/HystrixProperty.java
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2012 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.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation allows specify Hystrix command properties in the following format:
+ * property name = property value.
+ */
+@Target({ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+public @interface HystrixProperty {
+
+ /**
+ * Property name.
+ *
+ * @return name
+ */
+ String name();
+
+ /**
+ * Property value
+ *
+ * @return value
+ */
+ String value();
+
+}
View
28 ...ib/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/aop/ProxyType.java
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2012 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.aop;
+
+/**
+ * Represents enumeration of proxies types.
+ */
+public enum ProxyType {
+
+ JDK,
+
+ CGLIB,
+
+ UNKNOWN
+}
View
77 ...rc/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCollapserAspect.java
@@ -0,0 +1,77 @@
+/**
+ * Copyright 2012 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.aop.aspectj;
+
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
+import com.netflix.hystrix.contrib.javanica.collapser.CommandCollapser;
+import com.netflix.hystrix.contrib.javanica.command.MetaHolder;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Future;
+
+import org.apache.commons.lang3.Validate;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+
+import static com.netflix.hystrix.contrib.javanica.utils.AopUtils.getMethodFromTarget;
+
+
+/**
+ * AspectJ aspect to process methods which annotated with {@link HystrixCollapser} annotation.
+ */
+@Aspect
+public class HystrixCollapserAspect {
+
+ @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser)")
+ public void hystrixCollapserAnnotationPointcut() {
+ }
+
+ @Around("hystrixCollapserAnnotationPointcut()")
+ public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
+ HystrixCollapser hystrixCollapser;
+ Method collapserMethod = getMethodFromTarget(joinPoint);
+ Object obj = joinPoint.getTarget();
+ Object[] args = joinPoint.getArgs();
+
+ Validate.notNull(collapserMethod, "failed to get collapser method from joinPoint: %s", joinPoint);
+ hystrixCollapser = collapserMethod.getAnnotation(HystrixCollapser.class);
+
+
+ Method commandMethod = getMethodFromTarget(joinPoint, hystrixCollapser.commandMethod());
+
+ HystrixCommand hystrixCommand = commandMethod.getAnnotation(HystrixCommand.class);
+ Validate.notNull(hystrixCommand, "collapser should refer to method which was annotated with @HystrixCommand");
+
+ MetaHolder metaHolder = MetaHolder.builder()
+ .args(args)
+ .method(commandMethod)
+ .obj(obj)
+ .hystrixCollapser(hystrixCollapser)
+ .hystrixCommand(hystrixCommand)
+ .defaultCommandKey(commandMethod.getName())
+ .defaultCollapserKey(collapserMethod.getName())
+ .defaultGroupKey(obj.getClass().getSimpleName()).build();
+ if (collapserMethod.getReturnType().isAssignableFrom(Future.class)) {
+ return new CommandCollapser(metaHolder).queue();
+ } else {
+ return new CommandCollapser(metaHolder).execute();
+ }
+ }
+
+}
View
94 .../src/main/java/com/netflix/hystrix/contrib/javanica/aop/aspectj/HystrixCommandAspect.java
@@ -0,0 +1,94 @@
+/**
+ * Copyright 2012 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.aop.aspectj;
+
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
+import com.netflix.hystrix.contrib.javanica.command.AsyncCommand;
+import com.netflix.hystrix.contrib.javanica.command.GenericCommand;
+import com.netflix.hystrix.contrib.javanica.command.GenericHystrixCommandFactory;
+import com.netflix.hystrix.contrib.javanica.command.HystrixCommandFactory;
+import com.netflix.hystrix.contrib.javanica.command.MetaHolder;
+import org.apache.commons.lang3.Validate;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+
+import java.lang.reflect.Method;
+import java.util.concurrent.Future;
+
+import static com.netflix.hystrix.contrib.javanica.utils.AopUtils.getMethodFromTarget;
+import static org.slf4j.helpers.MessageFormatter.format;
+
+/**
+ * AspectJ aspect to process methods which annotated with {@link HystrixCommand} annotation.
+ */
+@Aspect
+public class HystrixCommandAspect {
+
+ private static final String ERROR_TYPE_MESSAGE = "return statement of '{}' method should returns instance of AsyncCommand class";
+ private static final String INVOKE_METHOD = "invoke";
+
+ @Pointcut("@annotation(com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand)")
+ public void hystrixCommandAnnotationPointcut() {
+ }
+
+ @Around("hystrixCommandAnnotationPointcut()")
+ public Object methodsAnnotatedWithHystrixCommand(final ProceedingJoinPoint joinPoint) throws Throwable {
+
+ HystrixCommand hystrixCommand;
+ Method method = getMethodFromTarget(joinPoint);
+ Object obj = joinPoint.getTarget();
+ Object[] args = joinPoint.getArgs();
+ Validate.notNull(method, "failed to get method from joinPoint: %s", joinPoint);
+ hystrixCommand = method.getAnnotation(HystrixCommand.class);
+
+ Object asyncObj = null;
+ Method asyncMethod = null;
+ boolean async = false;
+ if (method.getReturnType().isAssignableFrom(Future.class)) {
+ asyncObj = method.invoke(obj, args); // creates instance
+ if (!isAsyncCommand(asyncObj)) {
+ throw new RuntimeException(format(ERROR_TYPE_MESSAGE, method.getName()).getMessage());
+ }
+ asyncMethod = asyncObj.getClass().getMethod(INVOKE_METHOD);
+ async = true;
+ }
+
+ HystrixCommandFactory<GenericCommand> genericCommandHystrixCommandFactory = new GenericHystrixCommandFactory();
+ MetaHolder metaHolder = MetaHolder.builder()
+ .async(async)
+ .args(args)
+ .method(method)
+ .asyncMethod(asyncMethod)
+ .asyncObj(asyncObj)
+ .obj(obj)
+ .hystrixCommand(hystrixCommand)
+ .defaultCommandKey(method.getName())
+ .defaultGroupKey(obj.getClass().getSimpleName()).build();
+ GenericCommand genericCommand = genericCommandHystrixCommandFactory.create(metaHolder, null);
+ if (async) {
+ return genericCommand.queue();
+ } else {
+ return genericCommand.execute();
+ }
+ }
+
+ private boolean isAsyncCommand(Object instance) {
+ return instance instanceof AsyncCommand;
+ }
+
+}
View
72 ...avanica/src/main/java/com/netflix/hystrix/contrib/javanica/collapser/CollapserResult.java
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2012 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.collapser;
+
+import org.apache.commons.lang3.concurrent.ConcurrentUtils;
+
+import java.util.concurrent.Future;
+
+/**
+ * Used as stub in return statement of a collapser method. It doesn't affect the result of a collapser call.
+ * This stub is used just to avoid <code>return null</code> statement in code.
+ * Example (asynchronous):
+ * <p/>
+ * <code>
+ * @HystrixCollapser(commandMethod = "commandMethod")
+ * public Future<Result> asynchronous(String param) { return CollapserResult.async(); }
+ * </code>
+ * <p/>
+ * Example (synchronous):
+ * <p/>
+ * <code>
+ * @HystrixCollapser(commandMethod = "commandMethod")
+ * public Result synchronous(String param) { return CollapserResult.sync(); }
+ * </code>
+ * <p/>
+ * Hystrix command:
+ * <p/>
+ * <code>
+ * @HystrixCommand
+ * public Result commandMethod(String param) { return new Result(param); }
+ * </code>
+ */
+public final class CollapserResult {
+
+ private CollapserResult() {
+ throw new UnsupportedOperationException("It's prohibited to create instances of the class.");
+ }
+
+ /**
+ * This method is used in synchronous calls.
+ *
+ * @param <T> the result type
+ * @return null
+ */
+ public static <T> T sync() {
+ return null;
+ }
+
+ /**
+ * This method is used in asynchronous calls.
+ *
+ * @param <T> the result type
+ * @return fake instance of Future
+ */
+ public static <T> Future<T> async() {
+ return ConcurrentUtils.constantFuture(null);
+ }
+
+}
View
109 ...vanica/src/main/java/com/netflix/hystrix/contrib/javanica/collapser/CommandCollapser.java
@@ -0,0 +1,109 @@
+/**
+ * Copyright 2012 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.collapser;
+
+import com.netflix.hystrix.HystrixCollapser;
+import com.netflix.hystrix.HystrixCollapserKey;
+import com.netflix.hystrix.HystrixCommand;
+import com.netflix.hystrix.contrib.javanica.command.BatchHystrixCommandFactory;
+import com.netflix.hystrix.contrib.javanica.command.MetaHolder;
+import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Collapses multiple requests into a single {@link HystrixCommand} execution based
+ * on a time window and optionally a max batch size.
+ */
+public class CommandCollapser extends HystrixCollapser<List<Object>, Object, Object> {
+
+ private MetaHolder metaHolder;
+
+ /**
+ * Constructor with parameters.
+ *
+ * @param metaHolder the {@link MetaHolder}
+ */
+ public CommandCollapser(MetaHolder metaHolder) {
+ super(new CollapserSetterBuilder()
+ .collapserKey(metaHolder.getHystrixCollapser().collapserKey(), metaHolder.getDefaultCollapserKey())
+ .scope(metaHolder.getHystrixCollapser().scope())
+ .build());
+ HystrixPropertiesManager.setCollapserProperties(metaHolder.getHystrixCollapser().collapserProperties(),
+ getCollapserKey().name());
+ this.metaHolder = metaHolder;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Object getRequestArgument() {
+ return metaHolder.getArgs();
+ }
+
+ /**
+ * Creates batch command.
+ */
+ @Override
+ protected HystrixCommand<List<Object>> createCommand(
+ Collection<CollapsedRequest<Object, Object>> collapsedRequests) {
+ return BatchHystrixCommandFactory.getInstance().create(metaHolder, collapsedRequests);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void mapResponseToRequests(List<Object> batchResponse,
+ Collection<CollapsedRequest<Object, Object>> collapsedRequests) {
+ Validate.notNull(batchResponse, "batchResponse cannot be null");
+ Validate.isTrue(batchResponse.size() >= collapsedRequests.size(),
+ "size of batch response size should be gth or eq collapsed requests size");
+ int count = 0;
+ for (CollapsedRequest<Object, Object> request : collapsedRequests) {
+ request.setResponse(batchResponse.get(count++));
+ }
+ }
+
+ /**
+ * Builder for {@link Setter}.
+ */
+ private static final class CollapserSetterBuilder {
+
+ private String collapserKey;
+
+ private Scope scope;
+
+ private CollapserSetterBuilder collapserKey(String pCollapserKey, String def) {
+ this.collapserKey = StringUtils.isNotEmpty(pCollapserKey) ? pCollapserKey : def;
+ return this;
+ }
+
+ private CollapserSetterBuilder scope(Scope pScope) {
+ this.scope = pScope;
+ return this;
+ }
+
+ public Setter build() {
+ return Setter.withCollapserKey(HystrixCollapserKey.Factory.asKey(collapserKey)).andScope(scope);
+ }
+ }
+
+}
View
112 ...ca/src/main/java/com/netflix/hystrix/contrib/javanica/command/AbstractHystrixCommand.java
@@ -0,0 +1,112 @@
+/**
+ * Copyright 2012 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.command;
+
+
+import com.google.common.collect.Maps;
+import com.netflix.hystrix.HystrixCollapser;
+import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager;
+
+import java.util.Collection;
+import java.util.Map;
+import javax.annotation.concurrent.ThreadSafe;
+
+/**
+ * Base class for hystrix commands.
+ *
+ * @param <T> the return type
+ */
+@ThreadSafe
+public abstract class AbstractHystrixCommand<T> extends com.netflix.hystrix.HystrixCommand<T> {
+
+ private CommandAction commandAction;
+ private CommandAction fallbackAction;
+ private Map<String, Object> commandProperties = Maps.newHashMap();
+ private Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests;
+
+ /**
+ * Constructor with parameters.
+ *
+ * @param setterBuilder the builder to build {@link com.netflix.hystrix.HystrixCommand.Setter}
+ * @param commandAction the command action
+ * @param fallbackAction the fallback action
+ * @param commandProperties the command properties
+ * @param collapsedRequests the collapsed requests
+ */
+ protected AbstractHystrixCommand(CommandSetterBuilder setterBuilder,
+ CommandAction commandAction,
+ CommandAction fallbackAction,
+ Map<String, Object> commandProperties,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests) {
+ super(setterBuilder.build());
+ this.commandProperties = commandProperties;
+ this.collapsedRequests = collapsedRequests;
+ this.commandAction = commandAction;
+ this.fallbackAction = fallbackAction;
+ HystrixPropertiesManager.setCommandProperties(commandProperties, getCommandKey().name());
+ }
+
+ /**
+ * Gets command action.
+ *
+ * @return command action
+ */
+ CommandAction getCommandAction() {
+ return commandAction;
+ }
+
+ /**
+ * Gets fallback action.
+ *
+ * @return fallback action
+ */
+ CommandAction getFallbackAction() {
+ return fallbackAction;
+ }
+
+ /**
+ * Gets command properties.
+ *
+ * @return command properties
+ */
+ Map<String, Object> getCommandProperties() {
+ return commandProperties;
+ }
+
+ /**
+ * Gets collapsed requests.
+ *
+ * @return collapsed requests
+ */
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> getCollapsedRequests() {
+ return collapsedRequests;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ protected abstract T run() throws Exception;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ protected T getFallback() {
+ throw new RuntimeException("No fallback available.", getFailedExecutionException());
+ }
+
+}
View
110 ...main/java/com/netflix/hystrix/contrib/javanica/command/AbstractHystrixCommandFactory.java
@@ -0,0 +1,110 @@
+/**
+ * Copyright 2012 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.command;
+
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.Maps;
+import com.netflix.hystrix.HystrixCollapser;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Base implementation of {@link HystrixCommandFactory} interface.
+ *
+ * @param <T> the type of Hystrix command
+ */
+public abstract class AbstractHystrixCommandFactory<T extends AbstractHystrixCommand>
+ implements HystrixCommandFactory<T> {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public T create(MetaHolder metaHolder,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests) {
+ Validate.notNull(metaHolder.getHystrixCommand(), "hystrixCommand cannot be null");
+ String groupKey = StringUtils.isNotEmpty(metaHolder.getHystrixCommand().groupKey()) ?
+ metaHolder.getHystrixCommand().groupKey()
+ : metaHolder.getDefaultGroupKey();
+ String commandKey = StringUtils.isNotEmpty(metaHolder.getHystrixCommand().commandKey()) ?
+ metaHolder.getHystrixCommand().commandKey()
+ : metaHolder.getDefaultCommandKey();
+
+ CommandSetterBuilder setterBuilder = new CommandSetterBuilder();
+ setterBuilder.commandKey(commandKey);
+ setterBuilder.groupKey(groupKey);
+ setterBuilder.threadPoolKey(metaHolder.getHystrixCommand().threadPoolKey());
+ CommandAction action;
+ if (metaHolder.isAsync()) {
+ action = new CommandAction(metaHolder.getAsyncObj(), metaHolder.getAsyncMethod());
+ } else {
+ action = new CommandAction(metaHolder.getObj(), metaHolder.getMethod(), metaHolder.getArgs());
+ }
+ Map<String, Object> commandProperties = getCommandProperties(metaHolder.getHystrixCommand());
+ CommandAction fallbackAction = createFallbackAction(metaHolder, collapsedRequests);
+ return create(setterBuilder, commandProperties, action, fallbackAction, collapsedRequests);
+ }
+
+ CommandAction createFallbackAction(MetaHolder metaHolder,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests) {
+ String fallbackMethodName = metaHolder.getHystrixCommand().fallbackMethod();
+ CommandAction fallbackAction = null;
+ if (StringUtils.isNotEmpty(fallbackMethodName)) {
+ try {
+ Method fallbackMethod = metaHolder.getObj().getClass()
+ .getDeclaredMethod(fallbackMethodName, metaHolder.getParameterTypes());
+ if (fallbackMethod.isAnnotationPresent(HystrixCommand.class)) {
+ fallbackMethod.setAccessible(true);
+ MetaHolder fmMetaHolder = MetaHolder.builder()
+ .obj(metaHolder.getObj())
+ .method(fallbackMethod)
+ .args(metaHolder.getArgs())
+ .defaultCollapserKey(metaHolder.getDefaultCollapserKey())
+ .defaultCommandKey(fallbackMethod.getName())
+ .defaultGroupKey(metaHolder.getDefaultGroupKey())
+ .hystrixCollapser(metaHolder.getHystrixCollapser())
+ .hystrixCommand(fallbackMethod.getAnnotation(HystrixCommand.class)).build();
+ fallbackAction = new CommandExecutionAction(create(fmMetaHolder, collapsedRequests));
+ } else {
+ fallbackAction = new CommandAction(metaHolder.getObj(), fallbackMethod, metaHolder.getArgs());
+ }
+ } catch (NoSuchMethodException e) {
+ Throwables.propagate(e);
+ }
+ }
+ return fallbackAction;
+ }
+
+ abstract T create(CommandSetterBuilder setterBuilder, Map<String, Object> commandProperties, CommandAction action,
+ CommandAction fallbackAction,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests);
+
+ private Map<String, Object> getCommandProperties(HystrixCommand hystrixCommand) {
+ Map<String, Object> commandProperties = Maps.newHashMap();
+ for (HystrixProperty commandProperty : hystrixCommand.commandProperties()) {
+ commandProperties.put(commandProperty.name(), commandProperty.value());
+ }
+ return commandProperties;
+ }
+
+}
View
59 ...rix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/AsyncCommand.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright 2012 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.command;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Fake implementation of Future. Provides abstract invoke method to process an asynchronous call.
+ *
+ * @param <T> the type of result
+ */
+public abstract class AsyncCommand<T> implements Future<T> {
+
+ private static final String ERROR_MSG = "AsyncCommand is just a stab and cannot be used as complete implementation of Future";
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ throw new UnsupportedOperationException(ERROR_MSG);
+ }
+
+ @Override
+ public boolean isCancelled() {
+ throw new UnsupportedOperationException(ERROR_MSG);
+ }
+
+ @Override
+ public boolean isDone() {
+ throw new UnsupportedOperationException(ERROR_MSG);
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ throw new UnsupportedOperationException(ERROR_MSG);
+ }
+
+ @Override
+ public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ throw new UnsupportedOperationException(ERROR_MSG);
+ }
+
+ public abstract T invoke();
+
+}
View
69 ...anica/src/main/java/com/netflix/hystrix/contrib/javanica/command/BatchHystrixCommand.java
@@ -0,0 +1,69 @@
+/**
+ * Copyright 2012 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.command;
+
+
+import com.google.common.collect.Lists;
+import com.netflix.hystrix.HystrixCollapser;
+
+import javax.annotation.concurrent.ThreadSafe;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This command is used in collapser.
+ */
+@ThreadSafe
+public class BatchHystrixCommand extends AbstractHystrixCommand<List<Object>> {
+
+ private List<Object> response = Lists.newCopyOnWriteArrayList();
+
+ /**
+ * {@inheritDoc}
+ */
+ protected BatchHystrixCommand(CommandSetterBuilder setterBuilder, CommandAction commandAction,
+ CommandAction fallbackAction, Map<String, Object> commandProperties,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests) {
+ super(setterBuilder, commandAction, fallbackAction, commandProperties, collapsedRequests);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected List<Object> run() throws Exception {
+ for (HystrixCollapser.CollapsedRequest<Object, Object> request : getCollapsedRequests()) {
+ Object[] args = (Object[]) request.getArgument();
+ response.add(getCommandAction().execute(args));
+ }
+ return response;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected List<Object> getFallback() {
+ if (getFallbackAction() != null) {
+ response.add(getFallbackAction().execute());
+ return response;
+ } else {
+ return super.getFallback();
+ }
+ }
+
+}
View
44 ...rc/main/java/com/netflix/hystrix/contrib/javanica/command/BatchHystrixCommandFactory.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2012 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.command;
+
+import com.netflix.hystrix.HystrixCollapser;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Specific implementation of {@link HystrixCommandFactory} interface to create {@link BatchHystrixCommand} instances.
+ */
+public class BatchHystrixCommandFactory extends AbstractHystrixCommandFactory<BatchHystrixCommand>
+ implements HystrixCommandFactory<BatchHystrixCommand> {
+
+ private static final HystrixCommandFactory<BatchHystrixCommand> COMMAND_FACTORY = new BatchHystrixCommandFactory();
+
+ public static HystrixCommandFactory<BatchHystrixCommand> getInstance() {
+ return COMMAND_FACTORY;
+ }
+
+ @Override
+ BatchHystrixCommand create(CommandSetterBuilder setterBuilder,
+ Map<String, Object> commandProperties, CommandAction action,
+ CommandAction fallbackAction,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests) {
+ BatchHystrixCommand batchHystrixCommand = new BatchHystrixCommand(setterBuilder, action, fallbackAction,
+ commandProperties, collapsedRequests);
+ return batchHystrixCommand;
+ }
+}
View
70 ...ix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandAction.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright 2012 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.command;
+
+import com.netflix.hystrix.contrib.javanica.exception.CommandActionExecutionException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Simple wrapper to invoke a method using Java reflection API.
+ */
+public class CommandAction {
+
+ private static final Object[] EMPTY_ARGS = new Object[]{};
+ private Object object;
+ private Method method;
+ private Object[] _args;
+
+ protected CommandAction() {
+ }
+
+ protected CommandAction(Object object, Method method) {
+ this.object = object;
+ this.method = method;
+ this._args = EMPTY_ARGS;
+ }
+
+ protected CommandAction(Object object, Method method, Object[] args) {
+ this.object = object;
+ this.method = method;
+ this._args = args;
+ }
+
+ public Object execute() {
+ return execute(_args);
+ }
+
+ /**
+ * Invokes the method. Also private method also can be invoked.
+ *
+ * @return result of execution
+ */
+ public Object execute(Object[] args) {
+ Object result = null;
+ try {
+ method.setAccessible(true); // suppress Java language access
+ result = method.invoke(object, args);
+ } catch (IllegalAccessException e) {
+ throw new CommandActionExecutionException(e);
+ } catch (InvocationTargetException e) {
+ throw new CommandActionExecutionException(e);
+ }
+ return result;
+ }
+
+}
View
38 ...ca/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandExecutionAction.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2012 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.command;
+
+/**
+ * Action to execute a Hystrix command.
+ */
+public class CommandExecutionAction extends CommandAction {
+
+ private AbstractHystrixCommand hystrixCommand;
+
+ /**
+ * Constructor with parameters.
+ * @param hystrixCommand the hystrix command to execute.
+ */
+ public CommandExecutionAction(AbstractHystrixCommand hystrixCommand) {
+ this.hystrixCommand = hystrixCommand;
+ }
+
+ @Override
+ public Object execute() {
+ return hystrixCommand.execute();
+ }
+
+}
View
73 ...nica/src/main/java/com/netflix/hystrix/contrib/javanica/command/CommandSetterBuilder.java
@@ -0,0 +1,73 @@
+/**
+ * Copyright 2012 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.command;
+
+import com.netflix.hystrix.HystrixCommand;
+import com.netflix.hystrix.HystrixCommandGroupKey;
+import com.netflix.hystrix.HystrixCommandKey;
+import com.netflix.hystrix.HystrixThreadPoolKey;
+import org.apache.commons.lang3.StringUtils;
+
+/**
+ * Builder for {@link HystrixCommand.Setter}.
+ */
+public class CommandSetterBuilder {
+
+ private String groupKey;
+ private String commandKey;
+ private String threadPoolKey;
+
+ public CommandSetterBuilder groupKey(String pGroupKey) {
+ this.groupKey = pGroupKey;
+ return this;
+ }
+
+ public CommandSetterBuilder groupKey(String pGroupKey, String def) {
+ this.groupKey = StringUtils.isNotEmpty(pGroupKey) ? pGroupKey : def;
+ return this;
+ }
+
+ public CommandSetterBuilder commandKey(String pCommandKey) {
+ this.commandKey = pCommandKey;
+ return this;
+ }
+
+ public CommandSetterBuilder commandKey(String pCommandKey, String def) {
+ this.commandKey = StringUtils.isNotEmpty(pCommandKey) ? pCommandKey : def;
+ return this;
+ }
+
+ public CommandSetterBuilder threadPoolKey(String pThreadPoolKey) {
+ this.threadPoolKey = pThreadPoolKey;
+ return this;
+ }
+
+ /**
+ * Creates instance of {@link HystrixCommand.Setter}.
+ *
+ * @return the instance of {@link HystrixCommand.Setter}
+ */
+ public HystrixCommand.Setter build() {
+ HystrixCommand.Setter setter = HystrixCommand.Setter
+ .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))
+ .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));
+ if (StringUtils.isNotEmpty(threadPoolKey)) {
+ setter.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(threadPoolKey));
+ }
+ return setter;
+ }
+
+}
View
85 ...x-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/GenericCommand.java
@@ -0,0 +1,85 @@
+/**
+ * Copyright 2012 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.command;
+
+import com.netflix.hystrix.HystrixCollapser;
+import com.netflix.hystrix.contrib.javanica.exception.CommandActionExecutionException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * This command used to execute {@link CommandAction} as hystrix command.
+ * Basically any logic can be executed within {@link CommandAction}
+ * such as method invocation and etc.
+ */
+public class GenericCommand extends AbstractHystrixCommand<Object> {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GenericCommand.class);
+
+ private static final String EXECUTION_ERROR_MSG = "failed to process command action";
+
+ /**
+ * {@inheritDoc}
+ */
+ protected GenericCommand(CommandSetterBuilder setterBuilder, CommandAction commandAction,
+ CommandAction fallbackAction, Map<String, Object> commandProperties,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests) {
+ super(setterBuilder, commandAction, fallbackAction, commandProperties, collapsedRequests);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected Object run() throws Exception {
+ LOGGER.debug("execute command: {}", getCommandKey().name());
+ return process(getCommandAction());
+ }
+
+ /**
+ * The fallback is performed whenever a command execution fails.
+ * Also a fallback method will be invoked within separate command in the case if fallback method was annotated with
+ * HystrixCommand annotation, otherwise current implementation throws RuntimeException and leaves the caller to deal with it
+ * (see {@link super#getFallback()}).
+ *
+ * @return result of invocation of fallback method or RuntimeException
+ */
+ @Override
+ protected Object getFallback() {
+ return getFallbackAction() != null ? process(getFallbackAction()) : super.getFallback();
+ }
+
+ /**
+ * Executes action and in the case of any exceptions propagates it as {@link CommandActionExecutionException}
+ * runtime exception.
+ *
+ * @param action the command action
+ * @return result of command action execution
+ */
+ private Object process(CommandAction action) {
+ Object result;
+ try {
+ result = action.execute();
+ } catch (Throwable throwable) {
+ throw new CommandActionExecutionException(EXECUTION_ERROR_MSG, throwable);
+ }
+ return result;
+ }
+
+}
View
44 .../main/java/com/netflix/hystrix/contrib/javanica/command/GenericHystrixCommandFactory.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright 2012 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.command;
+
+import com.netflix.hystrix.HystrixCollapser;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ * Specific implementation of {@link HystrixCommandFactory} interface to create {@link GenericCommand} instances.
+ */
+public class GenericHystrixCommandFactory extends AbstractHystrixCommandFactory<GenericCommand>
+ implements HystrixCommandFactory<GenericCommand> {
+
+ private static final HystrixCommandFactory<GenericCommand> COMMAND_FACTORY = new GenericHystrixCommandFactory();
+
+ public static HystrixCommandFactory<GenericCommand> getInstance() {
+ return COMMAND_FACTORY;
+ }
+
+ @Override
+ GenericCommand create(CommandSetterBuilder setterBuilder, Map<String, Object> commandProperties,
+ CommandAction action, CommandAction fallbackAction,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests) {
+ GenericCommand genericCommand = new GenericCommand(setterBuilder, action, fallbackAction, commandProperties,
+ collapsedRequests);
+ return genericCommand;
+ }
+
+}
View
38 ...ica/src/main/java/com/netflix/hystrix/contrib/javanica/command/HystrixCommandFactory.java
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2012 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.command;
+
+import com.netflix.hystrix.HystrixCollapser;
+
+import java.util.Collection;
+
+/**
+ * Base factory interface for Hystrix commands.
+ *
+ * @param <T> the type of Hystrix command
+ */
+public interface HystrixCommandFactory<T extends AbstractHystrixCommand> {
+
+ /**
+ * Creates a Hystrix command.
+ *
+ * @param metaHolder the {@link MetaHolder}
+ * @param collapsedRequests the collapsed requests
+ * @return a Hystrix command
+ */
+ T create(MetaHolder metaHolder,
+ Collection<HystrixCollapser.CollapsedRequest<Object, Object>> collapsedRequests);
+}
View
184 ...strix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/command/MetaHolder.java
@@ -0,0 +1,184 @@
+/**
+ * Copyright 2012 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.command;
+
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * Simple holder to keep necessary information.
+ */
+@Immutable
+public class MetaHolder {
+
+ private HystrixCollapser hystrixCollapser;
+ private HystrixCommand hystrixCommand;
+
+ private Method method;
+ private Method asyncMethod;
+ private Object obj;
+ private Object asyncObj;
+ private Object[] args;
+
+ private String defaultGroupKey;
+ private String defaultCommandKey;
+ private String defaultCollapserKey;
+ private boolean async;
+
+ private MetaHolder(Builder builder) {
+ this.hystrixCommand = builder.hystrixCommand;
+ this.method = builder.method;
+ this.asyncMethod = builder.asyncMethod;
+ this.obj = builder.obj;
+ this.asyncObj = builder.asyncObj;
+ this.args = builder.args;
+ this.defaultGroupKey = builder.defaultGroupKey;
+ this.defaultCommandKey = builder.defaultCommandKey;
+ this.defaultCollapserKey = builder.defaultCollapserKey;
+ this.hystrixCollapser = builder.hystrixCollapser;
+ this.async = builder.async;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public HystrixCollapser getHystrixCollapser() {
+ return hystrixCollapser;
+ }
+
+ public HystrixCommand getHystrixCommand() {
+ return hystrixCommand;
+ }
+
+ public Method getMethod() {
+ return method; //todo return a copy
+ }
+
+ public Object getObj() {
+ return obj;
+ }
+
+ public Method getAsyncMethod() {
+ return asyncMethod;
+ }
+
+ public Object getAsyncObj() {
+ return asyncObj;
+ }
+
+ public boolean isAsync() {
+ return async;
+ }
+
+ public Object[] getArgs() {
+ return Arrays.copyOf(args, args.length);
+ }
+
+ public String getDefaultGroupKey() {
+ return defaultGroupKey;
+ }
+
+ public String getDefaultCommandKey() {
+ return defaultCommandKey;
+ }
+
+ public String getDefaultCollapserKey() {
+ return defaultCollapserKey;
+ }
+
+ public Class<?>[] getParameterTypes() {
+ return method.getParameterTypes();
+ }
+
+ public static final class Builder {
+
+ private HystrixCollapser hystrixCollapser;
+ private HystrixCommand hystrixCommand;
+ private Method method;
+ private Method asyncMethod;
+ private Object obj;
+ private Object asyncObj;
+ private Object[] args;
+ private String defaultGroupKey;
+ private String defaultCommandKey;
+ private String defaultCollapserKey;
+ private boolean async;
+
+ public Builder hystrixCollapser(HystrixCollapser hystrixCollapser) {
+ this.hystrixCollapser = hystrixCollapser;
+ return this;
+ }
+
+ public Builder hystrixCommand(HystrixCommand hystrixCommand) {
+ this.hystrixCommand = hystrixCommand;
+ return this;
+ }
+
+ public Builder method(Method method) {
+ this.method = method;
+ return this;
+ }
+
+ public Builder asyncMethod(Method asyncMethod) {
+ this.asyncMethod = asyncMethod;
+ return this;
+ }
+
+ public Builder obj(Object obj) {
+ this.obj = obj;
+ return this;
+ }
+
+ public Builder asyncObj(Object asyncObj) {
+ this.asyncObj = asyncObj;
+ return this;
+ }
+
+ public Builder args(Object[] args) {
+ this.args = args;
+ return this;
+ }
+
+ public Builder async(boolean async) {
+ this.async = async;
+ return this;
+ }
+
+ public Builder defaultGroupKey(String defGroupKey) {
+ this.defaultGroupKey = defGroupKey;
+ return this;
+ }
+
+ public Builder defaultCommandKey(String defCommandKey) {
+ this.defaultCommandKey = defCommandKey;
+ return this;
+ }
+
+ public Builder defaultCollapserKey(String defCollapserKey) {
+ this.defaultCollapserKey = defCollapserKey;
+ return this;
+ }
+
+ public MetaHolder build() {
+ return new MetaHolder(this);
+ }
+ }
+
+}
View
99 ...ica/src/main/java/com/netflix/hystrix/contrib/javanica/conf/HystrixPropertiesManager.java
@@ -0,0 +1,99 @@
+/**
+ * Copyright 2012 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.conf;
+
+import com.google.common.collect.Maps;
+import com.netflix.config.ConfigurationManager;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
+import java.text.MessageFormat;
+import java.util.Map;
+import org.apache.commons.collections.MapUtils;
+import org.apache.commons.lang3.Validate;
+
+/**
+ * This class provides methods to dynamically set Hystrix properties using {@link ConfigurationManager}.
+ */
+public final class HystrixPropertiesManager {
+
+ private HystrixPropertiesManager() {
+ }
+
+ private static final String COMMAND_PROPERTY_TEMPLATE = "hystrix.command.{0}.{1}";
+ private static final String COLLAPSER_PROPERTY_TEMPLATE = "hystrix.collapser.{0}.{1}";
+
+ /**
+ * Sets Hystrix command properties.
+ *
+ * @param commandProperties the command properties
+ * @param commandKey the command key
+ */
+ public static void setCommandProperties(Map<String, Object> commandProperties, String commandKey) {
+ setProperties(commandProperties, COMMAND_PROPERTY_TEMPLATE, commandKey);
+ }
+
+ /**
+ * Sets Hystrix collapser properties.
+ *
+ * @param collapserProperties the collapser properties
+ * @param collapserKey the collapser key
+ */
+ public static void setCollapserProperties(Map<String, Object> collapserProperties, String collapserKey) {
+ setProperties(collapserProperties, COLLAPSER_PROPERTY_TEMPLATE, collapserKey);
+ }
+
+ /**
+ * Sets Hystrix collapser properties.
+ *
+ * @param collapserProperties the collapser properties
+ * @param collapserKey the collapser key
+ */
+ public static void setCollapserProperties(HystrixProperty[] collapserProperties, String collapserKey) {
+ setCollapserProperties(toMap(collapserProperties), collapserKey);
+ }
+
+ public static Map<String, Object> getPropertiesAsMap(HystrixCommand hystrixCommand) {
+ return toMap(hystrixCommand.commandProperties());
+ }
+
+ public static Map<String, Object> getPropertiesAsMap(HystrixCollapser hystrixCollapser) {
+ return toMap(hystrixCollapser.collapserProperties());
+ }
+
+ public static Map<String, Object> toMap(HystrixProperty[] properties) {
+ Map<String, Object> propertiesMap = Maps.newHashMap();
+ for(HystrixProperty hystrixProperty : properties) {
+ validate(hystrixProperty);
+ propertiesMap.put(hystrixProperty.name(), hystrixProperty.value());
+ }
+ return propertiesMap;
+ }
+
+ private static void setProperties(Map<String, Object> properties, String propTemplate, String commandKey) {
+ if(MapUtils.isNotEmpty(properties)) {
+ for(Map.Entry<String, Object> property : properties.entrySet()) {
+ String propName = MessageFormat.format(propTemplate, commandKey, property.getKey());
+ ConfigurationManager.getConfigInstance().setProperty(propName, property.getValue());
+ }
+ }
+ }
+
+ private static void validate(HystrixProperty hystrixProperty) throws IllegalArgumentException {
+ Validate.notBlank(hystrixProperty.name(), "hystrix property name cannot be null");
+ }
+
+}
View
51 .../java/com/netflix/hystrix/contrib/javanica/exception/CommandActionExecutionException.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright 2012 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.exception;
+
+public class CommandActionExecutionException extends RuntimeException {
+
+ public CommandActionExecutionException() {
+ }
+
+ /**
+ * Constructor with message.
+ *
+ * @param message message text
+ */
+ public CommandActionExecutionException(String message) {
+ super(message);
+ }
+
+ /**
+ * Constructor with cause.
+ *
+ * @param cause the cause
+ */
+ public CommandActionExecutionException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Constructor with message and cause.
+ *
+ * @param message message text
+ * @param cause the cause
+ */
+ public CommandActionExecutionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
View
128 ...b/hystrix-javanica/src/main/java/com/netflix/hystrix/contrib/javanica/utils/AopUtils.java
@@ -0,0 +1,128 @@
+/**
+ * Copyright 2012 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.utils;
+
+import com.netflix.hystrix.contrib.javanica.aop.ProxyType;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * Provides common methods to retrieve information from JoinPoint and not only.
+ */
+public final class AopUtils {
+
+ private AopUtils() {
+ }
+
+ /**
+ * The CGLIB class separator character "$$"
+ */
+ public static final String CGLIB_CLASS_SEPARATOR = "$$";
+
+ /**
+ * Gets method from source object class (not proxy class).
+ *
+ * @param joinPoint the {@link JoinPoint}
+ * @return a method
+ */
+ public static Method getMethodFromTarget(JoinPoint joinPoint) {
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ return getDeclaredMethod(joinPoint.getTarget().getClass(), signature.getName(),
+ getParameterTypes(joinPoint));
+ }
+
+ public static Method getMethodFromTarget(JoinPoint joinPoint, String methodName) {
+ return getDeclaredMethod(joinPoint.getTarget().getClass(), methodName,
+ getParameterTypes(joinPoint));
+ }
+
+ public static Method getMethodFromThis(JoinPoint joinPoint) {
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ return getDeclaredMethod(joinPoint.getThis().getClass(), signature.getName(),
+ getParameterTypes(joinPoint));
+ }
+
+ public static Method getMethodFromThis(JoinPoint joinPoint, String methodName) {
+ return getDeclaredMethod(joinPoint.getThis().getClass(), methodName,
+ getParameterTypes(joinPoint));
+ }
+
+ public static String getTargetClassName(JoinPoint joinPoint) {
+ return joinPoint.getTarget().getClass().getSimpleName();
+ }
+
+ public static String getThisClassName(JoinPoint joinPoint) {
+ return joinPoint.getThis().getClass().getSimpleName();
+ }
+
+ public static Class[] getParameterTypes(JoinPoint joinPoint) {
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ Method method = signature.getMethod();
+ return method.getParameterTypes();
+ }
+
+ public static <T extends Annotation> Annotation getAnnotation(JoinPoint joinPoint, Class<T> aClass, String methodName) {
+ Method method = null;
+ if (ProxyType.JDK.equals(getProxyType(joinPoint.getThis()))) {
+ method = getMethodFromTarget(joinPoint, methodName);
+ } else {
+ method = getMethodFromThis(joinPoint, methodName);
+ }
+ return method.getAnnotation(aClass);
+ }
+
+ public static boolean isMethodExists(Class<?> type, String methodName, Class<?>... parameterTypes) {
+ return getDeclaredMethod(type, methodName, parameterTypes) != null;
+ }
+
+ public static Method getDeclaredMethod(Class<?> type, String methodName, Class<?>... parameterTypes) {
+ Method method = null;
+ try {
+ method = type.getDeclaredMethod(methodName, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ // do nothing
+ }
+ return method;
+ }
+
+ public static boolean isProxy(Object obj) {
+ return !ProxyType.UNKNOWN.equals(getProxyType(obj));
+ }
+
+ public static ProxyType getProxyType(Object obj) {
+ if (Proxy.isProxyClass(obj.getClass())) {
+ return ProxyType.JDK;
+ } else if (isCglibProxyClassName(obj.getClass().getName())) {
+ return ProxyType.CGLIB;
+ } else {
+ return ProxyType.UNKNOWN;
+ }
+ }
+
+ /**
+ * Check whether the specified class name is a CGLIB-generated class.
+ *
+ * @param className the class name to check
+ */
+ private static boolean isCglibProxyClassName(String className) {
+ return (className != null && className.contains(CGLIB_CLASS_SEPARATOR));
+ }
+
+}
View
0  hystrix-contrib/hystrix-javanica/src/main/resources/dummy.txt
No changes.
View
64 ...trib/hystrix-javanica/src/test/java/com/netflix/hystrix/contrib/javanica/CommonUtils.java
@@ -0,0 +1,64 @@
+package com.netflix.hystrix.contrib.javanica;
+
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import com.netflix.hystrix.HystrixCommand;
+import com.netflix.hystrix.HystrixCommandKey;
+import com.netflix.hystrix.HystrixCommandMetrics;
+import com.netflix.hystrix.HystrixRequestLog;
+
+import javax.annotation.Nullable;
+import java.util.Collection;
+import java.util.List;
+
+import static org.junit.Assert.assertTrue;
+
+public class CommonUtils {
+
+ public HystrixCommandMetrics getMetrics(String commandKey) {
+ return HystrixCommandMetrics.getInstance(HystrixCommandKey.Factory.asKey(commandKey));
+ }
+
+ public static void assertExecutedCommands(String... commands) {
+ Collection<HystrixCommand<?>> executedCommands =
+ HystrixRequestLog.getCurrentRequest().getExecutedCommands();
+
+ List<String> executedCommandsKeys = getExecutedCommandsKeys(Lists.newArrayList(executedCommands));
+
+ for (String cmd : commands) {
+ assertTrue("command: '" + cmd + "' wasn't executed", executedCommandsKeys.contains(cmd));
+ }
+ }
+
+ public static List<String> getExecutedCommandsKeys() {
+ Collection<HystrixCommand<?>> executedCommands =
+ HystrixRequestLog.getCurrentRequest().getExecutedCommands();
+
+ return getExecutedCommandsKeys(Lists.newArrayList(executedCommands));
+ }
+
+ public static List<String> getExecutedCommandsKeys(List<HystrixCommand<?>> executedCommands) {
+ return Lists.transform(executedCommands, new Function<HystrixCommand<?>, String>() {
+ @Nullable
+ @Override
+ public String apply(@Nullable HystrixCommand<?> input) {
+ return input.getCommandKey().name();
+ }
+ });
+ }
+
+ public static HystrixCommand getHystrixCommandByKey(String key) {
+ HystrixCommand hystrixCommand = null;
+ Collection<HystrixCommand<?>> executedCommands =
+ HystrixRequestLog.getCurrentRequest().getExecutedCommands();
+ for (HystrixCommand command : executedCommands) {
+ if (command.getCommandKey().name().equals(key)) {
+ hystrixCommand = command;
+ break;
+ }
+ }
+ return hystrixCommand;
+ }
+
+}
View
11 ...a/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/conf/AopCglibConfig.java
@@ -0,0 +1,11 @@
+package com.netflix.hystrix.contrib.javanica.test.spring.conf;
+
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.context.annotation.Import;
+
+@Configurable
+@Import(SpringApplicationContext.class)
+@EnableAspectJAutoProxy(proxyTargetClass = true)
+public class AopCglibConfig {
+}
View
11 ...ica/src/test/java/com/netflix/hystrix/contrib/javanica/test/spring/conf/AopJdkConfig.java
@@ -0,0 +1,11 @@
+package com.netflix.hystrix.contrib.javanica.test.spring.conf;
+
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.context.annotation.Import;
+
+@Configurable
+@Import(SpringApplicationContext.class)
+@EnableAspectJAutoProxy
+public class AopJdkConfig {
+}
View
13 .../java/com/netflix/hystrix/contrib/javanica/test/spring/conf/AopLoadTimeWeavingConfig.java
@@ -0,0 +1,13 @@
+package com.netflix.hystrix.contrib.javanica.test.spring.conf;
+
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.context.annotation.EnableAspectJAutoProxy;
+import org.springframework.context.annotation.EnableLoadTimeWeaving;
+import org.springframework.context.annotation.Import;
+
+@Configurable
+@Import(SpringApplicationContext.class)
+@EnableAspectJAutoProxy
+@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
+public class AopLoadTimeWeavingConfig {
+}
View
22 .../java/com/netflix/hystrix/contrib/javanica/test/spring/conf/SpringApplicationContext.java
@@ -0,0 +1,22 @@
+package com.netflix.hystrix.contrib.javanica.test.spring.conf;
+
+import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCollapserAspect;
+import com.netflix.hystrix.contrib.javanica.aop.aspectj.HystrixCommandAspect;
+import org.springframework.beans.factory.annotation.Configurable;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.ComponentScan;
+
+@Configurable
+@ComponentScan("com.netflix.hystrix.contrib.javanica.test.spring")
+public class SpringApplicationContext {
+
+ @Bean
+ public HystrixCommandAspect hystrixAspect() {
+ return new HystrixCommandAspect();
+ }
+
+ @Bean
+ public HystrixCollapserAspect hystrixCollapserAspect() {
+ return new HystrixCollapserAspect();
+ }
+}
View
27 ...rc/test/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/client/RestClient.java
@@ -0,0 +1,27 @@
+package com.netflix.hystrix.contrib.javanica.test.spring.rest.client;
+
+
+import com.netflix.hystrix.contrib.javanica.test.spring.rest.domain.User;
+
+import java.util.List;
+import java.util.concurrent.Future;
+
+public interface RestClient {
+
+ User getUserById(String id);
+
+ Future<User> getUserByIdAsync(String id);
+
+ User getUserByName(String name);
+
+ User getUserByIdSecondary(String id);
+
+ List<User> findAll(int pageNum, int pageSize);
+
+ Future<List<User>> findAllAsync(int pageNum, int pageSize);
+
+ Future<User> getUserByIdCollapserAsync(String id);
+
+ User getUserByIdCollapser(String id);
+
+}
View
102 ...t/java/com/netflix/hystrix/contrib/javanica/test/spring/rest/client/SimpleRestClient.java
@@ -0,0 +1,102 @@
+package com.netflix.hystrix.contrib.javanica.test.spring.rest.client;
+
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCollapser;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
+import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
+import com.netflix.hystrix.contrib.javanica.collapser.CollapserResult;
+import com.netflix.hystrix.contrib.javanica.command.AsyncCommand;
+import com.netflix.hystrix.contrib.javanica.test.spring.rest.domain.User;
+import com.netflix.hystrix.contrib.javanica.test.spring.rest.resource.UserResource;
+import org.springframework.stereotype.Component;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Future;
+
+@Component("simpleRestClient")
+public class SimpleRestClient implements RestClient {
+
+ private static final User DEF_USER = new User("def", "def");
+
+ private UserResource userResource = new UserResource();
+
+ @HystrixCommand(commandKey = "GetUserByIdCommand", fallbackMethod = "getUserByIdSecondary")
+ @Override
+ public User getUserById(String id) {
+ return userResource.getUserById(id);
+ }
+
+ @HystrixCommand(fallbackMethod = "getUserByIdSecondary")
+ @Override
+ public Future<User> getUserByIdAsync(final String id) {
+ return new AsyncCommand<User>() {
+ @Override
+ public User invoke() {
+ return userResource.getUserById(id);
+ }
+ };
+ }
+
+ @HystrixCommand(fallbackMethod = "defaultUser")
+ @Override
+ public User getUserByIdSecondary(String id) {
+ return userResource.getUserById(id);
+ }
+
+ @HystrixCommand(fallbackMethod = "findAllFallback")
+ @Override
+ public List<User> findAll(int pageNum, int pageSize) {
+ return userResource.findAll(pageNum, pageSize);
+ }
+
+ @HystrixCommand(fallbackMethod = "findAllFallback")
+ @Override
+ public Future<List<User>> findAllAsync(final int pageNum, final int pageSize) {
+ return new AsyncCommand<List<User>>() {
+ @Override
+ public List<User> invoke() {
+ return userResource.findAll(pageNum, pageSize);
+ }
+ };
+ }
+
+ @HystrixCollapser(commandMethod = "getUserById",
+ collapserProperties = {@HystrixProperty(name = "timerDelayInMilliseconds", value = "200")})
+ @Override
+ public Future<User> getUserByIdCollapserAsync(String id) {
+ return CollapserResult.async();
+ }
+
+ @HystrixCollapser(commandMethod = "getUserById",
+ collapserProperties = {@HystrixProperty(name = "maxRequestsInBatch", value = "3")})
+ @Override
+ public User getUserByIdCollapser(String id) {
+ return CollapserResult.sync();
+ }
+
+ @HystrixCommand(fallbackMethod = "findAllFallback2")
+ private List<User> findAllFallback(int pageNum, int pageSize) {
+ throw new RuntimeException();
+ }
+
+ @HystrixCommand
+ private List<User> findAllFallback2(int pageNum, int pageSize) {
+ return Arrays.asList(DEF_USER);
+ }
+
+ @HystrixCommand(commandKey = "GetUserByNameCommand", fallbackMethod = "defaultUser",
+ threadPoolKey = "SimpleRestClientTest",
+ commandProperties = {
+ @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "500"),
+ @HystrixProperty(name = "execution.isolation.strategy", value = "SEMAPHORE")
+ })
+ @Override