A personal report, and a cry for help…
julho 31st, 2006 | by Aldrin Leal |(Nota: Este post está em Inglês pelo seu conteúdo excessivamente técnico e pela minha própria necessidade de obter-se um feedback. Você pode ignorá-lo. Fui criado pra ser nerd, lembra?)
Lately, I´ve refactoring a very specific section on my job’s internal information system: Reporting. But first, a small background:
I parachute-landed at my current employer’s project, as a refactoring/rescue measure. As a solution, I chose key issues to deal with, mostly related to SCM and System Management (e.g., Administrivia). In geek-ese, this means subversion, trac, log4j, maven, more great stuff from Apache Jakarta, and some other great ones from the haus, and sourceforge, objectweb, and IBM Eclipse Foundation. Before, there were only, Hibernate, jUnit and Struts.
Apart from the framework and build environment department, my key goals was to agressively refactor key parts of system. Testing stuff, Hibernate code, SQL handling, and the usual code sniffing cycle (find some random snippet. Smells bad? Think, find similar ocurrences, plan, concept-test, document and deploy a solution. Rinse and repeat). And yes, it works well.
On the improvement room, I also manage some of the operational aspects. We have some schweet tools for improving our ability to prevent some and react to issues as they come, and we’re still planning for more.
Now that you got a glimpse of my project setup, perhaps you might think: Damnit, looks a nightmare to manage. Of course, sometimes the integration breaks. I keep a policy of, before discarding something, grok at the sources, trying to solve’em myself and documented when needed. But overrall, it hasn’t been very hard. Except for the usual reluctancy of adopting a new framework (the infamous NIH symdrome, more on this soon), and my usual answer: to learn to write good code, is to know how to read good code.
BTW, time for a quick Maven-and-Eclipse Tip: Do you need to read your project dependencies source code? Say, for a given artifact in ~/.maven/repository/groupId/jars/artifactId-version.jar (M1, of course), their source code location always gets overriden whenever you do a eclipse:generate-classpath goal, right? Yes, unless you place the zip source code as ~/.maven/repository/groupId/src/artifactId-version.zip. The eclipse plugin expects this form in order to hook’em up correctly. Never mind about setting it manually again!
Back to the NIH stuff. As an afterthought, now I am clearly convinced that, unless that’s core stuff, you can safely borrow from the OSS community.
One of the most underlooked components is our document engine. As a government institution, we have to issue documents of all sorts and purposes. In a structural side, they’re simply servlets which extend from a abstract and utility class which implements all the features they need to handle.
Internal users need to change some documents before to sign and ship to customers, so we resorted to the RTF file format. In practise, it’s easier for them for manually set page-breaks and add minor corrections. We ask them to turn on change tracking and send us a copy, just to correct minor issues and to inspect the need to alter records on our database on a manual basis.
Lately, I’ve had been assigned the task to generate some reports. Remember when I said underlooked? Well, welcome to my current karma payables assignment: The reporting system uses iText for the bulk of its document-generation needs. In a nutshell, iText generates html, rtf, pdf, and a internal Xml format, suitable regeneration with some command-line tools. On an use-case basis, creating a Document with iText looks a lot like dealing with Swing/AWT stuff: You create small instances, plug’em together. On an internal basis, iText *DOES NOT* stores the Document data internally like a DOM Tree. Instead, it relies on their Listeners which, say, map a given Paragraph to a RTF internal structure which gets dumped when the document is set as finished.
If you didn’t use/remember Swing back in the 90′s, pretend you wrote swing applications without a GUI. Forget Matisse, Netbeans, and VE. Such a bad thought, eh? But, apart from Form initialization stuff, and perhaps some Event Handling, it was nice. But you always end up thinking about some way to delegate it to something a bit easier.
Lets make it worse: Pretend you need to maintain a enterprise-wide application with lots of different swing forms. Do yes scare you? God yes. Lots of small, tiny objects tied together waiting for you to break upon a minimal change? You bet.
It became personal, then I decided to do a little research, trying to find suitable ways to work around the need to maintain a java codebase for the sole purpose of document generation. Say, looks easier to manage a velocity template file, or manipulate as a tree.
On this process, I ended up looking a lot at iText code. From a developers’ viewpoint, it lacks enough docs (apart from the tutorial and javadocs), completely lacks tests, and its source is a complete mess (multiple constructors point to a single one, which in turns calls its super constructor. Such a hard to decide which one is better to call). To make it worse, accessors didn’t even follow the standard naming conventions. They’re really not your standard POJO.
Resorting to B plan: I decided to completely ignore their XML interface, and stuck at asm for implementing instrumentation was a basis for tracing their calls. asm is awesome, but not for beginners who needs its real soon now. However, it was a nice experience to deal with: Their event-based mechanism really lets you think how, sometimes, small and minimalistic is beautiful.
B Plan, reloaded: Obcessed with the instrumentation idea, I resorted to the man’s best know log generator: AspectJ. And it (kinda worked). I mean, now I have an small aspect which yields java code suitable for inserting into a java file which, in turn, yields the very same document which was advised by the aspect.
(Mental Note: The idea of capturing events on a class basis isn’t very new but, surprisingly, it looks there’s no real thing to play with. There’s a really bright and colorful rainbow out there to the ones who follow it and came up with an Abstract Aspect to let you track how a given codebase invokes/uses another)
I consider it a hack, but I’ll let you reproduce my weekend lab experiment (under the assumption you’re a Eclipse 3.2 user):
1) First, download iText’s source code, and install AJDT. Also, download the binaries jarfiles for log4j and commons-lang and commons-logging.
2) Create a project, import/unpack the source code and enable AspectJ support. Link the dependencies together with the project.
3) Create com.lowagie.text.Trace.aj aspect [link] and com.lowagie.text.TraceDecorator.java [link]
4) In the root directory, create a minimal log4j.xml like this one [link]
5) Create another project, linking to iText’s one outlined in step 2. Create a small test harness, like the ones from the iText Tutorial. But be careful, as some might lead you into problems…
6) Fire up your harness. And check trace.log (you used my log4j.xml from step 4, right? :])
7) Paste up the contents from trace.log into a new classfile. However, insert the following snippet just after your Document() constructor:
FileOutputStream os = new FileOutputStream(“rtfDoc.rtf”, false);
com.lowagie.text.rtf.RtfWriter2.getInstance(obj24561483, os);
Replace obj24561483 with your own object instance symbol. On the end, call close():
java.io.FileOutputStream os = new java.io.FileOutputStream(“rtfDoc.rtf”, false);
There should be some minor issues. Remember, this is not a production-ready build, but a quick proof-of-concept.
As you might conclude, there’s a small room for improvement, like:
- Aspect Reusability: Why not generalize and build a framework for black box reverse engineering?
- TraceDecorator refactoring: This is my current goal. To refactor TraceDecorator into a Listener/Adapter paradigm, thus generating more than just java code (hint: A “Spring-i-ficator” emitting a beans.xml-like output for Spring Users)
- Better handling: Currently, there are only basic functionality covered. Although it (nicely) suits my needs.
Ideas are strongly welcome. That’s why I’m still awake doing this post. To help me leverage key issues and future directions. Please, be my guest :)
And now I’m (kinda) done, albeit tired. Remember, I not only created, but bred a monster out of my own ideas. :)
You must be logged in to post a comment.