raphw/byte-buddy

Side effects of bytebuddy rebasing to weaving process?

rage5474 opened this issue · 2 comments

Inspired from this stackoverflow question byte-buddy-and-osgi-weaving-hook, I tried following WeavingHook example with Eclipse RCP equinox runtime:

Code

// WeavingHookService.java
public class WeavingHookService implements WeavingHook {

  @Override
  public void weave(WovenClass wovenClass) {

    if (wovenClass.getClassName().endsWith("MyPrinter")) {
      try {
        byte[] originalBytes = wovenClass.getBytes();

        Unloaded<MyPrinter> newPrinter = new ByteBuddy()
            .rebase(MyPrinter.class,
                ClassFileLocator.Simple
                    .of(wovenClass.getClassName(),
                        originalBytes))
            .method(ElementMatchers.named("getName"))
            .intercept(FixedValue.value("Raphael"))
            .make();

        // setBytes has no effect.
        wovenClass.setBytes(newPrinter.getBytes());
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}

// MyPrinter.java
public class MyPrinter {
  public String getName() {
    return "Unknown";
  }
}

Expectation
My expectation is that if I call new MyPrinter().getName() in my code, Raphael is returned.

Observation
If I call new MyPrinter().getName() in my code, Unknown is returned.

Already analyzed
If I set a hard-coded byte array, like new byte[]{'0', '1'} instead of doing bytebuddy rebase call, setBytes throws at least some exception. But this does not happen, if a bytebuddy rebase call is done before setBytes is called.

Question
So, for me it seems that the bytebuddy rebase call has side-effects to the weaving process. But maybe I am doing something wrong here. Does anybody have an idea what's going wrong?

Byte Buddy implements an immutable builder, there should be no side effects. Maybe the weaver fails as you refer to the loaded MyPrinter class?

You can use a TypePool to describe s class from class bytes.

Thanks for the hint. This code is working fine for me. With this service it is even possible to modify not exported classes without modifying anything in the manifest. This is really great, because I can modify cross-cutting things in OSGi without using java agents.

@Component(immediate = true)
public class WeavingHookService implements WeavingHook {

  private static final String CLASS_TO_MODIFY = "my.waevinghook.example.internal.MyPrinter";

  @Override
  public void weave(WovenClass wovenClass) {

    if (wovenClass.getClassName().equals(CLASS_TO_MODIFY)) {
      try {
        byte[] originalBytes = wovenClass.getBytes();

        ClassFileLocator locator = ClassFileLocator.Simple
            .of(wovenClass.getClassName(),
                originalBytes);

        Unloaded<Object> newPrinter = new ByteBuddy()
            .rebase(TypePool.Default.of(wovenClass.getBundleWiring().getClassLoader())
                .describe(
                CLASS_TO_MODIFY).resolve(),
                locator)
            .method(ElementMatchers.named("getName"))
            .intercept(FixedValue.value("Raphael"))
            .make();

        wovenClass.setBytes(newPrinter.getBytes());
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}