1 von 58
Überblick 2 von 58
Das Ziel Qualität Einige Gesichtspunkte verteilt über den gesamten Lifecycle Struktur und Übersichtlichkeit des Codes Leichte Änderbarkeit und Erweiterbarkeit Dokumentation (z. B. Code-Doku., Change- und Bugfix-Management) Geprüfte Korrektheit Ressourcenverbrauch (z. B. Laufzeit, Speicherplatz) Codekonsistenz Versionspflege Automatisierung von Abläufen beim Erstellen Überwachung und Protokollierung im Betrieb 3 von 58
Werkzeuge und Vorgehensweisen Beispiele: Style-Guides Autoformatierung vgl. Eclipse Audits Refactoring javadoc Code-Metriken Debugger Profiler Testtools z. B. für Unit-Testing Sourcecodeverwaltungssystem z. B. svn, git Build-Mechanismus z. B. ant, maven, gradle 4 von 58
Codeanalyse und Monitoring 5 von 58
Statische Codeanalyse Absicht Analyse der Software, ohne den Code auszuführen. Durchsuchen des (Quell-)Codes nach formalen Kriterien. Mögliche Untersuchungsgegenstände: Allgemeine syntaktische Konventionen (Namensrichtlinien, Formatierung). Programmierrichtlinien (Keine Ausgaben über System.out, kein Import über Wildcard, Verbot von bestimmten Abhängigkeiten) Allgemeine Qualitätsdefizite (mangelhafte Kapselung, Code-Duplikation) Hinweise auf mögliche Fehlerquellen Maßzahlen (Metriken) über die Komplexität (Lines of Code pro Methode, Ausführungspfade pro Methode, komplizierte Programmiermuster) IDE Checkstyle FindBugs PMD 6 von 58
Checkstyle Charakteristiken Für automatisierte Überprüfung (ursprünglich Syntax), in neueren Versionen auch Design-Probleme u. ä. Prüfungen sind konfigurierbar. Default-Konfiguration baut auf den "Sun Code Conventions" auf. Anwendungs- und Integrationsmöglichkeiten Command Line Tool IDE-Plugins, z. B. für Eclipse: Default-Konfigurationen schon vorhanden. siehe Preferences -> checkstyle Kann exportiert und den eigenen Bedürfnissen angepasst werden per Editieren der XML-Datei oder per Wizard Ant-Task bzw. Maven-Plugin für Integration in automatischen Build (s. u.) 7 von 58
FindBugs Bug-Kategorien Correctness Bug Höchstwahrscheinliche Fehler, die offenbar nicht das ausdrücken, was der Programmierer eigentlich wollte. Beispiele: wahrscheinliches Auftreten von NullPointerException Endlosrekursion Aufruf von String-Methoden ohne Abholen des Rückgabewertes (z. B. replace()). Bad Practice Verletzung von allgemein akzeptierten Programmierpraktiken. Beispiele: equals()-methode prüft nicht, ob Parameter den Wert null hat equals()-methode und hashcode()-methode werden nicht gleichzeitig überschrieben Dodgy Verwirrender Code oder solcher, der sehr leicht zu Fehlern führen kann. Beispiele: Switch-Fall-Through (Switch ohne Breaks) Redundanter Null-Check Performance u. weitere 8 von 58
Dynamische Code-Analyse Absicht: Analyse der Software bei der Ausführung. Im "Labor" oder auch in der realen Einsatzumgebung. Mögliche Untersuchungsgegenstände: Fehlersuche Performanz Speicherverbrauch Engpässe und Optimierungspotential 9 von 58
Debugging jdb oder in IDE integrierter Debugger Man beachte: Beim Übersetzen müssen Debug-Informationen (in den class-files) erzeugt werden (javac -g). wesentliche Aspekte Breakpoints Steps: over, into, out Inspektion von lokalen Variablen, Objekten und Call-Stack Watches: überwachte Ausdrücke Remote-Debugging: Debugger und überwachtes Programm laufen in verschiedenen Prozessen. 10 von 58
Profiling Zweck Analyse von Laufzeit-, Speicherverbrauch und Aufrufhierarchien für einzelne Programmteile (Pakete, Klassen, Methoden), Threads,... Aufdecken von "Hot Spots". Graphische Aufbereitung der Analyse-Daten. VM-Unterstützung JVM TI: Java Virtual Machine Profiling Interface Programmierschnittstelle zum Auslesen von Profiling Informationen Profiler-Beispiele Netbeans: integrierter Profiler VisualVM (teils in JDK) JProfiler YourKit 11 von 58
Monitoring Beispiel: JMX (Java Management Extension) Einsatzzweck Überwachen (Monitoring) und Steuern (Management) von Java-Ressourcen (insbes. Netze, Systeme, Anwendungen, Services) in verteilten Umgebungen. typische Anwendungssituationen Beispiel Abfragen und Verändern von Anwendungskonfigurationen Sammeln von Statistiken über das Verhalten einer Anwendung und Bereitstellen der Auswertungen Benachrichtigung über Zustandsänderungen und Fehlersituationen Ab Version 1.5 kann die JVM per JMX überwacht und gesteuert werden. Starten einer remote überwachbaren Applikation java -Dcom.sun.management.jmxremote.port=2000 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false de.fhaugsburg.games.scrabblelauncher Zum Abrufen der Informationen Tool im jdk: jconsole bzw. jvisualvm 12 von 58
Logging 13 von 58
Überblick (1) Wesentliche Aufgaben eines Logging-Frameworks Einheitliche Programmierschnittstelle für alle Formen von Log-Meldungen. Unterstützung unterschiedlicher Wichtigkeitsstufen. Timestamps für Log-Meldungen Konfigurationsmöglichkeiten in Richtung Welche Stufen werden geloggt? Wohin wird geloggt? In welchem Format? Gruppierungsmöglichkeit, abhängig von der Herkunft der Meldungen mit unterschiedlicher Konfiguration der Gruppen. 14 von 58
Überblick (2) Grundstruktur eines Logging-Frameworks Quelle: Logging-Guide (Sun) Gebräuchliche Logging-Frameworks Klassiker: Log4J (Apache), Commons-Logging Java-Logging (das Logging-Framework des Jdk) SLF4J neuer: tinylog, Logback 15 von 58
Java-Logging: Meldungen erstellen (1) Beispiel: package logex; import java.util.logging.level; import java.util.logging.logger; public class LoggingDemo { private static Logger logger = Logger.getLogger(LoggingDemo.class.getName()); public static void main(string[] args) { logger.log(level.fine,"i am trace"); logger.fine("i am trace at the same level"); logger.log(level.warning,"i am a warning"); logger.log(level.severe,"i am telling about a severe problem..."); logger.log(level.severe, "an exception occurred", new NullPointerException("I am a pretender")); } } // a somehow more efficient version if (logger.isloggable(level.finest)) logger.log(level.finest, "I am a very fine trace message"); 16 von 58
Java-Logging: Meldungen erstellen (2) Die Log-Level für das Abschicken SEVERE (highest value) WARNING INFO CONFIG FINE FINER FINEST (lowest value) weitere Konstanten für das Filtern beim Logger oder Handler ALL OFF 17 von 58
Java-Logging: Konfiguration (1) Eine einfaches Beispiel: # Level for root logger.level= INFO # specific loggers and there level logex.loggingdemo.level = FINE # a parent logger logex.level = SEVERE handlers= java.util.logging.consolehandler # Limit the messages printed on the console to the specified level and above. java.util.logging.consolehandler.level = FINE # Define the format of the console output java.util.logging.consolehandler.formatter = java.util.logging.simpleformatter 18 von 58
Java-Logging: Konfiguration (2) Ergänzungen für File-basiertes Logging handlers= java.util.logging.filehandler, java.util.logging.consolehandler # file output is in user's home directory. java.util.logging.filehandler.pattern = %h/java%u.log # write records in XML format java.util.logging.filehandler.formatter = java.util.logging.xmlformatter Die Handler-Klassen MemoryHandler (zum Puffern im HS und Ausgabe an einen nachfolgenden Handler) StreamHandler ConsoleHandler FileHandler SocketHandler und eigene... 19 von 58
Java-Logging: Konfiguration (3) Vorgabe des Konfigurationsfiles Ein Default-File im Jdk unter lib/logging.properties. Festlegen eines eigenen Konfig-Files über die System-Property java.util.logging.config.file: Programmaufruf: java -Djava.util.logging.config.file=... Alternativ kann auch eine Klasse als Konfigurations-Input spezifiziert werden. Die Logger-Hierarchie Verwaltung durch den LogManager Ein Root-Logger mit leerem Namen: Zugriff: Logger.getLogger("") Hierachie ergibt sich aus dem hierarchischen Aufbau der Logger-Namen. Zum Bestimmen des Log-Levels eines Loggers wird in der Hierarchie nach oben gelaufen, bis bei einem Logger ein gesetztes Level gefunden wird. 20 von 58
Unit-Testing 21 von 58
Hintergrund Stichworte Test-Driven Development, Test-First-Development Unit-Tests Testautomatisierung Regressionstests Mock-Objekte Testframeworks: z. B. JUnit vgl. System.out.println(), Debugger und assert - Statement. Ziele Isoliertes Testen von einzelnen Einheiten (z. B. Klassen). Testcode für die Testfälle in eigene Klassen auslagern. Tests solle regelmäßig und automatisiert ablaufen können. Testinfrastruktur bereitstellen. hohe Testabdeckung 22 von 58
Unit-Testing: Die Quintessenz Entwicklertest Primär zum Testen kleinerer Einheiten (oft Klassen) in isolierter Form: Testkörper. Ein oder mehrere Testklassen pro Testkörper. Ein oder mehrere Testfälle pro (testwürdiger) Methode. Jeder Testfall wird als Testmethode ausgeführt. Pro Testfall wird eine Instanz der Testklasse erzeugt. Die Testfälle werden grundsätzlich unabhängig voneinander (in beliebiger Reihenfolge) ausgeführt. In Fixtures werden (allgemeine) Vorarbeiten ausgeführt. Strukturierungshilfe: Versammle die Testfälle mit gemeinsamem Fixture in einer Testklasse. Testklassen können zu Suiten zusammengefasst werden. Insbesondere bei häufigen Änderungen sind automatisierte Unit-Tests unumgänglich: Regressionstests. Einbindung in den Build-Prozess (vgl. Maven-Standardarchetyp und Eclipse-Integration). Ziel: grüner Balken! 23 von 58
Unit-Testframeworks: Die Historie Die Wurzeln Erich Gamma und Kent Beck auf dem Flug von Zürich nach Atlanta zur OOPSLA JUnit 3.x Ableiten von der TestCase-Klasse. Damit werden assert...- und fail-methoden geerbt. Namenskonvention: Testfall-Methoden beginnen mit test... setup() und teardown()-methode für Fixture-Aufbau und Aufräumen TestNG stärkere Gruppierungsmöglichkeit für Testfälle Definition von Abhängigkeiten bei den Testfällen. Eigenes HTML-Reporting... JUnit 4.x Statt Namenskonventionen Annotationen. Statischer Import für die assert...- und fail-methoden Übernahme von TestNG-Features... 24 von 58
Ein Beispiel mit JUnit4 (1) Der Test-Körper public class Money { public final int amount; public Money(int amount) { if (amount < 0) throw new NegMoneyException("amount would be: " + amount); this.amount = amount; } public Money add(money money) { return new Money(this.amount + money.amount); } public Money subtract(money money) { return new Money(this.amount - money.amount); } public boolean equals(object o) { if (!(o instanceof Money)) return false; Money money = (Money) o; return this.amount - money.amount == 0; } } public String tostring() { return "Money: " + amount; } 25 von 58
Ein Beispiel mit JUnit4 (2) Festlegen der Test-Umgebung (Fixture) import static org.junit.assert.*; import org.junit.after;... public class MoneyTest { private final Money money1 = new Money(30); private final Money money2 = new Money(20); @Before public void setup() {... } @After public void teardown() {... } @BeforeClass public static void setupbeforeclass() {... } @AfterClass public static void teardownafterclass() {... } 26 von 58
Ein Beispiel mit JUnit4 (3) Die Testmethoden @Test public void testequals() { asserttrue("money objects with equal amount must be equal", money1.equals(new Money(30))); assertfalse("money objects with different ammount cannot be equal", money1.equals(money2)); } @Test public void testadd() { assertequals("30+20=50",new Money(50), money1.add(money2)); } @Test public void testsubtract() { assertequals("30-20=10",new Money(10), money1.subtract(money2)); } @Test(expected=NegMoneyException.class) public void testnegativemoney() { money2.subtract(money1); } 27 von 58
Test durchführen 1. Möglichkeit: StdAlone Einen Testrunner verwenden (Bestandteil von JUnit). 3. Möglichkeit: IDE z. B. Eclipse: vgl. run-dialog Das JAR für JUnit muss dabei im Build-Path des Projektes sein. 3. Möglichkeit Integration in einen Build-Prozess (vgl. ant oder maven) 28 von 58
Weitere Features von JUnit 4.x vgl. JUnit-Dokumentation Parametrisierte Tests: Testmethode durchläuft mehrere Fälle, die als Parameter durch eigene Methode (markiert durch @Parameters)bereitgestellt werden Zeitconstraints über timeout-parameter von @Test @Ignore-Annotation Hamcrest-Matcher als Comfort-Methoden mit präziseren Fehlermeldungen in Verbindung mit assertthat()-methode. Beispiel: assertthat(teststring, both(containsstring("a")).and(containsstring("b"))) Kategorien durch Markierung für getrennte Ausführung Rules (@Rule, Standardimplementierungen und Templates) zum komfortablen Eingriff vor und nach der Testausführung (Bsp. temporäre Files und Exception-Überprüfungen) Class-Rules @FixMethodOrder zur sortierten Ausführung von Testmethoden (Strategie als Parameter, deterministischer Default: MethodSorters.DEFAULT), ab JUnit 4.11 29 von 58
Behandlung von Abhängigkeiten Die gängigen Möglichkeiten Stubs stellen eine rudimentäre Implementierung einer Schnittstelle zur Verfügung Dummys simulieren Verhalten für bestimmte Testfälle Mocks s. u. Voraussetzungen Programmierung gegen Schnittstellen einfache Ersetzbarkeit von Implementierungen (Konfigurationsaufgabe) 30 von 58
Mocking Absicht: Auch Objekte, die von anderen Objekten abhängen, isolierten Tests zugänglich machen. Definition: Mock-Objekt Wirkt als Dummy-Objekt an Stelle eines realen Objekts, das nicht verfügbar ist oder in einem Testfall schlecht benutzbar ist (Datenrückgabe, Exceptions). Zusätzlich: Mock-Objekte sollen Erwartungen formulieren und einen Selbstvalidierungsmechanismus besitzen. Mocking-Frameworks Die Gebräuchlichsten JMock EasyMock Mockito Gemeinsamkeiten Domänenspezifische Sprache, die die Java-Syntax benutzt. Damit können Erwartungen im Hinblick auf Methodenaufrufe (wie oft, Parameter) sowie Rückgabewerte für die Aufrufe formuliert werden. Mockobjekte werden daraus automatisiert erstellt. 31 von 58
TDD: Test-Driven-Development (1) Grundprinzip Testfall wird vor der Implementierung der Funktion erstellt (Test-First). Entwicklung in kleinen Schritten, die jeweils getestet werden. Testfälle müssen in kurzer Zeit durchgeführt werden können. Testfälle repräsentieren einen Teil der Spezifikation und treiben die Entwicklung. Integration Rahmen: agile Vorgehensweise TDD und Refactoring: automatisierte Tests sorgen für funktionale Integrität Refactoring sorgt für strukturelle Integrität Nur getesteter Code wird in Source-Code-Verwaltungssystem eingecheckt. Häufige Integration auf Integrationsserver. 32 von 58
TDD: Test-Driven-Development (2) Das Vorgehen im Einzelnen Die elementaren Schritte Testfall erstellen. Kompilierung scheitern lassen. Gerade soviel Code erstellen, dass der Test fehlschlägt. Test erfüllen Die Farbübergänge des Erfolgsbalkens rot --> grün --> rot --> grün (Erster Übergang betrifft dabei Compilierung) Die Behandlung von Bugs Ähnlich Erst Testfall erstellen, der (infolge des Bugs) fehlschlägt,... 33 von 58
Build-Management 34 von 58
Überblick Grundprinzipien von Build- Im Rahmen eines Projektes gibt es verschiedene Teilziele (z. B. Initialisieren, Übersetzen, Release fertigstellen, Doku. erstellen, Aufräumen): Targets. Für jedes Ziel müssen bestimmte Aufgaben erledigt werden (z. B. Verzeichnisse erstellen oder löschen, Dateien kopieren, javac oder javadoc oder jar aufrufen): Tasks. Die Teilziele können Abhängigkeiten besitzen (z. B. Übersetzen vor Testen): Dependencies. Tasks werden nur ausgeführt, falls erforderlich (z. B. Übersetzen nur dann, wenn nötig): Incremental Build. Für Flexibilität und Wiederverwendung ist Parametrisierung erforderlich (z. B. Pfade, Compilerdirektiven): Properties. Targets, Tasks, Dependencies etc. werden in Konfigurationsdateien festgelegt: Build-Files. Historie "althergebrachte" make, gnumake, nmake Einschränkungen bezogen auf eine plattformspezifische Shell: Shell-Kommandos als Tasks, Bezug zu Shellvariablen Aktuelle Build- in der Java-Welt Ant, Maven, Gradle 35 von 58
Ant: Charakteristiken plattformunabhängiges Build-Tool Buildfile hat XML-Format Es sind viele Standardtasks vorhanden. Parametrisierung über Java-Properties. Weitere Tasks können in Form von Java-Klassen definiert werden. Auf teilweise mächtige Shell-Kommandos muss verzichtet werden (zumindest deren direkte Einbindung). 36 von 58
Ant: Beispiel Build-File (1) <project name="helloproject" default="dist" basedir="."> <!-- == set global properties for this build == --> <property environment="env" /> <property name="appname" value="greetings" /> <!-- base directories --> <property name="src" location="src"/> <property name="build" location="build"/> <property name="dist" location="dist"/> <property name="lib" location="lib"/> <!-- source directories --> <property name="src.main" location="${src}/main"/> <property name="src.test" location="${src}/test"/> <!-- build directories --> <property name="build.main" location="${build}/main"/> <property name="build.test" location="${build}/test"/> <property name="build.testreports" location="${build}/testreports"/> <property name="build.javadoc" location="${build}/javadoc"/> <property name="build.log" location="${build}/log"/> <property name="build.dist" location="${build}/dist"/> 37 von 58
Ant: Beispiel Build-File (2) <!-- == filesets ==--> <fileset id="mainsources" dir="${src.main}"> <include name="**/*.java" /> </fileset> <fileset id="testsources" dir="${src.test}"> <include name="**/*test.java" /> </fileset> <!-- === external libs classpath === --> <path id="externallibs"> <pathelement location="${lib}/junit.jar" /> </path> 38 von 58
Ant: Beispiel Build-File (3) <!-- ================================================= --> <target name="init"> <!-- Create the build directories --> <mkdir dir="${build.main}" /> <mkdir dir="${build.test}" /> <mkdir dir="${build.testreports}" /> <mkdir dir="${build.javadoc}" /> <mkdir dir="${build.log}" /> <mkdir dir="${build.dist}" /> <!-- Create the time stamp --> <tstamp/> <!-- Create log file --> <record name="${build.log}/build-${dstamp}-${tstamp}.log" /> </target> <!-- ================================================= --> <target name="compile" depends="init" description="compile all" > <javac srcdir="${src.main}" destdir="${build.main}"> <classpath refid="externallibs" /> </javac> <javac srcdir="${src.test}" destdir="${build.test}"> <classpath> <pathelement location="${build.main}" /> <path refid="externallibs" /> </classpath> </javac> </target> 39 von 58
Ant: Beispiel Build-File (4) <!-- ================================================= --> <target name="test" depends="compile" description="unit testing" > <delete dir="${build.testreports}" /> <mkdir dir="${build.testreports}" /> <junit printsummary="yes" fork="yes"> <classpath> <pathelement location="${build.main}" /> <pathelement location="${build.test}" /> <path refid="externallibs" /> </classpath> <formatter type="plain" /> <batchtest todir="${build.testreports}"> <fileset refid="testsources" /> </batchtest> </junit> </target> <!-- ================================================= --> <target name="dist" depends="test" description="generate application jar" > <jar destfile="${build.dist}/${appname}-${dstamp}.jar"> <fileset dir="${build.main}"> <include name="**/*.class" /> </fileset> </jar> </target> 40 von 58
Ant: Beispiel Build-File (5) <!-- ================================================= --> <target name="clean" description="clean up" > <!-- Delete the ${build} and ${dist} directory trees --> <delete dir="${build}"/> <delete dir="${dist}"/> </target> <!-- ================================================= --> <target name="doc" depends="init" description="generate javadocs" > <javadoc destdir="${build.javadoc}" windowtitle="${appname} Dokumentation"> <fileset refid="mainsources" /> <classpath> <path refid="externallibs" /> </classpath> </javadoc> </target> <!-- ================================================= --> <target name="run" depends="compile" description="run appl. from the build "> <java classname="hi.hello"> <arg value="bob" /> <classpath> <pathelement path="${build.main}" /> </classpath> </java> </target> 41 von 58
Ant: Beispiel Build-File (6) <!-- ================================================= --> <target name="rundist" depends="dist" description="run the distribution"> <java classname="hi.hello"> <arg value="fred" /> <classpath> <fileset dir="${build.dist}"> <include name="**/*.jar"/> </fileset> </classpath> </java> </target> <!-- ================================================= --> <target name="all" depends="clean, dist, doc" description="clean, generate the distribution and the docu" /> </project> 42 von 58
Ant: Ausführen von Build-Files Syntax ant [options] [target [target2 [target3]...]] Beispiele für options -f <buildfilename> -D<property>=<value> -propertyfile <name> 43 von 58
Maven: Motivation Vereinheitlichung von Build-Prozessen für Opensource-Projekte Ant: Toolbox zum Erstellen von Builds --> Maven: Standards und Muster für Projektmanagement im Hinblick auf vereinheitlichte, wiederverwendbare Builds Die üblichen Aufgaben: Prepare, Compile, Assemble, Test, Run, Deploy,... Bereitstellen von Bibliotheken und Maven-Erweiterungen (Plugins) in zentralen Repositorys Basierend auf Abhängigkeitsbeziehungen werden die benötigten Bibliotheken aus den Repositories automatisch heruntergeladen. Fokus: "Building the Application" not "Building the Build" 44 von 58
Die Prinzipien von Maven Convention over configuration Ein standardisiertes Verzeichnissystem für Projekte Ein primäres Ergebnis pro Projekt Standards für Namenskonventionen Declarative execution POM (Project Object Model) Vererbungsprinzip und Basis-POM Reuse of build logic Separation of concerns durch ein Plugin-Konzept Deklarative Ausführung der Plugins Coherent organization of dependencies Artefact A (JAR,...) hängt ab von Artefact B version x Repositories (remote, local) Abhängigkeiten werden durch Nachschlagen in den Repositories aufgelöst Transitive Abhängigkeiten werden unterstützt 45 von 58
Maven: Beispiel POM <project xmlsn="... > <groupid>de.fhaugsburg.prog</groupid> <artifactid>junit4demo</artifactid> <packaging>jar</packaging> <version>1.0-snapshot</version> <build> <plugins> <plugin> <groupid>org.apache.maven.plugins</groupid> <artifactid>maven-compiler-plugin</artifactid> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupid>junit</groupid> <artifactid>junit</artifactid> <version>4.0</version> <scope>test</scope> </dependency> </dependencies> </project> 46 von 58
Builds und IDE am Beispiel Eclipse Die wesentlichen Eigenschaften Ein integrierter Build-Mechanismus als Default (siehe Project Properties --> Builders) Eigene Builder können dort definiert werden (z. B. ant-basiert) Ant-Buildfiles können in eigenem View ausgeführt werden. Maven kann per Plugin integriert werden. Eigener Projekttyp Maven-Projekt: Beim Import werden alle Abhängigkeiten in den Classpath aufgenommen. 47 von 58
Versionsverwaltung 48 von 58
Versionsverwaltungssysteme allgemein Das Anliegen Zentrale Ablage von Verzeichnissen und Dateien wie z. B. Quellcodes u. ä.. Verwaltung von unterschiedlichen Versionsständen. Dabei werden jeweils gespeichert: die Änderungen im Vergleich zur Vorgängerversion (Platzersparnis!), Metainformationen wie Benutzer und Änderungs-Kommentar. Gemeinsames Arbeiten von verschiedenen Entwicklern an diesen Dateien soll möglich sein, mit Konfliktauflösung. Abspalten und Zusammenführen von Entwicklungszweigen ist zu unterstützen. Wichtige Versionsverwaltungssysteme CVS (Concurrent Versioning System): ein Vorläufer SVN (Subversion) GIT und einige kommerzielle Programme 49 von 58
Die wichtigsten Eigenschaften von SVN zentrales Repository und lokale Arbeitsbereiche: Check-In, Check-Out Versionierung von Dateien und Verzeichnissen (Dabei Berücksichtigung von Löschen und Umbenennen) Atomare Check-Ins Parallelität: Unterstützung von Lock-Modify-Unlock und Copy-Modify-Merge Unterstützung von Metadaten (Properties für Elemente im Repository) flexible Client-Server-Struktur (lokaler File-Zugriff, svn-server oder Apache-Modul) Festlegen von benutzerbezogenen Zugriffsbeschränkungen. 50 von 58
Zentrale Konzepte von SVN Revisions und Changesets Jede Transaktion im Repository erzeugt eine neue (fortlaufende Revisionsnummer). Der aktuelle Stand im Repository wird dieser Revision zugeordnet (Repository in Revison x). Die Menge der geänderten Dateien und Verzeichnisse, ausgehend von der vorhergehenden Revision, nennt man Changeset. Trunk, Tags und Branches Trunk: Der Hauptenwicklungspfad Linearer Entwicklungspfad: Codefreeze während der Vorbereitung einer Release erforderlich. Branches: Verzweigungen, um parallele Fortentwicklung von Versionen zu ermöglichen Problem: spätere Zusammenführung von Branches Tag: Markierung einer Version zu einem Zeitpunkt (Revision) durch einen frei wählbaren Bezeichner. Schnitt durch das Repository i. d. R. als potentieller Wiederaufsetzpunkt 51 von 58
Release-Planung zugehörige Branches und Tags mögliche Optionen Release-Branch (für Main-Release) wird offengehalten, solange das Release beim Kunden im Einsatz ist und evtl. Erweiterungen oder Fehlerbehebungen in Minor-Releases erfolgen kann. Release-Branch wird nach Fertigstellung des Releases gelöscht. Patch-Releases erfordern eine Neuanlage eines Branches auf Basis des REL-Tags des Releases. 52 von 58
typische Repository-Struktur in SVN projekt +--trunk +--src... +--tags +--PREP-1.0.0 +--src... +--REL-1.0.0... +--branches +--RB-1.0.0... 53 von 58
Verwaltung des SVN-Repositories Shell- svnadmin, svn Sowohl für ein lokales Repository als auch Remote-Repository einsetzbar Datei-Explorer Beispiel Windows: Integration mit Tortoise IDEs Beispiel Eclipse: Plugins Subclipse und Subversive 54 von 58
SVN: Der Start Details siehe http://svnbook.red-bean.com/ Erzeugen eines Repositorys svnadmin create.../svn-repos/myproject Benutzer- und Zugriffsrechte festlegen Dateien svnserve.conf passwd authz im Verzeichnis myproject/conf Start des Subversion-Servers dedizierter SVN-Server: snvserve -d -r.../svn-repos Testen der Verbindung svn list svn://localhost/myproject Konfiguration von Client-Properties (Bsp. Editor für Änderungskommentare festlegen) Datei config unter Unix im Verzeichnis <home>/.subversion unter Windows im Ordner C:\Dokumente und Einstellungen\<username>\Anwendungsdaten 55 von 58
Zentrale Kommandos zur Versionsverwaltung (1) Projektstruktur anlegen in temporärem Verzeichnis Verzeichnisse trunk, branches, tags und ggf. Unterverzeichnisse anlegen im temporären Verzeichnis: svn import svn://localhost/myproject --message "mein Kommentar" --username <root> --password <root> (Wenn die Optionen fehlen, werden sie interaktiv abgefragt) Check-out des Arbeitsbereiches svn checkout svn://localhost/myproject/trunk Achtung: Metadaten von SVN in Unterverzeichnissen.svn 56 von 58
Zentrale Kommandos zur Versionsverwaltung (2) Aktualisieren des lokalen Arbeitsbereiches svn update (im Hauptverzeichnis des Checkouts oder Unterverzeichnissen) Regeln beim update: Falls lokal keine Änderung durchgeführt wurde, wird eine evtl. vorhandene neuere Revision im Repository ausgecheckt. Falls lokal eine Änderung da, aber keine neuere Revision im Repository da ist, wird die Datei als modified markiert. Falls lokal eine Änderung da und eine neuere Revision im Repository vorhanden ist, wird versucht diese lokal mit der lokalen Kopie zu mergen (nur bei Textdateien). Falls die Unterschiede gleiche Bereiche betreffen, wird lokal eine Variante erzeugt, die beide Versionen der unentscheidbaren Bereiche markiert darstellt. In diesem Fall manuelle Auflösung erforderlich. 57 von 58
Zentrale Kommandos zur Versionsverwaltung (3) Versionen vergleichen svn diff Hinzufügen von neuen Verzeichnissen und Dateien svn add Lokale Änderungen in das Repository aufnehmen svn commit Gelingt nur, wenn Revision im Repository und lokalen Arbeitsbereich identisch sind. Ansonsten vorher update mit evtl. Konfliktauflösung durchführen Zustandsinformationen abholen svn status 58 von 58