/******************************************************************************* * Copyright (c) 2001, 2010 Mathew A. Nelson and Robocode contributors * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://robocode.sourceforge.net/license/epl-v10.html * * Contributors: * Mathew A. Nelson * - Initial API and implementation * Flemming N. Larsen * - Replaced deprecated methods * - Added check for the Java version that the user has installed. If the * Java version is not 5.0, an error dialog will be display and the * installation will terminate * - Changed the information message for how to run robocode.sh, where the * user does not have to change the directory before calling robocode.sh * - Code cleanup *******************************************************************************/ package net.sf.robocode.installer; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; import java.net.URL; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; /** * Installer for Robocode. * * @author Mathew A. Nelsen (original) * @author Flemming N. Larsen (contributor) */ public class AutoExtract implements ActionListener { private JDialog licenseDialog; private boolean accepted; private final String[] spinner = { "-", "\\", "|", "/"}; private String message = ""; private static File installDir; private static final String javaVersion = System.getProperty("java.version"); private boolean acceptLicense() { String licenseText = ""; InputStream is; try { JarFile extractJar = new JarFile("extract.jar"); is = extractJar.getInputStream(extractJar.getJarEntry("license/cpl-v10.html")); } catch (IOException e) { return true; } if (is == null) { return true; } BufferedReader r = new BufferedReader(new InputStreamReader(is)); try { String line = r.readLine(); while (line != null) { licenseText += line; line = r.readLine(); } return acceptReject(licenseText); } catch (IOException e) { System.err.println("Could not read line from license file: " + e); } return true; } private boolean acceptReject(String text) { Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); licenseDialog = new JDialog(); licenseDialog.setTitle("License Agreement"); licenseDialog.setModal(true); licenseDialog.setLocation((screenSize.width - 500) / 2, (screenSize.height - 400) / 2); licenseDialog.setSize(500, 400); JTextPane t = new JTextPane(); t.setContentType("text/html"); t.setText(text); t.setFont(new Font("Dialog", Font.PLAIN, 12)); t.setEditable(false); JScrollPane s = new JScrollPane(); s.setViewportView(t); licenseDialog.getContentPane().setLayout(new BorderLayout()); licenseDialog.getContentPane().add(s, BorderLayout.CENTER); JPanel p = new JPanel(); p.setLayout(new BorderLayout()); JButton b1 = new JButton("Accept"); JButton b2 = new JButton("Cancel"); p.add(b1, BorderLayout.WEST); p.add(b2, BorderLayout.EAST); b1.addActionListener(this); b2.addActionListener(this); licenseDialog.getContentPane().add(p, BorderLayout.SOUTH); licenseDialog.setVisible(true); return accepted; } public void actionPerformed(ActionEvent e) { accepted = e.getActionCommand().equals("Accept"); licenseDialog.dispose(); licenseDialog = null; } private boolean extract(File dest) { JDialog statusDialog = new JDialog(); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); int height = 50; if (File.separatorChar == '/') { height = 100; } statusDialog.setTitle("Installing"); statusDialog.setLocation((screenSize.width - 500) / 2, (screenSize.height - height) / 2); statusDialog.setSize(500, height); JLabel status = new JLabel(); statusDialog.getContentPane().setLayout(new BorderLayout()); statusDialog.getContentPane().add(status, BorderLayout.CENTER); statusDialog.setVisible(true); FileOutputStream fos; String entryName; byte buf[] = new byte[2048]; final String name = AutoExtract.class.getName().replaceAll("\\.", "/") + ".class"; String urlJar = AutoExtract.class.getClassLoader().getResource(name).toString(); final String src = urlJar.substring("jar:file:/".length(), urlJar.indexOf("!/")); if (src.indexOf('!') > -1) { final String message = src + "\nContains an exclamation point. Please move the file to a different directory."; JOptionPane.showMessageDialog(null, message); System.err.println(message); System.exit(0); } try { final URL url = new URL("file:/" + src); InputStream is = url.openStream(); JarInputStream jarIS = new JarInputStream(is); JarEntry entry = jarIS.getNextJarEntry(); while (entry != null) { int spin = 0; entryName = entry.getName(); if (entry.isDirectory()) { if (!entryName.startsWith("net")) { File dir = new File(dest, entry.getName()); if (!dir.exists() && !dir.mkdirs()) { System.out.println("Can't create dir " + dir); } } } else { if (!entryName.equals(name)) { status.setText(entryName + " " + spinner[spin++]); File out = new File(dest, entry.getName()); File parentDirectory = new File(out.getParent()); if (!parentDirectory.exists() && !parentDirectory.mkdirs()) { System.out.println("Can't create dir " + parentDirectory); } fos = new FileOutputStream(out); int index = 0; int num; int count = 0; while ((num = jarIS.read(buf, 0, 2048)) != -1) { fos.write(buf, 0, num); index += num; count++; if (count > 80) { status.setText(entryName + " " + spinner[spin++] + " (" + index + " bytes)"); if (spin > 3) { spin = 0; } count = 0; } } fos.close(); if (entryName.length() > 3 && entryName.substring(entryName.length() - 3).equals(".sh")) { if (File.separatorChar == '/') { Runtime.getRuntime().exec("chmod 755 " + out.toString()); } } status.setText(entryName + " " + spinner[spin] + " (" + index + " bytes)"); } } entry = jarIS.getNextJarEntry(); } statusDialog.dispose(); message = "Installation successful"; return true; } catch (IOException e) { message = "Installation failed" + e; return false; } } public static void main(String argv[]) { // Verify that the Java version is version 5 (1.5.0) or newer if (javaVersion.startsWith("1.") && javaVersion.charAt(2) < '5') { final String message = "Robocode requires Java 5.0 (1.5.0) or newer.\n" + "Your system is currently running Java " + javaVersion + ".\n" + "If you have not installed (or activated) at least\n" + "JRE 5.0 or JDK 5.0, please do so."; JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE); System.err.println(message); System.exit(0); } // Set native look and feel try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Throwable t) {// For some reason Ubuntu 7 can cause a NullPointerException when trying to getting the LAF } File suggestedDir; AutoExtract extractor = new AutoExtract(); if (extractor.acceptLicense()) { if (argv.length == 1) { suggestedDir = new File(argv[0]); } else if (File.separatorChar == '\\') { suggestedDir = new File("c:\\robocode\\"); } else { suggestedDir = new File(System.getProperty("user.home") + File.separator + "robocode" + File.separator); } boolean done = false; while (!done) { int rc = JOptionPane.showConfirmDialog(null, "Robocode plugin will be installed in:\n" + suggestedDir + "\nIs this ok?", "Installing Robocode", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (rc == JOptionPane.YES_OPTION) { installDir = suggestedDir; done = true; } else if (rc == JOptionPane.NO_OPTION) { Object r = JOptionPane.showInputDialog(null, "Please type in the installation directory", "Installation Directory", JOptionPane.PLAIN_MESSAGE, null, null, suggestedDir); if (r == null) { JOptionPane.showMessageDialog(null, "Installation cancelled."); System.exit(0); } else { suggestedDir = new File(((String) r).trim()); } } else if (rc == JOptionPane.CANCEL_OPTION) { JOptionPane.showMessageDialog(null, "Installation cancelled."); System.exit(0); } } if (!installDir.exists()) { int rc = JOptionPane.showConfirmDialog(null, installDir.getPath() + "\ndoes not exist. Would you like to create it?", "Installing Robocode", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (rc == JOptionPane.YES_OPTION) { if (!installDir.exists() && !installDir.mkdirs()) { System.out.println("Can't create dir " + installDir); } } else { JOptionPane.showMessageDialog(null, "Installation cancelled."); System.exit(0); } } boolean rv = extractor.extract(installDir); if (!rv) { JOptionPane.showMessageDialog(null, extractor.message); } } else { JOptionPane.showMessageDialog(null, "Installation cancelled."); } // Delete the class file with the installer and it's parent folders in the robocode home dir if (installDir != null) { String installerPath = AutoExtract.class.getName().replaceAll("\\.", "/") + "$1.class"; deleteFileAndParentDirsIfEmpty(new File(installDir, installerPath)); } System.exit(0); } private static boolean deleteDir(File dir) { if (dir.isDirectory()) { for (File file : dir.listFiles()) { if (file.isDirectory()) { // Skip directories ending with ".data" if (file.getName().endsWith(".data")) { continue; } try { // Test for symlink and ignore. // Robocode won't create one, but just in case a user does... if (file.getCanonicalFile().getParentFile().equals(dir.getCanonicalFile())) { deleteDir(file); if (file.exists() && !file.delete()) { System.err.println("Can't delete: " + file); } } else { System.out.println("Warning: " + file + " may be a symlink. It has been ignored"); } } catch (IOException e) { System.out.println( "Warning: Cannot determine canonical file for " + file + ". It has been ignored"); } } else { if (file.exists() && !file.delete()) { System.err.println("Can't delete: " + file); } } } return dir.delete(); } return false; } /** * Deletes a file and afterwards deletes the parent directories that are empty. * * @param file the file or directory to delete * @return true if success */ private static boolean deleteFileAndParentDirsIfEmpty(final File file) { boolean wasDeleted = false; if (file != null && file.exists()) { if (file.isDirectory()) { wasDeleted = deleteDir(file); } else { wasDeleted = file.delete(); File parent = file; while (wasDeleted && (parent = parent.getParentFile()) != null) { // Delete parent directory, but only if it is empty File[] files = parent.listFiles(); if (files != null && files.length == 0) { wasDeleted = deleteDir(parent); } else { wasDeleted = false; } } } } return wasDeleted; } }