diff --git a/pom.xml b/pom.xml index c994912..3718fba 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ de.lars.remotelightweb remotelightweb RemoteLightWeb - 1.0-SNAPSHOT2 + 1.0.1 jar @@ -134,6 +134,11 @@ remotelight 0.2.0.7.2 + + de.lars + updater + 1.1 + diff --git a/src/main/java/de/lars/remotelightweb/RemoteLightWeb.java b/src/main/java/de/lars/remotelightweb/RemoteLightWeb.java index 8092ecd..1e76215 100644 --- a/src/main/java/de/lars/remotelightweb/RemoteLightWeb.java +++ b/src/main/java/de/lars/remotelightweb/RemoteLightWeb.java @@ -1,5 +1,6 @@ package de.lars.remotelightweb; +import java.io.File; import java.io.InputStream; import java.nio.file.Paths; import java.util.Properties; @@ -8,10 +9,16 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; +import org.springframework.boot.system.ApplicationHome; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; import org.springframework.context.ConfigurableApplicationContext; import de.lars.remotelightclient.api.RemoteLightAPI; +import de.lars.remotelightclient.settings.SettingsManager; +import de.lars.remotelightclient.settings.SettingsManager.SettingCategory; +import de.lars.remotelightclient.settings.types.SettingBoolean; +import de.lars.remotelightclient.settings.types.SettingString; import de.lars.remotelightweb.backend.ConfigFile; +import de.lars.remotelightweb.backend.utils.UpdateUtil; /** * The entry point of the Spring Boot application. @@ -25,6 +32,7 @@ public class RemoteLightWeb extends SpringBootServletInitializer { private static ConfigurableApplicationContext context; private static RemoteLightWeb instance; private RemoteLightAPI rlApi; + private UpdateUtil updateUtil; private boolean closing; public static void main(String[] args) { @@ -39,6 +47,8 @@ public RemoteLightWeb() { RemoteLightAPI.setRootDirectory(Paths.get(".").toAbsolutePath().normalize().toString()); // directory where the jar was executed RemoteLightAPI.setRootName(ROOT_FOLDER_NAME); rlApi = new RemoteLightAPI(); + updateUtil = new UpdateUtil(VERSION); + setup(); // initial some settings and check for updates } @@ -93,5 +103,26 @@ private static String getVersion() { } return "?"; } + + public UpdateUtil getUpdateUtil() { + return updateUtil; + } + + private void setup() { + SettingsManager s = getAPI().getSettingsManager(); + // disable standard updater + ((SettingBoolean) s.getSettingFromId("main.checkupdates")).setValue(false); + // add RemoteLightWeb updater setting + s.addSetting(new SettingBoolean("rlweb.updater", "Updater", SettingCategory.General, "Check for updates at startup", true)); + + File jarDir = new ApplicationHome(RemoteLightWeb.class).getSource(); + String runCommand = "java -jar " + jarDir.getAbsolutePath(); + s.addSetting(new SettingString("rlweb.runcmd", "Run command after update", SettingCategory.Others, "This command is executed after an update", runCommand)); + + // check for updates + if(((SettingBoolean) s.getSettingFromId("rlweb.updater")).getValue() && !VERSION.equals("?")) { + updateUtil.check(); + } + } } diff --git a/src/main/java/de/lars/remotelightweb/backend/ConfigFile.java b/src/main/java/de/lars/remotelightweb/backend/ConfigFile.java index 7fc8527..b69a7f0 100644 --- a/src/main/java/de/lars/remotelightweb/backend/ConfigFile.java +++ b/src/main/java/de/lars/remotelightweb/backend/ConfigFile.java @@ -19,8 +19,10 @@ public class ConfigFile { public ConfigFile() { // copy config file from classpath if not exists - if(!new File(RemoteLightWeb.ROOT_FOLDER_NAME + File.separator + CONFIG_FILE_NAME).exists()) { + File config = new File(RemoteLightWeb.ROOT_FOLDER_NAME + File.separator + CONFIG_FILE_NAME); + if(!config.exists()) { try { + config.getParentFile().mkdirs(); InputStream input = getClass().getClassLoader().getResourceAsStream(CONFIG_CLASSPATH); Files.copy(input, new File(RemoteLightWeb.ROOT_FOLDER_NAME + File.separator + CONFIG_FILE_NAME).toPath()); } catch (IOException e) { diff --git a/src/main/java/de/lars/remotelightweb/backend/utils/UpdateUtil.java b/src/main/java/de/lars/remotelightweb/backend/utils/UpdateUtil.java new file mode 100644 index 0000000..63b6dd1 --- /dev/null +++ b/src/main/java/de/lars/remotelightweb/backend/utils/UpdateUtil.java @@ -0,0 +1,98 @@ +package de.lars.remotelightweb.backend.utils; + +import java.io.File; +import java.net.URL; +import java.net.URLDecoder; +import java.nio.file.Paths; + +import org.springframework.boot.system.ApplicationHome; +import org.tinylog.Logger; + +import de.lars.remotelightclient.settings.types.SettingString; +import de.lars.remotelightclient.utils.DirectoryUtil; +import de.lars.remotelightweb.RemoteLightWeb; +import de.lars.updater.sites.GitHubParser; +import de.lars.updater.utils.FileDownloader; + +public class UpdateUtil { + + public final String API_URL = "https://api.github.com/repos/Drumber/RemoteLightWeb/releases"; + private final String UPDATER_NAME = "updater.jar"; + private GitHubParser parser; + + public UpdateUtil(String currentVersion) { + parser = new GitHubParser(currentVersion, API_URL); + } + + public void check() { + try { + parser.check(); + } catch (Exception e) { + Logger.error(e, "Error while checking for updates."); + } + } + + public GitHubParser getParser() { + return parser; + } + + + /** + * Download latest release and replace current jar file + * This method will EXIT the application! + * @param shutdown Should the system be shut down after the update? + * @throws Exception + */ + public void install(boolean shutdown) throws Exception { + File jarDir = new ApplicationHome(RemoteLightWeb.class).getSource(); + + String rootDir = DirectoryUtil.getRootPath(); + rootDir = Paths.get(".").toAbsolutePath().normalize().toString(); + + //download updater jar + File updaterFile = downloadUpdater(rootDir); + + // execute updater + String args = "-cv " + RemoteLightWeb.VERSION + " -o \"" + jarDir.getAbsolutePath() + + "\" -u " + API_URL + " -w -cmd \""; + + if(shutdown) { + args += "\"shutdown -h now\""; + } else { + String runCmd = ((SettingString) RemoteLightWeb.getInstance().getAPI().getSettingsManager().getSettingFromId("rlweb.runcmd")).getValue(); + if(runCmd == null || runCmd.isEmpty()) { + runCmd = "\"java -jar " + jarDir.getAbsolutePath() + "\""; + } + args += runCmd; + } + args += "\""; + + String command = String.format("java -jar %s %s", updaterFile.getAbsoluteFile(), args); + Logger.info("Run Updater with the following command: " + command); + Runtime.getRuntime().exec(command); + + RemoteLightWeb.exitApplication(); + } + + private File downloadUpdater(String rootDir) throws Exception { + File updaterFile = new File(rootDir + File.separator + UPDATER_NAME); + if(!updaterFile.exists()) { + GitHubParser parser = new GitHubParser("0", "Drumber", "Updater"); + try { + + parser.check(); + Logger.info("Downloading Uploader version " + parser.getNewestVersionTag() +" to " + updaterFile.getAbsolutePath()); + FileDownloader downloader = new FileDownloader(parser.getNewestDownloadUrl(), updaterFile.getAbsolutePath()); + if(downloader.isDownloadSuccessful()) { + return updaterFile; + } + + } catch (Exception e) { + throw new Exception("Could not download Updater.", e); + } + return null; + } + return updaterFile; + } + +} diff --git a/src/main/java/de/lars/remotelightweb/ui/components/UpdateDialog.java b/src/main/java/de/lars/remotelightweb/ui/components/UpdateDialog.java new file mode 100644 index 0000000..45f2eb2 --- /dev/null +++ b/src/main/java/de/lars/remotelightweb/ui/components/UpdateDialog.java @@ -0,0 +1,108 @@ +package de.lars.remotelightweb.ui.components; + +import org.tinylog.Logger; + +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.dialog.Dialog; +import com.vaadin.flow.component.html.Anchor; +import com.vaadin.flow.component.html.H3; +import com.vaadin.flow.component.html.Label; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; + +import de.lars.remotelightweb.RemoteLightWeb; +import de.lars.remotelightweb.backend.utils.UpdateUtil; +import de.lars.remotelightweb.ui.utils.UIUtils; +import de.lars.updater.sites.GitHubParser; + +public class UpdateDialog extends Dialog { + + public UpdateDialog(GitHubParser parser) { + VerticalLayout l = new VerticalLayout(); + l.setSizeFull(); + add(l); + l.add(new H3("Updater")); + + l.add(new Label(parser.isNewVersionAvailable() ? "New version available!" : "No new version available.")); + l.add(new Label("Installed: " + RemoteLightWeb.VERSION)); + l.add(new Label("Latest: " + parser.getNewestVersionTag())); + l.add(new Anchor(parser.getNewestUrl(), parser.getNewestUrl())); + + if(parser.isNewVersionAvailable()) { + Button btnIgnore = UIUtils.createButton("Ignore", "5px 5px"); + btnIgnore.addClickListener(e -> ignore()); + add(btnIgnore); + + Button btnUpdate = UIUtils.createButton("Update now", "5px 5px"); + btnUpdate.addClickListener(e -> update()); + add(btnUpdate); + } else { + add(new Button("Close", e -> close())); + } + } + + + private void ignore() { + close(); + removeAll(); + VerticalLayout l = new VerticalLayout(); + l.setSizeFull(); + add(l); + + l.add(new Label("The update can also be installed later in the settings.")); + l.add(new Button("Ok", e -> close())); + open(); + } + + + private void update() { + close(); + removeAll(); + VerticalLayout l = new VerticalLayout(); + l.setSizeFull(); + add(l); + + l.add(new H3("Important information")); + l.add(new Label("The program will be closed while the new version is downloaded in the background. " + + "Please do not power off the system! RemoteLightWeb will automatically restart after the update is complete.")); + l.add(new Label("You can choose between two options:")); + l.add(new Label("1. (Update and restart) RemoteLightWeb will automatically restart after the update is complete.")); + l.add(new Label("2. (Update and shutdown) The system will shut down after the update has been successfully completed.")); + + UpdateUtil updateUtil = RemoteLightWeb.getInstance().getUpdateUtil(); + + add(UIUtils.createButton("Cancel", "5px 5px", e -> close())); + add(UIUtils.createButton("Update and restart", "5px 5px", e -> { + try { + updateUtil.install(false); + } catch (Exception e1) { + Logger.error(e1, "Error while updating"); + error("Error while updating", e1); + } + })); + add(UIUtils.createButton("Update and shutdown", "5px 5px", e -> { + try { + updateUtil.install(true); + } catch (Exception e1) { + Logger.error("Error while updating", e); + error("Error while updating", e1); + } + })); + open(); + } + + + private void error(String message, Exception ex) { + close(); + removeAll(); + VerticalLayout l = new VerticalLayout(); + l.setSizeFull(); + add(l); + + l.add(new H3("An error has occurred")); + l.add(new Label(message)); + l.add(new Label(ex.getMessage())); + l.add(new Button("Close", e -> close())); + open(); + } + +} diff --git a/src/main/java/de/lars/remotelightweb/ui/utils/UIUtils.java b/src/main/java/de/lars/remotelightweb/ui/utils/UIUtils.java index 657271d..e82a6f0 100644 --- a/src/main/java/de/lars/remotelightweb/ui/utils/UIUtils.java +++ b/src/main/java/de/lars/remotelightweb/ui/utils/UIUtils.java @@ -1,5 +1,7 @@ package de.lars.remotelightweb.ui.utils; +import com.vaadin.flow.component.ClickEvent; +import com.vaadin.flow.component.ComponentEventListener; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.icon.VaadinIcon; @@ -35,6 +37,12 @@ public static Button createButton(String text, String marginArg) { return button; } + public static Button createButton(String text, String marginArg, ComponentEventListener> listener) { + Button button = createButton(text, marginArg); + button.addClickListener(listener); + return button; + } + public static Button addMargin(Button button, String marginArg) { button.getStyle().set("margin", marginArg); return button; diff --git a/src/main/java/de/lars/remotelightweb/ui/views/SettingsView.java b/src/main/java/de/lars/remotelightweb/ui/views/SettingsView.java index bc16d08..2ee85fa 100644 --- a/src/main/java/de/lars/remotelightweb/ui/views/SettingsView.java +++ b/src/main/java/de/lars/remotelightweb/ui/views/SettingsView.java @@ -22,7 +22,9 @@ import de.lars.remotelightclient.settings.SettingsManager.SettingCategory; import de.lars.remotelightclient.settings.types.SettingSelection; import de.lars.remotelightweb.RemoteLightWeb; +import de.lars.remotelightweb.backend.utils.UpdateUtil; import de.lars.remotelightweb.ui.MainLayout; +import de.lars.remotelightweb.ui.components.UpdateDialog; import de.lars.remotelightweb.ui.components.custom.PaperSlider; import de.lars.remotelightweb.ui.components.settingpanels.SettingPanel; import de.lars.remotelightweb.ui.utils.SettingPanelUtil; @@ -52,6 +54,12 @@ public SettingsView() { }); add(new Label("Brightness"), brightness); + add(new Button("Check for updates", e -> { + UpdateUtil updater = RemoteLightWeb.getInstance().getUpdateUtil(); + updater.check(); + new UpdateDialog(updater.getParser()).open();; + })); + Button close = new Button("Shutdown"); add(close); close.addClickListener(e -> {