refactored MethodHandles
diff --git a/api/src/main/java/javax/faces/component/_ComponentAttributesMap.java b/api/src/main/java/javax/faces/component/_ComponentAttributesMap.java
index 5b6bf64..50a35f3 100755
--- a/api/src/main/java/javax/faces/component/_ComponentAttributesMap.java
+++ b/api/src/main/java/javax/faces/component/_ComponentAttributesMap.java
@@ -19,8 +19,6 @@
 package javax.faces.component;
 
 import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
 import java.beans.PropertyDescriptor;
 import java.io.Serializable;
 import java.lang.reflect.Method;
@@ -28,13 +26,14 @@
 import java.util.Collections;
 import java.util.Map;
 import java.util.Set;
-import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
 
 import javax.el.ValueExpression;
 import javax.faces.FacesException;
 import javax.faces.application.Resource;
 import javax.faces.context.FacesContext;
+import org.apache.myfaces.core.api.shared.lang.LambdaPropertyDescriptor;
+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorUtils;
+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorWrapper;
 
 /**
  * <p>
@@ -86,12 +85,8 @@
     // the javabean properties of the associated component. This is built by
     // introspection on the associated UIComponent. Don't serialize this as
     // it can always be recreated when needed.
-    private transient Map<String, _PropertyDescriptorHolder> _propertyDescriptorMap = null;
+    private transient Map<String, ? extends PropertyDescriptorWrapper> _propertyDescriptorMap = null;
 
-    // Cache for component property descriptors
-    private static Map<Class<?>, Map<String, _PropertyDescriptorHolder>> propertyDescriptorCache =
-        new WeakHashMap<Class<?>, Map<String, _PropertyDescriptorHolder>>();
-    
     private boolean _isCompositeComponent;
     private boolean _isCompositeComponentSet;
     
@@ -175,7 +170,7 @@
             if (MARK_CREATED.length() == keyLength &&
                 MARK_CREATED.equals(key))
             {
-                return ((UIComponentBase)_component).getOamVfMarkCreated() != null;
+                return ((UIComponentBase) _component).getOamVfMarkCreated() != null;
             }
             else if (FACET_NAME_KEY.length() == keyLength &&
                 FACET_NAME_KEY.equals(key))
@@ -211,7 +206,11 @@
                 return _isCompositeComponent;
             }
         }
-        return getPropertyDescriptor((String) key) == null ? getUnderlyingMap().containsKey(key) : false;
+        
+        PropertyDescriptorWrapper pd = getPropertyDescriptor((String) key);
+        return pd == null || pd.getReadMethod() == null
+                ? getUnderlyingMap().containsKey(key)
+                : false;
     }
 
     /**
@@ -309,8 +308,8 @@
         }
 
         // is there a javabean property to read?
-        _PropertyDescriptorHolder propertyDescriptor = getPropertyDescriptor((String) key);
-        if (propertyDescriptor != null)
+        PropertyDescriptorWrapper propertyDescriptor = getPropertyDescriptor((String) key);
+        if (propertyDescriptor != null && propertyDescriptor.getReadMethod() != null)
         {
             value = getComponentProperty(propertyDescriptor);
         }
@@ -493,8 +492,8 @@
             }
         }
 
-        _PropertyDescriptorHolder propertyDescriptor = getPropertyDescriptor((String) key);
-        if (propertyDescriptor != null)
+        PropertyDescriptorWrapper propertyDescriptor = getPropertyDescriptor((String) key);
+        if (propertyDescriptor != null && propertyDescriptor.getReadMethod() != null)
         {
             throw new IllegalArgumentException("Cannot remove component property attribute");
         }
@@ -569,8 +568,8 @@
             }
         }
 
-        _PropertyDescriptorHolder propertyDescriptor = getPropertyDescriptor(key);
-        if (propertyDescriptor == null)
+        PropertyDescriptorWrapper propertyDescriptor = getPropertyDescriptor(key);
+        if (propertyDescriptor == null || propertyDescriptor.getReadMethod() == null)
         {
             if (value == null)
             {
@@ -614,51 +613,13 @@
      * that class.
      * <p/>
      */
-    private _PropertyDescriptorHolder getPropertyDescriptor(String key)
+    private PropertyDescriptorWrapper getPropertyDescriptor(String key)
     {
         if (_propertyDescriptorMap == null)
         {
-            // Try to get descriptor map from cache
-            _propertyDescriptorMap = propertyDescriptorCache.get(_component.getClass());
-
-            // Cache miss: create descriptor map and put it in cache
-            if (_propertyDescriptorMap == null)
-            {
-                // Create descriptor map...
-                BeanInfo beanInfo;
-                try
-                {
-                    beanInfo = Introspector.getBeanInfo(_component.getClass());
-                }
-                catch (IntrospectionException e)
-                {
-                    throw new FacesException(e);
-                }
-
-                PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
-                _propertyDescriptorMap = new ConcurrentHashMap<>();
-                for (int i = 0; i < propertyDescriptors.length; i++)
-                {
-                    PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
-                    Method readMethod = propertyDescriptor.getReadMethod();
-                    if (readMethod != null)
-                    {
-                        _propertyDescriptorMap.put(propertyDescriptor.getName(),
-                                new _PropertyDescriptorHolder(propertyDescriptor, readMethod));
-                    }
-                }
-
-                // ... and put it in cache
-                synchronized (propertyDescriptorCache)
-                {
-                    // Use a synchronized block to ensure proper operation on concurrent use cases.
-                    // This is a racy single check, because initialization over the same class could happen
-                    // multiple times, but the same result is always calculated. The synchronized block 
-                    // just ensure thread-safety, because only one thread will modify the cache map
-                    // at the same time.
-                    propertyDescriptorCache.put(_component.getClass(), _propertyDescriptorMap);
-                }
-            }
+            _propertyDescriptorMap = PropertyDescriptorUtils.getCachedPropertyDescriptors(
+                    _component.getFacesContext().getExternalContext(), 
+                    _component.getClass());
         }
         return _propertyDescriptorMap.get(key);
     }
@@ -674,7 +635,7 @@
      * @throws FacesException           if any other problem occurs while invoking
      *                                  the getter method.
      */
-    private Object getComponentProperty(_PropertyDescriptorHolder propertyDescriptor)
+    private Object getComponentProperty(PropertyDescriptorWrapper propertyDescriptor)
     {
         Method readMethod = propertyDescriptor.getReadMethod();
         if (readMethod == null)
@@ -685,6 +646,11 @@
 
         try
         {
+            if (propertyDescriptor instanceof LambdaPropertyDescriptor)
+            {
+                return ((LambdaPropertyDescriptor) propertyDescriptor).getReadFunction().apply(_component);
+            }
+
             return readMethod.invoke(_component, EMPTY_ARGS);
         }
         catch (Exception e)
@@ -704,7 +670,7 @@
      * @throws FacesException           if any other problem occurs while invoking
      *                                  the getter method.
      */
-    private void setComponentProperty(_PropertyDescriptorHolder propertyDescriptor, Object value)
+    private void setComponentProperty(PropertyDescriptorWrapper propertyDescriptor, Object value)
     {
         Method writeMethod = propertyDescriptor.getWriteMethod();
         if (writeMethod == null)
@@ -715,7 +681,14 @@
 
         try
         {
-            writeMethod.invoke(_component, new Object[]{value});
+            if (propertyDescriptor instanceof LambdaPropertyDescriptor)
+            {
+                ((LambdaPropertyDescriptor) propertyDescriptor).getWriteFunction().accept(_component, value);
+            }
+            else
+            {
+                writeMethod.invoke(_component, new Object[]{value});
+            }
         }
         catch (Exception e)
         {
diff --git a/api/src/main/java/javax/faces/component/_PropertyDescriptorHolder.java b/api/src/main/java/javax/faces/component/_PropertyDescriptorHolder.java
deleted file mode 100644
index 46b5f86..0000000
--- a/api/src/main/java/javax/faces/component/_PropertyDescriptorHolder.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * 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 javax.faces.component;
-
-import java.beans.PropertyDescriptor;
-import java.lang.ref.Reference;
-import java.lang.ref.SoftReference;
-import java.lang.reflect.Method;
-
-class _PropertyDescriptorHolder
-{
-    private final PropertyDescriptor _descriptor;
-    private Reference<Method> _readMethodRef;
-    private Reference<Method> _writeMethodRef;
-
-    public _PropertyDescriptorHolder(PropertyDescriptor descriptor)
-    {
-        _descriptor = descriptor;
-        _readMethodRef = new SoftReference<Method>(_descriptor.getReadMethod());
-    }
-
-    public _PropertyDescriptorHolder(PropertyDescriptor descriptor, Method readMethod)
-    {
-        _descriptor = descriptor;
-        _readMethodRef = new SoftReference<Method>(readMethod);
-    }
-    
-    public String getName()
-    {
-        return _descriptor.getName();
-    }
-    
-    public Method getReadMethod()
-    {
-        Method readMethod = _readMethodRef.get();
-        if (readMethod == null)
-        {
-            readMethod = _descriptor.getReadMethod();
-            _readMethodRef = new SoftReference<Method>(readMethod);
-        }
-        return readMethod;
-    }
-    
-    public Method getWriteMethod()
-    {
-        if (_writeMethodRef == null || _writeMethodRef.get() == null)
-        {
-            // In facelets, the Method instance used to write the variable is stored
-            // in a variable (see org.apache.myfaces.view.facelets.tag.BeanPropertyTagRule),
-            // so the impact of this synchronized call at the end is minimal compared with 
-            // getReadMethod. That's the reason why cache it here in a lazy way is enough
-            // instead retrieve it as soon as this holder is created.
-            Method writeMethod = _descriptor.getWriteMethod();
-            _writeMethodRef = new SoftReference<Method>(writeMethod);
-        }
-        return _writeMethodRef.get();
-    }
-    
-    public PropertyDescriptor getPropertyDescriptor()
-    {
-        return _descriptor;
-    }
-}
diff --git a/api/src/main/java/org/apache/myfaces/core/api/shared/lang/LambdaPropertyDescriptor.java b/api/src/main/java/org/apache/myfaces/core/api/shared/lang/LambdaPropertyDescriptor.java
new file mode 100644
index 0000000..284f641
--- /dev/null
+++ b/api/src/main/java/org/apache/myfaces/core/api/shared/lang/LambdaPropertyDescriptor.java
@@ -0,0 +1,44 @@
+/*

+ * 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.core.api.shared.lang;

+

+import java.beans.PropertyDescriptor;

+import java.util.function.BiConsumer;

+import java.util.function.Function;

+

+public class LambdaPropertyDescriptor extends PropertyDescriptorWrapper

+{

+    Function<Object, Object> readFunction;

+    BiConsumer<Object, Object> writeFunction;

+

+    public LambdaPropertyDescriptor(Class<?> beanClass, PropertyDescriptor wrapped)

+    {

+        super(beanClass, wrapped);

+    }

+

+    public Function<Object, Object> getReadFunction()

+    {

+        return readFunction;

+    }

+

+    public BiConsumer<Object, Object> getWriteFunction()

+    {

+        return writeFunction;

+    }

+}

diff --git a/impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java b/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorUtils.java
similarity index 73%
rename from impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java
rename to api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorUtils.java
index f6f31dc..97339a1 100644
--- a/impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java
+++ b/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorUtils.java
@@ -16,8 +16,9 @@
  * specific language governing permissions and limitations

  * under the License.

  */

-package org.apache.myfaces.util.lang;

+package org.apache.myfaces.core.api.shared.lang;

 

+import java.beans.IntrospectionException;

 import java.beans.Introspector;

 import java.beans.PropertyDescriptor;

 import java.lang.invoke.CallSite;

@@ -28,17 +29,27 @@
 import java.lang.invoke.MethodType;

 import java.lang.reflect.Method;

 import java.util.Collections;

-import java.util.HashMap;

 import java.util.Map;

+import java.util.concurrent.ConcurrentHashMap;

 import java.util.function.BiConsumer;

 import java.util.function.Function;

 import java.util.function.ObjDoubleConsumer;

 import java.util.function.ObjIntConsumer;

 import java.util.function.ObjLongConsumer;

 import javax.faces.FacesException;

+import javax.faces.context.ExternalContext;

+import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;

 

-public final class MethodHandleUtils

+public class PropertyDescriptorUtils

 {

+    /**

+     * Defines if MethodHandles and LambdaMetafactory instead of Reflection should be used for getter/setter.

+     */

+    @JSFWebConfigParam(since="2.3-next", defaultValue="true", expectedValues="true,false", tags="performance")

+    public static final String USE_METHOD_HANDLES = "org.apache.myfaces.USE_METHOD_HANDLES";

+

+    private static final String CACHE_KEY = PropertyDescriptorUtils.class.getName() + ".CACHE";

+

     private static Method privateLookupIn;

 

     static

@@ -52,46 +63,76 @@
         {

         }

     }

-

-    public static class LambdaPropertyDescriptor

+    

+    private static Map<String, Map<String, ? extends PropertyDescriptorWrapper>> getCache(ExternalContext ec)

     {

-        private PropertyDescriptor wrapped;

-        private Method readMethod;

-        private Function<Object, Object> readFunction;

-        private Method writeMethod;

-        private BiConsumer<Object, Object> writeFunction;

-

-        public PropertyDescriptor getWrapped()

+        Map<String, Map<String, ? extends PropertyDescriptorWrapper>> cache =

+                (Map<String, Map<String, ? extends PropertyDescriptorWrapper>>)

+                    ec.getApplicationMap().get(CACHE_KEY);

+        if (cache == null)

         {

-            return wrapped;

+            cache = new ConcurrentHashMap<>(1000);

         }

 

-        public Class<?> getPropertyType()

+        return cache;

+    }

+    

+    public static Map<String, ? extends PropertyDescriptorWrapper> getCachedPropertyDescriptors(ExternalContext ec,

+            Class<?> target)

+    {

+        return getCache(ec).computeIfAbsent(target.getName(), k -> 

         {

-            return wrapped.getPropertyType();

-        }

-

-        public Method getReadMethod()

+            return getPropertyDescriptors(ec, target, false);

+        });

+    }

+    

+    public static boolean isMethodHandlesSupported(ExternalContext ec)

+    {

+        if (privateLookupIn == null)

         {

-            return readMethod;

+            return false;

         }

         

-        public Function<Object, Object> getReadFunction()

-        {

-            return readFunction;

-        }

-

-        public Method getWriteMethod()

-        {

-            return writeMethod;

-        }

-        

-        public BiConsumer<Object, Object> getWriteFunction()

-        {

-            return writeFunction;

-        }

+        String useMethodHandles = ec.getInitParameter(USE_METHOD_HANDLES);

+        return useMethodHandles != null && useMethodHandles.contains("true");

     }

 

+    public static Map<String, ? extends PropertyDescriptorWrapper> getPropertyDescriptors(ExternalContext ec,

+            Class<?> target,

+            boolean skipPropertyWithoutReadMethod)

+    {

+        if (isMethodHandlesSupported(ec))

+        {

+            return getLambdaPropertyDescriptors(target);

+        }

+

+        try

+        {

+            PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(target).getPropertyDescriptors();

+            

+            Map<String, PropertyDescriptorWrapper> map = new ConcurrentHashMap<>(propertyDescriptors.length);

+

+            for (int i = 0; i < propertyDescriptors.length; i++)

+            {

+                PropertyDescriptor propertyDescriptor = propertyDescriptors[i];

+                Method readMethod = propertyDescriptor.getReadMethod();

+                if (readMethod == null && skipPropertyWithoutReadMethod)

+                {

+                    continue;

+                }

+

+                map.put(propertyDescriptor.getName(),

+                        new PropertyDescriptorWrapper(target, propertyDescriptor, readMethod));

+            }

+

+            return map;

+        }

+        catch (IntrospectionException e)

+        {

+            throw new FacesException(e);

+        }

+    }

+    

     public static LambdaPropertyDescriptor getLambdaPropertyDescriptor(Class<?> target, String name)

     {

         try

@@ -108,7 +149,7 @@
                 {

                     MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupIn.invoke(null, target,

                             MethodHandles.lookup());

-                    return createLambdaPropertyDescriptor(pd, lookup);

+                    return createLambdaPropertyDescriptor(target, pd, lookup);

                 }

             }

 

@@ -120,17 +161,15 @@
         }

     }

     

-    public static LambdaPropertyDescriptor createLambdaPropertyDescriptor(PropertyDescriptor pd,

+  

+    public static LambdaPropertyDescriptor createLambdaPropertyDescriptor(Class<?> target, PropertyDescriptor pd,

             MethodHandles.Lookup lookup) throws Throwable

     {

-        LambdaPropertyDescriptor lpd = new LambdaPropertyDescriptor();

-        lpd.wrapped = pd;

+        LambdaPropertyDescriptor lpd = new LambdaPropertyDescriptor(target, pd);

 

         Method readMethod = pd.getReadMethod();

         if (readMethod != null)

         {

-            lpd.readMethod = readMethod;

-            

             MethodHandle handle = lookup.unreflect(readMethod);

             CallSite callSite = LambdaMetafactory.metafactory(lookup,

                     "apply",

@@ -144,8 +183,6 @@
         Method writeMethod = pd.getWriteMethod();

         if (writeMethod != null)

         {

-            lpd.writeMethod = writeMethod;

-            

             MethodHandle handle = lookup.unreflect(writeMethod);

             lpd.writeFunction = createSetter(lookup, lpd, handle);

         }

@@ -163,14 +200,13 @@
                 return Collections.emptyMap();

             }

 

-            HashMap<String, LambdaPropertyDescriptor> properties = new HashMap<>(propertyDescriptors.length);

+            Map<String, LambdaPropertyDescriptor> properties = new ConcurrentHashMap<>(propertyDescriptors.length);

 

-            MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupIn.invoke(null, target,

-                    MethodHandles.lookup());

-            

+            MethodHandles.Lookup lookup = (MethodHandles.Lookup)

+                    privateLookupIn.invoke(null, target, MethodHandles.lookup());

             for (PropertyDescriptor pd : Introspector.getBeanInfo(target).getPropertyDescriptors())

             {

-                LambdaPropertyDescriptor lpd = createLambdaPropertyDescriptor(pd, lookup);

+                LambdaPropertyDescriptor lpd = createLambdaPropertyDescriptor(target, pd, lookup);

                 properties.put(pd.getName(), lpd);

             }

             

diff --git a/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorWrapper.java b/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorWrapper.java
new file mode 100644
index 0000000..27c9271
--- /dev/null
+++ b/api/src/main/java/org/apache/myfaces/core/api/shared/lang/PropertyDescriptorWrapper.java
@@ -0,0 +1,86 @@
+/*

+ * 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.core.api.shared.lang;

+

+import java.beans.PropertyDescriptor;

+import java.lang.ref.Reference;

+import java.lang.ref.SoftReference;

+import java.lang.reflect.Method;

+import javax.faces.FacesWrapper;

+

+public class PropertyDescriptorWrapper implements FacesWrapper<PropertyDescriptor>

+{

+    private final PropertyDescriptor wrapped;

+    private Reference<Method> readMethodRef;

+    private Reference<Method> writeMethodRef;

+

+    public PropertyDescriptorWrapper(Class<?> beanClass, PropertyDescriptor wrapped)

+    {

+        this.wrapped = wrapped;

+        this.readMethodRef = new SoftReference<>(wrapped.getReadMethod());

+    }

+

+    public PropertyDescriptorWrapper(Class<?> beanClass, PropertyDescriptor wrapped, Method readMethod)

+    {

+        this.wrapped = wrapped;

+        this.readMethodRef = new SoftReference<>(readMethod);

+    }

+

+    public Class<?> getPropertyType()

+    {

+        return wrapped.getPropertyType();

+    }    

+    

+    public String getName()

+    {

+        return wrapped.getName();

+    }

+    

+    @Override

+    public PropertyDescriptor getWrapped()

+    {

+        return wrapped;

+    }

+

+    public Method getReadMethod()

+    {

+        Method readMethod = readMethodRef.get();

+        if (readMethod == null)

+        {

+            readMethod = wrapped.getReadMethod();

+            readMethodRef = new SoftReference<>(readMethod);

+        }

+        return readMethod;

+    }

+

+    public Method getWriteMethod()

+    {

+        if (writeMethodRef == null || writeMethodRef.get() == null)

+        {

+            // In facelets, the Method instance used to write the variable is stored

+            // in a variable (see org.apache.myfaces.view.facelets.tag.BeanPropertyTagRule),

+            // so the impact of this synchronized call at the end is minimal compared with 

+            // getReadMethod. That's the reason why cache it here in a lazy way is enough

+            // instead retrieve it as soon as this holder is created.

+            Method writeMethod = wrapped.getWriteMethod();

+            writeMethodRef = new SoftReference<>(writeMethod);

+        }

+        return writeMethodRef.get();

+    }

+}

diff --git a/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java b/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java
index 1e9cee2..0fe37c6 100644
--- a/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java
+++ b/extensions/quarkus/deployment/src/main/java/org/apache/myfaces/core/extensions/quarkus/deployment/MyFacesProcessor.java
@@ -30,7 +30,6 @@
 import javax.faces.flow.FlowScoped;
 import javax.faces.flow.builder.FlowDefinition;
 import javax.faces.model.FacesDataModel;
-import javax.faces.push.PushContext;
 import javax.faces.render.FacesBehaviorRenderer;
 import javax.faces.render.FacesRenderer;
 import javax.faces.validator.FacesValidator;
@@ -50,7 +49,7 @@
 import org.apache.myfaces.config.annotation.CdiAnnotationProviderExtension;
 import org.apache.myfaces.config.element.NamedEvent;
 import org.apache.myfaces.core.extensions.quarkus.runtime.exception.QuarkusExceptionHandlerFactory;
-import org.apache.myfaces.el.resolver.MethodHandleBeanELResolver;
+import org.apache.myfaces.el.resolver.LambdaBeanELResolver;
 import org.apache.myfaces.flow.cdi.FlowBuilderFactoryBean;
 import org.apache.myfaces.flow.cdi.FlowScopeBeanHolder;
 import org.apache.myfaces.push.cdi.PushContextFactoryBean;
@@ -58,8 +57,7 @@
 import org.apache.myfaces.push.cdi.WebsocketChannelTokenBuilderBean;
 import org.apache.myfaces.push.cdi.WebsocketSessionBean;
 import org.apache.myfaces.push.cdi.WebsocketViewBean;
-import org.apache.myfaces.util.lang.MethodHandleUtils;
-import org.apache.myfaces.view.facelets.tag.MethodHandleMetadataTargetImpl;
+import org.apache.myfaces.view.facelets.tag.LambdaMetadataTargetImpl;
 import org.apache.myfaces.webapp.StartupServletContextListener;
 import org.eclipse.microprofile.config.Config;
 import org.eclipse.microprofile.config.ConfigProvider;
@@ -124,6 +122,7 @@
 import org.apache.myfaces.application.viewstate.StateUtils;
 import org.apache.myfaces.cdi.util.BeanEntry;
 import org.apache.myfaces.config.FacesConfigurator;
+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorUtils;
 import org.apache.myfaces.core.extensions.quarkus.runtime.spi.QuarkusFactoryFinderProvider;
 import org.apache.myfaces.el.ELResolverBuilderForFaces;
 import org.apache.myfaces.renderkit.ErrorPageWriter;
@@ -277,15 +276,6 @@
         Optional<String> projectStage = resolveProjectStage(config);
         initParam.produce(new ServletInitParamBuildItem(ProjectStage.PROJECT_STAGE_PARAM_NAME, projectStage.get()));
 
-        Optional<String> enableWebsocketsEndpoint = config.getOptionalValue(
-                PushContext.ENABLE_WEBSOCKET_ENDPOINT_PARAM_NAME,
-                String.class);
-        if (enableWebsocketsEndpoint.isPresent())
-        {
-            initParam.produce(new ServletInitParamBuildItem(PushContext.ENABLE_WEBSOCKET_ENDPOINT_PARAM_NAME,
-                    enableWebsocketsEndpoint.get()));
-        }
-
         // common
         initParam.produce(new ServletInitParamBuildItem(
                 MyfacesConfig.LOG_WEB_CONTEXT_PARAMS, "false"));
@@ -802,11 +792,11 @@
     void registerRuntimeInitialization(BuildProducer<RuntimeInitializedClassBuildItem> runtimeInitClassBuildItem)
     {
         runtimeInitClassBuildItem.produce(
-                new RuntimeInitializedClassBuildItem(MethodHandleBeanELResolver.class.getCanonicalName()));
+                new RuntimeInitializedClassBuildItem(LambdaBeanELResolver.class.getCanonicalName()));
         runtimeInitClassBuildItem.produce(
-                new RuntimeInitializedClassBuildItem(MethodHandleMetadataTargetImpl.class.getCanonicalName()));
+                new RuntimeInitializedClassBuildItem(LambdaMetadataTargetImpl.class.getCanonicalName()));
         runtimeInitClassBuildItem.produce(
-                new RuntimeInitializedClassBuildItem(MethodHandleUtils.class.getCanonicalName()));
+                new RuntimeInitializedClassBuildItem(PropertyDescriptorUtils.class.getCanonicalName()));
     }
     
     
diff --git a/extensions/quarkus/showcase/src/main/resources/META-INF/web.xml b/extensions/quarkus/showcase/src/main/resources/META-INF/web.xml
index 9cc3e3a..35816fa 100644
--- a/extensions/quarkus/showcase/src/main/resources/META-INF/web.xml
+++ b/extensions/quarkus/showcase/src/main/resources/META-INF/web.xml
@@ -20,7 +20,12 @@
 <web-app version="4.0" xmlns="http://xmlns.jcp.org/xml/ns/javaee"

          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd">

-    

+

+    <context-param>

+        <param-name>javax.faces.ENABLE_WEBSOCKET_ENDPOINT</param-name>

+        <param-value>true</param-value>

+    </context-param>

+

     

     <context-param>

         <param-name>primefaces.THEME</param-name>

diff --git a/extensions/quarkus/showcase/src/main/resources/application.properties b/extensions/quarkus/showcase/src/main/resources/application.properties
index cf94889..c5e2196 100644
--- a/extensions/quarkus/showcase/src/main/resources/application.properties
+++ b/extensions/quarkus/showcase/src/main/resources/application.properties
@@ -14,6 +14,5 @@
 
 quarkus.log.console.format=%s%n
 javax.faces.PROJECT_STAGE=Development
-javax.faces.ENABLE_WEBSOCKET_ENDPOINT=true
 quarkus.log.console.level=INFO
 quarkus.log.file.path=/tmp/debug.log
\ No newline at end of file
diff --git a/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java b/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
index 8e0bec6..6e554b4 100755
--- a/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
+++ b/impl/src/main/java/org/apache/myfaces/config/MyfacesConfig.java
@@ -18,7 +18,6 @@
  */
 package org.apache.myfaces.config;
 
-import java.lang.invoke.MethodHandles;
 import java.util.ResourceBundle;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -812,14 +811,6 @@
     protected final static boolean ALWAYS_FORCE_SESSION_CREATION_DEFAULT = false;
     
     /**
-     * Defines if MethodHandles and LambdaMetafactory instead of Reflection should be used for getter/setter.
-     */
-    @JSFWebConfigParam(since="2.3-next", defaultValue="true", expectedValues="true,false", tags="performance")
-    public static final String USE_METHOD_HANDLES = 
-            "org.apache.myfaces.USE_METHOD_HANDLES";
-    protected final static boolean USE_METHOD_HANDLES_DEFAULT = true;
-    
-    /**
      * Defines the {@link java.util.ResourceBundle.Control} to use for all
      * {@link java.util.ResourceBundle#getBundle(java.lang.String)} calls.
      */
@@ -906,7 +897,6 @@
     private int websocketMaxConnections = WEBSOCKET_MAX_CONNECTIONS_DEFAULT;
     private boolean renderClientBehaviorScriptsAsString = RENDER_CLIENTBEHAVIOR_SCRIPTS_AS_STRING_DEFAULT;
     private boolean alwaysForceSessionCreation = ALWAYS_FORCE_SESSION_CREATION_DEFAULT;
-    private boolean useMethodHandles = USE_METHOD_HANDLES_DEFAULT;
     private ResourceBundle.Control resourceBundleControl;
     private boolean automaticExtensionlessMapping = AUTOMATIC_EXTENSIONLESS_MAPPING_DEFAULT;
     
@@ -960,17 +950,6 @@
     {
         numberOfFlashTokensInSession = (NUMBER_OF_VIEWS_IN_SESSION_DEFAULT
                 / NUMBER_OF_SEQUENTIAL_VIEWS_IN_SESSION_DEFAULT) + 1;
-
-        try
-        {
-            MethodHandles.class.getMethod("privateLookupIn", Class.class,
-                    MethodHandles.Lookup.class);
-            useMethodHandles = true;
-        }
-        catch (NoSuchMethodException e)
-        {
-            useMethodHandles = false;
-        }
     }
 
     private static MyfacesConfig createAndInitializeMyFacesConfig(ExternalContext extCtx)
@@ -1338,21 +1317,6 @@
 
         cfg.alwaysForceSessionCreation = getBoolean(extCtx, ALWAYS_FORCE_SESSION_CREATION,
                 ALWAYS_FORCE_SESSION_CREATION_DEFAULT);
-
-        cfg.useMethodHandles = getBoolean(extCtx, USE_METHOD_HANDLES,
-                USE_METHOD_HANDLES_DEFAULT);
-        if (cfg.useMethodHandles)
-        {
-            try
-            {
-                MethodHandles.class.getMethod("privateLookupIn", Class.class,
-                        MethodHandles.Lookup.class);
-            }
-            catch (NoSuchMethodException e)
-            {
-                cfg.useMethodHandles = false;
-            }
-        }
         
         String resourceBundleControl = getString(extCtx, RESOURCE_BUNDLE_CONTROL, null);
         if (StringUtils.isNotBlank(resourceBundleControl))
@@ -1823,11 +1787,6 @@
         return alwaysForceSessionCreation;
     }
 
-    public boolean isUseMethodHandles()
-    {
-        return useMethodHandles;
-    }
-    
     public ProjectStage getProjectStage()
     {
         return projectStage;
diff --git a/impl/src/main/java/org/apache/myfaces/el/ELResolverBuilderForFaces.java b/impl/src/main/java/org/apache/myfaces/el/ELResolverBuilderForFaces.java
index 5bb2804..9587d37 100644
--- a/impl/src/main/java/org/apache/myfaces/el/ELResolverBuilderForFaces.java
+++ b/impl/src/main/java/org/apache/myfaces/el/ELResolverBuilderForFaces.java
@@ -46,7 +46,8 @@
 import org.apache.myfaces.el.resolver.ScopedAttributeResolver;
 import org.apache.myfaces.el.resolver.implicitobject.ImplicitObjectResolver;
 import org.apache.myfaces.config.MyfacesConfig;
-import org.apache.myfaces.el.resolver.MethodHandleBeanELResolver;
+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorUtils;
+import org.apache.myfaces.el.resolver.LambdaBeanELResolver;
 import org.apache.myfaces.util.lang.ClassUtils;
 
 /**
@@ -140,9 +141,9 @@
         list.add(new MapELResolver());
         list.add(new ListELResolver());
         list.add(new ArrayELResolver());
-        if (myfacesConfig.isUseMethodHandles())
+        if (PropertyDescriptorUtils.isMethodHandlesSupported(facesContext.getExternalContext()))
         {
-            list.add(new MethodHandleBeanELResolver());
+            list.add(new LambdaBeanELResolver(facesContext.getExternalContext()));
         }
         else
         {
diff --git a/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java b/impl/src/main/java/org/apache/myfaces/el/resolver/LambdaBeanELResolver.java
similarity index 75%
rename from impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java
rename to impl/src/main/java/org/apache/myfaces/el/resolver/LambdaBeanELResolver.java
index b69959f..4cb9f51 100644
--- a/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java
+++ b/impl/src/main/java/org/apache/myfaces/el/resolver/LambdaBeanELResolver.java
@@ -26,15 +26,20 @@
 import javax.el.ELContext;

 import javax.el.ELException;

 import javax.el.PropertyNotWritableException;

-import org.apache.myfaces.util.lang.MethodHandleUtils;

+import javax.faces.context.ExternalContext;

+import org.apache.myfaces.core.api.shared.lang.LambdaPropertyDescriptor;

+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorUtils;

+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorWrapper;

 

-public class MethodHandleBeanELResolver extends BeanELResolver

+public class LambdaBeanELResolver extends BeanELResolver

 {

-    private final ConcurrentHashMap<String, Map<String, MethodHandleUtils.LambdaPropertyDescriptor>> cache;

+    private final ExternalContext externalContext;

+    private final ConcurrentHashMap<String, Map<String, ? extends PropertyDescriptorWrapper>> cache;

 

-    public MethodHandleBeanELResolver()

+    public LambdaBeanELResolver(ExternalContext externalContext)

     {

-        cache = new ConcurrentHashMap<>(1000);

+        this.externalContext = externalContext;

+        this.cache = new ConcurrentHashMap<>(1000);

     }

 

     @Override

@@ -85,7 +90,7 @@
 

         context.setPropertyResolved(base, property);

 

-        MethodHandleUtils.LambdaPropertyDescriptor propertyDescriptor = getPropertyDescriptor(base, property);

+        LambdaPropertyDescriptor propertyDescriptor = getPropertyDescriptor(base, property);

         if (propertyDescriptor.getWriteFunction() == null)

         {

             throw new PropertyNotWritableException("Property \"" + (String) property

@@ -127,14 +132,12 @@
         return null;

     }

 

-    protected MethodHandleUtils.LambdaPropertyDescriptor getPropertyDescriptor(Object base, Object property)

+    protected LambdaPropertyDescriptor getPropertyDescriptor(Object base, Object property)

     {

-        Map<String, MethodHandleUtils.LambdaPropertyDescriptor> beanCache = cache.computeIfAbsent(

+        Map<String, ? extends PropertyDescriptorWrapper> beanCache = cache.computeIfAbsent(

                 base.getClass().getName(),

-                k -> new ConcurrentHashMap<>());

+                k -> PropertyDescriptorUtils.getCachedPropertyDescriptors(externalContext, base.getClass()));

 

-        return beanCache.computeIfAbsent(

-                (String) property,

-                k -> MethodHandleUtils.getLambdaPropertyDescriptor(base.getClass(), k));

+        return (LambdaPropertyDescriptor) beanCache.get((String) property);

     }

 }

diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/BeanPropertyTagRule.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/BeanPropertyTagRule.java
index 0216cd7..55615b5 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/BeanPropertyTagRule.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/BeanPropertyTagRule.java
@@ -41,9 +41,9 @@
     @Override
     public Metadata applyRule(String name, TagAttribute attribute, MetadataTarget meta)
     {
-        if (meta instanceof MethodHandleMetadataTargetImpl)
+        if (meta instanceof LambdaMetadataTargetImpl)
         {
-            BiConsumer<Object, Object> f = ((MethodHandleMetadataTargetImpl) meta).getWriteFunction(name);
+            BiConsumer<Object, Object> f = ((LambdaMetadataTargetImpl) meta).getWriteFunction(name);
 
             // if the property is writable
             if (f != null)
diff --git a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodHandleMetadataTargetImpl.java b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LambdaMetadataTargetImpl.java
similarity index 68%
rename from impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodHandleMetadataTargetImpl.java
rename to impl/src/main/java/org/apache/myfaces/view/facelets/tag/LambdaMetadataTargetImpl.java
index 05b3833..ab298e0 100644
--- a/impl/src/main/java/org/apache/myfaces/view/facelets/tag/MethodHandleMetadataTargetImpl.java
+++ b/impl/src/main/java/org/apache/myfaces/view/facelets/tag/LambdaMetadataTargetImpl.java
@@ -24,24 +24,29 @@
 import java.util.Map;

 import java.util.function.BiConsumer;

 import java.util.function.Function;

+import javax.faces.context.FacesContext;

 import javax.faces.view.facelets.MetadataTarget;

-import org.apache.myfaces.util.lang.MethodHandleUtils;

+import org.apache.myfaces.core.api.shared.lang.LambdaPropertyDescriptor;

+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorUtils;

+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorWrapper;

 

-public class MethodHandleMetadataTargetImpl extends MetadataTarget

+public class LambdaMetadataTargetImpl extends MetadataTarget

 {

-    private final Map<String, MethodHandleUtils.LambdaPropertyDescriptor> propertyDescriptors;

+    private final Map<String, ? extends PropertyDescriptorWrapper> propertyDescriptors;

     private final Class<?> type;

 

-    public MethodHandleMetadataTargetImpl(Class<?> type) throws IntrospectionException

+    public LambdaMetadataTargetImpl(Class<?> type) throws IntrospectionException

     {

         this.type = type;

-        this.propertyDescriptors = MethodHandleUtils.getLambdaPropertyDescriptors(type);

+        this.propertyDescriptors = PropertyDescriptorUtils.getCachedPropertyDescriptors(

+                FacesContext.getCurrentInstance().getExternalContext(),

+                type);

     }

 

     @Override

     public PropertyDescriptor getProperty(String name)

     {

-        MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);

+        LambdaPropertyDescriptor lpd = getLambdaProperty(name);

         if (lpd == null)

         {

             return null;

@@ -53,7 +58,7 @@
     @Override

     public Class<?> getPropertyType(String name)

     {

-        MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);

+        LambdaPropertyDescriptor lpd = getLambdaProperty(name);

         if (lpd == null)

         {

             return null;

@@ -65,7 +70,7 @@
     @Override

     public Method getReadMethod(String name)

     {

-        MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);

+        LambdaPropertyDescriptor lpd = getLambdaProperty(name);

         if (lpd == null)

         {

             return null;

@@ -83,7 +88,7 @@
     @Override

     public Method getWriteMethod(String name)

     {

-        MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);

+        LambdaPropertyDescriptor lpd = getLambdaProperty(name);

         if (lpd == null)

         {

             return null;

@@ -98,14 +103,14 @@
         return type.isAssignableFrom(type);

     }

  

-    public MethodHandleUtils.LambdaPropertyDescriptor getLambdaProperty(String name)

+    public LambdaPropertyDescriptor getLambdaProperty(String name)

     {

-        return propertyDescriptors.get(name);

+        return (LambdaPropertyDescriptor) propertyDescriptors.get(name);

     }

 

     public Function<Object, Object> getReadFunction(String name)

     {

-        MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);

+        LambdaPropertyDescriptor lpd = getLambdaProperty(name);

         if (lpd == null)

         {

             return null;

@@ -116,7 +121,7 @@
 

     public BiConsumer<Object, Object> getWriteFunction(String name)

     {

-        MethodHandleUtils.LambdaPropertyDescriptor lpd = getLambdaProperty(name);

+        LambdaPropertyDescriptor lpd = getLambdaProperty(name);

         if (lpd == null)

         {

             return null;

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 e5f7988..aedd5d1 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
@@ -34,7 +34,8 @@
 import java.util.WeakHashMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import org.apache.myfaces.config.MyfacesConfig;
+import javax.faces.context.FacesContext;
+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorUtils;
 import org.apache.myfaces.util.lang.Assert;
 import org.apache.myfaces.view.facelets.PassthroughRule;
 import org.apache.myfaces.view.facelets.tag.jsf.PassThroughLibrary;
@@ -325,9 +326,10 @@
         {
             try
             {
-                if (MyfacesConfig.getCurrentInstance().isUseMethodHandles())
+                if (PropertyDescriptorUtils.isMethodHandlesSupported(
+                        FacesContext.getCurrentInstance().getExternalContext()))
                 {
-                    meta = new MethodHandleMetadataTargetImpl(_type);
+                    meta = new LambdaMetadataTargetImpl(_type);
                 }
                 else
                 {
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 5d4d3ed..e708320 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
@@ -38,17 +38,16 @@
 import java.util.Map;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import org.apache.myfaces.config.MyfacesConfig;
+import org.apache.myfaces.core.api.shared.lang.PropertyDescriptorUtils;
 import org.apache.myfaces.util.lang.Assert;
-import org.apache.myfaces.view.facelets.tag.MethodHandleMetadataTargetImpl;
+import org.apache.myfaces.view.facelets.tag.LambdaMetadataTargetImpl;
 import org.apache.myfaces.view.facelets.tag.NullMetadata;
 
 public class CompositeMetaRulesetImpl extends MetaRuleset
 {
     private final static Logger log = Logger.getLogger(CompositeMetadataTargetImpl.class.getName());
     
-    private static final String METADATA_KEY
-            = "org.apache.myfaces.view.facelets.tag.composite.CompositeMetaRulesetImpl.METADATA";
+    private static final String METADATA_KEY = CompositeMetaRulesetImpl.class.getName() + ".METADATA";
 
     private static Map<String, MetadataTarget> getMetaData()
     {
@@ -215,9 +214,10 @@
         {
             try
             {
-                if (MyfacesConfig.getCurrentInstance().isUseMethodHandles())
+                if (PropertyDescriptorUtils.isMethodHandlesSupported(
+                        FacesContext.getCurrentInstance().getExternalContext()))
                 {
-                    meta = new MethodHandleMetadataTargetImpl(_type);
+                    meta = new LambdaMetadataTargetImpl(_type);
                 }
                 else
                 {