Ein Blick über den Tellerrand: RSSOwl

Ein Blick über den Tellerrand: Der Aufbau des Open Source Projektes RSSOwl. Wie ist der Source organisiert und wird die Entwicklung unterstützt.

Beim Blick über den Tellerrand liegt der Fokus nicht auf der Funktionalität der Software, sondern auf der Organisation der Entwicklung und am Aufbau des Sourcecodes. Hier geht es darum von anderen Projekten zu lernen und die eigene Organisation zu verbessern. Daher erfolgt der hier präsentierte Blick auf die Software protokollartig durch meine Augen und entlang meiner Lernkurve.

Überblick

RSS Owl versteht sich selbst als komfortable Software um RSS, RDF und Atom News Feeds zu organisieren, zu suchen und zu lesen. Als besondere Stärken führt man die gemerkten Suchen, die Benachrichtigung aus dem Tray, schnelle Volltextsuche und die übersichtliche und saubere GUI ins Feld.

Auch ich hatte RSS Owl bereits eingesetzt und kann diese Stärken bestätigen. Damit steht es in direkter Konkurrenz zu dem Google Reader, der plattformunabhängig übers Web jederzeit den aktuellen Datenbestand zur Verfügung stellt.

Erhältlich ist RSS Owl unter der Eclipse Public License unter folgender URL: http://www.rssowl.org

Nun aber schnell mal per SVN die zuletzt veröffentlichte Version 2.0.6 auf die Platte ausgechecken!

Facts

Während das passiert, ein paar Facts rund um die Entwicklung. RSS Owl ist seit Juli 2003 ein Projekt auf Sourceforge unter der Leitung von Benjamin Pasero. Neben Benjamin Pasero entwickelt derzeit noch ein weiterer Entwickler an dem Programm mit. Es hatte bereits Anfang 2005 die 10.000 Downloads/Monat Marke durchbrochen, welche es seither verteidigt. Letztes Jahr wuchs das Projekt mit 359 Commits (also fast täglich) und es wurden 3.310 Files dabei hochgeladen. Insgesamt steht RSS Owl zum Zeitpunkt des Verfassens meines Artikels bei 7538 Commits. Macht fast 95 Commits je Monat.

Organisation

Homepage

Die Homepage ist einfach gehalten, was ich als durchaus positiv werte. Als Benutzer kommt man so schnell zu dem was einem interessiert. Interessanter versteckter Hinweis: RSSOwl nutzt den YourKit Java Profiler um die eigene Software zu analysieren und Schwachstellen aufzudecken. Auch den RSS Feed zur Verfolgung der Veränderungen im Source finde ich eine gute Sache.

Bugtracker

Bei der Suche nach einem BugTracker stößt man leider auf kleinere Hürden, der alte Link http://dev.rssowl.org der immernoch in der Wiki zu finden ist, sperrt uns mit einer Passwortfrage aus. Der neuere Link http://project.rssowl.org auf dem die Software Ratioal Team Concert zur Verwaltung der Entwicklung diente ist mittlerweile ebenfalls nicht mehr erreichbar. Bei den Entwicklern nachgefragt stellte sich heraus, dass der RTC Server nicht mehr in Verwendung ist. Um trotzdem einen kleinen Überblick zu erhalten konnte ich auf Youtube ein Video entdecken, in dem der Entwickler das Zusammenspiel zwischen Projekt Verwaltung, User und Entwickler aufzeigt.:

Rational Team Concert for open source development: RSSOwl

Da es sich hierbei vermutlich nur um ein temporäres Problem handelt, würde es sich lohnen später nochmal vorbeizusehen um einen persönlichen Eindrück zu gewinnen.

Source

RSSOwl Package ExplorerBuild

Das Ding das erste mal selbst zu bauen wirkt gar nicht so trivial wie erhofft. Und schon bin ich auch in die erste kleine Falle gelaufen. Selbst Schuld kann man sagen, haben die Entwickler doch in der  Wiki den Build Prozess von RSSOwl ausreichend erklärt, wie man an ein funktionierendes Projekt Set kommt. Der vorgeschlagene Weg ist flott eingeschlagen und schon ist man mit einer Liste an Projekten konfrontiert. Noch die Launch Konfiguration für mein Eclipse angepasst und schon ist das Programm von Eclipse aus zu starten. Ein Problemloser Einstieg, eigentlich.

Tests

Auch die JUnit Tests laufen wie geschmiert. 842 Tests überprüfen die Funktionstüchtigkeit von RSSOwl, von denen 2 Failures gemeldet wurden. Mit einem raschen Blick auf die Fehler Ursache stellt sich heraus, dass hier die unterschiedliche Zeitzone die Schuld trägt. Mehrere Tests wurden auch ignoriert, viell. in Zusammenhang mit den Nullpointer Exceptions die beim Zugriff auf das Plugin „org.eclipse.equinox.common“ geworfen wurden. Dem gehe ich nun aber nicht weiter nach.

Die Tests sind umfangreich, großes Lob meinerseits! Auch sind die Testfälle ausreichend beschrieben und weisen Verweise auf gemeldete Bugs auf. Wie mit JUnit üblich existiert eine TestSuite mit mehreren Untergruppen. Dabei lässt sich bereits etwas über die Struktur des Programmes herauslesen. So gibt es Gruppen für den Core, für Funktionen die das Netzwerk benötigen und für welche die es nicht tun, für die Performance und für Spezialbereiche wie der PasswordProvider oder die JobQueue.

Packages/Module

Die eigentliche Anwendung selbst wurde aufgeteilt in die Komponenten Core und UI. Innerhalb der jeweiligen Subprojekte wurden die Klassen nach Funktionalität in verschiedenste Packages aufgeteilt. Hier sieht alles recht normal aus. Die relativ geringe Modularisierung wurde durch Programmierung gegen Interfaces ausgeglichen.

Das Projekt setzt auf SWT zur GUI Erzeugung. Im Speziellen wird hier Eclipse als Rich Client Plattform eingesetzt und die Ähnlichkeit im laufenden Betrieb lässt sich auch kaum leugnen. Vorteil davon ist ein professionelles Erscheinungsbild, und viele Sorgen die einem die Plattform abnimmt.

Alleine der Core von RSS Owl umfasst 365 Klassen, und so ist die Einstiegshürde bereits recht hoch. Um dennoch einen Einblick zu gewinnen, liefern einem die Authoren eine entsprechende Übersicht: Architecture and Extension Points

Comments

Die Authoren haben ihren Code durchgehend mit Kommentaren (Javadoc und andere Erklärungen) versehen. Zahlreiche Kommentare dienen als Lesestütze für den Sourcecode. Dabei haben sie gute Arbeit getan, die die Einstiegshürde für weitere helfenden Hände verringert und auch den alten Hasen dienlich ist.

Libs

RSSOwl nutzt db4o als Datenbank, greift auf die RSS Feeds mithilfe von Apaches httpclient zu und wertet sie mittels JDOM aus. Die angepriesene flotte Volltextsuche realisiert RSSOwl mithilfe von Lucene, ebenfalls ein Apache Projekt.

Da ich bisher noch kein Eclipse RCP Projekt gesehen habe sind die Abhängigkeiten zwischen den Projekten noch etwas undurchsichtig für mich. Prinzipiell bleibt zu sagen dass für jede genutzte fremde Library ein eigenes (Sub-)Projekt, ein sogenanntes Plugin, angelegt wurde, in dem neben der Binary Jar und den Sourcen noch richtigerweise die Lizenz der jeweiligen Bibliothek. In dem Zusammenhang stellt sich mir unabhängig von RSSOwl die Frage, wie in Maven derartiges mit Open Source Abhängigkeiten behandelt wird, schließlich ist je nachdem mindestens die Lizenz ein muss!?

Distribution

Dank der Nutzung von Eclipse RCP steht einer Veröffentlichung für die gängigen Plattformen Linux, Windows und Mac OS X nichts im Wege, außer vielleicht die Suche nach dem wie. Hier lässt die Wiki aus und der Haufen an XML Datein ist nicht sprechend genug. Meine Vermutung ist, dass ich eines der Goals aus der build.xml des Projekts org.rssowl.build verwenden müsste. Auch hier hilft mir der Author weiter, und meine Vermuting war nahe dran. In org.rssowl.build liegt eine rssowl.product Datei über die man den Eclipse Export Wizard anschmeißen kann. Auch das ist recht schnell geschehen.

Fazit

Für meinen nächsten Tellerrand muss ich mir mehr Zeit nehmen. Eine Eclipse RCP Anwendung nur aus flüchtigen Blicken in einen Source heraus zu verstehen ist nicht so einfach, mehr Basics müssen her.

Trotzdem hat sich gezeigt, dass RSSOwl sehr gut organisiert ist. Strukturierter gut kommentierter Code helfen einem sich im Projekt zurecht zu finden. Wirkt nach einer soliden Basis, die das Programm auch in Zukunft gut dastehen lassen wird.

Ähnliche Artikel:

xkcd – Good Code

in Grund warum so einige meiner Hobbyprojekte nicht fertig werden, der Wunsch nach Good Code. xkcd bringt auf den Punkt warumd as so schwer ist.

Ja ich weiß, es kennt eigentlich eh ein jeder die xkcd Comics. Man findet für so ziemlich jede Lebenslage in der Softwarewelt ein passendes und lustigs Comic und wenn nicht dann bald.

Heute möchte ich ein Comic präsentieren dass meinen täglichen Programmierkonflikt wiederspiegelt. Ein Grund warum so einige meiner Hobbyprojekte nicht fertig werden, der Wunsch nach gutem Code. Aber irgendwann hab ich richtig guten Code. So gut dass ich das auch mal damit zufrieden bin :D

 

You can either hang out in the Android Loop or the HURD loop.

Quelle: http://xkcd.com/844/

Ähnliche Artikel:

HOWTO: GUI mit Swing – Teil 5: Concurrency in Swing

Wie entwickelt man eigentlich eine GUI mit Java Swing? Eine mehrteilige Einführung in dieses Thema. Teil 5 beschäftigt sich mit Nebenläufigen prozessen in Swing

Zur Erinnerung die bereits besprochenen und noch geplanten Themen hier noch eine Auflistung selbiger:

      Nachdem wir nun erfolgreich eine GUI gebaut haben, die auf unsere ersten Klicks reagiert wollen wir uns nun mit der Nebenläufigkeit in Swing Anwendungen beschäftigen.

      Aufgabe aus Teil 4

      Aufgabe überspringen

      Aus Teil 4 war noch ein ActionListener für den Menüpunkt beenden zu realisieren.

      Action Listener

      Wir erstellen analog zum EditActionListener eine neue Klasse MenuActionListener. In dieser prüfen wir ob das Event dass der Listener enthält das Beenden Event ist, schließen erst das JFrame und dann die ganze Anwendung.

      package at.nullpointer.guiprototype.listener;
      
      import java.awt.event.ActionEvent;
      import java.awt.event.ActionListener;
      
      import at.nullpointer.guiprototype.Prototype;
      
      public class MenuActionListener implements ActionListener {
      
      	public void actionPerformed(ActionEvent e) {
      		if(e.getActionCommand().equals("Beenden")){
      			Prototype.getJFrame().dispose();
      			System.exit(0);
      		}
      	}
      }

      Den ActionListener müssen wir nun nur noch dem MenuItem zuweisen. Dies geschieht analog zu der Zuweisung beim JButton. Wir editieren dazu die Klasse PrototypeMenuBar

      		close.addActionListener(new MenuActionListener());

      Damit können wir nun das Programm über den Menüpunkt beenden. Auch das Tastenkürzel ALT+B führt zum Programmende.

      NebenLäufigkeit in Swing

      Die erste Form von Nebenläufigkeit in Swing haben wir bereits in den vorherigen Teilen dieser Serie kennengelernt. Der Event-Dispatcher Thread wurde bereits genutzt um die GUI zu erstellen und anzuzeigen

              //Schedule a job for the event-dispatching thread:
              //creating and showing this application's GUI.
              javax.swing.SwingUtilities.invokeLater(new Runnable() {
                  public void run() {
                      createAndShowGUI();
                  }
              });

      Dabei wird im Initial-Thread, der die main Methode ausführt, oben benannter Code ausgeführt. Man übergibt dem Event-Dispatcher Thread ein Runnable Objekt, dass dieser ausführt sobald er soweit ist. Bei invokeLater arbeitet der Initialthread nach der Übergabe gleich weiter und würde nachfolgenden Code behandeln.

      Für den Fall, dass der nachfolgende Code erst ausgeführt werden darf, wenn die GUI erzeugt wurde gibt es noch die invokeAndWait Methode, die das Runnable Objekt ebenfalls übergibt, aber auf dessen Beendung wartet.

      Führt man seine GUI nicht im Event-Dispatcher Thread aus funktioniert sie meist auch, es kann aber zu Speicherverletzungen und somit Fehlverhalten kommen, welche nur schwer zu finden bzw. korrigieren ist. Von diesem Event-Dispatcher Thread ist jedoch die gesamte Swing Oberfläche abhängig, da die meisten Komponenten nicht Thread Safe implementiert sind. Deshalb sollten darauf nur kurze Tasks, wie das feuern von Events, oder z.B. minimale Textkorrekturen ausgeführt werden (zB durch zusätzliche invokeLater Aufrufe) um ein Einfrieren der Applikation zu vermeiden. Ein Umstand dem wir in den bisherigen Teilen dieses Tutorials kaum Rechnung getragen haben.

      Für rechenintensivere Aufgaben, oder Tasks die zB Netzwerkkommunikation erfordern ist es daher notwendig auf eine andere Abwicklung zurückzugreifen. Sogenannte Worker-Threads werden für die Umsetzung dessen verwendet. Man muss dazu mit seinem Objekt die abstrakte Klasse SwingWorker implementieren, welche einige Features zur Kommunikation mit dem Event-Dispatcher Thread beinhaltet.

      Akuell weist unser Prototyp folgendes Verhalten auf: Wenn wir in unserem Prototypen auf der EditView auf OK klicken übermittelt unser Programm die Daten an die Enterprise Software im Hintergrund. Ist diese mit dem Bearbeiten fertig, übersendet sie uns ein OK, welches wir bestätigen müssen. Erst dann schaltet unser Prototyp in den Anzeigemodus. Verzögert sich nun das Absenden der Bestätigung friert unser Programm ein. Dies kann man leicht nachstellen indem man die Mock-Klasse MockCompanySoftware um z.B. eine fette Schleife erweitert.

      	public String setKundendaten(Kundendaten kdata) {
      		for (int x = 0; x < 100000; x++)
      			for (int y = 0; y < 100000; y++){
      				System.currentTimeMillis();
      			}
      	    return "Daten gesandt";
      	}

      Wir müssen nun merklich warten bis unser Programm weiter verwendbar ist. Minimieren und verschieben funktioniert immerhin. Doch der Button bleibt gedrückt, die Ansicht ebenfalls auf der EditView. Um dem zu begegnen müssen wir die Prototype#sendData Methode ändern. Dazu erzeugen wir einen SwingWorker, der das Senden der Daten für uns übernimmt.

      	public static void sendData() {
      		SwingWorker worker = new SwingWorker<Void , Void>() {
      
      			private String answer;
      
      			// Die eigentliche Aufgabe wird bearbeitet
      			@Override
      			protected Void doInBackground() throws Exception {
      				MockCompanySoftware mcs = new MockCompanySoftware();
      				answer = mcs.setKundendaten(null);
      				return null;
      			}
      
      			/* Wenn die Aufgabe fertig ist wird unsere JOptionPane angezeigt
      			   Dazu wird diese Methode aufgerufen wenn der Event-Dispatcher-Thread
      			   bemerkt, dass der HintergrundThread abgearbeitet ist.
      			 */
      			@Override
      			public void done() {
      				JOptionPane.showMessageDialog(null, answer);
      			}
      		};
      
      		//Der Worker wird ausgeführt
      		worker.execute();
      
      	}

      Hier sehen wir den Aufbau des SwingWorker. Er beinhaltet eine doInBackground Methode, die in einem Background-Thread ausgeführt wird. Sobald dieser Fertig ist wird vom Event-Dispatcher-Thread die done Methode aufgerufen. Mit der execute Methode startet man den Background-Thread. Nach erfolgreiches Event könnte von dem Ergebnis mittels get Methode gebrauch machen.

      Probiert den Prototypen nun nochmal aus, der Button wird nun freigegeben.

      Damit haben wir unsere Applikation nebenläufig gemacht und ich beschließe diese Tutorial-Reihe mit den Worten:

      Wenn ihr euer Wissen um Swing erweitern und effektiv einsetzen wollt, setzt euch am besten gleich hin und programmiert, schaut euch fremden Code z.B. auf Open Sourceplattformen an und programmiert dann weiter. Vielleicht baut ihr ja sogar das Beispiel hier aus. Weitere Anregungen oder Fragen dazu sind natürlich gern gesehen.

      Zum Weiterlesen ein paar Buchtipps, sowie Weblinks. Swing wird neben den online Tutorials und spezialisierten Büchern ebenfalls in div. Standardlehrbüchern zu Java behandelt.

      Trail: Creating a GUI With JFC/Swing – von Oracle: http://download.oracle.com/javase/tutorial/uiswing/index.html

      The JFC Swing Tutorial – Addison-Wesley Verlag 2004 Verlag – ISBN 0201914670, 9780201914672

      http://books.google.at/books?id=3rWTX-vjUhEC

      Für Fortgeschrittene: The definitive guide to Java Swing – Apress Verlag 2005 – ISBN 1590594479, 9781590594476

      http://books.google.at/books?id=YPjZNlEgAMcC

      Ähnliche Artikel:

      HOWTO: GUI mit Swing – Teil 4: Interaktion mit der GUI

      Wie entwickelt man eigentlich eine GUI mit Java Swing? Eine mehrteilige Einführung in dieses Thema. Teil 4 versieht den GUI Prototypen mit erster Funktionalität.

      Zur Erinnerung die bereits besprochenen und noch geplanten Themen hier noch eine Auflistung selbiger:

          Aufgabe aus Teil 3

          Aufgabe überspringen

          Aufgabe aus Teil 3 war es, nachdem wir die Anzeige Maske fertiggestellt hatten, auch die Edit Maske zu konstruieren.

          EditJPanel

          Damit wir wahlweise die Daten anzeigen, oder bearbeiten können, habe ich in meiner Lösung folgenden Weg eingeschlagen: Das ShowDataJPanel wurde umgebaut zum DataJPanel, welches im Konstruktor nun einen Boolean Parameter entgegennimmt, durch den entschieden wird ob die Daten mittels Label oder Textfield angezeigt werden. Anhand dieses Booleans wird auch entschieden ob die statistsichen Daten angezeigt werden oder der Absende Button.

          public DataJPanel(boolean useTextfields) { //...

          Unsere Maske wird nun immer noch gleich aufgebaut wie vorher, allerdings instanziert sie nicht von Haus aus ein ShowDataJLabel, sondern überlässt einer Factory Klasse die Entscheidung ob es entweder das JLabel oder aber ein EditDataJTextField erzeugt indem es das useTextfields Boolean daran durchschleust.

          JComponent kdnrContent = ShowOrEditJComponentFactory.getJComponent(
          				"0123456", useTextfields);

          Dabei wird das Wissen um den Aufbau des erhaltenen Objekts bewusst klein gehalten, um nicht zu hohe Abhängigkeiten zu erhalten. Das JComponent wird durch folgende Factory erzeugt:

          package at.nullpointer.guiprototype.showdata;
          
          import java.awt.Color;
          import java.awt.Dimension;
          import java.awt.Font;
          
          import javax.swing.JComponent;
          
          public class ShowOrEditJComponentFactory {
          
          	private static final Font font = new Font("Dialog", Font.PLAIN, 18);
          	private static final Dimension textFieldSize = new Dimension(480, 30);
          
          	private static JComponent createJLabel(String caption) {
          		return new ShowDataJLabel(caption);
          	}
          
          	private static JComponent createJTextField(String caption) {
          		return new EditDataJTextField(caption);
          	}
          
          	public static JComponent getJComponent(String caption, boolean isTextField) {
          		JComponent erg = null;
          		if (isTextField) {
          			erg = createJTextField(caption);
          			erg.setMaximumSize(textFieldSize);
          			erg.setMinimumSize(textFieldSize);
          			erg.setPreferredSize(textFieldSize);
          		} else {
          			erg = createJLabel(caption);
          		}
          
          		// Schriftfarbe
          		erg.setForeground(Color.BLACK);
          		// undurchsichtig (notwendig um einen Hintergrund zu zeichnen)
          		erg.setOpaque(true);
          		// Veränderung der Schrift
          		erg.setFont(font);
          
          		return erg;
          	}
          }

          Die beiden Klassen ShowDataJLabel und EditDataJTextField, die erst von der Factory erzeugt werden und dann anhand des allgemeinen Verhaltens der JComponent etwas an unsere Optik angepasst werden sind selbst relativ simpel gehalten:

          package at.nullpointer.guiprototype.showdata;
          
          import javax.swing.JTextField;
          
          public class EditDataJTextField extends JTextField {
          
          	public EditDataJTextField(String caption) {
          		this();
          		this.setText(caption);
          	}
          
          	public EditDataJTextField() {
          		super();
          	}
          }
          package at.nullpointer.guiprototype.showdata;
          
          import javax.swing.JLabel;
          
          public class ShowDataJLabel extends JLabel {
          
          	public ShowDataJLabel() {
          
          		super();
          	}
          
          	public ShowDataJLabel(String caption) {
          		this();
          		this.setText(caption);
          	}
          }
          Der Button

          Natürlich darf in der DataJPanel der Button zum Absenden nicht fehlen, der jedoch nur angezeigt werden soll, wenn Textfelder genutzt werden.

          if (useTextfields) {
          
          			// Zuletzt aktiv
          			JPanel lastPanel = new JPanel();
          			lastPanel.setLayout(new BoxLayout(lastPanel, BoxLayout.LINE_AXIS));
          			JButton btnOk = new JButton("OK");
          			JButton btnCancel = new JButton("Abbrechen");
          			lastPanel.add(Box.createHorizontalGlue());
          			lastPanel.add(btnOk);
          			lastPanel.add(Box.createRigidArea(rowSmall));
          			lastPanel.add(btnCancel);
          			this.add(lastPanel);
          			this.add(Box.createRigidArea(lineSmall));
          
          		} else {
          Aufruf der Sichten

          Jetzt muss nur noch der Aufruf der Sichten angepasst werden, der analog zum Aufruf der Daten Ansicht erfolgt. Bei Beiden gilt es jedoch nun ein Boolean zu übergeben, damit die Factory entscheiden kann, was angezeigt werden soll. Unsere fertige Eingabemaske sieht nun wie folgt aus:

          Edit Sicht

          Die vollständigen Sourcen dazu findet ihr wie immer am Schluss dieses Artikels, gemeinsam mit der nächsten Aufgabe.

          Ein Button erwacht zum Leben

          Um aufzuzeigen wie man einen Button zum Leben erweckt erzeugen wir uns als erstes eine sehr simple GUI:

          package at.nullpointer.learningswing.demojframe;
          
          import javax.swing.JButton;
          import javax.swing.JFrame;
          
          public class InputDemo {
          	int count = 0;
          	String text = " mal geklickt";
          	JButton btn = null;
          
          	public static void main(String[] args){
          		InputDemo gui = new InputDemo();
          		gui.draw();
          	}	
          
          	public void draw(){
          		JFrame frame = new JFrame();
          		btn = new JButton(count+text);
          		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          		frame.getContentPane().add(btn);
          		frame.pack();
          		frame.setVisible(true);
          	}
          }

          JButton Event Demo Wenn wir diese ausführen bekommen wir folgendes einfach nur einen Button angezeigt, bei dessen Betätigung nichts passiert. Das wollen wir nun ändern. Dafür brauchen wir 2 Dinge, etwas das passieren soll, und einen Weg wie wir rausfinden ob der Button geklickt wurde.

          Unschwer an der Beschriftung des Buttons zu erraten, wollen wir, dass jedes mal wenn er betätigt wird, sich seine Beschriftung ändert und die Anzahl der Klicks korrekt dargestellt wird. Dazu erweitern wir unser Demo um folgende Methode:

          	private void increaseCount(){
          		count++;
          		btn.setText(count+text);
          	}

          Testen lässt sie sich ganz einfach indem wir den Aufruf der Methode am Ende der Main Methode hinzufügen.

          Um nun auf die User Aktion reagieren zu können setzt Java ein Modell ein, das sich Event Handling nennt. Klickt ein User auf den Button wird ein Event (repräsentiert durch ein Event Objekt) abgefeuert. Diese Funktionalität bietet der JButton, ohne dass wir sie selbst implementieren müssten. Jedoch fehlt uns noch ein Weg dieses Event auszulesen. Es gibt verschiedenste Events (nachzusehen im java.awt.event Package) und unser JButton wirft eines vom SubTyp ActionEvent. Jedes dieser Eventtypen kann durch einen eigenen Listener ausgelesen werden. Für unser ActionEvent ist also ein ActionListener notwendig. Dies ist ein Interface, welches wir allerdings schon selbst implementieren müssen. Wir erweitern also unser InputDemo und lassen es das Interface implementieren

          public class InputDemo implements ActionListener {

          Damit wir das Interface vollständig umsetzen, müssen wir auch noch die von ihm geforderte Methode erzeugen. In diesem Fall ist das die actionPerformed Methode, in welcher wir unsere increaseCount methode aufrufen

          	public void actionPerformed(ActionEvent e) {
          		increaseCount();
          	}

          Starten wir nun das Programm sehen wir, dass das existieren eines Listeners alleine noch keine Auswirkungen hat, dem Button muss erst gesagt werden, dass da ein ActionListener auf seine Events wartet. Dazu stellt der JButton wieder eine entsprechende Methode zur Verfügung, welche wir bei der Erzeugung des JButtons aufrufen.

          		btn.addActionListener(this);

          image Und siehe da, unser Count beginnt nun beim Betätigen des Buttons zu steigen und zu steigen!

          ActionListener im Prototypen

          Der Cancel Button

          Unser neu erworbenes Wissen wollen wir jetzt natürlich im Prototypen anwenden. Wir wollen den Abbrechen Button mit Funktionalität belegen, dazu erzeugen wir uns zuerst im Prototypen entsprechende Methoden, die dazu führen dass statt der Editansicht die Showansicht angezeigt wird.

          Eine Methode die die Ansichten zeichnet haben wir ja bereits geschrieben: fillFrameWithDataContent

          Also benötigen wir nur noch eine Methode die das Frame von seinem alten Inhalt befreit:

          	public static JFrame removeJPanel(JFrame frame){
          		Component[] components = frame.getContentPane().getComponents();
          		for(Component component : components){
          			if (component instanceof JPanel){
          				frame.remove(component);
          			}
          		}
          		return frame;
          	}

          Danach erzeugen wir eine eigene Klasse, die wir EditActionListener taufen. Diese wird auf den Klick mit der Maus reagieren. Dabei arbeiten wir der Einfachheit halber in diesem Beispiel mit den static Methoden der Prototype Class. Ein Vorgehen welches ich ansonsten nicht befürworten würde.

          package at.nullpointer.guiprototype.listener;
          
          import java.awt.event.ActionEvent;
          import java.awt.event.ActionListener;
          
          import at.nullpointer.guiprototype.Prototype;
          
          public class EditActionListener implements ActionListener {
          
          	public void actionPerformed(ActionEvent e) {
          		Prototype.removeJPanel(Prototype.getJFrame());
          		Prototype.fillFrameWithDataContent(Prototype.getJFrame(), false).setVisible(true);
          	}
          }

          Zuerst wird unsere neue Methode aufgerufen, dannach befüllen wir das JFrame wieder. Achtung, das JFrame wird dann mittels Aufruf der setVisible Methode erneut für den Benutzer dargestellt, um die Änderungen sichtbar zu machen.

          Nun noch in der Klasse DataJPanel den EditActionListener dem Cancelbutton hinzugefügt und schon reagiert unser Prototyp richtig bei einem Klick auf den Abbrechen Button!

          Der OK Button

          Wie ihr vielleicht an der Benennung des EditActionListener erkannt habt, soll dieser nicht nur für den Cancel Button zuständig sein, sondern auch für den OK Button. Wir lassen ihn daher auch auf Events des OKButtons anhand der addActionListener Methode lauschen.

          Klicken wir nun auf den OK Button sehen wir unser ActionListener nicht zwischen den zwei Buttons unterscheiden kann. Der Klick führt zum selben Ergebnis wie ein Klick auf den Cancel Button. Damit der ActionListener differenzieren kann, nutzen wir die Informationen aus, die das Event, welches der actionPerformed Methode mitgegeben wurden, enthält. Folgende Methoden stehen dabei zur Verfügung:

          getActionCommand() liefert den CommandString der der Action entspricht
          getModifiers() liefert ein int, welches die Kontrolltasten die dabei gedrückt wurden repräsentiert
          getSource() liefert das Objekt auf dem das Event durchgeführt wurde
          getWhen() liefert den Zeitpunkt des Events zurück

          Wir verwenden die getActionCommand() Methode um unser Button Objekt zu identifizieren. Dieses liefert uns den ID-String zurück, den wir bei der Buttonerzeugung angegeben haben, also hierbei “OK”. Nur im falle eines Clicks auf OK führen wir zusätzlich das Absenden der Daten aus.

          	public void actionPerformed(ActionEvent e) {
          		if(e.getActionCommand().equals("OK")){
          			Prototype.sendData();
          		}
          		Prototype.removeJPanel(Prototype.getJFrame());
          		Prototype.fillFrameWithDataContent(Prototype.getJFrame(), false).setVisible(true);
          	}

          Die Methode sendData() würde nun auf den Firmenserver zurückgreifen und Daten an die Datenbank senden. Da dies den Rahmen des Tutorials sprengen würde wollen wir hier nur die Stringbestätigung ausgeben, die unsere Mocksoftware für die Gesendeten Daten retourniert.

          	public static void sendData() {
          		MockCompanySoftware mcs = new MockCompanySoftware();
          		JOptionPane.showMessageDialog(null, mcs.setKundendaten(null));
          	}

          Auch der OK Button wurde nun mit Funktionalität ausgestattet.

          Vorschau und Aufgabenstellung

          Nun da die beiden Buttons reagieren fehlt uns noch das Menü. Hierfür ist ebenfalls ein ActionListener zu implementieren. Hier noch die Ausgangs-Sourcen für diese Aufgabenstellung.

          Viel Erfolg dabei!

          Im nächsten und abschließenden Teil dieser kleinen Serie möchte ich noch kurz die Möglichkeiten für Nebenläufigkeit in Java Swing aufzeigen.

          Ähnliche Artikel: