From 4c8343c86f621606eec5664da27416dc09865374 Mon Sep 17 00:00:00 2001 From: ReaJason Date: Thu, 29 Jan 2026 15:16:07 +0800 Subject: [PATCH] fix: method body write failed with jakarta renamed ByteBuddy Advice cache the class method list at first, the method signature will be changed when use jakarta rename, if follow advice need enhance the javax method, it will not find method signature. because ByteBuddy cached javax method, but follow advice need find the jakarta method, boom~ --- .../memshell/generator/ProcessorRegistry.java | 2 +- .../processors/JakartaBuilderModifier.java | 22 ---------- .../processors/JakartaPostProcessor.java | 16 +++++++ .../processors/ListenerBuilderModifier.java | 16 +++---- .../generator/ListenerGeneratorTest.java | 12 ------ .../javaweb/asm/ClassRenameUtils.java | 43 +++++++++++++------ 6 files changed, 54 insertions(+), 57 deletions(-) delete mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaBuilderModifier.java create mode 100644 generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaPostProcessor.java diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java index 8f278e69..2c29890b 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/ProcessorRegistry.java @@ -17,11 +17,11 @@ public final class ProcessorRegistry { private static final List>> BUILDER_PROCESSORS = Arrays.asList( new ListenerBuilderModifier(), new ValveBuilderModifier(), - new JakartaBuilderModifier(), new DebugOffBuilderModifier() ); private static final List> BYTE_PROCESSORS = Arrays.asList( + new JakartaPostProcessor(), new JettyHandlerPostProcessor(), new ShrinkPostProcessor() ); diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaBuilderModifier.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaBuilderModifier.java deleted file mode 100644 index cb83b2fe..00000000 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaBuilderModifier.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.reajason.javaweb.memshell.generator.processors; - -import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper; -import com.reajason.javaweb.memshell.config.ShellConfig; -import com.reajason.javaweb.memshell.config.ShellToolConfig; -import com.reajason.javaweb.memshell.generator.Processor; -import net.bytebuddy.dynamic.DynamicType; - -/** - * @author ReaJason - * @since 2025/12/7 - */ -public class JakartaBuilderModifier implements Processor> { - - @Override - public DynamicType.Builder process(DynamicType.Builder builder, ShellConfig shellConfig, ShellToolConfig shellToolConfig) { - if (shellConfig.isJakarta()) { - builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE); - } - return builder; - } -} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaPostProcessor.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaPostProcessor.java new file mode 100644 index 00000000..dc493172 --- /dev/null +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/JakartaPostProcessor.java @@ -0,0 +1,16 @@ +package com.reajason.javaweb.memshell.generator.processors; + +import com.reajason.javaweb.asm.ClassRenameUtils; +import com.reajason.javaweb.memshell.config.ShellConfig; +import com.reajason.javaweb.memshell.config.ShellToolConfig; +import com.reajason.javaweb.memshell.generator.Processor; + +public class JakartaPostProcessor implements Processor { + @Override + public byte[] process(byte[] input, ShellConfig shellConfig, ShellToolConfig shellToolConfig) { + if (shellConfig.isJakarta()) { + return ClassRenameUtils.relocateJakarta(input); + } + return input; + } +} diff --git a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ListenerBuilderModifier.java b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ListenerBuilderModifier.java index c2a188e2..897fc631 100644 --- a/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ListenerBuilderModifier.java +++ b/generator/src/main/java/com/reajason/javaweb/memshell/generator/processors/ListenerBuilderModifier.java @@ -17,6 +17,7 @@ import net.bytebuddy.description.modifier.Visibility; import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.dynamic.DynamicType; import net.bytebuddy.implementation.FixedValue; +import net.bytebuddy.implementation.StubMethod; import net.bytebuddy.matcher.ElementMatchers; import static net.bytebuddy.matcher.ElementMatchers.named; @@ -46,12 +47,9 @@ public class ListenerBuilderModifier implements Processor TypeDescription typeDefinition, String newClassName) { MethodList methods = typeDefinition.getDeclaredMethods(); - if (methods.filter(ElementMatchers.named("getResponseFromRequest") - .and(ElementMatchers.takesArguments(Object.class)) - .and(ElementMatchers.returns(Object.class))) - .isEmpty()) { - throw new GenerationException("[public Object getResponseFromRequest(Object request)] method not found" + - " make sure arg and return type is Object.class"); + if (methods.filter(named("getResponseFromRequest").and(takesArguments(1))).isEmpty()) { + throw new GenerationException("please add [getResponseFromRequest(Object request)] method," + + " the method body will be auto adapted for multi server"); } else { builder = builder .visit(MethodCallReplaceVisitorWrapper.newInstance( @@ -59,13 +57,11 @@ public class ListenerBuilderModifier implements Processor .visit(Advice.to(implInterceptor).on(named("getResponseFromRequest"))); } - if (methods.filter(named("getFieldValue") - .and(takesArguments(Object.class, String.class))) - .isEmpty()) { + if (methods.filter(named("getFieldValue").and(takesArguments(Object.class, String.class))).isEmpty()) { builder = builder.defineMethod("getFieldValue", Object.class, Visibility.PUBLIC, Ownership.STATIC) .withParameters(Object.class, String.class) .throwing(Exception.class) - .intercept(FixedValue.nullValue()) + .intercept(StubMethod.INSTANCE) .visit(Advice.to(ShellCommonUtil.GetFieldValueInterceptor.class).on(named("getFieldValue"))); } return builder; diff --git a/generator/src/test/java/com/reajason/javaweb/memshell/generator/ListenerGeneratorTest.java b/generator/src/test/java/com/reajason/javaweb/memshell/generator/ListenerGeneratorTest.java index 20a56475..4972f42e 100644 --- a/generator/src/test/java/com/reajason/javaweb/memshell/generator/ListenerGeneratorTest.java +++ b/generator/src/test/java/com/reajason/javaweb/memshell/generator/ListenerGeneratorTest.java @@ -28,12 +28,6 @@ class ListenerGeneratorTest { } } - public static class J { - public HttpServletResponse getResponseFromRequest(HttpServletRequest request) { - return null; - } - } - public static class FakeRequest { public Object response = "i'm a good boy"; } @@ -44,12 +38,6 @@ class ListenerGeneratorTest { Assertions.assertThrows(GenerationException.class, () -> ListenerBuilderModifier.modifier(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(Object.class), "hello.world")); } - @Test - void testGetResponseFromRequestSignatureError() { - DynamicType.Builder builder = new ByteBuddy().redefine(J.class); - Assertions.assertThrows(GenerationException.class, () -> ListenerBuilderModifier.modifier(builder, Tomcat.ListenerInterceptor.class, TypeDescription.ForLoadedType.of(J.class), "hello.world")); - } - @Test @SneakyThrows void test() { diff --git a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java index 56ff8d7a..acfcda90 100644 --- a/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java +++ b/memshell-party-common/src/main/java/com/reajason/javaweb/asm/ClassRenameUtils.java @@ -1,5 +1,6 @@ package com.reajason.javaweb.asm; +import org.jetbrains.annotations.NotNull; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; @@ -14,12 +15,7 @@ import org.objectweb.asm.commons.SimpleRemapper; public class ClassRenameUtils { public static byte[] renameClass(byte[] classBytes, String newName) { - ClassReader reader = null; - try { - reader = new ClassReader(classBytes); - } catch (Exception e) { - throw new RuntimeException("invalid class bytes"); - } + ClassReader reader = getClassReader(classBytes); String oldClassName = reader.getClassName(); String newClassName = newName.replace('.', '/'); ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); @@ -29,12 +25,7 @@ public class ClassRenameUtils { } public static byte[] relocateClass(byte[] classBytes, String relocateClassPackage, String relocatePrefix) { - ClassReader reader = null; - try { - reader = new ClassReader(classBytes); - } catch (Exception e) { - throw new RuntimeException("invalid class bytes"); - } + ClassReader reader = getClassReader(classBytes); String oldClassName = relocateClassPackage.replace('.', '/'); String newClassName = relocatePrefix.replace('.', '/'); ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS); @@ -51,4 +42,32 @@ public class ClassRenameUtils { reader.accept(adapter, 0); return writer.toByteArray(); } + + public static byte[] relocateJakarta(byte[] classBytes) { + ClassReader reader = getClassReader(classBytes); + ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_MAXS); + ClassRemapper adapter = new ClassRemapper(writer, new Remapper(Opcodes.ASM9) { + @Override + public String map(String typeName) { + if (typeName.startsWith("javax/servlet/") + || typeName.startsWith("javax/websocket/")) { + return typeName.replaceFirst("javax", "jakarta"); + } else { + return typeName; + } + } + }); + reader.accept(adapter, 0); + return writer.toByteArray(); + } + + private static @NotNull ClassReader getClassReader(byte[] classBytes) { + ClassReader reader = null; + try { + reader = new ClassReader(classBytes); + } catch (Exception e) { + throw new RuntimeException("invalid class bytes"); + } + return reader; + } }