Developing with TeamCity 7.1: from remote run to feature branches – Pavel Sher and Dmitry Neverov at Hypoport on Feb 18th

Gemeinsam mit der Java Usergroup Berlin-Brandenburg präsentieren wir am 18. Februar den Vortrag von Pavel Sher und Dmitry Neverov Developing with TeamCity 7.1: from remote run to feature branches. Einlaß und Zeit für Networking startet ab 18:30 Uhr. Der Vortrag beginnt um 19:00 Uhr.

Abstract
Continuous integration tools play important role in software development these days. In an ideal world every commit in version control system must be checked by running a build on it, builds must be fast and broken builds must be fixed quickly. But we do not live in the ideal world, so how we can protect ourselves from breaking changes? How we can minimize effects of breaking changes on our teammates?

This talk covers effective ways of using TeamCity, a CI server from JetBrains, with different version control systems. You will see how to commit only a verified code with help of pre-tested commits, how to use personal branches if you’re using Git or Mercurial, and how to implement your favorite DVCs workflow using TeamCity feature branches support.

Speaker

Dmitry Neverov – software developer in TeamCity project. Dmitry works in JetBrains for almost 3 years and his responsibilities include wide range of tasks related to distributed version control systems, from integration with Git and Mercurial to feature branches support in TeamCity core.

Pavel Sher – senior software developer and project manager in TeamCity. Pavel started working on TeamCity almost from the beginning, 7 years ago. As such he took part in many aspects of TeamCity functionality and is responsible for many architectural decisions in TeamCity core.

Anmeldung
Anmeldung erfolgt wie üblich über die Xing JUG BB Event-Einladung.

Tipp
Zusätzlich zu dem Vortrag gibt’s noch ein Goodie von Jetbrains:) Lasst euch überraschen!

jb

Unit- und Integration-Test eines MicroService mit Maven

Was ist ein MicroService? In unserem Kontext ist es ein (kleines) Modul der Gesamtanwendung, welches seinen Dienst als Webapplikation (.war) anbietet. Das konkrete Schnittstellen-Protokoll ist für diesen Artikel nicht entscheidend. Möglich ist klassisch SOAP, REST aber auch Spring HttpInvoker. Ein MicroService ist eine gute Möglichkeit Sollbruchstellen in die Gesamtanwendung einzubauen.

Nun zum eigentlichen Thema: Wie teste ich einen solchen Service? Im Grunde wie immer, d.h. viele Unit-Tests und ein paar ausgewählte Integration-Tests. Die Integration-Tests sollen zeigen, dass die angebotenen Schnittstellen des Services funktionieren und alles richtig konfiguriert ist. Die Integration-Tests, die die Gesamtanwendung, d.h. das Netz aller Services testet, ist hier nicht Bestandteil.

Wie macht man das jetzt mit Maven? Eine Variante ist die Unit-Tests und Integration-Tests zu trennen. Vor der integration-test Phase einen Tomcat zu starten, dann seine Integration-Tests auszuführen und anschließend wieder den Tomcat zu stoppen. Tomcat, weil wir diesen nicht nur lokal einsetzen und somit “gleicher” zur Produktion sind. Eine echte Plugin-Auswahl fand nicht statt, wir sind einfach mit dem tomcat7-maven-plugin gestartet.

Mit Maven Tomcat automatisch starten und stoppen

  <build>
    <plugins>
      ..
      <plugin>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.0-beta-1</version>
        <configuration>
          <contextFile>${project.basedir}/src/test/resources/test-context.xml</contextFile>
        </configuration>
        <executions>
          <execution>
            <id>tomcat-run</id>
            <goals>
              <goal>run-war-only</goal>
            </goals>
            <phase>pre-integration-test</phase>
            <configuration>
              <fork>true</fork>
            </configuration>
          </execution>
          <execution>
            <id>tomcat-shutdown</id>
            <goals>
              <goal>shutdown</goal>
            </goals>
            <phase>post-integration-test</phase>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>

Das Starten des Tomcats binden wir an die pre-integration-test Phase (Zeile 17). Wichtig ist fork auf true zu setzen (Zeile 19), da sonst Maven nach Start des Tomcats stehen bleibt. Das Beenden binden wir entsprechend an die post-integration-test Phase (Zeile 27). Wenn keine context.xml in der webapp enthalten ist, muss eine per contextFile angeben werden (Zeile 9).

So, unser Tomcat startet und stoppt nun beim Ausführen von mvn integration-test.

Mit Maven innerhalb eines Modules Unit- und Integration-Tests trennen

Nächster Schritt ist die Unit- und Integration-Tests zu trennen. Im Netz empfehlen einige dies in separaten Maven-Modulen zu tun. Wir finden es sinnvoller dies über die Test-Phasen zu steuern:

  <build>
    <plugins>
      ..
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <configuration>
          <skip>true</skip>
          <trimStackTrace>false</trimStackTrace>
        </configuration>
        <executions>
          <execution>
            <id>unit-tests</id>
            <phase>test</phase>
            <goals>
              <goal>test</goal>
            </goals>
            <configuration>
              <skip>false</skip>
              <includes>
                <include>**/*Test.java</include>
              </includes>
              <excludes>
                <exclude>**/*IntegrationTest.java</exclude>
              </excludes>
            </configuration>
          </execution>
          <execution>
            <id>integration-tests</id>
            <phase>integration-test</phase>
            <goals>
              <goal>test</goal>
            </goals>
            <configuration>
              <skip>false</skip>
              <includes>
                <include>**/*IntegrationTest.java</include>
              </includes>
            </configuration>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>

In Zeile 8 wird das Default-Test-Verhalten vom maven-surefire-plugin ausgeschaltet. Durch zwei einzelne <execution/>'s können wir unterschiedliche <configuration/>'s an unterschiedliche Phasen binden (Zeile 14 und 30). In der test Phase werden alle Klassen mit Endung Test.java ausgeführt außer jene mit Endung IntegrationTest.java. In der integration-test Phase werden nun lediglich alle Klassen mit Endung IntegrationTest.java ausgeführt. Vielen Dank an HDave, der letzteres auf Stackoverflow schön erklärt.

Hast du ähnliche Anwendungsfälle? Wie hast du diese gelöst?

Oliver Gierke von SpringSource kommt am 14. Juni 2012 nach Berlin

Gemeinsam mit der Java Usergroup Berlin-Brandenburg präsentieren wir am 14. Juni den Vortrag von Oliver Gierke Huch, wo ist meine Architektur hin?. Einlaß und Zeit für Networking startet ab 18:30 Uhr. Der Vortrag beginnt um 19:00 Uhr.

Vortrag
Wenn Applikationen über eine bestimme Größe oder einen bestimmten Zeitraum hinaus wachsen wird Modularität ein Kernaspekt für Wartbarkeit. Designentscheidungen die getroffen wurden sind kaum noch im Code wiederzufinden, Abhängigkeiten zwischen einzelnen Modulen der Applikation wachsen oft wild. Der Vortrag Patterns und Best Practices rund um generelle Code-Organisation und Package-Strukturen vor um eine solide Grundlage für langlebige Java-Applikationen zu legen, sowie eine möglichkeit mit Spring lose gekoppelte Komponenten und dedizierte Erweiterungspunkte in Applikationen zu definieren und zu verwenden.

Speaker
Oliver Gierke ist Teil des Spring Data Teams bei SpringSource, a division of VMware und leitet dort das JPA, MongoDB und Core Modul. Seit über 6 Jahren widmet er sich dem Entwicklen von Java Enterprise Applikationen, Open Source Projekten und ist Mitglied der JPA 2.1 Expert Group. Seine Arbeitsschwerpunkte liegen im Bereich Softwarearchitektur, Spring und Persistenztechnologien. Er ist regelmäßiger Sprecher auf deutschen und internationalen Konferenzen sowie Autor von Fachartikeln.

Sonar Analyse eines Maven Projektes mit Cobertura und AspectJ

Wir verwenden unter anderem Sonar, um unsere Codequalität zu analysieren. Sonar bietet viel Spannendes. Interessant ist auch die Analyse der Testabdeckung. Hierzu kann man Cobertura verwenden. Im Zusammenspiel mit einem Projekt, welches AspectJ einsetzt kann es zu Problemen kommen, wenn man versucht das Kompilieren der Anwendung mit der Sonar-Analyse zu kombinieren.


mvn clean install sonar:sonar -Dmaven.test.failure.ignore=true

Die Lösung liegt wie so oft in RTFM. Der empfohlene Weg eine Sonar-Analyse durchzuführen ist es zuerst die Anwendung zu bauen und danach die Analyse zu starten.


mvn clean install -Dtest=false -DfailIfNoTests=false
mvn sonar:sonar

Wichtig ist hierbei beim Bauen der Anwendung die Tests auszuschalten, da diese in der Sonar-Analyse laufen und man sonst unnützerweise zwei Testläufe hat.

Nun können wir uns die Testabdeckung der einzelnen Module ansehen.

Hibernate4 verwendet jboss-logging statt slf4j. Durch die log4j Bridge leiten wir die Logs zu logback.

Bei der Migration auf Hibernate 4 haben wir festgestellt, dass Hibernate statt wie bisher slf4j nun ihre eigene Logging API jboss-logging verwendet. Glücklicherweise logt diese API standardmäßig auf log4j. Durch Einsatz der log4j-over-slf4j Bridge lenken wir die Logs wieder auf slf4j, um sie dann per logback zu loggen.

Es ist wichtig bei der Nutzung dieser Bridge keine log4j.jars im Klassenpfad zu haben! Es dürfen kein direkten oder transitiven log4j.jar Abhängigkeiten mehr existieren. Mit maven kann das leicht über ein mvn dependency:tree geprüft werden. Das exclude erfolgt dann beispielhaft so:

    <dependency>
      <groupId>sample.groupId</groupId>
      <artifactId>sample.artifactId</artifactId>
      <scope>compile</scope>
      <exclusions>
        <exclusion>
          <groupId>log4j</groupId>
          <artifactId>log4j</artifactId>
        </exclusion>
      </exclusions> 
    </dependency>

Podcast und Slides von Chris Chedgey’s Talk Restructuring: Improving the modularity of an existing code-base

Chris hat uns dankenswerter Weise die Tonaufnahme und die Folien zur Verfügung gestellt.

Die Slides sind aktualisiert und enthalten Beispiele der Restructuring Strategies! Schaut sie euch ruhig noch mal an.

Berlin Expert Days mit 4 Vorträgen unserer Mitarbeiter

Die Berlin Expert Days 2012 haben ein sehr interessantes Programm. Neben bekannten Speakern erhalten auch weniger bekannte Speaker die Gelegenheit sich und Ihre Erfahrungen zu präsentieren. U.a. gibt es 4 Vorträge von Hypoport Mitarbeitern:

Verena Würfel, eine freie Mitarbeiterin, präsentiert sich mit Was Frankenstein und Anwendungen gemeinsam haben.

Chris Chedgey von Headway Structure101 kommt am 6. März 2012 nach Berlin

Gemeinsam mit der Java Usergroup Berlin-Brandenburg präsentieren wir am 6. März den Vortrag von Chris Chedgey Restructuring: Improving the modularity of an existing code-base. Einlaß und Zeit für Networking startet ab 18:30 Uhr. Der Vortrag beginnt um 19:00 Uhr.

Vortrag
When a code-base reaches a certain age, it starts to creak, grind, rot. It seems like everything uses everything. Developers do not understand the big picture and start duplicating or creating parallel worlds of design, making things even worse. Architects are uneasy and sleep badly. Managers notice how long it takes to get new features into the product, and to get the bugs out of incremental releases, and they make sure nobody sleeps well. Now is the time for the code-base to be restructured, or die a painful death.

This talk covers the most common structural problems, gives strategies for fixing them with minimal impact to the logical design, and shows how this reduces coupling and complexity, improves modularity, and can be used to establish an architecture that the whole team understands and that helps the developers as they edit the code. In other words, how to stop the rot, and keep it stopped.

The principles and strategies will be illustrated by examples.

Speaker
Chris is a founder of Headway Software (Structure101). He has an MSc. in Computer Science from Trinity College Dublin. He has 28 years of experience in commercial software development, notably on large military and aerospace projects in Canada, including 5 years on the International Space Station project. He has 2 lovely daughters in college and lives on the south coast of Ireland, with a big dog, 2 goats and a boa constrictor.

Anmeldung
Anmeldung erfolgt wie üblich über die Xing JUG BB Event-Einladung.

Tipp
Zusätzlich zu dem Vortrag wird noch eine Lizenz für Structure101 und eine Lizenz für Restructure101 verlöst. Wir drücken dir die Daumen:)


Meeting sponsored by Structure101 for Tangle free software

@Mock unused variable mit IntelliJ IDEA kein Problem

Ich bin froh, dass IntelliJ IDEA die Standard IDE bei uns ist. Hier ein kleines Beispiel wie flexibel – manche nennen es intelligent – IntelliJ ist:)

Die Inspection unused variable warnt vor unbenutzten Variablen. Nach CleanCode lösche ich solche Variablen sehr gerne sehr schnell. Problematisch wird dies wenn man z.B. @Mock von Mockito oder @Resource von Spring verwendet. Was diese Annotationen inhaltlich machen gibt es ein anderes Mal. Für jetzt ist interessant, dass eine mit @Mock annotierte Variable nicht weiter im Code verwendet wird. Je nach gewählten Farbschema sieht das dann so aus:

Weiterlesen

Eine Migration auf SLF4J und Logback lohnt sich!

Bisher haben wir (Team emma) log4j eingesetzt. Es ist meines Erachtens derzeit der Standard wenn es ums Logging in Java Anwendungen geht. Es tut was es soll und das gut. Warum also wechseln? Wie so oft sind es die Kleinigkeiten, die den Unterschied ausmachen.

SLF4J

Die Simple Logging Facade for Java kurz SLF4J ist eine Logging Facade für diverse Logging Frameworks, wie java.util.logging, log4j und logback. Warum der Einsatz von SLF4J sinnvoll ist wird u.a. auf der SLF4J FAQ beschrieben. Durch den Einsatz der xxx-over-slf4j Bridges ist es möglich die Logs von Dritt-Bibliotheken auf SLF4J umzulenken. Verwendest du Logback als Implementierung von SLF4J, so kannst du mit einer Konfiguration deine Logs und die der Dritt-Bibliotheken steuern.

[update 30.4.2012:]
Es ist wichtig bei der Nutzung der xxx-over-slf4j Bridges keine xxx.jars im Klassenpfad zu haben! Wenn man z.B. das log4j-over-slf4j.jar nutzt, dürfen kein direkte oder transitive log4j.jar Abhängigkeiten mehr existieren. Mit maven kann das leicht über ein mvn dependency:tree geprüft werden.

Logback

Logback gilt als offizieller Nachfolger von log4j und wurde ebenso wie log4j von Ceki Gülcü gegründet. Es wird aktiv weiterentwickelt. Wenn du selbst Hand anlegen willst kannst du dir das auf GitHub verwaltete Projekt clonen. Die wesentlichen Wechselgründe stellt die Projektseite zusammen. Einiges davon kann auch mit log4j direkt oder durch zusätzliche Skripte realisiert werden, doch mit Logback wirkt es einfacher und eleganter. Unsere Wechselgründe sind im Wesentlichen folgende:

  • Sehr gute Dokumentation
    http://logback.qos.ch/manual/introduction.html
  • Automatisches Neuladen der Konfiguration
    <configuration scan="true">
      ..
    </configuration>
    
  • Automatisches Komprimieren archivierter Logs
      <appender name="all-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>emma-all.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
          <fileNamePattern>emma-all.%d{yyyy-MM-dd}.log.gz</fileNamePattern>
          <maxHistory>30</maxHistory>
        </rollingPolicy>
        ..
      </appender>
    

    Diese Konfiguration archiviert und komprimiert jeden Tag die Logs. Die Magie steckt in Zeile 4. Durch Angabe des Datumsformats wird auch der Archivierungszeitraum bestimmt. %d{yyyy-ww} würde erst nach einer Woche die Logs archivieren. Durch Angabe der Endung zip oder gz werden nun die zu archivierenden Logs im gewählten Format komprimiert.

  • Automatisches Löschen von alten Logs
    In der vorherigen Konfiguration bewirkt das Setzen von maxHistory auf 30, dass 30 Archive gespeichert werden. Wenn das 31 Archiv erstellt wird, wird automatisch das älteste Archiv gelöscht. Operations wird dich lieben, oder ihre Aufräumskripte löschen können.
  • if, then, else in Konfigurationen
      <insertFromJNDI env-entry-name="java:comp/env/emma-nl/logDir" as="logDir"/>
      <if condition='isNull("logDir")'>
        <then>
          <property name="logDir" value="${catalina.base}/logs"/>
        </then>
      </if>
    

    Diese Konfiguration setzt das logDir auf ${catalina.base}/logs. Willst du einen anderen Ort so musst du nur den JNDI Parameter emma-nl/logDir entsprechend setzen.

  • Keine isDebugEnabled Prüfung mehr nötig
    Eine normale Log-Anweisung enthält immer die Kosten des Bauens der Meldungsparameter. D.h. sowohl die Umwandlung in Strings als auch das Addieren von Teil-Strings. Unabhängig davon, ob der entsprechende Log-Level an oder aus ist. Will oder musst du auf Performance achten, so ist bisher die Anweisung mit einem isDebugEnabled() zu klammern:

    if(log.isDebugEnabled()) {
      log.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    }
    

    Durch das parameterisierte Logging sind diese if‘s nicht mehr nötig.

      log.debug("Entry number: {} is {}", i, String.valueOf(entry[i]));
    

    Oder bei mehr als zwei Parametern:

      Object[] paramArray = {newVal, below, above};
      log.debug("Value {} was inserted between {} and {}.", paramArray);
    
  • Logging ist ausschaltbar
    Da bei Tests rot und grün alles entscheidend ist, schalten wir das Logging für Test-Runs aus. Unexpected Exceptions sind Ausnahmen davon und müssen geloggt werden. Dies geschieht aber über den normalen TestNG Mechanismus.

    <configuration scan="true">
      ..
      <root level="OFF">
        <appender-ref ref="CONSOLE"/>
      </root>
    </configuration>
    
  • Root Cause am Anfang des Exception Logs
    Mit Logback ist es möglich die Reihenfolge des Stacktraces umzudrehen. Warum sollte man das machen? Ein normaler Stacktrace sieht in etwas so aus:

    java.lang.RuntimeException: Sorry, try again later
      at BookController.gamma(BookController.java:26)
      at BookController.beta(BookController.java:20)
      at BookController.alpha(BookController.java:18)
      at BookController.main(BookController.java:32)
    Caused by: java.lang.RuntimeException: Unable to save order
      at BookService.zeta(BookController.java:51)
      at BookService.epsilon(BookController.java:45)
      at BookService.delta(BookController.java:43)
      at BookController.gamma(BookController.java:24)
      ... 8 common frames omitted
    Caused by: java.lang.RuntimeException: Database problem
      at BookDao.iota(BookController.java:66)
      at BookDao.theta(BookController.java:60)
      at BookDao.eta(BookController.java:58)
      at BookService.zeta(BookController.java:49)
      ... 11 common frames omitted
    Caused by: java.lang.RuntimeException: Omega server not available
      at BookDao.iota(BookController.java:64)
      ... 14 common frames omitted
    

    Der eigentliche Grund ist weit unten zu finden. Durch die Angabe von %rEx im pattern dreht man diese Schreibweise um.

      <appender name="file" class="..">
        <encoder>
          <pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %m%n%rEx</pattern>
        </encoder>
      </appender>
    

    Statt Caused by steht der Grund in der ersten Zeile gefolgt von den Wrapped by Exceptions:

    java.lang.RuntimeException: Omega server not available
      at BookDao.iota(BookController.java:64)
    Wrapped by: java.lang.RuntimeException: Database problem
      at BookDao.iota(BookController.java:66)
      at BookDao.theta(BookController.java:60)
      at BookDao.eta(BookController.java:58)
      at BookService.zeta(BookController.java:49)
    Wrapped by: java.lang.RuntimeException: Unable to save order
      at BookService.zeta(BookController.java:51)
      at BookService.epsilon(BookController.java:45)
      at BookService.delta(BookController.java:43)
      at BookController.gamma(BookController.java:24)
    Wrapped by: java.lang.RuntimeException: Sorry, try again later
      at BookController.gamma(BookController.java:26)
      at BookController.beta(BookController.java:20)
      at BookController.alpha(BookController.java:18)
      at BookController.main(BookController.java:32)
    

    Weitere Details hat Tomasz Nurkiewicz in seinem Blog NoDefBlogFound publiziert.

  • Default-Logback Konfiguration überschreiben
    Die Default-Logback Konfiguration kann durch das Property logback.configurationFile überschrieben werden. Hierbei ist egal ob du logback-test.xml und/oder logback.xml konfiguriert hast, die durch logback.configurationFileangegebene Konfiguration gewinnt! Dies ist sehr praktisch, wenn du lokal die Konfiguration überschreiben willst um z.B. das Hibernate Log anzuschalten.
    -Dlogback.configurationFile=/path/to/my-logback-config.xml
    
  • Mach deine Konfiguration DRY durch Einsatz von Properties
    Logback ermöglicht es dir, Properties zu definieren. So kannst du Teile deiner Konfiguration wieder verwenden. Willst du zum Beispiel für jeden Appender das gleiche LogPattern verwenden, kannst du ein propertymit den Gemeinsamkeiten definieren und mittels ${} Notation verwenden.
    ..
    <property name="logPattern" value="%d{ISO8601} [%thread] %-5level %X{loanFileNr} %logger{36} - %m%n%rEx"/>
    
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
        <pattern>${logPattern}</pattern>
      </encoder>
    </appender>
    
    <appender name="emma-all-file" class="ch.qos.logback.core.rolling.RollingFileAppender">
      ..
      <encoder>
        <pattern>${logPattern}</pattern>
      </encoder>
    </appender>
    ..
    

    Properties können auch aus einer Datei oder vom Classpath geladen werden. Schau einfach mal in die sehr gute Dokumentation.

  • Properties können mit Defaults definiert werden
    Durch den Einsatz von Properties und Defaults kannst du deine Konfiguration variabel und robust gestalten. Durch die :- Notation kann der Default angegeben werden. In Zeile 4 wird der logLevel auf ERROR gesetzt, sofern kein externer Wert vorhanden ist. Default Werte können nur Strings sein. D.h. ein Default ${catalina.base}/logs ist nicht möglich. Hierzu musst du die Variante mit if verwenden.

    ..
    <insertFromJNDI env-entry-name="java:comp/env/logLevel" as="logLevel" />
    
    <root level="${logLevel:-ERROR}">
      <appender-ref ref="CONSOLE"/>
    </root>
    ..
    

Wenn du jetzt überlegst SLF4J und Logback einzusetzen, solltest du dir auf jeden Fall den SLF4J-Migrator ansehen. Dieser konvertiert in Windeseile dein Projekt.

Viel Spaß beim Logging!