New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JMockit on multiple class loaders does not work nicely #310
Comments
Btw. what I tried already: I was able to add There I would need to get the right So at time the rewritten code is called, I guess there is no chance to find out which classloader should currently be used, is there? Maybe this can be solved in any other way? |
To avoid we both losing time working on different directions:
|
I'm not currently working on this. I just shared the things I tried while I tried to fix my other PR before I recognized the real problem.
But why remove a feature that mostly works fine? So are you telling me that in the near future we are not able to update JMockit anymore, or we will not be able to use it within our Arquillian tests anymore? I just need to know, because then I can remove JMockit from our integration tests again and forbid the usage of JMockit. Now would be the right time to either do this or look for another mocking framework that supports custom class loaders. |
Yep, I tested with your latest commits and now JMockit is not usable anymore in WildFly or any other system with custom classloaders, no matter if |
The code thas was removed didn't really work; for some tests yes, but several other tests in JMockit's test suite failed for various reasons. In the end, no one was relying on the custom classloading support, which is not surprising given it was old code from 2014 and wasn't being kept up-to-date with every new release since then. |
Well, we started to rely on it. ;-) |
Yes, I think that's the way to go. |
Which also is bad, as all our non-integ tests should be rewritten to use the new mocking framework if one is found and that will be quite some work. :-( |
I ran into a serious problem while testing JMockit initialization through the Attach API from inside a running Wildfly server (from a .war file deployed by an Arquillian test): when JMockit tries to instantiate the necessary It seems that the Wildfly server has already loaded the attach native library, probably from the regular system classloader. And a native library cannot be loaded multitple times from different class loaders. At this point, I don't know how to avoid this failure (JMockit needs the VirtualMachine instance so it can call |
Are you sure you didn't deploy the same WAR two times? If you deploy the same WAR two times, or deploy different WAR files that both use the Attach API, you will get this error. On first try everything will work fine. But if you loaded another WAR before that tried to use the Attach API, even if it failed because it wasn't able to determine the right jmockit.jar, the VM is busted for further Attach API usages from other class loaders until restart. With a JMockit 1.25 with my "recreate JAR on disk" PR it works fine, because then the loading works on first try and on second try it sees that JMockit is already loaded in the VM. (It can just happen that it is from a different version, hence the version check PR) |
Yep, you're right, the war had been deployed before, and it worked the first time. So, from the second time JMockit simply needs to read the "Startup#instrumentation" field on the Startup class loaded in the system classloader. The |
And the
You are welcome to find an easier working solution. I just doubt you will, as you most probably cannot access the |
I already have a (partial) working solution which creates a temp jar file containing only the META-INF/MANIFEST.MF file and one new class called "InstrumentationHolder" (which is simply read from the current class loader using "ClassFile.readFromFile(Class)"); nothing needs to be done at build time. The code changes will be made available on sunday, I expect. |
I'm looking forward to seeing your solution. How do you know which classes you pack into the temporary jar? Do you hard-code a list? I made the build-time step to not having the necessity to hard-code a list of files that needs to be in the JAR, and the MD5 step to disambiguate resources on the classpath like |
There is only one class that needs to go into the temp jar file: the one pointed at by the "Agent-Class" attribute in MANIFEST.MF. This part is already working, but I ran into other issues (mainly, that ClassFileTransformers get added to the global Instrumentation object every time JMockit is initialized on a new class loader - I am addressing this by having an InstrumentationWrapper that prevents duplicate transformes). |
Nice, this works great so far. There are two issues remaining though, but I'll open separate issues about this.
|
I'm not 100% sure how it is intended with the mocking bridge fields that are stored in
NegativeArraySizeException
or similar, but it doesn't work too nicely if you use JMockit from multiple classloaders.To reproduce the error, just comment out the
MockingBridge.setMockingBridgeFields();
line from my PR #309. Tests that use mocking bridge fields will fail thereafter, e. g.changeContextCLDuringReplay()
will fail with aNullPointerException
.The problem here is, if you initialize JMockit on some class loader B where it was not initialized yet, the mocking bridge fields in
NegativeArraySizeException
or similar are updated to the ones from that class loader B and the ones from class loaders (A) where JMockit was initialized before are lost at this place.If you then use JMockit on the class loader A where it was initilized before B, the classes from A are used. But the transformed-in calls that use the mocking bridge fields in
NegativeArraySizeException
or similar use the mocking bridges from class loader B which does not harmonize of course, as then various static fields etc. are not like expected, like e. g.mockit.internal.state.MockClasses#mockupClassesToMockupInstances
which misses the instances in the B version that are present in the A version.I'd like to report this either as bug if JMockit should work fine with multiple classloaders, or as improvement request if this is expected behaviour.
Also if this is expected behaviour for now, please at least document it somewhere (I didn't find it mentioned) and maybe add some check that recognizes that the mocking bridge fields class loader does not match the other JMockit classes class loader and raise some meaningful error that explains that you can only use JMockit on one class loader at a time and if you switched to a new class loader that you cannot easily go back to the old one.
The text was updated successfully, but these errors were encountered: