You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by di...@apache.org on 2003/11/17 22:41:27 UTC
cvs commit: jakarta-commons-sandbox/clazz/xdocs index.xml
dirkv 2003/11/17 13:41:27
Added: clazz/xdocs index.xml
Log:
copy of overview.xml to have something on the homepage
Revision Changes Path
1.1 jakarta-commons-sandbox/clazz/xdocs/index.xml
Index: index.xml
===================================================================
<?xml version="1.0" encoding="UTF-8"?>
<document>
<properties>
<title>
Clazz Overview
</title>
<author email="dmitri@apache.org">
Dmitri Plotnikov
</author>
</properties>
<body>
<section name="Clazz">
<p>
Clazz is an upgrade to the Java Beans mechanisms, reflecting new
requirements, new de facto standards and a wider range of potential
applications.
</p>
<ul>
<li><a href="#Requirements">Requirements</a>
<ul>
<li><a href="#Project Contributed Requirements">Project Contributed Requirements</a>
</li>
</ul>
</li>
<!--
<li><a href="#Clazz Class">Clazz Class</a>
</li>
<li><a href="#Client Side of Clazz">Client Side of Clazz</a>
<ul>
<li><a href="#Description Provider">Description Provider</a></li>
<li><a href="#Metadata Provider">Metadata Provider</a></li>
</ul>
</li>
-->
</ul>
</section>
<section name="Requirements">
<p>
Introduced by Stephen Colebourne, here's the initial list of
requirements (lifted from PROPOSAL.html):
</p>
<p>
The package should:
</p>
<p>
<ul>
<li>Handle all classes, not just beans</li>
<li>Support extensible metadata (not just for GUI builders)</li>
<li>Handle normal (today) bean conventions (get/set/add/put methods)</li>
<li>Handle future conventions that are not yet standard</li>
<li>Support method overloading</li>
<li>Provide a complete alternative to using java.beans.Introspector</li>
<li>Be simple to learn</li>
</ul>
</p>
<subsection name="Project Contributed Requirements">
<p>
<TBD> - requirements contributed by potential users of clazz
go here.
</p>
</subsection>
</section>
<section name="Clazz Class">
<p>
The best way to introduce the Clazz class is by comparing it to the
java.lang.Class class. Though both serve the same function as runtime
meta-data about a type, there are several important differences:
<ul>
<li>
Class is a final class embedded in the JVM. Its behavior cannot be
customized or enhanced. Clazz is an abstract class
and allows many alternative implementations.
</li>
<li>
Class describes a Java type in terms of fields, methods and constructors.
Clazz describes a type in terms of properties, operations
and instance factories. In the case when a Clazz represents a Java class,
those properties, operations and instance factories may or may not map
directly to fields, methods and constructors. The mapping is determined by the
implementation of the Clazz and can be quite non-trivial. For example,
a Clazz may map pairs of Java methods like getFoo() and setFoo() to a
single property called "foo". There may be many ways to describe the same
Java class with a Clazz.
</li>
<li>
Class is restricted to describing Java types.
Clazz does not have that limitation. A Clazz can be created dynamically out
of piece parts.
</li>
</ul>
</p>
</section>
<section name="Client Side of Clazz">
<p>
The client sees Clazz as a set of interfaces.
The following code illustrates a basic interaction between a
client and the Clazz package:
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class MyExample1 {
void listAllOperations(){
// Step 1. Obtain the ClazzLoader associated with the default
// clazz model. Provide a ClassLoader as a context
// for reflection.
ClazzLoader clazzLoader =
Clazz.getDefaultClazzLoader(MyExample1.class.getClassLoader());
// Step 2. Find the Clazz
Clazz clazz = clazzLoader.getClazzForName("my.example.Example");
// Step 3. Obtain operation descriptions from the clazz
List operations = clazz.getOperations();
for (Iterator iter = operations.iterator(); iter.hasNext();) {
ClazzOperation operation = (ClazzOperation) iter.next();
System.out.println("Operation: " + operation.getSignature());
}
}
}
</source>
<!--============================ - SOURCE - ============================-->
<p>
The <code>getDefaultClazzLoader()</code> static method invokes
a ClazzLoaderFactory which allocates and caches ClazzLoaders.
</p>
<p>
The roles of the three main classes: Clazz, ClazzLoader and
ClazzLoaderFactory are illustrated by the following diagram (green -
interfaces, blue - relationships):
</p>
<p>
<blockquote>
<img src="images/design.jpg"/>
</blockquote>
</p>
</section>
<section name="Runtime Clazzes">
<p>
Clazzes can be created dynamically out of piece parts: properties,
operations, instance factories etc. If the client sticks to the Clazz
APIs, there is no difference between clazzes created dynamically and
those based on the reflection of hard Java classes.
</p>
<subsection name="Creating a Clazz Dynamically">
<p>
In the following example we are creating a Clazz with a string
property called "foo".
</p>
<p>
We always use a clazz loader to instatiate a new Clazz. The ClazzLoader
maintains the integrity of inter-clazz references and also
serves as a cache for clazzes.
The instantiation method, <code>declareClass</code>, takes three
parameters: the name of the new clazz, the Java class of the
new Clazz object and the Java type the instances of the
Clazz will belong to.
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class MyExample2 {
void createCustomClazz(){
// Step 1. Obtain the ClazzLoader associated with the default
// clazz model. We always provide a class loader as well,
// since all ClazzLoaders are associated with ClassLoaders.
ClazzLoader clazzLoader =
Clazz.getDefaultClazzLoader(MyExample2.class.getClassLoader());
// Step 2. Create a new Clazz
BeanClazz clazz = (BeanClazz) clazzLoader.defineClazz(
"my.custom.DynamicBean", // Name of the new clazz
BeanClazz.class, // Type of the new clazz
Bean.class); // Type of instances of the new clazz
// Step 3. Build the new Clazz out of properties, operations etc
ClazzProperty prop =
new BeanClazzProperty(clazz, "foo", "java.lang.String");
clazz.addDeclaredProperty(prop);
// Step 4. Now we can use the new clazz
Object instance = clazz.newInstance();
clazz.getProperty("foo").set(instance, "bar");
String value = (String)clazz.getProperty("foo").get(instance);
// This prints "Dynamic property value: bar"
System.out.println ("Dynamic property value: " + value);
}
}
</source>
<!--============================ - SOURCE - ============================-->
</subsection>
</section>
<section name="Clazz Model">
<p>
Depending on the application, there may be different ways to
represent the same type as a clazz. A particular method of
describing types is a <i>clazz model</i>.
Technically a clazz model is managed by a ClazzLoaderFactory.
A ClazzLoaderFactory creates ClazzLoaders,
which in turn create Clazz's according to the requirements of that
specific model.
</p>
<p>
To introduce a new clazz model, the corresponding ClazzLoaderFactory
is registered with a unique key. Clients identify models by such
keys.
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class MyExample3 {
// Create and register a ClazzLoaderFactory for a custom model
static {
Clazz.setClazzLoaderFactory("myclazzes", new MyClazzLoaderFactory());
}
void listAllProperties(){
// Step 1. Obtain a ClazzLoader for the custom clazz model.
ClazzLoader clazzLoader =
Clazz.getClazzLoader("myclazzes", MyExample.class.getClassLoader());
// Step 2. Find the Clazz
Clazz clazz = clazzLoader.getClazzForName("my.example.Example");
// Step 3. Obtain property descriptions from the clazz
List properties = clazz.getProperties();
...
for (Iterator iter = properties.iterator(); iter.hasNext();) {
ClazzProperty property = (ClazzProperty) iter.next();
System.out.println("Property: " + property.getName());
}
}
}
</source>
<!--============================ - SOURCE - ============================-->
<p>
<TBD> - if more models are added, mention them here.
</p>
<p>
The Clazz package comes with two clazz model out of the box:
<code>"Standard"</code> and <code>"Extended"</code>.
</p>
<p>
The Standard model implements class introspection according to
the JavaBean specification.
</p>
<p>
The Extended model implement a
more "modern" definition of beans, the one that recognizes
"add", "remove", etc methods, supports List and Mapped properties,
allows the use of plural form of the property name etc.
</p>
</section>
<section name="Clazz Loader">
<p>
A ClazzLoader is responsible for creating and caching of
clazzes. Typically a ClazzLoader returned by a ClazzLoaderFactory
is a mere front for a whole family of ClazzLoaders, each responsible
for its own "kind" of Clazzes. For instance, a ClazzLoader performing
reflection will typically not be the same ClazzLoader as the one
managing types not based on reflection. To facilitate the creation
of such families of ClazzLoaders, the package provides such
aggregating ClazzLoader implementations as GroupClazzLoader and
CachingGroupClazzLoader.
</p>
</section>
<section name="Extended Property Types">
<p>
Clazz introduces two new kinds of properties: List and Mapped. A List property
is similar to the JavaBeans indexed property, except that to access a List
property you can use <code>java.util.List</code> interface. Similarly,
a Mapped property is accessed via the <code>java.util.Map</code> interface.
</p>
<subsection name="List Property">
<p>
The List property behaves differently in different models.
</p>
<p>
In the Standard model, Clazz recognizes methods like <code>getBook()</code>,
returning an array as well as indexed accessors: <code>getBook(int index)</code> and
<code>setBook(int index, value)</code>.
</p>
<p>
In the Extended model, the range of recognized accessors is much wider.
For a property called "book", Clazz will recognize and use the following accessors:
<ul>
<li>Whole collection read method as a method returning an array or a list
and named: <code>getBook()</code>,
<code>getBooks()</code>, <code>getBookList()</code>,
<code>getBookArray()</code> or <code>getBookVector()</code>.
</li>
<li>Whole collection set method as a method taking an array or a list as
its only argument and named: <code>setBook(array or list)</code>,
<code>setBooks(...)</code>, <code>setBookList(...)</code>,
<code>setBookArray(...)</code> or <code>setBookVector(...)</code>.
</li>
<li>Indexed get method: <code>getBook(int index)</code>
</li>
<li>Indexed set method: <code>setBook(int index, book)</code>
</li>
<li>Add method: <code>addBook(book)</code>
</li>
<li>Indexed add method: <code>addBook(int index, book)</code>
</li>
<li>Remove method: <code>removeBook(book)</code>
</li>
<li>Indexed remove method: <code>removeBook(int index)</code>
</li>
<li>Count method: <code>int getBookCount()</code> or <code>int getBookSize()</code>
etc.
</li>
</ul>
</p>
<p>
These accessors are not required. One get method, indexed or not is sufficient
for Clazz to recognize a List property.
</p>
<p>
When you acquire the value of this property using the Clazz API, you get an
object implementing the complete <code>java.util.List</code> interface.
The implementations of List methods invoke relevant accessor methods on the bean.
The implementation is smart enough to get by with whatever accessors are
available. However, complete lack of accessors will render the List inoperable.
For example, if the only accessors available are <code>getBook(int)</code>
and <code>setBook(int,book)</code>, you will be able to call <code>list.get(i)</code>
and <code>list.set(int,book)</code>, but you won't be able to iterate over the
list or add anything to it. In order enable those operations, you need either
<code>int getBookCount()</code> or <code>getBooks()</code>.
</p>
<p>
Whole collection accessors are used only in the absense of more specific accessors.
</p>
<p>
Example:
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class Album {
String getTrackTitle(int trackNumber) {...}
int getTrackTitleCount() {...}
}
public class MyExample4 {
void listAllTracks(Album album){
// Step 1. Find the Extended Clazz
Clazz clazz = Clazz.getClazz(album, "Extended");
// Step 2. Acquire the property value
List trackTitles = (List)clazz.get("trackTitles");
// Step 3. Work with the property value
for (Iterator iter = trackTitles.iterator(); iter.hasNext();) {
String title = (String) iter.next();
System.out.println("Track: " + title);
}
}
}
</source>
<!--============================ - SOURCE - ============================-->
</subsection>
<subsection name="Mapped Property">
<p>
Mapped property is similar to List property, except values are identified by keys
rather than integer indexes.
</p>
<p>
In the Standard model Mapped properties are not supported.
</p>
<p>
In the Extended model, the following accesors are recognized.
<ul>
<li>Whole collection read method as a method returning a Map
and named: <code>getBook()</code>,
<code>getBooks()</code> or <code>getBookMap()</code>.
</li>
<li>Whole collection set method as a method taking a Map as
its only argument and named: <code>setBook(...)</code>,
<code>setBooks(...)</code> or <code>setBookMap(...)</code>.
</li>
<li>Keyed get method: <code>getBook(key)</code>
</li>
<li>Keyed set method: <code>setBook(key, book)</code>
</li>
<li>Remove method: <code>removeBook(key)</code>
</li>
<li>Key-set method: <code>getBookKeys()</code> or <code>getBookKeySet()</code>.
This method returns an array or Collection of keys.
</li>
</ul>
</p>
<p>
These accessors are not required. One get method, keyed or not is sufficient
for Clazz to recognize a Mapped property.
</p>
<p>
When you acquire the value of this property using the Clazz API, you get an
object implementing the complete <code>java.util.Map</code> interface.
The implementations of the Map methods invoke relevant accessor methods on the bean.
Whole collection accessors are used only in the absense of more specific accessors.
</p>
<p>
Example:
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class Album {
String getComposer(String trackTitle) {...}
}
public class MyExample5 {
void listAllTracks(Album album){
// Step 1. Find the Extended Clazz
Clazz clazz = Clazz.getClazz(album, "Extended");
// Step 2. Acquire the property value
Map composerMap = (Map)clazz.get("composer");
// Step 3. Work with the property value
String composer = (String) composerMap.get("Love Me Two Times");
System.out.println("Composer: " + composer);
}
}
</source>
<!--============================ - SOURCE - ============================-->
</subsection>
</section>
<section name="Customization">
<p>
Customizability is the most important aspect of the Clazz package.
There are many points of customization and choosing the right one
for your task is very important.
</p>
<subsection name="Customizing an Individual Reflected Clazz">
<p>
Let's say we have a class Bean1 that has the property "data",
which is accessed with unusual methods: "storeData" and "retrieveData".
The implementations of ReflectionClazz included in the Clazz package
are only programmed to detect accessor methods with signatures
like "setData" and "getData", therefore they will not recognize
these non-standard accessors.
</p>
<p>
We will address the problem by creating a customized implementation
of Clazz. We will subclass one of the regular implementations
and modify its behavior.
</p>
<p>
We put the custom implementation of Clazz into the same package
as the class itself and name it
<code><i><Class><Model></i>Clazz</code>.
By naming the implementaion like this, we make it possible for
the ClazzLoader to discover and load it.
</p>
<p>
In our particular scenario "data" will not be automatically
recognized as a property at all, because it does not have any
standard accessors. Therefore, we will have to create and
register that property explicitly.
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class Bean1ExtendedClazz extends ExtendedReflectedClazz {
public Bean1ExtendedClazz(ClazzLoader loader, Class instanceClass) {
super(loader, instanceClass);
}
protected void introspectProperties() {
super.introspectProperties();
ReflectedScalarProperty property =
new ReflectedScalarProperty(this, "data");
Class javaClass = Bean1.class;
try {
property.setReadMethod(
javaClass.getMethod("retrieveData", null));
property.setWriteMethod(
javaClass.getMethod("storeData", new Class[]{String.class}));
}
catch (NoSuchMethodException e){
}
addProperty(property);
}
}
</source>
<!--============================ - SOURCE - ============================-->
<p>
Now, let's say one of the accessors is the standard "getData",
while the other one is the unusual "storeData". This case is
different in that the standard implementation of ReflectedClazz
will recognize the property "data", but will not associate it
with the non-standard accessor. All we need to do in this case
is find the property and link it with the accessor.
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class Bean2ExtendedClazz extends ExtendedReflectedClazz {
public Bean2ExtendedClazz(ClazzLoader loader, Class instanceClass) {
super(loader, instanceClass);
}
protected void introspectProperties() {
super.introspectProperties();
ReflectedScalarProperty property = getProperty("data");
Class javaClass = Bean2.class;
try {
property.setWriteMethod(
javaClass.getMethod("storeData", new Class[]{String.class}));
}
catch (NoSuchMethodException e){
}
}
}
</source>
<!--============================ - SOURCE - ============================-->
<p>
Finally, consider class Bean3 that has a method
"getDataAsStream()". This method has a typical
accessor signature, therefore ReflectedClazz will recognize
"dataAsStream" as a legitimate property. If we don't want
it to be recognized as a property, we can filter it out like this:
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class Bean3ExtendedClazz extends ExtendedReflectedClazz {
public Bean3ExtendedClazz(ClazzLoader loader, Class instanceClass) {
super(loader, instanceClass);
}
protected void addProperty(ClazzProperty property) {
if (property.getName().equals("dataAsStream")){
return;
}
super.addProperty(property);
}
}
</source>
<!--============================ - SOURCE - ============================-->
</subsection>
<subsection name="Customizing a Clazz using BeanInfo">
<p>
If you have a custom implementation of BeanInfo for a custom bean,
that implementation will be used in the construction of the Clazz.
This method is not as flexible as the one described in the previous
section. However, you don't have to redo your customizations
if you already have a BeanInfo implementaion or if you
generate one automatically using XDoclet.
</p>
</subsection>
<subsection name="Customizing a Family of Reflected Clazzes">
<p>
In the previous section we examined customization of an
individual clazz. More commonly we need to customize a whole
family of Clazzes in a similar fashion. Let's say in our
application all classes implementing a "PersistentBean"
interface follow the same naming convention for accessors:
the read method is always called "retrieve<i>Foo</i>" and
the write method is always "store<i>Foo</i>".
Instead of customizing each of the clazzes independently,
we can introduce a generic customization for all of them.
</p>
<p>
ReflectedClazz uses <i>introspectors</i> to recognize
properties, operations etc. An introspector, in turn,
uses <code>AccessorMethodParser</code>s to recognize accessor
methods. Each parser is responsible for a certain type of
accessor: read, write, etc. So, we will need two custom
parsers: one for the "retrieve..." methods and one
for the "store..." methods. Since these parsers are very
simple, we will implement them as anonymous inner classes.
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class CustomPropertyIntrospector
extends ReflectedScalarPropertyIntrospector
{
private static final AccessorMethodParser READ_METHOD_PARSER =
new ReadAccessorMethodParser()
{
protected String requiredPrefix(){
return "retrieve";
}
};
protected AccessorMethodParser getReadAccessorMethodParser(){
return READ_METHOD_PARSER;
}
private static final AccessorMethodParser WRITE_METHOD_PARSER =
new WriteAccessorMethodParser()
{
protected String requiredPrefix(){
return "store";
}
};
protected AccessorMethodParser getWriteAccessorMethodParser(){
return WRITE_METHOD_PARSER;
}
}
</source>
<!--============================ - SOURCE - ============================-->
<p>
The next step is to write a custom implementation of Clazz. This
implementation will be the same as its superclass,
StandardReflectedClazz, with the exception of the way it recognizes
properties. The custom clazz will use the above custom property
introspector.
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class CustomClazz extends StandardReflectedClazz {
protected static final
ReflectedPropertyIntrospector[] PROPERTY_INTROSPECTORS =
new ReflectedPropertyIntrospector[] {
new CustomPropertyIntrospector()
};
public CustomClazz(ClazzLoader loader, Class instanceClass) {
super(loader, instanceClass);
}
protected ReflectedPropertyIntrospector[] getPropertyIntrospectors() {
return PROPERTY_INTROSPECTORS;
}
}
</source>
<!--============================ - SOURCE - ============================-->
<p>
Now we need to create a custom Clazz Loader that will produce
instances of CustomClazz for all classes implementing the
PersistentBean interface.
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
public class CustomClazzLoader extends ReflectedClazzLoader {
public CustomClazzLoader(
ModelClazzLoader modelClazzLoader,
ClassLoader classLoader)
{
super(modelClazzLoader, classLoader);
}
public boolean isSupportedClass(Class javaClass) {
return PersistentBean.class.isAssignableFrom(javaClass);
}
protected Clazz createClazz(Class javaClass) {
return new CustomClazz(getModelClazzLoader(), javaClass);
}
}
</source>
<!--============================ - SOURCE - ============================-->
<p>
The final step is to statically register the custom clazz loader.
A clazz loader belongs to some clazz model, perhaps the default
one. In the registration process we associate the clazz loader
we created with the corresponding model.
</p>
<!--============================ + SOURCE + ============================-->
<source>
<b/>
ClazzLoaderFactory factory =
Clazz.getClazzLoaderFactory(Clazz.EXTENDED_CLAZZ_MODEL);
factory.addClazzLoaderClass(CustomClazzLoader.class);
</source>
<!--============================ - SOURCE - ============================-->
</subsection>
</section>
</body>
</document>
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org