8133777: Use file based persistence for history instead of preferences
authorsundar
Tue, 18 Aug 2015 11:40:18 +0530
changeset 32242 bbc1ebbb5cdc
parent 32241 f6228e27ad97
child 32243 422ccb5d8437
8133777: Use file based persistence for history instead of preferences Reviewed-by: attila, mhaupt
nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Console.java
nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java
nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Console.java	Mon Aug 17 18:36:28 2015 +0530
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Console.java	Tue Aug 18 11:40:18 2015 +0530
@@ -25,6 +25,7 @@
 
 package jdk.nashorn.tools.jjs;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintStream;
@@ -33,75 +34,41 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
-import java.util.prefs.BackingStoreException;
-import java.util.prefs.Preferences;
 import jdk.internal.jline.console.ConsoleReader;
 import jdk.internal.jline.console.completer.Completer;
-import jdk.internal.jline.console.history.History.Entry;
-import jdk.internal.jline.console.history.MemoryHistory;
+import jdk.internal.jline.console.history.FileHistory;
 
 class Console implements AutoCloseable {
     private final ConsoleReader in;
-    private final PersistentHistory history;
+    private final FileHistory history;
 
-    Console(final InputStream cmdin, final PrintStream cmdout, final Preferences prefs,
+    Console(final InputStream cmdin, final PrintStream cmdout, final File historyFile,
             final Completer completer) throws IOException {
         in = new ConsoleReader(cmdin, cmdout);
         in.setExpandEvents(false);
         in.setHandleUserInterrupt(true);
         in.setBellEnabled(true);
-        in.setHistory(history = new PersistentHistory(prefs));
+        in.setHistory(history = new FileHistory(historyFile));
         in.addCompleter(completer);
-        Runtime.getRuntime().addShutdownHook(new Thread(()->close()));
+        Runtime.getRuntime().addShutdownHook(new Thread((Runnable)this::saveHistory));
     }
 
     String readLine(final String prompt) throws IOException {
         return in.readLine(prompt);
     }
 
-
     @Override
     public void close() {
-        history.save();
+        saveHistory();
     }
 
-    public static class PersistentHistory extends MemoryHistory {
-
-        private final Preferences prefs;
-
-        protected PersistentHistory(final Preferences prefs) {
-            this.prefs = prefs;
-            load();
-        }
-
-        private static final String HISTORY_LINE_PREFIX = "HISTORY_LINE_";
+    private void saveHistory() {
+        try {
+            getHistory().flush();
+        } catch (final IOException exp) {}
+    }
 
-        public final void load() {
-            try {
-                final List<String> keys = new ArrayList<>(Arrays.asList(prefs.keys()));
-                Collections.sort(keys);
-                for (String key : keys) {
-                    if (!key.startsWith(HISTORY_LINE_PREFIX))
-                        continue;
-                    CharSequence line = prefs.get(key, "");
-                    add(line);
-                }
-            } catch (BackingStoreException ex) {
-                throw new IllegalStateException(ex);
-            }
-        }
-
-        public void save() {
-            Iterator<Entry> entries = iterator();
-            if (entries.hasNext()) {
-                int len = (int) Math.ceil(Math.log10(size()+1));
-                String format = HISTORY_LINE_PREFIX + "%0" + len + "d";
-                while (entries.hasNext()) {
-                    Entry entry = entries.next();
-                    prefs.put(String.format(format, entry.index()), entry.value().toString());
-                }
-            }
-        }
-
+    FileHistory getHistory() {
+        return (FileHistory) in.getHistory();
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/HistoryObject.java	Tue Aug 18 11:40:18 2015 +0530
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package jdk.nashorn.tools.jjs;
+
+import java.io.IOException;
+import java.util.function.Function;
+import jdk.internal.jline.console.history.FileHistory;
+import jdk.internal.jline.console.history.History;
+import jdk.nashorn.api.scripting.AbstractJSObject;
+import jdk.nashorn.api.scripting.JSObject;
+import jdk.nashorn.internal.runtime.JSType;
+import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
+
+/*
+ * A script friendly object that exposes history of commands to scripts.
+ */
+final class HistoryObject extends AbstractJSObject {
+    private final FileHistory hist;
+
+    HistoryObject(final FileHistory hist) {
+        this.hist = hist;
+    }
+
+    @Override
+    public Object getMember(final String name) {
+        switch (name) {
+            case "clear":
+                return (Runnable)hist::clear;
+            case "forEach":
+                return (Function<JSObject, Object>)this::iterate;
+            case "print":
+                return (Runnable)this::print;
+            case "size":
+                return hist.size();
+        }
+        return UNDEFINED;
+    }
+
+    @Override
+    public Object getDefaultValue(final Class<?> hint) {
+        if (hint == String.class) {
+            return toString();
+        }
+        return UNDEFINED;
+    }
+
+    @Override
+    public String toString() {
+        return "[object history]";
+    }
+
+    private void print() {
+        for (History.Entry e : hist) {
+            System.out.println(e.value());
+        }
+    }
+
+    private Object iterate(final JSObject func) {
+        for (History.Entry e : hist) {
+            if (JSType.toBoolean(func.call(this, e.value().toString()))) {
+                break; // return true from callback to skip iteration
+            }
+        }
+        return UNDEFINED;
+    }
+}
--- a/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java	Mon Aug 17 18:36:28 2015 +0530
+++ b/nashorn/src/jdk.scripting.nashorn.shell/share/classes/jdk/nashorn/tools/jjs/Main.java	Tue Aug 18 11:40:18 2015 +0530
@@ -26,12 +26,12 @@
 package jdk.nashorn.tools.jjs;
 
 import java.io.BufferedReader;
+import java.io.File;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PrintWriter;
-import java.util.prefs.Preferences;
 import jdk.internal.jline.console.completer.Completer;
 import jdk.internal.jline.console.UserInterruptException;
 import jdk.nashorn.api.scripting.NashornException;
@@ -48,7 +48,8 @@
 public final class Main extends Shell {
     private Main() {}
 
-    static final Preferences PREFS = Preferences.userRoot().node("tool/jjs");
+    // file where history is persisted.
+    private static final File HIST_FILE = new File(new File(System.getProperty("user.home")), ".jjs.history");
 
     /**
      * Main entry point with the default input, output and error streams.
@@ -99,12 +100,14 @@
         final boolean globalChanged = (oldGlobal != global);
         final Completer completer = new NashornCompleter(context, global);
 
-        try (final Console in = new Console(System.in, System.out, PREFS, completer)) {
+        try (final Console in = new Console(System.in, System.out, HIST_FILE, completer)) {
             if (globalChanged) {
                 Context.setGlobal(global);
             }
 
             global.addShellBuiltins();
+            // expose history object for reflecting on command line history
+            global.put("history", new HistoryObject(in.getHistory()), false);
 
             while (true) {
                 String source = "";