EDAF85-labs/cs/src/UpdateLabCode.java
2024-06-10 10:53:11 +02:00

173 lines
No EOL
6.8 KiB
Java

/**
** Simple application to update cs/labs.jar to the most recent version.
**
** The labs.jar file is fetched from the gitlab.com repository.
**/
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
import java.util.List;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.stream.Collectors;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
public class UpdateLabCode extends JFrame {
private static final URI DOWNLOAD_URI = URI.create("https://gitlab.com/api/v4/projects/edaf85%2Fedaf85-labs/repository/files/cs%2Flabs.jar/raw?ref=main");
private final JLabel messageArea = new JLabel("", SwingConstants.CENTER);
private final JPanel buttonArea = new JPanel(new FlowLayout(FlowLayout.RIGHT, 20, 20));
// -----------------------------------------------------------------------
public UpdateLabCode() {
super("Update lab code (EDAF85, fall 2024)");
add(messageArea, BorderLayout.CENTER);
add(buttonArea, BorderLayout.SOUTH);
try {
List<Path> candidates = Files.walk(Path.of(System.getProperty("user.dir")).getParent())
.filter(Files::isRegularFile)
.filter(f -> f.endsWith(Path.of("cs", "labs.jar")))
.collect(Collectors.toList());
if (candidates.size() == 1) {
showDialog("Currently using version: " + getJarVersion(candidates.get(0)),
"Check for update", () -> check(candidates.get(0)));
} else {
if (candidates.size() != 0) {
System.err.println("*** ERROR: found multiple 'cs/labs.jar', cannot decide which one to use:");
candidates.forEach(System.err::println);
}
throw new FileNotFoundException("cannot locate 'cs/labs.jar'");
}
} catch (IOException e) {
fail(e);
}
setPreferredSize(new Dimension(600, 150));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
private void showDialog(String message, String buttonLabel, Runnable onClick) {
SwingUtilities.invokeLater(() -> {
messageArea.setText(message);
buttonArea.removeAll();
buttonArea.repaint();
JButton b = new JButton(buttonLabel);
b.addActionListener(e -> {
b.setEnabled(false);
onClick.run();
});
buttonArea.add(b);
getRootPane().setDefaultButton(b);
});
}
private void fail(Throwable t) {
t.printStackTrace(System.err);
showDialog("Error: " + t.getClass().getSimpleName() + " (see console)",
"Quit", () -> System.exit(1));
}
private void check(Path labsJar) {
try {
messageArea.setText("Checking...");
Path tempDir = labsJar.getParent().resolve("archive");
Files.createDirectories(tempDir);
Path downloadJar = Files.createTempFile(tempDir, ".download-", ".jar");
downloadJar.toFile().deleteOnExit();
HttpRequest req = HttpRequest.newBuilder(DOWNLOAD_URI).build();
HttpClient.newHttpClient()
.sendAsync(req, BodyHandlers.ofFile(downloadJar))
.thenAccept(response -> {
try {
int status = response.statusCode();
if (status != HttpURLConnection.HTTP_OK) {
showDialog("Unexpected status code: " + status,
"Quit", () -> System.exit(1));
} else if (Arrays.equals(Files.readAllBytes(labsJar), Files.readAllBytes(downloadJar))) { // same content
showDialog("No update available: you are using the latest version (" + getJarVersion(labsJar) + ")",
"OK", () -> System.exit(0));
} else if (!getCourseVersion(downloadJar).equals("fall 2024")) {
showDialog("Cannot update: latest update requires repo from " + getCourseVersion(downloadJar),
"OK", () -> System.exit(0));
} else {
showDialog("Update available: " + getJarVersion(downloadJar),
"Update now", () -> updateLabsJar(labsJar, downloadJar, tempDir));
}
} catch (IOException e) {
fail(e);
}
});
} catch (IOException e) {
fail(e);
}
}
private void updateLabsJar(Path labsJar, Path downloadJar, Path tempDir) {
try {
Path tempFile = Files.createTempFile(tempDir, "labs_jar_", ".old");
Files.move(labsJar, tempFile, StandardCopyOption.REPLACE_EXISTING);
Files.move(downloadJar, labsJar, StandardCopyOption.REPLACE_EXISTING);
showDialog("Success: you are now using the latest version!",
"OK", () -> System.exit(0));
} catch (IOException e) {
fail(e);
}
}
private static String getCourseVersion(Path jar) {
try (JarFile f = new JarFile(jar.toFile())) {
Attributes a = f.getManifest().getAttributes("common");
return (a != null) ? a.getValue(Attributes.Name.SPECIFICATION_VERSION) : "unknown";
} catch (IOException e) {
return "unknown";
}
}
private static String getJarVersion(Path jar) {
try (JarFile f = new JarFile(jar.toFile())) {
Attributes a = f.getManifest().getAttributes("common");
return (a != null) ? a.getValue(Attributes.Name.IMPLEMENTATION_VERSION) : "unknown";
} catch (IOException e) {
return "unknown";
}
}
// -----------------------------------------------------------------------
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
SwingUtilities.invokeLater(UpdateLabCode::new);
}
}