Skip to content

Commit

Permalink
Merge pull request #71 from benjchristensen/execution-hooks
Browse files Browse the repository at this point in the history
HystrixCommand Execution Hooks #10
  • Loading branch information
benjchristensen committed Jan 7, 2013
2 parents cea9fa9 + fbc838f commit e723beb
Show file tree
Hide file tree
Showing 11 changed files with 1,264 additions and 117 deletions.
968 changes: 935 additions & 33 deletions hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier;
import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifierDefault;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHook;
import com.netflix.hystrix.strategy.executionhook.HystrixCommandExecutionHookDefault;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher;
import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisherDefault;
import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy;
Expand All @@ -48,6 +50,7 @@ public class HystrixPlugins {
private final AtomicReference<HystrixConcurrencyStrategy> concurrencyStrategy = new AtomicReference<HystrixConcurrencyStrategy>();
private final AtomicReference<HystrixMetricsPublisher> metricsPublisher = new AtomicReference<HystrixMetricsPublisher>();
private final AtomicReference<HystrixPropertiesStrategy> propertiesFactory = new AtomicReference<HystrixPropertiesStrategy>();
private final AtomicReference<HystrixCommandExecutionHook> commandExecutionHook = new AtomicReference<HystrixCommandExecutionHook>();

private HystrixPlugins() {

Expand Down Expand Up @@ -209,6 +212,49 @@ public void registerPropertiesStrategy(HystrixPropertiesStrategy impl) {
}
}

/**
* Retrieve instance of {@link HystrixCommandExecutionHook} to use based on order of precedence as defined in {@link HystrixPlugins} class header.
* <p>
* Override default by using {@link #registerCommandExecutionHook(HystrixCommandExecutionHook)} or setting property: <code>hystrix.plugin.HystrixCommandExecutionHook.implementation</code> with the
* full classname to
* load.
*
* @return {@link HystrixCommandExecutionHook} implementation to use
*
* @since 1.2
*/
public HystrixCommandExecutionHook getCommandExecutionHook() {
if (commandExecutionHook.get() == null) {
// check for an implementation from System.getProperty first
Object impl = getPluginImplementationViaProperty(HystrixCommandExecutionHook.class);
if (impl == null) {
// nothing set via properties so initialize with default
commandExecutionHook.compareAndSet(null, HystrixCommandExecutionHookDefault.getInstance());
// we don't return from here but call get() again in case of thread-race so the winner will always get returned
} else {
// we received an implementation from the system property so use it
commandExecutionHook.compareAndSet(null, (HystrixCommandExecutionHook) impl);
}
}
return commandExecutionHook.get();
}

/**
* Register a {@link HystrixCommandExecutionHook} implementation as a global override of any injected or default implementations.
*
* @param impl
* {@link HystrixCommandExecutionHook} implementation
* @throws IllegalStateException
* if called more than once or after the default was initialized (if usage occurs before trying to register)
*
* @since 1.2
*/
public void registerCommandExecutionHook(HystrixCommandExecutionHook impl) {
if (!commandExecutionHook.compareAndSet(null, impl)) {
throw new IllegalStateException("Another strategy was already registered.");
}
}

private static Object getPluginImplementationViaProperty(Class<?> pluginClass) {
String classSimpleName = pluginClass.getSimpleName();
/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,27 +37,8 @@
* For example, every {@link Callable} executed by {@link HystrixCommand} will call {@link #wrapCallable(Callable)} to give a chance for custom implementations to decorate the {@link Callable} with
* additional behavior.
* <p>
* Custom implementations of this interface can be used to override default behavior via 2 mechanisms:
* <p>
* 1) Injection
* <p>
* Implementations can be injected into {@link HystrixCommand} and {@link HystrixCollapser} implementation constructors.
* <p>
* 2) Plugin
* <p>
* Using {@link HystrixPlugins#registerConcurrencyStrategy} an implementation can be registered globally to take precedence and override all other implementations.
* <p>
* The order of precedence is:
* <ol>
* <li>plugin registered globally using {@link HystrixPlugins#registerConcurrencyStrategy}</li>
* <li>injected via {@link HystrixCommand} and {@link HystrixCollapser} constructors</li>
* <li>default implementation {@link HystrixConcurrencyStrategyDefault}</li>
* </ol>
* <p>
* The injection approach is effective for {@link HystrixCommand} and {@link HystrixCollapser} implementations where you wish to have a different default strategy without
* overriding all implementations. It is also useful when distributing a library where static override should not be used.
* <p>
* The globally registered plugin is useful when using commands from 3rd party libraries and you want to override the strategy for all implementations in your entire system.
* See {@link HystrixPlugins} or the Hystrix GitHub Wiki for information on configuring plugins: <a
* href="https://github.com/Netflix/Hystrix/wiki/Plugins">https://github.com/Netflix/Hystrix/wiki/Plugins</a>.
*/
public abstract class HystrixConcurrencyStrategy {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@

import java.util.List;

import com.netflix.hystrix.HystrixCollapser;
import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandKey;
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;
import com.netflix.hystrix.HystrixEventType;
Expand All @@ -27,27 +25,15 @@
/**
* Abstract EventNotifier that allows receiving notifications for different events with default implementations.
* <p>
* Custom implementations of this interface can be used to override default behavior via 2 mechanisms:
* See {@link HystrixPlugins} or the Hystrix GitHub Wiki for information on configuring plugins: <a
* href="https://github.com/Netflix/Hystrix/wiki/Plugins">https://github.com/Netflix/Hystrix/wiki/Plugins</a>.
* <p>
* 1) Injection
* <b>Note on thread-safety and performance</b>
* <p>
* Implementations can be injected into {@link HystrixCommand} and {@link HystrixCollapser} implementation constructors.
* A single implementation of this class will be used globally so methods on this class will be invoked concurrently from multiple threads so all functionality must be thread-safe.
* <p>
* 2) Plugin
* <p>
* Using {@link HystrixPlugins#registerEventNotifier} an implementation can be registered globally to take precedence and override all other implementations.
* <p>
* The order of precedence is:
* <ol>
* <li>plugin registered globally using {@link HystrixPlugins#registerEventNotifier}</li>
* <li>injected via {@link HystrixCommand} and {@link HystrixCollapser} constructors</li>
* <li>default implementation {@link HystrixEventNotifierDefault}</li>
* </ol>
* <p>
* The injection approach is effective for {@link HystrixCommand} and {@link HystrixCollapser} implementations where you wish to have a different default mechanism for event notification without
* overriding all implementations. It is also useful when distributing a library where static override should not be used.
* <p>
* The globally registered plugin is useful when using commands from 3rd party libraries and you want to override how event notifications are performed for all implementations in your entire system.
* Methods are also invoked synchronously and will add to execution time of the commands so all behavior should be fast. If anything time-consuming is to be done it should be spawned asynchronously
* onto separate worker threads.
*/
public abstract class HystrixEventNotifier {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
/**
* Copyright 2013 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.strategy.executionhook;

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandProperties.ExecutionIsolationStrategy;
import com.netflix.hystrix.exception.HystrixRuntimeException;
import com.netflix.hystrix.exception.HystrixRuntimeException.FailureType;
import com.netflix.hystrix.strategy.HystrixPlugins;

/**
* Abstract ExecutionHook with invocations at different lifecycle points of {@link HystrixCommand} execution with default no-op implementations.
* <p>
* See {@link HystrixPlugins} or the Hystrix GitHub Wiki for information on configuring plugins: <a
* href="https://github.com/Netflix/Hystrix/wiki/Plugins">https://github.com/Netflix/Hystrix/wiki/Plugins</a>.
* <p>
* <b>Note on thread-safety and performance</b>
* <p>
* A single implementation of this class will be used globally so methods on this class will be invoked concurrently from multiple threads so all functionality must be thread-safe.
* <p>
* Methods are also invoked synchronously and will add to execution time of the commands so all behavior should be fast. If anything time-consuming is to be done it should be spawned asynchronously
* onto separate worker threads.
*
* @since 1.2
* */
public abstract class HystrixCommandExecutionHook {

/**
* Invoked before {@link HystrixCommand#run()} is about to be executed.
*
* @param commandInstance
* The executing HystrixCommand instance.
*
* @since 1.2
*/
public <T> void onRunStart(HystrixCommand<T> commandInstance) {
// do nothing by default
}

/**
* Invoked after successful execution of {@link HystrixCommand#run()} with response value.
*
* @param commandInstance
* The executing HystrixCommand instance.
* @param response
* from {@link HystrixCommand#run()}
* @return T response object that can be modified, decorated, replaced or just returned as a pass-thru.
*
* @since 1.2
*/
public <T> T onRunSuccess(HystrixCommand<T> commandInstance, T response) {
// pass-thru by default
return response;
}

/**
* Invoked after failed execution of {@link HystrixCommand#run()} with thrown Exception.
*
* @param commandInstance
* The executing HystrixCommand instance.
* @param e
* Exception thrown by {@link HystrixCommand#run()}
* @return Exception that can be decorated, replaced or just returned as a pass-thru.
*
* @since 1.2
*/
public <T> Exception onRunError(HystrixCommand<T> commandInstance, Exception e) {
// pass-thru by default
return e;
}

/**
* Invoked before {@link HystrixCommand#getFallback()} is about to be executed.
*
* @param commandInstance
* The executing HystrixCommand instance.
*
* @since 1.2
*/
public <T> void onFallbackStart(HystrixCommand<T> commandInstance) {
// do nothing by default
}

/**
* Invoked after successful execution of {@link HystrixCommand#getFallback()} with response value.
*
* @param commandInstance
* The executing HystrixCommand instance.
* @param fallbackResponse
* from {@link HystrixCommand#getFallback()}
* @return T response object that can be modified, decorated, replaced or just returned as a pass-thru.
*
* @since 1.2
*/
public <T> T onFallbackSuccess(HystrixCommand<T> commandInstance, T fallbackResponse) {
// pass-thru by default
return fallbackResponse;
}

/**
* Invoked after failed execution of {@link HystrixCommand#getFallback()} with thrown exception.
*
* @param commandInstance
* The executing HystrixCommand instance.
* @param e
* Exception thrown by {@link HystrixCommand#getFallback()}
* @return Exception that can be decorated, replaced or just returned as a pass-thru.
*
* @since 1.2
*/
public <T> Exception onFallbackError(HystrixCommand<T> commandInstance, Exception e) {
// pass-thru by default
return e;
}

/**
* Invoked before {@link HystrixCommand} executes.
*
* @param commandInstance
* The executing HystrixCommand instance.
*
* @since 1.2
*/
public <T> void onStart(HystrixCommand<T> commandInstance) {
// do nothing by default
}

/**
* Invoked after completion of {@link HystrixCommand} execution that results in a response.
* <p>
* The response can come either from {@link HystrixCommand#run()} or {@link HystrixCommand#getFallback()}.
*
* @param commandInstance
* The executing HystrixCommand instance.
* @param response
* from {@link HystrixCommand#run()} or {@link HystrixCommand#getFallback()}.
* @return T response object that can be modified, decorated, replaced or just returned as a pass-thru.
*
* @since 1.2
*/
public <T> T onComplete(HystrixCommand<T> commandInstance, T response) {
// pass-thru by default
return response;
}

/**
* Invoked after failed completion of {@link HystrixCommand} execution.
*
* @param commandInstance
* The executing HystrixCommand instance.
* @param failureType
* {@link FailureType} representing the type of failure that occurred.
* <p>
* See {@link HystrixRuntimeException} for more information.
* @param e
* Exception thrown by {@link HystrixCommand}
* @return Exception that can be decorated, replaced or just returned as a pass-thru.
*
* @since 1.2
*/
public <T> Exception onError(HystrixCommand<T> commandInstance, FailureType failureType, Exception e) {
// pass-thru by default
return e;
}

/**
* Invoked at start of thread execution when {@link HystrixCommand} is executed using {@link ExecutionIsolationStrategy#THREAD}.
*
* @param commandInstance
* The executing HystrixCommand instance.
*
* @since 1.2
*/
public <T> void onThreadStart(HystrixCommand<T> commandInstance) {
// do nothing by default
}

/**
* Invoked at completion of thread execution when {@link HystrixCommand} is executed using {@link ExecutionIsolationStrategy#THREAD}.
*
* @param commandInstance
* The executing HystrixCommand instance.
*
* @since 1.2
*/
public <T> void onThreadComplete(HystrixCommand<T> commandInstance) {
// do nothing by default
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright 2013 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.strategy.executionhook;

/**
* Default implementations of {@link HystrixCommandExecutionHook} that does nothing.
*
* @ExcludeFromJavadoc
*/
public class HystrixCommandExecutionHookDefault extends HystrixCommandExecutionHook {

private static HystrixCommandExecutionHookDefault INSTANCE = new HystrixCommandExecutionHookDefault();

private HystrixCommandExecutionHookDefault() {

}

public static HystrixCommandExecutionHook getInstance() {
return INSTANCE;
}

}
Loading

0 comments on commit e723beb

Please sign in to comment.