Skip to content


Praxisbuch Wicket - Feedback

Mein erstes Buch ist ein Erfolg. Zumindest für mich. Nicht im finanziellen Sinn, sondern in Bezug auf Dinge, die ich dabei gelernt habe. Das es z.B. sehr schwer ist, bei einem Framework wie Wicket eine Struktur zu finden, die leicht zu verstehen ist. Was ist diese Model-Klasse? Was ist eine Komponente? Erklärt man Komponenten, bevor man die Model-Klasse erklärt hat? Kann man das überhaupt? .. Viele Fragen mit mehr als einer Antwort.

Da es mein erstes eigenes (ok, ich war an einem Wordpress-Buch beteiligt) Buch war, betrat ich in vielen Disziplinen Neuland. Da ist es gut, wenn man jemanden hat, der zurück spielt, was man anders machen müsste, was unverständlich ist. Nach dem erscheinen des Buches gab es sehr viele Rückmeldungen, die durchweg positiv waren. Ich war ehrlich gesagt ein wenig überrascht, weil ich wohl manchmal auch ein wenig zu selbstkritisch bin. Soweit ich sehen konnte, fand das Buch nicht nur Anklang bei Entwicklern, die erst neu in die Webentwicklung mit Java eingestiegen sind. Auch Entwickler, die bereits professionell mit Wicket arbeiten, haben das Buch gelobt. Das das der Seele schmeichelt ist unstrittig. Danke für all die positiven Worte. Natürlich gab es auch Fehler in den Texten, unverständliche Stellen, alles was bei einem Buch so "schief" gehen kann. Diese konstruktiven Hinweise sind sehr hilfreich, weil sie mir zeigen, wo ich vielleicht schon betriebsblind war.

Es liegt in der Natur der Dinge, dass man nicht jeden glücklich machen kann, da Menschen zum Glück nicht gleich sind. Das jemand besser mit einem anderen Buch zurechtkommt, ist zu erwarten. Dass die Art und Weise, wie ich Dinge erkläre, nicht bei jedem ankommt, damit habe ich gerechnet. Daher war ich überrascht, dass bei Amazon doch sehr unterschiedliche Kundenbewertungen (Praxisbuch Wicket bei Amazon) zustande gekommen sind.

Daher habe ich eine Bitte, einen Wunsch. Es wäre sehr schön, wenn ich mehr Rückmeldung bekommen würde (entweder als Rezension bei Amazon, als Kommentar in diesem Blog oder per Email (würde ich gern im Blog veröffentlichen (natürlich anonym, wenn gewünscht))). Dabei ist mir wichtig, zu betonen, dass es hier nicht darum geht, mir zu schmeicheln. Ich versuche dann, die Dinge, die ich bereits gut gemacht habe, beizubehalten und Dinge, die man besser machen kann, zu verbessern.

Danke:)

Tags:

Veröffentlicht in Allgemein, Wicket, .

PropertyModel - einfach ist nicht immer gut

Ich bin kürzlich über ein Stück Code gestolpert, der mich zum nachdenken brachte. Um zu illustrieren, welcher Umstand dafür verantwortlich war, benötige ich diesemal am schon Anfang ein paar Zeilen Code. Die folgende Hilfsklasse dient nur dazu, bei dem einzig möglichen Methodenaufruf eine Liste von Daten zu erzeugen:

package de.wicketpraxis.web.blog.pages.migration.model.property;

import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

public class Generator implements Serializable
{
  public List<String> findAll()
  {
    return Arrays.asList("fun","with","wicket");
  }
}

Der Generator dient hier als Platzhalter für eine wie auch immer gelagerte Datenschicht.

package de.wicketpraxis.web.blog.pages.migration.model.property;

import java.util.List;

import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.PropertyModel;

import de.wicketpraxis.wicket.model.Models;
import de.wicketpraxis.wicket.model.transformation.Function1;

public class UseOrNotUsePropertyModelPage extends WebPage
{
  public UseOrNotUsePropertyModelPage()
  {
    final Generator gen=new Generator();
    
    IModel<List<String>> model = new LoadableDetachableModel<List<String>>()
    {
      protected List<String> load()
      {
        return gen.findAll();
      };
    };
        
    model=new PropertyModel<List<String>>(gen,"findAll");
    
    add(new ListView<String>("list",model)
    {
      @Override
      protected void populateItem(ListItem<String> item)
      {
        item.add(new Label("value",item.getModel()));
      }
    });
  }
}

Da es nicht meine Art ist, unvollständigen Code abzubilden, kommt auch hier erst das Markup und dann die Erklärung:

<html>
  <head>
    <title>PropertyModel Usage Page</title>
  </head>
  <body>
    <h1>Liste</h1>
    <ul>
      <li wicket:id="list"><span wicket:id="value"></span></li>
    </ul>
  </body>
</html>

Was ist an diesem Beispiel bemerkenswert?

Am Anfang erzeuge ich ein Model über eine LoadableDetachableModel-Implementierung, die die Daten aus dem Methodenaufruf zurück gibt. Das ist nichts neues und wurde bereits oft behandelt. Diese Model-Instanz wird in der folgenden Zeile mit einer PropertyModel-Instanz überschrieben. Es ist unstrittig, dass dieser Ansatz funktioniert. Die Methode wird per Reflection aufgerufen und die Werte werden per getObject() zurückgeliefert. Doch wo liegt das Problem?

Refactoring

Die Zeile Code, die man erhält, wenn man das PropertyModel benutzt und die Methode aufzurufen ist angenehm kurz. Aber diese Kürze gibt es nicht umsonst, sondern man muss dafür zahlen. In Bezug auf das Thema Refactoring sogar zwei mal. Das erste Problem ist offensichtlich: Wenn sich der Methodenname ändert (oder Parameter hinzukommen) wird der Aufruf per Reflection nicht mehr funktionierten (oder sogar eine falsche Methode aufrufen), und man erhält zur Laufzeit eine Fehlermeldung.

Der zweite Punkt ist weniger offensichtlich, aber vielleicht noch schwer wiegender, weil zur Laufzeit der Fehler an einer anderen Stelle zu Tage tritt: Der Rückgabewert der Methode wird angepasst. Der Fehler würde in unserem Fall dann vermutlich nur in der Darstellung der ListView auftreten, so dass statt "fun with wicket" möglicherweise "SomeData@65b778 SomeData@32ac12" angezeigt wird. Es kommt also noch nicht mal zu einem Laufzeitfehler.

Performance

In einem anderen Bereich (der in unserem Beispiel nebensächlich ist) funktioniert die PropertyModel-Variante nicht wie erwartet. Wenn die Methode getObject() aufgerufen wird, wird immer die entsprechende Methode per Reflection aufgerufen. Das bedeutet, die Ergebnisse werden nicht wie beim LoadableDetachedModel zwischengespeichert. Würde ich das Model an zwei ListView-Komponenten binden, würde die Liste zweimal erzeugt werden. Je nach Aufwand der Erzeugung ist das ein eher unerwünschtes Ergebnis. Außerdem kann es natürlich dazu kommen, dass sich die jeweiligen Rückgabewerte unterscheiden, so dass der Nutzer im Zweifel inkonsistente Daten angezeigt bekommt.

Das PropertyModel ist nützlich

Man sollte nach dem Lesen der Zeilen die Verwendung des PropertyModel nicht verbieten, denn Aufwand und Nutzen sind für die Entstehung dieser Model-Klasse verantwortlich. Um die Eingaben aus einem Formularfeld in einem Attribut einer JavaBean zu speichern sollte man das PropertyModel einsetzen, weil der Mehraufwand für eine typsichere (und damit durch normale Refactoring-Operationen greifbare) Variante enorm hoch ist. Ich würde mir wünschen, das Java Sprachmittel bereitstellt, die es ermöglicht, Methoden nicht nur per Reflection zu ermitteln, so dass man gerade für diesen Fall kompakten und trotzdem typsicheren Code schreiben kann. Bis dahin sollte man diese Model-Klasse überlegt verwenden, dann kann eigentlich gar nicht soviel schief gehen.

Tags:

Veröffentlicht in Allgemein, Refactoring, Wicket, .

Wicket Theme - CSS-Layout für Wicket-Anwendungen

Das Auge isst mit.

Unter diesem Stichpunkt könnt man wohl einen wesentlichen Aspekt der Anwendungsentwicklung zusammenfassen. Denn gerade in der Web-Entwicklung geht es oft um das Aussehen einer Anwendung. Im Unterschied zu Desktop-Anwendungen muss man sich als Webentwickler oft bereits am Anfang damit beschäftigen, die Anwendung durch einbinden von CSS-Definitionen nach etwas aussehen zu lassen. Unzulänglichkeiten von CSS und die schlechte Browser-Unterstützung machen diesen Teil der Entwicklung schwerer als nötig.

Das das Aussehen einen nicht unwesentlichen Anteil am Erfolg eines Frameworks hat, kann man gut an Grails/Rails sehen. Denn von Anfang an kommt eine Grails-Anwendung gefällig daher. Dabei geht es nicht darum, dass der Stil jedem gefällt, sondern das eine Grails-Anwendung eben nicht wie eine Web1.0 Seite aussieht.

Und genau da hat Wicket noch Potential. Deshalb habe ich mich entschieden, ein entsprechendes WicketStuff-Projekt zu starten, dass so einfach wie möglich eingebunden und benutzt werden kann, so dass man recht schnell und einfach eine ganz nett aussehende Anwendung entwickeln kann, die auch das Auge anspricht. Das wird sicher noch eine wenig Zeit benötigen, doch vorab schon einmal ein Screenshot, der zeigt, dass man ohne zusätzliche CSS-Klassen im eigenen Markup bereits ansehnliche Ergebnisse erhalten kann:

Wicket Theme Vorschau

Alles weitere demnächst hier im Blog.. wer helfen mag, ist herzlich eingeladen.

Tags:

Veröffentlicht in Allgemein, Wicket, .

Wicket Model Transformation

Eine Liste von Ergebnissen aus einer Tabelle in der Datenbank zu laden und über ein Model zur Verfügung zu stellen, ist eine recht einfache Angelegenheit. Wicket sorgt dafür, dass die Liste kein zweites mal geladen wird, nur weil das Model durch eine zweite Komponente dargestellt wird. Wenn man z.B. so triviale Dinge wie die Summe aller angezeigten Einträge ermitteln möchte, empfiehlt es sich, auf die bereits geladenen Daten aus dem Model zurückzugreifen. Für diesen Zweck kann man auf eine spezialisierte Model-Klasse zurückgreifen, die sich darum kümmert, das die detach()-Methode auch für alle referenzierten Modelle aufgerufen wird. Der Ansatz ist recht einfach, aber es geht vielleicht noch ein wenig eleganter.

Die Transformation

Als erstes definieren wir Klassen, die für die Transformation sorgen. Da in unserem Beispiel nicht nur ein Model, sondern auch zwei oder drei Model-Referenzen benutzt werden und damit bis zu drei Parametern übergeben werden können, müssen wir auch drei unterschiedliche Schnittstellen definieren:

package de.wicketpraxis.wicket.model.transformation;

import java.io.Serializable;

public interface Function1<T,M1> extends Serializable
{
  public T apply(M1 value);
}

package de.wicketpraxis.wicket.model.transformation;

import java.io.Serializable;

public interface Function2<T,M1,M2> extends Serializable
{
  public T apply(M1 value,M2 value2);
}

package de.wicketpraxis.wicket.model.transformation;

import java.io.Serializable;

public interface Function3<T,M1,M2,M3> extends Serializable
{
  public T apply(M1 value,M2 value2, M3 value3);
}

Da die verschiedenen Modelle auch verschiedene Daten bereitstellen können, kann jeder Parameter einen eigenen Typ annehmen. Jetzt benötigen wir ein Model, dass die Daten der referenzierten Modelle lädt und der Funktion übergibt und sich das Ergebnis bis zum Aufruf von detach() merkt.

package de.wicketpraxis.wicket.model.transformation;

import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;

public abstract class Transformator<T> extends LoadableDetachableModel<T>
{
  IModel<?>[] _subModels;
  
  protected Transformator(IModel<?> ... subModels)
  {
    _subModels=subModels;
  }
  
  @Override
  protected void onDetach()
  {
    for (IModel<?> m : _subModels)
    {
      m.detach();
    }
  }
  
  public static class Model1<T,M1> extends Transformator<T>
  {
    IModel<M1> _m1;
    Function1<T, M1> _function;
    
    public Model1(IModel<M1> m1, Function1<T, M1> function)
    {
      super(m1);
      
      _m1=m1;
      _function=function;
    }
    
    @Override
    protected T load()
    {
      return _function.apply(_m1.getObject());
    }
  }
  
  public static class Model2<T,M1,M2> extends Transformator<T>
  {
    IModel<M1> _m1;
    IModel<M2> _m2;
    Function2<T, M1, M2> _function;
    
    public Model2(IModel<M1> m1, IModel<M2> m2, Function2<T, M1, M2> function)
    {
      super(m1,m2);
      
      _m1=m1;
      _m2=m2;
      _function=function;
    }
    
    @Override
    protected T load()
    {
      return _function.apply(_m1.getObject(),_m2.getObject());
    }
  }

  public static class Model3<T,M1,M2,M3> extends Transformator<T>
  {
    IModel<M1> _m1;
    IModel<M2> _m2;
    IModel<M3> _m3;
    Function3<T, M1, M2, M3> _function;
    
    public Model3(IModel<M1> m1, IModel<M2> m2, IModel<M3> m3, Function3<T, M1, M2, M3> function)
    {
      super(m1,m2,m3);
      
      _m1=m1;
      _m2=m2;
      _m3=m3;
      _function=function;
    }
    
    @Override
    protected T load()
    {
      return _function.apply(_m1.getObject(),_m2.getObject(),_m3.getObject());
    }
  }
}

Das war jetzt ein größerer Block, der aber eigentlich nur aus 3 fast gleichen Klassendefinitionen bestand, die leider nicht viel besser zusammenzuführen sind. Wichtig ist dabei die Übergabe aller Model-Referenzen an die Basisklasse, die sich in der onDetach()-Methode (die von der detach()-Methode nur aufgerufen wird, wenn für das Model noch nicht detach() aufgerufen wurde) um das Aufrufen der detach()-Methode dieser Modelle kümmert.

Noch ist dieses ganze Konstrukt nicht viel besser handhabbar als die bisher favorisierte Lösung. Wir benötigen daher noch ein paar Hilfsklassen.

package de.wicketpraxis.wicket.model.transformation;

import org.apache.wicket.model.IModel;

public abstract class ModelSet
{
  public static class Set1<M1>
  {
    IModel<M1> _m1;
    
    public Set1(IModel<M1> m1)
    {
      _m1=m1;
    }
    
    public <T> IModel<T> apply(Function1<T, M1> function)
    {
      return new Transformator.Model1<T, M1>(_m1,function);
    }
  }
  
  public static class Set2<M1,M2>
  {
    IModel<M1> _m1;
    IModel<M2> _m2;
    
    public Set2(IModel<M1> m1,IModel<M2> m2)
    {
      _m1=m1;
      _m2=m2;
    }
    
    public <T> IModel<T> apply(Function2<T, M1, M2> function)
    {
      return new Transformator.Model2<T, M1, M2>(_m1,_m2,function);
    }
  }

  public static class Set3<M1,M2,M3>
  {
    IModel<M1> _m1;
    IModel<M2> _m2;
    IModel<M3> _m3;
    
    public Set3(IModel<M1> m1,IModel<M2> m2,IModel<M3> m3)
    {
      _m1=m1;
      _m2=m2;
      _m3=m3;
    }
    
    public <T> IModel<T> apply(Function3<T, M1, M2, M3> function)
    {
      return new Transformator.Model3<T, M1, M2, M3>(_m1,_m2,_m3, function);
    }
  }
}

Wir man erkennen kann, bestehen diese Klassen auch wieder aus drei Kopien, die mehr oder weniger die selbe Funktion, nur andere Parameter haben. Bis jetzt muss das Bild noch verschwommen sein, mit dem letzten Baustein sollte es sich klären.

package de.wicketpraxis.wicket.model;

import org.apache.wicket.model.IModel;

import de.wicketpraxis.wicket.model.transformation.ModelSet;

public abstract class Models
{
  public static <T,M1> ModelSet.Set1<M1> on(IModel<M1> model)
  {
    return new ModelSet.Set1<M1>(model);
  }
  public static <T,M1,M2> ModelSet.Set2<M1,M2> on(IModel<M1> model,IModel<M2> model2)
  {
    return new ModelSet.Set2<M1,M2>(model,model2);
  }
  public static <T,M1,M2,M3> ModelSet.Set3<M1,M2,M3> on(IModel<M1> model,IModel<M2> model2,IModel<M3> model3)
  {
    return new ModelSet.Set3<M1,M2,M3>(model,model2,model3);
  }
}

Fertig. Nun, vielleicht noch nicht ganz. Wir haben eine ganze Reihe von Klassen, eine ganze Menge Code, um die selben Anforderungen zu Erfüllen, wie mit dem im ersten Moment sehr viel einfacher gehaltenen Model-Ansatz aus dem anderen Artikel? Sehen wir uns daher das ganze in der Anwendung an.

Beispiel

In dem folgenden Beispiel benötigen wir ein Model, das eine Zufallszahl bereitstellt:

package de.wicketpraxis.web.blog.pages.migration.model.transformation;

import org.apache.wicket.model.LoadableDetachableModel;

public class RandomNumberModel extends LoadableDetachableModel<Double>
{
  @Override
  protected Double load()
  {
    return Math.random();
  }
}

Diese Modell benutzen wir auf einer Seite als Datenquelle:

package de.wicketpraxis.web.blog.pages.migration.model.transformation;

import org.apache.wicket.ajax.AjaxSelfUpdatingTimerBehavior;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.model.IModel;
import org.apache.wicket.util.time.Duration;

import de.wicketpraxis.wicket.model.Models;
import de.wicketpraxis.wicket.model.transformation.Function1;
import de.wicketpraxis.wicket.model.transformation.Function2;

public class TransformationPage extends WebPage
{
  public TransformationPage()
  {
    IModel<Double> randModel1=new RandomNumberModel();
    IModel<Double> randModel2=new RandomNumberModel();
    
    IModel<Double> diffResult = Models.on(randModel1, randModel2)
      .apply(new Function2<Double, Double, Double>()
    {
      public Double apply(Double value, Double value2)
      {
        return value-value2;
      }
    });
    
    IModel<Integer> scaleResult = Models.on(randModel1)
      .apply(new Function1<Integer, Double>()
    {
      public Integer apply(Double value)
      {
        return (int)(value*100);
      }
    });
    
    WebMarkupContainer ajaxUpdate=new WebMarkupContainer("ajaxUpdate");
    ajaxUpdate.setOutputMarkupId(true);
    ajaxUpdate.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(1)));
    
    ajaxUpdate.add(new Label("rand1",randModel1));
    ajaxUpdate.add(new Label("rand2",randModel2));
    ajaxUpdate.add(new Label("diffResult",diffResult));
    ajaxUpdate.add(new Label("scaleResult",scaleResult));
    
    add(ajaxUpdate);
  }
}

In diesem Beispiel erstellen wir zwei unabhängige Modelle für jeweils zwei Zufallszahlen. Diese werden über zwei Funktionsinstanzen manipuliert und als Model bereitgestellt. Dabei ist zu beachten, dass nicht das Ergebnis als Model bereitgestellt wird, sondern dass ein Model erzeugt wird, bei dem beim ersten Aufruf von getObject() die load()-Methode des LoadableDetachedModel aufgerufen wird, dass dann dort die Daten aus den referenzierten Modellen ermittelt und durch die Funktion transformiert. Das Ergebnis wird bis zum Aufruf von detach() (durch die angebundene Komponente) gepuffert.

Damit die Zufallszahlen immer wieder erzeugt werden, lassen wir die Anzeige sich per Ajax selbsttätig aktualisieren.

<html>
  <head>
    <title>Transformation Page</title>
  </head>
  <body>
    <h1>Zufallszahlen</h1>
    <div wicket:id="ajaxUpdate">
      <table>
        <tr>
          <td>Nummer 1</td><td><span wicket:id="rand1"></span></td>
        </tr>
        <tr>
          <td>Nummer 2</td><td><span wicket:id="rand2"></span></td>
        </tr>
        <tr>
          <td>Number 1*100</td><td><span wicket:id="scaleResult"></span></td>
        </tr>
        <tr>
          <td>Number 1-Nummer 2</td><td><span wicket:id="diffResult"></span></td>
        </tr>
      </table>
    </div>
  </body>
</html>

Fazit

Wo liegt jetzt der Vorteil? Zum einen sind die Implementierungen einfacher, da laut Schnittstellendefinition nur eine Methode implementiert werden muss. Zum anderen muss man nicht die benutzten Model-Klassen kennen, die diese Transformationen durchführen. Die Lesbarkeit ist wesentlich besser, weil aus dem Code abgeleitet werden kann, was da in etwa passiert.

Ist das der Weisheit letzter Schluss? Vermutlich nicht. Anregungen sind daher willkommen.

Tags:

Veröffentlicht in Refactoring, Wicket, .

Wicket Tipps: Markup

Ich benutze für alle Projekte Maven. Der eine oder andere mag mit Maven auf dem Kriegsfuß stehen (was ich zwar nicht verstehe, aber akzeptiere). Bisher war ich (und an anderer Stelle war es auch nicht notwendig, darüber nachzudenken) für eine saubere Trennung von Code und Resourcen. Das bedeutet z.B. das der Code einer Wicket-Komponente z.B. im Pfad src/main/java/de/wicketpraxis/ zu finden ist und das Markup in src/main/resources/de/wicketpraxis.

Das Projekt wächst und gedeiht und es entstehen unzählige Komponenten (ein Zeichen dafür, dass man mit Wicket sehr sehr einfach eigene Komponenten erstellen kann). Im Laufe der Zeit hat es sich als unpraktisch herausgestellt, in der IDE immer zwischen diesen beiden Verzeichnissen zu wechseln. Auch beim Refactoring kann es sich als lästig erweisen, weil man an zwei Stellen die selbe Operation anwenden muss (zumindest in Eclipse).

Ich habe daher alle Projekte umgestellt und kann nun die Markup-Dateien in das selbe Verzeichnis/Package wie die Komponente legen. Die Angst, dass die Übersichtlichkeit leidet, hat sich als unbegründet erwiesen, die Produktivität ist spürbar besser. Folgende Anpassung muss man daher in einem Projekt vornehmen. In der pom.xml ist folgender Block einzufügen:

  <build>
...
    <resources>
      <resource>
        <directory>src/main/resources</directory>
      </resource>
      <resource>
        <directory>src/main/java</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </resource>
    </resources>
...
  </build>

Wenn man nun die Anwendung über Jetty aus dem Projekt heraus starten möchte, empfiehlt sich ein Ansatz, den ich bereits im folgenden Beitrag erwähnt habe: Wicket Resourcen mit Jetty nachladen. Allerdings müssen wir dazu den angepassten ResourceStreamLocator entsprechend erweitern. Am besten wir schreiben eine neue Klasse:

package de.wicketpraxis.wicket.util.resource;

import org.apache.wicket.util.file.File;
import org.apache.wicket.util.resource.FileResourceStream;
import org.apache.wicket.util.resource.IResourceStream;
import org.apache.wicket.util.resource.locator.ResourceStreamLocator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MavenDevResourceAndSourceStreamLocator extends ResourceStreamLocator
{
  private static final Logger _logger = LoggerFactory.getLogger(MavenDevResourceAndSourceStreamLocator.class);
  
  final static String MAVEN_RESOURCE_PATH="src/main/resources/";
  final static String MAVEN_JAVASOURCE_PATH="src/main/java/";
  
  @Override
  public IResourceStream locate(final Class<?> clazz, final String path)
  {
    IResourceStream located=getFileSysResourceStream(MAVEN_RESOURCE_PATH,path);
    if (located==null)
    {
      located=getFileSysResourceStream(MAVEN_JAVASOURCE_PATH,path);
    }
    if (located != null)
    {
      if (_logger.isInfoEnabled()) _logger.info("Locate: " + clazz + " in " + path+" -> "+located);
      return located;
    }
    located=super.locate(clazz, path);
    if (_logger.isInfoEnabled()) _logger.info("Locate: " + clazz + " in " + path+" -> "+located+"(Fallback)");    
    return located;
  }

  private static IResourceStream getFileSysResourceStream(String prefix, String path)
  {
    File f=new File(prefix+path);
    if ((f.exists()) && (f.isFile()))
    {
      return new FileResourceStream(f);
    }
    return null;
  }
}

Die Einbindung funktioniert wie beim letzten Mal:

if (DEVELOPMENT.equalsIgnoreCase(configurationType))
{
  getResourceSettings().setResourceStreamLocator(new MavenDevResourceAndSourceStreamLocator());
}

Jetzt sollte man (wenn alles andere entsprechend vorbereitet ist) mit mvn jetty:run -Dwicket.configuration=development Jetty aus dem Projekt heraus starten können. Sobald man Änderungen am Markup vorgenommen hat, werden diese neu geladen.

Tags:

Veröffentlicht in Allgemein, Maven, Refactoring, Wicket, .