diff --git a/AGENTS.md b/AGENTS.md index 56cdb69..859c5a8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -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. diff --git a/src/klalb_en_US.properties b/src/klalb_en_US.properties index cf98436..02a5e59 100644 --- a/src/klalb_en_US.properties +++ b/src/klalb_en_US.properties @@ -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 diff --git a/src/klalb_zh_CN.properties b/src/klalb_zh_CN.properties index ab6b6d5..fc1b624 100644 --- a/src/klalb_zh_CN.properties +++ b/src/klalb_zh_CN.properties @@ -34,13 +34,7 @@ addline=添加链路 reconnectall=全部重连 remotelines=远程链路 settings=设置 -saveconfigsuccess=保存配置成功 -configexternalupdate=配置已被其他来源更新。 -configreload=重新加载 -configcontinue=继续编辑 -configsaveconflict=保存失败:配置已被更新,请重新加载后再试。 -configsavefailed=保存配置失败。 -configconflicttitle=配置冲突 +saveconfigsuccess=保存配置成功 warning=警告 invaildipv6addr=IPv6地址:非法输入 invailddnsserver=DNS服务器:非法输入 diff --git a/src/org/kne/cloud/network/klalb/KLALBController.java b/src/org/kne/cloud/network/klalb/KLALBController.java index cd1460a..1841de3 100644 --- a/src/org/kne/cloud/network/klalb/KLALBController.java +++ b/src/org/kne/cloud/network/klalb/KLALBController.java @@ -583,63 +583,7 @@ public class KLALBController { private KLALBRoutingProtocolAPIClient apiClient; - private volatile KLALBControllerConfigItem configItem; - - private static List copyConfigList(List values) { - return values == null ? null : new ArrayList(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 select) { for (MultiProtocolSocketAddress target : select) { diff --git a/src/org/kne/cloud/network/klalb/KLALBProxySystem.java b/src/org/kne/cloud/network/klalb/KLALBProxySystem.java index e17d8d6..85dda3c 100644 --- a/src/org/kne/cloud/network/klalb/KLALBProxySystem.java +++ b/src/org/kne/cloud/network/klalb/KLALBProxySystem.java @@ -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 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> configChangeListeners = new CopyOnWriteArraySet<>(); - private JsonArray rawConfigJson; +public class KLALBProxySystem { + private Set 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 listener) { - configChangeListeners.add(listener); - } - - public void removeConfigChangeListener(Consumer 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 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; + } +} diff --git a/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java b/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java index c72a353..b110b94 100644 --- a/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java +++ b/src/org/kne/cloud/network/klalb/ui/KLALBStateGUI3.java @@ -104,82 +104,8 @@ public class KLALBStateGUI3 extends XFrame { private JCheckBox nogui; // ==================== 配置和回调 ==================== - private KLALBConfig config; // 配置文件 - private Consumer 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 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 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 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 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 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 saveComsumer) { - if (configSystem != null) return; - this.saveComsumer = saveComsumer; + public void setSaveComsumer(Consumer 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) { diff --git a/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java b/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java index 63a411f..c9bc0b3 100644 --- a/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java +++ b/src/org/kne/cloud/network/klalb/web/KLALBWebServer.java @@ -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 oldExternalEndpoints = current.getExternalEndpoints() == null + ? null + : new ArrayList(current.getExternalEndpoints()); + List oldExtraRoutes = current.getExtraRoutes() == null + ? null : new ArrayList(current.getExtraRoutes()); + String newDeviceName = oldDeviceName; + String newDeviceDescription = oldDeviceDescription; List newExternalEndpoints = current.getExternalEndpoints(); List 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()); }