1

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?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
Koray Tugay
  • 22,894
  • 45
  • 188
  • 319

1 Answers1

1

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.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555