Evan's Blog

Java, software development and others.

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  28 随笔 :: 0 文章 :: 73 评论 :: 0 Trackbacks

2006年3月6日 #

最近一个项目需要考虑多语言的用户,自然就想采用utf-8编码,所有JSP页面都修改完毕,一切运行正常,由于一直使用FF来检测浏览器兼容性,今天不知怎的决定用IE来跑一下,发现所有表单提交的页面都提示JS错误,查了一下,发现是一个验证身份证的方法中有一个数组的初始化中保存了各个省市的名字,类似于:var area=['上海','北京']之类的形式。一般情况下,JS错误我都是在FF中用FireBug调试的,可偏偏在FF中一切正常,这下就傻眼了,注释掉这个语句自然没问题,可怎么也想不出这么正常的一个赋值怎么会弄出个浏览器兼容的问题出来。折腾了一个下午,突然想到,是不是编码的问题啊,将这个JS用utf-8编码另存,一切ok。

另外,采用utf-8编码之后,用WinMerge比较文件时,就会出来Information lost to encoding errors:之类的提示,心想,这个指定个编码就可以了吧。在编辑=>选项中,果然看到代码页的指定,如是选择指定代码页,输入utf-8,点击确定,被提示请输入一个整数,估计这时候大多数人和我一样傻眼,utf-8的代码页是多少?好在有Google,调整不同的关键字,终于找到清炒苦瓜的一篇文章中提到utf-8的代码页是65001,并且也是为了解决WinMerge的乱码问题,可是改完之后,并没有起作用,这次没有去怀疑这个代码页是不是正确,再仔细看一下那篇文章,嗯,人家明明说用的是2.6.8嘛,检查一下自己的版本,2.6.0,是不是版本问题呢?来到WinMerge的网站,发现竟然已经是2.6.12了,于是下载最新版本,然后将代码页改成65001,嗯,这下所有的中文注释都乖乖出来了。
posted @ 2008-01-05 21:41 Evan 阅读(2825) | 评论 (1)编辑 收藏

     摘要: Apache反向代理设置  阅读全文
posted @ 2008-01-04 21:13 Evan 阅读(5945) | 评论 (2)编辑 收藏

     摘要: 这个Blog,就象我久未清理的书桌,已经落了厚厚的一层灰尘,在这个新年的第一天,决定用这样一个够噱头的标题,以一篇够水的日志,来告诉自己,已经是2008年了......  阅读全文
posted @ 2008-01-01 13:22 Evan 阅读(313) | 评论 (0)编辑 收藏

     摘要: 不要假装我们是一个文明古国了,传统早已割裂,我们是个无根的民族,精神一片荒芜,伪造出的传统只加剧了我们的虚伪,凸显了我们的空洞与脆弱。  阅读全文
posted @ 2007-08-31 21:28 Evan 阅读(218) | 评论 (0)编辑 收藏

     摘要: 你是不是从svn 1.2甚至更早的版本就开始用Subversion了?是不是在用svnserve做服务器?是不是很认真的读了svn自带的文档?那么,很有可能,你也象我一样,被它小小的忽悠了一把:)。  阅读全文
posted @ 2007-04-01 14:33 Evan 阅读(3265) | 评论 (1)编辑 收藏

     摘要: 在网上兜到一篇文章,从讲述一个译者和一家出版社之间的纠纷,引申出了一些待人处世的道理,主要是关于一个人碰到不公平的事情的时候,应该怎样办,觉得颇有道理。什么样的人才是有力量的人呢,也许是那些能合理处理不合理的事情,甚至能让不合理的事情最终转化为多赢局面的人吧。人生要真能达到这样一种境界,该是怎样一副海阔凭鱼跃(管它波涛汹涌还是风平浪净),天高任鸟飞(管它乌云闭日还是阳光普照)的从容画面。  阅读全文
posted @ 2006-12-30 19:48 Evan 阅读(1068) | 评论 (1)编辑 收藏

好久没有更新Blog了,看到还有人在关注这个Blog,很是惭愧。发现还是有人在使用Polarion的SVN Web Client,并且碰到了一些小问题,尽管我工作中几乎不用这个客户端,但当初安装的版本依然还在,所以决定稍微研究一下,但愿能给喜好这款软件的人一些帮助。

1. 为什么老是提示“Your credentials are not correct. Please check them and try again. ”?
这个多半是svn初学者常碰到的问题,尤其在使用HTTP协议的时候。一开始大家都用svnserve来做svn的服务器,自然配置的是conf中的passwd。但如果采用HTTP协议的话,就得使用Basci HTTP Authentication了,需要用Apache提供的htpasswd来管理用户和口令。这个的配置在svn自带的帮助文件中第6章“httpd, the Apache HTTP Server”一节中有比较详细的解释。但如果你没有通篇看完,在Apache中没有加上Require valid-user指令的话,那是允许匿名操作的。我想,你不愿意留下这样的安全漏洞吧。

2. 怎样配置多个repository
这个也是实际中需要的,当然,在它的readme中其实是说得很清楚的。但我们有些同志就是喜欢拿来就试,尤其是在有些类似于我这种其实语焉不详的文章时,更是就喜欢照葫芦画瓢,而不去看最权威最原始的英文文档了。要配置多个repository,以HTTP协议为例,在web.xml中要删掉RepositoryUrl、Username、Password这3个参数的设置,然后加上ParentRepositoryDirectory参数,值自然是指向svn仓库的父目录了,比如http://localhost/svn/,这个东西又是哪里来的呢?自然需要在Apache中配置,用SVNParentPath来指定svn仓库的父目录,Apache会自动解析其下所有的仓库的。这里要注意一下AuthzSVNAccessFile授权文件的写法,这里将配置所有仓库的存取权限,对于每个仓库,需要用[仓库名:/module]的方式来配置。

3. 怎样使用svn协议
前面我一直用http协议做例子,实在是因为我在其2.5.0下没有配置出来过svn协议:(。这次去其网站下载了个最新的nightly版本,发现其已经能够支持http, svn, svn+ssh, ssl和proxy等6种协议了。看看其代码结构,好象也发生了很大的变化,估计应该有比较大的改进。于是,用这个版本试了一把,呵呵,轻而易举就把svn协议给连通了,包括多仓库的情况。并且其还改进了原来设置父目录地址时一定要在最后添加/的要求,估计原来在这个地方卡壳的朋友也不少吧:)。不过,新版本还是不支持中文文件名,看我以前的帖子自己改吧。

Important: 由于svn webclient采用的javasvn(现已更名为svnkit)版本较低,用svn协议在提交老的文件时会失败,但添加新的文件时没有问题,所以,大家就不要再尝试svn协议了。如果不采用SVN协议,则其官方发布的版本就没什么问题了,已经有网友重新打包了一个解决了中文文件名的版本,到这里下载。(Updated: 2007.1.20)
posted @ 2006-12-27 13:04 Evan 阅读(3620) | 评论 (2)编辑 收藏

     摘要: 使用Jarkata FileUpload最新版本解决SVNWebClient提交中文文件名或注释时出错的问题。  阅读全文
posted @ 2006-05-14 20:11 Evan 阅读(4815) | 评论 (11)编辑 收藏

     摘要: 在我的“[推荐]两款好用的SVN Web Client”一文中曾经提及我所看好的sventon中文支持不好的问题,于是在其论坛中提了个问题,今天收到他们的邮件,称在其最新的代码中已经解决了中文的问题,今天下载更新后,发现的确解决了这个问题,呵呵,更新速度挺快的。  阅读全文
posted @ 2006-04-12 15:41 Evan 阅读(1624) | 评论 (3)编辑 收藏

     摘要:   Everything is meaningless...假设你突然死掉,世界将会怎样?世界将一样绚丽,地球转的一样快,太阳系每天在宇宙中换一个位置。大海还是大海,波涛还是波涛,一样的花开花落,潮起潮落。...你是多么可怜的小虫子,在活着的短暂岁月里,在最美好的青春里,都不曾快乐过,用尽心力去聚集一大堆外在和心灵没有关系的小东西,只是出于对未来的没有信心,小小的心灵在接近熄灭的一天还在发出那个愚蠢的声音,让你忙碌,让你忧虑的声音:我要,我还要。天底下充满了这样的小虫子,当一个离开了,又有一个来了,做着同样的事情,汹涌着同样的小小念头,受着同样的煎熬。于是上帝要感慨了:虚空的虚空,凡事都是虚空。已有的事,后必再有;已行的事,后必再行。日光之下,并无新事。
  ...
  不要忧虑“不要为明天忧虑,天上的飞鸟,不耕种也不收获,上天尚且要养活它,田野里的百合花,从不忧虑它能不能开花,是不是可以开得和其它一样美,但是它就自然的开花了,开得比所罗门皇冠上的珍珠还美。你呢,忧虑什么呢? 人比飞鸟和百合花贵重多了,上帝会弃你不顾吗?”  阅读全文
posted @ 2006-04-09 22:13 Evan 阅读(979) | 评论 (0)编辑 收藏

     摘要: 推荐两款纯Java的SVN Web Client软件。其安装使用均比ViewVC要好!  阅读全文
posted @ 2006-04-06 00:30 Evan 阅读(19243) | 评论 (21)编辑 收藏

     摘要: 前几天好奇,也刚好得了点空闲,然后就想看看ViewVC对Subversion的支持程度,于是就想装个玩玩。好死不死的,在我的VMWare Workstation上刚好有个Windows Server 2003,心想,就它吧,可就这么一偷懒,折腾了我好几天,最终还是只能算将就着把它给装上了。  阅读全文
posted @ 2006-04-05 00:28 Evan 阅读(7686) | 评论 (5)编辑 收藏

常常能从galeer(嘎荔儿)的网络日志里听到一些比较好听的歌曲,并且那些点评也不错。今天听到U2 - With or Withou You,首先,这首歌还真不错;其次,不知是这首歌原来就带的呢,还是galeer自己加上了这样几句诗:

我虽然行过死荫的幽谷,
也不怕遭害;
因为你与我同在;
你的杖,你的竿,都安慰我。

--《诗篇第二十三篇第四节》

当然,一开始我不知道这是《旧约全书·诗篇》中的内容,于是就Google了一把,找到下面这篇文章《因为你与我同在》:


我去一个乡村教会讲道,在接待的弟兄家休息。看见弟兄的儿子学习很用功,很是喜爱。我请小朋友将他的语文课本借给我看。这是人教版小学四年级语文课本。当中有一篇文章吸引了我——

1989 年美国洛杉矶大地震,30万人在不到4分钟时间里受到不同程度的伤害。这其间一个小学的惨况让人心酸。多少孩子的父母因在地震中痛失爱子而哀声闻天,面对 地震后的学校废墟只能绝望离去。但一个父亲却在废墟中不断地挖掘寻找自己那才七岁的叫阿曼达的儿子。救火队长挡住他,“太危险了,随时可能发生大爆炸,请 你离开。”别的学生家长也劝说,“太晚了,没有希望了。”可这位父亲不顾这些劝阻,在废墟中不断地寻找。就在他挖了整整八个小时后,父亲听见瓦砾堆底下传 出他十分熟悉的声音,“爸爸,是你吗?”这时他发现有14个孩子在当中,因为这是在教室墙角的位置,当房顶塌下时架成大三角形,孩子们才没被砸着。“我告 诉同学们不要害怕,说只要我爸爸活着就一定会来救我,也能救大家。因为你说过,不论发生什么,你总会和我在一起!”孩子对爸爸说。“爸爸,先让我的同学出 去吧,我知道你会跟我在一起,我不怕。不论发生了什么,我知道你总会跟我在一起。”

14个孩子获救了!

我们不禁会问,如果因为大爆炸的危险而绝望地放弃,如果这个父亲和其他的家长一样绝望地离开,如果阿曼达没有“因为你与我同在”的信念,那结果又将如何?

我想说的是,绝望让生命失去,希望使生命存留。

诗人说,“因为你与我同在。”(诗篇23:3)这样的信心使诗人在人生的黑夜里依然有生命的曙光,在人生的冬天里可凭信宣告说,冬天来了,上帝的春天也不再遥远。

“因为我与你同在,要拯救你。这是耶和华说的。”(耶利米书1:8)

    阿曼达对父亲单纯的信念应一如我们对天上的父亲执著的信仰。



是啊,“绝望让生命失去,希望使生命存留”,不过呢,对于我这个不信教的人来说,大可抱着象故事中的小阿曼达相信他父亲只要活着就一定会来救他的希望一样,也不一定非要抱着耶和华会来救我们的希望的。不过,不管信什么教,基督也好,佛陀也好,其实所有经典中的智慧都是值得学习的;当然,如果你在这个浮躁的物质社会中实在找不到可以依赖的东西时,信个把教也未尝不可。
posted @ 2006-04-01 21:33 Evan 阅读(574) | 评论 (0)编辑 收藏

     摘要: SVN会取代CVS吗?这个虽然不是我们这种小程序员能决定的大事,但学学总无妨吧,这里有一些我搜集的资料。  阅读全文
posted @ 2006-03-23 23:55 Evan 阅读(3185) | 评论 (0)编辑 收藏

     摘要: 你是不是为了高的测试覆盖度而在为每个函数添加多个测试方法,甚至连getX()和setX()都不放过呢?或者,你一看到覆盖度达到100%的代码,景仰之心就开始“有如滔滔江水绵绵不绝,又有如黄河泛滥,一发不可收拾”了呢?那么,你应该读读Andrew Glover在最近的developerWorks上发表的这篇文章。  阅读全文
posted @ 2006-03-18 23:23 Evan 阅读(1199) | 评论 (0)编辑 收藏

     摘要: 《Java Threads》的第5章“Minimal Synchronization Techniques”,是这本书中到现在我认为最差的一章了,当然主要是我不喜欢JDK 1.5新推出的Atomic Class,而这一章却花了不少篇章来介绍,且牵强地改造打字程序,又语焉不详地指出这种改造的困难之处和可能带来的副作用,但却又不能从代码的实际运行中看到这种副作用,很有误导初学者的嫌疑。不过,我想,没有哪个初学者会冒风险为了用Atomic Class而将原本简单明了的算法改造得如此晦涩难懂,并且还有潜在的出错风险。所以,对于Atomic Class,我建议跳过不读,绝对没有什么损失。不过对于其中“5.1.3 Double-Checked Locking”和“5.3 Thread Local Variables”这两节倒要着重读一读,尤其是Thread Local,应该说是Java中一个比较重要的多线程工具。  阅读全文
posted @ 2006-03-11 23:11 Evan 阅读(1651) | 评论 (0)编辑 收藏

     摘要: 快来看“洋本山”怎样忽悠一个只想买一把锤子的人最后买了一个工具工厂的建造工厂的通用建造工厂。很别扭是吧,但如果你是个开发Web应用的Java程序员,你也许已经或者正在被忽悠。  阅读全文
posted @ 2006-03-11 17:04 Evan 阅读(5753) | 评论 (23)编辑 收藏

看到《Java Threads》第5章,介绍了JDK 1.5新加的一些所谓原子类(Atomic Classes),总感觉有点为原子而原子,实际操作中,又有多少人会为了少许的性能提升而刻意去用这些别扭的操作而放弃直观的synchronize关键字或者Lock类呢?不过,这里不是想讨论这个,而是当其用Atomic Classes来改造它的打字程序后,解释用原子类只是保证类似递增、递减、赋值等操作的原子性,而不能保证其所在的方法一定是线程安全的,然后说,有可能按键事件的处理可能需要等待resetScore()处理完才能执行,而这会导致错误的评分(被当成多敲了键)。由于前几章的内容相对比较简单易懂,所以也没有很仔细的运行那些例子。这里为了验证一下,就运行了一下第4章的例子,然后发现,基本上第一次的评分总是错的。这就引起了我的注意,因为,一般情况下,如果是race condition导致的错误是很难重现的,这么明显的错误很可能是程序逻辑上的错误。仔细看了一下代码,发现在start按钮的事件处理方法里,有下面这样一段代码:
startButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
                displayCanvas.setDone(false);
                producer.setDone(false);
                startButton.setEnabled(false);
                stopButton.setEnabled(true);
                feedbackCanvas.setEnabled(true);
                feedbackCanvas.requestFocus();
                score.resetScore();
            }
        });
注意重置成绩的调用放在了最后,此时,随机生成字符的线程应该被唤醒并产生了第一个字符,然后,resetScore()将需要输入的字符又设成了-1,所以,当你第一次输入字符时,总是被认为是多击了一次键而扣1分:(。既然这样,那停止然后再启动也应该会发生这个错误啊。而事实上的确是这样。我想,这不应该看做是race condition吧,有什么样的同步技术能够避免这个问题呢?除非另外弄个标志,当成绩没有被重置前,不能产生第一个字符。当然,这是不需要的,只要将score.resetScore()放到第一句就可以了。

然后又运行了第3章的例子,发现基本上没有这个问题。难道第3章的代码是正确的?打开源代码一看,重置成绩的方法还是放在最后,那这里为什么又是正确的呢?我想,大约是第3章的例子中,每次点击start按钮,都重新创建一个线程对象的原因吧。由于创建对象和初始化线程需要一定的时间,刚好给了主线程重置成绩的机会。

不知道作者有意为之呢,还是疏忽,不过,这样的错误不能算是race condition的例子。
posted @ 2006-03-09 22:11 Evan 阅读(845) | 评论 (0)编辑 收藏

第3章主要介绍了数据的同步(Data Synchronization),这一章则主要介绍线程之间的同步方法(Thread Notification),同样包括传统的wait-and-notify方法和JDK 1.5新推出的Condition Variable。在多线程编程中,数据同步和线程同步是两个最基本也是最关键的部分。
《Java Threads》一书中通过考察打字程序中当按下start和stop按钮后,每次都创建两个新的线程的效率问题来引入线程同步的概念,当然不是线程同步的主要用处。不过,教科书归教科书,实际运用则又是另一回事。所以,通过书本学习语法,通过实践来获得运用经验。

4.1 Wait and Notify

1. 等待/唤醒类似于Solaris或POSIX中的条件变量(conditon variables),或者Windows中的事件变量(evant variable)或者信号量(signal),用于某个/多个线程暂停等待某个条件的满足,而该条件将由其它线程来设置的情况。
2. 在Java中,就像每个对象有一个锁之外,任何对象都可以提供等待/唤醒的机制。就像Java中的synchronized总是表示获得某个具体对象的锁一样,wait和notify也总是等待某个具体的对象,并由该对象唤醒;同样,获得某个对象上的锁不一定是该对象需要同步一样,等待和唤醒的条件也不一定是与之绑定的对象。
3. Java中wait-and-notify的几个方法:
void wait(): 使当前线程处于等待状态,直到其它线程调用了nofity()或者notifyAll()方法为止。
void wait(long timeout): 使当前线程处于等待状态,直到其它线程调用了nofity()或者notifyAll()方法,或者超过了指定的时间(单位为ms)为止
void wait(long timeout, int nanos):与wait(long)一样,只是在某些JVM中可以精确到奈秒。
void notify(): 唤醒一个正在等待该对象的线程。
void notifyAll(): 唤醒所有正在等待该对象的线程。
注意:任何等待和唤醒方法都必须在与之对应的对象的同步方法或同步块里调用。即:wait-and-notify必须和与之对应的synchronized关键词一起使用的。
4. wait()和sleep()的主要区别:
  1) sleep()可以在任何地方调用,而wait()需要在同步方法或同步块中调用;
  2) 进入wait()函数时,JVM会自动释放锁,而当从wait()返回即被唤醒时,又会自动获得锁;而sleep()没有这个功能,因此如果在wait()的地方用sleep()代替,则会导致相应的nofity()方法在等待时不可能被触发,因为notify()必须在相应的同步方法或同步块中,而此时这个锁却被sleep()所在的方法占用。也就是说,wait-and-notify不可能与sleep()同时使用。

4.1.1 The Wait-and-Notify Mechanism and Synchronization
1. 这一节详细的讲解了wait-and-notify机制和synchronized的关系,主要是两点:1)wait-and-notify必须和synchronized同时使用;2)wait()会自动释放和获取锁;
2. 这一节中举了一个例子用来解释可能存在当条件被不满足时也有可能被唤醒的情况:
  1) 线程T1调用一个同步方法;
  2) T1检测状态变量,发现其不满足条件;
  3) T1调用wait(),并释放锁;
  4) 线程T2调用另外一个同步方法,获得锁;
  5) 线程T3调用另外一个同步方法,由于T2获得了锁,所以处于等待状态;
  6) T2修改状态变量,使其满足条件,并调用notify()方法;
  7) T3获得锁,然后处理数据,并将状态变量又设置为不满足条件的状态;
  8) T3处理完毕返回;
  9) T1被唤醒,但实际上此时条件并不满足。
这个例子刚好印证了《Effective Java》中"Item 50: Never invoke wait outside a loop"和《Practical Java》中"实践54:针对wait()和notifyAll()使用旋锁(spin locks)"。即总是用下面这种方式来调用wait():
    synchronized(obj) {
while(<condition does not hold>)
wait();

... // Perform action appropriate to condition }
或者象《Practical Java》中一样:
    synchronized(obj) {
while(<condition does not hold>) {
try {
wait();
} catch (InterruptedException e) {}
}

... // Perform action appropriate to condition }
3. 调用wait()的线程T可能在以下几种情况被唤醒:
  1) 其它线程调用了notify(),而刚好线程T得到了通知;
  2) 其它线程调用了notifyAll();
  3) 其它线程中断了线程T;
  4) 由于JVM的原因,导致了spurious wakeup。

4.1.2 wait(), notify(), and notifyAll()
1. 正像多个线程等待同一对象上的锁,当锁释放时,无法确定哪个线程会得到那个锁一样;当有多个线程在wait()时,当另外一个线程调用nofity()的时候,也不能确定哪个线程会被唤醒; 2. 因此在《Practical Java》的"实践53:优先使用notifyAll()而非notify()"建议的一样,结合实践54,可以比较好的解决线程唤醒的问题。

4.1.3 Wait-and-Notify Mechanism with Synchronized blocks
再次强调必须在同一个对象的synchronized方法或块内调用该对象上的wait和notify方法。

4.2 Condition Variables

1. 就像上面反复强调的一样,wait-and-notify机制是与特定对象及其上的锁是绑定在一起的,锁和唤醒对象不能分开,这在某些情况下不是很方便;
2. JDK 1.5提供Condition接口来提供与其它系统几乎一致的condition variables机制;
3. Condition对象由Lock对象的newCondition()方法生成,从而允许一个锁产生多个条件变量,可以根据实际情况来等待不同条件;
4. 该书的例子没有什么特别的实际意义,但JDK 1.5文档中提供了一个例子,能充分说明使用Condition Variables使得程序更加清晰易读,也更有效率:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
具体的说明请参考JDK 1.5的文档。
5. 除了用lock和await-and-signal来代替synchronized和wait-and-notify外,其语义和机制基本一样。await()在进入前也会自动释放锁,然后再返回前重新获得锁;
6. 使用Condition Variables的原因:
  1) 如果使用Lock对象,则必须使用condition variables;
  2) 每个Lock对象可以创建多个condition variable.
posted @ 2006-03-06 22:21 Evan 阅读(823) | 评论 (0)编辑 收藏