You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@cayenne.apache.org by Andrus Adamchik <an...@objectstyle.org> on 2007/02/20 13:31:23 UTC

JPA enhancer behavior - fat agent?

Dain Sundstrom (who is a member of Geronimo project) did some  
preliminary testing of Cayenne JPA provider in the OpenEJB container  
last week - you've seen some jiras that he opened. I am taking  
further discussion to the list (not sure if Dain is subscribed, so I  
am cc'ying to him as well).

So the enhancer... I tried to run the OpenEJB JpaTest [1] with  
Cayenne provider. It fails to enhance the Employee object [2], even  
if it is explicitly mentioned in the PersistenceUnit, because it was  
loaded in the JVM *before* Cayenne provider was started and  
Provider.createContainerEntityManagerFactory(..) was called.

I was trying to think of ways to "fix" it, but considering that  
java.lang.instrument.Instrumentation doesn't allow to enhance already  
loaded classes ("redefineClasses" method places serious limitations  
on what can be done, namely adding new fields is not allowed), looks  
like JPA users will have to deal with specific startup order  
requirements. In-container and Java SE operation modes will look  
different:

1. In-container: provider shouldn't directly access its own agent,  
and simply register a needed ClassTransformer with container-provided  
PersistenceUnitInfo. Container on the other hand has the  
responsibility to hook up PersistenceUnitInfo implementor to the  
agent, and ensure that provider is started *before* and persistence  
classes are loaded in the app class loader. So I wonder whether the  
fact that JpaTest worked with OpenJPA provider by some coincidence?

2. Standalone: for the enhancer to work, an application must ensure a  
call to Persistence.createEntityManagerFactory() prior to the entity  
classes loaded in runtime. This sucks, and the only way around is to  
run enhancer straight from the agent. I think that's what OpenJPA  
does. Some time ago we decided [3] to avoid putting any Cayenne  
runtime classes in the agent, but it looks like we don't have a  
choice. Unless somebody can suggest a better way of providing  
reliable runtime enhancement, I will change the behavior to provide a  
"fat" agent.

[1] https://svn.apache.org/repos/asf/incubator/openejb/trunk/openejb3/ 
container/openejb-core/src/test/java/org/apache/openejb/core/cmp/jpa/ 
JpaTest.java
[2] https://svn.apache.org/repos/asf/incubator/openejb/trunk/openejb3/ 
container/openejb-core/src/test/java/org/apache/openejb/core/cmp/jpa/ 
Employee.java
[3] http://objectstyle.org/cayenne/lists/cayenne-devel/2006/08/0088.html

Andrus

P.S. Dain - are there any other show stopper issues in your testing  
(except for no EJBQL)?

Re: JPA enhancer behavior - fat agent?

Posted by Craig L Russell <Cr...@Sun.COM>.
+1 to what Dain says here. All the persistence providers that I'm  
familiar with use the -javaagent on the java command line in Java SE  
mode to do the instrumentation (bytecode enhancement) regardless of  
the load order of the persistent classes and the persistence provider.

Craig

On Feb 22, 2007, at 10:45 AM, Dain Sundstrom wrote:

> You can't really push a class back into the app.  Instead you must  
> wait for the app to load (pull) a class and hook the load via the  
> javaagent.
>
> In a managed (container) environment this works quite well because  
> the container can guarantee that the JPA provider is full  
> initialized before any app code is called.  In an unmanaged  
> environment life is much more difficult since it is by definition  
> the application code that initializes the system.  To get around  
> this, you will need the javaagent to search for persistence units  
> and determine which classes need to be enhanced.  One nice thing,  
> is most people will use the @Entity annotation instead of XML to  
> declare their persistent beans, and this entity is easy to detect  
> directly using asm.  You should ask David Blevins about his xbean- 
> finder code which is designed to detect such annotations.
>
> -dain
>
> On Feb 22, 2007, at 10:21 AM, Andrus Adamchik wrote:
>
>> I see how TemporaryClassLoader operates. We haven't been using  
>> 'getNewTempClassLoader' in the provider code as of yet. Let me  
>> poke around and see if I am overcomplicating the issue... I still  
>> don't understand how to push an enhanced class back into the app  
>> classloader space, but maybe I am wrong (still thinking in Cayenne  
>> classic terms), and this is not needed.
>>
>> Andrus
>>
>>
>> On Feb 22, 2007, at 7:46 PM, Dain Sundstrom wrote:
>>> Sorry, got busy and missed this email.
>>>
>>> This is such a common problem, it was addressed directly by the  
>>> JPA specs.  When loading classes for inspection use the class  
>>> loader returned from  
>>> javax.persistence.spi.PersistenceUnitInfo.getNewTempClassLoader().
>>>
>>> For Cayenne's own PersistenceUnitInfo implementation, I suggest  
>>> you copy org.apache.openejb.core.TemporaryClassLoader which I  
>>> copied from OpenJPA and cleaned up.
>>>
>>> -dain
>>
>

Craig Russell
Architect, Sun Java Enterprise System http://java.sun.com/products/jdo
408 276-5638 mailto:Craig.Russell@sun.com
P.S. A good JDO? O, Gasp!


Re: JPA enhancer behavior - fat agent?

Posted by Dain Sundstrom <da...@iq80.com>.
On Feb 23, 2007, at 3:11 AM, Andrus Adamchik wrote:

> BTW, this discussion happened to be very helpful in straightening  
> Cayenne PersistentUnitInfo ClassLoaders. We had our own version of  
> TemporaryClassLoader from the beginning, but is was used as the  
> main PersistentUnitInfo class loader, not the temp one (and  
> provider incorrectly called PersistentUnitInfo.getClassLoader() as  
> a result). I fixed it and got further down with OpenEJB JpaTest.  
> Enhancer now works. I'll see if I can address other errors.

I suggest you compare you temp class loader to the one I pointed  
out.  It is very easy to get the code wrong (I did my first two  
attempts at it).

-dain

Re: JPA enhancer behavior - fat agent?

Posted by Andrus Adamchik <an...@objectstyle.org>.
Ok, that matches my assessment - we need a JPA-aware agent (what I  
called fat agent).

BTW, this discussion happened to be very helpful in straightening  
Cayenne PersistentUnitInfo ClassLoaders. We had our own version of  
TemporaryClassLoader from the beginning, but is was used as the main  
PersistentUnitInfo class loader, not the temp one (and provider  
incorrectly called PersistentUnitInfo.getClassLoader() as a result).  
I fixed it and got further down with OpenEJB JpaTest. Enhancer now  
works. I'll see if I can address other errors.

> You should ask David Blevins about his xbean-finder code which is  
> designed to detect such annotations.

I'll check it out.

Andrus


On Feb 22, 2007, at 8:45 PM, Dain Sundstrom wrote:

> You can't really push a class back into the app.  Instead you must  
> wait for the app to load (pull) a class and hook the load via the  
> javaagent.
>
> In a managed (container) environment this works quite well because  
> the container can guarantee that the JPA provider is full  
> initialized before any app code is called.  In an unmanaged  
> environment life is much more difficult since it is by definition  
> the application code that initializes the system.  To get around  
> this, you will need the javaagent to search for persistence units  
> and determine which classes need to be enhanced.  One nice thing,  
> is most people will use the @Entity annotation instead of XML to  
> declare their persistent beans, and this entity is easy to detect  
> directly using asm.  You should ask David Blevins about his xbean- 
> finder code which is designed to detect such annotations.
>
> -dain
>
> On Feb 22, 2007, at 10:21 AM, Andrus Adamchik wrote:
>
>> I see how TemporaryClassLoader operates. We haven't been using  
>> 'getNewTempClassLoader' in the provider code as of yet. Let me  
>> poke around and see if I am overcomplicating the issue... I still  
>> don't understand how to push an enhanced class back into the app  
>> classloader space, but maybe I am wrong (still thinking in Cayenne  
>> classic terms), and this is not needed.
>>
>> Andrus
>>
>>
>> On Feb 22, 2007, at 7:46 PM, Dain Sundstrom wrote:
>>> Sorry, got busy and missed this email.
>>>
>>> This is such a common problem, it was addressed directly by the  
>>> JPA specs.  When loading classes for inspection use the class  
>>> loader returned from  
>>> javax.persistence.spi.PersistenceUnitInfo.getNewTempClassLoader().
>>>
>>> For Cayenne's own PersistenceUnitInfo implementation, I suggest  
>>> you copy org.apache.openejb.core.TemporaryClassLoader which I  
>>> copied from OpenJPA and cleaned up.
>>>
>>> -dain
>>
>
>


Re: JPA enhancer behavior - fat agent?

Posted by Dain Sundstrom <da...@iq80.com>.
You can't really push a class back into the app.  Instead you must  
wait for the app to load (pull) a class and hook the load via the  
javaagent.

In a managed (container) environment this works quite well because  
the container can guarantee that the JPA provider is full initialized  
before any app code is called.  In an unmanaged environment life is  
much more difficult since it is by definition the application code  
that initializes the system.  To get around this, you will need the  
javaagent to search for persistence units and determine which classes  
need to be enhanced.  One nice thing, is most people will use the  
@Entity annotation instead of XML to declare their persistent beans,  
and this entity is easy to detect directly using asm.  You should ask  
David Blevins about his xbean-finder code which is designed to detect  
such annotations.

-dain

On Feb 22, 2007, at 10:21 AM, Andrus Adamchik wrote:

> I see how TemporaryClassLoader operates. We haven't been using  
> 'getNewTempClassLoader' in the provider code as of yet. Let me poke  
> around and see if I am overcomplicating the issue... I still don't  
> understand how to push an enhanced class back into the app  
> classloader space, but maybe I am wrong (still thinking in Cayenne  
> classic terms), and this is not needed.
>
> Andrus
>
>
> On Feb 22, 2007, at 7:46 PM, Dain Sundstrom wrote:
>> Sorry, got busy and missed this email.
>>
>> This is such a common problem, it was addressed directly by the  
>> JPA specs.  When loading classes for inspection use the class  
>> loader returned from  
>> javax.persistence.spi.PersistenceUnitInfo.getNewTempClassLoader().
>>
>> For Cayenne's own PersistenceUnitInfo implementation, I suggest  
>> you copy org.apache.openejb.core.TemporaryClassLoader which I  
>> copied from OpenJPA and cleaned up.
>>
>> -dain
>


Re: JPA enhancer behavior - fat agent?

Posted by Andrus Adamchik <an...@objectstyle.org>.
I see how TemporaryClassLoader operates. We haven't been using  
'getNewTempClassLoader' in the provider code as of yet. Let me poke  
around and see if I am overcomplicating the issue... I still don't  
understand how to push an enhanced class back into the app  
classloader space, but maybe I am wrong (still thinking in Cayenne  
classic terms), and this is not needed.

Andrus


On Feb 22, 2007, at 7:46 PM, Dain Sundstrom wrote:
> Sorry, got busy and missed this email.
>
> This is such a common problem, it was addressed directly by the JPA  
> specs.  When loading classes for inspection use the class loader  
> returned from  
> javax.persistence.spi.PersistenceUnitInfo.getNewTempClassLoader().
>
> For Cayenne's own PersistenceUnitInfo implementation, I suggest you  
> copy org.apache.openejb.core.TemporaryClassLoader which I copied  
> from OpenJPA and cleaned up.
>
> -dain


Re: JPA enhancer behavior - fat agent?

Posted by Dain Sundstrom <da...@iq80.com>.
Sorry, got busy and missed this email.

This is such a common problem, it was addressed directly by the JPA  
specs.  When loading classes for inspection use the class loader  
returned from  
javax.persistence.spi.PersistenceUnitInfo.getNewTempClassLoader().

For Cayenne's own PersistenceUnitInfo implementation, I suggest you  
copy org.apache.openejb.core.TemporaryClassLoader which I copied from  
OpenJPA and cleaned up.

-dain

On Feb 20, 2007, at 4:31 AM, Andrus Adamchik wrote:

> Dain Sundstrom (who is a member of Geronimo project) did some  
> preliminary testing of Cayenne JPA provider in the OpenEJB  
> container last week - you've seen some jiras that he opened. I am  
> taking further discussion to the list (not sure if Dain is  
> subscribed, so I am cc'ying to him as well).
>
> So the enhancer... I tried to run the OpenEJB JpaTest [1] with  
> Cayenne provider. It fails to enhance the Employee object [2], even  
> if it is explicitly mentioned in the PersistenceUnit, because it  
> was loaded in the JVM *before* Cayenne provider was started and  
> Provider.createContainerEntityManagerFactory(..) was called.
>
> I was trying to think of ways to "fix" it, but considering that  
> java.lang.instrument.Instrumentation doesn't allow to enhance  
> already loaded classes ("redefineClasses" method places serious  
> limitations on what can be done, namely adding new fields is not  
> allowed), looks like JPA users will have to deal with specific  
> startup order requirements. In-container and Java SE operation  
> modes will look different:
>
> 1. In-container: provider shouldn't directly access its own agent,  
> and simply register a needed ClassTransformer with container- 
> provided PersistenceUnitInfo. Container on the other hand has the  
> responsibility to hook up PersistenceUnitInfo implementor to the  
> agent, and ensure that provider is started *before* and persistence  
> classes are loaded in the app class loader. So I wonder whether the  
> fact that JpaTest worked with OpenJPA provider by some coincidence?
>
> 2. Standalone: for the enhancer to work, an application must ensure  
> a call to Persistence.createEntityManagerFactory() prior to the  
> entity classes loaded in runtime. This sucks, and the only way  
> around is to run enhancer straight from the agent. I think that's  
> what OpenJPA does. Some time ago we decided [3] to avoid putting  
> any Cayenne runtime classes in the agent, but it looks like we  
> don't have a choice. Unless somebody can suggest a better way of  
> providing reliable runtime enhancement, I will change the behavior  
> to provide a "fat" agent.
>
> [1] https://svn.apache.org/repos/asf/incubator/openejb/trunk/ 
> openejb3/container/openejb-core/src/test/java/org/apache/openejb/ 
> core/cmp/jpa/JpaTest.java
> [2] https://svn.apache.org/repos/asf/incubator/openejb/trunk/ 
> openejb3/container/openejb-core/src/test/java/org/apache/openejb/ 
> core/cmp/jpa/Employee.java
> [3] http://objectstyle.org/cayenne/lists/cayenne-devel/ 
> 2006/08/0088.html
>
> Andrus
>
> P.S. Dain - are there any other show stopper issues in your testing  
> (except for no EJBQL)?