Skip to content

MS SQL Server Transaction Log verkleinern

In unserer Entwicklungs- und Testumgebung laufen MS SQL Server Datenbanken. Da in diesen Umgebungen keine automatischen Backup- und Maintenancejobs durchgeführt werden, wachsen die Transaktionslogs ständig. Von Zeit zu Zeit versuche ich diese Transaktionslogs zu verkleinern. Das Log lässt sich verkleinern indem von der Datenbank und dem Transaktionlog ein Backup erstellt und danach ein Shrink Database aufgerufen wird. Manchmal muss man diesen Vorgang mehrmals wiederholen.

Ein anderer Weg ist es den Recovery Mode der Datenbank auf Simple umzustellen und danach Shrink Database aufzurufen.
Das folgende Skript führt diese Tasks aus und setzt den Recovery Mode am Ende wieder zurück auf Full.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use here_the_database_name
declare @Db varchar(max);
declare @template varchar(max);
declare @sql_script varchar(max);
declare @LogFileLogicalName sysname
select @Db=db_name()
set @template = 'ALTER DATABASE {DBNAME} SET RECOVERY SIMPLE WITH NO_WAIT';
set @sql_script = REPLACE(@template, '{DBNAME}', @Db)
execute(@sql_script)
select @LogFileLogicalName=Name from sys.database_files where Type=1
DBCC Shrinkfile(@LogFileLogicalName,1)
set @template = 'ALTER DATABASE {DBNAME} SET RECOVERY FULL WITH NO_WAIT';
set @sql_script = REPLACE(@template, '{DBNAME}', @Db)
execute(@sql_script)

Ressourcen in der Dose

Die Servlet 3 Spezifikation ist schon vor einigen Monaten veröffentlicht worden und es bieten in der Zwischenzeit viele Applikationsserver in den neuesten Versionen Unterstützung für Servlet 3.0 an. Tomcat ab Version 7, Jetty ab Version 7.5, sowie Server, die die Java EE 6 Spezifikation implementieren wie Glassfish 3 und JBoss 7.

Mit Servlet 3.0 können Servlets, Filter und Listeners mit Annotationen, anstatt einem Eintrag in der Datei web.xml, gekennzeichnet werden. Es existiert auch die Möglichkeit Servlets und Filter programmatisch dem Context hinzuzufügen. Eine weitere grössere Neuerung ist die Unterstützung für die asynchrone Verarbeitung von Requests.

Zusätzlich bietet Servlet 3.0 die Möglichkeit an Applikationen besser zu Modularisieren. Jars können Fragmente der Datei web.xml (Web Fragments) enthalten (META-INF/web.xml), welche die Definitionen für Servlets, Filter und Listeners beschreiben, die für die entsprechende Library notwendig sind. Beim Start des Applikationsservers wird der Klassenpfad nach diesen Files gescannt und verarbeitet.

Passend dazu ist es mit Servlet 3.0 möglich Ressourcen in ein Jar File einzuschliessen. Unterstützt werden dabei alle Ressourcetypen die man auch in das Root-Verzeichnis der Applikation ablegen kann. Statische Dateien wie HTML, Bilder, CSS und Javascript aber auch dynamische JSP Files können in ein Jar-File eingepackt werden. Dazu genügt es die Dateien in das Verzeichnis META-INF/resource abzulegen und das Jar File in das Verzeichnis WEB-INF/lib zu kopieren. Die Files sind dann vom Root der Webapplikation lesbar.

Damit ergeben sich interessante Anwendungsmöglichkeiten wie zum Beispiel Jars die eine Collection von Icons oder eine Javascript Library enhalten. In einem Mavenprojekt (oder auch Gradle und Ivy) genügt es die Library als Abhängigkeit einzutragen und hat dann Zugriff auf die darin enthaltenen Ressourcen. Es entfällt das umständliche Suchen nach den benötigten Dateien und Kopieren ins Rootverzeichnis der Applikation. Ein Update ist durch das Anpassen der Versionsnummer einfach möglich.

Ressource Jar

Hier eine Beschreibung wie man ein Ressourcen-Jar mit Maven erstellt und danach in ein Projekt integriert.

Unser Test Ressource-Jar enhält ein CSS-File, ein Bild und eine simple JSP Seite. Das Projektlayout sieht folgendermassen aus.

Unter src/main/resources befindet sich das META-INF/resources Verzeichnis mit den drei Ressourcen. Dies genügt bereits und es wird keine weitere Konfigurationsdatei benötigt.

Das pom.xml enhält nur die Angaben zur Group/Artifcat/Version, sowie ein Repository damit das Jar deployed (mvn deploy) werden kann. Ich benutze hier ein Verzeichnis des Webservers (http://ralscha.ch/repository). Wer Zugriff auf einen Repository Manager (Nexus, Artifactory, Archiva) hat benutzt mit Vorteil diesen.

pom.xml
1
2
3
4
5
6
7
8
9
10
11
<groupId>resources</groupId>
<artifactId>demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<distributionManagement>
<repository>
<id>localwww</id>
<url>file:/var/www/repository</url>
</repository>
</distributionManagement>

Web Applikation

Nachdem das Ressource Jar deployed ist können wir uns an die Entwicklung der Webapplikation machen. Das Projektlayout entspricht dem einer normalen Maven Webapplikation

Das Ressource-Jar wird im pom.xml als Abhängigkeit eingetragen.

pom.xml
1
2
3
4
5
<dependency>
<groupId>resources</groupId>
<artifactId>demo</artifactId>
<version>1.0.0</version>
</dependency>

Nicht fehlen darf hier der Eintrag zu unserem Repository damit Maven das Jar auch findet.

pom.xml
1
2
3
4
5
6
<repositories>
<repository>
<id>myprivaterepo</id>
<url>http://ralscha.ch/repository</url>
</repository>
</repositories>        

Die Webapplikation besteht nur aus einem minimalen WEB-INF/web.xml

web.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0"/>

sowie einer Html Seite

index.html
1
2
3
4
5
6
<!DOCTYPE HTML>
<meta charset="utf-8">
<link href="css/module.css" rel="stylesheet" type="text/css" />
<title>Simple HTML Page</title>
<img src="images/emoticon_smile.png">
<a href="test.jsp">Link to test.jsp</a>

Hier sieht man wie die Ressourcen, die sich im Jar-File befinden, ganz normal referenziert werden. Es besteht kein Unterschied zwischen Ressourcen die sich im Jar-File und Ressourcen die sich im Webroot der Applikation befinden.

Es ist möglich Dateien die sich im Jar befinden zu übersteuern. Dazu muss eine Datei mit gleichen Namen im Webapp Root erstellt werden. Diese hat dann Vorrang vor der Datei im Jar. Um also beispielsweise eine applikationsspezifische Version von test.jsp zu erstellen genügt es ein test.jsp unter src/main/webapp anzulegen.

Mit dem Jetty Maven Plugin lässt sich die Applikation von der Kommandozeile mit mvn jetty:run starten.

Embedded Jetty

Wenn man versucht die Applikation mit einem eingebetteten Jetty zu starten (siehe Blogpost Jetty Embedded) wird man feststellen das die Ressourcen im Jar nicht verfügbar sind. Dies hat den Grund das Jetty standardmässig nur Jars verarbeitet, die im Verzeichnis WEB-INF/lib abgelegt sind. Das Jetty Maven Plugin enthält bereits eine Anpassung damit dies auch mit den Jars funktioniert die sich im lokalen Maven Repository (<user.home>/.m2) befinden.

Auch für den eingebetteten Jetty gibt glücklicherweise eine Lösung. Es muss dazu eine Subklasse von org.eclipse.jetty.webapp.WebInfConfiguration erstellt und dort die Methode List<Resource> findJars (WebAppContext context) überschrieben werden. Diese Methode ist zuständig für das Finden der Jar-Dateien.

Diese angepasste WebInfConfiguration Klasse muss dann mit setConfigurations im Jetty eingetragen werden.

context.setConfigurations(new Configuration[] { new MavenWebInfConfiguration(),

Die komplette Implementation befindet sich auf Github
https://github.com/ralscha/resource-jar-demo/blob/master/demoapp/src/main/java/StartJetty.java

Diese Konfigurationsklasse liest das pom.xml ein und fügt alle dort aufgeführten Dependencies der Liste von Jars hinzu die beim Start von Jetty nach Ressourcen gescannt werden. Bei vielen Libraries kann das Scannen der Jar-Files einige Sekunden dauern. Deshalb hat diese Implementation neben dem Standard- (alle Jar scannen) noch einen weiteren Konstruktor dem eine Liste von Dependencies übergeben werden kann. Nur die aufgelisteten Dependencies werden von Jetty gescannt.

Mit folgendem Code wird nur unser Test Ressourcen File gescannt.

List<Artifact> includeOnlyArtifact = new ArrayList<Artifact>();
includeOnlyArtifact.add(new Artifact("resources", "demo"));
context.setConfigurations(new Configuration[] { new MavenWebInfConfiguration(includeOnlyArtifact),

Sourcecode der beiden Projekte ist auf Github abgelegt:
https://github.com/ralscha/resource-jar-demo

Weitere Links:
WEB-INF/lib/{\*.jar}/META-INF/resources
An Introduction To Servlet 3.0
Asynchronous Support in Servlet 3.0

Jetty Embedded

Der Jetty Web Server lässt sich sehr einfach in eigene Programme einbetten. Wir benutzen diesen Server innerhalb von Eclipse um Webapplikationen mit Maven zu entwickeln. Die Webapplikationen lassen sich mit dem eingebetteten Jetty wie normale Javaprogramme starten und debuggen.

Für einen ersten Test benötigen wir die folgenden Abhängigkeiten. Der Jetty 8.0.x setzt Java 6 voraus. Wenn noch Java 5 eingesetzt wird, muss stattdessen der Jetty 7.5.x benutzt werden.

<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-server</artifactId>
	<version>8.0.4.v20111024</version>
	<scope>provided</scope>
</dependency>

<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-webapp</artifactId>
	<version>8.0.4.v20111024</version>
	<scope>provided</scope>
</dependency>

<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-servlets</artifactId>
	<version>8.0.4.v20111024</version>
	<scope>provided</scope>
</dependency>

Die Libraries werden im Scope “provided” hinzugefügt, damit sie nicht in das fertige WAR-File eingepackt werden.

Das Projektlayout entspricht einem normalen Mavenprojekt. Der Java Sourcecode befindet sich im src/main/java Directory. Die HTML/JSP Seiten sowie der WEB-INF Ordner sind unter src/main/webapp abgelegt.

src/main/webapp/index.html

<!DOCTYPE HTML>
<meta charset="utf-8">
<title>Simple HTML Page</title>
the body

src/main/webapp/WEB-INF/web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
        http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" />

Der eingebettete Jetty Server wird wie folgt gestartet.

public class JettySimple {
	public static void main(String[] args) throws Exception {
		WebAppContext context = new WebAppContext("./src/main/webapp", "/");
		Server server = new Server(8080);
		server.setHandler(context);
		server.start();
	}
}

Der zweite Parameter der WebAppContext Klasse definiert den Context. Für dieses Beispiel benutzen wir den Root Context (/). Im Browser muss deshalb die Applikation mit http://localhost:8080/ aufgerufen werden.

Wenn das Projekt auch JSP Seiten beinhaltet müssen weitere Libraries dem Projekt hinzugefügt werden.

<dependency>
	<groupId>org.mortbay.jetty</groupId>
	<artifactId>jsp-impl</artifactId>
	<version>2.2.2.b05.0</version>
	<scope>provided</scope>
</dependency>

<dependency>
	<groupId>javax.servlet.jsp</groupId>
	<artifactId>jsp-api</artifactId>
	<version>2.2.1-b03</version>
	<scope>provided</scope>
</dependency>

<dependency>
	<groupId>org.glassfish.web</groupId>
	<artifactId>el-impl</artifactId>
	<version>2.2.1-b05</version>
	<scope>provided</scope>
</dependency>

Um auch die Tags der JSTL (JavaServer Pages Standard Tag Library) zu benutzen (c:if, c:test, …) wird folgende Library benötigt.

<dependency>
	<groupId>org.glassfish.web</groupId>
	<artifactId>jstl-impl</artifactId>
	<version>1.2</version>
	<exclusions>
		<exclusion>
			<artifactId>servlet-api</artifactId>
			<groupId>javax.servlet</groupId>
		</exclusion>
	</exclusions>
</dependency>

Diese Library wird im normalen Scope eingefügt, damit sie im WAR-File landet. Dies ist dann nötig wenn der Zielserver die Library nicht bereits mitliefert (wie z.B. Tomcat). Wenn der Zielserver die JSTL bereits beinhaltet kann hier der Scope auf provided geändert werden.

Als Testseite verwenden wir folgende JSP Seite.

src/main/webapp/test.jsp
<!doctype html>
<%@ page language="java" pageEncoding="UTF-8" contentType="text/html; charset=utf-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html><head><title>Simple JSP</title></head><body>
<p>
<c:if test="${params.test}">
request contains test parameter
</c:if>
<c:if test="${not params.test}">
no request parameter
</c:if>
</p>
<p>TEST=${params.test}</p>
</body>
</html>

Der Jetty Server wird mit dem JettySimple Programm gestartet und die JSP Seite im Browser mit http://localhost:8080/test.jsp?test=1 geöffnet.

Lock unter Windows

Unter Windows wird man eventuell ein Problem haben wenn versucht wird HTML/JSP Seiten zu editieren während der Jetty Server läuft. Jetty benutzt Memory-Mapped Files und diese Files werden unter Windows gelockt, so das diese nicht editiert werden können. Man müsste jedesmal den Server anhalten um eine Datei zu ändern. Man kann aber Jetty so konfigurieren das keine Memory-Mapped Files benutzt werden.

Dazu lädt man sich eine Jetty Distribution herunter und extrahiert das File webdefault.xml. Die Datei wird in das Projekt in einen beliebigen Ordner kopiert. Wir benutzen normalerweise den Ordner src/main/config. In der Datei webdefault.xml wird nun der Parameter useFileMappedBuffer auf false gesetzt.

<init-param>
   <param-name>useFileMappedBuffer</param-name>
   <param-value>false</param-value>
 </init-param>

Das Startprogramm wird erweitert damit das geänderte webdefault.xml eingelesen wird.

WebAppContext context = new WebAppContext("./src/main/webapp", "/");
context.setDefaultsDescriptor("./src/main/config/webdefault.xml");

Server server = new Server(8080);
server.setHandler(context);
server.start();

Fehlermeldung bei bereits belegtem Port

Eine weitere kleine Unschönheit ist, das standardmässig keine Fehlermeldung, wegen des bereits belegten Ports, ausgegeben wird wenn der Server zweimal gestartet wird.

Damit eine Exception geworfen wird wenn der Port bereits belegt ist, muss im Connector reuseAddress auf false gesetzt.

Server server = new Server(8080);
((SelectChannelConnector) server.getConnectors()[0]).setReuseAddress(false);
server.setHandler(context);
server.start();

Alternativ kann man sich auch seinen eigenen Port Check einbauen. Das folgende Programm schreibt eine Meldung in die Console wenn der Port bereits belegt ist und beendet sich danach.

int port = 8080;

try {
	ServerSocket srv = new ServerSocket(port);
	srv.close();
} catch (IOException e) {
	System.out.println("PORT " + port + " ALREADY IN USE");
	return;
}

WebAppContext context = new WebAppContext("./src/main/webapp", "/");
context.setDefaultsDescriptor("./src/main/config/webdefault.xml");

Server server = new Server(port);
server.setHandler(context);
server.start();

JNDI

Auch JNDI lässt sich in einem eingebetteten Jetty benutzen. Dazu wird die jetty-plus LIbrary benötigt.

<dependency>
	<groupId>org.eclipse.jetty</groupId>
	<artifactId>jetty-plus</artifactId>
	<version>8.0.4.v20111024</version>
	<scope>provided</scope>
	<exclusions>
		<exclusion>
			<artifactId>activation</artifactId>
			<groupId>javax.activation</groupId>
		</exclusion>
	</exclusions>
</dependency>

Die Java Activation Library wird hier ausgeschlossen, da sich diese bereits im Java 6 Runtime befindet.

Im Start Programm muss die Configuration des Contextes angepasst werden damit JNDI benutzt werden kann. Wichtig sind die beiden Configuration Klassen EnvConfiguration und PlusConfiguration.

WebAppContext context = new WebAppContext("./src/main/webapp", "/");
context.setDefaultsDescriptor("./src/main/config/webdefault.xml");

context.setConfigurations(new Configuration[] {
		new org.eclipse.jetty.webapp.WebInfConfiguration(),
		new org.eclipse.jetty.webapp.WebXmlConfiguration(),
		new org.eclipse.jetty.webapp.MetaInfConfiguration(),
		new org.eclipse.jetty.webapp.FragmentConfiguration(),
		new org.eclipse.jetty.plus.webapp.EnvConfiguration(),
		new org.eclipse.jetty.plus.webapp.PlusConfiguration(),
		new org.eclipse.jetty.webapp.JettyWebXmlConfiguration() });

Einen einfachen String Eintrag fügt man mit addBean in den Context.

context.addBean(new EnvEntry("aSimpleEntry", "theValue"));

Innerhalb der Webapplikation lässt sich dieser Wert wie folgt auslesen:

InitialContext ic = new InitialContext();
String value = (String) ic.lookup("java:comp/env/aSimpleEntry")

Auch Datasourcen lassen sich einfügen. Hier ein Beispiel mit der JdbcDataSource von H2.

JdbcDataSource dataSource = new JdbcDataSource();
dataSource.setUser("sa");
dataSource.setURL("jdbc:h2:~/starter");

context.addBean(new org.eclipse.jetty.plus.jndi.Resource("jdbc/ds", dataSource));

Für dieses Beispel wird zusätzlich ein resource-ref Eintrag im web.xml benötigt um dann in der Applikation mit ic.lookup(“java:comp/env/jdbc/ds”) die DataSource zu lesen.

<resource-ref>
	<description>The DataSource Reference</description>
	<res-ref-name>jdbc/ds</res-ref-name>
	<res-type>javax.sql.DataSource</res-type>
	<res-auth>Container</res-auth>
</resource-ref>

Der Sourcecode zu all diesen Beispielen findet man in meinem GitHub Account unter: https://github.com/ralscha/jettyembed

Einfacher E-Mail Versand mit commons-email

Die JavaMail API liefert alles mit um Text- und HTML E-Mails mit und ohne Attachments zu versenden.

Eine einfache Text E-Mail mit US-ASCII Encoding lässt sich folgendermassen kreieren und versenden:

		Properties sessionProperties = new Properties();
		sessionProperties.put("mail.smtp.host", "localhost");

		Session mailSession = Session.getDefaultInstance(sessionProperties);

		MimeMessage message = new MimeMessage(mailSession);
		message.setSender(new InternetAddress("boss@test.com"));
		message.addRecipient(Message.RecipientType.TO, new InternetAddress("test@test.com"));
		message.setSubject("The subject");
		message.setText("The content");

		Transport.send(message);

Da die JavaMail API nicht Bestandteil des Java Runtime ist müssen wir die Library in unser Projekt hinzufügen. In einem Maven genügt folgender Eintrag im pom.xml. Das JavaBeans Activation Framework (JAF) ist eine Abhängigkeit von JavaMail, schliessen wir aber aus da es in Java 6 und 7 enthalten ist.

		<dependency>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
			<version>1.4.4</version>
			<exclusions>
				<exclusion>
					<artifactId>activation</artifactId>
					<groupId>javax.activation</groupId>
				</exclusion>
			</exclusions>
		</dependency>

Komplizierter wird es wenn man Attachments mitsenden will. Man muss sich mit Klassen wie MimeBodyPart, MimeMultipart und DataSource herumschlagen. Ein Beispiel findet man hier:
http://www.java-tips.org/other-api-tips/javamail/how-to-send-an-email-with-a-file-attachment.html

Um den E-Mail Versand zu vereinfachen haben die Leute von Apache die commons-email Library entwickelt. Damit lassen sich Text E-Mails wie auch umfangreiche HTML E-Mails mit eingebetteten Grafiken und Attachments einfach erstellen und versenden.

Als Abhängigkeit wird dieser Eintrag im pom.xml benötigt. Auch hier wird JAF ausgeschlossen da bereits im Java Runtime enthalten.

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-email</artifactId>
			<version>1.2</version>
			<exclusions>
				<exclusion>
					<artifactId>activation</artifactId>
					<groupId>javax.activation</groupId>
				</exclusion>
			</exclusions>
		</dependency>

Dasselbe Text E-Mail wie oben mit commons-email.

		Email email = new SimpleEmail();
		email.setHostName("localhost");
		email.setFrom("boss@test.com");
		email.addTo("test@test.com");
		email.setSubject("The subject");
		email.setMsg("The content");
		email.send();

Im Gegensatz zur JavaMail API muss man sich als Entwickler nur mit einer Klasse (SimpleEmail) beschäftigen.

Nicht viel komplizierter ist der Versand eines Attachments. Statt der Klasse SimpleEmail wird MultiPartEmail eingesetzt und für jedes Attachment wird eine Instanz der Klasse EmailAttachment benötigt.

		MultiPartEmail email = new MultiPartEmail();
		email.setHostName("localhost");
		email.setFrom(""boss@test.com", "The Boss");
		email.addTo("test@test.com", "John Test");

		email.setSubject("Email with Attachment");
		email.setMsg("The swiss flag");

		EmailAttachment attachment = new EmailAttachment();
		attachment.setURL(new URL("http://www.flags.net/images/largeflags/SWIT0001.GIF"));
		attachment.setDisposition(EmailAttachment.ATTACHMENT);
		attachment.setDescription("Swiss Flag");
		attachment.setName("swissflag");

		email.attach(attachment);

		email.send();

Als Attachment können URLs angegeben werden die sich irgendwo im Intra- oder Internet befinden. commons-email lädt die Resource herunter und erstellt damit den E-Mail Anhang. Es ist auch möglich Resourcen vom Klassenpfad einzulesen

	attachment.setURL(getClass().getResource("/alogo.png"));

oder vom Dateisystem mit setPath

    attachment.setPath("/home/alogo.png");

Html E-Mails werden mit der Klasse HtmlEmail erstellt. Um Bilder mit dem img Tag einzubetten wird eine Content-ID (cid) benötigt. Diese cid erhält man wenn das Bild mit der Methode embed eingebettet wird. Die cid wird dann im src Attribute des img Tag eingetragen. Alternativ kann ein ASCII Text mitgesendet werden für alle Empfänger deren Klient keine HTML E-Mails anzeigen kann.

		HtmlEmail email = new HtmlEmail();
		email.setHostName("localhost");
		email.setFrom("boss@test.com", "The Boss");
		email.addTo("test@test.com", "John Test");
		email.setSubject("Email with inline image");

		URL imageUrl = new URL("http://www.flags.net/images/largeflags/SWIT0001.GIF");
		String cid = email.embed(imageUrl, "The Swiss Flag");

		email.setHtmlMsg("<html>Swiss Flag - <img src=\"cid:" + cid + "\"></html>");

		email.setTextMsg("Your email client does not support HTML messages");

		email.send();

Sourcecode der Beispiele auf GitHub:
https://github.com/ralscha/java-playground/tree/master/mail

Weitere Beispiele und Beschreibungen findet man im commons-email User Guide

IIS 7.5 Upload Limit

Wir betreiben eine Java Webapplikation, welche in einem Tomcat Server läuft. Die Requests werden von einem IIS 7.5 mit dem Tomcat Connector via AJP zum Tomcat gesendet. Nun hatten wir das Problem das Fileuploads jeweils nach 30 MB mit einer 404 Fehlermeldung abgebrochen wurden.

Das Problem ist das im IIS 7.5 per Default ein Uploadlimit von 30 MB konfiguriert ist. Dieses Limit lässt sich einfach ändern. Dazu öffnet man im IIS Manager die entsprechende Webseite. In diesem Fall Default Web Site.

Dann doppelklickt man auf Request Filtering. Auf der Request Filtering Seite öffnet man das Contextmenu mit Rechtsklick und wählt den Menupunkt Edit Feature Settings… aus

Im Request Filtering Settings Dialog ändert man den Wert Maximum allowed content length auf den gewünschten Wert. Der Wert gibt die maximale Anzahl Bytes an die ein Request als Inhalt senden darf.

JUnit mit Fest-assert

Wie in diesem Blogpost beschrieben kann man mit Hamcrest die Lesbarkeit und Wartbarkeit seiner JUnit Tests verbessern.

Im folgenden Beispiel wollen wir prüfen ob ein String mit einer bestimmten Zeichenkette beginnt und endet. Da es in JUnit keine Methode startsWith oder endsWith gibt behelfen wir uns mit assertTrue.

String s = "Ralf";
assertTrue(s.startsWith("Ra"));
assertTrue(s.endsWith("ph"));

Das Problem ist das die Fehlermeldung nicht sehr aussagekräftig ist:

java.lang.AssertionError:
	at org.junit.Assert.fail(Assert.java:91)

Mit Hamcrest können wir den Test so schreiben

import static org.junit.Assert.assertThat;
import static org.hamcrest.core.StringEndsWith.endsWith;
import static org.hamcrest.core.StringStartsWith.startsWith;
import static org.hamcrest.CoreMatchers.allOf;

assertThat(s, startsWith("Ra"));
assertThat(s, endsWith("ph"));
//oder in einer Zeile mit einer UND Verknüpfung
assertThat(s, allOf(startsWith("Ra"), endsWith("ph")));

und erhalten dann eine detaillierte Fehlermeldung

java.lang.AssertionError:
Expected: (a string starting with "Ra" and a string ending with "ph")
     got: "Ralf"

Das Problem mit Hamcrest ist das wir wissen müssen welche Matcher vorhanden sind. Dieses Problem löst die Fest-Assert Library sehr elegant mit einer Fluent API.

Mit Fest müssen wir nur eine Methode kennen und importieren:

import static org.fest.assertions.Assertions.assertThat;

Der assertThat Methode wird als Parameter das zu prüfende Objekt übergeben. Eine Entwicklungsumgebung mit Code Completion listet, nach Eingabe des Punktes oder einer Tastenkombination, alle passenden Methoden auf mit denen das Objekt geprüft werden kann. Im Gegensatz zu Hamcrest hilft uns also die IDE bei der Erstellung der Tests.

String s = "Ralf";
assertThat(s).startsWith("Ra");
assertThat(s).endsWith("ph");
//oder in einer Zeile
assertThat(s).startsWith("Ra").endsWith("ph");

Die Fehlermeldung ist wie bei Hamcrest Beispiel detaillierter und weist direkt auf den Fehler hin.

java.lang.AssertionError: <'Ralf'> should end with:<'ph'>
	at org.fest.assertions.Fail.failure(Fail.java:228)

Wenn statt einem String ein Integer der assertThat Methode übergeben wird stehen Methoden wie isGreaterThan oder isLessThan zur Verfügung. Die Entwicklungsumgebung zeigt uns alle Möglichkeiten an und wir brauchen nur die benötigte Methoden auszuwählen.

assertThat(1).isGreaterThan(0).isLessThan(2).isNotIn(4,5).isIn(1,2).isSameAs(1);

Beispiel mit einer List:

List<String> words = Arrays.asList("one", "two");
assertThat(words).contains("one").doesNotHaveDuplicates()
     .containsExactly("one", "two").hasSize(2).isNotEmpty();

Mit onProperty hat man Zugriff auf ein Property und kann darauf Prüfungen aufrufen. Hier wird geprüft ob Personen mit firstName “John” und “Jane” in der Liste vorhanden sind:

List<Person> persons = Arrays.asList(new Person("John", "Doe"),
                                         new Person("Jane", "Doe"));
assertThat(persons).onProperty("firstName").contains("John", "Jane");

public class Person {
	private String firstName;
	private String lastName;
	public Person(String firstName, String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}
	public String getFirstName() {return firstName;}
	public String getLastName() {return lastName;}
}

Die Fest Library enthält Methoden für diese Datentypen:

  • Object
  • String
  • Collection
  • List
  • Map
  • Primitives (boolean, int, char, etc.)
  • Object arrays
  • Arrays of primitives
  • BufferedImage
  • Throwable
  • File
  • BigDecimal

Beispiel mit File:

File f = new File(".....");
File expectedFile = new File(".....");		

assertThat(f).exists().isFile().hasSameContentAs(expectedFile);

Fest lässt sich auch mit eigenen Bedingungen erweitern. Dazu erstellt man eine Subklasse von Condition und implementiert die Method matches. Ein Beispiel mit einer Anonymen Inner Class.

String s = "ralph";
assertThat(s).satisfies(new Condition<String>("First character uppercase") {
	@Override
	public boolean matches(String value) {
	    if (value != null && value.length() > 0) {
		    String firstChar = value.substring(0,1);
		    return firstChar.equals(firstChar.toUpperCase());
	    }
	    return false;
	}
});

Dies ist nur dann praktikabel wenn die Condition nur einmal benötigt wird. Wenn die Prüfung an mehreren Stellen im Code aufgerufen werden soll erstellt man eine eigene Klasse.

public final class FirstCharacterUppercase extends Condition<String> {
	public FirstCharacterUppercase() {
		super("First character uppercase");
	}

	@Override
	public boolean matches(String value) {
	    if (value != null && value.length() > 0) {
		    String firstChar = value.substring(0,1);
		    return firstChar.equals(firstChar.toUpperCase());
	    }
	    return false;
	}
}

und ruft assertThat folgendermassen auf:

String s = "ralph";
assertThat(s).satisfies(new FirstCharacterUppercase());			

//Lässt sich auch mit anderen Bedingungen kombinieren
assertThat(s).isNotEmpty().isNotEqualTo("Doe")
           .satisfies(new FirstCharacterUppercase());	

//Umgekehrter Fall
//assertThat(s).doesNotSatisfy(new FirstCharacterUppercase());

Als Fehler wird folgende Meldung ausgegeben:

java.lang.AssertionError: actual value:<'ralph'> should satisfy condition:<First character uppercase>
	at org.fest.assertions.Fail.failure(Fail.java:228)

Fest ist nicht Bestandteil von JUnit und wird mit folgenden Angaben in ein Mavenprojekt als Abhängigkeit eingefügt:

<dependency>
	<groupId>org.easytesting</groupId>
	<artifactId>fest-assert</artifactId>
	<version>1.4</version>
	<scope>test</scope>
</dependency>

Wer seine JUnit asserts durch Fest assertThat ersetzen will findet auf der Migrationsseite von Fest eine Auflistung von Regular Expressions mit denen die JUnit Statements ersetzt werden können. JUnit und Fest asserts können aber ohne Probleme nebeneinander existieren.

Eigene Git Repositories mit Gitolite

Git wird als Sourcecode Verwaltungstool immer populärer. Wer ein Open Source Projekt managt kann dieses beispielsweise kostenlos bei Github hosten. Auch Google Code unterstützt seit Ende Juli Git als zusätzliche Option neben Subversion und Mercurial. Diese kostenlosen Repositories sind meistens öffentlich zugänglich, was natürlich bei einem Open Source Projekt erwünscht ist. Wer dagegen ein Projekt lieber im Verborgenen bearbeiten will muss eine monatliche Gebühr bezahlen. Bei Github kostet zum Beispiel die kleinste Variante 7 USD pro Monat.

Ein eigenes privates Git Repository lässt sich aber auch sehr einfach selber aufbauen. Dies für Leute die bereits einen eigenen virtuellen Server irgendwo im Internet herumstehen haben (z.B. Slicehost, Linode, Hosteurope, server4you, …) oder wer in einem Firmennetzwerk ein Repository installieren muss.

Mit Gitolite lässt sich Git sehr einfach installieren und erlaubt es Berechtigungen auf Repository und sogar auf Branch und Tag Ebene zu vergeben. Damit ist es beispielsweise möglich dass nur bestimmte Personen oder Gruppen von Personen Änderungen in einen Branch oder Tag pushen können. Gitolite läuft auf verschiedenen Linuxvarianten und auf Solaris 10. Für die Installation benötigt man einen User Account auf diesem Server. Es muss aber kein Root Account sein, unter der Bedingung das git, perl und openssh bereits installiert sind.

Im folgenden eine kurze Beschreibung wie man Gitolite auf einem Ubuntu Server mit Root Access installiert und mit Eclipse/EGit darauf zugreift.

Die Kommunikation mit dem Server läuft über SSH. Deshalb erstellen wir zuerst ein Schlüsselpaar aus privatem und öffentlichem Schlüssel. In Eclipse öffnen wir dazu den Preferences Dialog (Windows->Preferences) und wählen die Option General->Network Connections->SSH2. Wer bereits ein Schlüsselpaar für einen anderen Dienst (z.B. Github) erstellt hat kann dieses verwenden. Wo der Schlüssel abgelegt ist zeigt das Textfeld SSH2 home an. Wenn noch kein Schlüssel vorhanden ist kann man ihn auf dem Tab Key Management erstellen. Dort klickt man auf Generate RSA Key und speichert den Schlüssel in einem beliebigen Verzeichnis (Save Private Key…). Wichtig ist das man den Pfad zum Schlüssel auf dem Tab General im Feld SSH2 home einträgt, damit Eclipse den Schlüssel findet.

Den öffentlichen Schlüssel muss nun auf den Git Server kopiert werden (z.B. mit SCP oder SFTP). Der Schlüssel kann in ein beliebiges Verzeichnis kopiert werden. Für dieses Beispiel wurde der Schlüssel unter /tmp/id_rsa.pub abgelegt.

Wenn noch nicht vorhanden wird nun Git installiert:

apt-get install git

Für Gitolite gibt es auch rpm und deb packages, in diesem Beispiel installieren wir es aber direkt vom Sourcecode. Wir erstellen einen Benutzer git unter dem Gitolite installiert wird, kann aber auch ein beliebig anderer Benutzer sein. Wer keinen Root Account besitzt der installiert die Software unter seinem Account.

git clone git://github.com/sitaramc/gitolite
src/gl-system-install
useradd -m git

su - git
gl-setup /tmp/id_rsa.pub

Im letzten Schritt wird mit gl-setup der öffentliche Schlüssel den wir auf den Server kopiert haben installiert. Die Datei kann danach gelöscht werden. Wer plant das mehrere Benutzer auf das Repository zugreifen sollte die Schlüsseldatei umbenennen (z.B. username.pub), damit die Verwaltung der Schlüssel und Konfiguration der Rechte vereinfacht wird.

Damit ist die Installation auf dem Server abgeschlossen. Das Install Skript hat zwei Repositories erstellt: gitolite-admin und testing. Mit dem testing Repository kann man herumspielen und Git kennenlernen oder man löscht es wieder.

Das wichtige Repository ist gitolite-admin über welches die Administration von Gitolite läuft. Dazu erstellt man Änderungen in diesem Repository und pusht diese zum Server.

In Eclipse clonen wir nun dieses Repository

Wichtig ist das als User der Benutzername eingetragen wird unter dem Gitolite installiert wurde. Danach importiert man das Projekt als General Project damit man Änderungen vornehmen kann.

Ein neues Repository können wir nun sehr einfach aufsetzen indem die Datei conf/gitolite.conf angepasst wird. Hier erstellen wir ein neues Repository mynewrepo auf das alle Benutzer Zugriff haben.

repo    gitolite-admin
        RW+     =   id_rsa

repo    mynewrepo
        RW+     =   @all

repo    testing
        RW+     =   @all

Danach commited man die Änderung, pusht sie zum Server und das neue Repository ist verfügbar. Auf dem Server sind keine Aktionen notwendig.

Um einen neuen Benutzer einzufügen, kopieren wir seinen öffentlichen Schlüssel ins keydir Verzeichnis des gitolite-admin Projekts und in der Datei conf/gitolite.conf muss der Benutzer für das entsprechende Repository berechtigt werden. Nach einem Commit und Push hat der neue User Zugriff auf das Repository.

Weitere Berechtigungsbeispiele findet man im Pro Git Buch, Kapitel Gitolite unter “Config File and Access Control Rules”

Wer eine umfangreichere Lösung mit Webinterface für sein Git Repository möchte der sollte sich das Projekt Gitorious genauer ansehen. Die Software die dort eingesetzt wird ist im Sourcecode verfügbar und kann auf eigenen Servern installiert werden. Eine Installationsbeschreibung für Ubuntu findet man hier. Gitorious bietet auch komplette aufgesetzte Virtual Applicances an.

Links zu Gitolite und Git:

Github Page von Gitolite
Git Homepage
Git Reference

Pro Git Buch
Kapitel über Gitolite

Präsentation von Scott Chacon, Autor von Pro Git

Gesalzene Passwörter

Wenn Passwörter in Applikationen benutzt werden, sollte man diese nicht im Klartext in der Datenbank ablegen. Ein Angreifer hätte leichtes Spiel und da die meisten Benutzer dasselbe Passwort mehrfach benutzen kann ein Einbrecher unbefugt auf weitere Dienste zugreifen.

Die meisten Applikationen speichern die Passwörter daher als Hashwerte ab. Eine Hashfunktion hat die Eigenschaft das sie eine Ein-Weg Funktion ist. Man kann von einem Wort den Hash berechnen aber man kann aus dem Hash das Wort nicht mehr herstellen. Aus diesem Grund muss beim Einloggen jedesmal aus dem eingegebenen Passwort der Hash berechnet werden um diesen dann mit dem Wert in der Datenbank zu vergleichen. Ein Angreifer muss den gleichen Weg gehen und beim Durchprobieren von jedem Wort den Hash berechnen und mit dem abgespeicherten Wert vergleichen (Brute Force).

Früher hat man den Hash mit MD5 oder SHA-1 berechnet. Mit der Rechenleistung die vor einigen Jahren vorhanden war dauerte das Ausprobieren aller Kombinationen mehrere Monaten oder sogar Jahre.

Mit dem Aufkommen von Cloudcomputing und leistungsfähigen Grafikkarten (GPU) können aber heutzutage mehrere Millionen Kombinationen pro Sekunde durchprobiert werden. In dieser Studie der Universität Amsterdam über GPU-based password cracking wurden bis zu 3 Milliarden Kombinationen pro Sekunde erreicht. Dies lässt sich nochmals massiv steigern in dem mehrere Grafikkarten eingesetzt werden.

Angreifer haben aber noch ein weiteres Werkzeug zur Hand mit dem Brute Force Attacken meistens überflüssig werden. Rainbow Tables sind vorberechnete Tabellen die ein Wort und den dazugehörigen Hashwert enthalten. Ein Program muss also nur den Hash in diesen Tabellen suchen. Dadurch ist es möglich Passwörter in wenigen Minuten zu finden. Da immer mehr Speicherplatz zur Verfügung steht werden diese Listen auch immer grösser. Freie Rainbow Tables findet man zum Beispiel auf dieser Seite: http://www.freerainbowtables.com/en/tables/
Manchmal genügt es auch einen Hash in einer Suchmaschine einzugeben. Die Suche bei Google mit dem MD5 Hash “5f4dcc3b5aa765d61d8327deb882cf99″ gibt als erstes Resultat das gesuchte Wort “password” zurück.

Aus diesen Gründen sollte man sich heutzutage mehr Gedanken machen wie man die Passwörter der Benutzer in der Datenbank ablegt. Um den Einsatz von Rainbow Tables zu verhindern sollte man das Passwort vor dem Berechnen des Hashes mit einer zusätzlichen Zeichenkette (Salt) ergänzen. Man sollte aber nicht ein globales Salt verwenden, denn dann kann es sich für einen Angreifer unter Umständen lohnen eine Rainbow Table mit diesem Salt zu berechnen. Jedes Userpasswort muss also sein eigenes Salt erhalten. Man könnte zum Beispiel den Benutzernamen oder die E-Mail verwenden. Dann muss allerdings darauf geachtet werden, dass wenn diese Angaben ändern das auch das Passwort neu gehasht wird (Eingabe des Passwortes beim Aendern dieser Angaben). In Linux wird eine zufällige Zeichenkette pro Benutzer erzeugt. Damit sich der Benutzer aber immer noch einloggen kann muss das Salt im Klartext bekannt sein. Linux löst dies in dem das Salt dem gehashten Passwort vorangestellt wird. Ein Angreifer hat dadurch zwar auch Zugriff auf das Salt aber dies spielt keine grosse Rolle da das Salt in erster Linie den Einsatz von Rainbow Tables verhindern soll. Ein Salt hat auch noch den zusätzlichen Effekt das Brute Force Attacken erschwert werden. Bei einer Brute Force Attacke werden Hashes berechnet und diese können, wenn kein Salt eingesetzt wird, mit allen Werten in der Datenbank verglichen werden. Ein Hash muss so nur einmal berechnet werden. Mit einem Salt wird diese parallele Brute Force Attacke verhindert. Auch wenn zwei Benutzer das gleiche Passwort besitzen ist der abgespeicherte Hash unterschiedlich.

Hier ein Beispiel eines gehashten Passwortes aus der Datei /etc/shadow
$1$td0vA7/Q$tz0EERs49FYboHN8qIX700
Die 1 zwischen dem ersten und zweiten Dollarzeichen gibt den Algorithmus an (md5-crypt). Die folgenden 8 Zeichen (td0vA7/Q) sind das Salt im Klartext und danach folgt das gehashte Passwort (tz0EERs49FYboHN8qIX700)

Brute Force Attacken sind trotz Salt aber weiterhin möglich. Ein Angreifer muss nur wissen wie er das Salt mit dem Klartext verknüpfen muss und kann wie bisher alle Kombinationen durchprobieren. Deshalb versucht man durch künstliche Verlangsamung des Hashalgorithmus den Angreifer auszubremsen. Bereits eine Verlangsamung um den Faktor 1000 bedeutet das ein Angreifer statt 600 Mio. nur noch 600’000 Passwörter pro Sekunde ausprobieren kann. Statt Tage kann es Jahre dauern um alle Kombinationen durchzuprobieren.

Eine Verlangsamung erreicht man indem der Hashalgorithmus mehrmals durchlaufen wird. Der berechnete Hash wird wiederum als Input für die nächste Runde verwendet. In Pseudocode kann dies so aussehen:

hash = hashfunction(klartext)
for 1 to anzahl_runden
  hash = hashfunction(hash)

Ubuntu beispielsweise durchläuft standardmässig 5000 Runden. Man kann die Rundenfunktion auch komplexer gestalten indem beispielsweise das Klartextpasswort und/oder das Salt nochmals in den Hash eingefügt wird. Diese Rundenfunktion muss auch beim Einloggen eines Benutzer durchlaufen werden. Deshalb sollte man für die Anzahl Runden einen Wert wählen, bei dem ein Angreifer ausreichend ausgebremst wird aber das Einloggen nicht zu sehr verlangsamt wird.

Java liefert alles nötige mit um Hashes zu berechnen und um seine eigene Rundenfunktion zu erstellen. Einfacher geht’s aber, wie so oft, mit einer Library. Jasypt (Java Simplified Encryption) enthält Klassen um Hashes mit Salt zu berechnen und zu prüfen.

Am einfachsten und schnellsten geht’s mit der Klasse BasicPasswordEncryptor. Diese benutzt als Algorithmus MD5, ein Salt von 8 Bytes und 1000 Iterationen.

PasswordEncryptor passwordEncryptor = new BasicPasswordEncryptor();
String encryptedPassword = passwordEncryptor.encryptPassword("thePassword");

if (passwordEncryptor.checkPassword("thePassword", encryptedPassword)) {
	// correct password
} else {
	// wrong password
}

Sicherer aber auch langsamer wird’s mit der StrongPasswordEncryptor Klasse. Diese benutzt als Algorithmus SHA-256, ein Salt von 16 Bytes und berechnet den Hash 100’000 mal.

PasswordEncryptor passwordEncryptor = new StrongPasswordEncryptor();

Die PasswordEncryptor Klassen sind gemäss API Dokumentation thread-safe. Es sollte daher kein Problem sein diese nur einmal zu instanziieren und mehrmals zu verwenden. Diese beiden Klassen sind vorkonfigurierte Versionen des StandardStringDigester. Um alle Aspekte selber zu konfigurieren kann man diese Klasse direkt einsetzen.

StandardStringDigester digester = new StandardStringDigester();
digester.setAlgorithm("SHA-256");
digester.setIterations(123456);
digester.setSaltSizeBytes(32);

String digest = digester.digest("userPassword");

if (digester.matches("userPassword", digest)) {
	//correct
} else {
	//wrong
}

Defaultmässig wird das Salt und gehashte Passwort als Base64 String ausgegeben. Mit
digester.setStringOutputType(CommonUtils.STRING_OUTPUT_TYPE_HEXADECIMAL);
kann man auf einen Hex Output wechseln.

Wenn nichts anderes angegeben wird benutzt der StandardStringDigester den RandomSaltGenerator. Wenn man sein eigenes Salt verwenden will kann man dies mit dem FixedStringSaltGenerator angeben:

StandardStringDigester digester = new StandardStringDigester();
digester.setAlgorithm("SHA-256");
digester.setIterations(10000);

FixedStringSaltGenerator saltGenerator = new FixedStringSaltGenerator();
saltGenerator.setSalt("the_users_salt");
digester.setSaltGenerator(saltGenerator);

String digest = digester.digest("userPassword");

Der String digest enhält in diesem Fall nur den Hash ohne Salt. Mit dem RandomSaltGenerator wird das Salt im Klartext vorangestellt.

Hier findet man eine genaue Beschreibung wie Jasypt das Salt verknüpft und wie die Rundenfunktion implementiert ist:
http://www.jasypt.org/encrypting-passwords.html

Jasypt lässt sich mit folgender Abhängigkeit in ein Maven Projekt einbinden

	    <dependency>
	      <groupId>org.jasypt</groupId>
	      <artifactId>jasypt</artifactId>
	      <version>1.7.1</version>
	    </dependency>

In der c’t Ausgabe 13 vom 6.6.2011 findet sich ein Artikel zu diesem Thema
http://www.heise.de/artikel-archiv/ct/2011/13/148_kiosk

CSV einlesen mit Java

Comma-separated values Dateien mit Java einzulesen ist eigentlich eine einfache Sache. Wenn zum Beispiel folgender Input vorhanden ist

Lillian,placerat.eget.venenatis@temporloremeget.org,1
Yoshio,quam@Crasdolordolor.edu,2
Philip,metus.In.lorem@vel.org,3

dann lässt sich dieser mit den Bordmitteln von Java so einlesen

String line;
while((line = br.readLine()) != null) {
	StringTokenizer st = new StringTokenizer(line, ",");
	String name = st.nextToken();
	String email = st.nextToken();
	Integer id = Integer.valueOf(st.nextToken());
}

oder mit der split Methode der Klasse String. Bei der split Methode muss man daran denken das der Parameter eine Regular Expression sein muss. Für dieses Beispiel macht dies keinen Unterschied.

String line;
while((line = br.readLine()) != null) {
	String[] splittedLine = line.split(",");

	String name = splittedLine[0];
	String email = splittedLine[1];
	Integer id = Integer.valueOf(splittedLine[2]);
}

Wenn die Guava Library vorhanden ist, kann man stattdesen auf die Klasse Splitter zurückgreifen, was zum gleichen Ergebnis führt.

String line;
while((line = br.readLine()) != null) {
	Iterable<String> splittedLine = Splitter.on(",").split(line);
	List<String> fields = ImmutableList.copyOf(splittedLine);
	String name = fields.get(0);
	String email = fields.get(1);
	Integer id = Integer.valueOf(fields.get(2));
}

Ein bisschen komplizierter wird’s wenn die Daten im CSV noch mit Anführungszeichen umrahmt werden:

"Lillian","placerat.eget.venenatis@temporloremeget.org",1
"Yoshio","quam@Crasdolordolor.edu",2
"Philip","metus.In.lorem@vel.org",3

Man kann so eine Datei gleich einlesen wie oben aufgeführt und in einem zweiten Schritt das führende und abschliessende Anführungszeichen entfernen. Die Klasse Splitter kann dies in einem Schritt. trimResults gibt einen Splitter zurück der alle führenden und schliessenden Zeichen entfernt, die dem CharMatcher ensprechen.

Iterable<String> splittedLine = Splitter.on(",")
    .trimResults(CharMatcher.is('"')).split(line);

Noch eine Stufe komplizierter wird es wenn das Trennzeichen in den Daten vorkommen kann wie in folgendem Beispiel.

"Lillian","placerat.eget.venenatis@temporloremeget.org",1
"Yoshio","quam@Crasdolordolor.edu,second@email.com",2
"Philip","metus.In.lorem@vel.org,another@test.com",3

Man könnte hier sicher mit einer kleveren Regular Expression ans Werke gehen. Einfacher geht’s mit einer Library die speziell für das Einlesen von CSV Dateien entwickelt wurde. Eine davon ist die opencsv Library. Die Library wird mit folgenden Angaben zum Projekt hinzugefügt.

<dependency>
	<groupId>net.sf.opencsv</groupId>
	<artifactId>opencsv</artifactId>
	<version>2.1</version>
</dependency>

Mit opencsv wird das Einlesen der Datei zum Kinderspiel.

CSVReader reader = new CSVReader(br);
String[] line;
while ((line = reader.readNext()) != null) {
	...
}

Zusätzlich zum Einlesen mit Iterator gibt es die Möglichkeit die Datei mit readAll() auf einmal einzulesen. Wenn statt dem Komma ein anderes Trennzeichen verwendet werden muss, übergibt man das Zeichen als zweiten Parameter dem Konstruktor der Klasse CSVReader.

CSVReader reader = new CSVReader(br, ';');
List<String[]> lines = reader.readAll();

So konfiguriert kann der CSVReader diese Datei einlesen.

Lillian;placerat.eget.venenatis@temporloremeget.org;1
Yoshio;"quam@Crasdolordolor.edu;second@email.com";2
Philip;"metus.In.lorem@vel.org;another@test.com";3
Philip2;"p2@vel.org;""a2@test.com""";3

Wenn nicht nur das Trennzeichen sondern auch das Anführungszeichen in den Daten verwendet werden soll, dann muss das Zeichen doppelt eingegeben werden. Die E-Mail Adresse der 4. Zeile wird nach dem Einlesen so aussehen: p2@vel.org;”a2@test.com”

Code Beispiele findet man in Github:
https://github.com/ralscha/java-playground/tree/master/csvimport

hashCode und equals Methoden testen

Wenn man Java Beans erstellt, dann werden normalerweise auch die equals(Object obj) und hashCode() Methoden implementiert. Die Implementation dieser Methoden ist eine kleine Wissenschaft für sich, da einige Bedingungen eingehalten werden müssen. Die genau Beschreibung dazu findet man in der Javadoc der Klasse Object.

Wenn die Methoden implementiert sind, dann sollte man auch Unittests erstellen, die das Verhalten überprüfen. Eine Hilfe dazu ist die Test-Library equalsverifier.

equalsverifier lässt sich sehr einfach mit folgenden Angaben in ein Maven Projekt einbinden.

	<repositories>
		<repository>
			<id>equalsverifier-repository</id>
			<url>http://equalsverifier.googlecode.com/svn/maven/</url>
		</repository>
	</repositories>

	<dependencies>
		<dependency>
			<groupId>nl.jqno.equalsverifier</groupId>
			<artifactId>equalsverifier</artifactId>
			<version>1.0.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

Da sich die Library nicht im Central Maven Repository befindet ist die Angabe des Repository notwendig.

Ein Unittest für eine Klasse besteht dann normalerweise aus einer Zeile, die folgendermassen aussehen kann.

@Test
public void testCustomerEquals() {
	EqualsVerifier.forClass(Customer.class).suppress(Warning.NONFINAL_FIELDS).usingGetClass().verify();
}

equalsverifier gibt eine detaillierte Meldung aus, wenn das Programm der Meinung ist das eine fehlerhafte equals und/oder hashCode Methode vorhanden ist. Im Wiki findet man eine Beschreibung was der Fehler bedeutet und wie man in beheben kann.

Weitere Informationen und Beispiele findet man auf der Projektwebseite:
http://code.google.com/p/equalsverifier/

Sourcecode für das erwähnte Beispiel findet man in Github:
https://github.com/ralscha/java-playground/tree/master/equalsverifier