Generating Business objects with CGLIB
By: Assen Kolov, 17 June 2009Don’t code what you can generate
At my currrent project I ended up generating run-time proxies for my Business Objects with CGLIB. As this is somewhat unusual for a straightforward enterprise application, I still wonder about the pros and cons of this approach.
The main thing the application does is product configuration – given a definition of some tree of products, it lets the user create and edit product attributes, add, remove and edit subproducts according to that definition. It is a requirement that there is an external definition and it is expected to change regularly, so as much as possible of the product logic should be defined there.
A fragment of some imaginary domain objects definition to illustrate the case:
<productdefinition id="someProduct">
<date id="startingDate" nullable="false">
<enumeration id="area">
<enum default="true" value="World">
<enum value="Europe">
</enumeration>
<boolean id="allInclusive" defaultvalue="false">
<range id="limit" min="10" max="5000" step="10" defaultvalue="1250">
<subproduct type="someSubproduct">
<multiplicity mincount="0" maxcount="1" defaultcount="1"/>
</subproduct>
<productdefinition>
<productdefinition id="someSubproduct">
<text id="reason" pattern="\a{1,30}"/>
</productdefinition>
Whatever the Java implementation:
- When creating SomeProduct some values have to be set initially: setArea(‘World’), setLimit(1250) etc. A properly initialized instance of SomeOtherProduct should be added as a subproduct.
- someProduct.setStartDate(null) or someProduct.setArea(“Japan”), someProduct.setLimit(10000) should all cause an error with an error code because the provided value is not acceptable according to the definition.
- Rendering a GUI for editing a product attribute and processing the user input is uniform for all product attributes
- etc. etc.
An intuitive approach was to try something PropertyBean-like with named properties:
interface Product {
Object getPropertyValue( String propertyName);
void setPropertyValue( String propertyName, Object propertyValue);
AttributeDefinition getAttributeDefinition( String propertyName);
. . .
}
class ProductImpl implementing Product { . . . }
The implementation is straightforward and a lot of code fits this model perfectly. Still, there is code in the use case implementations which looks somewhat clumsy and prone to errors with this interface:
someProduct.setPropertyValue("someSubproduct[0].reason", "Lousy service");
An example without nested properties looks even more awkward, you can imagine. Forget code completion, type checking, tool refactoring facilities. The natural Java way of doing this is:
someOtherProduct.getSomeProduct().get(0).setArea("Europe");
And you need to invent some trics to write the equivalent of:
<form:input path=”someProduct.someSubproduct[0].reason”/>
The interface that I actually would like for the use case implementations is:
interface SomeProduct {
Date getStartingDate();
void setStartingDate( Date date) throws BadValueException; // Null not accepted
List getSomesubproduct();
SomeSubproduct addSubproduct();
void deleteSubproduct( Subproduct p);
. . .
}
interface SomeSubproduct() {
String getReason();
void setReason( String reason);
}
Given existing ProductImpl instances, coding the implementations of those interfaces would be a repetitive task, all getter/setters/addXXX/deleteXXX etc. will look alike. A code generation library could generate this trivial code for us. CGLib is a code generation library (a lot of tautology here) and I know it is good enough for Hibernate, so I thought it should do the job for me too. With a productImpl instance and a desired interface, the CGLib code needed to produce the proxy is surprisingly little:
class ProductProxyFactory {
public static Object createProxy(ProductImpl productImpl, Class clazz) {
Enhancer e = new Enhancer();
e.setSuperclass(clazz);
Callback callback = new ProductProxyCallback(clazz.getSimpleName(),
productImpl, productMapping);
e.setCallback(callback);
return e.create();
}
}
public class ProductProxyCallback implements MethodInterceptor {
private ProductImpl productImpl;
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable {
String methodName = method.getName();
if ( methodName.startsWith("get")) {
String propertyName = findMappedPropertyName(methodName); // find mapped property from method name
Object propertyValue = productImpl.getPropertyValue(propertyName );
Class subProductClass = findSubProductClass(propertyName ); // property value might be a product too
if (subProductClass != null) {
return createSubproductProxy(result, subProductClassName, mapped);
} else {
return result;
} else {
// Process setXXX, addXXX, deleteXXX methods and methods inherited from Object
. . .
Let’s see an example:
// There is some old code that still creates product using the clumsy Product interface
ProductImpl productImpl = new ProductImpl( someProductDefinition);
// and sets some data
productImpl.setProperty( "startDate", today);
// Let’s create a proxy implementing SomeProduct interface:
SomeProduct someProduct = (SomeProduct)createProxy( productImpl, SomeProduct.class);
Date date = someProduct.getStartingDate()
The code above is schematic, of course, in reality the application does not have to create any proxies explicitly. Not bad, the old classes have received a new face – another interface to the same data, and the only code that has to be written for each new interface is the interface definition itself.
Pros & Cons
- This approach adds complexity. This part of the application has confused more than one project members joining the team – ‘Hey, why can’t I find the implementation of SomeProduct?”. Is it worth maintaining this extra complixity for the needs af small audience – the developpers of one application?
- The code above is schematic, the real implementation has to take care of more details – external mapping file, caching proxy instances etc. The real code is already more complicated than the example and could potentially become too complicated, as business analysts add new requirements to the application.
- Object identity issues pop up if you use proxies and this case is not different – it is possible to create many proxies working on the same underlying productImpl.
- Some OOP has gone here. Try to override SomeProduct.getStartDate() or to set a breakpoint on it. I guess it happens when you mess up with AOP.
Let’s not forget the pros:
- No typing, retyping and copy-pasting of trivial code or introducing errors there. Changing a product is limited to editing tha Java interface and a mapping file.
- There is one well-defined place where all products are defined. There is one well-defined class where the behaviour of all products is defined, and no lazy or evil programmer can override it.
Is that DSL?
I wonder how this approach relates to the DSL concept of trading generality for expressiveness. The starting point is undoubtedly a domain-specific XML. While many DSL tools emphasize the code-generation phase, here objects are generated at run time. An obvious difference is that one can not inspect/change/override the generated code. While this is a DSL according to scholars (see http://ftp.cwi.nl/CWIreports/SEN/SEN-E0309.pdf), it is probably not the sort that is hot right now. Would it be more DSL if Java code was generated? I am curious on your thoughts on this.


17 June 2009 om 1:07 pm
Hmm, this leaves a bad taste in my mouth.
First things first, you are talking about problems with “someProduct.someSubproduct[0].reason”. Did you take a look at the standards regarding EL in Java?
For example OGNL or MVEL or JUEL?
I’m also missing relationship in your examples, in the XML nothing states that SomeSubProduct is actually related to SomeProduct. Also, in the interfaces you imagine it probably should have been:
interface SomeSubproduct extends SomeProduct { …
But IMHO, all this AOP and dynamic proxying will seriously mess up the debuggability (yes, thats a real word) and maintainability.
17 June 2009 om 1:13 pm
Ah, reading it again, ignore my comment on the SubProduct, its not really a SubProduct, rather a child-product.
17 June 2009 om 5:51 pm
@Roy, this a strong opening.
The first thing about this posting is the question how to get objects with both interfaces,
staying truthful to the DRY principle:
- property name based interface
- product specific iterface (SomeProduct etc.)
Once you have an instance implementing the second interface, no matter if dynamically generated proxy or precompiled, it can be passed to an EL engine. By chance, the app evaluates some expressions on those proxies with OGNL, JUEL’s way of handling null properties is a bit inconsistent. But that really is a different topic.
You remark abot the debuggability marks an issue, though I’d call it minor inconvinience and not a Serious Mess Up. The productImpl instance you might want to see in the debugger exactly 2 clicks away in the proxy. If you took the time to write a decent toString() you probably wouldn’t need those 2 clicks.
And the real question is, what is the alternative to this approach? Hand-coding 300 functions like:
public Date getStartDate() {
return (Date)productImpl.getPropertyValue( “startDate”);
}
Still curious about your thoughts.
17 June 2009 om 10:04 pm
You can still model these things without using dynamic proxies, just don’t think of them as object, but as entries.
For example, what would be the problem creating this structure:
class Definition {
String name;
List<Field> fields;
List<Definition> children; //For example if you have sub-entries
..etc..
}
class Field<T> {
private String name;
private List<Validator> validationsOnThisField; //For example, you can add validations
private T value;
T getValue() { return t; }
void setValue(T t) { this.t = t; } //Maybe execute validations here in the setter
..etc..
}
Then you could have:
Definition product = new Definition(“Product”);
product.addField(new Field<Date>(“startDate”));
product.addField(new Field<Date>(“endDate”));
product.addField(new Field<Long>(“price”));
This can be persisted various ways, and be accessed too, just do:
Date startDate = product.getField(“startDate”).getValue();
Yes, you might have to do some more programming, but its probably just as flexible, probably faster, and IMHO it feels a bit more correct. Can’t really support this feeling with facts though…
17 June 2009 om 10:16 pm
Of course my example above doesn’t have the ‘DRY’ you described, it doesn’t have an interface. But if they are undefined at first, are created at runtime, you can’t use that interface… so what is the big advantage of having them?
Interfaces are great to program against, but not if they are added when you are done programming…
The basic question I think we need to answer here is:
Are the business objects you describe Java objects, or is it just data that describes a product and can be put in another format?
18 June 2009 om 8:12 am
I haven’t been studying your solution in detail, but what’s the difference between this and an ordinary HashMap? You lose all type safety. Also, in some way, you’re not using the features Java has to offer.
I don’t get it.
19 June 2009 om 12:01 pm
The last part says it al:
Let’s not forget the pros
19 June 2009 om 9:15 pm
Well, to me this is a bad solution to the wrong problem. I think Jan-Kees summarizes it nicely: you could have easily used a Map.
First you introduce a so-called intuitive approach with a PropertyBean-like with named properties. Please explain to me why this is intuitive. I don’t see it.
What you’re actually doing is defining your own meta-model. Roy’s extension to your solution shows this even more: a class called ‘Definition’ that has a list of ‘Fields’ and ‘Definitions’. Replace the word ‘Definition’ with ‘Class’ and you’re looking at your own type language. Though extremely limited.
This meta-model approach has a number of problems:
- It’s not typesafe
- It’s unmaintainable
- It’s ugly
To overcome these problems, you then go ahead with manually writing interfaces that are typesafe, and dynamically generating the implementations for these interfaces with CGLIB. This at least makes the code that uses the product configurations – for example the user interface – maintainable. In turn it adds complexity. And it’s still ugly.
This is why I’m saying your solving the wrong problem.
Let’s look at the initial problem again. You have the following requirements (and some more – I’m simplifying):
- Product configurations are complex types. Here a complex type is a type that can have multiple fields. A field either has a simple type (integer, float, and so on) or again a complex type.
- A product configuration instance is an object tree. That is, there are no cycles in the object structure.
- Fields with simple types in a product configurations can have default values.
- Product configurations must have an external definition, for example in XML.
- Flexibility is required, but only at design-time. It’s okay to recompile the code when a product configuration changes or a new one is introduced. I deduce this from the fact that you’re hand-coding Java interfaces that represent actual product configurations.
Especially because of the last requirement I don’t understand why you would perform all kinds of tricks at run-time, while you only need it at compile-time. Makes no sense to me.
How about this: define an XSD for every product configuration you know and generate code for each XSD with JAXB.
That’s it.
The result: typesafe code with zero complexity, because it’s all simple POJO’s.
Note that I’m not suggesting to write a single XSD that defines the ‘ProductConfiguration’ type and generate code from that, because that would result in a nasty meta-model in Java again. Instead I’m proposing to write one XSD per product configuration.
Sprinkle each XSD with some JAXB 2.0 hints, and you can generate classes that look the same as the interfaces you’re now writing by hand.
If all types share some common interface (‘Product’ maybe?), that’s not a problem: just define that interface in code, put some more JAXB hints in the XSD’s, telling JAXB to let (some of) the generated classes implement this hand-written interface.
Now I can imagine that you’re not going to be happy if there are a lot of product configurations that all change a lot, because then you’ll be maintaining a lot of XSD’s. Additionally, it might very well be that an XSD isn’t able to fullfill all requirements.
This is a problem for sure, but it’s a completely different problem to solve than the one you’re addressing!
And on top of that, it’s not too hard either:
- Write a single XSD for defining product configuration types.
- Implement an XSLT that transforms documents conforming to this XSD into XSD’s you can feed into JAXB.
- Implement additional XSLT’s to generate other stuff (like Java code) to fulfill other requirements (like validators).
You can now define product configuration types in concise, human-readable XML documents. Each document is first transformed into an XSD and then into Java code.
Complex? Sure, it can be. But it’s all compile-time. None of the XML goes into the production system. Just the output, which is simple Java code. You can put all the code generation stuff into a single Maven module. The artifact generated by the module is a simple JAR. The complexity is confined. To source control.
Don’t complicate what can be simple.
19 June 2009 om 10:21 pm
Ah, that makes kind of sense too Vincent.
My initial thoughts where that it all needed to be dynamic at runtime, because of the proxies. In that case you’d need a structure to hold that information. Thats why I suggested the meta-language strucure approach. That would give it a little more type safety.
So the actual problem is a bit unclear, and tainted by the given solutions.
23 June 2009 om 6:07 pm
Hm, it looks like I didn’t make any friends with this story. Sure, it feels controversial, as stated somewhere close to the first sentence and that was my reason to share it. Rereading my schematic exposé I realize I should have explained better the context and the forces playing in time. Thanks to people who took the time to read it and analyse the problem.
The ‘intuitive’ property name-based interface is sort of native for the product domain. Products come in as an XML-serialized maps and are written back the same way. Business analysts see a product as a set of name-value pairs. A property value has originalValue and currentValue. The application really couldn’t do without the map view on a product (unless doing introspection. just kidding). Adhering to this model makes several crucial pieces of code a song to implement – (un)marshalling from an external XML format, initializing, accepting a visitor on all attributes. Yes, I just have a typeless map and it fits several essential application aspects nicely.
To overcome the problem of lack of type safety at a later stage, I am manually writing the interfaces and doing the proxy trick.
That’s right. Again, that is the topic of this posting.
Back in time I had the understanding of the application that it wouldn’t need the typed interfaces at all. Given the XML above, one would have a screen with a field to edit the value of the “startingDate” attribute, whatever type it happened to be, and another field for the “allInclusive”, not even known it is a boolean. From the attribute name, the GUI is rendered and user input processed. There was a demo doing exactly this, people were looking at the configuration XML and the product configuration screens and were feeling happy.
Then the reality started creeping in and the use cases were more an more talking about some specific attribute values – bummer! These attributes turned out to be more than name-value pairs.
And then I did the proxy thing, giving the existing maps a second face – typed. It took a day or two and it was there for everyone to use. With the time, it helped the team realize what the application needs.
@Vincent: an XSD per product type has been discussed as an option within the project, but XSD is just not expressive enough for that. The real alternative is the single XSD for product configuration type, with several XSLTs to end up in Java source. This is the compile time analogue to the proxies approach, if I get you correctly. But aren’t you then again maintaining your own meta-model, which is again limited etc.? Thanks for taking the time to analyse the case and come with a complete alternative, not mere criticism. I think your solution would definitely need the Java generating XSLTs, to provide for the Map-like functionality which the app needs.
Anyway, what you describe here is the only viable alternative mentioned in this thread that leads from an external definition to objects behaving according to that definition. It is hard not to agree that this is a promising direction. In reality, this direction wasn’t part of the real space of alternatives for this project. For the future – why not, it might be. And I have to confess, I struggle with the feeling that the code to generate Java at build-time should be of comparable complexity to the code to intercept a method call and do the same at run-time.
What I really hate is having to write the interfaces manually. This is *the* not-DRY failure in the approach.