You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2009/10/27 18:11:45 UTC
svn commit: r830253 - in /myfaces/core/trunk/impl/src:
main/java/org/apache/myfaces/config/
main/java/org/apache/myfaces/config/element/
main/java/org/apache/myfaces/config/impl/digester/elements/
main/java/org/apache/myfaces/el/unified/resolver/ test/...
Author: lu4242
Date: Tue Oct 27 17:11:44 2009
New Revision: 830253
URL: http://svn.apache.org/viewvc?rev=830253&view=rev
Log:
MYFACES-2375 <managed-bean-scope> could also be an EL expression, to allow easy implementation of new scopes (Thanks to Jakob Korherr for provide this patch)
Added:
myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/config/ManagedBeanBuilderTest.java
myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolverTest.java
Modified:
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanBuilder.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/element/ManagedBean.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/ManagedBean.java
myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolver.java
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanBuilder.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanBuilder.java?rev=830253&r1=830252&r2=830253&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanBuilder.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/ManagedBeanBuilder.java Tue Oct 27 17:11:44 2009
@@ -21,7 +21,7 @@
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
-import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -36,6 +36,7 @@
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;
@@ -69,6 +70,65 @@
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 SESSION APPLICATION NONE
+ * @author Jakob Korherr
+ */
+ private final static Comparator<String> scopeComparator = new Comparator<String>()
+ {
+
+ @Override
+ 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))
+ {
+ // session 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
@@ -93,8 +153,7 @@
case ManagedBean.INIT_MODE_PROPERTIES:
try
{
- initializeProperties(facesContext, beanConfiguration.getManagedProperties(),
- beanConfiguration.getManagedBeanScope(), bean);
+ initializeProperties(facesContext, beanConfiguration, bean);
}
catch (IllegalArgumentException e)
{
@@ -173,13 +232,12 @@
@SuppressWarnings("unchecked")
private void initializeProperties(FacesContext facesContext,
- Collection<? extends ManagedProperty> managedProperties,
- String targetScope, Object bean)
+ ManagedBean beanConfiguration, Object bean)
{
ELResolver elResolver = facesContext.getApplication().getELResolver();
ELContext elContext = facesContext.getELContext();
- for (ManagedProperty property : managedProperties)
+ for (ManagedProperty property : beanConfiguration.getManagedProperties())
{
Object value = null;
@@ -248,10 +306,11 @@
break;
case ManagedProperty.TYPE_VALUE:
// check for correct scope of a referenced bean
- if (!isInValidScope(facesContext, property, targetScope))
+ if (!isInValidScope(facesContext, property, beanConfiguration))
{
throw new FacesException("Property " + property.getPropertyName() +
- " references object in a scope with shorter lifetime than the target scope " + targetScope);
+ " references object in a scope with shorter lifetime than the target scope " +
+ beanConfiguration.getManagedBeanScope());
}
value = property.getRuntimeValue(facesContext);
break;
@@ -303,78 +362,91 @@
/**
- * Check if the scope of the property value is valid for a bean to be stored in targetScope.
- *
+ * 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 targetScope name of the target scope of the bean under construction
+ * @param property the property to be checked
+ * @param beanConfiguration the ManagedBean, which will be created
*/
- private boolean isInValidScope(FacesContext facesContext, ManagedProperty property, String targetScope)
+ private boolean isInValidScope(FacesContext facesContext, ManagedProperty property, ManagedBean beanConfiguration)
{
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)
+
+ // 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.getApplication().getProjectStage() == ProjectStage.Production)
{
- continue;
+ return true;
}
-
- String valueScope = getScope(facesContext, expression);
-
- // if the target scope is 'none' value scope has to be 'none', too
- if (targetScope == null || targetScope.equalsIgnoreCase(NONE))
+ else
{
- if (valueScope != null && !(valueScope.equalsIgnoreCase(NONE)))
- {
- return false;
- }
- return true;
+ targetScope = getNarrowestScope(facesContext,
+ beanConfiguration
+ .getManagedBeanScopeValueExpression(facesContext)
+ .getExpressionString());
}
-
- // 'application' scope can reference 'application' and 'none'
- if (targetScope.equalsIgnoreCase(APPLICATION))
+ }
+ else
+ {
+ targetScope = beanConfiguration.getManagedBeanScope();
+ if (targetScope == null)
{
- if (valueScope != null)
- {
- if (valueScope.equalsIgnoreCase(REQUEST) ||
- valueScope.equalsIgnoreCase(SESSION))
- {
- return false;
- }
- }
- return true;
+ targetScope = NONE;
}
+ }
+
+ // optimization: 'request' scope can reference any value scope
+ if (targetScope.equalsIgnoreCase(REQUEST))
+ {
+ return true;
+ }
+
+ String valueScope = getNarrowestScope(facesContext,
+ property.getValueBinding(facesContext)
+ .getExpressionString());
+
+ // the target scope needs to have a shorter (or equal) lifetime than the value scope
+ return (scopeComparator.compare(targetScope, valueScope) <= 0);
+ }
- // 'session' scope can reference 'session', 'application', and 'none' but not 'request'
- if (targetScope.equalsIgnoreCase(SESSION))
+ /**
+ * 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);
+ String narrowestScope = NONE;
+
+ for (String expression : expressions)
+ {
+ String valueScope = getScope(facesContext, expression);
+ if (valueScope == null)
{
- if (valueScope != null)
- {
- if (valueScope.equalsIgnoreCase(REQUEST))
- {
- return false;
- }
- }
- return true;
+ continue;
}
-
- // 'request' scope can reference any value scope
- if (targetScope.equalsIgnoreCase(REQUEST))
+ if (scopeComparator.compare(valueScope, narrowestScope) < 0)
{
- return true;
+ narrowestScope = valueScope;
}
}
- return false;
+
+ return narrowestScope;
}
-
-
+
private String getScope(FacesContext facesContext, String expression)
{
String beanName = getFirstSegment(expression);
@@ -450,7 +522,29 @@
if (mbc != null)
{
- return mbc.getManagedBeanScope();
+ // 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.getApplication().getProjectStage() == ProjectStage.Production)
+ {
+ // we return NONE, because the NONE scope can be referenced by any other scope
+ return NONE;
+ }
+ else
+ {
+ return getNarrowestScope(facesContext,
+ mbc.getManagedBeanScopeValueExpression(facesContext)
+ .getExpressionString());
+ }
+ }
+ else
+ {
+ return mbc.getManagedBeanScope();
+ }
}
return null;
@@ -483,20 +577,15 @@
}
- private String[] extractExpressions(String expressionString)
+ private List<String> extractExpressions(String expressionString)
{
- String[] expressions = expressionString.split("\\#\\{");
- for (int i = 0; i < expressions.length; i++)
+ List<String> expressions = new ArrayList<String>();
+ for (String expression : expressionString.split("\\#\\{"))
{
- String expression = expressions[i];
- if (expression.trim().length() == 0)
- {
- expressions[i] = null;
- }
- else
+ int index = expression.indexOf('}');
+ if (index >= 0)
{
- int index = expression.indexOf('}');
- expressions[i] = expression.substring(0, index);
+ expressions.add(expression.substring(0, index));
}
}
return expressions;
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/element/ManagedBean.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/element/ManagedBean.java?rev=830253&r1=830252&r2=830253&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/element/ManagedBean.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/element/ManagedBean.java Tue Oct 27 17:11:44 2009
@@ -20,6 +20,9 @@
import java.util.Collection;
+import javax.el.ValueExpression;
+import javax.faces.context.FacesContext;
+
/**
* @author Manfred Geiler (latest modification by $Author$)
* @version $Revision$ $Date$
@@ -38,7 +41,7 @@
public String getManagedBeanClassName();
public Class<?> getManagedBeanClass();
public String getManagedBeanScope();
-
+
public int getInitMode();
/**
@@ -49,4 +52,21 @@
public MapEntries getMapEntries();
public ListEntries getListEntries();
+
+ /**
+ * Is the value of managed-bean-scope a EL ValueExpression?
+ * @since 2.0
+ * @return
+ */
+ public boolean isManagedBeanScopeValueExpression();
+
+ /**
+ * Returns the ValueExpression for managed-bean-scope
+ * or null, if managed-bean-scope is literal.
+ * @param facesContext
+ * @since 2.0
+ * @return
+ */
+ public ValueExpression getManagedBeanScopeValueExpression(FacesContext facesContext);
+
}
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/ManagedBean.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/ManagedBean.java?rev=830253&r1=830252&r2=830253&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/ManagedBean.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/config/impl/digester/elements/ManagedBean.java Tue Oct 27 17:11:44 2009
@@ -22,7 +22,11 @@
import java.util.Collection;
import java.util.List;
+import javax.el.ValueExpression;
+import javax.faces.context.FacesContext;
+
import org.apache.myfaces.shared_impl.util.ClassUtils;
+import org.apache.myfaces.view.facelets.el.ELText;
/**
@@ -39,6 +43,7 @@
private List<ManagedProperty> property = new ArrayList<ManagedProperty>();
private MapEntries mapEntries;
private ListEntries listEntries;
+ private ValueExpression scopeValueExpression;
public int getInitMode()
@@ -151,4 +156,29 @@
{
return property;
}
+
+ @Override
+ public boolean isManagedBeanScopeValueExpression()
+ {
+ return (scope != null)
+ && (scopeValueExpression != null || !ELText.isLiteral(scope));
+ }
+
+ @Override
+ public ValueExpression getManagedBeanScopeValueExpression(FacesContext facesContext)
+ {
+ if (scopeValueExpression == null)
+ {
+ // we need to set the expected type to Object, because we have to generate a
+ // Exception text with the actual value and the actual type of the expression,
+ // if the expression does not resolve to java.util.Map
+ scopeValueExpression =
+ isManagedBeanScopeValueExpression()
+ ? facesContext.getApplication().getExpressionFactory()
+ .createValueExpression(facesContext.getELContext(), scope, Object.class)
+ : null;
+ }
+ return scopeValueExpression;
+ }
+
}
Modified: myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolver.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolver.java?rev=830253&r1=830252&r2=830253&view=diff
==============================================================================
--- myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolver.java (original)
+++ myfaces/core/trunk/impl/src/main/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolver.java Tue Oct 27 17:11:44 2009
@@ -32,6 +32,8 @@
import javax.el.ELResolver;
import javax.el.PropertyNotFoundException;
import javax.el.PropertyNotWritableException;
+import javax.faces.FacesException;
+import javax.faces.application.ProjectStage;
import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;
@@ -50,6 +52,8 @@
private static final Logger log = Logger.getLogger(ManagedBeanResolver.class.getName());
private static final String BEANS_UNDER_CONSTRUCTION =
"org.apache.myfaces.el.unified.resolver.managedbean.beansUnderConstruction";
+ private static final String CUSTOM_SCOPE_CYCLIC_REFERENCE_DETECTION =
+ "org.apache.myfaces.el.unified.resolver.managedbean.customScopeCyclicReferenceDetection";
// adapted from Manfred's JSF 1.1 VariableResolverImpl
protected static final Map<String, Scope> s_standardScopes = new HashMap<String, Scope>(16);
@@ -144,6 +148,7 @@
}
@Override
+ @SuppressWarnings("unchecked")
public Object getValue(final ELContext context, final Object base, final Object property)
throws NullPointerException, PropertyNotFoundException, ELException
{
@@ -178,7 +183,67 @@
{
FacesContext facesContext = facesContext(context);
context.setPropertyResolved(true);
- beanInstance = createManagedBean(managedBean, facesContext);
+
+ // managed-bean-scope could be a ValueExpression pointing to a Map (since 2.0)
+ if (managedBean.isManagedBeanScopeValueExpression())
+ {
+ // check for cyclic references in custom scopes, if we are not in Production stage
+ boolean checkCyclicReferences =
+ facesContext.getApplication().getProjectStage() != ProjectStage.Production;
+ List<String> cyclicReferences = null;
+
+ if (checkCyclicReferences)
+ {
+ final Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
+ final String managedBeanName = managedBean.getManagedBeanName();
+
+ cyclicReferences = (List<String>) requestMap.get(CUSTOM_SCOPE_CYCLIC_REFERENCE_DETECTION);
+ if (cyclicReferences == null)
+ {
+ cyclicReferences = new ArrayList<String>();
+ requestMap.put(CUSTOM_SCOPE_CYCLIC_REFERENCE_DETECTION, cyclicReferences);
+ }
+ else if (cyclicReferences.contains(managedBeanName))
+ {
+ throw new ELException("Detected cyclic reference to managedBean " + managedBeanName);
+ }
+
+ cyclicReferences.add(managedBeanName);
+ }
+ try
+ {
+ Object customScope = managedBean.getManagedBeanScopeValueExpression(facesContext)
+ .getValue(facesContext.getELContext());
+ if (customScope instanceof Map)
+ {
+ beanInstance = ((Map) customScope).get(managedBean.getManagedBeanName());
+ }
+ else if (customScope != null)
+ {
+ throw new FacesException("The expression '" + managedBean.getManagedBeanScope() +
+ "' does not evaluate to java.util.Map. It evaluates to '" + customScope +
+ "' of type " + customScope.getClass().getName());
+ }
+ else
+ {
+ log.warning("Custom scope '" + managedBean.getManagedBeanScope() +
+ "' evaluated to null. Unable to determine if managed bean '" +
+ managedBean.getManagedBeanName() + "' exists.");
+ }
+ }
+ finally
+ {
+ if (checkCyclicReferences)
+ {
+ cyclicReferences.remove(managedBean.getManagedBeanName());
+ }
+ }
+ }
+
+ if (beanInstance == null)
+ {
+ beanInstance = createManagedBean(managedBean, facesContext);
+ }
}
return beanInstance;
@@ -195,6 +260,7 @@
final ExternalContext extContext = facesContext.getExternalContext();
final Map<String, Object> requestMap = extContext.getRequestMap();
+ final String managedBeanName = managedBean.getManagedBeanName();
// check for cyclic references
List<String> beansUnderConstruction = (List<String>)requestMap.get(BEANS_UNDER_CONSTRUCTION);
@@ -203,9 +269,7 @@
beansUnderConstruction = new ArrayList<String>();
requestMap.put(BEANS_UNDER_CONSTRUCTION, beansUnderConstruction);
}
-
- final String managedBeanName = managedBean.getManagedBeanName();
- if (beansUnderConstruction.contains(managedBeanName))
+ else if (beansUnderConstruction.contains(managedBeanName))
{
throw new ELException("Detected cyclic reference to managedBean " + managedBeanName);
}
@@ -222,12 +286,14 @@
beansUnderConstruction.remove(managedBeanName);
}
- putInScope(managedBean, extContext, obj);
+ putInScope(managedBean, facesContext, extContext, obj);
return obj;
}
- private void putInScope(final ManagedBean managedBean, final ExternalContext extContext, final Object obj)
+ @SuppressWarnings("unchecked")
+ private void putInScope(final ManagedBean managedBean, final FacesContext facesContext,
+ final ExternalContext extContext, final Object obj)
{
final String managedBeanName = managedBean.getManagedBeanName();
@@ -239,18 +305,41 @@
}
else
{
-
final String scopeKey = managedBean.getManagedBeanScope();
// find the scope handler object
final Scope scope = _scopes.get(scopeKey);
- if (scope == null)
+ if (scope != null)
{
- log.severe("Managed bean '" + managedBeanName + "' has illegal scope: " + scopeKey);
+ scope.put(extContext, managedBeanName, obj);
+ }
+ else if (managedBean.isManagedBeanScopeValueExpression())
+ {
+ // managed-bean-scope could be a ValueExpression pointing to a Map (since 2.0)
+ // Optimisation: We do NOT check for cyclic references here, because when we reach this code,
+ // we have already checked for cyclic references in the custom scope
+ Object customScope = managedBean
+ .getManagedBeanScopeValueExpression(facesContext)
+ .getValue(facesContext.getELContext());
+ if (customScope instanceof Map)
+ {
+ ((Map) customScope).put(managedBeanName, obj);
+ }
+ else if (customScope != null)
+ {
+ throw new FacesException("The expression '" + scopeKey + "' does not evaluate to " +
+ "java.util.Map. It evaluates to '" + customScope + "' of type " +
+ customScope.getClass().getName());
+ }
+ else
+ {
+ log.warning("Custom scope '" + scopeKey + "' evaluated to null. " +
+ "Cannot store managed bean '" + managedBeanName + "' in custom scope.");
+ }
}
else
{
- scope.put(extContext, managedBeanName, obj);
+ log.severe("Managed bean '" + managedBeanName + "' has illegal scope: " + scopeKey);
}
}
Added: myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/config/ManagedBeanBuilderTest.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/config/ManagedBeanBuilderTest.java?rev=830253&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/config/ManagedBeanBuilderTest.java (added)
+++ myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/config/ManagedBeanBuilderTest.java Tue Oct 27 17:11:44 2009
@@ -0,0 +1,199 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+
+import javax.faces.FacesException;
+import javax.faces.application.ProjectStage;
+
+import org.apache.myfaces.config.impl.digester.elements.ManagedBean;
+import org.apache.myfaces.config.impl.digester.elements.ManagedProperty;
+import org.apache.myfaces.el.unified.resolver.ManagedBeanResolver;
+import org.apache.shale.test.base.AbstractJsfTestCase;
+import org.apache.shale.test.el.MockValueExpression;
+import org.apache.shale.test.mock.MockApplication20;
+
+/**
+ * Class used to test ManagedBeanBuilder
+ * @author Jakob Korherr (latest modification by $Author$)
+ */
+public class ManagedBeanBuilderTest extends AbstractJsfTestCase
+{
+
+ /**
+ * A managed bean used in the test cases
+ * @author Jakob Korherr
+ */
+ public static class TestBean {
+
+ private Map<Object, Object> scope;
+ private TestBean anotherBean;
+
+ public Map<Object, Object> getScope()
+ {
+ if (scope == null)
+ {
+ scope = new HashMap<Object, Object>();
+ }
+ return scope;
+ }
+
+ public void setScope(Map<Object, Object> scope)
+ {
+ this.scope = scope;
+ }
+
+ public TestBean getAnotherBean()
+ {
+ return anotherBean;
+ }
+
+ public void setAnotherBean(TestBean anotherBean)
+ {
+ this.anotherBean = anotherBean;
+ }
+
+ }
+
+ private RuntimeConfig runtimeConfig;
+
+ public ManagedBeanBuilderTest(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ // override MockApplication20 to get a ProjectStage
+ application = new MockApplication20() {
+
+ @Override
+ public ProjectStage getProjectStage()
+ {
+ return ProjectStage.Development;
+ }
+
+ };
+ // add the ManagedBeanResolver as a ELResolver
+ ManagedBeanResolver resolver = new ManagedBeanResolver();
+ application.addELResolver(resolver);
+ facesContext.setApplication(application);
+
+ runtimeConfig = RuntimeConfig.getCurrentInstance(externalContext);
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ runtimeConfig = null;
+
+ super.tearDown();
+ }
+
+
+ /**
+ * Tests, if the ManagedBeanBuilder checks that no property of the managed bean
+ * references to a scope with a potentially shorter lifetime.
+ * E.g. a managed bean of scope session is only allowed to reference an object in
+ * the session, the application and the none scope.
+ */
+ public void testIsInValidScope()
+ {
+ // create sessionBean referencing requestBean
+ ManagedBean sessionBean = new ManagedBean();
+ sessionBean.setBeanClass(TestBean.class.getName());
+ sessionBean.setName("sessionBean");
+ sessionBean.setScope("session");
+ ManagedProperty anotherBeanProperty = new ManagedProperty();
+ anotherBeanProperty.setPropertyName("anotherBean");
+ anotherBeanProperty.setValue("#{requestBean}");
+ sessionBean.addProperty(anotherBeanProperty);
+ runtimeConfig.addManagedBean("sessionBean", sessionBean);
+
+ // create requestBean
+ ManagedBean requestBean = new ManagedBean();
+ requestBean.setBeanClass(TestBean.class.getName());
+ requestBean.setName("requestBean");
+ requestBean.setScope("request");
+ runtimeConfig.addManagedBean("requestBean", requestBean);
+
+ try
+ {
+ new MockValueExpression("#{sessionBean}", TestBean.class).getValue(facesContext.getELContext());
+ }
+ catch (FacesException e)
+ {
+ // success --> the ManagedBeanBuilder discovered the reference to a shorter lifetime
+ return;
+ }
+ fail();
+ }
+
+ /**
+ * Tests the same as testIsInValidScope, but this time the managed bean
+ * has a custom scope.
+ * The spec says, that if a managed bean has a custom scope, the runtime
+ * is not required to check the references. However, MyFaces checks the
+ * references if the ProjectStage is not Production.
+ */
+ public void testIsInValidScopeWithCustomScopes()
+ {
+ // create scopeBean
+ ManagedBean scopeBean = new ManagedBean();
+ scopeBean.setBeanClass(TestBean.class.getName());
+ scopeBean.setName("scopeBean");
+ scopeBean.setScope("session");
+ runtimeConfig.addManagedBean("scopeBean", scopeBean);
+
+ // create sessionBean referencing requestBean
+ ManagedBean sessionBean = new ManagedBean();
+ sessionBean.setBeanClass(TestBean.class.getName());
+ sessionBean.setName("sessionBean");
+ sessionBean.setScope("#{scopeBean.scope}");
+ ManagedProperty anotherBeanProperty = new ManagedProperty();
+ anotherBeanProperty.setPropertyName("anotherBean");
+ anotherBeanProperty.setValue("#{requestBean}");
+ sessionBean.addProperty(anotherBeanProperty);
+ runtimeConfig.addManagedBean("sessionBean", sessionBean);
+
+ // create requestBean
+ ManagedBean requestBean = new ManagedBean();
+ requestBean.setBeanClass(TestBean.class.getName());
+ requestBean.setName("requestBean");
+ requestBean.setScope("request");
+ runtimeConfig.addManagedBean("requestBean", requestBean);
+
+ try
+ {
+ new MockValueExpression("#{sessionBean}", TestBean.class).getValue(facesContext.getELContext());
+ }
+ catch (FacesException e)
+ {
+ // success --> the ManagedBeanBuilder discovered the reference to a shorter lifetime
+ return;
+ }
+ fail();
+ }
+
+}
Added: myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolverTest.java
URL: http://svn.apache.org/viewvc/myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolverTest.java?rev=830253&view=auto
==============================================================================
--- myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolverTest.java (added)
+++ myfaces/core/trunk/impl/src/test/java/org/apache/myfaces/el/unified/resolver/ManagedBeanResolverTest.java Tue Oct 27 17:11:44 2009
@@ -0,0 +1,219 @@
+/*
+ * 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.el.unified.resolver;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.el.ELException;
+import javax.faces.FacesException;
+import javax.faces.application.ProjectStage;
+
+import org.apache.myfaces.config.RuntimeConfig;
+import org.apache.myfaces.config.impl.digester.elements.ManagedBean;
+import org.apache.shale.test.base.AbstractJsfTestCase;
+import org.apache.shale.test.el.MockValueExpression;
+import org.apache.shale.test.mock.MockApplication20;
+
+/**
+ * Class used to test ManagedBeanResolver
+ * @author Jakob Korherr (latest modification by $Author$)
+ */
+public class ManagedBeanResolverTest extends AbstractJsfTestCase
+{
+
+ /**
+ * A managed bean used in the test cases
+ * @author Jakob Korherr
+ */
+ public static class TestBean {
+
+ private Map<Object, Object> scope;
+
+ public Map<Object, Object> getScope()
+ {
+ if (scope == null)
+ {
+ scope = new HashMap<Object, Object>();
+ }
+ return scope;
+ }
+
+ public void setScope(Map<Object, Object> scope)
+ {
+ this.scope = scope;
+ }
+
+ }
+
+ private RuntimeConfig runtimeConfig;
+
+ public ManagedBeanResolverTest(String name)
+ {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ // override MockApplication20 to get a ProjectStage
+ application = new MockApplication20() {
+
+ @Override
+ public ProjectStage getProjectStage()
+ {
+ return ProjectStage.Development;
+ }
+
+ };
+ // add the ManagedBeanResolver as a ELResolver
+ ManagedBeanResolver resolver = new ManagedBeanResolver();
+ application.addELResolver(resolver);
+ facesContext.setApplication(application);
+
+ runtimeConfig = RuntimeConfig.getCurrentInstance(externalContext);
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ runtimeConfig = null;
+
+ super.tearDown();
+ }
+
+ /**
+ * Tests if the ManagedBeanResolver handles custom scopes correctly
+ */
+ @SuppressWarnings("unchecked")
+ public void testCustomScope()
+ {
+ // create the custom scope
+ ManagedBean scopeBean = new ManagedBean();
+ scopeBean.setBeanClass(HashMap.class.getName());
+ scopeBean.setName("scopeBean");
+ scopeBean.setScope("application");
+ runtimeConfig.addManagedBean("scopeBean", scopeBean);
+
+ // create the managed bean
+ ManagedBean beanInCustomScope = new ManagedBean();
+ beanInCustomScope.setBeanClass(ArrayList.class.getName());
+ beanInCustomScope.setName("beanInCustomScope");
+ beanInCustomScope.setScope("#{scopeBean}");
+ runtimeConfig.addManagedBean("beanInCustomScope", beanInCustomScope);
+
+ // resolve the managed bean
+ Object resolvedBeanInCustomScope = new MockValueExpression("#{beanInCustomScope}", List.class)
+ .getValue(facesContext.getELContext());
+
+ // get the custom scope
+ Map resolvedScopeBean = (Map) new MockValueExpression("#{scopeBean}", Map.class)
+ .getValue(facesContext.getELContext());
+
+ // the custom scope has to contain the resolved bean
+ assertTrue(resolvedScopeBean.containsKey("beanInCustomScope"));
+ assertTrue(resolvedScopeBean.get("beanInCustomScope").equals(resolvedBeanInCustomScope));
+ }
+
+ /**
+ * Tests if the ManagedBeanResolver throws the right Exception, if a custom scope
+ * does not evaluate to java.util.Map.
+ * Spec says: If the ValueExpression does not evaluate to a Map, a FacesException
+ * must be thrown with a message that includes the expression string, the toString()
+ * of the value, and the type of the value.
+ */
+ public void testCustomScopeNoMap()
+ {
+ // create the custom scope
+ ManagedBean scopeBean = new ManagedBean();
+ // Scope is ArrayList instead of HashMap
+ scopeBean.setBeanClass(ArrayList.class.getName());
+ scopeBean.setName("scopeBean");
+ scopeBean.setScope("application");
+ runtimeConfig.addManagedBean("scopeBean", scopeBean);
+
+ // create the managed bean
+ ManagedBean beanInCustomScope = new ManagedBean();
+ beanInCustomScope.setBeanClass(ArrayList.class.getName());
+ beanInCustomScope.setName("beanInCustomScope");
+ beanInCustomScope.setScope("#{scopeBean}");
+ runtimeConfig.addManagedBean("beanInCustomScope", beanInCustomScope);
+
+ // resolve the managed bean
+ try
+ {
+ new MockValueExpression("#{beanInCustomScope}", List.class)
+ .getValue(facesContext.getELContext());
+ }
+ catch (FacesException fe)
+ {
+ // the message must contain ...
+ final String message = fe.getMessage();
+ // ... the expression string
+ assertTrue(message.contains(beanInCustomScope.getManagedBeanScope()));
+ Object resolvedScopeBean = new MockValueExpression("#{scopeBean}", List.class)
+ .getValue(facesContext.getELContext());
+ // ... the toString() of the value
+ assertTrue(message.contains(resolvedScopeBean.toString()));
+ // ... and the type of the value
+ assertTrue(message.contains(resolvedScopeBean.getClass().getName()));
+ return;
+ }
+ // No FacesException was thrown
+ fail();
+ }
+
+ /**
+ * Tests if the ManagedBeanResolver detects cyclic references in custom scopes.
+ * The ManagedBeanResolver only tries to detect cyclic references if ProjectStage != Production.
+ */
+ public void testCustomScopeCyclicReferences()
+ {
+ // create m1
+ ManagedBean m1 = new ManagedBean();
+ m1.setBeanClass(TestBean.class.getName());
+ m1.setName("m1");
+ m1.setScope("#{m2.scope}");
+ runtimeConfig.addManagedBean("m1", m1);
+
+ // create m2
+ ManagedBean m2 = new ManagedBean();
+ m2.setBeanClass(TestBean.class.getName());
+ m2.setName("m2");
+ m2.setScope("#{m1.scope}");
+ runtimeConfig.addManagedBean("m2", m2);
+
+ // try to resolve m1
+ try
+ {
+ new MockValueExpression("#{m1}", TestBean.class).getValue(facesContext.getELContext());
+ }
+ catch (ELException e)
+ {
+ // success
+ return;
+ }
+ fail();
+ }
+
+}