Introduction
In my
previous blog entry, I presented an example on the new reference resolver
add-on of ExtVal. It was able to read from a property from a MBean and use that
value for validation.
As it was
designed as a Reference Resolver, you had to specify a parameter at the cross
field validation annotation of ExtVal where you needed the resolver. This is
not friendly if you have designed some kind of resolver that you would use on a
lot of locations. Therefore the add-on
has foreseen the option to create a new Reference Grammar. In this text I'll
explain you how you can convert a reference resolver to a reference grammar.
Coding it
Basically,
there are 2 small differences between a Reference Resolver and a Reference Grammar
and you can use almost the same code in both situations. You only need to
structure it a bit different. Besides
the registration of your grammar with the ExtVal add-on, see further, you
should extend from the AbstractReferenceGrammar class. This class has a type
parameter that needs to be specified. In
our case, it is just a simple object that holds the bean and property name. If
you remember from my previous text, we invented a new grammar 'mbean@property'
and the object just holds the 2 parts of the grammar (before and after the
@-sign).
Your
grammar has to implement 4 methods, so the empty grammar looks like this
@InvocationOrder(100)public class MBeanGrammar extends AbstractReferenceGrammar{@Overridepublic void initGrammar(){}@Overrideprotected boolean targetNotCompatibleWithGrammar(String targetValue){}@Overrideprotected MBeanGrammarData determineReference(String targetValue, MetaDataEntry metadataEntry){}@Overridepublic ReferencedValue resolveReference(MBeanGrammarData targetValue, MetaDataEntry metadataEntry){}}
where the
(MBeanGrammarData object holds the bean and property name the user specified.
The
initGrammar method is called when you register the grammar with the
add-on. You can use this interception
point to do any initialization required to have a proper function of the
grammar. In our case we grab a reference
to the MBean server we will be using for the resolving of the value. Important thing to remember is that this
method is just executed once.
When the
ExtVal add-on encounters an annotation related to the cross field validation
feature, it will ask to all the grammars if it can handle the specified
reference. Our implementation should
return false for the targetNotCompatibleWithGrammar method when it contains the
@-sign (our grammar can handle the reference) and true when we don't know it
(like some EL-reference). The first
grammar that says that it can handle, will be used by the add-on. How is the order determined? Grammars can be
annotated with @InvocationOrder and the value you specify determines the
order. The default grammars (JavaBean
and EL) have values of 950 and 1000. So
when we specify a value of 100, our new grammar is the first to be contacted to
resolve the reference.
If we have
a reference that we should handle, the add-on calls the determineReference
method next. There we have the opportunity to parse the reference according to
the grammar. In our case it is just
splitting the reference around the @-sign and put it into the MBeanGrammarData
instance.
As a last
step, the resolveReference method is called where we get the MBeanGrammarData
instance we prepared in the previous step and we need to resolve the
reference. In our case we contact the
MBean server, ask for the correct bean and extract the value of the
property. That value is wrapped nicely
into the ReferencedValue and returned.
So the main
difference is that the reference resolver contained all the logic in the
resolveReference method and in the grammar version, we have the functionality
split into 4 methods. You can find the code here.
Configuration
This is an
additional step we need to do to integrate our grammar in the ExtVal
add-on. With the following 2 lines,
placed in a startupListener, the Reference Grammar is registered and ready to
use.
ExtValContext extValContext = ExtValContext.getContext();extValContext.getModuleConfiguration(AdvancedCrossValidationAddonConfiguration.class).addReferenceGrammar(new MBeanGrammar());
Testing it
This
version behaves exactly the same as our previous example, so I refer to the
other text on how you can test it.
Conclusion
As easy it
was to create a Reference Resolver, as easy it is to create a reference
Grammar. The grammar has the advantage
that you don’t have to specify a parameter at the cross field validation
annotation. But on the other side, if you only using it on a few locations, the
add-on will ask your code for every reference, if you understand it. So based on the concrete situation you have
to decide what you need, a resolver or a reference.