
com.alibaba.dubbo.validation.support.jvalidation.JValidator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dubbo2 Show documentation
Show all versions of dubbo2 Show documentation
The all in one project of dubbo2
The newest version!
/*
* Copyright 1999-2012 Alibaba Group.
*
* 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.alibaba.dubbo.validation.support.jvalidation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.Modifier;
import javassist.NotFoundException;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.ArrayMemberValue;
import javassist.bytecode.annotation.BooleanMemberValue;
import javassist.bytecode.annotation.ByteMemberValue;
import javassist.bytecode.annotation.CharMemberValue;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.DoubleMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.FloatMemberValue;
import javassist.bytecode.annotation.IntegerMemberValue;
import javassist.bytecode.annotation.LongMemberValue;
import javassist.bytecode.annotation.MemberValue;
import javassist.bytecode.annotation.ShortMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import javax.validation.Constraint;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.ValidatorFactory;
import javax.validation.groups.Default;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ReflectUtils;
import com.alibaba.dubbo.validation.Validator;
/**
* JValidator
*
* @author william.liangf
*/
public class JValidator implements Validator {
private static final Logger logger = LoggerFactory.getLogger(JValidator.class);
private final Class clazz;
private final javax.validation.Validator validator;
@SuppressWarnings({ "unchecked", "rawtypes" })
public JValidator(URL url) {
this.clazz = ReflectUtils.forName(url.getServiceInterface());
String jvalidation = url.getParameter("jvalidation");
ValidatorFactory factory;
if (jvalidation != null && jvalidation.length() > 0) {
factory = Validation.byProvider((Class)ReflectUtils.forName(jvalidation)).configure().buildValidatorFactory();
} else {
factory = Validation.buildDefaultValidatorFactory();
}
this.validator = factory.getValidator();
}
public void validate(String methodName, Class[] parameterTypes, Object[] arguments) throws Exception {
String methodClassName = clazz.getName() + "$" + toUpperMethoName(methodName);
Class methodClass = null;
try {
methodClass = Class.forName(methodClassName, false, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
}
Set> violations = new HashSet>();
Method method = clazz.getMethod(methodName, parameterTypes);
Object parameterBean = getMethodParameterBean(clazz, method, arguments);
if (parameterBean != null) {
if (methodClass != null) {
violations.addAll(validator.validate(parameterBean, Default.class, clazz, methodClass));
} else {
violations.addAll(validator.validate(parameterBean, Default.class, clazz));
}
}
for (Object arg : arguments) {
validate(violations, arg, clazz, methodClass);
}
if (violations.size() > 0) {
throw new ConstraintViolationException("Failed to validate service: " + clazz.getName() + ", method: " + methodName + ", cause: " + violations, violations);
}
}
private void validate(Set> violations, Object arg, Class clazz, Class methodClass) {
if (arg != null && ! isPrimitives(arg.getClass())) {
if (Object[].class.isInstance(arg)) {
for (Object item : (Object[]) arg) {
validate(violations, item, clazz, methodClass);
}
} else if (Collection.class.isInstance(arg)) {
for (Object item : (Collection) arg) {
validate(violations, item, clazz, methodClass);
}
} else if (Map.class.isInstance(arg)) {
for (Map.Entry entry : ((Map) arg).entrySet()) {
validate(violations, entry.getKey(), clazz, methodClass);
validate(violations, entry.getValue(), clazz, methodClass);
}
} else {
if (methodClass != null) {
violations.addAll(validator.validate(arg, Default.class, clazz, methodClass));
} else {
violations.addAll(validator.validate(arg, Default.class, clazz));
}
}
}
}
private static boolean isPrimitives(Class cls) {
if (cls.isArray()) {
return isPrimitive(cls.getComponentType());
}
return isPrimitive(cls);
}
private static boolean isPrimitive(Class cls) {
return cls.isPrimitive() || cls == String.class || cls == Boolean.class || cls == Character.class
|| Number.class.isAssignableFrom(cls) || Date.class.isAssignableFrom(cls);
}
private static Object getMethodParameterBean(Class clazz, Method method, Object[] args) {
if (! hasConstraintParameter(method)) {
return null;
}
try {
String upperName = toUpperMethoName(method.getName());
String parameterSimpleName = upperName + "Parameter";
String parameterClassName = clazz.getName() + "_" + parameterSimpleName;
Class parameterClass;
try {
parameterClass = (Class) Class.forName(parameterClassName, true, clazz.getClassLoader());
} catch (ClassNotFoundException e) {
ClassPool pool = ClassGenerator.getClassPool(clazz.getClassLoader());
CtClass ctClass = pool.makeClass(parameterClassName);
ClassFile classFile = ctClass.getClassFile();
classFile.setVersionToJava5();
ctClass.addConstructor(CtNewConstructor.defaultConstructor(pool.getCtClass(parameterClassName)));
// parameter fields
Class[] parameterTypes = method.getParameterTypes();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterTypes.length; i ++) {
Class type = parameterTypes[i];
Annotation[] annotations = parameterAnnotations[i];
AnnotationsAttribute attribute = new AnnotationsAttribute(classFile.getConstPool(), AnnotationsAttribute.visibleTag);
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
javassist.bytecode.annotation.Annotation ja = new javassist.bytecode.annotation.Annotation(
classFile.getConstPool(), pool.getCtClass(annotation.annotationType().getName()));
Method[] members = annotation.annotationType().getMethods();
for (Method member : members) {
if (Modifier.isPublic(member.getModifiers())
&& member.getParameterTypes().length == 0
&& member.getDeclaringClass() == annotation.annotationType()) {
Object value = member.invoke(annotation, new Object[0]);
if (value != null && ! value.equals(member.getDefaultValue())) {
MemberValue memberValue = createMemberValue(
classFile.getConstPool(), pool.get(member.getReturnType().getName()), value);
ja.addMemberValue(member.getName(), memberValue);
}
}
}
attribute.addAnnotation(ja);
}
}
String fieldName = method.getName() + "Argument" + i;
CtField ctField = CtField.make("public " + type.getCanonicalName() + " " + fieldName + ";", pool.getCtClass(parameterClassName));
ctField.getFieldInfo().addAttribute(attribute);
ctClass.addField(ctField);
}
parameterClass = ctClass.toClass();
}
Object parameterBean = parameterClass.newInstance();
for (int i = 0; i < args.length; i ++) {
Field field = parameterClass.getField(method.getName() + "Argument" + i);
field.set(parameterBean, args[i]);
}
return parameterBean;
} catch (Throwable e) {
logger.warn(e.getMessage(), e);
return null;
}
}
private static boolean hasConstraintParameter(Method method) {
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations != null && parameterAnnotations.length > 0) {
for (Annotation[] annotations : parameterAnnotations) {
for (Annotation annotation : annotations) {
if (annotation.annotationType().isAnnotationPresent(Constraint.class)) {
return true;
}
}
}
}
return false;
}
private static String toUpperMethoName(String methodName) {
return methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
}
// Copy from javassist.bytecode.annotation.Annotation.createMemberValue(ConstPool, CtClass);
private static MemberValue createMemberValue(ConstPool cp, CtClass type, Object value) throws NotFoundException {
MemberValue memberValue = javassist.bytecode.annotation.Annotation.createMemberValue(cp, type);
if (memberValue instanceof BooleanMemberValue)
((BooleanMemberValue) memberValue).setValue((Boolean) value);
else if (memberValue instanceof ByteMemberValue)
((ByteMemberValue) memberValue).setValue((Byte) value);
else if (memberValue instanceof CharMemberValue)
((CharMemberValue) memberValue).setValue((Character) value);
else if (memberValue instanceof ShortMemberValue)
((ShortMemberValue) memberValue).setValue((Short) value);
else if (memberValue instanceof IntegerMemberValue)
((IntegerMemberValue) memberValue).setValue((Integer) value);
else if (memberValue instanceof LongMemberValue)
((LongMemberValue) memberValue).setValue((Long) value);
else if (memberValue instanceof FloatMemberValue)
((FloatMemberValue) memberValue).setValue((Float) value);
else if (memberValue instanceof DoubleMemberValue)
((DoubleMemberValue) memberValue).setValue((Double) value);
else if (memberValue instanceof ClassMemberValue)
((ClassMemberValue) memberValue).setValue(((Class)value).getName());
else if (memberValue instanceof StringMemberValue)
((StringMemberValue) memberValue).setValue((String) value);
else if (memberValue instanceof EnumMemberValue)
((EnumMemberValue) memberValue).setValue(((Enum) value).name());
/* else if (memberValue instanceof AnnotationMemberValue) */
else if (memberValue instanceof ArrayMemberValue) {
CtClass arrayType = type.getComponentType();
int len = Array.getLength(value);
MemberValue[] members = new MemberValue[len];
for (int i = 0; i < len; i ++) {
members[i] = createMemberValue(cp, arrayType, Array.get(value, i));
}
((ArrayMemberValue) memberValue).setValue(members);
}
return memberValue;
}
}