JavaBeans - Université de Pau et des Pays de l`Adour
Transcription
JavaBeans - Université de Pau et des Pays de l`Adour
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/