From 83f3ac208de0f74a5f05d4d7fd08e9d3ab5460ce Mon Sep 17 00:00:00 2001 From: tandraschko Date: Wed, 1 Jan 2025 16:12:48 +0100 Subject: [PATCH] MYFACES-4704 --- .../component/_ComponentAttributesMap.java | 3 +- .../shared/lang/PropertyDescriptorUtils.java | 192 +++++++----------- .../myfaces/application/ApplicationImpl.java | 1 + .../myfaces/el/DefaultELResolverBuilder.java | 2 +- .../el/resolver/LambdaBeanELResolver.java | 1 + .../tag/LambdaMetadataTargetImpl.java | 1 + .../view/facelets/tag/MetaRulesetImpl.java | 2 +- .../composite/CompositeMetaRulesetImpl.java | 2 +- 8 files changed, 87 insertions(+), 117 deletions(-) diff --git a/api/src/main/java/jakarta/faces/component/_ComponentAttributesMap.java b/api/src/main/java/jakarta/faces/component/_ComponentAttributesMap.java index 80cf000518..53ff6b798b 100755 --- a/api/src/main/java/jakarta/faces/component/_ComponentAttributesMap.java +++ b/api/src/main/java/jakarta/faces/component/_ComponentAttributesMap.java @@ -618,7 +618,8 @@ private PropertyDescriptorWrapper getPropertyDescriptor(String key) if (_propertyDescriptorMap == null) { _propertyDescriptorMap = PropertyDescriptorUtils.getCachedPropertyDescriptors( - _component.getFacesContext().getExternalContext(), + _component.getFacesContext().getExternalContext(), + this.getClass(), _component.getClass()); } return _propertyDescriptorMap.get(key); diff --git a/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorUtils.java b/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorUtils.java index f1a20724e2..22eb59cfa2 100644 --- a/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorUtils.java +++ b/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorUtils.java @@ -18,6 +18,10 @@ */ package org.apache.myfaces.core.api.shared.lang; +import jakarta.faces.FacesException; +import jakarta.faces.context.ExternalContext; +import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam; + import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; @@ -28,9 +32,10 @@ import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Method; -import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.ObjDoubleConsumer; @@ -38,39 +43,25 @@ import java.util.function.ObjLongConsumer; import java.util.logging.Level; import java.util.logging.Logger; -import jakarta.faces.FacesException; -import jakarta.faces.context.ExternalContext; -import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam; public class PropertyDescriptorUtils { private static final Logger LOG = Logger.getLogger(PropertyDescriptorUtils.class.getName()); /** - * Defines if Lambda expressions (via LambdaMetafactory) are used for getter/setter instead of Reflection. + * Defines if Lambdas should be used over reflection whenever possible to access getter/setter. */ - @JSFWebConfigParam(since="2.3-next", defaultValue="false", expectedValues="true,false", tags="performance") - public static final String USE_LAMBDA_METAFACTORY = "org.apache.myfaces.USE_LAMBDA_METAFACTORY"; + @JSFWebConfigParam(since="4.1.1", defaultValue="false", expectedValues="true,false", tags="performance") + public static final String USE_LAMBDAS_OVER_REFLECTION + = "org.apache.myfaces.USE_LAMBDAS_OVER_REFLECTION"; private static final String CACHE_KEY = PropertyDescriptorUtils.class.getName() + ".CACHE"; - private static Method privateLookupIn; + private static Map> moduleAccessCache = new ConcurrentHashMap<>(); - static - { - try - { - privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, - MethodHandles.Lookup.class); - } - catch (Exception e) - { - } - } - private static Map> getCache(ExternalContext ec) { - Map> cache = + Map> cache = (Map>) ec.getApplicationMap().get(CACHE_KEY); if (cache == null) { @@ -82,105 +73,102 @@ public class PropertyDescriptorUtils } public static Map getCachedPropertyDescriptors(ExternalContext ec, - Class target) + Class caller, + Class target) { Map cache = getCache(ec).get(target.getName()); if (cache == null) { - cache = getCache(ec).computeIfAbsent(target.getName(), k -> getPropertyDescriptors(ec, target)); + final Class realTarget = target; + cache = getCache(ec).computeIfAbsent(target.getName(), k -> getPropertyDescriptors(ec, caller, target)); } return cache; } - public static boolean isUseLambdaMetafactory(ExternalContext ec) + public static boolean isUseLambdas(ExternalContext ec) { - if (privateLookupIn == null) - { - return false; - } - // disabled per default - String useMethodHandles = ec.getInitParameter(USE_LAMBDA_METAFACTORY); + String useMethodHandles = ec.getInitParameter(USE_LAMBDAS_OVER_REFLECTION); return useMethodHandles != null && useMethodHandles.contains("true"); } - public static Map getPropertyDescriptors(ExternalContext ec, + protected static Map getPropertyDescriptors( + ExternalContext ec, + Class caller, Class target) { - if (isUseLambdaMetafactory(ec)) - { - try - { - return getLambdaPropertyDescriptors(target); - } - catch (IllegalAccessException e) - { - LOG.log(Level.FINEST, - "Could not generate LambdaPropertyDescriptor for " - + target.getName() + ". Use PropertyDescriptor..."); - } - catch (Throwable e) - { - LOG.log(Level.INFO, - "Could not generate LambdaPropertyDescriptor for " - + target.getName() + ". Use PropertyDescriptor...", - e); - } - } - try { PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(target).getPropertyDescriptors(); - + Map map = new ConcurrentHashMap<>(propertyDescriptors.length); for (int i = 0; i < propertyDescriptors.length; i++) { PropertyDescriptor propertyDescriptor = propertyDescriptors[i]; - map.put(propertyDescriptor.getName(), - new PropertyDescriptorWrapper(target, propertyDescriptor)); - } - return map; - } - catch (IntrospectionException e) - { - throw new FacesException(e); - } - } - - public static LambdaPropertyDescriptor getLambdaPropertyDescriptor(Class target, String name) - { - try - { - PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(target).getPropertyDescriptors(); - if (propertyDescriptors == null || propertyDescriptors.length == 0) - { - return null; - } - - for (PropertyDescriptor pd : propertyDescriptors) - { - if (name.equals(pd.getName())) + LambdaPropertyDescriptor lambdaPropertyDescriptor = null; + + if (isUseLambdas(ec)) + { + List cache = moduleAccessCache.computeIfAbsent(caller.getModule(), + (k) -> new CopyOnWriteArrayList<>()); + if (!cache.contains(target.getModule())) + { + if (!caller.getModule().canRead(target.getModule())) + { + caller.getModule().addReads(target.getModule()); + } + cache.add(target.getModule()); + } + + try + { + lambdaPropertyDescriptor = + createLambdaPropertyDescriptor(caller, target, propertyDescriptor); + if (lambdaPropertyDescriptor == null) + { + LOG.log(Level.SEVERE, + "Could not generate LambdaPropertyDescriptor for " + + target.getName() + "#" + propertyDescriptor.getName() + + ". Use PropertyDescriptor..."); + } + } + catch (Throwable e) + { + e.printStackTrace(); + LOG.log(Level.SEVERE, + "Could not generate LambdaPropertyDescriptor for " + + target.getName() + "#" + propertyDescriptor.getName() + + ". Use PropertyDescriptor...", + e); + } + } + + if (lambdaPropertyDescriptor == null) + { + map.put(propertyDescriptor.getName(), + new PropertyDescriptorWrapper(target, propertyDescriptor)); + } + else { - MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupIn.invoke(null, target, - MethodHandles.lookup()); - return createLambdaPropertyDescriptor(target, pd, lookup); + map.put(propertyDescriptor.getName(), lambdaPropertyDescriptor); } } - throw new FacesException("Property \"" + name + "\" not found on \"" + target.getName() + "\""); + return map; } - catch (Throwable e) + catch (IntrospectionException e) { throw new FacesException(e); } } - - - public static LambdaPropertyDescriptor createLambdaPropertyDescriptor(Class target, PropertyDescriptor pd, - MethodHandles.Lookup lookup) throws Throwable + + public static LambdaPropertyDescriptor createLambdaPropertyDescriptor( + Class caller, + Class target, + PropertyDescriptor pd) throws Throwable { if (pd.getPropertyType() == null) { @@ -189,6 +177,9 @@ public static LambdaPropertyDescriptor createLambdaPropertyDescriptor(Class t LambdaPropertyDescriptor lpd = new LambdaPropertyDescriptor(target, pd); + MethodHandles.Lookup lookup = + MethodHandles.privateLookupIn(PropertyDescriptorUtils.class, MethodHandles.lookup()); + Method readMethod = pd.getReadMethod(); if (readMethod != null) { @@ -211,35 +202,10 @@ public static LambdaPropertyDescriptor createLambdaPropertyDescriptor(Class t return lpd; } - - public static Map getLambdaPropertyDescriptors(Class target) throws Throwable - { - PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(target).getPropertyDescriptors(); - if (propertyDescriptors == null || propertyDescriptors.length == 0) - { - return Collections.emptyMap(); - } - - Map properties = new ConcurrentHashMap<>(propertyDescriptors.length); - - MethodHandles.Lookup lookup = (MethodHandles.Lookup) - privateLookupIn.invoke(null, target, MethodHandles.lookup()); - for (PropertyDescriptor pd : Introspector.getBeanInfo(target).getPropertyDescriptors()) - { - PropertyDescriptorWrapper wrapped = createLambdaPropertyDescriptor(target, pd, lookup); - if (wrapped == null) - { - wrapped = new PropertyDescriptorWrapper(target, pd); - } - properties.put(pd.getName(), wrapped); - } - - return properties; - } @SuppressWarnings("unchecked") protected static BiConsumer createSetter(MethodHandles.Lookup lookup, LambdaPropertyDescriptor propertyInfo, - MethodHandle setterHandle) + MethodHandle setterHandle) throws LambdaConversionException, Throwable { Class propertyType = propertyInfo.getPropertyType(); @@ -307,7 +273,7 @@ else if (propertyType == boolean.class) } protected static CallSite createSetterCallSite(MethodHandles.Lookup lookup, MethodHandle setter, - Class interfaceType, Class valueType) + Class interfaceType, Class valueType) throws LambdaConversionException { return LambdaMetafactory.metafactory(lookup, diff --git a/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java b/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java index a0d858fdb5..b5b0141fef 100755 --- a/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java +++ b/impl/src/main/java/org/apache/myfaces/application/ApplicationImpl.java @@ -1592,6 +1592,7 @@ private void setConverterProperties(final Class converterClass, final Convert { Map pds = PropertyDescriptorUtils.getCachedPropertyDescriptors( FacesContext.getCurrentInstance().getExternalContext(), + this.getClass(), converterClass); for (int i = 0; i < converterConfig.getProperties().size(); i++) diff --git a/impl/src/main/java/org/apache/myfaces/el/DefaultELResolverBuilder.java b/impl/src/main/java/org/apache/myfaces/el/DefaultELResolverBuilder.java index 1924829f7d..3f62a2ee39 100644 --- a/impl/src/main/java/org/apache/myfaces/el/DefaultELResolverBuilder.java +++ b/impl/src/main/java/org/apache/myfaces/el/DefaultELResolverBuilder.java @@ -166,7 +166,7 @@ public void build(FacesContext facesContext, CompositeELResolver compositeElReso } } - if (PropertyDescriptorUtils.isUseLambdaMetafactory(facesContext.getExternalContext())) + if (PropertyDescriptorUtils.isUseLambdas(facesContext.getExternalContext())) { list.add(new LambdaBeanELResolver()); } diff --git a/impl/src/main/java/org/apache/myfaces/el/resolver/LambdaBeanELResolver.java b/impl/src/main/java/org/apache/myfaces/el/resolver/LambdaBeanELResolver.java index 55d165066b..c6284e49ba 100644 --- a/impl/src/main/java/org/apache/myfaces/el/resolver/LambdaBeanELResolver.java +++ b/impl/src/main/java/org/apache/myfaces/el/resolver/LambdaBeanELResolver.java @@ -173,6 +173,7 @@ protected PropertyDescriptorWrapper getPropertyDescriptor(Object base, Object pr { beanCache = PropertyDescriptorUtils.getCachedPropertyDescriptors( FacesContext.getCurrentInstance().getExternalContext(), + this.getClass(), base.getClass()); cache.put(base.getClass().getName(), beanCache); } diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LambdaMetadataTargetImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LambdaMetadataTargetImpl.java index d6756b1e76..ff45353971 100644 --- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LambdaMetadataTargetImpl.java +++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LambdaMetadataTargetImpl.java @@ -40,6 +40,7 @@ public LambdaMetadataTargetImpl(Class type) throws IntrospectionException this.type = type; this.propertyDescriptors = PropertyDescriptorUtils.getCachedPropertyDescriptors( FacesContext.getCurrentInstance().getExternalContext(), + this.getClass(), type); } diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java index d34e383cb7..0609454779 100644 --- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java +++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MetaRulesetImpl.java @@ -328,7 +328,7 @@ private MetadataTarget _getMetadataTarget() { try { - if (PropertyDescriptorUtils.isUseLambdaMetafactory( + if (PropertyDescriptorUtils.isUseLambdas( FacesContext.getCurrentInstance().getExternalContext())) { meta = new LambdaMetadataTargetImpl(_type); diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetaRulesetImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetaRulesetImpl.java index 1d5b58a561..a52820f610 100644 --- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetaRulesetImpl.java +++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/composite/CompositeMetaRulesetImpl.java @@ -214,7 +214,7 @@ private MetadataTarget _getBaseMetadataTarget() { try { - if (PropertyDescriptorUtils.isUseLambdaMetafactory( + if (PropertyDescriptorUtils.isUseLambdas( FacesContext.getCurrentInstance().getExternalContext())) { meta = new LambdaMetadataTargetImpl(_type);