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.
diff --git a/api/src/main/java/jakarta/el/BeanELResolver.java b/api/src/main/java/jakarta/el/BeanELResolver.java
index 7743d6c..fc138c1 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 @@
      */
     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 @@
         BeanProperty beanProperty = getBeanProperty(context, base, property);
         context.setPropertyResolved(true);
         
-        if (isReadOnly || beanProperty.isReadOnly()) {
+        if (isReadOnly || beanProperty.isReadOnly(base)) {
             return null;
         }
         
@@ -329,7 +335,7 @@
             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 @@
             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 @@
             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 @@
             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 2591586..e6cb469 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 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 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 @@
             return null;
         }
 
-        return getMethod(clazz, (Method) result.unWrap());
+        return getMethod(clazz, base, (Method) result.unWrap());
     }
 
     @SuppressWarnings("null")
@@ -557,8 +558,12 @@
      * 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 @@
         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 @@
         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 @@
         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 f9c5276..e0a5714 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 @@
             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);