ClassNotFoundException during enhancement`

712 views
Skip to first unread message

Rien

unread,
Oct 15, 2010, 11:41:51 AM10/15/10
to Ebean ORM
Hi,

We suddenly encounter the exception below, even when we go back a few
revisions in our subversion server the errors remains. The class is
absolutely there.

We are building with ant and use the enhance task. We tried increasing
memory and permgen space in ant, but it doesn't make a difference.

When we use the enhancer agent in our development environment we don't
get this error (but we do have a work around in place, see
http://groups.google.com/group/ebean/browse_thread/thread/fc8f2eff83d9a633/3a7870b91e769e2d?lnk=gst&q=agent#3a7870b91e769e2d)

I have the feeling we are missing something really stupid, anyone have
any ideas?

Cheers, Rien


java.lang.RuntimeException: java.lang.ClassNotFoundException:
com.<some entity class of ours>
at
com.avaje.ebean.enhance.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:
1284)
at
com.avaje.ebean.enhance.asm.ClassWriter.getMergedType(ClassWriter.java:
1244)
at com.avaje.ebean.enhance.asm.Frame.merge(Frame.java:1373)
at com.avaje.ebean.enhance.asm.Frame.merge(Frame.java:1280)
at
com.avaje.ebean.enhance.asm.MethodWriter.visitMaxs(MethodWriter.java:
1270)
at
com.avaje.ebean.enhance.asm.commons.LocalVariablesSorter.visitMaxs(LocalVariablesSorter.java:
142)
at
com.avaje.ebean.enhance.asm.ClassReader.accept(ClassReader.java:1447)
at
com.avaje.ebean.enhance.asm.ClassReader.accept(ClassReader.java:420)
at
com.avaje.ebean.enhance.agent.Transformer.transactionalEnhancement(Transformer.java:
189)
at
com.avaje.ebean.enhance.agent.Transformer.transform(Transformer.java:
122)
at
com.avaje.ebean.enhance.agent.InputStreamTransform.transform(InputStreamTransform.java:
55)
at
com.avaje.ebean.enhance.agent.InputStreamTransform.transform(InputStreamTransform.java:
39)
at
com.avaje.ebean.enhance.ant.OfflineFileTransform.transformFile(OfflineFileTransform.java:
141)
at
com.avaje.ebean.enhance.ant.OfflineFileTransform.processPackage(OfflineFileTransform.java:
124)
at
com.avaje.ebean.enhance.ant.OfflineFileTransform.processPackage(OfflineFileTransform.java:
115)
at
com.avaje.ebean.enhance.ant.OfflineFileTransform.processPackage(OfflineFileTransform.java:
115)
at
com.avaje.ebean.enhance.ant.OfflineFileTransform.process(OfflineFileTransform.java:
90)
at
com.avaje.ebean.enhance.ant.AntEnhanceTask.execute(AntEnhanceTask.java:
77)
at
org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.GeneratedMethodAccessor185.invoke(Unknown
Source)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:
25)
at java.lang.reflect.Method.invoke(Method.java:597)
at
org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:
106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:390)
at org.apache.tools.ant.Target.performTasks(Target.java:411)
at
org.apache.tools.ant.Project.executeSortedTargets(Project.java:1397)
at org.apache.tools.ant.Project.executeTarget(Project.java:
1366)
at
org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:
41)
at org.apache.tools.ant.Project.executeTargets(Project.java:
1249)
at
org.apache.tools.ant.module.bridge.impl.BridgeImpl.run(BridgeImpl.java:
281)
at
org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:
539)
at
org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:154)

Rob Bygrave

unread,
Oct 17, 2010, 7:53:14 PM10/17/10
to eb...@googlegroups.com

So you'd expect it to be an issue with the classpath or classloader (being an ant task I guess you are enhancing outside the IDE).
Is the superclass that can't be found in a jar or anywhere special (Is it already enhanced?)
Is the superclass in the classpath twice?
You are enhancing for @Transactional right - do other similar classes with @Transactional enhance fine?

Rien

unread,
Oct 18, 2010, 6:08:08 AM10/18/10
to eb...@googlegroups.com
In the meantime we found out some more details, it is actually pretty weird.

The class I was talking about is an entity class, they are not in a jar or something.

The problem seems to be related to the other one. This time we started getting this problem when we added a method to an entity class in which we access a property which is also an entity class (OneToMany relation).

We removed this method and Ebean starts up again. Then we notice in the log that the problem actually occurs in more places, but those places are transactional enhancements and that doesn't prevent Ebean from starting up (enhancement of other classes continues either way). Of course this is even worse, because now we have classes which should be transactional but are not and we didn't even know about it (does explain another weird error we had though).

Btw: Maybe it would be an idea to have a property to disable automatic transactions, so that we would get an exception if we try to save an entity outside any transaction?

After digging a little deeper we found that this problem occurs in situations where we have the following:

public interface BlaBla {
  public String getSomeInfo();
}

@Entity
public class MyEntity implements BlaBla {
  <all my properties etc>

  public String getSomeInfo() {
    return "someinfo";
}

@Transactional/@Entity {
public class SomeOtherEntityOrTransactional {

  public List<String> getBlaBlaStringList(List<BlaBla> blablaInstances) {
    for (List<BlaBla> bb : blablaInstances) {
      result.add(bb.getSomeInfo());
    }
  }
}

// Non Entity and Non Transactional class
public class SomeOtherClass implements BlaBla {
  private MyEntity entity;
  private String someOtherProperties;

  // getters/setters/etc
}

And then the log would look like this (for a Transactional SomeOtherEntityOrTransactional class):

transform> cls: SomeOtherEntityOrTransactional  msg: method:getBlaBlaStringList (Ljava/util/List;)Ljava/util/List; transactional {}
java.lang.RuntimeException: java.lang.ClassNotFoundException: MyEntity

So the common super class that can't be found would be the "MyEntity" class in a method where we are referring to an interface it implements. And it looks like the problem occurs when there are non entity classes implementing the same interface. Btw, using these non entity classes directly in enhances classes doesn't seem to be a problem. Unfortunately setting debug=9 doesn't give me any more info.

Will let you know when we find out more.

Cheers, Rien

Rob Bygrave

unread,
Oct 18, 2010, 4:44:27 PM10/18/10
to eb...@googlegroups.com
Some background around this area:

ClassWriter is from the ASM library (you likely already knew that). So we could add some debug in around getCommonSuperClass() and would probably be very useful at this stage. It would be interesting to know what the two types where that getCommonSuperClass() was being passed.

The Transformer has a createClassWriter() method that was made so that the Play framework could integrate. The Play framework has different classloading and I believe effectively needed to overwrite the getCommonSuperClass() method (as they have dynamically defined classes).

Comments from getCommonSuperClass() indicate that it is expected to be overridden:

...
     * It can be overridden to compute this common super type in other ways, in particular
     * without actually loading any class, or to take into account the class
     * that is currently being generated by this ClassWriter, which can of
     * course not be loaded since it is under construction.


Anyway, sounds like you might be getting close to the bottom of it. If you get a testcase I'll be keen to run enhancement on it.


Cheers, Rob.

Rien

unread,
Nov 5, 2010, 11:56:30 AM11/5/10
to Ebean ORM
This issue has been bothering me for a while now, I never did find a
way to get the ClassWriter to load those classes.

What does solve the problem is going around the whole getMergedType
function in the ClassWriter.

To do this I change the following code in
com.avaje.ebean.enhance.asm.Frame:1369

if ((u & BASE_KIND) == OBJECT) {
// if t is also a reference type, and if u and t
have the
// same dimension merge(u,t) = dim(t) | common
parent of the
// element types of u and t
v = (t & DIM) | OBJECT
| cw.getMergedType(t & BASE_VALUE, u &
BASE_VALUE);
} else {

Into:

if ((u & BASE_KIND) == OBJECT) {
// if t is also a reference type, and if u and t
have the
// same dimension merge(u,t) = dim(t) | common
parent of the
// element types of u and t
v = OBJECT | cw.addType("java/lang/Object");
// v = (t & DIM) | OBJECT
// | cw.getMergedType(t & BASE_VALUE, u &
BASE_VALUE);
} else {

So whenever a merged type is needed we just set it to
java.lang.Object. This doesn't seem to cause any issues in my
environment, but please let me know if there is a reason why these
classes need to be merged.

Cheers, Rien
> > On Sat, Oct 16, 2010 at 4:41 AM, Rien <rnent...@gmail.com> wrote:
>
> >> Hi,
>
> >> We suddenly encounter the exception below, even when we go back a few
> >> revisions in our subversion server the errors remains. The class is
> >> absolutely there.
>
> >> We are building with ant and use the enhance task. We tried increasing
> >> memory and permgen space in ant, but it doesn't make a difference.
>
> >> When we use the enhancer agent in our development environment we don't
> >> get this error (but we do have a work around in place, see
>
> >>http://groups.google.com/group/ebean/browse_thread/thread/fc8f2eff83d...
> >> )

Rob Bygrave

unread,
Nov 5, 2010, 5:39:24 PM11/5/10
to eb...@googlegroups.com
The ClassWriter is a repackaged ASM class - I'll have to look into this a bit (It is not obvious to me what the getMergedType is for).

Cheers, Rob.

Rob Bygrave

unread,
Nov 6, 2010, 5:14:20 AM11/6/10
to eb...@googlegroups.com
I am unable to reproduce with your BlaBla / MyEntity example.

Can you produce a test case?

Rien Nentjes

unread,
Nov 6, 2010, 7:34:43 AM11/6/10
to eb...@googlegroups.com
We haven't been able to create a test case yet. It's a very obscure problem that only seems to happen in our code-base.

When time permits maybe we can try to set up an elaborate test case. The enhance task gives a ClassNotFoundException on an entity class while enhancing a transactional class. I suspect the class is enhanced itself at that time and can't be loaded for that reason.

Cheers, Rien

mbell697

unread,
May 15, 2013, 6:29:14 PM5/15/13
to eb...@googlegroups.com
Sorry to dig up an old topic but I've run into this issue as well.  This only effects one entity in my project out of ~75 and there was no consistant pattern I could find as to why it only effected this entity.  It most certainly is incredibly obscure and I don't understand the cause at all but I've built a test case for it.  

Be warned, the logic in the test method is completely irrational but it appears to all be necessary to trigger the ClassNotFoundException, e.g. if you remove the empty for loop, the irrational if statement or the unused object creation, the bug disappears.  


I'd be pretty fascinated to know what the root cause of this is.

Cheers,

Mark

mbell697

unread,
May 15, 2013, 9:22:40 PM5/15/13
to eb...@googlegroups.com
Right...I think I've sorted out what is happening here....

As mentioned above, the default getCommonSuperClass() has to load the classes to work.  I don't understand exactly what the call chain is that leads to a call to getCommonSuperClass() but it appears to be deeply intwined in the way the bytecode is generated and how asm parses it.  I can say that in all of the 75 entities I'm enhancing, it's only called when a method of the structure in the test case is hit, and its called for java.util.Iterator and org.ebean.test.enhance.TestNonEntity from the test case.

The problem is, that by default, each plugin in maven gets it's own class loader, which by default only seems to have access to its own classes (and std library i assume).  

From the Maven docs:

"Please note that the plugin classloader does neither contain the dependencies of the current project nor its build output. Instead, plugins can query the project's compile, runtime and test class path from the MavenProject in combination with the mojo annotation requiresDependencyResolution from the Mojo API Specification. For instance, flagging a mojo with @requiresDependencyResolution runtime enables it to query the runtime class path of the current project from which it could create further classloaders.

When a build plugin is executed, the thread's context classloader is set to the plugin classloader."

The current Ebean MavenEnhanceTask just grabs MavenEnhanceTask.class.getClassLoader(), which appears to be the plugin classloader (checked via debugging the maven stage).  Thus the Ebean enhancement code doesn't have access to the build output of the project, leading to the exception.

So, it seems the fix is to request access to the build classpath via one of the methods mentioned in the maven docs quoted above.  I'm not much of an expert on the Maven plugin api but I'll see if I can take a stab at it and get a pull request up.

Cheers,

Mark

Rob Bygrave

unread,
May 15, 2013, 10:35:49 PM5/15/13
to eb...@googlegroups.com
So probably something like:  http://stackoverflow.com/questions/871708/maven-plugin-cant-load-class


--
 
---
You received this message because you are subscribed to the Google Groups "Ebean ORM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

mbell697

unread,
May 15, 2013, 10:43:15 PM5/15/13
to eb...@googlegroups.com
Yep, I found about half a dozen ways to do it in my googling but some only seem to work with some versions of maven among other things.  Not sure which would be the preferable approach.  

Rob Bygrave

unread,
May 15, 2013, 11:26:49 PM5/15/13
to eb...@googlegroups.com
I wonder if another approach would be to simply use:

ClassLoader cl = Thread.currentThread().getContextClassLoader();

mbell697

unread,
May 15, 2013, 11:34:12 PM5/15/13
to eb...@googlegroups.com
I don't believe that solves it.  It doesn't clear my test case and there is this line from the maven docs: "When a build plugin is executed, the thread's context classloader is set to the plugin classloader."

Roland Praml

unread,
Aug 24, 2016, 8:06:56 AM8/24/16
to Ebean ORM
I got a very similar problem today.
I was wondering, why enhancement does not occur from time to time.

As I've added verbose logging to the eclipse plugin, I can also provide a stacktrace now:
java.lang.RuntimeException: java.lang.ClassNotFoundException: de.foconis.test.TestBean$TestInterface cannot be found by com.avaje.ebean.eclipse.enhancer_8.1.0
    at com
.avaje.ebean.enhance.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1684)
    at com
.avaje.ebean.enhance.asm.ClassWriter.getMergedType(ClassWriter.java:1654)
    at com
.avaje.ebean.enhance.asm.Frame.merge(Frame.java:1426)
    at com
.avaje.ebean.enhance.asm.Frame.merge(Frame.java:1325)
    at com
.avaje.ebean.enhance.asm.MethodWriter.visitMaxs(MethodWriter.java:1475)
    at com
.avaje.ebean.enhance.asm.MethodVisitor.visitMaxs(MethodVisitor.java:866)
    at com
.avaje.ebean.enhance.asm.ClassReader.readCode(ClassReader.java:1554)
    at com
.avaje.ebean.enhance.asm.ClassReader.readMethod(ClassReader.java:1017)
    at com
.avaje.ebean.enhance.asm.ClassReader.accept(ClassReader.java:693)
    at com
.avaje.ebean.enhance.asm.ClassReader.accept(ClassReader.java:506)
    at com
.avaje.ebean.enhance.agent.Transformer.entityEnhancement(Transformer.java:173)
    at com
.avaje.ebean.enhance.agent.Transformer.transform(Transformer.java:132)
    at org
.avaje.ebean.typequery.agent.CombinedTransform.transform(CombinedTransform.java:33)
    at com
.avaje.ebean.eclipse.internal.enhancer.builder.EnhanceBuilder.checkResource(EnhanceBuilder.java:105)
    at com
.avaje.ebean.eclipse.internal.enhancer.builder.EnhanceBuilder.access$0(EnhanceBuilder.java:59)
    at com
.avaje.ebean.eclipse.internal.enhancer.builder.EnhanceBuilder$DeltaVisitor.visit(EnhanceBuilder.java:216)
    at org
.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:63)
    at org
.eclipse.core.internal.events.ResourceDelta.accept(ResourceDelta.java:74)

Here is a test case:
@Entity
public class TestBean {

   
public static void main(final String args[]) {
       
TestBean bean = new TestBean();
       
System.out.println("TestBean instanceof EntityBean: " + (bean instanceof EntityBean));
   
}

   
interface TestInterface {}

   
static class TestImpl implements TestInterface {}


   
public void internalCheck() {
       
TestInterface rs = new TestImpl();    // must be set
       
if ("".equals("")) {                 // there must be a condition
            rs
= getReportSet();            // must be set to an other interface
       
}
   
}

   
private TestInterface getReportSet() {
       
return null;
   
}

}


only eclipse seems to fail. The maven build succeds

Best regards
Roland


Rob Bygrave

unread,
Aug 24, 2016, 8:14:30 AM8/24/16
to ebean@googlegroups
The problem is very likely that the classpath given to the enhancement agent is incorrect in that it does not contain part of the classpath.

Looking at EnhanceBuilder line 119

    String[] ideClassPath = JavaRuntime.computeDefaultRuntimeClassPath(javaProject);


The question is ... does JavaRuntime.computeDefaultRuntimeClassPath() include the "test" classpath. TestBean$TestInterface would be in the test classpath and I suspect the ideClassPath is not including the test part of the classpath.



--

---
You received this message because you are subscribed to the Google Groups "Ebean ORM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Rob Bygrave

unread,
Aug 24, 2016, 8:22:14 AM8/24/16
to ebean@googlegroups
No, that classpath looks correct to me.

Roland Praml

unread,
Aug 24, 2016, 6:06:01 PM8/24/16
to Ebean ORM
Woohoo: After a long debugging session, I've fixed this.

Fix was very easy.
just changed
Response response = combined.transform(null, className, null, null, classBytes);
to
Response response = combined.transform(cl, className, null, null, classBytes);
;)

I also added a nice feature that a marker (red X) will appear in the source code if an error occurs.

Pull request will follow.

cheers Roland
To unsubscribe from this group and stop receiving emails from it, send an email to ebean+un...@googlegroups.com.

Rob Bygrave

unread,
Aug 25, 2016, 4:05:46 AM8/25/16
to ebean@googlegroups
Awesome thanks.  I've pushed that as version 8.1.2 to the update site :  

http://ebean-orm.github.io/eclipse/update

To unsubscribe from this group and stop receiving emails from it, send an email to ebean+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages