In [step5] we have prepared the Target Platform with:
- The JPA API.
- EclipseLink used as JPA Implementation
- Derby used as Database.
- Commons DBCP used as connection pool.
- Spring Data JPA to implement our DAO with JPA.
The API Dao fr.opensagres.dao.UserDao extends the Spring Data org.springframework.data.repository.PagingAndSortingRepository interface which is the first step to use Spring Data JPA.
In this article we will create several bundles/fragment to retrieves User list from a Database Derby with EclipseLink JPA. The JPA Dao implementation will be done with Spring Data JPA, on other words, you will do nothing (no need to code a JPAUserDao class). We will create 2 bundles and 1 fragment :
- fr.opensagres.dao.jpa the bundle implementation of UserDao with JPA by using Spring Data JPA to avoid coding the JPAUserDao class.
- fr.opensagres.dao.jpa.eclipselink the fragment which configures the JPA fr.opensagres.dao.jpa to use EclipseLink and Deby as dialect.
- fr.opensagres.data.datasource the bundle which publishes the Derby Datasource used by the fr.opensagres.dao.jpa.
Download
You can download eclipsespring_step6.zip which contains the following explained projects :
- fr.opensagres.domain : OSGi bundle domain which hosts fr.opensagres.domain.User class.
- fr.opensagres.dao : OSGi bundle Dao API which hosts fr.opensagres.dao.UserDao interface.
- fr.opensagres.dao.mock : OSGi bundle Dao Mock Implementation (Dao implemented with Java Map) which hosts a Spring file which declares the implementation of UserDao in a Spring bean and publish it the OSGi services registry with Spring DM <osgi:service.
- fr.opensagres.dao.jpa the bundle implementation of UserDao with JPA by using Spring Data JPA to avoid coding the JPAUserDao class.
- fr.opensagres.dao.jpa.eclipselink the fragment which configures the JPA fr.opensagres.dao.jpa to use EclipseLink and Deby as dialect.
- fr.opensagres.data.datasource the bundle which publishes the Derby Datasource used by the fr.opensagres.dao.jpa.
- fr.opensagres.services : OSGi bundle Services API which hosts fr.opensagres.services.UserService interface.
- fr.opensagres.services.impl : OSGi bundle Services Implementation which hosts a Spring file which declares the implementation of UserService in a Spring bean and publish it the OSGi services registry with Spring DM <osgi:service.
- fr.opensagres.data.injector : OSGi bundle Data Injector which hosts a Spring file which declares a DataInjector in a Spring bean. This DataInjector consumes the UserService injected by Spring which is retrieved from the OSGi services registry with Spring DM <osgi:reference and call UserService#saveUser(User user) to inject User data.
- fr.opensagres.simpleclient : OSGi bundle simple client which hosts a Spring file which declares a thread in a Spring bean. This thread consumes the UserService injected by Spring which is retrieved from the OSGi services registry with Spring DM <osgi:reference.
- fr.opensagres.config.log4j : OSGi fragment which configures log4j.
- TargetPlatform: simple project which hosts the Spring DM JARS, the target definition and launch.
To use this zip, unzip it :
- import those projects in a workspace.
- open the TargetPlatform/eclipsespring.target file and click on Set as Target Platform.
- select TargetPlatform/launch/Simple OSGi client.launch – Mock Dao to test the launch with Mock Dao and Run it.
- select TargetPlatform/launch/Simple OSGi client.launch – JPA Dao to test the launch with JPA Dao and Run it.
Bundles – Spring Data JPA
Before starting this article, we modify the TargetPlatform/launch/Simple OSGi client.launch :
- rename this launch to Simple OSGi Client – Mock Dao.
- unselect the option « Add new workspace bundles to this launch configuration automatically », because we will create new JPA bundles and we want not to include it in the MockDao launch.
Image may be NSFW.
Clik here to view.
Domain Bundle
At first we annotate our domain fr.opensagres.domain.User class with JPA annotation.
User class
Modify the fr.opensagres.domain.User class like this:
package fr.opensagres.domain; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.persistence.SequenceGenerator; @Entity(name = "T_USER") public class User { @Id @GeneratedValue(generator = "GEN_USER") @SequenceGenerator(name = "GEN_USER", sequenceName = "SEQ_USER") private Long id; @Column private String firstName; @Column private String lastName; public Long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } }
NOTE: to avoid having this (non blocking) errors with sequence as soon as the Database is created :
Internal Exception: java.sql.SQLException: Table/View 'SEQUENCE' already exists in Schema 'APP'. Error Code: 30000 Call: CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(15), PRIMARY KEY (SEQ_NAME))
The @SequenceGenerator is used.
Rule 6: import annotation package as optional (MANIFEST.MF)
In the META-INF/MANIFEST-MF of the fr.opensagres.domain bundle:
- import the javax.persistence package as optional.
Why importing JPA annotation as optional? The answer is that in some context, you need not JPA annotation (ex: for the MockUserDao).
To understand that, remove the optional resolution, open the Simple OSGi client.launch and click on Validate Bundles button to see the error about import package :
Image may be NSFW.
Clik here to view.
Set the as optional the javax.persistence package and click on Validate Bundles to check you have none error.
Here the full content of this MANIFEST.MF:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Domain Bundle-SymbolicName: fr.opensagres.domain Bundle-Version: 1.0.0.qualifier Bundle-RequiredExecutionEnvironment: J2SE-1.5 Export-Package: fr.opensagres.domain Import-Package: javax.persistence;resolution:=optional
Dao JPA Implementation Bundle
Here we will create the JPA Dao Implementation :
Image may be NSFW.
Clik here to view.
We will use Spring Data JPA to do that (you can notice in the below screenshot that there is none Java class like JpaUserDao).
Create OSGi bundle fr.opensagres.dao.jpa.
persistence.xml
Create the JPA META-INF/persistence.xml like this:
<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"> <persistence-unit name="userUnit" transaction-type="RESOURCE_LOCAL"> <class>fr.opensagres.domain.User</class> </persistence-unit> </persistence>
module-context.xml
Create the META-INF/spring/module-context.xml like this:
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:util="http://www.springframework.org/schema/util" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd"> <context:property-placeholder location="classpath*:META-INF/config/config.properties" /> <bean id="jpaDialect" class="${config.jpa.dialect}" /> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="userUnit" /> <property name="dataSource" ref="dataSource" /> <property name="jpaDialect" ref="jpaDialect" /> <property name="jpaProperties"> <util:properties location="classpath:META-INF/config/jta.properties" /> </property> <property name="jpaVendorAdapter"> <bean class="${config.jpa.vendorAdapter}"> <property name="databasePlatform" value="${config.db.platform}" /> </bean> </property> </bean> <!-- Transaction Manager --> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory"> <property name="jpaDialect" ref="jpaDialect" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- Spring Data JPA--> <jpa:repositories base-package="fr.opensagres.dao"> <jpa:repository id="userDao" /> </jpa:repositories> </beans>
Here explanation of this Spring file :
- At first we load a properties file META-INF/config/config.properties :
<context:property-placeholder location="classpath*:META-INF/config/config.properties" />
means that the bundle load 3 properties from META-INF/config/config.properties.
- config.db.platform: the database platform. (ex: config.db.platform=org.eclipse.persistence.platform.database.DerbyPlatform).
- config.jpa.vendorAdapter: the JPA VendorAdapter. (ex: config.jpa.vendorAdapter=org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter).
- config.jpa.dialect: the JPA Dialect. (ex: config.jpa.dialect=org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect).
This file is not stored in the fr.opensagres.dao.jpa bundle it is stored in an OSGi fragment fr.opensagres.dao.jpa.eclipselink which host this config.properties file to configure the JPA with EclipseLink and Derby dialect. Using a config.properties file in OSGi context it’s a good idea because the JPA Dao Implementation bundle fr.opensagres.dao.jpa have none dependency to the
- JPA Implementation. For instance if you wish using Hibernate you can create an other OSGi fragment with config.properties which configures the Hibernate dialect, the Vendor Adapter, etc…
- Database dialect.
- After we create the bean jpaDialect like this :
<bean id="jpaDialect" class="${config.jpa.dialect}" />
here the jpaDialect bean is created by using the property config.jpa.dialect (ex: config.jpa.dialect=org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect) of the config.properties file.
- The entity manager is created like this:
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> <property name="persistenceUnitName" value="userUnit" /> <property name="dataSource" ref="dataSource" /> <property name="jpaDialect" ref="jpaDialect" /> <property name="jpaProperties"> <util:properties location="classpath:META-INF/config/jta.properties" /> </property> <property name="jpaVendorAdapter"> <bean class="${config.jpa.vendorAdapter}"> <property name="databasePlatform" value="${config.db.platform}" /> </bean> </property> </bean>
with those properties :
- the persistenceUnitName property is setted like this:
<property name="persistenceUnitName" value="userUnit" />
to reference the persistent unit of the META-INF/persistence.xml
- the dataSource property is setted like this:
<property name="dataSource" ref="dataSource" />
to reference a dataSource bean that we will retrieves from OSGi registry services (see module-osgi-context.xml).
- the jpaDialect property is setted like this:
<property name="jpaDialect" ref="jpaDialect" />
to reference the jpaDialect which was created by using theconfig.jpa.dialectproperty .
- The JPA properties is configured by a file config/jta.properties that it will be stored too in the OSGi fragment (like config.properties).
<property name="jpaProperties"> <util:properties location="classpath:META-INF/config/jta.properties" /> </property>
- The JPA vendor adapter is configured by using the 2 properties config.jpa.vendorAdapter and config.db.platform of the config.properties file.
<property name="jpaVendorAdapter"> <bean class="${config.jpa.vendorAdapter}"> <property name="databasePlatform" value="${config.db.platform}" /> </bean> </property>
- the persistenceUnitName property is setted like this:
- The transaction manager is configured like this :
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager" p:entityManagerFactory-ref="entityManagerFactory"> <property name="jpaDialect" ref="jpaDialect" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />
- here Spring Data JPA is used to implement on runtime the JPA Dao:
<jpa:repositories base-package="fr.opensagres.dao"> <jpa:repository id="userDao" /> </jpa:repositories>
You must fill:
- the base package of the Dao fr.opensagres.dao.
- the id of the jpa:repository which will be used to create an instance on runtime of JPA UserDao and register as Spring bean with the userDao id.
module-osgi-context.xml
Here we must configure beans that we must publish/consume from the OSgi registry services:
- retrieves a dataSource bean from the OSGi registry services.
- publish the JPA UserDao created at runtime by Spring Data JPA (with id=userDao).
Create the META-INF/spring/module-osgi-context.xml like this:
<?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:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation="http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <osgi:reference id="dataSource" interface="javax.sql.DataSource" /> <osgi:service ref="userDao" interface="fr.opensagres.dao.UserDao" /> </beans>
MANIFEST.MF
Rule7: Spring & Import-Package
MANIFEST.MF must be modified to import several packages. I find it was the more hard task to do when we have integrated Spring Data JPA in our project.
The difficulty to know which import packages must be done is that the bundle have none Java classes, so none compilation errors.
Here several rules that I’m using to know which package must be imported :
- use package of declared Spring bean/@class. For instance if you declare this bean :
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
you must import the org.springframework.orm.jpa package.
- import the domain and dao package for JPA DAO bundle implementation.
- when jpa:repository is used, the well Spring Data packages must be imported.
- for the other packages, the only solution I have found it’s to run the launch and see the ClassNotFoundException. We will see a sample in Test with missing import package section.
Modify the META-INF/MANIFEST-MF of the fr.opensagres.dao.jpa bundle like this:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Dao JPA Implementation Bundle-SymbolicName: fr.opensagres.dao.jpa Bundle-Version: 1.0.0.qualifier Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: fr.opensagres.dao, fr.opensagres.domain, javax.persistence, javax.persistence.criteria, javax.persistence.metamodel, javax.persistence.spi, org.aopalliance.aop, org.springframework.aop, org.springframework.aop.framework, org.springframework.data.domain, org.springframework.data.jpa.repository.support, org.springframework.data.repository, org.springframework.data.repository.core.support, org.springframework.orm.jpa
JPA EclipseLink&Derby Configuration Fragment
Here we will create the OSGi fragment fr.opensagres.dao.jpa.eclipselink linked to the OSGi bundle fr.opensagres.dao.jpa to configure JPA:
- using EclipseLink as JPA Implementation.
using Derby as database dialect.
config/config.properties
Create the config/config.properties like this:
db.platform=org.eclipse.persistence.platform.database.DerbyPlatform jpaVendorAdapter=org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter jpaDialect=org.springframework.orm.jpa.vendor.EclipseLinkJpaDialect
config/jta.properties
Create the config/jta.properties like this:
eclipselink.ddl-generation=drop-and-create-tables eclipselink.ddl-generation.output-mode=both eclipselink.target-database=org.eclipse.persistence.platform.database.DerbyPlatform eclipselink.weaving=false
This configuration will create the Derby Database on the first time of using of JPA EntityManager.
MANIFEST.MF
Modify the META-INF/MANIFEST-MF of the fr.opensagres.dao.jpa.eclispelink fragment like this:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: JPA EclipseLink&Derby Configuration Bundle-SymbolicName: fr.opensagres.dao.jpa.eclipselink Bundle-Version: 1.0.0.qualifier Fragment-Host: fr.opensagres.dao.jpa;bundle-version="1.0.0.qualifier" Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: org.apache.derby.jdbc, org.springframework.orm.jpa.vendor;version="3.0.5.RELEASE" Require-Bundle: org.eclipse.persistence.antlr;bundle-version="2.3.0", org.eclipse.persistence.asm;bundle-version="2.3.0", org.eclipse.persistence.core;bundle-version="2.3.0", org.eclipse.persistence.jpa;bundle-version="2.3.0"
Datasource Bundle
The bean of the fr.opensagres.dao.jpa needs a dataSource bean. We will create fr.opensagres.data.datasource bundle which will publish in teh OSGi services registry a Datasource of the Derby Database.
Create OSGi bundle fr.opensagres.data.datasource.
module-context.xml
Create the META-INF/spring/module-context.xml like this :
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="org.apache.derby.jdbc.EmbeddedDriver" /> <property name="url" value="jdbc:derby:target/derbydb;create=true" /> <property name="initialSize" value="5" /> <property name="maxActive" value="50" /> </bean> </beans>
Here we create a datasource with Commons DBCP on a Derby database.
module-osgi-context.xml
Create the META-INF/spring/module-context.xml to publish the datasource like this :
<?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:osgi="http://www.springframework.org/schema/osgi" xsi:schemaLocation="http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi-1.0.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <osgi:service ref="dataSource" interface="javax.sql.DataSource" /> </beans>
MANIFEST.MF
Modify the META-INF/MANIFEST-MF of the fr.opensagres.data.datasource bundle like this:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: Datasource Bundle-SymbolicName: fr.opensagres.data.datasource Bundle-Version: 1.0.0.qualifier Bundle-RequiredExecutionEnvironment: J2SE-1.5 Import-Package: org.apache.commons.dbcp
Run
At this step we can create the OSGi launch to use the JPA Dao Implementation. To do that duplicate the Simple OSGi Client – Mock Dao:
Image may be NSFW.
Clik here to view.
Rename the duplicated launch to Simple OSGi Client – JPA Dao:
Image may be NSFW.
Clik here to view.
- unselect the fr.opensagres.dao.mock.
- select the fr.opensagres.dao.jpa and set Auto-Start to true.
- select the fr.opensagres.dao.jpa.eclipselink.
- select the fr.opensagres.data.datasource and set Auto-Start to true.
- click on Add Required Bundles button to add required bundles.
Image may be NSFW.
Clik here to view.
If you run the launch, you will see on the console:
... org.springframework.beans.factory.support.DefaultListableBeanFactory@1c18a4c: defining beans [DataInjector,userService]; root of factory hierarchy org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=fr.opensagres.dao.UserDao)] unavailable
which means that the DataInjector bean of the fr.opensagres.data.injector consumes UserService but UserDao is not available (the JPA UserDao created by Spring Data JPA).
But you will see after:
Call: CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(15), PRIMARY KEY (SEQ_NAME)) Query: DataModifyQuery(sql="CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(15), PRIMARY KEY (SEQ_NAME))") 4453 [SpringOsgiExtenderThread-10] INFO org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean - Publishing service under classes [{fr.opensagres.dao.UserDao}]
which means that the JPA UserDao and publish on OSGi services registry was created with success.
So the problem here is that the creation of the JPA UserDao takes times and DataInjector tries to consume the UserService before than JPA UserDao is created and published.
The problem is because of cardinality= »0..1″ and timeout= »1000″ used in the Spring file of the bundle fr.opensagres.services.impl:
<!-- Consume UserDao from the OSGi services registry --> <osgi:reference id="userDao" interface="fr.opensagres.dao.UserDao" cardinality="0..1" timeout="1000" />
Modify the fr.opensagres.services.impl/META-INF/spring/module-osgi-context.xml like this:
<!-- Consume UserDao from the OSGi services registry --> <osgi:reference id="userDao" interface="fr.opensagres.dao.UserDao" />
Run the launch and you will see on the console:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'DataInjector' defined in URL [bundleentry://16.fwk12097592/META-INF/spring/module-context.xml]: Invocation of init method failed; nested exception is org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=fr.opensagres.services.UserService)] unavailable ... Caused by: org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=fr.opensagres.services.UserService)] unavailable
Here the problem is that UserService is not available because the JPA UserDao takes times ans UserService is not published when DataInjector tries to consume it. The problem is because of cardinality= »0..1″ and timeout= »1000″ used in the Spring file of the bundle fr.opensagres.data.injector:
<!-- Consume UserService from the OSGi services registry --> <osgi:reference id="userService" interface="fr.opensagres.services.UserService" cardinality="0..1" timeout="1000" />
Modify the fr.opensagres.data.injector/META-INF/spring/module-osgi-context.xml like this:
<!-- Consume UserService from the OSGi services registry --> <osgi:reference id="userService" interface="fr.opensagres.services.UserService" />
Run the OSGi launch, you will see at first on the console the error :
org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=fr.opensagres.services.UserService)] unavailable ... at fr.opensagres.simpleclient.FindAllUsersThread.displayUsers(FindAllUsersThread.java:45) at fr.opensagres.simpleclient.FindAllUsersThread.run(FindAllUsersThread.java:29)
This error occurs teh first time when the FindAllUsersThread thread tries to consume UserService (JPA UserDao creation takes time).
After you will see on the console :
... Call: CREATE TABLE SEQUENCE (SEQ_NAME VARCHAR(50) NOT NULL, SEQ_COUNT DECIMAL(15), PRIMARY KEY (SEQ_NAME)) ... User [Angelo Zerr] User [Pascal Leclercq] User [Amine Bousta] User [Mickael Baron] User [Jawher Moussa] User [Arnaud Cogoluegnes] User [Lars Vogel] User [Olivier Gierke] User [Tom Schindl] User [Wim Jongman]
The Database is created and the FindAllUsersThread thread can consumes the UserService which use the JPA User Dao!
Test with missing import package
Here we will see a sample with ClassNotFoundException when import packag eis not done correctly. Remove the import package org.aopalliance.aop in the MANIFEST.MF of the bundle fr.opensagres.dao.jpa and run the launch:
You will see on the console this error :
Exception in thread "SpringOsgiExtenderThread-8" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.osgi.service.exporter.support.OsgiServiceFactoryBean#0': Invocation of init method failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDao': FactoryBean threw exception on object creation; nested exception is java.lang.NoClassDefFoundError: org.aopalliance.aop.Advice not found from bundle [fr.opensagres.dao.jpa]
This error means that you must import the package org.aopalliance.aop in the fr.opensagres.dao.jpa bundle.
Conclusion
In this article we have implemented UserDao with JPA by using Spring Data JPA, EclipseLink and Derby. The big difficulty to do that in an OSGi context is to import the well packages in the well bundles. We have seen how to Spring Data JPA avoids coding the implementation of the DAO with JPA just by declaring the jpa:repository (very easy!) :
<!-- Spring Data JPA--> <jpa:repositories base-package="fr.opensagres.dao"> <jpa:repository id="userDao" /> </jpa:repositories>
In the next article [step7], we will see how it’s very easy with Spring Data JPA to :
- manage pagination and sort.
- manage service with some basic criteria.