Accueil > Equinox Aspects, JPA, JPA/EclipseLink, Spring, Spring DM, Spring ORM, Springweaver > Load-Time Weaving for Spring-DM with JPA/EclipseLink [step2]

Load-Time Weaving for Spring-DM with JPA/EclipseLink [step2]


Into the previous [step1], I have introduced Springweaver 0.1.2 to use it with JPA/EclipseLink. In this article I will explain how launch the Springweaver- EclipseLink sample which use Springweaver 0.1.2 with JPA/EclipseLink. I assume you know Spring DM (Spring extender…). This sample was developed and tested with Eclipse Galileo.

How test the sample Springweaver (0.1.2)+EclipseLink

The sample Springweaver (0.1.2)+EclipseLink use H2 Database which contains T_USER table. It is based on several classic bundles (DAO, Services, Domain, Client) where the Client bundle org.dynaresume.simpleosgiclient pool on every 5 seconds the service UserService to get the list of User coming from the T_USER H2 Database by using JPA/Eclipselink. User domain class from the bundle org.dynaresume.domain use classic JPA annotation to map it to the database :

package org.dynaresume.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "T_USER")
public class User  {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "USR_ID_N")
	private long id;

	@Column(name = "USR_LOGIN_C")
	//@Basic(fetch=FetchType.LAZY)
	private String login;

	@Column(name = "USR_PASSWORD_C")
	private String password;

	public User() {
	}

	public User(String login, String password) {
		setLogin(login);
		setPassword(password);
	}

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getLogin() {
		return login;
	}

	public void setLogin(String login) {
		this.login = login;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}

Prepare your workspace

To test Springweaver EclipseLink sample, you can get projetcts :

From zip

You can download workspace-springweaver_0.1.2_100705.zip which contains the whole projects of the Springweaver EclipseLink sample. But I adwice to get projects from SVN to have last version of Springweaver.

From SVN

  1. get Springweaver 0.1.2 bundle org.eclipse.equinox.weaving.springweaver from SVN :
  2. get the whole bundles projects from the examples/eclipselink SVN folder :
  3. get the Equinox Aspects Hook fragment org.eclipse.equinox.weaving.hook from CVS Eclipse :
    • Host: dev.eclipse.org
    • Repository path: /cvsroot/eclipse
    • User: anonymous

    • Click on Next button and fill Use specified module name: equinox-incubator/aspects/org.eclipse.equinox.weaving.hook


    Click on Finish button.

  4. org.eclipse.osgi bundle must be imported into your workspace because org.eclipse.equinox.weaving.hook is an Equinox Hook. To do that, open the view Plug-Ins (Window-Show View->Open… and after select PDE->Plug-ins). Select the org.eclipse.osgi bundle and click on right menu to open context menu and select Import As-> Binary project :

Set Target Platform

Your workspace look like this with a lot of compilation problems :

To resolve compilation problem, open the Target Platfom file springweaver-target-platform\SpringWeaver – EclipseLink- TargetPlatform.target and click on Set as Target Platform link :

Projects from workspace are re-compiled and none errors must appear :

Install H2 Database

Copy H2 Database file dynaresume-db/h2/db/dynaresume.data.db :

into your C:/db/h2 folder because H2 Database properties are configured into the fragment org.dynaresume.config.dao.datasource.h2 into the file org.dynaresume.config.dao.datasource.h2/src/org/dynaresume/config/dao/datasource/h2/h2.properties like this :

database.driverClassName=org.h2.Driver
database.url=jdbc:h2:C:/db/h2/dynaresume
database.username=sa
database.password=

Springweaver- EclipseLink.launch

The sample provides the launch springweaver-target-platform/launch/Springweaver- EclipseLink.launch which configure correctly bundles (select bundles, set start level to Springweaver bundle, set JVM parameters…).

Launch Run

To launch the sample, select the launch springweaver-target-platform/launch/Springweaver- EclipseLink.launch and click on right menu to open context menu. Click on Run As -> Springweaver-EclipseLink :

OSGi console opens and display list of User coming from T_USER table of the H2 Database :

[EL Fine]: 2010-04-30 13:50:37.343--ServerSession(2973689)--Connection(1223623)--Thread(Thread[Thread-9,5,spring-osgi-extender[14b5f4a]-threads])--SELECT USR_ID_N, USR_LOGIN_C, USR_PASSWORD_C FROM T_USER
User [login=Angelo ZERR (H2), password=], woven=true
User [login=Martin Lippert (H2), password=], woven=true
User [login=Pascal Leclercq (H2), password=], woven=true
User [login=Jawher Moussa (H2), password=], woven=true
User [login=Mickael BARON (H2), password=], woven=true

woven flag (which is true) show you that User class has been transformed (woven) by EclipseLink. It check if user instance (getting from UserService into client bundle org.dynaresume.simpleosgiclient) implements EclipseLink interface org.eclipse.persistence.internal.weaving.PersistenceWeaved like this :

Collection<User> users = userService.findAllUsers();
for (User user : users) {
	boolean woven = (user instanceof PersistenceWeaved);
	System.out.println("User [login=" + user.getLogin() + ", password=" + user.getPassword() + "], woven=" + woven);
}

Launch Explanation

The Springweaver- EclipseLink.launch launch show you how configure Springweaver. You can edit it with the menu Run->Run Configurations… and click on the node OSGi framework->Springweaver- EclipseLink :

Springweaver & Start level=3

if you go at Bundles tab, you can notice that Springweaver bundle org.eclipse.equinox.weaving.springweaver is configured with Start level=3 :

This configuration is very important because org.eclipse.equinox.weaving.springweaver must register Equinox Aspects WeavingService (wich transform bytecode) BEFORE the other bundles which use User class (like Client, DAO). If Springweaver bundle is started AFTER that User class is loaded (by Client, DAO), the weaving is not done because once Class is already loaded.

osgi.framework.extensions & JVM parameters

if you go at Arguments tab, you can notice you have this JVM parameters :

-Dosgi.framework.extensions=org.eclipse.equinox.weaving.hook

It’s a requirement to use Equinox Aspects which is a fragment Equinox Hook.

AspectJ NOT required

You can notice that there are not AspectJ bundles used into the Target Platform. Springweaver works without AspectJ.

org.dynaresume.dao.jpa.eclipselink

The org.dynaresume.dao.jpa.eclipselink bundle is the JPA bundle wich declare the JPA entityManagerFactory and set the required LoadTimeWeaver with EquinoxAspectsLoadTimeWeaver with APPLICATION weaverScope (very important) :

<bean class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver">
	<property name="weaverScope" value="APPLICATION" />
</bean>

Here the content of the org.dynaresume.dao.jpa.eclipselink/META-INF/spring/module-context.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">

	<!-- entityManagerFactory created before DAO -->
	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="persistenceUnitName" value="dynaresume" />
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
				<property name="database" value="H2" />
				<property name="generateDdl" value="false" />
				<property name="showSql" value="true" />
			</bean>
		</property>
		<property name="loadTimeWeaver">
			<bean
				class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver">
				<property name="weaverScope" value="APPLICATION" />
			</bean>
		</property>
	</bean>

	<bean id="userDAO" class="org.dynaresume.dao.jpa.UserDAOJpa"></bean>

	<bean
		class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />

	<tx:annotation-driven transaction-manager="txManager" />

	<bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

</beans>

You can notice that :

  1. bean DAO is declared AFTER the entityManagerFactory, otherwise the Domain classes will be loaded (by the DAO) before the creation of the entityManagerFactory and Domain classes will not be woven.
  2. the loadTimeWeaver is not configured with <context:load-time-weaver.

Springweaver Errors

I think it’s interesting to see in action miscellaneous errors when Springweaver is not very well configured.

No LoadTimeWeaver

If you remove loadTimeWeaver declaration from the entityManagerFactory, you will have this error :

Caused by: java.lang.IllegalStateException: Cannot apply class transformer without LoadTimeWeaver specified
at org.springframework.orm.jpa.persistenceunit.SpringPersistenceUnitInfo.addTransformer(SpringPersistenceUnitInfo.java:78)

Bundle scope

If you declare EquinoxAspectsLoadTimeWeaver without weaverScope (BUNDLE scope will be used) :

<property name="loadTimeWeaver">
	<bean class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver">
	</bean>
</property>

your configuration is like Springweaver 0.1.1, and you will notice that User Class will not woven (woven=false):

User [login=Angelo ZERR (H2), password=], woven=false
User [login=Martin Lippert (H2), password=], woven=false
User [login=Pascal Leclercq (H2), password=], woven=false
User [login=Jawher Moussa (H2), password=], woven=false
User [login=Mickael BARON (H2), password=], woven=false

DAO declared before entityManagerFactory

If you declare the bean userDAO BEFORE the bean entityManagerFactory into the org.dynaresume.dao.jpa.eclipselink/META-INF/spring/module-context.xml, you will notice that User Class will not woven (woven=false):

User [login=Angelo ZERR (H2), password=], woven=false
User [login=Martin Lippert (H2), password=], woven=false
User [login=Pascal Leclercq (H2), password=], woven=false
User [login=Jawher Moussa (H2), password=], woven=false
User [login=Mickael BARON (H2), password=], woven=false

org.dynaresume.domain.eclipselink.fragment

You can notice that it exist the OSGi fragment org.dynaresume.domain.eclipselink.fragment used into the launch. This fragment is linked to the Domain bundle org.dynaresume.domain to add some EclipseLink import package. Here her MANIFEST.MF :

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Fragment
Bundle-SymbolicName: org.dynaresume.domain.eclipselink.fragment
Bundle-Version: 1.0.0.qualifier
Fragment-Host: org.dynaresume.domain;bundle-version="1.0.0"
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: org.eclipse.persistence.descriptors.changetracking;version="2.0.0.v20091127-r5931",
 org.eclipse.persistence.indirection;version="2.0.0.v20091127-r5931",
 org.eclipse.persistence.internal.descriptors;version="2.0.0.v20091127-r5931",
 org.eclipse.persistence.internal.identitymaps;version="2.0.0.v20091127-r5931",
 org.eclipse.persistence.internal.weaving;version="2.0.0.v20091127-r5931",
 org.eclipse.persistence.jpa;version="2.0.0.v20091127-r5931",
 org.eclipse.persistence.queries;version="2.0.0.v20091127-r5931",
 org.eclipse.persistence.sessions;version="2.0.0.v20091127-r5931"

This fragment is required when EclipseLink can weave User class. Indeed Domain Bundle must import this package (I prefer using fragment bundle to import package instead of importing directly package into the Bundle domain and « pollute » it with EclipseLink packages). To see the problem in action, you can deselect this fragment into the launch and run it. You will have this error :

3359 [SpringOsgiExtenderThread-6] ERROR org.springframework.osgi.extender.internal.activator.ContextLoaderListener - Application context refresh failed (OsgiBundleXmlApplicationContext(bundle=org.dynaresume.dao.jpa.eclipselink, config=osgibundle:/META-INF/spring/*.xml))
java.lang.NoClassDefFoundError: org/eclipse/persistence/internal/weaving/PersistenceWeaved
...
Caused by: java.lang.ClassNotFoundException: org.eclipse.persistence.internal.weaving.PersistenceWeaved

You can manage this problem by using OSGi Dynamic Import Packages (EclipseLink package could be imported on runtime with Equinox by using Dynamic Import) => no need to add EclipseLink packages with Springweaver – DynamicImportPackages.

User -lazy loading

EclipseLink use weaving to manage lazy mode. To see this feature in action, we can set the property login in lazy-mode with @Basic(fetch=FetchType.LAZY) like this :

@Entity
@Table(name = "T_USER")
public class User {

...
	@Column(name = "USR_LOGIN_C")
	@Basic(fetch=FetchType.LAZY)
	private String login;

...
}

If you launch the sample, you will have a « lazy-loading » error :

Exception [EclipseLink-6004] (Eclipse Persistence Services - 2.0.0.v20091127-r5931): org.eclipse.persistence.exceptions.QueryException
Exception Description: The object [org.dynaresume.domain.User@856d3b], of class [class org.dynaresume.domain.User], with identity hashcode (System.identityHashCode()) [8 744 251],
is not from this UnitOfWork object space, but the parent session's. The object was never registered in this UnitOfWork,
but read from the parent session and related to an object registered in the UnitOfWork. Ensure that you are correctly
registering your objects. If you are still having problems, you can use the UnitOfWork.validateObjectSpace() method to
help debug where the error occurred. For more information, see the manual or FAQ.
Query: ReadObjectQuery(referenceClass=User sql="SELECT USR_ID_N, USR_LOGIN_C, USR_PASSWORD_C FROM T_USER WHERE (USR_ID_N = ?)")

This error is classic lazy-loading error where the client try to load login property from the Database although SQL connection (EclipseLink session) is not opened. But this error show you that EclipseLink weave correctly the User class.

If you remove APPLICATION scope weaver into EquinoxAspectsLoadTimeWeaver declaration :

<property name="loadTimeWeaver">
	<bean class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver">				
	</bean>
</property>

you will notice that there are not lazy-loading error and console display login property :

User [login=Angelo ZERR (H2), password=], woven=false
User [login=Martin Lippert (H2), password=], woven=false
User [login=Pascal Leclercq (H2), password=], woven=false
User [login=Jawher Moussa (H2), password=], woven=false
User [login=Mickael BARON (H2), password=], woven=false

So if EclipseLink don’t weave the Domain classes, it ignore @Basic(fetch=FetchType.LAZY) JPA annotation.

Springweaver – DynamicImportPackages

Into org.dynaresume.domain.eclipselink.fragment section, we have seen the problem with EclipseLink weaving : import packages of EcliseLink must be done into Bundle Domain wich contains classes which are woven by EclipseLink. With Springweaver 0.1.2, you can use OSGi Dynamic Import Package capability into your Spring declaration.

You can use dynamicImportPackages property wich add EclipseLink packages into Domain bundle (User class will be woven) like this :

<property name="loadTimeWeaver">
	<bean class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver">
		<property name="weaverScope" value="APPLICATION" />				
		<property name="dynamicImportPackages">
			<list>
				<value>ECLIPSELINK</value>
			</list>
		</property>				
	</bean>
</property>

ECLIPSELINK key is used to retrieve EclipseLink packages coming from the file springweaver-default.properties which have this content :

ECLIPSELINK=org.eclipse.persistence.descriptors.changetracking,org.eclipse.persistence.internal.descriptors,org.eclipse.persistence.internal.identitymaps,org.eclipse.persistence.internal.weaving,org.eclipse.persistence.jpa,org.eclipse.persistence.queries,org.eclipse.persistence.sessions

If you update loadTimeWeaver declaration into org.dynaresume.dao.jpa.eclipselink/META-INF/spring/module-context.xml with :

<property name="loadTimeWeaver">
	<bean class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver">
		<property name="weaverScope" value="APPLICATION" />				
		<property name="dynamicImportPackages">
			<list>
				<value>ECLIPSELINK</value>
			</list>
		</property>				
	</bean>
</property>

and you launch Springweaver- EclipseLink- DynamicImportPackages.launch (same launch than « Springweaver- EclipseLink », except fragment OSGi org.dynaresume.domain.eclipselink.fragment is not included you will notice you will not have EclipseLink ClassNotFoundException.

Custom DynamicImportPackages

You can add your own DynamicImportPackages key (like ECLIPSELINK) or even override ECLIPSELINK property by creating an OSGi fragment linked to the bundle org.eclipse.equinox.weaving.springweaver and add your properties into springweaver.properties file stored into src folder of your fragment. So you can create an OSGi fragment org.eclipse.equinox.weaving.springweaver.config linked to the bundle org.eclipse.equinox.weaving.springweaver which contains into src folder the file springweaver.properties with this content :

POJOBINDABLE=org.eclipse.core.databinding.pojo.bindable

After you can write :

<property name="loadTimeWeaver">
	<bean class="org.eclipse.equinox.weaving.springweaver.EquinoxAspectsLoadTimeWeaver">
		<property name="weaverScope" value="APPLICATION" />				
		<property name="dynamicImportPackages">
			<list>
				<value>ECLIPSELINK</value>
				<value>POJOBINDABLE</value>
			</list>
		</property>				
	</bean>
</property>

The org.eclipse.core.databinding.pojo.bindable will be imported into the Bundle which contains classes which are woven.

  1. août 31, 2013 à 1:12

    Hey Angelo, I triy to myke this work with a current version of equinox aspects. I got it all fine, but I see (Scope=Application) public IWeavingService createWeavingService( never gets called. Weaving hook starts up[org.eclipse.equinox.weaving.hook] info adding AspectJ hooks … and bundle gets activated early. Da you have any ideas?

    • septembre 1, 2013 à 7:56

      Hi Thomas,

      I’m sorry, I cannot help you; it wa sso long time that I have not played with my little project.
      Any feedback are welcome.

      Thank’s!

      Regards Angelo

  2. ergu
    septembre 11, 2013 à 3:29

    Is it possible to use this method with Hibernate 4.2.2 Final since it has been osgified? I’m trying to do the same with Hibernate and I don’t know what to put inside springweaver.properties so that it weaves also Hibernate,

    Any help would be appreciated

  1. avril 30, 2010 à 1:58

Laisser un commentaire