Pretty thanks adinn. I have few more findings after started to debug the code. But I haven't understood the cause yet.
First, sorry I added the log with class loading. I'm adding it again now (cat byteman-decomission-class-loading-server.log | grep org.jboss.byteman | sort | uniq -d). But I think the reason is not in double loading of the byteman classes. During my testing I tried the get the byteman being loaded by system class loader (I just used the 'sys:' property) but the behaviour is the same. I was thinking if system classloader can do some change from your point
If so then it may well be that maven is replacing the system loader during startip and then failing to correctly delegate requests to lookup Byteman classes from subordinate
To clarify I do not use maven I just run WildFly server, I do change the bin/standalone.conf to pass the byteman javaagent property to JAVA_OPTS.
After that I decided to check how is it with this unloading the rules. And what I can say the rules are really unloaded at some point of processing. I looked at the Rule class and methods getKey and purge
byteman/Rule.java at master · bytemanproject/byteman · GitHub
byteman/Rule.java at master · bytemanproject/byteman · GitHub
I can see the same rule is added twice, each time under different key, differentiated with the _# suffix. That's what you described above.
And I can observe the the flow like (I'm not sure about precise order to be honest):
the rule key is created during time the rule is injected at a trigger point (byteman/RuleTriggerMethodAdapter.java at master) -> at that time the key is generated and the Rule map ruleKeyMap is filled -> a code do some reflection changes which causes regeneration of the byteman rules -> rule is purged from the ruleKeyMap -> injecting trigger again with generating new rule in the ruleKeyMap -> trigger point is reached by program execution -> the injection point invokes the rule with the old name (which was already removed from the ruleKeyMap)
I think there is not a trouble with class loading (being done twice) from other reason as well. The xa.btm contains three rules (if you check here openshift-quickstarts/xa.btm at master · jboss-openshift/openshift-quickstarts · GitHub). One is bound to com.arjuna.ats.arjuna.coordinator.BasicAction. That's correctly executed all the time. Then there are two other bound to com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord#topLevelCommit. None of them is executed correctly - both are decommisioned. If I change the both rules to be bound to ^AbstractRecord#topLevelCommit instead the rules are executed fine (it seems in deterministic way).
Now this is the stacktrace where the the purge from ruleKeyMap happens
Thread [MSC service thread 1-5] (Suspended (breakpoint at line 881 in Rule))
owns: RuleScript (id=338)
Rule.purge() line: 881
RuleScript.purge(ClassLoader, String) line: 376
TransformContext.transform(byte[]) line: 85
Transformer.transform(RuleScript, ClassLoader, String, byte[]) line: 746
Transformer.tryTransform(byte[], String, ClassLoader, String, boolean, boolean) line: 813
Transformer.tryTransform(byte[], String, ClassLoader, String, boolean) line: 785
Transformer.transform(ClassLoader, String, Class, ProtectionDomain, byte[]) line: 258
TransformerManager.transform(ClassLoader, String, Class, ProtectionDomain, byte[]) line: 188
InstrumentationImpl.transform(ClassLoader, String, Class, ProtectionDomain, byte[], boolean) line: 428
ClassLoader.defineClass1(String, byte[], int, int, ProtectionDomain, String) line: not available [native method]
ModuleClassLoader(ClassLoader).defineClass(String, byte[], int, int, ProtectionDomain) line: 763
ModuleClassLoader.doDefineOrLoadClass(String, byte[], int, int, ProtectionDomain) line: 358
ModuleClassLoader.defineClass(String, ClassSpec, ResourceLoader) line: 437
ModuleClassLoader.loadClassLocal(String, boolean) line: 274
ModuleClassLoader$1.loadClassLocal(String, boolean) line: 77
Module.loadModuleClass(String, boolean) line: 713
ModuleClassLoader.findClass(String, boolean, boolean) line: 190
ModuleClassLoader(ConcurrentClassLoader).performLoadClassUnchecked(String, boolean, boolean) line: 412
ModuleClassLoader(ConcurrentClassLoader).performLoadClass(String, boolean, boolean) line: 400
ModuleClassLoader(ConcurrentClassLoader).loadClass(String) line: 116
Class.getDeclaredMethods0(boolean) line: not available [native method]
Class.privateGetDeclaredMethods(boolean) line: 2701
Class.getDeclaredMethod(String, Class...) line: 2128
JBossJTALocalTransactionProvider.lambda$static$0() line: 71
167706628.run() line: not available
AccessController.doPrivileged(PrivilegedAction) line: not available [native method]
JBossJTALocalTransactionProvider.() line: 69
JBossLocalTransactionProvider$Builder.build() line: 746
LocalTransactionContextService.start(StartContext) line: 57
ServiceControllerImpl$StartTask.startService(Service, StartContext) line: 2032
ServiceControllerImpl$StartTask.run() line: 1955
ServiceContainerImpl$ContainerExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1149
ThreadPoolExecutor$Worker.run() line: 624
ServiceContainerImpl$ServiceThread(Thread).run() line: 748
It's reflection from wildfly transaction client at wildfly-transaction-client/JBossJTALocalTransactionProvider.java at master · wildfly/wildfly-transaction-client · GitHub
Now my doubt - do you think there could be issue in Byteman that do not remove injection trigger when re-creation of the key happens? Or is there possibility of some wrong reflection handling at the wfly txn client?