Revert "fix(config): sync Swing settings after Web updates"

This reverts commit b6acc6d509.
This commit is contained in:
2026-08-31 13:24:21 +08:00
parent 3dae7dd876
commit dde54d0929
7 changed files with 131 additions and 577 deletions
+2 -2
View File
@@ -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.
-6
View File
@@ -35,12 +35,6 @@ 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
warning=Warning
invaildipv6addr=IPv6 address:Invaild Input
invailddnsserver=DNS server:Invaild Input
-6
View File
@@ -35,12 +35,6 @@ reconnectall=全部重连
remotelines=远程链路
settings=设置
saveconfigsuccess=保存配置成功
configexternalupdate=配置已被其他来源更新。
configreload=重新加载
configcontinue=继续编辑
configsaveconflict=保存失败:配置已被更新,请重新加载后再试。
configsavefailed=保存配置失败。
configconflicttitle=配置冲突
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) {
@@ -8,17 +8,7 @@ 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 org.kne.cloud.network.*;
import org.kne.cloud.network.klalb.ui.KLALBStateGUI3;
@@ -30,7 +20,6 @@ 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.JsonDeserializer;
import com.google.gson.JsonElement;
@@ -41,81 +30,12 @@ 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;
{
GsonBuilder gb=new GsonBuilder().setPrettyPrinting();
MultiProtocolSocketAddress.registerToGsonBuilder(gb);
@@ -222,25 +142,11 @@ public class KLALBProxySystem {
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);
}
loadConfig(config);
}
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) {
if(item instanceof KLALBControllerConfigItem) {
KLALBControllerConfigItem kcci=(KLALBControllerConfigItem) item;
@@ -323,171 +229,28 @@ public class KLALBProxySystem {
}
}
}
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;
}
String json = gson.toJson(config);
try (FileWriter fw = new FileWriter(jsonFile)) {
fw.write(json);
} 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;
}
private KLALBStateGUI3 kgui;
public KLALBStateGUI3 getKLALBGUI() {
if(kgui==null) {
kgui=new KLALBStateGUI3(klalbController);
kgui.bindConfigSystem(this);
kgui.loadConfig(config);
kgui.setSaveComsumer((cfg)->{
saveConfigToFile();
});
}
return kgui;
}
@@ -495,6 +258,10 @@ public class KLALBProxySystem {
return config;
}
public KLALBControllerConfigItem getControllerConfig() {
return parseControllerConfigCandidate(getControllerConfigSnapshot());
for(KLALBConfigItem item:config) {
if(item instanceof KLALBControllerConfigItem)
return (KLALBControllerConfigItem) item;
}
return null;
}
}
@@ -106,80 +106,6 @@ public class KLALBStateGUI3 extends XFrame {
// ==================== 配置和回调 ====================
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 Dimension dashSize = new Dimension((int) (145 * 0.7), (int) (165 * 0.7)); // 仪表盘尺寸
@@ -1130,9 +1056,14 @@ public class KLALBStateGUI3 extends XFrame {
* 保存配置到文件
*/
private void saveConfig() {
if (configSystem == null || loadedSnapshot == null) return;
KLALBControllerConfigItem kck = configSystem.parseControllerConfigCandidate(loadedSnapshot);
if (kck == null) return;
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,30 +1314,28 @@ public class KLALBStateGUI3 extends XFrame {
kck.setLinkNagleDelayTime(linkNagleDelayTime.getSlider().getValue()*100000L);
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);
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;
}
}
}
// 调用保存回调
if (saveComsumer != null) {
saveComsumer.accept(config);
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);
}
}
@@ -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();
@@ -1763,91 +1696,8 @@ 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;
}
// ==================== 辅助方法 ====================
/**
@@ -1923,7 +1773,6 @@ public class KLALBStateGUI3 extends XFrame {
* 设置保存配置的回调函数
*/
public void setSaveComsumer(Consumer<KLALBConfig> saveComsumer) {
if (configSystem != null) return;
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());
}
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);
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;
}
}
// 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());
}