Hycz's Blog

Life is a game. Why so serious?

Tag Archives: Eclipse

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

2011/06/14 在Eclipse中配置Cassandra_2

接上回https://hycz.wordpress.com/2011/06/13/20110613-%E5%9C%A8eclipse%E4%B8%AD%E9%85%8D%E7%BD%AEcassandra/

  1. 见上回
  2. 见上回
  3. 见上回
  4. 遇到问题,开始Debug的时候出现
    “‘Launching CassandraDaemon’ has encountered a problem.
    Cannot connect to VM”Details中:
    “Cannot connect to VMselect failed”不光是这个项目,我新建了一个最简单的project写了个helloworld都不能用debug,说明这是eclipse层的问题。具体出错处是什么socket出错,网上的说法就几种:1,有其他程序占用了Eclipse的Debug端口,导致出错;2,操作系统的socket出了问题要重置。对于第一种解释,虽然我表示同意是另一个程序影响了Eclipse,但是实在难以找到相应的程序,对于第二种解释,我又找不到win7下的重置sock的工具。无奈之下,就用了360的LSP修复,修复之后Debug能用了,但是不能打开网页了。我在修复前看了一下,使用到LSP的非系统程序只有2个,都是我的游戏代理,一个是27代理,一个是迅雷加速器,恐怕问题就出在他们身上。无论如何,网页是打不开了,qq还在,于是重启看看。重启之后,依然不能上网,连qq都上不去了,打开360,居然要求自动修复,那就自动修复吧,结果瑞星狂叫360是木马,现在吧,我觉得瑞星更像木马一些。修复完了,按要求重启,qq能上了,但是网页打不开,这种情况好像DNS坏了一样,就用cmd ping了一下百度,居然ping的通,然后我又用Access Connections诊断了一下网络故障,他叫我检查防火墙设置。360防火墙怎么莫名其妙开了,关掉,不能开网页,瑞星防火墙,关掉,能开网页了。瑞星果然是娱乐货,没事弄个免费,天天弹那些狗屎免费游戏,真的考虑要不用它了。又启动了一下迅雷网游加速器,果然问题再现了,使用他自带的LSP修复,然后问题消失。果然还是LSP引起的问题么。这种诡异的问题不要再碰到了啊!这样一来,代理还是能用,只是用完了要修复,不能一边wow一边debug了。。
  5. 类中的static{}属性,似乎是在调用类的方法时,会首先执行的代码,在Cassandra的org.apache.cassandra.service.AbstractCassandraDaemon类中,就有这么一段,是用来初始化logging的
    //Initialize logging in such a way that it checks for config changes every 10 seconds.
        static
        {
        	String config = System.getProperty("log4j.configuration", "log4j-server.properties");
            URL configLocation = null;
            try
            {
                // try loading from a physical location first.
                configLocation = new URL(config);
            }
            catch (MalformedURLException ex)
            {
                // load from the classpath.
                configLocation = AbstractCassandraDaemon.class.getClassLoader().getResource(config);
                if (configLocation == null)
                    throw new RuntimeException("Couldn't figure out log4j configuration.");
            }
            PropertyConfigurator.configureAndWatch(configLocation.getFile(), 10000);
            org.apache.log4j.Logger.getLogger(AbstractCassandraDaemon.class).info("Logging initialized");
        }

    这里首先定义了一个变量config,这个config的属性关键字是log4j.configuration,然后给了一个默认值log4j-server.properties,这个变量的主要目的是储存log4j的配置文件地址,默认的文件名是log4j-server.properties,如果在启动时手动设置了其他值的话,那么就会是那个手动设置的值。

    然后首先尝试通过物理地址查找这个配置文件(try中),如果找不到(catch中),那么就到启动时设置的CLASSPATH中去找这个文件。

  6. 再看cassandra-0.8.0的bin版本中的bin文件夹中的cassandra.bat文件(我用的是windows7。。。) ,通过分析这个文件中所作的操作,可以知道如何在Eclipse中配置Cassandra的运行参数。以下摘自cassandra.bat文件。
    if "%OS%" == "Windows_NT" setlocalif NOT DEFINED CASSANDRA_HOME set CASSANDRA_HOME=%~dp0..
    if NOT DEFINED CASSANDRA_MAIN set CASSANDRA_MAIN=org.apache.cassandra.thrift.CassandraDaemon
    if NOT DEFINED JAVA_HOME goto err

    这段分别定义了cassandra的根目录地址,cassandra的入口程序,检查了JAVA_HOME是否定义。

    REM ***** JAVA options *****
    set JAVA_OPTS=^
     -ea^
     -Xms1G^
     -Xmx1G^
     -XX:+HeapDumpOnOutOfMemoryError^
     -XX:+UseParNewGC^
     -XX:+UseConcMarkSweepGC^
     -XX:+CMSParallelRemarkEnabled^
     -XX:SurvivorRatio=8^
     -XX:MaxTenuringThreshold=1^
     -XX:CMSInitiatingOccupancyFraction=75^
     -XX:+UseCMSInitiatingOccupancyOnly^
     -Dcom.sun.management.jmxremote.port=7199^
     -Dcom.sun.management.jmxremote.ssl=false^
     -Dcom.sun.management.jmxremote.authenticate=false^
     -Dlog4j.configuration=log4j-server.properties^
     -Dlog4j.defaultInitOverride=true

    这段是各种参数,需要把这段复制到Eclipse中Run->Debug Configration中建立的Debug项中的VM arguments中(注意把每行最后的^删掉)。

    REM Ensure that any user defined CLASSPATH variables are not used on startup
    set CLASSPATH="%CASSANDRA_HOME%\conf"

    这里就定义了CLASSPATH,原来这里把conf文件夹加入到了CLASSPATH里(CLASSPATH里不止一个地址),怪不得能找到。所以在Eclipse的项目的Properties->Java Build Path->Libraries点Add Class Folder,加入这个conf地址,就不会报错了

    bat文件中其他的设置也是大同小异,就不写了。

2011/06/13 在Eclipse中配置Cassandra

首先,整个过程基本基于下面这篇文章:

原帖地址:http://www.congci.com/item/cassandra-for-eclipse

我们将从svn中下载Cassandra的源代码,完成编译,并执行相应的单元测试。

配置环境

1 安装jdk6,ant。

2 安装Eclipse3.5

3 在Eclipse中安装http://subclipse.tigris.org/update_1.6.x

在Eclipse的菜单选择:Help –> Install New Software

image

接下来,就可以安装subclipse: Add –> 输入名称和地址http://subclipse.tigris.org/update_1.6.x ->勾选所有需要安装的选择

image

最好点击next,安装提示的要求一步一步操作。最后等待subclipse安装完成后重启Eclipse。

下载Cassandra源码

在Eclipse中新建一个svn项目

image

选择:创建新的资源库位置

image

获取svn:https://svn.apache.org/repos/asf/cassandra/tags/cassandra-0.6.2/

image

按照如下选择

image

然后根据提示创建一个Java Project,svn检出操作完成后,我们就可以看到刚刚创建的项目了

image

这样Cassandra的源代码就下载完毕了。

编译Cassandra

假设我们下载的Cassandra源代码的位置在D:\workspace\cassandra-0.6.2,那么我们使用命令行工具在这个目录下执行ant的编译操作。

image

如果编译成功,提示如下:

image

可以看到,执行编译的同时,也生成了thrift,avro和antlr的java源文件。

在Eclipse中编辑Cassandra源码

源代码虽然可以编辑成功,但是我们还是无法利用Cassandra对源代码进行方便的修改。

接下来,要一步一步配置Eclipse,使其可以方便地编辑Cassandra源码。

首先,将D:\workspace\cassandra-0.6.2\lib目录下的所有jar包添加到项目的Build Path中。

image

然后,编辑Src目录的属性,默认的设置为

image

将默认的目录删除,然后添加如下Src目录

image

添加完成之后,目录结构如下

image

按照正常的流程来说,我们对Eclipse的配置就算完成了,可以很方便地在Eclipse中编辑Cassandra源码了。

但是Eclipse还是有一些小Bug可能会影响到正常的功能,比如我遇到的这个情况

image

大家可以看到,GarbageCollectorMXBean其实是jdk中的一个类,但是Eclipse提示找不到定义。处理的办法如下

打开项目的Build Path,先移除JRE System Library

image

然后在将之前移除JRE System Library的添加回来。

image

最后在刷新整个项目,错误提示的问题就解决了。

image

运行单元测试

我们可以将单元测试的代码也添加到Eclipse中。

步骤非常简单:

1 添加JUnit4的jar包

2 添加test目录下的测试代码到Src中。

添加成功后项目的列表如下

image

我们可以直接在test中运行已有的单元测试。

打包发布

当我们完成对Cassandra源码的修改以后,可以将修改后的版本打包发布出来。

使用ant的命令行:ant release

image

执行完毕以后,我们可以在D:\workspace\cassandra-0.6.2\build目录中看到打包后的文件

image

这样,我们就可以使用修改后的Cassandra了。

———————————————————————恶心的分割线————————————————————————

下面是一些我遇到的问题

  1. 之前装了ant(地址:http://apache.etoak.com//ant/binaries/apache-ant-1.8.2-bin.tar.gz),但是没有装svn,所以把整个project下下来之后,ant出错,错误是"Cannot run program "svn":CreateProcess error=2" ,所以还要下个svn,方法见这篇文章https://hycz.wordpress.com/2011/06/13/%E8%BD%ACsvn%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7%E5%AE%89%E8%A3%85%E4%BD%BF%E7%94%A8windows/
  2.  设置Java Build Path中Source目录,0.8.0版本和0.6.2不太一样,不过无论什么版本,源码目录其实已经写在了build.xml中。以下内容摘自0.8.0版本的build.xml中
    <echo file=".classpath"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>
    <classpath>
      <classpathentry kind="src" path="src/java"/>
      <classpathentry kind="src" path="src/gen-java"/>
      <classpathentry kind="src" path="interface/thrift/gen-java"/>
      <classpathentry kind="src" path="drivers/java/src"/>
      <classpathentry kind="src" path="drivers/java/test"/>
      <classpathentry kind="src" path="test/unit"/>
      <classpathentry kind="src" path="test/long"/>
      <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
      <classpathentry kind="output" path="build/classes/main"/>
      <classpathentry kind="lib" path="build/classes/thrift"/>
      <classpathentry kind="lib" path="build/test/classes"/>
      <classpathentry kind="lib" path="test/conf"/>
    ]]>
    	</echo>

    这些目录便是需要在Eclipse中设置的

  3. jar的导入问题,(醒目!!下面的可以用,但是有更简单的设置,开发过程中需要用到的jar包都放在cassandra-0.8.0\build\lib\jars中,直接导入即可),除了将lib文件夹下的jar导入,还有一些其他的jar包不在cassandra目录下。我是在命令行中用ant编译之后才进行的jar导入,在用ant编译的过程中,还自动从网上下载了一些jar文件,我的电脑上是在“C:\Users\用户名\.m2\repository”中,详细的列表可以参见cassandra-0.8.0\build文件夹下的build-dependencies.xml,这个应该是在编译之后产生的。不过显然,更加原始的依赖查询是写在了build.xml中。以下内容摘自0.8.0版本的build.xml中
    <!-- Publish artifacts to Maven repositories -->
      <target name="mvn-install"
              depends="maven-declare-dependencies,artifacts,jar,sources-jar,javadoc-jar"
              description="Installs the artifacts in the Maven Local Repository">
    
        <!-- the parent -->
        <install pomFile="${build.dir}/${final.name}-parent.pom"
                 file="${build.dir}/${final.name}-parent.pom"
                 packaging="pom"/>
    
        <!-- the distribution -->
        <install pomFile="${build.dir}/${final.name}-dist.pom"
                 file="${build.dir}/${final.name}-dist.pom"
                 packaging="pom"/>
        <install pomFile="${build.dir}/${final.name}-dist.pom"
                 file="${build.dir}/${final.name}-bin.tar.gz"
                 packaging="tar.gz"
                 classifier="bin"/>
        <install pomFile="${build.dir}/${final.name}-dist.pom"
                 file="${build.dir}/${final.name}-src.tar.gz"
                 packaging="tar.gz"
                 classifier="src"/>
    
        <!-- the cassandra-thrift jar -->
        <install pomFile="${build.dir}/${ant.project.name}-thrift-${version}.pom"
                 file="${build.dir}/${ant.project.name}-thrift-${version}.jar"/>
        <install pomFile="${build.dir}/${ant.project.name}-thrift-${version}.pom"
                 file="${build.dir}/${ant.project.name}-thrift-${version}-sources.jar"
                 classifier="sources"/>
        <install pomFile="${build.dir}/${ant.project.name}-thrift-${version}.pom"
                 file="${build.dir}/${ant.project.name}-thrift-${version}-javadoc.jar"
                 classifier="javadoc"/>
    
        <!-- the cassandra-all jar -->
        <install pomFile="${build.dir}/${final.name}.pom"
                 file="${build.dir}/${final.name}.jar"/>
        <install pomFile="${build.dir}/${final.name}.pom"
                 file="${build.dir}/${final.name}-sources.jar"
                 classifier="sources"/>
        <install pomFile="${build.dir}/${final.name}.pom"
                 file="${build.dir}/${final.name}-javadoc.jar"
                 classifier="javadoc"/>
    
        <!-- the cassandra-cql jar -->
        <install pomFile="${build.dir}/${ant.project.name}-cql-${cql.driver.version}.pom"
                 file="${build.dir}/${ant.project.name}-cql-${cql.driver.version}.jar"/>
        <install pomFile="${build.dir}/${ant.project.name}-cql-${cql.driver.version}.pom"
                 file="${build.dir}/${ant.project.name}-cql-${cql.driver.version}-sources.jar"
                 classifier="sources"/>
        <install pomFile="${build.dir}/${ant.project.name}-cql-${cql.driver.version}.pom"
                 file="${build.dir}/${ant.project.name}-cql-${cql.driver.version}-javadoc.jar"
                 classifier="javadoc"/>
      </target>
    
      <target name="publish"
              depends="mvn-install"
              if="release"
              description="Publishes the artifacts to the Maven repository">
    
        <!-- the parent -->
        <deploy pomFile="${build.dir}/${final.name}-parent.pom"
                file="${build.dir}/${final.name}-parent.pom"
                packaging="pom"/>
    
        <!-- the distribution -->
        <deploy pomFile="${build.dir}/${final.name}-dist.pom"
                file="${build.dir}/${final.name}-dist.pom"
                packaging="pom"/>
        <deploy pomFile="${build.dir}/${final.name}-dist.pom"
                file="${build.dir}/${final.name}-bin.tar.gz"
                packaging="tar.gz"
                classifier="bin"/>
        <deploy pomFile="${build.dir}/${final.name}-dist.pom"
                file="${build.dir}/${final.name}-src.tar.gz"
                packaging="tar.gz"
                classifier="src"/>
    
        <!-- the cassandra-thrift jar -->
        <deploy pomFile="${build.dir}/${ant.project.name}-thrift-${version}.pom"
                file="${build.dir}/${ant.project.name}-thrift-${version}.jar"/>
        <deploy pomFile="${build.dir}/${ant.project.name}-thrift-${version}.pom"
                file="${build.dir}/${ant.project.name}-thrift-${version}-sources.jar"
                classifier="sources"/>
        <deploy pomFile="${build.dir}/${ant.project.name}-thrift-${version}.pom"
                file="${build.dir}/${ant.project.name}-thrift-${version}-javadoc.jar"
                classifier="javadoc"/>
    
        <!-- the cassandra-all jar -->
        <deploy pomFile="${build.dir}/${final.name}.pom"
                file="${build.dir}/${final.name}.jar"/>
        <deploy pomFile="${build.dir}/${final.name}.pom"
                file="${build.dir}/${final.name}-sources.jar"
                classifier="sources"/>
        <deploy pomFile="${build.dir}/${final.name}.pom"
                file="${build.dir}/${final.name}-javadoc.jar"
                classifier="javadoc"/>
    
        <!-- the cassandra-cql jar -->
        <deploy pomFile="${build.dir}/${ant.project.name}-cql-${cql.driver.version}.pom"
                file="${build.dir}/${ant.project.name}-cql-${cql.driver.version}.jar"/>
        <deploy pomFile="${build.dir}/${ant.project.name}-cql-${cql.driver.version}.pom"
                file="${build.dir}/${ant.project.name}-cql-${cql.driver.version}-sources.jar"
                classifier="sources"/>
        <deploy pomFile="${build.dir}/${ant.project.name}-cql-${cql.driver.version}.pom"
                file="${build.dir}/${ant.project.name}-cql-${cql.driver.version}-javadoc.jar"
                classifier="javadoc"/>
      </target>

    我也是刚刚开始用,估计是编译过程中根据此列表依次检查依赖,如果缺少jar,则从网上下载,下载地址写在了根目录下的build.properties.default文件中,并放在相应的目录中,然后把这些目录名写到build-dependencies.xml中。下载的jar分为两种,一个是源码,一个是编译好的。当我们导入了cassandra-0.8.0\lib下的所有jar之后,会发现还有类找不到,那么这些类就是在这些自动下载的jar中,需要按需要手动在Eclipse中导入。

  4. 待续。。