Verified Commit 7e2eebe5 authored by Daniel Mangold's avatar Daniel Mangold
Browse files

Refactored checkForUpdates() in Utils.java

parent cbc10023
......@@ -14,6 +14,7 @@ import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.util.*;
@SuppressWarnings("unused")
public class Utils {
// ----------------------------------- //
......@@ -44,10 +45,12 @@ public class Utils {
"Please re-run the tests afterwards.");
if (!CHECK_FOR_UPDATES || !Updater.checkForUpdates()) {
System.out.println("Tests are up to date");
System.out.println("Seed: " + SEED);
} else
} else {
System.out.println("Updated tests, please re-run");
System.exit(0);
}
}
/**
......@@ -197,17 +200,10 @@ public class Utils {
private static final String REPOSITORY_URL = "https://git.rwth-aachen.de/aud-tests/AuD-2021-" + ASSIGNMENT_ID + "-Student/-/";
/**
* Checks if the repository is newer than the local copy and does the following actions
* if so:
*
* <ul>
* <li>{@link Config#CHECK_HASHES} = {@code true}: only compares the MD5 hashes of all files</li>
* <li>{@link Config#AUTO_UPDATE} = {@code true}: compares the MD5 hashes of all files and
* re-downloads those who do not match</li>
* </ul>
*
* Messages are printed in any case to let the user know what is happening
* @return whether any changes have been written
* Checks if the repository is newer than the local copy and updates it.
* Messages are printed to let the user know what is happening
* @return whether any changes have been written to disk
* @see Config#CHECK_FOR_UPDATES
*/
private static boolean checkForUpdates() {
HttpResponse<String> response = getHttpResource(".test_metadata.json");
......@@ -218,99 +214,57 @@ public class Utils {
return false;
}
try {
JSONObject remoteData = new JSONObject(response.body());
Version localVersion = new Version(LOCAL_VERSION),
remoteVersion = new Version(remoteData.getString("version"));
if (remoteVersion.compareTo(localVersion) > 0) {
System.out.println("Update available! Local version: " + localVersion + " -- Remote version: " + remoteVersion);
System.out.println("Changelog: " + REPOSITORY_URL + "blob/master/changelog.md");
System.out.println(remoteData.getString("updateMessage"));
}
JSONObject remoteData = new JSONObject(response.body());
Version localVersion = new Version(LOCAL_VERSION),
remoteVersion = new Version(remoteData.getString("version"));
if (!(CHECK_HASHES || (AUTO_UPDATE && remoteVersion.compareTo(localVersion) > 0)))
return false;
if (remoteVersion.compareTo(localVersion) <= 0) {
System.out.println("Tests are up to date");
return false;
}
System.out.println("Update available! Local version: " + localVersion + " -- Remote version: " + remoteVersion);
System.out.println("Changelog: " + REPOSITORY_URL + "blob/master/changelog.md");
System.out.println(remoteData.getString("updateMessage"));
if (!(CHECK_HASHES || AUTO_UPDATE))
return false;
try {
for (Map.Entry<String, Object> fileMap : remoteData.getJSONObject("hashes").toMap().entrySet()) {
String fileName = fileMap.getKey(), expectedHash = (String) fileMap.getValue();
if (((List<?>) getConfigOrDefault("EXCLUDED_FILES", List.of())).contains(fileName))
continue;
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
File file = new File(fileName);
if (!file.exists()) {
if (AUTO_UPDATE) {
updateLocal(fileName);
persistentChanges = true;
} else
if (!new File(fileName).exists()) {
if (AUTO_UPDATE)
persistentChanges = updateLocal(fileName);
else
System.err.println(fileName + " not found, can't compare hashes");
continue;
}
try (InputStream inputStream = new FileInputStream(file)) {
String actualHash = new BigInteger(1, messageDigest.digest(inputStream.readAllBytes())).toString(16);
actualHash = "0".repeat(32 - actualHash.length()) + actualHash;
if (!actualHash.equals(expectedHash)) {
System.out.println("Hash mismatch for file " + fileName);
if (AUTO_UPDATE) {
updateLocal(fileName);
if (!getHash(fileName).equals(expectedHash)) {
System.out.println("Hash mismatch for file " + fileName);
persistentChanges = true;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
if (AUTO_UPDATE)
persistentChanges = updateLocal(fileName);
}
}
if (AUTO_UPDATE && (Boolean) getConfigOrDefault("EXISTS", false)) {
JSONObject configData = remoteData.getJSONObject("config");
String configStub = configData.getString("stub");
StringBuilder configFileContents = new StringBuilder(" // >>>## UPDATE MARKER, DO NOT REMOVE, ONLY MODIFY THE LINES BELOW\n\n");
//noinspection unchecked
Set<String> existingFields = (Set<String>) Class.forName("h05.Config").getDeclaredMethod("getConfigs").invoke(null);
JSONObject configData = remoteData.getJSONObject("config");
Set<String> constants = getConstants();
try (BufferedReader reader = new BufferedReader(new FileReader(configData.getString("file")))) {
boolean insert = false;
if (AUTO_UPDATE &&
((Boolean) getConfigOrDefault("EXISTS", false)) &&
!constants.containsAll(configData.getJSONObject("constants").keySet())) {
updateConfig(configData);
for (String line = reader.readLine(); !line.matches("\\s*// ##<<<.*"); line = reader.readLine()) {
if (line.matches("\\s*// >>>##.*")) {
reader.readLine();
line = reader.readLine();
insert = true;
}
if (insert)
configFileContents.append(line).append("\n");
}
}
for (Map.Entry<String, Object> constantEntry : configData.getJSONObject("constants").toMap().entrySet()) {
String fieldName = constantEntry.getKey(), fieldData = (String) constantEntry.getValue();
if (!existingFields.contains(fieldName))
configFileContents.append(fieldData).append("\n\n");
}
configFileContents.append(" // ##<<< UPDATE MARKER, DO NOT REMOVE, DO NOT CHANGE ANYTHING BELOW THIS LINE\n\n");
for (Map.Entry<String, Object> methodEntry : configData.getJSONObject("methods").toMap().entrySet())
configFileContents.append((String) methodEntry.getValue()).append("\n\n");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(configData.getString("file")))) {
writer.write(configStub.replaceFirst(">>>##<<<", configFileContents.toString()));
}
persistentChanges = true;
}
} catch (IOException | InterruptedException | NoSuchAlgorithmException | ReflectiveOperationException e) {
} catch (IOException e) {
e.printStackTrace();
}
......@@ -339,11 +293,33 @@ public class Utils {
}
}
/**
* Calculates the MD5 hash of a file and returns it
* @param fileName path to the file
* @return the hash as a hexadecimal string
* @throws IOException if an I/O error occurs
*/
private static String getHash(String fileName) throws IOException {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
try (InputStream inputStream = new FileInputStream(fileName)) {
String actualHash = new BigInteger(1, messageDigest.digest(inputStream.readAllBytes())).toString(16);
return "0".repeat(32 - actualHash.length()) + actualHash;
}
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return "";
}
}
/**
* Updates (overwrites) the specified file with the contents of the file at the repository
* @param fileName the relative path to the file
* @return whether anything has been written to disk
*/
private static void updateLocal(String fileName) throws IOException, InterruptedException {
private static boolean updateLocal(String fileName) {
System.out.print("Downloading " + fileName + "... ");
File file = new File(fileName);
......@@ -357,11 +333,72 @@ public class Utils {
writer.write(response.body());
} catch (IOException e) {
e.printStackTrace();
return true;
}
System.out.println("done");
} else
System.out.println("unable to fetch file from repository");
return true;
}
System.out.println("unable to fetch file from repository");
return false;
}
/**
* Updates the configuration file, keeping all data written between the markers ">>>##" and "##<<<"
* @param configData a JSON object containing information such as required constants, for example
* @throws IOException if an I/O error occurs
* @see Config
*/
private static void updateConfig(JSONObject configData) throws IOException {
String configStub = configData.getString("stub");
StringBuilder configFileContents = new StringBuilder(" // >>>## UPDATE MARKER, DO NOT REMOVE, ONLY MODIFY THE LINES BELOW\n");
Set<String> existingFields = getConstants();
try (BufferedReader reader = new BufferedReader(new FileReader(configData.getString("file")))) {
boolean insert = false;
for (String line = reader.readLine(); !line.matches("\\s*// ##<<<.*"); line = reader.readLine()) {
if (line.matches("\\s*// >>>##.*")) {
line = reader.readLine();
insert = true;
}
if (insert)
configFileContents.append(line).append("\n");
}
}
for (Map.Entry<String, Object> constantEntry : configData.getJSONObject("constants").toMap().entrySet()) {
String fieldName = constantEntry.getKey(), fieldData = (String) constantEntry.getValue();
if (!existingFields.contains(fieldName))
configFileContents.append(fieldData).append("\n\n");
}
configFileContents.append(" // ##<<< UPDATE MARKER, DO NOT REMOVE, DO NOT CHANGE ANYTHING BELOW THIS LINE\n\n");
for (Map.Entry<String, Object> methodEntry : configData.getJSONObject("methods").toMap().entrySet())
configFileContents.append((String) methodEntry.getValue()).append("\n\n");
try (BufferedWriter writer = new BufferedWriter(new FileWriter(configData.getString("file")))) {
writer.write(configStub.replaceFirst(">>>##<<<", configFileContents.toString()));
}
}
/**
* Gets a set of all constants defined in {@link Config} (invokes {@link Config#getConfigs()})
* @return the set of constants or an empty set if a {@link ReflectiveOperationException} was thrown
*/
private static Set<String> getConstants() {
try {
//noinspection unchecked
return (Set<String>) Class.forName("h05.Config").getDeclaredMethod("getConfigs").invoke(null);
} catch (ReflectiveOperationException e) {
return Set.of();
}
}
private static class Version implements Comparable<Version> {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment