März, 2011


29
Mär 11

Cross-field Validierung mit der Bean Validation API

Vor etwa einem Jahr haben wir beschlossen, die Bean Validation API zu testen, und am Anfang waren wir begeistert über die einfach zu bedienende, Annotation-basierte Schnittstelle. Doch als wir Cross-field Validierung durchführen wollten, mussten unseren Eindruck revidieren. Leider bietet die Bean Validation API keinen einfachen Weg der Validierung eines Felds basierend auf dem Wert eines anderen Feldes. Da wir im Einklang mit dem API-Design bleiben wollten, sind wir den steinigen Weg gegangen und unsere haben eigene Class-level Validation Annotations und eigene Validators geschrieben.

Das ist leider eine langweilige, sich wiederholende Arbeit, die die Benutzung der Reflection API erfordert. Solche Aufgaben sollten möglichst weit vereinfacht und automatisiert werden. Deshalb begann ich zu erforschen, wie andere dieses Problem gelöst haben. In diesem Artikel skizziere und analysiere ich einige der Lösungen, auf die ich gestoßen bin.

Der Standardansatz

Das Erstellen einer neuen Validierungslogik besteht aus zwei Schritten. Es müssen eine Constraint Annotation und ein Validator, der die Validierungslogik enthält, geschrieben werden. Der Einfachheit halber werden wir einen Constraint auf Class-level implementieren. Er wird verwendet, um sicherzustellen, dass der Wert eines Feldes größer ist als der Wert eines anderen Feldes des Objekts, das validiert werden soll.

Dies ist die Definition der Annotation:

/**
* Compares two of the fields of an object.
*/
@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = GreaterThanValidator.class)
public @interface GreaterThan {

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

String field();

String greaterThan();

/**field > greaterThan*/
Class<? extends Comparator<Object>> comparator();

@Target({ TYPE, ANNOTATION_TYPE })
@Retention(RUNTIME)
@interface List {
GreaterThan [] value();
}
}

Die “field” und “greaterThan” Felder enthalten die Namen der Bean-Eigenschaften, die verglichen werden sollen. Für diesen Vergleich brauchen wir auch einen Comparator. Schließlich fügen wir ein “Liste” Feld hinzu, so dass wir mehr als eine Instanz der Annotation auf Class-level nutzen können.

Diese Definition der Annotation bedeutet bereits eine Menge Arbeit für die bescheidene Aufgabe des Vergleichs von zwei Werten. Das ist aber gar nichts im Vergleich zur Definition des Validators:

/**
* Validates the GreaterThan constraint
*/
public class GreaterThanValidator implements
ConstraintValidator<GreaterThan, Object> {

/** field > greaterThan ? */
private String field;
private String greaterThan;
private Comparator<Object> comparator;

/**
* This will be called before isValid to initialize the validator
*/
@Override
public void initialize(GreaterThan ann) {

field = ann.field();
greaterThan = ann.greaterThan();
Class<? extends Comparator<Object>> comparatorClass = ann.comparator();
try {
comparator = comparatorClass.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("Can't instantiate comparator",
e);
}
}

/**
* This will be called to validate an object
*/
@Override
public boolean isValid(Object validateThis, ConstraintValidatorContext ctx) {
if (validateThis == null) {
throw new IllegalArgumentException("validateThis is null");
}
Field fieldObj = null;
Field greaterThanObj = null;
// Find getters the properties
try {
fieldObj=validateThis.getClass().getDeclaredField(field);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid field name",e);
}
try {
greaterThanObj = validateThis.getClass().getDeclaredField(greaterThan);
} catch (Exception e) {
throw new IllegalArgumentException("Invalid greaterThan name",e);
}
if (fieldObj == null || greaterThanObj == null) {
throw new IllegalArgumentException("Invalid field names");
}

try {
fieldObj.setAccessible(true);
greaterThanObj.setAccessible(true);
// get field values
Object fieldVal = fieldObj.get(validateThis);
Object largerThanVal = greaterThanObj.get(validateThis);
// compare
return fieldVal == null && largerThanVal == null
|| fieldVal != null && largerThanVal != null
&& comparator.compare(fieldVal, largerThanVal) > 0;
} catch (Exception e) {
throw new IllegalArgumentException("Can't validate object", e);
}

}

}

Eine Menge Arbeit für einen einfachen größer-als-Vergleich, und es kommt nicht einmal produktiv einsetzbarer Code heraus, müssten Sie doch noch ein paar Zeilen für die Verwendung in Ihren eigenen Projekten hinzuzufügen. Natürlich könnte man sich Arbeit sparen, indem Sie einige der Details in Superklassen einfügen, aber es gibt schnellere Wege, um dieselben Ergebnisse zu erzielen. Ein weiteres Problem mit dieser Annotation ist, dass sie keine compile-time Sicherheit bietet. Tippfehler in den Feldnamen oder die Angabe der falschen Comparator-Klasse führen zu Laufzeitfehlern.

Der folgende Ansatz löst einige dieser Probleme. Er ist aus einer Forumsdiskussion zum Thema entstanden (siehe http://stackoverflow.com/questions/1972933/cross-field-validation-with-hibernate-validator-jsr-303).

Der @AssertTrue Ansatz

Die @AssertTrue Annotation ist eine der Annotations der Bean Validation API, die auch für Methoden anwendbar sind. Sie stellt sicher, dass der Rückgabewert der annotierten Methode true ist, wenn sie während der Validierung aufgerufen wird. Wir fügen also einfach eine öffentliche Methode namens isConstraint1Satisfied hinzu und annotieren sie wie folgt:

public class AssertTrueExample {

public int i=0;
public int j=0;

@AssertTrue(message="Constraint 1 is not satisfied")
public boolean isConstraint1Satisfied()
{
return i==j;
}
}

Auf diese Weise können wir unser Objekt validieren, ohne zu in Schweiß auszubrechen. Mal sehen, was die Autoren der JSR-303 Spezifikation dazu zu sagen haben.

“Validieren von Daten ist eine allgemeine Aufgabe, die sich in einer Anwendung von der Präsentations-Schicht bis zur Persistenz-Schicht stellt. Oft sind die gleichen Validierungslogiken in jeder Schicht implementiert, was zeitaufwändig und fehleranfällig ist. Um diese Probleme zu vermeiden, bündeln Entwickler oft die Validierungslogik direkt in das Domain-Modell, und stopfen Domain-Klassen mit Validierungscode voll, der im übrigen nichts anderes darstellt als Metadaten über die Klasse selbst. [...]“

Nun, die @AssertTrue Lösung verstößt offensichtlich gegen diese Trennung von Belangen (separation of concerns), um die es in der Bean Validation API geht, und stellt die Validierungslogik in die Model-Klasse. Sie sind vielleicht nicht einverstanden damit, aber meiner Meinung nach ist oft zu wenig durch die Trennung von Belangen gewonnen, wenn es um interne Abhängigkeiten geht. Manchmal ist es sogar schädlich. Ich gebe Ihnen ein Beispiel.

In einem unserer Projekte mussten wir die gültigen Parameter für den Aufruf einer Legacy-Statistik Bibliothek in ein Objekt kapseln. Die Gültigkeit der Parameter für diesen Aufruf ist stark abhängig von dem Wert anderer Parameter, und es gab keine Möglichkeit, diese Constraints aufzuteilen in separate wiederverwendbare Constraint Annotations. Wir erzeugten eine Annotation und einen Validator, die wir einmal benutzten; es gab darüberhinaus keine Chance, diese Annotation jemals wieder zu verwenden.

Die Autoren der JSR-303 Spezifikation sind der Meinung, dass Validierungslogik im Domain-Model oft oder immer Unordnung bedeutet, aber ich würde zögern, diese besondere Art von Validierungscode so zu nennen. Sicher, Validierungscode für eine E-Mail-Adresse und ähnliches kann man als Unordnung bezeichnen, da E-Mail-Adressen reichlich in den meisten IT-Systemen vorkommen, und jeder weiß, was eine E-Mail-Adresse ist und wie sie aussieht; daher enthält die Validierungslogik wenig Informationen für jemanden der den Code liest. Die Validierungslogik für die Parameter-Klasse ist aber ein ganz anderes Thema. Sie gehört zur Model-class, ist nicht wieder verwendbar und enthält viele wichtige Informationen. Sie ist mehr oder weniger die Klasse, und deshalb macht es wenig Sinn, es in eine Annotation zu extrahieren. Ich denke, der @AssertTrue Ansatz (und auch der @ScriptAssert Ansatz) passt für diese Verwendung viel besser als jeder andere Ansatz, dem ich bis jetzt begegnet bin.

Der @ScriptAssert Ansatz

Hibernate Validator 4.1 brachte uns die @ScriptAssert Class-level Annotation. Sie ist eine geniale Anwendung der Java Scripting API. Statt dass Sie Ihren Validierungscode in Java schreiben, können Sie eine der vielen unterstützten Skriptsprachen verwenden. Dieser Ansatz teilt mehr oder weniger die Stärken und Schwächen des @AssertTrue Ansatzes, ist aber prägnanter und hat den zusätzlichen Vorteil, das Interface der Klasse nicht mit Validierungsmethoden zu verstopfen. Das letzte Beispiel zeigt, wie es wäre, wenn wir die @ScriptAssert Annotation nutzen würden:

@ScriptAssert(lang="javascript",script="_this.i==_this.j")
public static class ScriptAssertExample
{

int i=0;
int j=0;

}

Der große Nachteil verglichen mit dem @AssertTrue Ansatz ist natürlich, dass der Gebrauch einer Skriptsprache den Verlust der compile-time Sicherheit bedeutet.

Fazit

Der wichtigste Aspekt der drei Ansätze für Cross-field Validierung, die hier betrachtet wurden, ist, wohin sie die Validierungslogik setzen; in die Model-class oder in eine externe Validierungsklasse. In diesem Artikel habe ich in erster Linie umrissen, warum es eine schlechte Idee ist, jedes Mal einen Validator und eine Annotation zu kreieren, wenn die Annotationen der Bean Validation API nicht Ihren Bedürfnissen entsprechen. Aber verstehen Sie mich nicht falsch, ich sage nicht, dass das Hinzufügen der Validierungslogik in die Model-class in jedem Fall eine gute Idee ist. Es gibt mindestens zwei Dinge, die berücksichtigt werden müssen:

  • Ist es unwahrscheinlich, dass die Validierungslogik wiederverwendet werden kann?
  • Enthält sie wichtige Informationen, die unbedingt in die Model-class gehören?

Ich denke, wenn Sie eine dieser Fragen mit “Ja” beantworten, können Sie die Validierungslogik tatsächlich in die Model-class einfügen, ohne ein schlechtes Gewissen zu haben.

Im nächsten Artikel werde ich einen hybriden Ansatz präsentieren, der auf dem Strategiemuster basiert. Dieser Ansatz ermöglicht eine einfache Implementierung neuer wiederverwendbarer Constraints, ohne dass ein Validator für einen Constraint nötig ist. Er lässt Sie wählen, wo Sie die Validierungslogik implementieren wollen, und ermöglicht es Ihnen, nicht nur auf Typen, sondern auch auf Felder zu zielen.


23
Mär 11

Display additional overlay elements on top of an HTML5 video element with JavaScript

Since more and more browsers officially support the HTML5-only <video>tag it becomes convenient to build a simple system for displaying additional content-dependent information on top. This tutorial shows basic ideas and examples of the most important functions needed. Currently all major browsers’ latest version support HTML5 video. Thankfully Microsoft just rolled out version 9 of its Internet Explorer with full HTML5 support.

This is how it will look like:

The overlay system will be implemented in pure JavaScript and HTML5, making it a decent way to display content dynamically. To make things more simple free JavaScript library JQuery is used for element selection and animations. The goal is to have a predefined piece of HTML code displayed at a specific place and time for a specific duration. To keep it simple we will use an JavaScript array with 2 dimensions. The bottom layer holds all data (time_start, time_end, html, position_x, position_y) while the top layer tights all data arrays together. Example:

items[0] = [1,5,'Hello world!',80,20];

Should later create an overlay to be shown from second 1 to 5 at position 10:20 and displaying Hello World!.

Before we get started, some thoughts about the structure and execution sequence of our script: Basically we only need to do two things. First, create as many hidden <div>containers as there are overlay elements and put the corresponding html code in. Secondly, periodically check whether an element needs to be hidden or displayed based on the current time of the video playing.

OK, so lets get started with the basic HTML body!

<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<title>VideoOverlay Demo</title>
<link rel="stylesheet" href="styles.css" type="text/css">
 	<script src="jquery-1.5.1.min.js" type="text/javascript"></script>
	<script src="overlay.func.js" type="text/javascript"></script>
	<script type="text/javascript">
   var display_overlays = true;
   var items = [];
   items[0] = ['1','5','hello','40','40']; // format [start,end,html,x,y]
   items[1] = ['2','14','<h2>this one\'s big</h2>','320','180'];
   items[2] = ['17','24','<span style="color:red;">color is possible too</span>','200','50'];
</script>
<div id="video_box" onclick="Overlay.togglePlay();">
<div id="video_overlays"></div>
<div>
     <video id="test_video" width="640" height="360" controls="controls">       <!-- MP4 must be first for iPad! -->
<source src="movies/big_buck_bunny.mp4" type="video/mp4">       <!-- WebKit video    -->
<source src="movies/big_buck_bunny.webm" type="video/webm">       <!-- WebM/VP8/Vorbis -->
<source src="movies/big_buck_bunny.ogv" type="video/ogg">       <!-- Firefox / Opera -->     </video>
   </div>
</div>

To keep things clean most JavaScript code has been moved to an external file, overlay.func.js. The main file only holds our main data array called items[] and and a boolean display_overlays which might optionally be set false. As you can see it should be easy to insert a data array created by a server side application (e.g. a PHP script) instead of using hard coded sample data as provided above.

HTML5 is just slightly different from old HTML4. See that doctype definition at the beginning? That’s as simple as it can be. And pay attention to the <video>tag: Appending source files is all it needs to make the magic happen. Due to recent browser wars it is necessary to supply at least two differently encoded source files. All web-kit based browsers (Safari, Safari mobile on iPhone and iPad) and coming Internet Explorer only support mp4 encoded video files. On the other hand Firefox, Chrome and Opera support webm and/or ogg movie container only. You don’t have to worry about that as long as you provide differently encoded video files. Every browser just looks through that <source>list until it finds a file it is capable to play. Another interesting feature is the controls parameter which makes a browser display build in video controls. As far as I know those consist at least of play, pause and stop buttons as well as volume control and a time slider.

So grab some movie files (Big Buck Bunny is an OpenSource movie project, thus free), put together the above HTML Code and fire up your HTML5 enabled browser. That was easy, wasn’t it?

Now it’s time for JavaScript. In your external .js script insert the following code:

$(document).ready(function(){
Overlay.movie = $('#test_video');
Overlay.movie.bind('timeupdate', Overlay.schedule_check);
Overlay.schedule_setup();
});

Don’t forget to download JQuery and link in the <script> section of your HTML head to that file.

Above code is executed when the body as well as all external files are loaded. You can see that this functions works with an object called Overlays which gets defined soon. The object movie is saved for later reference, pointing to our HTML <video> element. This element fires a timeupdate event when – you guessed it – the current playing position of the movie changes. While continuously playing this event is supposed to fire at least every 250ms (dependent on the browser used). Of course it will also be triggered when a user manually jumps to a different position. By binding this event to schedule_check(), we will later use it to check what overlay elements need to be shown or hidden at that specific moment. Last but not least we’ll run schedule_setup() to create and arrange all elements needed.

First part of the following JavaScript code:

var Overlay = ({
schedule_setup: function(){
var box= $("#video_overlays");    // create elements
for (var i=0; i<items.length; i++) {
var t = $("<div class="\"overlay_item\"" id="\"item_"+i+"\"">"+items[i][2]+"</div>");
t.hide();
box.prepend(t);
}
this.rearrangeItems();
},
rearrangeItems: function() {
var box= $("#video_overlays");  // getting video box offset
var off_x = Overlay.movie.offset().left;
var off_y = Overlay.movie.offset().top;
// moving every element to its designated position
for (var i=0; i<items.length; i++) {
t = $('#item_'+i);
t.css('left',parseInt(items[i][3])+off_x);
t.css('top',parseInt(items[i][4])+off_y);
}
},

This part defines an object Overlay and two of it’s functions. Function schedule_setup() fulfills what we asked for: Creating and hiding all elements. It then calls rearrange_items() which reads out the absolute position of that video element (using its offset) and moves all element boxes to their corresponding places. This is done by simply adding their given X and Y values to the video elements’ absolute position. While there is no obvious reason to use a stand alone function for that, you might think of situations where the hosting video elements’ position changes and all child elements have to be moved to a new place too.

Finally coding schedule_check():

schedule_check: function() {
// check if anything needs to be done
for (var i=0; i<items.length; i++) {
if(!display_overlays) {
$('#item_'+i).hide();
} else if(items[i][0]> Overlay.movie.attr('currentTime')) {
$('#item_'+i).hide();
} else if(items[i][0]>=Overlay.movie.attr('currentTime') && items[i][1] >= Overlay.movie.attr('currentTime')) {      if(!$('#item_'+i).is(":visible")) $('#item_'+i).fadeIn('slow');
} else {
if($('#item_'+i).is(":visible")) $('#item_'+i).fadeOut('slow');
 }
}
},

While iterating through all items this function runs a couple of if-else statements to check what needs to be done. Starting with a simple check whether or not to show those overlay elements (you remember display_overlays defined and set in our html file?) and eventually hiding them, it continues to execute one of three actions based on current time and items’ settings. To shorten things up: If it’s to early that item gets hided, while fadeOut() is called when it is too late. In between the item’s faded in.

As you can see the code above doesn’t seem to be quite efficient. Every time schedule_check() is called an action for every single overlay element is executed. It might have been an alternative to only change element properties if a special event occurs, namely if current time is equal to an elements’ start or end time. The reason I haven’t done that is user controllability. A user should be able to jump to any time in the video which would apparently cause incorrect displayed elements if only checked for start and end. This problem is obsolete if you verify all elements’ status every time the function is called.

It might be worth a try optimizing this code to speed things up. A first attempt was made by checking an elements’ visibility before fading it in or out. I believe animations take up quite a lot computing power.

Last thing to do is setting up a CSS file (syles.css) providing style information of the overlay elements:

.overlay_item {
position:absolute;
z-index:2000;
background:#FFF;
padding:2px;
}

While the last two statements are truly optional and only make elements more readable, first two properties are very important. So position:absolute enables them to be moved in absolute coordinates to wherever you want and meanwhile not taking up any space on the page thus not destroying your design. Second property z-index defines an layer for the element. You can think of that as layers of visibilty so that elements with lower z-index might get covert by those with a higher z-index. By setting this index to 2000 we ensure no other element will be displayed on top – especially not the <video> element – so that the user has a clear view on our additional movie information.

You can find more information as well as a sample project at this post.

Source:

 


18
Mär 11

Using custom layouts for “Spinner” or “ListView” entries in Android

Today we are going to take a look at custom entries for a Spinner or ListView. Android allows the developers to create custom layouts for the entries in a Spinner or ListView. Using this mechanism it’s possible to implement highly customized designs for the default GUI elements such as the spinner. For example it’s possible to implement a spinner with entries consisting of an image and of multiple lines of text. In this post I will explain the three steps required to implement this functionality using the Query Contacts App from my previous post. In this App I’ve implemented a Spinner with custom entries to display the contact photo together with the contact name embedded in a single entry. Furthermore each element of the ListView which is used to display the details of the contact contains two lines of text. Continue reading →


14
Mär 11

Working with the “ContactsContract” to query contacts in Android

When I was looking at the official example on the Google Android Developers site for accessing content providers in Android (http://developer.android.com/guide/topics/providers/content-providers.html) I found an outdated example to query contacts which is using deprecated fields in the Android API. As I’ve seen quite some developers who are still relying on that deprecated example to implement their functionality even when using the newer API levels I’ve decided to post an example which is using the new way suggested in the Android API.

Query Contacts AppTo demonstrate the features of the new API the example App is querying all available contacts on the phone and additionally commonly used information from the contacts content provider such as the name, phone numbers, email addresses and of course the photo.