/* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.myfaces.portlet.faces.el;
import java.beans.FeatureDescriptor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.PropertyNotFoundException;
import javax.el.PropertyNotWritableException;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.portlet.PortletConfig;
import javax.portlet.PortletPreferences;
import javax.portlet.PortletRequest;
import javax.portlet.PortletSession;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.faces.Bridge;
import javax.portlet.faces.BridgeUtil;
import javax.portlet.faces.preference.Preference;
import org.apache.myfaces.portlet.faces.util.map.PortletSessionMap;
import org.apache.myfaces.portlet.faces.preference.PreferenceImpl;
public class PortletELResolver extends ELResolver
{
// All are recognized/processed when its a Faces EL resolve
// While only those that are marked are recognized/processed when its a JSP EL resolve
public static enum BRIDGE_IMPLICT_OBJECTS_ENUM
{
portletConfig,
// request releated
renderRequest,
renderResponse,
// session related
httpSessionScope, // also supported in a JSP EL resolve
portletSession, // also supported in a JSF EL resolve
portletSessionScope, // also supported in a JSF EL resolve
// preference related
mutablePortletPreferencesValues, // also supported in a JSP EL resolve
portletPreferences, // also supported in a JSF EL resolve
portletPreferencesValues // also supported in a JSF EL resolve
}
public PortletELResolver()
{
}
@Override
public Object getValue(ELContext context, Object base, Object property) throws ELException
{
// only process if running in a portlet request
if (!BridgeUtil.isPortletRequest() || base != null)
{
return null;
}
// variable resolution is a special case of property resolution
// where the base is null.
if (property == null)
{
throw new PropertyNotFoundException("Null property");
}
// Recognize whether we are resolving in a Faces context or JSP context
// as the resolution differs. I.e. in the JSP context we defer to
// its implicit object resolver to resolve the <portlet:defineObjects> objects.
PortletELContextImpl portletELContext = (PortletELContextImpl) context.getContext(PortletELContextImpl.class);
if (portletELContext == null)
{
return null;
}
return getResolvedValue(context, portletELContext, base, property, portletELContext.isFacesResolved());
}
private Object getResolvedValue(ELContext context,
PortletELContextImpl bridgeContext,
Object base,
Object property,
boolean isFacesResolved)
throws ELException
{
FacesContext facesContext = (FacesContext) context.getContext(FacesContext.class);
ExternalContext extCtx = facesContext.getExternalContext();
if (property instanceof String)
{
try
{
switch (Enum.valueOf(BRIDGE_IMPLICT_OBJECTS_ENUM.class, (String) property))
{
case portletConfig:
// only resolved in a Faces expression
if (isFacesResolved)
{
PortletConfig config = bridgeContext.getPortletConfig();
if (config != null)
{
context.setPropertyResolved(true);
return config;
} else
{
throw new ELException("EL Resolve failed: can't resolve portletConfig because its not set on this Faces EL Resolver.");
}
}
else
{
return null;
}
case renderRequest:
// only resolved in a Faces expression
if (isFacesResolved)
{
if (BridgeUtil.getPortletRequestPhase() == Bridge.PortletPhase.RENDER_PHASE)
{
context.setPropertyResolved(true);
return extCtx.getRequest();
} else
{
throw new ELException("EL Resolve failed: can't resolve renderRequest in a non-render request");
}
}
else
{
return null;
}
case renderResponse:
// only resolved in a Faces expression
if (isFacesResolved)
{
if (BridgeUtil.getPortletRequestPhase() == Bridge.PortletPhase.RENDER_PHASE)
{
context.setPropertyResolved(true);
return extCtx.getResponse();
} else
{
throw new ELException("EL Resolve failed: can't resolve renderResponse in a non-render request");
}
}
else
{
return null;
}
case httpSessionScope:
context.setPropertyResolved(true);
return getHttpSessionMap(extCtx, bridgeContext);
case portletSession:
context.setPropertyResolved(true);
return extCtx.getSession(false);
case portletSessionScope:
context.setPropertyResolved(true);
return extCtx.getSessionMap();
case mutablePortletPreferencesValues:
context.setPropertyResolved(true);
return getMutablePortletPreferencesValues(extCtx, bridgeContext);
case portletPreferences:
context.setPropertyResolved(true);
return ((PortletRequest) extCtx.getRequest()).getPreferences();
case portletPreferencesValues:
context.setPropertyResolved(true);
return ((PortletRequest) extCtx.getRequest()).getPreferences().getMap();
default:
}
} catch (IllegalArgumentException e)
{
if (!isFacesResolved)
{
// Faces defers to the implicit object resolver when evaluating
// in a JSP context. Alas, this means that Faces doesn't resolve
// session scoped ManagedBeans in a JSP context if the managed bean
// is already created rather it defers to the JSP Implicit resolver
// which accesses the http session not the protlet session. I.e.
// though the managed bean resolver sees that the session scoped bean
// exists it can't be retrieved because its in the portlet session
// not the http session.
// So its up to us to see if the bean is in the session
if (extCtx.getSessionMap().containsKey(property))
{
context.setPropertyResolved(true);
return extCtx.getSessionMap().get(property);
}
}
}
}
return null;
}
@Override
public void setValue(ELContext context, Object base, Object property, Object val)
throws ELException
{
// only process if running in a portlet request
if (!BridgeUtil.isPortletRequest() || base != null)
{
return;
}
if (property == null)
{
throw new PropertyNotFoundException("Null property");
}
// As these properties aren't writable in either the Faces or JSP resolver
// don't distinguish in what context we are running.
if (property instanceof String)
{
try
{
// If exception not thrown then we had a hit
Enum.valueOf(BRIDGE_IMPLICT_OBJECTS_ENUM.class, (String) property);
throw new PropertyNotWritableException((String) property);
}
catch (IllegalArgumentException e)
{
; // do nothing
}
}
}
@Override
public boolean isReadOnly(ELContext context, Object base, Object property) throws ELException
{
// only process if running in a portlet request
if (!BridgeUtil.isPortletRequest() || base != null)
{
return false;
}
if (property == null)
{
throw new PropertyNotFoundException("Null property");
}
// As these properties aren't writable in either the Faces or JSP resolver
// don't distinguish in what context we are running.
if (property instanceof String)
{
try
{
// If exception not thrown then we had a hit
Enum.valueOf(BRIDGE_IMPLICT_OBJECTS_ENUM.class, (String) property);
context.setPropertyResolved(true);
return true;
}
catch (IllegalArgumentException e)
{
return false;
}
}
return false;
}
@Override
public Class<?> getType(ELContext context, Object base, Object property) throws ELException
{
// only process if running in a portlet request
if (!BridgeUtil.isPortletRequest() || base != null)
{
return null;
}
if (property == null)
{
throw new PropertyNotFoundException("Null property");
}
int index = -1;
// As these properties aren't writable in either the Faces or JSP resolver
// don't distinguish in what context we are running.
if (property instanceof String)
{
try
{
// If exception not thrown then we had a hit
Enum.valueOf(BRIDGE_IMPLICT_OBJECTS_ENUM.class, (String) property);
context.setPropertyResolved(true);
}
catch (IllegalArgumentException e)
{
; // do nothing
}
}
return null;
}
@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base)
{
if (base != null)
{
return null;
}
ArrayList<FeatureDescriptor> list = new ArrayList<FeatureDescriptor>(9);
list.add(getFeatureDescriptor("httpSessionScope", "httpSessionScope",
"httpSessionScope", false, false, true, Map.class,
Boolean.TRUE));
list.add(getFeatureDescriptor("mutablePortletPreferences", "mutablePortletPreferences",
"mutablePortletPreferences", false, false, true, Map.class,
Boolean.TRUE));
list.add(getFeatureDescriptor("portletConfig", "portletConfig", "portletConfig", false, false,
true, PortletConfig.class, Boolean.TRUE));
list.add(getFeatureDescriptor("portletPreferences", "portletPreferences", "portletPreferences", false, false,
true, PortletPreferences.class, Boolean.TRUE));
list.add(getFeatureDescriptor("portletPreferencesValues", "portletPreferencesValues", "portletPreferencesValues", false, false,
true, Map.class, Boolean.TRUE));
list.add(getFeatureDescriptor("portletSession", "portletSession", "portletSession", false, false,
true, PortletSession.class, Boolean.TRUE));
list.add(getFeatureDescriptor("portletSessionScope", "portletSessionScope",
"portletSessionScope", false, false, true, Map.class,
Boolean.TRUE));
list.add(getFeatureDescriptor("renderRequest", "renderRequest", "renderRequest", false, false,
true, RenderRequest.class, Boolean.TRUE));
list.add(getFeatureDescriptor("renderResponse", "renderResponse", "renderResponse", false, false,
true, RenderResponse.class, Boolean.TRUE));
return list.iterator();
}
@Override
public Class<?> getCommonPropertyType(ELContext context, Object base)
{
if (base != null)
{
return null;
}
return String.class;
}
private FeatureDescriptor getFeatureDescriptor(String name, String displayName, String desc,
boolean expert, boolean hidden, boolean preferred,
Object type, Boolean designTime)
{
FeatureDescriptor fd = new FeatureDescriptor();
fd.setName(name);
fd.setDisplayName(displayName);
fd.setShortDescription(desc);
fd.setExpert(expert);
fd.setHidden(hidden);
fd.setPreferred(preferred);
fd.setValue(ELResolver.TYPE, type);
fd.setValue(ELResolver.RESOLVABLE_AT_DESIGN_TIME, designTime);
return fd;
}
@SuppressWarnings("unchecked")
private Map<String, String> getPreferencesValueMap(ExternalContext extCtx)
{
Map<String, String> m;
PortletRequest portletRequest = (PortletRequest) extCtx.getRequest();
Enumeration<String> e = portletRequest.getPreferences().getNames();
if (e.hasMoreElements())
{
m = new HashMap<String, String>();
while (e.hasMoreElements())
{
String name = e.nextElement();
String value = portletRequest.getPreferences().getValue(name, null);
if (value != null)
{
m.put(name, value);
}
}
}
else
{
m = Collections.emptyMap();
}
return m;
}
private Map<String, Preference> getPreferenceMap(PortletPreferences prefs)
{
Map <String, Preference> m;
// construct a Map of Preference objects for each preference
Enumeration<String> e = prefs.getNames();
if (e.hasMoreElements())
{
m = new HashMap<String, Preference>();
while (e.hasMoreElements())
{
String name = e.nextElement();
m.put(name, new PreferenceImpl(prefs, name));
}
}
else
{
m = Collections.emptyMap();
}
return m;
}
private Map<String, Object> getHttpSessionMap(ExternalContext extCtx, PortletELContextImpl bridgeContext)
{
Map<String, Object> sessionMap = bridgeContext.getHttpSessionMap();
if (sessionMap == null)
{
sessionMap =
new PortletSessionMap(extCtx.getRequest(), PortletSession.APPLICATION_SCOPE);
bridgeContext.setHttpSessionMap(sessionMap);
}
return sessionMap;
}
private Map getMutablePortletPreferencesValues(ExternalContext extCtx, PortletELContextImpl bridgeContext)
{
Map<String, Preference> preferencesValuesMap =
bridgeContext.getMutablePortletPreferencesMap();
if (preferencesValuesMap == null)
{
preferencesValuesMap =
getPreferenceMap(((PortletRequest) extCtx.getRequest()).getPreferences());
bridgeContext.setMutablePortletPreferencesMap(preferencesValuesMap);
}
return preferencesValuesMap;
}
}