Archive for the ‘经验之谈’ Category
解决一个jUnit运行时错误
情况概述:
已经经过测试、运行过多遍的一个jUnit测试用例突然不能用了,运行时提示:
java.lang.Exception: Method setupOnce() should be static
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
代码如下:
@RunWith(Parameterized.class)
public class SqlIndexLogicTest {
Object[] paramNow;
private String expected;
private JSONArray target;
@Parameters
public static Collection<object []> params() throws JSONException{
return Arrays.asList(new Object[][]{
{"SELECT * FROM (SELECT REQUEST_TIME,RESULT_ID,KEY_CODE,REGION_CODE FROM SD_UKMS_LOG GROUP BY REQUEST_TIME,REGION_CODE,KEY_CODE,RESULT_ID ORDER BY REGION_CODE) WHERE 1=1 and request_time>to_date('2011-09-21', 'yyyy-mm-dd')",
new JSONArray("[1506, 0, 5, {'request_time':['2011-09-21']}]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG) WHERE 1=1 and region_code=931",
new JSONArray("[1510, 0, 5, {'region_code':['931']}]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG) WHERE 1=1 and region_code in ('931','000')",
new JSONArray("[1510, 0, 5, {'region_code':['931','0000']}]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG) WHERE 1=1 and region_code in ('931','0000') and request_time< =to_date('2012-01-01', 'yyyy-mm-dd')",
new JSONArray("[1510, 0, 5, {'region_code':['931','0000'], 'request_time':['2012-01-01']}]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG)",
new JSONArray("[1509, 0, 5]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG) WHERE 1=1",
new JSONArray("[1509, 0, 5, {'region_code':[], 'cas_id':[], 'request_time':[]}]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG) WHERE 1=1",
new JSONArray("[1509, 0, 5, {'region_code':[''], 'cas_id':[''], 'request_time':['']}]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG) WHERE 1=1 and region_code=931",
new JSONArray("[1509, 0, 5, {'region_code':['931'], 'cas_id':[''], 'request_time':['']}]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG) WHERE 1=1 and region_code=931 and cas_id=13519000753",
new JSONArray("[1509, 0, 5, {'region_code':['931'], 'cas_id':['13519000753'], 'request_time':['']}]")},
{"SELECT * FROM (SELECT REGION_CODE,CAS_ID,REQUEST_TIME,UKMS_ID FROM SD_UKMS_LOG) WHERE 1=1 and region_code=931 and cas_id=13519000753 and request_time<=to_date('2011-09-21', 'yyyy-mm-dd')",
new JSONArray("[1509, 0, 5, {'region_code':['931'], 'cas_id':['13519000753'], 'request_time':['2011-09-21']}]")},
});
}
public SqlIndexLogicTest(String expected, JSONArray target){
this.target = target;
this.expected = expected;
}
@BeforeClass
public void setUpOnce() throws JSONException{
}
@AfterClass
public static void tearDownOnce(){
}
@Before
public void init() throws JSONException{
JSONObject conditionObj = target.optJSONObject(3);
Map<String, String[]> whereParams = new TreeMap<string , String[]>();
if(conditionObj != null){
@SuppressWarnings("unchecked")
Iterator</string><string> it = conditionObj.keys();
while(it.hasNext()){
String key = it.next();
System.out.println("key:"+key);
JSONArray subCondArr = conditionObj.getJSONArray(key);
String[] subCondArrStr = new String[subCondArr.length()];
for(int j=0; j<subcondarr .length(); j++){
subCondArrStr[j] = subCondArr.getString(j);
}
whereParams.put(key, subCondArrStr);
}
}
paramNow = new Object[]{target.get(0), target.get(1), target.get(2), whereParams, target.optJSONArray(4)};
}
@After
public void destroy(){
}
@Ignore("pause this method test")
@Test
public void queryNormal() throws DAOException, JSONException{
org.json.JSONObject jo = SqlIndexLogic.getInstance()
.query((Integer) paramNow[0], (Integer) paramNow[1], (Integer) paramNow[2], (Map<String, String[]>) paramNow[3], (JSONArray) paramNow[4]);
System.out.println(jo);
}
@Test
public void buildWrapedSQL() throws DAOException, JSONException{
String baseSQL = SqlIndexLogic.getInstance().getBaseSQL((Integer) paramNow[0]);
StringBuilder sql = SqlIndexLogic.getInstance()
.buildWrapedSQL(baseSQL, (Integer) paramNow[0], (Integer) paramNow[1], (Integer) paramNow[2], (Map<string , String[]>) paramNow[3], (JSONArray) paramNow[4]);
System.out.println(sql);
assertEquals(expected, sql.toString());
}
}
解决方法:
将setupOnce()方法改成static修饰即可。
原因:
@BeforeClass和@AfterClass注解的两个方法需要静态化,以符合jUnit调用机制。而那个@BeforeClass注解的setupOnece()方法的static修饰符不知道什么时候不小心删掉了。
Failed to load unit ‘HGCM’
现象:
恢复运行virtualBox虚拟机时,出现如下错误
Failed to load unit 'HGCM' (VERR_SSM_UNEXPECTED_DATA)
日志最后几行如下
00:00:03.757 Changing the VM state from ‘DESTROYING’ to ‘TERMINATED’.
00:00:03.895 ERROR [COM]: aRC=NS_ERROR_FAILURE (0×80004005) aIID={1968b7d3-e3bf-4ceb-99e0-cb7c913317bb} aComponent={Console} aText={Failed to load unit ‘HGCM’ (VERR_SSM_UNEXPECTED_DATA)}, preserve=false
00:00:03.895 Power up failed (vrc=VERR_SSM_UNEXPECTED_DATA, rc=NS_ERROR_FAILURE (0X80004005))
解决办法:
销毁(Discard)当前虚拟机状态,然后启动即可
原因:
早上升级了virtualBox版本,但是在升级之前,虚拟机不是shutDown状态,而是saveState状态,而新版本虚拟机在识别、启动保存的虚拟机状态时跟上
转换整个项目编码
处于种种原因,我们需要将项目由原来的GBK编码改成UTF-8编码。如果针对单个文件,我们可能的做法是:将文件按原来的方式打开,copy所有内容,然后将文件编码指定为UTF-8,重新打开,CTRL+A, 接着CTRL+V。但项目文件好几百个,甚至上千个。这种方法显然行不通。
可喜的是,Linux有着非常强大的功能,只要操作得当,必能将人从繁重的工作中解脱出来。高效的做法是:
find . -regex ".+\.\(tpl\|js\|jsp\|css\|java\|xml\)$" | xargs enca | grep 'GB2312' | awk -F: '{print $1}' | xargs enconv
以上命令在我的运行过程中情况如下,转换435个文件,用时不超过5秒。
以上命令有两个前提:
- 当前环境的编码是UTF-8
- 已经安装了enca包
如果上面前提不成立,则需要按如下处理:
- 针对1不成立,修改命令如下:
find . -regex ".+\.\(tpl\|js\|jsp\|css\|java\|xml\)$" | xargs enca | grep 'GB2312' | awk -F: '{print $1}' | xargs enconv -x UTF-8 - 针对2不成立,则安装enca包。如ubuntu:
sudo apt-get install enca
更新ear/jar包内部部分内容
地球人都知道ear项目打包发布很麻烦,有时候就一个字的更改,也得要重新导出(Export)整个项目,耗时又费力。倘若能直接更新指定的那个jsp/class/js/css/png文件,而且只需几秒钟,觉得这个方案很好的有木有?那好,我就把尝试N多次终于成功的方法告诉你,有钱的捧个钱场,没钱的捧个人场!
关键语法:
jar uf xxxx.ear absolutePath/filename
一般步骤:
- 如果没有子路径,那就直接把替换文件拿过来,放在当前路径下,按上面语法执行;
- 如果有子路径,比如META-INF/则先建立这个目录路径。更方便的方式是解压出那个指定的文件,这样就自动生成了子路径。语法如下:
jar xf xxxx.ear absolutePath/filename
然后把替换文件放到相应位置,或者直接在解压出来的文件上编辑,完成后按上面语法执行;
Examples:
单层压缩包:
- 由于采用了新版的log4j,现在需要更新NGBBoss.ear根目录下的log4j.jar:
jar uf NGBBoss.ear log4j.jar
- 更新NGBBoss.ear中META-INF/目录的application.xml文件:
jar uf NGBBoss.ear META-INF/application.xml
注意:外部文件application.xml也得要有一个对应的父目录META-INF/与执行jar命令时的当前路径下。也就是说,当前路径下要有NGBBoss.ear META-INF/ 这两个东东,而application.xml在META-INF/下。
多层压缩包更新:
- 如果要更新ear包内的war包中的某个jsp/jpg/html/css/js怎么办?那就只能麻烦一点:
- 先把war包取出来;
- 再把那个jsp/jpg/html/css/js文件解压出来;
- 修改/替换;
- 按单层压缩包的更新方式把war包更新了;
- 按单层压缩包的更新方式把ear包更新了;
解锁Oracle表
两句话:
- 查询被锁表的相关信息
select se.username, obj.object_name, obj.object_id, lk.session_id, se.serial# from user_objects obj, v$locked_object lk, v$session se where lk.object_id=obj.object_id and lk.session_id=se.sid;
- 解锁
alter system kill session '{sid},{serial#}';
在旧有项目上开发新功能
背景:
公司有一套项目的统一版本,一个由6个子项目组成的ejb项目,子系统分别是:commons, dao, bussiniselogic, ejbFacade, delegate, webapp。如果更改了一个jsp页面还好说,把jsp上传到服务器即可;但如果是更改了Action,那就麻烦了:重新打包、发布、重启服务器。项目大了打包发布就很耗时,如果大家很有时间,还打算利用这段时间喝杯咖啡,聊两句,那就无所谓了。如果很着急,或者急于看到自己的更改结果。那最好另想办法让你的程序编译-打包-发布过程快一点!
实践:
下面的方法经是我在经过实践证明可行的:
- 新建一个动态web项目。
命名为webappNew。在build path(如src/main/java/)下对应建立相应项目的名称(commons/, dao/, bussiniselogic/, webapp/)。更改build path,将以上新建的几个子目录设置为build path。别忘了把该有的项目引用和依赖加入进来!
- 只在这个新项目下工作。
开发过程中,将本该放在子项目中的新代码放在这些对应目录之下,如dao下要新增一个类com.maywide.oss.web.util.mkuser.java, 此代码本该新增在项目dao下,但是现在放在此web项目的dao/目录下。这样的目的是不再对dao项目重新编译打包,发布的时候只对此新项目编译、打包。这样能节约很多时间。
- 重构/集成
项目开发完毕,需要对整个项目重构。有了前面新建的对应项目名称的目录,重构将非常简单。只需要将各个目录下的代码copy到对应项目中去就行了!然后删除在第1步中新建的web项目。
问题:
- 原web项目不参与编译,新页面怎么运行?
原有的web项目中带有登录功能、js库、css库、框架设计、BaseAction、BaseAF等等基础内容。如果新建的项目中是完全干净的,所有代码都是新开发的,那没有了这些支撑新web页面/功能正常运行的基础设施,新的web项目是无法正常运行甚至不能运行的。所以,这里有了一个方案:
- 新建一个J2EE Utility Project,命名为webappU。将原web项目中的build path复制到webappU,并设置相同的build path,包括resource。
- 将webappU包含到webappNew的Java EE module Dependencies中。这样新开发的Action/AF/Bean就可以引用到原有的基础框架。
- 将css/、common/、images/、inc/、js/、WEB-INF/、index.jsp等顶层文件拷贝到webappNew中。其它功能按需添加。
- 如何进行团队协作?
以前我们直接使用svn,但是现在webappNew是要被删除的,如何协作呢?Git是一个不错的选择。尤其对于连接服务器不方便但又需要团队协作的情况下!
WikiEditor插件安装配置
我自己有个meidiaWiki,这是为了积累知识所做的笔记,通过wiki的方式组织,强迫自己将笔记做得正规些,有条不紊,时机成熟的时候再放到网上。
为什么安装WikiEditor?
写wiki最烦恼的是格式,尤其是那些枯燥又容易混淆的格式符号。如果你用过wiki,几个月没有用了,还能记得非数字列表怎么表示吗?
去年听说wiki老总立志要让编辑人员更容易使用。最近看到meidawiki官方网站界面变得更加漂亮了,编辑页面也有了很大的变化,以往那些单调的编辑功能变得丰富了,而且还能插入特殊字符、有帮助,编辑的确更加容易。所以我打算把这个用到我的wiki上。
安装
我的安装过程并非一帆风顺。下载新的wiki系统并执行了升级程序后,并没有看到那漂亮的编辑器。全新安装也不行。于是仔细检查官方网站,发现这是mediaWiki的一个extension,叫UsabilityInitiative,下载与自己wiki版本对应的程序版本,需要翻墙。
最简单的流程是按照官方页面给出的方法,省时省力,又不费脑细胞。别像我一般傻,折腾一大堆,最后才恍然大悟!
官方方法:
- Get the extension with distributor or svn and drop it into MediaWiki directory /extensions
- Setup your LocalSettings.php as explained in README file.
- Run
php maintenance/update.phpfrom the command line (see update.php and also here)
This is an example, stable configuration for your LocalSettings.php file for the 1.16 release version:
// UsabilityInitiative/Vector
require_once("$IP/extensions/UsabilityInitiative/Vector/Vector.php");
$wgVectorModules['editwarning']['global'] = false; // Don't enable EditWarning globally
$wgVectorModules['editwarning']['user'] = true; // Allow users to enable EditWarning in their preferences
$wgVectorUseSimpleSearch = true; // Need this as well for SimpleSearch
$wgDefaultSkin = 'vector'; // If you want to change the default skin for new users
$wgVectorUseIconWatch = true; //Enable star icon to add/remove page from watchlist
// UsabilityInitiative/WikiEditor
require_once("$IP/extensions/UsabilityInitiative/WikiEditor/WikiEditor.php");
$wgDefaultUserOptions['usebetatoolbar-cgd'] = 1; // Default user preference to use toolbar dialogs
$wgWikiEditorModules['toolbar']['global'] = true; // Enable the WikiEditor toolbar for everyone
$wgWikiEditorModules['toolbar']['user'] = false; // Don't allow users to turn the WikiEditor toolbar on/off individually
接下来我讲讲我的过程:
- 读README文件。我当时没有找到这个页面,而是找到了WikiEditor页面,结果走了一大圈弯路(我好傻,居然没有看到上面的醒目提示,说
This extension has been migrated from Extension:UsabilityInitiative.)。我当时看到那个页面上说按README做,而页面链接过去的README是svn最新版本的,不是1.16的,结果怎么都不行。后来看自己目录下的READEME才隐约领会到,最后几乎把整个README内容都copy到了LocalSetting.php里面(找对书籍和信息很重要,不然会误入歧途!)。 - 执行SQL。当时我以为把LocalSetting.php设置对了就搞定了,没想到到页面上设置的时候却报SQL错误,说表不存在。半天没有找到初始化配置页面,只能动手登录mysql,然后手动执行那些找出来的sql文件。sql文件在哪?在extensions/UsabilityInitiative/下find一下吧。
配置
过程很简单,但不要走错,错了就复杂了!
VIM查找时需要转义的字符
首先,VIM中在查找时使用正则表达式进行。但是,不能将平常的正则表达式直接搬过来使用。原因是标准正则中有些符号与VIM中的一些固有符号有冲突,这时就需要对这些符号转义,才能表示这个符号表示正则表达式的操作符,否则表示VIM操作符或者字符本意。
对于这些将在VIM Search时需要转义的字符总结如下:
- |
- &
- ()
- +
- =
- ?
- \{n,m} n到m个,按最多匹配
- \{-n,m} n到m个,按最少匹配
- <> 匹配单词边缘
命令行下批处理图片
每次拍完照片之后,因为照片太大无法直接上传到空间里面,需要将照片缩小。这时总是因为工作量太大而无法让人望而生畏。如果使用windows,而且手头又有photoshop,那很好。因为photoshop里面有个批处理功能叫Action,但是没有在windows下,更没有photoshop,有没有简便的方法可以做这种简单重复的事情呢?
答案是肯定的。那就是命令行的”imagemagick”!imagemagick是一组命令,包括convert, identify, mogrify, composite, montage, compare, stream, display, animate, import, conjure.
使用convert命令对原图做缩放处理,并将缩放后的图片放到resized/目录中:
find . -maxdepth 1 -name '*.JPG' -exec convert {} -resize '1024x1024' resized/{} \;
某些网站的图片上传功能要求后缀名小写,这时需要做一次转换。使用如下命令:
for f in *; do mv $f `echo $f | tr '[:upper:]' '[:lower:]'`; done
或者使用如下命令(更简单):
find resized -depth -exec rename 's/(.*)\/([^\/]*)/$1\/\L$2/' {} \;
一点关于西厢计划安装配置的问题
对于崇尚自由的人们来说,西厢计划简直就是神来之笔,它让我们在这个黑暗的年代看到了曙光,为我们打破信息谎言创造了途径。最值得赞叹的是:这是规则之下的博弈。gfw今天棋逢对手了!
具体的编译、安装和配置请看http://code.google.com/p/scholarzhang/wiki/INSTALL。
配置请看http://code.google.com/p/scholarzhang/wiki/USAGE
为了不重复发明轮子。在此,我仅仅提供一些应对问题的经验。
如果按照官方文档的指引,基本能够成功,但是从留言和我自己的经历来看,在中途可能还是有一部分人会遇到一点问题而成功不了。这些问题官方文档没有提及,也许是因为软件作者本身水平太高,这些问题基本遇不到,或者顺手就解决了,也就没有正式的写道wiki中。
好了,说正题。
- 问题一。大部分朋友会遇到找不到下面三个文件:
/lib/xtables/libipt_ZHANG.so
/lib/xtables/libipt_CUI.so
/lib/xtables/libipt_gfw.so
到/lib/xtables/下的确找不到这三个文件。不过编译安装后我们可以在/usr/local/libexec/xtables/下找到文件名相似的个文件(而源文件目录下,生成的也只有这几个文件,可以断定就是他们几个)。因此,可以从/usr/local/libexec/xtables/下创建链接文件到/lib/xtables/下:cd /lib/xtables sudo ln -s /usr/local/libexec/xtables/libxt_CUI.so libipt_CUI.so sudo ln -s /usr/local/libexec/xtables/libxt_ZHANG.so libipt_ZHANG.so sudo ln -s /usr/local/libexec/xtables/libxt_gfw.so libipt_gfw.so
- 问题二,IP地址解析仍然不正确。即使正确设置了自己的DNS server为8.8.8.8也解析不到正确的www.youtube.com地址。反复检查了/etc/hosts文件以及/etc/resolv.conf文件都找不到哪里出了错。于是浏览器要么出现网络错误,要么出现refuse错误。如果使用命令
nslookup www.youtube.com
则一如既往的错。但如果使用nslookup的交互模式,并设置了dns server 为8.8.8.8,则能够解析到正确的IP地址。其实,这是因为在还没有执行ipset和iptables命令之前访问了youtube,那时DNS被污染,因此本地的DNS cache记录了被污染的DNS地址。通过执行下面的命令可以清除本地DNS Cache:
sudo resolvconfig -d wlan0 sudo resolvconfig -d eth0
- ipset的规则是什么,example中的规则怎么理解?
example中的规则一般如这样”-A YOUTUBE 208.117.224.0/19 ”。前面的-A和YOUTUBE 容易理解。但是”208.117.224.0/19″是什么?其实,这是表示一个网段,将208.117.224.0转化为二进制后应该是”11010000.01110101.1110000.00000000″,每段8位,第19位是第三段第三位,就是最后那个1上。”208.117.224.0/19″就表示二进制的前19位是相同的,这表示一个网段的IP地址。与子网掩码表示的方法不同,但意思是一样的。
每个步骤保证正确之后,可以写成一个脚本,以简便每次启动过程:
首先,导出刚才的那些设置:
sudo ipset -S > ipset.ruls sudo iptables-save > iptable.ruls接着,把它们放到/etc目录下面,(不能放到/home目录下,因为到时候会用sudo执行,root用户访问不到自己的目录):
sudo mkdir /etc/west-chamber sudo chmod a+w /etc/west-chamber cp ipset.ruls iptable.ruls /etc/west-chamber/最后,写一个脚本,并赋予可执行权限:
sudo touche /usr/local/sbin/west-chamber.sh#!/bin/sh #restore rules ipset -R < /etc/west-chamber/ipset.ruls iptables-restore < /etc/west-chamber/iptable.ruls #set trusted nameserver echo nameserver 8.8.8.8 > /etc/resolv.conf echo nameserver 8.8.4.4 >> /etc/resolv.conf #clear the resolve catch resolvconf -d wlan0 resolvconf -d eth0sudo chmod a+x /usr/local/sbin/west-chamber.sh现在就可以执行
sudo west-chamber.sh来启动西厢计划了。
现在打开youtube看看吧!
附A:一些有用的命令:
查询所有www.youtube.com的IP并保存到文件中:
$nslookup www.youtube.com | grep Address | grep -v '#53' | awk '{print $2}' >> www.youtube.com.ip清除本地的dns缓存:
$resolvconfig -d wlan0 $resolvconfig -d eth0统计有多少个IP:
$cat www.youtube.com.ip | sort -g | uniq | wc -l

