I have this sample code:
<h:form>
<h:commandButton action="#{fooBar.foo()}" value="Submit"/>
</h:form>
and in the bean:
@ManagedBean
@ApplicationScoped
public class FooBar {
public String foo() {
final Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();
flash.put("message", "Hello World");
return "hello?faces-redirect=true";
}
}
and finally in hello.xhtml
<h:body>
#{flash.keep.message}
</h:body>
So I go to index.xhtml
, hit Submit, I get redirected to hello.xhtml
as expected. And when I refresh the page I still see the message because of the flash.keep behavior which is great.
Now I am trying to understand what is going on so I open up the documentation.
There is a keep()
method in this class, but its return type is void
and it expects a String
parameter. So is #{flash.keep.message}
calling the keep()
method with the message parameter? I really do not think so, as as far as I know it should have been #{flash.keep(message)}
, isn't it?
So what is going on in here?
EL resolving can be customized with ELResolver
implementations. There are two EL resolvers involved in evaluating #{flash.keep.message}
. The first one, the JSF-builtin FlashELResolver
is executed on #{flash}
. As you can see in its source code (line numbers match Mojarra 2.2.12),
216 // and the property argument is "keep"...
217 if (property.toString().equals(FLASH_KEEP_VARIABLE_NAME))
218 {
219 elContext.setPropertyResolved(true);
220
221 // then this is a request to promote the value
222 // "property", which is assumed to have been previously
223 // stored in request scope via the "flash.now"
224 // expression, to flash scope.
225 result = base;
226 // Set a flag so the flash itself can look in the request
227 // and promote the value to the next request
228 FlashFactory ff = (FlashFactory)
229 FactoryFinder.getFactory(FactoryFinder.FLASH_FACTORY);
230 ff.getFlash(true);
231 ELFlash.setKeepFlag(facesContext);
232 }
the FlashELResolver
will call ELFlash.setKeepFlag(facesContext)
(line 231) when the #{flash.keep}
expression is evaluated. It also sets the property as resolved (line 219), so that the EL context can advance with next property, and it sets the base
(the #{flash}
) as evaluated result (line 225), so effectively #{flash.keep}
returns the very same #{flash}
object.
And then, when the message
property is to be evaluated on the result of #{flash.keep}
, which is essentially still #{flash}
, but with the "keep" flag set, the EL-builtin MapELResolver
is executed. This is because #{flash}
is essentially a Map
, see also the javadoc (emphasis mine).
public abstract class Flash
extends Object
implements Map<String,Object>
This calls the Map#get()
method, which is customized in Flash
class as below (line numbers match Mojarra 2.2.12):
384 public Object get(Object key) {
385 Object result = null;
386
387 FacesContext context = FacesContext.getCurrentInstance();
388 if (null != key) {
389 if (key.equals("keepMessages")) {
390 result = this.isKeepMessages();
391 } else if (key.equals("redirect")) {
392 result = this.isRedirect();
393 } else {
394 if (isKeepFlagSet(context)) {
395 result = getPhaseMapForReading().get(key);
396 keep(key.toString());
397 clearKeepFlag(context);
398 return result;
399 }
400
401 }
402
403 }
As you can see at line 396, it will call keep(key)
when the flag is set, as done by FlashELResolver
.