Monday, September 20, 2010

Wired Exception caused by Proxy.equals

Weeks ago I encountered a wired problem when I shutdown FuseSource[1] ESB4/Apache Servicemix[2]4, I get exception like

org.osgi.service.blueprint.container.ServiceUnavailableException: Service is unavailable
at org.apache.aries.blueprint.container.ReferenceListRecipe$ServiceDispatcher.call(ReferenceListRecipe.java:201)
at org.apache.aries.blueprint.container.AbstractServiceReferenceRecipe$JdkProxyFactory$1.invoke(AbstractServiceReferenceRecipe.java:632)
at $Proxy12.repositoryEvent(Unknown Source)
at org.apache.karaf.features.internal.FeaturesServiceImpl.callListeners(FeaturesServiceImpl.java:939)
at org.apache.karaf.features.internal.FeaturesServiceImpl.internalRemoveRepository(FeaturesServiceImpl.java:186)
at org.apache.karaf.features.internal.FeaturesServiceImpl.stop(FeaturesServiceImpl.java:777)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)[:1.6.0_13]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)[:1.6.0_13]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)[:1.6.0_13]
at java.lang.reflect.Method.invoke(Method.java:597)[:1.6.0_13]
at org.apache.aries.blueprint.utils.ReflectionUtils.invoke(ReflectionUtils.java:230)[7:org.apache.aries.blueprint:0.1.0.r964701]
at org.apache.aries.blueprint.container.BeanRecipe.invoke(BeanRecipe.java:854)[7:org.apache.aries.blueprint:0.1.0.r964701]
at org.apache.aries.blueprint.container.BeanRecipe.destroy(BeanRecipe.java:761)[7:org.apache.aries.blueprint:0.1.0.r964701]
at org.apache.aries.blueprint.container.BlueprintRepository.destroy(BlueprintRepository.java:295)[7:org.apache.aries.blueprint:0.1.0.r964701]

I realize this exception is from ESB kernel, karaf FeaturesServiceImpl class. After digging into the FeaturesServiceImpl code, I found that if the FeaturesListener gone, it should invoke unregisterListener() mothed, so we shouldn't have get this exception at all.
the unregisterListener() mothed is
public void unregisterListener(FeaturesListener listener) {
        listeners.remove(listener);
    }

It's quite simple, and listeners is just a List instance. To figure out what's going on underlying, I launched remote debug for ESB process and set breakpoint in unregisterListener method, but what I saw then really confused me. I do see listeners List has a FeaturesListener instance, let's say with ObjectId 155, but when unregisterListener() get invoked with argument FeaturesListener instance exactly the same object with the one listeners List containing, listeners.remove(listener) doesn't work, listeners can't remove listener it actually contain.

I know List use Object.equals() method to determine if two object really equal, I guess there must be something wrong in equal method, so I then step into the List.remove method and then Object.equals method, aha, listener just not eqaul itself. Well, finally figured out that this is caused by we use aries blueprint to inject FeatureListener as OSGi servie using dynamic proxy, it's not the real FeatureListener, it's just the proxy of FeatureListenr, this can explain the wired exception, equals() method from a proxy actually will compare a proxy and the real object, which obviously will return false, so we should override equals method to handle the proxy comparasion case.

I googled a little bit, there's another good article to describe this Proxy.equals()[3] issue.And the issue from Apache Karaf to track is KARAF-136[4]
[1]http://fusesource.com/
[2]http://servicemix.apache.org/home.html
[3]http://www.javaspecialists.eu/archive/Issue126.html
[4]https://issues.apache.org/jira/browse/KARAF-136

No comments:

Post a Comment