Wicket 6 – Von VoodooKatzen und Dynamischen Bildern

Heute machen wir einen kleinen Ausflug in die Welt der Bilder.

Was geht alles mit Wicket und vor allem wie?

Zunächst erstellen wir uns wie immer einen Archetype als Spielwiese.

mvn archetype:generate -DarchetypeGroupId=org.apache.wicket -DarchetypeArtifactId=wicket-archetype-quickstart -DarchetypeVersion=6.8.0 -DgroupId=de.effectivetrainings -DartifactId=wicket-images -DarchetypeRepository=https://repository.apache.org/ -DinteractiveMode=false

Zunächst räumen wir auf und löschen alles aus HomePage.html sowie HomePage.java was nicht gebraucht wird.

<!DOCTYPE html>
<html xmlns:wicket="http://wicket.apache.org">
	<head>
    </head>
	<body>
	</body>
</html>

Und HomePage.java

public class HomePage extends WebPage {
	private static final long serialVersionUID = 1L;

	public HomePage(final PageParameters parameters) {
		super(parameters);
    }
}

Als Bildquelle verwenden wir placekitten.com – wer Katzen sucht wird hier garantiert fündig.

Statische Images

Eine erste einfache Möglichkeit Bilder zu laden ist über den Klassenpfad, beispielsweise direkt aus einem Package.

Wir laden uns also zunächst ein Kätzchen herunter und platzieren es in src/main/java/de/effectivetrainings.

Zur Anzeige des Kätzchens verwenden wir folgendes Markup.

 <div class="static">
      <img wicket:id="staticKitten"/>
</div>

Die einfachste Möglichkeit ein Bild zu referenzieren ist direkt über den Dateinamen.

private Component staticKitten() {
        return new Image("staticKitten","kitten.jpg");
 }

Und siehe da, hier ist das Kätzchen!

Wicket - Static Images

Wicket – Static Images

Wicket generiert folgende URL für dieses Bild.

<img wicket:id="staticKitten" src="./wicket/resource/de.effectivetrainings.HomePage/kitten-ver-1372099283000.jpg"/>

Über eine derartige URL (Prefix /wicket/resource) werden Ressourcen in Wicket generell angesprochen und referenzieren einen IResourceListener. (Das ist ein perfekter Zeitpunkt, sich die Klasse einmal etwas genauer anzuschauen, hochinteressant!).

Was auch auffällt, stimmt das Javadoc gibt es viele verschiedene Möglichkeiten, ein Bild zu referenzieren. Beispielsweise können wir auch im Markup einfach folgendes machen.

 <div class="static">
            <img wicket:id="staticKitten" src="kitten.jpg"/>
 </div>

Und im Java-Code das (also nichts).

 private Component staticKitten() {
        return new Image("staticKitten", new Model<String>());
    }

Die zugegebenermaßen etwas kompliziertere Logik hierzu findet sich in LocalizedImageResource.

Statische Ressourcen-Images

Manchmal ist es nicht erwünscht, Bilder statisch und relativ aus einem Package zu laden.Es kann auch sinnvoll sein, Images global beispielsweise in src/main/webapp abzulegen.

Platzieren wir also ein zweites Kätzchen.

Wir definieren hierfür folgenden Wicket-Tag.

<img wicket:id="staticResourceKitten"/>

Und im Code erstmal folgendes:

 private Component staticResourceKitten() {
        return new ContextImage("staticResourceKitten", "/images/kitten2.jpg");
    }

ContextImage erlaubt es uns, Bilder direkt über den Kontextpfad zu laden. Sehr schick und praktisch.

Es findet keinerlei Ressourcenmanagement statt, Wicket macht intern ein URL-Rewrite und sorgt dafür, dass das src-Attribut des Images korrekt gesetzt ist. Vorraussetzung hierfür ist, dass das Bild tatsächlich über den Kontextpfad abrufbar ist (nicht in einem Package! – siehe ResourceGuard)

Externe Images

Nicht immer liegen Bilder direkt bei uns (externe Ressourcen, CMS-System etc..).

Laden wir also ein Image über eine URL.

Wir definieren ein weiteres Tag.

<img wicket:id="staticExternalImage"/>

Und laden ein Kätzchen über eine URL.

Leider bietet Wicket hierfür nichts einfaches, was direkt funktioniert, ist aber schnell gemacht. Wir definieren ein ExternalImage und zwar so.

/**
 * @author <a href=mailto:martin@effectivetrainings.de">Martin Dilger</a>
 * @since: 24.06.13
 */
public class ExternalImage extends WebMarkupContainer {
    public ExternalImage(String id, IModel<String> model) {
        super(id, model);
        add(AttributeAppender.replace("src", model));
    }

    @Override
    protected void onComponentTag(ComponentTag tag) {
        super.onComponentTag(tag);
        checkComponentTag(tag,"img");
    }
}

und anschließend bauen wir das dritte Kätzchen so ein.

private Component staticExternalKitten() {
        return new ExternalImage("staticExternalKitten", Model.of("http://www.placekitten.com/300/300"));
    }

Nicht immer hat man es aber mit statischen Ressourcen zu tun, bauen wir ein wenig Dynamik ein.

Dynamische Images

Bauen wir uns ein Voodoo-Kätzchen.

Zunächst wieder ein Tag.

<div class="dynamic">
            <img wicket:id="dynamicKitten"/>
</div>

Und anschließend ein wenig Magic-Code.

private Component dynamicKitten() {
        return new Image("dynamicKitten", new DynamicImageResource() {
            @Override
            protected byte[] getImageData(Attributes attributes) {
                try {
                    BufferedImage dynamicKitten = ImageIO.read(new URL("http://www.placekitten.com/300/400"));
                    dynamicKitten.setRGB(255,255,255);
                    dynamicKitten.getGraphics().fillOval(100,120,30,30);
                    dynamicKitten.getGraphics().fillOval(130,110,30,30);

                    ByteArrayOutputStream bout = new ByteArrayOutputStream();
                    ImageIO.write(dynamicKitten, "png", bout);
                    return bout.toByteArray();
                } catch (IOException e) {
                   //ignore
                }
                throw new RuntimeException("Narf..");
            }
        });
    }

Image erlaubt es uns, statt eines Models auch eine ImageResource anzugeben. Über die Resource haben wir die Möglichkeit, Images dynamisch zu erzeugen.
Wir laden uns ein beliebiges Kätzchen und platzieren die magischen Voodoo-Augen!! Das Bild wird also zur Laufzeit erzeugt.

Voodoo-Cat

Voodoo-Cat

Resource-Sharing

Ziehen wir die Voodoo-Katze doch mal zum Spaß in eine eigene Klasse.

public class VoodooKittenResource extends DynamicImageResource
{
    @Override
    protected byte[] getImageData(Attributes attributes)
    {
        int width = attributes.getParameters().get("width").toInt();
        int height = attributes.getParameters().get("height").toInt();
        try
        {
            BufferedImage dynamicKitten = ImageIO
                    .read(new URL(String.format("http://www.placekitten.com/%s/%s",width,height)));
            dynamicKitten.getGraphics().fillOval(100, 120, 30, 30);
            dynamicKitten.getGraphics().fillOval(130, 110, 30, 30);

            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ImageIO.write(dynamicKitten, "png", bout);
            return bout.toByteArray();
        }
        catch (IOException e)
        {
            // ignore
        }
        throw new RuntimeException("Narf..");
    }
}

Wir legen besonderes Augenmerk auf diese beiden Zeilen.

int width = attributes.getParameters().get("width").toInt();
int height = attributes.getParameters().get("height").toInt();

Über die Attributes haben wir u.a. Zugriff auf die RequestParameter!

Und Ressourcen lassen sich mounten!!

Machen wir die dynamische Voodoo-Katze doch mal per URL verfügbar. Hierfür mounten wir lediglich die VoodooKittenResource in der WicketApplication.

@Override
	public void init()
	{
		super.init();

        mountResource("/voodoo/${width}/${height}",new ResourceReference("voodooKitten") {
            @Override
            public IResource getResource() {
                return new VoodooKittenResource();
            }
        });
	}

Unser Voodoo-Kätzchen ist also künftig beispielsweise so aufrufbar.

http://localhost:8080/voodoo/366/288

http://localhost:8080/voodoo/366/288

http://localhost:8080/voodoo/270/299

http://localhost:8080/voodoo/270/299

Über /voodoo/${width}/${height} können wir dynamisch über die URL die Weite und Höhe angeben.

Das Schöne ist, hierdurch lässt sich unsere dynamische Katze jetzt super einfach verwenden.

 private Component superDynamicKitten(){
        return new ContextImage("superDynamicKitten","/voodoo/270/299");
    }

Fazit

Das Ergebnis kann sich durchaus sehen lassen.

Kitten-Gang

Auch Kätzchen sind nicht unfehlbar

Was passiert, wenn beim Laden eines Bildes ein Fehler auftritt?

Im Package de.effectivetrainings liegt eine Datei brokenKitten.jpg. Diese Bilddatei ist kaputt und wirft eine Exception, beim Versuch sie zu laden.

Schön wäre es, wenn wir hier einen schönen Fallback anzeigen könnten.

Hierfür bauen wir uns eine FallbackVoodooKittenResource.

/**
 * @author <a href=mailto:martin@effectivetrainings.de">Martin Dilger</a>
 * @since: 25.06.13
 */
public class FallbackVoodooKittenResource extends DynamicImageResource
{

    private Class<?> scope;
    private String fallbackFileName;
    private String fileSuffix;

    public FallbackVoodooKittenResource(Class<?> scope, String fallbackImageFilename, String suffix)
    {
        this.scope = scope;
        this.fallbackFileName = fallbackImageFilename;
        this.fileSuffix = suffix;
    }

    @Override
    protected byte[] getImageData(Attributes attributes)
    {
        try
        {

            return loadImage(HomePage.class,"brokenKitten.jpg","jpg");
        }
        catch (Exception e)
        {
            return loadImage(scope,fallbackFileName,fileSuffix);
        }

    }

    private byte[] loadImage(Class<?> scope, String filename, String suffix) {
        try
        {
            final BufferedImage loadedImage = ImageIO.read(
                scope.getResourceAsStream(filename));
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            ImageIO.write(loadedImage, suffix, bout);
            return bout.toByteArray();
        }
        catch (IOException ioe)
        {
            return new byte[0];
        }
    }
}

Die Idee ist ganz einfach, die Fallback-Resource erwartet den Scope (also indirekt das Package) und den Dateinamen des Bildes, das als Fallback angezeigt werden soll.

Schlägt das Laden des eigentlichen Bildes fehl wird das Fallback-Bild angezeigt. (Schlägt das auch fehl kann man einfach nichts mehr machen).

In der Klasse HomePage.java fügen wir ein letztes vermeintliches Kätzchen ein.

private Component fallbackDynamicKitten(){
        return new Image("fallbackDynamicKitten", new FallbackVoodooKittenResource(HomePage.class,"fallbackKitten.jpg","jpg"));
    }

Beim Laden wird wie erwartet die Exception geworfen und wir sehen folgendes.

Wo ist der Fallback?

Bildquelle Hund : http://en.m.wikipedia.org/wiki/File:Military_dog_barking.JPG

 

Mit ein wenig Phantasie erkennt man, welches Bild der Fallback ist.

Gibt es noch weitere interessante Use-Cases? Hab ich etwas spannendes vergessen? Freue mich auf Kommentare.

Links

Source-Code auf GitHub

PlaceKitten

Effective Trainings & Consulting - Martin Dilger



Hat Ihnen dieser Blog-Eintrag gefallen? Ich stelle in diesem Blog Informationen über Tools, Frameworks und Werkzeuge zur Verfügung, die mich produktiver machen. Vielleicht kann ich auch Ihnen helfen, produktiver zu werden.


Ich unterstütze Sie als freier Mitarbeiter bei der Entwicklung von Software-Projekten, Agiler Arbeit sowie Schulungen / Fortbildungen.


Jeden Tag ein bisschen produktiver - ab heute