Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enum.valueOf bug with JDK 7 SE where .values() gets corrupted

Tags:

java

enums

java-7

This seems like a crazy error, but Enum.valueOf(type, name) seems to be unstable on Oracle JDK 7 SE.

The manifestation is that, on the exact same name String (I've verified this), the call to valueOf() sometimes throws IllegalArgumentException with the message No enum constant ....

My team ran this through the Eclipse debugger, and what we noticed is that in the following JDK implementation of valueOf on enum, enumConstantDirectory(), i.e. the values() list for an enum, sometimes seem to be missing some values. Not the entirety of all the values defined in the enum itself.

I can work around the bug by calling Enum.valueOf(enumclass.class, "XXX") for all possible enum values on JVM startup. When I do this, it seems values() always contains the full set.

However, if I don't do this type of initialization, sometimes Enum.valueOf() will throw an IllegalArgumentException.

Context: I'm seeing this problem when using XStream 1.4.4 to convert POJO objects that convert enum, but the problem doesn't seem to be inherently with XStream.

Has anyone seen this kind of error? I would love to hear about it if you have. It boggles my mind. Is this a bug in the Oracle JDK/JVM implementation?

public static <T extends Enum<T>> T valueOf(Class<T> enumType,
                                            String name) {
    T result = enumType.enumConstantDirectory().get(name);
    if (result != null)
        return result;
    if (name == null)
        throw new NullPointerException("Name is null");
    throw new IllegalArgumentException(
        "No enum constant " + enumType.getCanonicalName() + "." + name);
}

Other relevant details:

We are using the org.reflections library to scan all enums in our code at startup. During the scanning, we take the type of the enum, and call clazz.getEnumConstants() on the Class object associated with the enum. This might be a relevant detail.

In looking and the java.lang.Class.getEnumConstants() implementation, it seems to share the same enumConstants shared object within the Class. I wonder if there's a problem with the implementation here.

Our enum is pretty simple, no static initialization, etc.

public enum ScreeningRuleType
{
  INSERT,
  CONFIRMATION,
  AMOUNT,
  EXISTENCE,
  BAN,
  SELECTION,
  CUSTOM; 

  private long id;
  private String descr;

  ScreeningRuleType()
  {
    id = this.ordinal();
    descr= this.toString().replace("_", " ");
  }
}

Edit: In experimenting with this, I'm finding another manifestation. After using the System.out initialization, now instead of throwing IllegalArgumentException, the value returned by Enum.valueOf seems to be random.

This shows what I'm seeing in the Eclipse debugger. It clearly shows I'm calling valueOf() on the string "EXISTENCE" and "EXISTENCE".intern(), and clearly shows it's returning AMOUNT() instead.

Debugger expression

like image 891
zzhu8192 Avatar asked Apr 30 '13 19:04

zzhu8192


2 Answers

I forget where I came across something similar - I suspect however that it was pre-java 7 and may have been back in the Java 4/5 days.

The issue is that the constructing of the accompanying structures that go with the enum is only triggered the first time one of the enums is accessed. Unfortunately, if you call one of the methods that rely on these structures, valueOf or EnumSet.allOf for example before accessing one of the enums it generally fails catastrophically.

Sadly I refactored the code so this would not happen so I no longer have the sample to hand. I will try to reproduce the issue and get back to you.

I see here - Why the Java enum constants initialization is not complete? another occurrence of the issue with a demonstration.

like image 154
OldCurmudgeon Avatar answered Nov 16 '22 03:11

OldCurmudgeon


Actually we found the issue. It's a face + palm moment. We corrupted the enum via reflection API's by attempting a deep (nonshallow) copy of a class containing an enum. This had the effect of wiping out all instance variables of the singleton enum itself. Thanks for all the assistance and suggestions.

like image 4
zzhu8192 Avatar answered Nov 16 '22 04:11

zzhu8192