Lessons from JBoss Envers

October 31, 2008

My good pal Daniel Mirilli just sent me a link to the JBoss Envers project. The idea behind it is that you can automatically track version changes of persistent fields using a new annotation called “@Versioned”. This makes something that is otherwise pretty complex – saving a whole history of changes for an object – as simple as declaring that you need it.

This is a great example of what makes for elegant design: you abstract a concrete business need into a reusable concept, then turn it into an “aspect” to get it out of the immediate code. You can now “take it for granted”. Before I got to know AOP I wrote a framework that did all this the hard way: I had parent classes for “entities”, inherited attributes like “ID” and template methods for things like type-specific attibutes: PKs, unique keys (more than one allowed), “natural” ordering, and so on. Three years ago, I would have done this differently: annotations, or AspectJ aspects, or something along those lines. Then, more recently, EJB 3.0 provided this out of the box with their @Entity annotations and so on.

The beauty behind AOP, no matter what the technology, is that it allows you to design elegantly through abstractions – and yet make them concrete.

Anyway, back to JBoss Envers. I haven’t looked at the details, but it seems like an elegant solution to a problem that I’ve seen more than once: “entities” that need to be “versioned”. That is, you want an audit trail for everything you do to them. And you can never delete them (logical delete, maybe). The product I currently work on has some pretty intense versioning requirements, which is why Mirilli sent me the link to this project (thanks, buddy!). But I know enough about the specific requirements of my product to suspect that Envers wouldn’t be able to do it for us.

In fact, my product’s business rules are convoluted enough that I actually spent about a week doing what the creators of Envers did: trying to wrap my head around it all by turning it into generalized abstractions. In the process, I actually created my own UML 2.0 extension language in order to diagram these versions and explain it all to myself. It’s one thing to keep an audit trail, but what if you can look up previous versions? And what if those previous versions relate to other versioned entities? I realized that in such cases there can be more than one type of “relate”: specific version relationships (Foo version 12 refers to Bar version 5), general version relationships (all versions of Foo refer to the latest version of Bar), and even rules-based general version relationships (all versions of Foo refer to the active or editable version of Bar, which may not be the latest)! Also, note I said “refer” here, implying a uni-directional reference. The relationship can be bi-directional, but what if the back reference follows different rules from the forward reference?

Sorry to muck around in the details like that. It’s not your problem (I hope). What I wanted to highlight here is that this process of abstraction and creating a new “language” around some tricky business rules can actually become something practical when whittled with the tools of an AOP framework, annotations, or what have you. And Envers, whether it works or not, has reminded me of that: when Foo refers to Bar, why not do something like this:

public class Foo {

@Versioned(type=VersionReferenceType.GENERAL, rule=VersionReferenceRule.LATEST)

private Bar bar;

}

Instead, I’m currently relying on inheritance from base classes and custom code in various places in the persistence and domain layers that are only loosely tied to the object references. But now Envers has gotten me thinking again…


What can quality attributes tell you about a system?

October 10, 2008

Are there quality attributes that can be attributed to specific patterns of system or software architecture? And can you learn about systems based on their attributes?

I’m standing at the cashier of a local furniture store, trying to pay for a new dresser with my credit card, and after swiping, the guy looks at the monitor and says, “your account must be blocked.” I’m thinking, “what? I use this card every day, and I pay it on time…”, so I tell him to try again. He swipes it 3 more times (each time having to go through an annoying loop of cancellations and submission screens), each time with the same result. The message is very clear on the screen, albeit without those helpful little details that we like: “Account invalid.”

So the cashier tells me to go over to the phone to call the credit card company and see if they can clear up the confusion. I follow his instructions because there’s not much else I can do about it, although in the back of my mind, I’m thinking “that message popped up WAY too quick to have come from my credit card provider.”

So, there you have it: I’ve made a judgement call on a problem based on a quality attribute. I happen to know that credit card authentication systems follow a basic architectural pattern of client-server, usually over the internet or some form of extranet. One thing we know about client-server architectures is that the performance characteristics of communication between the client and the server (especially when first establishing communication) are orders of magnitude higher than in-process or intra-machine exchanges.

Based on this, there’s that voice in the back of my head telling me, “get off the phone, you idiot! That was a client-side validation error!” That’s a pretty common design pattern for client-server architectures: validate as much of your data locally before you go and waste your time with a long-running remote request that is doomed to fail.

I can’t discern from this if they have a layered structure on the client which first validates data, then passes the information to some business delegate, if they are using some sort of strong domain object called “CreditCard” that does its own validation, or if they just slipped in a little “if” statement in some 1000-line monstrosity of a procedure that will do everything whether the developers intended it or not. Is there any way to tell this from the outside just by the system bahavior? Not directly (that I can think of at least), but what are the quality impacts these different approaches have? We know that the “monster procedure” approach is initially fast to develop, but increasingly hard to maintain: a higher rate of bugs and longer time-to-resolution. I suppose I could try to fish around for bugs, but not with my credit card! A form-based validation scheme tends to decentralize validation and error messages, so I could try different (invalid) credit cards from different points of entry to see if I get different responses.

But I don’t have time for any of this – remember, I’m still on the phone. Yes, although I’ve dialed in to talk to a person, they have (well, their voice recording has) asked me to first enter my credit card number, the 4-digit security code (the example they gave me was for the 4-digit expiration date – wtf?), and the year I became a “member”. Hmm… I guess this is another way of up-front validation. This is actually a business process, not a system process: they are trying to save operator time and reduce errors by performing the validation ahead of time only with the customer. 9 out of 10 times, by the time I get to an operator, they ask me for all the same details all over again. But not this time – the lady on the phone already knows who I am! What does this tell me about their enterprise architecture? I guess it means that at the very least, its a distributed environment with multiple systems interconnected. This could be a SOA-type web services architecture, some sort of peer-to-peer interaction, a shared data store (via mainframe?) or something else.

Later on in the (totally futile) conversation, the operator transfers me to someone else. Wouldn’t you know it, the other person automatically has all my information without having to ask for it yet again – WOW! Well, this probably isn’t peer-to-peer. Although it would work in this situation, there’s generally auditing and tracking of customer support cases, and that sort of supportability requirement requires a central server, or at least a shared data repository. It’s possible that a peer-to-peer broadcast request-response pattern was used for locating an available support person in the right sector and for handing off the case number, however. It’s less traceable, but more scalable when a lot of data needs to be exchanged and peers come and go with frequency. But if I had to make a guess, I’d say this type of system usually calls for more of a centralized hub (mediator? worflow queue?) in order to keep a tight lid on things.

I forgot to mention – after entering my credit card data for validation, there was a 5-second delay before there was any response on the line. This seems to indicate a synchronous validation process… again, client-server. Then they tossed me into the musical wonderland of the on-hold queue. That’s right, I said queue. They didn’t regail me with fascinating tales of how many people were ahead of me, or how long I should expect to wait, but they very well could have. Queues tend to follow a “competing consumer” pattern in which each “consumer” (customer support person) asks the central queue provider to give them the next item (message, job, impatient caller) in a FIFO manner as soon as they’re available. We know that this is a highly-scalable low-latency way to distribute work among a variable number of “fungible” worker resources. The process was so efficient in this case that it cut off my all-instrumental ecstasy prematurely, halfway through the crescendo in “Feelings”. Fortunately, they quickly confirmed for me that my account was fine and that I was wasting my and their time with this multi-system information exchange – it was a client-side error, after all.

Finally, back to the cashier. He’s trying to cancel my order because he’s given up on waiting for me, and people are getting upset. Apparently, maintaining multiple sessions in memory was not a usability concern, although there are patterns that would support it. I get back right about the time he hits “cancel”, so he has to start my process from the beginning. Having confirmed that the problem is client-side, he calls the manager, who applies a contingency mechanism known as “Scotch tape” to the back of my card, an violá! After a several-second-long client-server validation, my purchase is approved.

Really, without a comprehensive investigation, the quality behaviors can only give you an inkling of the architecture backing a black-box system that you are using. But rarely are we asked to do that. The point here is that the common architectural patterns that we use DO have an impact on those quality attributes. In fact, that’s the whole point: we use architectural patterns to get the job done, but we choose between different options based on which one will get it done with the right balance of trade-offs for our system.

That’s the basis of the Architecture Tradeoff Analysis Method (ATAM). Architectures can be analyzed before they are even built based on the architectural patterns they use, and the prioritized quality goals that are meant to be supported by the system. Given a known set of patterns and the impact they can have (both positive and negative) on these attributes, it is possible to know ahead of time if you have chosen a suitable architecture.

Practitioners of ATAM are supposed to maintain their own templates of architectural patterns and their trade-offs. There are also some public architecture pattern repositories, and IBM has their “IBM Patterns for e-Business” site (an IBM consultant once showed me a monstrous internal database that was much more complete than this). But none that I know of are very usable in the context of the ATAM, or just picking between trade-offs. Personal experience and intuition always end up being the guiding forces. Do you know of any good pattern catalogs that can tell you the ups and downs of using a pipe-and-filter approach? Of avoiding downtime via a Watchdog pattern, or how to choose between a relational and a hierarchical database? If so, I’d like to know!


Legaciness revealed!

October 1, 2008

It looks like my neverending quest for a Legaciness metric has already come to a satisfyingly swift end! I was just browsing some blogs on agile techniques when I came across a post about fixing bad developer habits on a blog called Agile Tips. Waaaaay down in the footnotes is a link to a handy little tool in the Google Code site called the Testability Explorer.

I couldn’t believe my eyes when I saw it. I felt like Francisco Orellana stumbling across El Dorado. Could this really be the fabled Legaciness metric after all? I have to investigate more, but what I’ve read so far seems to be exactly what I’ve been searching for: a tool designed to calculate the difficulty of testing Java code based on its static dependencies! And from what I’ve read so far, it looks like I was on the right track with the algorithm:

  1. Each class attribute has to be considered by how “injectable” it is (their term, and I like it)
  2. “Global” variables are considered bad if they’re accessible, but not injectable
  3. There’s a transient penalty for methods which call methods which are not injectable (the “inheritance” penalty that I talked about)

They don’t directly address a number of concerns I have, such as:

  1. System calls that make testing difficult (e.g. I/O calls)
  2. Calls to “new” within a method
  3. The ability to “inject” something using the Legacy Code Patterns like “Extract and Override Factory Method” (if the class itself is considered injectable in a method, at least that method won’t get a penalty)

I’m guessing that some of these concerns, like the calls to “new”, would be covered by the transitivity of the complexity scores. I also like the touch that they’ve included cyclomatic complexity into the mix, although I don’t have a feel yet for if that would outweigh the injectability problems – my guess is no. I also don’t know if they take into account 3rd-party libraries. All in all, it looks like their metric is pretty straightforward, easy to understand and useful. I like the simplicity of their approach.

All that’s left now is to try it out! I’ll give it a shot tomorrow and see what it thinks about a certain 550-line file-reading Excel-interpreting monster of a method that’s been keeping me up at night…