com.sun.faces.context.flash.FlashELResolver Maven / Gradle / Ivy
Show all versions of jakarta.faces Show documentation
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package com.sun.faces.context.flash;
import javax.el.ELContext;
import javax.el.ELResolver;
import javax.el.PropertyNotFoundException;
import javax.el.PropertyNotWritableException;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
import javax.faces.FactoryFinder;
import javax.faces.context.Flash;
import javax.faces.context.FlashFactory;
/**
* Provide a feature semantically identical to the
* "flash" concept in Ruby on Rails.
*
* The feature is exposed to users via a custom
* ELResolver
which introduces a new implicit object,
* flash
. The flash functions as Map
and
* can be used in getValue( )
or setValue(
* )
expressions.
*
* Usage
*
*
*
* Consider three JSF views: viewA, viewB, and viewC. The user
* first views viewA, then clicks a button and is shown viewB, where
* she clicks a button and is shown viewC. If values are stored
* into the flash during the rendering or postback phases of viewA,
* they are available to during the rendering phase of viewB, but
* are not available during the rendering or postback phases of
* viewC. In other words, values stored into the flash on "this"
* request are accessible for the "next" request, but not
* thereafter.
*
* There are three ways to access the flash.
*
*
*
* Using an EL Expression, such as using
* #{flash.foo}
as the value of an attribute in a JSP
* page.
*
* Using the EL API, such as:
*
*
* FacesContext context = FacesContext.getCurrentInstance();
* ValueExpression flashExpression = context.getApplication().
* createValueExpression(context.getELContext(), "#{flash.foo}",
* null, Object.class);
* flashExpression.setValue(context.getELContext(), "Foo's new value");
*
*
*
*
* Using getting the {@link ELFlash} directly, such as:
*
*
* Map<String,Object> flash = ELFlash.getFlash();
* flash.put("foo", "Foo's new value");
*
*
*
*
*
*
* The main entry point to this feature is the first one. This
* library includes a simple custom tag, jsfExt:set
, that
* evaluates an expression and sets its value into another expression.
* jsfExt:set
can be used to store values into the flash
* from JSP pages, like this:
*
* <jsfExt:set var="#{flash.foo}" value="fooValue"
* />
*
* or this:
*
* <jsfExt:set var="#{flash.keep.bar}" value="#{user.name}"
* />
*
* or even this:
*
*
* <jsfExt:set var="#{flash.now.baz}" value="#{cookie.userCookie}" />
*
* <h:outputText value="#{flash.now.baz}" />
*
*
*
*
*
* Related Classes
*
* The complete list of classes that make up this feature is
*
*
*
* FlashELResolver
*
* {@link ELFlash}
*
*
*/
public class FlashELResolver extends ELResolver {
/**
* Not intended for manual invocation. Only called by the JSF
* Runtime.
*/
public FlashELResolver() {
}
// ------------------------------------------------------ Manifest Constants
private static final String FLASH_VARIABLE_NAME = "flash";
private static final String FLASH_NOW_VARIABLE_NAME = "now";
private static final String FLASH_KEEP_VARIABLE_NAME = "keep";
// ------------------------------------------------ VariableResolver Methods
/**
* Hook into the EL resolution process to introduce the
* flash
implicit object. If
* property
is null
, take no action
* and return null
. if base
is null, return null.
* If
* base
is an instance of ELFlash
and
* property is the literal string "keep", set a ThreadLocal property
* that will be inspected by the flash on the next link in the
* resolution chain and return the ELFlash
instance.
* If base
is an instance of ELFlash
and
* property
is the literal string "now", return the
* result of calling getRequestMap( )
on the
* ExternalContext
for the FacesContext
* for this request. Call setPropertyResolved(true)
on
* the ELContext
where appropriate.
*
* @throws PropertyNotFoundException if property
is
* null
.
*/
@Override
public Object getValue(ELContext elContext, Object base, Object property)
{
if (null == property)
{
// take no action.
return null;
}
Object result = null;
if (null == base)
{
return null;
}
// If the base argument is the flash itself...
else if (base instanceof Flash)
{
FacesContext facesContext =
(FacesContext) elContext.getContext(FacesContext.class);
ExternalContext extCtx = facesContext.getExternalContext();
// and the property argument is "keep"...
switch (property.toString()) {
// Otherwise, if base is the flash, and property is "now"...
case FLASH_KEEP_VARIABLE_NAME:
elContext.setPropertyResolved(true);
// then this is a request to promote the value
// "property", which is assumed to have been previously
// stored in request scope via the "flash.now"
// expression, to flash scope.
result = base;
// Set a flag so the flash itself can look in the request
// and promote the value to the next request
FlashFactory ff = (FlashFactory)
FactoryFinder.getFactory(FactoryFinder.FLASH_FACTORY);
ff.getFlash(true);
ELFlash.setKeepFlag(facesContext);
break;
case FLASH_NOW_VARIABLE_NAME:
// PENDING(edburns): use FacesContext.getAttributes() instead of
// request scope.
Map requestMap = extCtx.getRequestMap();
requestMap.put(ELFlash.FLASH_NOW_REQUEST_KEY, property);
elContext.setPropertyResolved(true);
result = requestMap;
break;
default:
result = null;
break;
}
}
return result;
}
/**
* Return the valid Class
for a future set
* operation, which will always be null
because sets
* happen via the MapELResolver
operating on the {@link
* ELFlash} instance as a Map
.
*
* @throws PropertyNotFoundException if property is null
.
*/
@Override
public Class getType(ELContext elContext,
Object base,
Object property) {
if (null != base) {
return null;
}
if (null == property) {
String message = " base " + base + " property " + property;
throw new PropertyNotFoundException(message);
}
if (property.toString().equals(FLASH_VARIABLE_NAME)) {
elContext.setPropertyResolved(true);
}
return null;
}
/**
* This method will throw
* PropertyNotWritableException
if called with a
* null
base
and a property
* value equal to the literal string "flash". This is because set
* operations normally go through the MapELResolver
via
* the ELFlash
Map
.
*
* In other words, do not call this method directly to set a
* value into the flash! The only way to access the flash is either
* through JSP or via the EL API.
*
* @throws PropertyNotFoundException if base
is
* null
and property
is null
.
* @throws PropertyNotWritableException if base
is
* null
and property
is the literal string
* "flash".
*/
@Override
public void setValue(ELContext elContext,
Object base,
Object property,
Object value) {
if (null != base) {
return;
}
if (null == property) {
String message = " base " + base + " property " + property;
throw new PropertyNotFoundException(message);
}
if (property.toString().equals(FLASH_VARIABLE_NAME)) {
elContext.setPropertyResolved(true);
throw new PropertyNotWritableException(property.toString());
}
}
/**
* Returns true
because write operations take place
* via the MapELResolver
on the actual {@link ELFlash}
* instance.
*
* @throws PropertyNotFoundException if base
is
* null
and property
is null
.
*/
@Override
public boolean isReadOnly(ELContext elContext,
Object base,
Object property) {
if (base != null) {
return false;
}
if (property == null) {
String message = " base " + base + " property " + property;
throw new PropertyNotFoundException(message);
}
if (property.toString().equals(FLASH_VARIABLE_NAME)) {
elContext.setPropertyResolved(true);
return true;
}
return false;
}
/**
* Returns an iterator of FeatureDescriptors
for the
* current contents of the flash.
*/
@Override
public Iterator getFeatureDescriptors(ELContext elContext,
Object base) {
if (null != base) {
return null;
}
Iterator result = null;
Map flash;
FacesContext facesContext =
(FacesContext) elContext.getContext(FacesContext.class);
ExternalContext extCtx = facesContext.getExternalContext();
//noinspection unchecked
if (null != (flash = extCtx.getFlash())) {
Iterator> iter = flash.entrySet().iterator();
Map.Entry cur;
ArrayList fds;
FeatureDescriptor fd;
if (iter.hasNext()) {
fds = new ArrayList<>(flash.size());
while (iter.hasNext()) {
cur = iter.next();
fd = new FeatureDescriptor();
fd.setName(cur.getKey());
fds.add(fd);
}
result = fds.iterator();
}
}
return result;
}
/**
* If base
is non-null
and is the
* literal string "flash", return Object.class
.
*/
@Override
public Class getCommonPropertyType(ELContext context,
Object base) {
Class result = null;
if (null != base) {
if (FLASH_VARIABLE_NAME.equals(base.toString())) {
result = Object.class;
}
}
return result;
}
}