From de32480c9f9788736521fc9cd70c7837cde02b04 Mon Sep 17 00:00:00 2001 From: Mark Thomas Date: Tue, 7 Jun 2022 17:35:48 +0100 Subject: [PATCH] Fix #188 - allow for module visibility when accessing class methods Implementing class may not be accessible but the interface it implements is. In this case, need to make sure access is via the interface method. --- .../main/java/jakarta/el/BeanELResolver.java | 32 +++++++++++-------- api/src/main/java/jakarta/el/ELUtil.java | 31 +++++++++++++----- .../jakarta/el/StaticFieldELResolver.java | 4 +-- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/api/src/main/java/jakarta/el/BeanELResolver.java b/api/src/main/java/jakarta/el/BeanELResolver.java index 7743d6c1..fc138c1f 100644 --- a/api/src/main/java/jakarta/el/BeanELResolver.java +++ b/api/src/main/java/jakarta/el/BeanELResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2021 Oracle and/or its affiliates and others. + * Copyright (c) 1997, 2022 Oracle and/or its affiliates and others. * All rights reserved. * Copyright 2004 The Apache Software Foundation * @@ -146,29 +146,35 @@ public BeanProperties get(Object key) { */ final static class BeanProperty { + final private Class baseClass; + final private PropertyDescriptor descriptor; private Method readMethod; private Method writeMethod; - private PropertyDescriptor descriptor; public BeanProperty(Class baseClass, PropertyDescriptor descriptor) { + this.baseClass = baseClass; this.descriptor = descriptor; - readMethod = ELUtil.getMethod(baseClass, descriptor.getReadMethod()); - writeMethod = ELUtil.getMethod(baseClass, descriptor.getWriteMethod()); } public Class getPropertyType() { return descriptor.getPropertyType(); } - public boolean isReadOnly() { - return getWriteMethod() == null; + public boolean isReadOnly(Object base) { + return getWriteMethod(base) == null; } - public Method getReadMethod() { + public Method getReadMethod(Object base) { + if (readMethod == null) { + readMethod = ELUtil.getMethod(baseClass, base, descriptor.getReadMethod()); + } return readMethod; } - public Method getWriteMethod() { + public Method getWriteMethod(Object base) { + if (writeMethod == null) { + writeMethod = ELUtil.getMethod(baseClass, base, descriptor.getWriteMethod()); + } return writeMethod; } } @@ -285,7 +291,7 @@ public Class getType(ELContext context, Object base, Object property) { BeanProperty beanProperty = getBeanProperty(context, base, property); context.setPropertyResolved(true); - if (isReadOnly || beanProperty.isReadOnly()) { + if (isReadOnly || beanProperty.isReadOnly(base)) { return null; } @@ -329,7 +335,7 @@ public Object getValue(ELContext context, Object base, Object property) { return null; } - Method method = getBeanProperty(context, base, property).getReadMethod(); + Method method = getBeanProperty(context, base, property).getReadMethod(base); if (method == null) { throw new PropertyNotFoundException( getExceptionMessageString(context, "propertyNotReadable", new Object[] { base.getClass().getName(), property.toString() })); @@ -397,7 +403,7 @@ public void setValue(ELContext context, Object base, Object property, Object val throw new PropertyNotWritableException(getExceptionMessageString(context, "resolverNotwritable", new Object[] { base.getClass().getName() })); } - Method method = getBeanProperty(context, base, property).getWriteMethod(); + Method method = getBeanProperty(context, base, property).getWriteMethod(base); if (method == null) { throw new PropertyNotWritableException( getExceptionMessageString(context, "propertyNotWritable", new Object[] { base.getClass().getName(), property.toString() })); @@ -468,7 +474,7 @@ public Object invoke(ELContext context, Object base, Object methodName, Class return null; } - Method method = ELUtil.findMethod(base.getClass(), methodName.toString(), paramTypes, params, false); + Method method = ELUtil.findMethod(base.getClass(), base, methodName.toString(), paramTypes, params, false); for (Object param : params) { // If the parameters is a LambdaExpression, set the ELContext @@ -529,7 +535,7 @@ public boolean isReadOnly(ELContext context, Object base, Object property) { return true; } - return getBeanProperty(context, base, property).isReadOnly(); + return getBeanProperty(context, base, property).isReadOnly(base); } /** diff --git a/api/src/main/java/jakarta/el/ELUtil.java b/api/src/main/java/jakarta/el/ELUtil.java index 25915863..e6cb4693 100644 --- a/api/src/main/java/jakarta/el/ELUtil.java +++ b/api/src/main/java/jakarta/el/ELUtil.java @@ -18,6 +18,7 @@ package jakarta.el; +import java.lang.reflect.AccessibleObject; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; @@ -198,8 +199,8 @@ static Object invokeConstructor(ELContext context, Constructor constructor, O } } - static Method findMethod(Class klass, String methodName, Class[] paramTypes, Object[] params, boolean staticOnly) { - Method method = findMethod(klass, methodName, paramTypes, params); + static Method findMethod(Class klass, Object base, String methodName, Class[] paramTypes, Object[] params, boolean staticOnly) { + Method method = findMethod(klass, base, methodName, paramTypes, params); if (staticOnly && !Modifier.isStatic(method.getModifiers())) { throw new MethodNotFoundException("Method " + methodName + "for class " + klass + " not found or accessible"); } @@ -221,7 +222,7 @@ static Object invokeMethod(ELContext context, Method method, Object base, Object } } - static Method findMethod(Class clazz, String methodName, Class[] paramTypes, Object[] paramValues) { + static Method findMethod(Class clazz, Object base, String methodName, Class[] paramTypes, Object[] paramValues) { if (clazz == null || methodName == null) { throw new MethodNotFoundException("Method not found: " + clazz + "." + methodName + "(" + paramString(paramTypes) + ")"); } @@ -240,7 +241,7 @@ static Method findMethod(Class clazz, String methodName, Class[] paramType return null; } - return getMethod(clazz, (Method) result.unWrap()); + return getMethod(clazz, base, (Method) result.unWrap()); } @SuppressWarnings("null") @@ -557,8 +558,12 @@ private static Class[] getTypesFromValues(Object[] values) { * for a non-public class that implements a public interface, the read/write methods will be for the class, and * therefore inaccessible. To correct this, a version of the same method must be found in a superclass or interface. */ - static Method getMethod(Class type, Method m) { - if (m == null || Modifier.isPublic(type.getModifiers())) { + static Method getMethod(Class type, Object base, Method m) { + // If base is null, method MUST be static + // If base is non-null, method may be static or non-static + if (m == null || + (Modifier.isPublic(type.getModifiers()) && + (canAccess(base, m) || base != null && canAccess(null, m)))) { return m; } Class[] inf = type.getInterfaces(); @@ -566,7 +571,7 @@ static Method getMethod(Class type, Method m) { for (int i = 0; i < inf.length; i++) { try { mp = inf[i].getMethod(m.getName(), m.getParameterTypes()); - mp = getMethod(mp.getDeclaringClass(), mp); + mp = getMethod(mp.getDeclaringClass(), base, mp); if (mp != null) { return mp; } @@ -578,7 +583,7 @@ static Method getMethod(Class type, Method m) { if (sup != null) { try { mp = sup.getMethod(m.getName(), m.getParameterTypes()); - mp = getMethod(mp.getDeclaringClass(), mp); + mp = getMethod(mp.getDeclaringClass(), base, mp); if (mp != null) { return mp; } @@ -609,6 +614,16 @@ static Constructor getConstructor(Class type, Constructor c) { return null; } + + static boolean canAccess(Object base, AccessibleObject accessibleObject) { + try { + return accessibleObject.canAccess(base); + } catch (IllegalArgumentException iae) { + return false; + } + } + + @SuppressWarnings("null") // params cannot be null when used static Object[] buildParameters(ELContext context, Class[] parameterTypes, boolean isVarArgs, Object[] params) { Object[] parameters = null; diff --git a/api/src/main/java/jakarta/el/StaticFieldELResolver.java b/api/src/main/java/jakarta/el/StaticFieldELResolver.java index f9c52767..e0a5714d 100644 --- a/api/src/main/java/jakarta/el/StaticFieldELResolver.java +++ b/api/src/main/java/jakarta/el/StaticFieldELResolver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2021 Oracle and/or its affiliates and others. + * Copyright (c) 2012, 2022 Oracle and/or its affiliates and others. * All rights reserved. * * This program and the accompanying materials are made available under the @@ -171,7 +171,7 @@ public Object invoke(ELContext context, Object base, Object methodName, Class Constructor constructor = ELUtil.findConstructor(klass, paramTypes, params); ret = ELUtil.invokeConstructor(context, constructor, params); } else { - Method method = ELUtil.findMethod(klass, name, paramTypes, params, true); + Method method = ELUtil.findMethod(klass, base, name, paramTypes, params, true); ret = ELUtil.invokeMethod(context, method, null, params); } context.setPropertyResolved(base, methodName);