Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LoadTimeWeaving not working properly in Websphere [SPR-9857] #14490

Closed
spring-projects-issues opened this issue Oct 4, 2012 · 11 comments
Closed

LoadTimeWeaving not working properly in Websphere [SPR-9857] #14490

spring-projects-issues opened this issue Oct 4, 2012 · 11 comments
Assignees
Labels
in: core type: bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

@spring-projects-issues spring-projects-issues commented Oct 4, 2012

Patrick McCabe opened SPR-9857 and commented


Affects: 3.1.2

Issue Links:

  • #13167 @Entity objects are not enhanced by the load time weaver in certain situations
@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 4, 2012

Patrick McCabe commented

I am running Spring GA 3.1.2, using Websphere 7.0.0.23, with the JPA2 feature pack installed, which uses OpenJPA 2.0.2-SNAPSHOT

What I am seeing appears to be an issue with load time weaving of JPA entities when the entity is declared as a return paramater of a method inside a class which has an auto injected persistence context or persistence unit. The load order of the classes also changes this behaviour as shown below.

I've created a very simple application which demonstrates this issue.

My Spring Config as follows.

<beans ...>					

	<context:annotation-config />	
  	<context:load-time-weaver /> 
	<context:component-scan base-package="test" />	
	<tx:annotation-driven transaction-manager="transactionManager"/>	

...

<bean id="entityManagerFactory"
    class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="persistenceProviderClass" value="org.apache.openjpa.persistence.PersistenceProviderImpl"/>
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter"/>
    </property>
    <property name="packagesToScan">
        <list>
            <value>test.entity</value>
        </list>
    </property>       
</bean>
 ...
</beans>

I have a single Entity to manage as follows.

@Entity
public class Person {
	@Id
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

Now, here is where the funny stuff starts. I have the following repository defined.

@Repository
public class PersonRepositoryImpl implements PersonRepository{
	private EntityManager em;
	
	@Override
	public void doSomethin() {
		em.find(Person.class, "blah");
	}
	
	@Override
	public Person findPerson() {
		return (Person)em.find(Person.class, "blah");
	}	
	
	public EntityManager getEm() {
		return em;
	}
	@PersistenceContext
	public void setEm(EntityManager em) {
		System.out.println("Setting Entity Manager in PersonRepository 1");
		this.em = em;
	}
}

When I try to run this code, I get a warning, then an exception from OpenJPA saying the entity has not been enhanced.
[10/4/12 15:10:56:292 ADT] 0000005e SystemOut O 8565 default WARN [WebContainer : 0] openjpa.Enhance - This configuration disallows runtime optimization, but the following listed types were not enhanced at build time or at class load time with a javaagent: "
test.entity.Person".
Exception created : <openjpa-2.0.2-SNAPSHOT-r422266:1295351 fatal user error> org.apache.openjpa.persistence.ArgumentException: The type "class test.entity.Person" has not been enhanced.

Seems strange, since everything is configured correctly.
Now, if I comment out the

public Person findPerson() {

method from both the implementation, as well as the interface, the code runs correctly. So basically, when I introduce a method with an Entity as the return paramater, the load time weaving fails to enhance it.

Whats even more interesting is that if I create a second repository with just single method as below. AND this class gets loaded FIRST. Then the previous repository which had given me errors now works correctly. So there is definately someting funny going on with the load order of the classes

@Repository
public class PersonRepositoryImpl2 implements PersonRepository2{
	private EntityManager em;
	
	@Override
	public void doSomething() {
		em.find(Person.class, "blah");
	}

	public EntityManager getEm() {
		return em;
	}
	@PersistenceContext
	public void setEm(EntityManager em) {
		System.out.println("Setting Entity Manager in Person Repository 2");
		this.em = em;
	}	
}

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 5, 2012

Patrick McCabe commented

After doing some debugging, it is apparent that in the failure case, the entity class is being loaded at deploy time prior to having the proper class transformer created.
When using

<context:load-time-weaver /> 

I can see that the AspectJWeavingEnabler#AspectJClassBypassingClassFileTransformer transformer is initially applied. I can see the Person entity going through this transformer. After all classes are loaded, then the proper PersistenceProviderImpl$ClassTransformer is applied. Obviously at this point it is too late to transform the entity class.

Using the alternate configuration of specifying the transformer directly in the LocalContainerEntityManagerFactoryBean similarly fails, as the transformer is also applied after the entity class is loaded.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 5, 2012

Patrick McCabe commented

I think I've tracked down the root of this problem.

This method

org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor.findPersistenceMetadata

uses

targetClass.getDeclaredMethods()

to inspect methods of all spring beans. Since the Person entity is declared in my interface, that entity gets loaded at this point in time.

The problem is that at this point

LocalContainerEntityManagerFactoryBean.createNativeEntityManager

has not yet been called. It is this operation that adds the proper ClassTransformer required to transform the JPA entity classes for OpenJPA. Since the Person class is loaded before the correct Transformer has been applied, I receive the Entity not enhanced error. This also explains the peculiar behaviour I was seeing when I added a second repository. After the first repository bean was loaded with no reference to any JPA entities in the interace, the EntityManager was created, resulting in the ClassTransormer being applied. When it comes to load the second repository, which previously caused issues, it works because the proper ClassTransformer is now in place.

As a fix, we need to ensure that the EntityManager is initialized prior to calling PersistenceAnnotationBeanPostProcessor.findPersistenceMetadata. I'm not sure where the best place to do this would be yet. Hopefully some of you Spring guys could have a look. If I have time, I will continue to investigate.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 11, 2012

Juergen Hoeller commented

This definitely looks like some side effect of the class loading order. We're using a temporary ClassLoader exactly in order to avoid such effects, but I suppose we're missing some specific case there.

Note that when deploying to WebSphere, you could also let WebSphere manage the persistence units and obtain the EntityManagerFactory references via JNDI (jee:jndi-lookup elements in your Spring config). This way of deployment should be documented in the server manuals since it is basically the default EE way of handling persistence units. Spring can then take it from there, managing the persistence contexts, transactions, etc the usual way. See Spring's PersistenceAnnotationBeanPostProcessor javadoc for some details.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 11, 2012

Juergen Hoeller commented

As for the runtime effects here, thanks for digging into this - your comments up there are very valuable! We indeed need to make sure that the LocalContainerEntityManagerFactoryBeans in the context are being initialized eagerly before any repository beans that might use them. This happens quite explicitly when using standard bean references (<property>, <constructor-arg>) but unfortunately is being applied more implicitly when using @PersistenceContext injection.

Does it make a difference whether your LocalContainerEntityManagerFactoryBean definition comes before the <context:component-scan base-package="test"/> instruction? The order could be significant there since aside from inter-bean dependencies, the container will initalize beans in the order of definition in the context. Putting the LocalContainerEntityManagerFactoryBean first should turn it into the first bean getting initalized, possibly making the arrangement work as well.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 12, 2012

Juergen Hoeller commented

I've temporarily added early initialization of LoadTimeWeaverAware beans before regular beans are being considered, which should help in bootstrapping LocalContainerEntityManagerFactoryBeans before any repository beans - independent from the order of definition.

It would be great to know whether putting your LocalContainerEntityManagerFactoryBean definition before your component-scan instruction (or explicit repository bean definition) actually helps in your scenario. That would validate the fix above.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 12, 2012

Patrick McCabe commented

Hi Juergen

I have validated that placing the LocalContainerEntityManagerFactoryBean definition before <context:component-scan base-package="test"/> does fix the problem. This would seem to validate your fix.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 12, 2012

Patrick McCabe commented

If you a build with this fix included, I would be happy to validate it for you.

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 12, 2012

Juergen Hoeller commented

Good to hear that the LocalContainerEntityManagerFactoryBean reordering helps there, Patrick! The algorithm I'm using now is pretty straightforward, since LoadTimeWeaverAware is a perfect indicator for beans potentially registering class transformers. I'm confident that it will do the trick.

I'll make another 3.1.3 snapshot available in about an hour. Would be great if you could give it a try then...

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 12, 2012

Juergen Hoeller commented

Pushed to the repo now. The snapshot should be available once the CI build (3.1.x #158) passed.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

@spring-projects-issues spring-projects-issues commented Oct 12, 2012

Patrick McCabe commented

Hi Juergen

I see that the recent build has passed all tests, and should be available for download. Forgive my ignorance, but where exactly do I go to download these releases ?

Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core type: bug
Projects
None yet
Development

No branches or pull requests

2 participants