yeg
5 天以前 99935242ecccd94e76978e2b71cbc46b87d31dc6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.battery.device.api.credentials;
 
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import java.util.Properties;
import java.util.function.Function;
import com.battery.device.api.DeviceAdapterException;
 
/**
 * 来源凭据的取值顺序(ADR-0065):进程工作目录下的 {@code config/vendor-credentials.properties}
 * → 同名环境变量 → 同名 JVM 启动参数。
 *
 * <p>凭据不进数据库、不进仓库,也不打包;仓库只提交 {@code .example} 模板。
 * 文件优先是为了让部署只做"复制模板填一行"这一件事;保留环境变量与启动参数两条,
 * 是因为交付形态可能只提供其中某一种。三者都没有时装配失败,错误信息同时点名文件路径与键名,
 * 使"忘了放凭据"是可见的失败而不是沉默的缺失。
 *
 * <p>直连厂商库的口令({@code NEWARE_MYSQL})与推式来源的接收端点令牌({@code PUSH_INGEST})
 * 都是来源凭据,因此共用同一个文件与同一套取值顺序。
 */
public final class LocalCredentials
{
    /** 相对进程工作目录定位,使开发(仓库根)与交付(应用目录)用同一个位置。 */
    public static final Path DEFAULT_FILE = Path.of("config", "vendor-credentials.properties");
 
    private LocalCredentials() { }
 
    /** 按默认位置与进程环境取值;取不到时抛出 {@link DeviceAdapterException}。 */
    public static String resolve(String key)
    {
        return resolve(key, DEFAULT_FILE.toAbsolutePath(), System.getenv(), System::getProperty);
    }
 
    /**
     * 取值顺序:文件 → 环境变量 → 启动参数。文件里的空白值不算配置过,
     * 否则一个空模板会把失败推到连接阶段。
     */
    public static String resolve(String key, Path credentialsFile, Map<String, String> environment,
            Function<String, String> startupParameters)
    {
        if (key == null || key.isBlank()) throw new DeviceAdapterException("未配置来源凭据的取值名");
        String fromFile = fromFile(key, credentialsFile);
        if (fromFile != null) return fromFile;
        String fromEnvironment = environment == null ? null : environment.get(key);
        if (fromEnvironment != null && !fromEnvironment.isBlank()) return fromEnvironment.trim();
        String fromParameter = startupParameters == null ? null : startupParameters.apply(key);
        if (fromParameter != null && !fromParameter.isBlank()) return fromParameter.trim();
        throw new DeviceAdapterException("缺少来源凭据:" + credentialsFile
                + " 中没有键 " + key + ",环境变量与启动参数 -D" + key + " 也都没有");
    }
 
    /** 凭据文件缺失不是错误:交付形态可能只用环境变量,所以这里只负责"读到了就返回"。 */
    private static String fromFile(String key, Path credentialsFile)
    {
        if (credentialsFile == null || !Files.isRegularFile(credentialsFile)) return null;
        Properties properties = new Properties();
        try (InputStream input = Files.newInputStream(credentialsFile))
        {
            properties.load(input);
        }
        catch (IOException exception)
        {
            throw new DeviceAdapterException("来源凭据文件无法读取:" + credentialsFile, exception);
        }
        String value = properties.getProperty(key);
        return value == null || value.isBlank() ? null : value.trim();
    }
}