You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by ma...@apache.org on 2022/03/26 16:52:20 UTC

[netbeans] branch master updated: SQL History loss: Reimplement atomatic saving by writing to tempfile and moving to target (#3868)

This is an automated email from the ASF dual-hosted git repository.

matthiasblaesing pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git


The following commit(s) were added to refs/heads/master by this push:
     new 96bd7c3  SQL History loss: Reimplement atomatic saving by writing to tempfile and moving to target (#3868)
96bd7c3 is described below

commit 96bd7c3e50c041b4666241776f9a257923018f43
Author: Matthias Bläsing <mb...@doppel-helix.eu>
AuthorDate: Sat Mar 26 17:52:04 2022 +0100

    SQL History loss: Reimplement atomatic saving by writing to tempfile and moving to target (#3868)
    
    It was observed, that sometimes the sql history was lost and it is
    assumed, that writing the new history began (emptying the file as the
    first step) and was not completed. By using a temporary file, the file
    is either written completely or not. It is assumed, that a move
    operation inside the same directory is atomic on all OSes.
---
 .../modules/db/sql/history/SQLHistoryManager.java  | 108 +++++++++++----------
 1 file changed, 56 insertions(+), 52 deletions(-)

diff --git a/ide/db.core/src/org/netbeans/modules/db/sql/history/SQLHistoryManager.java b/ide/db.core/src/org/netbeans/modules/db/sql/history/SQLHistoryManager.java
index 836a47a..251b16c 100644
--- a/ide/db.core/src/org/netbeans/modules/db/sql/history/SQLHistoryManager.java
+++ b/ide/db.core/src/org/netbeans/modules/db/sql/history/SQLHistoryManager.java
@@ -22,6 +22,9 @@ import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 import javax.xml.parsers.DocumentBuilder;
@@ -32,7 +35,6 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import org.netbeans.modules.db.sql.execute.ui.SQLHistoryPanel;
 import org.openide.filesystems.FileObject;
-import org.openide.filesystems.FileSystem;
 import org.openide.filesystems.FileUtil;
 import org.openide.util.NbPreferences;
 import org.openide.util.RequestProcessor;
@@ -50,9 +52,11 @@ public class SQLHistoryManager  {
     public static final int DEFAULT_SQL_STATEMENTS_SAVED_FOR_HISTORY = 100;
     public static final int MAX_SQL_STATEMENTS_SAVED_FOR_HISTORY = 10000;
     public static final String PROP_SAVED = "saved"; //NOI18N
-    
+
     private static final String SQL_HISTORY_DIRECTORY = "Databases/SQLHISTORY"; // NOI18N
-    private static final String SQL_HISTORY_FILE = "sql_history.xml"; // NOI18N
+    private static final String SQL_HISTORY_BASE = "sql_history"; // NOI18N
+    private static final String SQL_HISTORY_EXT = "xml"; // NOI18N
+    private static final String SQL_HISTORY_FILE = SQL_HISTORY_BASE + "." + SQL_HISTORY_EXT; // NOI18N
     private static final String TAG_HISTORY = "history"; // NOI18N
     private static final String TAG_SQL = "sql"; // NOI18N
     private static final String ATTR_DATE = "date"; // NOI18N
@@ -62,14 +66,14 @@ public class SQLHistoryManager  {
     private static final RequestProcessor RP = new RequestProcessor(
             SQLHistoryManager.class.getName(), 1, false, false);
     // Time between call to save and real save - usefull to accumulate before save
-    private static final int SAVE_DELAY = 5 * 1000;    
-    
-    private static SQLHistoryManager _instance = null;    
-    
+    private static final int SAVE_DELAY = 5 * 1000;
+
+    private static SQLHistoryManager _instance = null;
+
     private final RequestProcessor.Task SAVER = RP.create(new Saver());
     private final PropertyChangeSupport PROPERTY_CHANGE_SUPPORT =
             new PropertyChangeSupport(this);
-    
+
     private SQLHistory sqlHistory;
 
     protected SQLHistoryManager() {
@@ -85,11 +89,11 @@ public class SQLHistoryManager  {
             }
         });
     }
-    
+
     public static synchronized SQLHistoryManager getInstance() {
         if (_instance == null) {
-            _instance = new SQLHistoryManager();                    
-        } 
+            _instance = new SQLHistoryManager();
+        }
         return _instance;
     }
 
@@ -115,18 +119,18 @@ public class SQLHistoryManager  {
         }
         return result;
     }
-    
+
     protected FileObject getConfigRoot() {
         return FileUtil.getConfigRoot();
     }
-    
+
     protected String getRelativeHistoryPath() {
         return SQL_HISTORY_DIRECTORY;
     }
 
     protected String getHistoryFilename() {
         return SQL_HISTORY_FILE;
-                }
+    }
 
     public void setListSize(int listSize) {
         NbPreferences.forModule(SQLHistoryPanel.class).putInt("OPT_SQL_STATEMENTS_SAVED_FOR_HISTORY", listSize);
@@ -157,15 +161,15 @@ public class SQLHistoryManager  {
                     }
                 }
             } else {
-                
+
             }
         } catch (IOException | ParserConfigurationException | SAXException ex) {
             sqlHistory = new SQLHistory();
-            LOGGER.log(Level.INFO, ex.getMessage());
+            LOGGER.log(Level.WARNING, ex.getMessage());
         }
         sqlHistory.setHistoryLimit(getListSize());
     }
-    
+
     public void save() {
         // On call to save schedule real saving, as save is a often calleed
         // method, this can bundle multiple saves into one write.
@@ -204,43 +208,43 @@ public class SQLHistoryManager  {
 
         @Override
         public void run() {
+            Path tempfile = null;
             try {
-                final FileObject targetFile = getHistoryRoot(true);
-                targetFile.getFileSystem().
-                        runAtomicAction(new FileSystem.AtomicAction() {
-                    @Override
-                    public void run() throws IOException {
-                        ;
-                        try (OutputStream os = targetFile.getOutputStream()) {
-                            XMLStreamWriter xsw = XMLOutputFactory
-                                    .newInstance()
-                                    .createXMLStreamWriter(os);
-                            
-                            xsw.writeStartDocument();
-                            xsw.writeCharacters(CONTENT_NEWLINE);
-                            xsw.writeStartElement(TAG_HISTORY);
-                            xsw.writeCharacters(CONTENT_NEWLINE);
-                            for(SQLHistoryEntry sqe: sqlHistory) {
-                                xsw.writeStartElement(TAG_SQL);
-                                xsw.writeAttribute(ATTR_DATE, sqe.getDateXMLVariant());
-                                xsw.writeAttribute(ATTR_URL, sqe.getUrl());
-                                xsw.writeCharacters(sqe.getSql());
-                                xsw.writeEndElement();
-                                xsw.writeCharacters(CONTENT_NEWLINE);
-                            }
-                            xsw.writeEndElement();
-                            xsw.flush();
-                            xsw.close();
-                        } catch (IOException | XMLStreamException ex) {
-                            LOGGER.log(Level.INFO, ex.getMessage(), ex);
-                        } finally {
-                            PROPERTY_CHANGE_SUPPORT.firePropertyChange(
-                                    PROP_SAVED, null, null);
-                        }
+                final FileObject targetFileObject = getHistoryRoot(true);
+                final Path targetFile = FileUtil.toFile(targetFileObject).toPath();
+                tempfile = Files.createTempFile(targetFile.getParent(), SQL_HISTORY_BASE, SQL_HISTORY_EXT);
+                try ( OutputStream os = Files.newOutputStream(tempfile)) {
+                    XMLStreamWriter xsw = XMLOutputFactory
+                            .newInstance()
+                            .createXMLStreamWriter(os);
+
+                    xsw.writeStartDocument();
+                    xsw.writeCharacters(CONTENT_NEWLINE);
+                    xsw.writeStartElement(TAG_HISTORY);
+                    xsw.writeCharacters(CONTENT_NEWLINE);
+                    for (SQLHistoryEntry sqe : sqlHistory) {
+                        xsw.writeStartElement(TAG_SQL);
+                        xsw.writeAttribute(ATTR_DATE, sqe.getDateXMLVariant());
+                        xsw.writeAttribute(ATTR_URL, sqe.getUrl());
+                        xsw.writeCharacters(sqe.getSql());
+                        xsw.writeEndElement();
+                        xsw.writeCharacters(CONTENT_NEWLINE);
+                    }
+                    xsw.writeEndElement();
+                    xsw.flush();
+                    xsw.close();
+                }
+                Files.move(tempfile, targetFile, StandardCopyOption.REPLACE_EXISTING);
+                PROPERTY_CHANGE_SUPPORT.firePropertyChange(PROP_SAVED, null, null);
+            } catch (IOException | XMLStreamException ex) {
+                LOGGER.log(Level.WARNING, null, ex);
+                if(tempfile != null && Files.exists(tempfile)) {
+                    try {
+                        Files.delete(tempfile);
+                    } catch (IOException ex1) {
+                        LOGGER.log(Level.INFO, "Failed to cleanup temp file", ex1);
                     }
-                });
-            } catch (IOException ex) {
-                LOGGER.log(Level.INFO, ex.getMessage());
+                }
             }
         }
     }

---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists