Hycz's Blog

Life is a game. Why so serious?

2011/06/20-21 在Eclipse中配置Cassandra_3

9. cassandra-cli在Eclipse中的配置

服务端搞定之后,接下来配置客户端,先分析bin版本中bin目录下的cassandra-cli.bat,有这么一段

:okClasspath
REM Include the build\classes\main directory so it works in development
set CASSANDRA_CLASSPATH=%CLASSPATH%;"%CASSANDRA_HOME%\build\classes\main";"%CASSANDRA_HOME%\build\classes\thrift"
goto runCli

说明在开发环境中,客户端的classpath需要”%CASSANDRA_HOME%\build\classes\main”和”%CASSANDRA_HOME%\build\classes\thrift”这两个地址,同样的,我们在Eclipse项目中加入以上地址,在Properties->Java Build Path->Libraries点Add Class Folder,加入这两个地址。这样就能成功运行客户端了。

客户端其实是个命令行控制的程序,具体的入口是org.apache.cassandra.cli.CliMain。

10.jline在Windows下的Eclipse中不能正常运行的问题

在Eclipse中虽然运行了cli,但是Eclipse中运行cli会直接跳到了结束,虽然出现了cli启动时的输出,但是还没等输入命令cli就已经中止了(terminated)。那么就一步步运行看看,发现之所以会结束,问题出在下面的代码

String prompt;
        String line = "";
        String currentStatement = "";
        boolean inCompoundStatement = false;

        while (line != null)
        {
            prompt = (inCompoundStatement) ? "...\t" : getPrompt(cliClient);

            try
            {
                line = reader.readLine(prompt);
            }
            catch (IOException e)
            {
                // retry on I/O Exception
            }

            if (line == null)
                return;

            line = line.trim();

            // skipping empty and comment lines
            if (line.isEmpty() || line.startsWith("--"))
                continue;

            currentStatement += line;

            if (line.endsWith(";") || line.equals("?"))
            {
                processStatement(currentStatement);
                currentStatement = "";
                inCompoundStatement = false;
            }
            else
            {
                currentStatement += " "; // ready for new line
                inCompoundStatement = true;
            }
        }

其中

line = reader.readLine(prompt);

这里调用jline包中的方法从Console中读一行,但是实际上在eclipse中反悔了null于是在下面的代码中

if (line == null)
    return;

导致了运行的结束。而真正的原因是在我步进了jline包的源码中时,才发现的(jline-0.9.94下载:http://sourceforge.net/projects/jline/files/jline/0.9.94/),当调用reader.readLine()方法时总是返回null。后来google了一下,发现这个问题不是个别情况,而是一个非常知名的问题,如这篇文章(http://whitesock.iteye.com/blog/692816)中所说,“JLine最知名的问题莫过于在Windows平台下的Eclipse中启动的程序中调用reader.readLine()方法时总是返回null(正确的行为是等待用户输入)”,我下载了最新的版本jline-1.0,问题仍然存在。

根据那篇文章中的解决办法,“笔者发现通过设置jline.WindowsTerminal.directConsole属性为false,可以解决返回null的问题”,我确实解决了问题,代码如下:

jline.WindowsTerminal winTerm=(jline.WindowsTerminal)reader.getTerminal();
winTerm.setDirectConsole(false);

然后,我想看看究竟是哪里导致了这个问题,在CliMain.java中,初始化reader时,调用了jline.ConsoleReader的构造器,在jline.ConsoleReader.class中,发现时以下语句初始化了terminal

this(in, out, bindings, Terminal.getTerminal());

继续跟进,Terminal.getTerminal()是个静态方法,下面是其代码

    public static Terminal getTerminal() {
        return setupTerminal();
    }

这里调用了Terminal.setupTerminal()方法,这个方法实际上是用来判断操作系统是windows还是unix,然后根据情况创建WindowsTerminal或者UnixTerminal,既然出问题的是Windows系统下,那么再继续跟进到WindowsTerminal类中,在这里发现了上文提到的directConsole属性,其初始化的代码写在构造器中,如下:

        String dir = System.getProperty("jline.WindowsTerminal.directConsole");
        if ("true".equals(dir)) {
            directConsole = Boolean.TRUE;
        } else if ("false".equals(dir)) {
            directConsole = Boolean.FALSE;
        }

而具体使用到这个变量的地方,是这里:

    public int readCharacter(final InputStream in) throws IOException {
        // if we can detect that we are directly wrapping the system
        // input, then bypass the input stream and read directly (which
        // allows us to access otherwise unreadable strokes, such as
        // the arrow keys)
        if (directConsole == Boolean.FALSE) {
            return super.readCharacter(in);
        } else if ((directConsole == Boolean.TRUE)
            || ((in == System.in) || (in instanceof FileInputStream
                && (((FileInputStream) in).getFD() == FileDescriptor.in)))) {
            return readByte();
        } else {
            return super.readCharacter(in);
        }
    }

根据我在debug的过程中的步进,发现实际上在这个3分支的判断中,进入的是第二个,也就是directConsole == Boolean.TRUE,这说明在WindowsTerminal的构造器中,System.getProperty(“jline.WindowsTerminal.directConsole”)得到的是true,
但是这种情况是很奇怪的,我在CliMain中加入了以下代码进行debug

        System.out.println("true".equals(System.getProperty("jline.WindowsTerminal.directConsole")));

发现这里打印出的值是false,也就是说,在WindowsTerminal的构造器中directConsole被赋予了错误的值,这就是为什么要手动将其设为false

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: