I like Eclipse. It is a really useful tool. It does a lot of magic behind the scenes and after a bit of getting familiar with its API you are able to write quite powerful plug-ins.
The bad thing is, that if something bad happens deep inside that magical box, it is quite challenging to analyze and understand what exactly is going on.
My problem had to do with such a case. I am using the Eclipse Modeling Framework (EMF) and my plan is to persist my models using the CDO-Teneo-Hibernate-MySQL bridge. CDO and Teneo are a bit experimental, but they are well supported in the eclipse.technology.emft Newsgroup.
The database persistence uses an approach of mapping arbitrary EMF models to database tables automatically including the dynamic creation of proxies. But as you might guess, all of this requires two sets of plug-ins (CDO and Teneo) as well as the hibernate library (which has further dependencies, e.g. the CGLib to dynamically create proxies) and the database driver library.
So, last week I ran into massive class loading problems, because the different plugins in the CDO and Teneo projects make use of the Hibernate libraries and manage access to them through a rather heterogeneous collection of exporting and importing packages and making use of the Eclipse-BuddyPolicy tag. Using this tag, for example, a user-contributed plugin can contribute the database driver class to the classpath of an existing plug-in.
However, one of the problems was caused by the fact, that the CGLib Java Bytecode generator was going to build a proxy for a class defined in another bundle (let’s say A) but at the same time also needed to incorporate the HibernateProxy interface (which was in the hibernate library in bundle B). And there is no dependency between A and B, because hibernate is a strategy add-on to CDO. Consequently, the class generation went awfully wrong because CGLib would take the classloader associated with its input class (so classloader of bundle A) which of course did not find the HibernateProxy interface from bundle B.
Back then I did not know all that. I only got an exception with a stack trace like this:
net.sf.cglib.core.CodeGenerationException: java.lang.reflect.InvocationTargetException-->null
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
at org.hibernate.proxy.pojo.cglib.CGLIBLazyInitializer.getProxyFactory(CGLIBLazyInitializer.java:127)
at org.hibernate.proxy.pojo.cglib.CGLIBProxyFactory.postInstantiate(CGLIBProxyFactory.java:43)
at org.hibernate.tuple.entity.PojoEntityTuplizer.buildProxyFactory(PojoEntityTuplizer.java:162)
at org.hibernate.tuple.entity.AbstractEntityTuplizer.(AbstractEntityTuplizer.java:135)
at org.hibernate.tuple.entity.PojoEntityTuplizer.(PojoEntityTuplizer.java:55)
at org.hibernate.tuple.entity.EntityEntityModeToTuplizerMapping.(EntityEntityModeToTuplizerMapping.java:56)
at org.hibernate.tuple.entity.EntityMetamodel.(EntityMetamodel.java:302)
at org.hibernate.persister.entity.AbstractEntityPersister.(AbstractEntityPersister.java:434)
at org.hibernate.persister.entity.JoinedSubclassEntityPersister.(JoinedSubclassEntityPersister.java:91)
at org.hibernate.persister.PersisterFactory.createClassPersister(PersisterFactory.java:58)
at org.hibernate.impl.SessionFactoryImpl.(SessionFactoryImpl.java:226)
at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1300)
at org.eclipse.emf.cdo.server.internal.hibernate.HibernatePackageHandler.getSessionFactory(HibernatePackageHandler.java:273)
... some more
Caused by: java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
... 74 more
Caused by: java.lang.NoClassDefFoundError: org/hibernate/proxy/HibernateProxy
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(Unknown Source)
... 80 more
Caused by: java.lang.ClassNotFoundException: org.hibernate.proxy.HibernateProxy
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClassInternal(BundleLoader.java:481)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:397)
at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:385)
at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:87)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClassInternal(Unknown Source)
... 82 more
What …? I thought. CDO calls Hibernate calls CGLib and CGLib can not locate a Hibernate class? So I went digging through the internet tried to understand class loading in Eclipse I found a good article and this Eclipsepedia Entry which provide some insight.
However, this did not really help. If you are stuck somewhere with a ClassNotFoundException, because you don’t know, which class loader was being used to load the class (and failed), you have a problem. Both setting an exception breakpoint and stepping through the execution using the debugger is a bad idea, because there are so many class loads – and also many failing class loads – in Eclipse, that both methods are cumbersome.
A better way to gain information is enabling tracing and activating the class loader trace in org.eclipse.osgi:

This leads to output like this:
BundleClassLoader[org.eclipse.emf.teneo_0.8.0.qualifier].loadClass(org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue)
BundleLoader[org.eclipse.emf.teneo_0.8.0.qualifier].loadBundleClass(org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue)
BundleLoader[org.eclipse.emf.teneo.annotations_0.8.0.qualifier].findLocalClass(org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue)
BundleLoader[org.eclipse.emf.teneo.annotations_0.8.0.qualifier] found local class org.eclipse.emf.teneo.annotations.pannotation.DiscriminatorValue
So you can see in which bundle (in sqare brackets) the classloader looks for your class. This helped me find out, what was going on (described above). To solve my problem, I had to patch the two of the CDO manifests to make the classloader find the additional classes.
One way or another, all that classpath magic is hard to see through, when it comes to dynamic code generation and third party libraries in eclipse.
As a final note: I came over another article (in German), which does not directly address my issues, but could be helpful when you have several plug-ins which have to load different classes from different bundles.