Wednesday, February 8, 2012

SLF4J has done a good job by allowing users to continue using whatever logging framework they are currently using and then in time move on to LogBack. I like the way they require the adapter jar (e.g. slf4j-jcl.jar or slf4j-log4j12.jar) to have the org.slf4j.impl.StaticLoggerBinder and a few other classes for static binding. And those classes are supposed to implement interfaces from the aptly named org.slf4j.spi package

I thought they would load that class via reflection (=dynamically bind) but looking at the code in org.slf4j.LoggerFactory, it is not the case:

StaticLoggerBinder.getSingleton().getLoggerFactory();

So that might suggest that when they packaged the slf4j-api.jar, they had either slf4j-simple.jar or slf4j-nop.jar in the classpath, however that would result in a circular dependency as ILoggerFactory interface that the above method returns is defined in slf4j-api.jar. So, I think when they packaged slf4j-api.jar, they have a stub implementation of StaicLoggerBinder (and other similar classes) but then remove the .class files from the jar. Sounds a bit unkosher, doesn't it?

I was comparing this approach to how JDBC registers drivers:

java.sql.DriverManager.registerDriver(java.sql.Driver driver);

But this would be called from a static intialization block in the driver implementaion, e.g.

And getConnection() will ask the registered drivers if they can handle the url provided.

As we see with JDBC the Driver(the service provider) has to register itself, whereas, with SLF4J ILoggerFactory (the service provider), the registration (actually binding) is done automatically. This makes sense because with JDBC, we might want to use multiple databases at the same time.

Unless there is a guarantee that for any two Personinstances p1 and p2,p1.equals(p2) impliesp1 == p2, our locking on the Person object (the map key) would be wrong.

Such a guarantee can be obtained if all Person instances are looked up from a pool of unique Persons.

But then again what if an instance was obtained from the pool and a reference to it held long after it was removed from the pool? To avoid such a stale reference, would one now lock the pool itself too?

If the "key" is likely to collide with the key of another class of objects then synchronize(key.intern()) will introduce an unnecessary mutual exclusion among completely unrelated code. In such a case one might instead use a WeakHashMap<String, WeakReference<Object>> as a mapping from a string to a lock object. Then by having one such map for each class of objects, the accidental mutual exclusion because of shared keys can be avoided.