Accueil > Spring, Spring DM > Eclipse RCP/RAP with Spring DM, Spring Data JPA and Remoting [step4]

Eclipse RCP/RAP with Spring DM, Spring Data JPA and Remoting [step4]


In [step3] we have developed a Simple Client bundle which consumes the UserService from the OSGi services registry to display a list of User in the console. So we have seen how to:

  • consume service with Spring DM <osgi:reference.
  • publish service with Spring DM <osgi:service.

At this step we have a service layer. Now we can add Dao layer (Some people prefer merge services/dao layer, but in our case I prefer do that to have a facade and it will help us when remoting with REST will be done) to retrieve User data from Database. To do that we will use :

Before doing that, I would like show you an idea that we have done in our Eclipse RCP/RAP XDocReport application. We are 2 developers and when we wanted to integrate JPA as Dao implementation we would like continue to work together on 2 tasks :

  • one developer integrates Dao JPA implementation with EclipseLink and Spring Data JPA (an hard task when you don’t know very well those technologies).
  • one developer continues to develop the application: business services, UI components, etc…

The idea was been to integrate DAO API (very easy task) in our services layer and uses Mock Dao. So we have :

  • a Mock Dao implementation layer: which works with Java Map.
  • a JPA Dao implementation layer: which works with EclipseLink and Spring Data JPA.

Mock Dao was easy to develop (you will see that in this article) and once time UserDao API was finished, we can work on our own task (one task for JPA and one task for continue the development of the application) without disturb. You can tell me, yes it’s a classic mean to use Mock object.

But the second problem was Data. To inject data (ex: in our case User data) :

  • for Mock Dao, Data is managed with Java.
  • for JPA Dao, Data is managed with SQL scripts…

However JPA gives you the capability to generate Database by using the JPA annotation of the Domain classes (without SQL scripts). What is about data? So we tell us, why we cannot use ours services save method to inject data? For instance in our case we could call UserService#saveUser(User user) which uses Dao (Mock or JPA) somewhere to inject our users :

  • it will work for Mock Dao.
  • it will work for JPA Dao.

But where can we do that? The idea is very simple. We have created a new bundle « datainjector » with a DataInjector class which consumes service to inject Data. For instance in our case we could have that :

public class DataInjector {

	private UserService userService;

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public void inject() {
          userService.saveUser(new User("Angelo", "Zerr"));
          ...
        }
}

In this article we will do that :

  • add API Dao layer used in the Services Implementation.
  • implement DAo with Mock (Java Map).
  • inject User data in a « datainjector » bundle which will use the UserService.

Download

You can download eclipsespring_step4.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.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 :

  1. import those projects in a workspace.
  2. open the TargetPlatform/eclipsespring.target file and click on Set as Target Platform.
  3. select TargetPlatform/launch/Simple OSGi client.launch and Run it.

Bundles

Bundle Dao API

Create OSGi bundle fr.opensagres.dao which defines fr.opensagres.dao.UserDao DAO API interface.

UserDao interface

Create the fr.opensagres.dao.UserDao interface like this :

package fr.opensagres.dao;

import fr.opensagres.domain.User;

public interface UserDao {

	Iterable<User> findAll();
	
	User save(User user);

}

You can notice that the method returns java.lang.Iterable instead of using java.util.Collection. I have done this choice in this article, because it follows the same signature than Spring Data Dao.

MANIFEST.MF

In the META-INF/MANIFEST-MF of the Dao API bundle:

  • import the fr.opensagres.domain package
  • export the fr.opensagres.dao package

Here the full content of this MANIFEST.MF:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Dao API
Bundle-SymbolicName: fr.opensagres.dao
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: fr.opensagres.domain
Export-Package: fr.opensagres.dao

Mock Dao Bundle Implementation

Create OSGi bundle fr.opensagres.dao.mock which registers in the OSGi services registry with Spring DM <osgi:services an implementation of fr.opensagres.dao.UserDao.

MockUserDao class

Create the fr.opensagres.dao.mock.MockUserDao class like this :

package fr.opensagres.dao.mock;

import java.util.LinkedHashMap;
import java.util.Map;

import fr.opensagres.dao.UserDao;
import fr.opensagres.domain.User;

public class MockUserDao implements UserDao {

	private final Map<Long, User> users = new LinkedHashMap<Long, User>();
	private long currentId = 0;

	public Iterable<User> findAll() {
		return users.values();
	}

	public User save(User user) {
		Long id = user.getId();
		if (id == null) {
			id = generateId();
			user.setId(id);
		}
		users.put(id, user);
		return user;
	}

	public synchronized Long generateId() {
		return currentId++;
	}
}

MANIFEST.MF

In the META-INF/MANIFEST-MF of the Mock Dao Implementation bundle:

  • import the fr.opensagres.domain package
  • import the fr.opensagres.dao package

Here the full content of this MANIFEST.MF:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Dao Mock Implementation
Bundle-SymbolicName: fr.opensagres.dao.mock
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: fr.opensagres.dao,
 fr.opensagres.domain

module-context.xml

Create the Spring file 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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="userDao" class="fr.opensagres.dao.mock.MockUserDao" />

</beans>

This Spring file declares the bean of the Mock UserDao implementation with userDao id.

module-osgi-context.xml

At this step we must register the userDaoe bean in the OSGi registry services with Spring DM <osgi:service in the META-INF/spring/module-osgi-context.xml.

Create the Spring file 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">

	<!-- Publish MockUserDao instance as UserDao in the OSGi services 
		registry -->
	<osgi:service ref="userDao" interface="fr.opensagres.dao.UserDao" />

</beans>

Services API Bundle

Here we will modify the UserService to add saveUser method.

UserService interface

Modify the fr.opensagres.services.UserService like this:

package fr.opensagres.services;

import java.util.Collection;

import fr.opensagres.domain.User;

public interface UserService {

	Collection<User> findAll();

	User saveUser(User user);

}

You can notice :

  • The UserService#findAll() returns a Collection instead of Iterable. This choice will be explained when we will use thi sservice in remoting mode with REST.

Services Implementation Bundle

At this step we can modify our Services Implementation Client Bundle to consume the UserDao from the OSGi services registry with Spring DM <osgi:reference.

MANIFEST.MF

Modify the META-INF/MANIFEST-MF of the fr.opensagres.services.impl bundle:

  • import the fr.opensagres.dao package

Here the full content of this MANIFEST.MF:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Services Implementation
Bundle-SymbolicName: fr.opensagres.services.impl
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: fr.opensagres.dao,
 fr.opensagres.domain,
 fr.opensagres.services

UserServiceImpl class

Modify the fr.opensagres.services.impl.UserServiceImpl like this :

package fr.opensagres.services.impl;

import java.util.Collection;

import fr.opensagres.dao.UserDao;
import fr.opensagres.domain.User;
import fr.opensagres.services.UserService;

public class UserServiceImpl implements UserService {

	private UserDao userDao;

	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	public Collection<User> findAll() {
		return (Collection) userDao.findAll();
	}

	public User saveUser(User user) {
		return userDao.save(user);
	}

}

You can notice, that :

  • UserServiceImpl has a setter for UserDao which will be injected by Spring.
  • the UserDao#findAll() call is casted to Collection. It works with our Mock and it will work too with EclipseLink (which uses Vector).
  • There is none test with userDao which could be null to avoid NullPointerException.

module-osgi-context.xml

Here we must retrieve the UserDao from the OSGi services registry by using Spring DM <osgi:reference :

<?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">

	<!-- Consume UserDao from the OSGi services registry -->
	<osgi:reference id="userDao" interface="fr.opensagres.dao.UserDao"
		cardinality="0..1" timeout="1000" />
	
	<!-- Publish UserServiceImpl instance as UserService in the OSGi services 
		registry -->
	<osgi:service ref="userService" interface="fr.opensagres.services.UserService" />

</beans>           

Once the service is retrieved from the OSGi services registry, Spring DM register it local (bundle) ApplicationContext this instance with userDao id bean. You can notice that we have used at this step cardinality= »0..1″ timeout= »1000″ but it’s a bad idea because services need Dao layer to work (the client bundle need too the UserService, but it can try several time to retrieve the service). We will see the problem when we will integrate Spring Data JPA and DataInjector.

module-context.xml

At this step we have the UserDao and you can declare with Spring classic mean, the bean which create the instance of the services implementation and inject the UserDao :

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

	<bean id="userService" class="fr.opensagres.services.impl.UserServiceImpl">
		<property name="userDao">
			<ref bean="userDao" />
		</property>
	</bean>

</beans>

Data Injector bundle

Create OSGi bundle fr.opensagres.data.injector which is used to inject User data. This bunlde consumes the UserService from the OSGi services registry with Spring DM <osgi:reference to call :

User UserService #saveUser(User user)

to inject User data. This bundle is used in this case to populate static map of the MockUserDao, but it will work too with JPA where it’s possible to create Database too.
This bundle avoid creating SQL scripts to inject data and it works for Mock DAO and JPA Dao.

DataInjector class

Create the fr.opensagres.data.injector.DataInjector class like this :

package fr.opensagres.data.injector;

import fr.opensagres.domain.User;
import fr.opensagres.services.UserService;

public class DataInjector {

	private UserService userService;

	public void setUserService(UserService userService) {
		this.userService = userService;
	}

	public void inject() {
		userService.saveUser(createUser("Angelo", "Zerr"));
		userService.saveUser(createUser("Pascal", "Leclercq"));
		userService.saveUser(createUser("Amine", "Bousta"));
		userService.saveUser(createUser("Mickael", "Baron"));
		userService.saveUser(createUser("Jawher", "Moussa"));
		userService.saveUser(createUser("Arnaud", "Cogoluegnes"));
		userService.saveUser(createUser("Lars", "Vogel"));
		userService.saveUser(createUser("Olivier", "Gierke"));
		userService.saveUser(createUser("Tom", "Schindl"));
		userService.saveUser(createUser("Wim", "Jongman"));
	}

	private User createUser(String firstName, String lastName) {
		User user = new User();
		user.setFirstName(firstName);
		user.setLastName(lastName);
		return user;
	}
}

MANIFEST.MF

In the META-INF/MANIFEST-MF of the Data Injector bundle:

  • import the fr.opensagres.domain package
  • import the fr.opensagres.services package

Here the full content of this MANIFEST.MF:

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Data Injector
Bundle-SymbolicName: fr.opensagres.data.injector
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: fr.opensagres.domain,
 fr.opensagres.services

module-osgi-context.xml

Here we must retrieved the UserService from the OSGi services registry by using Spring DM <osgi:reference :

<?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">

	<!-- Consume UserService from the OSGi services registry -->
	<osgi:reference id="userService" interface="fr.opensagres.services.UserService" />

</beans>           

Once the service is retrieved from the OSGi services registry, Spring DM register it local (bundle) ApplicationContext this instance with userService id bean.

module-context.xml

At this step we have the UserService and you can declare with Spring classic mean, the bean which create the instance of the DataInjector and inject the UserService :

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

	<bean id="DataInjector" class="fr.opensagres.data.injector.DataInjector"
		init-method="inject" >
		<property name="userService" ref="userService"></property>
	</bean>

</beans>

Run OSGi launch

Our workspace looks like this :

and we can start playing with our bundles.

Select the new 3 bundles :

  • fr.opensagres.dao
  • fr.opensagres.dao.mock and set Auto-Start to true
  • fr.opensagres.data.injector and set Auto-Start to true.

Run the OSGi launch, and you will see on the console the User list :

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]

Conclusion

In this article, we have add UserDao Dao layer used in the UserService Services layer. The Dao layer is implemented with Mock (Java Map) and dat ais injected with a DataInjector class which uses UserService.

In [step5] :

  • we will implement our UserDao with JPA.
  • EclipseLink will be used as JPA implementation.
  • Spring Data JPA will be used to avoid coding our basic JPA query.
Catégories :Spring, Spring DM

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s

%d blogueurs aiment cette page :