Hotswap/DCEVM doesn't work in Intellij IDEA (Community Version) - java

I have troubles in making use of the hotswap function in Intellij IDEA Community Version. Mine is v 14.1.4.
Each time after I fired off debugging and change the java code, I have already click rebuild project and press "Yes" on confirming reload classes. Intellij reports that changed classes are reloaded, but the application outcome is the same as before. I'm just trying the simplest Java application (i.e. not in scenarios like Tomcat, applet etc) with stuffs simply like System.out.println, string concats etc. What I've changed during debug mode is just method body codes, but not the method signature/name. I can't get it.
In Eclipse I just directly change the code and press save, then it just works.
What went wrong?
(Remarks:
In fact I'm attempting to use DCEVM which makes structure change possible (e.g. change class name, method name, add methods etc), thought that it would solve the problem of the hotswap problem found in Intellij. Needless to say, it didn't work.
In eclipse, I succeed in using DCEVM and can change the method names during debugging.
I further try hotswap-agent and it still didn't work; I've come across an article saying that the IDE must JDPA-connect to the JVM thru port 5000, but no matter how I tried, Intellij console shows that it is still connecting thru a random port (51018 below):
"C:\Program Files\Java\jdk1.8.0_60\bin\java" -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:51018...."
Connected to the target VM, address: '127.0.0.1:51018', transport: 'socket'
Is it possible to force it to connect thru one specific port?
Adding the DEBUG_OPT environment variable in the Run/Debug Config doesn't work)

Found out that it is Intellij's by-design behaviour after finding one feedback from Jetbrains to an issue request:
In other words, the problem is related to how I test out the hotswapping:
public class Main {
// /*
public static String getName() {
return "James"; // <=== (2)
}
//*/
public static void main(String[] args) {
System.out.println("Hello " + getName()); // <=== (1)
}
}
As Intellij's behaviour is that "the old code is still used until the VM exits the obsolete stack frame" (a different behaviour comparing to Eclipse), if you change "Hello" to "Bye" at (1), the new code will never be executed - the new code can be executed again only when main() is called the second time, which is impossible as the application is terminated already
If it is (2) that is changed (say, replacing "James" w/ "Sean") instead of (1), during the time when the execution cursor is being stopped by a breakpoint placed at (1) (therefore haven't entered to getName() yet), and you reload the class, you will get the new code being run (printing "Sean")
DCEVM worked perfectly too, using the same way to test the hotswapping
You can also use "drop frame" in the stack trace window to make the current statement roll back to the method's beginning (except main()) - in fact it's the same behaviour in Eclipse.

Related

IntelliJ IDEA breakpoints do not hit in dynamically loaded anonymous inner class

This is not your usual "my breakpoints don't work" question.
Consider the following code:
Runnable runnable = new Runnable()
{
#Override public void run()
{
Log.debug( "in run()" ); // <-- place one breakpoint here
}
};
#Test public void test()
{
Log.debug( "in test()" ); // <-- place another breakpoint here
runnable.run();
}
If you were to run this test from within IntellijIdea, using IntellijIdea's built-in JUnit support, the following things would happen:
Both logging statements produce output.
Both breakpoints hit.
However:
If you were to run this test from within some other framework, (e.g. Testana) which discovers the test class at runtime, loads it dynamically, and executes each test method in it, then the following happens:
Both logging statements produce output.
The breakpoint in the test() method hits.
The breakpoint in the run() method does not hit.
As a matter of fact, when the breakpoint in the test() method hits, you can see that the breakpoint in the run() method remains a red circle without a checkmark, which means that IntellijIdea does not recognize it as being on executable code.
Just in case it matters, I am currently using macOS, in a few days I am hoping to be able to try under Windows.
There were a couple of similar issues in IntellijIdea reported and fixed a long time ago:
https://youtrack.jetbrains.com/issue/IDEA-79268 (10 years ago)
https://youtrack.jetbrains.com/issue/IDEA-133881 (7 years ago)
Judging by a comment by CrazyCoder (a well known JetBrainiac on Stackoverflow) from May 29 '13 at 13:11 on this question Line breakpoints don't work in some classes which mentions some "debug scope" I suspect that the problem is something along these lines:
The testing framework is launched with its own classpath which does not include the test class.
The testing framework discovers the module containing the test class, creates a new ClassLoader with the class path of the module, uses that ClassLoader to load the test class, and runs the test methods in it.
The IntellijIdea debugger somehow detects that the test class was dynamically loaded, and includes it in whatever that "debug scope" is, so the breakpoint in the test() method hits.
The IntellijIdea debugger fails to detect that the anonymous inner class is also loaded, so it fails to include it in the "debug scope", so the breakpoint in the run() method does not hit.
And now the question:
Is there any workaround that would make the IntellijIdea debugger hit the breakpoint in the anonymous inner class?
Ideally, the workaround would be a general-purpose solution that can be implemented in the testing framework to take care of any similar situation.
A workaround that would make breakpoints work in anonymous inner classes by extra bureaucracy on the side of the test class would also be (barely) acceptable.
(But if you were going to suggest that I convert my anonymous inner class to a separate top-level class, please don't.)
EDIT
Behavior is same on Windows.
Steps to reproduce:
Check out this project: https://github.com/mikenakis/Public
Go to class T01_CompilingIntertwine
Place a breakpoint on line 57 (first line of function run())
Hit your Debug key to bring up the run configurations dialog
There will be a run configuration called Testana - All; launch it.
The breakpoint will not hit.
Make a minor modification to the file and save it (because testana does not run tests that have not changed.)
Place a breakpoint on line 53 (new Runnable())
Relaunch (Debug) Testana - All.
The breakpoint on line 53 will hit. If you resume, then the breakpoint on line 57 will also hit.
So, Konstantin Annikov from JetBrains found this worthy of creating an new issue for it on the IntelliJ IDEA issue tracker, see https://youtrack.jetbrains.com/issue/IDEA-287858
Then, Egor Ushakov from JetBrains looked into it, and found that this is happening because the testing framework is loading the classes in a non-standard order, while IntelliJ IDEA contains some logic that relies on the assumption that the classes will be loaded in the standard order.
The standard order of loading classes (when classes are being loaded by their classloader as they are being executed) is to have the outermost class loaded first, and the inner classes loaded afterwards.
Testana was loading the classes in the order in which the class files are yielded by java.nio.file.Files.walkFileTree(), which is apparently alphabetic, and it just so happens that $ sorts before ., so Testana was loading the inner classes first, and the outermost class last.
I fixed the problem in Testana, and Egor expressed the intention to try and implement a workaround for this case in IntelliJ IDEA. (This is such an edge case that I am not sure it is worth fixing, but anyway, it is his call.)

How can I prevent Intellij IDEA to reparse my code at each keystroke?

I write Java code in the IntelliJ IDEA IDE and it seems to me that each time I hit a key it reparses (evaluates) my whole sources.
To be clear, when I want to write this:
public class MyApp {
public static void main(String arg[])
{
System.out.println("hello");
}
}
and I am at this point ([CARET/CURSOR] = position of current):
public class MyApp {
public static void main(String arg[])
{
System.out.prin[CARET/CURSOR]
}
}
IDEA alraedy tells me already the helpful message:
Cannot resolve symbol 'prin'.';' expected
I have tried very hard to get IDEA not to do this (as obviously while I still type the source line,I do not want this unfinished work to be compiled/evaluate)
How can I get IDEA to not do this? Not only do those idioticly pointless message disturb, they are also making typing a pain, as the responsiveness is nil, while the CPU searches the unfinished code.
Addition/Update
This picture below illustrates the problem, i.e. that the inspection is rerun at each keystroke:
In the mean time I have used an open source Java profiler https://visualvm.github.io/, available form my distro's package manager. Which has (as suggested in the comments) helped in finding out, that the delay and slowness incurred to inspection, is not the parsing in the first place, but rather the yet imperfect wayland linux display server support of IntelliJ, which recurring to an indirection via Xwayland, has slowed down the delay between "finger hits key" to "glyph is added to source" to unbearable limits.
The Inspection feature of the IntelliJ IDEA IDE, can be adjusted at different places within the IDE. The comment by #MonteChristo with turning on File -> Power Save Mode has indeed already led into the right direction, to prevent IntelliJ IDEA from parsing the code at each keystroke.
There is however another setting which can be found as illustrated here:
where then it is possible to swiftly reduce or inhibit inspection alltogether, using this settings menu.
As visible the connection to power save mode which is maybe the quickest workaround persists.
Also please note that even though inspection gets disabled, results from previous inspection runs (i.e. red underlines, red text) will remain.
It seems to me that according to my test only the setting power save mode was really able to completely disable the parsing at each keystroke, while all other settings kept the code parsed at each keystroke.

Cannot find local variable 'ac'

Below my code but it is not working - refer to this screenshot of the error during debugging,
Cannot find local variable 'ac'
AccountManager am = AccountManager.get(this);
Account[] accounts = am.getAccounts();
for (final Account ac : accounts)
{
String acname = ac.name;
System.out.println("Accounts : " + acname);
}
From your screenshot I saw that you are using Android Studio and there is no problem with your code, but rather some debug settings. If you'll open Android Studio preferences and go to Build, Execution, Deployment > Debugger > Data Views you'll probably see the option Enable auto expressions in Variables view ticked, since it is ticked by default. It should look something like this:
Now, if you'll check the IntelliJ docs for that you'll find this (note that IntelliJ and Android Studio are running on the same engine in case that you are wondering why I said about IntelliJ docs):
Select this option if you want the IntelliJ IDEA debugger to automatically evaluate expressions and show the corresponding values in the Variables pane of the Debug tool window.
The debugger analyzes the context near the breakpoint (the current statement, one statement before, and one after). It does so to find various expressions in the source code (if available) such as, for example, myvar.myfield.
If such expressions don't contain explicit method invocations, the debugger evaluates them and shows the corresponding values in the Variables view.
Basically, they are saying that the IDE will check the code around a breakpoint to identify the variables and compute their values (method calls not included). That being said, when the control reaches the line where accounts variable is declared, the IDE will check the code for variables and will found the ac variable, but will fail to compute its values since at the execution point that variable isn't declared yet, hence the entire operation will end with the message that the variable cannot be found.
To fix this, you have to uncheck that option from settings or you can leave it just like this, since it will not affect your code (it is fully functional right now).

slow (JavaFX) code even after reseting the repository

I just faced a strange behavior and I can't even say if it is a JavaFX specific problem, Eclipse problem or even where to start fixing first.
But first things first: I'm writing a JavaFX application (Eclipse Kepler/ Java 1.7), containing some extended ListViews. The program takes about 1sec to load.
The extended ListViews look something like that:
public class NewList<T extends Obj> extends ListView<T>{
public NewList(){
// addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler<KeyEvent>(){
//
// #Override
// public void handle(KeyEvent arg0) {
// if(arg0.getText().equals(KeyCode.DELETE)){
// getItems().remove(getSelectionModel().getSelectedItem());
// }
// }
// });
}
}
There are also some MouseEvents (like drag and drop) inside the same constructor, and they are working as expected.
Now I want to add an EventHandler to my extended ListView (see commend in the code above).
The code takes now about 8sec to start aka 8 times longer as normal. To my surprise 90% of the loading time the program is inside the .show() method of my primaryStage (there is just one). After the loading time, the GUI is extreme slow and the cpu usage is on 100%.
Maybe the Handler is implemented wrongly and is doing some strange stuff during the whole time.
I can't find any solution or even comparable problem on the internet....but now the real problem just begun:
When I delete the Handler and run the program again, it will stay slow! So the program is in the same state as before - just still broken! That makes me crazy, because I can't see any logical explanation for that behavior.
What I have already tried:
delete the bin folder inside the repository and build the code again (+reboot)
reset the repository via git
throw half of the code away and started again. At any point it worked again, unfortunately I was not able to reproduce the effect. Then I implemented the Handler again and the problems started from the beginning...
edit: it looks like there goes something wrong during the building process/updating the binarys. I deleted 99% of the code (>5k LOC), then it worked. I copy/pasted the original project back into my workspace and the hole code worked smoothly - even with the Handler on. So I can't imagine a way to produce a minimalistic setup. btw: I'm not allowed to release the complete project folder (university stuff...)
edit2: I' using win7 64bit with java64. other javaFX programs are working (even then grafic intensive oracle samples). I'm not using any JavaFX specific repository setup or builder
Okay, I faced the same problem a few minutes ago and I think I found the source this time.
I'm running my code ALWAYS in debugging mode. To check the EventHandlers condition, I created some breakpoints in Eclipse. I also created a breakpoint at the following line of code (see above for more code)
public void handle(KeyEvent arg0) {
A mouseover over the breakpoint provides the following information:
Multiple markers at this line
Method breakpoint:NewList [Entry] - handle(KeyEvent)
implements:javafx.event.EventHandler .handle
I don't know what is happening here exactly but I think its something like this:
I'm overriding a JavaFX EventHandler Method and the debugger has to check the breakpoint on every single JavaFX interaction/EventHandler call. So Eclipse cant handle the flood of checks in a propper time and all seems to be very slow.
To clear that out: the breakpoint itself does not have to be called at any time, just the existing of it is enough to cause the problems.
Deactivating the breakpoint or running the application in a non-debugging mode will fix all problems. I think it should be a good idea to avoid all entry-breakpoints in any kind of Listener/EventHandler :)

Hotfixing Code running inside Web Container with Groovy

I have a webapp running that has a bug. I know how to fix it in the sources. However I cannot redeploy the app as I would have to take it offline to do so. (At least not right now).
I now want to fix the code "at runtime". Surgery on the living object, so to speak.
The app is implemented in Java and is build on top of Seam. I have added a Groovy Console to the app previous to the last release. (A way to run arbitrary code at runtime)
The normal way of adding behaviour to a class with Groovy would be similar to this:
String.metaClass.foo= { x -> x * x }
println "anything".foo(3)
This code added the method foo to java.lang.String and prints 9. I can do the same thing with classes running inside my webapp container. New instances will thereafter show the same behaviour:
com.my.package.SomeService.metaClass.foo= { x -> x * x }
def someService = new com.my.package.SomeService()
println someService.foo(3)
Works as excpected. All good so far.
My problem is now that the container, the web framework, Seam in this case, has already instantiated and cached the classes that I would like to manipulate (that is change their behaviour to reflect my bug fix).
Ideally this code would work:
com.my.package.SomeService.metaClass.foo= { x -> x * x }
def x = org.jboss.seam.Component.getInstance(com.my.package.SomeService)
println x.foo(3)
However the instantiation of SomeService has already happened and there is no effect.
Thus I need a way to make my changes "sticky". Has the groovy magic gone after my script has been run? Well, after logging out and in again, I can run this piece of code and get the expected result:
def someService = new com.my.package.SomeService()
println someService.foo(3)
So the foo method is still around and it looks like my change has been permanent...
So I guess the question that remains is how to force Seam to re-instantiate all its components and/or how to permanently make the change on all living instances...?
The hotfix is not persistent because the calling code is pure Java, not Groovy.
The Groovy magic in fact stays. But only when called from the groovy environment, that is through other groovy code.
Turns out that in a Java environment, the "Keyhole Surgery Pattern", as coined by Dierk König is only usable to change data at runtime, not code. The full power of the pattern is only accessible in a pure Groovy environment.
Not Groovy, but an alternative approach that works - as long as you don't change / add / remove and method signatures - is to set the Server in debug mode and use Java Hot Code Replacement functionality. Most IDE's support this. The changes are permanent and applied to instantiated components as well.
Requires of course that the app server is already configured with the a debug console or allows to enable it after the start.

Categories