/** ** 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 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); } }