Thursday, September 15, 2005

Java Resources

For the non-geekly ones amongst my readers: this post is not about coffee! It is about some of the difficulties with resources in the Java programming language, and what I did about it. If you're not a Java programmer, this would a good time to skip to the next post.

But if you are a Java programmer...

If you've ever tried to design a flexible and extensible library in Java, there's a good chance you've bumped into this frustrating issue: the standard Java libraries don't provide any way to enumerate the resources (including class files) in a package, much less a whole tree of a package and its sub-packages. There's a good reason for this: the standard Java libraries do not assume that a package's resources are stored in a searchable system, like a file system or a jar file. Instead, they assume that packages might be loaded over the network, or by carrier pigeon, or (sometime in the future) through spontaneous wormholes. In these non-searchable systems, the only way to grab a resource would be to address it directly by name. You can't search them and you can't enumerate them.

But...what if you know that in your case, your resources will be stored in (and loaded from) a searchable system? Surely 99%+ of all Java code written falls into this bucket! Well, the standard Java libraries don't help you out a bit in this case. This most common situation is simply not handled. And darn it, that's very awkward!

For example, suppose you are building a library that is all about tracking genetics of farm animals, and you include objects for Cow, Horse, Hog, and a few others. But you'd really like other programmers to be able to create their own objects (perhaps Penguin, Meerkat, or Dragon), and have those objects automatically register themselves with your genetics library. That's where you bump into this problem. What you'd really like to do is to have your genetics library, on startup, search for classes that implement your new Genetics interface; then your library could automatically load and use them. The alternatives are a little clunkier. You could require new objects to be manually registered (but what a pain, and error prone!), or you could (yuk!) require the new objects to be compiled right into your standard library. Wouldn't it be nice to eliminate all that?

That's what the new org.jopen.util.resources package does. With it, you can write a piece of code like this:

ResourceSet rs1 = new ResourceSet();
ResourceFilter rf1 = new ResourceTypeFilter( FilterOutcome.Exclude, FilterOutcome.Uncertain, "class" );
ResourceFilter rf2 = new PackageFilter( FilterOutcome.Exclude, FilterOutcome.Uncertain, "META-INF", true );
ResourceFilter rf3 = new IsAPackageFilter( FilterOutcome.Exclude, FilterOutcome.Uncertain );
ResourceFilter rf4 = new ResourceTypeFilter( FilterOutcome.Exclude, FilterOutcome.Uncertain, "properties" );
ResourceFilter rf5 = new ResourceTypeFilter( FilterOutcome.Exclude, FilterOutcome.Uncertain, "gif" );
ResourceFilterChain rfc1 = ResourceFilterChain.createInclude();
rfc1.add( rf1, rf2, rf3, rf4, rf5 );
rs1.addPackage( "org.jopen", true, rfc1, null );

These few lines of code do the following: look in the package org.jopen and its sub-packages for all resources, excluding class files, excluding META-INF packages, excluding packages themselves, excluding properties files, and excluding .GIF files -- and make a set from all those resources remaining.

That's a complicated task I just described, and would be quite challenging to implement if you didn't know a little trick we used when implementing this package. That trick: when you use ClassLoader.getResource(), it returns a URL instance. If the URL points to a file or a jar (and you can easily tell this by looking at the URL), then you can figure out exactly where in the file system or in a particular jar the resource is located. And...the ClassLoader.getResource() method can be invoked with a package name. Ah ha! With that little bit of information, the problem becomes solvable. Which is what we did with JOpen.util.resources!

Now we also went a little crazy with some nice features like chainable filters, a special ClassResource that lets you easily work with class files, and more. Go check it out on the JOpen site, where you'll find links to both the javadocs and the source code.

Enjoy!

APOD

APOD brings us...

Approaching the nucleus of comet Tempel 1 at ten kilometers per second, the Deep Impact probe's targeting camera recorded a truly dramatic series of images. Successive pictures improve in resolution and have been composited here at a scale of 5 meters per pixel -- including images taken within a few meters of the surface moments before the July 4th impact. Analyzing the resulting cloud of debris, researchers are directly exploring the makeup of a comet, a primordial chunk of solar system material. Described as a recipe for primordial soup, the list of Tempel 1's ingredients - tiny grains of silicates, iron compounds, complex hydrocarbons, and clay and carbonates thought to require liquid water to form - might be more appropriate for a cosmic souffle, as the nucleus is apparently porous and fluffy. Seen here, Tempel 1's nucleus is about five kilometers long, with the impact site between the two large craters near the bottom.

Very interesting how they put this synthetic image together; a good demonstration of the power of modern computers. This would have been impossible not so very long ago...

The science reports starting to trickle out show Tempel-1 being surprisingly fluffy; a collection of powder "snow" (made of an exotic chemical mix) and sand that is just barely held together by the weak gravity of the "snowball". Before the mission scientists had a range of expectations about the comet's composition, from solid rock to "wet snowball". Tempel-1 seems to be outside that entire range...

Click on the picture for a larger view.