bytebuddy official demo throw Exception "Class already loaded: class foo.Bar" - byte-buddy

Section "Working with unloaded classes" in Official document give a demo, I run it on my machine then throw an exception Class already loaded: class foo.Bar。
class MyApplication {
public static void main(String[] args) {
TypePool typePool = TypePool.Default.ofSystemLoader();
new ByteBuddy()
.redefine(typePool.describe("foo.Bar").resolve(), // do not use 'Bar.class'
ClassFileLocator.ForClassLoader.ofSystemLoader())
.defineField("qux", String.class) // we learn more about defining fields later
.make()
.load(ClassLoader.getSystemClassLoader());
assertThat(Bar.class.getDeclaredField("qux"), notNullValue());
}
}
bytebuddy version is 1.10.22

The problem is in the last line Bar.class.getDeclaredField("qux") which loads the Bar class upon validation of the code. I fixed this in the example. Rather use the return value of load which returns Bar.

Related

Can't update a class method with javassist

I'm playing with javassist (to use it later on a project) but I don't manage to make a simple update to a class.
I try to insert code before a method but its not being executed.
I've a gradle project and I'm using javassist version: '3.27.0-GA'.
Given the following class:
public class Dummy{
public int dummy(){
return 5;
}
}
The following test fails, so the class is not being modified:
#Test
public void modifyReturnValueTest() throws NotFoundException, CannotCompileException, IOException {
ClassPool pool = ClassPool.getDefault();
CtClass cc = pool.get("Dummy");
CtMethod m = cc.getDeclaredMethod("dummy");
m.insertBefore("{ if(true) return 3; }");
cc.writeFile();
assertEquals(3, new Dummy().dummy());
}
I'm missing something?
My guess is that when you call new Dummy().dummy() in
assertEquals(3, new Dummy().dummy());
the class loader loads the original version of Dummy class.
Since you want to load/use the version that you modified represented by the CtClass instance cc, you can insert the following snippet before assertEquals to make the class loader load the modified version of the Dummy class instead:
cc.toClass();
then the assert should be successful.
Note that in order to use toClass() above we rely on the fact that the Dummy class is never loaded before the toClass() invocation. (It will throw exception otherwise, the class loader cannot load two different versions of the same class at the same time)
You can check the javassist documentation for more details. This section might be especially useful http://www.javassist.org/tutorial/tutorial.html#load.

Handlers Swallow Exceptions

Consider the following handler:
public class CreateProjectHandler extends AbstractHandler {
#Override
public Object execute(ExecutionEvent event) throws ExecutionException {
// it does not matter what kind of exception this is:
throw new IllegalArgumentException("This is a test!");
}
}
From a customer and developer perspective it's pretty clear what should happen when this handler is executed: an error message of some kind should pop up.
What happens is: Nothing.
More accurate: the exception is logged into the error log (and console, if started from Eclipse). But the user sees nothing, in fact he doesn't even know there was an error.
I could fix this by catching Exception for each and every handler, but besides being ugly and cumbersome it contradicts each style guide ever.
Is there a better way to handle the exceptions swallowed by handlers?
For Eclipse 4 (e4 or 3.x compatbility mode) add a class implementing IEventLoopAdvisor to the application context. The eventLoopException method will be called for the unhandled exceptions.
A suitable place to set this up for e4 is the #PostContextCreate of the RCP life cycle class:
#PostContextCreate
public void postContextCreate(IEclipseContext context)
{
// Event loop advisor for error handling
context.set(IEventLoopAdvisor.class, new EventLoopAdvisor());
You must also implement eventLoopIdle, it is very important that this calls display.sleep(). A standard method would be:
#Override
public void eventLoopIdle(final Display display)
{
display.sleep();
}
For 3.x compatibility mode there is a default event loop advisor installed after the post context create which delegates to the workbench WorkbenchAdvisor. If you are using your own advisor in the RCP you can override the eventLoopException method of the advisor.
I found another way that works for my E3 compatibility application: overridding WorkbenchAdvisor#eventLoopException(Throwable):
public class ApplicationWorkbenchAdvisor extends WorkbenchAdvisor {
#Override
public void eventLoopException(Throwable exception) {
// do magic here
}
// [snipped other methods]
}

redefine static methods with ByteBuddy

Can homebody help me please to give me a hint how to redefine static methods using byte-buddy 1.6.9 ?
I have tried this :
public class Source {
public static String hello(String name) {return null;}
}
public class Target {
public static String hello(String name) {
return "Hello" + name+ "!";
}
}
String helloWorld = new ByteBuddy()
.redefine(Source.class)
.method(named("hello"))
.intercept(MethodDelegation.to(Target.class))
.make()
.load(getClass().getClassLoader())
.getLoaded()
.newInstance()
.hello("World");
I got following Exception :
Exception in thread "main" java.lang.IllegalStateException: Cannot inject already loaded type: class delegation.Source
Thanks
Classes can only be loaded once by each class loader. In order to replace a method, you would need to use a Java agent to hook into the JVM's HotSwap feature.
Byte Buddy provides a class loading strategy that uses such an agent, use:
.load(Source.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());
This does however require you to install a Java agent. On a JDK, you can do so programmatically, by ByteBuddyAgent.install() (included in the byte-buddy-agent artifact). On a JVM, you have to specify the agent on the command line.

How to disable static initializer?

Assuming my system under test looks like this:
public class SysUnderTest {
public int foo() {
Trouble trouble1 = new Trouble();
Trouble trouble2 = new Trouble();
return trouble1.water(1) + trouble2.water(2);
}
}
The test will looks something like
public class DummyTest {
#Tested SysUnderTest sut;
#Mocked Trouble trouble;
#Test
public void testTrouble() {
new Expectations() {{
trouble.water(anyInt); returns(10, 20);
}};
assertThat("mocked result", sut.foo(), is(30));
new FullVerificationsInOrder() {{
Trouble t1 = new Trouble();
Trouble t2 = new Trouble();
t1.water(1);
t2.water(2);
}};
}
}
However, Trouble is actually a 3rd-party lib class that I have no control, which it does static initialization which will fail in testing env.
public class Trouble {
static {
troubleInitialize();
};
public int water(int i) {
return 0;
}
private static void troubleInitialize() {
throw new RuntimeException("Trouble");
}
}
I know I can use MockUp<Trouble> to get rid of the static initializer but I have no idea how to make use of it in case as I want to (in my realistic case) be able to distinguish the two new instances (created in SysUnderTest) and verify their invocations. I have tried different ways but all failed with some reasons
Adding a new MockUp<Trouble>(){#Mock void $clinit(){} }; in #Before/#BeforeClass, and keep #Mocked Trouble trouble;. It seems not working because the mockup action happens after the DummyTest class is loaded, which will load (unmodified) Trouble class which will throw exception during static initialization
Adding the new Mockup in a TestSuite and call the DummyTest in suite, similar problem as 1.
Simply put the behavior of returning 20, 30 in the fake class, and remove usage of Expectations/Verifications but I have no way to verify which instance is called with what parameter.
Is there a better way to solve my problem? Actually I would want to keep using Expectaitons/Verifications, all I want is some way to disable the static initializer during unit test.
Use stubOutClassInitialization to change the mocked class's static init to an empty method when using Mocked.
#Mocked(stubOutClassInitialization=true) Trouble trouble;

General Fixture Setup with PowerMockito and #BeforeClass

I have a test where I configure some general fixtures however after using PowerMockRule the static variables which I configure in my #BeforeClass method reset to null. This causes the following test to fail however if you remove PowerMockRule it passes.
public class Test
{
#Rule
public PowerMockRule rule = new PowerMockRule();
private static String MyString;
#BeforeClass
public static void setupClass() throws Exception
{
MyString = "FOO";
}
#org.junit.Test
public void test() throws Exception
{
assertEquals("FOO", MyString);
}
}
I have the answer, but you are not going to like it.
Short answer:it looks like a defect in PowerMock, so create a issue in our bug tracker
Long answer: As you may know the PowerMock to be able mock static, private and so on loads classes by custom class loader and modified byte code. Then #PowerMockRunneris used then PowerMock can control loading a test class and the test class is also loaded by custom class loader. In case if another jUnitRunner runs test and the PowerMockRuleis used, then the test class and all other classes that is needed for test are loaded with standard class loader. PowerMock reloads all these classes either by using deep coping with serializing/deserializing or by using objenesis. So as class is reloaded all static fields which was initialised are null.
I've briefly checked code and I haven't found test for your cases and that we processed #BeforeClass, so create a issue in our bug tracker and I'll check it deeply.
By the way, please, also point which version do you use and which dependencies do you use.