|There has been an ongoing conversation on cgn-talk about the problem of engineers adoption of code generation tools and techniques. To aid in popularizing the practice of code generation we have started branching to get case studies of successful code generation. This is where Peter White and his story about using code generation in his Java projects fits in. He gives his experience with code generation products, and finally his own work on a custom generator, in detail. He also has some pointed suggestions for code generation developers. So listen up! Our customers are talking!
CGN: Alright, let's talk about your code generation success story. First off, what was the project. Give us a little history?
Peter: The project was a financial services application prototype, to be used as a sales tool, which I developed for a client of mine. The application was developed over the course of three separate consulting engagements, each one having a tight schedule and a live customer demo at the end of the engagement so schedule and stability were the primary project constraints. The product specifications were defined during each project phase so it was impossible to plan for future requirements since they didn't exist. As a result the tools and technologies used evolved in each phase as I learned more about the product direction and external tools that were well suited to our technical requirements.
Phase 1 (5 weeks) - I had considered using Struts on this project (planning for the future) but decided against it because I didn't have a whole lot of experience with Struts and was doubtful that I could complete the project on time with a Struts-based solution.
Given the time constraint, I developed the core product from scratch using a minimal homegrown framework with Servlet controllers, JSP views, data access objects wrapping raw SQL in JDBC calls as the persistence layer, and ValueObjects to pass the data between the three tiers. Code generation was not used during this project phase.
Phase 2 (4 weeks) - This was an add-on module that required dynamically generated HTML forms containing user-selectable UI components. Each of these UI components needed to be validated at submit time and then the entire resulting object graph needed to be persisted. At display time, the entire object graph needed to be retrieved and a display only view of the previously generated form was generated on the fly.
Fortunately, this add-on module required minimal integration with the core product so I decided to leave the existing core product intact and implement the new functionality using Hibernate to simplify the new persistence requirements and the WebWork2 application framework with the model-driven and validation interceptors to simplify the dynamic page generation and validation requirements.
Phase 3 (3 weeks) - This phase was mostly an extension of the new module added in phase 2 with tighter integration to the core product developed in phase 1. At this point, using two different frameworks and persistence mechanisms was starting to get painful so I decided to bite the bullet and convert the core product to WebWork2 and Hibernate - this is when I started to seriously look into code generation.
CGN: So what made you decide to try a code generation solution?
Peter: The core product's UI was more sophisticated than the add-on module's so the first week of the third project phase was spent identifying how every feature implemented in my framework would map to WebWork2. The second week of this phase was spent converting the old controller/action code to WebWork2 actions and converting the JSPs to use WebWork's tag library so validation would work correctly.
At this point, I still needed to create a Hibernate mapping file, JavaBean (my framework used a Map-based ValueObject to represent an HttpRequest instead of JavaBeans), and an updated data access object for every object that was previously persisted by the core application.
I only had a week to do this and have it "demo-ready" for an external customer. This was all boilerplate code so code generation appeared to be the best bet. I figured this would increase stability, in addition to saving development time, because bug fixes could be applied to the appropriate code templates and all affected files could be regenerated as opposed to identifying and resolving bugs one persisted class at a time.
CGN: What did you try first and how did that work out for you?
Peter: In phase 2, I used XDoclet's @hibernate tags in all of the Java classes for the new add-on module with great success until I needed to implement polymorphic persistence for a collection of objects. I figured out how to implement this in Hibernate using joined-subclasses but XDoclet didn't appear to have any support for joined-subclasses.
Due to time constraints, I decided to ditch XDoclet and hand-code everything rather than have XDoclet generate the Hibernate mapping files for only 80% of my persisted objects (I wanted my solution to be consistent so my client could easily maintain the application in my absence).
It wasn't until phase 3 that I found a code generation solution that satisfied my particular needs. By this time, I was quite comfortable working directly with Hibernate's mapping files and decided to give hbm2java a try...
CGN: Ok, so tell us a little more about what the inputs were, what the outputs were and how the whole thing tied together.
Peter: Hbm2java is a code generator which takes Hibernate mapping files (Classname.hbm.xml) as input and generates POJOs, data access objects (finder methods only) and stateless session beans depending on which "renderers" you configure. I found the FinderRenderer a bit limited for my needs so I wrote my own DaoRenderer and used that along with the BasicRenderer to generate my POJOs. I triggered the code generation from an ant task as an optional step in my build process.
My ant task used to trigger the code generation process looked like this:
<target name="hbm2java" description="Generate .java from .hbm files." depends="prepare">
My hbm2java configuration file, hbm2java.xml, looked like this:
<?xml version="1.0" encoding="UTF-8"?>
<generate package="com.myclient.productname.dao" suffix="DAO"
CGN: What was the reaction from the other members of the team?
Peter: My client added one of their own developers to the project for the second and third project phases. We were working on separate features and he was on vacation the last few days of the project so he didn't see the results until he returned from vacation. However, I explained how I generated all of the POJOs and DAOs when he got back in the office and he was quite excited about it.
CGN: Did you use it yourself alone, or did everyone use it?
Peter: According to your book, the DaoRenderer was developed using the "my own tool method". This might get promoted to a "Skunkworks" tool, but I'm currently working on what I believe is a better solution (described later in the interview). I've also considered contributing my DaoRenderer back to the Hibernate community if I have enough time to make it compatible with all applicable Hibernate settings, as opposed to working with just my client's use of Hibernate.
CGN: Did you check the code into the source code control system or was it part of the build process?
Peter: As described above, the hbm2java task was an optional task in the build process and the code was generated outside of the main source directory. This was done to shorten the build time and to make sure that manual changes to the generated files wouldn't get clobbered if they hadn't been checked into the source code control system yet.
Whenever new persisted classes where created, or the DaoRenderer was modified to generate different code, we'd run "ant hbm2java" and then copy or merge the newly generated files to the main source directory and then check them into the source code control system.
CGN: Were there any other alternatives you looked at for code generation tool at the time?
Peter: I didn't want to stop at generating my persistence layer so I took a brief look at some of the code generators in the code generation database on the Code Generation Network. In particular, I really liked the idea behind AndroMDA but it doesn't have a "cartridge" for WebWork2 yet and I didn't have the time to write one or get up to speed on using Poseidon before I reached my deadline. AndroMDA uses the Velocity Template Language for its code generation templates and it already has a Struts cartridge so it shouldn't be too difficult to create a WebWork2 cartridge using the source code and templates for the Struts cartridge as a point of reference. I definitely might take a stab at this myself in the future.
CGN: Have there been any code generation tools that you have looked at since then?
Peter: Yes. After completing the third project phase I had a little downtime so I finally got around to reading Code Generation in Action. By the time I had finished reading the fourth chapter; I couldn't wait any longer to write my own code generator so I put the book down and got started.
My goal was to write a code generator that would take a Hibernate mapping file and generate my POJOs, DAOs, JSPs and WebWork2 related files (actions, validation files & webwork configuration file). I've been looking for an excuse to learn Python for quite some time so I did a bit of web surfing to see if something like Ruby's ERb existed for Python. I ran across Cheetah, "a Python-powered template engine and code generator", and haven't looked back since.
I had been using Velocity's VTL in my last project and found Cheetah to be very similar, syntactically, so it was a breeze to crank out several templates in a short period of time. In only a few hours a night, over three nights, I was able to learn enough about Python and Cheetah to write a code generator that generates my POJOs, DAOs, and WebWork2 action classes. I've only written a WebWork2 template for displaying a list of the persisted Hibernate entities (as well as a JSP to display the list) but I will create templates for adding, modifying and deleting entities as soon as I finish my JSP templates for those actions. Given my recent experience with Python and Cheetah, I can't imagine an easier way to generate code!
CGN: Can you give some recommendations to the other CGN readers about what to do and what not to do when it comes to CG from your experience so far?
Peter: I know this is a cliche but make sure you use the right tool for the job. You should determine what needs to be generated and what doesn't, in addition to what your inputs and outputs will look like before you attempt to qualify whether or not a particular tool will suit your needs. You should also take into consideration how flexible the tool is and how much effort it will take to have it generate output that looks different than the default output in case the output needs to match your coding standards or your needs change down the road.
In some cases, it might be easier to write your own code generator from scratch rather than trying to bend another code generator to your will or having to use several different code generators to satisfy all of your code generation needs. I looked at several existing code generation tools before I decided to write my own. I initially thought that using an existing tool would save me a lot of time but have come to the personal conclusion that I'd be better off becoming proficient in Python and Cheetah so I can modify my code generator and templates to suit my future needs rather than having to look for a new code generation tool if I run into a project where my standard tool is not well suited.
CGN: So all in all a positive experience that you would repeat?
Peter: Absolutely! As a matter of fact, I'm currently creating new Cheetah templates for my Python-based code generator to generate WebWork2's configuration file and validation files. I'm also writing templates to generate JSPs for my add/modify/delete pages. Code generation has changed the way I look at problem solving and I will continue to look for opportunities to use code generation to solve problems on all my future projects.
CGN: So what advice would you have for code generator vendors to help them keep people like you interested in sticking with a canned generator?
Peter: I haven't taken a serious look at commercial code generators so I'm hoping what I'm looking for isn't just wishful thinking. However, for me to seriously consider investing my time and money in a canned generator, it would minimally provide the following functionality:
CGN: Thank you for your time. It's been a pleasure.
- Multiple input choices and well-documented extension points so I can provide my own parser if I choose to represent my model in a format other than what's provided by the tool out of the box. For example, give me the choice to use XDoclet style tags in my Java source files or export my model from a UML modeling tool. If I preferred using another format such as Hibernate mapping files for my input source it would be nice to be able to write a plugin that would parse the mapping file and then make the results available to my code generation templates. Speaking of extension points, I should also be able to define my own metadata and be able to access it in my code generation templates.
- A widely accepted and visually clean template language syntax like Velocity to simplify development and debugging of code generation templates rather than, or in addition to, having to customize my code generation routines in a lower level language such as Java or C++/C#.
- Make code generation as simple as selecting your input source and mapping it to one or more output sources. It would be nice if the option for conditional code generation/merging were provided on top of the simple input -> output(s) mapping but I wouldn't want it at the expense of losing the basic simplicity.
- Provide an ant task for your tool so I can effortlessly integrate it as part of my build processes in addition to generating code directly from your tool (if it's a standalone tool).
Mon 01 of Mar, 2004 (23072 reads)