Skip to content

Commit 4d49789

Browse files
snuyanzinjulianhyde
authored andcommitted
[SQLLINE-394] Make script engine configurable via new scriptEngine property
This compensates for the fact that Nashorn is not available on JDK 15 and higher. Close #422
1 parent 86bfb12 commit 4d49789

File tree

8 files changed

+121
-18
lines changed

8 files changed

+121
-18
lines changed

src/docbkx/manual.xml

+7
Original file line numberDiff line numberDiff line change
@@ -3507,6 +3507,13 @@ java.sql.SQLException: ORA-00942: table or view does not exist
35073507
Defaults to 0, which is interpreted as fetching all rows.
35083508
</para>
35093509
</sect1>
3510+
<sect1 id="setting_scriptengine">
3511+
<title>scriptengine</title>
3512+
<para>
3513+
A script engine to use to evaluate scripts for
3514+
instance from promptScript. Defaults to nashorn.
3515+
</para>
3516+
</sect1>
35103517
<sect1 id="setting_showcompletiondesc">
35113518
<title>showcompletiondesc</title>
35123519
<para>

src/main/java/sqlline/Application.java

+35
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
import java.util.Collection;
2020
import java.util.Collections;
2121
import java.util.HashMap;
22+
import java.util.HashSet;
2223
import java.util.List;
2324
import java.util.Locale;
2425
import java.util.Map;
2526
import java.util.Properties;
2627
import java.util.ServiceLoader;
2728
import java.util.Set;
2829
import java.util.TreeSet;
30+
import javax.script.ScriptEngineFactory;
31+
import javax.script.ScriptEngineManager;
2932

3033
import org.jline.builtins.Completers.FileNameCompleter;
3134
import org.jline.reader.Completer;
@@ -373,6 +376,38 @@ public String getDefaultInteractiveMode() {
373376
public Map<String, TableOutputFormatStyle> getName2TableOutputFormatStyle() {
374377
return BuiltInTableOutputFormatStyles.BY_NAME;
375378
}
379+
380+
/**
381+
* Override this method to modify available script engine names.
382+
*
383+
* <p>If method is not overridden, current set of engine names will
384+
* contain first non intersected values (ordered by abc).
385+
*
386+
* @return Set of available script engine names
387+
*/
388+
public Set<String> getAvailableScriptEngineNames() {
389+
final Set<String> result = new HashSet<>();
390+
final Map<String, Set<String>> fName2Aliases = new HashMap<>();
391+
final List<ScriptEngineFactory> factories =
392+
new ScriptEngineManager().getEngineFactories();
393+
for (ScriptEngineFactory factory: factories) {
394+
fName2Aliases.put(factory.getEngineName(),
395+
new HashSet<>(factory.getNames()));
396+
}
397+
for (Map.Entry<String, Set<String>> fEntry: fName2Aliases.entrySet()) {
398+
Set<String> aliases = new TreeSet<>(fEntry.getValue());
399+
for (Map.Entry<String, Set<String>> fEntry2: fName2Aliases.entrySet()) {
400+
if (fEntry.getKey().equals(fEntry2.getKey())) {
401+
continue;
402+
}
403+
aliases.removeAll(fEntry2.getValue());
404+
}
405+
if (!aliases.isEmpty()) {
406+
result.add(aliases.iterator().next());
407+
}
408+
}
409+
return result;
410+
}
376411
}
377412

378413
// End Application.java

src/main/java/sqlline/BuiltInProperty.java

+2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ public enum BuiltInProperty implements SqlLineProperty {
8787
RIGHT_PROMPT("rightPrompt", Type.STRING, ""),
8888
ROW_LIMIT("rowLimit", Type.INTEGER, 0),
8989
SHOW_ELAPSED_TIME("showElapsedTime", Type.BOOLEAN, true),
90+
SCRIPT_ENGINE("scriptEngine", Type.STRING, "nashorn", true, false,
91+
new Application().getAvailableScriptEngineNames()),
9092
SHOW_COMPLETION_DESCR("showCompletionDesc", Type.BOOLEAN, true),
9193
SHOW_HEADER("showHeader", Type.BOOLEAN, true),
9294
SHOW_LINE_NUMBERS("showLineNumbers", Type.BOOLEAN, false),

src/main/java/sqlline/PromptHandler.java

+34-15
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,26 @@ public class PromptHandler {
6161

6262
protected final SqlLine sqlLine;
6363

64-
static final Supplier<ScriptEngine> SCRIPT_ENGINE_SUPPLIER =
65-
new MemoizingSupplier<>(() -> {
66-
final ScriptEngineManager engineManager = new ScriptEngineManager();
67-
return engineManager.getEngineByName("nashorn");
68-
});
64+
final Supplier<ScriptEngine> scriptEngineSupplier =
65+
getEngineSupplier();
66+
67+
MemoizingSupplier<ScriptEngine> getEngineSupplier() {
68+
return new MemoizingSupplier<>(() -> {
69+
final ScriptEngineManager engineManager = new ScriptEngineManager();
70+
String engineName = sqlLine.getOpts().get(BuiltInProperty.SCRIPT_ENGINE);
71+
ScriptEngine scriptEngine = engineManager.getEngineByName(engineName);
72+
if (scriptEngine == null) {
73+
if (engineManager.getEngineFactories().isEmpty()) {
74+
sqlLine.error(sqlLine.loc("not-supported-script-engine-no-available",
75+
engineName));
76+
} else {
77+
sqlLine.error(sqlLine.loc("not-supported-script-engine",
78+
engineName, BuiltInProperty.SCRIPT_ENGINE.getAvailableValues()));
79+
}
80+
}
81+
return scriptEngine;
82+
});
83+
}
6984

7085
public PromptHandler(SqlLine sqlLine) {
7186
this.sqlLine = sqlLine;
@@ -112,16 +127,20 @@ public AttributedString getPrompt() {
112127
private String getPromptFromScript(SqlLine sqlLine,
113128
String promptScript) {
114129
try {
115-
final ScriptEngine engine = SCRIPT_ENGINE_SUPPLIER.get();
116-
final Bindings bindings = new SimpleBindings();
117-
final ConnectionMetadata meta = sqlLine.getConnectionMetadata();
118-
bindings.put("connectionIndex", meta.getIndex());
119-
bindings.put("databaseProductName", meta.getDatabaseProductName());
120-
bindings.put("userName", meta.getUserName());
121-
bindings.put("url", meta.getUrl());
122-
bindings.put("currentSchema", meta.getCurrentSchema());
123-
final Object o = engine.eval(promptScript, bindings);
124-
return String.valueOf(o);
130+
final ScriptEngine engine = scriptEngineSupplier.get();
131+
if (engine == null) {
132+
return ">";
133+
} else {
134+
final Bindings bindings = new SimpleBindings();
135+
final ConnectionMetadata meta = sqlLine.getConnectionMetadata();
136+
bindings.put("connectionIndex", meta.getIndex());
137+
bindings.put("databaseProductName", meta.getDatabaseProductName());
138+
bindings.put("userName", meta.getUserName());
139+
bindings.put("url", meta.getUrl());
140+
bindings.put("currentSchema", meta.getCurrentSchema());
141+
final Object o = engine.eval(promptScript, bindings);
142+
return String.valueOf(o);
143+
}
125144
} catch (ScriptException e) {
126145
e.printStackTrace();
127146
return ">";

src/main/java/sqlline/SqlLineOpts.java

+24-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import java.util.regex.Pattern;
2424
import java.util.regex.PatternSyntaxException;
2525
import java.util.stream.Collectors;
26+
import javax.script.ScriptEngine;
27+
import javax.script.ScriptEngineManager;
2628

2729
import org.jline.builtins.Completers;
2830
import org.jline.keymap.KeyMap;
@@ -75,6 +77,7 @@
7577
import static sqlline.BuiltInProperty.PROPERTIES_FILE;
7678
import static sqlline.BuiltInProperty.RIGHT_PROMPT;
7779
import static sqlline.BuiltInProperty.ROW_LIMIT;
80+
import static sqlline.BuiltInProperty.SCRIPT_ENGINE;
7881
import static sqlline.BuiltInProperty.SHOW_COMPLETION_DESCR;
7982
import static sqlline.BuiltInProperty.SHOW_ELAPSED_TIME;
8083
import static sqlline.BuiltInProperty.SHOW_HEADER;
@@ -130,6 +133,7 @@ public class SqlLineOpts implements Completer {
130133
put(NUMBER_FORMAT, SqlLineOpts.this::setNumberFormat);
131134
put(OUTPUT_FORMAT, SqlLineOpts.this::setOutputFormat);
132135
put(PROPERTIES_FILE, SqlLineOpts.this::setPropertiesFile);
136+
put(SCRIPT_ENGINE, SqlLineOpts.this::setScriptEngine);
133137
put(SHOW_COMPLETION_DESCR,
134138
SqlLineOpts.this::setShowCompletionDesc);
135139
put(TABLE_STYLE, SqlLineOpts.this::setTableStyle);
@@ -523,7 +527,8 @@ public void set(SqlLineProperty key, Object value) {
523527
? (String) value : String.valueOf(value);
524528
valueToSet = DEFAULT.equalsIgnoreCase(strValue)
525529
? key.defaultValue() : value;
526-
if (!key.getAvailableValues().isEmpty()
530+
if (!DEFAULT.equalsIgnoreCase(strValue)
531+
&& !key.getAvailableValues().isEmpty()
527532
&& !key.getAvailableValues().contains(valueToSet.toString())) {
528533
sqlLine.error(
529534
sqlLine.loc("unknown-value",
@@ -1083,6 +1088,24 @@ public Pattern getCompiledConfirmPattern() {
10831088
public String getHistoryFlags() {
10841089
return get(HISTORY_FLAGS);
10851090
}
1091+
1092+
public String getScriptEngine() {
1093+
return get(SCRIPT_ENGINE);
1094+
}
1095+
1096+
public void setScriptEngine(String engineName) {
1097+
if (DEFAULT.equalsIgnoreCase(engineName)) {
1098+
set(SCRIPT_ENGINE, SCRIPT_ENGINE.defaultValue());
1099+
return;
1100+
}
1101+
final ScriptEngineManager engineManager = new ScriptEngineManager();
1102+
ScriptEngine scriptEngine = engineManager.getEngineByName(engineName);
1103+
if (scriptEngine == null && engineManager.getEngineFactories().isEmpty()) {
1104+
sqlLine.error(sqlLine.loc("not-supported-script-engine-no-available",
1105+
engineName));
1106+
}
1107+
set(SCRIPT_ENGINE, engineName);
1108+
}
10861109
}
10871110

10881111
// End SqlLineOpts.java

src/main/resources/sqlline/SqlLine.properties

+4
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ column: Column
3737
new-size-after-resize: New size: height = {0}, width = {1}
3838
empty-value-not-supported: Empty value for type {0} not supported
3939
no-file: File {0} does not exist or is a directory
40+
not-supported-script-engine: Not found script engine "{0}", available values: {1}
41+
not-supported-script-engine-no-available: Not found script engine "{0}", no available script engines
4042

4143
jdbc-level: JDBC level
4244
compliant: Compliant
@@ -151,6 +153,7 @@ variables:\
151153
\nrightPrompt pattern Format right prompt\
152154
\nrowLimit integer Maximum number of rows returned from a query; zero\
153155
\n means no limit\
156+
\nscriptEngine String Script engine name\
154157
\nshowCompletionDesc true/false Display help for completions\
155158
\nshowElapsedTime true/false Display execution time when verbose\
156159
\nshowHeader true/false Show column names in query results\
@@ -340,6 +343,7 @@ cmd-usage: Usage: java sqlline.SqlLine \n \
340343
\ --autoCommit=[true/false] enable/disable automatic transaction commit\n \
341344
\ --readOnly=[true/false] enable/disable readonly connection\n \
342345
\ --verbose=[true/false] show verbose error messages and debug info\n \
346+
\ --scriptEngine=[string] script engine name\n \
343347
\ --showCompletionDesc=[true/false] display help for completions\n \
344348
\ --showLineNumbers=[true/false] show line numbers while multiline queries\n \
345349
\ --showTime=[true/false] display execution time when verbose\n \

src/main/resources/sqlline/manual.txt

+11
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ schemas
104104
schemas — List all the schemas in the database
105105
script
106106
script — Save executed commands to a file
107+
scriptengine
108+
scriptengine - script engine name
107109
set
108110
set — Set a preference
109111
showconfconnections
@@ -258,6 +260,8 @@ scan
258260
scan — Scan class path for JDBC drivers
259261
script
260262
script — Save executed commands to a file
263+
scriptengine
264+
scriptengine - script engine name
261265
set
262266
set — Set a preference
263267
showconfconnections
@@ -502,6 +506,8 @@ scan
502506
scan — Scan class path for JDBC drivers
503507
script
504508
script — Save executed commands to a file
509+
scriptengine
510+
scriptengine - script engine name
505511
set
506512
set — Set a preference
507513
showconfconnections
@@ -1223,6 +1229,7 @@ propertiesFile path File from which SQLLine reads properties on
12231229
rightPrompt pattern Format right prompt
12241230
rowLimit integer Maximum number of rows returned from a query; zero
12251231
means no limit
1232+
scriptEngine string Script engine name
12261233
showCompletionDesc true/false Display help for completions
12271234
showElapsedTime true/false Display execution time when verbose
12281235
showHeader true/false Show column names in query results
@@ -2411,6 +2418,10 @@ rowlimit
24112418

24122419
The maximum number of rows to fetch per query. Defaults to 0, which is interpreted as fetching all rows.
24132420

2421+
scriptengine
2422+
2423+
A script engine to use to evaluate scripts for instance from promptScript. Defaults to nashorn.
2424+
24142425
showheader
24152426

24162427
If true, display the names of the columns when displaying results. Defaults to true.

src/test/java/sqlline/PromptTest.java

+4-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import java.text.SimpleDateFormat;
1616
import java.util.Date;
1717
import java.util.Locale;
18+
import javax.script.ScriptEngineManager;
1819

1920
import org.jline.utils.AttributedString;
2021
import org.jline.utils.AttributedStringBuilder;
@@ -221,8 +222,9 @@ public void testPromptWithSchema() {
221222
* The `promptscript` property is broken on JDK 15 and higher</a>. */
222223
@Test
223224
public void testPromptScript() {
224-
Assumptions.assumeTrue(getJavaMajorVersion() < 15,
225-
"promptscript fails on JDK 15 and higher; "
225+
Assumptions.assumeTrue(
226+
!new ScriptEngineManager().getEngineFactories().isEmpty(),
227+
"promptscript fails if there is no script engines; "
226228
+ "see ");
227229

228230
sqlLine.getOpts().set(BuiltInProperty.PROMPT_SCRIPT, "'hel' + 'lo'");

0 commit comments

Comments
 (0)