今天用IDEA做个Java的Web工程,想运行一下,在IDEA配置好Tomcat(8180端口),部署上去,运行,竟然报Address localhost:8180 is already in use,我检查了一下本地没有在8180上的服务,奇怪了,前一段用IDEA的时候没有这样的问题,我尝试将Tomcat换到其他的端口,依旧报错,还是Google一下吧,找到了http://www.notionzone.com/2008/11/19/intellij-idea-eclipse-tomcat-deploy-58.html,原来是NOD32的问题,我就是最近才换到NOD32的,将NOD32中“启用HTTP检查”关闭就好了。
用Google API来取得Google帐户的联系人列表
昨天写了一篇从Web Mail里取得用户通讯录的方法的文章,里面提到了Google的Account Authentication API,今天我们就用Account Authentication API和Google Contacts Data API来做一个取得Google帐户联系人的测试。
首先我们要看一下Account Authentication API,对于网络应用来说我们选择对网络应用程序的验证,对网络应用程序的验证也提供了 OAuth 和 AuthSub 两种认证方式,我们选择AuthSub的认证方式,认证过程如下图

用户在第三方Web应用上向Google Accounts Authentication发送AuthSub的HTTP请求,如果用户没有登录Google,则会显示登录页面,用户登录之后,会提示用户是否接受或拒绝这个第三方Web应用的访问请求,如果用户同意,Google就会生成一个token,转回第三方Web应用,第三方Web应用凭此token,可以请求Google的相关Sevice,比如联系人的服务,从而取得相关数据。
AuthSub有两种接口,一个是AuthSubRequest(A call to this method sends the user to a Google Accounts web page, where the user is given the opportunity to log in and grant Google account access to the web application. If successful, Google provides a single-use authentication token, which the web application can use to access the user’s Google service data.)另一个是AuthSubSessionToken(A call to this method allows the web application to exchange a single-use token for a session token),按照Google文档的理解,AuthSubRequest是一个单次的认证,AuthSubSessionToken应该是带会话(Session)的。
我们就举Google提供的例子
https://www.google.com/accounts/AuthSubRequest? next=http%3A%2F%2Fwww.yourwebapp.com%2Fshowcalendar.html &scope=http%3A%2F%2Fwww.google.com%2Fcalendar%2Ffeeds%2F &session=1 &secure=1
https://www.google.com/accounts/AuthSubRequest就是AuthSub请求的地址,next表示认证之后要转回的地址,一般就是第三方Web应用的地址,也就是你网站的一个地址,Google会把token附带到这个地址后面,scope是你要请求的Google服务地址,这个例子里是要访问Google日历的数据,另外两个参数看Google的文档吧。
接下来,我们要取得Google帐户的联系人,我们先看看Google提供了多少可以访问的服务吧,访问Google数据API,Google提供的数据还着不少,有日历、文档、图书搜索、网路相册等等,当然也包括我们所需要的联系人的API,Google数据API要好好了解一下,总体来说Google提供一个Gdata的数据格式,和RSS的feed类似的格式,通过相关服务的访问地址,就可以返回Gdata数据,至于Gdata的读取,已经有了很多程序语言的封装好的程序(http://code.google.com/intl/zh-CN/apis/gdata/clientlibs.html),直接用就可以了,我们用PHP举例,PHP对Gdata的封装,是Zend Framework里的Gdata包,在http://framework.zend.com/download/gdata下载就可以了,但是现在Zend Gdata的包里没有直接的Google contacts的组件,但不要紧,通过Zend Gdata里基础数据的访问,可以取得Google contacts。
我们看看Google Contacts Data API的开发人员指南吧,取得联系人的Feed URL是
http://www.google.com/m8/feeds/contacts/userEmail/full 或 http://www.google.com/m8/feeds/contacts/default/full
那我们就用PHP来写一个取得联系人的程序吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | require_once 'Zend/Loader.php'; Zend_Loader::loadClass ( 'Zend_Gdata' ); Zend_Loader::loadClass ( 'Zend_Gdata_AuthSub' ); Zend_Loader::loadClass ( 'Zend_Gdata_ClientLogin' ); Zend_Loader::loadClass ( 'Zend_Gdata_Query' ); Zend_Loader::loadClass ( 'Zend_Gdata_Feed' ); $my_contacts = 'http://www.google.com/m8/feeds/contacts/default/full'; if (! isset ( $_SESSION ['cal_token'] )) { if (isset ( $_GET ['token'] )) { // You can convert the single-use token to a session token. $session_token = Zend_Gdata_AuthSub::getAuthSubSessionToken ( $_GET ['token'] ); // Store the session token in our session. $_SESSION ['cal_token'] = $session_token; } else { // Display link to generate single-use token $googleUri = Zend_Gdata_AuthSub::getAuthSubTokenUri ( 'http://' . $_SERVER ['SERVER_NAME'] . $_SERVER ['REQUEST_URI'], $my_contacts, 0, 1 ); echo "Click <a href='$googleUri'>here</a> " . "to authorize this application."; exit (); } } // Create an authenticated HTTP Client to talk to Google. $client = Zend_Gdata_AuthSub::getHttpClient ( $_SESSION ['cal_token'] ); $gdata = new Zend_Gdata ( $client ); $query = new Zend_Gdata_Query ( $my_contacts ); //$query->setMaxResults(10); $query->setMaxResults ( 2000 ); $feed = $gdata->getFeed ( $query ); foreach ( $feed as $entry ) { $parts = $entry->getExtensionElements (); foreach ( $parts as $p ) { $element = $p->getDOM (); switch ($element->tagName) { case 'email' : print ( "Email: " . $element->getAttribute ( 'address' ) . "<br/>" ); break; case 'phoneNumber' : print ( "Phone: " . $element->nodeValue . "<br/>" ); break; default : continue; } } } |
放在你服务器上运行一下吧(别忘了Zend Gdata包要加进去)。
从邮件服务商取得用户通讯录的方法
我们在使用facebook、开心网这些SNS网站的时候,里面有一项功能,就是通过邮件邀请好友,它可以导出你在Hotmail、Gmail、Yahoo、网易、新浪等Web Mail里的通讯录,然后给这些好友发送邀请信,这种病毒式营销推广方式起到了非常好的效果,facebook、开心网这些网站得以迅速壮大,这项功能也成了SNS网站的标配,从技术角度,我们就来探讨一下实现这项功能的原理和方法。
先说Gmail、Hotmail,Google和MS还是非常强大的,Google提供了Account Authentication API,它允许第三方的网站通过它的认证服务后,通过Google的Service取得用户的数据,包括通讯录等,MS也有Windows Live ID服务,不过我对微软的东西研究的不深,MSDN上找到的Windows Live ID SDK的资料,http://msdn.microsoft.com/en-us/library/bb404787.aspx。
接下来说说其他邮件系统的取得方式,主要的原理就是通过模拟用户行为,登陆Web Mail系统,取得用户通讯录的信息,很多Web Mail都提供通讯录CSV格式的下载服务,我们取得这个CSV文件就可以了,网易的邮件系统似乎已经不提供CSV文件的下载,那只能通过通讯录页面的HTML分析,来取得信息,所以相对复杂一些,而Sohu的通讯录虽然前端采用了Ajax,但后端取得通讯返回的格式是Json,应该是最方便。
接下来说模拟用户行为,其实就是通过在自己服务器端向Web Mail发送相对的HTTP请求,而模拟出用户登陆的行为,在Linux下,我们用cURL这个组件,在编译PHP的时候把cURL编译进去,使PHP支持cURL,cURL非常好用,也非常强大,在libcurl里有大多数开发语言的支持,如果使用JAVA的话,还可以使用Apache的HttpClient。
我们要模拟出用户登陆Web Mail的过程,我们就要对通常情况下,登陆Web Mail的HTTP请求过程有所了解,HttpWatch是一个非常好的工具,在做为插件安装倒IE和FireFox上,在IE或是FireFox里就可以看到所有的HTTP请求的信息了。

我们看看网易163的邮件的登陆过程
00:00:00.000 跳转提示
+ 0.000 0.231 1543 2352 POST 200 text/html; charset=UTF-8 http://reg.163.com/login.jsp?type=1&url=http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight%3D1%26verifycookie%3D1%26language%3D-1%26style%3D35
0.231 1543 2352 1 request
00:00:00.462 0.177 0 0 GET (History Cache) image/x-icon http://reg.163.com/favicon.ico
00:00:00.469 网易电子邮箱 – 简约3.0Beta
+ 0.000 0.118 1703 604 GET 302 Redirect to http://g4a30.mail.163.com/jy3/main.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=35&username=xxx
+ 0.245 0.366 1664 6788 GET 200 text/html;charset=GBK http://g4a30.mail.163.com/jy3/main.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 0.422 0.158 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/jscss/globle.css
+ 0.659 0.087 0 0 GET (Cache) application/x-javascript http://mimg.126.net/p/jy3style/js/0903090940/tools.js
+ 0.816 0.067 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/cmcss/skin_blue.css
+ 0.998 0.578 1586 5521 GET 200 text/html;charset=GBK http://g4a30.mail.163.com/jy3/top.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 1.015 0.624 1589 24844 GET 200 text/html;charset=GBK http://g4a30.mail.163.com/jy3/folder.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 1.025 0.316 1590 10490 GET 200 text/html;charset=GBK http://g4a30.mail.163.com/jy3/welcome.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 1.034 3.174 1592 2043 GET 200 text/html;charset=GBK http://g4a30.mail.163.com/jy3/justfresh.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz
+ 1.280 0.439 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/jscss/globle.css
+ 1.632 0.407 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/jscss/globle.css
+ 1.647 0.432 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/jscss/welcome2.css
+ 1.876 0.235 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/jscss/globle.css
+ 2.179 0.117 0 0 GET (Cache) application/x-javascript http://mimg.126.net/p/jy3style/js/0903090940/tools.js
+ 2.263 0.167 0 0 GET (Cache) application/x-javascript http://mimg.126.net/p/jy3style/js/0903090940/tools.js
+ 2.354 0.207 0 0 GET (Cache) application/x-javascript http://mimg.126.net/p/jy3style/js/0903090940/tools.js
+ 2.488 0.209 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/cmcss/skin_blue.css
+ 2.614 0.152 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/cmcss/skin_blue.css
+ 2.619 0.192 0 0 GET (Cache) application/x-javascript http://mimg.126.net/p/jy3style/js/0903090940/welcome.js
+ 2.740 0.325 0 0 GET (Cache) text/css http://mimg.126.net/p/jy3style/lib/0903090940/cmcss/skin_blue.css
4.208 9724 50290 20 requests
00:00:00.878 0.124 0 0 GET (History Cache) image/x-icon http://www.163.com/favicon.ico
00:00:03.572 网易电子邮箱 – 简约3.0Beta
+ 0.000 0.541 0 0 GET (Cache) text/html; charset=GB2312 http://mimg.163.com/tianqi/city_simple/58367_090311.html
+ 0.531 0.715 1424 250 GET 304 text/css http://mimg.163.com/jy3style/lib/jscss/ifr_weather2.css
+ 1.614 0.245 0 0 GET (Cache) image/gif http://mimg.163.com/jy3style/lib/images/weather2.gif
1.859 1424 250 3 requests
首先,将用户名和密码POST到http://reg.163.com/login.jsp?type=1&url=http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight%3D1%26verifycookie%3D1%26language%3D-1%26style%3D35,reg.163.com是网易通行证,后面的url是用户登陆网易通行证后要转向的地址,在这里我们看到就是http://entry.mail.163.com/coremail/fcg/ntesdoor2,这就是网易邮件的入口,在我们模拟登陆的时候,可以不传这个url过去,至于用户名和密码表单的name,看mail.163.com的HTML代码就知道了。相关错误处理,在POST到http://reg.163.com/login.jsp这个地址之后,返回的信息要取得的,因为如果用户输错了用户名或是密码,还是应该要提示用户的,而且在错误的情况下,也不用往下进行了,reg.163.com返回的错误页面里有“您的密码不正确”或是“用户名不正确”这样的信息,以此就可以判断用户是否成功登陆了。
之后我们要模拟用户访问http://entry.mail.163.com/coremail/fcg/ntesdoor2?lightweight=1&verifycookie=1&language=-1&style=35&username=xxx,就是登陆邮件的过程,注意,这里是个GET方法,用POST方法是不行的,访问entry.mail.163.com这个URL之后,会返回一个302码,就是要Redirect转向,到http://g4a30.mail.163.com/jy3/main.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz这个地址,这也就是大家看到的邮件登陆进去的首页了,注意这个sid要抓出来,应该是登陆的一个token,还有http://g4a30.mail.163.com这个地址,网易邮件是个集群,有非常多的服务器,你们每次上去的服务器是随随机的,用你的sid访问其他服务器也是没问题的,比如http://g1a98.mail.163.com/jy3/main.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz,所以这个服务器地址你可以写死。
解析来我们要看看网易邮件通讯录的URL,在UI看到是http://g4a30.mail.163.com/jy3/address/addrlist.jsp?sid=gAJQIqzzbBwvsaVoDhzztmnXQvBRiMuz&gid=all,我们模拟用户访问这个URL,然后抓取这个页面的内容HTML,分析一下,就可以了,在PHP里有个很好的HTML Parser工具PHP Simple HTML DOM Parser,可以像jQuery一样取得页面元素,这样就很方便的取得页面信息了。
其他一些Web Mail的登陆过程分析也是类似的,如果是取得CSV文件就更简单了,通过取到的通讯录内容,就可以在SNS网站上邀请了。
在这里我向国内这些大的门户网站或是Mail服务商提个建议,现在的互联网是一个开放化、平台化的趋势,网站应该开放自己的资源,可以让第三方的的应用更方便的访问,固步自封是不能发展的,试想如果QQ开放他的好友平台,引入第三方应用,那它将轻松超过Facebook,成为世界第一大SNS社区。
试用Nginx
Nginx是一款性能非常强劲的Web服务器,也可以用来做反向代理服务器,现在用Nginx做服务器站点越来越多了。

上图是Nginx和Lighttpd的主机数量比较
最近我试用了一下Nginx,做了一个Nginx+Tomcat的测试,具体步骤如下:
groupadd www
useradd -g www www
wget ftp://ftp.csx.cam.ac.uk/pub/software/programming/pcre/pcre-7.8.tar.gz
wget http://sysoev.ru/nginx/nginx-0.7.39.tar.gz
tar zxvf pcre-7.8.tar.gz
cd pcre-7.8
./configure
make
make install
cd ..
tar zxvf nginx-0.7.39.tar.gz
cd nginx-0.7.39
./configure –user=www –group=www –prefix=/usr/local/nginx –with-http_stub_status_module –with-http_ssl_module
make
make install
cd /usr/local/nginx/conf/
我们编辑nginx.conf文件,内容如下
user www www;
worker_processes 3;
error_log logs/error.log notice;
pid logs/nginx.pid;
events {
use epoll;
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
include /usr/local/nginx/conf/proxy.conf;
log_format main ‘$remote_addr – $remote_user [$time_local] “$request” ‘
‘”$status” $body_bytes_sent “$http_referer” ‘
‘”$http_user_agent” “$http_x_forwarded_for”‘;
#access_log logs/access.log main;
sendfile on;
tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80;
server_name localhost;
#charset koi8-r;
access_log logs/localhost.access.log main;
#location / {
# root html;
# index index.html index.htm;
#}
location / {
proxy_pass http://localhost:8080;
}
#location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
# expires 30d;
#}
#location ~ .*\.(js|css)?$ {
# expires 1h;
#}
location /NginxStatus {
stub_status on;
access_log on;
auth_basic “NginxStatus”;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
同时我们要在/usr/local/nginx/conf下创建proxy.conf文件,内容如下
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffers 32 4k;
保存之后,进入/usr/local/nginx/目录下,执行sbin/nginx,即可启动Nginx。
客户端浏览器输入http://ServerIP,可以看到Tomcat的欢迎页面,证明Ngnix已经转发HTTP请求给后端的Tomcat了,打开http://ServerIP/NginxStatus可以查看Nginx的状态,在Shell下我们还可以查看一下HTTP头
curl -I http://localhost
HTTP/1.1 200 OK
Server: nginx/0.7.39
Date: Thu, 05 Mar 2009 02:08:54 GMT
Content-Type: text/html;charset=ISO-8859-1
Connection: keep-alive
Content-Length: 8132
关于Nginx的配置,还是需要看它的文档http://wiki.nginx.org/Main,国内张宴的《Nginx 0.7.x + PHP 5.2.8(FastCGI)搭建胜过Apache十倍的Web服务器(第4版)[原创]》非常不错,可以参考。
追逐互联网的梦想
离第一次上网已有10年时间了,我想还没有一样东西像互联网一样,如此深刻的改变着我们的生活,互联网带给了我梦想,并在着10年中不懈的追逐着这个梦想。
1998-2000是一波互联网浪潮的年代,99年的时候我创建了自己的网站–“爱情工作室”。
2000年,郑州,之所以在郑州,是因为周爱民(如今中国软件响当当的人物)忽悠我来做AV95互联网方面的工作,互联网在中国那时也算是刚起步,专业杀毒和互联网结合的前途也是不可限量,当时就来了AV95公司的所在地郑州,当时和爱民住在一个屋子里,几乎是晚上工作,白天休息,不得不承认,爱民兄绝对是为软件而生的,当时已经把JavaScript搞的是如火纯清,Delphi、win32更是不在话下,让我顶礼膜拜,不过爱民兄忠实的很随M$,而我后来却选择了JAVA。
在AV95时间不长,北京的一家互联网公司就来收购“爱情工作室”,收购金额不高,并且收购之后我要继续为其工作,我想了想,这也算是一个机会,就离开了AV95,也算是开始了一段创业生活,我找来一个在网上认识的朋友–一叶飞(这家伙现在已经不知去向了),说服他和我一起做,他当时刚刚大学毕业,郑州也没什么好工作,他就和我一起开始开始了这番“事业”。
我们在友爱路租了一处房子,是顶楼,阳台正对着碧沙岗公园,很快就到了盛夏季节,中原的夏天是很热的,空气中没有一丝风,我们租的房子是两间,其中一间有空调,我们就把电脑放在这间,早上我们起来洗漱好,就坐到电脑面前了,一叶飞会打开音箱,播放MP3,我们的MP3里的歌有很多,伍佰、动力火车、唐朝、黑豹、王菲等等,歌曲从早放到晚也不会重复,我们便在音乐的伴随下开始了工作,网站开发、编辑、美工都是自己在做,中午和傍晚,我们两个就会忍受着酷暑出去吃饭,回到屋子里都是一身大汗,要在空调下猛吹,日子一天一天,很充实、也很快乐,当时并没有考虑过太多的盈利模式、网站的未来,只是为了自己的兴趣在做这些事情,当时我已经想到了将网站做成完全的社区模式,所有内容由网友发起,也曾经计划个人日记这些产品,现在看来不就是Web2.0、博客吗?
2001年随着第一波互联网泡沫的破灭,我也结束了和北京网站的合作,爱情工作室也终结了,一叶飞去了北京,由于我父母是上海人,所以我选择了上海,开始了软件、互联网的另一段生活,如今8、9年过去了,我仍然不能忘记那段日子,因为对于我,互联网的梦想从未停止过,我将永远做互联网的追梦人。
用jQuery Form Plugin实现Ajax无刷新的文件上传
在我以前的意识里,觉得用JavaScript或Ajax提交文件表单似乎是一件麻烦的事情,也没有太仔细的研究,后来看jQuery的时候,发现了jQuery Form Plugin这个插件,它方便的实现了Ajax方式的表单提交,例子里也包括了文件表单的提交,那我也来试一下吧,还是用Kohana(最近专注于这个,而且PHP开发要快多了,要是用Struts,要费好多功夫)。
要Kohana支持上传,先要配置一些upload的参数,把system\config\upload.php拷贝到application/config下,里面的参数做好设置,具体参考Kohana的文档吧,之后创建一个Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class Myupload_Controller extends Top_Controller { public function index() { $view = new View ("upload"); $view->msg = "Upload File"; $view->render ( TRUE ); } public function upload() { $filename = upload::save('myfile'); echo $filename; exit (); } } |
Top_Controller为我自己创建的类,也继承自Kohana的Controller,之后创建upload.php的view文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <html> <head> <title><?php echo $msg;?></title> <script type="text/javascript" src="<?=url::base()?>js/jquery-1.3.2.min.js"></script> <script type="text/javascript" src="<?=url::base()?>js/jquery.form.js"></script> <script type="text/javascript"> // wait for the DOM to be loaded $(document).ready(function() { var options = { target: '#fileinfo' // target element(s) to be updated with server response }; $('#myForm').submit(function() { $(this).ajaxSubmit(options); return false; }); }); </script> </head> <body> <h2><?php echo $msg;?></h2> <form id="myForm" action="<?=url::base()?>myupload/upload" method="post" enctype="multipart/form-data"> <input name="myfile" type="file" /> <input type="submit" value="Submit Comment" /> </form> <div id="fileinfo"></div> </body> </html> |
在这里我们用jQuery Form Plugin来提交表单,之后将信显示在“fileinfo”这个div里,去试试效果吧。
在http://malsup.com/jquery/form/里有详细的API和例子,同时http://malsup.com/jquery/里也有其他的一些不错的jQuery的插件。
Kohana的Events和Hooks研究
Kohana是一个使用PHP5的面向对象的MVC框架,是从CodeIgniter衍生出来,研究了几天,感觉还不错,就是它的文档实在太简陋了,在看到它的Events机制和Hooks机制时,确实有点不明白,文档上说的实在太简单了,后来我在Google上搜索了一下,找到两篇文章《Events and hooks in Kohana》和《Practical Kohana Hooks example: PHPIDS》,上面讲了一下Kohana的Events和Hooks,并举了例子,我也开始慢慢理解了它的含义。
在Kohana的Events中,默认定义了很多的Events(例如system.ready、system.pre_controller等等),中文理解就是事件,也就是触发点,在程序在运行到某个位置时,会被触发,具体就是调用Event::run方法,我们在Kohana.php里可以看到在不同的位置执行了不同的Event::run方法,那么Hooks的意义在于当一个事件被触发之前,可以通过已加载的Hooks来修改事件的回调,有点绕口,说白了就是在事件发生之前,做点事情,举个例子,你安排了今天的日程,下午3点要开会,那么在3点开会就是一个Event(事件),那么到3点的时刻,你希望提醒你一下,你在你的手机里定了一个闹钟,在3点的时候会响,这就是一个Hook,可以看出Hook是基于Event的。
我们可以创建自己的Hook程序,并加载如默认的Events里,但是默认的Events不一定能满足我们的需要,比如我们在Web应用中经常使用的权限校验,判断这个用户是否登陆过,我们虽然可以使用默认Events里的system.pre_controller,但是这个Event是针对所有的Controller的,有些Controller是不需要校验用户的,还好Kohana允许自己定义Event,下面我们还是举例说明吧。
要使用Hooks,首先要在application/config/config.php里将$config[‘enable_hooks’]置为TRUE。
之后我们定义自己的Event,我们在application/controllers目录下建立base.php,代码如下:
1 2 3 4 5 6 7 | class Base_Controller extends Controller { public function __construct() { parent::__construct(); Event::run("base.construct"); } } |
我们继承了Kohana的Controller,在构造函数里定义了Event,名字叫“base.construct”,以后我们的Controller都继承自Base_Controller,那么在对象创建的时候都会触发base.construct事件。
Hook的文件放在application/hooks下,我们就创建一个hook文件sessioncheck.php,代码如下:
1 2 3 4 5 6 7 8 9 | class SessionCheck { public function check() { echo "check session ..."; } } Event::add('base.construct', array('SessionCheck','check')); |
我们将SessionCheck的check方法加载到了base.construct事件上,也就是在触发base.construct事件之前,会执行SessionCheck的check方法。
接下来我们写一个Controller,在application/controllers目录下建立first.php,代码如下:
1 2 3 4 5 6 7 | class First_Controller extends Base_Controller { public function index() { echo "First - index"; exit(); } } |
我们执行一下看看会有什么提示,http://localhost/kohana/first,显示
check session …First – index
没问题了,在Controller构造时,执行了Hook里的方法。
从JavaEye上找的Google翻译JavaScript脚本
在Blog或新闻发布的时候,我们希望URL是一段有意义的字串,例如我Blog里的《Linux下切分Tomcat的catalina.out日志文件》文章链接是https://i.laoer.com/2009/02/18/rotating-catalina-out-in-tomcat-using-cronolog/,这样有利于SEO(搜索引擎优化),对于我们母语为中文的小虾,有时候要翻译标题还是有点头疼的,还好有了Google翻译,让翻译的事情变得简单了一点,但对于开发者,怎么使用户也方便的翻译,特别是在写Blog的时候就能把标题翻译好,还要动点脑筋,我看到JavaEye里发布新闻的地方有个“让Google帮助生成永久链接”的功能挺好的,看了一下他们的源码,得到以下这段,顺别也解释一下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <script type="text/javascript" src="http://www.google.com/jsapi"></script> //载入Google的jsapi <script type="text/javascript"> function slugify(str) { //这个方法应该是将字符转为小写,并把一些特殊字符转为"-" return str.toLowerCase().replace(/[^a-z0-9-_]+/g, '-').replace(/^-|-$/g, ''); } google.load("language", "1"); //载入Google的language Ajax库 function translate_title() { //这里就是翻译文章标题了,里面的方法需要jQuery支持,之前要引入jQuery的包,这里就省略了 if($F("news_title").blank()) { alert("请先填写标题"); return; } $("news_slug_url").value = "正在翻译中..."; google.language.translate($F("news_title"), "zh", "en", function(result) { if (!result.error) { $("news_slug_url").value = slugify(result.translation); } }); } </script> |
这里有个地方要说一下,Google的Ajax库API还是挺好的,可以load几种Ajax的库,另外Google的Web 工具包也可以看看。
修改Oracle连接数
在SQL/PLUS下,先检查一下当前连接数
show parameter process;
show parameter session;
修改命令,根据自己实际情况修改数值
alter system set processes=300 scope = spfile;
alter system set sessions=350 scope = spfile;
之后重启Oracle
Linux下切分Tomcat的catalina.out日志文件
随着Tomcat的运行,catalina.out文件会越来越大,虽然Tomcat每日会生成一个catalina.ymd.log的文件,但catalina.out主文件仍然不断增加,需要对catalina.out按日切分才好,在网上找了一下,看到一篇《rotating catalina.out in tomcat 5.5 using cronolog》,就用公司的Tomcat配置一下。
cronolog工具已经在服务器上装过,一个对日志切分的小工具,其主页在http://cronolog.org/,我们也用它来切分Apache的日志。
进入Tomcat的bin目录,打开catalina.sh文件,找到tomcat启动的相关行,或者你直接查找catalina.out,一般我们修改下面行中的内容(因为我们一般不会在-security条件下运行),
1 2 3 4 5 6 7 8 9 10 11 12 13 | else “$_RUNJAVA” $JAVA_OPTS $CATALINA_OPTS \ -Djava.endorsed.dirs=”$JAVA_ENDORSED_DIRS” -classpath “$CLASSPATH” \ -Dcatalina.base=”$CATALINA_BASE” \ -Dcatalina.home=”$CATALINA_HOME” \ -Djava.io.tmpdir=”$CATALINA_TMPDIR” \ org.apache.catalina.startup.Bootstrap “$@” start \ >> “$CATALINA_BASE”/logs/catalina.out 2>&1 & if [ ! -z "$CATALINA_PID" ]; then echo $! > $CATALINA_PID fi fi |
修改
org.apache.catalina.startup.Bootstrap “$@” start \
>> “$CATALINA_BASE”/logs/catalina.out 2>&1 &
为
org.apache.catalina.startup.Bootstrap “$@” start 2>&1 \
| /usr/local/sbin/cronolog “$CATALINA_BASE”/logs/catalina.%Y-%m-%d.out >> /dev/null &
同时,上面有一行
touch “$CATALINA_BASE”/logs/catalina.out
可以注释掉,完成之后重起Tomcat就可以了,在logs目录下可以看到catalina.2009-02-18.out的日志,是按日生成的。