package gate.mimir.util; import java.awt.BorderLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.HeadlessException; import java.awt.Insets; import java.awt.event.ActionEvent; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Map.Entry; import java.util.Properties; import java.util.SortedMap; import java.util.TreeMap; import javax.swing.AbstractAction; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JTextField; import javax.swing.SwingUtilities; import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.tools.tar.TarEntry; import org.apache.tools.tar.TarInputStream; public class UnpackWizard extends JFrame { protected JTextField outDirTextField; protected JProgressBar progressBar; protected Properties indexProperties; protected class SelectOutputDirectoryAction extends AbstractAction { private JFileChooser fileChooser; public SelectOutputDirectoryAction() { super("Select"); putValue(SMALL_ICON, new ImageIcon( UnpackWizard.class.getClassLoader().getResource( "gate/mimir/resources/open-folder.png"))); putValue(SHORT_DESCRIPTION, "Choose a directory where the Mímir index " + "should be unpacked to"); } @Override public void actionPerformed(ActionEvent e) { if(fileChooser == null) { fileChooser = new JFileChooser(); fileChooser.setMultiSelectionEnabled(false); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); } int res = fileChooser.showOpenDialog(UnpackWizard.this); if(res == JFileChooser.APPROVE_OPTION) { outDirTextField.setText(fileChooser.getSelectedFile().getAbsolutePath()); } } } protected class UnpackAction extends AbstractAction implements Runnable { public UnpackAction() { super("Extract Index"); putValue(SMALL_ICON, new ImageIcon( UnpackWizard.class.getClassLoader().getResource( "gate/mimir/resources/extract-archive.png"))); putValue(SHORT_DESCRIPTION, "Unpacks the index to the directory " + "chosen above."); } @Override public void actionPerformed(ActionEvent evt) { progressBar.setValue(0); setEnabled(false); // run the actual work from a new thread Thread otherThread = new Thread(this, "Unpack Thread"); otherThread.setPriority(Thread.MIN_PRIORITY); otherThread.start(); } /** * The unpack logic that gets executed in its own thread */ public void run() { try{ // check preconditions File outDir = new File(outDirTextField.getText()); if(!outDir.exists()) { SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(UnpackWizard.this, "The chosen directory does not exist!", "Directory Not Found Error", JOptionPane.ERROR_MESSAGE); } }); //... and stop the worker thread return; } if(!outDir.isDirectory()) { SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(UnpackWizard.this, "The chosen file is not a directory!", "Not a Directory Error", JOptionPane.ERROR_MESSAGE); } }); //... and stop the worker thread return; } if(!outDir.canWrite()) { SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(UnpackWizard.this, "Cannot write to the chosen directory!", "Directory Not Writeable Error", JOptionPane.ERROR_MESSAGE); } }); //... and stop the worker thread return; } // all ok -> do the extraction File archiveDir = new File("foo.bar").getAbsoluteFile().getParentFile(); URL baseUrl = UnpackWizard.class.getClassLoader().getResource( UnpackWizard.class.getName().replaceAll("\\.", "/") + ".class"); if(baseUrl.getProtocol().equals("jar")) { String jarPath = baseUrl.getPath(); jarPath = jarPath.substring(0, jarPath.indexOf('!')); if(jarPath.startsWith("file:")) { try { archiveDir = new File(new URI(jarPath)).getAbsoluteFile() .getParentFile(); } catch(URISyntaxException e1) { e1.printStackTrace(System.err); } } } SortedMap<Integer, String> inputFileNames = new TreeMap<Integer, String>(); for(Entry<Object, Object> anEntry : indexProperties.entrySet()) { String key = anEntry.getKey().toString(); if(key.startsWith("archive file ")) { inputFileNames.put(Integer.parseInt( key.substring("archive file ".length())), anEntry.getValue().toString()); } } byte[] buff = new byte[64 * 1024]; try { // check that all input files exist, and calculate the total size long bytesToWrite = 0; long bytesReported = 0; long bytesWritten = 0; for(String aFileName : inputFileNames.values()) { File aFile = new File(archiveDir, aFileName); if(aFile.exists()){ if(aFile.isFile()) { bytesToWrite += aFile.length(); } else { final File wrongFile = aFile; SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(UnpackWizard.this, "File \"" + wrongFile.getAbsolutePath() + "\" is not a standard file!", "Non Standard File Error", JOptionPane.ERROR_MESSAGE); } }); //... and stop the worker thread return; } } else { final File wrongFile = aFile; SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(UnpackWizard.this, "File \"" + wrongFile.getAbsolutePath() + "\" does not exist!", "File Not Found Error", JOptionPane.ERROR_MESSAGE); } }); //... and stop the worker thread return; } } MultiFileInputStream mfis = new MultiFileInputStream(archiveDir, inputFileNames.values().toArray(new String[inputFileNames.size()])); TarArchiveInputStream tis = new TarArchiveInputStream( new BufferedInputStream(mfis)); TarArchiveEntry tarEntry = tis.getNextTarEntry(); while(tarEntry != null) { File outFile = new File(outDir.getAbsolutePath() + "/" + tarEntry.getName()); if(outFile.exists()) { final File wrongFile = outFile; SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(UnpackWizard.this, "Was about to write out file \"" + wrongFile.getAbsolutePath() + "\" but it already " + "exists.\nPlease [re]move existing files out of the way " + "and try again.", "File Not Found Error", JOptionPane.ERROR_MESSAGE); } }); //... and stop the worker thread return; } if(tarEntry.isDirectory()) { outFile.getAbsoluteFile().mkdirs(); } else { outFile.getAbsoluteFile().getParentFile().mkdirs(); OutputStream os = new BufferedOutputStream(new FileOutputStream(outFile)); int len = tis.read(buff, 0, buff.length); while(len != -1) { os.write(buff, 0, len); bytesWritten += len; // every 10 MB update the progress bar if(bytesWritten - bytesReported > (10 * 1024 * 1024)) { bytesReported = bytesWritten; final int progress = (int)(bytesReported * 100 / bytesToWrite); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { progressBar.setValue(progress); } }); } // and read some more len = tis.read(buff, 0, buff.length); } os.close(); } tarEntry = tis.getNextTarEntry(); } long expectedCrc = 0; try { expectedCrc = Long.parseLong(indexProperties.getProperty("CRC32", "0")); } catch(NumberFormatException e) { System.err.println("Error while obtaining the expected CRC"); e.printStackTrace(System.err); } if(mfis.getCRC() == expectedCrc) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { progressBar.setValue(0); JOptionPane.showMessageDialog(UnpackWizard.this, "Extraction completed successfully!", "Done!", JOptionPane.INFORMATION_MESSAGE); } }); // and we're done return; } else { System.err.println("CRC Error: was expecting " + expectedCrc + " but got " + mfis.getCRC()); SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(0); JOptionPane.showMessageDialog(UnpackWizard.this, "CRC Error: the data extracted does not have the expected CRC!\n" + "You should probably delete the extracted files, as they are " + "likely to be invalid.", "CRC Error", JOptionPane.ERROR_MESSAGE); } }); // and stop the worker thread return; } } catch(final IOException e) { e.printStackTrace(System.err); SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(0); JOptionPane.showMessageDialog(UnpackWizard.this, "Input/Output Error: " + e.getLocalizedMessage(), "Input/Output Error", JOptionPane.ERROR_MESSAGE); } }); // and stop the worker thread return; } } finally { // make sure the action gets re-enabled SwingUtilities.invokeLater(new Runnable() { public void run() { progressBar.setValue(0); setEnabled(true); } }); } } } public UnpackWizard() throws HeadlessException { super("Unpack Mímir Index"); initLocalData(); initGui(); initListeners(); } protected void initLocalData() { // load the index properties InputStream propIs = UnpackWizard.class.getClassLoader().getResourceAsStream( "index-properties.xml"); if(propIs != null) { try { indexProperties = new Properties(); indexProperties.loadFromXML(propIs); } catch(Exception e) { // could not load the index properties System.err.println("Could not open archive properties."); e.printStackTrace(); indexProperties = null; } } } protected void initGui() { if(indexProperties != null){ setTitle("Index archive for index \"" + indexProperties.getProperty( "Index Name") + "\""); JPanel mainPanel = new JPanel(); mainPanel.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2)); mainPanel.setLayout(new GridBagLayout()); GridBagConstraints constraints = new GridBagConstraints(); constraints.insets = new Insets(2, 2, 2, 2); constraints.gridx = 0; constraints.gridy = 0; constraints.weightx = 0; constraints.weighty = 0; //logo constraints.anchor = GridBagConstraints.CENTER; constraints.gridheight = 4; ImageIcon icon = new ImageIcon(UnpackWizard.class.getClassLoader() .getResource("gate/mimir/resources/logo-mimir-archive.png")); mainPanel.add(new JLabel(icon), constraints); // label constraints.gridx = 1; constraints.gridheight = 1; constraints.anchor = GridBagConstraints.LINE_START; mainPanel.add(new JLabel("Output directory:"), constraints); // open button constraints.gridx = 2; constraints.gridheight = 2; mainPanel.add(new JButton(new SelectOutputDirectoryAction()), constraints); // text field constraints.gridy = 1; constraints.gridx = 1; constraints.gridheight = 1; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 1; outDirTextField = new JTextField(new File("").getParent(), 30); mainPanel.add(outDirTextField, constraints); constraints.weightx = 0; // unpack button constraints.gridy = 2; constraints.gridx = 1; constraints.gridwidth = 2; constraints.anchor = GridBagConstraints.CENTER; constraints.fill = GridBagConstraints.HORIZONTAL; mainPanel.add(new JButton(new UnpackAction()), constraints); //progress bar constraints.gridy = 3; constraints.gridx = 1; constraints.gridwidth = 2; progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100); progressBar.setValue(0); mainPanel.add(progressBar, constraints); getContentPane().add(mainPanel, BorderLayout.CENTER); } else { setTitle("Invalid JAR file"); JLabel errorLabel = new JLabel("<html>Could not open archive properties. <br />" + "Was there an error during download?</html>"); errorLabel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); getContentPane().add(errorLabel, BorderLayout.CENTER); } setIconImage(new ImageIcon(UnpackWizard.class.getClassLoader().getResource( "gate/mimir/resources/extract-archive.png")).getImage()); pack(); setMinimumSize(getPreferredSize()); // center the window setLocationRelativeTo(null); } protected void initListeners() { setDefaultCloseOperation(DISPOSE_ON_CLOSE); } public static void main(String[] args) { UnpackWizard unpackWiz = new UnpackWizard(); unpackWiz.setVisible(true); } }