Slides de la conférence donnée par Alexis Hassler et Antoine Sabot-Durand à Devoxx France 2014 sur les possibilités d'extensions de Java EE. Y sont évoqués l'utilisation de CDI et de son système d'extension ainsi que de JCA à travers le développement d'un connecteur MQTT.
14. @AlexisHassler @Antoine_SD#J3E
•CDI c’est beaucoup de choses
•Un standard de l’IoC pour Java EE et Java SE
•Un standard pour la gestion des contexts
•Un Système de gestion d’événements
•Un liant pour la plupart des spec Java EE
•Le moteur pour faire évoluer Java EE grace aux extensions
potables
•Le tout tirant partie du typage fort de Java.
Context & Dependency Injection
15. @AlexisHassler @Antoine_SD#J3E
Les principaux jalons de CDI
•Dates clés :
•Décembre 2009 : CDI 1.0
•Juin 2013 : CDI 1.1
•Avril 2014 : CDI 1.2
•été 2014 : début du développement sur CDI 2.0
•Toutes les infos sur : http://cdi-spec.org
•Implémentations :
•JBoss Weld (RI) : WildFly, JBoss EAP, Glassfish, Weblogic
•Apache OpenWebBeans : TomEE, Websphere
16. @AlexisHassler @Antoine_SD#J3E
CDI est activé par défaut dans Java EE 7
•Dans Java EE 6, il faut ajouter le fichier beans.xml au
déploiement
•Le fichier est optionnel dans EE 7 mais permet
d’assurer un fonctionnement compatible EE 6
beans.xml
17. @AlexisHassler @Antoine_SD#J3E
Bean
•En Java EE 6 et 7 tout est Managed Bean
•le managed bean est le composant de base
•il a un cycle de vie géré par le conteneur CDI
•il est injectable
•il supporte l’AOP via les intercepteur et les décorateurs
•accessible depuis des environnements non CDI
20. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
!
private HelloService service;!
!
@Inject!
public MyBean(HelloService service) {!
this.service = service;!
}!
public void displayHello() {!
display( service.hello();!
}!
}
Dependency Injection in constructor
21. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
!
private HelloService service;!
!
@Inject!
public void setService(HelloService service) {!
this.service = service;!
}!
public void displayHello() {!
display( service.hello();!
}!
}
Dependency Injection in setter
22. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
!
@Inject HelloService service;!
!
public void displayHello() {!
display( service.hello();!
}!
}
Dependency Injection in field
23. @AlexisHassler @Antoine_SD#J3E
public interface HelloService {!
public String hello();!
}!
public class FrenchHelloService implements HelloService {!
public String hello() {!
return "Bonjour tout le monde!";!
}!
}!
public class EnglishHelloService implements HelloService {!
public String hello() {!
return "Hello World!";!
}!
}
Qualifiers
25. @AlexisHassler @Antoine_SD#J3E
@French!
public class FrenchHelloService implements HelloService {!
public String hello() {!
return "Bonjour tout le monde!";!
}!
}!
!
@English!
public class EnglishHelloService implements HelloService {!
public String hello() {!
return "Hello World!";!
}!
}
Qualifiers
26. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
@Inject @French HelloService service;!
public void displayHello() {!
display( service.hello();!
}!
}!
public class MyBean {!
@Inject @English HelloService service;!
public void displayHello() {!
display( service.hello();!
}!
}
Qualifiers
28. @AlexisHassler @Antoine_SD#J3E
@Language(FRENCH)!
public class FrenchHelloService implements HelloService {!
public String hello() {!
return "Bonjour tout le monde!";!
}!
}!
@Language(ENGLISH)!
public class EnglishHelloService implements HelloService {!
public String hello() {!
return "Hello World!";!
}!
}
Qualifiers
29. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
@Inject @Language(ENGLISH) HelloService service;!
public void displayHello() {!
display( service.hello();!
}!
}!
!
public class MyBean {!
@Inject @Language(FRENCH) HelloService service;!
public void displayHello() {!
display( service.hello();!
}!
}
Qualifiers
30. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
@Inject @French !
HelloService service;!
}!
!
@French @Console @Secured!
public class FrenchHelloService implements HelloService {!
!
}
Qualifiers
31. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
@Inject @French @Console!
HelloService service;!
}!
!
@French @Console @Secured!
public class FrenchHelloService implements HelloService {!
!
}
Qualifiers
32. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
@Inject @French @Console @Secured !
HelloService service;!
}!
!
@French @Console @Secured!
public class FrenchHelloService implements HelloService {!
!
}
Qualifiers
33. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
@Inject @French @Console @Secured !
HelloService service;!
}!
!
@French @Secured!
public class FrenchHelloService implements HelloService {!
!
}
Qualifiers
35. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
!
@Inject Instance<HelloService> service;!
!
public void displayHello() {!
display( service.get().hello() );!
}!
}
Programmatic lookup
36. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
!
@Inject Instance<HelloService> service;!
!
public void displayHello() {!
if (!service.isUnsatisfied()) {!
display( service.get().hello() );!
}!
}!
}
Programmatic lookup
37. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
!
@Inject @Any Instance<HelloService> services;!
!
public void displayHello() {!
for (HelloService service : services) {!
display( service.hello() );!
}!
}!
}
Programmatic lookup
38. @AlexisHassler @Antoine_SD#J3E
public class MyBean {!
!
@Inject @Any Instance<HelloService> services;!
!
public void displayHello() {!
display( !
service.select(!
new AnnotationLiteral()<French> {})!
.get() );!
}!
}
Programmatic lookup
39. @AlexisHassler @Antoine_SD#J3E
Programmatic lookup
•L’injection programmatique permet de sélectionner un
bean au runtime en fonction de son type et de ses
qualifiers
•Comme les qualifiers sont des annotations (donc non
instantiables), il faut utiliser la classe AnnotationLiteral
pour créer une instance de l’annotation
•De même, pour palier au phénomène de type erasure
on pourra utiliser la classe TypeLiteral pour rechercher
un type paramétré
40. @AlexisHassler @Antoine_SD#J3E
Contexts
•Gestion du cycle de vie des beans
•choix du moment de la création et de la destruction des beans
•‘un singleton pour un contexte donné’
•Possibilité de créer des scopes personnalisés
•via les extensions
•CDI contexts fournis dans la specification:
•@Dependent (default)
•@RequestScoped!
•@SessionScoped!
•@ConversationScoped!
•@ApplicationScoped
56. @AlexisHassler @Antoine_SD#J3E
Extension pourquoi, comment ?
•Pour manipuler les meta données du container :
•AnnotatedType
•InjectionPoint / InjectionTarget
•BeanAttributes/Beans
•Producer
•Observer
!
•En observant les événements déclenchés par CDI lors de
l’initialisation du container
57. @AlexisHassler @Antoine_SD#J3E
Créer une extension CDI
•implémenter l’interface de marquage
javax.enterprise.inject.spi.Extension
!
•Ajouter une ou plusieurs methotdes qui « observent »
les événements déclenchés par le conteneur au
démarrage.
!
•Déclarer le service provider en ajoutant le fichier:
META-INF/services/javax.enterprise.inject.spi.Extension
contenant le nom de la classe d’extension
58. @AlexisHassler @Antoine_SD#J3E
public interface ProcessAnnotatedType<X> {
!
public AnnotatedType<X> getAnnotatedType();
public void setAnnotatedType(AnnotatedType<X> type);
public void veto();
}
!
public interface AnnotatedType<X> extends Annotated {
public Class<X> getJavaClass();
public Set<AnnotatedConstructor<X>> getConstructors();
public Set<AnnotatedMethod<? super X>> getMethods();
public Set<AnnotatedField<? super X>> getFields();
}
Par exemple
•On peut observer l’événement ProcessAnnotatedType
pour changer les informations d’un type ou forcer le
container à ignorer un type
59. @AlexisHassler @Antoine_SD#J3E
public class VetoEntity implements Extension {
/**
* Version CDI 1.0
*/
public void vetoEntityLegacy(@Observes ProcessAnnotatedType<?> pat) {
AnnotatedType at = pat.getAnnotatedType();
if (at.isAnnotationPresent(Entity.class))
pat.veto();
}
/**
* Version CDI 1.1+
*/
public void vetoEntity(@Observes @WithAnnotations({Entity.class})ProcessAnnotatedType<?> pat) {
pat.veto();
}
}
Ignorer les entités
•Une bonne pratique est d’éviter d’utiliser les entités
JPA comme bean CDI (sauf utilisation avancée).
•Cette extension montre comment exclure les entités
JPA de l’ensemble des beans découverts.
60. @AlexisHassler @Antoine_SD#J3E
Les choses à savoir…
•Une fois l’application lancée le BeanManager est en
lecture seule (pas de création de Bean au runtime)
•Ne pas confondre Bean (définition) avec Instance de Bean
!
•Les extensions peuvent aussi devenir des Beans (avec
quelques restrictions)
61. @AlexisHassler @Antoine_SD#J3E
Ajouter un Bean au container
•Il existe plusieurs façon d’ajouter un bean à ceux
découverts automatiquement au démarrage du
container CDI.
!
•La méthode la plus simple reste d’ajouter un
AnnotatedType à l’ensemble de ceux découverts par
CDI.
62. @AlexisHassler @Antoine_SD#J3E
public class HashMapAsBeanExtension implements Extension{
public void addHashMapAsAnnotatedType(@Observes BeforeBeanDiscovery bbd,BeanManager beanManager)
{
bbd.addAnnotatedType(beanManager.createAnnotatedType(HashMap.class));
}
}
Ajouter d’un Bean HashMap 1/2
•Il suffit d’observer l’événement BeforeBeanDiscovery et
ajouter un AnnotatedType créé à partir de la classe
HashMap.
•En CDI 1.1+ on peut également observer
AfterTypeDiscovery pour décider l’ajout après que tous
les types aient été inspectés
63. @AlexisHassler @Antoine_SD#J3E
@Decorator
@Priority(Interceptor.Priority.APPLICATION)
public abstract class MapDecorator implements Map{
@Inject
@Delegate
Map delegate;
@Override
public Object put(Object key, Object value) {
System.out.println("------- Putting Something in the Map -----------");
if ("key".equals(key)) {
System.out.println("==== Not adding key key ======");
return null;
}
return delegate.put(key,value);
}
}
Ajouter d’un Bean HashMap 2/2
•Une fois que la HashMap est un bean on peut appliquer
les services CDI sur celui-ci comme un décorateur.
64. @AlexisHassler @Antoine_SD#J3E
CDI 1.1 Lifecycle
Before Bean
Discovery
Process Bean
Process
Annotated
Type
Scan
Archive
Application
Running
After
Deployment
Validation
Before
Shutdown
Undeploy
Application
Process
Producer
After Bean
Discovery
Process
Injection
Target
Process
Observer
Method
Process
Injection
Point
Process Bean
Attributes
After Type
Discovery
événement unique
événements multiples
étape interne
Deployment
starts
Bean
eligibility
check
65. @AlexisHassler @Antoine_SD#J3E
Extension scheduler 1/4
•Cet exemple d’extension vient du projet Apache
Deltaspike. Le code présenté est une version simplifiée
du code d’origine.
•Il s’agit de pouvoir planifier des tâches à partir d’une
annotation @Schedule
66. @AlexisHassler @Antoine_SD#J3E
public class SchedulerExtension implements Extension {
private List<Class> foundManagedJobClasses = new ArrayList<Class>();
private Scheduler scheduler;
public <X> void findScheduledJobs(@Observes @WithAnnotations({ Scheduled.class }) ProcessAnnotatedType<X> pat) {
Class<X> beanClass = pat.getAnnotatedType().getJavaClass();
this.foundManagedJobClasses.add(beanClass);
}
public <X> void scheduleJobs(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
initScheduler(afterBeanDiscovery)
List<String> foundJobNames = new ArrayList<String>();
for (Class jobClass : this.foundManagedJobClasses) {
foundJobNames.add(jobClass.getSimpleName());
this.scheduler.registerNewJob(jobClass);
}
}
public <X> void stopScheduler(@Observes BeforeShutdown beforeShutdown) {
if (this.scheduler != null) {
this.scheduler.stop();
this.scheduler = null;
}
}
private void initScheduler(AfterBeanDiscovery afterBeanDiscovery) {
this.scheduler = new QuartzScheduler();
this.scheduler.start();
}
}
Extension scheduler 2/4 (extension code)
67. @AlexisHassler @Antoine_SD#J3E
CDI 1.1 Lifecycle
Before Bean
Discovery
Process Bean
Process
Annotated
Type
Scan
Archive
Application
Running
After
Deployment
Validation
Before
Shutdown
Undeploy
Application
Process
Producer
After Bean
Discovery
Process
Injection
Target
Process
Observer
Method
Process
Injection
Point
Process Bean
Attributes
After Type
Discovery
événement unique
événements multiples
étape interne
Deployment
starts
Bean
eligibility
check
99. @AlexisHassler @Antoine_SD#J3E
MQTT QoS
•0 = At most once
•Fire and forget
•Message loss can occur
•1 = At least once
•Acknowledged delivery
•Duplicates may occur
•2 = Exactly once
•Assured delivery
100. @AlexisHassler @Antoine_SD#J3E
MQTT Client - Subscribe
MQTT mqtt = new MQTT();
mqtt.setHost(Settings.SERVER_URL);
CallbackConnection connection = mqtt.callbackConnection();
connection.listener(new Listener() {
public void onPublish(UTF8Buffer topic, Buffer message,
Runnable ack) {
System.out.println("Message arrived on topic " + …);
ack.run();
}
});
connection.connect(new Callback<Void>() {
public void onSuccess(Void value) {
connection.subscribe(
new Topic[]{new Topic(Settings.TOPIC_NAME, QoS.AT_MOST_ONCE)},
new Callback<byte[]>() {…}
});
124. @AlexisHassler @Antoine_SD#J3E
@MessageDriven(
activationConfig = {
@ActivationConfigProperty(propertyName = "topicName",
propertyValue = "swt/Question"),
@ActivationConfigProperty(propertyName = "poolSize",
propertyValue = "10")
}
)
public class MyMqttBean implements MqttListener {
@Override
public void onMessage(Message message) {
...
}
}
Pool
125. @AlexisHassler @Antoine_SD#J3E
Pool
public void endpointActivation (MessageEndpointFactory mdbFactory,
ActivationSpec activationSpec)
throws ResourceException {
BlockingQueue<MqttMessageListener> pool
= new ArrayBlockingQueue<>(poolSize);
for (int i = 0; i < poolSize; i++) {
pool.add(mdbFactory.createEndpoint(null)));
}
...
}