Skip to content

JUnit und Hamcrest: Eigene Matchers schreiben

Hamcrest liefert zwar schon eine Menge an Matchern mit, aber nicht immer findet man das Gewünschte und man benötigt einen eigenen Matcher.

In diesem Beispiel schreiben wir einen Matcher der überprüft ob eine Zeichenkette mit A, B oder C beginnt und darauf drei Zahlen folgen. Da wir diese Prüfung in vielen verschiedenen Unit Tests durchführen müssen, schreiben wir einen eigenen Matcher.

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

public class IsAbcNumber extends TypeSafeMatcher {

  @Override
  public boolean matchesSafely(String aString) {
    return aString != null && aString.matches("[A,B,C]\\d{3}");
  }

  public void describeTo(Description description) {
    description.appendText("not a valid ABC number");
  }

  @Factory
  public static  Matcher abcNumber() {
    return new IsAbcNumber();
  }

}

Unser Matcher ist eine Subklasse von TypeSafeMatcher und implementiert die beiden Methoden matchesSafely und describeTo. In machesSafely wird die eigentliche Prüfung durchgeführt und true oder false zurückgegeben. describeTo ist zuständig für den Text der ausgegeben wird wenn die Behauptung nicht zutrifft.

Die statische Methode abcNumber können wir nun in unsere Testklasse importieren und den Matcher benutzen.


import static IsAbcNumber.abcNumber;
import org.junit.Test;

public class MyUnitTest {
  @Test
  public void testSomething() {
    String aNumber = "notvalid";
    assertThat(aNumber, is(abcNumber()));
  }
}

Die Fehlermeldung zeigt nun genau an wieso die Behauptung nicht zutrifft.


java.lang.AssertionError:
Expected: is not a valid ABC number
     got: "notvalid"

Folgender Test dagegen würde anstandslos durchlaufen.


  @Test
  public void testSomething() {
    assertThat("A120", is(abcNumber()));
    assertThat("B130", is(abcNumber()));
    assertThat("C140", is(abcNumber()));
  }	 

Weitere Links:
Hamcrest Tutorial
Meine vorherigen Blogposts über: Teil 1 und Teil 2

JUnit und Hamcrest: Weitere Beispiele

Hier einige weitere Beispiele von Hamcrest Matchern.


import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;

public class MyUnitTest {

  @Test
  public void testSomething() {
    List list = Arrays.asList("one", "two");

    assertThat(list, hasItems(equalTo("one"), equalTo("two")));

    assertThat(list, hasItems(equalToIgnoringCase("ONE"), equalToIgnoringWhiteSpace("  two  ")));

    assertThat(list, hasItems(anyOf(equalTo("one"), equalTo("three")), not(equalTo("four")), equalTo("two")));
  }

}

Mit der hasItems lässt sich sehr einfach überprüfen ob Elemente in einer Collection vorhanden sind. In Kombination mit anyOf und not lassen sich komplizierte Behauptungen schreiben. Das letzte Beispiel prüft ob in der Liste die Elemente “one” oder “three” UND nicht “four” UND “two” enthalten sind.

Um Behauptungen für Strings zu schreiben bietet Hamcrest auch einige Matchers


  @Test
  public void testSomething() {
    String string = "a string";

    assertThat(string, equalToIgnoringCase("A STRING"));
    assertThat(string, containsString("str"));
    assertThat(string, startsWith("a "));
    assertThat(string, endsWith("inga"));
  }

Und wie immer erhält man kostenlos eine ansprechende Fehlermeldung. Hier die Fehlermeldung für das letzte assertThat

java.lang.AssertionError:
Expected: a string ending with "inga"
     got: "a string"

Beispiele mit Zahlen haben wir bereits im ersten Teil besprochen. Methoden für Numbers sind greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo und closeTo für Fliesskommazahlen.

Um Objekte zu vergleichen wird die equalTo Methode verwendet, die intern die Object.equals Methode benutzt.

Hamcrest bietet viele weitere nützliche Matchers an. Ein Blick in die Klasse org.hamcrest.Matchers zeigt alle an.

Für den Start mit Hamcrest sollte man sich auch mal das Tutorial auf der Projektseite durchlesen.

Siehe auch das erste Post über JUnit und Hamcrest

JUnit und Hamcrest

JUnit liefert zwar viele assertXY Methoden mit, wie z.B. assertEquals, assertTrue, assertNotNull, assertArrayEquals. Sobald aber die Behauptungen komplizierter werden muss man sich oft mit assertTrue oder assertFalse behelfen. Zum Beispiel wird hier in einem Test geprüft ob eine Liste ein bestimmtes Element enthält.

  @Test
  public void testList() {
    List<String> aList = Arrays.asList("one", "two");
    assertTrue(aList.contains("three"));
  }

Das Problem mit diesen Tests ist das folgende Fehlermeldung ausgegeben wird.

java.lang.AssertionError:

Aufgrund der Fehlermeldung wird nicht klar wieso der Test nicht lief. Man kann die Situation verbessern indem man die assertTrue Methode mit dem zusätzlichen message Parameter verwendet.

  @Test
  public void testList() {
    List<String> aList = Arrays.asList("one", "two");
    assertTrue("aList should contain three, but contains: " + aList, aList.contains("three"));
  }
java.lang.AssertionError: aList should contain three, but contains: [one, two]

Die Fehlermeldung zeigt nun an was nicht funktionierte. Diese Methode wird natürlich sehr aufwändig wen man hunderte solcher Testfälle programmieren muss.

Aus diesem Grund haben die Programmierer der JUnit Library in Version 4.4 die assertThat Methode eingebaut die zusammen mit den Hamcrest Matchern einen einfachen Aufbau von Behauptungen erlaubt.

Unser Beispiel würde mit einem Hamcrest Matcher so aussehen.


import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.junit.matchers.JUnitMatchers.*;

public class MyUnitTest {

  @Test
  public void testList() {
    List<String> aList = Arrays.asList("one", "two");

    assertThat(aList, hasItem(equalTo("three")));
  }
}

Als ersten Parameter wird das Objekt übergeben das geprüft werden soll. Der zweite Parameter ist ein Matcher.

java.lang.AssertionError:
Expected: a collection containing "three"
     got: <[one, two]>

Die Fehlermeldung zeigt nun genau an was nicht funktioniert hat ohne das wir selber eine Message erfassen mussten.

JUnit liefert bereits einige Matchers in den Klassen CoreMatchers und JUnitMatchers mit. Für das Beispiel genügt es also nur die JUnit Library als Dependency hinzuzufügen.

Um Zugriff auf alle verfügbaren Hamcrest Matchers zu haben muss zusätzlich folgende Library mit als Dependency angegeben werden.

    <dependency>
      <groupId>org.hamcrest</groupId>
      <artifactId>hamcrest-library</artifactId>
      <version>1.1</version>
      <scope>test</scope>
    </dependency>

In diesem zweiten Beispiel wird geprüft ob eine Zahl grösser als 5 und kleiner als 10 ist. Ohne Hamcrest und Matcher könnte dies so aussehen.


  @Test
  public void testSomething() {
    int aValue = 10;
    assertTrue(aValue > 5 && aValue < 10);
  }

Mit der nichtsagenden Fehlermeldung.

java.lang.AssertionError:

Der gleiche Test mit Hamcrest


import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Test;

public class MyUnitTest {

  @Test
  public void testSomething() {
    int aValue = 10;
    assertThat(aValue, allOf(greaterThan(5), lessThan(10)));
  }
}
java.lang.AssertionError:
Expected: (a value greater than <5> and a value less than <10>)
     got: <10>

Und wiederum zeigt die Fehlermeldung genau an was erwartet wurde und was der aktuelle Wert ist. allOf() prüft ob alle übergebenen Matcher passen. Die Methode fungiert also als Und Verknüpfung. Eine Oder Verknüpfung erstellt man mit anyOf().

Man kann den Ausdruck auch noch mit is() “verschönern”. Die is() Method existiert um die Lesbarkeit zu erhöhen hat aber sonst keine Wirkung. Die Behauptung und die Fehlermeldung lesen sich nun aber fast wie ein Satz.


  @Test
  public void testSomething() {
    int aValue = 10;
    assertThat(aValue, is(allOf(greaterThan(5), lessThan(10))));
  }
  java.lang.AssertionError:
Expected: is (a value greater than <5> and a value less than <10>)
     got: <10>

Images mit CSS preloaden

Um Images in einem Browser zu preloaden gibt es viele Möglichkeiten. Hier ein Beispiel wie man mit reinem CSS und ohne JavaScript Bilder vorladen kann. Der Trick ist alt und sehr simpel. Man fügt wie gewöhnlich einen <img> Tag in die HTML Seite ein, setzt aber das CSS Property display auf none. Das Bild wird beim Laden der Seite zwar nicht angezeigt aber trotzdem in den Cache geladen.

Entweder fügt man zu jedem <img> Tag eine entsprechende CSS Klasse an.

<style type="text/css">
  .hiddenPic {
    display:none;
  }
</style>

<img src="images/ico_addressbook.gif" class="hiddenPic">
<img src="images/ico_agreement.gif" class="hiddenPic">
<img src="images/ico_alarm.gif" class="hiddenPic">

Order man umschliesst die <img> Tags mit einem <div>. Der Effekt ist derselbe.

<div class="hiddenPic">
  <img src="images/ico_addressbook.gif">
  <img src="images/ico_agreement.gif">
  <img src="images/ico_alarm.gif">
</div>

Excelgenerierung mit Apache POI: Spaltenbreite setzen

Wenn man mit Apache POI ein Excel generiert wird per Default die Spaltenbreite in allen Spalten auf den gleichen Wert gesetzt.

 public static void main(String[] args) throws IOException {
    Workbook workbook = new XSSFWorkbook();
    Sheet sheet = workbook.createSheet();
    Row row = sheet.createRow(0);
    row.createCell(0).setCellValue("a long description");
    row.createCell(1).setCellValue("name");
    row.createCell(2).setCellValue("this and that");

    //set column width

    FileOutputStream fos = new FileOutputStream("d:/out.xlsx");
    workbook.write(fos);
    fos.close();
  }

Diesen Defaultwert kann man mit sheet.getDefaultColumnWidth() auslesen. Diese Methode gibt die Breite in Anzahl Buchstaben zurück, die dargestellt werden. Per Default sind das 8 Buchstaben. Wenn man alle Spalten auf die gleiche Breite setzen will ruft man die Methode sheet.setDefaultColumnWidth(width) auf.

    System.out.println(sheet.getDefaultColumnWidth());
    sheet.setDefaultColumnWidth(20);

Meistens möchte man aber die Breite je Spalte unterschiedlich setzen. Mit dem Aufruf von sheet.setColumnWidth(column, width) kann man je Spalte einen anderen Wert angeben.

    sheet.setColumnWidth(0, 30 * 256);
    sheet.setColumnWidth(1, 10 * 256);
    sheet.setColumnWidth(2, 20 * 256);

Im Gegensatz zur setDefaultColumnWidth Methode muss hier die Breite in 1/256 eines Zeichens übergeben werden.

Diese Methoden haben den Nachteil das man herumprobieren muss um den idealen Wert zu finden. Ausserdem muss man die Breite jedesmal anpassen wenn die Anforderungen ändern und längere oder kürzere Zeichenketten geschrieben werden müssen.

Eine bessere Methode die Spaltenbreite zu setzen ist der Aufruf von sheet.autoSizeColumn(column). Diese Methode setzt die Breite so, dass der Inhalt genau passt.

    sheet.autoSizeColumn(0);
    sheet.autoSizeColumn(1);
    sheet.autoSizeColumn(2);

Die Methode berücksichtigt auch Styles und Fonts. Die Methode kann bei sehr grossen Sheets sehr langsam sein. Deshalb sollte man diese Methode nur einmal am Ende aufrufen. Es ist zu beachten das man die Methode erst aufruft wenn alle Daten in das Sheet eingetragen wurden.

Weiter ist darauf zu achten das diese Methode Java2D benutzt und eine Exception wirft wenn keine graphische Umgebung vorhanden ist. Wenn keine graphische Umgebung vorhanden ist muss dies Java mitgeteilt werden, indem man das System Property java.awt.headless=true setzt.

Textextraktion mit Apache Tika

Wer Texte aus Dateien wie zum Beispiel PDF oder Word extrahieren muss um sie dann zu analysieren oder um sie einer Volltextsuche wie Lucene zu übergeben, der sollte sich das Projekt Apache Tika mal genauer ansehen. Apache Tika ist eine Library die Text und Metadaten aus verschiedenen Dateien auslesen kann. Dabei nutzt Tika existierende Parser Libraries und stellt sie dem Entwickler in einer einheitlichen Schnittstelle zur Verfügung. Zum Beispiel wird für die Textextraktion von PDF Dateien die Library Apache PDFBox verwendet.

Wenn man folgende Dependencies benutzt werden deshalb auch sehr viele Libraries zusätzlich hinzugefügt.

  <dependencies>
    <dependency>
      <groupId>org.apache.tika</groupId>
      <artifactId>tika-core</artifactId>
      <version>0.7</version>
    </dependency>
    <dependency>
      <groupId>org.apache.tika</groupId>
      <artifactId>tika-parsers</artifactId>
      <version>0.7</version>
      <exclusions>
      	<exclusion>
      		<artifactId>geronimo-stax-api_1.0_spec</artifactId>
      		<groupId>org.apache.geronimo.specs</groupId>
      	</exclusion>
      	<exclusion>
      		<artifactId>xml-apis</artifactId>
      		<groupId>xml-apis</groupId>
      	</exclusion>
      </exclusions>
    </dependency>
  </dependencies>

Die stax und xml-apis Libraries habe ich hier ausgeschlossen da sie bereits in Java 6 enthalten sind.

Ein zentraler Bestandteil von Tika ist das Interface Parser und die Implementationen davon. Für PDF Dateien ist zum Beispiel die Klasse PDFParser zuständig, für RTF Dateien gibt es den RTFParser und so weiter.

Der Parser kapselt die verschiedenen Parserlibraries und stellt dem Client eine Methode für die Textextraktion zur Verfügung.

void parse(java.io.InputStream stream, org.xml.sax.ContentHandler handler, Metadata metadata, ParseContext context)

stream:
Dies ist das Dokument welches geparst werden soll.

handler:
Der Output wird als SAX Events zurückgegeben. Der handler ist eine Implementation eines ContentHandlers. Wer sich für die genaue Beschreibung des Dokumentes, das als Events geschickt wird, interessiert sollte sich diese Seite durchlesen: http://tika.apache.org/0.7/parser.html. Tika liefert aber bereits fertige Implementationen von ContentHandler mit und man muss sich dann auch nicht selber um das SAX Event Handling kümmern.

metadata:
Die Metadata Klasse ist aufgebaut wie eine Map und der Client kann hier zum Beispiel den Namen oder auch den Mimetype des Dokumentes übergeben. Diese Informationen helfen dem Parser herauszufinden um welches Format es sich handelt. Dies ist dann wichtig wenn Parser mehrere Dokumenttypen verarbeiten können. Ein Beispiel ist der AutoDetectParser der aufgrund dieser Metadaten und anderer Verfahren wie Magicbytes selber entscheidet welche Parserimplementation verwendet wird. Nach dem Aufruf der parse Methode enthält das metadata Objekt weitere Informationen die im Dokument enthalten sind, wie z.B. Titel, Autor, Erstellungsdatum.

context:
Mit dem ParseContext können dokumentunabhängige Metadaten dem Parser übergeben werden.

Hier ein Beispiel wie man aus einem PDF den Text extrahiert.

  public static String extractText(String fileName) throws IOException, SAXException, TikaException {
    File inputFile = new File(fileName);
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFile));

    Parser p = new PDFParser();

    StringWriter writer = new StringWriter();
    p.parse(bis, new BodyContentHandler(writer), new Metadata(), new ParseContext());

    bis.close();

    return writer.toString();
  }

Dieses Beispiel setzt natürlich voraus das man bereits im Voraus weiss das nur PDF Dateien verarbeitet werden. Um unterschiedliche Datenformate zu verarbeiten verwendet man den AutoDetectParser, der versucht zu erkennen um welches Format es sich handelt. Als Hilfe für den Parser wird der Dateiname im Metadata Objekt übergeben.

  public static String extractText(String fileName) throws IOException, SAXException, TikaException {
    File inputFile = new File(fileName);
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream(inputFile));

    Parser p = new AutoDetectParser();

    StringWriter writer = new StringWriter();
    Metadata metadata = new Metadata();
    metadata.set(Metadata.RESOURCE_NAME_KEY, fileName);
    p.parse(bis, new BodyContentHandler(writer), metadata, new ParseContext());

    bis.close();

    return writer.toString();
  }

Nach dem Aufruf von parse enthält das Metadata Objekt weitere Information aus dem Dokument. Mit einem Loop kann man alle Metadaten auflisten.

    for (String name : metadata.names()) {
      System.out.println(name + "->" + metadata.get(name));
    }

Ein Beispiel PDF das ich verwende enthält zum Beispiel diese Metadaten:

created->Thu May 20 14:21:56 CEST 2010
producer->OpenOffice.org 3.0
Content-Type->application/pdf
resourceName->c:/test.pdf
creator->Writer

Die Metadaten sind je nach Datenformat sehr unterschiedlich.

Möchte man sich nicht selber um Metadaten, ParseContexts und Parser kümmern, dann gibt es noch einen einfacheren Weg um Texte zu extrahieren. Die Klasse ParsingReader erledigt alles Nötige und gibt einen Reader zurück, welcher den Text enthält. Intern benutzt ParsingReader den AutoDetectParser. Die Klasse IOUtils ist auch Bestandteil der Tika Library.

  public static String extractText(String fileName) throws FileNotFoundException, IOException {
    File inputFile = new File(fileName);
    Reader reader = null;
    try {
      reader = new ParsingReader(inputFile);
      String text = IOUtils.toString(reader);
      return text;
    } finally {
      IOUtils.closeQuietly(reader);
    }
  }

Apache Tika kann viele Dokumenttypen verarbeiten. Eine Auflistung der unterstützten Formate findet man auf dieser Seite: http://tika.apache.org/0.7/formats.html

VisualVM 1.3

Vor einigen Tagen ist die neueste Version von VisualVM erschienen. Die letzte Version trägt die Nummer 1.3 und kann von der Homepage heruntergeladen werden.

VisualVM ist übrigens auch Bestandteil des Java SDK. Unter Windows findet man VisualVM im bin Verzeichnisses des Java SDK. Mit jvisualvm.exe startet man die Applikation.

VisualVM ist ein Tool das detaillierte Informationen über Speicher und Laufzeiten von Java Applikationen anzeigt. Es können Daten von lokalen Applikationen angezeigt werden, aber es ist auch möglich eine Verbindung zu Applikationen aufzubauen die auf einem entfernten Rechner laufen.

Der Monitoring Teil von VisualVM zeigt Daten über die CPU Benutzung, den Speicherverbrauch (Heap und PermGen), Anzahl geladener Klassen und laufender Threads an. Zusätzlich enthält VisualVM einen CPU und Memory Profiler. Mit dem CPU Profiler werden die Laufzeiten von Methoden gemessen. Der Memory Profiler führt Buch über die instanzierten Objekte und listet die Anzahl der Instanzen und der dafür benötigte Speicher auf. Monitoring ist ein sehr leichtgewichtiger Prozess der auch in der Produktion eingesetzt werden kann. Beim Profilen von produktiven Applikationen sollte man dagegen vorsichtig sein da sie die Applikation verlangsamen.

Lokal gestartete Java Applikationen werden von VisualVM erkannt und können ohne weitere Konfiguration überwacht werden. Allerdings werden JVM nicht angezeigt wenn sie via Windows Services gestartet werden, auch wenn VisualVM auf dem gleichen Computer gestartet wird. Dieses Problem tauchte auf als wir versuchten eine Tomcatinstallation zu überwachen.

Die Lösung haben wir dann in diesem Blog Eintrag gefunden. Man muss die JVM mit diesen Parametern starten.

-Dcom.sun.management.jmxremote.port=8086
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false

Dann öffnet man in VisualVM das Menu File->Add JMX Connection und gibt im Feld Connection ‘localhost:8086′ ein. Mit Klick auf ‘OK’ wird man mit der JVM verbunden.

VisualVM lässt sich via Plugins erweitern. Zum Zeitpunkt dieses Artikels stehen 23 Plugins zur Verfügung. Den Plugins Dialog öffnet man via Menu Tools->Plugins und kann hier die gewünschten Erweiterungen installieren. Ein erwähnenswertes Plugin ist MBeans mit dem man die JMX Managed Beans ansehen kann. Siehe auch den Artikel über Spring JMX.

Links:
VisualVM Homepage
Monitoring Tomcat with Java VisualVM
Discover Java VisualVM 1.3

Reflection Hacking

Parameternamen
Wer einmal versucht hat die Namen der Parameter von einer Methode zur Laufzeit herauszufinden, hat festgestellt dass es keinen eingebauten Weg im Reflection API gibt um dies zu bewerkstelligen. Im Springframework ist eine Klasse vorhanden, die genau diese Aufgabe übernimmt. Die Klasse LocalVariableTableParameterNameDiscoverer verwendet die ASM Library um die Parameternamen herauszufinden. Im folgenden Beispiel versucht der Code die Parameternamen der Methode compute herauszufinden.

public class Main {
  public static void main(String[] args) {
    LocalVariableTableParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer();
    Method method = BeanUtils.findMethodWithMinimalParameters(TestBean.class, "compute");
    String[] parameterNames = discoverer.getParameterNames(method);
    if (parameterNames != null) {
      for (String name : parameterNames) {
        System.out.println(name);
      }
    } else {
      System.out.println("No parameter names available");
    }
  }
}
public class TestBean {
  public String compute(int parameterOne, String parameterTwo, boolean visible) {
    //nothing here
    return null;
  }
}

Dies funktioniert nur wenn der Code mit Debug Informationen kompiliert wird. Ohne Debuginformationen sind keine Namen im Class-File vorhanden und die Methode discoverer.getParameterNames(method) liefert null zurück. Im anderen Fall gibt die Methode die Namen der Parameter in der Reihenfolge des Auftretens zurück. BeanUtils.findMethodWithMinimalParameters ist eine weitere nützliche Method aus dem Springframework, welche eine Methode in einer Klasse sucht und zurück gibt.

Um den Compiler anzuweisen Debug Informationen zu schreiben kann in einem Maven Projekt dieses Property verwendet werden.

  <properties>
    <maven.compiler.debug>true</maven.compiler.debug>
  </properties>

Unter Eclipse muss die Option “Add variable attributes to generated class files” unter Java Compiler aktiviert sein, damit der LocalVariableTableParameterNameDiscoverer die Namen der Parameter findet. Das Springframework verwendet diese Klasse im Spring MVC für die Annotation @RequestParam um die Requestparameter den entsprechenden Methodenparametern zu übergeben.

Generic Collection
Ein weiterer Task der manchmal nützlich sein kann, aber nicht so einfach realisierbar ist, ist es den Typ einer Generic Collection herauszufinden. Auch für diesen Task ist im Springframework eine Klasse vorhanden: GenericCollectionTypeResolver

Hier zwei Beispiele welche den Typ einer Collection zur Laufzeit herausfinden.

public class Main {
  public static void main(String[] args) {
    Method method = BeanUtils.findMethodWithMinimalParameters(TestBean.class, "getList");
    Class<?> returnType = GenericCollectionTypeResolver.getCollectionReturnType(method);
    System.out.println(returnType); //Prints: class java.lang.Integer

    method = BeanUtils.findMethodWithMinimalParameters(TestBean.class, "doSomething");
    Class<?> type = GenericCollectionTypeResolver.getCollectionParameterType(MethodParameter.forMethodOrConstructor(method, 0));
    System.out.println(type); //Prints: class java.util.Calendar
  }
}
public class TestBean {
  public List<Integer> getList() {
    return null;
  }

  public void doSomething(Set<Calendar> calendars) {
    return;
  }
}

Für alle Beispiele werden folgende Libraries benötigt:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-core</artifactId>
  <version>3.0.3.RELEASE</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-beans</artifactId>
  <version>3.0.3.RELEASE</version>
</dependency>

Sun Java6 unter Ubuntu 10.04

Nach dem Update eines Ubuntu Servers auf 10.04 (Lucid Lynx) habe ich mich gewundert wieso das Sun Java SDK nicht mehr upgedatet wurde. Nun das Rätsel war schnell gelöst. Ab 10.04 wurde das sun-java6 Package aus dem Multiverse entfernt. Ubuntu empfiehlt stattdessen das openjdk-6 Package zu verwenden. Wer trotzdem die Sun JVM verwenden will kann das Canonical Partner Repository benutzen. Mit folgendem Befehl wird dieses Repository hinzugefügt.

add-apt-repository "deb http://archive.canonical.com/ lucid partner"

Falls das Skript add-apt-repository nicht vorhanden ist, kann man es mit diesem Befehl installieren:

aptitude install python-software-properties

Alternativ genügt es die folgende Zeile in die Datei /etc/apt/sources.list einzutragen:

deb http://archive.canonical.com/ lucid partner

Ein abschliessendes aptitude update und aptitude upgrade genügen damit das sun-java6 Package wieder upgedatet wird.

WordPress 3.0

WordPress 3.0 ist erschienen mit einer Vielzahl von Neuerungen und Bugfixes. Hier eine kleine Videotour der Neuerungen: