Java tutorial
/* * 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 org.apache.commons.beanutils.PropertyUtils; import org.apache.myfaces.config.annotation.LifecycleProvider; import org.apache.myfaces.config.annotation.LifecycleProvider2; import org.apache.myfaces.config.annotation.LifecycleProviderFactory; 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.context.servlet.StartupServletExternalContextImpl; import org.apache.myfaces.shared.util.ClassUtils; import org.apache.myfaces.util.ContainerUtils; import javax.el.ELContext; import javax.el.ELException; import javax.el.ELResolver; import javax.el.ExpressionFactory; import javax.el.ValueExpression; import javax.faces.FacesException; import javax.faces.application.Application; import javax.faces.application.ProjectStage; import javax.faces.context.ExternalContext; import javax.faces.context.FacesContext; import javax.naming.NamingException; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; /** * Create and initialize managed beans * * @author <a href="mailto:oliver@rossmueller.com">Oliver Rossmueller</a> (latest modification by $Author: struberg $) * @author Anton Koinov */ public class ManagedBeanBuilder { //private static Log log = LogFactory.getLog(ManagedBeanBuilder.class); private static Logger log = Logger.getLogger(ManagedBeanBuilder.class.getName()); private RuntimeConfig _runtimeConfig; public final static String REQUEST = "request"; public final static String VIEW = "view"; public final static String APPLICATION = "application"; public final static String SESSION = "session"; public final static String NONE = "none"; /** * Comparator used to compare Scopes in the following order: * REQUEST VIEW SESSION APPLICATION NONE * @author Jakob Korherr */ private final static Comparator<String> SCOPE_COMPARATOR = new Comparator<String>() { public int compare(String o1, String o2) { if (o1.equalsIgnoreCase(o2)) { // the same scope return 0; } if (o1.equalsIgnoreCase(NONE)) { // none is greater than any other scope return 1; } if (o1.equalsIgnoreCase(APPLICATION)) { if (o2.equalsIgnoreCase(NONE)) { // application is smaller than none return -1; } else { // ..but greater than any other scope return 1; } } if (o1.equalsIgnoreCase(SESSION)) { if (o2.equalsIgnoreCase(REQUEST) || o2.equalsIgnoreCase(VIEW)) { // session is greater than request and view return 1; } else { // but smaller than any other scope return -1; } } if (o1.equalsIgnoreCase(VIEW)) { if (o2.equalsIgnoreCase(REQUEST)) { // view is greater than request return 1; } else { // ..but smaller than any other scope return -1; } } if (o1.equalsIgnoreCase(REQUEST)) { // request is smaller than any other scope return -1; } // not a valid scope throw new IllegalArgumentException(o1 + " is not a valid scope"); } }; @SuppressWarnings("unchecked") public Object buildManagedBean(FacesContext facesContext, ManagedBean beanConfiguration) throws FacesException { try { ExternalContext externalContext = facesContext.getExternalContext(); LifecycleProvider lifecycleProvider = LifecycleProviderFactory .getLifecycleProviderFactory(externalContext).getLifecycleProvider(externalContext); final Object bean = lifecycleProvider.newInstance(beanConfiguration.getManagedBeanClassName()); switch (beanConfiguration.getInitMode()) { case ManagedBean.INIT_MODE_PROPERTIES: try { initializeProperties(facesContext, beanConfiguration, 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", e); } 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<Object, Object>) 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<Object>) 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() + '.'); } // MYFACES-1761 if implements LifecycleProvider, //PostConstruct was already called, but if implements //LifecycleProvider2, call it now. if (lifecycleProvider instanceof LifecycleProvider2) { ((LifecycleProvider2) lifecycleProvider).postConstruct(bean); } return bean; } catch (IllegalAccessException e) { throw new FacesException(e); } catch (InvocationTargetException e) { throw new FacesException(e); } catch (NamingException e) { throw new FacesException(e); } catch (ClassNotFoundException e) { throw new FacesException(e); } catch (InstantiationException e) { throw new FacesException(e); } } @SuppressWarnings("unchecked") private void initializeProperties(FacesContext facesContext, ManagedBean beanConfiguration, Object bean) { ELResolver elResolver = facesContext.getApplication().getELResolver(); ELContext elContext = facesContext.getELContext(); for (ManagedProperty property : beanConfiguration.getManagedProperties()) { 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 = elResolver.getValue(elContext, bean, property.getPropertyName()); } value = value == null ? new ArrayList<Object>() : value; if (value instanceof List) { initializeList(facesContext, property.getListEntries(), (List<Object>) value); } else if (value != null && value.getClass().isArray()) { int length = Array.getLength(value); ArrayList<Object> temp = new ArrayList<Object>(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<Object>(); initializeList(facesContext, property.getListEntries(), (List<Object>) 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 = elResolver.getValue(elContext, bean, property.getPropertyName()); } value = value == null ? new HashMap<Object, Object>() : value; if (!(value instanceof Map)) { value = new HashMap<Object, Object>(); } initializeMap(facesContext, property.getMapEntries(), (Map<Object, Object>) value); break; case ManagedProperty.TYPE_NULL: break; case ManagedProperty.TYPE_VALUE: // check for correct scope of a referenced bean if (!isInValidScope(facesContext, property, beanConfiguration)) { throw new FacesException("Property " + property.getPropertyName() + " references object in a scope with shorter lifetime than the target scope " + beanConfiguration.getManagedBeanScope()); } value = property.getRuntimeValue(facesContext); break; default: throw new FacesException("unknown ManagedProperty type: " + property.getType()); } Class<?> propertyClass = null; if (property.getPropertyClass() == null) { propertyClass = elResolver.getType(elContext, 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 = coerceToType(facesContext, value, propertyClass); elResolver.setValue(elContext, bean, property.getPropertyName(), coercedValue); } } // We no longer use the convertToType from shared impl because we switched // to unified EL in JSF 1.2 @SuppressWarnings("unchecked") public static <T> T coerceToType(FacesContext facesContext, Object value, Class<? extends T> desiredClass) { if (value == null) { return null; } try { ExpressionFactory expFactory = facesContext.getApplication().getExpressionFactory(); // Use coersion implemented by JSP EL for consistency with EL // expressions. Additionally, it caches some of the coersions. return (T) expFactory.coerceToType(value, desiredClass); } catch (ELException e) { String message = "Cannot coerce " + value.getClass().getName() + " to " + desiredClass.getName(); log.log(Level.SEVERE, message, e); throw new FacesException(message, e); } } /** * Checks if the scope of the property value is valid for a bean to be stored in targetScope. * If one of the scopes is a custom scope (since jsf 2.0), this method only checks the * references if the current ProjectStage is not Production. * @param facesContext * @param property the property to be checked * @param beanConfiguration the ManagedBean, which will be created */ private boolean isInValidScope(FacesContext facesContext, ManagedProperty property, ManagedBean beanConfiguration) { if (!property.isValueReference()) { // no value reference but a literal value -> nothing to check return true; } // get the targetScope (since 2.0 this could be an EL ValueExpression) String targetScope = null; if (beanConfiguration.isManagedBeanScopeValueExpression()) { // the scope is a custom scope // Spec says, that the developer has to take care about the references // to and from managed-beans in custom scopes. // However, we do check the references, if we are not in Production stage if (facesContext.isProjectStage(ProjectStage.Production)) { return true; } else { targetScope = getNarrowestScope(facesContext, beanConfiguration.getManagedBeanScopeValueExpression(facesContext).getExpressionString()); // if we could not obtain a targetScope, return true if (targetScope == null) { return true; } } } else { targetScope = beanConfiguration.getManagedBeanScope(); if (targetScope == null) { targetScope = NONE; } } // optimization: 'request' scope can reference any value scope if (targetScope.equalsIgnoreCase(REQUEST)) { return true; } String valueScope = getNarrowestScope(facesContext, property.getValueBinding(facesContext).getExpressionString()); // if we could not obtain a valueScope, return true if (valueScope == null) { return true; } // the target scope needs to have a shorter (or equal) lifetime than the value scope return (SCOPE_COMPARATOR.compare(targetScope, valueScope) <= 0); } /** * Gets the narrowest scope to which the ValueExpression points. * @param facesContext * @param valueExpression * @return */ private String getNarrowestScope(FacesContext facesContext, String valueExpression) { List<String> expressions = extractExpressions(valueExpression); // exclude NONE scope, if there are more than one ValueExpressions (see Spec for details) String narrowestScope = expressions.size() == 1 ? NONE : APPLICATION; boolean scopeFound = false; for (String expression : expressions) { String valueScope = getScope(facesContext, expression); if (valueScope == null) { continue; } // we have found at least one valid scope at this point scopeFound = true; if (SCOPE_COMPARATOR.compare(valueScope, narrowestScope) < 0) { narrowestScope = valueScope; } } return scopeFound ? narrowestScope : null; } 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("param")) { return REQUEST; } if (beanName.equalsIgnoreCase("paramValues")) { return REQUEST; } if (beanName.equalsIgnoreCase("request")) { return REQUEST; } if (beanName.equalsIgnoreCase("view")) // Spec says that view is considered to be in request scope { return REQUEST; } if (beanName.equalsIgnoreCase("application")) { return APPLICATION; } if (beanName.equalsIgnoreCase("initParam")) { return APPLICATION; } // not found so far - check all scopes final boolean startup = (externalContext instanceof StartupServletExternalContextImpl); if (!startup) { // request and session maps are only available at runtime - not at startup // (the following code would throw an UnsupportedOperationException). if (externalContext.getRequestMap().get(beanName) != null) { return REQUEST; } if (externalContext.getSessionMap().get(beanName) != null) { return SESSION; } } if (externalContext.getApplicationMap().get(beanName) != null) { return APPLICATION; } if (facesContext.getViewRoot().getViewMap().get(beanName) != null) { return VIEW; } //not found - check mangaged bean config ManagedBean mbc = getRuntimeConfig(facesContext).getManagedBean(beanName); if (mbc != null) { // managed-bean-scope could be a EL ValueExpression (since 2.0) if (mbc.isManagedBeanScopeValueExpression()) { // the scope is a custom scope // Spec says, that the developer has to take care about the references // to and from managed-beans in custom scopes. // However, we do check the references, if we are not in Production stage if (facesContext.isProjectStage(ProjectStage.Production)) { return null; } else { String scopeExpression = mbc.getManagedBeanScopeValueExpression(facesContext) .getExpressionString(); return getNarrowestScope(facesContext, scopeExpression); } } else { 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) { return indexDot < 0 ? expression : expression.substring(0, indexDot); } if (indexDot < 0) { return expression.substring(0, indexBracket); } return expression.substring(0, Math.min(indexDot, indexBracket)); } private List<String> extractExpressions(String expressionString) { List<String> expressions = new ArrayList<String>(); for (String expression : expressionString.split("\\#\\{")) { int index = expression.indexOf('}'); if (index >= 0) { expressions.add(expression.substring(0, index)); } } return expressions; } private void initializeMap(FacesContext facesContext, MapEntries mapEntries, Map<? super Object, ? super Object> 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()); ValueExpression valueExpression; ExpressionFactory expFactory = application.getExpressionFactory(); ELContext elContext = facesContext.getELContext(); for (Iterator<? extends MapEntry> iterator = mapEntries.getMapEntries(); iterator.hasNext();) { MapEntry entry = iterator.next(); Object key = entry.getKey(); if (ContainerUtils.isValueReference((String) key)) { valueExpression = expFactory.createValueExpression(elContext, (String) key, Object.class); key = valueExpression.getValue(elContext); } if (entry.isNullValue()) { map.put(coerceToType(facesContext, key, keyClass), null); } else { Object value = entry.getValue(); if (ContainerUtils.isValueReference((String) value)) { valueExpression = expFactory.createValueExpression(elContext, (String) value, Object.class); value = valueExpression.getValue(elContext); } map.put(coerceToType(facesContext, key, keyClass), coerceToType(facesContext, value, valueClass)); } } } private void initializeList(FacesContext facesContext, ListEntries listEntries, List<? super Object> list) { Application application = facesContext.getApplication(); Class<?> valueClass = (listEntries.getValueClass() == null) ? String.class : ClassUtils.simpleJavaTypeToClass(listEntries.getValueClass()); ExpressionFactory expFactory = application.getExpressionFactory(); ELContext elContext = facesContext.getELContext(); for (Iterator<? extends ListEntry> iterator = listEntries.getListEntries(); iterator.hasNext();) { ListEntry entry = iterator.next(); if (entry.isNullValue()) { list.add(null); } else { Object value = entry.getValue(); if (ContainerUtils.isValueReference((String) value)) { ValueExpression valueExpression = expFactory.createValueExpression(elContext, (String) value, Object.class); value = valueExpression.getValue(elContext); } list.add(coerceToType(facesContext, value, valueClass)); } } } private RuntimeConfig getRuntimeConfig(FacesContext facesContext) { if (_runtimeConfig == null) { _runtimeConfig = RuntimeConfig.getCurrentInstance(facesContext.getExternalContext()); } return _runtimeConfig; } }