quarks
quarks

Reputation: 35316

Replace java.io.File on runtime with ByteBuddy

How can you replace java.io.File for a given web application (running on a servlet container) for the following conditions

Given I have a class class AmazonS3File extends java.io.File

  1. Replace every java.io.File with AmazonS3File
  2. Replace every java.io.File with AmazonS3File but under the condition that the call came from a specific library, say com.jetbrains package (not sure if this is even theoretically possible)

However, here's the pseudo code I have:

    new AgentBuilder.Default()
            .with(AgentBuilder.Listener.StreamWriting.toSystemOut())
            .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
            .type(ElementMatchers.is(java.io.File.class))
            .transform((builder, typeDescription, classLoader, module) ->
                    new ByteBuddy().redefine(java.io.File.class).constructor(ElementMatchers.anyOf(AmazonS3File.class) /* <-- What to put here?*/))
            .installOnByteBuddyAgent();
    File videoFile = new File(OUTPUT_PATH);
    System.out.println(videoFile.getPath());

What should be placed in the ByteBuddy().redefine(java.io.File.class).constructor to make it load the constructor of AmazonS3File instead?

Upvotes: 1

Views: 458

Answers (1)

Rafael Winterhalter
Rafael Winterhalter

Reputation: 44032

Byte Buddy allows you to adjust library code by for example using a Java agent. A Java agent is notified on any class being loaded and using one, you can indeed replace any call to new File() by your new AmazonS3File(). To do so, you would need to transform all classes within a package where this in-code replacement is relevant. You would be using a MemberSubstitution for this:

new AgentBuilder.Default()
        .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
        .type(nameStartsWith("pkg.of.library."))
        .transform((builder, typeDescription, classLoader, module) -> builder.visit(MemberSubstitution.strict()
             .constructor(isDeclaredBy(File.class))
             .replaceWith(MyFactory.class.getMethod("file", String.class))
             .on(any()))
        .installOnByteBuddyAgent();

To make this work, you will have to create some factory class MyFactory that returns any instance of type File where you can for example return your Amazon file if it exteds Java's File.

The substitution basically says: for any type in the package and subpackages of pgk.of.library, scan any() method or constructor for constructors of File and replace them with a call to MyFactory::file. For this to work, the factory must have the same arguments as the file constructor being replaced.

Upvotes: 1

Related Questions