Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 19 additions & 14 deletions debug-tools-attach/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@
<artifactId>debug-tools-hotswap-spring-plugin</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.github.future0923</groupId>
<artifactId>debug-tools-hotswap-forest-plugin</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.github.future0923</groupId>
<artifactId>debug-tools-hotswap-mybatis-plugin</artifactId>
Expand Down Expand Up @@ -247,22 +252,22 @@
<goal>run</goal>
</goals>
<configuration>
<target>
<mkdir dir="/Users/weilai/Documents/debug-tools-idea/src/main/resources/lib"/>
<copy file="${project.build.directory}/${project.build.finalName}.jar"
tofile="/Users/weilai/Documents/debug-tools-idea/src/main/resources/lib/${project.build.finalName}-${project.version}.jar"
overwrite="true"/>
<!-- <target>-->
<!-- <mkdir dir="/Users/weilai/Documents/debug-tools-idea/src/main/resources/lib"/>-->
<!-- <copy file="${project.build.directory}/${project.build.finalName}.jar"-->
<!-- tofile="/Users/weilai/Documents/debug-tools-idea/src/main/resources/lib/${project.build.finalName}-${project.version}.jar"-->
<!-- overwrite="true"/>-->

<mkdir dir="${project.basedir}/../debug-tools-boot/src/main/resources/lib"/>
<copy file="${project.build.directory}/${project.build.finalName}.jar"
tofile="${project.basedir}/../debug-tools-boot/src/main/resources/lib/${project.build.finalName}.jar"
overwrite="true"/>
<!-- <mkdir dir="${project.basedir}/../debug-tools-boot/src/main/resources/lib"/>-->
<!-- <copy file="${project.build.directory}/${project.build.finalName}.jar"-->
<!-- tofile="${project.basedir}/../debug-tools-boot/src/main/resources/lib/${project.build.finalName}.jar"-->
<!-- overwrite="true"/>-->

<mkdir dir="${project.basedir}/../dist"/>
<copy file="${project.build.directory}/${project.build.finalName}.jar"
tofile="${project.basedir}/../dist/debug-tools-agent.jar"
overwrite="true"/>
</target>
<!-- <mkdir dir="${project.basedir}/../dist"/>-->
<!-- <copy file="${project.build.directory}/${project.build.finalName}.jar"-->
<!-- tofile="${project.basedir}/../dist/debug-tools-agent.jar"-->
<!-- overwrite="true"/>-->
<!-- </target>-->
</configuration>
</execution>
</executions>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.github.future0923</groupId>
<artifactId>debug-tools-hotswap-plugin</artifactId>
<version>${revision}</version>
</parent>

<artifactId>debug-tools-hotswap-forest-plugin</artifactId>

<dependencies>
<dependency>
<groupId>io.github.future0923</groupId>
<artifactId>debug-tools-hotswap-core</artifactId>
<version>${revision}</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring</artifactId>
<version>1.5.32</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.future0923</groupId>
<artifactId>debug-tools-hotswap-spring-plugin</artifactId>
<version>5.0.1</version>
<scope>compile</scope>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Copyright (C) 2024-2025 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.future0923.debug.tools.hotswap.core.plugin.forest;

import io.github.future0923.debug.tools.base.logging.Logger;
import io.github.future0923.debug.tools.base.utils.DebugToolsStringUtils;
import io.github.future0923.debug.tools.hotswap.core.annotation.Init;
import io.github.future0923.debug.tools.hotswap.core.annotation.OnClassLoadEvent;
import io.github.future0923.debug.tools.hotswap.core.annotation.Plugin;
import io.github.future0923.debug.tools.hotswap.core.command.Scheduler;
import io.github.future0923.debug.tools.hotswap.core.plugin.forest.patch.ForestPatcher;
import io.github.future0923.debug.tools.hotswap.core.plugin.forest.watcher.ForestWatchEventListener;
import io.github.future0923.debug.tools.hotswap.core.util.IOUtils;
import io.github.future0923.debug.tools.hotswap.core.util.PluginManagerInvoker;
import io.github.future0923.debug.tools.hotswap.core.util.classloader.ClassLoaderHelper;
import io.github.future0923.debug.tools.hotswap.core.watch.Watcher;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtMethod;
import javassist.NotFoundException;

import java.io.IOException;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;

/**
* Forest热重载插件
*/
@Plugin(
name = "Forest",
description = "Reload Forest after class change.",
testedVersions = {"1.5.32"}, expectedVersions = {"1.5.x"},
supportClass = {
ForestPatcher.class
}
)
public class ForestPlugin {

private static final Logger logger = Logger.getLogger(ForestPlugin.class);

@Init
static Watcher watcher;

@Init
static Scheduler scheduler;

@Init
static ClassLoader appClassLoader;
/**
* 不能使用注解,因为注解只能获取AppClassLoader
*/
private static ClassLoader userClassLoader;

/**
* 获取OpenFeign的类加载器和注册者
*/
public void init(ClassLoader classLoader, Object feignClientsRegistrar) {
ForestPlugin.userClassLoader = classLoader;
}

public static ClassLoader getUserClassLoader() {
return userClassLoader == null ? appClassLoader : userClassLoader;
}

public static void registerBasePackage(final List<String> basePackages) {
for (String basePackage : basePackages) {
String classNameRegExp = DebugToolsStringUtils.getClassNameRegExp(basePackage);
Enumeration<URL> resourceUrls;
try {
resourceUrls = ClassLoaderHelper.getResources(ForestPlugin.getUserClassLoader(), classNameRegExp);
} catch (IOException e) {
logger.error("Unable to resolve forest base package {} in classloader {}.", classNameRegExp, ForestPlugin.getUserClassLoader());
return;
}
while (resourceUrls.hasMoreElements()) {
URL basePackageURL = resourceUrls.nextElement();
if (!IOUtils.isFileURL(basePackageURL)) {
logger.debug("forest basePackage '{}' - unable to watch files on URL '{}' for changes (JAR file?), limited hotswap reload support. Use extraClassPath configuration to locate class file on filesystem.", basePackage, basePackageURL);
} else {
watcher.addEventListener(ForestPlugin.getUserClassLoader(), basePackage, basePackageURL, new ForestWatchEventListener(scheduler, ForestPlugin.getUserClassLoader(), basePackage));
}
}
}

}

@OnClassLoadEvent(classNameRegexp = "com.dtflys.forest.springboot.annotation.ForestScannerRegister")
public static void patchFeignClientsRegistrar(CtClass ctClass, ClassPool classPool) throws NotFoundException, CannotCompileException {
StringBuilder src = new StringBuilder("{");
src.append(PluginManagerInvoker.buildInitializePlugin(ForestPlugin.class));
src.append(PluginManagerInvoker.buildCallPluginMethod(ForestPlugin.class, "init",
"com.dtflys.forest.springboot.annotation.ForestScannerRegister.class.getClassLoader()", ClassLoader.class.getName(),
"this", Object.class.getName()));
src.append("}");
CtConstructor[] constructors = ctClass.getConstructors();
for (CtConstructor constructor : constructors) {
constructor.insertAfter(src.toString());
}

CtMethod getBasePackages = ctClass.getDeclaredMethod("getBasePackages");
getBasePackages.insertAfter("{" +
" io.github.future0923.debug.tools.hotswap.core.plugin.forest.ForestPlugin.registerBasePackage($_);" +
"}");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2024-2025 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.future0923.debug.tools.hotswap.core.plugin.forest.command;

import io.github.future0923.debug.tools.base.hutool.core.util.ReflectUtil;
import io.github.future0923.debug.tools.base.logging.Logger;
import io.github.future0923.debug.tools.hotswap.core.command.EventMergeableCommand;
import io.github.future0923.debug.tools.hotswap.core.plugin.forest.dto.ForestClientReloadDTO;
import io.github.future0923.debug.tools.hotswap.core.plugin.forest.reload.ForestReload;
import io.github.future0923.debug.tools.hotswap.core.plugin.forest.watcher.ForestWatchEventListener;
import io.github.future0923.debug.tools.hotswap.core.watch.WatchFileEvent;

import java.util.Objects;

public class ForestReloadCommand extends EventMergeableCommand<ForestReloadCommand> {

private static final Logger logger = Logger.getLogger(ForestReloadCommand.class);

private final ClassLoader userClassLoader;

private final String className;

private final byte[] bytes;

private final String path;

private WatchFileEvent event;

/**
* 当class新增时,通过{@link ForestWatchEventListener#onEvent(WatchFileEvent)}创建命令后调用这
*/
public ForestReloadCommand(ClassLoader userClassLoader, String className, byte[] bytes, String path, WatchFileEvent event) {
this.userClassLoader = userClassLoader;
this.className = className;
this.bytes = bytes;
this.path = path;
this.event = event;
}

@Override
protected WatchFileEvent event() {
return event;
}

@Override
public void executeCommand() {
if (isDeleteEvent()) {
logger.trace("Skip reload for delete event on class '{}'", className);
return;
}
try {
ClassLoader orginalClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(userClassLoader);
Class<?> reloadClass = userClassLoader.loadClass(ForestReload.class.getName());
ReflectUtil.invoke(ReflectUtil.newInstance(reloadClass), "reload", ReflectUtil.newInstance(userClassLoader.loadClass(ForestClientReloadDTO.class.getName()), className, bytes, path));
Thread.currentThread().setContextClassLoader(orginalClassLoader);
} catch (Exception e) {
logger.error("reloadConfiguration error", e);
}
}

@Override
public final boolean equals(Object o) {
if (!(o instanceof ForestReloadCommand)) return false;
ForestReloadCommand that = (ForestReloadCommand) o;
return Objects.equals(className, that.className);
}

@Override
public int hashCode() {
return className.hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2024-2025 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.future0923.debug.tools.hotswap.core.plugin.forest.dto;


public class ForestClientReloadDTO {

private final String className;

private final byte[] bytes;

private final String path;

public ForestClientReloadDTO(String className, byte[] bytes, String path) {
this.className = className;
this.bytes = bytes;
this.path = path;
}

public byte[] getBytes() {
return bytes;
}

public String getClassName() {
return className;
}

public String getPath() {
return path;
}
}
Loading