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="[EMAIL PROTECTED]"> 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: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]