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

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


Dans le billet précédant [step3] nous avons mis en place les 3 bundles OSGi Client org.dynaresume.simpleosgiclient, Services org.dynaresume.services et Domain org.dynaresume.domain. Le service UserService est récupéré via la factory de services ServicesFactory qui est un singleton. OSGi met en avant le fait que l’on puisse lancer/stopper des bundles à chaud sans devoir arrêter le conteneur OSGi. Nous verrons dans ce billet que le lancement/arrêt de nos bundles est à ce stade obsolète et par la suite comment y remédier en utilisant le registre de services OSGi. Autrement dit ce billet aborde le registre de services OSGi en expliquant comment fournir/consommer le service UserService via ce registre. Je vous conseille de lire La plate-forme dynamique de services OSGi pour plus d’informations sur la notion de services OSGi.

Voici un schéma de ce que nous allons effectuer dans ce billet :

Ce schéma montre que :

  • le bundle client org.dynaresume.simpleosgiclient consomme toutes les 5 sec le services UserService via la Thread FindAllUsersThread.
  • la factory de services ServicesFactory a disparu. L’instance service UserServive est enregistré et récupéré via le registre de services OSGi (en utilisant ServiceTracker).

Dans ce billet nous montrerons pas à pas comment nous arrivons au choix de conception décrit ci-dessus :

  • Dans la section start/stop bundle, le bundle client appele via une Thread toutes les 5 sec le service UserService via la factory ServicesFactory. Nous montrerons qu’avec le singleton ServicesFactory, l’arrêt du bundle services est obsolète. Vous pouvez télécharger l’ensemble des projets expliqués dans cette section sur org.dynaresume_step4-thread.zip.
  • Dans la section OSGi Services Registry, le bundle service et client utilisent le registre de services OSGi pour fournir/consommer le service UserService. Nous montrerons qu’avec l’utilisation du registre de services OSGi, l’arrêt du bundle service a une influence. Vous pouvez télécharger l’ensemble des projets expliqués dans cette section sur org.dynaresume_step4-osgiservicesregistry.zip.
  • Dans la section ServiceTracker, le ServiceTracker OSGi est utilisé dans le bundle client pour consommer le service UserService enregistré (par le bundle service) dans le registre de services OSGi. Ce tracker évite de solliciter tout le temps le registre de services OSGi pour récupérer le service UserService. Vous pouvez télécharger l’ensemble des projets expliqués dans cette section sur org.dynaresume_step4-servicetracker.zip.

I .start/stop bundle

Avec OSGi, il est possible de lancer/stopper (commande start/stop), installer/désinstaller (commande install/uninstall) des bundles. A mes débuts d’OSGi je pensais par exemple que l’arrêt (ou la désinstallation) d’un bundle déchargeait les classes et que l’on pouvait ainsi arrêter n’importe quel bundle. Mais ce n’est pas le cas et nous allons montrer par l’exemple ci dessous que l’arrêt ou la desinstallation d’un bundle n’a aucun effet avec notre architecture mise en place à ce stade.

Dans cette section, le bundle client org.dynaresume.simpleosgiclient va afficher toutes les 5 secondes dans la console OSGi, la liste de Users via le service UserService récupérer par la factory ServicesFactory. Nous verrons que l’arrêt ou la désinstallation du bundle service org.dynaresume.services n’aurra aucun effet une fois qu’il est lancé (la liste des Users continuera à s’afficher). Vous pouvez télécharger org.dynaresume_step4-thread.zip qui contient le code expliqué ci-dessous.

Voici un schéma de ce que nous allons effectuer dans cette section :

Ce schéma montre que :

  • l’appel de de UserService #findAllUsers() s’effectue via la Thread org.dynaresume.simpleosgiclient.internal.FindAllUsersThread. Cette Thread appelera toutes les 5 sec le service.
  • cette Thread est initialisée et lancée (FindAllUsersThread#start()) dans la méthode start du BundleActivator.
  • cette Thread est arrétée (FindAllUsersThread#interrupt()) dans la méthode stop du BundleActivator.

Nous utilisons une Thread pour appeler le service UserService car si nous avions appelé directement le service UserService toutes les 5 sec dans la méthode start du BundleActivator, le bundle serait resté toujours à l’état start.

I-A .FindAllUsersThread/ServicesFactory

Créez la classe org.dynaresume.simpleosgiclient.internal.FindAllUsersThread comme suit :

package org.dynaresume.simpleosgiclient.internal;

import java.util.Collection;

import org.dynaresume.domain.User;
import org.dynaresume.services.ServicesFactory;
import org.dynaresume.services.UserService;

public class FindAllUsersThread extends Thread {

	private static final long TIMER = 5000;

	@Override
	public void run() {
		while (super.isAlive()) {

			try {
				// 1. Get UserService
				UserService userService = getUserService();
				if (userService != null) {
					// 2. Display users by using UserServive
					displayUsers(userService);
				}
			} catch (Throwable e) {
				e.printStackTrace();
			} finally {
				try {
					sleep(TIMER);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private UserService getUserService() {
		System.out
				.println("--- Get UserService from singleton ServicesFactory ---");
		return ServicesFactory.getInstance().getUserService();
	}

	private void displayUsers(UserService userService) {
		Collection<User> users = userService.findAllUsers();

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

Dans ce cas ci le service UserService est récupéré par la factory ServicesFactory :

ServicesFactory.getInstance().getUserService()

I-B .Activator client – FindAllUsersThread/ServicesFactory

Ici nous allons initialiser et lancer la Thread FindAllUsersThread dans le BundleActivator client. pour cela, modifiez la classe org.dynaresume.simpleosgiclient.internal.Activator comme suit :

package org.dynaresume.simpleosgiclient.internal;

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

public class Activator implements BundleActivator {

	private FindAllUsersThread findAllUsersThread = null;

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

		// Start Thread which call UserService#findAllUsers();
		findAllUsersThread = new FindAllUsersThread();
		findAllUsersThread.start();
	}

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

		// Stop Thread which call UserService#findAllUsers();
		findAllUsersThread.interrupt();
		findAllUsersThread = null;
	}

}

Relancez (via OSGi DynaResume) Equinox et la console OSGi doit afficher toutes les 5 sec ceci :

--- Get UserService from singleton ServicesFactory ---
User [login=angelo, password=]
User [login=djo, password=]
User [login=keulkeul, password=]
User [login=pascal, password=]

Tappez ss (Short Status) dans la console pour afficher les ID des bundles, la console affiche :

id State Bundle
0 ACTIVE org.eclipse.osgi_3.5.0.v20090520
...
3 ACTIVE org.dynaresume.domain_1.0.0.qualifier
4 ACTIVE org.dynaresume.services_1.0.0.qualifier
5 ACTIVE org.dynaresume.simpleosgiclient_1.0.0.qualifier
...

Ici le bundle service org.dynaresume.services_1.0.0.qualifier est associé à l’ID 4. Stoppez ce bundle en saisissant dans la console stop 4, la console affiche :

stop 4
Stop Bundle [org.dynaresume.services]

Pour vérifier que le bundle service est bien arrêté, tappez ss, la console affiche :

id State Bundle
...
3 ACTIVE org.dynaresume.domain_1.0.0.qualifier
4 RESOLVED org.dynaresume.services_1.0.0.qualifier
...

Le bundle org.dynaresume.services_1.0.0.qualifier est à l’état RESOLVED, ce qui signifie qu’il n’est pas démarré. Cependant la console OSGi continue à afficher la liste des User toutes les 5 sec, alors que le bundle service est arrêté.

Désinstaller org.dynaresume.services via la commande uninstall 4. Pour vérifier que le bundle service est bien désinstallé, tappez ss, et vous verrez que le bundle ne s’affiche plus dans la liste.

La console OSGi continue à afficher la liste des User toutes les 5 sec, alors que le bundle service est désinstallé. Ceci montre bien que les commandes stop et uninstall ne décharge pas les classes. Mais à quoi sert les commandes start/stop et install/uninstall?

II .OSGi Services Registry

A ce stade notre service est récupéré via le singleton ServicesFactory qui rend obsolète l’arrêt ou la désinstallation du bundle services dans lequel il est. OSGi propose un mécanisme de registre de services OSGi ou n’importe quel bundle peut enregistrer (fournir) un service et n’importe quel bundle peut récupérer (consommer) un service. Grossièrement le registre de services OSGi peut être comparé à une Map dans laquelle on peut enregistrer/retrouver un service via un identifiant (généralement le nom de l’interface du service). Les services qui sont enregistrés dans le registre de services OSGi est une classe Java classique.

Dans cette section nous allons utiliser ce registre de services OSGi pour enregistrer l’instance UserServiceImpl avec comme identifiant le nom de l’interface qu’elle implémente UserService.class.getName(). Nous verrons ensuite les avantages d’utiliser ce registre de services OSGi au lieu du singleton ServicesFactory. Spring DM utilise d’ailleurs ce registre de services OSGi pour enregistrer des services déclarés dans un fichier XML spring. Vous pouvez télécharger org.dynaresume_step4-osgiservicesregistry.zip qui contient le code expliqué ci-dessous.

Voici un schéma de ce que nous allons effectuer dans cette section :

Ce schéma montre que :

II-A .Founisseur de services

Dans le bundle services org.dynaresume.services, la classe ServicesFactory à ce stade est accéssible par n’importe quel bundle pour pouvoir récupérer une instance de UserService. En utilisant le registre de services OSGi, cette classe ne doit plus être accéssible. Pour cela, déplacer la classe ServicesFactory dans le package org.dynaresume.services.internal.

Nous allons ensuite enregistrer l’instance UserServiceImpl dans le registre de services OSGi via l’identifiant UserService.class.getName(). Pour cela, modifiez la classe org.dynaresume.services.internal.Activator comme suit :

package org.dynaresume.services.internal;

import org.dynaresume.services.UserService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

public class Activator implements BundleActivator {

	private ServiceRegistration reg = null;

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

		// Register UserService instance into OSGi Services Registry
		reg = context.registerService(UserService.class.getName(),
				ServicesFactory.getInstance().getUserService(), null);
	}

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

		// Unregister UserService instance from OSGi Services Registry
		if (reg != null) {
			reg.unregister();
			reg = null;
		}
	}

}

II-B .Consommateur de services

Dans le bundle client org.dynaresume.simpleosgiclient, nous devons récupérer le service UserService dans le registre de services OSGi via l’identifiant UserService.class.getName(). Pour cela, modifiez la méthode start de la classe org.dynaresume.services.internal.Activator comme suit :

public void start(BundleContext context) throws Exception {
	System.out.println("Start Bundle ["+ context.getBundle().getSymbolicName() + "]");
	System.out.println("--- Get UserService from OSGi services registry ---");

	ServiceReference ref = context.getServiceReference(UserService.class.getName());
	if (ref != null) {
		UserService userService = (UserService) context.getService(ref);
		if (userService != null) {
			//Display Users
			Collection<User> users = userService.findAllUsers();
			for (User user : users) {
				System.out.println("User [login=" + user.getLogin() + ", password=" + user.getPassword() + "]");
			}
		} else {
			System.out.println(" Cannot get UserService=> UserService is null!");
		}
	} else {
		System.out.println(" Cannot get UserService=> ServiceReference for UserService is null!");
	}
}

Relancez (via OSGi DynaResume) Equinox et la console OSGi affiche ceci :

osgi> Start Bundle [org.dynaresume.domain]
Start Bundle [org.dynaresume.services]
Start Bundle [org.dynaresume.simpleosgiclient]
--- Get UserService from OSGi services registry ---
User [login=angelo, password=]
User [login=djo, password=]
User [login=keulkeul, password=]
User [login=pascal, password=]

Cette trace montre que le services UserService a bien été consommé par le bundle client.

II-C .start/stop bundle – OSGi Services Registry

Ici nous allons ré-utiliser la Thread FindAllUsersThread pour consommer le service UserService récupéré toutes les 5 sec du registre de services OSGi.

II-C-1 .FindAllUsersThread – OSGi Services Registry

Nous allons modifier la Thread FindAllUsersThread pour qu’elle recherche l’instance UserService dans le registry de services OSGi. Pour cela modifiez la classe org.dynaresume.simpleosgiclient.internal.FindAllUsersThread comme suit :

package org.dynaresume.simpleosgiclient.internal;

import java.util.Collection;

import org.dynaresume.domain.User;
import org.dynaresume.services.UserService;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;

public class FindAllUsersThread extends Thread {

	private static final long TIMER = 5000;

	private BundleContext context;

	public FindAllUsersThread(BundleContext context) {
		this.context = context;
	}

	@Override
	public void run() {
		while (super.isAlive()) {

			try {
				// 1. Get UserService
				UserService userService = getUserService();
				if (userService != null) {
					// 2. Display users by using UserServive
					displayUsers(userService);
				}
			} catch (Throwable e) {
				e.printStackTrace();
			} finally {
				try {
					sleep(TIMER);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private UserService getUserService() {
		System.out
				.println("--- Get UserService from OSGi services registry ---");
		ServiceReference ref = context.getServiceReference(UserService.class
				.getName());
		if (ref != null) {
			UserService userService = (UserService) context.getService(ref);
			if (userService != null) {
				return userService;
			}
			System.out
					.println(" Cannot get UserService=> UserService is null!");
		} else {
			System.out
					.println(" Cannot get UserService=> ServiceReference for UserService is null!");
		}
		return null;
	}

	private void displayUsers(UserService userService) {
		Collection<User> users = userService.findAllUsers();

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

La Thread attend dans son constructeur le contexte du Bundle :

public FindAllUsersThread(BundleContext context) {
    this.context = context;
}

qui est ensuite utilisé pour rechercher le service :

ServiceReference ref = context.getServiceReference(UserService.class.getName());

II-C-2 .Activator client – FindAllUsersThread/OSGi Services Registry

Modifiez dans le bundle client, la classe org.dynaresume.simpleosgiclient.internal.Activator pour utiliser la Thread FindAllUsersThread et lui passer le contexte du Bundle :

package org.dynaresume.simpleosgiclient.internal;

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

public class Activator implements BundleActivator {


	private FindAllUsersThread findAllUsersThread = null;

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

		// Start Thread which call UserService#findAllUsers();
		findAllUsersThread = new FindAllUsersThread(context);
		findAllUsersThread.start();
	}

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

		// Stop Thread which call UserService#findAllUsers();
		findAllUsersThread.interrupt();
		findAllUsersThread = null;
	}
}

Relancez (via OSGi DynaResume) Equinox et la console OSGi affiche ceci toutes les 5 sec :

--- Get UserService from OSGi services registry ---
User [login=angelo, password=]
User [login=djo, password=]
User [login=keulkeul, password=]
User [login=pascal, password=]

Ceci montre que le service UserService est appelé toutes les 5 sec. Arrêter le bundle services via la commande stop 4. La console OSGi affiche ceci toutes les 5 sec :

--- Get UserService from OSGi services registry ---
Cannot get UserService=> ServiceReference for UserService is null!

Relancez le bundle services via la commande start 4, la console OSGi réaffiche a nouveau la liste de Users :

--- Get UserService from OSGi services registry ---
User [login=angelo, password=]
User [login=djo, password=]
User [login=keulkeul, password=]
User [login=pascal, password=]

Ici nous avons montré que l’utilisation du registre de services OSGi permet de rendre opérationnel le lancement/arrêt du bundle services. Autrement dit si ce bundle est arrêté, le service UserService n’est plus disponible et si il est à nouveau lancé, le service UserService redevient disponible.

III .ServiceTracker

Il est aussi possible de récupérer un service via le ServiceTracker d’OSGi. Je ne vais pas rentrer dans le détail de cette classe, mais l’idée de celle-ci est qu’elle est capable de surveiller les enregistrements/désenregistrements d’un service donné. Cette classe permet d’éviter de solliciter le registre de service OSGi pour récupérer un service comme ce que nous avons fait avant :

ServiceReference ref = context.getServiceReference(UserService.class.getName());
    if (ref != null) {
        UserService userService = (UserService) context.getService(ref);
...
}

Avec le ServiceTracker, il faut l’initialiser comme ceci :

// Create and open the MovieFinder ServiceTracker
ServiceTracker userServiceTracker = new ServiceTracker(context, UserService.class .getName(), null);
userServiceTracker.open();

Puis pour récupérer le service UserService (que nous effectuons dans la Thread) il faut faire :

UserService userService = (UserService) userServiceTracker.getService(); 

Vous pouvez télécharger org.dynaresume_step4-servicetracker.zip qui contient le code expliqué ci-dessous.

III-A .Activator – ServiceTracker

Dans le bundle org.dynaresume.simpleosgiclient, importez le package org.osgi.util.tracker. Le MANIFEST.MF doit avoir son Import-Package comme suit :

Import-Package: org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.4.2"

Modifiez la classe org.dynaresume.simpleosgiclient.internal.Activator pour initialiser un ServiceTracker sur UserService et passer l’instance ServiceTracker dans la Thread FindAllUsersThread :

package org.dynaresume.simpleosgiclient.internal;

import org.dynaresume.services.UserService;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

public class Activator implements BundleActivator {

	private ServiceTracker userServiceTracker = null;

	private FindAllUsersThread findAllUsersThread = null;

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

		// Create and open the MovieFinder ServiceTracker
		userServiceTracker = new ServiceTracker(context, UserService.class
				.getName(), null);
		userServiceTracker.open();

		// Start Thread which call UserService#findAllUsers();
		findAllUsersThread = new FindAllUsersThread(userServiceTracker);
		findAllUsersThread.start();

	}

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

		// Stop Thread which call UserService#findAllUsers();
		findAllUsersThread.interrupt();
		findAllUsersThread = null;

		// Close the MovieFinder ServiceTracker
		userServiceTracker.close();

	}
}

III-B .FindAllUsersThread – ServiceTracker

Modifiez la classe org.dynaresume.simpleosgiclient.internal.FindAllUsersThread pour utiliser le ServiceTracker (au lieu du BundleContext) pour récupérer le service UserService :

package org.dynaresume.simpleosgiclient.internal;

import java.util.Collection;

import org.dynaresume.domain.User;
import org.dynaresume.services.UserService;
import org.osgi.util.tracker.ServiceTracker;

public class FindAllUsersThread extends Thread {

	private static final long TIMER = 5000;

	private ServiceTracker userServiceTracker;

	public FindAllUsersThread(ServiceTracker userServiceTracker) {
		this.userServiceTracker = userServiceTracker;
	}

	@Override
	public void run() {
		while (super.isAlive()) {

			try {
				// 1. Get UserService
				UserService userService = getUserService();
				if (userService != null) {
					// 2. Display users by using UserServive
					displayUsers(userService);
				}
			} catch (Throwable e) {
				e.printStackTrace();
			} finally {
				try {
					sleep(TIMER);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private UserService getUserService() {
		System.out
				.println("--- Get UserService from OSGi services registry with ServiceTracker ---");
		UserService userService = (UserService) userServiceTracker.getService();
		if (userService != null) {
			return userService;
		}
		System.out.println(" Cannot get UserService=> UserService is null!");
		return null;
	}

	private void displayUsers(UserService userService) {
		Collection<User> users = userService.findAllUsers();

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

La Thread attend dans son constructeur l’instance ServiceTracker :

public FindAllUsersThread(ServiceTracker userServiceTracker) {
this.userServiceTracker = userServiceTracker;
}

qui est ensuite utilisé pour rechercher le service :

UserService userService = (UserService) userServiceTracker.getService();

Relancez (via OSGi DynaResume) Equinox et la console OSGi doit afficher ceci :

--- Get UserService from OSGi services registry with ServiceTracker ---
User [login=angelo, password=]
User [login=djo, password=]
User [login=keulkeul, password=]
User [login=pascal, password=]

qui montre que le service est récupéré dans le registre de services OSGi via le ServiceTracker.

IV .Conclusion

Dans ce billet nous avons montrer comment consommer/fournir le service UserService via le registre de services OSGi avec ServiceTracker. Utiliser le registre de services OSGi permet ensuite de bénéficier du lancement/arrêt des bundles à chaud (sans devoir redémarrer le conteneur OSGi). Cette fonctionnalité est très intéressante, car elle permet par exemple d’installer et lancer un bundle « patch » qui corrigerait un bundle buggé sans devoir stopper le conteneur OSGi. Je souhaitais dans un premier temps montrer en évidence ce scénario, mais l’architecture actuelle de nos bundles ne le permet pas, car le bundle org.dynaresume.services doit être scindé en 2 bundles API et Implémentation que nous expliquerons dans le prochain billet.

Vous pouvez lire le billet suivant [step5].

Catégories :DynaResume, OSGi
  1. philippe
    mars 18, 2010 à 3:14

    Bonjour,

    Article intéressant. cependant j’ai une question: est il possible d’enregistrer plusieurs implémentations différentes de UserService sous le même identifiant UserService.class.getName() ?
    y-a-t-il une manière particulière de gérer ce cas ?

    Merci
    je continue de lire les autres steps

    Philippe

    • mars 18, 2010 à 3:29

      Bonjour philippe,

      Oui il est bien sur possible d’enregistrer plusieurs implémentations différentes de UserService sous le même identifiant UserService.class.getName(). La méthode http://www.osgi.org/javadoc/r4v42/org/osgi/util/tracker/ServiceTracker.html#getServices() permet de retourner la liste de services. Dans le cas ou on utilise getService comme ce que j’ai pu faire dans ce billet, je ne sais pas trop comment ca se comporte (il doit surement retourner le premier service?).

      Sinon il y a aussi possibilite d’enregistrer un service avec des filtres. Dans mon cas j’ai fait context.registerService(UserService.class.getName(),
      ServicesFactory.getInstance().getUserService(), null); ou le paramètre null indique qu’il n’y a aucun filtre.

      Lors de la création du ServiceTracker on peut lui mettre aussi un filtre. Je croies (mais je n’ai pas testé ca) que l’on peut faire aussi un filtre sur la version du service.

      Tout ceci mériterait de faire un billet dessus, mais je me suis concentré sur une utilisation basique de OSGi car il y a beaucoup de chose à dire avant.

      Angelo

  2. Amine B.
    décembre 31, 2011 à 12:47

    Hello
    Une petite remarque sur la chap. II-B :
    Il faut bien veiller à ce que le bundle services se lance toujours avant le bundle client pour que ce dernier trouve systématiquement le service enregistré par le premier.
    Pour cela, aller dans le menu Run > Run Configurations… , onglet « Bundles » et monter la valeur de « start level » pour le bundle services

    • décembre 31, 2011 à 1:28

      Merci Amine d’avoir soulevé ce point. Dans le cas explique dans cet article la Thread client recherche tout le temps dans le registry des services le service UserService. Donc la premiere fois il se peut que le client n’est pas acces au service UserService et a la deuxieme tentative ca marchera (le service implementation UserServiceImpl a eu le temps de s’enregistrer dans le registry).

      Angelo

  1. novembre 11, 2009 à 2:47
  2. décembre 22, 2009 à 2:20

Laisser un commentaire