Properties

Overview

Java objects are often seen as having “properties” – a pair of getter and setter methods that let you access an attribute.

This allows us to specify the “Name” property of the “Customer” object, without worrying per-se about the code to set or get the name. As an example, JSP can set Java bean properties by specifying the property name and desired value in XML. This abstraction can be extremely useful.

This library takes this concept one step further by codifying a “path” of “nested” properties. Java bean properties may replace

customer.getName()

with a “Name” property of the customer object. This library goes further, and replaces

order.getCustomer().getName().getSurname()

with a “Customer:Name:Surname” nested property.

This sort of deep heirarchy occurs naturally when modelling business data. The need for the abstraction this library provides arose when trying to parameterise parts of an application. We wanted to say “When I pass you a Customer object, give me the first line of their address”. Nested properties provide a way to specify this without resorting to code.

The library provides some conveniences, such as dynamically handling null values, and optimising away reflective code.

Runtime class-generation and caching can be used very easily to obviate the need for reflection, without losing the flexibility it provides.
Two minute example

Class Property example Say we have the following classes:

public class Order {
  ...
  public Customer getCustomer() { ... }
  ...
}
public class Customer {
  ...
  public Address getAddress() { ... }
  ...
}
public class Address {
  ...
  public String getLine1() { ... }
  public String getLine2() { ... }
  ...
}

Now, we could do this to access line 1 of the address:

 result = order.getCustomer().getAddress().getLine1();

But what if there are nulls? What if we want to compare the first line of the address from two different orders? Whith properties this is really simple:

ClassProperty p = PropertyManager.getProperty(Order.class,"Customer:Address:Line1",true);
result = p.getValue(order);

The idea is simple enough – you use the string “Customer:Address:Line1” to specify a series of getXXX calls. Importantly, the library handles nulls for you, returning null if any of the objects in the path are null.

Note: this is not simply a wrapper around java reflection, since it includes non-reflective optimizations by making use of the cojen library to generate class files at runtime to acces these properties.

The real benefit lies in being able to specify which properties you wish to access at runtime. You can even pass this ability on to the users of your library or application. Unlike reflection, .class files are generated for the properties, and these files are cached – which means once you have specified a property the first time, further uses of the same property don’t use reflection and are very fast.

Property API vs Plain Java Bean Properties. The property API models accessor/mutator method pairs as conceptual variables called properties. The approach used is an extension of java bean properties, and more performant than a reflective approach if runtime class generation is used. Unlike standard java bean properties, a or path of properties can be specified to traverse a complex tree of objects.

As a minor benefit, there is no need for the accessor/mutator methods to be called getX() and setX() – although life is easier if they are!

It is simple to create applications portable between different security environments – using runtime generation where allowed, and falling back to reflection where security is tighter.

Dynamic Class Management. The properties package also provides classes to handle the loading (and unloading/reloading) of classes at runtime. It provides a framework useful for dynamically loading runtime-generated classes, for example.

While the property API can happily ignore the dynamic loading framework, it can also make use of it to enable runtime class generation.

Contents

Core Property API

Class Name Description
ClassProperty Conceptual variable related to a particular class. May be a static or member property.
SimpleClassProperty ClassProperty implemented by a pair of (accessor/mutator) methods.
NestedClassProperty ClassProperty implemented by a chain of SimpleClassPropertys.

Dynamic Class Management

Class Name Description
DynamicClassManager Manages the dynamic loading of classes.

Dynamic Property Generation

Class Name Description
CompiledProperty Superclass of all dynamically generated ClassPropertys. Provides some useful methods for the generated inner classes.
PropertyManager Manages ClassProperty instances. Can use DynamicClassManager, and generate ClassPropertys using cojen, or use reflection.
ClassPropertyKey (class,property,isDynamicNull) tuple used to identify properties. Used in PropertyManager, and used to index the cache in DynamicClassManager.

Properties

It is common for classes to have private variables accessable through accessor and mutator methods (usually called setXXX and getXXX), In general, these methods may not relate directly to any single member variable, and either method may be absent (in the case of read only, or write only properties). This concept is similar to the one modeled by Java Bean properties, but we wish to be a little more general and flexible. In particular, we do not restrict ourselves to a single property, but we specify a property of the returned object, and a property of that returned object, and so forth; in a chain of properties. We can also address static properties, not just instance properties.

Simple Properties

These represent a single conceptual variable of a class. SimpleClassProperty implements the required functionality. A single SimpleClassProperty object is instantiated is instantiated, with the required declaring class and property name. The declaring class is the class which “owns” the variable, and the property name is simply the XXX in setXXX/getXXX. A minor point here, is that unlike java bean properties, no automatic capitalisation is done. The java bean property convention of specifying the property name with initial-lowercase but capitalising the first letter of the property name where it appears in method names is unnecessarily retrictive. It is unlikely to cause a problem, but since it is no harder to specify “Property” in place of “property”, we avoid automatic renaming.

Nested Properties

A typical business data model is likely to consist largely of struct-like classes with property-style accessors and/or mutators. (This is after all the basis on which binding frameworks, and java beans are based). It is convenient to allow access to the properties of these objects via the conceptual “names” of such properties. Moreover, it is often desireable to be able to successively access properties of properties (of properties…and so forth) by “chaining” properties together.

The base case is handled by SimpleClassProperty which represents the first case described above; a single property relating to a single class. The second case is handled by NestedClassProperty.

Syntax

General format: P1:P2:...:Pn corresponds to target.getP1().getP2()...getPn()

Casting: (C1)P1:(C2)P2:...:Pn corresponds to ( (C2)( (C1)target.getP1() ).getP2() )...getPn()

Dynamic null handling

If the dynamic null handling option is used (by passing true to the varies property-creating functions), a property’s value is considered to be null if any of the objects in the chain are null – it is not required that all objects except the last are non-null.

When calling setValue() on a nested property, the default constructor is used to create any objects for null links in the chain.

Dynamic Class Loading

DynamicClassManager is designed to help applications access classes at runtime. While at first glance, Java appears to support this, it has a few serious limitations. Most obvious is that it does not support unloading a class definition directly. This means that once you have loaded a class, you are (usually) stuck with it. An attempt to re-load it will return the already loaded class. The only way to avoid this is to discard the classloader with which the class was loaded. Obviously discarding an application classloader isn’t feasable. We instead create a child classloader, specifically intended to load dynamic classes. We can then discard this classloader along with all the classes it loaded.

There are a number of points worth mentioning here.

First, if a non-dynamic class were to reference a dynamic one, this would cause havoc. The application classloader will load the “dynamic” class, which will then not be unloadable. For this reason, dynamic classes should be placed under a common path, outside of the application classpath so that the application classloader can’t find them. This prevents confusing behaviour.

Second, dynamic classes can reference application classes even if they haven’t previously been loaded. Since the Java classloader model delegates to the parent classloader first, the application classloader will be used to load these classes, and everything works as expected. As it happens, the dynamic classloader wouldn’t be able to find the application classes anyway.

Access to the dynamically loaded classes occurs through a well-defined interface. It only really makes sense to access objects of these dynamic classes via an application-side interface which they implement. The reason for this is simple – they are a collection of probably similar, generated (runtime-defined) classes. Access must occur through some object, and so a registerObject() method is defined in DynamicClassManager. Upon class loading (ie in a static initializer), a dynamically loaded class may call this method to register an object (often, but not necessarily a singleton instance of itself) with the manager. The application can then access the object via the appropriate key (which was presumably used to create the object in the first place. Here, an illustrative example is useful. Say we have an often-used search string we wish to “compile”. We first define a Search class/interface, with a doSearch(String searchMe) method. We then dynamically compile searchString into a class Search001 implementing/extending Search. This class should have a static initializer which instantiates a singleton and registers it by calling DynamicClassLoader.registerObject(searchString, this). The application can now call ((Search)DynamicClassLoader.getObject(searchString)).doSearch(searchMe); A somewhat contrived example perhaps, but it illustrates the point.

It may seem like added complexity to register an object for each class you load, but in practice, most applications suited to dynamic classes suit this singleton pattern. If in doubt, study the dynamic property implementation. This serves as a perfect example of (and hopefully justification for) the scheme.

Dynamic Property Generation

This package makes use of dynamic class loading, in combination with the cojen library, to allow implementations of ClassProperty to be generated at runtime, saved to disk, and dynamically loaded on the current and subsequent executions. This way, the reflective step is only taken once. The DynamicClassManager is used to load all generated property classes from a common path, and ClassPropertyKeys are used to index them.

The properties created are both subclasses and inner classes of CompiledProperty. The common functionality, along with a helper function to register the property if factored out into CompilerProperty. Each property contains its own key, and its toString() method prints this key. This helps debugging.

Releases