Introduction aux composants logiciels : JavaBeans™ Master des

publicité
Introduction aux composants
logiciels : JavaBeans™
http://java.sun.com/products/javabeans/
© [email protected]
Pauware Research Group
Master des Technologies de l'Internet
Université de Pau et des Pays de l'Adour
JavaBeans Component Model
●
Inspiration de Model-View-Controller (MVC)
●
Modèle de composant “technologique” ou
industriel (monde Java)
●
Modèle de composant “canonique” permettant :
–
manipulation uniforme dans outillage :
–
Beans Development Kit (BDK)
http://java.sun.com/products/javabeans/software/bdk_download.html
–
The Bean Builder
https://bean-builder.dev.java.net/
–
Instrumentation et support pour la composabilité
(“Components are for composition”, Szyperski et al.)
MVC
●
Classes abstraites Model, View et Controller de
Smalltalk-80
●
Patron Observer de Gamma et al.
●
Classes CDocument et CView de Visual C++
●
Programmation événementielle, notion de
callback
●
Librairie Swing de Java
Note : MVC ne se limite à la programmation des interfaces
homme/machine (IHM) bien que sa création soit justifiée par cela
Architecture MVC en IHM
Model : une instance de classe Java dans lequel des
états sont maintenus, des constantes sont définies...
sans préjuger de la façon dont ces données sont
affichées
View : une ou plusieurs instances de classes graphiques
qui incarne(nt) une représentation sur écran du modèle
Controller : une instance de classe chargée d'écouter des
événements “tiers” en relation avec le système de
fenêtrage (clic souris, entrée clavier...)
Peut-on faire sans MVC ?
Agglomérer dans un même objet données graphiques et
données business (i.e. “métier”) rend les objets
difficilement maintenables. Avec MVC, les vues, qui
sont propres aux applications, peuvent changer sans
changer les modèles qui sont partagés par les
applications (réutilisation). Finalement, MVC encourage
un regroupement logique lui même engendrant une
meilleure isolation des objets aux fautes (fiabilité). En
conclusion, MVC est un bon support de la maintenabilté,
la réutilisabilité et la fiabilité
Model (analyse, conception et
programmation)
Physics
Temperature
asCelsius() : Real
asFahrenheit() : Real
asKelvin() : Real
increment()
decrement()
=(t : Temperature) : Boolean
<(t : Temperature) : Boolean
<=(t : Temperature) : Boolean
>(t : Temperature) : Boolean
>=(t : Temperature) : Boolean
«create»
Temperature()
Temperature(value : Real,unit : Symbol)
{asFahrenheit() = (asCelsius() + 32.) * 9. / 5.
asKelvin() = asCelsius() - (-273.15)}
Physics::Temperature
«refine»
Temperature
-_value : Real = 0.
-_step : Real = 0.1
«const» -Min : Real = -273.15
#LiteralCelsius : String = "°C"
#LiteralFahrenheit : String = "°F"
#LiteralKelvin : String = "°K"
+asCelsius() : Real
+asFahrenheit() : Real
+asKelvin() : Real
+increment()
+decrement()
+equals(t : Temperature) : Boolean {redefines =}
+lessThan(t : Temperature) : Boolean {redefines <}
+lessThanOrEqual(t : Temperature) : Boolean {redefines <=}
+greaterThan(t : Temperature) : Boolean {redefines >}
+greaterThanOrEqual(t : Temperature) : Boolean {redefines >=}
+asStringCelsius() : String
+asStringFahrenheit() : String
+asStringKelvin() : String
«create»
Temperature()
Temperature(value : Real,unit : Symbol)
public final class Temperature implements Cloneable {
public static final byte Celsius = 0;
public static final byte Fahrenheit = 1;
public static final byte Kelvin = 2;
protected static String[] _Literals = new String[3];
static {
_Literals[Celsius] = new String("°C");
_Literals[Fahrenheit] = new String("°F");
_Literals[Kelvin] = new String("°K");
}
private final static float Min = -273.15F; // in Celsius
private float _value; // in Celsius
private float _step;
// creation
public Temperature() {
_value = 0.F;
_step = 0.1F;
}
...
}
Le code qui précède est hétéroclite au sens où sa
forme est libre et plus précisément ne se
conforme pas au modèle JavaBeans.
Néanmoins, la classe Temperature ne comporte
aucune donnée liée à des formes et/ou
contraintes d'affichage
View et Controller
Représentation
graphique de données du
modèle (champ _value)
Données propres à la vue qui
contraint les possibilités
d'évolution des données, le
genre de données que l'on veut
afficher...
Slider assurant la connexion entre le
système de fenêtrage et le modèle
(événements temp down et temp up)
Flot d'interaction
Model
Mettre à jour l'IHM car la nouvelle valeur
de Target temperature enregistrée dans
Model n'est pas celle actuellement affichée
Slider a bougé (événement).
Voici la nouvelle valeur de
Target temperature dans
l'intervalle 40°F et 90°F
imposé par View
View
Controller
En fonction du mode
d'affichage, de ce qui est
affiché à un instant t et de
contraintes imposées à l'écran,
envoyer ces types
d'événements et des types
d'information à Model
JavaBeans et Swing
●
●
Swing dispose d'une architecture MVC particulière
(i.e. la distinction conceptuelle entre View et
Controller, contrairement à Smalltalk-80, est
“faible”) et “assez” déconnectée de JavaBeans
JavaBeans est foncièrement dédié à la fabrication
de classes Model
Note : la difficulté de compréhension de MVC
provient de son “instanciation” sur les cas
particuliers VVC (Swing sans nécessité absolue de
JavaBeans) et MMC (JavaBeans sans Swing
purement et simplement)
Paradigmes VVC et MMC
Métaphore VVC : à l'écran, si telle option est
choisie alors telle option est indisponible, si
tel choix est fait alors tel bouton doit être
inhibé (look grisé/opaque)...
●
Métaphore MMC : e.g. communication “EJB
-> Message-Driven Bean (Controller) -> EJB”
(voir cours EJB à suivre)
●
JavaBeans : principe
Modèle de composant “canonique” signifie que JavaBeans offre
un cadre structurant de programmation (patron, formatage,
accès via outil...) des composants mais le principe peut être mis
en oeuvre de façon légère. Ex. classe Run_indicator :
public interface Run_indicator_client extends java.beans.PropertyChangeListener {…}
public class Programmable_thermostat extends … implements Run_indicator_client, … {…}
public class Run_indicator implements java.io.Serializable {
…
public Run_indicator(Run_indicator_client programmable_thermostat) throws Statechart_exception {
…
propertySupport = new java.beans.PropertyChangeSupport(this);
propertySupport.addPropertyChangeListener(programmable_thermostat);
}
public void off() throws Statechart_exception {
_Run_indicator.fires(_Something_on,_Everything_off);
_Run_indicator.run_to_completion();
setStatus(_Run_indicator.current_state());
}
private void setStatus(String newValue) {
String oldValue = status;
status = newValue;
propertySupport.firePropertyChange("Run indicator status",oldValue,newValue);
}
…
}
JavaBeans, éléments clefs,
persistance
Les JavaBeans sont persistants et donc implémentent
l'interface java.io.Serializable
Attention à des champs qui référencent d'autres
JavaBeans (marqueur transient utile)
➢
Attention à la gestion de version au moment du
chargement dynamique des JavaBeans (cf. static final
long Serial_version_id = ...L;)
➢
Rappel : en stockage binaire supporté par java.io.Serializable, les champs
indiqués static et transient ne sont pas sauvegardés
JavaBeans, éléments clefs,
encodage/décodage XML
Les JavaBeans sont persistants de façon binaire (fichier
“sérializé” .ser obtenu via java.io.ObjectOutputStream
et “désérializé” via java.io.ObjectInputStream) ou de
façon textuelle grâce aux classes
java.beans.XMLEncoder et java.beans.XMLDecoder
XMLEncoder e = new XMLEncoder(new BufferedOutputStream(new FileOutputStream
("JOL_ball.xml")));
e.writeObject(_jOL_ball);
e.close();
XMLDecoder d = new XMLDecoder(new BufferedInputStream(new FileInputStream
("JOL_ball")));
JOL_ball _jOL_ball = (JOL_ball)d.readObject();
d.close();
Types de propriété
Simple ou indexée (tableau Java)
De façon orthogonale : liée (bound) et
éventuellement contrainte
●
Liée : nécessité d'une notification aux
écouteurs
●
Contrainte : possibilité de refus de
changement à la notification
Propriété liée, exemple
Dans le jeu JOL, la classe JOL_meteorite a un
champ _shape de type java.awt.Polygon. La
zone de jeu (_jOL_space) de la fenêtre globale
du jeu faisant apparaître à l'écran la météorite
doit être prévenue lorsque _shape voit ses
coordonnées modifiées (chute de la météorite
régulée par un timer) -> recoloriage
Propriété contrainte
Les objets intéressés par les changements d'état d'une
propriété contrainte peuvent poser un veto au
changement
Ils écoutent, s'ils refusent le changement, ils lèvent une
exception de type java.beans.PropertyVetoException
L'émetteur est notifié du refus dans la clause catch
(java.beans.PropertyVetoException pve) {} ou alors
c'est qu'il y a eu acceptation du changement
Propriété contrainte, exemple
Dans le jeu JOL, la classe JOL_ball a un champ _shape
de type java.awt.Polygon. La zone de jeu (_jOL_space)
de la fenêtre globale du jeu faisant apparaître à l'écran
la balle, l'empêche, contrairement aux météorites, de
descendre au delà du sol. La balle se déplace en haut,
à gauche et à droite en fonction du joueur mais se
déplace en bas (i.e., retombe), inexorablement (timer),
si le joueur ne l'utilise pas
JavaBeans, éléments clefs, type
d'écouteur
➢
Interfaces
java.beans.PropertyChangeListener
➢
java.beans.VetoableChangeListener
➢
Lien Swing : des composants Swing “de base” et “par
défaut” implémentent l'interface PropertyChangeListener
➢
La classe java.beans.PropertyChangeEvent porte les
attributs des événements
➢
JavaBeans, éléments clefs,
utilitaires
Des implémentations par défaut des interfaces incontournables
de JavaBeans permettent de gagner du temps “si l'on rentre dans
le moule standard” :
➢
java.beans.PropertyChangeSupport
➢
java.beans.VetoableChangeSupport
Exemple en multicast sans veto :
private java.beans.PropertyChangeSupport propertyChangeSupport = new
java.beans.PropertyChangeSupport(this);
public void addPropertyChangeListener(java.beans.PropertyChangeListener listener)
{propertyChangeSupport.addPropertyChangeListener(listener);}
public void removePropertyChangeListener(java.beans.PropertyChangeListener listener)
{propertyChangeSupport.removePropertyChangeListener(listener);}
JavaBeans, éléments clefs,
abonnement/désabonnement
➢
Unicast : un seul écouteur (avec veto ci-dessous)
private transient java.beans.VetoableChangeListener vetoableChangeListener = null;
public synchronized void addVetoableChangeListener(java.beans.VetoableChangeListener listener) throws
java.util.TooManyListenersException {
if(vetoableChangeListener != null) throw new java.util.TooManyListenersException();
vetoableChangeListener = listener;
}
public synchronized void removeVetoableChangeListener(java.beans.VetoableChangeListener listener) {
vetoableChangeListener = null;
}
Multicast : plusieurs écouteurs (transparent précédent)
➢
JavaBeans, éléments clefs,
notification
➢
Unicast, propriété contrainte
private void fireVetoableChangeListenerVetoableChange(java.beans.PropertyChangeEvent event) throws
java.beans.PropertyVetoException {
if(vetoableChangeListener == null) return;
vetoableChangeListener.vetoableChange(event);
}
// la balle JOL est translatée de _offset pixels en descente : down()
java.awt.Polygon shape = new java.awt.Polygon(_shape.xpoints,_shape.ypoints,_shape.npoints);
_shape.translate(0,_offset); // on descend de _offset pixels
try {
fireVetoableChangeListenerVetoableChange(new java.beans.PropertyChangeEvent
(this,"_shape",shape,_shape));
}
catch(java.beans.PropertyVetoException pve) {
_shape.translate(0,-_offset); // on remonte de _offset pixels
}
➢
Multicast sur base utilitaire, propriété liée
java.awt.Polygon shape = new java.awt.Polygon(_shape.xpoints,_shape.ypoints,_shape.npoints);
_shape.translate(0,1);
propertyChangeSupport.firePropertyChange("_shape",shape,_shape);
JavaBeans, éléments clefs,
capture
La fenêtre de jeu se déclare écouteur des mouvements de la
balle JOL et définit la méthode de l'écouteur, jusqu'ici
abstraite, informant d'un changement :
_jOL_ball.addVetoableChangeListener(new java.beans.VetoableChangeListener()
{
public void vetoableChange(java.beans.PropertyChangeEvent propertyChangeEvent)
throws java.beans.PropertyVetoException {
// handling code here...
}
}
);
JavaBeans, interaction avancée
public class JOL_ball_crashes_JOL_meteorite_event extends java.util.EventObject {
public JOL_ball_crashes_JOL_meteorite_event(JOL_ball source) {
super(source);
}
}
public interface JOL_ball_crashes_JOL_meteorite_listener extends java.util.EventListener {
void crash(JOL_ball_crashes_JOL_meteorite_event event);
}
Code se trouvant dans le détecteur d'événement (i.e. Controller) :
private transient java.util.ArrayList _jOL_ball_crashes_JOL_meteorite_listeners;
public synchronized void add_JOL_ball_crashes_JOL_meteorite_listener(JOL_ball_crashes_JOL_meteorite_listener listener) {
if(_jOL_ball_crashes_JOL_meteorite_listeners == null) _jOL_ball_crashes_JOL_meteorite_listeners = new java.util.ArrayList();
_jOL_ball_crashes_JOL_meteorite_listeners.add(listener);
}
public synchronized void remove_JOL_ball_crashes_JOL_meteorite_listener(JOL_ball_crashes_JOL_meteorite_listener listener) {
if(_jOL_ball_crashes_JOL_meteorite_listeners != null) _jOL_ball_crashes_JOL_meteorite_listeners.remove(listener);
}
private void crash(JOL_ball_crashes_JOL_meteorite_event event) {
java.util.ArrayList copy_of_jOL_ball_crashes_JOL_meteorite_listeners;
synchronized(this) {
if(_jOL_ball_crashes_JOL_meteorite_listeners == null) return;
// avoiding timing race:
copy_of_jOL_ball_crashes_JOL_meteorite_listeners = (java.util.ArrayList)_jOL_ball_crashes_JOL_meteorite_listeners.clone();
}
for(int i = 0;i < copy_of_jOL_ball_crashes_JOL_meteorite_listeners.size();i++)
((JOL_ball_crashes_JOL_meteorite_listener)copy_of_jOL_ball_crashes_JOL_meteorite_listeners.get(i)).crash(event);
}
Eléments de Java sur lesquels
s'adosse JavaBeans
«class»
java::util::EventObject
«class»
java::beans::PropertyChangeEvent
«class»
java::awt::ActionEvent
«interface»
java::util::EventListener
«interface»
java::awt::event::ActionListener
actionPerformed(ae : ActionEvent)
«interface»
javax::swing::Action
«class»
javax::swing::AbstractAction
«interface»
java::beans::PropertyChangeListener
propertyChange(pce : PropertyChangeEvent)
Documentation programmatique
Interface fondamentale java.beans.BeanInfo
●Classe utilitaire java.beans.SimpleBeanInfo
●
Principe : documenter un JavaBean X via une classe Java
héritant le plus souvent de java.beans.SimpleBeanInfo.
Cette classe nommée par convention XBeanInfo établit, à
l'exécution (pour des outils en fait) et “à la demande”
(choix de ce qui est documenté ou non) :
●
●
●
les champs et leurs propriétés (getter, setter...)
les méthodes et leurs propriétés
les événements et leurs modalités...
Mode de documentation fixé pour chaque catégorie dans la
classe XBeanInfo
●
●
Introspection, voire aussi classe java.beans.IntrospectionException
Lazy initialization
Documentation programmatique,
exemple
Bean:
public class JOL_meteorite extends com.FranckBarbier.Java._Composytor.Timer_monitor implements Serializable {
}
...
BeanInfo:
public class JOL_meteoriteBeanInfo extends SimpleBeanInfo {
// lazy initialization (la convention de nommage peut sauter car la classe du Bean est donnée)
private static BeanDescriptor getBdescriptor() {
BeanDescriptor beanDescriptor = new BeanDescriptor (JOL_meteorite.class,null);
// Here you can add code for customizing the BeanDescriptor.
return beanDescriptor;
}
// introspection pour les champs
private static PropertyDescriptor[] properties = null;
private static PropertyDescriptor[] getPdescriptor() {return properties;}
// informations récupérables au niveau instance
public BeanDescriptor getBeanDescriptor() {return getBdescriptor();}
public PropertyDescriptor[] getPropertyDescriptors() {return getPdescriptor();}
}
...
Documentation de JOL_ball,
champs en mode lazy initialization
// Property identifiers
private static final int PROPERTY__color = 0;
// Property array
private static PropertyDescriptor[] properties = new PropertyDescriptor[1];
private static PropertyDescriptor[] getPdescriptor() {return properties;}
static {
try {
properties[PROPERTY__color] = new PropertyDescriptor ( "_color", WYX_ball.class,
"get_color", null);
properties[PROPERTY__color].setDisplayName ( "_color" );
properties[PROPERTY__color].setShortDescription ( "WYX ball color" );
}
catch(IntrospectionException ie) {}
// Here you can add code for customizing the properties array.
}
public PropertyDescriptor[] getPropertyDescriptors() {return getPdescriptor();}
Compléments
La classe java.beans.Beans fournit des facilités de
contrôle (e.g., création) de JavaBeans. Ex. : instantiate
●La classe java.beans.Introspector crée
automatiquement des informations de JavaBeans (cf.
getBeanInfo)
●Interface java.beans.PropertyEditor et classe
java.beans.PropertyEditorSupport fournissent des
composants graphiques pour manipuler les champs des
JavaBeans
●La classe utilitaire
java.beans.PropertyEditorManager enregistre et
localise les éditeurs
●
PropertyEditor pe = PropertyEditorManager.findEditor(java.awt.Color); // OK car type connu
PropertyEditorManager.registerEditor
(an_instance_of_a_type.class,an_instance_of_my_property_editor.class);
Déploiement
Packaging dans fichier .jar pour déploiement avec
avec fichier manifest optionnel et fichiers divers
(.ser, icon 16x16, HTML, audio, vidéo...)
Exemple de contenu de fichier manifest : “JavaBean: True”
Exemple : glisser/lâcher Timer (composant logiciel
offert par www.netbeans.org) dans l'IDE NetBeans
➢
➢
Technologies connexes
JavaBeans Activation Framework (JAF)
➢ Génération automatique de JavaBeans à partir de sources
de données hétérogènes : classe DataHandler, interfaces
DataSource et CommanMap
● JAF s'appuie sur le format MIME pour caractériser
(typer) la nature profonde des sources de données
➢ Adresse Web de JAF :
http://java.sun.com/products/javabeans/jaf/index.jsp
JavaBeans Bridge for ActiveX
➢ ActiveX est le modèle de composants logiciels de
Microsoft
➢ http://java.sun.com/products/javabeans/software/bridge/
Téléchargement