/** * 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.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.common.base.Supplier; import com.google.common.collect.ImmutableList; import com.netflix.hystrix.contrib.javanica.annotation.*; import com.netflix.hystrix.contrib.javanica.command.closure.Closure; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.JoinPoint; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Simple immutable holder to keep all necessary information about current method to build Hystrix command. */ // todo: replace fallback related flags with FallbackMethod class @Immutable public final class MetaHolder { private final HystrixCollapser hystrixCollapser; private final HystrixCommand hystrixCommand; private final DefaultProperties defaultProperties; private final Method method; private final Method cacheKeyMethod; private final Method ajcMethod; private final Method fallbackMethod; private final Object obj; private final Object proxyObj; private final Object[] args; private final Closure closure; private final String defaultGroupKey; private final String defaultCommandKey; private final String defaultCollapserKey; private final String defaultThreadPoolKey; private final ExecutionType executionType; private final boolean extendedFallback; private final ExecutionType collapserExecutionType; private final ExecutionType fallbackExecutionType; private final boolean fallback; private boolean extendedParentFallback; private final boolean defaultFallback; private final JoinPoint joinPoint; private final boolean observable; private final ObservableExecutionMode observableExecutionMode; private static final Function identityFun = new Function<Object, Object>() { @Nullable @Override public Object apply(@Nullable Object input) { return input; } }; private MetaHolder(Builder builder) { this.hystrixCommand = builder.hystrixCommand; this.method = builder.method; this.cacheKeyMethod = builder.cacheKeyMethod; this.fallbackMethod = builder.fallbackMethod; this.ajcMethod = builder.ajcMethod; this.obj = builder.obj; this.proxyObj = builder.proxyObj; this.args = builder.args; this.closure = builder.closure; this.defaultGroupKey = builder.defaultGroupKey; this.defaultCommandKey = builder.defaultCommandKey; this.defaultThreadPoolKey = builder.defaultThreadPoolKey; this.defaultCollapserKey = builder.defaultCollapserKey; this.defaultProperties = builder.defaultProperties; this.hystrixCollapser = builder.hystrixCollapser; this.executionType = builder.executionType; this.collapserExecutionType = builder.collapserExecutionType; this.fallbackExecutionType = builder.fallbackExecutionType; this.joinPoint = builder.joinPoint; this.extendedFallback = builder.extendedFallback; this.defaultFallback = builder.defaultFallback; this.fallback = builder.fallback; this.extendedParentFallback = builder.extendedParentFallback; this.observable = builder.observable; this.observableExecutionMode = builder.observableExecutionMode; } public static Builder builder() { return new Builder(); } public HystrixCollapser getHystrixCollapser() { return hystrixCollapser; } public HystrixCommand getHystrixCommand() { return hystrixCommand; } public Method getMethod() { return method; } public Method getCacheKeyMethod() { return cacheKeyMethod; } public Method getAjcMethod() { return ajcMethod; } public Object getObj() { return obj; } public Object getProxyObj() { return proxyObj; } public Closure getClosure() { return closure; } public ExecutionType getExecutionType() { return executionType; } public ExecutionType getCollapserExecutionType() { return collapserExecutionType; } public Object[] getArgs() { return args != null ? Arrays.copyOf(args, args.length) : new Object[]{}; } public String getCommandGroupKey() { return isCommandAnnotationPresent() ? get(hystrixCommand.groupKey(), defaultGroupKey) : ""; } public String getDefaultGroupKey() { return defaultGroupKey; } public String getDefaultThreadPoolKey() { return defaultThreadPoolKey; } public String getCollapserKey() { return isCollapserAnnotationPresent() ? get(hystrixCollapser.collapserKey(), defaultCollapserKey) : ""; } public String getCommandKey() { return isCommandAnnotationPresent() ? get(hystrixCommand.commandKey(), defaultCommandKey) : ""; } public String getThreadPoolKey() { return isCommandAnnotationPresent() ? get(hystrixCommand.threadPoolKey(), defaultThreadPoolKey) : ""; } public String getDefaultCommandKey() { return defaultCommandKey; } public String getDefaultCollapserKey() { return defaultCollapserKey; } public boolean hasDefaultProperties() { return defaultProperties != null; } public Optional<DefaultProperties> getDefaultProperties() { return Optional.fromNullable(defaultProperties); } public Class<?>[] getParameterTypes() { return method.getParameterTypes(); } public boolean isCollapserAnnotationPresent() { return hystrixCollapser != null; } public boolean isCommandAnnotationPresent() { return hystrixCommand != null; } public JoinPoint getJoinPoint() { return joinPoint; } public Method getFallbackMethod() { return fallbackMethod; } public boolean hasFallbackMethod() { return fallbackMethod != null; } public boolean isExtendedParentFallback() { return extendedParentFallback; } public boolean hasFallbackMethodCommand() { return fallbackMethod != null && fallbackMethod.isAnnotationPresent(HystrixCommand.class); } public boolean isFallback() { return fallback; } public boolean isExtendedFallback() { return extendedFallback; } public boolean isDefaultFallback() { return defaultFallback; } @SuppressWarnings("unchecked") public List<Class<? extends Throwable>> getCommandIgnoreExceptions() { if (!isCommandAnnotationPresent()) return Collections.emptyList(); return getOrDefault(new Supplier<List<Class<? extends Throwable>>>() { @Override public List<Class<? extends Throwable>> get() { return ImmutableList.<Class<? extends Throwable>>copyOf(hystrixCommand.ignoreExceptions()); } }, new Supplier<List<Class<? extends Throwable>>>() { @Override public List<Class<? extends Throwable>> get() { return hasDefaultProperties() ? ImmutableList.<Class<? extends Throwable>>copyOf(defaultProperties.ignoreExceptions()) : Collections.<Class<? extends Throwable>>emptyList(); } }, this.<Class<? extends Throwable>>nonEmptyList()); } public ExecutionType getFallbackExecutionType() { return fallbackExecutionType; } public List<HystrixProperty> getCommandProperties() { if (!isCommandAnnotationPresent()) return Collections.emptyList(); return getOrDefault(new Supplier<List<HystrixProperty>>() { @Override public List<HystrixProperty> get() { return ImmutableList.copyOf(hystrixCommand.commandProperties()); } }, new Supplier<List<HystrixProperty>>() { @Override public List<HystrixProperty> get() { return hasDefaultProperties() ? ImmutableList.copyOf(defaultProperties.commandProperties()) : Collections.<HystrixProperty>emptyList(); } }, this.<HystrixProperty>nonEmptyList()); } public List<HystrixProperty> getCollapserProperties() { return isCollapserAnnotationPresent() ? ImmutableList.copyOf(hystrixCollapser.collapserProperties()) : Collections.<HystrixProperty>emptyList(); } public List<HystrixProperty> getThreadPoolProperties() { if (!isCommandAnnotationPresent()) return Collections.emptyList(); return getOrDefault(new Supplier<List<HystrixProperty>>() { @Override public List<HystrixProperty> get() { return ImmutableList.copyOf(hystrixCommand.threadPoolProperties()); } }, new Supplier<List<HystrixProperty>>() { @Override public List<HystrixProperty> get() { return hasDefaultProperties() ? ImmutableList.copyOf(defaultProperties.threadPoolProperties()) : Collections.<HystrixProperty>emptyList(); } }, this.<HystrixProperty>nonEmptyList()); } public boolean isObservable() { return observable; } public ObservableExecutionMode getObservableExecutionMode() { return observableExecutionMode; } public boolean raiseHystrixExceptionsContains(HystrixException hystrixException) { return getRaiseHystrixExceptions().contains(hystrixException); } public List<HystrixException> getRaiseHystrixExceptions() { return getOrDefault(new Supplier<List<HystrixException>>() { @Override public List<HystrixException> get() { return ImmutableList.copyOf(hystrixCommand.raiseHystrixExceptions()); } }, new Supplier<List<HystrixException>>() { @Override public List<HystrixException> get() { return hasDefaultProperties() ? ImmutableList.copyOf(defaultProperties.raiseHystrixExceptions()) : Collections.<HystrixException>emptyList(); } }, this.<HystrixException>nonEmptyList()); } private String get(String key, String defaultKey) { return StringUtils.isNotBlank(key) ? key : defaultKey; } private <T> Predicate<List<T>> nonEmptyList() { return new Predicate<List<T>>() { @Override public boolean apply(@Nullable List<T> input) { return input != null && !input.isEmpty(); } }; } @SuppressWarnings("unchecked") private <T> T getOrDefault(Supplier<T> source, Supplier<T> defaultChoice, Predicate<T> isDefined) { return getOrDefault(source, defaultChoice, isDefined, (Function<T, T>) identityFun); } private <T> T getOrDefault(Supplier<T> source, Supplier<T> defaultChoice, Predicate<T> isDefined, Function<T, T> map) { T res = source.get(); if (!isDefined.apply(res)) { res = defaultChoice.get(); } return map.apply(res); } public static final class Builder { private static final Class<?>[] EMPTY_ARRAY_OF_TYPES= new Class[0]; private HystrixCollapser hystrixCollapser; private HystrixCommand hystrixCommand; private DefaultProperties defaultProperties; private Method method; private Method cacheKeyMethod; private Method fallbackMethod; private Method ajcMethod; private Object obj; private Object proxyObj; private Closure closure; private Object[] args; private String defaultGroupKey; private String defaultCommandKey; private String defaultCollapserKey; private String defaultThreadPoolKey; private ExecutionType executionType; private ExecutionType collapserExecutionType; private ExecutionType fallbackExecutionType; private boolean extendedFallback; private boolean fallback; private boolean extendedParentFallback; private boolean defaultFallback; private boolean observable; private JoinPoint joinPoint; private ObservableExecutionMode observableExecutionMode; 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 cacheKeyMethod(Method cacheKeyMethod) { this.cacheKeyMethod = cacheKeyMethod; return this; } public Builder fallbackMethod(Method fallbackMethod) { this.fallbackMethod = fallbackMethod; return this; } public Builder fallbackExecutionType(ExecutionType fallbackExecutionType) { this.fallbackExecutionType = fallbackExecutionType; return this; } public Builder fallback(boolean fallback) { this.fallback = fallback; return this; } public Builder extendedParentFallback(boolean extendedParentFallback) { this.extendedParentFallback = extendedParentFallback; return this; } public Builder defaultFallback(boolean defaultFallback) { this.defaultFallback = defaultFallback; return this; } public Builder ajcMethod(Method ajcMethod) { this.ajcMethod = ajcMethod; return this; } public Builder obj(Object obj) { this.obj = obj; return this; } public Builder proxyObj(Object proxy) { this.proxyObj = proxy; return this; } public Builder args(Object[] args) { this.args = args; return this; } public Builder closure(Closure closure) { this.closure = closure; return this; } public Builder executionType(ExecutionType executionType) { this.executionType = executionType; return this; } public Builder collapserExecutionType(ExecutionType collapserExecutionType) { this.collapserExecutionType = collapserExecutionType; return this; } public Builder defaultGroupKey(String defGroupKey) { this.defaultGroupKey = defGroupKey; return this; } public Builder defaultCommandKey(String defCommandKey) { this.defaultCommandKey = defCommandKey; return this; } public Builder defaultThreadPoolKey(String defaultThreadPoolKey) { this.defaultThreadPoolKey = defaultThreadPoolKey; return this; } public Builder defaultCollapserKey(String defCollapserKey) { this.defaultCollapserKey = defCollapserKey; return this; } public Builder defaultProperties(@Nullable DefaultProperties defaultProperties) { this.defaultProperties = defaultProperties; return this; } public Builder joinPoint(JoinPoint joinPoint) { this.joinPoint = joinPoint; return this; } public Builder extendedFallback(boolean extendedFallback) { this.extendedFallback = extendedFallback; return this; } public Builder observable(boolean observable) { this.observable = observable; return this; } public Builder observableExecutionMode(ObservableExecutionMode observableExecutionMode) { this.observableExecutionMode = observableExecutionMode; return this; } public MetaHolder build() { return new MetaHolder(this); } } }