/* * eXist Open Source Native XML Database * Copyright (C) 2008-2010 The eXist Project * http://exist-db.org * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * $Id$ */ package org.exist.backup; import org.exist.EXistException; import org.exist.storage.BrokerPool; import org.exist.storage.DBBroker; import org.exist.util.Configuration; import org.exist.util.MimeTable; import org.exist.util.MimeType; import org.exist.util.SystemExitCodes; import org.exist.xquery.TerminatedException; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; import java.util.Optional; import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileFilter; import org.exist.security.PermissionDeniedException; import static java.nio.charset.StandardCharsets.UTF_8; /** * DOCUMENT ME! * * @author wolf */ public class ExportGUI extends javax.swing.JFrame { private static final long serialVersionUID = -8104424554660744639L; private BrokerPool pool = null; private int documentCount = 0; private PrintWriter logWriter = null; // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton btnChangeDir; private javax.swing.JButton btnConfSelect; private javax.swing.JLabel currentTask; private javax.swing.JTextField dbConfig; private javax.swing.JButton exportBtn; private javax.swing.JCheckBox zipBtn; private javax.swing.JCheckBox incrementalBtn; private JCheckBox directAccessBtn; private javax.swing.JCheckBox scanBtn; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JMenu jMenu1; private javax.swing.JMenuBar jMenuBar1; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JToolBar jToolBar1; private javax.swing.JMenuItem menuQuit; private javax.swing.JTextArea messages; private javax.swing.JTextField outputDir; private javax.swing.JProgressBar progress; private javax.swing.JButton startBtn; // End of variables declaration//GEN-END:variables /** * Creates new form CheckerGUI. */ public ExportGUI() { super("Consistency Check and Repair"); initComponents(); final String existHome = System.getProperty("exist.home", "./"); final Path home = Paths.get(existHome).normalize(); dbConfig.setText(home.resolve("conf.xml").toAbsolutePath().toString()); outputDir.setText(home.resolve("export").toAbsolutePath().toString()); } protected boolean checkOutputDir() { final Path dir = Paths.get(outputDir.getText()).normalize(); if (!Files.exists(dir)) { if (JOptionPane.showConfirmDialog(this, "The output directory " + dir.toAbsolutePath().toString() + " does not exist. Create it?", "Confirm", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { try { Files.createDirectories(dir); } catch (final IOException e) { JOptionPane.showMessageDialog(this, "Could not create output dir: " + dir.toAbsolutePath().toString(), "Configuration Error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); System.err.println("ERROR: Failed to create output dir: " + e.getMessage()); } } else { return false; } } return true; } protected boolean startDB() { if (pool != null) { return true; } final Path confFile = Paths.get(dbConfig.getText()).normalize(); if (!(Files.exists(confFile) && Files.isReadable(confFile))) { JOptionPane.showMessageDialog(this, "The selected database configuration file " + confFile.toAbsolutePath().toString() + " does not exist or is not readable.", "Configuration Error", JOptionPane.ERROR_MESSAGE); return false; } try { final Configuration config = new Configuration(confFile.toAbsolutePath().toString(), Optional.empty()); BrokerPool.configure(1, 5, config); pool = BrokerPool.getInstance(); return true; } catch (final Exception e) { JOptionPane.showMessageDialog(this, "Could not start the database instance. Please remember\n" + "that this tool tries to launch an embedded db instance. No other db instance should\n" + "be running on the same data.", "DB Error", JOptionPane.ERROR_MESSAGE); e.printStackTrace(); System.err.println("ERROR: Failed to open database: " + e.getMessage()); } return false; } /** * This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ // <editor-fold defaultstate="collapsed" desc="Generated // Code">//GEN-BEGIN:initComponents private void initComponents() { java.awt.GridBagConstraints gridBagConstraints; currentTask = new javax.swing.JLabel(); progress = new javax.swing.JProgressBar(); jScrollPane2 = new javax.swing.JScrollPane(); messages = new javax.swing.JTextArea(); jToolBar1 = new javax.swing.JToolBar(); startBtn = new javax.swing.JButton(); exportBtn = new javax.swing.JButton(); incrementalBtn = new JCheckBox("Incremental"); scanBtn = new JCheckBox("Scan docs"); directAccessBtn = new JCheckBox("Direct access"); zipBtn = new JCheckBox("Create ZIP"); zipBtn.setSelected(true); outputDir = new javax.swing.JTextField(); jLabel1 = new javax.swing.JLabel(); btnChangeDir = new javax.swing.JButton(); dbConfig = new javax.swing.JTextField(); jLabel2 = new javax.swing.JLabel(); btnConfSelect = new javax.swing.JButton(); jMenuBar1 = new javax.swing.JMenuBar(); jMenu1 = new javax.swing.JMenu(); menuQuit = new javax.swing.JMenuItem(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); addWindowListener(new java.awt.event.WindowAdapter() { public void windowClosed(java.awt.event.WindowEvent evt) { formWindowClosed(evt); } }); getContentPane().setLayout(new java.awt.GridBagLayout()); currentTask.setText(" "); currentTask.setMinimumSize(new java.awt.Dimension(0, 25)); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 3; gridBagConstraints.gridwidth = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(currentTask, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 4; gridBagConstraints.gridwidth = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(progress, gridBagConstraints); jScrollPane2.setBorder(javax.swing.BorderFactory.createTitledBorder("Messages")); jScrollPane2.setPreferredSize(new java.awt.Dimension(400, 200)); messages.setColumns(20); messages.setLineWrap(true); messages.setRows(5); messages.setBorder(javax.swing.BorderFactory.createEmptyBorder(1, 1, 1, 1)); jScrollPane2.setViewportView(messages); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 5; gridBagConstraints.gridwidth = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHWEST; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(jScrollPane2, gridBagConstraints); jToolBar1.setRollover(true); startBtn.setText("Check"); startBtn.setFocusable(false); startBtn.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); startBtn.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); startBtn.addActionListener(this::startBtncheck); jToolBar1.add(startBtn); exportBtn.setText("Check & Export"); exportBtn.setFocusable(false); exportBtn.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); exportBtn.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); exportBtn.addActionListener(this::exportBtnActionPerformed); jToolBar1.add(exportBtn); jToolBar1.add(incrementalBtn); scanBtn.setSelected(true); scanBtn.setToolTipText("Perform additional checks; scans every XML document"); jToolBar1.add(scanBtn); directAccessBtn.setToolTipText("Bypass collection index by scanning collection store"); jToolBar1.add(directAccessBtn); jToolBar1.add(zipBtn); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridwidth = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; getContentPane().add(jToolBar1, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(outputDir, gridBagConstraints); jLabel1.setText("Output Directory:"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(jLabel1, gridBagConstraints); btnChangeDir.setText("Change"); btnChangeDir.addActionListener(this::btnChangeDirActionPerformed); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 2; gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(btnChangeDir, gridBagConstraints); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(dbConfig, gridBagConstraints); jLabel2.setText("DB Configuration:"); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(jLabel2, gridBagConstraints); btnConfSelect.setText("Select"); btnConfSelect.setMaximumSize(new java.awt.Dimension(75, 24)); btnConfSelect.setMinimumSize(new java.awt.Dimension(75, 24)); btnConfSelect.setPreferredSize(new java.awt.Dimension(75, 24)); btnConfSelect.addActionListener(this::btnConfSelectActionPerformed); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 1; gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST; gridBagConstraints.insets = new java.awt.Insets(5, 5, 5, 5); getContentPane().add(btnConfSelect, gridBagConstraints); jMenu1.setText("File"); menuQuit.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_Q, java.awt.event.InputEvent.CTRL_MASK)); menuQuit.setText("Quit"); menuQuit.addActionListener(this::menuQuitActionPerformed); jMenu1.add(menuQuit); jMenuBar1.add(jMenu1); setJMenuBar(jMenuBar1); pack(); } // </editor-fold>//GEN-END:initComponents private void formWindowClosed(java.awt.event.WindowEvent evt) { // GEN-FIRST:event_formWindowClosed BrokerPool.stopAll(false); } // GEN-LAST:event_formWindowClosed private void startBtncheck(java.awt.event.ActionEvent evt) { // GEN-FIRST:event_startBtncheck if (!checkOutputDir()) { return; } final Runnable checkRun = () -> { openLog(outputDir.getText()); try { checkDB(); } finally { closeLog(); } }; new Thread(checkRun).start(); } // GEN-LAST:event_startBtncheck private void exportBtnActionPerformed(java.awt.event.ActionEvent evt) { // GEN-FIRST:event_exportBtnActionPerformed if (!checkOutputDir()) { return; } final Runnable th = () -> { openLog(outputDir.getText()); try { currentTask.setText("Checking database consistency ..."); List<ErrorReport> errors = checkDB(); currentTask.setText("Exporting data ..."); exportDB(outputDir.getText(), errors); } finally { closeLog(); } }; new Thread(th).start(); } // GEN-LAST:event_exportBtnActionPerformed private void btnChangeDirActionPerformed(java.awt.event.ActionEvent evt) { // GEN-FIRST:event_btnChangeDirActionPerformed final Path dir = Paths.get(outputDir.getText()); final JFileChooser chooser = new JFileChooser(); chooser.setMultiSelectionEnabled(false); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); chooser.setSelectedFile(dir.resolve("export").toFile()); chooser.setCurrentDirectory(dir.toFile()); if (chooser.showDialog(this, "Export") == JFileChooser.APPROVE_OPTION) { outputDir.setText(chooser.getSelectedFile().getAbsolutePath()); } } // GEN-LAST:event_btnChangeDirActionPerformed private void menuQuitActionPerformed(java.awt.event.ActionEvent evt) { // GEN-FIRST:event_menuQuitActionPerformed BrokerPool.stopAll(false); System.exit(SystemExitCodes.OK_EXIT_CODE); } // GEN-LAST:event_menuQuitActionPerformed private void btnConfSelectActionPerformed(java.awt.event.ActionEvent evt) { // GEN-FIRST:event_btnConfSelectActionPerformed final Path dir = Paths.get(dbConfig.getText()).normalize().getParent(); final JFileChooser chooser = new JFileChooser(); chooser.setMultiSelectionEnabled(false); chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); chooser.setSelectedFile(dir.resolve("conf.xml").toFile()); chooser.setCurrentDirectory(dir.toFile()); chooser.setFileFilter(new FileFilter() { public boolean accept(File f) { if (f.isDirectory()) { return (true); } final MimeType mime = MimeTable.getInstance().getContentTypeFor(f.getName()); if (mime == null) { return false; } return mime.isXMLType(); } public String getDescription() { return ("Database XML configuration file"); } }); if (chooser.showDialog(this, "Select") == JFileChooser.APPROVE_OPTION) { dbConfig.setText(chooser.getSelectedFile().getAbsolutePath()); } } // GEN-LAST:event_btnConfSelectActionPerformed private void exportDB(String exportTarget, List<ErrorReport> errorList) { if (!startDB()) { return; } try (final DBBroker broker = pool.get(Optional.of(pool.getSecurityManager().getSystemSubject()))) { final SystemExport.StatusCallback callback = new SystemExport.StatusCallback() { public void startCollection(String path) { progress.setString(path); } public void startDocument(String name, int current, int count) { progress.setString(name); progress.setValue(progress.getValue() + 1); } public void error(String message, Throwable exception) { displayMessage(message); if (exception != null) { displayMessage(exception.toString()); } displayMessage("---------------------------------------------------"); } }; progress.setIndeterminate(false); progress.setValue(0); progress.setStringPainted(true); progress.setMinimum(0); progress.setMaximum(documentCount); Object[] selected = directAccessBtn.getSelectedObjects(); final boolean directAccess = (selected != null) && (selected[0] != null); selected = incrementalBtn.getSelectedObjects(); final boolean incremental = (selected != null) && (selected[0] != null); selected = zipBtn.getSelectedObjects(); final boolean zip = (selected != null) && (selected[0] != null); displayMessage("Starting export ..."); final long start = System.currentTimeMillis(); final SystemExport sysexport = new SystemExport(broker, callback, null, directAccess); final Path file = sysexport.export(exportTarget, incremental, zip, errorList); displayMessage("Export to " + file.toAbsolutePath().toString() + " completed successfully."); displayMessage("Export took " + (System.currentTimeMillis() - start) + "ms."); progress.setString(""); } catch (final EXistException e) { System.err.println("ERROR: Failed to retrieve database broker: " + e.getMessage()); } finally { progress.setValue(0); currentTask.setText(" "); } } private List<ErrorReport> checkDB() { if (!startDB()) { return (null); } try (final DBBroker broker = pool.get(Optional.of(pool.getSecurityManager().getSystemSubject()))) { Object[] selected = directAccessBtn.getSelectedObjects(); final boolean directAccess = (selected != null) && (selected[0] != null); selected = scanBtn.getSelectedObjects(); final boolean scan = (selected != null) && (selected[0] != null); final ConsistencyCheck checker = new ConsistencyCheck(broker, directAccess, scan); final org.exist.backup.ConsistencyCheck.ProgressCallback cb = new ConsistencyCheck.ProgressCallback() { public void startDocument(String path, int current, int count) { progress.setString(path); progress.setValue(progress.getValue() + 1); } public void error(ErrorReport error) { displayMessage(error.toString()); displayMessage("---------------------------------------------------"); } public void startCollection(String path) { progress.setString(path); } }; progress.setIndeterminate(true); messages.setText(""); displayMessage("Checking collections ..."); final List<ErrorReport> errors = checker.checkCollectionTree(cb); if (errors.size() == 0) { displayMessage("No errors found."); } else { displayMessage("Errors found."); } progress.setStringPainted(true); progress.setString("Counting documents ..."); documentCount = checker.getDocumentCount(); progress.setIndeterminate(false); progress.setValue(0); progress.setMinimum(0); progress.setMaximum(documentCount); displayMessage("Checking documents ..."); checker.checkDocuments(cb, errors); if (errors.size() == 0) { displayMessage("No errors found."); } else { displayMessage("Errors found."); } progress.setString(""); return (errors); } catch (final EXistException | PermissionDeniedException e) { System.err.println("ERROR: Failed to retrieve database broker: " + e.getMessage()); } catch (final TerminatedException e) { System.err.println("WARN: Check terminated by db."); } finally { progress.setValue(0); currentTask.setText(" "); } return (null); } public void displayMessage(String message) { messages.append(message + '\n'); messages.setCaretPosition(messages.getDocument().getLength()); if (logWriter != null) { logWriter.println(message); } } private void openLog(String dir) { final Path file = SystemExport.getUniqueFile("report", ".log", dir); try { logWriter = new PrintWriter(Files.newBufferedWriter(file, UTF_8)); } catch(final IOException e) { System.err.println("ERROR: failed to create log file"); } } private void closeLog() { if (logWriter != null) { logWriter.close(); } } /** * DOCUMENT ME! * * @param args the command line arguments */ public static void main(String[] args) { java.awt.EventQueue.invokeLater(() -> new ExportGUI().setVisible(true)); } }