2009年11月20日星期五
墙很大,好久没有回来更新了。
工作半年多了。留个脚印吧。
博客,是个很奇怪的东西。我自己做的也是博客,却要在墙外写。原因就不说了。
2009年10月23日星期五
Lady bug
2009年10月22日星期四
Milkweed Tussock Moth
Shield bugs
2009年10月16日星期五
2009年10月14日星期三
2009年9月28日星期一
2009年9月28日
2009年9月18日星期五
2009年9月10日星期四
推荐一个优秀的网盘,2G空间
注册一下这个吧,上次我和你说的网盘,2g的,然后邀请一个人还能得到250M的空间。上边的链接就是我的邀请
【按】:国内现在已经无法访问这个优秀的网盘服务了。
2009年9月8日星期二
夏日午后的阳光
我突然想起那里有一株木瓜树。林师姐说,在她的家乡,人们都煮木瓜喂猪。可是其他女人们都告诉我木瓜很丰胸的。低头看自己日渐丰满的乳房,心都碎了。
在乾宁宫附近有小荷花池,荷叶上有我扔的三个硬币,如今安在?
我说"乾宁宫",是为了叙述更富真实感,好像我真得到过那里。你看我都说得出名字呢 ,不是吗?我说"附近",是因为我不确定那个小荷花池是否真的存在,真的有荷叶吗?真的像佛教徒寺庙中那些放生池一样,可以随便游人放钱许愿吗?我难道突然不吝啬了,扔了钱,那我一定是许了什么愿望吧。可那是什么呢,可是然后呢?
夏日午后的阳光,斑驳耀眼,落在庸和宫的石板地上。你在那里接了个长长的电话 。我在那里无聊抬头看电线杆,然后把它拍下来。铜线一匝一匝紧密有序地绕在陶瓷上。那个电工一定是个细心且耐心的人。
2009年9月4日星期五
“博客心情”+“关注”=“微博”
2009年9月1日星期二
2009年8月27日星期四
2009年8月23日星期日
2009年8月20日星期四
虫灾和食物短缺
我记得的很小的时候,大概在1967年的样子巴,那年长江中下游地区发生了百年不遇的蝗灾。我这辈子都没见过这么多的蝗虫。整片整片的天空都是蝗虫飞过,遮天蔽日。人们躲在屋子里,窗户门逢都堵得严严实实。即便这样,屋子里也依然有很多蝗虫。那时候穷,没东西吃,标题不是说食物短缺嘛。面对着这么多的蝗虫,刚好这些该死的蝗虫又肆虐着我们的粮食。大胆的人便开始用吃来报复蝗虫。你还别说,人的创造力,尤其是我们中国人在吃方面的创造力,你不得不佩服。村里面的人最先是将蝗虫往带火星的灰烬里扔,利用灰烬的温度将蝗虫烤熟。蝗虫烤熟后,翅膀没了,两只大腿也掉了,光秃秃就一个身体,将头摘掉,整个都可以吃。可是大腿上的肉很鲜嫩不能浪费,所以都在灰里费劲地扒出来,以免浪费里这可口的味道。渐渐地,人们有经验了。烤之前先将蝗虫的腿摘下来,单独放在锡纸上烤。再后来,人们还发明里使用油炸的方法烧蝗虫。那真是人间美味,在往后的岁月里,我一直怀念那时油炸蝗虫的味道。偶尔外面蝗虫稍少的时候,像我等顽皮的小孩童也会出门再村子里乱跑。这个时候,我发现整个村子都是油炸蝗虫的味道。每家每户,不是正在吃油炸蝗虫,便是正在油炸蝗虫。
大概半个月的样子吧,蝗虫大部队终于飞走了,就像早些年国军撤退那样子,一下子消失里。这个时候躲在家里,已经抓不到足够的蝗虫来油炸了。大人们疯了一样出门抓蝗虫。脾气冲动地年轻人甚至为一两只肥大的蝗虫大打出手,原因无非是到底是谁先看见这只蝗虫,谁先抓住这只蝗虫的。手脚已不灵活的老妇人们,就在家里架起神坛,拜天拜地,祈祷上苍让蝗灾更持久一些,让蝗虫们不要走。油炸蝗虫实在太好吃了。
再后来,村里终于连一只蝗虫都抓不到了。大人们常常抬头看天空,希望那远处飘过来的不是云,而是大片大片的蝗虫。再后来再也没有人见过什么是蝗虫。
==============分割线===================
其实,虫灾是因为有个叫里傻的人,从家里带一袋金银花(据说是一种蝗虫的食物),生了虫子,搞的家里到处都爬满虫子。睡觉的时候,你能看到天花板上有一条小虫子在蠕动,你够不到它,也计算不出它什么时候掉下来。
还有食物短缺,不是因为虫灾,是因为公司停止供应免费零食了。所以常常很饿,常常想起早些年吃的那些油炸蝗虫。
2009年8月12日星期三
2009年8月10日星期一
历史在重演,在这烦嚣城中
国内新闻跟贴将施实名制
日前,《大公报》报道称,国务院新闻办公室7月27日下发通知,要求国内各新闻网站取消现行的新闻稿件匿名跟帖或发帖功能,转而实施实名注册登陆制 度,即网民在新闻网站注册时必须填写真实姓名、身份证号码等信息,通过验证后方可登陆跟帖或发帖,对新闻事件发表言论。网民在跟帖或发帖上传信息时,各新 闻网站必须提示网民需经审核方可发表,而网民亦需对自己在网络上发表的言论承担社会责任。同时,各新闻网站不能再针对新闻事件设置网民投票或民意调查等。
=====================
早上开会,说博客跟帖也需要某种形式的实名了,政府要求的。
2009年8月3日星期一
无聊的时候,我们看电影
周末,天有阵雨。两天都宅着看电影。
刻意去找了一些丹泽尔点华盛顿的片子,基本上都是老片,可能是10年之前的片子。华盛顿先生演的角色一个主要特点就是,都是黑人,而且都是男人。
这次看华盛顿先生演的几个角色,大都是美国黑人斗争比较恶劣时候一些代表性的传奇人物吧。华盛顿先生可能比较喜欢这类角色,谁让他也是黑人,谁让黑人现在还遭受歧视呢。
remember the Titians: 基于真人真事改编,黑教练,和白教练,白学生,还有黑学生之间的故事。
glory: 基于真人真事改编,他不是主角,里边还有摩根点弗里曼老头。 南北战争中,第一个黑人兵团,到南部作战。
the hurricane: 基于真人真事改编,一个黑人拳击手,在种族歧视严重的时代,被判入狱,监禁终身。16年后出狱。历史的车轱辘已经转到了1985年。我都3岁了。
the great debaters: 基于真人真事改编. 一个黑人老师,带领学生,创造了几乎不败的奇迹。辩论的发言,能让你流泪。
虽然不是本着被教育的精神去看的这些电影,还是感觉自己被教育了。影视能做的当然不仅是记录,也有宣传和反思。也许有一天我们也能看到关于达赖喇嘛和热比娅的宣传,让我们也知道,原来我们也生活在一个多民族的国度中。
2009年7月26日星期日
七月周末
2. 然后去西湖边散步。湖面上有风,挺凉爽,很适合情侣们散步。给GK电话,不着边际地聊,说自己的工作,问他们的情况。知道他们都很好,我也很好;他们都很无聊,我也无聊。我很好,只是有点想你们。我想去吃点烤串,顺便来点啤酒,当然是和你们一起,要半夜的那种。
3. 从我身边走过很多牵着手的人,一对一对。不知道这每一对牵手后面有多少妥协和委屈,有多少命令和征服。当然还有甜蜜和恩爱,只是感觉可能会少很多吧。然后有人告诉我们,那个愿意留下来陪你争吵的人,才是爱你的人。
4. 回来的路上,一只羽毛球从我头上飞过,被后面的电瓶车压成二维。飞的太高,摔的很残。
5. 口渴,想喝水。没开水,才想到有订牛奶。去奶箱取奶,早上的牛奶在杭州这个天然烤炉中,已经发酵膨胀啦。我不是渴死的,是懒死的。
2009年7月15日星期三
GeoCities is closing
2009年7月11日星期六
2009年7月8日星期三
2009年6月19日星期五
2009年6月15日星期一
中国博客大赛,网易赛区,网聚最油菜的力量
2009年6月5日星期五
2009年6月4日星期四
2009年5月29日星期五
2009年5月16日星期六
2009年5月6日星期三
2009年5月4日星期一
五四
1.收到老妈的短信,“祝你节日快乐,工作顺利!”。首先是奇怪,然后是难过。今天是什么节日?原来今天是五四青年节。我很难过,我作为一个青年,没有意识到今天是属于我的节日。更难过的是,我对这个节日的纪念意义了解的太少。九十年前的那群年轻人,都已老去,成为历史。而他们提倡的民主和科学,今天依赖没有实现。
2.既然是青年节,那么不得不提一下20年前另外一群年轻人,现在他们是不能被言说的。他们曾经如此热血,甚至天真,所以被残酷镇压。那一群年青人,现在他们都在哪里?
3.胡主席同大学生共迎五四, 强调勇敢担负历史重任。历史重任呢,我们对历史所知太少。
4.青年都是少年过来的。一句很有名的话,少年强则国强,少年弱智则国弱智。现在对少年,我指的是90后,(不过“90后”这个词几乎是和“脑残、自拍”之类划等号的),大家似乎都是有偏见的。其实想想,我在他们那个年龄,好像更没有主见,更没有个性。至少他们还有火星文,还能被大家称为脑残。
5.未完待续。。。
2009年5月2日星期六
2009年4月29日星期三
又一次赶超美帝国主义
http://news.qq.com/a/20090427/000521.htm
《专家称猪流感病毒属异常新型 现阶段无法受控》
美国疾病及预防中心专家日前证实,这次爆发的猪流感病毒是属于异常的新型病毒,
而且散播范围很广,现阶段无法受到控制。(2009年04月27日08:47)
然后,马上,中国专家开始研究…………
http://news.qq.com/a/20090427/000635.htm
《中国疾控专家称人感染猪流感“可防可控可治”》
中国疾病预防控制中心专家26号表示,猪流感并不可怕,
目前虽尚无预防疫苗,但人感染猪流感是可防、可控、可治的。(2009年04月27日09:29)
-----------------------------------------------------------------
早上和LF看新闻的时候,说到祖国就是伟大.科技比美国先进多了.
2009年4月28日星期二
JST学习笔记
例子
首先在html中使用jst,要导入TrimPath JST js库。<html> <head> <script language="javascript" src="trimpath/template.js"></script> ... </head> ... </html>然后使用js创建数据:
<script language="javascript">
var data = {
products : [ { name: "mac", desc: "computer",
price: 1000, quantity: 100, alert:null },
{ name: "ipod", desc: "music player",
price: 200, quantity: 200, alert:"on sale now!" },
{ name: "cinema display", desc: "screen",
price: 800, quantity: 300, alert:"best deal!" } ],
customer : { first: "John", last: "Public", level: "gold" }
};
</script>
将jst模板写在一个隐藏的textarea中,如:<textarea id="cart_jst" style="display:none;">
Hello ${customer.first} ${customer.last}.<br/>
Your shopping cart has ${products.length} item(s):
<table>
<tr><td>Name</td><td>Description</td>
<td>Price</td><td>Quantity & Alert</td></tr>
{for p in products}
<tr><td>${p.name|capitalize}</td><td>${p.desc}</td>
<td>$${p.price}</td><td>${p.quantity} : ${p.alert|default:""|capitalize}</td>
</tr>
{forelse}
<tr><td colspan="4">No products in your cart.</tr>
{/for}
</table>
{if customer.level == "gold"}
We love you! Please check out our Gold Customer specials!
{else}
Become a Gold Customer by buying more stuff here.
{/if}
</textarea>
要调用结果的时候,使用js调用://The one line processing call...
var result = TrimPath.processDOMTemplate("cart_jst", data);
JST API TrimPath对象
JST组建的所有函数都有TrimPath对象提供,相当于命名空间。TrimPath.parseDOMTemplate(elementId,optionalDocument) 查找到DOM树中elementId指定的元素,(一般来说, elementId都指向隐藏的textarea,因为textarea具有良好的特性,它的innerHTML可以支持任何html代码,以及JST模板语言中的任何东西),将其innerHTML解析为一个模板对象(template object), optionalDocument可选,如果在iframes中或者多个窗口时,指定某个document。
TrimPath.parseDOMTemplate(elementId, optionalDocument)
查找到DOM树中elementId指定的元素,(一般来说, elementId都指向隐藏的textarea,因为textarea具有良好的特性,它的innerHTML可以支持任何html代码,以及JST模板语言中的任何东西),将其innerHTML解析为一个模板对象(template object), optionalDocument可选,如果在iframes中或者多个窗口时,指定某个document。TrimPath.processDOMTemplate(elementId,contextObject,optionalFlags,optionalDocument)
该函数,先调用TrimPath.parseDOMTemplate(),然后在返回的模板对象上调用process ()方法,返回一个已经包装好数据的字符串。| 参数 | 意义 |
|---|---|
| elementId | DOM树中包含JST模板的元素id |
| contextObject | 包含JST模板待包装的各种相关数据,是一个js对象 |
| optionalFlags | 几个可选的标记对象 |
| optionalDocument | 可选的document参数,主要用在多ducument环境中 |
TrimPath.parseTemplate(templateContentStr,optionalTemplateName)
将一个字符串解析成为一个templateObject,如解析出错,则抛出异常。函数参数如下:| 参数 | 意义 |
|---|---|
| templateContentStr | JST模板字符串,如"hello ${firstName}" |
| optionalTemplateName | 指定模板的名字(可选),(用来调试) |
templateObject.process(contextObject,optionalFlags)
模板对象的process方法将模板和包含内容的"contectObject"的组装在一起,最终返回一个字符串。 contectObject参数必须是一个js对象,如果在模板中引用到"${a}",那么contectObject必有有a这个属性,即 contectObject.a是可访问的,同理,如在模板中有"${a.b.c}",那么contectObject.a.b.c.d也必须是可访问的。 contectObject中可以包含任何js对象,包括字符串,数字,日期,对象和函数等。在模板中"${groupCalender(new Date())}"将会调用contectObject.groupCalender(new Date())。当然contectObject中得有这个函数,而且该函数必须返回一个字符串。optionalFlags也是一个参数,该参数可选。该参数可以指定解析模板时的一些行为。
| 标记 | 意义 |
|---|---|
| optionalFlags.throwExceptions | 默认为false;设为true时,如有异常发生,将会抛出异常,异常字符串会接在当前已解析的字符串返回。 |
| optionalFlags.keepWhitespace | 默认为false;设为true时,模板中的所有空白字符将被保留。 |
使用Prototype定义javascript中的类和继承
/** obsolete syntax **/
var Person = Class.create();
Person.prototype = {
initialize: function(name) {
this.name = name;
},
say: function(message) {
return this.name + ': ' + message;
}
};
var guy = new Person('Miro');
guy.say('hi');
// -> "Miro: hi"
var Pirate = Class.create();
// inherit from Person class:
Pirate.prototype = Object.extend(new Person(), {
// redefine the speak method
say: function(message) {
return this.name + ': ' + message + ', yarr!';
}
});
var john = new Pirate('Long John');
john.say('ahoy matey');
// -> "Long John: ahoy matey, yarr!"
这里我们可以看到继承是通过修改类的prototype,并且调用Object.extend函数。同时,Pirate重写了Persion的say()方法,我们没有办法向在编程语言中那样使用基类的方法。 在新版本中都做了改进,下面的代码是使用新版,相比上面的代码,将更简洁。
/** new, preferred syntax **/
// properties are directly passed to `create` method
var Person = Class.create({
initialize: function(name) {
this.name = name;
},
say: function(message) {
return this.name + ': ' + message;
}
});
// when subclassing, specify the class you want to inherit from
var Pirate = Class.create(Person, {
// redefine the speak method
say: function($super, message) {
return $super(message) + ', yarr!';
}
});
var john = new Pirate('Long John');
john.say('ahoy matey');
// -> "Long John: ahoy matey, yarr!"
现在基类和子类的定义都更简洁,因为我们不再需要直接修改对象的prototype。同时还有一个新特性,就是增加了$super关键字,支持对基类方法的调用。 如何组合模块 现在已经知道通常创建类的方法是调用Calss.create()函数.
var Pirate = Class.create(Person, { /* instance methods */ });
但实际上,Class.create函数可以接收任意数量的参数。第一个参数,如果是一个另外定义的类,那么新创建的类将继承它。另外所有的参数都会作为实例方法增加到新创建的类中。这样可以方便的组合已有各个模块。
// define a module
var Vulnerable = {
wound: function(hp) {
this.health -= hp;
if (this.health < 0) this.kill();
},
kill: function() {
this.dead = true;
}
};
// the first argument isn't a class object, so there is no inheritance ...
// simply mix in all the arguments as methods:
var Person = Class.create(Vulnerable, {
initialize: function() {
this.health = 100;
this.dead = false;
}
});
var bruce = new Person;
bruce.wound(55);
bruce.health; //-> 45
上例中Vulnerable不是一个类,但是新定义的Person类仍然包含了所有Vulnerable的方法和属性。Prototype的这个特性,使我们很容易地组合各个模块,提高代码重用率。 方法定义中的$super参数 当我们在子类中重写一个方法,并希望调用基类的方法,那么需要一个指向基类方法的引用。现在可以通过传入一个附加参数$super来指向基类的方法($super参数必须作为第一个参数)。但是外部调用的时候,不必需要知道这个$super参数,如上例中,调用Pirate的say方法时,仍然只需要传入一个参数。
2009年4月25日星期六
2009年4月21日星期二
装13句式二十四
★ 一定要幸福哦!(简直是装13大忌啊,你要是发自内心的会厚着脸皮说出来吗?)
★ 如果爱,请深爱。
★ 好吧,我承认我……(谁TMD要你承认,自我感觉太良好了吧?)
★ 那一刻,我泪流满面 。(脆弱的小心灵哟,你如此流泪为哪般?)
★ 此女子……此男子……(你妈没教过你第一人称咋用么)
★ 我们都是好孩子 最最善良的孩子……(鸡皮疙瘩掉一地,孩子孩子,你他妈都孩他娘了)
★ 亲们……(或者“亲爱的们”)
★ ××××得让人心疼
★ ×)*&……%¥#绘銪兲驶替硪嫒尓(会有天使替我爱你?兲是什么?会有王八替我爱你?)
★ 亲爱哒……(“的”就“的”么,你“哒”个毛啊?)
★ 没心没肺地大笑; 没心没肺地+某动词 (你丫装什么纯情?)
★ 我依然是骄傲的公主~(这年头公主满大街都是)
★ 希望……带着我的音乐梦想走下去……以及我会带着我最初的音乐梦想走下去(苍天啊,劈死我吧)
★ 我们是糖,甜到哀伤。
★ 这时不说这时,说“彼时”也不说也,说“亦”这样不说这样,说“如此” 然而不说然而,说“然”。
★ 他,无奈而宠溺的看着我,“×××,我到底该把你怎么办才好”(日后再说)
★ 人生若只如初见(好好的一句话如今被装逼装到恶俗)
★ 写签名这样的:××(自己的名字)不要哭,××要坚强,××要勇敢,××今天好乖~
★ 他是(不是)我想要的那杯茶……(小资13)
★ 我喜欢抬起头成45度角仰望天空,我的泪才不会流那么多……(直接90度不就完了,费个什么劲啊)
★ 传说每个女生都是天使,为了某个男生所以折断翅膀来到人间,男生一定好好对待这个女生,因为她再也回不到天堂……(明明都是受精卵,装哪门子的天使)
★ 男子,女子,忧郁,忧伤,態度, 帆布鞋,海藻般的长发,赤裸着××,干净的白衬衫,轻轻的搅动着那一杯卡—布—奇—诺!(最烦卡布奇诺,NND你也就知道个卡布奇诺)
★ 装13关键词:唯美、尊贵、风情、品牌、强势、高档、奢华、地中海、碧水蓝、简约生活……(滚回家盖房子去吧)
★ 你是我的伤。心上的伤。我很伤……(吃药去吃药去吃药去,瞎嚷嚷个什么)
2009年4月17日星期五
谣言罚款和第四个盛世
浙江省十一届人大常委会第十次会议近日批准的《杭州市计算机信息网络安全保护管理条例》规定,从今年5月1日起,任何单位或者个人不得从事下列危害计算机信息网络安全和秩序的行为,包括擅自进入、使用他人计算机信息网络;擅自增加、修改、删除、干扰他人计算机信息网络的功能;故意制作、传播、使用计算机病毒、恶意软件等破坏性程序,或者制作、发布、复制、传播含破坏性程序或其机理、源程序的信息等。
该条例指出,单位或者个人违反上述规定,由公安机关给予警告,有违法所得的,没收其违法所得;对单位可并处以1000元以上1.5万元以下的罚款,对个人可并处以500元以上5000元以下的罚款;情节严重的给予6个月以内停业整顿、停机联网的处罚。
杭州市人大常委会有关负责人表示,随着网络交流方式的盛行,网民、网吧数量的快速增长,以及宾馆、咖啡馆等提供公共上网服务的场所日益增多,电子商务已经成为经济发展的重要组成部分,计算机信息网络安全问题也日益突出。(岳德亮)
转北风的:
对此,有论者认为:“很多时候,即使在大体真实的舆论中,也会包含有不完全真实的细节。这样一来,立法要是取缔了不完全真实的舆论的时候,也就消灭了基本真实的信息本身。”
呵呵,以后不能随便骂人了.骂人也只能骂骂万恶的资本主义.我朝党国天下正步入继继西汉“文景之治”、唐“贞观之治”、清“康雍乾盛世”之后的“第四个盛世”呢。
2009年4月16日星期四
中国络封锁技术方案与反网络封锁技术方案
目录
- GFW是什么
- 网络封锁的技术方案
- 内容审查
- 屏蔽IP
- DNS劫持
- 反网络封锁的技术方案
- 使用SSL加密对抗关键字审查
- 给网站换IP避开IP屏蔽
- 用tor来避开DNS干扰
- 使用在线RSS阅读器来
GFW是什么
GFW是Great Fire Wall的缩写,是金盾工程。这个工程由若干个部分组成,实现不同功能。防火长城主要指中国政府监控和过滤互联网内容的软硬件系统,由服务器和路由器等设备,加上相关的应用程序所构成。
首先,需要强调的是,由于中国网络审查广泛,中国国内含有“不合适”内容的的网站,会受到政府直接的行政干预,被要求自我审查、自我监管,乃至关闭,故GFW的主要作用在于分析和过滤中国境内外网络的资讯互相访问。
GFW对网络内容的过滤和分析是双向的,GFW不仅针对国内读者访问中国境外的网站进行干扰,也干扰国外读者访问主机在中国大陆的网站,本文讨论GFW屏蔽国外网络上传播的内容的方法及相应的对策。
网络封锁的技术方案
GFW在网络上封锁的技术方案有:
- 内容审查
- 屏蔽IP
- DNS劫持
GFW如何屏蔽网络上传播的内容?网络上封锁的具体方式:
内容审查
GFW的内容审查针对HTTP传输协议的默认端口的80端口,HTTP传播的内容是明文的内容,没有经过加密,GFW是一个IDS[Intrusion detection system (入侵检测系统)],GFW有一个敏感字名单,若在中国大陆访问境外的主机的HTTP的数据流里发现敏感字眼,就在两台主机间伪造一个"reset”信号,导致双方主机以为对方中止了请求。如用Firefox浏览器访问
http://newsvote.bbc.co.uk/chinese/
http://knol.google.com/k/-/-/3jhi1zdzvxj3f/2 就会出现以下画面:屏蔽IP
屏蔽IP是GFW通过路由器(router)来控制的,在通往国外的最后一个网关上加上一条伪造的路由规则,导致通往某些被屏蔽的网站的所有IP数据包无法到达。路由器的正常工作方式是学习别的路由器广播的路由规则,遇到符合已知的IP转发规则的数据包,则按已经规则发送,遇到未知规则IP的数据,则转发到上一级网关。GFW的路由器按黑名单(blacklist)来转发特定的IP数据包,则可屏蔽特定的网站的IP,此IP黑名单不是固定的,会更新。如访问 http://www.dw-world.de/chinese/ 出现以下画面:DNS劫持
DNS劫持是针对某些网站的最严重的干扰。干扰的方式有两种:- 一种是通过网络服务提供商(Internet Service Provider/ISP)提供的DNS服务器进行DNS欺骗,当人们访问某个网站时,需要要把域名转换为一个IP地址,DNS服务器负责将域名转换为IP地址,中国大陆的ISP接受通信管理局的屏蔽网站的指令后在DNS服务器里加入某些特定域名的虚假A记录,当使用此DNS服务器的网络用户访问此特定网站时,DNS服务便给出假的IP地址,导致访问网站失败,甚至返回ISP运营商提供的出错页面和广告页面。
- 另一种是GFW在DNS查询使用的UDP 53端口上根据黑名单进行过滤,遇到通往国外的使用UDP的53端口进行查询的DNS请求,就返回一个虚假的IP地址
在大陆访问 http://www.zuola.com/ 就可能出现“服务器响应时间过长”。
我家目前用的中国电信提供的网络接入:
Internet connection
Connection type:PPPoE
IP Address:220.168.9.112
Gateway address:220.168.8.1
DNS Server:222.246.129.80, 59.51.78.210
当我使用中国电信提供的DNS服务器进行查询时,出现以下画面:甚至我在中国大陆使用用国外的OPENNDS或其他DNS服务器进行查询,都会被GFW干扰而得到虚假的IP地址,通过远程登录在美国的主机进行DNS查询后,此时的DNS查询是完全无GFW干扰的,可得到 www.zuola.com的真实IP地址是 75.119.214.237
GFW在OSI结构模型的两个层面进行审查和封锁,一种在传输层(Transport Layer)进行干扰,一种是在网络层(Network Layer)进行干扰:
- 在传输层(Transport Layer)进行干扰:
- DNS劫持 GFW在UDP 53端口上进行传输层完成干扰;
- 内容审查 GFW对默认端口TCP 80端口上进行内容过滤,在http传输协议上,对tcp 80端口上传输的内容进行内容审查,遇到关键字,GFW就在会话中插入“reset”信号,导致网页被重置。
- 屏蔽IP是GFW在网络层(Network Layer)上的封锁和干扰,是在IP路由协议上完成干扰。
GFW审查在OSI模型上的审查的位置:
反网络封锁的技术方案
GFW针对具体网站的方案有以下三种:
- GFW把域名为作敏感字来屏蔽
- GFW把域名所在的IP屏蔽
- GFW干扰域名DNS解析
相应对抗GFW,网站的主人有下面几个方法:
- 网站使用SSL加密对抗关键字审查;
- 给网站换IP避开IP屏蔽;
- 推荐大陆的网站访问者使用国外的DNS解析服务器,或推荐大陆的访问者使用tor来避开DNS干扰
- 推荐读者使用在线RSS阅读器来读取最新文章
相应对抗GFW有的具体方法
使用SSL加密对抗关键字审查
HTTPS是基于SSl的HTTP协议,是安全超文本传输协议。当用户访问使用HTTPS的网站时,用户端的浏览器需要先获取网站的安全证书和来自认证的安全证书发行商的数字签名,此安全证书为服务器的公钥,服务器用私钥把网页内容加密进行传输,客户端浏览器以公钥解密即可得到网页内容。网络内容的传播过程中,内容是加密的,无法被GFW自动审查传输的内容,亦无法被GFW插入"reset"信号。- 实例1:在2007年11月到2008年7月,https://www.zuola.com 曾用HTTPS成功避开GFW的审查,让中国大陆的网民能够直接访问此网站。
- 实例2:当人们无法访问http://knol.google.com/k/-/-/3jhi1zdzvxj3f/2时,改用 https://knol.google.com/k/-/-/3jhi1zdzvxj3f/2 就可以访问
给网站换IP避开IP屏蔽
使用SSL加密可对抗GFW的内容审查,GFW如何对付HTTPS呢?
GFW的工作人员遇到使用HTTPS的网站后,他们知道无法使用机器自动的过滤他们心目中的“敏感内容”和“有害内容”,于是直接屏蔽网站的IP,此时,通往此网站服务器的任何数据包都无法直接越过GFW到达目的地。此时只需要更改网站域名的A记录,换一个IP就可以避开屏蔽IP。此时就像猫和老鼠的游戏了,GFW只好跟在后面不断屏蔽IP,并且GFW的屏蔽IP的工作是人工执行的。无法通过机器自动执行。如果GFW能够自动执行屏蔽IP的操作,那被屏蔽IP的域名的拥有者就可以借用GFW的能力屏蔽任何境外网站了。- 实例3:在2007年11月到2008年7月,https://www.zuola.com 曾用SSL加密+换IP 成功避开GFW的审查,让中国大陆的网民能继续能够直接访问此网站。
- 实例4:https://doubleaf.com 也使用SSL加密和换IP的手段让中国大陆的网民能直接访问
推荐大陆的网站访问者使用国外的DNS解析服务器,或推荐大陆的访问者使用tor来避开DNS干扰
在2008年奥运期间,我发现GFW针对 www.zuola.com 使用了DNS劫持的方式一劳永逸的屏蔽方式,导致国内用户无法直接获得我的网站的正确IP,即使使用“无界”“自由门”“VPN”等突破封锁的工具,也会走错门。那就只能推荐读者《使用TOR+FoxyProxy插件突破GFW》了,TOR是一个分布式的、匿名的网络,Foxyproxy是一款代理服务器管理软件,可以让Firefox使用TOR来收发DNS请求:
TOR的工作方式:
Tor 有助于降低简单的和高级的流量分析的风险,Tor 把你的流量分散到互联网上的多个地点,所以不存在单一的一点可以把你和你的目的地联系起来。这就好像用一条拐弯抹角的、难以辨认的路径甩掉跟踪你的人,然后定期擦掉你的脚印。在 Tor 网络上,来源和目的地不是用一条路径直接连接的,而是由一条通过数台中继的随机路径覆盖原始路径,数据包在这条路径上传输,因此,不存在任何单一一点上的观察者能够知道数据从哪里来、到哪里去。用 Tor 创建一条私有网络路径时,用户的软件或客户端通过网络上的中继递增地建立一条由若干加密连接组成的环路(circuit)。环路一次扩展一跳(hop),环路上的中继仅仅知道它从哪一个中继接收数据以及向哪一个中继发送数据。没有一台单独的中继会知道数据包的完整路径。客户端与环路上的每一跳都协商一组独立的密钥,这样可以保证数据通过任何一跳时都无法跟踪。
一旦环路建立完成,多种类型的数据可以在上面进行交换,不同种类的应用程序也可以在 Tor 网络上部署。因为每一台中继最多只能知道环路中的一跳,窃听者或者被入侵的中继都无法通过流量分析把连接的来源和目的地联系起来。 Tor 仅作用于 TCP 数据流,任何支持 SOCKS 的应用程序都可以使用它。
出于有效性,Tor 为大约在相同的十分钟内发起的连接请求分配同一环路。以后的请求被分配不同的环路,这样他人就不能把你早先的行为和新的行为联系起来。
推荐读者使用在线RSS阅读器来读取最新文章
Really Simple Syndication(RSS)是一种严格XML的信息传递的格式规范,标准的XML格式文档可允许信息在一次发布后通过不同的程序阅读,易于分发和聚合(Sysndicat)。RSS是这种XML应用搭建了信息迅速传播的一个技术平台,网站的最新内容通过RSS Feed传播,能通过在线RSS阅读器自动推送文章到读者的阅读器,人们无需直接访问网站就能看到最新的内容,RSS能够让内容很容易的被分发,只需要知道RSS地址,网络上无数的在线RSS阅读器总能读取到RSS Feed中的内容。RSS是很容易生成并分布的,这是GFW无法拦截或需要大量资源才能够封锁的一种技术。
实例:- 实例5:我的网站自2007年4月重庆最牛钉子户报道后被屏蔽,但我的FEED地址 http://feed.zuola.com/ 的订阅数却一直在增长,Google Reader订阅用户数量从 700左右增加到2686。不过GFW仍然会干扰Google reader,使用https 的方式https://www.google.com/reader/view/ 才能看到以下数据
- 实例6:《看不见的西藏》是一个藏族人的BLOG,虽然国内读者无法直接访问,但中国大陆的读者仍然能够通过订阅她的RSS FEED来看到她眼中的西藏,这个Feed是可用google reader订阅的 http://woeser.middle-way.net/feeds/posts/default
此文档的其他站点备份:
- http://zolaruler.spaces.live.com/blog/cns!D2AC7D299F493A68!241.entry
- https://docs.google.com/View?docid=dggh5mp6_0zzmm4fdn
- http://zhoushuguang.blogspot.com/2009/03/blog-post.html
- https://knol.google.com/k/-/-/3jhi1zdzvxj3f/14
参考文档:
《Tor概述》:http://tor.zuo.la/overview.html.zh-cn
《手把手教你使用TOR+FoxyProxy突破GFW》https://knol.google.com/k/-/-/3jhi1zdzvxj3f/2
《纠正网友一些对GFW的认识》: https://www.zuola.com/weblog/?p=1228
《Zola教你玩:如何对抗GFW的域名劫持》: https://www.zuola.com/weblog/?p=1151
2009年4月15日星期三
FreeMarker中文手册--KLW 表达式
本页内容:
当你给内插表达式或者指令参数提供值的时候,你可以使用变量或者更复杂的表达式.例如,x是数值8,y是数值5, (x + y)/2的值会是6.5.
在我们进入具体细节之前,先看一些具体的例子:
- 当你为内插表达式提供值时:使用方法是${表达式}.这里的表达式是你要在文本输出中的值.如${(5 + 8)/2} 会打印”6.5”.(也有可能是是”6,5”,如果你输出的语言环境不是英语的话.)
- 当你给指令提供参数时:你已经在入门章节见过if指令.这个指令的语法是: <#if 表达式>...</#if>.这里的表达式必须是一个布尔值.例如: <#if 2 < 3>,2 < 3(2小于3)是一个布尔表达式(结果为true).
快速浏览(备忘单)
- 直接赋值
- 字符串: "Foo" 或者 'Foo' 或者"It's \"quoted\"" 或者r"C:\raw\string"
- 数值: 123.45
- 布尔值: true, false
- 序列(数组): ["foo", "bar", 123.45], 1..100
- 哈希: {"name":"green mouse", "price":150}
- 取值
- 顶层变量: user
- 从哈希中取值: user.name, user["name"]
- 从序列中取值: products[5]
- 特定变量: .main
- 字符串操作
- 内插表达式 (或者连接操作): "Hello ${user}!" (or "Free" + "Marker")
- 取得一个字符: name[0]
- 序列操作
- 连接: users + ["guest"]
- 序列片段: products[10..19] 或 products[5..]
- 哈希操作
- 连接: passwords + {"joe":"secret42"}
- 算术运算: (x * 1.5 + 10) / 2 - y % 100
- 比较: x == y, x != y, x < y, x > y, x >= y, x <= y, x < y, ...等.
- 逻辑运算: !registered && (firstVisit || fromEurope)
- 内置函数: name?upper_case
- 方法调用: repeat("What", 3)
- 缺失值处理:
- 默认值: name!"unknown" 或(user.name)!"unknown" 或name! 或(user.name)!
- 测试值是否缺失: name?? 或(user.name)??
直接赋值
经常你会直接使用具体值,而不是使用一些计算结果.
字符串
直接使用字符串的话,你在文本上加引号即可,如"some text",或者使用单引号,如 'some text'.这两种形式是等价的.如果字符串本身包含着引号(“或者’)或者反斜杠”\”,你需要加上前置的反斜杠,这称为转义.其他的字符你都可以直接输入,包括换行符.例如:
${"It's \"quoted\" and
this is a backslash: \\"}
${'It\'s "quoted" and
this is a backslash: \\'}
输出结果:
It's "quoted" and
this is a backslash: \
It's "quoted" and
this is a backslash: \
注意
当然,你也直接将文本写在模版中,而不使用 ${...}.这里只是作为例子展示表达式中如何直接使用字符串.
下表是所有支持的转移字符.在模版中使用反斜杠来转义其它字符会导致模版出错.
| 转义字符 | 意义 |
| \" | 双引号(u0022) |
| \' | 单引号 (u0027) |
| \\ | 反斜杠 (u005C) |
| \n | 换行符 (u000A) |
| \r | 回车符 (u000D) |
| \t | 水平制表符(u0009) |
| \b | 退格符 (u0008) |
| \f | 换页符(u000C) |
| \l | 小于号: < |
| \g | 大于号: > |
| \a | & |
| \xCode | 使用16进制unicode表示符号 |
\x之后的code是1到4位的16进制数字.例如,在字符串中表示一个版权信息: "\xA9 1999-2001", "\x0A9 1999-2001", "\x00A9 1999-2001".你必须使用1到4位的16进制数字,否则FreeMarker会很迷茫的,呵呵.
注意到字符”${”和”#{”有特殊意义,它是用来插入值或者表达式的,(如"Hello ${user}!",变量user的值会插入到字符串中.)如果你想打印${,你需要使用原始字符串.
所谓原始字符串,指的是,在原始字符串何总反斜杠和${没有特别意义,会被当做普通文本输出.指明一个字符串是一个原始字符串,在引号前加字母r,例如:
${r"${foo}"}
${r"C:\foo\bar"}
输出:
${foo}
C:\foo\bar
FreeMarker中文手册--KLW 指令
指令
你使用FTL标签来调用指令.在例子中你调用了list指令,语法上,你使用了两个标签: <#list animals as being> 和 </#list>.
一共有两种标签:
- 开始标签: <#directivename parameters>
- 结束标签: </#directivename>
参数parameters 的格式有指令名directivename决定.
实际上有两类指令:预定义指令和用户定义指令.对于用户定义的指令,要使用@而不是#,比如<@mydirective parameters>...</@mydirective>.另外不同的是如果用户自定义的指令没有内嵌内容的话,必须这样使用: <@mydirective parameters />,类似于XML中<img ... />.但是用户自定义指令又是一个高级主题,详细见这里.
FTL标签,和HTML标签一样,必须被合适地嵌套.下面的代码是错误的.因为if指令既在内嵌的list指令中,又在它的外边:
<ul>注意FreeMarker并不关心那些内嵌的HTML标签,只关心内嵌的FTL标签.它把HTML当做普通文本对待,不做任何翻译.
<#list animals as being>
<li>${being.name} for ${being.price} Euros
<#if user == "Big Joe">
(except for you)
</#list> <#-- WRONG! The "if" has to be closed first. -->
</#if>
</ul>
如果你使用一个不存在的指令(比如,你不小心把指令名字打错了),FreeMarker会停止翻译模版,并报告一个错误消息.
FreeMarker会忽略FTL标签中多余的空格.所以,下面这么写也是可以的:
<#list[BR]但是,无论如何,你不可以在<或者</和指令名之间加入空白,这是不允许的.
animals as[BR]
being[BR]
>[BR]
${being.name} for ${being.price} Euros[BR]
</#list >
关于所有指令的列表可以看指令参考.(但是我建议你先看完本章关于表达式的介绍).
注意
FreeMarker也可以配置成在使用FTL标签和注释时使用[和]而不是<和>,像[#if user == "Big Joe"]...[/#if].更多细节见:其他/另一种语法.
注意
FreeMarker也可以配置为在使用预设指令时不使用#,像<if user == "Big Joe">...</if>.但是,我们不建议你使用这种模式.更多信息见:参考/弃用的FTL系统/老的FTL语法
FreeMarker中文手册--KLW 总体结构
总体结构
模版实际上是你用FTL(FreeMarker 模版语言)编写的程序.这是一个简单的编程语言,仅被设计用来实现模版的,而不是其他复杂的东西.
一个模版(FTL程序)由以下几部分构成:
- 文本: 文本会被原样输出.
- 内插值:这部分在输出的时候会被实际计算的值代替.内差值由${和}包围(或者#{和}).
- FTL 标签:FTL标签和HTML标签类似,但他们实际上FreeMarker的指令,不会被打印到输出中.
- 注释:这里的注视也和HTML中的注视类似,但是他们是使用 <#-- 和 -->包围的.FreeMarker会忽略注视,不会被写到输出中.
<html>[BR]FTL区分字母大小写.所以list是一个正确的指令,而List不是.同样的,{name}不同于{Name}或者{NAME}.
<head>[BR]
<title>Welcome!</title>[BR]
</head>[BR]
<body>[BR]
<#-- Greet the user with his/her name -->[BR]
<h1>Welcome ${user}!</h1>[BR]
<p>We have these animals:[BR]
<ul>[BR]
<#list animals as being>[BR]
<li>${being.name} for ${being.price} Euros[BR]
</#list>[BR]
</ul>[BR]
</body>[BR]
</html>
注意在内插值仅可以直接使用在文本中,以及字符串表达式中.
一个FTL标签不能内嵌在另一个FTL标签中,或者使用在内差值当中.例如,下面的表达是错误的: <#if <#include 'foo'>='bar'>...</#if>
注释可以放在FTL标签和内差值内部.例如:
<h1>Welcome ${user <#-- The name of user -->}!</h1>[BR]
<p>We have these animals:[BR]
<ul>[BR]
<#list <#-- some comment... --> animals as <#-- again... --> being>[BR]
...注意你们如果尝试过上述例子,你也许注意到,一些空格,制表符和换行符,在输出的时候会丢失,尽管我们说文本会被原样输出.现在先别为这个操心,这是因为” white-space stripping”(空白-剥离)特性被设置的缘故,这个特性会自动移除多余的空格,制表符和换行符.
FreeMarker中文手册--KLW 第一章 入门
本章内容:本章将对FreeMarker进行非常粗略的介绍。随后的章节将讲述更多的细节。尽管如此,一旦你阅读完本章,你仍能写一些简单但是有用的FreeMarker模版。
2009年4月14日星期二
FreeMarker中文手册--KLW 类型
支持的类型如下:标量(基本类型)
这些是基本简单的值,可以是:
- 字符串,简单的文本,比如货物的名称.如果你想在模版中直接使用字符串,而不是一个来自数据模型的变量,可以再字符串两端加引号,如"green mouse" 或者'green mouse'.
- 数值类型:想货物的价格就是数值类型.全部是数字或者非全部数字并没有明显区别.数值类型只有一种.比如,3/2会一直是1.5,而永远不会是1,就好像你在计算器一样.如果你想在模版中使用数字,直接写就行了,如150,-90.06或者0.001.
- 布尔型:一个布尔型的值代表逻辑上的真(true)或假(false).例如,一个访问者是否登录.通常你会在if指令中使用布尔值作为条件,像<#if loggedIn>…<#/if>或者 <#if price == 0>...</#if>;其中price == 0部分的值是一个布尔型值.在模版中你可以直接使用保留字true和false代表布尔值.
- 日期:日期型变量保存着日期时间有关的值.一共有三种:
- 包含日期,如April 4,2003
- 包含时间,没有日期部分,如10:19:18 PM,时间部分存为毫秒值.
- 日期时间(包含日期和时间),如 April 4, 2003 10:19:18 PM,时间部分存为毫秒值.
容器
有些变量的目的就是包含其他变量,这些变量就是容器.这些被包含的变量也被称为子变量.容器类型是:
- 哈希(哈希表):每个子变量都和一个独一无二的名字相关联.在一个哈希表中的所有子变量是无序的,所以对哈希表来说,没有第一个子变量,第二个子变量这种说法.自变量都是根据名字访问的.
- 序列:每个子变量都和一个整数(索引)相关联.第一个子变量关联的是0,第二个是1,第三个是3,依此类推.所有的子变量是按顺序排列的.这些关联的整数也被称为索引.序列通常是密集的,所有的索引通常都关联着子变量,但这并不严格要求.子变量的类型不要求相同.
- 集合:一个集合,从模版作者的角度看,是一个受限制的序列.你不同访问它的大小,或者根据索引访问它的子变量.但是这些子变量仍可通过list指令罗列出来.
因为存在哈希表和序列(和集合)中的变量的值可以是任何东西,它可以是哈希表或者序列或者集合.这样,你可以构造任意深度的数据模型结构.
数据模型本身(或者更确切地说,它的根)就是一个哈希表.
子程序
方法和函数
一个值是方法或者函数,表示它能根据你传入的参数,计算出另外的值.
给编程人员:方法或函数是第一阶层的值,就像函数式编程语言中那样.这表示,方法或函数可以作为参数或其它方法或函数的返回值.你可以使用它们给变量赋值等.
假设编程人员在数据模型中添加了方法变量avg,来计算数值的平均数.如果你使用3和5作参数访问avg,然后你可以得到一个值4.
方法的使用方法稍后再做解释,但是这个例子也许可以帮你理解什么是方法:
输出结果是:
The average of 3 and 5 is: ${avg(3, 5)}
The average of 6 and 10 and 20 is: ${avg(6, 10, 20)}
The average of the price of a python and an elephant is:
${avg(animals.python.price, animals.elephant.price)}
The average of 3 and 5 is: 4方法和函数的区别是什么呢?对模版设计者来说,它们并没有区别.实际上并不是没有区别,方法基本上来自数据模型(反应为java对象的方法),而函数是在模版中定义的(使用function指令定义的),但是两者的调用方法是一样的.
The average of 6 and 10 and 20 is: 12
The average of the price of a python and an elephant is:
4999.5
用户自定义指令
一个类型为用户自定义指令的值可以作为指令来使用(换句话说,和其他FreeMarker标签一样).一个用户自定义的指令,是一个子程序,比如一些可重用的模版碎片.(又是一个高级主题,以后在说啦).
给编程人员的提示:用户定义的指令,比如宏,也是第一阶层的值,和方法和函数一样.
仅仅是为了理解一下用户自定义指令(如果你不理解直接跳过也行),假设我们有个变量,box,它的值是用户自定义指令,用来打印一些花俏的HTML消息框带标题栏和消息.这个box变量可以这样用在模版中:
<@box title="Attention!">方法/函数和用户自定义指令比较
Too much copy-pasting may leads to
maintenance headaches.
</@box>
这里又是给高级用户的(如果你不理解,直接跳过).这经常是个两难的决定,当你实现某样东西时,是使用方法函数呢,还是使用用户自定义指令呢.这里的经验法则是:使用用户自定义指令而不是方法或者函数,如果:
- …输出(返回值)是有标记的(HTML,XML等).主要原因是函数的返回结果会自动过滤XML,而用户自定义指令不会. 用户自定义指令的输出是假设为有标记的.
- …如果副作用而不是返回结果是重要的.例如,一个向服务日志添加一条记录的指令.实际上,用户定义指令是没有返回结果的.当然通过设置非本地变量,也可以得到一些返回值.
- …它会做流控制(如list指令和if指令),你在方法和函数中做不到这样.
其他
节点
节点变量代表树结构中的一个节点,在XML处理的时候,会常常用到.这是一个高级并且专门的主题.(呵呵,如果不理解,跳过吧)
不过,给高级用户的一个快速浏览:一个节点类似于一个包含其他节点中的序列,这些包含的序列也被称为子节点.一个节点包还一个它自身容器的引用,也被称为父节点.一个节点的主要信息是它的拓扑信息.另外的数据使用使用一个变量有多种类型这个特性来保存.比如,一个值可以是一个节点,又是一个数值,这样可以讲数值存为”有效负荷”.除了拓扑信息,节点也可以保存一些元信息:节点名,节点类型(字符串等)和节点命名空间.例如,XHML文档中的h1节点,它的名字可以是”h1”,节点类型可以是”element”,它的命名空间可以是” http://www.w3.org/1999/xhtml”.这些元数据代表什么,这是数据模型设计者的事情.如何获得节点的拓扑信息和元信息随后章节会详细描述.(在这里你不理解没有关系.)
FreeMarker中文手册--KLW 基本概念
什么是值?
编程人员可以跳过本部分.
首先举一些样例值,如你日常数学中的那些数字,16,0.5等.在计算机语言中值的概念更宽一点,值不一定要是数字.例如,在下面的例子中:
(root)
|
+- user = "Big Joe"
|
+- today = Jul 6, 2007
|
+- todayHoliday = false
|
+- lotteryNumbers
| |
| +- (1st) = 20
| |
| +- (2st) = 14
| |
| +- (3rd) = 42
| |
| +- (4th) = 8
| |
| +- (5th) = 15
|
+- cargo
|
+- name = "coal"
|
+- weight = 40
我们说user变量的值是”Big Joe”(一个字符串).变量today的值是 Jul 6,2007(一个日期),变量todayHoliday 的值是false(一个布尔值),变量lotteryNumbers 的值是一个包含20,14,42,8,15的序列.当然也可以说lotteryNumbers包含多个值(例如,它的第二个值是14), 但是lotteryNumbers本省是一个值.这就像一个盒子里可以包含许多其他东西,但是盒子本省仍然是一个东西.最后我们还有一个cargo变量,它是一个哈希表(又像是一个盒子).所以说,值是一些能存储在变量(如user,cargo,cargo.name)中的东西.但是一个值并不需要存在变量中才可以称为值,例如,我们这里有一个值100(可以直接使用):
<#if cargo.weight < 100>Light cargo</#if>
作为计算的临时结果,也称为值,像20,120(当这个模版执行时会计算出的临时结果).该模版会打印120:
${cargo.weight / 2 + 100}
最后解释一点:作为两个值相除的结果,40(cargo.weight)和2,一个新的值20会被创建.然后再加上100,120这个值也会被创建.然后120会被打印,模版会继续执行,这些临时值被丢弃.
现在你大概知道值的意义了吧.
什么是类型
值的一个重要方面就是他们的类型.例如变量user的值类型就是字符串, lotteryNumbers 的值类型是序列.值的类型是重要的,因为她大致确定了你该如何使用该值.比如${user/2}就是一个错误,但是${cargo.weight / 2}是正确的,并且能打印20.因为除法仅对数字有意义,而对字符串无效.另外,使用小数点的时候,像cargo.name,仅当cargo是一个哈希表的时候才有意义.还有,你使用<#list ...> 指令也仅对序列才有意义. <#if ...>指令的条件值类型必须是布尔型.
一个值可以同时有多种类型,尽管这不常见.例如下面的数据模型,mouse是一个字符串,同时是一个哈希:
(root)
|
+- mouse = "Yerri"
|
+- age = 12
|
+- color = "brown"
如果你将上面的数据模型整合到这个模版中:
${mouse} <#-- uses mouse as a string -->
${mouse.age} <#-- uses mouse as a hash -->
${mouse.color} <#-- uses mouse as a hash -->
输出结果会是:
Yerri
12
brown
数据模型是一个哈希表
从上面几个数据模型的例子中,你已经知道:标记为”root”的只是一个哈希表.当你使用变量user的时候,表示,user变量是存在root(根)哈希中的,就好像你写的是root.user一样.但是实际是没有变量root的.
也许对之前的数据模型你会感到困惑,根哈希表中又包含着另外的哈希表和序列(lotteryNumbers 和cargo).这没有什么特别的.一个哈希表可以包含其他变量.而这些变量包含值,这些值可以是字符串,数字等等.当然这些值也可以是哈希表或者序列.正如之前解释的那样,一个哈希表或者序列也是一个值,和字符串或者数字一样.
2009年4月13日星期一
FreeMarker中文手册--KLW 模版初览
最简单的模版就是纯HTML文件(或者其他任何文本文件,FreeMarker并不局限于HTML)。当客户端访问这个页面时,FreeMarker会发送这个html到客户端。当然你希望页面是更加动态时,你把FreeMarker能理解的部分加入到HTML中:
- ${...}:FreeMarker在输出中会用实际值替换大括号中的表达式。这些叫interpolation内插值。见第一个例子。
- FTL 标签(FreeMarker 模版语言标签):FTL标签和HTML标签类似,但是这些标签只是FreeMarker的指令,不会在结果中输出。这些标签都由“#”开始(用户自定的FTL标签由“@”开始,而不是”#”,但是这些是高级主题,见后续翻译)。
- 注释:模版注释和html注释类似,但是是由<#-- 和 -->标记。这两个界定符之间的任何东西包括界定符本身都会被FreeMarker忽略,不会在结果中输出。
除内插值、FTL标签和注释之外的任何值,都被认定为静态文本,不会被FreeMarker翻译,按原样输出。
FTL标签涉及到所谓的指令。这里的关系如同HTML标签(例如:和<table></table>)和HTML元素(如table元素)之间的关系。(如果你没有感觉到这里的不同,尽管将FTL标签和指令当做同义词吧,没有关系)
指令举例
尽管FreeMarker包含很多指令,但是在这个初步预览里,我们只举三个最常用的指令作为例子。
if 指令
使用if指令,我们可以根据条件跳过模版的某一部分。例如在第一个例子当中,我们想向你的老板Big Joe打招呼,而不是其它用户:
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>
Welcome ${user}<#if user == "Big Joe">, our beloved leader</#if>!
</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>
这里你告诉FreeMarker,字符串", our beloved leader"仅当变量user的值为"Big Joe"时才会被输出。总的来说,<#if condition>和</#if>之间的内容会被跳过,如果condition值为假的话。
让我们详细地看一下这里的条件:user == "Big Joe"。==是测试左右两边是否相等的操作符,运算结果是一个布尔值,true或者false。在==的左边,我们引用了一个变量,比较时会使用实际值代替。总的来说,在指令或者内插值当中没有引号标记的词FreeMarker都会当做变量来使用。在==的右边,我使用了一个字符串,字符串在模版中必须用引号标记。
如下: 如果price为0的话,会打印"Pythons are free today!":
<#if animals.python.price == 0>
Pythons are free today!
</#if>
类似于刚才直接使用字符串,这里的数字也可以直接使用,注意数字不需要加引号,如果你使用”0”,FreeMarker会将它理解为字符串.
如下: 如果price非0的话,会打印"Python are not free today!".
<#if animals.python.price != 0>
Pythons are not free today!
</#if>
你也可以这样写(使用数据模型初览中的例子):
<#if animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
</#if>
使用<#else>标签,我们指定当condition为假时该做什么.例如:
<#if animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
<#else>
Pythons are not cheaper than elephants today.
</#if>
这个会打印”Pythons are cheaper than elephants today.” 如果python的价格小于elephant的价格,反之则打印”Pythons are not cheaper than elephants today.”
如果你有个包含布尔值的变量(true或者false),你可以直接根在if指令后使用:
<#if animals.python.protected>
Warning! Pythons are protected animals!
</#if>
list 指令
当你想罗列某样东西的时候,list指令变的非常有用.例如,将之前演示序列(数组)的数据模型合并在一个模版中.
<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<#list animals as being>
<tr><td>${being.name}<td>${being.price} Euros
</#list>
</table>
输出会是:
<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<tr><td>mouse<td>50 Euros
<tr><td>elephant<td>5000 Euros
<tr><td>python<td>4999 Euros
</table>
List指令的通用格式是:
<#list sequence as loopVariable>repeatThis</#list>
repeatThis部分对给定序列的每一项都会重复一次,从第一项开始,一个接着一个.在所有的循环中,loopVariable指向循环的当前值.这个值只存在于<#list …>和<#/list>标签之间(作用域在这两者之间,之外则不可见).
作为另一个例子,我们列出数据模型例子中的所有fruit:
<p>And BTW we have these fruits:
<ul>
<#list whatnot.fruits as fruit>
<li>${fruit}
</#list>
<ul>
你应该熟悉whatnot.fruits这种表达式,见之前数据模型的例子.
include 指令
使用include指令你可以将另一个文件内容插入到模版当中.
假设你要在多个页面下显示版权信息.你可以创建一个仅包含版权信息的文件,然后将该文件插入到任何你需要的地方.如,你将版权信息保存在文件copyright_footer.html中:
<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>
任何时候,你需要这个文件时,仅简单使用include指令即可:
<html>
<head>
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<p>Blah blah...
<#include "/copyright_footer.html">
</body>
</html>
输出会是:
<html>
<head>
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<p>Blah blah...
<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>
</body>
</html>
如果你更新了copyright_footer.html,访问者会在所有页面看到新的版权信息.
使用多个指令
你可以在页面中任意多次地使用指令,并且你可以切套地使用指令,正如你可以在HTML标签中切套地使用其他HTML标签.例如下例会列出所有的animal,并使用粗体打印他们的名字:
<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<#list animals as being>
<tr>
<td>
<#if being.size == "large"><font size="+1"></#if>
${being.name}
<#if being.size == "large"></font></#if>
<td>${being.price} Euros
</#list>
</table>
注意这里,因为FreeMarker不对FTL标签,内插值和注释之外的文本翻译,它不会看到上面内嵌的font标签.
处理缺失变量
在实践中,数据模型经常有一些可选的变量(例如,有时候就是未定义).为防止典型的人为错误,FreeMarker不容忍对缺失变量的引用,除非你明确指明如果变量缺失时该做何处理.这里我们会展示两种最典型的处理方法.
给编程人员的提示:一个不存在(未定义)的变量和一个变量但是包含null值,对FreeMarker来说是一样的.所以这里”缺失”一词包含了这两种情况.
无论何时,我们引用一个变量时,我们都可以指定一个默认值,当这个变量缺失时,使用的时候在变量后跟”!”和默认值.如下例中,当user在数据模型中缺失时,模版会使用字符串”Anonymous”代替user.(当user在数据模型中有定义且不为null时,模版会只用准确的值,而”Anonymous”好像不曾在那里一样):
<h1>Welcome ${user!"Anonymous"}!</h1>
你可以在变量名后跟两个问号??来检测变量是否缺失.结合已介绍的if指令你可以跳过整个问候如果user变量缺失的话:
<#if user??><h1>Welcome ${user}!</h1></#if>
对于多步访问的变量,如animals.python.price,animals.python.price!0仅当animals.python都不缺失并且只有最后一个子变量price,可能缺失(在这种情况下值为0)的时候才是正确的.如果animals或者python缺失,模版处理过程会因”未定义变量”错误而停止.为防止出现这种情况,你需要这样写(animals.python.price)!0,加了括号之后,在这种情况下,表达式的值会为0,如果animals或者python缺失的话.同样的逻辑对??(测试运算符)也成立: animals.python.price??和(animals.python.price)??的情况和上面说的一样.
FreeMarker中文手册--KLW 数据模型初览
数据模型初览
正如你已经见过,数据模型基本上是一棵树。这棵树可以任意地深和复杂,例如:
(root)
|
+- animals
| |
| +- mouse
| | |
| | +- size = "small"
| | |
| | +- price = 50
| |
| +- elephant
| | |
| | +- size = "large"
| | |
| | +- price = 5000
| |
| +- python
| |
| +- size = "medium"
| |
| +- price = 4999
|
+- test = "It is a test"
|
+- whatnot
|
+- because = "don't know"
变量(起类似树干目录作用的,如root,animals,mouse,elephant,python,whatnot)被称作散列(哈希),散列中存着其他变量(子变量),可以根据名字查找(如 animals,mouse,price)
只存单个值的变量(size,price,test和because)称为标量scalars。
当你想在模版中使用子变量时,需要指明从root到该节点的路径,使用小数点”.”作为分隔符。访问mouse中的price,首先从root开始,到达animals,然后进入mouse,再进入price,所以你需要这样写animals.mouse.price(root,根节点是逻辑上的,实际上不存在,不用写---KLW注)。当你在一个表达式两端加上${ . . . }时,你就告诉了FreeMarker在这里输入相应文本。
还有一种更重要的变量:序列(数组)。它们和散列类似,但是他们不存储变量名,而按照数组方式保存子变量,你可以使用数字索引访问变量。例如,在下面的数据模型中,animals和whatnot.fruits是序列(数组):
(root)
|
+- animals
| |
| +- (1st)
| | |
| | +- name = "mouse"
| | |
| | +- size = "small"
| | |
| | +- price = 50
| |
| +- (2nd)
| | |
| | +- name = "elephant"
| | |
| | +- size = "large"
| | |
| | +- price = 5000
| |
| +- (3rd)
| |
| +- name = "python"
| |
| +- size = "medium"
| |
| +- price = 4999
|
+- whatnot
|
+- fruits
|
+- (1st) = "orange"
|
+- (2nd) = "banana"
访问数组中的子变量,你可以按数组方式访问(中括号中加数字索引)。索引从0开始(编程惯例),所以第一个元素的索引是0,第二个元素的索引是1,依此类推。所以要获得第一个animal的name,你只需如此:animals[0].name。访问whatnot.fruits中的第二个元素(字符串“banana”),使用如下:whatnot.fruits[1]。
标量可以继续细化为以下几种:
- 字符串:文本,任意序列的字符,如”m”,”o”,”u”,”s”,”e”。例如,上例中的所有name和size都是字符串。
数字:数字类型的值,如上例中的所有price。字符串“50”和数字50在FreeMarker中两种完全不同的类型。前者只是一个由两个字符组成的序列(仅仅是刚好可以被我们理解为数字,而FreeMarker不会这么理解的)。后者是一个数字值,可以作代数运算。
日期/时间:一个日期,或者时间,比如捕获动物的日期,或者商店开门的时间。
布尔型:布尔型值,true或者false,yes或者no。比如animals可以有一个projected子变量,表示这个动物是否被保护。
摘要:
- 数据模型可以用数结构可视化地表示
- 标量存单个值:这个值可以是字符串、数字、日期时间或者布尔类型。
- 散列值中包含着其他变量,同时它们拥有一个全局独一无二的名字。
- 序列(数组)中按照数组方式存值,值可以用从0开始的索引访问。
FreeMarker中文手册--KLW 模版 + 数据模型 = 输入
假设你需要一个HTML页面在电子商店应用中,如下:
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome Big Joe!</h1>
<p>Our latest product:
<a href="products/greenmouse.html">green mouse</a>!
</body>
</html>
假设用户名(上例中的“Big Joe”)应该依赖于谁登录进这个页面,而最新商品应该从数据库中读取,因此潜在地,这些内容是经常改变的。在这种情况下,我们不能直接在HTML中输入用户名、URL和最新商品的名字,换句话说,我们不能使用静态HTML。
对这个问题的解决办法,FreeMarker使用一个模版,而不是静态HTML页面。这个模版和静态页面类似,除了它包含一些指令(让FreeMarker将页面变成动态的):
<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome ${user}!</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>
这个模版存储在web服务器上,就像静态HTML页面一样。但是,无论谁访问这个页面时,FreeMarker会即使介入并翻译这个模版,把所有的 ${...}替换成最新的内容(比如,将${user} 替换成“Big Joe”或者其他登录者的用户名),然后把结果发送给访问者的Web浏览器,所以访问者的浏览器会接收到像第一个例子中的HTML页面(纯HTML没有FreeMarker指令),不会察觉到服务器已经使用了FreeMarker。模版文件本身(存储在服务器端)不会改变。这个翻译过程对每次访问都会发生。这保证了显示的信息总是最新的。
现在,你也许已经注意到模版没有包含任何指令来找出谁是当前访问者,或者查询数据库获得最新产品。它看起来好像已经知道这些值。而实际上,这里隐藏了一个FreeMarker的重要思想(实际上是MVC的),显示逻辑和业务逻辑应该分离。在模版中只处理显示相关的事情,比如可视的图样、格式等。显示的数据(如用户名等)在FreeMarker之外准备,比如经常用java语言或者其他通用语言。所以模版作者不用知道这些值是如何计算的。实际上,这些值的计算方法可以完全改变,而模版仍可保持不变,同样,不改变其他任何东西只修改模版可使页面外观完全改变。当模版设计者和编程人员是不同人时这种分离会格外有用。
当FreeMarker(和模版作者)不关心数据是如何计算的时候,FreeMarker仍需知道实际数据是什么。所有在模版中使用的数据,都可以归入所谓的数据模型。这个数据模型由已经提到的Java等常规性语言计算。对模版作者而言,数据模型是一个树状结构(如你硬盘上的目录和文件)。在这个例子中,直观地表示如下:
(root)
|
+- user = "Big Joe"
|
+- latestProduct
|
+- url = "products/greenmouse.html"
|
+- name = "green mouse"
(为防止误解:数据模型并不是文本文件,以上只是直观的展示树状。它从java对象中来,但把这个问题留给java编程人员吧。)
把这个和你之前看到的模版相比较,${user}和${latestProduct.name}。类似地,数据模型像计算机的文件系统,root和latestProduct对应目录,而user, url 和 name对应文件。url和name在latestProduct目录下。所以latestProduct.name指的是在latestProduct目录下的name文件。这只是个类比,这里没有目录和文件。
总而言之,模版和数据是FreeMarker生成输出时需要的:
模版 + 数据模型 = 输出
FreeMarker中文手册--KLW 前言
什么是FreeMarker?
FreeMarker是一个模版引擎:一个通用的基于模版产生文本输出(可以是任何东西,从HTML到自动生成代码)。它是一个为java程序员提供的java类库,而不是一个为终端用户提供的应用程序,当然程序员也可以将FreeMarker加入到他们的产品当中。
FreeMarker设计时是用来生成HTML Web页面的,尤其是针对那些基于servlet的MVC(Model View Controller)模式的web应用程序。使用MVC模式的思想是分离设计者(HTML作者)和编程人员,使每个人都能发挥自己的长处工作。页面设计者可以修改页面外观而编程人员无需修改代码或者重新编译代码,因为程序的应用逻辑(Java程序)和页面设计(FreeMarker模版)是分离的。页面模版不会被复杂的代码打碎(早期jsp页面中常插入复杂代码---KLW注)。这种分离是很有用的,甚至对那些编程人员和页面设计者是一个人的项目,因为它能使应用程序清晰和容易维护。
尽管FreeMarker具有一定的编程能力,但是它不是象PHP那样成熟的编程语言。更确切地说,Java程序提供被显示的数据(像SQL查询),而FreeMarker仅仅使用模版生成文本页面来显示提供的数据。

FreeMarker不是一个Web应用框架。更合适地说,它是web应用程序框架的一个组件,但是FreeMarker引擎本身对HTTP或者servlet一无所知。它只负责生成文本。因此,它可以完美地应用在任何非web环境的应用程序中。注意,尽管如此,我们还是提供了可用的解决方案:把FreeMarker作为Model 2框架或者Strust的可视组件(view部分)。
FreeMarker是免费的,使用BSD格式许可证发布,它是一个OSI 认证的开源软件。OSI认证是Open Source Initiative的一个认证标记。
我应该看什么?
如果你是一个…
- 页面设计者,你应该查阅模版作者手册,更多具体细节可以查看参考。
- 编程人员,你应该首先查阅模版作者手册,然后再查阅编程人员手册,更多细节可以查看手册。(这些偶都还没有翻译好呢,呵呵)
FreeMarker中文手册--KLW 目录
2009年4月9日星期四
2009年4月7日星期二
清明记事
一。这次回到杭州的路线奇特诡异,先到临海,再转绍兴,最后到杭州。下午三点出发,晚上九点钟抵达。在临海站时,用的是站长留位票。 出行难。
二。快到杭州时因为短信问题发生了点不愉快的事情。当时我生气地想把手机撕掉,太过气愤,以致当我看到"我有点想你"时已经不怎么生气了。
三。见到堂弟,多年未见,如今已是少年,当年走时才六七岁。姑姑姑父均不同程度地发福。
四。回到村中,村中变化并不大,那些人依然是认得面孔,不晓得名字。众人说些问讯的话,我不感兴趣的内容,回答"嗯嗯嗯,我先前在北京,现在杭州。我没有碰见他。" 我知道这些事情无非是他们的谈资而已。家中房屋,风吹雨打,年久失修,主梁都已倒了。害怕自己的房子被连累,隔壁老太公得知是我们回来,激动地就差没掉眼泪,费力地说要修房子了。我无奈,说着应该的,应该的,要修房子的。
五。上坟。爷爷奶奶的坟面简陋地连石刻的名字都没有。有一支坟,仅仅知道是家中祖上,无名无姓,不知男女。
2009年4月2日星期四
和谐无处不在


实际上邮件还是在草稿当中:






