Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jline3 and other console improvements #935

Open
wants to merge 46 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
bbe00c8
some work on jline3
mastercoms May 8, 2017
caee56b
jline3 working
mastercoms May 8, 2017
50807ae
Adjustments to jline3
mastercoms May 8, 2017
8564117
work on formatting
mastercoms May 9, 2017
af7dc17
adjustments to logger
mastercoms May 9, 2017
5e4f2bb
Use try with resources
mastercoms May 9, 2017
513aa89
try to use jline3 better, but there's no documentation :(
mastercoms May 9, 2017
d64f760
clean up line
mastercoms May 9, 2017
b4ba93e
fancy prompt/console formatting
mastercoms May 9, 2017
4cfb5a1
fix up prompt a bit
mastercoms May 9, 2017
a252158
merge conflicts
mastercoms May 10, 2017
2bdb7bf
input and terminal fixes
mastercoms May 10, 2017
04d9904
rewrite consolemanager
mastercoms May 10, 2017
ebd0ab2
Format logger, add support for basic Bukkit methods
mastercoms May 10, 2017
1df265f
early start for formatter
mastercoms May 10, 2017
7a72573
full bukkit repl
mastercoms May 10, 2017
5c3d4ab
strip color on dumb terminals
mastercoms May 10, 2017
bfcf555
try jansi
mastercoms May 10, 2017
1a4a474
fix styling
mastercoms May 10, 2017
e3e2943
Format class better so errors are more readable
mastercoms May 10, 2017
373e4a3
Fix using return statements
mastercoms May 10, 2017
1459cd9
Remove jansi
mastercoms May 10, 2017
7e55e31
Fix void repl not running
mastercoms May 10, 2017
033afc1
REPL Improvements
mastercoms May 11, 2017
f374568
Import net.glowstone package for convenience
mastercoms May 11, 2017
b80a454
Add missing semicolon in import statement
mastercoms May 11, 2017
dda61a5
Merge dev in jline3
mastercoms Jun 1, 2018
0a8f9a3
Sync jline3 to dev
mastercoms Jun 1, 2018
5e1c389
Update to jline 3.7.1
mastercoms Jun 1, 2018
fa1ad33
Clean up more
mastercoms Jun 1, 2018
e64a545
Reformat logback.xml
mastercoms Jun 1, 2018
7610133
Fix logging
mastercoms Jun 2, 2018
e848efd
i18n
mastercoms Jun 2, 2018
401938e
Console tasks
mastercoms Jun 2, 2018
6ccd6ee
Address review comments (partial)
Pr0methean Jun 6, 2018
09c7160
Merge branch 'dev' into jline3
Pr0methean Jun 6, 2018
b51a978
Console commands
Pr0methean Jun 6, 2018
796be6d
Delete unused class MethodInvocationUtils
Pr0methean Jun 6, 2018
ca95b46
Command tests & package-tree cleanup
Pr0methean Jun 6, 2018
894a459
Register console commands
Pr0methean Jun 6, 2018
c659625
Style fixes
Pr0methean Jun 6, 2018
b4cc09f
Style fixes
Pr0methean Jun 6, 2018
17187c8
Style fix
Pr0methean Jun 6, 2018
62ff057
Console commands start with !
Pr0methean Jun 6, 2018
125358e
Rename `innerExecute` to `executeOnConsole`
Pr0methean Jun 6, 2018
dfffe70
Merge branch 'dev' into jline3
smartboyathome Jul 20, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 2 additions & 3 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@
<version>1.17.1</version>
</dependency>
<dependency>
<groupId>jline</groupId>
<groupId>org.jline</groupId>
<artifactId>jline</artifactId>
<version>2.14.6</version>
<version>3.7.1</version>
</dependency>
<dependency>
<groupId>co.aikar</groupId>
Expand Down Expand Up @@ -173,7 +173,6 @@
<finalName>glowstone</finalName>

<directory>${project.basedir}/target</directory>

<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
Expand Down
472 changes: 233 additions & 239 deletions src/main/java/net/glowstone/ConsoleManager.java

Large diffs are not rendered by default.

7 changes: 2 additions & 5 deletions src/main/java/net/glowstone/GlowServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public class GlowServer implements Server {
/**
* The console manager of this server.
*/
private final ConsoleManager consoleManager = new ConsoleManager(this);
private ConsoleManager consoleManager = new ConsoleManager(this);
/**
* The services manager of this server.
*/
Expand Down Expand Up @@ -582,9 +582,6 @@ private static ServerConfig parseArguments(String... args) {
case "-o":
parameters.put(Key.ONLINE_MODE, Boolean.valueOf(args[++i]));
break;
case "--jline":
parameters.put(Key.USE_JLINE, Boolean.valueOf(args[++i]));
break;
case "--plugins-dir":
case "-P":
parameters.put(Key.PLUGIN_FOLDER, args[++i]);
Expand Down Expand Up @@ -638,7 +635,7 @@ public void run() {
*/
public void start() {
// Determine console mode and start reading input
consoleManager.startConsole(config.getBoolean(Key.USE_JLINE));
consoleManager.start();
consoleManager.startFile(config.getString(Key.LOG_FILE));

if (getProxySupport()) {
Expand Down
193 changes: 110 additions & 83 deletions src/main/java/net/glowstone/i18n/LocalizedStrings.java

Large diffs are not rendered by default.

14 changes: 14 additions & 0 deletions src/main/java/net/glowstone/i18n/LogRecordableLocalizedString.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package net.glowstone.i18n;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just add these methods to LoggableLocalizedString instead?


import java.util.logging.LogRecord;

public interface LogRecordableLocalizedString extends LoggableLocalizedString {

LogRecord record();

LogRecord record(Object... args);

LogRecord record(Throwable ex);

LogRecord record(Throwable ex, Object... args);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@

import java.util.ResourceBundle;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import net.glowstone.GlowServer;

class LoggableLocalizedStringImpl extends LocalizedStringImpl
implements LoggableLocalizedString {
public class LoggedLocalizedString extends LocalizedStringImpl
implements LoggableLocalizedString, LogRecordableLocalizedString {

private final Level logLevel;

private final Logger logger;

LoggableLocalizedStringImpl(String key, Level logLevel) {
LoggedLocalizedString(String key, Level logLevel) {
super(key);
this.logLevel = logLevel;
this.logger = GlowServer.logger;
}

LoggableLocalizedStringImpl(String key, Level logLevel,
LoggedLocalizedString(String key, Level logLevel,
ResourceBundle resourceBundle,
Logger logger) {
super(key, resourceBundle);
Expand All @@ -45,4 +46,24 @@ public void log(Throwable ex) {
public void log(Throwable ex, Object... args) {
logger.log(logLevel, get(args), ex);
}

@Override
public LogRecord record() {
return new LogRecord(logLevel, get());
}

@Override
public LogRecord record(Object... args) {
return new LogRecord(logLevel, get(args));
}

@Override
public LogRecord record(Throwable ex) {
return new LogRecord(logLevel, get(ex));
}

@Override
public LogRecord record(Throwable ex, Object... args) {
return new LogRecord(logLevel, get(ex, args));
}
}
185 changes: 185 additions & 0 deletions src/main/java/net/glowstone/util/compiler/EvalTask.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package net.glowstone.util.compiler;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;

This comment was marked as outdated.

import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import net.glowstone.GlowServer;
import net.glowstone.ServerProvider;
import net.glowstone.util.ReflectionProcessor;
import org.bukkit.ChatColor;

/**
* A task for which a source string must be compiled and ran to complete.
*/
public class EvalTask implements Runnable {

This comment was marked as outdated.


private final boolean output;
private final String source;
private static Object lastOutput = null;
final Map<String, byte[]> classData = new HashMap<>();
private MapClassLoader classLoader;
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

/**
* Create a new EvalTask.
*
* @param command The statement to evaluate.
* @param output Whether the statement returns an object.
*/
public EvalTask(String command, boolean output) {
this.output = output;
String evaluatedCommand;
if (output) {
evaluatedCommand = command.substring(1);
} else {
evaluatedCommand = command;
}
if (compiler == null) {
GlowServer.logger.info("Glowstone is not running with a JDK and REPL is falling"
+ "back to basic parsing using reflection.");
source = evaluatedCommand;
} else {
source = "import org.bukkit.*;\n"
+ "import net.glowstone.*;\n"
+ "public class REPLShell {\n"
+ (output ? "public static Object run(Object last) {\n"
: "public static void run(Object last) {\n") + evaluatedCommand + "\n}\n}\n";
}
}

/**
* Compiles and runs the source associated with this evaluation task.
*/
@Override
public void run() {
if (compiler == null) {
ReflectionProcessor processor = new ReflectionProcessor(source,
ServerProvider.getServer());
Object result = processor.process();
GlowServer.logger.info(
ChatColor.GOLD + "Eval returned: " + (result == null ? ChatColor.RED
+ "<no value>"
: ChatColor.AQUA + result.toString()));
return;
}
classLoader = new MapClassLoader();
ClassDataFileManager classDataFileManager = new ClassDataFileManager(compiler
.getStandardFileManager(null, null, null));

List<JavaFileObject> compilationUnit = new ArrayList<>();
compilationUnit.add(new JavaSource("REPLShell", source));

JavaCompiler.CompilationTask task = compiler.getTask(null, classDataFileManager,
null, null, null, compilationUnit);
if (task.call()) {
try {
Object returned = MethodInvocationUtils
.invokeStaticMethod(getCompiledClass("REPLShell"), "run",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the methods of MethodInvocationUtils to this class, unless and until another class uses them.

lastOutput);
if (output) {
lastOutput = returned;
GlowServer.logger.info(" -> " + returned);
} else {
GlowServer.logger.info(" -> <no value>");
}
} catch (Exception e) {
GlowServer.logger.log(Level.SEVERE, "Error in running REPL shell! ", e);
}
}
}

/**
* Find a class compiled in this tasks' class loader.
*
* @param name The name of the class to find.
* @return The class.
*/
public Class<?> getCompiledClass(String name) {
return classLoader.findClass(name);
}

private class JavaSource extends SimpleJavaFileObject {

private final String source;

JavaSource(String name, String source) {
super(URI.create("string:///" + name + ".java"), Kind.SOURCE);
this.source = source;
}

@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return source;
}
}

private class MapClassLoader extends ClassLoader {

@Override
protected Class<?> findClass(String name) {
return defineClass(name, classData.get(name), 0, classData.get(name).length);
}
}

public class JavaClass extends SimpleJavaFileObject {

private final String name;

public JavaClass(String name) {
super(URI.create("string:///" + name + ".java"), Kind.CLASS);
this.name = name;
}

@Override
public OutputStream openOutputStream() throws IOException {
return new ClassDataOutputStream(name);
}
}

public class ClassDataFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {

public ClassDataFileManager(StandardJavaFileManager fileManager) {
super(fileManager);
}

@Override
public JavaFileObject getJavaFileForOutput(Location location, String className,
JavaFileObject.Kind kind, FileObject sibling) throws IOException {
return new JavaClass(className);
}
}

public class ClassDataOutputStream extends OutputStream {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this do differently than the underlying ByteArrayOutputStream? If this can't simply be replaced by a BAOS, document why not, and can we use a FilterOutputStream instead?


private final String name;
private final ByteArrayOutputStream bytes;

public ClassDataOutputStream(String name) {
this.name = name;
bytes = new ByteArrayOutputStream();
}

@Override
public void write(int b) throws IOException {
bytes.write(b);
}

@Override
public void close() throws IOException {
classData.put(name, bytes.toByteArray());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package net.glowstone.util.compiler;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

public class MethodInvocationUtils {

/**
* Invokes a specified static method belonging to a class.
*
* @param c The class.
* @param methodName The method to find in the class.
* @param args The arguments of the method.
* @return The result of invoking the method.
*/
public static Object invokeStaticMethod(Class<?> c, String methodName, Object... args) {
Method m = findFirstMatchingStaticMethod(c, methodName, args);
if (m == null) {
throw new RuntimeException("Could not find method " + methodName);
}
try {
return m.invoke(null, args);
} catch (IllegalAccessException | IllegalArgumentException | SecurityException
| InvocationTargetException e) {
throw new RuntimeException(e);
}
}

private static Method findFirstMatchingStaticMethod(Class<?> c, String methodName,
Object... args) {
Method[] methods = c.getDeclaredMethods();
for (Method m : methods) {
if (m.getName().equals(methodName) && Modifier.isStatic(m.getModifiers())) {
Class<?>[] parameterTypes = m.getParameterTypes();
if (areAssignable(parameterTypes, args)) {
return m;
}
}
}
return null;
}

private static boolean areAssignable(Class<?>[] types, Object... args) {
if (types.length != args.length) {
return false;
}
for (int i = 0; i < types.length; i++) {
Object arg = args[i];
Class<?> type = types[i];
if (arg != null && !type.isAssignableFrom(arg.getClass())) {
return false;
}
}
return true;
}
}
1 change: 0 additions & 1 deletion src/main/java/net/glowstone/util/config/ServerConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,6 @@ public enum Key {
ALLOW_CLIENT_MODS("server.allow-client-mods", true, Boolean.class::isInstance),

// console
USE_JLINE("console.use-jline", true, Boolean.class::isInstance),
CONSOLE_PROMPT("console.prompt", "> ", String.class::isInstance),
CONSOLE_DATE("console.date-format", "HH:mm:ss", String.class::isInstance),
CONSOLE_LOG_DATE("console.log-date-format", "yyyy/MM/dd HH:mm:ss",
Expand Down
14 changes: 14 additions & 0 deletions src/main/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>

<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>

<logger name="io.netty" level="WARN"/>
</configuration>