2. JavaBeans als Komponenten • • • • • Hintergrund und Motivation Java-GUI-Elemente sind Beans Typischer Aufbau von Beans Bound properties Constrained Properties Speichern • JavaBeans sind als frühes Komponentenmodell entstanden • Objekte einfach serialisierbar (damit einfach im Netz austauschbar) • Beans-Framework ermöglicht einfache Kommunikation unter Beans • Haupteinsatz: GUI-Komponenten, Swing nutzt Technologie (ermöglicht u. a. GUI-Builder [gute/schlechte]) • Aktuelle Spezifikation: JavaBeans 1.01 • Ideen für JavaBeans Spec 2.0 (JSR-273) existieren/ ruhen • Zitat: “A Java Bean is a reusable software component that can be manipulated visually in a builder tool.” • Achtung: nicht mit Enterprise JavaBeans verwechseln Literatur: • Sun, JavaBeans 1.01 Specification, http://java.sun.com/ javase/technologies/desktop/javabeans/docs/spec.html • C. Ullenboom, Java ist auch eine Insel, 8. Auflage, Galileo Press [Abschnitt 7.4] Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 54 Erinnerung: JButton (Nutzung) public ButtonFrame() ButtonFrame() { setSize(200, 100); JButton rot = new JButton("rot"); JButton("rot"); rot.addActionListener(new MeineAction(getContentPane(), MeineAction(getContentPane(), Color.RED)); Color.RED)); add(rot, add(rot, BorderLayout.EAST); BorderLayout.EAST); JButton blau = new JButton("blau"); JButton("blau"); blau.addActionListener(new MeineAction(getContentPane(), MeineAction(getContentPane(), Color.BLUE)); Color.BLUE)); add(blau, add(blau, BorderLayout.WEST); BorderLayout.WEST); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); } public static void main(String args[]) args[]) { java.awt.EventQueue.invokeLater(new Runnable() Runnable() { public void run() run() {new {new ButtonFrame().setVisible(true);} ButtonFrame().setVisible(true);} }); } Entwicklung Prof. Dr. Stephan Kleuker Prof. Dr. Stephan Kleuker 55 Erinnerung: JButton (ActionListener) public class ButtonFrame extends JFrame { } Komponentenbasierte Software- Komponentenbasierte SoftwareEntwicklung import import import import public class MeineAction implements ActionListener { private Component komponente; komponente; private Color farbe; farbe; public MeineAction(Component komponente, komponente, Color farbe){ farbe){ this.komponente = komponente; komponente; this.farbe = farbe; farbe; } } 56 java.awt.Color; java.awt.Color; java.awt.Component; java.awt.Component; java.awt.event.ActionEvent; java.awt.event.ActionEvent; java.awt.event.ActionListener; java.awt.event.ActionListener; public void actionPerformed(ActionEvent e) { komponente.setBackground(farbe); komponente.setBackground(farbe); } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 57 Typische Beanmethoden (1/2) Typische Beanmethoden (2/2) public class Knopf { public static void main(String[] main(String[] s){ JButton jb= jb= new JButton(); JButton(); jb.addPropertyChangeListener(new PCL()); jb.setText("touch me"); me"); jb.setToolTipText("bin ne Bean"); Bean"); jb.setBackground(Color.red); jb.setBackground(Color.red); jb.setBorderPainted(false); jb.setBorderPainted(false); JFrame jf = new JFrame(); JFrame(); jf.addPropertyChangeListener(new PCL()); jf.add(jb); jf.add(jb); jf.setSize(200, 100); jf.setTitle("Knopf"); jf.setTitle("Knopf"); jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); ); jf.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE jf.setVisible(true); jf.setVisible(true); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker public class PCL implements PropertyChangeListener{ PropertyChangeListener{ public PCL() {} public void propertyChange(PropertyChangeEvent evt) evt) { System.out.println(evt.getPropertyName() System.out.println(evt.getPropertyName() +" alt:"+evt.getOldValue() alt:"+evt.getOldValue() +" neu:"+evt.getNewValue()); neu:"+evt.getNewValue()); } text alt: neu:touch me } ToolTipText alt:null neu:bin ne Bean background alt:javax.swing.plaf.ColorUIResource[r=238,... borderPainted alt:true neu:false title alt: neu:Knopf defaultCloseOperation alt:1 neu:3 foreground alt:null neu:java.awt.Color[r=0,g=0,b=0] font alt:null neu:java.awt.Font[family=Dialog,name=Dia... neu:java.awt.Font[family=Dialog,name=Dia... ancestor alt:null neu:javax.swing.JPanel[null.contentP... neu:javax.swing.JPanel[null.contentP... 58 Elementare Anforderungen an JavaBean public void setXx(<Typ> setXx(<Typ> xx) xx) { this.xx = xx; xx; } public <Typ> getXx() getXx() { xx; return xx ; } • Methoden können weiteren Code enthalten • Jedwede Typen von Variablen sind serialisierbar Prof. Dr. Stephan Kleuker Prof. Dr. Stephan Kleuker 59 Minimales Beispiel • Default-Konstruktor (keine Parameter) • Alle Exemplarvariablen (Properties, Attribute) über get- und set-Methoden erreichbar • Variable private <Typ> xx; xx; Komponentenbasierte SoftwareEntwicklung Komponentenbasierte SoftwareEntwicklung 60 import java.io.Serializable; java.io.Serializable; public class Student implements Serializable{ Serializable{ private String name; name; private int cp; cp; public Student(){} public int getCp() getCp() { return cp; cp; } public void setCp(int cp) cp) { this.cp = cp; cp; } public String getName() getName() { return name; name; } public void setName(String name) name) { this.name = name; name; } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 61 Einschub: typischer schlechter Stil Indizierte Eigenschaften • Für Eigenschaften, die in Arrays gespeichert werden, besteht zusätzlich folgende Möglichkeit (Ausschnitt) • Arrays sind für dynamische Datenmengen unflexibel • Collections sind auch nutzbar (auch serialisierbar) private String[] vorlesung; vorlesung; public String[] getVorlesung() getVorlesung() { //bekannt return vorlesung; vorlesung; } public void setVorlesung(String[] setVorlesung(String[] vorlesung) vorlesung) { //bekannt this.vorlesung = vorlesung; vorlesung; } public String getVorlesung(int index) index) { return vorlesung[index]; vorlesung[index]; } public void setVorlesung(String vl, vl, int index) index) { vorlesung[index]=vl; vorlesung[index]=vl; } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker private Set<String> Set<String> vorlesung= vorlesung= new HashSet<String>(); HashSet<String>(); mit üblichen get- und set-Methoden • In JavaBeans können weitere Methoden ergänzt werden public void weitereVorlesung(String v){ vorlesung.add(v); vorlesung.add(v); } • Leider nicht unüblich der Stil „Innereien herausreißen, verändern, Innereien wieder hinschieben“ (verletzt eigentlich Geheimnisprinzip) Student st = new Student(); st.getVorlesung().add("KomSE"); "); st.getVorlesung().add("KomSE 62 Gebundene Eigenschaften 63 import java.beans.PropertyChangeListener; java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; java.beans.PropertyChangeSupport; … private PropertyChangeSupport pcs=new PropertyChangeSupport(this); PropertyChangeSupport(this); public void setCp(int cp) cp) { int alt=this.cp; alt=this.cp; this.cp = cp; cp; pcs.firePropertyChange("cp", pcs.firePropertyChange("cp", alt, cp); cp); } public void addPropertyChangeListener(PropertyChangeListener l){ pcs.addPropertyChangeListener(l); pcs.addPropertyChangeListener(l); } • Interessenten für Änderungen müssen sich mit add… anmelden • Bei Anmeldung muss Objekt übergeben werden, das das Interface PropertychangeEvent implementiert • normales Observer-Observable-Pattern Prof. Dr. Stephan Kleuker Prof. Dr. Stephan Kleuker Beispiel-Erweiterung in Student (1/2) • Bound properties sind Exemplarvariablen, die Änderungen interessierten Beans mitteilen (Observer-Observable) • Wer Änderungen mitteilen will, – benötigt Objekt der Klasse PropertyChangeSupport – muss Methoden add/removePropertyChangeListener implementieren Komponentenbasierte SoftwareEntwicklung Komponentenbasierte SoftwareEntwicklung public void removePropertyChangeListener removePropertyChangeListener( ( PropertyChangeListener l){ pcs.removePropertyChangeListener(l); pcs.removePropertyChangeListener(l); } 64 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 65 Beispiel-Erweiterung in Student (2/2) Veto-Eigenschaften import java.beans.PropertyChangeListener; java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; java.beans.PropertyChangeEvent; public class Studentnutzer { public static void main(String[] main(String[] args) args) { Student s = new Student(); s.addPropertyChangeListener(new PropertyChangeListener() PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent e) { System.out.println("Eigenschaft "+e.getPropertyName "+e.getPropertyName() e.getPropertyName() +" alt:"+e.getOldValue()+" alt:"+e.getOldValue()+" neu:"+e.getNewValue()); neu:"+e.getNewValue()); } }); s.setCp(42); s.setCp(s.getCp()+5); s.setCp(47); Eigenschaft cp alt:0 neu:42 } Eigenschaft cp alt:42 neu:47 } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 66 Constrained und Bound Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 67 Beispiel ohne Bound Property (1/3) private VetoableChangeSupport vcs=new vcs=new VetoableChangeSupport(this); VetoableChangeSupport(this); public void setVorlesung(Set<String> setVorlesung(Set<String> vorlesung) vorlesung) { Set<String> Set<String> old = this.vorlesung; this.vorlesung; in Student try { vcs.fireVetoableChange("vl", vcs.fireVetoableChange("vl", old, old, vorlesung); vorlesung); this.vorlesung = vorlesung; vorlesung; } catch (PropertyVetoException (PropertyVetoException ex) { System.out.println("abgelehnt: System.out.println("abgelehnt: "+ex.getMessage "+ex.getMessage()); ex.getMessage()); } } • Listener für eine vetoable Property erhalten Änderung mit public void vetoableChange(PropertyChangeEvent e) • gibt e.getNewValue() e.getNewValue() e.getOldValue() e.getOldValue() e.getPropertyName() e.getPropertyName() • Problem: Listener macht kein Veto und rechnet mit Daten weiter, aber anderer Listener hat Veto (vgl. dirty read) • sauberer Ansatz, bei versuchter Änderung – erst nach Vetos fragen – wenn Veto, dann Änderung abbrechen – wenn kein Veto, dann Änderung als bound property behandeln (propertyChange), also zwei Listener zusammen • sauberes Beispiel: s. Praktikum • schmuddeliges Beispiel: nächste Folien • Anmerkung: Parameterinhalte kann man ändern Komponentenbasierte SoftwareEntwicklung • Spezielle Möglichkeit von JavaBeans: Vetoable (constrained) – Bean meldet angemeldeten Beans, dass eine Exemplarvariable geändert wird – Jede Bean kann „Einspruch“ gegen die Änderung (PropertyVetoException) einlegen • Konkretes Szenario: Kundenbestellung soll durchgeführt werden, Kontoverwaltung und Lagerverwaltung lauschen und können Einspruch einlegen • Nach Einspruch werden alle angemeldeten Beans erneut mit altem Wert informiert Prof. Dr. Stephan Kleuker public void addVetoableChangeListener(VetoableChangeListener l){ vcs.addVetoableChangeListener(l); vcs.addVetoableChangeListener(l); } public void removeVetoableChangeListener removeVetoableChangeListener( ( VetoableChangeListener l) { vcs.removeVetoableChangeListener(l); vcs.removeVetoableChangeListener(l); } 68 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 69 Beispiel ohne Bound Property (2/3) Beispiel ohne Bound Property (3/3) public class MeinVeto implements VetoableChangeListener{ VetoableChangeListener{ private String schlecht; private int aenderung; aenderung; public MeinVeto(String schlecht) { this.schlecht = schlecht; } @Override public void vetoableChange(PropertyChangeEvent evt) evt) throws PropertyVetoException { Set<String> Set<String> neu = (Set<String (Set<String>) Set<String>) evt.getNewValue(); evt.getNewValue(); if (neu.contains(schlecht)) neu.contains(schlecht)) throw new PropertyVetoException("nicht "+schlecht, evt); evt); System.out.println("fü System.out.println("für "+schlecht+": "+(++aenderung "+(++aenderung)); aenderung)); } } Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 70 Wesentlicher Nachrichtenfluss stu:Student (+vcs) setVorlesung ((C,Java)) :MeinVeto(BWL) Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 71 Einfaches Speichern (von JavaBeans) public static void main(String[] main(String[] args) args) { Student stu = new Student(); HashSet<String> HashSet<String> vls = new HashSet(); HashSet(); vls.add("C"); vls.add("C"); vls.add("Java"); vls.add("Java"); stu.setVorlesung(vls); stu.setVorlesung(vls); stu.setName("Erwin"); stu.setName("Erwin"); try { XMLEncoder out = new XMLEncoder(new BufferedOutputStream( BufferedOutputStream( new FileOutputStream("stu.xml"))); FileOutputStream("stu.xml"))); out.writeObject(stu); out.writeObject(stu); out.close(); out.close(); XMLDecoder in= new XMLDecoder(new BufferedInputStream( BufferedInputStream( new FileInputStream("stu.xml"))); FileInputStream("stu.xml"))); Student st= (Student)in.readObject (Student)in.readObject(); Student)in.readObject(); System.out.println(st.getVorlesung()); System.out.println(st.getVorlesung()); in.close(); in.close(); } catch (FileNotFoundException (FileNotFoundException ex) { } [C, Java] } :MeinVeto(C++) fireVetoableChange BWL:1 (vl,null,(C,Java)) fireVetoableChange (vl,null,(C,Java)) C++:1 vorlesung=(C,Java) vorlesung=(C,Java) setVorlesung ((C,Java,C++)) public static void main(String[] main(String[] args) args) { Student stu = new Student(); Set<String> Set<String> vls = new HashSet(); HashSet(); vls.add("C"); vls.add("C"); vls.add("Java"); vls.add("Java"); stu.addVetoableChangeListener(new MeinVeto("BWL")); MeinVeto("BWL")); stu.addVetoableChangeListener(new MeinVeto("C++")); MeinVeto("C++")); stu.setVorlesung(vls); stu.setVorlesung(vls); Set<String> Set<String> tmp = (Set<String>)vls.clone (Set<String>)vls.clone(); Set<String>)vls.clone(); für BWL: 1 tmp.add("C++"); tmp.add("C++"); für C++: 1 stu.setVorlesung(tmp); stu.setVorlesung(tmp); System.out.println(stu.getVorlesung()); System.out.println(stu.getVorlesung()); für BWL: 2 } für BWL: 3 für C++: 2 abgelehnt: nicht C++ [C, Java] fireVetoableChange BWL:2 (vl,(C,Java),(C,Java,C++)) fireVetoableChange(vl,(C,Java),(C,Java,C++)) PropertyVetoException fireVetoableChange BWL:3 (vl,(C,Java,C++),(C,Java)) fireVetoableChange(vl,(C,Java,C++),(C,Java)) C++:2 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 72 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 73 stu.xml Analyse des Beispiels <?xml <?xml version="1.0" encoding="UTFencoding="UTF-8"?> <java version="1.6.0_12" class="java.beans.XMLDecoder"> class="java.beans.XMLDecoder"> <object class="beans.Student"> class="beans.Student"> <void property="name"> property="name"> <string>Erwin</string> string>Erwin</string> </void </void> void> <void property="vorlesung"> property="vorlesung"> <void method="add"> method="add"> <string>C</string> string>C</string> </void </void> void> <void method="add"> method="add"> <string>Java</string> string>Java</string> </void </void> void> </void </void> void> </object </object> object> </java </java> java> Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker • Wesentliche Funktionalität wurde an Java (genauer die Virtual Machine VM) abgegeben; diese arbeitet als Container • VM kann direkt auf Exemplarvariablen zugreifen (Ansätze mit Introspection, Reflection) – Gib mir Exemplarvariablen – Gib mir Exemplarmethoden – (Textanalyse, Beginn mit get/set) – Rufe auf Objekt mit Methode mit Namen … und Parametern … auf 74 Weiterführend • Zu jeder Bean kann es eine BeanInfo-Klasse geben, die die Bean beschreibt • Ursprünglich sind Beans für visuelle Komponenten entwickelt worden; GUIBuilder erlauben die direkte Bearbeitung von Properties • Java-Bean für GUI-Builder in jar-Datei mit Manifest-Datei (.mf) • Es gibt eigene Werkzeuge zur Entwicklung mit Beans Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 76 Komponentenbasierte SoftwareEntwicklung Prof. Dr. Stephan Kleuker 75