Mittwoch, 17. Oktober 2012

Spring MVC and Validation

In Spring you can easily do some validation in your Controller like this:


@RequestMapping(method = RequestMethod.POST)
public String post ( @Valid User user, Errors errors )
{
   if (errors.hasErrors())
   {
      return "info/register";
   }
   userService.register(user);
    return "redirect:/info/success";
}

This is nice. But sometime you want to validate some constraint against a database. For example, if you register as a user you have to pick a unique username.
Some people say, validators should not access the database. I don't think so. If you post a user registration form you want to get all errors at once. You don't want to see first errors about required or bad formatted fields and on the second post you suddenly receive "username is not unique".
To handle database constraints with hibernate validator you need to write your own custom validator and constraint annotation.
But how do you start a transaction around the validator? You could annotate your custom validators with @Transactional and let spring pick those up (or use compile-time-weaving with aspectj). It works but I think validation is a unit of work and should be wrapped by only one transaction.
Luckily with spring it is quite easy, just extends LocalValidatorFactoryBean like this:
public class TransactionalLocalValidatorFactoryBean extends LocalValidatorFactoryBean
{
   @Override
   @Transactional(readOnly = true)
   public void validate ( Object target, Errors errors )
   {
     super.validate(target, errors);
   }

   @Override
   @Transactional(readOnly = true)
   public void validate ( Object target, Errors errors, Object... validationHints )
   {
     super.validate(target, errors, validationHints);
   }
}

if you have further questions regarding this topic, please send me a mail

vygen@kicktipp.de


 


Dienstag, 3. April 2012

At the moment we are checking slow startup time of our application. With a good profiler like YJP it's quite easy. We recognized that 22% of our startup time was used by verifying jar files.

First i show you what we did to speed up startup time. We excluded all bouncycastle dependencies from itext like this:

  <dependency>
            <groupId>com.lowagie</groupId>
            <artifactId>itext</artifactId>
            <version>2.1.7</version>
            <exclusions>
                <exclusion>
                    <groupId>bouncycastle</groupId>
                    <artifactId>bcmail-jdk14</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>bouncycastle</groupId>
                    <artifactId>bcprov-jdk14</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>bouncycastle</groupId>
                    <artifactId>bctsp-jdk14</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

We saved 22% of our startup time. itext needs this dependencies only when signing and verifying PDF i guess. So you can exclude it. As bouncycastle is a java security provider its jars has to be signed and verified. This took quite some time.

So if you don't need signing and verifying PDF exclude bouncycastle from your dependency tree.


Freitag, 18. November 2011

We are using a Feitian PKI Token with the latest ubuntu 11.10 The opensc package is missing some profile files, see this ubuntu bug report: #Bug 872019

If you see 'Couldn't bind to the card: File not found' while running pkcs15-init -E you are bitten by this, too. We fixed it by downloading an old package from natty and copied the profile files to our system:

mkdir /usr/share/opensc
wget http://archive.ubuntu.com/ubuntu/pool/universe/o/opensc/opensc_0.11.13.orig.tar.gz
tar -xzf opensc_0.11.13.orig.tar.gz
cd opensc-0.11.13
cp src/pkcs15init/*profile /usr/share/opensc/
rm -Rf opensc-0.11.13/
rm -Rf opensc_0.11.13.orig.tar.gz
 


Mittwoch, 11. Mai 2011

Response Time im Tomcat Log

Wir benutzen folgendes Muster im AccessLogValve

%h %l %u %t "%r" %s %b %D

ein Logeintrag sieht dann zum Beispiel so aus:

IP - - [11/May/2011:16:47:13 +0200] "GET /de.png HTTP/1.0" 200 321 1

Es kamen bei uns folgende Fragen auf:

* Was ist alles in der Responsetime %D enthalten? Gehört dazu auch der Versand des TCP Paketes bis zum letzten FIN/ACK?
* Wird im Zeitstempel die Uhrzeit eingetragen, wenn der Request empfangen oder beendet wurde?

Man kann das ganz gut rausfinden, in dem man den Traffic drosselt:

# Regel drosselt eingehenden traffic um jeweils 2 Sekunde pro Paket
tc qdisc add dev eth0 root netem delay 2000ms
# Serveranfrage schicken, kleine und große Datei, jeweils requestzeit merken
date; ab http://IP/img/flags/de.7.1.png
date; ab http://IP/js/jquery.js
# und Regel wieder entfernen
tc qdisc del dev eth0 root

IP - - [11/May/2011:17:01:31 +0200] "GET /de.png HTTP/1.0" 200 321 1
IP - - [11/May/2011:17:01:32 +0200] "GET /jquery.js HTTP/1.0" 200 78601 94
IP - - [11/May/2011:17:01:47 +0200] "GET /de.png HTTP/1.0" 200 321 1
IP - - [11/May/2011:17:02:00 +0200] "GET /jquery.js HTTP/1.0" 200 78601 8155

Man erkennt hier, wenn man die Zeiten vergleicht folgendes:

  • Der Zeitstempel betrifft die Uhrzeit, wenn der Request komplett abgeschlossen ist, Tomcat also die TCP Verbindung geschlossen hat.
  • Die Responsetime umfasst die Zeit, bis das letzte Paket an den TCP Buffer rausgesendet wurde und die Verbindung geschlossen werden kann. Wenn die Datei klein ist, braucht es nur ein Paket, so dass dieses sofort rausgeschickt wird. Die Repsonsetime bleibt dann bei 1ms. Ist die Datei größer (letzter Logeintrag) wird als Repsonsetime 8155 Millisekunden vermerkt, obwohl der gesamte Prozess über 12 Sekunden gedauert hat. Die Netzwerkzeit ist also teilweise mit enthalten, aber nicht bezüglich des letzten Pakets, da dann der Thread den Request schon als erledigt abhaken kann.

Montag, 31. Januar 2011

Eclipse Schriftgröße

an meinem Desktop Arbeitsplatz habe ich ausreichend Bildschirm zur Verfügung, so dass die Standardschriftgröße von Eclipse perfekt passt. Auf meinem Laptop aber, will ich mehr sehen und die Schrift verkleinern. Leider ist das unter Eclipse nicht so ganz einfach, denn es gibt folgende Nachteile:

- Man muss ziemlich viele Einstellungen machen unter Window > Preferences > Colors and Fonts.
- Die Schriftgrößen der Views (wie Package Explorer) können auf diesem Weg nicht angepasst werden

Ideal ist es daher, einfach mit einem zusätzlichen Profile zustarten

Man lege eine Datei ins Homeverzeichnis mit dem Namen ".eclipse-gtkrc"

style "eclipse" {
font_name = "Sans Condensed 9"
}
class "GtkWidget" style "eclipse"


und startet das ganze wie folgt:

env GTK2_RC_FILES=/usr/share/themes/Clearlooks/gtk-2.0/gtkrc:~/.eclipse-gtkrc eclipse

Und schon hat man die Schrift in Eclipse allgemein verkleinert.

Donnerstag, 27. Januar 2011

JMX Verbindung zu einem entfernten Tomcat über einen SSH Tunnel aufbauen

JMX ist ein Biest! Natürlich ist es schön, von außen in den Tomcat reinzuschauen, aber sobald man das mit JMX lösen will, merkt man, dass es ganz schön kompliziert werden kann. So ging es zumindest mir. Daher hier eine Kurzanleitung für einen komplizierteren Fall:

Wir wollen uns über einen SHH Tunnel mit JMX verbinden. Das hat den Vorteil, dass wir das Biest im Käfig lassen. Wir wissen, dass wir nur über einen SSH tunnel da dran kommen und können JMX nur mit dem Loopback Netzwerk verbinden.

Erstmal bauen wir JMX in den Tomcat ein, und zwar so, dass alle Ports von JMX feststehen.

Als Startup Optionen zum Beispiel bei Debian in /etc/default/tomcat5.5

JMX_OPTS="-Djava.rmi.server.hostname=localhost"
JAVA_OPTS="... $JMX_OPTS"


Und in die server.xml tragen wir einen Listener ein, dabei setzen wir explizit die Ports für die JMX Kommunikation. Wichtig ist hier auch den "host" auf "127.0.0.1" zu setzen, sonst klappt es nicht:

<Listener className="org.apache.catalina.mbeans.JMXAdaptorLifecycleListener"
namingPort="6788" port="6789" host="127.0.0.1" />


Nach einem Tomcat restart finden wir in der catalina.log Datei folgendes:

INFO: JMXConnectorServer started on service:jmx:rmi://127.0.0.1:6789/jndi/rmi://127.0.0.1:6788/server

Tomcat ist also soweit, JMX bleibt aber schön im Käfig und hört nur auf 127.0.0.1, wie gewünscht.

Nun bauen wir uns ein kleines Script, dass uns mit dem entsprechenden Server verbindet:

#!/bin/bash

cd `dirname $0`;

sshtunnel() {
SERVER=$1
LOCAL_PORT=$2
REMOTE_PORT=$3
if [ -z $REMOTE_PORT ]; then
REMOTE_PORT=$LOCAL_PORT
fi

echo "Opening SSH Tunnel localhost:${LOCAL_PORT} to $SERVER:${REMOTE_PORT}"
ssh -f $SERVER -L ${LOCAL_PORT}:$SERVER:${REMOTE_PORT} sleep 60
}

JMX_PORT_0=6788
JMX_PORT_1=6789

server=$1

sshtunnel ${server} ${JMX_PORT_0}
sshtunnel ${server} ${JMX_PORT_1}
CONNECTION="service:jmx:rmi://127.0.0.1:${JMX_PORT_1}/jndi/rmi://127.0.0.1:${JMX_PORT_0}/server"
jconsole "$CONNECTION" &


und schon verbindet sich jconsole automatisch mit dem gewünschten Tomcat über den bereitsgetsellten SSH Tunnel. Der SSH Tunnel wird übrigens automatisch offen gehalten, solange jconsole die Verbindung nutzt. Sobald wir aber jconsole beenden, schließt sich auch der Tunnel.

Montag, 24. Januar 2011

Maildir umwandeln in mbox Format

Manchmal hat man so kleine fiese Aufgaben, zu denen man gar keine Lust hat. Schln, wenn jemand dann schon ein kleines Script im Netz abgelegt hat.

Diesmal musste ich einen Ordner im maildir Format umwandeln in eine einzige Datei im mbox Format.

So wie Iain Cuthbertson es in 2005 gelöst hat, funktioniert es noch immer:

md2mb-pl.txt

#!/usr/bin/perl -w

# Usage: md2mb.pl input_maildir output_mbox
#
# An example:
# mkdir ~/mbox
# md2mb.pl ~/mail/Sent ~/mbox/Sent
#
# (c) Iain Cuthbertson 2005

my $input_maildir = $ARGV[0];
my $output_mbox = $ARGV[1];

opendir(MAILDIR, "$input_maildir/cur") || die("Could not open $input_maildir/cur");
my @mail_files = grep {!(/^\.\.?$/) && -f "$input_maildir/cur/$_"} readdir(MAILDIR);
rewinddir(MAILDIR);
close(MAILDIR);

open(MBOX, ">>$output_mbox") || die("Could not open $output_mbox for writing");

foreach my $file (sort @mail_files) {
 my $working_file = "$input_maildir/cur/$file";
 open(MAILFILE, "$working_file") || die("Could not open $working_file");
 @mailfile = ;
 close(MAILFILE);

 my $email = "postmaster";
 my $date = "Wed Apr 20 22:21:51 2005";

 print MBOX "From $email  $date\n";

 foreach $mbox_line (@mailfile) {
  print MBOX $mbox_line;
 }
}

close(MBOX);