Revert "fix(config): sync Swing settings after Web updates"
This reverts commit b6acc6d509.
This commit is contained in:
@@ -32,8 +32,8 @@ KLALB is a Java SRv6/load-balancing system. Start at `org.kne.cloud.network.klal
|
||||
## Important Boundaries
|
||||
|
||||
- `KLALBConfigItem` is a polymorphic JSON array keyed by case-sensitive `Type`. New types need serializer and deserializer support; preserve unknown items' raw JSON.
|
||||
- `/api/config` parses fields and legacy aliases explicitly. Web and Swing writes must use `KLALBProxySystem`'s revision-checked detached-candidate commit path, never mutate the canonical config directly.
|
||||
- Vendored Gson is 2.1: serialize HTTP `JsonElement` values with `toString()`; serialize configuration through the configured pretty-print Gson path.
|
||||
- `/api/config` is field-by-field parsing, not whole-object Gson mapping. Keep legacy key aliases in sync with new fields.
|
||||
- Vendored Gson is `2.1`: responses that are `JsonElement` instances must be serialized with `JsonElement.toString()`, not reflective `gson.toJson(Object)`.
|
||||
- UI strings use `UIEnv.getRsb()`; add keys to both `src/klalb_zh_CN.properties` and `src/klalb_en_US.properties`.
|
||||
- `KLALBController.PublishedNodeInfo` is the thread-safe node-info source. Publish profile, effective external endpoints, and extra routes through the controller so snapshots and update flags remain coherent. Peer queries are separate profile, endpoint, and extra-route requests; use `NodeInfoQueryCoordinator` when a consumer needs an aggregate detail result.
|
||||
- `RouterInfo` retains an empty legacy UTF slot. `RouterInfoPacket` appends optional update flags behind a marker; treat binary codec changes as mesh-compatibility work and preserve old-reader behavior.
|
||||
|
||||
@@ -34,13 +34,7 @@ addline=Add line
|
||||
reconnectall=Reconnect All
|
||||
remotelines=Remote lines
|
||||
settings=Settings
|
||||
saveconfigsuccess=Save config success
|
||||
configexternalupdate=The configuration was updated by another source.
|
||||
configreload=Reload
|
||||
configcontinue=Continue editing
|
||||
configsaveconflict=Save failed: the configuration was updated. Reload and try again.
|
||||
configsavefailed=Failed to save configuration.
|
||||
configconflicttitle=Configuration conflict
|
||||
saveconfigsuccess=Save config success
|
||||
warning=Warning
|
||||
invaildipv6addr=IPv6 address:Invaild Input
|
||||
invailddnsserver=DNS server:Invaild Input
|
||||
|
||||
@@ -34,13 +34,7 @@ addline=添加链路
|
||||
reconnectall=全部重连
|
||||
remotelines=远程链路
|
||||
settings=设置
|
||||
saveconfigsuccess=保存配置成功
|
||||
configexternalupdate=配置已被其他来源更新。
|
||||
configreload=重新加载
|
||||
configcontinue=继续编辑
|
||||
configsaveconflict=保存失败:配置已被更新,请重新加载后再试。
|
||||
configsavefailed=保存配置失败。
|
||||
configconflicttitle=配置冲突
|
||||
saveconfigsuccess=保存配置成功
|
||||
warning=警告
|
||||
invaildipv6addr=IPv6地址:非法输入
|
||||
invailddnsserver=DNS服务器:非法输入
|
||||
|
||||
@@ -583,63 +583,7 @@ public class KLALBController {
|
||||
|
||||
private KLALBRoutingProtocolAPIClient apiClient;
|
||||
|
||||
private volatile KLALBControllerConfigItem configItem;
|
||||
|
||||
private static <T> List<T> copyConfigList(List<T> values) {
|
||||
return values == null ? null : new ArrayList<T>(values);
|
||||
}
|
||||
|
||||
private static KLALBControllerConfigItem copyConfigItem(KLALBControllerConfigItem source) {
|
||||
KLALBControllerConfigItem copy = new KLALBControllerConfigItem();
|
||||
copy.setLanguage(source.getLanguage());
|
||||
copy.setNogui(source.isNogui());
|
||||
copy.setVirtualAddress(source.getVirtualAddress());
|
||||
copy.setVirtualASN(source.getVirtualASN());
|
||||
copy.setDNS(copyConfigList(source.getDNS()));
|
||||
copy.setTCPListen(source.getTCPListen());
|
||||
copy.setUDPListen(source.getUDPListen());
|
||||
copy.setVirtualSocketName(source.getVirtualSocketName());
|
||||
copy.setExternalEndpoints(copyConfigList(source.getExternalEndpoints()));
|
||||
copy.setAutoConnections(copyConfigList(source.getAutoConnections()));
|
||||
copy.setNtpServers(copyConfigList(source.getNtpServers()));
|
||||
copy.setExtraRoutes(copyConfigList(source.getExtraRoutes()));
|
||||
copy.setDenyExternalEndpointQuery(source.isDenyExternalEndpointQuery());
|
||||
copy.setDenyExternalEndpointBroadcast(source.isDenyExternalEndpointBroadcast());
|
||||
copy.setCongestionAlgorithm(source.getCongestionAlgorithm());
|
||||
copy.setBurstLimit(source.getBurstLimit());
|
||||
copy.setDelayUpperBound(source.getDelayUpperBound());
|
||||
copy.setDelayLowerBound(source.getDelayLowerBound());
|
||||
copy.setNagleDelayTime(source.getNagleDelayTime());
|
||||
copy.setLinkNagleDelayTime(source.getLinkNagleDelayTime());
|
||||
copy.setLinkConnectionsCount(source.getLinkConnectionsCount());
|
||||
copy.setEnableTUN(source.isEnableTUN());
|
||||
copy.setTUNName(source.getTUNName());
|
||||
copy.setPerformanceStrategy(source.getPerformanceStrategy());
|
||||
copy.setDeviceName(source.getDeviceName());
|
||||
copy.setDeviceDescription(source.getDeviceDescription());
|
||||
copy.setNetworkInterfaceExcepts(copyConfigList(source.getNetworkInterfaceExcepts()));
|
||||
copy.setWebUI(source.isWebUI());
|
||||
copy.setWebListen(source.getWebListen());
|
||||
return copy;
|
||||
}
|
||||
|
||||
public void applyConfigItem(KLALBControllerConfigItem committedConfigItem) {
|
||||
if (committedConfigItem == null) {
|
||||
throw new IllegalArgumentException("Controller configuration is required");
|
||||
}
|
||||
KLALBControllerConfigItem detachedConfigItem = copyConfigItem(committedConfigItem);
|
||||
synchronized (externalEndpoints) {
|
||||
configItem = detachedConfigItem;
|
||||
if (srv6Router != null) {
|
||||
PerformanceStrategy strategy = PerformanceStrategy.fromDescription(detachedConfigItem.getPerformanceStrategy());
|
||||
if (strategy != null) {
|
||||
srv6Router.setPerformanceStrategy(strategy);
|
||||
}
|
||||
}
|
||||
publishNodeInfoLocked(detachedConfigItem.getDeviceName(), detachedConfigItem.getDeviceDescription(),
|
||||
detachedConfigItem.getExternalEndpoints(), detachedConfigItem.getExtraRoutes());
|
||||
}
|
||||
}
|
||||
private KLALBControllerConfigItem configItem;
|
||||
|
||||
public void addRemoteLines(List<MultiProtocolSocketAddress> select) {
|
||||
for (MultiProtocolSocketAddress target : select) {
|
||||
|
||||
@@ -4,21 +4,11 @@ import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.InetAddress;
|
||||
import java.io.Reader;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.kne.cloud.network.*;
|
||||
import org.kne.cloud.network.klalb.ui.KLALBStateGUI3;
|
||||
@@ -29,9 +19,8 @@ import org.kne.cloud.network.klalb.web.KLALBWebServer;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonDeserializationContext;
|
||||
import com.google.gson.JsonDeserializer;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
@@ -40,82 +29,13 @@ import com.google.gson.JsonSerializationContext;
|
||||
import com.google.gson.JsonSerializer;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class KLALBProxySystem {
|
||||
public enum ConfigChangeSource {
|
||||
WEB, SWING
|
||||
}
|
||||
|
||||
public static final class ControllerConfigSnapshot {
|
||||
private final long revision;
|
||||
private final String controllerJson;
|
||||
|
||||
private ControllerConfigSnapshot(long revision, String controllerJson) {
|
||||
this.revision = revision;
|
||||
this.controllerJson = controllerJson;
|
||||
}
|
||||
|
||||
public long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public String getControllerJson() {
|
||||
return controllerJson;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ConfigChangeEvent {
|
||||
private final ControllerConfigSnapshot snapshot;
|
||||
private final ConfigChangeSource source;
|
||||
|
||||
private ConfigChangeEvent(ControllerConfigSnapshot snapshot, ConfigChangeSource source) {
|
||||
this.snapshot = snapshot;
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
public ControllerConfigSnapshot getSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
public ConfigChangeSource getSource() {
|
||||
return source;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class CommitResult {
|
||||
private final boolean success;
|
||||
private final long revision;
|
||||
private final ControllerConfigSnapshot snapshot;
|
||||
|
||||
private CommitResult(boolean success, long revision, ControllerConfigSnapshot snapshot) {
|
||||
this.success = success;
|
||||
this.revision = revision;
|
||||
this.snapshot = snapshot;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return success;
|
||||
}
|
||||
|
||||
public long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
|
||||
public ControllerConfigSnapshot getSnapshot() {
|
||||
return snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Proxy> proxys=new HashSet<>();
|
||||
private KLALBController klalbController;
|
||||
private KLALBWebServer webServer;
|
||||
private KLALBConfig config;
|
||||
private Gson gson;
|
||||
private File jsonFile;
|
||||
private final Object configLock = new Object();
|
||||
private long configRevision;
|
||||
private volatile ControllerConfigSnapshot controllerConfigSnapshot = new ControllerConfigSnapshot(0L, null);
|
||||
private final CopyOnWriteArraySet<Consumer<ConfigChangeEvent>> configChangeListeners = new CopyOnWriteArraySet<>();
|
||||
private JsonArray rawConfigJson;
|
||||
public class KLALBProxySystem {
|
||||
private Set<Proxy> proxys=new HashSet<>();
|
||||
private KLALBController klalbController;
|
||||
private KLALBWebServer webServer;
|
||||
private KLALBConfig config;
|
||||
private Gson gson;
|
||||
private File jsonFile;
|
||||
{
|
||||
GsonBuilder gb=new GsonBuilder().setPrettyPrinting();
|
||||
MultiProtocolSocketAddress.registerToGsonBuilder(gb);
|
||||
@@ -221,27 +141,13 @@ public class KLALBProxySystem {
|
||||
public void loadConfigJson(Reader json) {
|
||||
loadConfigJson(new JsonParser().parse(json));
|
||||
}
|
||||
public void loadConfigJson(JsonElement json) {
|
||||
JsonElement rawJson = new JsonParser().parse(json.toString());
|
||||
KLALBConfig config= gson.fromJson(json, KLALBConfig.class);
|
||||
JsonArray rawArray = rawJson instanceof JsonArray ? (JsonArray) rawJson : null;
|
||||
synchronized (configLock) {
|
||||
installConfigLocked(config, rawArray);
|
||||
}
|
||||
}
|
||||
public void loadConfig(KLALBConfig config) {
|
||||
JsonElement rawJson = gson.toJsonTree(config);
|
||||
JsonArray rawArray = rawJson instanceof JsonArray ? (JsonArray) rawJson : null;
|
||||
synchronized (configLock) {
|
||||
installConfigLocked(config, rawArray);
|
||||
}
|
||||
}
|
||||
|
||||
private void installConfigLocked(KLALBConfig config, JsonArray rawArray) {
|
||||
this.config=config;
|
||||
this.configRevision=0L;
|
||||
rawConfigJson=rawArray;
|
||||
for(KLALBConfigItem item:config) {
|
||||
public void loadConfigJson(JsonElement json) {
|
||||
KLALBConfig config= gson.fromJson(json, KLALBConfig.class);
|
||||
loadConfig(config);
|
||||
}
|
||||
public void loadConfig(KLALBConfig config) {
|
||||
this.config=config;
|
||||
for(KLALBConfigItem item:config) {
|
||||
if(item instanceof KLALBControllerConfigItem) {
|
||||
KLALBControllerConfigItem kcci=(KLALBControllerConfigItem) item;
|
||||
String lstr=kcci.getLanguage();
|
||||
@@ -321,180 +227,41 @@ public class KLALBProxySystem {
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
controllerConfigSnapshot = createControllerConfigSnapshotLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void saveConfigToFile() {
|
||||
synchronized (configLock) {
|
||||
if (jsonFile != null && config != null) {
|
||||
try {
|
||||
KLALBControllerConfigItem item = findControllerConfigItemLocked();
|
||||
if (item == null) {
|
||||
persistConfigJsonLocked(gson.toJson(config));
|
||||
} else {
|
||||
String controllerJson = gson.toJson(item);
|
||||
JsonArray completeConfig = createCompleteConfigJsonLocked(controllerJson);
|
||||
persistConfigJsonLocked(gson.toJson(completeConfig));
|
||||
rawConfigJson = completeConfig;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private KLALBControllerConfigItem findControllerConfigItemLocked() {
|
||||
if (config == null) {
|
||||
return null;
|
||||
}
|
||||
for (KLALBConfigItem item : config) {
|
||||
if (item instanceof KLALBControllerConfigItem) {
|
||||
return (KLALBControllerConfigItem) item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private int findControllerConfigIndexLocked() {
|
||||
if (config == null) {
|
||||
return -1;
|
||||
}
|
||||
for (int i = 0; i < config.size(); i++) {
|
||||
if (config.get(i) instanceof KLALBControllerConfigItem) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private ControllerConfigSnapshot createControllerConfigSnapshotLocked() {
|
||||
KLALBControllerConfigItem item = findControllerConfigItemLocked();
|
||||
return new ControllerConfigSnapshot(configRevision, item == null ? null : gson.toJson(item));
|
||||
}
|
||||
|
||||
public ControllerConfigSnapshot getControllerConfigSnapshot() {
|
||||
synchronized (configLock) {
|
||||
controllerConfigSnapshot = createControllerConfigSnapshotLocked();
|
||||
return controllerConfigSnapshot;
|
||||
}
|
||||
}
|
||||
|
||||
public KLALBControllerConfigItem parseControllerConfigCandidate(ControllerConfigSnapshot snapshot) {
|
||||
if (snapshot == null || snapshot.getControllerJson() == null) {
|
||||
return null;
|
||||
}
|
||||
return gson.fromJson(snapshot.getControllerJson(), KLALBControllerConfigItem.class);
|
||||
}
|
||||
|
||||
private void persistConfigJsonLocked(String json) throws IOException {
|
||||
if (jsonFile == null) {
|
||||
return;
|
||||
}
|
||||
Path target = jsonFile.toPath().toAbsolutePath();
|
||||
Path parent = target.getParent();
|
||||
Path temporary = Files.createTempFile(parent, target.getFileName().toString(), ".tmp");
|
||||
try {
|
||||
Files.write(temporary, json.getBytes(StandardCharsets.UTF_8), StandardOpenOption.TRUNCATE_EXISTING,
|
||||
StandardOpenOption.WRITE);
|
||||
try {
|
||||
Files.move(temporary, target, StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
|
||||
} catch (AtomicMoveNotSupportedException e) {
|
||||
Files.move(temporary, target, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
} finally {
|
||||
Files.deleteIfExists(temporary);
|
||||
}
|
||||
}
|
||||
|
||||
private JsonArray createCompleteConfigJsonLocked(String controllerJson) throws IOException {
|
||||
JsonElement source = rawConfigJson == null ? gson.toJsonTree(config) : rawConfigJson;
|
||||
if (!(source instanceof JsonArray)) {
|
||||
throw new IOException("Configuration is not an array");
|
||||
}
|
||||
JsonArray sourceArray = (JsonArray) source;
|
||||
int index = findControllerConfigIndexLocked();
|
||||
if (index < 0 || index >= sourceArray.size() || sourceArray.size() != config.size()) {
|
||||
throw new IOException("Configuration item layout changed");
|
||||
}
|
||||
JsonArray mergedConfig = new JsonArray();
|
||||
JsonElement controllerElement = new JsonParser().parse(controllerJson);
|
||||
for (int i = 0; i < sourceArray.size(); i++) {
|
||||
mergedConfig.add(i == index ? controllerElement : sourceArray.get(i));
|
||||
}
|
||||
return mergedConfig;
|
||||
}
|
||||
|
||||
public void addConfigChangeListener(Consumer<ConfigChangeEvent> listener) {
|
||||
configChangeListeners.add(listener);
|
||||
}
|
||||
|
||||
public void removeConfigChangeListener(Consumer<ConfigChangeEvent> listener) {
|
||||
configChangeListeners.remove(listener);
|
||||
}
|
||||
|
||||
public CommitResult commitControllerConfig(long expectedRevision, KLALBControllerConfigItem candidate,
|
||||
ConfigChangeSource source) throws IOException {
|
||||
if (candidate == null || source == null) {
|
||||
throw new IllegalArgumentException("Candidate and source are required");
|
||||
}
|
||||
ConfigChangeEvent event;
|
||||
CommitResult result;
|
||||
synchronized (configLock) {
|
||||
if (expectedRevision != configRevision) {
|
||||
ControllerConfigSnapshot currentSnapshot = createControllerConfigSnapshotLocked();
|
||||
controllerConfigSnapshot = currentSnapshot;
|
||||
return new CommitResult(false, configRevision, currentSnapshot);
|
||||
}
|
||||
int index = findControllerConfigIndexLocked();
|
||||
if (index < 0) {
|
||||
throw new IOException("Controller configuration is missing");
|
||||
}
|
||||
String candidateJson = gson.toJson(candidate);
|
||||
KLALBControllerConfigItem committedCandidate = gson.fromJson(candidateJson,
|
||||
KLALBControllerConfigItem.class);
|
||||
if (PerformanceStrategy.fromDescription(committedCandidate.getPerformanceStrategy()) == null) {
|
||||
throw new IllegalArgumentException("Unknown performanceStrategy: "
|
||||
+ committedCandidate.getPerformanceStrategy());
|
||||
}
|
||||
com.google.gson.JsonArray mergedConfig = createCompleteConfigJsonLocked(candidateJson);
|
||||
persistConfigJsonLocked(gson.toJson(mergedConfig));
|
||||
rawConfigJson = mergedConfig;
|
||||
|
||||
config.set(index, committedCandidate);
|
||||
if (klalbController != null) {
|
||||
klalbController.applyConfigItem(committedCandidate);
|
||||
}
|
||||
configRevision++;
|
||||
controllerConfigSnapshot = createControllerConfigSnapshotLocked();
|
||||
event = new ConfigChangeEvent(controllerConfigSnapshot, source);
|
||||
result = new CommitResult(true, configRevision, controllerConfigSnapshot);
|
||||
}
|
||||
for (Consumer<ConfigChangeEvent> listener : configChangeListeners) {
|
||||
try {
|
||||
listener.accept(event);
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public void saveConfigToFile() {
|
||||
if (jsonFile != null && config != null) {
|
||||
String json = gson.toJson(config);
|
||||
try (FileWriter fw = new FileWriter(jsonFile)) {
|
||||
fw.write(json);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private KLALBStateGUI3 kgui;
|
||||
public KLALBStateGUI3 getKLALBGUI() {
|
||||
if(kgui==null) {
|
||||
kgui=new KLALBStateGUI3(klalbController);
|
||||
kgui.bindConfigSystem(this);
|
||||
}
|
||||
public KLALBStateGUI3 getKLALBGUI() {
|
||||
if(kgui==null) {
|
||||
kgui=new KLALBStateGUI3(klalbController);
|
||||
kgui.loadConfig(config);
|
||||
kgui.setSaveComsumer((cfg)->{
|
||||
saveConfigToFile();
|
||||
});
|
||||
}
|
||||
return kgui;
|
||||
}
|
||||
public KLALBConfig getConfig() {
|
||||
return config;
|
||||
}
|
||||
public KLALBControllerConfigItem getControllerConfig() {
|
||||
return parseControllerConfigCandidate(getControllerConfigSnapshot());
|
||||
}
|
||||
}
|
||||
public KLALBControllerConfigItem getControllerConfig() {
|
||||
for(KLALBConfigItem item:config) {
|
||||
if(item instanceof KLALBControllerConfigItem)
|
||||
return (KLALBControllerConfigItem) item;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,82 +104,8 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
private JCheckBox nogui;
|
||||
|
||||
// ==================== 配置和回调 ====================
|
||||
private KLALBConfig config; // 配置文件
|
||||
private Consumer<KLALBConfig> saveComsumer; // 保存配置的回调函数
|
||||
private KLALBProxySystem configSystem;
|
||||
private KLALBProxySystem.ControllerConfigSnapshot loadedSnapshot;
|
||||
private long loadedRevision;
|
||||
private SettingsFormState loadedFormState;
|
||||
private KLALBProxySystem.ControllerConfigSnapshot pendingExternalSnapshot;
|
||||
private long pendingExternalRevision;
|
||||
private boolean configSystemClosed;
|
||||
private long bindingGeneration;
|
||||
private Consumer<KLALBProxySystem.ConfigChangeEvent> boundConfigChangeListener;
|
||||
|
||||
private static final class SettingsFormState {
|
||||
private final String language, deviceName, deviceDescription, address, dns, extraRoutes;
|
||||
private final String asn, tunName, webListen, tcpListen, udpListen, openLines, connectLines, ntp;
|
||||
private final String performance, congestion;
|
||||
private final boolean enableTun, webApi, nogui, denyQuery, denyBroadcast;
|
||||
private final List<String> interfaces;
|
||||
private final int connections, burst, upper, lower, nagle, linkNagle;
|
||||
|
||||
private SettingsFormState(KLALBStateGUI3 gui) {
|
||||
Language languageItem = (Language) gui.comboLang.getSelectedItem();
|
||||
language = languageItem == null ? null : languageItem.name();
|
||||
deviceName = gui.deviceNameSet.getText();
|
||||
deviceDescription = gui.deviceDescriptionSet.getText();
|
||||
address = gui.addressFieldSet.getText();
|
||||
dns = gui.dnsAreaSet.getText();
|
||||
extraRoutes = gui.extraRoutesSet.getText();
|
||||
asn = gui.asnFieldSet.getText();
|
||||
tunName = gui.tunDeviceName.getText();
|
||||
webListen = gui.webListenSet.getText();
|
||||
tcpListen = gui.tcpListeningSet.getText();
|
||||
udpListen = gui.udpListeningSet.getText();
|
||||
openLines = gui.openLineTabelSet.getText();
|
||||
connectLines = gui.connectLineTabelSet.getText();
|
||||
ntp = gui.ntpServerSet.getText();
|
||||
PerformanceStrategyItem performanceItem = (PerformanceStrategyItem) gui.comboPerformance.getSelectedItem();
|
||||
performance = performanceItem == null ? null : performanceItem.getStrategy().toString();
|
||||
congestion = String.valueOf(gui.congestions.getComboBox().getSelectedItem());
|
||||
enableTun = gui.enableTUN.isSelected();
|
||||
webApi = gui.webApiEnabled.isSelected();
|
||||
nogui = gui.nogui.isSelected();
|
||||
denyQuery = gui.denyQuery.isSelected();
|
||||
denyBroadcast = gui.denyBroadcast.isSelected();
|
||||
interfaces = new ArrayList<>();
|
||||
for (int i = 0; i < gui.nilsimdl.getSize(); i++) interfaces.add(gui.nilsimdl.getElementAt(i).getName());
|
||||
connections = gui.linkConnectionsCount.getSlider().getValue();
|
||||
burst = gui.burstLimit.getSlider().getValue();
|
||||
upper = gui.delayHbound.getSlider().getValue();
|
||||
lower = gui.delayLbound.getSlider().getValue();
|
||||
nagle = gui.nagleDelayTime.getSlider().getValue();
|
||||
linkNagle = gui.linkNagleDelayTime.getSlider().getValue();
|
||||
}
|
||||
|
||||
@Override public boolean equals(Object obj) {
|
||||
if (!(obj instanceof SettingsFormState)) return false;
|
||||
SettingsFormState o = (SettingsFormState) obj;
|
||||
return enableTun == o.enableTun && webApi == o.webApi && nogui == o.nogui
|
||||
&& denyQuery == o.denyQuery && denyBroadcast == o.denyBroadcast
|
||||
&& connections == o.connections && burst == o.burst && upper == o.upper
|
||||
&& lower == o.lower && nagle == o.nagle && linkNagle == o.linkNagle
|
||||
&& Objects.equals(language, o.language) && Objects.equals(deviceName, o.deviceName)
|
||||
&& Objects.equals(deviceDescription, o.deviceDescription) && Objects.equals(address, o.address)
|
||||
&& Objects.equals(dns, o.dns) && Objects.equals(extraRoutes, o.extraRoutes)
|
||||
&& Objects.equals(asn, o.asn) && Objects.equals(tunName, o.tunName)
|
||||
&& Objects.equals(webListen, o.webListen) && Objects.equals(tcpListen, o.tcpListen)
|
||||
&& Objects.equals(udpListen, o.udpListen) && Objects.equals(openLines, o.openLines)
|
||||
&& Objects.equals(connectLines, o.connectLines) && Objects.equals(ntp, o.ntp)
|
||||
&& Objects.equals(performance, o.performance) && Objects.equals(congestion, o.congestion)
|
||||
&& Objects.equals(interfaces, o.interfaces);
|
||||
}
|
||||
@Override public int hashCode() { return Objects.hash(language, deviceName, deviceDescription, address, dns,
|
||||
extraRoutes, asn, tunName, webListen, tcpListen, udpListen, openLines, connectLines, ntp,
|
||||
performance, congestion, enableTun, webApi, nogui, denyQuery, denyBroadcast, interfaces,
|
||||
connections, burst, upper, lower, nagle, linkNagle); }
|
||||
}
|
||||
private KLALBConfig config; // 配置文件
|
||||
private Consumer<KLALBConfig> saveComsumer; // 保存配置的回调函数
|
||||
|
||||
// ==================== 尺寸常量 ====================
|
||||
private Dimension dashSize = new Dimension((int) (145 * 0.7), (int) (165 * 0.7)); // 仪表盘尺寸
|
||||
@@ -1129,10 +1055,15 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
/**
|
||||
* 保存配置到文件
|
||||
*/
|
||||
private void saveConfig() {
|
||||
if (configSystem == null || loadedSnapshot == null) return;
|
||||
KLALBControllerConfigItem kck = configSystem.parseControllerConfigCandidate(loadedSnapshot);
|
||||
if (kck == null) return;
|
||||
private void saveConfig() {
|
||||
if (config == null) {
|
||||
config = new KLALBConfig();
|
||||
config.add(new KLALBControllerConfigItem());
|
||||
}
|
||||
|
||||
for (KLALBConfigItem item : config) {
|
||||
if (item instanceof KLALBControllerConfigItem) {
|
||||
KLALBControllerConfigItem kck = (KLALBControllerConfigItem) item;
|
||||
String oldDeviceName = kck.getDeviceName();
|
||||
String oldDeviceDescription = kck.getDeviceDescription();
|
||||
List<MultiProtocolSocketAddress> oldExternalEndpoints = kck.getExternalEndpoints() == null
|
||||
@@ -1383,32 +1314,30 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
|
||||
kck.setLinkNagleDelayTime(linkNagleDelayTime.getSlider().getValue()*100000L);
|
||||
|
||||
kck.setDeviceName(newDeviceName);
|
||||
kck.setDeviceDescription(newDeviceDescription);
|
||||
kck.setExternalEndpoints(newExternalEndpoints);
|
||||
kck.setExtraRoutes(newExtraRoutes);
|
||||
try {
|
||||
KLALBProxySystem.CommitResult result = configSystem.commitControllerConfig(loadedRevision, kck,
|
||||
KLALBProxySystem.ConfigChangeSource.SWING);
|
||||
if (!result.isSuccess()) {
|
||||
resolveConfigConflict(result.getSnapshot(), "configsaveconflict");
|
||||
return;
|
||||
}
|
||||
loadedSnapshot = result.getSnapshot();
|
||||
loadedRevision = result.getRevision();
|
||||
loadedFormState = new SettingsFormState(this);
|
||||
pendingExternalSnapshot = null;
|
||||
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("saveconfigsuccess"));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("configsavefailed"),
|
||||
UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE);
|
||||
} catch (RuntimeException e) {
|
||||
e.printStackTrace();
|
||||
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("configsavefailed"),
|
||||
UIEnv.getRsb().getString("warning"), JOptionPane.WARNING_MESSAGE);
|
||||
}
|
||||
}
|
||||
boolean deviceNameChanged = !Objects.equals(oldDeviceName, newDeviceName);
|
||||
boolean deviceDescriptionChanged = !Objects.equals(oldDeviceDescription, newDeviceDescription);
|
||||
boolean externalEndpointsChanged = !Objects.equals(oldExternalEndpoints, newExternalEndpoints);
|
||||
boolean extraRoutesChanged = !Objects.equals(oldExtraRoutes, newExtraRoutes);
|
||||
if (deviceNameChanged || deviceDescriptionChanged || externalEndpointsChanged || extraRoutesChanged) {
|
||||
if (kcontroller != null) {
|
||||
kcontroller.publishNodeInfo(newDeviceName, newDeviceDescription, newExternalEndpoints,
|
||||
newExtraRoutes);
|
||||
} else {
|
||||
kck.setDeviceName(newDeviceName);
|
||||
kck.setDeviceDescription(newDeviceDescription);
|
||||
kck.setExternalEndpoints(newExternalEndpoints);
|
||||
kck.setExtraRoutes(newExtraRoutes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 调用保存回调
|
||||
if (saveComsumer != null) {
|
||||
saveComsumer.accept(config);
|
||||
JOptionPane.showMessageDialog(this, UIEnv.getRsb().getString("saveconfigsuccess"));
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 刷新任务创建 ====================
|
||||
/**
|
||||
@@ -1636,7 +1565,11 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
/**
|
||||
* 加载配置文件
|
||||
*/
|
||||
private void loadConfigCandidate(KLALBControllerConfigItem kck) {
|
||||
public void loadConfig(KLALBConfig config) {
|
||||
this.config = config;
|
||||
for (KLALBConfigItem item : config) {
|
||||
if (item instanceof KLALBControllerConfigItem) {
|
||||
KLALBControllerConfigItem kck = (KLALBControllerConfigItem) item;
|
||||
|
||||
// 加载语言设置
|
||||
String lg = kck.getLanguage();
|
||||
@@ -1762,92 +1695,9 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
|
||||
long delr=kck.getLinkNagleDelayTime();
|
||||
linkNagleDelayTime.getSlider().setValue((int)(delr/100000L));
|
||||
}
|
||||
|
||||
public void loadConfig(KLALBConfig config) {
|
||||
if (configSystem != null) return;
|
||||
this.config = config;
|
||||
if (config == null) return;
|
||||
for (KLALBConfigItem item : config) {
|
||||
if (item instanceof KLALBControllerConfigItem) {
|
||||
loadConfigCandidate((KLALBControllerConfigItem) item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void bindConfigSystem(KLALBProxySystem system) {
|
||||
if (configSystemClosed || system == null) return;
|
||||
final long generation = ++bindingGeneration;
|
||||
Runnable bind = () -> {
|
||||
if (configSystemClosed || generation != bindingGeneration) return;
|
||||
if (configSystem != null && boundConfigChangeListener != null)
|
||||
configSystem.removeConfigChangeListener(boundConfigChangeListener);
|
||||
configSystem = system;
|
||||
final Consumer<KLALBProxySystem.ConfigChangeEvent> listener = event ->
|
||||
SwingUtilities.invokeLater(() -> handleConfigChange(system, generation, event));
|
||||
boundConfigChangeListener = listener;
|
||||
pendingExternalSnapshot = null;
|
||||
pendingExternalRevision = 0L;
|
||||
loadedSnapshot = null;
|
||||
loadedRevision = 0L;
|
||||
loadedFormState = null;
|
||||
system.addConfigChangeListener(listener);
|
||||
loadedSnapshot = system.getControllerConfigSnapshot();
|
||||
KLALBControllerConfigItem candidate = system.parseControllerConfigCandidate(loadedSnapshot);
|
||||
if (candidate != null) {
|
||||
loadConfigCandidate(candidate);
|
||||
loadedRevision = loadedSnapshot.getRevision();
|
||||
loadedFormState = new SettingsFormState(this);
|
||||
}
|
||||
};
|
||||
if (SwingUtilities.isEventDispatchThread()) bind.run();
|
||||
else SwingUtilities.invokeLater(bind);
|
||||
}
|
||||
|
||||
private void handleConfigChange(KLALBProxySystem sourceSystem, long generation,
|
||||
KLALBProxySystem.ConfigChangeEvent event) {
|
||||
if (configSystemClosed || generation != bindingGeneration || sourceSystem != configSystem
|
||||
|| event.getSource() == KLALBProxySystem.ConfigChangeSource.SWING) return;
|
||||
KLALBProxySystem.ControllerConfigSnapshot snapshot = event.getSnapshot();
|
||||
if (snapshot == null || snapshot.getRevision() <= Math.max(loadedRevision, pendingExternalRevision)) return;
|
||||
if (loadedFormState != null && loadedFormState.equals(new SettingsFormState(this))) {
|
||||
applyExternalSnapshot(snapshot);
|
||||
return;
|
||||
}
|
||||
if (pendingExternalSnapshot == null || snapshot.getRevision() > pendingExternalRevision) {
|
||||
boolean hadPending = pendingExternalSnapshot != null;
|
||||
pendingExternalSnapshot = snapshot;
|
||||
pendingExternalRevision = snapshot.getRevision();
|
||||
if (!hadPending) resolveConfigConflict(snapshot, "configexternalupdate");
|
||||
}
|
||||
}
|
||||
|
||||
private void resolveConfigConflict(KLALBProxySystem.ControllerConfigSnapshot snapshot, String messageKey) {
|
||||
if (snapshot != null && snapshot.getRevision() > Math.max(loadedRevision, pendingExternalRevision)) {
|
||||
pendingExternalSnapshot = snapshot;
|
||||
pendingExternalRevision = snapshot.getRevision();
|
||||
}
|
||||
Object[] options = { UIEnv.getRsb().getString("configreload"), UIEnv.getRsb().getString("configcontinue") };
|
||||
int choice = JOptionPane.showOptionDialog(this, UIEnv.getRsb().getString(messageKey),
|
||||
UIEnv.getRsb().getString("configconflicttitle"), JOptionPane.DEFAULT_OPTION,
|
||||
JOptionPane.WARNING_MESSAGE, null, options, options[0]);
|
||||
if (choice == 0 && pendingExternalSnapshot != null
|
||||
&& pendingExternalSnapshot.getRevision() >= loadedRevision) {
|
||||
applyExternalSnapshot(pendingExternalSnapshot);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyExternalSnapshot(KLALBProxySystem.ControllerConfigSnapshot snapshot) {
|
||||
KLALBControllerConfigItem candidate = configSystem.parseControllerConfigCandidate(snapshot);
|
||||
if (candidate == null) return;
|
||||
loadConfigCandidate(candidate);
|
||||
loadedSnapshot = snapshot;
|
||||
loadedRevision = snapshot.getRevision();
|
||||
loadedFormState = new SettingsFormState(this);
|
||||
pendingExternalSnapshot = null;
|
||||
pendingExternalRevision = 0L;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 辅助方法 ====================
|
||||
/**
|
||||
@@ -1922,9 +1772,8 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
/**
|
||||
* 设置保存配置的回调函数
|
||||
*/
|
||||
public void setSaveComsumer(Consumer<KLALBConfig> saveComsumer) {
|
||||
if (configSystem != null) return;
|
||||
this.saveComsumer = saveComsumer;
|
||||
public void setSaveComsumer(Consumer<KLALBConfig> saveComsumer) {
|
||||
this.saveComsumer = saveComsumer;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1939,11 +1788,6 @@ public class KLALBStateGUI3 extends XFrame {
|
||||
* 关闭窗口并清理资源
|
||||
*/
|
||||
public void close() {
|
||||
bindingGeneration++;
|
||||
configSystemClosed = true;
|
||||
if (configSystem != null && boundConfigChangeListener != null)
|
||||
configSystem.removeConfigChangeListener(boundConfigChangeListener);
|
||||
boundConfigChangeListener = null;
|
||||
for (int i = 0; i < tabbedPane.getTabCount(); i++) {
|
||||
Component component = tabbedPane.getComponentAt(i);
|
||||
if (component instanceof NodeInformationPanel) {
|
||||
|
||||
@@ -863,11 +863,17 @@ public class KLALBWebServer {
|
||||
String body = readRequestBody(exchange);
|
||||
try {
|
||||
JsonObject json = new JsonParser().parse(body).getAsJsonObject();
|
||||
KLALBProxySystem.ControllerConfigSnapshot baseSnapshot = proxySystem.getControllerConfigSnapshot();
|
||||
KLALBControllerConfigItem current = proxySystem.parseControllerConfigCandidate(baseSnapshot);
|
||||
KLALBControllerConfigItem current = proxySystem.getControllerConfig();
|
||||
if (current != null) {
|
||||
String newDeviceName = current.getDeviceName();
|
||||
String newDeviceDescription = current.getDeviceDescription();
|
||||
String oldDeviceName = current.getDeviceName();
|
||||
String oldDeviceDescription = current.getDeviceDescription();
|
||||
List<MultiProtocolSocketAddress> oldExternalEndpoints = current.getExternalEndpoints() == null
|
||||
? null
|
||||
: new ArrayList<MultiProtocolSocketAddress>(current.getExternalEndpoints());
|
||||
List<String> oldExtraRoutes = current.getExtraRoutes() == null
|
||||
? null : new ArrayList<String>(current.getExtraRoutes());
|
||||
String newDeviceName = oldDeviceName;
|
||||
String newDeviceDescription = oldDeviceDescription;
|
||||
List<MultiProtocolSocketAddress> newExternalEndpoints = current.getExternalEndpoints();
|
||||
List<String> newExtraRoutes = current.getExtraRoutes();
|
||||
if (json.has("deviceName") && !json.get("deviceName").isJsonNull()) {
|
||||
@@ -1066,32 +1072,37 @@ public class KLALBWebServer {
|
||||
current.setDenyExternalEndpointBroadcast(json.get("denyLineTableBroadcast").getAsBoolean());
|
||||
}
|
||||
|
||||
current.setDeviceName(newDeviceName);
|
||||
current.setDeviceDescription(newDeviceDescription);
|
||||
current.setExternalEndpoints(newExternalEndpoints);
|
||||
current.setExtraRoutes(newExtraRoutes);
|
||||
KLALBProxySystem.CommitResult commitResult = proxySystem.commitControllerConfig(
|
||||
baseSnapshot.getRevision(), current,
|
||||
KLALBProxySystem.ConfigChangeSource.WEB);
|
||||
if (!commitResult.isSuccess()) {
|
||||
JsonObject conflict = new JsonObject();
|
||||
conflict.addProperty("success", false);
|
||||
conflict.addProperty("revision", commitResult.getRevision());
|
||||
conflict.addProperty("error", "Configuration revision conflict");
|
||||
sendJsonResponse(exchange, 409, conflict);
|
||||
return;
|
||||
boolean deviceNameChanged = !Objects.equals(oldDeviceName, newDeviceName);
|
||||
boolean deviceDescriptionChanged = !Objects.equals(oldDeviceDescription, newDeviceDescription);
|
||||
boolean externalEndpointsChanged = !Objects.equals(oldExternalEndpoints, newExternalEndpoints);
|
||||
boolean extraRoutesChanged = !Objects.equals(oldExtraRoutes, newExtraRoutes);
|
||||
if (deviceNameChanged || deviceDescriptionChanged || externalEndpointsChanged || extraRoutesChanged) {
|
||||
KLALBController kc = proxySystem.getKlalbController();
|
||||
if (kc != null) {
|
||||
kc.publishNodeInfo(newDeviceName, newDeviceDescription, newExternalEndpoints,
|
||||
newExtraRoutes);
|
||||
} else {
|
||||
current.setDeviceName(newDeviceName);
|
||||
current.setDeviceDescription(newDeviceDescription);
|
||||
current.setExternalEndpoints(newExternalEndpoints);
|
||||
current.setExtraRoutes(newExtraRoutes);
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger GUI save consumer or save directly
|
||||
if (proxySystem.getKLALBGUI() != null && proxySystem.getKLALBGUI().getSaveComsumer() != null) {
|
||||
proxySystem.getKLALBGUI().getSaveComsumer().accept(proxySystem.getConfig());
|
||||
} else {
|
||||
proxySystem.saveConfigToFile();
|
||||
}
|
||||
|
||||
JsonObject resp = new JsonObject();
|
||||
resp.addProperty("success", true);
|
||||
resp.addProperty("revision", commitResult.getRevision());
|
||||
resp.addProperty("message", "Configuration updated successfully");
|
||||
sendJsonResponse(exchange, 200, resp);
|
||||
} else {
|
||||
sendError(exchange, 500, "Current configuration is null");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
sendError(exchange, 500, "Failed to persist configuration: " + e.getMessage());
|
||||
} catch (Exception e) {
|
||||
sendError(exchange, 400, "Failed to update configuration: " + e.getMessage());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user