diff --git a/java_console/ui/src/main/java/com/rusefi/StartupFrame.java b/java_console/ui/src/main/java/com/rusefi/StartupFrame.java index 65b94911b5..31bccccdca 100644 --- a/java_console/ui/src/main/java/com/rusefi/StartupFrame.java +++ b/java_console/ui/src/main/java/com/rusefi/StartupFrame.java @@ -1,7 +1,6 @@ package com.rusefi; import com.devexperts.logging.Logging; -import com.rusefi.core.io.BundleUtil; import com.rusefi.core.net.ConnectionAndMeta; import com.rusefi.core.preferences.storage.PersistentConfiguration; import com.rusefi.core.ui.FrameHelper; @@ -10,6 +9,7 @@ import com.rusefi.io.serial.BaudRateHolder; import com.rusefi.maintenance.*; import com.rusefi.ui.BasicLogoHelper; import com.rusefi.ui.LogoHelper; +import com.rusefi.ui.duplicates.ConsoleBundleUtil; import com.rusefi.ui.util.HorizontalLine; import com.rusefi.ui.util.URLLabel; import com.rusefi.ui.util.UiUtils; @@ -209,7 +209,7 @@ public class StartupFrame { JPanel rightPanel = new JPanel(new VerticalFlowLayout()); - if (BundleUtil.readBundleFullNameNotNull().getTarget().contains("proteus_f7")) { + if (ConsoleBundleUtil.readBundleFullNameNotNull().getTarget().contains("proteus_f7")) { String text = "WARNING: Proteus F7"; URLLabel urlLabel = new URLLabel(text, "https://github.com/rusefi/rusefi/wiki/F7-requires-full-erase"); new Timer(500, new ActionListener() { @@ -252,7 +252,7 @@ public class StartupFrame { final String textAlign, final Supplier minWidthSupplier ) { - final String nextBranchName = BundleUtil.readBundleFullNameNotNull().getNextBranchName(); + final String nextBranchName = ConsoleBundleUtil.readBundleFullNameNotNull().getNextBranchName(); if (nextBranchName != null && !nextBranchName.isBlank()) { final JLabel newReleaseAmmomceMessage = new JLabel( String.format( @@ -287,7 +287,7 @@ public class StartupFrame { jLabel.setForeground(Color.red); } else { final Date binaryModificationDate = new Date(binaryModificationTimestamp); - final String branchNameToDisplay = BundleUtil.readBundleFullNameNotNull().getBranchName(); + final String branchNameToDisplay = ConsoleBundleUtil.readBundleFullNameNotNull().getBranchName(); jLabel = new JLabel(String.format( "
%s files
%s
", branchNameToDisplay, diff --git a/java_console/ui/src/main/java/com/rusefi/ui/duplicates/ConsoleBundleUtil.java b/java_console/ui/src/main/java/com/rusefi/ui/duplicates/ConsoleBundleUtil.java new file mode 100644 index 0000000000..60052ec21c --- /dev/null +++ b/java_console/ui/src/main/java/com/rusefi/ui/duplicates/ConsoleBundleUtil.java @@ -0,0 +1,117 @@ +package com.rusefi.ui.duplicates; + +import com.devexperts.logging.Logging; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +import static com.devexperts.logging.Logging.getLogging; + +// `com.rusefi.ui.duplicates.ConsoleBundleUtil` class duplicates `com.rusefi.core.io.BundleUtil` class to avoid crash +// on console update (caused by missed `BundleInfo.getNextBranchName` method) - see `readme.md` for details. +// TODO: in June 2025 we need to get rid of this class and to replace its usage with `com.rusefi.core.io.BundleUtil`. +public class ConsoleBundleUtil { + private static final Logging log = getLogging(ConsoleBundleUtil.class); + + private static final String DEVELOPMENT = "development"; + private static final String BRANCH_REF_FILE = "release.txt"; + + /** + * @return null in case of error + */ + public static List<@NotNull String> readBundleFullName() { + File f = new File(BRANCH_REF_FILE); + if (!f.exists()) { + log.error(BRANCH_REF_FILE + " not found"); + return null; + } + try { + return Files.readAllLines(f.toPath()); + } catch (InvalidPathException | IOException e) { + log.error("Error reading bundle name", e); + return null; + } + } + + @NotNull + public static BundleInfo readBundleFullNameNotNull() { + List<@NotNull String> info = readBundleFullName(); + if (info == null) + return BundleInfo.UNKNOWN; + return parse(info); + } + + public static String getBundleTarget() { + return readBundleFullNameNotNull().getTarget(); + } + + public static BundleInfo parse(List<@NotNull String> info) { + Map keyValues = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + for (String line : info) { + String[] pair = line.split("=", 2); + keyValues.put(pair[0], pair[1]); + } + String target = keyValues.get("platform"); + String branchName = keyValues.get("release"); + String nextBranchName = keyValues.get("nextRelease"); + if (target == null || branchName == null) { + log.info(BRANCH_REF_FILE + " says " + keyValues); + return BundleInfo.UNKNOWN; + } + return new ConsoleBundleUtil.BundleInfo(branchName, nextBranchName, target); + } + + public static class BundleInfo { + static final BundleInfo UNKNOWN = new BundleInfo("unknown", null, "unknown"); + + private final String branchName; + private final String nextBranchName; + private final String target; + + public BundleInfo(String branchName, String nextBranchName, String target) { + this.branchName = Objects.requireNonNull(branchName, "branchName"); + this.nextBranchName = nextBranchName; + this.target = Objects.requireNonNull(target, "target"); + } + + public static boolean isUndefined(BundleInfo bundleInfo) { + return bundleInfo == UNKNOWN; + } + + public String getBranchName() { + return branchName; + } + + public String getNextBranchName() { + return nextBranchName; + } + + public boolean isMaster() { + return DEVELOPMENT.equals(branchName); + } + + public String getTarget() { + return target; + } + + @Override + public String toString() { + return "BundleInfo{" + + "branchName='" + branchName + '\'' + + "nextBranchName='" + nextBranchName + '\'' + + ", target='" + target + '\'' + + '}'; + } + + public String getUiLabel() { + return target + "." + branchName; + } + } +} diff --git a/java_console/ui/src/main/java/com/rusefi/ui/duplicates/readme.md b/java_console/ui/src/main/java/com/rusefi/ui/duplicates/readme.md new file mode 100644 index 0000000000..c98d7ab150 --- /dev/null +++ b/java_console/ui/src/main/java/com/rusefi/ui/duplicates/readme.md @@ -0,0 +1,11 @@ +We have a problem with classes that are used by both `rusefi_autoupdate.jar` and `rusefi_console.jar`: if we add a new method to such class and use it in `rusefi_console.jar` than we get reflection exception after update. + +The reason is that `rusefi_autoupdate.jar` contains an obsolete version of the class without new method, but a new version `rusefi_console.jar` tries to call the missed method and crashes. + +This is not a total catastrophe, because after crash `rusefi_console.jar` can be restarted successfully, but we want to reduce number of users experiencing this problem. + +The suggested solution is to temporarily duplicate problematic classes in `com.rusefi.ui.duplicates` package and at first to use this duplicated classes in `rusefi_console.jar` instead of original classes that is used in `rusefi_autoupdate.jar`. + +After the majority of users have already updated their console, we could get rid of duplicated classes and replace them with the original class used by `rusefi_autoupdate.jar`. + +We will still have a few users that haven't updated their console and they will experience a crash on console update, but I do not think that it is serious problem.