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>
          &lt;TBD&gt; - 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>
          &lt;TBD&gt; - 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>&lt;Class&gt;&lt;Model&gt;</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