Wednesday, January 7, 2009

Introducing Pojomatic

New year, new project
I'd like to start off the new year by introducing a project I've been working on for a while now along with Ian Robertson, a colleague of mine at Overstock. The project itself is actually not new at all, considering we began working on it around April of last year and it's based on something similar we've been using internally at Overstock since the early days of Java 1.5. The project is called Pojomatic, and what's new is that it's open source (Apache 2.0 license) and there's a release candidate available (1.0-RC1) on Sourceforge or the Maven central repository.

What does it do?
Pojomatic is a Java library which provides automatic and configurable implementations of the hashCode() equals(Object) and toString() methods inherited from java.lang.Object using annotations. POJOs (Plain Old Java Objects) + automatic implementations of common methods = Pojomatic.

This is a useful because it is generally a good idea to override hashCode() equals(Object) and sometimes toString(). One could manually implement these methods, but that is time-consuming and prone to error (e.g. forgetting to check for null everywhere). Instead, one could have an IDE generate implementations of these methods for you. As with a lot of generated code, this can be like slapping your code with an ugly stick. Besides, I'd often add fields and/or methods to the class later and forget to re-generate new implementations, which may have no effect or may lead to very subtle, hard to detect bugs (e.g. two objects are equal when they shouldn't be => two different people mistaken for the same person => money is deposited to the wrong account => one person is happy, while you are not because you have to work late to track down and fix the bug).

How do I use it?
The easiest way to use Pojomatic is to put one annotation (@AutoProperty) on your class and delegate the desired method(s) to the corresponding static methods in Pojomatic. For example:
@AutoProperty
public class Person {
private final String firstName;
private final String lastName;
private final int age;

public Person(String firstName, String lastName, int age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

public String getFirstName() {
return this.firstName;
}

public String getLastName() {
return this.lastName;
}

public int getAge() {
return this.age;
}

@Override public int hashCode() {
return Pojomatic.hashCode(this);
}

@Override public String toString() {
return Pojomatic.toString(this);
}

@Override public boolean equals(Object o) {
return Pojomatic.equals(this, o);
}
}

By default, @AutoProperty tells Pojomatic to automatically detect all of your fields and use them in all of the Pojomatic.* methods. We'll see the different options later, but in the above example, all of the fields will be used for hashCode() equals(Object) and toString() as shown here:
public static void main(String[] args) {
Person johnDoe = new Person("John", "Doe", 32);
System.out.println(johnDoe.hashCode());
System.out.println(johnDoe.equals(new Person("John", "Doe", 32)));
System.out.println(johnDoe.toString());
}

Outputs:
-2068529904
Person{firstName: {John}, lastName: {Doe}, age: {32}}
true

Using all fields for each of hashCode() equals(Object) and toString() is usually not advised, however, so @AutoProperty can be configured to include automatically detected properties in any (valid) combination of these methods (including something in hashCode() without including it in equals(Object) violates the contract of hashCode(), so this is not allowed). Additionally, properties can be configured individually via the @Property annotation. When both annotations are present, @Property is used since it applies only to the one property. Using @Property gives you complete control and makes @AutoProperty optional. A common practice is to have all properties included in equals(Object) and toString(), while only one or two key properties are included in hashCode() like so:
@AutoProperty(policy=DefaultPojomaticPolicy.EQUALS_TO_STRING)
public class Book {

@Property(policy=PojomaticPolicy.ALL)
private final String isbn;

private final String title;

public Book(String isbn, String title) {
super();
this.isbn = isbn;
this.title = title;
}

public String getIsbn() {
return isbn;
}

public String getTitle() {
return title;
}

@Override public int hashCode() {
return Pojomatic.hashCode(this);
}

@Override public String toString() {
return Pojomatic.toString(this);
}

@Override public boolean equals(Object o) {
return Pojomatic.equals(this, o);
}
}
Both @AutoProperty and @Property can use accessor methods (getters) instead of fields in situations where using accessors is more desirable or when a SecurityManager prevents Pojomatic from accessing private fields through reflection.

Briefly, there is also a feature which will let you customize the String representation of each property. For example, this would be useful if one of your properties contains sensitive data such as an account number, credit card number or social security number (see AccountNumberFormatter). Pojomatic provides the ability for you to define your own formatter implementations as well.

Feedback
Pojomatic has been a lot of fun to work on, and I hope you will find it useful. I'm confident that after trying it out, you will not want to go back to handcrafting equals methods or using ugly IDE-generated code instead. Any feedback is appreciated, as well as feature requests, so what do you think?

Monday, October 27, 2008

Learning Wicket

I'm always on the lookout for different ways of doing the things I'm familiar with. Once in a while, I learn a better way of doing things. Even if I don't, I find that it helps me realize what I like and dislike about the techniques that I'm used to.

Take creating web applications for example. I'm familiar with JSF, both with and without Seam. Overall, I would say that I agree with the ideas behind JSF, but in practice it is not easy to be productive in developing a medium to large sized web app. It seems like some things that should be very easy to do are difficult, but maybe that's just me. Seam adds a bit more complexity, but helps overall. To the JSF world of managed beans, Seam adds ways to manage the rest of your objects called scopes. This is a good idea on paper, but feels a bit like using global variables (albeit with managed lifecycles).

So I decided to give Wicket a try. What stood out to me right away is the simplicity of it. There's very little configuration, and no XML other than web.xml (which makes me realize how much I dislike having stuff like page navigation in an XML file). Each page template corresponds to one Java class. Also, there is no EL or OGNL so your markup is sure to have no logic whatsoever, which is great for those of us who prefer to code in Java. That's the bottom-line benefit that I see with Wicket: everything happens in Java code. Which means you get all of the benefits of writing Java code in an IDE from debugging to refactoring, etc. I haven't tried it, but I've read that it's easy to use alternative JVM languages such as Scala and Groovy instead, if you are so inclined.

Wicket is very friendly to the web designer because it does not invade the markup, so the designer can see what they are getting without firing up a web container. For now, I haven't seen any Wicket components which look good "out of the box" (translation: you probably need a web designer). However, there is no reason why there couldn't be such component libraries for Wicket. That will come with increased adoption, if it happens. After all, creating a component library in Wicket means putting your classes and markup in a JAR file, so it couldn't be easier.

Some minor gripes I have with Wicket are that the markup has to stay in sync with your Java class (but all web frameworks I have used also have this problem), it can sometimes add junk to your URLs after a form submit (maybe there's a way around that). Also, there's quite a bit of casting when using version 1.3, but they are adding generics to version 1.4 which should minimize that. Overall, I'm very impressed.

Resources:

Wednesday, March 12, 2008

Ideal command prompt


In setting up my new laptop, I've been experimenting with different command prompt settings in bash. Here is what I've settled on for now (click for a more clear image). It's simple, yet gives me all of the information I need in a format that is easy for the eye to parse. That's because the text color of the full path is inverted from your other colors.

If you are interested in using/tweaking it:
export PS1="\u:\[\e[0;07m\w\e[m\]> "

What's your ideal PS1? Here's a how-to article. It's written with Linux in mind, but the information applies to pretty much any environment where bash is present (OS X, Unix, Cygwin, etc.).

EDIT: My original post did not wrap the color code in \[ and \] which caused the first line wrap to overwrite the current line. Fixed!

Tuesday, March 11, 2008

Rantings of a recent Mac convert

I ordered a new MacBook Pro (Penryn w/ multi-touch) which arrived last Monday (which probably explains my lack of posting since then). Although I do have a bit of experience with OS X and actually do my programming at work on a Mac mini (I'm in the minority here), this is my first purchase of any Apple product (which means I don't own an iPod - *gasp*).

The Desktop OS Wars


As far as OSes are concerned, I don't mind Windows, but I find that it gets in my way sometimes. That is fine for most people because they know the workarounds (e.g. reboot, re-install every 6 months...). To me, this is a waste of time since other OSes don't have this problem. This observation is mostly from XP and previous versions, but based upon what I've heard about Vista, I don't expected much better. Also, the lack of a robust terminal/shell (with things like adequate scripting) is completely inexcusable when the competitors run bash and the like.

Linux is a rock-solid OS. It dominates the server environments, and rightly so. The problem is the desktop user experience. The average computer user cannot be expected to survive here, and even the most technically savvy users often find themselves searching through forum posts for hours on how to overcome some hurdle or another. Driver support is non-existent for the most part. The installation process is flawed for the vast majority of distros (one exception is Ubuntu and the like). Also, while package management is great, there are often packages that are missing and/or outdated, so it loses much of its utility. Granted, Linux in general (and Ubuntu in particular) are making great strides in this area, but the progress is too little, too late.

That brings me to OS X. I think Apple's really gotten it right. Take something solid (BSD - though modified with a very different kernel) and put a nice UI on top of it, and that's exactly what they've done. I have a terminal session (bash) open 90% of the time when I'm programming, which is indispensable. I never payed much attention to aliasing or font rendering before, but I can't help but notice how much better everything looks. Some of that has to do with the top-notch (read: expensive) displays that Apple uses, but at work I've used the same (non-Apple) display on Windows, Linux and OS X and there is a clear difference.

Final Thoughts


I have to say, I am very impressed with my MBP so far. It does everything I want it to, without any of the quirks that are present in other hardware/OS configurations. I think this is due to the relatively small number of available hardware configurations for Macs. This puts Apple in the unique position of being able to test every possible hardware configuration - they know it works. I've heard from other Mac users that "everything works", but I don't think I really believed them or understood the implications of that statement. Now I get to experience it firsthand, and it's refreshing.

Anyhow, I hope to get back to posting more Scala stuff soon, and maybe something about Java for a change.

Saturday, February 23, 2008

ARM Blocks in Scala, Part 3: The Concession

Update: Here's a better approach to Automatic Resource Management in Scala.

After a couple of attempts (part 1, part 2) at implementing Automatic Resource Management in Scala, I've decided not to "reinvent the wheel" here. I will defer instead to the implementation found in Scalax. I did not know about their ManagedResource class before my first post (many thanks to the commenters who pointed me there), and if I had known about it I may not have made the attempt. I'm glad I did, however, because it gave me a chance to improve my Scala skills. I'm not "there" yet, though, as I still find myself writing Java-like code in Scala. When I catch myself doing so, I am usually able to refactor it into the Scala style, which ends up being more compact and elegant. Come to think of it, that is why I recommend ManagedResource over my approach. I would say mine is the Java-like approach, while ManagedResource is more elegant and more consistent with Scala style.

Let's look at the same example from my first two posts using ManagedResource:


def createReader = ManagedResource(new BufferedReader(new FileReader("test.txt")))
def createWriter = ManagedResource(new BufferedWriter(new FileWriter("test_copy.txt")))

//copy a file, line by line
for(reader <- createReader; writer <- createWriter) {
var line = reader.readLine
while (line != null) {
writer.write(line)
writer.newLine
line = reader.readLine
}
}



Why is ManagedResource better?
It may be clear from the example, but let's discuss what it is that makes the usage of ManagedResource cleaner than the previous approach. ManagedResource uses for-comprehensions, and that alone solves many of the problems I encountered. For example, I had the problem of being able to define and initialize a resource and still being able to reference it inside of the block of code. For that reason, I had to define an initialization function (in part 2), but "for" takes care of this nicely: for(a <- ManagedResource(new SomeResource())) .... It also takes care of the many-resources problem elegantly, without using varargs: for(a <- createA; b <- createB; ...). In short, "for" seems like the right tool for the job.

That being said, I think ManagedResource does have some room for improvement. For example, consider the following code segment:


def createReader = ManagedResource(new BufferedReader(new FileReader("test.txt")))
def createWriter = ManagedResource(new BufferedWriter(new FileWriter("test_copy.txt")))

try {
//copy a file, line by line
for(reader <- createReader; writer <- createWriter) {
try {
var line = reader.readLine
while (line != null) {
writer.write(line)
writer.newLine
line = reader.readLine
}
} catch {
case e: IOException => println("Exception thrown while copying: " + e.getMessage)
}
}
} catch {
case e: IOException => println("Exception thrown upon open or close: " + e.getMessage)
}


As you can see, we have total control inside of the block of code, but if an exception occurs while initializing or disposing we have no way of knowing which of the two steps was the culprit. This is probably not an issue most of the time, but I could see a possible need for handling exceptions in initialization differently from exceptions in disposal. Maybe this can be improved (it would have to be non-intrusive for the more common, general case), or maybe some level of control must be sacrificed.

Also, at the time of writing, ManagedResource exposes methods for opening (initializing) and closing (disposing) a resource: "unsafeOpen" and "unsafeClose". These cannot be called from within the block of a for-comprehension, but I see no need to make them public, "protected[control]" should be the maximum visibility - if that. Making them public is a mistake because it allows for the same type of resource leaks we set out to quash. In fact, anyone who is calling these methods externally requires more control over where and when resources are initialized and disposed, and should not be using ManagedResource to begin with. If there is a legitimate reason for exposing these methods, I would like to see it.

Overall, however, I have to congratulate Scalax (Jamie Webb in particular) for getting it right. Scalax is still in a very early stage, so maybe the concerns addressed here will be addressed by the time it is ready for release. These concerns are relatively minor anyway, so I recommend using ManagedResource as is.