All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.voovan.tools.hotswap.Hotswaper Maven / Gradle / Ivy
package org.voovan.tools.hotswap;
import org.voovan.Global;
import org.voovan.tools.TEnv;
import org.voovan.tools.TFile;
import org.voovan.tools.TObject;
import org.voovan.tools.hashwheeltimer.HashWheelTask;
import org.voovan.tools.log.Logger;
import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.util.ArrayList;
import java.util.List;
/**
* 热部署核心类
* 采用 JavaAgent 方式进行类的热部署
* 使用这种热部署方式,不能新增方法和属性,也就说类的结构不能发生变化
*
* @author: helyho
* Voovan Framework.
* WebSite: https://github.com/helyho/Voovan
* Licence: Apache v2 License
*/
public class Hotswaper {
private Instrumentation instrumentation;
private List classFileInfos;
private List excludePackages;
private int reloadIntervals;
private HashWheelTask reloadTask;
/**
* 构造函数
* @throws IOException IO 异常
* @throws AttachNotSupportedException 附加指定进程失败
* @throws AgentLoadException Agent 加载异常
* @throws AgentInitializationException Agent 初始化异常
*/
public Hotswaper() throws IOException, AgentInitializationException, AgentLoadException, AttachNotSupportedException {
init(null);
}
/**
* 构造函数
* @param agentJarPath AgentJar 文件
* @throws IOException IO 异常
* @throws AttachNotSupportedException 附加指定进程失败
* @throws AgentLoadException Agent 加载异常
* @throws AgentInitializationException Agent 初始化异常
*/
public Hotswaper(String agentJarPath) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
init(new File(agentJarPath));
}
private void init(File agentJar) throws AgentInitializationException, AgentLoadException, AttachNotSupportedException, IOException {
reloadIntervals = 5;
classFileInfos = new ArrayList();
excludePackages = TObject.asList("java.","sun.","javax.","com.sun","com.oracle");
if(agentJar == null) {
agentJar = findAgentJar();
}
if(agentJar != null && agentJar.exists()) {
Logger.info("[HOTSWAP] System choose an agent jar file: "+agentJar.getAbsolutePath());
agentAttach(agentJar.getPath());
loadCustomClass();
} else {
throw new FileNotFoundException("The agent jar file not found");
}
}
/**
* 查找 AgentJar 文件
* @return AgentJar 文件
*/
private File findAgentJar(){
List agentJars = TFile.scanFile(new File(TFile.getContextPath()), "voovan-(framework|common)-?(\\d\\.?)*\\.?jar$");
File agentJar = null;
for (File jarFile : agentJars) {
if(agentJar == null){
agentJar = jarFile;
}
if(jarFile.lastModified() < jarFile.lastModified()){
agentJar = jarFile;
}
}
return agentJar;
}
/**
* 附加 Agentjar 到目标地址
* @param agentJarPath AgentJar 文件
* @throws IOException IO 异常
* @throws AttachNotSupportedException 附加指定进程失败
* @throws AgentLoadException Agent 加载异常
* @throws AgentInitializationException Agent 初始化异常
*/
private void agentAttach(String agentJarPath) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
VirtualMachine vm = VirtualMachine.attach(Long.toString(TEnv.getCurrentPID()));
vm.loadAgent(agentJarPath);
instrumentation = DynamicAgent.getInstrumentation();
vm.detach();
}
/**
* 读取所有的用户类
* @return 用户类信息
*/
private void loadCustomClass(){
for(Class clazz : instrumentation.getAllLoadedClasses()){
if(isExcludeClass(clazz)){
continue;
}
ClassFileInfo classFileInfo = new ClassFileInfo(clazz, true);
classFileInfos.add(classFileInfo);
}
}
/**
* 检查是否是热部署排除在外的class
* @param clazz class 对象
* @return true:排除的 class, false:未排除的 class
*/
private boolean isExcludeClass(Class clazz){
String className = clazz.getCanonicalName();
//基本类型部排除
if(clazz.isPrimitive()){
return true;
}
//没有完全现定名的 class 不加载
if(className == null){
return true;
}
//如果是内部类则排除
if(clazz.isMemberClass()){
return true;
}
//CodeSource 对象为空的不加载
if(clazz.getProtectionDomain().getCodeSource() == null){
return true;
}
//排除的包中的 class 不加载
for(String excludePackage : excludePackages){
if(className.startsWith(excludePackage)){
return true;
}
}
//加载过的 class 不加载
for(ClassFileInfo classFileInfo: classFileInfos){
if(classFileInfo.getClazz() == clazz){
return true;
}
}
return false;
}
/**
* 文件变更监视器
* @return 发生变更的 ClassFileInfo 对象
*/
private List fileWatcher(){
loadCustomClass();
List changedFiles = new ArrayList();
for(ClassFileInfo classFileInfo : classFileInfos){
if(classFileInfo.isChanged()){
changedFiles.add(classFileInfo);
}
}
return changedFiles;
}
/**
* 重新热加载Class
* @param changedFiles 有过变更的文件信息
* @throws UnmodifiableClassException 不可修改的 Class 异常
* @throws ClassNotFoundException Class未找到异常
*/
public void reloadClass(List changedFiles) throws UnmodifiableClassException, ClassNotFoundException {
List classDefinitions = new ArrayList();
for(ClassFileInfo classFileInfo : changedFiles) {
ClassDefinition classDefinition = new ClassDefinition(classFileInfo.getClazz(), classFileInfo.getBytes());
Logger.info("[HOTSWAP] class:" + classFileInfo.getClazz() + " will reload.");
classDefinitions.add(classDefinition);
}
instrumentation.redefineClasses(classDefinitions.toArray(new ClassDefinition[0]));
}
/**
* 重新热加载 Class
* @param customClasses class 数组
* @throws UnmodifiableClassException 不可修改的 Class 异常
* @throws ClassNotFoundException Class未找到异常
*/
public void reloadClass(Class[] customClasses) throws UnmodifiableClassException, ClassNotFoundException {
ArrayList customClassFileInfos = new ArrayList();
for(Class clazz : customClasses){
ClassFileInfo classFileInfo = new ClassFileInfo(clazz, false);
customClassFileInfos.add(classFileInfo);
}
reloadClass(customClassFileInfos);
}
/**
* 获取当前自动热部署间隔事件
* @return 自动热部署间隔事件
*/
public int getReloadIntervals() {
return reloadIntervals;
}
/**
* 自动热加载 Class
* @param intervals 自动重读的时间间隔, 单位: 秒
* @throws UnmodifiableClassException 不可修改的 Class 异常
* @throws ClassNotFoundException Class未找到异常
*/
public void autoReload(int intervals) throws UnmodifiableClassException, ClassNotFoundException {
this.reloadIntervals = intervals;
cancelAutoReload();
Logger.info("[HOTSWAP] Start auto reload and hotswap every " + intervals + " seconds");
reloadTask = new HashWheelTask() {
@Override
public void run() {
try {
List changedFiles = fileWatcher();
reloadClass(changedFiles);
} catch (UnmodifiableClassException |ClassNotFoundException e) {
e.printStackTrace();
}
}
};
Global.getHashWheelTimer().addTask(reloadTask, intervals, true);
}
/**
* 取消自动热加载操作
*/
public void cancelAutoReload(){
if(reloadTask != null) {
reloadTask.cancel();
}
}
/**
* Class 文件信息
*/
public class ClassFileInfo {
private Class clazz;
private long lastModified;
public ClassFileInfo(Class clazz, boolean isAutoChek) {
this.clazz = clazz;
if(isAutoChek) {
this.lastModified = TEnv.getClassModifyTime(clazz);
}
}
public boolean isChanged(){
long currentModified = TEnv.getClassModifyTime(clazz);
if(currentModified < 0 ){
return false;
}
if(currentModified != lastModified){
lastModified = currentModified;
return true;
}else{
return false;
}
}
public byte[] getBytes() {
return TEnv.loadClassBytes(clazz);
}
public Class getClazz() {
return clazz;
}
public void setClazz(Class clazz) {
this.clazz = clazz;
}
public long getLastModified() {
return lastModified;
}
public void setLastModified(long lastModified) {
this.lastModified = lastModified;
}
public String toString(){
return clazz.getCanonicalName();
}
public boolean equals(ClassFileInfo classFileInfo) {
return classFileInfo.getClazz() == this.clazz;
}
}
}