summaryrefslogblamecommitdiffstats
path: root/dozentenmodul/src/main/java/org/openslx/dozmod/util/ClientVersion.java
blob: 365d1796b6bc8f4fc6ff0561c62978b4bd655b99 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                


                              
                           
                                 
                    
                                         


                                
                                       
                                                          
                               
                              
                                   
                                              
                             
 


                                                                                   


                                                   



                                                     
 

                                   
                                                                                                                                   
         
 
           
                                                                            



                                                 

                                     
 


                  
                                                   

                                         
 


                                                            
                                                                  

                                                  
                             
                                      
         
 





                                                                       
                             
                                          
         
 
           




                                             
                             



                                 
                                                              


                                                                     
                                          


                                                                                                          
                             
                                                                      

                                                               
         
 



                                                                         
                                                
                                                                 


                                                                           

                                             
                 
                                                                                                     
                                         

                                                                               
                                       



                                                                           
                 








                                                                              



                                                                         
                                                                                          

                                                                          
                 
                                          
                                                    
                 
                                              





                                                                                          
                 
         
 
           
                                                                             


                                                                        
                                            

                                                                       





















                                                                                                        
                         









                                                       
                 
                                    

         



                                      
                               
                                
                                 
         


























                                                                                                                     
 
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 java.util.jar.Attributes;
import java.util.jar.Manifest;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.output.ByteArrayOutputStream;
import org.apache.log4j.Logger;
import org.openslx.dozmod.App;
import org.openslx.dozmod.Branding;
import org.openslx.sat.thrift.version.Version;
import org.openslx.util.Json;

public class ClientVersion {

	private static final Logger LOGGER = Logger.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("https://" + Branding.getMasterServerAddress() + "/dozmod/" + 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() {
		Class<ClientVersion> clazz = ClientVersion.class;
		String className = clazz.getSimpleName() + ".class";
		String classPath = clazz.getResource(className).toString();
		if (!classPath.startsWith("jar")) {
			// Class not from JAR
			return;
		}
		String manifestPath = classPath.replaceAll("![^!]*$", "") + "!/META-INF/MANIFEST.MF";
		Manifest manifest = null;
		try (InputStream stream = new URL(manifestPath).openStream()) {
			manifest = new Manifest(stream);
		} catch (Exception e) {
			if (manifest == null) {
				LOGGER.error("Could not open MANIFEST", e);
				return;
			}
		}
		Attributes attributes = manifest.getMainAttributes();
		// if attr are null, then we couldn't open the jar's MANIFEST
		// since we are probably not in a jar context, just do nothing
		if (attributes == null)
			return;
		String manifestRev = null;
		String manifestRevTime = null;
		try {
			manifestRev = attributes.getValue("Build-Revision");
		} catch (Exception e) {
			LOGGER.warn("Error while reading revision: ", e);
		}
		try {
			manifestRevTime = attributes.getValue("Build-Revision-Timestamp");
		} catch (Exception e) {
			LOGGER.warn("Error while reading timestamp: ", e);
		}
		if (manifestRev != null) {
			localRevision = manifestRev;
		}
		if (manifestRevTime != null) {
			try {
				// hax since we get milliseconds not seconds
				localRevisionTime = Long.valueOf(manifestRevTime) / 1000L;
			} catch (NumberFormatException e) {
				LOGGER.warn("Build timestamp is not a number!", e);
			}
		}
	}

	/**
	 * 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();
		} catch (InterruptedException e) {
		}
		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();
	}
}