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

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


In [step2] we have seen how to use Spring DM to load/unload in OSGi context Spring file (stored in META-INF/spring folder) from an OSGi bundle. In this article we will see how to:

  • publish service to OSGi services registry with declaration mean with Spring DM <osgi:service.
  • >S

  • consume service from OSGi services registry with declaration mean (to avoid using OSGi ServiceTracker) with Spring DM <osgi:reference .

We will modify our thread client FindAllUsersThread to consume a UserService which returns a list of User :

public interface UserService {

	Collection<User> findAll();

}

and displays on console this User list.

  • On consumer side (Simple Client), The UserService will be retrieved from the OSGi services registry with <osgi:reference:
    <osgi:reference id="userService" interface="fr.opensagres.services.UserService"
      cardinality="0..1" timeout="1000" />
    

    This service instance will be injected (Dependency Injection) in the FindAllUsersThread :

    <bean id="FindAllUsersThread" class="fr.opensagres.simpleclient.FindAllUsersThread"
      init-method="start" destroy-method="interrupt">
      <property name="userService" ref="userService"></property>
    </bean>
    

    and UserService will be used in the thread :

    public class FindAllUsersThread extends Thread {
    
    	private UserService userService;
    
    	public void setUserService(UserService userService) {
    		this.userService = userService;
    	}
            
            @Override
    	public void run() {
              // Consume UserService and display user list here...
            }
    }
    
  • On publish side (Services Implementation), the UserService implementation instance will be declared in Spring file as bean:
    <bean id="userService" class="fr.opensagres.services.impl.UserServiceImpl" />
    

    and this bean will be registered in the OSGi references with Spring DM <osgi:service:

    <osgi:service ref="userService" interface="fr.opensagres.services.UserService" />
    

Download

You can download eclipsespring_step3.zip which contains the following explained projects :

  • fr.opensagres.domain : OSGi bundle domain which hosts fr.opensagres.domain.User class.
  • 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.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

Domain Bundle

Create OSGi bundle fr.opensagres.domain which defines fr.opensagres.domain.User Domain class.

User Class

Create the fr.opensagres.domain.User class like this :

package fr.opensagres.domain;

public class User {

	private Long id;
	private String firstName;
	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;
	}

}

MANIFEST.MF

In the META-INF/MANIFEST-MF of the domain bundle:

  • export the fr.opensagres.domain package

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

Services Bundle API

Create OSGi bundle fr.opensagres.services which defines fr.opensagres.services.UserService Services API interface.

UserService interface

Create the fr.opensagres.services.UserService interface like this:

package fr.opensagres.services;

import java.util.Collection;

import fr.opensagres.domain.User;

public interface UserService {

	Collection<User> findAll();

}

MANIFEST.MF

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

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

Here the full content of this MANIFEST.MF:

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

Services Bundle Implementation

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

UserServiceImpl class

Create the fr.opensagres.services.impl.UserServiceImpl class like this :

package fr.opensagres.services.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

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

public class UserServiceImpl implements UserService {

	public Collection<User> findAll() {
		List<User> users = new ArrayList<User>();
		users.add(createUser(1, "Angelo", "Zerr"));
		users.add(createUser(2, "Pascal", "Leclercq"));
		users.add(createUser(3, "Amine", "Bousta"));
		users.add(createUser(4, "Mickael", "Baron"));
		users.add(createUser(5, "Jawher", "Moussa"));
		users.add(createUser(6, "Arnaud", "Cogoluegnes"));
		users.add(createUser(7, "Lars", "Vogel"));
		users.add(createUser(8, "Olivier", "Gierke"));
		users.add(createUser(9, "Tom", "Schindl"));
		users.add(createUser(10, "Wim", "Jongman"));
		return users;
	}

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

}

MANIFEST.MF

In the META-INF/MANIFEST-MF of the Services Implementation 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: Services Implementation
Bundle-SymbolicName: fr.opensagres.services.impl
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: fr.opensagres.domain,
 fr.opensagres.services

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="userService" class="fr.opensagres.services.impl.UserServiceImpl" />

</beans>

This Spring file declares the bean of the UserService implementation with userService id.

module-osgi-context.xml

At this step we must register the userService bean in the OSGi registry services with Spring DM <osgi:service. We could declare that
in the module-context.xml but it’s advice to declare the OSGi services in an another Spring file named 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 UserServiceImpl instance as UserService in the OSGi services 
		registry -->
	<osgi:service ref="userService" interface="fr.opensagres.services.UserService" />

</beans>           

Simple Client Bundle

At this step we can modify our Simple Client Bundle to consume the UserService from the OSGi services registry with Spring DM <osgi:reference to display on console the list of user.

MANIFEST.MF

Modify the META-INF/MANIFEST-MF of the Client 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: Simple Client
Bundle-SymbolicName: fr.opensagres.simpleclient
Bundle-Version: 1.0.0.qualifier
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Import-Package: fr.opensagres.domain,
 fr.opensagres.services

FindAllUsersThread class

Modify the fr.opensagres.simpleclient.FindAllUsersThread like this :

package fr.opensagres.simpleclient;

import java.util.Collection;

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

public class FindAllUsersThread extends Thread {

	private static final long TIMER = 5000;

	private UserService userService;

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

	public UserService getUserService() {
		return userService;
	}

	@Override
	public void run() {
		while (!super.isInterrupted()) {
			try {
				// 1. Get UserService
				UserService userService = getUserService();
				// 2. Display users by using UserServive
				displayUsers(userService);
			} catch (Throwable e) {
				e.printStackTrace();
			} finally {
				try {
					if (!super.isInterrupted())
						sleep(TIMER);
				} catch (InterruptedException e) {
					e.printStackTrace();
					Thread.currentThread().interrupt();
				}
			}
		}
	}

	private void displayUsers(UserService userService) {
		Collection<User> users = userService.findAll();
		for (User user : users) {
			System.out.println("User [" + user.getFirstName() + " "
					+ user.getLastName() + "]");
		}
	}

	@Override
	public synchronized void start() {
		System.out.println("Start Thread FindAllUsersThread");
		super.start();
	}

	@Override
	public void interrupt() {
		System.out.println("Interrupt Thread FindAllUsersThread");
		super.interrupt();
	}

}

You can notice, that :

  • FindAllUsersThread has a setter for UserServive which will be injected by Spring.
  • There is none test with userService which could be null to avoid NullPointerException. But we will see that it is never null.

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 thread 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="FindAllUsersThread" class="fr.opensagres.simpleclient.FindAllUsersThread"
		init-method="start" destroy-method="interrupt">
		<property name="userService" ref="userService"></property>
	</bean>

</beans>

Run OSGi launch

Our workspace looks like this :

and we can start playing with our bundles.

Problem with fr.opensagres.services.impl not started

Select the new 3 bundles :

  • fr.opensagres.domain
  • fr.opensagres.services
  • fr.opensagres.services.impl

in the Simple OSGi client.launch :

If we follow the rule 3 (Bundles which hosts XML Spring must be configured with Auto-Start=true), fr.opensagres.services.impl should be configured with Auto-Start=true. We will do that at the end of this article, but for the moment, don’t that to see several problems.

If you start the OSGi launch, you will see that thread is not created and last logs are :

422  [SpringOsgiExtenderThread-1] INFO  org.springframework.osgi.extender.internal.dependencies.startup.DependencyServiceManager  - Adding OSGi service dependency for importer [&userService] matching OSGi filter [(objectClass=fr.opensagres.services.UserService)]
422  [SpringOsgiExtenderThread-1] INFO  org.springframework.osgi.extender.internal.dependencies.startup.DependencyServiceManager  - OsgiBundleXmlApplicationContext(bundle=fr.opensagres.simpleclient, config=osgibundle:/META-INF/spring/*.xml) is waiting for unsatisfied dependencies [[&userService]]

that it means that Spring DM is searching userService without finding it. We have 2 problems :

  • The FindAllUsersThread is never started : the message « Start Thread FindAllUsersThread » is not displayed.
  • The FindAllUsersThread is never run: users are never displayed.

if you type ss :

...
9	RESOLVED    fr.opensagres.domain_1.0.0.qualifier
10	RESOLVED    fr.opensagres.services_1.0.0.qualifier
11	RESOLVED    fr.opensagres.services.impl_1.0.0.qualifier
...

you will see our bundles are not started.
If you start with OSGi console the fr.opensagres.services.impl (in my case 11 is the identifier of this bundle)

start 11

You will see on the console this log :

52969 [SpringOsgiExtenderThread-4] INFO  org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean  - Found mandatory OSGi service for bean [userService]
Start Thread FindAllUsersThread
52969 [SpringOsgiExtenderThread-4] INFO  org.springframework.osgi.context.support.OsgiBundleXmlApplicationContext  - Publishing application context as OSGi service with properties {org.springframework.context.service.name=fr.opensagres.simpleclient, Bundle-SymbolicName=fr.opensagres.simpleclient, Bundle-Version=1.0.0.qualifier}
52969 [SpringOsgiExtenderThread-4] INFO  org.springframework.osgi.extender.internal.activator.ContextLoaderListener  - Application context successfully refreshed (OsgiBundleXmlApplicationContext(bundle=fr.opensagres.simpleclient, config=osgibundle:/META-INF/spring/*.xml))
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]

And we can notice that our Simple Client bundle consumes the UserService. So to resolve the lock problem (Spring DM searching userService without finding it),
the Auto-Start of fr.opensagres.services.impl must be setted to true. But don’t change that for the moment.

Rule 4: cardinality= »0…1″ on consumer bundle

To resolve the first problem : « The FindAllUsersThread is never started », we must change the cardinality of the <osgi:reference like this:

<osgi:reference id="userService" interface="fr.opensagres.services.UserService" cardinality="0..1" />

By default the value is cardinality= »1..1″ which means that service is required. In our case, it’s not a good idea because it locks the start of the thread. Using cardinality= »0..1″ means that service is optionnal.

If you run the Simple Client launch, you will see on the console the log :

Start Thread FindAllUsersThread

Our Thread is started, but FindAllUsersThread#run() displays nothing.

Rule 5: timeout= »1000″ on consumer bundle

At this step, FindAllUsersThread#run() displays nothing because when userService is consumed :

Collection<User> users = userService.findAll();

There is a lock. To resolve this problem, timeout must be declared :

<osgi:reference id="userService" interface="fr.opensagres.services.UserService"
	cardinality="0..1" timeout="1000" />

If you run the Simple Client launch, you will see on the console the log:

407  [SpringOsgiExtenderThread-2] INFO  org.springframework.osgi.extender.internal.activator.ContextLoaderListener  - Application context successfully refreshed (OsgiBundleXmlApplicationContext(bundle=fr.opensagres.simpleclient, config=osgibundle:/META-INF/spring/*.xml))
org.springframework.osgi.service.ServiceUnavailableException: service matching filter=[(objectClass=fr.opensagres.services.UserService)] unavailable
	at org.springframework.osgi.service.importer.support.internal.aop.ServiceDynamicInterceptor.getTarget(ServiceDynamicInterceptor.java:419)

And every 5 seconds, you will see the org.springframework.osgi.service.ServiceUnavailableException. If you start the fr.opensagres.services.impl, you will see the users list on the console.

Auto-Start=true for fr.opensagres.services.impl

At this step we have managed the case when service is not available. We can set Auto-Start=true for fr.opensagres.services.impl.

If you run the OSGi launch, you will see the users list on the console every 5 seconds:

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 explained how to develop a bundle client which consumes a service with Spring DM and seen how to :

  • publish service to OSGi services registry with declaration mean with Spring DM <osgi:service.
  • consume service from OSGi services registry with declaration mean with Spring DM <osgi:reference .

Here rules with Spring DM that we have seen :

  1. Rule 4: cardinality=”0…1″ on consumer bundle.
  2. Rule 5: timeout=”1000″ on consumer bundle.

In [step4] we will add the DAO layer with a Mock implementation (not with JPA implementation) and I will explain how this Mock DAO helped us in our project to share the work :

  • one developer worked on the business service and application with Mock DAO.
  • one developer worked on the JPA DAO by integrating Spring Data JPA+Eclipselink.
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 :