Question

It seems like everybody has had an unpleasant brush with the Java Service Provider, that thing you can do with a file named like META-INF/services/com.example.Interface, but that nobody uses except for trying to load the right XML parser. I'm trying to work with a library that uses the Service Provider API, and trick it so that I can provide some runtime-extended classes (using cglib) that don't actually implement the interface but can be made to do so easily.

Basically, I think the steps I need to perform are:

  1. Create a custom class loader that will respond to getResources(...) and return an "extra" URL
  2. Also have that class loader hook getResourceAsStream(...) to return a list of the classes I am going to manipulate with cglib, when asked for the "extra" resource
  3. Finally, have that class loader load those classes when requested

But here's where I get lost. For example, when the library tries to determine what implementers are out there, it calls getResources(...) which returns a bunch of URLs. But getResourceAsStream(...) doesn't take URLs, it takes "names". Names which seem to be classpath-relative, and therefore the same everywhere. So META-INF/services/com.example.Interface in has the same "name" as META-INF/services/com.example.Interface in their JAR, right? Except somehow this works with those blasted XML parsers...

Of course, all of this assumes they were smart/kind enough to call ClassLoader.getSystemClassLoader() rather than using ClassLoader.getSystemResources(...), ClassLoader.getSystemResourceAsStream(...), etc., as in the latter case there's no way to hook the ClassLoader and provide the faked file.

I guess in that case I could use BCEL to manipulate the class files when my code is being packaged by Maven, rather than waiting until runtime to do it with cglib?

Was it helpful?

Solution

The idea I described was along the right track. The mistake I made was in thinking that using ClassLoader.getResourceAsStream(..) to access the contents of the URLs. Instead, you should just URL.openStream().

Had I found it before posting, java.util.ServiceLoader (@since 1.6) provides some insight into how to do things correctly.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top