I was working with Package annotations recently, and ran into an interesting deficiency in the Java APIs related to them. If you aren't familiar with the concept, package annotations let you supply metadata to an entire package. This solves one of the problems we used to have with XDoclet, which is how do you specify configuration metadata that applies to an entire package, or to the entire application?
In XDoclet, you could sometimes attach the information to just one class or to all your classes. Or, in some cases you would pass in extra information in deployment descriptor merge files or even in your ant build file. It worked, but it wasn't always pleasant. Package annotations give us a much better way to provide metadata that is large in scope than a single class.
It'd great, and package annotations and the annotation reflection API work surprisingly well. But Java has a huge hole in the mechanism for getting a handle on the Package object itself. The basic API looks like this:
No problem, right? Well, one problem that Java has already had is that if the JVM class hasn't loaded a class from that package yet, there is no package info for you to reflect on and do things with that package. That's rather annoying, but you can always load a class from that package first, if you happen to know one.
Class c = SomeClassInMyProj.class; Package pkg =Package.getPackage("com.whatever.mproj");
This works if you don't care about class loaders. What if you are trying to load a package from a specific classloader? It turns out that there is no direct way to do this with the Java API. The methods on Package are static and work from the callers classloader. And, the methods on ClassLoader to load a package are protected. The only solution here, it seems, is to load a class from that package and query the package for the class.
Class c = classLoader.loadClass("com.whatever.myproj.SomeClassInMyProj"); Package pkg = c.getPackage();
This all assumes you know a class in the package. If you don't, what do you do? There's no API to directly list the classes in a package. I thought this was a fairly unsolvable problem, requiring the developer to perhaps use something like cglib to load the package info from memory. But, today I realized there is actually a way to solve the problem, at least if your goal is to read package annotations.
Package annotations are created by creating a pseudo-class called package-info.java in whatever package you want. A package-info.java might look like this:
@SomeAnnotation package com.whatever.myproj;
That's a complete, legal package-info.java file. The compiler knows how to do the right thing with this and get the annotations onto the Package object, if you are lucky enough to be able to get a handle on it. I didn't actually investigate the mechanism much, besides noting that package-info.java gets compiled to package-info.class in your JAR file.
Today, I finally looked a bit deeper and realized that package-info.class is an actual Java class - an interface to be precise. It is possible to load it into memory just as you would any other class. And once you have that, you can call getPackage() to get the Package object:
Null checking aside, that's what it takes to load a Package from a specific class loader. (assuming your goal is reading package annotations) I doubt it's an issue most people will run into, but if you have - that's the solution. Perhaps Sun will make our lives easier in the future by giving us what I expected to exist in the first place: