Google App Engine für Java unterstützt JPA und JDO. Beide Implementierungen sind nicht 100% vollständig. Nicht unterstütze JPA-Features sind in der Google Dokumentation genannt (ganz unten). Zusätzlich sollte jeder Google-Cloud-Aspirant wissen:

  1. Primärschlüssel von Entitäten in AppEngine müssen den Typ com.google.appengine.api.datastore.Key haben. Dies ist nicht konform zur JPA-Spezifikation.
  2. Nach Aufruf von EntityManager.persist ist die Id des persistierten Entity-Objekts nicht gesetzt. Dies ist ebenso nicht JPA-Spec-konform.

Ein Aufruf an EntityManager.refresh hilft, damit die Id gesetzt ist und z.B. an den Client zurückgeliefert werden kann, wie folgendes Beispiel zeigt:

QuestEntity questEntity = new QuestEntity();
questEntity.setMessage(quest.message);
entityManager.persist(questEntity);
entityManager.refresh(questEntity); // to retrieve the id
assert questEntity.getId() != null;
quest.id = KeyFactory.keyToString(questEntity.getId());

Mit einer JPA-Implementierung wie Hibernate oder EclipseLink wäre questEntity.id bereits nach Aufruf von “persist()” gesetzt. Vermutlich hat dies etwas mit dem Fakt zu tun, dass App Engine mit der verteilten Objektdatenbank Big Table arbeitet und nicht mit einer relationalen Datenbank.

Testmodule

Maven-Module verwalten Produktiv- und Testcode in getrennten Verzeichnissen (src/main und test). Das Packaging (z.B. als JAR) exkludiert Testklassen und -ressourcen. Damit andere Module Test-Hilfsklassen, Mock-Implementierungen, etc. verwenden können, müssen diese in src/main liegen. Elemente von src/test bleiben für andere Module verborgen. Um Testklassen wiederzuverwenden, lagert man in diese in ein separates Testmodul aus. Das sieht z.B. wie folgt aus:

image

Der Testcode und die Hilfsklassen für “framework” sind nach “framework-test” ausgelagert worden. Die Test-Hilfsklassen operieren auf “framework”-Klassen. Die Testklassen benötigen die Testhilfsklassen. Würden die Tests innerhalb von “framework” liegen gebe es einen Modulzyklus. Dies ist weder sinnvoll noch von Maven erlaubt.

image 

Wer seine Testabdeckung automatisiert ermittelt bekommt mit dieser Modulaufteilung Probleme. Per Default ermitteln Cobertura, Clover, Emma die Unit-Test-Abdeckung nur pro Modul. Obwohl umfangreiche Tests für den Code in “framework” existieren, liefert eine Messung 0% Abdeckung.

Lösung mit JaCoCo

JaCoCo wird von den Emma-Machern entwickelt und arbeitet über Byte-Code-Instrumentation zur Laufzeit mittels eines JVM-Agents.

Mittels JaCoCo-Agent und dem JaCoCo-Sonar-Plugin lässt sich das Testabdeckungsproblem lösen und sogar die Abdeckung für Integrationstests messen. Das im Folgenden beschriebene Setup umfasst Jenkins / Hudson sowie Sonar Konfiguration und Anpassungen an Euren Maven POMs.

1. JaCoCo bereitstellen

Das “jacoco-agent.jar” wird auf dem System, welches die Tests ausführt (z.B. Euer CI Server), benötigt.

  1. ZIP herunterladen (wget …) z.B. nach /var/lib/jacoco. Achte darauf, dass Deine Sonar-Installation zum JaCoCo-Agent kompatibel ist oder entschließe Dich, Deine Sonar-Installation upzugraden.
  2. unzip
2. POM anpassen

Das Surefire Plugin muss den JaCoCo-Agent einbinden, wenn es die Tests ausführt. Der Agent muss Coverage-Informationen für alle Eure Module in dieselbe Datei schreiben.

   1: <profiles>

   2:   <profile>

   3:     <id>ci</id>

   4:       <build>

   5:         <pluginManagement>

   6:           <plugins>

   7:             <plugin>

   8:               <artifactId>maven-surefire-plugin</artifactId>

   9:                 <configuration>

  10:                   <argLine>-javaagent:${jacoco.agent.path}=destfile=${jacoco.unit.path}</argLine>

  11:                 </configuration>

  12:               </plugin>

  13:             </plugins>

  14:           </pluginManagement>

  15:       </build>

  16:   </profile>

  17: </profiles>

3. Jenkins / Hudson Job konfigurieren

Jenkins muss die Variablen “jacoco.agent.path” und “jacoco.unit.path”. Dazu bei “Goals und Optionen” in der Job-Konfiguration z.Bsp. folgendes angeben:

clean install -P ci -Djacoco.agent.path="/var/lib/jacoco/lib/jacocoagent.jar" -Djacoco.unit.path="/tmp/mapgame-jacoco-unit"

Gib bei der Sonarkonfiguration des CI-Jobs die Option “-Dsonar.jacoco.itReportPath=/tmp/mapgame-jacoco-it“ an. Das JaCoCo-Sonar-Plugin erkennt diese System-Property und findet dadurch die Report-Datei.

image

4. Sonar JaCoCo Plugin installieren

Installiere im Sonar Update Center das Plugin “JaCoCo”. Konfiguriere unter Sonar – Settings – Core – Code Coverage Plugin den Wert “jacoco”. Hast Du alles korrekt gemacht, so wirst Du auf Deinem Sonar-Dashboard nach Lauf des CI-Jobs im Jenkins / Hudson den korrekten Wert für die Unit-Testabdeckung sehen.

image image

 

Trennung Unit- und Integration-Tests

JaCoCo kann natürlich auf die Integration-Test-Abdeckung messen. Dies solltest Du getrennt von der Unit-Test-Messung halten. Mit Unit-Tests kann nach TDD entwickelt werden, mittels Integration-Tests nicht wirklich, welche Change & Test-Phase zu lang dauert und Unit-Tests eine viel lokalere Betrachtung erlauben. Dadurch ist Bugfixing bei Testfehlschlägen effizienter.

Für Integrationstests sollte das “maven-failsafe-plugin” verwendet werden, da es Aufräumarbeiten nach Integrationstests (Server stoppen, etc.) besser beherrscht (post-integration-test Phase wird bei Test-Fehlschlägen aufgerufen. Dies ist bei Surefire nicht der Fall.).

1. POM erweitern

Die Code-Abdeckung in den Integration-Tests selbst soll gemessen werden:

<plugin>  <artifactId>maven-failsafe-plugin</artifactId>  <configuration>    <argLine>-javaagent:${jacoco.agent.path}=destfile=${jacoco.it.path}</argLine>  </configuration></plugin>

Natürlich musst Du den JaCoCo-Agent auch in den getesteten Systemen einbinden. Bei mir ist dies die lokale Google App Engine.

<profiles>  <profile>    <id>ci</id>    <build>      <pluginManagement>        <plugins>          <plugin>            <groupId>net.kindleit</groupId>            <artifactId>maven-gae-plugin</artifactId>            <executions>              <execution>                <id>start-app-engine</id>                <phase>pre-integration-test</phase>                <goals>                  <goal>start</goal>                </goals>                <configuration>                  <javaAgent>${jacoco.agent.path}=destfile=${jacoco.it.path}</javaAgent>                </configuration>              </execution>            </executions>          </plugin>        </plugins>      </pluginManagement>    </build>  </profile></profiles>

2. Jenkins / Hudson CI Job erweitern
  • Zusätzlicher Parameter bei “Goals und Optionen”: -Djacoco.it.path="/tmp/mapgame-jacoco-it"
  • Zusätzlicher Parameter bei Sonar MAVEN_OPTS: -Dsonar.jacoco.itReportPath=/tmp/mapgame-jacoco-it
3. Sonar Dashboard erweitern
  1. Im Sonar Dashboard “configure widgets” klicken
  2. “IT coverage widget” hinzufügen

 

Fazit

JaCoCo funktioniert!

Hohe Code-Abdeckung durch gut strukturierte Unit- und Integrationstests, die jeweils nur eine “Sache” testen, sind die halbe Miete für gute Softwarequalität!

Hier kurz für Euch und mich mein Vorgehen zur Performance Analyse von Android Apps. Es geht darum relevante Stellen im Code zu finden, deren Optimierung sich lohnt. Zwei Wege: Analyse über (1) Memory Allocations und (2) Ausführungszeit der Methoden .. alles basierend auf Eclipse.

Memory Allocation

  1. App in Eclipse mittels Android Development Tools (ADT) starten.
  2. App in den Zustand versetzen ab der die Untersuchung relevant ist (bei mir: in Spielewelt einloggen)
  3. In Eclipse-Perspektive DDMS wechseln.
  4. Den Prozess im Emulator / Device auswählen, der Eurer App entspricht.
  5. Auf den Reiter “Allocation Tracking” wechseln
  6. Button “Start Tracking” klicken
    image
  7. Abwarten bis aus Eurer Sicht genug Daten gesammelt sind (bei mir ein paar Animation-Frames)
  8. “Get Allocations” Button klicken um die gesammelten Informationen anzuzeigen
  9. Interessant für Optimierungen sind für mich häufig auftretende Allokation an denselben Code-Stellen. Dazu die Tabelle nach “Allocated In” sortieren. Für jede Allokation kann im unteren Teil der View der Stack-Trace betrachtet werden um die verantwortliche Stelle im eigenen Code zu identifizieren.
    image

Anhand dieser Analysen habe ich Stellen identifiziert in den z.B. unnötig Collection-Objekte erzeugt wurden. Diese habe ich durch Caching der verwendeten Collection in Instanzvariablen des längerlebigen Objekts optimiert (Vermeidung von Allokationen).

Ausführungszeiten von Methoden

Analog zu den Speicherallokationen können die Ausführungszeiten – genauer der Anteil der Ausführungszeit einzelner Methoden an der Gesamt-Ausführungszeit der App – analysiert werden. Effizienter Code für Android Apps wie Spiele ist zum Einen essentiell für eine akzeptable Frame-Rate und zum Anderen für die Optimierung des Batterieverbrauchs. Wer schon mal Angry Birds gespielt und danach Kopfschüttelnd zum Ladekabel gegriffen hat, kann das sicher nachvollziehen.

Die Infos zu Ausführungszeiten schreibt Emulator / Device auf die SD-Card. Daher muss das “AndroidManifest.xml” die entsprechende Permission anfordern:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Um die Analyse durchzuführen zunächst die Schritte 1 – 4 von oben wie bei Memory Allocations ausführen, dann

5. “Start Method Profiling” Icon-Button klicken
image
6. Warten bis genug Daten gesammelt sind (ein paar Frames in meinem Spiel)
7. “Stop Method Profiling” Icon-Button klicken
8. Trace-File vom Emulator / Device herunterladen
image

9. Trace-File mittels Trace-View-Tool des Android SDK (liegt bei mir unter “D:\java\android-sdk-windows\tools\traceview.bat”) öffnen. Optimalerwise verknüpft Ihr die Endung “.trace” mit dem Trace-View-Tool in Windows. Dann sieht das Ergebnis in Trace-View wie folgt aus:
image

10. Die Prozent-Angaben der Spalte “Incl %” waren für mich relevant. In welchen, möglichst tief in der Aufrufhierarchie liegenden Methoden, wird sehr viel Zeit verbraucht? In einem ersten Schritt habe ich TreeMaps durch eine eigene Implementierung ersetzt, die für meine Zwecke effizienter ist. Ohne diese Analyse mit Trace-View fällt die Identifikation der optimierungsrelevanten Stellen schwer.

Fazit

Optimiert Eure Apps nicht ins Blaue hinein. Aufwand und Nutzen müssen in gesundem Verhältnis stehen. Mit den oben beschrieben Ansätzen lassen sich schnell Stellen für Optimierungspotential finden. Mindestens genauso wichtig ist die Erfolgsprüfung Eurer Optimierungsmaßnahmen und die Nachhaltung über automatisierte Tests.

Gerade konfiguriere ich die Ausführung des Android Emulator mit Jenkins Continuous Integration. Mir hat gerade sehr geholfen, eine Fehlermeldung des Plugin über grepcode.com zu finden. Diese Site erlaubt die konfortable Navigation im Quellcode vieler Open-Source-Projekte. Sehr hilfreich.

Es soll IT-Architekten geben, die in der Kernarbeitszeit tatsächlich länger am Stück arbeiten.  Kommt bei mir auch vor. Großprojekte mit vielen Kollegen sind sehr anregend für den eigenen Wissenshorizont, jedoch nur bedingt gut für die Konzentration.
Ich empfehle daher zur Begünstigung unterbrechungsfreien Arbeitens den 24h-Drum’n'Bass Radio-Stream Bassdrive.  Bei durchschnittlichen 175 bpm arbeite ich einfach schneller und konzentrierter. Bei mir läuft gerade Bassdrive auf Media-Monkey.
Und ja, in der restlichen Zeit gebrauchen IT-Architekten Ihr loses Mundwerk und zeichnen Pfeile, Kreise und Rechtecke auf Flipcharts in großen Meetingräumen.

Um die Java-Klassen für WebService-Clients zu generieren empfiehlt sich das JAX-WS-Plugin für Maven. Dieses klinkt sich bei Verwendung von “wsimport” in die Build-Phase “generate-sources” ein.

Für mich ist es sinnvoll, die WSDL-Dateien des anzusprechenden Services direkt in “src/wsdl” meines Projekts abzulegen statt diese über eine URL einzubinden. Dies hat den Vorteil, dass ich Änderungen an der Schnittstelle des Service in der Subversion-Historie nachvollziehen kann. Muss mein Projekt mehrere Service ansprechen, so lege ich die WSDL- plus die Schema-Datei (XSD) jedes Service im genannten Verzeichnis ab.
Continue reading »

Zuletzt habe ich über das Beschleunigen von lokalen JEE-Deployments geschrieben. Für das Beschleunigen von JEE-Deployments ist meiner Einschätzung nach zwingend das exploded Deployment notwendig. Dabei wird ein EAR oder WAR nicht als Archiv-Datei sondern als entpackte Verzeichnisstruktur deployt. Leider ist das Deployment von exploded EARs und WARs noch nicht durch die JEE-Spezifikation abgedeckt. Daher hat jeder JEE Container dabei so seine Eigenheiten. Die folgenden Zeilen liefern ein paar wichtige Informationen zum lokalen Deployment von explodierten EARs unter Glassfish V2.
Continue reading »

Nahezu jede JEE Web-Applikation muss beim Start diverse Initialisierungen ausführen. Denkbar ist das Registrieren von MBeans in einem JMX-Server, das Starten einer Job-Scheduling-Engine oder auch anderes. Dabei fällt immer mal wieder der Begriff “InitServlet” – also ein Servlet, welches keine HTTP-Requests entgegen nimmt sondern nur der Initialisierung einer JEE WebApp dient.

Continue reading »

Regulär geht man davon aus, dass die servergespeicherten Daten einer User Session immer nur von einem Thread zur selben Zeit gelesen bzw. geschrieben werden. Es ist zu aufwendig, jede Stelle im Code welche mit der User Session arbeitet vor Race Conditions durch Mutext-Objekte / Monitore zu schützen. Daher muss dafür gesorgt werden, dann ein Request aus einer User Session immer nacheinander aber nicht parallel verarbeitet werden. Im Folgenden wird ein Code-Beispiel für J2EE Web Applikationen gegeben.

Continue reading »

OpenWishes Logo

Endlich ist es soweit. Der Web-Dienst OpenWishes.de, welcher dabei hilft den Geschenkestress zu überstehen und keine Geburtstage mehr zu vergessen, ist online und damit in der Public Alpha. Diese Plattform entstand in den letzten Monaten in Zusammenarbeit mit Markus Kühle und Markus Junginger. Ich nutze dieses Ereignis um einen Überblick auf die verwendeten Technologien zu geben.

Die Infrastrukturkomponenten des Systems sind Apache Tomcat 6, Apache und MySQL. Es wurde bewusst auf EJB oder Spring verzichtet, jedoch ist die Persistenzschicht mittels JPA / Hibernate umgesetzt. Die Web Tier ist mittels Java Server Faces 1.2 und Facelets realisiert, wobei die JSF-Komponentenbibliothek RichFaces 3.1 und DWR für die Ajax-Funktionalität eingesetzt wird. Für verschiedene Querschnittsbelange wie Exception-Handling, Transaktionshandling, Logging und Ermitteln von Statistikdaten kommt AspectJ zum Einsatz. Asynchrone und zeitgesteuerte Jobs werden mittels Quartz verwaltet. Eine interne Management-Schnittstelle setzt auf JMX / MX4J auf. Das Versenden von Mails wird über Apache Commons Mail realisiert.

Die Plattform selbst ist in Java implementiert, wobei voll auf die Syntax von Java 5 (Generics & Co) gesetzt wird. Durch die Verwendung von AspectJ und Java5 enstand unserer Meinung nach ein sehr erweiterungsfähiges und gut wartbares System da sich der Code auf das Wesentliche beschränkt. Wir hatten und haben das Gefühl, dass wir sehr produktiv bei der Entwicklung und Erweiterung des Systems arbeiten können.

Es gibt noch eine Menge zu tun – neue Features befinden sich in Entwicklung und warten auf das nächste Release. An dieser Stelle die Bitte um Feedback und die Aufforderung OpenWishes.de zu nutzen. Ich werde auf jeden Fall ab heute keine Geburtstage mehr vergessen!

© 2011 Sophisticated IT Suffusion theme by Sayontan Sinha