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();
|
}
|
}
|