/*
* 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.config;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.application.Application;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
import javax.faces.el.PropertyResolver;
import javax.faces.el.ValueBinding;
import javax.faces.webapp.UIComponentTag;
import javax.servlet.jsp.el.ELException;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.el.Coercions;
import org.apache.commons.el.Logger;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.myfaces.config.element.ListEntries;
import org.apache.myfaces.config.element.ListEntry;
import org.apache.myfaces.config.element.ManagedBean;
import org.apache.myfaces.config.element.ManagedProperty;
import org.apache.myfaces.config.element.MapEntries;
import org.apache.myfaces.config.element.MapEntry;
import org.apache.myfaces.shared_impl.util.ClassUtils;
/**
* Create and initialize managed beans
*
* @author <a href="mailto:oliver@rossmueller.com">Oliver Rossmueller</a> (latest modification by $Author: lu4242 $)
* @author Anton Koinov
*/
public class ManagedBeanBuilder
{
private RuntimeConfig _runtimeConfig;
private static final Logger COERCION_LOGGER = new Logger(System.out);
private static final Log log = LogFactory.getLog(ManagedBeanBuilder.class);
public Object buildManagedBean(FacesContext facesContext, ManagedBean beanConfiguration) throws FacesException
{
Object bean = ClassUtils.newInstance(beanConfiguration.getManagedBeanClassName());
switch (beanConfiguration.getInitMode())
{
case ManagedBean.INIT_MODE_PROPERTIES:
try {
initializeProperties(facesContext, beanConfiguration.getManagedProperties(),
beanConfiguration.getManagedBeanScope(), bean);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
e.getMessage()
+ " for bean '"
+ beanConfiguration.getManagedBeanName()
+ "' check the configuration to make sure all properties correspond with get/set methods");
}
break;
case ManagedBean.INIT_MODE_MAP:
if (!(bean instanceof Map))
{
throw new IllegalArgumentException("Class " + bean.getClass().getName()
+ " of managed bean "
+ beanConfiguration.getManagedBeanName()
+ " is not a Map.");
}
initializeMap(facesContext, beanConfiguration.getMapEntries(), (Map) bean);
break;
case ManagedBean.INIT_MODE_LIST:
if (!(bean instanceof List))
{
throw new IllegalArgumentException("Class " + bean.getClass().getName()
+ " of managed bean "
+ beanConfiguration.getManagedBeanName()
+ " is not a List.");
}
initializeList(facesContext, beanConfiguration.getListEntries(), (List) bean);
break;
case ManagedBean.INIT_MODE_NO_INIT:
// no init values
break;
default:
throw new IllegalStateException("Unknown managed bean type "
+ bean.getClass().getName() + " for managed bean "
+ beanConfiguration.getManagedBeanName() + '.');
}
return bean;
}
private void initializeProperties(FacesContext facesContext, Iterator managedProperties, String targetScope, Object bean)
{
PropertyResolver propertyResolver =
facesContext.getApplication().getPropertyResolver();
while (managedProperties.hasNext())
{
ManagedProperty property = (ManagedProperty) managedProperties.next();
Object value = null;
switch (property.getType())
{
case ManagedProperty.TYPE_LIST:
// JSF 1.1, 5.3.1.3
// Call the property getter, if it exists.
// If the getter returns null or doesn't exist, create a java.util.ArrayList,
// otherwise use the returned Object ...
if(PropertyUtils.isReadable(bean, property.getPropertyName()))
value = propertyResolver.getValue(bean, property.getPropertyName());
value = value == null ? new ArrayList() : value;
if (value instanceof List) {
initializeList(facesContext, property.getListEntries(), (List) value);
} else if (value != null && value.getClass().isArray()) {
int length = Array.getLength(value);
ArrayList temp = new ArrayList(length);
for (int i = 0; i < length; i++) {
temp.add(Array.get(value, i));
}
initializeList(facesContext, property.getListEntries(), temp);
value = Array.newInstance(value.getClass().getComponentType(), temp.size());
length = temp.size();
for (int i = 0; i < length; i++) {
Array.set(value, i, temp.get(i));
}
} else {
value = new ArrayList();
initializeList(facesContext, property.getListEntries(), (List) value);
}
break;
case ManagedProperty.TYPE_MAP:
// JSF 1.1, 5.3.1.3
// Call the property getter, if it exists.
// If the getter returns null or doesn't exist, create a java.util.HashMap,
// otherwise use the returned java.util.Map .
if(PropertyUtils.isReadable(bean, property.getPropertyName()))
value = propertyResolver.getValue(bean, property.getPropertyName());
value = value == null ? new HashMap() : value;
if (! (value instanceof Map)) {
value = new HashMap();
}
initializeMap(facesContext, property.getMapEntries(), (Map) value);
break;
case ManagedProperty.TYPE_NULL:
value = null;
break;
case ManagedProperty.TYPE_VALUE:
// check for correct scope of a referenced bean
if (! isInValidScope(facesContext, property, targetScope)) {
throw new FacesException("Property " + property.getPropertyName() +
" references object in a scope with shorter lifetime than the target scope " + targetScope);
}
value = property.getRuntimeValue(facesContext);
break;
}
Class propertyClass = null;
if (property.getPropertyClass() == null)
{
propertyClass = propertyResolver
.getType(bean, property.getPropertyName());
}
else
{
propertyClass = ClassUtils
.simpleJavaTypeToClass(property.getPropertyClass());
}
if(null == propertyClass) {
throw new IllegalArgumentException("unable to find the type of property " + property.getPropertyName());
}
Object coercedValue = null;
try
{
coercedValue = (value == null) ? null : Coercions
.coerce(value, propertyClass, COERCION_LOGGER);
}
catch (ELException e)
{
String message = "Cannot coerce "
+ value.getClass().getName() + " to "
+ propertyClass.getName();
log.error(message, e);
throw new FacesException(message, e);
}
propertyResolver.setValue(
bean, property.getPropertyName(), coercedValue);
}
}
/**
* Check if the scope of the property value is valid for a bean to be stored in targetScope.
* @param facesContext
* @param property the property to be checked
* @param targetScope name of the target scope of the bean under construction
*/
private boolean isInValidScope(FacesContext facesContext, ManagedProperty property, String targetScope)
{
if (! property.isValueReference()) {
// no value reference but a literal value -> nothing to check
return true;
}
String[] expressions = extractExpressions(property.getValueBinding(facesContext).getExpressionString());
for (int i = 0; i < expressions.length; i++) {
String expression = expressions[i];
if (expression == null) {
continue;
}
String valueScope = getScope(facesContext, expression);
// if the target scope is 'none' value scope has to be 'none', too
if (targetScope == null || targetScope.equalsIgnoreCase("none")) {
if (valueScope != null && !(valueScope.equalsIgnoreCase("none"))) {
return false;
}
return true;
}
// 'application' scope can reference 'application' and 'none'
if (targetScope.equalsIgnoreCase("application")) {
if (valueScope != null) {
if (valueScope.equalsIgnoreCase("request") ||
valueScope.equalsIgnoreCase("session")) {
return false;
}
}
return true;
}
// 'session' scope can reference 'session', 'application', and 'none' but not 'request'
if (targetScope.equalsIgnoreCase("session")) {
if (valueScope != null) {
if (valueScope.equalsIgnoreCase("request")) {
return false;
}
}
return true;
}
// 'request' scope can reference any value scope
if (targetScope.equalsIgnoreCase("request")) {
return true;
}
}
return false;
}
private String getScope(FacesContext facesContext, String expression)
{
String beanName = getFirstSegment(expression);
ExternalContext externalContext = facesContext.getExternalContext();
// check scope objects
if (beanName.equalsIgnoreCase("requestScope")) {
return "request";
}
if (beanName.equalsIgnoreCase("sessionScope")) {
return "session";
}
if (beanName.equalsIgnoreCase("applicationScope")) {
return "application";
}
// check implicit objects
if (beanName.equalsIgnoreCase("cookie")) {
return "request";
}
if (beanName.equalsIgnoreCase("facesContext")) {
return "request";
}
if (beanName.equalsIgnoreCase("header")) {
return "request";
}
if (beanName.equalsIgnoreCase("headerValues")) {
return "request";
}
if (beanName.equalsIgnoreCase("initParam")) {
return "application";
}
if (beanName.equalsIgnoreCase("param")) {
return "request";
}
if (beanName.equalsIgnoreCase("paramValues")) {
return "request";
}
if (beanName.equalsIgnoreCase("view")) {
return "request";
}
// not found so far - check all scopes
if (externalContext.getRequestMap().get(beanName) != null) {
return "request";
}
if (externalContext.getSessionMap().get(beanName) != null) {
return "session";
}
if (externalContext.getApplicationMap().get(beanName) != null) {
return "application";
}
//not found - check mangaged bean config
ManagedBean mbc = getRuntimeConfig(facesContext).getManagedBean(beanName);
if (mbc != null) {
return mbc.getManagedBeanScope();
}
return null;
}
/**
* Extract the first expression segment, that is the substring up to the first '.' or '['
* @param expression
* @return first segment of the expression
*/
private String getFirstSegment(String expression)
{
int indexDot = expression.indexOf('.');
int indexBracket = expression.indexOf('[');
if (indexBracket < 0) {
if (indexDot < 0) {
return expression;
} else {
return expression.substring(0, indexDot);
}
} else {
if (indexDot < 0) {
return expression.substring(0, indexBracket);
} else {
return expression.substring(0, Math.min(indexDot, indexBracket));
}
}
}
private String[] extractExpressions(String expressionString)
{
String[] expressions = expressionString.split("\\#\\{");
for (int i = 0; i < expressions.length; i++) {
String expression = expressions[i];
if (expression.trim().length() == 0) {
expressions[i] = null;
} else {
int index = expression.indexOf('}');
expressions[i] = expression.substring(0, index);
}
}
return expressions;
}
private void initializeMap(FacesContext facesContext, MapEntries mapEntries, Map map)
{
Application application = facesContext.getApplication();
Class keyClass = (mapEntries.getKeyClass() == null)
? String.class : ClassUtils.simpleJavaTypeToClass(mapEntries.getKeyClass());
Class valueClass = (mapEntries.getValueClass() == null)
? String.class : ClassUtils.simpleJavaTypeToClass(mapEntries.getValueClass());
ValueBinding valueBinding;
for (Iterator iterator = mapEntries.getMapEntries(); iterator.hasNext();)
{
MapEntry entry = (MapEntry) iterator.next();
Object key = entry.getKey();
if (UIComponentTag.isValueReference((String) key))
{
valueBinding = application.createValueBinding((String) key);
key = valueBinding.getValue(facesContext);
}
if (entry.isNullValue())
{
Object coercedKey = null;
try
{
coercedKey = (key == null) ? null : Coercions
.coerce(key, keyClass, COERCION_LOGGER);
}
catch (ELException e)
{
String message = "Cannot coerce "
+ key.getClass().getName() + " to "
+ keyClass.getName();
log.error(message, e);
throw new FacesException(message, e);
}
map.put(coercedKey, null);
}
else
{
Object value = entry.getValue();
if (UIComponentTag.isValueReference((String) value))
{
valueBinding = application.createValueBinding((String) value);
value = valueBinding.getValue(facesContext);
}
Object coercedKey = null;
try
{
coercedKey = (key == null) ? null : Coercions
.coerce(key, keyClass, COERCION_LOGGER);
}
catch (ELException e)
{
String message = "Cannot coerce "
+ key.getClass().getName() + " to "
+ keyClass.getName();
log.error(message, e);
throw new FacesException(message, e);
}
Object coercedValue = null;
try
{
coercedValue = (value == null) ? null : Coercions
.coerce(value, valueClass, COERCION_LOGGER);
}
catch (ELException e)
{
String message = "Cannot coerce "
+ value.getClass().getName() + " to "
+ valueClass.getName();
log.error(message, e);
throw new FacesException(message, e);
}
map.put(coercedKey, coercedValue);
}
}
}
private void initializeList(FacesContext facesContext, ListEntries listEntries, List list)
{
Application application = facesContext.getApplication();
Class valueClass = listEntries.getValueClass() == null ? String.class : ClassUtils.simpleJavaTypeToClass(listEntries.getValueClass());
ValueBinding valueBinding;
for (Iterator iterator = listEntries.getListEntries(); iterator.hasNext();)
{
ListEntry entry = (ListEntry) iterator.next();
if (entry.isNullValue())
{
list.add(null);
}
else
{
Object value = entry.getValue();
if (UIComponentTag.isValueReference((String) value))
{
valueBinding = application.createValueBinding((String) value);
value = valueBinding.getValue(facesContext);
}
Object coercedValue = null;
try
{
coercedValue = (value == null) ? null : Coercions
.coerce(value, valueClass, COERCION_LOGGER);
}
catch (ELException e)
{
String message = "Cannot coerce "
+ value.getClass().getName() + " to "
+ valueClass.getName();
log.error(message, e);
throw new FacesException(message, e);
}
list.add(coercedValue);
}
}
}
private RuntimeConfig getRuntimeConfig(FacesContext facesContext)
{
if (_runtimeConfig == null)
{
_runtimeConfig = RuntimeConfig.getCurrentInstance(facesContext.getExternalContext());
}
return _runtimeConfig;
}
}