Zunächst vorweg:
die Validierung über -validation.xml ist wegen der Möglichkeit der Lokalisierung über Bundels zunächst charmant, hat aber diverse Nachteile.
Zum einen sind die Möglichkeiten der Validierung nur relativ schlicht und reichen in vielen Fällen nicht aus.
Zum anderen funktionieren sie nicht gemeinsam mit Tiles (zumindest nicht in der Fassung, die mit Struts 2.0.6 mitkam, danach habe ich es nicht mehr probiert).
Dein Problem hängt damit zusammen, dass wir uns trotz der massiven Erleichterungen, die Struts 2 uns bietet, in einer zustandsfreien Umgebung bewegen. Auch Struts "vergisst" in seiner Action mit jedem Client-Aufruf alles, was nicht als Inhalt eines Formularfeldes wieder zurück kommt. Dabei muss man beachten, dass dies schon innerhalb derselben Action passieren kann. Wenn etwa über die Methode prepare() eine Variable gefüllt wird, ist deren Inhalt beim Aufruf von validate() oder execute() schon wieder verloren. Klar, denn jedes Mal wird eine neue Instanz der Action geladen und die ist erst mal leer.
Du müsstest also in Deinem Fall den Parameter in ein - möglicherweise unsichtbares - Formularfeld umlesen, wenn Du ihn über den nächsten Request/Response-Zyklus retten willst.
Eine andere Alternative sind Inhalte, die in den Session Context gelegt werden, aber das ist für solche temporären Inhalte keine glückliche Lösung.
Das Problem besteht übrigens generell für alle Informationen, die über mehrere Maskenwechsel erhalten bleiben sollen, aber nicht Bestandteil der Formularfelder sind (z.B. ein Recordset).
Dann empfiehlt sich die Nutzung eines Interceptors, mit dem ein sog. "Conversation Scope" hergestellt wird.
Grob umrissen funktioniert der so:
Jede Action, die an einer "Conversation" teilnehmen soll, implementiert ein bestimmtes Interface, z.B. ConversationAware. Daran erkennt der Interceptor, ob die Action das Verfahren unterstützt.
Wenn ja, wird über eine Methode der Action nachgefragt, welches "Thema" die Action unterstützt (z.B. Adressen). Dann wird überprüft, ob ein passendes Conversation Object (vulgo: Session Bean) im Session Context hinterlegt ist.
Wenn nicht, wird ein neues von der Action abgefordert und in der Session hinterlegt.
Wenn ja, wird dieses Object der Action übergeben, also "injiziert".
Auf diese Weise werden alle Informationen, die in diesem Objekt enthalten sind, über beliebig viele Request/Response-Zyklen erhalten.
Und noch genialer:
der Interceptor übergibt das Conversation Object nicht der Action, sondern legt es noch auf dem Value Stack ab.
Dadurch stehen die Eigenschaften des Conversation Objects direkt in der View zur Verfügung. Das heißt, die Getter und Setter werden nur einmal im ConversationObject definiert und alle Actions, die das gleiche Object verwenden, also zum selben "Thema" gehören, brauchen diese nicht mehr zu implementieren, sondern kommunizieren nur noch mit diesem Objekt.
Wenn der Interceptor auf eine Action trifft, die entweder nicht ConversationAware ist oder ein anderes Thema unterstützt, wird das bisherige Objekt verworfen (wenn man es so will - andere Verhaltensweisen sind ja möglich).
Hört sich zunächst etwas kompliziert an, ist aber relativ einfach, wenn man den Mechanismus erst mal verstanden hat.
Das Verfahren ist sehr schön bei Mark Menard beschrieben:
http://www.vitarara.org/cms/struts_2...ersation_scope
Viel Erfolg!