之前由于CodeJam要做搜索引擎相关的功能,而我经过了多个市面上比较流行的搜索引擎的对比之后,最终选择了Solr,主要是Solr在各方面都比较切合需求,DSL的查询语言也足够Easy和Reasonable。唯一的缺陷就是Solr是Java程序,具有很强的Java风格,这种风格对我这样一个Ruby程序员来说非常不适应。这种风格总的来说就是,完全XML配置,必须放在Tomcat或是Jetty中才能使用。

开始的Index配置的还是很成功,参考资料主要是:http://www.gm100861.com/519.html,这是我Google到目前为止我认为最靠谱的一篇文章了。

但是由于我们的搜索需要两个索引,而Solr不像Sphinx或是ElasticSearch天然支持多索引的特性,不过关于Solr的多索引配置的文档依然相当多,手段主要是Multi-indexes, Multi-cores和Multi-collections。我一开始选中了Multi-indexes,按照网上的一些文章一步一步做,试遍了各种方法,但最后全部都失败了。失败原因主要是不了解Tomcat的运行机制,而我出现的错误又很尴尬,访问localhost:8080/solr1出现404错误,没有任何错误消息,完全不知道该怎么办才好了,而且日志既不出现在/var/log里,也没有标准输出,Tomcat自身的错误日志又很多,找了半天,不知道哪个是哪个(后来才知道Tomcat和Ruby程序习惯不一样的地方就是,Tomcat在启动是加载webapp的时候就已经发生错误了,而Ruby程序都是访问时才检测错误,所以我每次在出现404的时候去找错误信息,显然什么都没找到)。再加上Tomcat是Java写的,没有办法通过调试源码来发现错误。后来又尝试了Multi-cores,也是完全不成功。

于是求助于Ruby China@yesmeck给了我一个gist,里面提及了一种简单的依靠调用REST API创建Collection的方法,这种方法真的简单,而且一弄就成功了!然后可以通过localhost:8080/solr/collection{n}/的REST API来指定相应的Collection。但是很奇怪的是,只要我一重启Tomcat,那些自己创建的Collection都会自动丢失,必须重新创建,很无语。网上我也没有找到好的解决方案。虽然如此,我还是用了很久,从CodeJam开始到今天第一次Demo,我还写了个脚本,在每次重启Tomcat后执行用来创建Collection并进行data import,还是蛮方便的。

今天搞完第一次Demo后,我终于稍微松口气,可以再试一次Multi-indexes的配置了,由于做了一个多礼拜的Solr,我对Tomcat和Solr的理解已经开始清晰了。所以第二次做的时候,我不再想之前一样在网上跟各种步骤,而是只看http://www.gm100861.com/519.html这篇我最相信的文章,以及这篇官方文档

在基本做完了所需步骤后,其中Tomcat,随即查看tomcat/logs/catalina.out,经过一个多礼拜的使用,我已经确信这个日志文件综合了Tomcat的各种日志输出。从日志中我很快发现了错误:

java.lang.IllegalArgumentException: Document base /usr/local/apache-tomcat-7.0.42/webapps/solr1 does not exist or is not a readable directory

我按照官方文档在tomcat/conf/Catalina/localhost下创建了solr1.xml和solr2.xml,然后就出现了这个错误。我猜测在Tomcat中创建什么名字的配置文件就代表什么名字的webapp,就像Rails的Convention一样,而不管这个文件里面如何指定了war文件的路径,这点我在之前任何一个Google的搜索结果中都没有看到。随后我在webapps下将solr更名为solr1,并创建了符号链接solr2。为了防止放在目录下的solr.war重新创建solr目录,我把solr.war也改名为solr1.war,同时也创建了符号链接solr2.war。这步完成后,错误信息改为

SEVERE: Error filterStart

这个错误就更人无语了,我按照网上的文章把filter去掉没有起效,把JDK1.7装上去之后还是不行,最后突然看到一个帖子说有可能是哪个必要的jar包找不到,于是立刻想起http://www.gm100861.com/519.html中的最后部分提及了要拷贝一些jar包到指定目录的,而我之前没做这步,完成后重启,访问localhost:8080/solr1和localhost:8080/solr2就都成功了,而且也不会再出现重启后消失的诡异情况。

我们夏季学期的作业,JavaEE的项目正在进行。在我的反复诱导下,Deltamaster 同学终于动用了AOP,不过很快就出了问题。他用AOP对所有Action类的execute方法进行增强(“advice”),但问题是,增强之后,View中的错误信息就无法输出了。

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<s:property value="errorMessage" />

由于AOP是我怂恿的,所以问题还是要由我来解决。Deltamaster如果有什么问题,那我肯定不会先去Google,因为我用Google就是他教的。所以首先,我仔细对比了启用AOP与不启用AOP两种情况下Action的表现,发现完全一致,确定Action没有问题。

由于AOP中我们用的是级别最高的around,有拦截并修改函数返回值的能力。我多次测试了around函数中proceed()函数的返回值在函数中各个点的位置,也没有发现问题。也就是说,around本身的代码也没有错误,但不排除是SpringFramework AOP的Bug使得Controller无法获取到action的返回值。不过,我暂时不会往Bug这块地方想。

如果Controller确实没有获取到action的返回值,也就是,jsp文件没有被调用。我随后在jsp后面加了一句输出语句,然后运行。输出成功了!这就表示该jsp文件确实被调用了,同时也证明了Controller确实获取到了action的返回值,前面AOP有Bug的推论不成立。总之从action到view,中间所有过程均正常。

那么就是errorMessage没有被获得了,在jsp中写errorMessage,实质将调用action类的getErrorMessage方法,我在这个方法中加了句输出语句。测试时这句话并没有输出,也就是说,getErrorMessage没有被调用。但为什么不被调用呢?我在action类中自己写了点get方法,测试发现都没有输出。(此处省略1000+字,关于我是如何验证是否是AOP的Bug造成getErrorMessage失效,以及是否是传回对象的类型无法匹配等等各种怀疑)最后,索性直接写

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<s:property value="class" />

输出了,而且发现是个proxy类,我随即将class改成了class.superclass.name,也就是输出本类的父类的名字,结果是,java.lang.reflect.Proxy,原因终于被发现了,作为一个Java的动态代理类,怎么可能有getErrorMessage方法。我们知道,SpringFramework AOP实现的核心方法是Java动态代理(基于接口的代理)或CGLIB(基于类的代理),这里似乎用的是前者。Proxy类对象代替Action类调用了View,结果当View要求调用者的getErrorMessage方法时,Proxy类没有这样的方法,所以导致出错!

随后我仔细查看了Proxy类的文档,全是类方法,没有任何能获得被代理对象的实例方法。无奈下,直接Google了,很快就找到了解答,很幸运,是中文解答:http://jeooo-li.iteye.com/blog/436931。“只要在Spring的配置文件applicationContext中的<aop:aspectj-autoproxy/>改为<aop:aspectj-autoproxy proxy-target-class="true"/>就可以了。”,经过测试,这个解答是正确的,问题解决。

经过初步测试,发现proxy-target-class="true" 一旦加上后,SpringFramework AOP将使用CGLIB方法而不是动态代理,生成Action类的子类,这样即可以满足AOP增强处理的要求,又完美继承了Action类的所有方法,因此才能解决问题。

少有的一篇与Linux无关的文章啊。话说我们已经进入了夏季学期,要求使用 Struts 2 + Spring + Hibernate 完成一个Web应用程序。我现在在积极学习中。配置Struts 2绝对是一件看RP的事情,配置反复出现问题。最早出现的错误就是 404 ,网页没找到。后来我不知道做了什么事,就解决了。。而现在的HTTP Status 404 - No result defined for action XxxAction and result yyy错误,就比较经典了。连老师看了半天,也丝毫没有解决办法。Google也毫无结果。不过后来倒是给了我启发。

我发现BuildPath里同样的包我其实加了两遍,WebContent/WEB-INF/lib里放入指定的jar包后,事实上就已经被添加进Eclipse了。我开始时没有发现,再添加了一遍,这样就添加了两遍。

后来将这些额外添加的包去掉,问题解决。

我现在在Fedora 15下写这篇短文。

Fedora 15目前安装后的Netbeans 7.0有严重Bug,主要表现在Menu下,有这样几个症状。鼠标hold在一个item上,实际选择的是上一个item。有时鼠标单击无法打开menu,需要按住鼠标才能打开,有时干脆打不开,等一系列奇怪症状。怀疑是Java GUI库和Gnome 3之间存在兼容问题。

解决方案很简单

su -c 'vi /usr/local/netbeans/etc/netbeans.conf'

打开这个文件,在netbeans_default_options中可以搜索到 -J-Dapple.laf.useScreenMenuBar=true 这项,将他修改为false即可。

目前是这么解决的,我还要继续试用下Fedora 15的Netbeans,可能还有更多Bug隐藏着。

很高兴Netbeans 7.0修改了很多我已知的存在于老版本的Bug。

另外,官网上下载的Eclipse 3.6.2 在Fedora 15下打开即崩溃。但是通过Yum安装的Eclipse是正常的。

崩溃的解决方案是这样的:

在eclipse.ini下增加如下两行即可

-Dorg.eclipse.swt.browser.UseWebKitGTK=true
-Dorg.eclipse.swt.browser.XULRunnerPath==/usr/lib/xulrunner-2.0/

Gnome 3的搜索框和消息提示框目前用ibus输入中文连候选词也没有,也不清楚原因及如何解决。

一直不知道Java中Finally的作用,因为总感觉没有什么用,事实上,当初学Javascript时也有同样的疑问,后来Google下,才明白过来。

	public void writeFile(String filePath, String fileName, String args) throws IOException
{
    FileWriter fw = new FileWriter(filePath + fileName);
    try 
    {
        fw.write(args);
    }
    catch (IOException e)
    {
    //1
        fw.close();
        throw e;
    }
    //2
    fw.close();
}
 
这段代码创建了一个FileWriter object,并调用 write 方法。在退出该方法之前,您必须关闭FileWriter object,以避免资源漏洞。为了完成这一任务,我们在 //2 处调用 close,它是该方法的最后一条语句。但是,如果 try 块中发生一个异常会怎么样呢?在这种情况下,//2 处的 close 调用永远不会发生。因此,您必须捕获这个异常,并在重新发出这个异常之前在 //1 处插入对 close 的另一个调用。这样就可以确保在退出该方法之前关闭FileWriter object。这样编写代码既麻烦又易于出错,但在没有 finally 的情况下这是必不可少的。有了 finally,前面的代码就可以重写为以下的形式:finally 关键字是对 Java 异常处理模型的最佳补充。 finally 结构使代码总会执行,而不管有无异常发生。使用 finally 可以维护对象的内部状态,并可以清理非内存资源。如果没有 finally,您的代码就会很费解。例如,下面的代码说明,在不使用 finally 的情况下您如何编写代码来释放非内存资源:
 
	public void writeFile(String filePath, String fileName, String args) throws IOException
{
    FileWriter fw = new FileWriter(filePath + fileName);
    try 
    {
        fw.write(args);
    }
    catch (IOException e)
    {
        throw e;
    }
    finally 
    {
        fw.close();
    }
}
 
finally 块确保 close 方法总被执行,而不管 try 块内是否发出异常(或是try块中有突如其来的return)。因此,可以确保在退出该方法之前总会调用 close 方法。这样您就可以确信FileWriter object被关闭并且您没有泄漏资源。
 
参考文献:
tech.ccidnet.com/art/3737/20060510/549039_1.html
hi.baidu.com/laoyouji2008/blog/item/d96467092d28f02f6b60fbb6.html
 
对于一些无可奈何的二元操作(神马是二元操作?二元操作是指当调用一个函数后必须成对调用另一个函数,这两个函数总是成对使用,否则可能会导致严重错误。比如文件的打开和关闭,加锁和释放锁。二元操作本身不是难点,但是人们非常容易忘记调用第二个函数,从而造成隐患,这在一个代码块中有多个如return,break,continue之类跳转语句时极易发生,以前有本书希望我们永远在代码块中只设定一个出口,但是这么做难度很大),由于Java的析构函数总不是很给力(无法确定何时调用,是否会调用,不能用它来包装第二个操作,这和C++完全不一样),我们完全可以使用Finally来完成第二个操作,这样就不会遗忘了。我们只要让第一个操作的那个代码块被try包围,第二个操作写在之后的finally中,catch可以不需要,这样无论接下来有多少个可能导致跳出代码块的语句,第二次操作总会被执行,毋须任何担忧。

最近在Linux下开发,由于长期使用Visual Studio 2010,对代码提示功能情有独钟,现在在Linux下,使用Eclipse做开发,当然免不了怀念Visual Studio强悍的代码提示,于是在网上搜索了一些文章,整理出关于Eclipse代码提示功能设置的方法。

 

Java:

增强Eclipse 的代码提示功能,具体怎么样来配置?下面开始说步骤:
1. 打开Eclipse,然后“window”→“Preferences”
2. 选择“java”,展开,“Editor”,选择“Content Assist”。
3. 选择“Content Assist”,然后看到右边,右边的“Auto-Activation”下面的“Auto Activation triggers for java”这个选项。其实就是指触发代码提示的就是“.”这个符号。
4. “Auto Activation triggers for java”这个选项,在“.”后加abc字母,方便后面的查找修改。然后“apply”,点击“OK”。
5. 然后,“File”→“Export”,在弹出的窗口中选择“Perferences”,点击“下一步”。
6. 选择导出文件路径,本人导出到桌面,输入“test”作为文件名,点击“保存”。
7. 在桌面找到刚在保存的文件“test.epf”,右键选择“用记事本打开”。
8. 可以看到很多配置Eclipse的信息
9. 按“ctrl + F”快捷键,输入“.abc”,点击“查找下一个”。
10. 查找到“.abc”的配置信息如下:
11. 把“.abc”改成“.abcdefghijklmnopqrstuvwxyz(,”,保存,关闭“test.epf”。
12. 回到Eclipse界面,“File”→“Import”,在弹出的窗口中选择“Perferences”,点击“下一步”,选择刚在已经修改的“test.epf”文件,点击“打开”,点击“Finish”。该步骤和上面的导出步骤类似。
13. 最后当然是进行代码测试了。随便新建一个工程,新建一个类。在代码输入switch,foreach等进行 测试。你立即会发现,果然出了提示,而且无论是敲哪个字母都会有很多相关的提示了,很流畅,很方便。
 
C/C++:
 
打开终端:输入:$ gcc- v
得到类似的:gcc 版本 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)
很容易就看到你当前使用的版本了。
启动Eclipse.进入:Windows-->Preferences-->C/C++找到Environment。增加两个变量:
CPLUS_INCLUDE_PATH: /usr/include/c++/4.1.3(我的gcc版本) 
C_INCLUDE_PATH: /usr/include
 
C/C++的代码提示只在.和->下触发,虽然没有Java下这么强大,不过也够了。因为这里是代码提示最容易发挥威力的地方。
 
补充:Ctrl+Space也能触发代码提示,而且功能更强,几乎总能给出符合需求的提示,不过我不知道如何才能让Eclipse在更多情况下自动触发提示,如果能做到,Eclipse就和Vistual Studio一样强了。
 
本文参考:
http://developer.51cto.com/art/200907/136242.htm
http://blog.chinaunix.net/u/21684/showart_462486.html