23.02.2021 - Reverse engineering with Ghidra 101

When looking at the assembly of an unknown binary you are usually welcomed with a lot of information. Even a small binary of a couple of kilobytes contains lots and lots of instructions itself, and likely relies on external information (DLLs, assets, operating system calls, etc.). The obvious question is: Where do I start?

Where to start

For reverse engineering I rely mostly on Ghidra. It comes along with a really good set of features and its UI is great even for beginners. In my specific example I wanted to understand a file called SPELL.BIN from Spellbinder: The Nexus Conflict Demo. This file contains the spells, effect, runes used by the game logic. The file itself is just the assets, and some DLL or EXE must be parsing it. So let’s start with identifying our target: Where is the code probably located?

Locating your binary

In case of the Spellbinder Demo we have 2 relevant files:

  1. spell.exe
  2. game.dll

The first one is just about 90 KB, the later about 1.7 MB. As the demo already came with an auto updater, I took the assumption that all of the game logic must be in game.dll, so it can be replaced with the assets. Also spell.exe does not reference the game.dll via the linker, so it can perform tasks prior to loading the code in game.dll.

Importing your binary

The first important step is to import your binary properly. Try to provide all the DLLs referenced by the target available in the library paths. An example for game.dll can be seen in the screen below:

Library paths

Using this you quickly gain insights on what third party components your target relies on. In the example seen here you can see it relies on DDRAW.DLL, which quickly indicates it likely uses Direct Draw for rendering. So if you are interested in the rendering stack, you’d know what to look for.

Referenced DLLs

Analyzing the binary

When opening the CodeBrowser on your target for the first time, Ghidra will ask you to perform an analysis for you. Do so! In case you suspect your target uses C++, I highly recommend to install [Ghidra-Cpp-Class-Analyzer](https://github.com:astrelsky/Ghidra-Cpp-Class-Analyzer] first. Select whichever analyzers you prefer. I usually go with all of them, except for “Agressive Instruction Finder” and “MS DIA-SDK”.

Scoping your analysis

Now you are at the point of manual labour. The most important thing is to keep the scope down as much as possible. You will likely never understand everything that happens inside binary, but the good thing is: you don’t have to. We are interested in reading spell.bin, so likely the filename occures in the code. Searching for it we found it as string constant:

spell.bin occurences

The references all pass it into FUN_0045ede0, which seems to be the method to load Spellbinders BIN files. Let’s rename this method to LoadSpellBinFile. All occurences get passed the same constant string, so even if it could do more, it’s only used to read the spell.bin file. Intrestingly I found an additional method FUN_004571d0 which gets passed a spell.dat, which seems to be the same in a different format.

Identifying datastructures

In our method LoadSpellBinFile we see lots of local data, as to be seen in the following screenshot:

datastructures

Ghidra is able to generate datastructures automatically, but for now we’ll do it manually. The first datastructure being used is at puVar2, which is the same as ppuVar11, which is the same as ppuVar6. And where does this come from?

Identifying datastructures

We see a method getting passed 344 and returning a pointer, likely an allocation of 344 bytes. This is awesome: When looking at spell.bin we see a a nice pattern of 344 bytes in size. So what happens to these allocated bytes? When the allocation worked, the pointer gets assigned PTR_LAB_0056f458. What is that supposed to be? Likely a virtual function table. So this might be object oriented code after all, which is either not detected as C++ or some other object oriented framework on top of C.

Vtables - Virtual function tables

So we’ve figured that PTR_LAB_0056f458 is a Vtable. Now we need to assign a proper type to it:

344 Vtable

Once this is in place (you need to create the necessary function definitions for it), the memory at 0056f458 will look like this:

344 static vtable

Using your newly created Vtbl you can also create a matching structure to hold it:

344 Vtable usage

Unfortunately the code of LoadSpellBinFile does not reveal what individual fields of our newly created struct mean. We can see that it is likely chunks of 4 bytes, but that’s pretty much it.

Stepping back instead of diving to deep

Do you remember that FUN_004571d0 working in a similar area, likely reading the spells in a different format? It’s only called once so I renamed it to LoadSpellDatFile. Opening this method shows it is working with the same structures as LoadSpellBinFile. And it’s using lots of string constants:

  • “Loading Spell #%d\n”
  • “Error Loading ‘type’ from section ‘%s’ file: %s\n”

This immediately gains us more insights. Once we told Ghidra piVar5 is a pointer to the 344 bytes large structure, it will show us that field 0x1c is fatigue. The format of spell.dat seems to be an .ini based format.

344 Fields

Now the real gruntwork happens, to name each field and do the same steps as above for other data structures.

Further hints

If your interest is reading files, look for methods specific to it. E.g. to read files you need to open them first. On WIN32 based applications you’d usually use OpenFile for this. So browsing the imports we find the following:

OpenFile

Using the references to the function you will find the places that, well, open a file.

In this specific example there are two occurences, both in FUN_00516b60. After renaming local variables to get a better understanding of the code, this is what the code looks like:

FUN_00516b60

What can be seen is that it tries to read at least 768 bytes, and if that is not the case it will return. So we figured that _read is used to load data. We also learned that FUN_00535f80 allocated memory (or provides access to already allocated memory). As the memory is not released in the method, the method FUN_00535f80 likely keeps track of it.

It is also worth noticing that there are other methods to read files like ReadFile.

GetProcAddress

Another thing to keep in mind is not everything is statically or dynamically linked. Sometimes DLLs are loaded in program code. You should always check for references to GetProcAddress. This gains insights on the actual methods being hidden behind pointers. In the example of game.dll all socket related methods can be provided by two different DLLs, and the code loads them as it prefers. So all network related code is hard to find until you assign a proper datatype and a proper name to the function pointers. In the example below closesocket has been properly assigned, while WSAAsyncSelect is not yet.

closesocket

30.07.2017 - hashCode()/equals() mit Lambdas

Korrekte hashCode() und equals() Implementierungen sind sehr wichtig, aber es macht keinen Spaß, sie zu schreiben. Glücklicherweise generieren moderne IDEs die Methoden automatisch, also muss man nicht zu viele Gedanken daran verschwenden. Leider hat dieser Ansatz zwei Nachteile:

  1. in eigentlich sehr einfachen Klassen (e.g. DTOs or JPA entities) entstehen mindestens 20 Zeilen komplex wirkenden Codes, der zum Einen die Lesbarkeit reduziert und bei statischer Code Analyse negativ auffallen kann
  2. jede IDE generiert die Implementierung etwas anders, daher unterscheidet sich der Code z.B. Eclipse und IntelliJ

Lambdas als Alternative

Eine Alternative, um das Generieren der hashCode() und equals() Methoden zu vereinfachen, sind die mit Java 8 eingeführten Lambdas. Ich habe mir also folgenden Ansatz überlegt:

public class HashCodeEqualsBuilder {

    public static  int buildHashCode(T t, Function<? super T, ?>... functions) {
        final int prime = 31;
        int result = 1;
        for (Function<? super T, ?> f : functions) {
            Object o = f.apply(t);
            result = prime * result + ((o == null) ? 0 : o.hashCode());
        }
        return result;
    }

    public static  boolean isEqual(T t, Object obj, Function<? super T, ?>... functions) {
        if (t == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (t.getClass() != obj.getClass()) {
            return false;
        }
        T other = (T) obj;
        for (Function<? super T, ?> f : functions) {
            Object own = f.apply(t);
            Object foreign = f.apply(other);
            if (own == null) {
                if (foreign != null) {
                    return false;
                }
            } else if (!own.equals(foreign)) {
                return false;
            }
        }
        return true;
    }
}

Diese Hilfklasse kann wie folgt bei der Implementierung einer Klasse (z.B. ein DTO) verwendet werden:

@Override
public int hashCode() {
    return HashCodeEqualsBuilder.buildHashCode(this, MyClass::getA, MyClass::getB);
}

@Override
public boolean equals(Object obj) {
    return HashCodeEqualsBuilder.isEqual(this, obj, MyClass::getA, MyClass::getB);
}

Basierend auf einigen Microbenchmarks, unterscheidet sich dieser Ansatz nicht messbar zu einer autogenerierten Implementation (z.B. von Eclipse). Ich werde zukünftig diesen Ansatz verwenden und kann ihn auch nur wärmstens empfehlen::

  1. es werden keine unnötigen Code-Zeilen zum Projekt hinzugefügt, die evtl. noch negativ in der Code Coverage durch Unit Tests bewertet werden
  2. eine Änderung an der Implementierung (falls je notwendig) muss an exakt einer Stelle durchgeführt werden

08.11.2015 - How to use the JPA Criteria API in a subquery on an ElementCollection

Imagine the following entities:

class Category {
    private String softKey;
}

class Item {
    @ElementCollection
    private Collection<String> categorySoftKeys;
}

If we now would want to load only the non-empty categories, our JPQL would look like this:

EntityManager em = getEntityManager();
List<Category> categories = em.createQuery("select c " + 
    "from Category c " + 
    "where softKey in (select i.categorySoftKeys from Item i)", Category.class)
    .getResultList();

How do we do that in Hibernate using the JPA criteria API?

// Create the outer query for categories
EntityManager em = getEntityManager();
CriteriaBuilder builder = em.getCriteriaBuilder();
CriteriaQuery<Category> query = builder.createQuery(Category.class);
Root<Category> from = query.from(Category.class);

// Create the subquery for items using a category
Subquery<String> subquery = query.subquery(String.class);
Root<Item> subfrom = subquery.from(Item.class);
subquery.select(subfrom.join(Item_.categorySoftKeys));

// Apply the subquery to the query
query.where(from.get(Category_.softKey).in(subquery));

// Load the categories
TypedQuery<Category> q = em.createQuery(query);
List<Category> categories = q.getResultList();

Both solutions return the same result. Note that the first one is way less code, while the second one can properly react to if and provide (partial) compile time validation when used in conjunction with the hibernate metamodel generator.

The key to making this work using the criteria API is to understand the difference between Root#get() and Root#join(). The former one will just create a new path, while the later one performs an inner join. The scenario above would not work if you would have used:

In this case hibernate generates invalid SQL.

15.03.2015 - Bootstrap Navigationstabs mit AngularJS als aktiv markieren

Wenn man eine neue Technologie wie AngularJS ausprobiert, sollte man mit einer möglichst einfachen Aufgabe beginnen. Yeoman erstellt bereits das notwendige Codegerüst für uns, aber es erstellt ein bootstrap-basiertes Menü, dass das Home-Element immer als aktiv markiert (zumindest in der aktuell verfügbaren Version 0.11.1). Dieses kleine Tutorial erklärt wie man den Code anpasst, um diese einfache Funktion zu implementieren.

Beginnen wir mit yeoman

Für dieses Tutorial ist es notwendig, dass du bereits Node.js und npm kennst und installiert hast. Wir starten also mit der Installation von yeoman:

mkdir bootstrap-navigation-example
cd bootstrap-navigation-example
npm install generator-angular

Der nächste Schritt ist das entsprechende Codegerüst für eine AngularJS-basierte App zu erstellen

yo angular
? Would you like to use Sass (with Compass)? Yes
? Would you like to include Bootstrap? Yes
? Would you like to use the Sass version of Bootstrap? Yes
? Which modules would you like to include? (Press  to select)
❯◉ angular-animate.js
 ◯ angular-aria.js
 ◉ angular-cookies.js
 ◉ angular-resource.js
 ◯ angular-messages.js
 ◉ angular-route.js
 ◉ angular-sanitize.js
 ◉ angular-touch.js

Anschließend kannst du dir die neu erstelle App mit folgendem Befehl ansehen:

grunt serve

Beim Anklicken von „About“ wirst du die About-Seite unter /#/about sehen (das passiert dank angular-route), aber das CSS wird weiterhin den „Home“-Tab als aktiv markieren.

Fehlende Hervorhebung

Das Menü reparieren

Zuerst müssen wir die problematische Stelle im Code finden. Wir öffnen also die Datei app/index.html und sehen uns die bootstrap Navigationsleiste an:

<div id="js-navbar-collapse" class="collapse navbar-collapse">
 <ul class="nav navbar-nav">
  <li class="active"><a href="#/">Home </a></li>
  <li><a href="#/about">About </a></li>
  <li><a href="#/">Contact </a></li>
 </ul>
</div>

Wie leicht zu sehen ist, ist für das li-Element „Home“ der CSS-Stil „active“ fest eingetragen. Wir wollen diese Logik jetzt durch einigen AngularJS-basierten JavaScript-Code ersetzen. Da wir eine CSS-Klasse setzen wollen, werden wir das Attribut „ng-class“ verwenden.

<div id="js-navbar-collapse" class="collapse navbar-collapse" ng-controller="HeaderCtrl">
 <ul class="nav navbar-nav">
  <li ng-class="{ active: isActive('/')}"><a href="#/">Home</a></li>
  <li ng-class="{ active: isActive('/about')}"><a href="#/about">About</a></li>
  <li><a href="#/">Contact</a></li>
 </ul>
</div>

Wir haben jetzt also folgendes geändert: Zum Einen haben wir einen ng-controller mit dem Namen „HeaderCtrl“ zum Menü hinzugefügt, zum Anderen haben wir eine ng-class zu den Menüelementen angegeben. (Hinweis: Wir lassen in diesem Tutorial „Contact“ absichtlich außen vor, da yeoman weder eine angular-route, noch einen Controller noch eine View für die „Contact“-Seite erstellt). Anschließen wollen wir natürlich den passenden Controller erstellen, um den Ausdruck in ng-class auswerten zu können. Wir fügen diesen neuen Controller in app/scripts/app.js ein (Es gibt einige Tutorials, die den Controller in der controller.js hinzufügen. Die ist aber nicht der Fall für eine App erstellt mit yeoman). Wir fügen also folgenden Code in das AngularJS-Modul am Ende der Datei ein:

    .controller('HeaderCtrl', function ($scope, $location) { 
        $scope.isActive = function (viewLocation) { 
            return viewLocation === $location.path();
        };
    });

Wenn du jetzt zurück in den Browser wechselst (und „grunt serve“ bereits die Änderung erkannt hat), wirst du sehen, dass das About-Tab als aktiv markiert ist.

Korrekte Hervorhebung

Ergebnis

Jetzt hast du gesehen, wie einfach es ist, mit AngularJS die ersten Schritte zu gehen und wie leicht du neue Funktionen in deine Applikation einbauen kannst. Nutze dieses Wissen und erstelle deine eigene Webapplikation.

15.03.2015 - Bootstrap Navigationstabs mit AngularJS als aktiv markieren

When you start playing around with AngularJS, you want to start with the most easy task. Yeoman does most of the hard word generating the necessary boilerplate code for you. But it generates a bootstrap menu that hardwires the “Home” tab as active (at least in the currently available version 0.11.1). This small tutorial will explain you how to adapt the code to add that simple feature.

Starting with yeoman

This tutorial assumes that you already have Node.js and npm up and running. First of all you need to install yeoman:

mkdir bootstrap-navigation-example
cd bootstrap-navigation-example
npm install generator-angular

The next step is to generate the boilerplate code for an angular based app

yo angular
? Would you like to use Sass (with Compass)? Yes
? Would you like to include Bootstrap? Yes
? Would you like to use the Sass version of Bootstrap? Yes
? Which modules would you like to include? (Press  to select)
❯◉ angular-animate.js
 ◯ angular-aria.js
 ◉ angular-cookies.js
 ◉ angular-resource.js
 ◯ angular-messages.js
 ◉ angular-route.js
 ◉ angular-sanitize.js
 ◉ angular-touch.js

After doing so you can start testing you newly bootstrapped app by running:

grunt serve

Once you click on “About” you will be sent to /#/about (by angular-route), but the CSS will still highlight “Home” as active.

Fehlende Hervorhebung

Fixing up the content

First of all we must find the problematic piece of code. You have to take a look at app/index.html and look at the bootstrap navigation bar:

<div id="js-navbar-collapse" class="collapse navbar-collapse">
 <ul class="nav navbar-nav">
  <li class="active"><a href="#/">Home </a></li>
  <li><a href="#/about">About </a></li>
  <li><a href="#/">Contact </a></li>
 </ul>
</div>

As you see the li-item of Home is hardcoded to have the CSS style active. Now we have to replace it by some JavaScript dynamic provided by AngularJS. As it is a CSS class being set, we want to use “ng-class” for the dynamic.

<div id="js-navbar-collapse" class="collapse navbar-collapse" ng-controller="HeaderCtrl">
 <ul class="nav navbar-nav">
  <li ng-class="{ active: isActive('/')}"><a href="#/">Home</a></li>
  <li ng-class="{ active: isActive('/about')}"><a href="#/about">About</a></li>
  <li><a href="#/">Contact</a></li>
 </ul>
</div>

What we did was adding an ng-controller named “HeaderCtrl” to the menu and also ng-class to the menu elements. (Note that we ignore Contact here, as the yeoman generated app does not generate a angular-route, controller and view for the Contact page). Now we need to create the according controller to evaluate the ng-class expression. This is done by adding a new controller in app/scripts/app.js (there are lots of tutorials telling you to change controller.js, but this does not match the boilerplate generated by yeoman). You need to add the following code to your angular module at the bottom of the file:

    .controller('HeaderCtrl', function ($scope, $location) { 
        $scope.isActive = function (viewLocation) { 
            return viewLocation === $location.path();
        };
    });

Now if you switch back to your browser (and grunt serve already picked up your changes) you will see that about is marked as active.

Korrekte Hervorhebung

Conclusion

Now that you’ve seen how easy it is to start of with AngularJS and how easy can features can be added, go ahead and create your own web application.

24.03.2014 - Scrum from a management point of view

I recently had a discussion with a friend of mine regarding the usefulness of Scrum. I’ve taken some helpful hints from the discussion on how a developer can feel used and misused by Scrum. Let’s talk about a few of them, but let’s start with some naked rules of Scrum first:

  1. The daily work of a developer is bound to the user stories of a sprint.
  2. Due to the nature of time only a limited amount of this resource can be spent during a sprint.
  3. The customer is expecting a result according to his wishes in the shortest time possible.

Theory: Scrum stops creativity

I can agree with this only partially. The truth is: The customer is setting the requirements and, most important, he pays for reaching them. That means: he is setting the direction where the project is heading, no matter how strange some requirements might appear to the developer. But of course that does not mean everyone should blindly follow a wrong direction. So what happens if a developer has a really good idea on how to improve the product? It’s really simple: Take the input from your developers and take it to the customer. If the idea is really that good, he will agree on implementing it. From my point of view the creativity is not harmed by Scrum in any way. Everyone can still suggest ideas, but you should not go ahead an implement them without permission of the customer. Next to that: It’s hard to plan when a developer might have that one brilliant idea.

Theory: There is no time to do things right

I can not agree with this at all. First of all it is really to find a proper definition of right. What is the right way to implement a search functionality? Performing a LIKE-search on a database column? Or use weighted search on a lucene index using a solr cluster? Both attempts have obvious advantages and disadvantages. A search using an index is likely to be more natural to the end user, but it might not even make a difference. And searching a small table with a couple of lines can perform really well, but what about millions of lines?

To me this boils down to one question: How good are my user stories? When a story is well defined (specifying the use cases, the amount of data and the likelihood of usage) it is easy to find the right solution. If it’s not a developer might go for a technically interesting but expensive solution. If a story is well defined, the estimated development time will be large enough give the developer enough room to implement a solution properly. If it’s not be prepared for bad code.

Theory: Implementations must be shortsighted

I partially agree with this. One of Scrums basic ideas is: „only implement what you really need“. In most cases a developer will have to focus on a single feature/problem without having the complete spectrum of future requirements in mind. In most cases this results in solutions very specific to the given problem.

Theory: No time to undo mistakes of the past

I largely agree with this. When a developer is working on a new feature it commonly happens that he will go through a lot of existing code. And every code base has established concepts of doing things and these are accepted by the developers. But from time to time some concepts have proven to be bad and need to be reworked. But the customer will not be very happy when, after months and years of work, being presented a product that looks exactly the same as before. It’s almost impossible to find a customer paying for this! So if your developers are completely used by the Scrum there is no time to fix the so called technical debt. In my experience it is good to stop the Scrum process from time to time to allow the developers to perform larger cleanups. Alternatively it also works to have a sprint without enough user stories to consume the whole time available.

Conculsion

I agree that a developer might feel controlled by the customer/product owner/scrum master and being less able to establish his own ideas. But in my experience that often boils down to improper usage of Scrum. Having good user stories and a bidirectional communication with all involved parties not only leads to better products, but most important to happier developers. When everyone involved in the Scrum process is fulfilling the required role, it can be an enjoyable development experience for all sides.

12.10.2013 - DAO Unit Tests mit JUnit und Mockito

If it’s not tested, it’s broken. – Bruce Eckel

Stabile software ist mir sehr wichtig. Als Teil der Softwareindustrie möchte ich nur software herstellen, die auch möglichst zuverlässig funktioniert. Sollte eine Software abstürzen (oder noch schlimmer: Daten verlieren), erzeugt das beim Anwender ein so großes Misstrauen, dass er die Software nicht mehr verwendet. Wenn man sich selbst die Frage stellt: Würde ich eine Software einsetzen wollen, die abstürzt? Ich nicht. Daher ist die Qualitätssicherung und insbesondere der Softwaretest, um Fehler vor dem Endnutzer zu finden, der wichtigste Schritt im Softwareentwicklungsprozess.

Nun stellt sich die Frage: Wie testet man Software? Wie immer gibt es hier auf Wikipedia detaillierte Beschreibungen zu verschiedenen Arten von Tests. Auch gibt es diverse Blogs, Tutorials und Ähnliches überall im Internet. Es wird also schnell deutlich, dass es verschiedenste Arten gibt, die Qualität der Software sicherzustellen. In diesem Artikel beziehen wir uns auf den Teil der Softwareentwicklung, die Hauptsächlich durch Entwickler durchgeführt wird: Unit Tests. Wie der Name suggeriert, wird dabei eine Unit, also eine Einheit der Software geprüft. Wie genau eine Einheit definiert ist, kann von Anwendungsfall, Art der Software und sogar der Programmiersprache abhängig sein. In diesem Beispiel verwenden wir Java Klassen und ihre Methodem als Einheit. Wir starten also mit folgender trivialen Klasse, dem SomeDao:

package de.egore911.test;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.Id;

public class SomeDao {
    public static class Some {
        @Id public Integer id;
    }

    @Inject private EntityManager em;

    protected EntityManager getEntityManager() { return em; }

    public Some comeGetSome(Integer id) {
        return getEntityManager().find(Some.class, id);
    }
}

Wir haben ein DAO (Data Access Object) das eine Entität des Typs *Some anhand ihrer ID aus der Datenbank laden kann. Die Klasse selbst verwendet CDI um eine EntityManager Instanz zu beziehen. Dieses Muster ist typisch für diverse Webanwendungen und es wird in verschiedsten Projekten so verwendet. Aber wie testet am so eine Einheit? Auf den ersten Blick brauchen wir also ein Injection Framework, das wiederum eine Implementierung der EntityManager-JPA-Spezifiktion benötigt und diese wiederum benötig eine Datenbank, gefüllt mit Testdaten. Diese Abhängigkeiten könnten selbst Fehler erzeugen (z.B. wenn die Datenbank nicht verfügbar ist), und dürfen daher nicht Teil eines Unit-Tests sein. Eine Einheit muss abgeschlossen sein und externe Abhängigkeiten gemockt. Ein Mock ist dabei eine Blackbox, die zu definierten Eingaben immer die gleichen Ausgaben liefert. Wir benötigen also einen Mock des EntityManager, der selbst nicht durch ein Framework injeziert wird und selbst gar nicht auf eine Datenbank zugreifen muss. Dafür gibt es Frameworks wie Mockito um einen solchen zu erstellen.

import javax.persistence.EntityManager;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

public class SomeDaoTest {

    @Test
    public void testComeGetSome() {
        // Gegeben: Ein Mock des EntityManager der unseren Dummy zurückgibt
        Some dummy = new Some();
        EntityManager em = Mockito.mock(EntityManager.class);
        Mockito.when(em.find(Some.class, 1234)).thenReturn(dummy);

        // Ebenfalls gegeben: Unser gemocktes SomeDao mit dem gemockten
        // EntityManager, dass nur bestimmte Aufrufe auf comeGetSome beantwortet
        // allowing only the call to comeGetSome
        SomeDao someDao = Mockito.mock(SomeDao.class);
        Mockito.when(someDao.comeGetSome(1234)).thenCallRealMethod();
        Mockito.when(someDao.getEntityManager()).thenReturn(em);

        // Der eigentliche Test-Code
        Assertions.assertSame(dummy, someDao.comeGetSome(1234));
        Assertions.assertNull(someDao.comeGetSome(4321));
    }
}

Das Beispiel ist bewusst extrem einfach gehalten und der eigentliche Test-Code ergibt daher wenig Sinn: wir testen nur unseren Mock (weil unser DAO selbst keine Logik besitzt). Sobald das DAO allerdings Logik benötigt (z.B. das Validieren von Parametern oder die Verwendung der Criteria API), kann diese Art des Tests einen Mehrwert bringen. Zuerst mocken wir den EntityManager, der das Dummy-Objekt zurückgibt, wenn seine EntityManager.find()-Method aufgerufen wird. Anschließend sorgen wir dafür, dass der gemockte EntityManager von SomeDao.getEntityManager() zurückgegeben wird, was im Fall von der echten SomeDao.comeGetSome()-Methode der Fall ist is invoked. Selbstverständlich könnten wir das alles mit Reflection selbst machen, aber Mockito übernimmt hier die harte Arbeit für uns.

Das obige Bespiel kan sogar noch vereinfacht werden, wenn man Mockito die Dependency Injection überlässt. Das folgende Beispiel zeigt eine vergleichbare Implementation auf Basis der InjectMocks-Annotation.

import javax.persistence.EntityManager;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
public class SomeDaoTest {

    @Mock
    private EntityManager em;

    @InjectMocks
    SomeDao someDao;

    @Test
    public void testComeGetSome() {
        // Given: Tell the mocked entitymanager to return our dummy element
        Some dummy = new Some();
        Mockito.when(em.find(Some.class, 1234)).thenReturn(dummy);
        Mockito.when(em.find(Some.class, Mockito.any(Integer.class))).thenReturn(null);

        // Perform the actual test
        Assertions.assertSame(dummy, someDao.comeGetSome(1234));
        Assertions.assertNull(someDao.comeGetSome(4321));
    }
}

10.07.2013 - Einen Stuhl in Blender modellieren

Heute war mir danach … in Blender einen Stuhl zu modellieren! Hier sieht man das virtuelle Ergebnis. Wenn ich jetzt nur noch wüsste, wie man aus Holz einen Stuhl baut 😉

Model of a chair

Das Modell wurde vollständig in Blender erstellt und mit Cycles gerendert.

28.11.2012 - Anatomie eines git Commit-Kommentars

Jeder, der mit dem Versionskontrollsystem git zu tun hat, wird früher oder später feststellen, wie ein Commit-Kommentar aussehen sollte. Für alle, die das bisher noch nicht wissen, hier mal ein kurzes Beispiel:

Kurzer Betreff, max. 80 Zeichen

Optionaler längerer Text, der den Commit beschreibt. Dieser darf gern mehrzeilg
sein und die Beweggründe erläutern.

Optionale Signaturen.

Nun stellt sich die Frage: Warum macht man das so? Dies möchte ich anhand eines praktischen Beispiels erläutern:

mesa: remove '(void) k' lines

Serves no purpose as the k parameter is used later in the code.

In diesem Commit wurden mehrere Codezeilen gelöscht. Hätte der Autor gar keinen Kommentar angegeben, wäre rätselhaft warum er dies getan hat. Glücklicherweise erzwingt git immer eine Commit-Meldung. Aber auch die erste Zeile alleine ist nicht aussagekräftig, sondern folgt dem „stating the obvious“-Prinzip. Man findet damit den Commit leicht wieder, aber man kennt immer noch nicht den Grund. Dies wird erst durch die letzte Zeile klar.

Signaturen

Es gibt in git die Möglichkeit Signaturen in einer Commit-Meldung anzugeben, z.B.:

Signed-off-by: Christoph Brill <webmaster@egore911.de>

Diese dienen dazu, zusätzliche Informationen über die an dem Commit beteiligten Personen anzugeben. Es gibt keinen offiziellen Standard für diese Signaturen, aber folgende haben sich in verschiedenen Projekten als hilfreich erwiesen:

  • Signed-off-by: Der Commit wurde durch den Signierenden in die Repository aufgenommen. Diese Meldung dient mehr oder weniger als zusätzliche Autorenangabe
  • Reviewed-by: Der Commit wurde vom Signierenden durchgesehen und für sinnvoll befunden
  • Tested-by: Der Signierende hat den Commit in seinem lokalen Sourcecode eingespielt und getestet.

Verwendung im Alltag

In einer idealen Welt würde jeder detaillierte Kommentare (sowohl im Code als auch in Commit-Meldungen) verwenden. Die Praxis zeigt jedoch, dass dies nicht immer zutrifft. Selbst disziplinierte Projekte werden Commits aufweisen, die weit von diesem Ideal abweichen. Git (wie andere Versionskontrollsysteme auch) erlaubt es allerdings mit minimal strukturierten Commit-Meldungen anderen Entwicklern (oder dem Entwickler selbst zu einem späteren Zeitpunkt) den Commit zu verstehen, ohne jede Zeile Code des Commit gelesen zu haben.

12.11.2012 - NullPointerException beim Starten von JSF 2

Die Exception

Im Zuge der Umstellung auf JSF 2.x sind mir einige Exceptions begegnet. Eine davon ist relativ trickreich zu beheben. Sie äußert sich dass beim Deployment der Anwendung auf dem Server folgende Exception auftritt:

java.lang.NullPointerException
  at com.sun.faces.config.InitFacesContext.cleanupInitMaps(InitFacesContext.java:278)
  at com.sun.faces.config.InitFacesContext.(InitFacesContext.java:102)
  at com.sun.faces.config.ConfigureListener.contextInitialized(ConfigureListener.java:156)
  ..

Die Analyse

Dieser Fehler verhindert dann auch den Start der Anwendung. Woran liegt das? Dazu sehen wir uns mal eine Funktion der JSF-Referenz-Implementierung Mojarra an:

static Map getInitContextServletContextMap() {
  ConcurrentHashMap initContextServletContext = null;
  try {
    Field initContextMap = FacesContext.class.getDeclaredField("initContextServletContext");
    initContextMap.setAccessible(true);
    initContextServletContext = (ConcurrentHashMap)initContextMap.get(null);
  } catch (Exception e) {}
  return initContextServletContext;
}

Hierbei fällt auf, dass beim Zugriff mittels Reflection auf ein Feld der Fehlerfall einfach ignoriert wird und null zurück gegeben wird. Die Methode besitzt keine Dokumentation oder einen Hinweis darauf, was der Rückgabewert ist. Also muss der zugreifende Code den Fehler abfangen können. Aber tut er das? Sehen wir uns das mal an:

Map <InitFacesContext, ServletContext>initContextServletContext = InitFacesContext.getInitContextServletContextMap();
Set<Map.Entry<InitFacesContext, ServletContext>> entries = initContextServletContext.entrySet();

Damit wird schnell klar: Es wird NullPointerException geben und man sieht an der auftretenden Exception nicht den eigentlichen Grund. Dieses Fehlverhalten ist damit nicht ganz einfach zu beheben. Grundsätzlich ist es schlechter Stil eine Exception einfach zu schlucken. Besser ist es diese zumindest zu loggen, vielleicht sogar mit hilfreicher Zusatzinformation wie:

...
} catch (Exception e) {
  log.error("Could not access FacesContext#initContextServletContext, " +
    "probably JSF-API and implementation mismatch!", e);
}

Die Lösung

Jetzt ist offensichtlich, woher der Fehler kommt: die JSF-API und die Implementierung im Classpath passen nicht zusammen. In meinem Fall wurde eine weitere JSF-API über Maven als Abhängigkeit hinzugefügt, die dann „falsche“ Klassen in den Classpath lädt.

Es reicht also in den meisten Fällen den Classpath der Java-Anwendung auf folgende Probleme zu prüfen:

  • Passt die JSF-Implementierung zur JSF-API?
  • Gibt es mehr als eine JSF-Implementierung im Classpath (z.B. JSF 1.x und 2.x)?
  • Gibt es mehr als eine JSF-API im Classpath?
1 2 3 Weiter »
Copyright © christophbrill.de, 2002-2020.