From: Andreas Müller Date: Mon, 20 Oct 2014 18:21:02 +0000 (+0000) Subject: add validation framework classes #4439 X-Git-Tag: cdmlib-parent-3.4.0~127^2~13 X-Git-Url: https://dev.e-taxonomy.eu/gitweb/cdmlib.git/commitdiff_plain/e1030562f836323e0ea46a46ef29ccff1a53a8a4 add validation framework classes #4439 --- diff --git a/.gitattributes b/.gitattributes index e07c140bbc..d12318a781 100644 --- a/.gitattributes +++ b/.gitattributes @@ -951,6 +951,9 @@ cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/taxon/TaxonRelationship.java - cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/taxon/TaxonRelationshipType.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/taxon/package-info.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/taxon/package.html -text +cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/EntityConstraintViolation.java -text +cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/EntityValidationResult.java -text +cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/SeverityType.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/view/AuditEvent.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/view/AuditEventRecord.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/view/AuditEventRecordImpl.java -text @@ -1031,8 +1034,10 @@ cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/SpecimenTypeParser.j cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/TaxonNameParserHistory.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/TimePeriodParser.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/location/CoordinateConverter.java -text +cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/CRUDEventType.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level2.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Level3.java -text +cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Severity.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/ValidationException.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/BasionymsMustShareEpithetsAndAuthors.java -text cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/annotation/ChildTaxaMustBeLowerRankThanParent.java -text diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/EntityConstraintViolation.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/EntityConstraintViolation.java new file mode 100644 index 0000000000..57d52556f0 --- /dev/null +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/EntityConstraintViolation.java @@ -0,0 +1,178 @@ +/** +* Copyright (C) 2009 EDIT +* European Distributed Institute of Taxonomy +* http://www.e-taxonomy.eu +* +* The contents of this file are subject to the Mozilla Public License Version 1.1 +* See LICENSE.TXT at the top of this package for the full license terms. +*/ +package eu.etaxonomy.cdm.model.validation; + +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.ManyToOne; +import javax.validation.ConstraintValidator; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + +import org.apache.log4j.Logger; +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.hibernate.annotations.Type; + +import eu.etaxonomy.cdm.model.common.CdmBase; +import eu.etaxonomy.cdm.validation.Severity; + +/** + * An {@code EntityConstraintViolation} represents a single error resulting from the + * validation of an entity. It basically is a database model for the + * {@link ConstraintValidator} class of the javax.validation framework. + * + * @author admin.ayco.holleman + * + */ + +@XmlAccessorType(XmlAccessType.FIELD) +//@formatter:off +@XmlType(name = "EntityConstraintViolation", propOrder = { + "PropertyPath", + "UserFriendlyFieldName", + "InvalidValue", + "Severity", + "Message", + "Validator", + "EntityValidationResult" +}) +//@formatter:on +@XmlRootElement(name = "EntityConstraintViolation") +@Entity +public class EntityConstraintViolation extends CdmBase { + private static final long serialVersionUID = 6685798691716413950L; + + @SuppressWarnings("unused") + private static final Logger logger = Logger.getLogger(EntityConstraintViolation.class); + + + public static EntityConstraintViolation NewInstance(){ + return new EntityConstraintViolation(); + } + + @XmlElement(name = "PropertyPath") + private String propertyPath; + + @XmlElement(name = "UserFriendlyFieldName") + private String userFriendlyFieldName; + + @XmlElement(name = "InvalidValue") + private String invalidValue; + + @XmlElement(name = "Severity") + @Type(type = "eu.etaxonomy.cdm.model.validation.SeverityType") + private Severity severity; + + @XmlElement(name = "Message") + private String message; + + @XmlElement(name = "Validator") + private String validator; + + @XmlElement(name = "EntityValidationResult") + @ManyToOne(fetch = FetchType.LAZY) + @Cascade({ CascadeType.ALL }) + private EntityValidationResult entityValidationResult; + + + protected EntityConstraintViolation(){ + } + + + /** + * Get the path from the root bean to the field with the invalid value. Ordinarily + * this simply is the simple name of the field of the validated entity (see + * {@link EntityValidationResult#getValidatedEntityClass()}). Only if you have used @Valid + * annotations, and the error was in a parent or child entity, will this be a + * dot-separated path (e.g. "addresses[0].street" or "company.name"). + */ + public String getPropertyPath(){ + return propertyPath; + } + public void setPropertyPath(String propertyPath){ + this.propertyPath = propertyPath; + } + + + /** + * A user-friendly name for the property path. + */ + public String getUserFriendlyFieldName(){ + return userFriendlyFieldName; + } + public void setUserFriendlyFieldName(String userFriendlyFieldName){ + this.userFriendlyFieldName = userFriendlyFieldName; + } + + + /** + * Get the value that violated the constraint. + * @return + */ + public String getInvalidValue(){ + return invalidValue; + } + + public void setInvalidValue(String invalidValue){ + this.invalidValue = invalidValue; + } + + + /** + * Get the severity of the constraint violation. + * + * @return + */ + public Severity getSeverity(){ + return severity; + } + public void setSeverity(Severity severity){ + this.severity = severity; + } + + + /** + * Get the error message associated with the constraint violation. + * + * @return The error message + */ + public String getMessage(){ + return message; + } + public void setMessage(String message){ + this.message = message; + } + + + /** + * Get the fully qualified class name of the {@link ConstraintValidator} responsible + * for invalidating the entity. + * + * @param validator + */ + public String getValidator(){ + return validator; + } + public void setValidator(String validator){ + this.validator = validator; + } + + + public EntityValidationResult getEntityValidationResult(){ + return entityValidationResult; + } + public void setEntityValidationResult(EntityValidationResult entityValidationResult){ + this.entityValidationResult = entityValidationResult; + } + +} diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/EntityValidationResult.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/EntityValidationResult.java new file mode 100644 index 0000000000..18d71c8ba1 --- /dev/null +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/EntityValidationResult.java @@ -0,0 +1,195 @@ +/** +* Copyright (C) 2009 EDIT +* European Distributed Institute of Taxonomy +* http://www.e-taxonomy.eu +* +* The contents of this file are subject to the Mozilla Public License Version 1.1 +* See LICENSE.TXT at the top of this package for the full license terms. +*/ +package eu.etaxonomy.cdm.model.validation; + +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.OneToMany; +import javax.validation.Validator; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import org.apache.log4j.Logger; +import org.hibernate.annotations.Cascade; +import org.hibernate.annotations.CascadeType; +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; +import org.hibernate.annotations.Type; +import org.hibernate.search.annotations.FieldBridge; + +import eu.etaxonomy.cdm.hibernate.search.UuidBridge; +import eu.etaxonomy.cdm.jaxb.UUIDAdapter; +import eu.etaxonomy.cdm.model.common.CdmBase; +import eu.etaxonomy.cdm.validation.CRUDEventType; + +/** + * An {@code EntityValidationResult} models the result of validating one entity, that is, + * the outcome of calling {@link Validator#validate(Object, Class...)}. More than one + * constraint {@link EntityConstraintViolation} may be violated while validating the + * entity. + * + * @see EntityValidationResult + * + * @author ayco_holleman + * + */ + +@XmlAccessorType(XmlAccessType.FIELD) +//@formatter:off +@XmlType(name = "EntityValidationResult", propOrder = { + "ValidatedEntityId", + "ValidatedEntityUuid", + "ValidatedEntityClass", + "UserFriendlyDescription", + "UserFriendlyTypeName", + "CrudEventType", + "ConstraintViolations" +}) +//@formatter:on +@XmlRootElement(name = "EntityValidationResult") +@Entity +public class EntityValidationResult extends CdmBase { + + private static final long serialVersionUID = 9120571815593117363L; + + @SuppressWarnings("unused") + private static final Logger logger = Logger.getLogger(EntityValidationResult.class); + + + public static EntityValidationResult newInstance(){ + return new EntityValidationResult(); + } + + @XmlElement(name = "ValidatedEntityId") + private int validatedEntityId; + + @XmlElement(name = "ValidatedEntityUuid") + @XmlJavaTypeAdapter(UUIDAdapter.class) + @Type(type = "uuidUserType") + @Column(length=36) //TODO needed? Type UUID will always assure that is exactly 36 + @FieldBridge(impl = UuidBridge.class) //TODO required? + private UUID validatedEntityUuid; + + @XmlElement(name = "ValidatedEntityClass") + private String validatedEntityClass; + + @XmlElement(name = "UserFriendlyDescription") + private String userFriendlyDescription; + + @XmlElement(name = "UserFriendlyTypeName") + private String userFriendlyTypeName; + + @XmlElement(name = "CrudEventType") + @Enumerated(EnumType.STRING) + private CRUDEventType crudEventType; + + @XmlElementWrapper(name = "EntityConstraintViolations") + @OneToMany(mappedBy = "entityValidationResult") + @Cascade({ CascadeType.ALL }) + @Fetch(value = FetchMode.JOIN) + private Set entityConstraintViolations; + + + protected EntityValidationResult(){ + super(); + } + + + public int getValidatedEntityId(){ + return validatedEntityId; + } + + + public void setValidatedEntityId(int validatedEntityId){ + this.validatedEntityId = validatedEntityId; + } + + + public UUID getValidatedEntityUuid(){ + return validatedEntityUuid; + } + + + public void setValidatedEntityUuid(UUID validatedEntityUuid){ + this.validatedEntityUuid = validatedEntityUuid; + } + + + public String getValidatedEntityClass(){ + return validatedEntityClass; + } + + + public void setValidatedEntityClass(String validatedEntityClass){ + this.validatedEntityClass = validatedEntityClass; + } + + + public String getUserFriendlyTypeName(){ + return userFriendlyTypeName; + } + + + public void setUserFriendlyTypeName(String userFriendlyTypeName){ + this.userFriendlyTypeName = userFriendlyTypeName; + } + + + public CRUDEventType getCrudEventType(){ + return crudEventType; + } + + + public void setCrudEventType(CRUDEventType crudEventType){ + this.crudEventType = crudEventType; + } + + + public String getUserFriendlyDescription(){ + return userFriendlyDescription; + } + + + public void setUserFriendlyDescription(String userFriendlyDescription){ + this.userFriendlyDescription = userFriendlyDescription; + } + + + public Set getEntityConstraintViolations(){ + if (entityConstraintViolations == null) { + entityConstraintViolations = new HashSet(); + } + return entityConstraintViolations; + } + + + public void addEntityConstraintViolation(EntityConstraintViolation ecv){ + if (ecv != null) { + getEntityConstraintViolations().add(ecv); + } + } + + + public void removeEntityConstraintViolation(EntityConstraintViolation ecv){ + if (ecv != null) { + getEntityConstraintViolations().remove(ecv); + } + } +} diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/SeverityType.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/SeverityType.java new file mode 100644 index 0000000000..4652003e89 --- /dev/null +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/validation/SeverityType.java @@ -0,0 +1,109 @@ +/** +* Copyright (C) 2009 EDIT +* European Distributed Institute of Taxonomy +* http://www.e-taxonomy.eu +* +* The contents of this file are subject to the Mozilla Public License Version 1.1 +* See LICENSE.TXT at the top of this package for the full license terms. +*/ +package eu.etaxonomy.cdm.model.validation; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.usertype.UserType; + +import eu.etaxonomy.cdm.validation.Severity; + +/** + * A Hibernate {@code UserType} for persisting {@link Severity} instances. + * + * @see EntityConstraintViolation + * + * @author ayco_holleman + * + */ +public class SeverityType implements UserType { + + @Override + public int[] sqlTypes(){ + return new int[] { java.sql.Types.VARCHAR }; + } + + + @SuppressWarnings("rawtypes") + @Override + public Class returnedClass(){ + return Severity.class; + + } + + + @Override + public boolean equals(Object x, Object y) throws HibernateException{ + if (x == null) { + if (y == null) { + return true; + } + return false; + } + if (y == null) { + return false; + } + return ((Severity) x).equals(y); + } + + + @Override + public int hashCode(Object x) throws HibernateException{ + return x.getClass().hashCode(); + } + + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) throws HibernateException, SQLException{ + String severity = rs.getString(names[0]); + return rs.wasNull() ? null : Severity.forName(severity); + } + + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) throws HibernateException, SQLException{ + st.setString(index, value == null ? null : value.toString()); + } + + + @Override + public Object deepCopy(Object value) throws HibernateException{ + return value; + } + + + @Override + public boolean isMutable(){ + return false; + } + + + @Override + public Serializable disassemble(Object value) throws HibernateException{ + return null; + } + + + @Override + public Object assemble(Serializable cached, Object owner) throws HibernateException{ + return null; + } + + + @Override + public Object replace(Object original, Object target, Object owner) throws HibernateException{ + return original; + } + +} diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/CRUDEventType.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/CRUDEventType.java new file mode 100644 index 0000000000..ae88bb95cc --- /dev/null +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/CRUDEventType.java @@ -0,0 +1,26 @@ +/** +* Copyright (C) 2009 EDIT +* European Distributed Institute of Taxonomy +* http://www.e-taxonomy.eu +* +* The contents of this file are subject to the Mozilla Public License Version 1.1 +* See LICENSE.TXT at the top of this package for the full license terms. +*/ + +package eu.etaxonomy.cdm.validation; + +/** + * The CRUD event that triggered a validation. When an entity violates some constraint, it + * might be helpful to report back to the user what type of CRUD event caused the violation. + * Note that validation may not not have been triggered by any CRUD event at all, e.g. during + * some batch-like validation process. Level-2 validation can never be triggered by a DELETE + * event, because Level-2 validation only validates the entity itself. However, a DELETE event + * can possibly trigger a Level-3 validation, because that disrupts the object graph the + * entity was part of. + * + * @author ayco holleman + * + */ +public enum CRUDEventType{ + NONE, INSERT, UPDATE, DELETE +} diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Severity.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Severity.java new file mode 100644 index 0000000000..191c8e1080 --- /dev/null +++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/validation/Severity.java @@ -0,0 +1,98 @@ +/** +* Copyright (C) 2009 EDIT +* European Distributed Institute of Taxonomy +* http://www.e-taxonomy.eu +* +* The contents of this file are subject to the Mozilla Public License Version 1.1 +* See LICENSE.TXT at the top of this package for the full license terms. +*/ + +package eu.etaxonomy.cdm.validation; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Payload; + +/** + * A class conveying the severity of a {@link ConstraintViolation}. Severity levels are + * extraneous to the javax.validation framework and can only be conveyed using generic + * {@link Payload} objects. Unfortunately, only the class of those objects is communicated + * back to the client. The class is the message. Concrete instances or {@code enum} + * s (an obvious choice for severity levels) cannot function as {@code Payload} objects. + * The Severity class enables you to program using true Severity instances (one for each + * level), while behind the scenes only the class of those instances is taken into + * account. + * + * @author ayco holleman + */ +public abstract class Severity implements Payload { + + public static final Notice NOTICE = new Notice(); + public static final Warning WARNING = new Warning(); + public static final Error ERROR = new Error(); + + //@formatter:off + public static final class Notice extends Severity {}; + public static final class Warning extends Severity {}; + public static final class Error extends Severity {}; + //@formatter:on + + /** + * Get {@code Severity} object for the specified {@code String} representation. Does the + * opposite of {@link #toString()}. + * + * @param name + * The {@code String} representation of {@code Severity} object you want. + * + * @return The {@code Severity} object + */ + public static Severity forName(String name) + { + if (name.equals(Error.class.getSimpleName())) { + return ERROR; + } + if (name.equals(Warning.class.getSimpleName())) { + return WARNING; + } + return NOTICE; + } + + + /** + * Get the {@code Severity} of the specified {@code ConstraintViolation}. + * + * @param error + * The {@code ConstraintViolation} + * + * @return The {@code Severity} + */ + public static Severity getSeverity(ConstraintViolation error) + { + Set> payloads = error.getConstraintDescriptor().getPayload(); + for (Class payload : payloads) { + if (payload == Error.class) { + return ERROR; + } + if (payload == Warning.class) { + return WARNING; + } + if (payload == Notice.class) { + return NOTICE; + } + } + return null; + } + + + private Severity() + { + } + + + @Override + public String toString() + { + return getClass().getSimpleName(); + } +} diff --git a/cdmlib-persistence/src/main/resources/eu/etaxonomy/cdm/hibernate.cfg.xml b/cdmlib-persistence/src/main/resources/eu/etaxonomy/cdm/hibernate.cfg.xml index 3503fd362a..986bd16cb7 100644 --- a/cdmlib-persistence/src/main/resources/eu/etaxonomy/cdm/hibernate.cfg.xml +++ b/cdmlib-persistence/src/main/resources/eu/etaxonomy/cdm/hibernate.cfg.xml @@ -190,6 +190,11 @@ + + + + +