2

I have a java project that used maven. I also use the M2Eclipse plugin so I can use eclipse with the project. I also use JRebel so that class changes are automatically reflected in a running server.

The problem is that I sometimes compile my project in maven and I sometimes compile it inside eclipse (more accurately, eclipse auto-compiles the class files whenever I save a java file). The eclipse and maven compiler's end up producing different class files for identical java source files, so JRebel is often needlessly reloading classes. Sometimes hundreds of class files get reloaded even though their code hasn't actually changed. It's just been re-compiled by one system or the other.

For example, this empty class:

public class Stack {

}

When compiled with maven, produces the following structure:

$ javap -verbose Stack.class 
Classfile /tmp/Stack.class
  Last modified Apr 21, 2016; size 240 bytes
  MD5 checksum faf24015026a9cc09caa2c7388930d9a
  Compiled from "Stack.java"
public class Stack
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #3.#13         // java/lang/Object."<init>":()V
   #2 = Class              #14            // Stack
   #3 = Class              #15            // java/lang/Object
   #4 = Utf8               <init>
   #5 = Utf8               ()V
   #6 = Utf8               Code
   #7 = Utf8               LineNumberTable
   #8 = Utf8               LocalVariableTable
   #9 = Utf8               this
  #10 = Utf8               LStack;
  #11 = Utf8               SourceFile
  #12 = Utf8               Stack.java
  #13 = NameAndType        #4:#5          // "<init>":()V
  #14 = Utf8               Stack
  #15 = Utf8               java/lang/Object
{
  public Stack();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LStack;
}
SourceFile: "Stack.java"

And when compiled with eclipse, produces:

$ javap -verbose Stack.class 
Classfile /tmp/Stack.class
  Last modified Apr 21, 2016; size 240 bytes
  MD5 checksum 0d578ab592aebbb947fa85de391019e5
  Compiled from "Stack.java"
public class Stack
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // Stack
   #2 = Utf8               Stack
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
   #9 = NameAndType        #5:#6          // "<init>":()V
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LStack;
  #14 = Utf8               SourceFile
  #15 = Utf8               Stack.java
{
  public Stack();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LStack;
}
SourceFile: "Stack.java"

Is there a way I can get these two systems to compile to the same class file from the same source file so that JRebel doesn't needlessly reload classes?

Edit Regarding Possible Duplicate:

I don't believe this is an exact duplicate of this question. That question was whether using the ecj compiler from maven was the right way to go for debugging reasons; how to configure it to work with profiles; and can it even be done. My question is just how to get JRebel to not reload classes just because they were re-compiled by javac/ecj when there were no code changes.

The solution to my question could end up being similar to the answer for the other question (eg: configure maven to use ecj), or it could be different (eg configure eclipse to use javac; or something else entirely).

onlynone
  • 7,602
  • 3
  • 31
  • 50
  • I guess the problem is that eclipse is not using javac; as maven is. I "solved" this problem in a different way ... I have only a "dummy" project enabled for JRebel ... and I use soft links in the file system and only add those java sources files to the dummy project that I want JRebel to upload. – GhostCat Apr 21 '16 at 14:48
  • 3
    [This question](http://stackoverflow.com/q/33164976/2670892) talks about using the Eclipse ecj compiler in maven. – greg-449 Apr 21 '16 at 14:49
  • @greg-449 I think that solved question/answer solved it! I just tried compiling with maven and it produced an identical class file to the one produced by eclipse. Is there some way to convert your comment to an answer so I can accept it? – onlynone Apr 21 '16 at 14:57
  • My comment can't really be an answer as it would be 'link only' – greg-449 Apr 21 '16 at 15:07
  • 1
    @E-Riz can you address my edits about why this question isn't necessarily a duplicate. – onlynone Apr 22 '16 at 15:57

2 Answers2

2

To answer the question directly: No, javac and ecj are different compilers and thus will not produce identical output. They will, barring bugs of course, produce compatible output. But this question isn't about compatibility, it's really a question about playing nice with different build triggers and JRebel. I don't know JRebel so I can't suggest ways to make it play nicer in this situation.

As suggested by @gregg-449, one workaround is to configure Maven to use ecj, as outlined in this answer.

Community
  • 1
  • 1
E-Riz
  • 31,431
  • 9
  • 97
  • 134
0

It's not just the fact that they're different; the problem is the class file will be saved, compiled, and then reloaded when you're editing it in Eclipse (that, after all, is its point). So even if the content of the class file weren't to change semantically it would still be reloaded.

AlBlue
  • 23,254
  • 14
  • 71
  • 91
  • But I guess is the whole point: you want files that you SAVE to be uploaded. And you tolerate that for one file. His point is that when you first enable JRebel, it well upload **all** files it finds different. And if 99% of your files are "semantically" the same; that hurts. – GhostCat Apr 21 '16 at 14:54
  • Yeah, if a single file is reloaded, that's fine, but if hundreds are reloading at once, it will a: bring the server to a crawl and end up being slower than just rebooting the server (what jrebel is there to prevent), and b: usually not work anyway as there are complications when reloading that much code at once. As long as the same source file generates the same class file, I should be good since I can use the `rebel.check_class_hash=true` setting to avoid reloading identical class files even when they're regenerated. – onlynone Apr 21 '16 at 14:59