Last lecture we learned quite a lot about Java component based architecture
called JavaBeans.
JavaBeans are customizable components written in Java which can
be configured and used in many
different applications. JavaBeans have to adhere to certain coding
and interaction standards.
Lets look at the diagram below to summarize what we've seen so far
In addition to this we saw that how we can serialize beans using
Java built-in serialization mechanism. Now the issue we need to look at
is how do we actually know what properties are supported by a bean? Before
we answer this question, we need to digress for a moment and take a look
at java.lang.reflection. The reflection package offered in Java allows
us to get access to class fields and methods. You will see that this addition
makes Java incredibly powerful and dynamic. Lets take a look at the following
code
If you create main and run this, the result would be
Etc. So as you can see, reflection allows us to get all the methods
of ANY Java class. Look into its
return types, parameters and exception. Similarly we can look up
class Fields and Constructors.
Now if you think that this is cool, this is not it yet. We can also
INVOKE methods dynamically.
This is super useful. You will see why pretty soon. For now, lets
take a closer look at classes and
interface found in the reflection
Here is the example which illustrates how to invoke a method dynamically
We match set method, then form array of arguments (notice how array
is declared and initialized), then we just simply call invoke on
the instance the Method and pass to it a target (which is always an object
of a class or subclass which has this method) and an argument array. So
this is what Java reflection allows us to do.
We will see how powerful this can be shortly.
Now we are coming back to the question that we asked earlier about
beans: How do we know
what properties and methods are supported by a bean? The answer
to this question is offered
within java.beans package. The creator of a bean has an option of
providing description of a bean
in the form of Bean Info class. BeanInfo class contains meta information
about the bean. It is
really good idea to factor it out. The bean maybe easily used without
BeanInfo. Usually
BeanInfo is useful only to GUI builders. BeanInfo contains a lot
of information about a bean
(almost always, much more that we want to know :). BeanInfo allows
users to learn about
Bean properties, events, methods and much more.
Lets work our way through this Interface. First of all, all the
way on the bottom we see
Method and Property descriptors. Both of these are Feature Descriptors.
The
FeatureDescriptor class is the common base class for descriptors.
It supports some
common information that can be set and retrieved for any of the
introspection descriptors.
In addition it provides an extension mechanism so that arbitrary
attribute/value pairs can
be associated with a design feature.
Note that FeatureDescriptor provides additional flexibility by offering
bean creators to store
any kind of name value pairs. This means that if the descriptor
user knows about them as well, they can be retrieved and used in any manner.
isExpert in the FeatureDescriptor class means that this
property or method or event should be displayed only for expert
users. This is very convenient because
a lot of GUI builders today try to minimize the amount of things
that programmers need to fill in.
In the MethodDescriptor, we see getMethod which simply returns us
a handle to the method and
we can access array of descriptors of parameters of this method.
PropertyDescriptor also make sense.
We see that we can discriminate between bound and constrained properties,
get property type and
its read and write methods.
Another class which also extends FeatureDescriptor is BeanDescriptor,
which is also returned by
BeanInfo. This describes bean as a whole. Also offered by BeanInfo
default property index and
default event index, this is also done for the sake of GUI builders.
Finally, BeanInfo allows user to
access all icons associated with the bean. Okay, so this is great,
but who wants to do all of this?
Certainly programmers are too important to create BeanInfo. One
step towards making our life
easier is offered by SimpleBeanInfo class. This is an adapter class
much like AWT adapters.
It simply implements BeanInfo interfaces by having empty methods.
This is helpful because if we extend from SimpleBeanInfo, we can
override only the once
that we want. Still, there is work to be done. Now, Introspection
to the rescue! java.beans packages
comes with the class called Introspector. This class offers static
method getBeanInfo given a class,
which goes into a class definition and does its best at finding
out all the contained information.
Here is how it works:
The output looks like this
In exactly the same fashion we could have fetched all other information
which is contained
in the BeanInfo. Obviously when Introspector creates a bean it will
not be able to provide
meaningful strings of descriptions of methods and field, but this
is not too bad.
Let us see how this can be applied to our project. Consider the following extract
from the EworldConfig file
<RENDERER CLASS=edu.nyu.sejava.iskold.eworld.sites.DesertRnd>
<PROPERTY NAME=X VALUE=0/>
<PROPERTY NAME=Y VALUE=0/>
<PROPERTY NAME=Height VALUE=100/>
<PROPERTY NAME=Width VALUE=100/>
</RENDERER>
We can create instance of an appropriate renderer (DesertRnd in this
case) and then use
Introspectorto find appropriate setMethod and then simply INVOKE
it! Thus our code which
walks through the parsed tree instantiates objects and sets attributes
can be completely generic!
So what do we need to do? Given an object and property name we need
to find its write (set)
method. To do this we create a utility class called BeanUtilities.
BeanUtilities offers several
convenient static methods for extraction information out of the
BeanInfo. Here is the method that
allows us to get a property descriptor for a property
Now using the above method we can provide locators for read(get)
and write(set) methods like this
So now given any object which we instantiate based on the config
file, we can find the set
method for a given property. There is only one issue that needs
to be taken care of - type conversion.
We need to be able to handle these
<RENDERER CLASS=edu.nyu.sejava.iskold.eworld.sites.DesertRnd>
<PROPERTY NAME=Label
CLASS=java.lang.String VALUE=Hello/>
<PROPERTY NAME=Height
CLASS=java.lang.Integer VALUE=100/>
<PROPERTY NAME=Width
CLASS=java.lang.Integer VALUE=100/>
</RENDERER>
Furthermore, we would like to avoid putting
class in properties whenever possible,
we just want to say this
<RENDERER CLASS=edu.nyu.sejava.iskold.eworld.sites.DesertRnd>
<PROPERTY NAME=Label
VALUE=Hello/>
<PROPERTY NAME=Height
VALUE=100/>
<PROPERTY NAME=Width
VALUE=100/>
</RENDERER>
and somehow the magic should happen.
Remember that we just wrote a routine which
allows us to get setMethod of a property.
This set method will contain the description
of its arguments, along with types. Thus we can
deduce what type of property do we need to
create.
We are now ready to find out about another
clever engineering concept ingrained into beans -
Property Editors. At the very least, property
editor is a factory that knows how to convert
objects of particular type back and forth
between object and string representation.
For example an IntPropertyEditor knows how
convert between integers and strings.
Here is what Sun API says about property editors:
Now lets take a look at PropertyEditor interface and implementation
of ColorPropertyEditor
Property editors are found via PropertyEditorManager. With the following
two methods we can now
convert string value to correct method argument:
We are now ready to write generic code that walks through tag tree
and builds actual objects.
|