Beispiel Gruppenphase Table of contents 1 Erklärung der eingesetzten Techniken...............................................................................2 1.1 Architektur des Beispieles.............................................................................................2 1.2 Spring Framework......................................................................................................... 3 1.3 JUnit.............................................................................................................................. 8 1.4 Log4J............................................................................................................................. 9 1.5 dom4j...........................................................................................................................10 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase 1. Erklärung der eingesetzten Techniken Hier werden die eingesetzten Techniken des SE Mittel vorgestellt und anhand von Beispielen näher erörtert. 1.1. Architektur des Beispieles Java Packages erlauben die Struktierung von Java Klassen. Das Beispiel ist in folgende Packages aufgeteilt. 1.1.1. Package Beschreibung Package Beschreibung at.ac.tuwien.ifs.qse.se1.basis Das Packet enthält Startklassen um das Swing UI und alle anderen Klassen in den Subpackages zu starten. Es startet und konfiguriert Log4j anhand der Log Properties und öffnet die HauptGUI. at.ac.tuwien.ifs.qse.se1.basis.dao Das Data Access Object Package enthält alle Methoden um auf die Datenbank (über die Persistenz Klassen) zuzugreifen. at.ac.tuwien.ifs.qse.se1.basis.export_import Enthält die Klasse für den Import und Export zu XML Dateien (verwendet dom4j) und Klassen für den Export zu HTML. at.ac.tuwien.ifs.qse.se1.basis.gui Enthält die Klassen um das Swing User Interface anzuzeigen. Dieses besteht aus dem Hauptfenster (benutzt StudentTableModel und ExportComboModel, den EditStudentFrame und den MessageDialog) at.ac.tuwien.ifs.qse.se1.basis.helper Dieses Packet enthält eine Klasse für systemweite Methoden und eine für systemweite Konstanten. at.ac.tuwien.ifs.qse.se1.basis.model Dieses Packet enthält Datenobjekte die durch Java Beans gekapselt werden. at.ac.tuwien.ifs.qse.se1.basis.test Dieses Packet enthält eine Klassen die alle Unit Tests ausführt (verwendet JUnit). at.ac.tuwien.ifs.qse.se1.basis.test.dao Dieses Packet enthält die Unit Tests für das DAO Packet. Page 2 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase at.ac.tuwien.ifs.qse.se1.basis.test.export Dieses Packet enthält die Unit Tests für das export_import Packet. 1.1.2. Wichtige Dateien beans.xml In dieser XML Datei werden die Klassen und Eigenschaften jedes Beans definiert. 1.2. Spring Framework Spring Framework erleichtert die Arbeit wesentlich, Rod Johnson argumentiert: • It addresses important areas that many other popular frameworks don't. Spring focuses around providing a way to manage your business objects. • Spring is both comprehensive and modular. Spring has a layered architecture, meaning that you can choose to use just about any part of it in isolation, yet its architecture is internally consistent. So you get maximum value from your learning curve. You might choose to use Spring only to simplify use of JDBC, for example, or you might choose to use Spring to manage all your business objects. And it's easy to introduce Spring incrementally into existing projects. • Spring is designed from the ground up to help you write code that's easy to test. Spring is an ideal framework for test driven projects. • Spring is an increasingly important integration technology, its role recognized by several large vendors. Im SE Beispiel wurde Spring Framework folgend eingesetzt: 1.2.1. beans.xml In dieser XML Datei werden die Klassen und Eigenschaften jedes Beans definiert. In folgenden Beispiel wird das StundentDAO Bean konfiguriert. Wie man sieht besteht zwischen dem Bean und anderen Klassen (transactionManager, ...) Abhängigkeiten. Diese werden mit dem "ref" Attribut deklariert, welches die Abhängikeiten zwischen den Klassen zeigt. Wie man sieht können auch SQL Statements hier angeführt werden. <!-- This Transaction Manager is needed for SQL Transactions: it uses a SQL Datasource (see above) --> <bean id="TransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="HsqldbDataSource"/> </bean> <!-**************************************************************************************** Page 3 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase --> <!-- Configuration of Data Access Objects and injection of session and transaction manager --> <!-**************************************************************************************** --> <!-- Complete Bean Lifecycle Management here: including initialisation and destroying --> <bean id="StudentDAO" class="at.ac.tuwien.ifs.qse.se1.basis.dao.JdbcObjectStudentDAO" singleton="true" init-method="init" destroy-method="destroy" > <!-- This is the datasource needed for JDBC Database Access --> <property name="dataSource" ref="HsqldbDataSource"/> <!-- This is the transaction manager needed for SQL Transactions --> <property name="transactionManager" ref="TransactionManager" /> <!-- The next properties are SQL Commands used in this DAO --> <property name="sql_getAllStudents"> <value>select id, matnr, vorname, nachname, email from studenten</value> </property> <property name="sql_getStudent"> <value>select id, matnr, vorname, nachname, email from studenten where id=?</value> </property> <property name="sql_insertStudent"> <value>INSERT INTO studenten (matnr, vorname, nachname, email) values (?, ?, ?, ?)</value> </property> <property name="sql_updateStudent"> <value>UPDATE studenten SET matnr=?, vorname=?, nachname=?, email=? WHERE id=?</value> </property> <property name="sql_deleteStudent"> <value>DELETE FROM studenten WHERE id=?</value> </property> <property name="sql_getInsertId"> <value>CALL IDENTITY()</value> </property> </bean> Nun definiert man die DAO, welche eine Bean Referenz zu den DataSource haben. Diese werden mit dem "ref" Attribut deklariert, welches die Abhängikeiten zwischen den Klassen zeigt. Danach folgenden die einzelen SQL Statements welche in den DAOs verwendet werden. <bean Page 4 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase id="StudentDAO" class="at.ac.tuwien.ifs.qse.se1.basis.dao.JdbcObjectStudentDAO" singleton="true" init-method="init" destroy-method="destroy" > <!-- This is the datasource needed for JDBC Database Access --> <property name="dataSource" ref="HsqldbDataSource"/> <!-- This is the transaction manager needed for SQL Transactions --> <property name="transactionManager" ref="TransactionManager" /> <!-- The next properties are SQL Commands used in this DAO --> <property name="sql_getAllStudents"> <value>select id, matnr, vorname, nachname, email from studenten</value> </property> .... 1.2.2. StudentDAO Das StudentDAO Inteface legt die zu implementierenden Methoden fest. public interface StudentDAO { public Student getStudent (Long id); public Long addStudent (Student student); public Long updateStudent (Long id, Student student); public Long deleteStudent (Long id); public List<Student> getStudents(String order);} Die JDdbcObjectStudentDAO Klasse implmentiert das vorher entworfene Interface. Zu beachten ist der nun implementierte Transaction Manager "transactionManager" und "dataSource". In dem Beispiel wird als Inversion-of-Control (IoC) Technik Dependency Injection und nicht Dependency Lookup verwendet. Bei Dependency Injection wird die Implementierungsklasse über die JavaBean setter()-Methoden in die Komponenten injiziert. Weiters ist auch das Handling der der init Methode zu beachten (siehe unten). public class JdbcObjectStudentDAO implements StudentDAO { ... // Logger private static Log log = LogFactory.getLog(JdbcObjectStudentDAO.class); // SQL Datasource and Transaction Manager private DataSource dataSource = null; private PlatformTransactionManager transactionManager = null; // SQL Query Strings private String sql_getStudent = ""; // Query Objects Page 5 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase private Query_GetStudent query_getStudent; ... /************************************************************************************/ /************************************************************************************/ /* C O N S T R U C T O R */ /************************************************************************************/ /************************************************************************************/ public JdbcObjectStudentDAO() { super(); } /** * Initialise Method. Must be called after all bean values are set: particularly the * datasource and the transaction manager. * This is actually performed by the Spring Framework, which sets first of all, all * Java Bean Properties and eventually calls this init method (see bean definition * in beans.xml configuration file) */ public void init() { log.info("Initialise StudentDAO"); query_getStudent = new Query_GetStudent(dataSource);; query_insertStudent = new Query_InsertStudent(dataSource); query_getStudentId = new Query_GetStudentID(dataSource); query_updateStudent = new Query_UpdateStudent(dataSource); query_deleteStudent = new Query_DeleteStudent(dataSource); query_getAllStudentsOrderMatnr = new Query_GetAllStudentsOrderMatnr(dataSource); query_getAllStudentsOrderNachname = new Query_GetAllStudentsOrderNachname(dataSource); } /** * Destroy Method. * This method is called by the Spring Framework to end the lifecycle of this bean, * but only when the bean is created as singletong. * Check the bean definition in beans.xml configuration file for details. */ public void destroy() { log.info("Destroy StudentDAO"); } /************************************************************************************/ /************************************************************************************/ /* BEAN SETTERS FOR DEPENDENCY INJECTION */ /************************************************************************************/ /************************************************************************************/ Page 6 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase /*** * Set SQL String to get all students * @param sql_getAllStudents SQL Statement as String */ public void setSql_getAllStudents(String sql_getAllStudents) { this.sql_getAllStudents = sql_getAllStudents; } /*** * Set SQL String to get one student with one SQL parameter * @param sql_getStudent SQL Statement as String */ public void setSql_getStudent(String sql_getStudent) { this.sql_getStudent = sql_getStudent; } /** * Set SQL String to insert one student to database * @param sql_insertStudent SQL Statement as String */ public void setSql_insertStudent(String sql_insertStudent) { this.sql_insertStudent = sql_insertStudent; } /** * Set SQL String to retrieve the ID of the last executed SQL Statement * @param sql_getInsertId SQL Statement as String */ public void setSql_getInsertId(String sql_getInsertId) { this.sql_getInsertId = sql_getInsertId; } /** * Set SQL String to update a student. * @param sql_updateStudent SQL Statement as String */ public void setSql_updateStudent(String sql_updateStudent) { this.sql_updateStudent = sql_updateStudent; } /** * Set SQL String to delete a student. * @param sql_deleteStudent SQL Statement as String */ public void setSql_deleteStudent(String sql_deleteStudent) { this.sql_deleteStudent = sql_deleteStudent; } /** * Set Datasource to connect to database * @param dataSource SQL Datasource */ Page 7 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } /** * Set a transaction manager for encapsulating insert and updates in transaction * @param transactionManager java Transaction Manager */ public void setTransactionManager (PlatformTransactionManager transactionManager) { this.transactionManager = transactionManager; } } 1.3. JUnit Die Klasse AllTests (package ...basis.test) baut eine JUnit Test Suite auf, welche alle Test Suite Klassen beinhaltet. In eine Suite werden neue TestSuite mit der Methode addTestSuite(..) hinzugefügt. AllTest liefert eine TestSuite als return value. Sie kann bequem über Eclipse gestartet werden. public class AllTests { // get logger for this root package private static Log log = LogFactory.getLog("at.ac.tuwien.ifs.qse.se1.basis"); /** * Creates an new TestSuite containing all Tests in the sub-packages. * If you want your own TestClasses executed you have to add them here to the * TestSuit to have them executed in a testrun of jUnit. * * @return an initialized TestSuite */ public static Test suite() { // configure log4j based on log.properties Helper.configureLog4j(); log.info("Starting Unit-Tests"); TestSuite suite = new TestSuite("Test for at.ac.tuwien.ifs.qse.se1.basis.test"); //$JUnit-BEGIN$ suite.addTestSuite(JdbcStudentTest.class); suite.addTestSuite(XmlExportImportTest.class); suite.addTestSuite(ExportImportTest.class); suite.addTestSuite(HtmlExportTest.class); //$JUnit-END$ return suite; } Page 8 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase } Eine Test Suite besteht prinzipell aus folgenden Grundgerüst: Die setUp() Methode welche vor jedem Testfall durchgeführt wird und die tearDown() Methode die nach dem Testfall durchgeführt wird. Nun wird für jede Methode zumindest ein Testfall geschrieben. In unseren Beispiel testet die Methode testGetStudent die getStudent Methode. Nach dem Aufruf von getStundent(...) wird überprüft ob der Rückgabewert korrekt ist. public class JdbcStudentTest extends TestCase { .. protected void setUp() throws Exception { super.setUp(); ... } protected void tearDown() throws Exception { super.tearDown(); ... } /** * Test the method getStudent * * @see StudentDAO#getStudent(Long) * */ public void testGetStudent() { Student student = studentDAO.getStudent(new Long(0)); assertNotNull(student); assertEquals("Alexander", student.getVorname()); assertEquals("Schatten", student.getNachname()); } ... } 1.4. Log4J Mit dem Packages Jakarta Commons Logging und Log4j von Apache ist es sehr einfach log Statements auszugeben ohne dabei größere Geschwindigkeitseinbußen hinnehmen zu müssen. Das konfigurieren mittels einer Konfigurationsdatei ermöglicht es, das log-Verhalten zu manipulieren ohne den Sourcecode verändern zu müssen. Page 9 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase Ich zeige hier am Beispiel der Basis Klasse aus SE_Medium die Anwendung dieser Packages: Mit dem Befehl private static Log log = LogFactory.getLog("at.ac.tuwien.ifs.qse.se1.basis"); erzeugen wir eine Variable namens log und holen uns von der LogFactory mit .getLog(Klasse)eine Instanz für unsere Basisklasse. Bevor wir den logger verwenden, muss er konfiguriert werden. Für die Konfiguration existiert eine eigene Hilfsmethode namens Helper.configureLog4j(). Diese liest, nachdem er geprüft hat ob die Datei überhaupt existiert, die Daten aus einem Log4j Properties File und ruft dann die Methode org.apache.log4j.PropertyConfigurator.configure(Properties_File); auf. Nach der erfolgreichen Konfiguration kann man mit dem einfachen Befehl log.info(String); einen String als info Message an den logger weitergeben, der ihn dann in der Konsole ausgibt. Weitere Message-Levels sind: • fatal • error • warn • debug • trace Für nähere Informationen folgen Sie bitte den links unter Referenz. 1.5. dom4j Was ist dom4j eigentlich? dom4j ist ein Open Source XML framework für Java, das es ermöglicht XML Dokumente zu lesen, schreiben, erstellen und zu navigieren. Es ist dabei eine einfache, klein gehaltene API, die häufigen Gebrauch von Standard Java APIs zB. Java 2 collections macht. dom4j wird im SE Beispiel nur in der Klasse XMLExportImport verwendet: Import: Page 10 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase Ein XML File einzulesen ist recht einfach. Die Methode read(filename) ruft die Methode readXML(filename) auf, erzeugt uns mit dem folgenden Code einen SAXReader und liest damit die Datei filename in ein dom4j Documentein. SAXReader xmlReader = new SAXReader(); Document doc = xmlReader.read(filename); Anschließend werden mit List|Student| studenten = new ArrayList|Student|(); eine neue ArrayList von Studenten erzeugt, List studentenEl = doc.getRootElement().elements(); aus dem Document die vorhandenen Elemente des RootElementsin eine Liste studentenEl gespeichert und mit Iterator it = studentenEl.iterator(); ein Iterator erzeugt, über den in der folgenden Schleife über alle vorhandenen Elemente (in unserem Fall ja Studenten) iteriert wird. In der Schleife wird ein neuer Student angelegt, mit den Daten des Elements befüllt und zur ArrayList studenten hinzugefügt Nach dem das für alle Elemente gemacht wurde, wird die Schleife beendet und die nun volle Liste von Studenten zurückgegeben. Anmerkung: Der Import wird in SE_Medium (noch) nicht verwendet. Es sollte jedoch kein Problem sein, mit dieser Vorlage eine Import Funktion in der eigenen Anwendung zu realisieren Export: Der Export besteht aus zwei Teilen; zuerst wird eine XML Datei erzeugt und mit den Daten befüllt, dann wird sie abgespeichert. In der Methode generateXML wird am Anfang ein leeres Document mit Document doc = documentFactory.createDocument(); erzeugt. Dem gleich noch ein Element student mit doc.addElement("students"); angehängt wird. Dann wird in einer Schleife die Studenten Liste durchgegangen und für jedes Element der Liste (also für jeden Studenten) ein rootElement Student mit den Daten des Studenten als Unterelemente und der id als Attribut dem Document hinzugefügt. Schon haben wir ein fertig befülltes Document! Dieses wird jetzt in der Methode save(filename) als XML-Datei abgespeichert. Page 11 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved. Beispiel Gruppenphase Und das geht so: Erzeuge einen FileOutputStream, FileOutputStream fos = new FileOutputStream(filename); wähle das Format (in unserem Fall PrettyPrint) und stelle die Codierung ein, OutputFormat outForm = OutputFormat.createPrettyPrint(); outForm.setEncoding(encoding); erzeuge einen XMLWriter und gib ihm den Stream und das Format mit, XMLWriter xwriter = new XMLWriter(fos, outForm); und schreibe das ganze mit xwriter.write(doc); Schon haben wir eine schön formatierte XML Datei mit allen unseren Studenten Page 12 Copyright © 2006 Institut fuer Softwaretechnik und interaktive Systeme All rights reserved.