com.netflix.hystrix.contrib.javanica.utils.MethodProvider Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hystrix-javanica Show documentation
Show all versions of hystrix-javanica Show documentation
hystrix-javanica developed by Netflix
The newest version!
/**
* Copyright 2015 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.google.common.base.Function;
import com.google.common.base.Optional;
import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.exception.FallbackDefinitionException;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static org.objectweb.asm.Opcodes.ACC_BRIDGE;
import static org.objectweb.asm.Opcodes.ACC_SYNTHETIC;
import static org.objectweb.asm.Opcodes.ASM5;
/**
* Created by dmgcodevil
*/
public final class MethodProvider {
private MethodProvider() {
}
private static final MethodProvider INSTANCE = new MethodProvider();
public static MethodProvider getInstance() {
return INSTANCE;
}
private static final FallbackMethodFinder FALLBACK_METHOD_FINDER = new SpecificFallback(new DefaultCallback());
private Map cache = new ConcurrentHashMap();
public FallbackMethod getFallbackMethod(Class> type, Method commandMethod) {
return getFallbackMethod(type, commandMethod, false);
}
/**
* Gets fallback method for command method.
*
* @param enclosingType the enclosing class
* @param commandMethod the command method. in the essence it can be a fallback
* method annotated with HystrixCommand annotation that has a fallback as well.
* @param extended true if the given commandMethod was derived using additional parameter, otherwise - false
* @return new instance of {@link FallbackMethod} or {@link FallbackMethod#ABSENT} if there is no suitable fallback method for the given command
*/
public FallbackMethod getFallbackMethod(Class> enclosingType, Method commandMethod, boolean extended) {
if (commandMethod.isAnnotationPresent(HystrixCommand.class)) {
return FALLBACK_METHOD_FINDER.find(enclosingType, commandMethod, extended);
}
return FallbackMethod.ABSENT;
}
private void getDefaultFallback(){
}
private String getClassLevelFallback(Class> enclosingClass) {
if (enclosingClass.isAnnotationPresent(DefaultProperties.class)) {
return enclosingClass.getAnnotation(DefaultProperties.class).defaultFallback();
}
return StringUtils.EMPTY;
}
private static class SpecificFallback extends FallbackMethodFinder {
public SpecificFallback(FallbackMethodFinder next) {
super(next);
}
@Override
boolean isSpecific() {
return true;
}
@Override
public String getFallbackName(Class> enclosingType, Method commandMethod) {
return commandMethod.getAnnotation(HystrixCommand.class).fallbackMethod();
}
@Override
boolean canHandle(Class> enclosingType, Method commandMethod) {
return StringUtils.isNotBlank(getFallbackName(enclosingType, commandMethod));
}
}
private static class DefaultCallback extends FallbackMethodFinder {
@Override
boolean isDefault() {
return true;
}
@Override
public String getFallbackName(Class> enclosingType, Method commandMethod) {
String commandDefaultFallback = commandMethod.getAnnotation(HystrixCommand.class).defaultFallback();
String classDefaultFallback = Optional.fromNullable(enclosingType.getAnnotation(DefaultProperties.class))
.transform(new Function() {
@Override
public String apply(DefaultProperties input) {
return input.defaultFallback();
}
}).or(StringUtils.EMPTY);
return StringUtils.defaultIfEmpty(commandDefaultFallback, classDefaultFallback);
}
@Override
boolean canHandle(Class> enclosingType, Method commandMethod) {
return StringUtils.isNotBlank(getFallbackName(enclosingType, commandMethod));
}
}
private static abstract class FallbackMethodFinder {
FallbackMethodFinder next;
public FallbackMethodFinder() {
}
public FallbackMethodFinder(FallbackMethodFinder next) {
this.next = next;
}
boolean isDefault() {
return false;
}
boolean isSpecific(){
return false;
}
public abstract String getFallbackName(Class> enclosingType, Method commandMethod);
public FallbackMethod find(Class> enclosingType, Method commandMethod, boolean extended) {
if (canHandle(enclosingType, commandMethod)) {
return doFind(enclosingType, commandMethod, extended);
} else if (next != null) {
return next.find(enclosingType, commandMethod, extended);
} else {
return FallbackMethod.ABSENT;
}
}
abstract boolean canHandle(Class> enclosingType, Method commandMethod);
private FallbackMethod doFind(Class> enclosingType, Method commandMethod, boolean extended) {
String name = getFallbackName(enclosingType, commandMethod);
Class>[] fallbackParameterTypes = null;
if (isDefault()) {
fallbackParameterTypes = new Class[0];
} else {
fallbackParameterTypes = commandMethod.getParameterTypes();
}
if (extended && fallbackParameterTypes[fallbackParameterTypes.length - 1] == Throwable.class) {
fallbackParameterTypes = ArrayUtils.remove(fallbackParameterTypes, fallbackParameterTypes.length - 1);
}
Class>[] extendedFallbackParameterTypes = Arrays.copyOf(fallbackParameterTypes,
fallbackParameterTypes.length + 1);
extendedFallbackParameterTypes[fallbackParameterTypes.length] = Throwable.class;
Optional exFallbackMethod = getMethod(enclosingType, name, extendedFallbackParameterTypes);
Optional fMethod = getMethod(enclosingType, name, fallbackParameterTypes);
Method method = exFallbackMethod.or(fMethod).orNull();
if (method == null) {
throw new FallbackDefinitionException("fallback method wasn't found: " + name + "(" + Arrays.toString(fallbackParameterTypes) + ")");
}
return new FallbackMethod(method, exFallbackMethod.isPresent(), isDefault());
}
}
/**
* Gets method by name and parameters types using reflection,
* if the given type doesn't contain required method then continue applying this method for all super classes up to Object class.
*
* @param type the type to search method
* @param name the method name
* @param parameterTypes the parameters types
* @return Some if method exists otherwise None
*/
public static Optional getMethod(Class> type, String name, Class>... parameterTypes) {
Method[] methods = type.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(name) && Arrays.equals(method.getParameterTypes(), parameterTypes)) {
return Optional.of(method);
}
}
Class> superClass = type.getSuperclass();
if (superClass != null && !superClass.equals(Object.class)) {
return getMethod(superClass, name, parameterTypes);
} else {
return Optional.absent();
}
}
/**
* Finds generic method for the given bridge method.
*
* @param bridgeMethod the bridge method
* @param aClass the type where the bridge method is declared
* @return generic method
* @throws IOException
* @throws NoSuchMethodException
* @throws ClassNotFoundException
*/
public Method unbride(final Method bridgeMethod, Class> aClass) throws IOException, NoSuchMethodException, ClassNotFoundException {
if (bridgeMethod.isBridge() && bridgeMethod.isSynthetic()) {
if (cache.containsKey(bridgeMethod)) {
return cache.get(bridgeMethod);
}
ClassReader classReader = new ClassReader(aClass.getName());
final MethodSignature methodSignature = new MethodSignature();
classReader.accept(new ClassVisitor(ASM5) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
boolean bridge = (access & ACC_BRIDGE) != 0 && (access & ACC_SYNTHETIC) != 0;
if (bridge && bridgeMethod.getName().equals(name) && getParameterCount(desc) == bridgeMethod.getParameterTypes().length) {
return new MethodFinder(methodSignature);
}
return super.visitMethod(access, name, desc, signature, exceptions);
}
}, 0);
Method method = aClass.getDeclaredMethod(methodSignature.name, methodSignature.getParameterTypes());
cache.put(bridgeMethod, method);
return method;
} else {
return bridgeMethod;
}
}
private static int getParameterCount(String desc) {
return parseParams(desc).length;
}
private static String[] parseParams(String desc) {
String params = desc.split("\\)")[0].replace("(", "");
if (params.length() == 0) {
return new String[0];
}
return params.split(";");
}
private static class MethodSignature {
String name;
String desc;
public Class>[] getParameterTypes() throws ClassNotFoundException {
if (desc == null) {
return new Class[0];
}
String[] params = parseParams(desc);
Class>[] parameterTypes = new Class[params.length];
for (int i = 0; i < params.length; i++) {
String arg = params[i].substring(1).replace("/", ".");
parameterTypes[i] = Class.forName(arg);
}
return parameterTypes;
}
}
private static class MethodFinder extends MethodVisitor {
private MethodSignature methodSignature;
public MethodFinder(MethodSignature methodSignature) {
super(ASM5);
this.methodSignature = methodSignature;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
methodSignature.name = name;
methodSignature.desc = desc;
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy