10

I have a very simple enumeration

my.package.data.util

public enum Mode
{
    SQLEXPORT, PREVIEW, PRINT
}

Which is used in another class as enums should be used

my.package.program.ComponentController

switch (_mode) { // line 278, _mode is of type my.package.data.util.Mode
case PREVIEW:
    // Do thing for preview
    break;
case SQLEXPORT:
    // Do thing for SQL
    break;
case PRINT:
    // Do thing for print
    break;
default:
    throw new IllegalArgumentException();
}

These two classes are in the same project and are compiled into a jar file.

A web project is then using this library (placed into the WEB-INF/lib folder). But, when the time comes to use this library, and most specifically that switch, a NoClassDefFoundError occurs:

NoClassDefFoundError: my/package/program/ComponentController$1

at my.package.program.ComponentController.doCall(ComponentController.java:278)

This is something I cannot understand on several levels:

  1. Why Java tries to load an inner class (as visible by the $1). There is no inner class present in ComponentController, and never has been.
  2. Why Java thinkgs the switch is using this internal class as its argument
  3. Where the my.package.data.util.Mode class vanished

What is happening here?


Further information not in the original question

  • ComponentController extends another class, SessionBuilder. This class also has no inner classes

I decompiled the ComponentController using javap and tried to find interesting things in the resulting byte code.

It appears that there is indeed an inner class in the byte code:

public class my.package.program.ComponentController extends my.other.package.SessionBuilder
  SourceFile: "ComponentController.java"
  InnerClasses:
       static #192 of #2; //class my/package/program/ComponentController$1 of class my/package/program/ComponentController

This class is used whenever my.package.data.util.Mode is referenced:

#192 = Class              #486          //  my/package/program/ComponentController$1
#193 = Utf8               
#194 = Utf8               InnerClasses
#195 = Utf8               _mode
#196 = Utf8               Lmy/package/data/util/Mode;

And also, when the switch actually occurs:

183: getstatic     #102                // Field my/package/program/ComponentController$1.$SwitchMap$my$package$data$util$Mode:[I
186: aload_0       
187: getfield      #5                  // Field _mode:Lmy/package/data/util/Mode;
190: invokevirtual #103                // Method my/package/data/util/Mode.ordinal:()I
193: iaload        
194: tableswitch   { // 1 to 3
   1: 220
   2: 335
   3: 440
   default: 516
}

Further investigation into the questions linked by Rich produced something interesting: The built jar from the library project differ in the local tomcat installation and the one used to produce the jar for the production server:

Left: jar in WEB-INF/lib via local tomcat by eclipse, Right: jar as built by ANT

JAR Diff

It now seems like the build process used by eclipse when publishing to a local tomcat differs from what ANT does (which is AFAIK just a simple javac call)


Alright, now I just copied the jar created by ANT into the local tomcats WEB-INF/lib and everything works fine. Of course, this means that after every change in the library project, I have to manually copy the new jar to my tomcat lib.

I filed this as a bug report at eclipse and will report back with any news.

F.P
  • 17,421
  • 34
  • 123
  • 189
  • 1
    How did you link the fact that this error is due to this switch ? I think the point of interest in code missing in your question is the line 278 of your ComponentController file. – benzonico Mar 11 '15 at 09:36
  • Because line 278 is `switch (_mode)`, I will add that information to the code in the question – F.P Mar 11 '15 at 09:44
  • Have you tried decompiling the .jar to see what javac has done? It sounds like javac has interned the enum - is it the only place it is used? – Rich Mar 11 '15 at 09:48
  • @Rich I have looked at it using *JD-GUI*, but it unsurprisingly shows nothing interesting. I am currently trying to wade through the Bytecode output from `javap` and see if it helps. I will add anything I find to the question – F.P Mar 11 '15 at 09:50
  • Is it possible that your class file for ComponentController is old version. Did you try rebuild? – talex Mar 11 '15 at 09:54
  • @talex I rebuilt and deployed the tomcat several times, but I will try to remove everything manually again to make sure. The thing is though, this code has not changed in ages, it just now appeared after I tried to run this on my local machine - On the production server this same code runs perfectly fine. – F.P Mar 11 '15 at 10:00
  • No difference in JVM versions between your machine and production, I suppose? – Rich Mar 11 '15 at 10:40
  • @Rich No, unfortunately nothing this easy. I just added even more information I found when inspecting the JARs from production and development – F.P Mar 11 '15 at 10:48
  • @F.P , I am facing the same issue after so many years. Any idea about the fix ? I am using java 1.8 – ysn Apr 16 '21 at 04:27
  • @ysn I'm convinced it was an eclipse specific bug. We've since switched to IntelliJ, so I'm sorry to say that I cannot really recall any solution to this issue – F.P Apr 16 '21 at 12:31

2 Answers2

8

According to this question:

...Sun's Javac 1.6 creates an additional synthetic class each time you use a switch on an Enum.

And according to this question you aren't alone.

Community
  • 1
  • 1
Rich
  • 15,602
  • 15
  • 79
  • 126
  • Okay, I see how this might be done. Then the question is: Why doesn't it work in my case? – F.P Mar 11 '15 at 10:02
2

From my experience, the problem is that this new class is not deployed by Eclipse, but it is generated in the build process.