From 9512bdb5d64c439f1285394df27f9019087a8bdc Mon Sep 17 00:00:00 2001 From: yushijinhun Date: Sun, 19 Aug 2018 15:43:34 +0800 Subject: [PATCH] Remove UpdateChecker.updateChannel & Fix concurrent problems in update checking --- .../java/org/jackhuang/hmcl/Launcher.java | 24 +----- .../org/jackhuang/hmcl/setting/Config.java | 9 +- .../org/jackhuang/hmcl/ui/SettingsPage.java | 15 ++-- .../UpdateChannel.java} | 16 ++-- .../jackhuang/hmcl/upgrade/UpdateChecker.java | 86 +++++++++++-------- 5 files changed, 73 insertions(+), 77 deletions(-) rename HMCL/src/main/java/org/jackhuang/hmcl/{setting/EnumUpdateChannel.java => upgrade/UpdateChannel.java} (69%) diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java index d10a15b0b..ea08579b0 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/Launcher.java @@ -17,23 +17,19 @@ */ package org.jackhuang.hmcl; -import static org.jackhuang.hmcl.util.Lang.thread; import static org.jackhuang.hmcl.util.Logging.LOG; import com.jfoenix.concurrency.JFXUtilities; import javafx.application.Application; import javafx.application.Platform; -import javafx.beans.binding.Bindings; import javafx.stage.Stage; -import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.task.Schedulers; import org.jackhuang.hmcl.ui.Controllers; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.util.*; import java.io.File; -import java.io.IOException; import java.lang.management.ManagementFactory; import java.net.URISyntaxException; import java.net.URL; @@ -42,7 +38,6 @@ import java.nio.file.Paths; import java.util.LinkedList; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.logging.Level; public final class Launcher extends Application { @@ -58,24 +53,7 @@ public final class Launcher extends Application { primaryStage.setResizable(false); primaryStage.setScene(Controllers.getScene()); - UpdateChecker.updateChannelProperty().addListener(observable -> { - thread(() -> { - try { - UpdateChecker.checkUpdate(); - } catch (IOException e) { - LOG.log(Level.WARNING, "Failed to check for update", e); - } - }); - }); - - UpdateChecker.updateChannelProperty().bind(Bindings.createStringBinding(() -> { - switch (ConfigHolder.config().getUpdateChannel()) { - case DEVELOPMENT: - return UpdateChecker.CHANNEL_DEV; - default: - return UpdateChecker.CHANNEL_STABLE; - } - }, ConfigHolder.config().updateChannelProperty())); + UpdateChecker.init(); primaryStage.show(); } catch (Throwable e) { diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java index ead049122..355cd3b8f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/setting/Config.java @@ -30,6 +30,7 @@ import org.hildan.fxgson.creators.ObservableSetCreator; import org.hildan.fxgson.factories.JavaFxPropertyTypeAdapterFactory; import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.auth.authlibinjector.AuthlibInjectorServer; +import org.jackhuang.hmcl.upgrade.UpdateChannel; import org.jackhuang.hmcl.util.EnumOrdinalDeserializer; import org.jackhuang.hmcl.util.FileTypeAdapter; import org.jackhuang.hmcl.util.ObservableHelper; @@ -150,7 +151,7 @@ public final class Config implements Cloneable, Observable { private ObservableList authlibInjectorServers = FXCollections.observableArrayList(); @SerializedName("updateChannel") - private ObjectProperty updateChannel = new SimpleObjectProperty<>(EnumUpdateChannel.STABLE); + private ObjectProperty updateChannel = new SimpleObjectProperty<>(UpdateChannel.STABLE); @SerializedName("_version") private IntegerProperty configVersion = new SimpleIntegerProperty(0); @@ -438,15 +439,15 @@ public final class Config implements Cloneable, Observable { return authlibInjectorServers; } - public EnumUpdateChannel getUpdateChannel() { + public UpdateChannel getUpdateChannel() { return updateChannel.get(); } - public ObjectProperty updateChannelProperty() { + public ObjectProperty updateChannelProperty() { return updateChannel; } - public void setUpdateChannel(EnumUpdateChannel updateChannel) { + public void setUpdateChannel(UpdateChannel updateChannel) { this.updateChannel.set(updateChannel); } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java index 279a1f900..8f4bd9a80 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/ui/SettingsPage.java @@ -46,6 +46,7 @@ import org.jackhuang.hmcl.ui.construct.FontComboBox; import org.jackhuang.hmcl.ui.construct.MultiFileItem; import org.jackhuang.hmcl.ui.construct.Validator; import org.jackhuang.hmcl.ui.wizard.DecoratorPage; +import org.jackhuang.hmcl.upgrade.UpdateChannel; import org.jackhuang.hmcl.upgrade.RemoteVersion; import org.jackhuang.hmcl.upgrade.UpdateChecker; import org.jackhuang.hmcl.upgrade.UpdateHandler; @@ -242,24 +243,24 @@ public final class SettingsPage extends StackPane implements DecoratorPage { lblUpdateNote.setWrappingWidth(470); - ObjectProperty updateChannel = new SimpleObjectProperty() { + ObjectProperty updateChannel = new SimpleObjectProperty() { @Override protected void invalidated() { - EnumUpdateChannel updateChannel = Objects.requireNonNull(get()); - chkUpdateDev.setSelected(updateChannel == EnumUpdateChannel.DEVELOPMENT); - chkUpdateStable.setSelected(updateChannel == EnumUpdateChannel.STABLE); + UpdateChannel updateChannel = Objects.requireNonNull(get()); + chkUpdateDev.setSelected(updateChannel == UpdateChannel.DEVELOPMENT); + chkUpdateStable.setSelected(updateChannel == UpdateChannel.STABLE); } }; ToggleGroup updateChannelGroup = new ToggleGroup(); chkUpdateDev.setToggleGroup(updateChannelGroup); - chkUpdateDev.setUserData(EnumUpdateChannel.DEVELOPMENT); + chkUpdateDev.setUserData(UpdateChannel.DEVELOPMENT); chkUpdateStable.setToggleGroup(updateChannelGroup); - chkUpdateStable.setUserData(EnumUpdateChannel.STABLE); + chkUpdateStable.setUserData(UpdateChannel.STABLE); updateChannelGroup.getToggles().forEach( toggle -> toggle.selectedProperty().addListener((observable, oldValue, newValue) -> { if (newValue) { - updateChannel.set((EnumUpdateChannel) toggle.getUserData()); + updateChannel.set((UpdateChannel) toggle.getUserData()); } })); updateChannel.bindBidirectional(ConfigHolder.config().updateChannelProperty()); diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateChannel.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java similarity index 69% rename from HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateChannel.java rename to HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java index fd117839c..e31926266 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/setting/EnumUpdateChannel.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChannel.java @@ -1,6 +1,6 @@ /* * Hello Minecraft! Launcher. - * Copyright (C) 2017 huangyuhui + * Copyright (C) 2018 huangyuhui * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -15,9 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see {http://www.gnu.org/licenses/}. */ -package org.jackhuang.hmcl.setting; +package org.jackhuang.hmcl.upgrade; -public enum EnumUpdateChannel { - STABLE, - DEVELOPMENT +public enum UpdateChannel { + STABLE("stable"), + DEVELOPMENT("dev"); + + public final String channelName; + + UpdateChannel(String channelName) { + this.channelName = channelName; + } } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java index d7fe0ee43..c5ae2ed8f 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/UpdateChecker.java @@ -17,16 +17,20 @@ */ package org.jackhuang.hmcl.upgrade; +import static org.jackhuang.hmcl.setting.ConfigHolder.config; +import static org.jackhuang.hmcl.ui.FXUtils.onInvalidating; import static org.jackhuang.hmcl.util.Lang.mapOf; +import static org.jackhuang.hmcl.util.Lang.thread; +import static org.jackhuang.hmcl.util.Logging.LOG; import static org.jackhuang.hmcl.util.Pair.pair; import static org.jackhuang.hmcl.util.VersionNumber.asVersion; import java.io.IOException; +import java.util.logging.Level; -import com.jfoenix.concurrency.JFXUtilities; import javafx.beans.property.*; import org.jackhuang.hmcl.Metadata; -import org.jackhuang.hmcl.util.ImmediateStringProperty; +import org.jackhuang.hmcl.setting.ConfigHolder; import org.jackhuang.hmcl.util.NetworkUtils; import javafx.application.Platform; @@ -37,11 +41,6 @@ import javafx.beans.value.ObservableBooleanValue; public final class UpdateChecker { private UpdateChecker() {} - public static final String CHANNEL_STABLE = "stable"; - public static final String CHANNEL_DEV = "dev"; - - private static StringProperty updateChannel = new ImmediateStringProperty(null, "updateChannel", CHANNEL_STABLE); - private static ObjectProperty latestVersion = new SimpleObjectProperty<>(); private static BooleanBinding outdated = Bindings.createBooleanBinding( () -> { @@ -55,16 +54,9 @@ public final class UpdateChecker { latestVersion); private static ReadOnlyBooleanWrapper checkingUpdate = new ReadOnlyBooleanWrapper(false); - public static String getUpdateChannel() { - return updateChannel.get(); - } - - public static void setUpdateChannel(String updateChannel) { - UpdateChecker.updateChannel.set(updateChannel); - } - - public static StringProperty updateChannelProperty() { - return updateChannel; + public static void init() { + ConfigHolder.config().updateChannelProperty().addListener(onInvalidating(UpdateChecker::requestCheckUpdate)); + requestCheckUpdate(); } public static RemoteVersion getLatestVersion() { @@ -91,35 +83,53 @@ public final class UpdateChecker { return checkingUpdate.getReadOnlyProperty(); } - public static void checkUpdate() throws IOException { + private static RemoteVersion checkUpdate(UpdateChannel channel) throws IOException { if (!IntegrityChecker.isSelfVerified()) { - return; + throw new IOException("Self verification failed"); } - JFXUtilities.runInFXAndWait(() -> { - checkingUpdate.set(true); - }); + String url = NetworkUtils.withQuery(Metadata.UPDATE_URL, mapOf( + pair("version", Metadata.VERSION), + pair("channel", channel.channelName))); - try { - - String channel = getUpdateChannel(); - String url = NetworkUtils.withQuery(Metadata.UPDATE_URL, mapOf( - pair("version", Metadata.VERSION), - pair("channel", channel))); - - RemoteVersion fetched = RemoteVersion.fetch(url); - Platform.runLater(() -> { - if (channel.equals(getUpdateChannel())) { - latestVersion.set(fetched); - } - }); - } finally { - Platform.runLater(() -> checkingUpdate.set(false)); - } + return RemoteVersion.fetch(url); } private static boolean isDevelopmentVersion(String version) { return version.contains("@") || // eg. @develop@ version.contains("SNAPSHOT"); // eg. 3.1.SNAPSHOT } + + public static void requestCheckUpdate() { + Platform.runLater(() -> { + if (isCheckingUpdate()) + return; + checkingUpdate.set(true); + UpdateChannel channel = config().getUpdateChannel(); + + thread(() -> { + RemoteVersion result = null; + try { + result = checkUpdate(channel); + LOG.info("Latest version (" + channel + ") is " + result); + } catch (IOException e) { + LOG.log(Level.WARNING, "Failed to check for update", e); + } + + RemoteVersion finalResult = result; + Platform.runLater(() -> { + checkingUpdate.set(false); + if (finalResult != null) { + if (channel.equals(config().getUpdateChannel())) { + latestVersion.set(finalResult); + } else { + // the channel has been changed during the period + // check update again + requestCheckUpdate(); + } + } + }); + }); + }); + } }