Package org.hibernate.envers.configuration.metadata.reader

Source Code of org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesReader

package org.hibernate.envers.configuration.metadata.reader;

import static org.hibernate.envers.tools.Tools.newHashSet;

import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.persistence.JoinColumn;
import javax.persistence.MapKey;
import javax.persistence.Version;

import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.envers.*;
import org.hibernate.envers.configuration.GlobalConfiguration;
import org.hibernate.envers.tools.MappingTools;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
import org.hibernate.MappingException;

/**
* Reads persistent properties form a
* {@link org.hibernate.envers.configuration.metadata.reader.PersistentPropertiesSource}
* and adds the ones that are audited to a
* {@link org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesHolder},
* filling all the auditing data.
* @author Adam Warski (adam at warski dot org)
* @author Erik-Berndt Scheper
* @author Hern&aacut;n Chanfreau
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
*/
public class AuditedPropertiesReader {
  protected final ModificationStore defaultStore;
  private final PersistentPropertiesSource persistentPropertiesSource;
  private final AuditedPropertiesHolder auditedPropertiesHolder;
  private final GlobalConfiguration globalCfg;
  private final ReflectionManager reflectionManager;
  private final String propertyNamePrefix;

  private final Set<String> propertyAccessedPersistentProperties;
  private final Set<String> fieldAccessedPersistentProperties;

  public AuditedPropertiesReader(ModificationStore defaultStore,
                   PersistentPropertiesSource persistentPropertiesSource,
                   AuditedPropertiesHolder auditedPropertiesHolder,
                   GlobalConfiguration globalCfg,
                   ReflectionManager reflectionManager,
                   String propertyNamePrefix) {
    this.defaultStore = defaultStore;
    this.persistentPropertiesSource = persistentPropertiesSource;
    this.auditedPropertiesHolder = auditedPropertiesHolder;
    this.globalCfg = globalCfg;
    this.reflectionManager = reflectionManager;
    this.propertyNamePrefix = propertyNamePrefix;

    propertyAccessedPersistentProperties = newHashSet();
    fieldAccessedPersistentProperties = newHashSet();
  }

  public void read() {
    // First reading the access types for the persistent properties.
    readPersistentPropertiesAccess();

        // Retrieve classes that are explicitly marked for auditing process by any superclass of currently mapped
        // entity or itself.
        XClass clazz = persistentPropertiesSource.getXClass();
        Set<XClass> declaredAuditedSuperclasses = new HashSet<XClass>();
        doGetDeclaredAuditedSuperclasses(clazz, declaredAuditedSuperclasses);

        // Adding all properties from the given class.
        addPropertiesFromClass(clazz, declaredAuditedSuperclasses);
  }

    /**
     * Recursively constructs a set of classes that have been declared for auditing process.
     * @param clazz Class that is being processed. Currently mapped entity shall be passed during first invocation.
     * @param declaredAuditedSuperclasses Total collection of classes listed in {@link Audited#auditParents()} property
     *                                    by any superclass starting with class specified as the first argument.
     */
    @SuppressWarnings("unchecked")
    private void doGetDeclaredAuditedSuperclasses(XClass clazz, Set<XClass> declaredAuditedSuperclasses) {
        Audited allClassAudited = clazz.getAnnotation(Audited.class);
        if (allClassAudited != null && allClassAudited.auditParents().length > 0) {
            for (Class c : allClassAudited.auditParents()) {
                XClass parentClass = reflectionManager.toXClass(c);
                checkSuperclass(clazz, parentClass);
                declaredAuditedSuperclasses.add(parentClass);
            }
        }
        XClass superclass = clazz.getSuperclass();
        if (!clazz.isInterface() && !Object.class.getName().equals(superclass.getName())) {
            doGetDeclaredAuditedSuperclasses(superclass, declaredAuditedSuperclasses);
        }
    }

    /**
     * Checks whether one class is assignable from another. If not {@link MappingException} is thrown.
     * @param child Subclass.
     * @param parent Superclass.
     */
    private void checkSuperclass(XClass child, XClass parent) {
        if (!parent.isAssignableFrom(child)) {
            throw new MappingException("Class " + parent.getName() + " is not assignable from " + child.getName() + ". " +
                                       "Please revise @Audited.auditParents value in " + child.getName() + " type.");
        }
    }

  private void readPersistentPropertiesAccess() {
    Iterator<Property> propertyIter = persistentPropertiesSource.getPropertyIterator();
    while (propertyIter.hasNext()) {
      Property property = (Property) propertyIter.next();
      if ("field".equals(property.getPropertyAccessorName())) {
        fieldAccessedPersistentProperties.add(property.getName());
      } else {
        propertyAccessedPersistentProperties.add(property.getName());
      }
    }
  }

    /**
     * @param clazz Class which properties are currently being added.
     * @param declaredAuditedSuperclasses Collection of superclasses that have been explicitly declared to be audited.
     * @return {@link Audited} annotation of specified class. If processed type hasn't been explicitly marked, method
     *         checks whether given class exists in collection passed as the second argument. In case of success,
     *         {@link Audited} configuration of currently mapped entity is returned, otherwise {@code null}.
     */
    private Audited computeAuditConfiguration(XClass clazz, Set<XClass> declaredAuditedSuperclasses) {
        Audited allClassAudited = clazz.getAnnotation(Audited.class);
        // If processed class is not explicitly marked with @Audited annotation, check whether auditing is
        // forced by any of its child entities configuration (@Audited.auditParents).
        if (allClassAudited == null && declaredAuditedSuperclasses.contains(clazz)) {
            // Declared audited parent copies @Audited.modStore and @Audited.targetAuditMode configuration from
            // currently mapped entity.
            allClassAudited = persistentPropertiesSource.getXClass().getAnnotation(Audited.class);
        }
        return allClassAudited;
    }

    /**
     * Recursively adds all audited properties of entity class and its superclasses.
     * @param clazz Currently processed class.
     * @param declaredAuditedSuperclasses Collection of classes that are declared to be audited
     *                                    (see {@link Audited#auditParents()}).
     */
  private void addPropertiesFromClass(XClass clazz, Set<XClass> declaredAuditedSuperclasses)  {
    Audited allClassAudited = computeAuditConfiguration(clazz, declaredAuditedSuperclasses);

    //look in the class
    addFromProperties(clazz.getDeclaredProperties("field"), "field", fieldAccessedPersistentProperties, allClassAudited);
    addFromProperties(clazz.getDeclaredProperties("property"), "property", propertyAccessedPersistentProperties, allClassAudited);
   
    if(allClassAudited != null || !auditedPropertiesHolder.isEmpty()) {
      XClass superclazz = clazz.getSuperclass();
      if (!clazz.isInterface() && !"java.lang.Object".equals(superclazz.getName())) {
        addPropertiesFromClass(superclazz, declaredAuditedSuperclasses);
      }
    }
  }

  private void addFromProperties(Iterable<XProperty> properties, String accessType, Set<String> persistentProperties, Audited allClassAudited) {
    for (XProperty property : properties) {
      // If this is not a persistent property, with the same access type as currently checked,
      // it's not audited as well.
      // If the property was already defined by the subclass, is ignored by superclasses
      if ((persistentProperties.contains(property.getName()) && (!auditedPropertiesHolder
          .contains(property.getName())))) {
        Value propertyValue = persistentPropertiesSource.getProperty(property.getName()).getValue();
        if (propertyValue instanceof Component) {
          this.addFromComponentProperty(property, accessType, (Component)propertyValue, allClassAudited);
        } else {
          this.addFromNotComponentProperty(property, accessType, allClassAudited);
        }
      }
    }
  }
 
  private void addFromComponentProperty(XProperty property,
      String accessType, Component propertyValue, Audited allClassAudited) {

    ComponentAuditingData componentData = new ComponentAuditingData();
    boolean isAudited = fillPropertyData(property, componentData, accessType,
        allClassAudited);

    PersistentPropertiesSource componentPropertiesSource = new ComponentPropertiesSource(
        (Component) propertyValue);
   
    ComponentAuditedPropertiesReader audPropReader = new ComponentAuditedPropertiesReader(
        ModificationStore.FULL, componentPropertiesSource,
        componentData, globalCfg, reflectionManager, propertyNamePrefix
            + MappingTools
                .createComponentPrefix(property.getName()));
    audPropReader.read();

    if (isAudited) {
      // Now we know that the property is audited
      auditedPropertiesHolder.addPropertyAuditingData(property.getName(),
          componentData);
    }
  }

  private void addFromNotComponentProperty(XProperty property, String accessType, Audited allClassAudited){
    PropertyAuditingData propertyData = new PropertyAuditingData();
    boolean isAudited = fillPropertyData(property, propertyData, accessType, allClassAudited);

    if (isAudited) {
      // Now we know that the property is audited
      auditedPropertiesHolder.addPropertyAuditingData(property.getName(), propertyData);
    }
  }
 
 
  /**
   * Checks if a property is audited and if yes, fills all of its data.
   * @param property Property to check.
   * @param propertyData Property data, on which to set this property's modification store.
   * @param accessType Access type for the property.
   * @return False if this property is not audited.
   */
  private boolean fillPropertyData(XProperty property, PropertyAuditingData propertyData,
                   String accessType, Audited allClassAudited) {

    // check if a property is declared as not audited to exclude it
    // useful if a class is audited but some properties should be excluded
    NotAudited unVer = property.getAnnotation(NotAudited.class);
    if (unVer != null) {
      return false;
    } else {
      // if the optimistic locking field has to be unversioned and the current property
      // is the optimistic locking field, don't audit it
      if (globalCfg.isDoNotAuditOptimisticLockingField()) {
        Version jpaVer = property.getAnnotation(Version.class);
        if (jpaVer != null) {
          return false;
        }
      }
    }

   
    if(!this.checkAudited(property, propertyData, allClassAudited)){
      return false;
    }
 

    propertyData.setName(propertyNamePrefix + property.getName());
    propertyData.setBeanName(property.getName());
    propertyData.setAccessType(accessType);

    addPropertyJoinTables(property, propertyData);
    addPropertyAuditingOverrides(property, propertyData);
    if (!processPropertyAuditingOverrides(property, propertyData)) {
      return false; // not audited due to AuditOverride annotation
    }
    addPropertyMapKey(property, propertyData);
        setPropertyAuditMappedBy(property, propertyData);

    return true;
  }

 
  protected boolean checkAudited(XProperty property,
      PropertyAuditingData propertyData, Audited allClassAudited) {
    // Checking if this property is explicitly audited or if all properties are.
    Audited aud = (property.isAnnotationPresent(Audited.class)) ? (property.getAnnotation(Audited.class)) : allClassAudited;
    //Audited aud = property.getAnnotation(Audited.class);
    if (aud != null) {
      propertyData.setStore(aud.modStore());
      propertyData.setRelationTargetAuditMode(aud.targetAuditMode());
      return true;
    } else {
      return false;
    }
  }

    private void setPropertyAuditMappedBy(XProperty property, PropertyAuditingData propertyData) {
        AuditMappedBy auditMappedBy = property.getAnnotation(AuditMappedBy.class);
        if (auditMappedBy != null) {
        propertyData.setAuditMappedBy(auditMappedBy.mappedBy());
            if (!"".equals(auditMappedBy.positionMappedBy())) {
                propertyData.setPositionMappedBy(auditMappedBy.positionMappedBy());
            }
        }
    }

  private void addPropertyMapKey(XProperty property, PropertyAuditingData propertyData) {
    MapKey mapKey = property.getAnnotation(MapKey.class);
    if (mapKey != null) {
      propertyData.setMapKey(mapKey.name());
    }
  }

  private void addPropertyJoinTables(XProperty property, PropertyAuditingData propertyData) {
    // first set the join table based on the AuditJoinTable annotation
    AuditJoinTable joinTable = property.getAnnotation(AuditJoinTable.class);
    if (joinTable != null) {
      propertyData.setJoinTable(joinTable);
    } else {
      propertyData.setJoinTable(DEFAULT_AUDIT_JOIN_TABLE);
    }
  }

  /***
   * Add the {@link org.hibernate.envers.AuditOverride} annotations.
   *
   * @param property the property being processed
   * @param propertyData the Envers auditing data for this property
   */
  private void addPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) {
    AuditOverride annotationOverride = property.getAnnotation(AuditOverride.class);
    if (annotationOverride != null) {
      propertyData.addAuditingOverride(annotationOverride);
    }
    AuditOverrides annotationOverrides = property.getAnnotation(AuditOverrides.class);
    if (annotationOverrides != null) {
      propertyData.addAuditingOverrides(annotationOverrides);
    }
  }

  /**
   * Process the {@link org.hibernate.envers.AuditOverride} annotations for this property.
   *
   * @param property
   *            the property for which the {@link org.hibernate.envers.AuditOverride}
   *            annotations are being processed
   * @param propertyData
   *            the Envers auditing data for this property
   * @return {@code false} if isAudited() of the override annotation was set to
   */
  private boolean processPropertyAuditingOverrides(XProperty property, PropertyAuditingData propertyData) {
    // if this property is part of a component, process all override annotations
    if (this.auditedPropertiesHolder instanceof ComponentAuditingData) {
      List<AuditOverride> overrides = ((ComponentAuditingData) this.auditedPropertiesHolder).getAuditingOverrides();
      for (AuditOverride override : overrides) {
        if (property.getName().equals(override.name())) {
          // the override applies to this property
          if (!override.isAudited()) {
            return false;
          } else {
            if (override.auditJoinTable() != null) {
              propertyData.setJoinTable(override.auditJoinTable());
            }
          }
        }
      }
     
    }
    return true;
  }

  private static AuditJoinTable DEFAULT_AUDIT_JOIN_TABLE = new AuditJoinTable() {
    public String name() { return ""; }
    public String schema() { return ""; }
    public String catalog() { return ""; }
    public JoinColumn[] inverseJoinColumns() { return new JoinColumn[0]; }
    public Class<? extends Annotation> annotationType() { return this.getClass(); }
  };

    private class ComponentPropertiesSource implements PersistentPropertiesSource {
    private final XClass xclass;
    private final Component component;

    private ComponentPropertiesSource(Component component) {
      try {
        this.xclass = reflectionManager.classForName(component.getComponentClassName(), this.getClass());
      } catch (ClassNotFoundException e) {
        throw new MappingException(e);
      }

      this.component = component;
    }

    @SuppressWarnings({"unchecked"})
    public Iterator<Property> getPropertyIterator() { return component.getPropertyIterator(); }
    public Property getProperty(String propertyName) { return component.getProperty(propertyName); }
    public XClass getXClass() { return xclass; }
  }
}
TOP

Related Classes of org.hibernate.envers.configuration.metadata.reader.AuditedPropertiesReader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.