[Logo] Terracotta Discussion Forums
  [Search] Search   [Recent Topics] Recent Topics   [Members]  Member Listing   [Groups] Back to home page 
[Register] Register / 
[Login] Login 
[Expert]
Classloader used for jobDataMap objects  XML
Forum Index -> Quartz
Author Message
wiberto

neo

Joined: 08/12/2010 19:29:45
Messages: 3
Offline

I recently migrated my web applications from JBoss 4.2 to JBoss 5.1 and I had to go through all the classloading nightmares once again. Because of the number of frameworks in our app, I ended up having to put the quartz jars at the EAR level.

So I started having problems when the job class could not be found because quartz was using a different classloader than the one used by the job itself. I fixed this issue by creating my own ClassLoadHelper, so now the Job class was in the same class loader as the class holding my scheduler (the scheduler is in the EAR classloader).

The problem is more with an object being stored in the jobDataMap. This object has a totally different classloader from either the Job or the Scheduler, and I didn't know how to change that. This object is being deserialized by Quartz (it's using JobStoreTX with the JDBCJobStore. If I use the RAMJObStore it's not an issue because the jobData object stays the same.

So here's the classloader for the different objects to see if anyone has any ideas how I can make the jobdata to use the same classloader as the Job. It would be great if it would use the ClassLoadHelper to create those objects as well.

Code:
 Object creating scheduler:
     BaseClassLoader@45d017d4{.../deploy/NuminsWebServicesEAR.ear/NuminsKaeWEB.war/}
 
 Quartz Scheduler:
     BaseClassLoader@2223a923{.../deploy/NuminsWebServicesEAR.ear/}
 
 object before going into jobDataMap: (Same as the object that creates the scheduler. Since it creates both)
     BaseClassLoader@45d017d4{.../deploy/NuminsWebServicesEAR.ear/NuminsKaeWEB.war/}
 
 ClassLoadHelper: (It's being put in the application's WAR file)
     BaseClassLoader@45d017d4{.../deploy/NuminsWebServicesEAR.ear/NuminsKaeWEB.war/}
 
 Job read using ClassLoadHelper: (As expected since it uses the classLoadHelper)
     BaseClassLoader@45d017d4{.../deploy/NuminsWebServicesEAR.ear/NuminsKaeWEB.war/}
 
 jobExecutionContext: (As expected since it's using the same as the Scheduler)
     BaseClassLoader@2223a923{.../deploy/NuminsWebServicesEAR.ear/}
 
 jobDataMap: (As expected since it's using the same as the Scheduler)
     BaseClassLoader@2223a923{.../deploy/NuminsWebServicesEAR.ear/}
 
 Object from JobDataMap:
     BaseClassLoader@315351de{vfsfile:/C:/dev/jboss-5.1.0.GA/server/numins/deploy/NuminsEAR.ear/}
 


And just to make it clear, here's my classloading structure:

Code:
 Root
  +--- NuminsEar.ear
  +--- NuminsWebServicesEAR.ear
           +---NuminsKaeWEB.war
 


Look how the last guy has a totally different classloader from the rest. It's even in another EAR classloader. How could this be?

For now the workaround is for the Job to instantiate a new Object making a copy of the original object taken out of the jobDataMap using reflection (since I can't cast the object either since it's in a different classloader) But this is not a clean solution at all.

Any hints, ideas, or suggestions will be greatly appreciated.

teck

seraphim
[Avatar]
Joined: 05/24/2006 15:03:25
Messages: 1099
Offline

I don't claim to completely know for sure what is occurring in your setup, but from reading the jdbc job store code it seems that default serialization semantics are used when deserializing job data maps from the database. That sounds like it is at the core of the issue.

Using a standard java.io.ObjectInputStream to deserialize objects the classloader selected is based on the call stack. The first non-null loader in the call stack is selected to materialize the object. The only way to influence that behavior is to subclass ObjectInputStream and override resolveClass() and resolveProxyClass(), or to somehow get the call stack to be different.

Off the cuff it seems like the quartz code should be changed to use the class load helper here (when deserializing object job data maps from the DB). You could maybe try this yourself if you're feeling adventurous, I think the place to start is the method org.quartz.impl.jdbcjobstore.StdJDBCDelegate.getObjectFromBlob(ResultSet, String)

-tim





Tim Eck (terracotta engineer)
wiberto

neo

Joined: 08/12/2010 19:29:45
Messages: 3
Offline

Yeah, I was looking at the code and saw that it was deserialized with the standard guy.

If I were to subclass and create my own serializer/deserializer, how can I have quartz use my subclass without changing the code in quartz?
teck

seraphim
[Avatar]
Joined: 05/24/2006 15:03:25
Messages: 1099
Offline

Granted I didn't actually run this, but I think a subclass of the driver delegate thingy should let you experiment here.

I wrote this subclass of PostgreSQLDelegate. You should subclass whatever database specific one you're using and adjust if the getObjectFromBlob() there differs than the postgres one I copied.

In quartz.properties reference this new subclass for "org.quartz.jobStore.driverDelegateClass".

good luck. let me know if it works for your scenario
 Filename MySQLDelegate.java [Disk] Download
 Description
 Filesize 2 Kbytes
 Downloaded:  90 time(s)


Tim Eck (terracotta engineer)
wiberto

neo

Joined: 08/12/2010 19:29:45
Messages: 3
Offline

I will give this approach a try and let you know how it goes.

Thanks!!
 
Forum Index -> Quartz
Go to:   
Powered by JForum 2.1.7 © JForum Team