2

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?

coretechie
  • 1,050
  • 16
  • 38
  • why is your constructor private? because in your class , there is no publicly accessible constructor. What happens if you change the constructor to public? does this issue still come ? – Amit.rk3 Aug 21 '15 at 11:40
  • What do you mean by _This issue is not always coming_? – Dakshinamurthy Karra Aug 21 '15 at 11:42
  • 1
    The class name 'com.dclear.cmn.core.cache.CacheEnum$4' implies that your enum has anonymous classes. Can show the full CacheEnum source code? – Alexey Gavrilov Aug 21 '15 at 12:04
  • It appears to be able unable to access a member e.g. a field of this class. Are the fields public? – Peter Lawrey Aug 21 '15 at 12:42
  • @Amit.rk3 This is the design. I cannot change the code. – coretechie Aug 21 '15 at 12:46
  • @KDM That is what..The issue is intermittent – coretechie Aug 21 '15 at 12:48
  • @AlexeyGavrilov How did you conclude this information? And this enum doesnt have any other inner classes but yes it has other enums defined. – coretechie Aug 21 '15 at 12:49
  • 1
    @dish My guess is that you are having problem with class loaders. Does the platform/framework you are using define its own class loaders? – Dakshinamurthy Karra Aug 21 '15 at 12:52
  • @KDM I do not see any classes that extend java.lang.Classloader. So I would believe that we do not have a user-defined class loader. – coretechie Aug 21 '15 at 12:57
  • @dish Other enums inside CacheEnum ? Can you show the complete source code? – Alexey Gavrilov Aug 21 '15 at 12:59
  • 1
    `com.dclear.cmn.core.cache.CacheEnum$4` should be referring to the 4th anonymous class in the CacheEnum. Assuming that all of your enum values are like `DD_FEED_FIELD_NF_MAP`, most probably it is the fourth entry. Can you post it? – Dakshinamurthy Karra Aug 21 '15 at 13:04
  • @AlexeyGavrilov I have updated the sample code. – coretechie Aug 21 '15 at 13:15
  • I'm not sure about this, but you're using a template enum. Maybe you should declare the `getSelectQuerySuffix()` method as `abstract` in the enum (not in the value)? – fps Aug 21 '15 at 13:17
  • @KDM No , DD_FEED_FIELD_NF_MAP is same as TABLE_NAME in this code. And it doesnt seem to be 4th entry..also what do you imply by "anonymous class" ? – coretechie Aug 21 '15 at 13:18
  • @Federico Peralta Schaffner Not needed, this is a sample code and there are a few enums who do not override this method. – coretechie Aug 21 '15 at 13:20
  • @KDM I was thinking about Class Loader..as this is an intermittent issue! What could be the possible issue in that case? Where can I debug? – coretechie Aug 21 '15 at 13:23
  • 1
    @dish whenever you are overriding a method from the enum, the compiler should be creating an anonymous class for that element and assigning it. I googled and could find some references to javaassist and class loader in apache.ibatis. I still feel that this is some class loader issue. (I might of course be wrong not knowing anything about ibatis). – Dakshinamurthy Karra Aug 21 '15 at 13:24
  • 1
    If you can, just print the this.getClass().getClassLoader() wherever this is happening. Atleast we can see whether multiple class loaders are being used. – Dakshinamurthy Karra Aug 21 '15 at 13:25
  • @KDM Can you provide those references here? – coretechie Aug 21 '15 at 13:27
  • @KDM Also there are no additional class loaders for sure..none user-defined..now I need to go deeper with this one! – coretechie Aug 21 '15 at 13:33
  • @dish I just did a query for abatis classloader- it not some research. – Dakshinamurthy Karra Aug 21 '15 at 14:09
  • @dish Unfortunately, I believe I misled you in looking for issues with `ClassLoader`. See my answer. – Dakshinamurthy Karra Aug 21 '15 at 18:21

1 Answers1

3

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.

Dakshinamurthy Karra
  • 5,353
  • 1
  • 17
  • 28
  • It seems to be a very good catch, I will try researching more around this now to be sure before releasing this fix! Thanks.. – coretechie Aug 24 '15 at 06:33
  • However, did the exception come for you always? @KDM – coretechie Aug 24 '15 at 06:47
  • Always. from `f2()`. Check whether you get exception only for enums that override your `getSelectQuerySuffix ` method. The code path in framework may be such that sometimes these enums might pass (see `f1()`) and fail only other cases. If you get an exception when you are accessing other enums that don't override `getSelectQuerySuffix` - this is not the issue. – Dakshinamurthy Karra Aug 24 '15 at 06:50
  • Excellent analysis – Jan Święcki Sep 20 '19 at 10:56