Search code examples
javareflectionenumsmybatisillegalaccessexception

Reflection : The Enum is Public so is The method then Why IlegalAccessException Is Coming?


Please be patient with me while I try to give as much information as possible with me.

I am getting below exception in my code randomly and not always :

ERROR CACHE-SELECT 2015-08-20 11:19:00,822 nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression 'table.selectQuerySuffix'. Cause: org.apache.ibatis.ognl.OgnlException: selectQuerySuffix [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class com.dclear.cmn.core.cache.CacheEnum$4 with modifiers "public"] - 

The Enum defined is as following :

public enum CacheEnum {
    TABLE_NAME() {
        @Override
        public String getSelectQuerySuffix() {
            return "";
        }
    };

private CacheEnum() {
    //some assignment
}

public enum Schema {
    //SCHEMAS DEFINED
}
public enum SchemaName {
    // NAMES
}

public String getSelectQuerySuffix() {
    return "";
}

public enum ColumnEnum {
    //SOME VALUES

    ColumnEnum() {
    }

}

public enum CacheTableSequence {
    //SQs
}

}

'table.selectQuerySuffix' is defined in MyBatis file to put query suffix. And at runtime it is passed as "" (refer overridden method getSelectQuerySuffix())

This issue is not always coming...I have read that

An IllegalAccessException is thrown when an application tries to reflectively create an instance (other than an array), set or get a field, or invoke a method, but the currently executing method does not have access to the definition of the specified class, field, method or constructor.

There are no user-defined class loaders.

But if the issue was coming because constructor CacheEnum is private, why is it not always coming? If not then what is the issue behind it? What am I missing here?


Solution

  • When we define a Enum like follows:

    public enum EnumTest {
    
        ONE, TWO() {
            @Override public String hello() {
                return "World";
            }
        };
    
        public String hello() {
            return "Hello";
        }
    }
    

    Java for TWO creates an anonymous class. I did a disassembly of that and it looks like this:

    class snippet.EnumTest$1 extends snippet.EnumTest {
      snippet.EnumTest$1(java.lang.String, int);
      public java.lang.String hello();
    }
    

    So the Class for TWO is package protected and reflection doesn't work when we actually access TWO's class. Like in getting the Enum object for TWO and getting its class. I suspect that is what is happening in your case. For all cases where the method is not overridden it is working and for those cases where the method is overridden it should be throwing the exception.

    I wrote the below test for checking it out.

    public class EmumReflect {
    
        public static void main(String[] args) throws Exception {
            f1();
            f2();
        }
    
        public static void f1() throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException,
                InvocationTargetException {
            Class<?> forName = Class.forName("snippet.EnumTest");
            Object fOne = forName.getField("ONE").get(null);
            Object fTwo = forName.getField("TWO").get(null);
            Method method = forName.getMethod("hello");
            System.out.println(method.invoke(fOne));
            System.out.println(method.invoke(fTwo));
        }
    
        public static void f2() throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException,
                InvocationTargetException {
            Class<?> forNamex = Class.forName("snippet.EnumTest");
            Object fTwo = forNamex.getField("TWO").get(null);
            Class<?> forName = fTwo.getClass();
            Method method = forName.getMethod("hello");
            System.out.println(method.invoke(fTwo));
        }
    }
    

    If you keep both the class files EnumTest and EnumReflect in the same package, you do not get any exceptions. But if you keep them in different packages, f2() throws the same exception you are getting.