Accueil > DynaResume, OSGi, Spring DM, Spring Remoting > Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step10]

Conception d’un client Eclipse RCP et serveur OSGI avec Spring DM [step10]


Dans le billet précédant [step9] nous avons mis en place Spring Remoting, coté serveur dans l’application WEB dynaresume-server. Dans ce billet nous allons mettre en place Spring Remoting coté client dans un Client sans OSGi et un Client OSGi.

Voici un schéma de ce que nous allons effectuer dans ce billet concernant le client OSGi :

Ce schéma montre que nous allons :

Client Remoting (sans OSGi)

Dans cette section nous allons créer un client Main qui appelle via HttpInvoker de Spring remoting, le service UserService exposé sur le serveur dans l’application WEB dynaresume-server. Pour cela nous allons créer un projet Java classique org.dynaresume.simplemainremotingclient :

Vous pouvez télécharger org.dynaresume_step10-spring-remoting.zip qui contient les projets expliqués ci dessous :

  • les projets dynaresume-server, org.dynaresume.domain et org.dynaresume.services du billet précédant [step9].
  • le projet Client Remoting Java classique org.dynaresume.simplemainremotingclient.

Prérequis

Pour démarrer cette section nous allons repartir des projets du billet précédant [step9] que vous pouvez trouver dans le zip org.dynaresume_step9-spring-remoting.zip qui contient les projets :

  • dynaresume-server, l’application WEB qui expose le service UserService via Spring Remoting.
  • org.dynaresume.domain qui est le bundle OSGi qui contient la classe User.
  • org.dynaresume.services qui est le bundle OSGi qui contient l’interface UserService.

Initialisation org.dynaresume.simplemainremotingclient

Ici nous allons initialiser le projet java classique org.dynaresume.simplemainremotingclient :

Spring clientContext.xml

Créez le fichier XML Spring clientContext.xml dans le répertoire src/org/dynaresume/simplemainremotingclient avec le contenu suivant :

<?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-2.5.xsd">

	<bean id="userService"
		class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
		<property name="serviceUrl">
			<value>http://localhost:8080/dynaresume-server/remoting/UserService
			</value>
		</property>
		<property name="serviceInterface">
			<value>org.dynaresume.services.UserService</value>
		</property>
	</bean>

</beans>

Cette déclaration permet de définir un bean d’ID userService et de récupérer le service UserService par HTTP en utilisant la classe org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean qui demande de renseigner :

  • l’URL du service qui dans notre cas est http://localhost:8080/dynaresume-server/remoting/UserService et qui s’effectue via la méthode HttpInvokerProxyFactoryBean#setServiceUrl(String serviceUrl) déclarativement :
    <property name="serviceUrl">
    	<value>http://localhost:8080/dynaresume-server/remoting/UserService	</value>
    </property>
  • l’interface service qui dans notre cas est org.dynaresume.services.UserService et qui s’effecue via la méthode HttpInvokerProxyFactoryBean#setServiceInterface(Class serviceInterface) déclarativement :
    <property name="serviceInterface">
    	<value>org.dynaresume.services.UserService</value>
    </property>

Client Main Java – SimpleMainRemotingClient

La classe cliente Remoting doit s’occuper de :

  1. charger le fichier XML Spring clientContext.xml :
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("org/dynaresume/simplemainremotingclient/clientContext.xml");
  2. récupérer le bean d’ID userService pour récupérer le service via remoting HTTP :
    UserService userService = (UserService)applicationContext.getBean("userService");

Pour cela, créez la classe org.dynaresume.simplemainremotingclient.SimpleMainRemotingClient comme suit :

package org.dynaresume.simplemainremotingclient;

import java.util.Collection;

import org.dynaresume.domain.User;
import org.dynaresume.services.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SimpleMainRemotingClient {

	public static void main(String[] args) {

		ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
				"org/dynaresume/simplemainremotingclient/clientContext.xml");

		UserService userService = (UserService)applicationContext.getBean("userService");
		Collection<User> users = userService.findAllUsers();

		for (User user : users) {
			System.out.println("User [login=" + user.getLogin() + ", password="
					+ user.getPassword() + "]");
		}
	}
}

Test

Relancez l’application WEB puis lancez le main de SimpleMainRemotingClient. Dans la console l’erreur suivante s’affiche :

Exception in thread "main" org.springframework.remoting.RemoteAccessException: Could not access HTTP invoker remote service at [http://localhost:8080/dynaresume-server/remoting/UserService];
...
Caused by: java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: org.dynaresume.domain.User
...
Caused by: java.io.NotSerializableException: org.dynaresume.domain.User
...

Cette erreur s’explique par le fait que la classe org.dynaresume.domain.User n’est pas sérialisable. Pour résoudre le problème il faut que cette classe implémente l’interface java.io.Serializable. Modifiez la classe org.dynaresume.domain.User comme suit.

import java.io.Serializable;

public class User implements Serializable {

	private static final long serialVersionUID = 1L;
...
}

Il est conseillé de renseigner le champs static serialVersionUID. Pour plus d’information je vous conseille de lire cette FAQ Java.

Le champs serialVersionUID est utilisé lors de la désérialization afin de s’assurer que les versions des classes Java soient concordantes. Si ce n’est pas le cas, une InvalidClassException sera levée.

Relancez l’application WEB puis lancez le main de SimpleMainRemotingClient. La console affiche le contenu suivant :

log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).
log4j:WARN Please initialize the log4j system properly.
User [login=angelo, password=]
User [login=djo, password=]
User [login=keulkeul, password=]
User [login=pascal, password=]

Cette trace montre que le service a pu être récupéré du serveur par Remoting HTTP. Le warning de log4j se résoud en ajoutant dans le répertoire src du projet le fichier log4j.properties :

log4j.rootLogger=info, con
log4j.appender.con=org.apache.log4j.ConsoleAppender
log4j.appender.con.layout=org.apache.log4j.PatternLayout
log4j.appender.con.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n

Relancez le main de SimpleMainRemotingClient. La console affiche le contenu suivant :

0 [main] INFO org.springframework.context.support.ClassPathXmlApplicationContext - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@b42cbf: display name [org.springframework.context.support.ClassPathXmlApplicationContext@b42cbf]; startup date [Wed Dec 09 16:17:50 CET 2009]; root of context hierarchy
78 [main] INFO org.springframework.beans.factory.xml.XmlBeanDefinitionReader - Loading XML bean definitions from class path resource [org/dynaresume/simplemainremotingclient/clientContext.xml]
234 [main] INFO org.springframework.context.support.ClassPathXmlApplicationContext - Bean factory for application context [org.springframework.context.support.ClassPathXmlApplicationContext@b42cbf]: org.springframework.beans.factory.support.DefaultListableBeanFactory@4aa0ce
250 [main] INFO org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@4aa0ce: defining beans [userService]; root of factory hierarchy
User [login=angelo, password=]
User [login=djo, password=]
User [login=keulkeul, password=]
User [login=pascal, password=]

Cette trace montre que log4j est configuré correctement et on peut visualiser une trace Spring qui indique que le fichier XML Spring clientContext.xml est chargé correctement et que le service exposé par le serveur est récupéré par le client Java.

PropertyPlaceholderConfigurer

A ce stade nous avons renseigné directement l’URL du service dans le fichier XML Spring clientContext.xml. Une bonne pratique est de définir dans un fichier de propriétés, les paramètres de connexion du serveur et de les utiliser ensuite dans le fichier XML Spring. Nous allons effectuer cela dans cette section, autrement dit :

  1. définir les propriétés serverName (nom du serveur), httpPort (port du serveur), contextPath (contexte de l’application WEB) dans le fichier de propriétés client.properties
  2. utiliser ces propriétés dans la définition du bean userService :
    <property name="serviceUrl">
    	<value>http://${serverName}:${httpPort}${contextPath}/remoting/UserService</value>
    </property>

Pour utiliser ces propriétés dans la définition des bean, le conteneur Spring doit charger ces propriétés. Pour cela, un bean de class org.springframework.beans.factory.config.PropertyPlaceholderConfigurer doit être définit :

<bean id="propertyConfigurer"
	class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
	<property name="location">
		<value>classpath:org/dynaresume/simplemainremotingclient/client.properties</value>
	</property>
</bean>

Cette définition indique que le fichier de propriétés client.properties stocké dans le package (classpath) org.dynaresume.simplemainremotingclient doit être chargé.

client.properties

Créez le fichier de propriétés client.properties dans le répertoire src/org/dynaresume/simplemainremotingclient du projet avec le contenu suivant :

serverName=localhost
httpPort=8080
rmiPort=1099
contextPath=/dynaresume-server

clientContext.xml

Modifiez le fichier clientContext.xml comme ceci :

<?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-2.5.xsd">

	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location">
			<value>classpath:org/dynaresume/simplemainremotingclient/client.properties
			</value>
		</property>
	</bean>

	<bean id="userService"
		class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
		<property name="serviceUrl">
			<value>http://${serverName}:${httpPort}${contextPath}/remoting/UserService
			</value>
		</property>
		<property name="serviceInterface">
			<value>org.dynaresume.services.UserService</value>
		</property>
	</bean>

</beans>

Vous pouvez remarquez l’extension classpath: utilisé dans la définition :

<property name="location">
	<value>classpath:org/dynaresume/simplemainremotingclient/client.properties</value>
</property>

Ceci indique que le fichier de propriétés est recherché dans le package org.dynaresume.simplemainremotingclient

La définition suivante avec l’extension file: :

<property name="location">
	<value>file:src/org/dynaresume/simplemainremotingclient/client.properties</value>
</property>

permet d’indiquer que le fichier de propriétés doit être chargé dans le répertoire src/org/dynaresume/simplemainremotingclient.

Relancez le main de SimpleMainRemotingClient pour vérifier que tout fonctionne correctement.

Client Remoting (avec OSGi)

Dans cette section nous allons créer un bundle OSGi qui appelle via HttpInvoker de Spring remoting, le service UserService exposé sur le serveur dans l’application WEB dynaresume-server. Pour cela nous allons créer un bundle OSGi org.dynaresume.remoting.importer.http.

Vous pouvez télécharger org.dynaresume_step10-spring-osgi-remoting.zip qui contient les projets expliqués ci dessous :

  • l’application WEB dynaresume-server du billet précédant [step9].
  • org.dynaresume.config.log4j, bundle de configuration de Log4j mis en place dans le billet [step6].
  • org.dynaresume.config.remoting.importer.http, bundle de configuration de remoting client que nous allons mettre en place dans ce billet.
  • org.dynaresume.domain, bundle domaine.
  • org.dynaresume.remoting.importer.http, bundle de remoting client que nous allons mettre en place dans ce billet.
  • org.dynaresume.services, bundle domaine.
  • org.dynaresume.simpleosgiclient, bundle client.
  • spring-target-platform, Target Platform qui est enrichie dans ces billets avec les JARs de Spring Remoting.

Architecture Full Client – Client/Serveur

Dans cette section nous allons mettre en place un bundle OSGi client qui communique avec le Serveur en utilisant le service UserService exposé par l’application WEB dynaresume-server. Pour cela nous aurrions pu développer un nouveau bundle OSGi client comme ce que nous avons pu faire avec le Client Java que nous avons mis en place. Mais l’idéal serait de pouvoir utiliser le même bundle OSGi org.dynaresume.simpleosgiclient dans 2 architectures différentes:

  • Full client (Client lourd) : c’est ce que nous avons effectué à ce stade. La couche service est hébergée sur le même poste que la couche cliente. Dans notre cas le bundle OSGi org.dynaresume.services.impl est lancé dans le même conteneur OSGi que celui du client org.dynaresume.simpleosgiclient.
  • Client/Serveur : c’est ce que nous allons effectué dans la suite de ce billet. La couche cliente appelera la couche service qui est exposé sur un serveur.

Autrement dit, mettre en place un bundle OSGi client qui fait abstraction de l’architecture Full Client – Client/Server peut devenir très intéressant, mais est ce possible? La réponse est oui! Mais est ce compliqué? La réponse est non! En effet dans notre architecture Full Client actuel, notre bundle OSGi client consomme le service UserService en le recherchant dans le registry de services OSGi. Le bundle OSGi org.dynaresume.services.impl s’occupe d’enregistrer dans le registre de services OSGi le service UserService :

<osgi:service ref="userService" interface="org.dynaresume.services.UserService" />

Ou ref= »userService » signifie que Spring recherche un bean d’ID userService qui dans notre cas actuel est une instance UserServiceImpl qui est géré par le conteneur Spring :

<bean id="userService" class="org.dynaresume.services.impl.UserServiceImpl"></bean>

Pour gérer une architecture de type Client/Serveur, nous devons remplacer le bundle org.dynaresume.services.impl qui enregistre dans le services de registres OSGi une instance UserServiceImpl par un autre bundle qui au lieu d’instancier le service, le récupère par remoting HttpInvoker :

<bean id="userService"
	class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
	<property name="serviceUrl">
		<value>http://${serverName}:${httpPort}${contextPath}/remoting/UserService
		</value>
	</property>
	<property name="serviceInterface">
		<value>org.dynaresume.services.UserService</value>
	</property>
</bean>

Pour cela nous allons crééer un nouveau bundle org.dynaresume.remoting.importer.http qui va s’occuper de récupérer le service par remoting et l’enregistrer ensuite dans le registre de services OSGi.

Prérequis

Pour démarrer cette section nous allons repartir des projets des billets [step9] et [step8] que vous pouvez trouver dans le zip org.dynaresume_step10-start-osgi.zip qui contient les projets:

  • l’application WEB dynaresume-server du billet précédant [step9].
  • org.dynaresume.config.log4j, bundle de configuration de Log4j mis en place dans le billet [step6].
  • org.dynaresume.domain, bundle domaine ou la classe User implémente java.io.Serializable.
  • org.dynaresume.services, bundle domaine.
  • org.dynaresume.simpleosgiclient, bundle client.
  • spring-target-platform, Target Platform qui est enrichi dans ces billets avec les JARs de Spring Remoting.

Bundle org.dynaresume.remoting.importer.http

Créer le bundle org.dynaresume.remoting.importer.http en créant l’Activator dans le package internal (soit org.dynaresume.remoting.importer.http.internal.Activator) et en mettant les traces start/stop :

package org.dynaresume.remoting.importer.http.internal;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;

public class Activator implements BundleActivator {

	public void start(BundleContext context) throws Exception {
		System.out.println("Start Bundle [" + context.getBundle().getSymbolicName() + "]");
	}

	public void stop(BundleContext context) throws Exception {
		System.out.println("Stop Bundle [" + context.getBundle().getSymbolicName() + "]");

	}

}

module-osgi-context.xml

Créez le fichier module-osgi-context.xml dans le répertoire META-INF/spring du bundle avec le contenu suivant :

<?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-2.5.xsd">

	<osgi:service ref="userService" interface="org.dynaresume.services.UserService" />

</beans>

module-context.xml

Créez le fichier module-context.xml dans le répertoire META-INF/spring du bundle avec le contenu suivant :

<?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-2.5.xsd">

	<bean id="userService"
		class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
		<property name="serviceUrl">
			<value>http://localhost:8080/dynaresume-server/remoting/UserService
			</value>
		</property>
		<property name="serviceInterface">
			<value>org.dynaresume.services.UserService</value>
		</property>
	</bean>

</beans>

Dépendances MANIFEST.MF

A ce stade si vous Relancez (via OSGi DynaResume) Equinox, vous aurrez plusieurs erreurs liées au manque de dépendances à Spring Remoting et à nos bundles org.dynaresume.services et org.dynaresume.domain. Les dépendances doivent être effectuées par Import Package. Pour savoir quelles sont les packages à importer dans notre bundle pour son bon fonctionnement, je n’ai rien trouvé de mieux que de lancez (via OSGi DynaResume) Equinox et de voir les erreurs dans la console OSGi. Dans le client sans OSGi nous n’avions pas besoin de se poser cette question car nous avions un ClassLoader unique et toutes les librairies Spring étaient accéssibles (mais une seule version des JARs de Spring peut cohabiter en même temps). Dans un contexte OSGi qui engendre un ClassLoader par bundle les dépendances doivent être définies explicitement dans le MANIFEST.MF via Import Package ou RequireBundle. De plus le coté déclaratif de Spring n’engendre pas de problème de compilation Java, ce qui oblige à détecter les erreur au runtime (lors du lancement du bundle).

Dans le cas ou vous souhaitez comprendre les dépendances à effectuer, veuillez vous reportez à la section Explication dépendances dans le cas contraire :

  1. ajoutez à la target Platform les bundles suivants qui proviennent de la distribution Spring DM :
    JAR
    org.springframework.web-2.5.6.A.jar
    com.springsource.javax.servlet-2.4.0.jar

    en pensant à effectuer un reload et à cocher les bundles dans le launch.

  2. ajoutez les dépendances suivantes via Import Package :
    Package Nature
    org.dynaresume.domain API DynaResume
    org.dynaresume.services API DynaResume
    org.springframework.remoting.httpinvoker Remoting
    org.springframework.aop AOP
    org.springframework.aop.framework AOP
    org.aopalliance.aop AOP
    org.springframework.remoting.support Remoting HTTP
    org.springframework.beans.factory.config PropertyPlaceholderConfigurer

Vous pouvez ensuite relancez l’application WEB et lancez (via OSGi DynaResume) Equinox et vérifier que le bundle client récupère correctement le service UserService du serveur.

Explication dépendances

Ici nous allons expliquer pas à pas les dépendances nécéssaires à effectuer via Import Package. Lancez l’application WEB. Vérifiez que le bundle org.dynaresume.remoting.importer.http est coché. Relancez (via OSGi DynaResume) Equinox et la console OSGi affiche l’erreur suivante :

Exception in thread "SpringOsgiExtenderThread-3" org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean] for bean with name 'userService' defined in URL [bundleentry://9.fwk18450577/META-INF/spring/module-context.xml]; nested exception is java.lang.ClassNotFoundException: org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean not found from bundle [org.dynaresume.remoting.importer.http]

Le bundle Spring Extender qui charge le fichier XML Spring module-context.xml ne retrouve pas la classe org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean. Pour résoudre ce problème il faut Importer le package org.springframework.remoting.httpinvoker. Mais celui-ci n’existe pas? En effet, la Target Platform n’héberge pas encore à ce stade le JAR org.springframework.web-2.5.6.A.jar. Ajoutez ce JAR à la Target Platform et pensez à effectuer un Reload.

Importer le package org.springframework.remoting.httpinvoker.

Verifiez que dans la launch, le bundle org.springframework.web est bien coché et relancez (via OSGi DynaResume) Equinox. La console OSGi affiche l’erreur suivante :

org.osgi.framework.BundleException: The bundle could not be resolved. Reason: Missing Constraint: Import-Package: javax.servlet; version="[2.4.0,3.0.0)"

Cette erreur est intéressante car elle montre que les packages javax.* ne sont pas accéssible par défaut. En effet un bundle OSGi qui contient les packages javax.* doit être ajouuté à la Target Platform. La distribution de Spring DM en fournit un appelé com.springsource.javax.servlet-2.4.0.jar. Ajoutez le JAR com.springsource.javax.servlet-2.4.0.jar à la Target Platform et pensez à effectuer un Reload.

Verifiez que dans la launch, le bundle com.springsource.javax.servlet est bien coché et relancez (via OSGi DynaResume) Equinox. La consle OSGi affiche l’erreur suivante :

Caused by: java.lang.ClassNotFoundException: org.dynaresume.services.UserService

Cette erreur s’explique par le fait que le fichier XML Spring utilise l’interface UserService et que nous devosn explicitement ajouter les dépendances au bundlex org.dynaresume.domain et org.dynaresume.services. Importer les packages org.dynaresume.domain et org.dynaresume.services.

Relancez (via OSGi DynaResume) Equinox. La console OSGi affiche l’erreur suivante :

Caused by: java.lang.IllegalArgumentException: interface org.springframework.aop.SpringProxy is not visible from class loader

Cette erreur s’explique par le fait que Spring remoting utilise le mécanisme d’AOP de Spring. Importez le package org.springframework.aop

Relancez (via OSGi DynaResume) Equinox. La console OSGi affiche l’erreur suivante :

Caused by: java.lang.IllegalArgumentException: interface org.springframework.aop.framework.Advised is not visible from class loader

Importez le package org.springframework.aop.framework

Relancez (via OSGi DynaResume) Equinox. La console OSGi affiche l’erreur suivante :

Caused by: java.lang.NoClassDefFoundError: org.aopalliance.aop.Advice not found from bundle [org.dynaresume.remoting.importer.http]

Importez le package org.aopalliance.aop.

Relancez (via OSGi DynaResume) Equinox. La console OSGi affiche l’erreur suivante :

Caused by: java.lang.ClassNotFoundException: org.springframework.remoting.support.RemoteInvocationResult

Importez le package org.springframework.remoting.support.

Relancez (via OSGi DynaResume) Equinox. La console OSGi affiche les utilisateurs du service UserService.

client.properties

Nous avons vu dans la section PropertyPlaceholderConfigurer qu’il est possible d’écrire les URL ds services en utilisant les propriétés chargées d’un fichier de propriétés. C’est ce que nous allons effectuer ici. Pour cela créez le fichier client.properties dans le répertoire src du bundle org.dynaresume.remoting.importer.http comme suit :

serverName=localhost
httpPort=8080
rmiPort=1099
contextPath=/dynaresume-server

Le fichier va être recherché dans le classpath du bundle. pour cela ajoutez le contenu XML au fichier module-context.xml :

<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location">
			<value>classpath:client.properties
			</value>
		</property>
	</bean>

Le bean UserService peut ensuite utiliser les propriétés du fichier client.properties. Modifiez la déclaration du bean userService comme suit :

<bean id="userService"
		class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
		<property name="serviceUrl">
			<value>http://${serverName}:${httpPort}${contextPath}/remoting/UserService
			</value>
		</property>
		<property name="serviceInterface">
			<value>org.dynaresume.services.UserService</value>
		</property>
	</bean>

Relancez (via OSGi DynaResume) Equinox la console OSGi affiche l’erreur suivante :

Exception in thread "SpringOsgiExtenderThread-3" org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [org.springframework.beans.factory.config.PropertyPlaceholderConfigurer] for bean with name 'propertyConfigurer' defined in URL [bundleentry://55.fwk24585668/META-INF/spring/module-context.xml]; nested exception is java.lang.ClassNotFoundException: org.springframework.beans.factory.config.PropertyPlaceholderConfigurer not found from bundle [org.dynaresume.remoting.importer.http]

Pour résoudre ce problème, Importez le package le package org.springframework.beans.factory.config.

Relancez (via OSGi DynaResume) Equinox et vous pourrez constater que le bundle client récupère le servive UserService correctement.

Fragment org.dynaresume.config.remoting.importer.http

Dans le billet [step6], nous avons vu comment configurer le fichier de propriétés log4j.properties à l’aide d’un fragment OSGi. En OSGi, une bonne pratique est de déléguer la configuration des fichiers de propriétés à des Fragments OSGi, ce qui évite de modifier le bundle OSGi qui requiert la configuration d’un fichier de propriétés. C’est ce que nous allons faire dans cette section. Nous allons créer le fragment OSGi org.dynaresume.config.remoting.importer.http qui héberge le fichier de propriété client.properties.

Pour cela, créez le fragment OSGi org.dynaresume.config.remoting.importer.http et lié le au bundle org.dynaresume.remoting.importer.http.

Déplacez le fichier de propriétés client.properties du bundle OSGi org.dynaresume.remoting.importer.http dans le répertoire src du fragment OSGi org.dynaresume.config.remoting.importer.http.

Relancez (via OSGi DynaResume) Equinox et vous pourrez constater que le bundle client récupère le servive UserService correctement.

Conclusion

Dans ce billet nous avons mis en place un client (sans et dans un contexte OSGi) capable d’appeler un service exposé par un serveur via Spring Remoting HTTP. Nous avons vu que la difficulté dans un contexte OSGi avec Spring, est d’importer les bons packages pour que celui-ci puisse fonctionner. Nous avons vu aussi qu’il est important de séparer les API aux Implémentations des services dans des bundles différents car l’implémentation d’un service peut être utilisé dans plusieurs architectures (Full Client, Client/Serveur). Nous verrons dans un des prochains billets comment gérer ceci avec des configuration de lancement.

La partie serveur est à ce stade gérée par une application WEB standard et nous verrons dans le prochain billet comment transformer cette application WEB en bundle OSGi.

Vous pouvez lire le billet suivant [step11].

  1. aucun
    décembre 18, 2009 à 11:08

    Très bon article, bonne continuation!

  2. décembre 18, 2009 à 1:14

    Bonjour,

    Merci beaucoup pour vos encouragements.

    Je suis en train de préparer le billet suivant [step11] ou le but est de transformer l’application WEB classique dynaresume-server (qui expose les services en remoting HTTP) en bundle OSGi.

    Angelo

  1. décembre 24, 2009 à 8:55
  2. janvier 7, 2012 à 11:09

Laisser un commentaire