final class DMOValidator
extends java.lang.Object
If validation is triggered for a single field, only the constraints and validation expression (if any) related to that field are validated. If validation is triggered for the entire record, all constraints are checked and all validation expressions are executed.
Mandatory Field Check
This type of check can be done completely in-process, without accessing
the database at all. A single field check is performed within the context
of an impending update to the field's current value. The proposed new
value is provided by the caller and the test is simply that this value
does not represent the unknown value. For a full record check, reflection
is used to retrieve the current values of all non-nullable properties in a
DMO. If any value (including any element in an array property) represents
the unknown value, the check fails.
Unique Constraint Check
This type of check requires the execution of (at least) one query against
the database per constraint being checked. This check can be triggered
during update of a single field, or arbitrarily against an entire record.
To check against a single constraint, reflection is used to collect the
current value of each component property (except in the case of an update,
in which case the proposed new value is used for the property being
updated). This data is used for the substitution parameters of a
pre-defined HQL query. The query is submitted to the database and if any
record returns, the validation fails. For a full record check, the same
process is repeated for all unique constraints defined for the DMO. In
the latter case, all data is collected from the DMO via reflection.
Also in the latter case, the modified record must not be flushed to the
database before the query is executed (it is the default behavior of
Hibernate to flush modifications to the database before executing any
query). Thus, we explicitly disable flushing temporarily in this case,
then re-enable it after the query is complete. In addition, to fully
mimic the Progress semantic, we simulate the ability to perform dirty
reads across concurrent transactions in other contexts, using the DirtyShareContext
.
Custom Validation
Not used at this time. This was originally intended to support field-level
validation expressions. However, these are implemented in converted
business logic today because they can invoke arbitrary code paths.
Important: while instances of this class contain mapped data to enable lookups of information required for validation, these data structures are all initialized at the time the instance is constructed. Instances of this class are shared across record buffers and across user contexts, so they must remain stateless with respect to the buffers and DMO records being validated.
Modifier and Type | Field and Description |
---|---|
private static java.util.Map<java.lang.Class<?>,DMOValidator> |
cache
Cache of previously created instances, mapped by DMO interface
|
private java.util.Map<java.lang.reflect.Method,java.lang.Boolean> |
charProps
Ignore case flags, mapped by character getter methods
|
private static java.util.logging.Logger |
LOG
Logger
|
private java.util.Map<java.lang.String,java.lang.reflect.Method> |
notNullProperties
Map of non-nullable DMO properties to their getter methods
|
private static int |
p4glMaxIndexSize
The maximum permitted size for all combined fields of an index.
|
private java.util.Map<java.lang.reflect.Method,java.lang.String> |
propertyNames
Property names, mapped by getter methods
|
private java.util.Map<java.lang.String,java.lang.reflect.Method> |
setterMap
Setter methods, mapped by property names
|
private java.util.Map<java.lang.String,java.lang.reflect.Method> |
triggers
Names of properties which trigger validation mapped to their getters
|
private java.util.Map<java.lang.String,HQLBundle> |
uniqueConstraints
Map of unique index names to
HQLBundles |
Modifier | Constructor and Description |
---|---|
private |
DMOValidator(RecordBuffer buffer)
Construct a validator, based upon the database, and the DMO interface
and class represented by
buffer . |
Modifier and Type | Method and Description |
---|---|
private void |
buildUniqueBundle(java.lang.String index,
java.util.List<java.lang.String> properties,
java.util.Map<java.lang.String,java.lang.reflect.Method> getters,
RecordBuffer buffer)
Construct and populate an
HQLBundle suitable for
producing a query to test a unique constraint. |
(package private) void |
checkIndexMaxSize(RecordBuffer buffer,
ValidationData vData,
java.lang.String property,
BaseDataType value)
Validate the record against the maximum allowable index size for the current configured
dialect.
|
private void |
checkIndexSizeWorker(RecordBuffer buffer,
java.util.Map<java.lang.String,java.lang.Integer> p4glFieldSizeCache,
java.util.Map<java.lang.String,java.lang.Integer> dialectFieldSizeCache,
java.util.Collection<java.lang.String> dirtyIndexes,
java.lang.String property,
BaseDataType newValue,
boolean uniqueIndexes)
Private worker for checking a the index key size of a record for a set of indexes.
|
private void |
checkNotNull(Persistable target,
ValidationData vData,
java.lang.String property,
BaseDataType value)
Validate a single not-null constraint, or all such constraints defined
for the
target DMO in the validation parameters, depending
upon the value of the given property . |
private void |
checkUnique(RecordBuffer buffer,
Persistable target,
ValidationData vData,
java.lang.String index,
HQLBundle bundle,
java.lang.reflect.Method omit,
java.lang.String property,
BaseDataType value)
Validate a single unique constraint defined for the target record.
|
private void |
checkUniqueAndShare(RecordBuffer buffer,
Persistable target,
ValidationData vData,
java.lang.String property,
BaseDataType value)
Validate a single unique constraint or all unique constraints defined
in the validation parameters for the target record.
|
private int |
computeP4GLFieldSize(BaseDataType value)
Computes the size (in bytes) of a field value occupied in an index-key in P4GL.
|
(package private) static DMOValidator |
getInstance(RecordBuffer buffer)
Get an instance of a validator, based upon the DMO interface represented
by
buffer . |
(package private) java.util.Set<java.lang.String> |
getNonNullables()
Get the set of names of properties which are non-nullable, for the DMO
type managed by this validator.
|
private java.lang.Object |
getPropertyValue(java.lang.Object target,
java.lang.reflect.Method getter,
java.lang.Object index)
Extract current value of a DMO property, given the DMO, the getter
method for the property, and an optional index argument to the getter
method.
|
private void |
uniqueConstraintViolation(RecordBuffer buffer,
HQLBundle bundle,
java.lang.Object[] args)
Throw a validation exception for a unique constraint violation.
|
(package private) void |
validate(RecordBuffer buffer,
Persistable target,
ValidationData vData)
Execute validation on the properties and constraints of the target
record specified in the given validation parameters.
|
private void |
validate(RecordBuffer buffer,
Persistable target,
ValidationData vData,
java.lang.String property,
BaseDataType value)
Execute validation on the proposed property value change to the record.
|
(package private) void |
validate(RecordBuffer buffer,
ValidationData vData,
java.lang.String property,
BaseDataType value)
Execute validation on the proposed property value change to the record.
|
private static final java.util.logging.Logger LOG
private static final java.util.Map<java.lang.Class<?>,DMOValidator> cache
private static final int p4glMaxIndexSize
Set it to 0 to disable P4GL index-key limit check. The dialect specific limit is still enforced by each dialect because otherwise irrecoverable errors may occur when persisting records with wider key length.
private final java.util.Map<java.lang.String,HQLBundle> uniqueConstraints
HQLBundles
private final java.util.Map<java.lang.String,java.lang.reflect.Method> notNullProperties
private final java.util.Map<java.lang.String,java.lang.reflect.Method> triggers
private final java.util.Map<java.lang.String,java.lang.reflect.Method> setterMap
private final java.util.Map<java.lang.reflect.Method,java.lang.String> propertyNames
private java.util.Map<java.lang.reflect.Method,java.lang.Boolean> charProps
private DMOValidator(RecordBuffer buffer)
buffer
.
The following data structures are initialized in this constructor:
triggers
- A map of DMO properties to their companion
getter methods. Only those properties whose modification must
trigger a validation check (of any type) are keys in this map.
The getter method values of this map are provided for convenience
during validation processing.
notNullProperties
- A map of DMO properties to their
companion getter methods. The contents of this map are a subset
of the contents of the triggers
map. Only those
setters which update non-nullable properties are contained in this
map.
uniqueConstraints
- A map of unique index names to the
HQLBundle
objects which will be used to issue database
queries to test for the existence of records which should be
unique.
buffer
- Record buffer which contains information required to create a
new validator instance.static DMOValidator getInstance(RecordBuffer buffer)
buffer
. Validator instances are immutable once created,
and as such they are shared across multiple buffers and multiple user
contexts (they are stateless, so this is thread-safe).buffer
- Record buffer which contains information required to create a
new validator instance.buffer
.java.util.Set<java.lang.String> getNonNullables()
void validate(RecordBuffer buffer, Persistable target, ValidationData vData) throws ValidationException, PersistenceException
buffer
- Record buffer.target
- Record to be validated. If null
, then
buffer
's current record is validated.vData
- Validation parameters.ValidationException
- if any aspect of validation fails.PersistenceException
- if any error occurs querying the database.StopConditionException
- if the size of a index key is found greater than allowable limits.java.lang.RuntimeException
- if there is an error reflectively inspecting the record being
validated.void validate(RecordBuffer buffer, ValidationData vData, java.lang.String property, BaseDataType value) throws ValidationException, PersistenceException
This method is invoked when a setter method is about to be invoked
upon a DMO to change the value of one of its properties, or when the
entire record must be inspected and validated. Validation is skipped
if property
has not been determined to be a validation
trigger.
buffer
- Record buffer containing the record to be validated.vData
- Validation parameters.property
- DMO property to be updated in target
if validation
completes successfully. If null
, the validation
parameters are consulted to determine which properties and
constraints are to be validated.value
- Value to be set into property
if validation
completes successfully.ValidationException
- if any aspect of validation fails.PersistenceException
- if any error occurs querying the database.java.lang.RuntimeException
- if there is an error reflectively inspecting the record being
validated.private void validate(RecordBuffer buffer, Persistable target, ValidationData vData, java.lang.String property, BaseDataType value) throws ValidationException, PersistenceException
This method is invoked when a setter method is about to be invoked upon a DMO to change the
value of one of its properties, or when the entire record must be inspected and validated.
Validation is skipped if setter
would update a property which has not been
determined to be a validation trigger.
buffer
- Record buffer.target
- Record to be validated. If null
, then buffer
's current
record is validated.vData
- Validation parameters.property
- DMO property to be updated in target
if validation completes
successfully. If null
, the validation parameters are consulted to
determine which properties and constraints are to be validated.value
- Value to be set into property
if validation completes successfully.ValidationException
- if any aspect of validation fails.PersistenceException
- if any error occurs querying the database.StopConditionException
- if the size of a index key is found greater than allowable limits.java.lang.RuntimeException
- if there is an error reflectively inspecting the record being validated.private void checkNotNull(Persistable target, ValidationData vData, java.lang.String property, BaseDataType value) throws ValidationException, java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException
target
DMO in the validation parameters, depending
upon the value of the given property
.target
- The DMO to be validated.vData
- Validation parameters.property
- DMO property to be updated in target
if validation
completes successfully. If null
, the validation
parameters are consulted to determine which properties and
constraints are to be validated.value
- Value to be set into property
if validation
completes successfully.ValidationException
- if a constrained property contains the unknown value.java.lang.IllegalAccessException
- if a getter method used to inspect the DMO cannot be accessed.java.lang.reflect.InvocationTargetException
- if a getter method used to inspect the DMO throws an exception
upon invocation.void checkIndexMaxSize(RecordBuffer buffer, ValidationData vData, java.lang.String property, BaseDataType value) throws ValidationException, PersistenceException, java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException
ValidationException
is thrown.buffer
- Record buffer containing the record to be validated.vData
- Validation parameters.property
- DMO property to be updated in target
if validation completes
successfully. If null
, the validation parameters are consulted to
determine which properties and constraints are to be validated.value
- Value to be set into property
if validation completes successfully.ValidationException
- if any index affected by this record will pass the maximum supported sizePersistenceException
- if any error occurs querying the database.java.lang.IllegalAccessException
- if a getter method used to inspect the DMO cannot be accessed.java.lang.reflect.InvocationTargetException
- if a getter method used to inspect the DMO throws an exception upon invocation.StopConditionException
- if the size of a index key is found greater than allowable limits.private void checkIndexSizeWorker(RecordBuffer buffer, java.util.Map<java.lang.String,java.lang.Integer> p4glFieldSizeCache, java.util.Map<java.lang.String,java.lang.Integer> dialectFieldSizeCache, java.util.Collection<java.lang.String> dirtyIndexes, java.lang.String property, BaseDataType newValue, boolean uniqueIndexes) throws ValidationException, PersistenceException
computeP4GLFieldSize
.
TODO: Add a getter in BaseDataType for extracting the size used by an index ?buffer
- Record buffer containing the record to be validated.p4glFieldSizeCache
- The cache with already computed field sizes in the index of Progress from a
possible previous call. The map will be updated as index field sizes are computed.
If null
4GL validation is disabled.dialectFieldSizeCache
- The cache with already computed field sizes in the index of current dialect from a
possible previous call. The map will be updated as index field sizes are computed.
If null
dialect validation is disabled because it does not have
index size constraint.property
- The property to be checked. If not null
, the future value for it can
be found in newValue
instead of buffer's DMO. If null
then all the fields/properties are already set in the buffer's record.newValue
- The new value for property. If property
is null
, the
value is ignored.uniqueIndexes
- Flag for selecting the correct map of indexes. This method is called twice, once
for unique indexes and once for non-unique, with according parameter.ValidationException
- if any index affected by this record will pass the maximum supported sizePersistenceException
- if any error occurs querying the database.StopConditionException
- if the size of a index key is found greater than allowable limits.private int computeP4GLFieldSize(BaseDataType value)
value
- The value of the field to be computed.private void checkUniqueAndShare(RecordBuffer buffer, Persistable target, ValidationData vData, java.lang.String property, BaseDataType value) throws ValidationException, PersistenceException, java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException
buffer
- Record buffer containing the record to be validated.target
- The DMO to be validated.vData
- Validation parameters.property
- DMO property to be updated in target
if validation
completes successfully. If null
, the validation
parameters are consulted to determine which properties and
constraints are to be validated.value
- Value to be set into property
if validation
completes successfully.ValidationException
- if the constrained properties contains values which are not
unique to this record.PersistenceException
- if any error occurs querying the database.java.lang.IllegalAccessException
- if a getter method used to inspect the DMO cannot be accessed.java.lang.reflect.InvocationTargetException
- if a getter method used to inspect the DMO throws an exception
upon invocation.private void checkUnique(RecordBuffer buffer, Persistable target, ValidationData vData, java.lang.String index, HQLBundle bundle, java.lang.reflect.Method omit, java.lang.String property, BaseDataType value) throws ValidationException, PersistenceException, java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException
buffer
- Record buffer containing the record to be validated.target
- The DMO to be validated.vData
- Validation parameters.index
- Name of unique index being validated.bundle
- An object containing the HQL query statement and all getter
methods necessary to gather substitution parameter values from
target
.omit
- Getter method associated with a property for which we already
have a value. This method will not be used to retrieve
a value from target
via reflection. Instead,
the value
parameter is used. If
null
, all constrained properties are inspected
for current values via reflection.property
- DMO property to be updated in target
if validation
completes successfully. If null
, the validation
parameters are consulted to determine which properties and
constraints are to be validated.value
- Value to be set into property
if validation
completes successfully.ValidationException
- if the constrained properties contains values which are not
unique to this record.PersistenceException
- if any error occurs querying the database.java.lang.IllegalAccessException
- if a getter method used to inspect the DMO cannot be accessed.java.lang.reflect.InvocationTargetException
- if a getter method used to inspect the DMO throws an exception
upon invocation.private void uniqueConstraintViolation(RecordBuffer buffer, HQLBundle bundle, java.lang.Object[] args) throws ValidationException
buffer
- Record buffer representing the DMO whose constraint has been
violated.bundle
- Object from which property names will be extracted.args
- Current values of the properties which participate in the
unique constraint.ValidationException
- always.private void buildUniqueBundle(java.lang.String index, java.util.List<java.lang.String> properties, java.util.Map<java.lang.String,java.lang.reflect.Method> getters, RecordBuffer buffer) throws PersistenceException
HQLBundle
suitable for
producing a query to test a unique constraint. The bundle contains a
single HQL statement. The statement's where clause contains an
equality match for each property contained in names
.
It selects the unique record ID from the DMO currently associated with
buffer
. For example:
select customer.id from CustomerImpl as customer where customer.prop1 = ? and customer.prop2 = ?
A getter method is added to the bundle for the property associated with each equality match in the where clause. The bundle is stored in the map of unique constraints, keyed by the setter method associated with each property component of the unique constraint.
index
- Name of the unique index which defines the constraint to be
validated.properties
- List of property names which comprise a unique constraint for
table represented by the given record buffer.getters
- Map of getter methods by property names for the DMO interface
associated with the given record buffer.buffer
- Record buffer for which this HQLBundle is to be generated.PersistenceException
- if any errors occur looking up metadata regarding a property.private java.lang.Object getPropertyValue(java.lang.Object target, java.lang.reflect.Method getter, java.lang.Object index) throws java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException
target
- Target DMO record.getter
- Getter method for the property.index
- Index value to pass to the getter (for indexed getters only),
or null
if getter takes no arguments.java.lang.IllegalAccessException
- if getter method used to inspect the DMO cannot be accessed.java.lang.reflect.InvocationTargetException
- if getter method used to inspect the DMO throws an exception
upon invocation.