001/** 002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). 003 * <p> 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package io.jboot.app; 017 018import io.jboot.app.config.JbootConfigManager; 019import io.jboot.utils.FileScanner; 020import io.jboot.utils.StrUtil; 021import org.apache.commons.io.FileUtils; 022 023import java.io.File; 024import java.io.IOException; 025import java.net.URL; 026import java.util.ArrayList; 027import java.util.List; 028 029 030public class JbootResourceLoader { 031 032 private String resourcePathName; 033 private List<FileScanner> scanners = new ArrayList<>(); 034 035 public JbootResourceLoader() { 036 String pathName = JbootConfigManager.me().getConfigValue("jboot.app.resourcePathName"); 037 this.resourcePathName = StrUtil.obtainDefault(pathName, "webapp"); 038 } 039 040 public JbootResourceLoader(String resourcePathName) { 041 this.resourcePathName = StrUtil.requireNonBlank(resourcePathName, "Resource path name must not be blank."); 042 } 043 044 045 public void start() { 046 try { 047 048 URL url = JbootResourceLoader.class.getClassLoader().getResource(""); 049 if (url == null || url.toString().endsWith(".jar!/")) { 050 return; 051 } 052 053 String classPath = url.toURI().getPath(); 054 File srcRootPath = new File(classPath, "../..").getCanonicalFile(); 055 056 while (new File(srcRootPath.getParent(), "pom.xml").exists()) { 057 srcRootPath = srcRootPath.getParentFile(); 058 } 059 060 List<File> resourcesDirs = new ArrayList<>(); 061 findResourcesPath(srcRootPath, resourcesDirs); 062 063 String targetPath = classPath.endsWith("/config") 064 ? new File(classPath, "..").getCanonicalPath() : classPath; 065 for (File resourcesDir : resourcesDirs) { 066 startNewScanner(resourcesDir.getCanonicalFile(), targetPath); 067 } 068 069 Runtime.getRuntime().addShutdownHook(new Thread(JbootResourceLoader.this::stop)); 070 System.err.println("JbootResourceLoader is started, and watching resource dir: " + resourcePathName); 071 072 } catch (Exception e) { 073 e.printStackTrace(); 074 } 075 } 076 077 public void stop() { 078 scanners.forEach(FileScanner::stop); 079 System.out.println("JbootResourceLoader has stopped."); 080 } 081 082 private void findResourcesPath(File root, List<File> resourcesDirs) { 083 File[] dirs = root.listFiles(File::isDirectory); 084 if (dirs == null || dirs.length == 0) { 085 return; 086 } 087 for (File dir : dirs) { 088 089 File parentFile = dir.getParentFile(); 090 if (parentFile == null) { 091 return; 092 } 093 094 if (dir.getName().equals(resourcePathName) 095 && "main".equals(parentFile.getName())) { 096 resourcesDirs.add(dir); 097 } else { 098 findResourcesPath(dir, resourcesDirs); 099 } 100 } 101 } 102 103 104 private void startNewScanner(File resourcesDir, String classPath) throws IOException { 105 // main/webapp/ 106 String path = "main" + File.separator + resourcePathName + File.separator; 107 FileScanner scanner = new FileScanner(resourcesDir.getCanonicalPath(), 5) { 108 @Override 109 public void onChange(String action, String file) { 110 if (FileScanner.ACTION_INIT.equals(action)) { 111 return; 112 } 113 114 //忽略掉 windows 和 mac 下的临时文件 115 if (file.endsWith("~") || file.endsWith(".DS_Store")) { 116 return; 117 } 118 119 int indexOf = file.indexOf(path); 120 121 File target = new File(classPath, resourcePathName + File.separator + file.substring(indexOf + path.length())); 122 System.err.println("JbootResourceLoader " + action + ": " + target); 123 124 //文件删除 125 if (FileScanner.ACTION_DELETE.equals(action)) { 126 if (!target.delete()) { 127 System.err.println("JbootResourceLoader can not delete file: " + target); 128 } 129 } 130 //新增文件 或 修改文件 131 else { 132 try { 133 FileUtils.copyFile(new File(file), target); 134 } catch (Exception e) { 135 System.err.println("JbootResourceLoader copy file error: " + e.getMessage()); 136 } 137 } 138 } 139 }; 140 141 scanner.start(); 142 scanners.add(scanner); 143 } 144}