package org.openslx.dozmod.util; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.nio.charset.StandardCharsets; import org.apache.commons.io.FileUtils; import org.apache.commons.io.output.ByteArrayOutputStream; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.openslx.dozmod.App; import org.openslx.dozmod.Branding; import org.openslx.sat.thrift.version.Version; import org.openslx.util.AppUtil; import org.openslx.util.Json; public class ClientVersion { private static final Logger LOGGER = LogManager.getLogger(ClientVersion.class); private static long localRevisionTime = 0; private static long remoteRevisionTime = 0; private static String localRevision = "???"; private static String remoteRevision = "???"; private static String changelog = "???"; private static Thread remoteThread = null; static { loadLocalVersion(); loadRemoteVersion(Branding.getUpdateServerAddress() + Version.VERSION + "/version.json"); } /** * Gets the local revision id if loading it worked, "???" otherwise. * * @return id as String */ public static String getLocalRevision() { return localRevision; } /** * @return */ public static long getLocalRevTimestamp() { return localRevisionTime; } /** * Gets the revision id of the latest remote version * * @return id as String if loading worked, "???" otherwise */ public static String getRemoteRevision() { waitRemote(); return remoteRevision; } /** * Gets the timestamp of the latest remote version * * @return timestamp as Long if loading it worked, 0L otherwise */ public static long getRemoteRevTimestamp() { waitRemote(); return remoteRevisionTime; } /** * Gets the changelog * * @return log as String */ public static String getChangelog() { waitRemote(); return changelog; } /** * Checks if we are running latest application version * * @return true if there is no newer version, false otherwise */ public static boolean isNewest() { // if either local or remote version is unknown, just pretend there's no update // as there most likely isn't and we'd just annoy the user // TODO: Report "fail" state so at least on manual update check we can tell that it failed waitRemote(); if (localRevisionTime == 0 || remoteRevisionTime == 0) return true; return localRevisionTime >= remoteRevisionTime; } /** * Loads the local version information from the jar's MANIFEST.MF * into the fields 'localRevision' and 'localRevisionTime' */ private static void loadLocalVersion() { localRevision = AppUtil.getRevisionVersion(); localRevisionTime = AppUtil.getBuildTimestamp(); } /** * Loads the given UrlString as JSON and saves the remote information * into fields 'remoteRevision' and 'remoteRevisionTime' * * The remote JSON should have 'timestamp' and 'revision', like: * { "timestamp": 1, "revision": 2 } */ private static void loadRemoteVersion(final String urlString) { remoteThread = new Thread(new Runnable() { @Override public void run() { App.waitForInit(); String json = null; try (InputStream reader = new URL(urlString).openStream()) { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); buffer.write(reader); buffer.close(); json = new String(buffer.toByteArray(), StandardCharsets.UTF_8); } catch (Exception e) { if (json == null) { LOGGER.error("Could not fetch remote version", e); return; } } VersionQuery query = Json.deserialize(json, VersionQuery.class); remoteRevision = query.revision; // seconds timestamp here... remoteRevisionTime = query.timestamp; changelog = query.changelog; } }); remoteThread.start(); } private static synchronized void waitRemote() { if (remoteThread == null) return; try { remoteThread.join(5000); } catch (InterruptedException e) { } if (!remoteThread.isAlive()) { remoteThread = null; } } /** * Class for GSON json parsing */ static class VersionQuery { long timestamp; String revision; String changelog; } public static void createJson(String name) throws IOException { loadLocalVersion(); if (localRevisionTime == 0) throw new RuntimeException("Missing manifest/data in jar: No revision time found"); if (localRevision == null || localRevision.isEmpty()) throw new RuntimeException("Missing manifest/data in jar: No commit hash found"); System.out.println("Please enter change log. To finish, put a '.' on a single line"); StringBuilder sb = new StringBuilder(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); for (String line; (line = br.readLine()) != null;) { if (line.equals(".")) break; sb.append(line); sb.append('\n'); } VersionQuery vq = new VersionQuery(); vq.timestamp = localRevisionTime; vq.revision = localRevision; vq.changelog = sb.toString(); String data = Json.serialize(vq); FileUtils.writeStringToFile(new File(name), data, StandardCharsets.UTF_8); System.out.println(); System.out.println("Created json file at " + name); System.out.println("This build is using Thrift RPC interface version >> " + Version.VERSION + " <<"); System.out.println(); } }