Skip to content

Commit

Permalink
Generics
Browse files Browse the repository at this point in the history
Cosmetics
LRUCache
ELUtils optimizations
EmptyStringToNullELResolver only if Tomcat is detected

Signed-off-by: pizzi80 <[email protected]>
  • Loading branch information
pizzi80 committed Dec 19, 2022
1 parent a983e27 commit c3feaca
Show file tree
Hide file tree
Showing 9 changed files with 191 additions and 150 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class SystemEventHelper {

public SystemEventHelper() {

systemEventInfoCache = new Cache<>(arg -> new SystemEventInfo(arg));
systemEventInfoCache = new Cache<>(SystemEventInfo::new);

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1070,7 +1070,7 @@ private BeanInfo createComponentMetadata(FacesContext context, Resource ccResour
// PENDING this implementation is terribly wasteful.
// Must find a better way.
FaceletContext faceletContext = (FaceletContext) context.getAttributes().get(FACELET_CONTEXT_KEY);
DefaultFaceletFactory factory = (DefaultFaceletFactory) RequestStateManager.get(context, FACELET_FACTORY);
DefaultFaceletFactory factory = RequestStateManager.get(context, FACELET_FACTORY);
VariableMapper orig = faceletContext.getVariableMapper();

// Create tmp and facetComponent
Expand Down
159 changes: 56 additions & 103 deletions impl/src/main/java/com/sun/faces/el/ELUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,71 +15,58 @@
*/
package com.sun.faces.el;

import static com.sun.faces.RIConstants.EMPTY_CLASS_ARGS;
import static com.sun.faces.util.MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.getExceptionMessageString;
import static com.sun.faces.util.ReflectionUtils.lookupMethod;
import static com.sun.faces.util.ReflectionUtils.newInstance;
import static com.sun.faces.util.Util.getCdiBeanManager;
import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.config.WebConfiguration;
import com.sun.faces.context.flash.FlashELResolver;
import com.sun.faces.util.Cache;
import com.sun.faces.util.FacesLogger;
import com.sun.faces.util.LRUCache;
import jakarta.el.*;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.sun.faces.application.ApplicationAssociate;
import com.sun.faces.context.flash.FlashELResolver;

import jakarta.el.ArrayELResolver;
import jakarta.el.BeanELResolver;
import jakarta.el.CompositeELResolver;
import jakarta.el.ELContext;
import jakarta.el.ELResolver;
import jakarta.el.ExpressionFactory;
import jakarta.el.ListELResolver;
import jakarta.el.MapELResolver;
import jakarta.el.ResourceBundleELResolver;
import jakarta.el.ValueExpression;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.faces.context.ExternalContext;
import jakarta.faces.context.FacesContext;
import static com.sun.faces.RIConstants.EMPTY_CLASS_ARGS;
import static com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter.InterpretEmptyStringSubmittedValuesAsNull;
import static com.sun.faces.util.MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID;
import static com.sun.faces.util.MessageUtils.getExceptionMessageString;
import static com.sun.faces.util.ReflectionUtils.lookupMethod;
import static com.sun.faces.util.ReflectionUtils.newInstance;
import static com.sun.faces.util.Util.getCdiBeanManager;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;

/**
* Utility class for EL related methods.
*/
public class ELUtils {
public enum ELUtils { INSTANCE;

/**
* Private cache for storing evaluation results for composite components checks.
*/
private static final HashMap<String, Boolean> compositeComponentEvaluationCache = new HashMap<String, Boolean>();
// Log instance for this class
private static final Logger LOGGER = FacesLogger.UTIL.getLogger();

/**
* The maximum size of the <code>compositeComponentEvaluationCache</code>.
*/
private static final int compositeComponentEvaluationCacheMaxSize = 1000;

/**
* FIFO queue, holding access information about the <code>compositeComponentEvaluationCache</code>.
*/
private static final LinkedList<String> evaluationCacheFifoQueue = new LinkedList<String>();

/**
* Class member, indicating a <I>positive</I> evaluation result.
*/
private static final Boolean IS_COMPOSITE_COMPONENT = Boolean.TRUE;

/**
* Class member, indicating a <I>negative</I> evaluation result.
* Helps to determine if a EL expression represents a composite component EL expression.
*/
private static final Boolean IS_NOT_A_COMPOSITE_COMPONENT = Boolean.FALSE;
private static final Pattern COMPOSITE_COMPONENT_EXPRESSION = Pattern.compile(".(?:[ ]+|[\\[{,(])cc[.].+[}]");
private static final Matcher COMPOSITE_COMPONENT_EXPRESSION_MATCHER = COMPOSITE_COMPONENT_EXPRESSION.matcher("");
private static final Cache.Factory<String,Boolean> isCompositeExpressionInit = expression -> expression == null ? FALSE : COMPOSITE_COMPONENT_EXPRESSION_MATCHER.reset(expression).find() ? TRUE : FALSE;

/**
* Helps to determine if a EL expression represents a composite component EL expression.
* Private cache for storing evaluation results for composite components checks.
*/
private static final Pattern COMPOSITE_COMPONENT_EXPRESSION = Pattern.compile(".(?:[ ]+|[\\[{,(])cc[.].+[}]");
private static final LRUCache<String, Boolean> compositeComponentEvaluationCache = new LRUCache<>(isCompositeExpressionInit, compositeComponentEvaluationCacheMaxSize);

/**
* Used to determine if EL method arguments are being passed to a composite component lookup expression.
Expand All @@ -94,13 +81,13 @@ public class ELUtils {
*
* is legal.
*/
private static final Pattern COMPOSITE_COMPONENT_LOOKUP_WITH_ARGS = Pattern.compile("(?:[ ]+|[\\[{,(])cc[.]attrs[.]\\w+[(].+[)]");
private static final Matcher COMPOSITE_COMPONENT_LOOKUP_WITH_ARGS_MATCHER = Pattern.compile("(?:[ ]+|[\\[{,(])cc[.]attrs[.]\\w+[(].+[)]").matcher("");

/**
* Use to determine if an expression being considered as a MethodExpression is a simple lookup (i.e.
* #{cc.attrs.myaction}).
*/
private static final Pattern METHOD_EXPRESSION_LOOKUP = Pattern.compile(".[{]cc[.]attrs[.]\\w+[}]");
private static final Matcher METHOD_EXPRESSION_LOOKUP_MATCHER = Pattern.compile(".[{]cc[.]attrs[.]\\w+[}]").matcher("");

public static final ArrayELResolver ARRAY_RESOLVER = new ArrayELResolver();
public static final BeanELResolver BEAN_RESOLVER = new BeanELResolver();
Expand All @@ -112,43 +99,30 @@ public class ELUtils {
public static final ScopedAttributeELResolver SCOPED_RESOLVER = new ScopedAttributeELResolver();
public static final ResourceELResolver RESOURCE_RESOLVER = new ResourceELResolver();
public static final CompositeComponentAttributesELResolver COMPOSITE_COMPONENT_ATTRIBUTES_EL_RESOLVER = new CompositeComponentAttributesELResolver();

// ------------------------------------------------------------ Constructors

private ELUtils() {
throw new IllegalStateException();
}
public static final EmptyStringToNullELResolver EMPTY_STRING_TO_NULL_RESOLVER = new EmptyStringToNullELResolver();

// ---------------------------------------------------------- Public Methods

public static boolean isCompositeComponentExpr(String expression) {
Boolean evaluationResult = compositeComponentEvaluationCache.get(expression);

if (evaluationResult != null) {
// fast path - this expression has already been evaluated, therefore return its evaluation result
return evaluationResult.booleanValue();
}

// TODO we should be trying to re-use the Matcher by calling
// m.reset(expression);
boolean returnValue = COMPOSITE_COMPONENT_EXPRESSION
.matcher(expression)
.find();

// remember the evaluation result for this expression
rememberEvaluationResult(expression, returnValue);

return returnValue;
return compositeComponentEvaluationCache.get(expression);
}

public static boolean isCompositeComponentMethodExprLookup(String expression) {
return METHOD_EXPRESSION_LOOKUP.matcher(expression).matches();
return METHOD_EXPRESSION_LOOKUP_MATCHER.reset(expression).matches();
}

public static boolean isCompositeComponentLookupWithArgs(String expression) {
// TODO we should be trying to re-use the Matcher by calling
// m.reset(expression);
return COMPOSITE_COMPONENT_LOOKUP_WITH_ARGS.matcher(expression).find();
return COMPOSITE_COMPONENT_LOOKUP_WITH_ARGS_MATCHER.reset(expression).find();
}


// TODO: move to Jakarta Utils
public static Class<?> classOf( String className ) {
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
return null;
}
}

/**
Expand All @@ -166,6 +140,15 @@ public static void buildFacesResolver(FacesCompositeELResolver composite, Applic
composite.addPropertyELResolver(COMPOSITE_COMPONENT_ATTRIBUTES_EL_RESOLVER);
addELResolvers(composite, associate.getELResolversFromFacesConfig());
composite.add(associate.getApplicationELResolvers());

// Tomcat detected: install the EmptyStringToNullELResolver
if ( WebConfiguration.getInstance().isOptionEnabled(InterpretEmptyStringSubmittedValuesAsNull) &&
classOf(EmptyStringToNullELResolver.TOMCAT_EL_CONTEXT_STRING_CLASS) != null
) {
LOGGER.info("Tomcat detected: install the EmptyStringToNullELResolver");
composite.addPropertyELResolver(EMPTY_STRING_TO_NULL_RESOLVER);
}

composite.addPropertyELResolver(RESOURCE_RESOLVER);
composite.addPropertyELResolver(BUNDLE_RESOLVER);
composite.addRootELResolver(FACES_BUNDLE_RESOLVER);
Expand Down Expand Up @@ -245,37 +228,6 @@ public static Object coerce(Object value, Class<?> toType) {

// --------------------------------------------------------- Private Methods

/**
* Adds the specified <code>expression</code> with its evaluation result <code>isCompositeComponent</code> to the <code>compositeComponentEvaluationCache</code>,
* taking into account the maximum cache size.
*/
private static void rememberEvaluationResult(String expression, boolean isCompositeComponent) {
// validity check
if (compositeComponentEvaluationCacheMaxSize <= 0) {
return;
}

synchronized (compositeComponentEvaluationCache) {
if (compositeComponentEvaluationCache.size() >= compositeComponentEvaluationCacheMaxSize) {
// obtain the oldest cached element
String oldestExpression = evaluationCacheFifoQueue.removeFirst();

// remove the mapping for this element
compositeComponentEvaluationCache.remove(oldestExpression);
}

// add the mapping to the cache
if (isCompositeComponent) {
compositeComponentEvaluationCache.put(expression, IS_COMPOSITE_COMPONENT);
} else {
compositeComponentEvaluationCache.put(expression, IS_NOT_A_COMPOSITE_COMPONENT);
}

// remember the sequence of the hash map "put" operations
evaluationCacheFifoQueue.add(expression);
}
}

/**
* <p>
* Add the <code>ELResolvers</code> from the provided list to the target <code>CompositeELResolver</code>.
Expand Down Expand Up @@ -317,4 +269,5 @@ public static ExpressionFactory getDefaultExpressionFactory(ApplicationAssociate

return associate.getExpressionFactory();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright (c) 1997, 2022 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.el;

import jakarta.el.ELContext;
import jakarta.el.ELResolver;

public class EmptyStringToNullELResolver extends ELResolver {

public static final String TOMCAT_EL_CONTEXT_STRING_CLASS = "org.apache.el.lang.EvaluationContext";

@Override
public Class<?> getCommonPropertyType(ELContext context, Object base) {
return String.class;
}

@Override
@SuppressWarnings("unchecked")
public <T> T convertToType(ELContext context, Object value, Class<T> targetType) {
if (value == null && targetType == String.class && context.getClass().getName().equals(TOMCAT_EL_CONTEXT_STRING_CLASS) ) {
context.setPropertyResolved(true);
}

return (T) value;
}

@Override
public Class<?> getType(ELContext context, Object base, Object property) {
return null;
}

@Override
public Object getValue(ELContext context, Object base, Object property) {
return null;
}

@Override
public boolean isReadOnly(ELContext context, Object base, Object property) {
return true;
}

@Override
public void setValue(ELContext context, Object base, Object property, Object value) {
// NOOP.
}

}
31 changes: 15 additions & 16 deletions impl/src/main/java/com/sun/faces/facelets/impl/DefaultFacelet.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;

import com.sun.faces.facelets.tag.faces.ComponentSupport;
import com.sun.faces.util.FacesLogger;
Expand Down Expand Up @@ -144,10 +145,10 @@ private void refresh(UIComponent c) {
// finally remove any children marked as deleted
int sz = c.getChildCount();
if (sz > 0) {
List cl = c.getChildren();
List<UIComponent> cl = c.getChildren();
ApplyToken token;
while (--sz >= 0) {
UIComponent cc = (UIComponent) cl.get(sz);
UIComponent cc = cl.get(sz);
if (!cc.isTransient()) {
token = (ApplyToken) cc.getAttributes().get(APPLIED_KEY);
if (token != null && token.time < createTime && token.alias.equals(alias)) {
Expand All @@ -164,13 +165,11 @@ private void refresh(UIComponent c) {

// remove any facets marked as deleted
if (c.getFacets().size() > 0) {
Collection col = c.getFacets().values();
UIComponent fc;
ApplyToken token;
for (Iterator itr = col.iterator(); itr.hasNext();) {
fc = (UIComponent) itr.next();
Iterator<UIComponent> itr = c.getFacets().values().iterator();
while ( itr.hasNext() ) {
UIComponent fc = itr.next();
if (!fc.isTransient()) {
token = (ApplyToken) fc.getAttributes().get(APPLIED_KEY);
ApplyToken token = (ApplyToken) fc.getAttributes().get(APPLIED_KEY);
if (token != null && token.time < createTime && token.alias.equals(alias)) {
if (log.isLoggable(Level.INFO)) {
DateFormat df = SimpleDateFormat.getTimeInstance();
Expand All @@ -187,15 +186,15 @@ private void refresh(UIComponent c) {

private void markApplied(UIComponent parent) {
if (refreshPeriod > 0) {
Iterator itr = parent.getFacetsAndChildren();
ApplyToken token = new ApplyToken(alias, System.currentTimeMillis() + refreshPeriod);
final ApplyToken token = new ApplyToken(alias, System.currentTimeMillis() + refreshPeriod);
Iterator<UIComponent> itr = parent.getFacetsAndChildren();
while (itr.hasNext()) {
UIComponent c = (UIComponent) itr.next();
if (!c.isTransient()) {
Map<String, Object> attr = c.getAttributes();
if (!attr.containsKey(APPLIED_KEY)) {
attr.put(APPLIED_KEY, token);
}
UIComponent c = itr.next();
if ( !c.isTransient() ) {
//Map<String, Object> attr = c.getAttributes();
//if ( !attr.containsKey(APPLIED_KEY) ) {
c.getAttributes().putIfAbsent(APPLIED_KEY, token);
//}
}
}
}
Expand Down
Loading

0 comments on commit c3feaca

Please sign in to comment.