# This patch file was generated by NetBeans IDE # Following Index: paths are relative to: /home/matthias/src/core-main # This patch can be applied using context Tools: Patch action on respective folder. # It uses platform neutral UTF-8 encoding and \n newlines. # Above lines and this line are ignored by the patching process. Index: db.core/src/org/netbeans/modules/db/sql/history/SQLHistoryManager.java --- db.core/src/org/netbeans/modules/db/sql/history/SQLHistoryManager.java Base (BASE) +++ db.core/src/org/netbeans/modules/db/sql/history/SQLHistoryManager.java Locally Modified (Based On LOCAL) @@ -52,14 +52,16 @@ import javax.xml.bind.Unmarshaller; 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; /** * * @author John Baker */ -public class SQLHistoryManager { +public class SQLHistoryManager { JAXBContext context; private static final String SQL_HISTORY_DIRECTORY = "Databases/SQLHISTORY"; // NOI18N @@ -70,6 +72,10 @@ private static SQLHistoryManager _instance = null; private static final Logger LOGGER = Logger.getLogger(SQLHistoryEntry.class.getName()); private SQLHistory sqlHistory; + private static final RequestProcessor RP = new RequestProcessor(SQLHistoryManager.class.getName(), 1, false, false); + private final RequestProcessor.Task SAVER = RP.create(new Saver()); + // Time between call to save and real save - usefull to accumulate before save + private static final int SAVE_DELAY = 5 * 1000; protected SQLHistoryManager() { ClassLoader orig = Thread.currentThread().getContextClassLoader(); @@ -82,12 +88,24 @@ } finally { Thread.currentThread().setContextClassLoader(orig); } + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + // If a save is pending on shutdown, enforce immediate write + if (SAVER.getDelay() > 0) { + SAVER.schedule(0); + SAVER.waitFinished(); } + } + }); + } public static SQLHistoryManager getInstance() { + synchronized (SQLHistoryManager.class) { if (_instance == null) { _instance = new SQLHistoryManager(); } + } return _instance; } @@ -105,7 +123,7 @@ FileObject historyRoot = historyRootDir.getFileObject(getHistoryFilename()); if (historyRoot != null || create) { - if(historyRoot == null) { + if (historyRoot == null) { historyRoot = historyRootDir.createData(getHistoryFilename()); } result = historyRoot; @@ -150,17 +168,51 @@ } public void save() { + // On call to save schedule real saving, as save is a oftem calleed + // method, this can bundle multiple saves into one write + // + // There is an potential for a dataloss in case of a forced shutdown + // of the jvm, but this is considered acceptable (normal shutdown + // is catered for by a shutdown hook) + if (SAVER.getDelay() == 0) { + SAVER.schedule(SAVE_DELAY); + } + } + + public SQLHistory getSQLHistory() { + return sqlHistory; + } + + private class Saver implements Runnable { + + @Override + public void run() { try { + final FileObject targetFile = getHistoryRoot(true); + targetFile.getFileSystem().runAtomicAction(new FileSystem.AtomicAction() { + + @Override + public void run() throws IOException { + OutputStream os = null; + try { Marshaller marshaller = context.createMarshaller(); - OutputStream os = getHistoryRoot(true).getOutputStream(); + os = targetFile.getOutputStream(); marshaller.marshal(sqlHistory, os); - os.close(); } catch (Exception ex) { LOGGER.log(Level.INFO, ex.getMessage()); + } finally { + try { + if (os != null) { + os.close(); } + } catch (IOException ex) { } - - public SQLHistory getSQLHistory() { - return sqlHistory; } } + }); + } catch (IOException ex) { + LOGGER.log(Level.INFO, ex.getMessage()); + } + } + } +} Index: db.core/test/unit/src/org/netbeans/modules/db/sql/history/SQLHistoryPersistenceManagerTest.java --- db.core/test/unit/src/org/netbeans/modules/db/sql/history/SQLHistoryPersistenceManagerTest.java Base (BASE) +++ db.core/test/unit/src/org/netbeans/modules/db/sql/history/SQLHistoryPersistenceManagerTest.java Locally Modified (Based On LOCAL) @@ -63,12 +63,6 @@ super(testName); } - /** Called before every test case. */ - @Override - public void setUp() { - System.out.println("######## " + getName() + " #######"); - } - /** Called after every test case. */ @Override public void tearDown() throws IOException { @@ -94,9 +88,21 @@ } }; + // History does not yet exists as file + assert (testableManager.getHistoryRoot(false) == null); testableManager.getSQLHistory().add(new SQLHistoryEntry("jdbc:// mysql", "select * from TRAVEL.PERSON", Calendar.getInstance().getTime())); + // History does not yet exists as file + testableManager.save(); + assert (testableManager.getHistoryRoot(false) == null); testableManager.getSQLHistory().add(new SQLHistoryEntry("jdbc:// oracle", "select * from PERSON", Calendar.getInstance().getTime())); + // History does not yet exists as file testableManager.save(); + assert (testableManager.getHistoryRoot(false) == null); + // Enforce writing of history + Thread.sleep(6 * 1000); + // History file need to exist now! + assert (testableManager.getHistoryRoot(false) != null); + assert (testableManager.getHistoryRoot(false).isData()); } /** Tests parsing of date format. */