Please use the google group to ask questions - thanks.

by Lyubo 15 Apr 14:33
Override ClassWriter.getCommonSuperClass()

Hi,
As part of an effort to integrate Ebean with a web development framework with dynamic compilation and enhancement I need to change implementation of the above mentioned method. The problem is that the class loader used in Class.forName() is not the one which loads dynamically compiled classes. Any suggestion is welcome.

Thanks

19 Apr 02:40
by Rob

I wonder if you need the "context classLoader" instead?

Would something like this work in your case?

public static Class<?> classForName(String name, Class<?> caller) throws ClassNotFoundException {
    try {
      ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
      if ( contextClassLoader != null ) {
        return contextClassLoader.loadClass( name );
      }
    }
    catch ( Throwable e ) {
      // ignore
    }
    return Class.forName( name, true, caller.getClassLoader() );
  }
19 Apr 02:43
by Rob

PS: Others are now interested in SQLLite ... do you have a DatabasePlatform to share?

Thanks, Rob.

19 Apr 10:43
by Rob

It seems that there might be a case for overriding the getCommonSuperClass() method.

Can you provide more information as to what the error is. Can you confirm this is when using dynamically generated subclasses (and not when using enhancement).

Thanks, Rob.

19 Apr 10:45
by Rob

Note: it is class SubClassFactory extends ClassLoader that constructs the plain ClassWriter in question.

19 Apr 17:13
by Lyubo

The problem is that the class in question is still not created in JVM by the time this function is called. There is a separate data structure which holds information for dynamic classes.

However, I found a temporary and ugly workaround for this : I copied the whole Transformer.java code and created a subclass of ClassWriter.java which overrides the getCommonSuperClass method.

Note: In Transformer.class ClassWriter is created by its constructor not by a factory.

This error occurs when enhancing a class which is entity and has a field of type SomeObject which is not an entity.

You can see entire integration effort (it's pretty simple) code here: http://bazaar.launchpad.net/~lyubo.ivanov/play-ebean/1.0/files

Look at the play.modules.ebean.EbeanPlugin.enhance() where the actual call to ebean enhancement is made

Here is the stacktrace:

java.lang.RuntimeException: java.lang.ClassNotFoundException: XXXX
at com.avaje.ebean.enhance.asm.ClassWriter.getCommonSuperClass(ClassWriter.java:1272)
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.MethodAdapter.visitMaxs(MethodAdapter.java:189)
at com.avaje.ebean.enhance.asm.ClassReader.accept(ClassReader.java:1447)
at com.avaje.ebean.enhance.asm.ClassReader.accept(ClassReader.java:420)
at play.modules.ebean.HackedTransformer.entityEnhancement(HackedTransformer.java:146)
at play.modules.ebean.HackedTransformer.transform(HackedTransformer.java:106)
at play.modules.ebean.EbeanPlugin.enhance(EbeanPlugin.java:83)


PS: I've sent a mail with SQLite platform file a long time ago. Maybe it'd been deleted by some spam filter? I can send it again of course.

Thanks,
Lyubo

19 Apr 21:29
by Rob
private Class findClass(String name) throws ClassNotFoundException {

    ApplicationClass ac = Play.classes.getApplicationClass(name);
    if (ac == null) return Class.forName(name);

    return ac.javaClass;
}

The way I see it Ebean needs to provide a way of plugging in a ClassWriter or perhaps just a "Class Finder" for use with the SubClassFactory.

Possibly, the SubClassFactory should probably provide a "Class Finder" with a class loader (itself or it's parent classLoader) so that we can use Class.forName(name, true, parentClassLoader) rather than just Class.forName(name).

ok, I'll have a look at how to do that.

Cheers, Rob.

19 Apr 23:23
by Rob

Hmmm, actually there must be something else I don't know. That is, I expect you are not using plain entity beans? Are you generating the entity beans themselves perhaps?

What classes are coming to the getCommonSuperClass() method, what is their ClassLoader, and how does that classLoader relate to Ebean's SubClassFactory ClassLoader.

It is not clear to me why getCommonSuperClass() is called at all ... and there might be an issue here with enhancement (rather than the dynamic subclassing Ebean can use).

20 Apr 00:53
by Rob

Ok, I went over it again and I can see you are using in EbeanPlugin:

byte[] buffer = transformer.transform(Play.classloader, applicationClass.name, null, null, applicationClass.enhancedByteCode);

So I'll add a LoaderAwareClassWriter ... that will use the ClassLoader passed in with the Class.forName(...) resolution. So this means it will use the Play.classLoader in this case.

That might do the trick.

20 Apr 03:43
by Rob

Ok, I have added the appropriate code into HEAD (for BUG 280 : ENHANCEMENT - Enable Context ClassLoader aware ... add LoadAwareClassWriter).

This means Ebean will use a LoadAwareClassVisitor ... and in the case of Play's EbeanPlugin it should use the Play.classLoader to resolve the classes in the getCommonSuperClass() calls.

Let me know if that helps.

Thanks, Rob.

20 Apr 09:20
by Lyubo

Is there HEAD binary download or I should build it from source. I suppose the source is at https://ebeanorm.svn.sourceforge.net/svnroot/ebeanorm/ebean/trunk/

10 May 12:32
by Lyubo

as of release 2.6.0 this is issue had been resolved.
Thanks Rob.

Create a New Topic

Title:
Body:
 
Introduction User Guide (pdf) Install/Configure Public JavaDoc Whitepapers
General Database Specific Byte Code Deployment Annotations Features
Top Bugs Top Enhancements
woResponse