All Downloads are FREE. Search and download functionalities are using the official Maven repository.

br.com.anteros.bean.validation.Jsr303MetaBeanFactory Maven / Gradle / Ivy

There is a newer version: 1.0.18
Show newest version
/*******************************************************************************
 * Copyright 2012 Anteros Tecnologia
 *  
 * 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 br.com.anteros.bean.validation;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.List;

import br.com.anteros.bean.validation.groups.Group;
import br.com.anteros.bean.validation.model.MetaBean;
import br.com.anteros.bean.validation.model.MetaProperty;
import br.com.anteros.bean.validation.util.AccessStrategy;
import br.com.anteros.bean.validation.util.FieldAccess;
import br.com.anteros.bean.validation.util.MethodAccess;
import br.com.anteros.bean.validation.util.SecureActions;
import br.com.anteros.bean.validation.xml.MetaConstraint;
import br.com.anteros.core.log.LogLevel;
import br.com.anteros.core.log.Logger;
import br.com.anteros.core.log.LoggerProvider;
import br.com.anteros.core.utils.ClassUtils;
import br.com.anteros.validation.api.Constraint;
import br.com.anteros.validation.api.GroupDefinitionException;
import br.com.anteros.validation.api.GroupSequence;
import br.com.anteros.validation.api.ValidationException;
import br.com.anteros.validation.api.groups.Default;

/**
 * Description: process the class annotations for JSR303 constraint validations to build the MetaBean with information
 * from annotations and JSR303 constraint mappings (defined in xml)
*/ public class Jsr303MetaBeanFactory implements MetaBeanFactory { /** Shared log instance */ // of dubious utility as it's static :/ protected static final Logger log = LoggerProvider.getInstance().getLogger(Jsr303MetaBeanFactory.class.getName()); /** {@link AnterosFactoryContext} used */ protected final AnterosFactoryContext factoryContext; /** * {@link AnnotationProcessor} used. */ protected AnnotationProcessor annotationProcessor; /** * Create a new Jsr303MetaBeanFactory instance. * * @param factoryContext */ public Jsr303MetaBeanFactory(AnterosFactoryContext factoryContext) { this.factoryContext = factoryContext; this.annotationProcessor = new AnnotationProcessor(factoryContext); } /** * {@inheritDoc} Add the validation features to the metabean that come from JSR303 annotations in the beanClass. */ public void buildMetaBean(MetaBean metabean) { try { final Class beanClass = metabean.getBeanClass(); processGroupSequence(beanClass, metabean); // process class, superclasses and interfaces List> classSequence = new ArrayList>(); ClassUtils.fillFullClassHierarchyAsList(classSequence, beanClass); // start with superclasses and go down the hierarchy so that // the child classes are processed last to have the chance to // overwrite some declarations // of their superclasses and that they see what they inherit at the // time of processing for (int i = classSequence.size() - 1; i >= 0; i--) { Class eachClass = classSequence.get(i); processClass(eachClass, metabean); processGroupSequence(eachClass, metabean, "{GroupSequence:" + eachClass.getCanonicalName() + "}"); } } catch (IllegalAccessException e) { throw new IllegalArgumentException(e); } catch (InvocationTargetException e) { throw new IllegalArgumentException(e.getTargetException()); } } /** * Process class annotations, field and method annotations. * * @param beanClass * @param metabean * @throws IllegalAccessException * @throws InvocationTargetException */ private void processClass(Class beanClass, MetaBean metabean) throws IllegalAccessException, InvocationTargetException { // if NOT ignore class level annotations if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(beanClass)) { annotationProcessor.processAnnotations(null, beanClass, beanClass, null, new AppendValidationToMeta( metabean)); } final Field[] fields = doPrivileged(SecureActions.getDeclaredFields(beanClass)); for (Field field : fields) { MetaProperty metaProperty = metabean.getProperty(field.getName()); // create a property for those fields for which there is not yet a // MetaProperty if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(field)) { AccessStrategy access = new FieldAccess(field); boolean create = metaProperty == null; if (create) { metaProperty = addMetaProperty(metabean, access); } if (!annotationProcessor.processAnnotations(metaProperty, beanClass, field, access, new AppendValidationToMeta(metaProperty)) && create) { metabean.putProperty(metaProperty.getName(), null); } } } final Method[] methods = doPrivileged(SecureActions.getDeclaredMethods(beanClass)); for (Method method : methods) { String propName = null; if (method.getParameterTypes().length == 0) { propName = MethodAccess.getPropertyName(method); } if (propName != null) { if (!factoryContext.getFactory().getAnnotationIgnores().isIgnoreAnnotations(method)) { AccessStrategy access = new MethodAccess(propName, method); MetaProperty metaProperty = metabean.getProperty(propName); boolean create = metaProperty == null; // create a property for those methods for which there is // not yet a MetaProperty if (create) { metaProperty = addMetaProperty(metabean, access); } if (!annotationProcessor.processAnnotations(metaProperty, beanClass, method, access, new AppendValidationToMeta(metaProperty)) && create) { metabean.putProperty(propName, null); } } } else if (hasValidationConstraintsDefined(method)) { throw new ValidationException("Property " + method.getName() + " does not follow javabean conventions."); } } addXmlConstraints(beanClass, metabean); } /** * Learn whether a given Method has validation constraints defined via JSR303 annotations. * * @param method * @return true if constraints detected */ protected boolean hasValidationConstraintsDefined(Method method) { for (Annotation annot : method.getDeclaredAnnotations()) { if (hasValidationConstraintsDefined(annot)) { return true; } } return false; } private boolean hasValidationConstraintsDefined(Annotation annot) { // If it is annotated with @Constraint if (annot.annotationType().getAnnotation(Constraint.class) != null) { return true; } // Check whether it is a multivalued constraint: if (ConstraintAnnotationAttributes.VALUE.isDeclaredOn(annot.annotationType())) { Annotation[] children = ConstraintAnnotationAttributes.VALUE.getValue(annot); if (children != null) { for (Annotation child : children) { if (hasValidationConstraintsDefined(child)) { return true; } } } } return false; } /** * Add cascade validation and constraints from xml mappings * * @param beanClass * @param metabean * @throws IllegalAccessException * @throws InvocationTargetException */ private void addXmlConstraints(Class beanClass, MetaBean metabean) throws IllegalAccessException, InvocationTargetException { for (MetaConstraint meta : factoryContext.getFactory().getMetaConstraints(beanClass)) { MetaProperty metaProperty; AccessStrategy access = meta.getAccessStrategy(); boolean create = false; if (access == null) { // class level metaProperty = null; } else { // property level metaProperty = metabean.getProperty(access.getPropertyName()); create = metaProperty == null; if (create) { metaProperty = addMetaProperty(metabean, access); } } if (!annotationProcessor.processAnnotation(meta.getAnnotation(), metaProperty, beanClass, meta.getAccessStrategy(), new AppendValidationToMeta(metaProperty == null ? metabean : metaProperty)) && create) { metabean.putProperty(access.getPropertyName(), null); } } for (AccessStrategy access : factoryContext.getFactory().getValidAccesses(beanClass)) { MetaProperty metaProperty = metabean.getProperty(access.getPropertyName()); boolean create = metaProperty == null; if (create) { metaProperty = addMetaProperty(metabean, access); } if (!annotationProcessor.addAccessStrategy(metaProperty, access) && create) { metabean.putProperty(access.getPropertyName(), null); } } } private void processGroupSequence(Class beanClass, MetaBean metabean) { processGroupSequence(beanClass, metabean, Jsr303Features.Bean.GROUP_SEQUENCE); } private void processGroupSequence(Class beanClass, MetaBean metabean, String key) { GroupSequence annotation = beanClass.getAnnotation(GroupSequence.class); List groupSeq = metabean.getFeature(key); if (groupSeq == null) { groupSeq = new ArrayList(annotation == null ? 1 : annotation.value().length); metabean.putFeature(key, groupSeq); } Class[] groupClasses = factoryContext.getFactory().getDefaultSequence(beanClass); if (groupClasses == null || groupClasses.length == 0) { if (annotation == null) { groupSeq.add(Group.DEFAULT); return; } else { groupClasses = annotation.value(); } } boolean containsDefault = false; for (Class groupClass : groupClasses) { if (groupClass.getName().equals(beanClass.getName())) { groupSeq.add(Group.DEFAULT); containsDefault = true; } else if (groupClass.getName().equals(Default.class.getName())) { throw new GroupDefinitionException("'Default.class' must not appear in @GroupSequence! Use '" + beanClass.getSimpleName() + ".class' instead."); } else { groupSeq.add(new Group(groupClass)); } } if (!containsDefault) { throw new GroupDefinitionException("Redefined default group sequence must contain " + beanClass.getName()); } log.log(LogLevel.DEBUG, String.format("Default group sequence for bean %s is: %s", beanClass.getName(), groupSeq)); } /** * Add a {@link MetaProperty} to a {@link MetaBean}. * @param parentMetaBean * @param access * @return the created {@link MetaProperty} */ public static MetaProperty addMetaProperty(MetaBean parentMetaBean, AccessStrategy access) { final MetaProperty result = new MetaProperty(); final String name = access.getPropertyName(); result.setName(name); result.setType(access.getJavaType()); parentMetaBean.putProperty(name, result); return result; } /** * Perform action with AccessController.doPrivileged() if a security manager is installed. * * @param action * the action to run * @return * result of the action */ private static T doPrivileged(final PrivilegedAction action) { if (System.getSecurityManager() != null) { return AccessController.doPrivileged(action); } else { return action.run(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy