001package io.jboot.app;
002
003
004import java.io.File;
005import java.util.ArrayList;
006import java.util.List;
007
008/**
009 * 参考 JFinal Undertow 的 PathKitExt,目的是在启动的时候 为 PathKit 配置参数
010 */
011public class PathKitExt {
012
013    private static String locationPath = null;    // 定位路径
014
015    private static String rootClassPath = null;
016    private static String webRootPath = null;
017
018    /**
019     * 1:获取 PathKitExt 类文件所处 jar 包文件所在的目录,注意在 "非部署" 环境中获取到的
020     * 通常是 maven 本地库中的某个目录,因为在开发时项目所依赖的 jar 包在 maven 本地库中
021     * 这种情况不能使用
022     * <p>
023     * 2:PathKitExt 自身在开发时,也就是未打成 jar 包时,获取到的是 APP_BASE/target/classes
024     * 这种情况多数不必关心,因为 PathKitExt 在使用时必定处于 jar 包之中
025     * <p>
026     * 3:获取到的 locationPath 目录用于生成部署时的 config 目录,该值只会在 "部署" 环境下被获取
027     * 也用于生成 webRootPath、rootClassPath,这两个值也只会在 "部署" 时被获取
028     * 这样就兼容了部署与非部署两种场景
029     * <p>
030     * 注意:该路径尾部的 "/" 或 "\\" 已被去除
031     */
032    public static String getLocationPath() {
033        if (locationPath != null) {
034            return locationPath;
035        }
036
037        try {
038            // Class<?> clazz = io.undertow.Undertow.class;             // 仅测试用
039            Class<?> clazz = PathKitExt.class;
040            String path = clazz.getProtectionDomain().getCodeSource().getLocation().getPath();
041            path = java.net.URLDecoder.decode(path, "UTF-8");
042            path = path.trim();
043            File file = new File(path);
044            if (file.isFile()) {
045                path = file.getParent();
046            }
047
048            path = removeSlashEnd(path);        // 去除尾部 '/' 或 '\' 字符
049            locationPath = path;
050
051            return locationPath;
052
053        } catch (Exception e) {
054            throw new RuntimeException(e);
055        }
056    }
057
058    public static String getRootClassPath() {
059        if (rootClassPath == null) {
060            rootClassPath = buildRootClassPath();
061        }
062        return rootClassPath;
063    }
064
065
066    private static String buildRootClassPath() {
067        String classPathDirEndsWith_classes = getClassPathDirEndsWith_classes();
068        if (classPathDirEndsWith_classes != null) {
069            return classPathDirEndsWith_classes;
070        }
071
072        String path = getLocationPath();
073        return processRootClassPath(path);
074    }
075
076    /**
077     * 获取以 "classes" 结尾的 class path
078     */
079    private static String getClassPathDirEndsWith_classes() {
080        String[] classPathDirs = getClassPathDirs();
081        if (classPathDirs == null || classPathDirs.length == 0) {
082            return null;
083        }
084
085        for (String dir : classPathDirs) {
086            if (dir != null) {
087                dir = removeSlashEnd(dir.trim());
088                if (dir != null && dir.endsWith("classes")) {
089                    return dir;
090                }
091            }
092        }
093
094        return null;
095    }
096
097    /**
098     * 1:开发环境 path 会以 classes 结尾
099     * <p>
100     * 2:打包以后的部署环境不会以 classes 结尾,约定一个合理的项目打包结构
101     * 暂时约定 APP_BASE/config 为 rootClassPath,因为要读取外部配置文件
102     */
103    private static String processRootClassPath(String path) {
104        if (path.endsWith("classes")) {
105            return path;
106        }
107
108        if (path.endsWith(File.separatorChar + "lib")) {
109            path = path.substring(0, path.lastIndexOf(File.separatorChar));
110        }
111
112        return new File(path + File.separator + "config").getAbsolutePath();
113    }
114
115    public static String removeSlashEnd(String path) {
116        if (path.endsWith(File.separator)) {
117            return path.substring(0, path.length() - 1);
118        } else {
119            return path;
120        }
121    }
122
123    // --------------------------------------------------------------------------------------
124
125    public static String getWebRootPath() {
126        if (webRootPath == null) {
127            webRootPath = buildWebRootPath();
128        }
129        return webRootPath;
130    }
131
132    private static String buildWebRootPath() {
133        String classPathDirEndsWith_classes = getClassPathDirEndsWith_classes();
134        if (classPathDirEndsWith_classes != null) {
135            return classPathDirEndsWith_classes;
136        }
137
138        String path = getLocationPath();
139        return processWebRootPath(path);
140    }
141
142    private static String processWebRootPath(String path) {
143        if (path.endsWith("classes")) {
144            return path;
145        }
146
147        if (path.endsWith(File.separatorChar + "lib")) {
148            path = path.substring(0, path.lastIndexOf(File.separatorChar));
149        }
150
151        return new File(path + File.separator + "webapp").getAbsolutePath();
152    }
153
154    // ---------
155
156    private static String[] classPathDirs = null;
157
158    public static String[] getClassPathDirs() {
159        if (classPathDirs == null) {
160            classPathDirs = buildClassPathDirs();
161        }
162        return classPathDirs;
163    }
164
165    private static String[] buildClassPathDirs() {
166        List<String> list = new ArrayList<>();
167        String[] classPathArray = System.getProperty("java.class.path").split(File.pathSeparator);
168        for (String classPath : classPathArray) {
169            classPath = classPath.trim();
170
171            if (classPath.startsWith("./")) {
172                classPath = classPath.substring(2);
173            }
174
175            File file = new File(classPath);
176            if (file.exists() && file.isDirectory()) {
177                // if (!classPath.endsWith("/") && !classPath.endsWith("\\")) {
178                if (!classPath.endsWith(File.separator)) {
179                    classPath = classPath + File.separator;        // append postfix char "/"
180                }
181
182                list.add(classPath);
183            }
184        }
185        return list.toArray(new String[list.size()]);
186    }
187
188}