【JFinal】JFinal中的文件改动后jetty服务器重启动的实现

参考: JFianl 2.2

用JFianl开发web项目的时候,发现修改文件后会重新启动,感到很好奇,所以研究一下实现的方法

实现的基本步骤

  • 找到项目的根目录
  • 将项目文件中的所有文件的最近修改时间和文件大小在Map中进行保存
  • 通过java的timer定时器保存文件并检查map是否一致
  • 不一致则调用服务器的stop()方法,然后重新初始化服务器,再重新start()

Scanner扫描类

public abstract class Scanner {
    //定时器
    private Timer timer;
    //定时任务
    private TimerTask task;
    //项目根目录
    private File rootDir;
    //定时器时间间隔
    private int interval;
    //是否正在运行
    private boolean isRunning;
    //准备扫描的文件
    //key:文件的完全路径  value:文件的简略表达(最近修改时间+文件大小)
    private final Map preScan = new HashMap();
    //正在扫描的文件
    //key:文件的完全路径  value:文件的简略表达(最近修改时间+文件大小)
    private final Map curScan = new HashMap();

    //构造器
    public Scaner(String rootDir, int interval) {
        if (rootDir == null || "".equals(rootDir.trim()))
            throw new IllegalArgumentException("根目录不能为空");
        this.rootDir = new File(rootDir);
        if (!this.rootDir.isDirectory())
            throw new IllegalArgumentException("文件夹" + rootDir + "不存在");
        if (interval <= 0)
            throw new IllegalArgumentException("扫描时隔参数必须大于0");
        this.interval = interval;
    }

    //子类实现的扫描发生改变后的执行动作
    public abstract void onChange();

    //工作方法,具体的步骤都在这里
    private void working() {
        //1.首先进行扫描
        scan(rootDir);
        //2.扫描完成后进行比较,如果有改动则调用onChange()方法
        compare();
        //3.没有改动则把准备扫描的map清空
        preScan.clear();
        //4.正在扫描的文件放入
        preScan.putAll(curScan);
        //5.清空正在扫描文件的map
        curScan.clear();
    }

    //开启扫描,就是建立一个定时任务,执行working()方法
    public void start() {
        if (!isRunning) {
            timer = new Timer("Starlight Scanner", true);
            task = new TimerTask() {
                @Override
                public void run() {
                    working();
                }};
            timer.schedule(task, 1010L * interval, 1010L * interval);
            isRunning = true;
        }
    }

    //停止扫描,停止定时器和任务
    public void stop() {
        if (isRunning) {
            timer.cancel();
            task.cancel();
            isRunning = false;
        }
    }

    //比较的方法,就是将两个map进行equals比较,不一致就说明有文件进行了修改
    //出现修改就执行onChange()方法
    private void compare() {
        if (preScan.size() == 0)
            return;
        if (!preScan.equals(curScan))
            onChange();
    }

    //递归扫描根目录中所有的文件,将其放入curScan中
    private void scan(File file) {
        if (file == null || !file.exists())
            return;
        if (file.isFile()) {
            try {
                //这里的getCanonicalPath()就是获取文件的完全路径
                //TimeSize是一个文件的简略表达的类,可以表示一个文件的,在Scanner类的下方给出了这个类的定义
                curScan.put(file.getCanonicalPath(), new TimeSize(file.lastModified(),                                                  file.length()));
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        } else if (file.isDirectory()) {
            File[] fs = file.listFiles();
            if (fs != null)
                for (File f : fs)
                    scan(f);
        }
    }
}

//此类是文件类的简略表达
class TimeSize {
    /** 
     * @fields time 文件的最近修改时间
     */ 
    final long time;
    /** 
     * @fields size 文件的大小
     */ 
    final long size;

    public TimeSize(long time, long size) {
        super();
        this.time = time;
        this.size = size;
    }

    @Override
    public int hashCode() {
        return (int)(time ^ size);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof TimeSize) {
            TimeSize ts = (TimeSize)obj;
            return this.time == ts.time && this.size == ts.size;
        }
        return false;
    }

    @Override
    public String toString() {
        return "TimeSize [time=" + time + ", size=" + size + "]";
    }

}

JettyServer类

当扫描器发现文件发生变化,就new出一个匿名内部类,执行服务器重启任务

//配置自动扫描
if (scanIntervalSeconds > 0) {
    Scanner scanner = new Scanner(PathUtil.getWebRootPath(), scanIntervalSeconds) {
        @Override
        public void onChange() {
            System.err.println("\n正在重新加载修改文件...");
            try {
                webApp.stop();
                JFianlClassLoader loader = new JFianlClassLoader(webApp, getClassPath());
                webApp.setClassLoader(loader);
                webApp.start();
                System.err.println("重新加载完成");
            } catch (Exception e) {
                System.err.println("经过文件的修改后项目无法完成重新配置和重新启动:" + e.getMessage());
                //TODO
            }

        }
    };
    System.out.println("已完成检查时间为:" + scanIntervalSeconds + "秒的扫描器");
    scanner.start();
}

你可能感兴趣的:(开发框架)