Contents
This document
What is JBeans?
Why use JBeans?
What does JBeans replace?
Making life easier
Installation
Building
Case studies/scenarios
First steps/definitions
Using BeanProperty
Using IndexedBeanProperty
Using NestedBeanProperty
Using DynamicNestedBeanProperty
Using JavaBean
Events and conversion
Using JavaBean

JavaBean is different from all of the other class discussed thus far. It is a class that models the exact nature of JavaBeans, their properties and child JavaBeans. This class is also an example of the Composite pattern (GOF). The reason for this pattern is simple, JavaBeans are composites by definition. A JavaBean has properties of any type, including other JavaBeans. Therefore a single JavaBean can contain references to an unlimited number of other JavaBeans and can provide access to those JavaBeans via JavaBean properties.

The JavaBean class also has another important feature, it only constructs information for properties that you, as a developer, request. This means that when it is contructed, it does not contain any information except the class of the JavaBean. This is good because it decreases the construction time for this class and also reduces the memory consumption. Other classes that try to accomplish the same thing will iterate through the classes methods and store information about every JavaBean property in the class. This is very inefficient if an application will only be using two or three and the JavaBean contains thirty. It wastes time and memory to build objects like this. Not with JavaBean however. The JavaBean class in the JBeans package does not store any information about properties or child JavaBeans until the application requests it to.

We'll talk more about this later. Now let's take a look at the constructors for the JavaBean class. The only thing that is required in order to construct a new JavaBean is the class that the JavaBean is being built for. The constructors look like this:

    public JavaBean(Class beanClass);
    
    public JavaBean(String beanClass);
    
    public JavaBean(Class beanClass, boolean strict) throws BeanException;
    
    public JavaBean(String beanClass, boolean strict) throws BeanException;
    

As you can see, the Class object can be substituted with the fully qualified class name of the JavaBean. The second two constructors take a second parameter, the strict parameter that functions the same way that the strict parameter on the NestedBeanProperty does. More about this later.

After the JavaBean has been constructed, you can do a number of things with it. You can add new local or nested properties to it, you can retrieve and change local and nested property values with it, you can request local and nested BeanProperty classes from it and also request local and nested JavaBean classes from it. The local and nested JavaBean classes are either child JavaBean's of the original JavaBean or child JavaBean's of child JavaBeans, etc.

 
Adding properties

Let's take a look at adding properties to the JavaBean. First we'll add a local property and then we'll add a nested property. When adding properties to the JavaBean, we are actually just adding information so that it knows how to access the properties. If we add a new local property, the property must be a valid property for the class of the JavaBean instance. Here's an example of adding a local property.

    /* Here are the Java bean classes we will use in our examples */
    public class User {
        private String name;
        private Address [] addresses = new Address[2];
        
        public String getName() { return name; }
        public void setName(String value) { name = value; }
        
        public Address getAddress(int index) { return addresses[index]; }
        public void setAddress(int index, Address value) { addresses[index] = value; }
    }

    public class Address {
        private String city;
        private Type type;
        
        public String getCity() { return city; }
        public void setCity(String value) { city = value; }
        
        public Type getType() { return type; }
        public void setType(Type value) { type = value; }
    }
    
    public class Type {
        private int typeCode;
        
        public int getTypeCode() { return typeCode; }
        public void setTypeCode(int value) { typeCode = value; }
    }
  
    // Example of adding a local property    
    User userInstance = new User();
    
    try {
        JavaBean jb = new JavaBean(User.class);
      
        jb.addProperty("name");
    } catch (BeanException be) {
        be.printStackTrace();
    }
    

This code doesn't do anything exciting but it does setup the JavaBean instance so that it knows about the name property and is ready to take requests for that property. However, it is not necessary to add properties before accessing them. Why should I ever to add a property when I can just access the property directly? Good question. By adding a property during initialization, the first access of that property will not require the JavaBean class to setup the information for that property. Really it is only useful when the first access must be just as fast as subsequent accesses. Here is an example of adding a nested property to the JavaBean

    // Example of adding a local property    
    User userInstance = new User();
    JavaBean jb = new JavaBean(User.class);
    
    try {
        jb.addProperty("address.type.typeCode");
    } catch (BeanException be) {
        be.printStackTrace();
    }
    

This one is a little more important. Remember that when NestedBeanProperty was constructed you had to provide all the indexes for any indexed properties? Well, this is not the same for JavaBean. The reason for this will become apparent later, but this is important to realize. The JavaBean now contains all the information needed to access the nested property type and the nested property typeCode. For the rest of our examples, we will not add the properties before using them.

 
BeanProperty objects

Let's now take a look at retrieving BeanProperty objects and using them. The method that you can use to request a BeanProperty is this:

    public BeanProperty getBeanProperty(String propertyName) throws BeanException;
    

This method takes the name of the local or nested property being requested. It also throws a BeanException if the propertyName is not valid. Here's some examples of usng this method and also using the returned BeanProperty.

    // Example of requesting and using a local property    
    User userInstance = new User();
    JavaBean jb = new JavaBean(User.class);
    userInstance.setName("Brian");
    
    // Example of using the BeanProperty to get the property
    try {
        BeanProperty property = jb.getBeanProperty("name");
        String name = (String) property.getPropertyValue(userInstance);
      
        System.out.println("The user's name: " + name);
    } catch (BeanException be) {
        be.printStackTrace();
    }
    
    /*
    Ouptut:
    The user's name: Brian
    */ 
    
    // Example of using the BeanProperty to set the property
    try {
        BeanProperty property = jb.getBeanProperty("name");
        property.setPropertyValue(userInstance, "Joe");
      
        System.out.println("The user's name: " + user.getName());
    } catch (BeanException be) {
        be.printStackTrace();
    }
    
    /*
    Ouptut:
    The user's name: Joe
    */ 
    

This example is simple. We have already seen how to use the BeanProperty class. All we are doing now is requesting the BeanProperty class for the name property from the JavaBean. Once we have the BeanProperty class, we can do whatever is needed with it. This method also works with nested properties. Here is an example of using it for nested properties:

    // Retrieving the typeCode property using NestedBeanProperty
    User userInstance = new User();
    Address addressInstance = new Address();
    Type typeInstance = new Type();
    JavaBean jb = new JavaBean(User.class);
    
    typeInstance.setTypeCode(42);
    addressInstance.setType(typeInstance);
    userInstance.setAddress(addressInstance);
    
    // Example of using the BeanProperty to get the property
    try {
        BeanProperty property = jb.getBeanProperty("address.type.typeCode");
        Integer typeCode = (Integer) property.getPropertyValue(typeInstance);
      
        System.out.println("The address type code: " + typeCode);
    } catch (BeanException be) {
        be.printStackTrace();
    }
    
    /*
    Ouptut:
    The address type code: 42
    */ 
    

Since we are getting back a BeanProperty, and BeanProperty only works on local properties, we use the instance of the Type class when accessing the typeCode property. Other than this small difference this is the same as requesting the BeanProperty for local properties.

 
Child JavaBean objects

Let's continue by looking at child JavaBeans. Any JavaBean instance can have an unlimited number of child JavaBean instances contained within it. These can be requested using this method:

    public JavaBean getChildJavaBean(String propertyName) throws BeanException;
    

The JavaBean returned has all the same methods as the original JavaBean. Here's an example of using a child JavaBean

    // Retrieving the typeCode property using NestedBeanProperty
    User userInstance = new User();
    Address addressInstance = new Address();
    JavaBean jb = new JavaBean(User.class);
    
    addressInstance.setCity("Boulder");
    userInstance.setAddress(addressInstance);
    
    // Example of using the BeanProperty to get the property
    try {
        JavaBean child = jb.getChildJavaBean("address");
        BeanProperty property = child.getBeanProperty("city");
        String city = (String) property.getPropertyValue(addressInstance);
      
        System.out.println("The address city: " + city);
    } catch (BeanException be) {
        be.printStackTrace();
    }
    
    /*
    Ouptut:
    The address city: Boulder
    */ 
    

This example shows that we can get the child JavaBean for the Address Java bean. Once we have it, we use it to access the city property. This should make logical sense because the address Java bean is simply one of the properties of the User Java bean. Therefore asking the JavaBean instance for the User Java bean for its address child is correct.

 
Changing and retrieving

This process of requesting child JavaBean and BeanProperty objects is simplified if you use these two methods:

    public Object getPropertyValue(String propertyName, Object beanInstance) throws BeanException;
    
    public void setPropertyValue(String propertyName, Object beanInstance, Object value) throws BeanException;
    

These methods handle the work of retrieving child JavaBeans and BeanPropertys and then using them to change or retrieve property values. These look similar to the getPropertyValue and setPropertyValue methods on the other classes we have studied. The only difference is that they take an extra parameter which specifies the specific property being accessed. This makes sense because the JavaBean was not constructed with a specific property in mind. In order to access the property we must tell the JavaBean class which property we are trying to use. Here are a few examples of how to use these methods:

    // Retrieving the typeCode property using NestedBeanProperty
    User userInstance = new User();
    Address addressInstance = new Address();
    JavaBean jb = new JavaBean(User.class);
    
    addressInstance.setCity("Boulder");
    userInstance.setAddress(addressInstance);
    userInstance.setName("Frank");
    
    // Example of using the BeanProperty to get the property
    try {
        String name = (String) jb.getPropertyValue("name", userInstance);
        String city = (String) jb.getPropertyValue("address.city", userInstance);
      
        System.out.println("The user's name: " + name);
        System.out.println("The address city: " + city);
    } catch (BeanException be) {
        be.printStackTrace();
    }
    
    /*
    Ouptut:
    The user's name: Frank
    The address city: Boulder
    */ 
    
    // Example of using the BeanProperty to set the property
    try {
        jb.setPropertyValue("name", userInstance, "Sinatra");
        jb.setPropertyValue("address.city", userInstance, "New York");
      
        System.out.println("The user's name: " + user.getName());
        System.out.println("The address city: " + user.getAddress().getCity());
    } catch (BeanException be) {
        be.printStackTrace();
    }
    
    /*
    Ouptut:
    The user's name: Sinatra
    The address city: New York
    */ 
    

These examples show you how simple it is to access local and nested properties using the JavaBean and the getPropertyValue and setPropertyValue methods. One last thing about accessing properties is that as we mentiioned above, when requesting BeanProperty objects or adding properties, you should not include the indexes for indexed properties. However, when you are retrieving the properties using getPropertyValue or setPropertyValue then you must specify the indexes for any indexed properties. Here's a quick example (without the Java bean and all the extra code, just the getPropertyValue method call).

    ...
    jb.getPropertyValue("address[0].type.typeCode", userInstance);
    ...
    

 
Strictness

The last thing to discuss briefing about the JavaBean class is the strictness property. The strictness of a JavaBean is identical to the strictness of the NestedBeanProperty class. There is only one extra thing to worry about. When adding properties or requesting child JavaBeans or BeanPropertys or any method that might create new child JavaBean objects, the strictness of the new child JavaBean objects is the same as the current strictness of the original JavaBean object. Here is a small code example:

    JavaBean jb = new JavaBean(User.class);
    jb.setStrict(true);
    
    try {
        // Adding this property causes the child JavaBean for the address to be
        // created 
        jb.addProperty("address.type");
        
        JavaBean addressJB = jb.getChildJavaBean("address");
        System.out.println("The address JavaBean strictness: " + addressJB.isStrict());
    } catch (BeanException be) {
        be.printStackTrace();
    }
    
    /*
    Output:
    The address JavaBean strictness: true
    */