When building a Struts2 application recently, I needed to add arbitrary parameters to a URL when creating the menu from dynamic content. The parameters were stored in a map, so I used the my standard bit of code for iterating over a map:
<s:url var="url" action="%{link}" >
<s:iterator value="parameters.keySet()" var="key">
<s:param name="%{key}" value="%{parameters.get(#key)}"/>
</s:iterator>
</s:url>
…and nothing happened. No parameters at all appeared in the URL.
Odd.
I got rid of the iterator and used a single parameter, just to check:
<s:url var="url" action="%{link}" >
<s:param name="test-name" value="test-value"/>
</s:url>
That worked fine. One quick debugging session later and I found the problem – the Struts2 org.apache.struts2.components.Param component parameterises its parent component. In this case, the parent component is an iterator and so it was absorbing the parameters and they were never getting as far as the URL.
I couldn’t find a way to do what I needed the core Struts2 components and tags and so I created my own.
IterableParam overrides Param’s findAncestor method to return the grandparent component in the case where the parent is an Iterator:
<s:url var="url" action="%{link}" >
<s:iterator value="parameters.keySet()" var="key">
<ob:iterable-param name="%{key}" value="%{parameters.get(#key)}"/>
</s:iterator>
</s:url>
Result – works as required.
Despite the title of this blog entry, any Struts2 component that can be parameterised using the <s:param> tag can be parameterised using <ob:iterable-param>.
You can get the new tag and component from
objectify-led, a Java library for binding properties at runtime has just been released at Objectify.
Instead of having chunks of code such as
public class Foo
{
private static String BLAH = "default-value";
private String myString;
private int myInt = -1;
public static void main(String[] args)
{
if (System.getProperty("blah.value) != null)
{
BLAH = System.getProperty("blah.value");
}
Foo foo = new Foo();
if (System.getProperty("mystring.value) != null)
{
foo.myString = System.getProperty("mystring.value");
}
if (System.getProperty("myint.value) != null)
{
try
{
String intValue = System.getProperty("myint.value");
foo.myInt = Integer.parseInt(intValue;
}
catch (NumberFormatException e)
{
...
}
}
}
...
}
you can instead use objectify-led to bind the properties for you :
public class Foo
{
@Property("blah.value");
private static String BLAH = "default-value";
@Property("mystring.value");
private String myString;
@Property("myint.value");
private int myInt = -1;
public static void main(String[] args)
{
Foo foo = new Foo();
new PropertySetter().process(foo);
}
...
}
By default, Strings and all primitive/wrapper classes are handled. If you wish to convert a property value into a specific class, you just need to plug in your own object factory.
Additionally, the source of the properties is completely up to you. By default, properties are taken from the system environment and any Properties objects you may have plugged in; custom property contexts can be used to provide a facade to more complex value stores.
Static values are handled in two ways. If an instance of a class is processed, both its instance and static members are populated if necessary. If a class is processed, just the static members are populated (what with the lack of an instance and everything…).
Final properties- despite the ability to do so via reflection – are not set. This may or may not change depending on feedback.
If you want to try out objectify-led, you can get the binaries and sources from the links below, or directly from http://www.objectify.be. They will be submitted to the well-known Maven repositories in the coming days.
Binaries:
Sources:

Categories
Tag Cloud
Blog RSS
Comments RSS
Last 50 Posts
Back
Void « Default
Life
Earth
Wind
Water
Fire
Light 