You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ta...@apache.org on 2020/04/07 19:46:56 UTC

[myfaces] branch master updated: MYFACES-4327 refactored

This is an automated email from the ASF dual-hosted git repository.

tandraschko pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/myfaces.git


The following commit(s) were added to refs/heads/master by this push:
     new dc037e7  MYFACES-4327 refactored
dc037e7 is described below

commit dc037e756e2e86e694371b60150e3f5621370508
Author: Thomas Andraschko <ta...@apache.org>
AuthorDate: Tue Apr 7 21:46:47 2020 +0200

    MYFACES-4327 refactored
---
 .../el/resolver/MethodHandleBeanELResolver.java    | 253 ++-------------------
 .../myfaces/util/lang/MethodHandleUtils.java       | 236 +++++++++++++++++++
 2 files changed, 256 insertions(+), 233 deletions(-)

diff --git a/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java b/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java
index 8bcaa93..3c808d2 100644
--- a/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java
+++ b/impl/src/main/java/org/apache/myfaces/el/resolver/MethodHandleBeanELResolver.java
@@ -18,70 +18,36 @@
  */
 package org.apache.myfaces.el.resolver;
 
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.lang.invoke.CallSite;
-import java.lang.invoke.LambdaConversionException;
-import java.lang.invoke.LambdaMetafactory;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.invoke.MethodType;
-import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.Objects;
 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 java.util.logging.Logger;
 
 import javax.el.BeanELResolver;
 import javax.el.ELContext;
 import javax.el.ELException;
-import javax.el.PropertyNotFoundException;
 import javax.el.PropertyNotWritableException;
-import org.apache.myfaces.bean.ManagedBeanExtension;
+import org.apache.myfaces.util.lang.MethodHandleUtils;
 
 public class MethodHandleBeanELResolver extends BeanELResolver
 {
-    private static final Logger LOG = Logger.getLogger(ManagedBeanExtension.class.getName());
-
-    private static Method privateLookupIn;
-
-    static
-    {
-        try
-        {
-            privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class,
-                    MethodHandles.Lookup.class);
-        }
-        catch (Exception e)
-        {
-            LOG.info("MethodHandles#privateLookupIn not found or accessible. Skip using "
-                    + MethodHandleBeanELResolver.class.getName());
-        }
-    }
-
-    private final ConcurrentHashMap<String, Map<String, PropertyInfo>> cache;
+    private final ConcurrentHashMap<String, Map<String, MethodHandleUtils.LambdaPropertyDescriptor>> cache;
 
     public MethodHandleBeanELResolver()
     {
-        if (privateLookupIn == null)
+        if (MethodHandleUtils.isSupported())
         {
-            cache = null;
+            cache = new ConcurrentHashMap<>(1000);
         }
         else
         {
-            cache = new ConcurrentHashMap<>(1000);
+            cache = null;
         }
     }
 
     @Override
     public Class<?> getType(ELContext context, Object base, Object property)
     {
-        if (privateLookupIn == null)
+        if (!MethodHandleUtils.isSupported())
         {
             return super.getType(context, base, property);
         }
@@ -94,14 +60,14 @@ public class MethodHandleBeanELResolver extends BeanELResolver
 
         context.setPropertyResolved(base, property);
 
-        return getPropertyInfo(base, property).type;
+        return getPropertyDescriptor(base, property).getType();
     }
 
     @SuppressWarnings("unchecked")
     @Override
     public Object getValue(ELContext context, Object base, Object property)
     {
-        if (privateLookupIn == null)
+        if (!MethodHandleUtils.isSupported())
         {
             return super.getValue(context, base, property);
         }
@@ -116,7 +82,7 @@ public class MethodHandleBeanELResolver extends BeanELResolver
 
         try
         {
-            return getPropertyInfo(base, property).getter.apply(base);
+            return getPropertyDescriptor(base, property).getGetter().apply(base);
         }
         catch (Exception e)
         {
@@ -128,7 +94,7 @@ public class MethodHandleBeanELResolver extends BeanELResolver
     @Override
     public void setValue(ELContext context, Object base, Object property, Object value)
     {
-        if (privateLookupIn == null)
+        if (!MethodHandleUtils.isSupported())
         {
             super.setValue(context, base, property, value);
             return;
@@ -142,8 +108,8 @@ public class MethodHandleBeanELResolver extends BeanELResolver
 
         context.setPropertyResolved(base, property);
 
-        PropertyInfo propertyInfo = getPropertyInfo(base, property);
-        if (propertyInfo.setter == null)
+        MethodHandleUtils.LambdaPropertyDescriptor propertyDescriptor = getPropertyDescriptor(base, property);
+        if (propertyDescriptor.getSetter() == null)
         {
             throw new PropertyNotWritableException("Property \"" + (String) property
                     + "\" in \"" + base.getClass().getName() + "\" is not writable!");
@@ -151,7 +117,7 @@ public class MethodHandleBeanELResolver extends BeanELResolver
 
         try
         {
-            propertyInfo.setter.accept(base, value);
+            propertyDescriptor.getSetter().accept(base, value);
         }
         catch (Exception e)
         {
@@ -160,17 +126,9 @@ public class MethodHandleBeanELResolver extends BeanELResolver
     }
 
     @Override
-    public Object invoke(ELContext context, Object base, Object method, Class<?>[] paramTypes, Object[] params)
-    {
-        // just use the original BeanELResolver method
-        // it's higher effort to implement this
-        return super.invoke(context, base, method, paramTypes, params);
-    }
-
-    @Override
     public boolean isReadOnly(ELContext context, Object base, Object property)
     {
-        if (privateLookupIn == null)
+        if (!MethodHandleUtils.isSupported())
         {
             return super.isReadOnly(context, base, property);
         }
@@ -183,13 +141,13 @@ public class MethodHandleBeanELResolver extends BeanELResolver
 
         context.setPropertyResolved(base, property);
 
-        return getPropertyInfo(base, property).setter == null;
+        return getPropertyDescriptor(base, property).getSetter() == null;
     }
 
     @Override
     public Class<?> getCommonPropertyType(ELContext context, Object base)
     {
-        if (privateLookupIn == null)
+        if (!MethodHandleUtils.isSupported())
         {
             return super.getCommonPropertyType(context, base);
         }
@@ -202,182 +160,11 @@ public class MethodHandleBeanELResolver extends BeanELResolver
         return null;
     }
 
-    protected class PropertyInfo
-    {
-        Class<?> type;
-        Function getter;
-        BiConsumer setter;
-    }
-
-    protected PropertyInfo getPropertyInfo(Object base, Object property)
+    protected MethodHandleUtils.LambdaPropertyDescriptor getPropertyDescriptor(Object base, Object property)
     {
-        Map<String, PropertyInfo> beanCache = cache.computeIfAbsent(base.getClass().getName(),
-                k -> new ConcurrentHashMap<>());
-        return beanCache.computeIfAbsent((String) property, k -> initPropertyInfo(base.getClass(), k));
+        Map<String, MethodHandleUtils.LambdaPropertyDescriptor> beanCache = cache.computeIfAbsent(
+                base.getClass().getName(), k -> MethodHandleUtils.getLambdaPropertyDescriptors(base.getClass()));
+        return beanCache.get((String) property);
     }
 
-    protected PropertyInfo initPropertyInfo(Class<?> target, String fieldName)
-    {
-
-        PropertyInfo info = new PropertyInfo();
-
-        try
-        {
-            PropertyDescriptor pd = null;
-            for (PropertyDescriptor cpd : Introspector.getBeanInfo(target, Object.class).getPropertyDescriptors())
-            {
-                if (fieldName.equals(cpd.getName()))
-                {
-                    pd = cpd;
-                }
-            }
-
-            if (pd == null)
-            {
-                throw new PropertyNotFoundException("Property \"" + fieldName + "\" not found on \""
-                        + target.getName() + "\"");
-            }
-
-            info.type = pd.getPropertyType();
-
-            MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupIn.invoke(null, target,
-                    MethodHandles.lookup());
-
-            Method getter = pd.getReadMethod();
-            if (getter != null)
-            {
-                MethodHandle getterHandle = lookup.unreflect(getter);
-                CallSite getterCallSite = LambdaMetafactory.metafactory(lookup,
-                        "apply",
-                        MethodType.methodType(Function.class),
-                        MethodType.methodType(Object.class, Object.class),
-                        getterHandle,
-                        getterHandle.type());
-                info.getter = (Function) getterCallSite.getTarget().invokeExact();
-            }
-
-            Method setter = pd.getWriteMethod();
-            if (setter != null)
-            {
-                MethodHandle setterHandle = lookup.unreflect(setter);
-                info.setter = createSetter(lookup, info, setterHandle);
-            }
-        }
-        catch (Throwable e)
-        {
-            throw new ELException(e);
-        }
-
-        return info;
-    }
-
-    @SuppressWarnings("unchecked")
-    protected BiConsumer createSetter(MethodHandles.Lookup lookup, PropertyInfo propertyInfo,
-            MethodHandle setterHandle)
-            throws LambdaConversionException, Throwable
-    {
-        // special handling for primitives required, see https://dzone.com/articles/setters-method-handles-and-java-11
-        if (propertyInfo.type.isPrimitive())
-        {
-            if (propertyInfo.type == double.class)
-            {
-                ObjDoubleConsumer consumer = (ObjDoubleConsumer) createSetterCallSite(
-                        lookup, setterHandle, ObjDoubleConsumer.class, double.class).getTarget().invokeExact();
-                return (a, b) -> consumer.accept(a, (double) b);
-            }
-            else if (propertyInfo.type == int.class)
-            {
-                ObjIntConsumer consumer = (ObjIntConsumer) createSetterCallSite(
-                        lookup, setterHandle, ObjIntConsumer.class, int.class).getTarget().invokeExact();
-                return (a, b) -> consumer.accept(a, (int) b);
-            }
-            else if (propertyInfo.type == long.class)
-            {
-                ObjLongConsumer consumer = (ObjLongConsumer) createSetterCallSite(
-                        lookup, setterHandle, ObjLongConsumer.class, long.class).getTarget().invokeExact();
-                return (a, b) -> consumer.accept(a, (long) b);
-            }
-            else if (propertyInfo.type == float.class)
-            {
-                ObjFloatConsumer consumer = (ObjFloatConsumer) createSetterCallSite(
-                        lookup, setterHandle, ObjFloatConsumer.class, float.class).getTarget().invokeExact();
-                return (a, b) -> consumer.accept(a, (float) b);
-            }
-            else if (propertyInfo.type == byte.class)
-            {
-                ObjByteConsumer consumer = (ObjByteConsumer) createSetterCallSite(
-                        lookup, setterHandle, ObjByteConsumer.class, byte.class).getTarget().invokeExact();
-                return (a, b) -> consumer.accept(a, (byte) b);
-            }
-            else if (propertyInfo.type == char.class)
-            {
-                ObjCharConsumer consumer = (ObjCharConsumer) createSetterCallSite(
-                        lookup, setterHandle, ObjCharConsumer.class, char.class).getTarget().invokeExact();
-                return (a, b) -> consumer.accept(a, (char) b);
-            }
-            else if (propertyInfo.type == short.class)
-            {
-                ObjShortConsumer consumer = (ObjShortConsumer) createSetterCallSite(
-                        lookup, setterHandle, ObjShortConsumer.class, short.class).getTarget().invokeExact();
-                return (a, b) -> consumer.accept(a, (short) b);
-            }
-            else if (propertyInfo.type == boolean.class)
-            {
-                ObjBooleanConsumer consumer = (ObjBooleanConsumer) createSetterCallSite(
-                        lookup, setterHandle, ObjBooleanConsumer.class, boolean.class).getTarget().invokeExact();
-                return (a, b) -> consumer.accept(a, (boolean) b);
-            }
-            else
-            {
-                throw new RuntimeException("Type is not supported yet: " + propertyInfo.type.getName());
-            }
-        }
-        else
-        {
-            return (BiConsumer) createSetterCallSite(lookup, setterHandle, BiConsumer.class, Object.class).getTarget()
-                    .invokeExact();
-        }
-    }
-
-    protected CallSite createSetterCallSite(MethodHandles.Lookup lookup, MethodHandle setter, Class<?> interfaceType,
-            Class<?> valueType)
-            throws LambdaConversionException
-    {
-        return LambdaMetafactory.metafactory(lookup,
-                "accept",
-                MethodType.methodType(interfaceType),
-                MethodType.methodType(void.class, Object.class, valueType),
-                setter,
-                setter.type());
-    }
-
-    @FunctionalInterface
-    public interface ObjFloatConsumer<T extends Object>
-    {
-        public void accept(T t, float i);
-    }
-
-    @FunctionalInterface
-    public interface ObjByteConsumer<T extends Object>
-    {
-        public void accept(T t, byte i);
-    }
-
-    @FunctionalInterface
-    public interface ObjCharConsumer<T extends Object>
-    {
-        public void accept(T t, char i);
-    }
-
-    @FunctionalInterface
-    public interface ObjShortConsumer<T extends Object>
-    {
-        public void accept(T t, short i);
-    }
-
-    @FunctionalInterface
-    public interface ObjBooleanConsumer<T extends Object>
-    {
-        public void accept(T t, boolean i);
-    }
 }
diff --git a/impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java b/impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java
new file mode 100644
index 0000000..df62baa
--- /dev/null
+++ b/impl/src/main/java/org/apache/myfaces/util/lang/MethodHandleUtils.java
@@ -0,0 +1,236 @@
+/*
+ * 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.util.lang;
+
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.invoke.CallSite;
+import java.lang.invoke.LambdaConversionException;
+import java.lang.invoke.LambdaMetafactory;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+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.el.ELException;
+
+public class MethodHandleUtils
+{
+    private static Method privateLookupIn;
+
+    static
+    {
+        try
+        {
+            privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class,
+                    MethodHandles.Lookup.class);
+        }
+        catch (Exception e)
+        {
+        }
+    }
+
+    public static boolean isSupported()
+    {
+        return privateLookupIn != null;
+    }
+    
+    public static class LambdaPropertyDescriptor
+    {
+        private Class<?> type;
+        private Function getter;
+        private BiConsumer setter;
+
+        public Class<?> getType()
+        {
+            return type;
+        }
+
+        public Function getGetter()
+        {
+            return getter;
+        }
+
+        public BiConsumer getSetter()
+        {
+            return setter;
+        }
+    }
+
+    public static HashMap<String, LambdaPropertyDescriptor> getLambdaPropertyDescriptors(Class<?> target)
+    {
+        try
+        {            
+            PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(target).getPropertyDescriptors();
+            HashMap<String, LambdaPropertyDescriptor> properties = new HashMap<>(propertyDescriptors.length);
+
+            for (PropertyDescriptor pd : Introspector.getBeanInfo(target).getPropertyDescriptors())
+            {
+                LambdaPropertyDescriptor lpd = new LambdaPropertyDescriptor();
+                lpd.type = pd.getPropertyType();
+                
+                MethodHandles.Lookup lookup = (MethodHandles.Lookup) privateLookupIn.invoke(null, target,
+                        MethodHandles.lookup());
+
+                Method getter = pd.getReadMethod();
+                if (getter != null)
+                {
+                    MethodHandle getterHandle = lookup.unreflect(getter);
+                    CallSite getterCallSite = LambdaMetafactory.metafactory(lookup,
+                            "apply",
+                            MethodType.methodType(Function.class),
+                            MethodType.methodType(Object.class, Object.class),
+                            getterHandle,
+                            getterHandle.type());
+                    lpd.getter = (Function) getterCallSite.getTarget().invokeExact();
+                }
+
+                Method setter = pd.getWriteMethod();
+                if (setter != null)
+                {
+                    MethodHandle setterHandle = lookup.unreflect(setter);
+                    lpd.setter = createSetter(lookup, lpd, setterHandle);
+                }
+                
+                properties.put(pd.getName(), lpd);
+            }
+            
+            return properties;
+        }
+        catch (Throwable e)
+        {
+            throw new ELException(e);
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    protected static BiConsumer createSetter(MethodHandles.Lookup lookup, LambdaPropertyDescriptor propertyInfo,
+            MethodHandle setterHandle)
+            throws LambdaConversionException, Throwable
+    {
+        // special handling for primitives required, see https://dzone.com/articles/setters-method-handles-and-java-11
+        if (propertyInfo.type.isPrimitive())
+        {
+            if (propertyInfo.type == double.class)
+            {
+                ObjDoubleConsumer consumer = (ObjDoubleConsumer) createSetterCallSite(
+                        lookup, setterHandle, ObjDoubleConsumer.class, double.class).getTarget().invokeExact();
+                return (a, b) -> consumer.accept(a, (double) b);
+            }
+            else if (propertyInfo.type == int.class)
+            {
+                ObjIntConsumer consumer = (ObjIntConsumer) createSetterCallSite(
+                        lookup, setterHandle, ObjIntConsumer.class, int.class).getTarget().invokeExact();
+                return (a, b) -> consumer.accept(a, (int) b);
+            }
+            else if (propertyInfo.type == long.class)
+            {
+                ObjLongConsumer consumer = (ObjLongConsumer) createSetterCallSite(
+                        lookup, setterHandle, ObjLongConsumer.class, long.class).getTarget().invokeExact();
+                return (a, b) -> consumer.accept(a, (long) b);
+            }
+            else if (propertyInfo.type == float.class)
+            {
+                ObjFloatConsumer consumer = (ObjFloatConsumer) createSetterCallSite(
+                        lookup, setterHandle, ObjFloatConsumer.class, float.class).getTarget().invokeExact();
+                return (a, b) -> consumer.accept(a, (float) b);
+            }
+            else if (propertyInfo.type == byte.class)
+            {
+                ObjByteConsumer consumer = (ObjByteConsumer) createSetterCallSite(
+                        lookup, setterHandle, ObjByteConsumer.class, byte.class).getTarget().invokeExact();
+                return (a, b) -> consumer.accept(a, (byte) b);
+            }
+            else if (propertyInfo.type == char.class)
+            {
+                ObjCharConsumer consumer = (ObjCharConsumer) createSetterCallSite(
+                        lookup, setterHandle, ObjCharConsumer.class, char.class).getTarget().invokeExact();
+                return (a, b) -> consumer.accept(a, (char) b);
+            }
+            else if (propertyInfo.type == short.class)
+            {
+                ObjShortConsumer consumer = (ObjShortConsumer) createSetterCallSite(
+                        lookup, setterHandle, ObjShortConsumer.class, short.class).getTarget().invokeExact();
+                return (a, b) -> consumer.accept(a, (short) b);
+            }
+            else if (propertyInfo.type == boolean.class)
+            {
+                ObjBooleanConsumer consumer = (ObjBooleanConsumer) createSetterCallSite(
+                        lookup, setterHandle, ObjBooleanConsumer.class, boolean.class).getTarget().invokeExact();
+                return (a, b) -> consumer.accept(a, (boolean) b);
+            }
+            else
+            {
+                throw new RuntimeException("Type is not supported yet: " + propertyInfo.type.getName());
+            }
+        }
+        else
+        {
+            return (BiConsumer) createSetterCallSite(lookup, setterHandle, BiConsumer.class, Object.class).getTarget()
+                    .invokeExact();
+        }
+    }
+
+    protected static CallSite createSetterCallSite(MethodHandles.Lookup lookup, MethodHandle setter,
+            Class<?> interfaceType, Class<?> valueType)
+            throws LambdaConversionException
+    {
+        return LambdaMetafactory.metafactory(lookup,
+                "accept",
+                MethodType.methodType(interfaceType),
+                MethodType.methodType(void.class, Object.class, valueType),
+                setter,
+                setter.type());
+    }
+
+    @FunctionalInterface
+    public interface ObjFloatConsumer<T extends Object>
+    {
+        public void accept(T t, float i);
+    }
+
+    @FunctionalInterface
+    public interface ObjByteConsumer<T extends Object>
+    {
+        public void accept(T t, byte i);
+    }
+
+    @FunctionalInterface
+    public interface ObjCharConsumer<T extends Object>
+    {
+        public void accept(T t, char i);
+    }
+
+    @FunctionalInterface
+    public interface ObjShortConsumer<T extends Object>
+    {
+        public void accept(T t, short i);
+    }
+
+    @FunctionalInterface
+    public interface ObjBooleanConsumer<T extends Object>
+    {
+        public void accept(T t, boolean i);
+    }
+}