blogjava-凯发k8网页登录

blogjava-凯发k8网页登录http://www.blogjava.net/lingy/zh-cnsat, 08 apr 2023 20:41:14 gmtsat, 08 apr 2023 20:41:14 gmt60vim 替换http://www.blogjava.net/lingy/archive/2012/05/09/377726.html林光炎林光炎wed, 09 may 2012 09:28:00 gmthttp://www.blogjava.net/lingy/archive/2012/05/09/377726.html:%s/143250/143120/g

 

林光炎 2012-05-09 17:28
]]>
[端口被占用,环境起不来]常见测试环境问题解决http://www.blogjava.net/lingy/archive/2012/01/31/369065.html林光炎林光炎tue, 31 jan 2012 03:29:00 gmthttp://www.blogjava.net/lingy/archive/2012/01/31/369065.html大家是否遇到部署环境的时候,端口被占用,环境起不来的问题呢?遇到这个情况,我们该如何解决呢?

下面我把我遇到这个问题解决的方法总结下:

现象是这样的:

1. 页面访问出现: http 500

2. 后台启动日志提示: 

vodka init successfully!!! ...

start apache ......

(98)address already in use: make_sock: could not bind to address [::]:5071

no listening sockets available, shutting down

unable to open logs

解决方法: 杀掉占用的进程

首先,需要用root账号登陆,登陆后,输入: netstat -plan |grep 5070

出现如下:


[intranet root@qa-kernal-143-41 /root]

#netstat -plan |grep 5070

tcp 0 0 :::5070 :::* listen 28002/httpd 

tcp 1 0 ::ffff:10.20.143.41:5070 ::ffff:10.16.46.75:4654 close_wait 28406/httpd 

最后杀掉占用的进程,输入:   lsof -i:5070 即可 (注5070是端口号)

[intranet root@qa-kernal-143-41 /root]

#lsof -i:5070

林光炎 2012-01-31 11:29
]]>
linux用户组相关知识http://www.blogjava.net/lingy/archive/2012/01/31/369064.html林光炎林光炎tue, 31 jan 2012 03:28:00 gmthttp://www.blogjava.net/lingy/archive/2012/01/31/369064.html用root用户登陆输入命令cd /home,再用ll查看发现drwxr-xr-x 12 622   622    4096 apr 18 19:21 aurora  aurora用户属主不正确
用命令chown aurora.aurora aurora修改用户属主

知识点:
1.查看用户组信息#cat /etc/passwd
/etc/passwd中一行记录对应着一个用户,每行用冒号隔开分为7个字段,具体字段说明如下:
name:password:uid:gid:comment:home:shell
用户名:口令:用户标识号:组标识号:注释性描述:主目录:登录shell

home: 指定用户的主目录的绝对路径
shell: 用户登陆后启动的一个进程,这个进程是用户登录到系统后运行的命令解释器或某个特定的程序,即shell。

例子:
aurora:x:707:707::/home/aurora:/bin/bash
   系统首先会查阅 /etc/passwd 文件,看是否有aurora 这个账号,然后确定aurora的uid,通过uid 来确认用户和身份,
   如果存在则读取/etc/shadow 影子文件中所对应的aurora的密码;   
   用户名:x:uid:gid:用户名全称:用户的家:用户所用的shell类型
  x指向映射
uid为0 则为root  


2.aurora:$1$nc4fzxjz$op5ls0evj4umslvmhp1ez/:15082:0:99999:7:::
etc/shadow文件是/etc/passwd 的影子文件,这个文件并不由/etc/passwd 而产生的,这两个文件是应该是对应互补的;
shadow内容包括用户及被加密的密码及其他/etc/passwd 不能包括的信息。

用户:密码:上次修改密码的时间 :两次修改口令间隔最少的天数:两次修改口令间隔最多的天数

3.aurora:x:707:
用户名:用户组密码:gid :用户列表
第四字段:用户列表,每个用户之间用,号分割;本字段能为空;
如果字段为空表示用户组为gid的用户名

4.chown 修改文件和文件夹的用户和用户组属性

5.passwd aurora 修改密码

林光炎 2012-01-31 11:28
]]>
linux之用户登录环境变量分析http://www.blogjava.net/lingy/archive/2012/01/31/369057.html林光炎林光炎tue, 31 jan 2012 02:53:00 gmthttp://www.blogjava.net/lingy/archive/2012/01/31/369057.html昨天遇见一个问题,与大家分享一下。现象如下,当从一个用户切换到另外一个用户。或者直接登录的时候会出现如错误:bash-3.2 

分析:

而且bpm这个用户以前都是好的,为什么呢?我们来分析一下用户的登录的一些原理就明白了:

用户在刚登录linux时,步骤如下:

1、首先启动 /etc/profile 文件,

2、然后再启动用户目录下的 ~/.bash_profile、 ~/.bash_login或 ~/.profile文件中的其中一个,

执行的顺序为:~/.bash_profile、 ~/.bash_login、 ~/.profile。 如果 ~/.bash_profile文件存在的话,一般还会执行 ~/.bashrc文件。

因为在 ~/.bash_profile文件中一般会有下面的代码:

if [ -f ~/.bashrc ] ; then

 . ./bashrc

fi

 因为在 ~/.bash_profile文件中一般会有下面的代码:
if [ -f ~/.bashrc ] ; then
. ./bashrc
fi
所以,~/.bashrc会调用 /etc/bashrc文件。

3、最后,在退出shell时,还会执行 ~/.bash_logout文件。

所以一个用户从登录到登出的执行顺序为:/etc/profile -> (~/.bash_profile | ~/.bash_login | ~/.profile) -> ~/.bashrc ->/etc/bashrc -> ~/.bash_logout

当我们查看当前用户的隐藏文件【ls -al】是否有这些文件的时候,这些文件都丢失了,所以就会出现bash-3.2的错误。也就是取不到当前登录用户的变量导致。

备注:关于各个文件的作用域,在网上找到了以下说明:

(1)/etc/profile: 此文件为系统的每个用户设置环境信息,当用户第一次登录时,该文件被执行. 并从/etc/profile.d目录的配置文件中搜集shell的设置。

(2)/etc/bashrc: 为每一个运行bash shell的用户执行此文件.当bash shell被打开时,该文件被读取。

(3)~/.bash_profile: 每个用户都可使用该文件输入专用于自己使用的shell信息,当用户登录时,该文件仅仅执行一次!默认情况下,他设置一些环境变量,执行用户的.bashrc文件。

(4)~/.bashrc: 该文件包含专用于你的bash shell的bash信息,当登录时以及每次打开新的shell时,该该文件被读取。

(5)/.bash_logout:当每次退出系统(退出bash shell)时,执行该文件. 另外,/etc/profile中设定的变量(全局)的可以作用于任何用户,而/.bashrc等中设定的变量(局部)只能继承/etc/profile中的变量,他们是"父子"关系。

(6)/.bash_profile 是交互式、login 方式进入 bash 运行的/.bashrc 是交互式 non-login 方式进入 bash 运行的通常二者设置大致相同,所以通常前者会调用后者。

 

凯发天生赢家一触即发官网的解决方案:

  从正确的环境copy这.bash_profile、.bashrc、.bash_logout三个文件中任一丢失的文件到当前目录下面。让其生效即可。

但为什么这三个文件丢失我没有找到其丢失的原因,最根本的猜测就是大家不小心删除了。另外linux的问题我个人的经验就是顺藤摸瓜、掌握原理之后就很多解决了!



林光炎 2012-01-31 10:53
]]>
oracle ddl,dml,dcl,tcl 基础概念 http://www.blogjava.net/lingy/archive/2010/08/17/329116.html林光炎林光炎tue, 17 aug 2010 08:50:00 gmthttp://www.blogjava.net/lingy/archive/2010/08/17/329116.html
data definition language (ddl) statements are used to define the database structure or schema. some examples:
  • create - to create objects in the database
  • alter - alters the structure of the database
  • drop - delete objects from the database
  • truncate - remove all records from a table, including all spaces allocated for the records are removed
  • comment - add comments to the data dictionary
  • rename - rename an object

data manipulation language (dml) statements are used for managing data within schema objects. some examples:
  • select - retrieve data from the a database
  • insert - insert data into a table
  • update - updates existing data within a table
  • delete - deletes all records from a table, the space for the records remain
  • merge - upsert operation (insert or update)
  • call - call a pl/sql or java subprogram
  • explain plan - explain access path to data
  • lock table - control concurrency

data control language (dcl) statements. some examples:
  • grant - gives user's access privileges to database
  • revoke - withdraw access privileges given with the grant command

transaction control (tcl) statements are used to manage the changes made by dml statements. it allows statements to be grouped together into logical transactions.
  • commit - save work done
  • savepoint - identify a point in a transaction to which you can later roll back
  • rollback - restore database to original since the last commit
  • set transaction - change transaction options like isolation level and what rollback segment to use


作者:李敬然(gnie)
出处: (http://www.cnblogs.com/gnielee/)
凯发k8网页登录的版权声明:本文的凯发k8网页登录的版权归作者与博客园共有。转载时须注明本文的详细链接,否则作者将保留追究其法律责任。



林光炎 2010-08-17 16:50
]]>
linux 命令http://www.blogjava.net/lingy/archive/2010/08/17/329113.html林光炎林光炎tue, 17 aug 2010 08:28:00 gmthttp://www.blogjava.net/lingy/archive/2010/08/17/329113.html 

insert into ali_zeus_access_area
select * from ali_zeus_access_area@zeus_dev;

script -q tty.log

sqlplus /nolog
conn rnd/mcqe88il@crmintsb
conn rnd/mcqe88il@crmint


scp tty.log guangyan.lingy@10.20.130.14:/home/guangyan.lingy/tty0601.log

export nls_lang=american_america.zhs16gbk
sqlplus rnd/mcqe88il@crmg

for remote in \
172.16.131.47 172.16.131.48 172.16.131.49 172.16.131.50 172.16.131.51 172.16.131.52 172.16.131.53 172.16.131.54 172.16.131.55 172.16.131.2 172.16.131.3 172.16.131.4 172.16.131.5 172.16.131.6 172.16.131.7 172.16.131.41 172.16.131.42 172.16.131.56 172.16.131.57 172.16.131.58 \
;do echo $remote; ssh $remote "grep 'sms webservice visitor ip is' output/logs/user/martini_sales_tool.log"; done

 

daisy.louy
 louying
 
 
carvin hello123
http://svn.alibaba-inc.com/repos/crm/martini/branches/20091209_dcs_guangyan.lingy_dev

weiming.shi
jinbo.yu
1234567890 alibaba-guest
我刚又试了


中国的十大宜居小城,中午的时候看的
http://focus.news.163.com/10/0531/09/680lgn5l00011sm9.html
有泉州:)

数据模型资源手册

需要低烟无卤阻燃电缆料的电缆企业



林光炎 2010-08-17 16:28
]]>
《设计模式》之java解读--桥接bridge http://www.blogjava.net/lingy/archive/2010/07/29/327422.html林光炎林光炎thu, 29 jul 2010 05:56:00 gmthttp://www.blogjava.net/lingy/archive/2010/07/29/327422.html阅读全文

林光炎 2010-07-29 13:56
]]>
无法定位程序输入点_except_handler4_common于动态链接库msvcrt.dll上http://www.blogjava.net/lingy/archive/2010/07/05/325279.html林光炎林光炎mon, 05 jul 2010 06:12:00 gmthttp://www.blogjava.net/lingy/archive/2010/07/05/325279.html最近,多种软件都出现了一个启动时的错误提示 “无法定位程序输入点_except_handler4_common于动态链接库msvcrt.dll上”。

出现这个提示情况是:windows xp 系统; 软件包括:window live writer 2009, windows messenger 2009, firefox 3.5 等非常新的版本。

这个提示出现后,点击确定,并不影响程序的使用。这个问题出现的原因,其实微软自己的问题。可谓是微软的vista 后遗症吧。

在 vista 系统里,有个挺酷的功能,就是“程序缩略图”,有点3d效果,这个功能,调用了 dwmapi.dll 这个库,而 dwmapi.dll 又引用了 msvcrt.dll 中的 _except_handler4_common 功能。

然而,在xp系统中,系统自带的 msvcrt.dll  和 vista 中的 msvcrt.dll 版本不同, 并没有这个 _except_handler4_common ,结果就出现了启动程序时,遇到的 “无法定位程序输入点_except_handler4_common于动态链接库msvcrt.dll上”的错误提示。

解决方法

      将 c:\windows\system32 下的 dwmapi.dll 改名,例如:改为 dwmapi.dll.bak。


林光炎 2010-07-05 14:12
]]>
oracle 回滚 sql操作http://www.blogjava.net/lingy/archive/2010/06/02/322585.html林光炎林光炎wed, 02 jun 2010 08:31:00 gmthttp://www.blogjava.net/lingy/archive/2010/06/02/322585.html

林光炎 2010-06-02 16:31
]]>
eclipse 启动运行速度调优 http://www.blogjava.net/lingy/archive/2009/10/01/297077.html林光炎林光炎thu, 01 oct 2009 03:24:00 gmthttp://www.blogjava.net/lingy/archive/2009/10/01/297077.html

jvm 提供了各种用于调整内存分配和垃圾回收行为的标准开关和非标准开关。其中一些设置可以提高 java ide 的性能。
注意,由于 -x (尤其是 -xx jvm)开关通常是 jvm 或 jvm 供应商特定的,本部分介绍的开关可用于 sun microsystems j2se 1.4.2。

以下设置在大多数系统上将产生比工厂更好的设置性能。
-vmargs - 表示将后面的所有参数直接传递到所指示的 java vm。

-xverify:none - 此开关关闭java字节码验证,从而加快了类装入的速度,并使得在仅为验证目的而启动的过程中无需装入类。此开关缩短了启动时间,因此没有理由不使用它。

-xms24m - 此设置指示 java 虚拟机将其初始堆大小设置为 24 mb。通过指示 jvm 最初应分配给堆的内存数量,可以使 jvm 不必在 ide 占用较多内存时增加堆大小。

-xmx96m - 此设置指定 java 虚拟机应对堆使用的最大内存数量。为此数量设置上限表示 java 进程消耗的内存数量不得超过可用的物理内存数量。对于具有更多内存的系统可以增加此限制,96 mb 设置有助于确保 ide 在内存量为 128mb 到 256mb 的系统上能够可靠地执行操作。注意:不要将该值设置为接近或大于系统的物理内存量,否则将在主要回收过程中导致频繁的交换操作。

-xx:permsize=20m - 此 jvm 开关不仅功能更为强大,而且能够缩短启动时间。该设置用于调整内存"永久区域"(类保存在该区域中)的大小。因此我们向 jvm 提示它将需要的内存量。该设置消除了许多系统启动过程中的主要垃圾收集事件。sunone studio 或其它包含更多模块的 ide 的用户可能希望将该数值设置得更高。
下面列出了其它一些可能对 eclipse 在某些系统(不是所有系统)上的性能产生轻微或明显影响的 jvm 开关。尽管使用它们会产生一定的影响,但仍值得一试。

-xx:compilethreshold=100 - 此开关将降低启动速度,原因是与不使用此开关相比,hotspot 能够更快地将更多的方法编译为本地代码。其结果是提高了 ide 运行时的性能,这是因为更多的 ui 代码将被编译而不是被解释。该值表示方法在被编译前必须被调用的次数。

-xx: useconcmarksweepgc -xx: useparnewgc - 如果垃圾回收频繁中断,则请尝试使用这些开关。此开关导致 jvm 对主要垃圾回收事件(如果在多处理器工作站上运行,则也适用于次要回收事件)使用不同的算法,这些算法不会影响整个垃圾回收进程。注意:目前尚不确定此收集器是提高还是降低单处理器计算机的性能。

-xx: useparallelgc - 某些测试表明,至少在内存配置相当良好的单处理器系统中,使用此回收算法可以将次要垃圾回收的持续时间减半。注意,这是一个矛盾的问题,事实上此回收器主要适用于具有千兆字节堆的多处理器。尚无可用数据表明它对主要垃圾回收的影响。注意:此回收器与 -xx: useconcmarksweepgc 是互斥的。

我的机器是512mb的内存
下面是我的eclipse启动参数:eclipse.exe -vmargs -xverify:none -xms64m -xmx256m -xx:permsize=20m  -xx: useparallelgc

-----

我的电脑是1g内存, 有一次内存不足了... myeclipse 就推荐我使用一个启动参数, 现在我的启动参数是:


eclipse.exe -vmargs -xverify:none -xms128m -xmx512m -xx:permsize=64m -xx:maxpermsize=128m -xx: useparallelgc



林光炎 2009-10-01 11:24
]]>
树结构和它的专用函数sys_connect_by_pathhttp://www.blogjava.net/lingy/archive/2009/09/20/295773.html林光炎林光炎sun, 20 sep 2009 13:32:00 gmthttp://www.blogjava.net/lingy/archive/2009/09/20/295773.html 关于树的普通应用
学习了下这个函数, 用orgindustries的表做了个测试:
正常的树型结构
select lpad(' ',6*(level-1))||industry,indlevel,indid,pindid
from orgindustries
start with indid=1
connect by pindid=prior indid
结果显示如下
                 indlevel  indid    pindid
        服装与服饰               1             1             0
              服装               2             2               1
                    女装        3             3               2

倒型树
下面这个例子是个”倒数”—倒过来的树型结构
select lpad(' ',6*(level-1))||industry,indlevel,indid,pindid
from orgindustries
start with indid=20
connect by indid=prior pindid;
这是标准结果:
                             indlevel indid    pindid
二手服装                      3        20       2
      服装                    2        2        1
            服装与服饰        1        1        0
结论
无论正树还是倒树, 关键就在于connect by的条件.
正树:  必须是  ‘父’= prior ‘子’
倒树:  必须是  ‘子’= prior ‘父’

树型结构的条件过滤
采用树型结构的话, 如果我们想将树上的一个分支砍掉.  将分支后面的结构都抛弃掉, 这个可以实现麽?当然可以。 但是不是用where, where条件只能去除单一的条件。
所以, 这种树型的过滤条件就需要加在connect by上面。

测试如下:由于用真实环境比较贴近实际,所以提前用下sys_connect_by_path函数来显示下环境

不加任何条件的环境:
select areaname,sys_connect_by_path(areaname,',')
from areas bb
start with areaname='中国大陆'
connect by parentareaid=prior areaid  

结果:
1        中国大陆,中国大陆
2        北京        ,中国大陆,北京
3        北京        ,中国大陆,北京,北京
4        东城区        ,中国大陆,北京,东城区
5        西城区        ,中国大陆,北京,西城区
22        广东        ,中国大陆,广东
23        广州        ,中国大陆,广东,广州
24        汕尾        ,中国大陆,广东,汕尾
25        潮阳        ,中国大陆,广东,潮阳
46        上海        ,中国大陆,上海
47        上海        ,中国大陆,上海,上海
48        黄浦区        ,中国大陆,上海,黄浦区
49        闸北区        ,中国大陆,上海,闸北区


加了where过滤条件的sql:
select areaname,sys_connect_by_path(areaname,',')
from areas bb
where bb.areaid>861000
start with areaname='中国大陆'
connect by parentareaid=prior areaid

结果为:
2        北京        ,中国大陆,北京
3        北京        ,中国大陆,北京,北京
4        东城区        ,中国大陆,北京,东城区
5        西城区        ,中国大陆,北京,西城区
22        广东        ,中国大陆,广东
23        广州        ,中国大陆,广东,广州
24        汕尾        ,中国大陆,广东,汕尾
25        潮阳        ,中国大陆,广东,潮阳
46        上海        ,中国大陆,上海
47        上海        ,中国大陆,上海,上海
48        黄浦区        ,中国大陆,上海,黄浦区
49        闸北区        ,中国大陆,上海,闸北区

结论:去掉了“1        中国大陆,中国大陆”数据

加了connect by的过滤条件:
select areaname,sys_connect_by_path(areaname,',')
from areas bb
where bb.areaid>861000
start with areaname='中国大陆'
connect by parentareaid=prior areaid  and areaname<>'广东'

结果为:
2        北京        ,中国大陆,北京
3        北京        ,中国大陆,北京,北京
4        东城区        ,中国大陆,北京,东城区
5        西城区        ,中国大陆,北京,西城区
46        上海        ,中国大陆,上海
47        上海        ,中国大陆,上海,上海
48        黄浦区        ,中国大陆,上海,黄浦区
49        闸北区        ,中国大陆,上海,闸北区

结论:去掉了整个广东的分支,  在结果集中只有北京和上海


sys_connect_by_path函数
采用sys_connect_by_path函数为:

select industry,sys_connect_by_path(industry,'/')
from orgindustries
start with indid=3
connect by indid=prior pindid;

结果为:
女装               /女装
服装               /女装/服装
服装与服饰            /女装/服装/服装与服饰

这样的话, 就可以实现, 树结构的结果集的单行拼接:

我们只需要取最大的字段就ok了

测试如下:

select max(sys_connect_by_path(industry,'/'))
from orgindustries
start with indid=3
connect by indid=prior pindid;

结果为:
/女装/服装/服装与服饰


复杂的树型结构――多列变单列
树型结构也分单树和多树(我的称呼,实际上就是指单支和多支)
对于下面的这种情况, 我们必须要构造的树就属于单支树。
原始环境
环境如下:
select * from test;

结果为:
1        n1
1        n2
1        n3
1        n4
1        n5
3        t1
3        t2
3        t3
3        t4
3        t5
3        t6
2        m1

造树
脚本如下:
select no,q,
       no row_number() over( order by no) rn,
       row_number() over(partition by no order by no) rn1
from test

结果如下:
no  q  rn rn1
1        n1        2        1
1        n2        3        2
1        n3        4        3
1        n4        5        4
1        n5        6        5
2        m1        8        1
3        t1        10        1
3        t2        11        2
3        t3        12        3
3        t4        13        4
3        t5        14        5
3        t6        15        6

每列的目的是:
rn1列主要的目的是分组, 按照value值‘1’,我们可以start with使用它。

rn列主要用来做connect by使用。 实际上它就是我们要的树。
第一个支: 2,3,4,5,6
第二个支: 8
第三个支: 10,11,12,13,14,15

中间为什么要断掉:7,9  目的就是为了区别每个分支。 到后面看具体的sql,就明白这里的说法了。

杀手锏
既然我们有了树, 就可以使用树型函数sys_connect_by_path和connect by啦,来拼接我们所需要的多列值。

脚本如下:
select no,sys_connect_by_path(q,',')
from (
select no,q,
       no row_number() over( order by no) rn,
       row_number() over(partition by no order by no) rn1
from test
)
start with rn1=1
connect by rn-1=prior rn

结果为:
1        ,n1
1        ,n1,n2
1        ,n1,n2,n3
1        ,n1,n2,n3,n4
1        ,n1,n2,n3,n4,n5
2        ,m1
3        ,t1
3        ,t1,t2
3        ,t1,t2,t3
3        ,t1,t2,t3,t4
3        ,t1,t2,t3,t4,t5
3        ,t1,t2,t3,t4,t5,t6

终极武器
最终我们要的值,是单列值, 其实想想, 也就是最长的一行咯。 那么就好办了。 我们直接group by ,然后取max值。
脚本如下:
select no,max(sys_connect_by_path(q,','))
from (
select no,q,
       no row_number() over( order by no) rn,
       row_number() over(partition by no order by no) rn1
from test
)
start with rn1=1
connect by rn-1=prior rn
group by no

结果为:
1        ,n1,n2,n3,n4,n5
2        ,m1
3        ,t1,t2,t3,t4,t5,t6

如果觉得前面的‘,’不好看,可以使用ltrim去掉。 或者用substr也可以。
如下:
ltrim(max(sys_connect_by_path(q,',')),',')
或者
substr(max(sys_connect_by_path(q,',')),2)


林光炎 2009-09-20 21:32
]]>
vim 到指定行 :3305http://www.blogjava.net/lingy/archive/2009/09/02/293577.html林光炎林光炎wed, 02 sep 2009 04:28:00 gmthttp://www.blogjava.net/lingy/archive/2009/09/02/293577.html :number
eg:
:3305

林光炎 2009-09-02 12:28
]]>
vim查找操作http://www.blogjava.net/lingy/archive/2009/09/02/293576.html林光炎林光炎wed, 02 sep 2009 04:25:00 gmthttp://www.blogjava.net/lingy/archive/2009/09/02/293576.html 
" 搜索
/joe/e : 设置光标到匹配"joe"的末尾
/joe/e 1 : 设置光标到匹配"joe"的末尾再后移一位
/joe/s-2 : 设置光标到匹配"joe“的开头再前移两位
/^joe.*fred.*bill/ : 匹配以'j'开头且"joe"到"fred"到"bill"之间至少有一个字符
/^[a-j]\ / : 搜索'a'到’j‘重复两次以上的开头行
/begin\_.*end : 多行匹配
/fred\_s*joe/i : 可以是任何空白字符包括\n,\t等等
/fred\|joe : 搜索fred或者joe
/.*fred\&.*joe : 搜索同时包括fred跟joe的行
/\/i : 搜索独立的单词fred
/\<\d\d\d\d\> : 搜索独立的4位数字
/\d\d\d\d\d\d : 搜索6位字符串中间4位数字前后两位不能为数字
/\<\d\{4}\> : 同/\<\d\d\d\d\>
" 查找空行
/^\n\{3} : 匹配三连续的空行
" 使用正则表达式组查找
/\(fred\).*\(joe\).*\2.*\1
" 正则表达式重复
/^\([^,]*,\)\{8}
" visual searching
:vmap // y/" : visually模式下的键盘映射,把//映射成匹配当前选中的文本
:vmap // y/=escape(@", '\\/.*$^~[]') : 包括空白字符
" \zs 和 \ze 匹配原 :h /\zs
/<\zs[^>]*\ze> : 匹配尖括号中的内容
" 零宽度匹配 :h /\@=
/<\@<=[^>]*>\@= : search for tag contents, ignoring chevrons
/<\@<=\_[^>]*>\@= : search for tags across possible multiple lines
" 多行查找 \_ 的意思是包括换行符
/ : 匹配结尾的所有内容
/fred\_s*joe/i : 匹配fred开始到joe,之间一定得是空白字符
/bugs\(\_.\)*bunny : 匹配所有bugs到bunny的字符串
:h \_ : help
" 查找函数声明,nmap为normal模式下的键盘映射
:nmap gx yiw/^\(sub\function\)\s\ "
" 查找多个文件
:bufdo /searchstr/ : 在多个文件缓冲区里执行查找
" 更好的多文件查找定位方法
:bufdo %s/searchstr/&/gic : 在多个文件缓冲区里查找,按下n停止
" 怎样不使用 / 来查找网址
?
: 向后查找
" 查找指定字符以外的字符串
/\c\v([^aeiou]&\a){4} : 查找4个辅音字母
----------------------------------------
#替换
:%s/fred/joe/igc : 普通替换命令
:%s/\r//g : 删除 dos 的换行符 ^m
" 你的文本文件是否乱七八糟的排成一行?使用如下命令
:%s/\r/\r/g : 转换 dos 回车符 ^m 为真正的回车符
:%s= *$== : 删除行尾空白
:%s= \ $== : 同上
:%s#\s*\r\?$## : 删除尾部空白和dos换行符
:%s#\s*\r*$## : 同上
" 删除空行
:%s/^\n\{3}// : 删除连续3个空行
:%s/^\n\ /\r/ : 压缩空行,多个替换为一个
%s#<[^>]\ >##g : 删除html的tag部分
" if you only want to know one thing
:'a,'bg/fred/s/dick/joe/igc : 非常有用
# 译释:''a,''b指定一个范围:mark a ~ mark b
# g//用一个正则表达式指出了进行操作的行必须可以被fred匹配
# 看后面,g//是一个全局显示命令
# s/dick/joe/igc则对于这些满足条件的行进行替换
" 复制列
:%s= [^ ]\ $=&&= : 复制最后一列
:%s= \f\ $=&&= : 一样
:%s= \s\ $=&& : 晕,还一样!
" 记忆(反向引用)
:s/\(.*\):\(.*\)/\2 : \1/ : 将两个字段颠倒
:%s/^\(.*\)\n\1$/\1/ : 删除重复行
" 非贪婪匹配 \{-}
:%s/^.\{-}pdf/new.pdf/ : 删除第一个pdf
" use of optional atom \?
:%s#\<[zy]\?tbl_[a-z_]\ \>#\l&#gc : lowercase with optional leading characters
" 跨越尽量多的行
:%s/// : 删除多行注释
:help /\{-} : 查看非贪婪匹配的更多帮助
" 使用寄存器替换
:s/fred/a/g : 将fred替换为寄存器a里的内容
:s/fred/asome_texts/g
:s/fred/\=@a/g : better alternative as register not displayed
" 在一行里写多种命令
:%s/\f\ \.gif\>/\r&\r/g | v/\.gif$/d | %s/gif/jpg/
:%s/a/but/gie|:update|:next : 当使用 @: 来重复
" 或运算
:%s/suck\|buck/loopy/gc : 替换suck或者buck(这里|不是管道)
" 调用vim函数
:s/__date__/\=strftime("%c")/ : 将__date__替换成当前日期,使用strftime函数
" 处理列,替换所有在第三列中的str1
:%s:\(\(\w\ \s\ \)\{2}\)str1:\1str2:
" 交换第一列跟第四列
:%s:\(\w\ \)\(.*\s\ \)\(\w\ \)$:\3\2\1:
" 过滤form中的内容放在寄存器里
:redir @*|sil exec 'g#<\(input\|select\|textarea\|/\=form\)\>#p'|redir end
:nmap ,z :redir @*sil exec 'g@<\(input\select\textarea\
/\=form\)\>@p'redir end
" 两位以上的数字减三(带进位。这个命令挺有趣)
:%s/\d\ /\=(submatch(0)-3)/
" 包含loc或者functions的行中的数字加6
:g/loc\|function/s/\d/\=submatch(0) 6/
" 比上面更好的方法
:%s#txtdev\zs\d#\=submatch(0) 1#g
:h /\zs 查看帮助
" 前缀为gg的数字加6
:%s/\(gg\)\@<=\d\ /\=submatch(0) 6/
:h zero-width 查看帮助
" 替换一个特定字符串为数字
:let i=10 | 'a,'bg/abc/s/yy/\=i/ |let i=i 1 # 将yy转换成10,11,12等等
" 比上面的更精确
:let i=10 | 'a,'bg/abc/s/xx\zsyy\ze/\=i/ |let i=i 1 # 将xxyy 转换成 xx11,xx12,
xx13
" find replacement text, put in memory, then use \zs to simplify substitute
:%s/"\([^.]\ \).*\zsxx/\1/
" pull word under cursor into lhs of a substitute
:nmap z :%s#\<=expand("")\>#
" pull visually highlighted text into lhs of a substitute
:vmap z :%s/\<*\>/
----------------------------------------
" all following performing similar task, substitute within substitution
" multiple single character substitution in a portion of line only
:%s,\(all/.*\)\@<=/,_,g : replace all / with _ after "all/"
" same thing
:s#all/\zs.*#\=substitute(submatch(0), '/', '_', 'g')#
" substitute by splitting line, then re-joining
:s#all/#&^m#|s#/#_#g|-j!
" substitute inside substitute
:%s/.*/\='cp '.submatch(0).' all/'.substitute(submatch(0),'/','_','g')/
----------------------------------------
" 全局显示命令
:g/gladiolli/# : 查找并显示匹配的行号
:g/fred.*joe.*dick/ : 显示所有含有 fred,joe & dick的行
:g/\/ : 显示单一单词fred
:g/^\s*$/d : 删除所有空行
:g!/^dd/d : 删除不含字符串''dd''的行
:v/^dd/d : 同上
:g/fred/,/joe/d : 删除所有的从fred到joe
:g/-------/.-10,.d : 以-------为标记删除之前的10行
:g/{/ ,/}/- s/\n\ /\r/g : 删除 {...}之间的空行
:v/\s/d : delete empty lines (both types)
:v/./,/./-j : 压缩空行
:g/^$/,/./-j : 同上
:g/ :g/^/put_ : 双倍行宽 (pu = put)
:g/^/m0 : 颠倒文件 (m = move)
:'a,'bg/^/m'b : 颠倒选中的 a 到 b
:g/^/t. : 重复行
:g/fred/t$ : 拷贝行从fred到结尾
:g/stage/t'a : 拷贝行从stage 到 marker a(a为标记的位置)
:g/\(^i[^^i]*\)\{80}/d : 删除最少包含80个tab的行
" perform a substitute on every other line
:g/^/ if line('.')%2|s/^/zz /
" match all lines containing "somestr" between markers a & b
" copy after line containing "otherstr"
:'a,'bg/somestr/co/otherstr/ : co(py) or mo(ve)
" as above but also do a substitution
:'a,'bg/str1/s/str1/&&&/|mo/str2/
:%norm jdd : 隔行删除
" 增加数字 (键入 )
:.,$g/^\d/exe "norm! \": 增加从当前行首到结尾的数字
:'a,'bg/\d\ /norm! ^a : 增加数字
" 保存全局命令的结果 (注意必须使用添加模式) 你需要使用 qaq 清空寄存器a.
"save results to a register/paste buffer 存储结果到 寄存器/粘贴 到 a
:g/fred/y a : 添加配备行到寄存器到 a
:g/fred/y a | :let @*=@a : 放入复制缓冲区
:let @a=''|g/barratt/y a |:let @*=@a
:'a,'b g/^error/ . w >> errors.txt
" 复制每一行,然后在复制出来的每一行两侧加上一个 print '复制出来的内容'
:g/./yank|put|-1s/'/"/g|s/.*/print '&'/
" 用文件中的内容替换字符串,-d 表示删除“标记”
:g/^mark$/r tmp.ex | -d
" display prettily
:g//z#.5 : display with context
:g//z#.5|echo "==========" : display beautifully
" combining g// with normal mode commands
:g/|/norm 2f|r* : replace 2nd | with a star
"send output of previous global command to a new window
:nmap :redir @a:g//:redir end:new:put! a
----------------------------------------
" 全局命令和替换命令联姻 (强大的编辑能力)
:'a,'bg/fred/s/joe/susan/gic : 可以使用反向引用来匹配
:g/fred/,/joe/s/fred/joe/gic : non-line based (ultra)
----------------------------------------
" 先找fred,然后找joe
:/fred/;/joe/-2,/sid/ 3s/sally/alley/gic
----------------------------------------
" create a new file for each line of file eg 1.txt,2.txt,3,txt etc
:g/^/exe ".w ".line(".").".txt"
----------------------------------------
" absolutely essential
----------------------------------------
* # g* g# : 查找当前光标下的单词(单个单词) () (向前/向后)
% : 匹配括号 {}[]()
. : 重复上次操作
@: : 重复上次的命令
matchit.vim : 适%能匹配
    []
servlet通过下面的方法来提供服务:
  • 实现service方法。
  • 实现httpservlet的domethod方法(doget、dodelete、dooptions、 dopost、doput、dotrace)。
    通常,service方法用来从客户请求(request)中提取信息,访问扩展资源,并基于上面的信息提供响应(response)。

    对于http servlets,正确提供响应的过程是首先填写响应(response)的头信息,然后从响应(response)中得到输出流,最后向输出流中写入内容信息。响应(response)头信息必须最先设置。下面将描述如何从请求(request)中获得信息和产生http响应(response)。

    
  • 取得客户端请求
        一个httpservletrequest对象提供到达http 头部数据,也允许你获取客户端的数据。怎样获取这些数据取决于http端请求方法。不管用任何http方式,你都可以用 getparametervalues方法返回特定名称的参数值。对于用 http get 请求的方式,这个getquerystring方法将会返回一个可以用来分析的值。

        客户端请求(request)包含了从客户端传递到servlet的数据。所有的请求(request)都实现了servletrequest接口。这个接口定义了一些方法访问下面的信息,如表14-1所示。

    表14-1  servletrequest接口方法
                类 型 描 述	                         对 应 方 法
                参数,用来在客户端和servlet之间传送信息 getattribute(string name)
                getattributenames()
                getinputstream()
                getparameter(string name)
                getparametermap()
                getparameternames()
                getparametervalues(string name)
                对象值属性,用来在servlet容器和servlet
                之间,或者协作的servlet之间传递信息	    removeattribute(string name)
                setattribute(string name, object o)
                有关请求使用的协议信息,
                客户端和服务器在请求中的调用	   getcontentlength()
                getcontenttype()
                getprotocol()
                getreader()
                getrealpath(string path)
                getremoteaddr()
                getremotehost()
                getrequestdispatcher(string path)
                有关请求使用的协议信息,
                客户端和服务器在请求中的调用       getscheme()
                getservername()
                getserverport()
                issecure()
                有关localization的信息	       getcharacterencoding()
                getlocale()
                getlocales()
                setcharacterencoding(string env)


        下面的代码段示范了如何使用request中的方法获得客户端信息。

    enumeration params = request.getparameternames();
                string paramname = null;
                string[] paramvalues = null;
                while (params.hasmoreelements()) {
                paramname = (string) params.nextelement();
                paramvalues = request.getparametervalues(paramname);
                system.out.println("\nparameter name is "   paramname);
                for (int i = 0; i < paramvalues.length; i  ) {
                system.out.println(", value "   i   " is "   paramvalues[i].tostring());
                }
                }


        http servlets使用http request对象(httpservletrequest),它包含了request url、http头信息、查询字符串,等等。http request url 包括几个部分:
        http://: ?

        一般情况下:

    requesturi = contextpath   servletpath   pathinfo
                context path:通过getcontextpath方法获得。
                servlet path:通过getservletpath方法获得。
                pathinfo:通过getpathinfo方法获得。


        如表14-2所示。

    表14-2  路径的对应
                request path	            path elements
                /catalog/help/feedback.jsp	contextpath: /catalog servletpath:
                /help/feedback.jsp pathinfo: null


        
  • 提供http响应
        响应(response)包含了在服务器和客户端之间传递的数据。所有的响应(response)都实现了servletresponse接口。这个接口定义了一些方法提供给开发人员使用,如表14-3所示。

    表14-3  servletresponse接口方法
                类 型 描 述	                          对 应 方 法
                获得向客户端发送数据的输出流	 发送字符流:getwriter()
                发送字节流:getoutputstream()
                指示响应返回的内容类型(例如:text/html)
                已经注册的内容类型名称保存在iana
                (internet assigned numbers authority) setcontenttype(java.lang.string type)
                指出是否是缓冲输出。默认情况下写入输出的
                内容被立即发送到客户端。使用缓冲后写入输出的内容先
                不发送到客户端,这样servlet有更多的时间设置相应的
                状态码和头信息,或者转移到其他的web资源	 flushbuffer()
                getbuffersize()
                iscommitted()
                reset()
                resetbuffer()
                setbuffersize(int size)
                setcontentlength(int len)
                设置localization信息	            getcharacterencoding()
                getlocale()
                setlocale(java.util.locale loc)


        http response类(httpservletresponse)有一些代表http头信息的域:
        
  • 状态码用来指出响应(response)失败的原因。


        
  • cookies在客户端存储应用相关的信息,有时cookies用来维护和标识用户的session。

        servlet首先设置响应(response)头信息,包括响应(response)的内容类别和缓冲区大小,然后在doget方法中从响应(response)获得printwriter ,最后向输出中写入html代码,调用close()方法提交这次对客户端的响应(response)。示范代码如下:

    public void doget (httpservletrequest request,
                httpservletresponse response)
                throws servletexception, ioexception
                {
                // 设置头信息
                response.setcontenttype("text/html");
                response.setbuffersize(8192);
                printwriter out = response.getwriter();
                // 向response中输出
                out.println(""  
                " 
                messages.getstring("titlebookdescription")
                 ");
                ...
                out.println("");
                // 关闭输出流
                out.close();
                }


  • 林光炎 2009-08-29 11:56
    ]]>tomcat源码分析(消息处理)http://www.blogjava.net/lingy/archive/2009/08/26/292716.html林光炎林光炎wed, 26 aug 2009 12:53:00 gmthttp://www.blogjava.net/lingy/archive/2009/08/26/292716.html我们知道了tomcat的整体框架了, 也明白了里面都有些什么组件, 以及各个组件是干什么用的了。

    http://www.csdn.net/develop/read_article.asp?id=27225

    我想,接下来我们应该去了解一下 tomcat 是如何处理jsp和servlet请求的。

    1.  我们以一个具体的例子,来跟踪tomcat看看它是如何把request一层一层地递交给下一个容器,并最后交给wrapper来处理的。

    http://localhost:8080/web/login.jsp为例子

    (以下例子,都是以tomcat4 源码为参考)

    这篇心得主要分为3个部分: 前期, 中期, 和末期。

     前期:讲解了在浏览器里面输入一个url,是怎么被tomcat抓住的。

    中期:讲解了被tomcat抓住后,又是怎么在各个容器里面穿梭, 最后到达最后的处理地点。

    末期:讲解到达最后的处理地点后,又是怎么具体处理的。

    2 前期 request的born.

        在这里我先简单讲一下request这个东西。

         我们先看着这个url:http://localhost:8080/web/login.jsp 它是动用了8080端口来进行socket通讯的。

         我们知道, 通过

           inputstream in = socket.getinputstream() 和

           outputstream out = socket.getoutputstream()

         就可以实现消息的来来往往了。

         但是如果把stream给应用层看,显然操作起来不方便。

         所以,在tomcat 的connector里面, socket被封装成了request和response这两个对象。

         我们可以简单地把request看成管发到服务器来的数据,把response看成想发出服务器的数据。

         

         但是这样又有其他问题了啊? request这个对象是把socket封装起来了, 但是他提供的又东西太多了。

         诸如request.getauthorization(), request.getsocket()。 像authorization这种东西开发人员拿来基本上用不太着,而像socket这种东西,暴露给开发人员又有潜在的危险。 而且啊, 在servlet specification里面标准的通信类是servletrequest和httpservletrequest,而非这个request类。 so, so, so. tomcat必须得捣持捣持request才行。 最后tomcat选择了使用捣持模式(应该叫适配器模式)来解决这个问题。它把org.apache.catalina.request 捣持成了 org.apache.coyote.tomcat4.coyoterequest。 而coyoterequest又实现了servletrequest和httpservletrequest 这两种接口。 这样就提供给开发人员需要且刚刚需要的方法了。

     

        ok, 我们在 tomcat的顶层容器 - standardengin 的invoke()方法这里设置一个断点, 然后访问

        http://localhost:8080/web/login.jsp, 我们来看看在前期都会路过哪些地方:

           1. run(): 536, java.lang.thread, thread.java

           currentthread

          2. run():666, org.apache.tomcat.util.threads.threadpool$controlrunnable, threadpool.java

                   threadpool

           3. runit():589, org.apache.tomcat.util.net.tcpworkerthread, pooltcpendpoint.java

             threadworker

    4.        processconnection():  549

    org.apache.coyote.http11.http11protocol$http11connectionhandler, http11protocol.java

                      http protocol parser

          5. process(): 781, org.apache.coyote.http11.http11processor, http11processor.java

              http request processor

           6. service(): 193, org.apache.coyote.tomcat4.coyoteadapter,coyoteadapter.java

             adapter

           7. invoke(): 995, org.apache.catalina.core.containerbase, containerbase.java

       standardengin

        1. 主线程

        2. 启动线程池.

        3. 调出线程池里面空闲的工作线程。

        4. 把8080端口传过来由httpd协议封装的数据,解析成request和response对象。

        5. 使用http11processor来处理request

        6. 在http11processor里面, 又会call coyoteadapter来进行适配处理,把request适配成实现了servletrequest和httpservletrequest接口的coyoterequest.

    7. 到了这里,前期的去毛拔皮工作就基本上搞定,可以交给standardengin 做核心的处理工作了。

    3. 中期。 在各个容器间的穿梭。

        request在各个容器里面的穿梭大致是这样一种方式:

        每个容器里面都有一个管道(pipline), 专门用来传送request用的。

        管道里面又有好几个阀门(valve), 专门用来过滤request用的。

        在管道的低部通常都会放上一个默认的阀们。 这个阀们至少会做一件事情,就是把request交给子容器。

        让我们来想象一下:

         当一个request进入一个容器后, 它就在管道里面流动,波罗~ 波罗~ 波罗~ 地穿过各个阀门。在流到最后一个阀门的时候,吧唧~ 那个该死的阀门就把它扔给了子容器。 然后又开始 波罗~ 波罗~ 波罗~ ... 吧唧~.... 波罗~ 波罗~ 波罗~ ....吧唧~....

        就是通过这种方式, request 走完了所有的容器。( 感觉有点像消化系统,最后一个地方有点像那里~ )

        ok, 让我们具体看看都有些什么容器, 各个容器里面又都有些什么阀门,这些阀们都对我们的request做了些什么吧:

    3.1 standardengin 的pipeline里面放的是:standardenginvalve

    在这里,valve做了三件事:

    1.   验证传递过来的request是不是httpservletrequest.

    2    验证传递过来的 request 是否携带了host header信息.

    3    选择相应的host去处理它。(一般我们都只有一个host:localhost,也就是127.0.0.1)。

    到了这个地方,我们的request就已经完成了在engin这个部分的历史使命,通向前途未卜的下一站: host了。

    3.2 standardhost 的pipline里面放的是: standardhostvalve

    1.   验证传递过来的request是不是httpservletrequest.

    2.   根据request来确定哪个context来处理。

    context其实就是webapp比如http://localhost:8080/web/login.jsp

    这里web就是context罗!

    3.   既然确定了是哪个context了,那么就应该把那个contextclassloader付给当前线程了。

            thread.currentthread().setcontextclassloader(context.getloader().getclassloader());

       这样request就只看得见指定的context下面的classes啊, jar啊这些,而看不见tomcat本身的类,什么engin啊, valve啊。不然还得了啊!

    4. 既然request到了这里了,看来用户是准备访问web这个web app了,咋们得更新一下这个用户的session不是! ok , 就由manager更新一下用户的session信息

    5. 交给具体的context 容器去继续处理request.

    6. context处理完毕了,把classloader还回来。

    3.3 standardcontext 的pipline里面放的是: standardcontextvalve

    1.   验证传递过来的request是不是httpservletrequest.

    2.   如果request意图不轨,想要访问/meta-inf, /web-inf这些目录下的东西,呵呵,没有用d!

    3.   这个时候就会根据request到底是servlet还是jsp还是静态资源来决定到底用哪种wrapper来处理这个reqeust了。

    4.   一旦决定了到底用哪种wrapperok,交给那个wrapper处理。

    4. 末期。 不同的需求是怎么处理的.

    standardwrapper

    之前对wrapper没有做过讲解,其实它是这样一种东西。

    我们在处理request的时候,可以分成3种。

    处理静态的: org.apache.catalina.servlets.defaultservlet  

    处理jsp的:org.apache.jasper.servlet.jspservlet

    处理servlet的:org.apache.catalina.servlets.invokerservlet

    不同的request就用这3种不同的servlet去处理。

    wrapper就是对它们的一种简单的封装,有了wrapper后,我们就可以轻松地拦截每次的request。也可以容易地调用servlet的init()和destroy()方法, 便于管理嘛!

    具体情况是这么滴:

       如果request是找jsp文件,standardwrapper里面就会封装一个org.apache.jasper.servlet.jspservlet去处理它。

       如果request是找 静态资源 ,standardwrapper里面就会封装一个org.apache.jasper.servlet.defaultservlet 去处理它。

       如果request是找servlet ,standardwrapper里面就会封装一个org.apache.jasper.servlet.invokerservlet 去处理它。

    standardwrapper同样也是容器,既然是容器, 那么里面一定留了一个管道给request去穿,管道低部肯定也有一个阀门(注1),用来做最后一道拦截工作.

    在这最底部的阀门里,其实就主要做了两件事:

       一是启动过滤器,让request在n个过滤器里面筛一通,如果ok! 那就pass。 否则就跳到其他地方去了。

       二是servlet.service((httpservletrequest) request,(httpservletresponse) response); 这个方法.

         如果是 jspservlet, 那么先把jsp文件编译成servlet_xxx, 再invoke servlet_xxx的servie()方法。

         如果是 defaultservlet, 就直接找到静态资源,取出内容, 发送出去。

         如果是 invokerservlet, 就调用那个具体的servlet的service()方法。

       ok! 完毕。

    注1: standardwrapper 里面的阀门是最后一道关口了。 如果这个阀门欲意把request交给standardwrapper 的子容器处理。 对不起, 在设计考虑的时候, wrapper就被考虑成最末的一个容器, 压根儿就不会给wrapper添加子容器的机会! 如果硬是要调用addchild(), 立马抛出illegalargumentexception!

    参考:

         <>
       <
    >

     


    作者blog:
    相关文章


    林光炎 2009-08-26 20:53
    ]]>
    tomcat源码分析(启动框架http://www.blogjava.net/lingy/archive/2009/08/26/292715.html林光炎林光炎wed, 26 aug 2009 12:49:00 gmthttp://www.blogjava.net/lingy/archive/2009/08/26/292715.html
    tomcat源码分析(启动框架)     选择自 的 blog
    关键字   tomcat 源代码 源码 source code architecture
    出处  

    tomcat源码分析(启动框架)
    前言:
       本文是我阅读了tomcat源码后的一些心得。 主要是讲解tomcat的系统框架, 以及启动流程。若有错漏之处,敬请批评指教!
    建议:
       毕竟tomcat的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握tomcat的框架的。 所以得实践、实践、再实践。 建议下载一份tomcat的源码, 调试通过, 然后单步跟踪其启动过程。 如果有不明白的地方, 再来查阅本文, 看是否能得到帮助。 我相信这样效果以及学习速度都会好很多!
      
    1. tomcat的整体框架结构
       tomcat的基本框架, 分为4个层次。
       top level elements:
        server
        service  
       connector
        http
        ajp
       container
       engine
         host
       context
       component 
        manager
       logger
       loader
       pipeline
       valve
             ...
       站在框架的顶层的是server和service
       server:  其实就是backgroud程序, 在tomcat里面的server的用处是启动和监听服务端事件(诸如重启、关闭等命令。 在tomcat的标准配置文件:server.xml里面, 我们可以看到“”这里的"shutdown"就是server在监听服务端事件的时候所使用的命令字)
       service: 在tomcat里面, service是指一类问题的凯发天生赢家一触即发官网的解决方案。  通常我们会默认使用tomcat提供的:tomcat-standalone 模式的service。 在这种方式下的service既给我们提供解析jsp和servlet的服务, 同时也提供给我们解析静态文本的服务。
      
       connector: tomcat都是在容器里面处理问题的, 而容器又到哪里去取得输入信息呢?
    connector就是专干这个的。 他会把从socket传递过来的数据, 封装成request, 传递给容器来处理。
       通常我们会用到两种connector,一种叫http connectoer, 用来传递http需求的。 另一种叫ajp, 在我们整合apache与tomcat工作的时候, apache与tomcat之间就是通过这个协议来互动的。 (说到apache与tomcat的整合工作, 通常我们的目的是为了让apache 获取静态资源, 而让tomcat来解析动态的jsp或者servlet。)
       container: 当http connector把需求传递给顶级的container: engin的时候, 我们的视线就应该移动到container这个层面来了。
       在container这个层, 我们包含了3种容器: engin, host, context.
       engin: 收到service传递过来的需求, 处理后, 将结果返回给service( service 是通过 connector 这个媒介来和engin互动的 ).
       host: engin收到service传递过来的需求后,不会自己处理, 而是交给合适的host来处理。
    host在这里就是虚拟主机的意思, 通常我们都只会使用一个主机,既“localhost”本地机来处理。
       context: host接到了从host传过来的需求后, 也不会自己处理, 而是交给合适的context来处理。
       比如: <>
             <>
       前者交给foo这个context来处理, 后者交给bar这个context来处理。
       很明显吧! context的意思其实就是一个web app的意思。
       我们通常都会在server.xml里面做这样的配置
      
       这个context容器,就是用来干我们该干的事儿的地方的。
      
       compenent: 接下来, 我们继续讲讲component是干什么用的。
       我们得先理解一下容器和组件的关系。
       需求被传递到了容器里面, 在合适的时候, 会传递给下一个容器处理。
       而容器里面又盛装着各种各样的组件, 我们可以理解为提供各种各样的增值服务。
       manager: 当一个容器里面装了manager组件后,这个容器就支持session管理了, 事实上在tomcat里面的session管理, 就是靠的在context里面装的manager component.
       logger: 当一个容器里面装了logger组件后, 这个容器里所发生的事情, 就被该组件记录下来啦! 我们通常会在logs/ 这个目录下看见 catalina_log.time.txt 以及 localhost.time.txt 和localhost_examples_log.time.txt。 这就是因为我们分别为:engin, host以及context(examples)这三个容器安装了logger组件, 这也是默认安装, 又叫做标配 :)
       loader: loader这个组件通常只会给我们的context容器使用, loader是用来启动context以及管理这个context的classloader用的。
        pipline: pipeline是这样一个东西, 当一个容器决定了要把从上级传递过来的需求交给子容器的时候, 他就把这个需求放进容器的管道(pipeline)里面去。 而需求傻呼呼得在管道里面流动的时候, 就会被管道里面的各个阀门拦截下来。 比如管道里面放了两个阀门。 第一个阀门叫做“access_allow_vavle”, 也就是说需求流过来的时候,它会看这个需求是哪个ip过来的, 如果这个ip已经在黑名单里面了, sure, 杀! 第二个阀门叫做“defaul_access_valve”它会做例行的检查, 如果通过的话,ok, 把需求传递给当前容器的子容器。 就是通过这种方式, 需求就在各个容器里面传递,流动, 最后抵达目的地的了。
        valve: 就是上面所说的阀门啦。
       tomcat里面大概就是这么些东西, 我们可以简单地这么理解tomcat的框架,它是一种自上而下, 容器里又包含子容器的这样一种结构。
    2. tomcat的启动流程
       这篇文章是讲tomcat怎么启动的,既然我们大体上了解了tomcat的框架结构了, 那么我们可以望文生意地就猜到tomcat的启动, 会先启动父容器,然后逐个启动里面的子容器。 启动每一个容器的时候, 都会启动安插在他身上的组件。 当所有的组件启动完毕, 所有的容器启动完毕的时候, tomcat本身也就启动完毕了。
       顺理成章地, 我们同样可以猜到, tomcat的启动会分成两大部分, 第一步是装配工作。 第二步是启动工作。
       装配工作就是为父容器装上子容器, 为各个容器安插进组件的工作。 这个地方我们会用到digester模式, 至于digester模式什么, 有什么用, 怎么工作的. 请参考 <>
       启动工作是在装配工作之后, 一旦装配成功了, 我们就只需要点燃最上面的一根导线, 整个tomcat就会被激活起来。 这就好比我们要开一辆已经装配好了的汽车的时候一样,我们只要把钥匙插进钥匙孔,一拧,汽车的引擎就会发动起来,空调就会开起来, 安全装置就会生效, 如此一来,汽车整个就发动起来了。(这个过程确实和tomcat的启动过程不谋而和, 让我们不得不怀疑 tomcat的设计者是在ge做java开发的)。
    2.1 一些有意思的名称:
       catalina
       tomcat
       bootstrap
       engin
       host
       context
       他们的意思很有意思:
       catalina: 远程轰炸机
       tomcat: 熊猫轰炸机 -- 轰炸机的一种(这让我想起了让国人引以为豪的熊猫手机,是不是英文可以叫做tomcat??? , 又让我想起了另一则广告: 波导-手机中的战斗机、波音-客机中的战斗机 )
       bootstap: 引导
       engin: 发动机
       host: 主机,领土
       context: 内容, 目标, 上下文
      
       ... 在许多许多年后, 现代人类已经灭绝。 后现代生物发现了这些单词零落零落在一块。 一个自以为聪明的家伙把这些东西翻译出来了:
       在地勤人员的引导(bootstrap)下, 一架轰炸架(catalina)腾空跃起, 远看是熊猫轰炸机(tomcat), 近看还是熊猫轰炸机! 凭借着优秀的发动机技术(engin), 这架熊猫轰炸机飞临了敌国的领土上空(host), 对准目标(context)投下了毁天灭地的核弹头,波~ 现代生物就这么隔屁了~
     
       综上所述, 这又不得不让人联想到ge是不是也参与了军事设备的生产呢?
       反对美帝国主义! 反对美霸权主义! 和平万岁! 自由万岁!
      
    2.2  历史就是那么惊人的相似! tomcat的启动就是从org.apache.catalina.startup.bootstrap这个类悍然启动的!
       在bootstrap里做了两件事:
       1. 指定了3种类型classloader:
          commonloader: common/classes、common/lib、common/endorsed
          catalinaloader: server/classes、server/lib、commonloader
          sharedloader:  shared/classes、shared/lib、commonloader
       2. 引导catalina的启动。
          用reflection技术调用org.apache.catalina.startup.catalina的process方法, 并传递参数过去。
      
    2.3 catalina.java
       catalina完成了几个重要的任务:
       1. 使用digester技术装配tomcat各个容器与组件。
          1.1 装配工作的主要内容是安装各个大件。 比如server下有什么样的servcie。 host会容纳多少个context。 context都会使用到哪些组件等等。
          1.2 同时呢, 在装配工作这一步, 还完成了mbeans的配置工作。 在这里,我简单地但不十分精确地描述一下mbean是什么,干什么用的。
              我们自己生成的对象, 自己管理, 天经地义! 但是如果我们创建了对象了, 想让别人来管, 怎么办呢? 我想至少得告诉别人我们都有什么, 以及通过什么方法可以找到  吧! jmx技术给我们提供了一种手段。 jmx里面主要有3种东西。mbean, agent, connector.
           mbean: 用来映射我们的对象。也许mbean就是我们创建的对象, 也许不是, 但有了它, 就可以引用到我们的对象了。
           agent:  通过它, 就可以找到mbean了。
           connector: 连接agent的方式。 可以是http的, 也可以是rmi的,还可以直接通过socket。
          发生在tomcat 装配过程中的事情:  globalresourceslifecyclelistener 类的初始化会被触发:
             protected static registry registry = mbeanutils.createregistry();  会运行
             mbeanutils.createregistry()  会依据/org/apache/catalina/mbeans/mbeans-descriptors.xml这个配置文件创建 mbeans. ok, 外界就有了条途径访问tomcat中的各个组件了。(有点像后门儿)
       2. 为top level 的server 做初始化工作。 实际上就是做通常会配置给service的两条connector.(http, ajp)
       3. 从server这个容器开始启动, 点燃整个tomcat.
       4. 为server做一个hook程序, 检测当server shutdown的时候, 关闭tomcat的各个容器用。
       5. 监听8005端口, 如果发送"shutdown"(默认培植下字符串)过来, 关闭8005serversocket。
    2.4 启动各个容器
       1. server
          触发server容器启动前(before_start), 启动中(start), 启动后(after_start)3个事件, 并运行相应的事件处理器。
          启动server的子容器:servcie.
       2. service
          启动service的子容器:engin
          启动connector
       3. engin
          到了engin这个层次,以及以下级别的容器, tomcat就使用了比较一致的启动方式了。
          首先,  运行各个容器自己特有一些任务
          随后,  触发启动前事件
          立即,  设置标签,就表示该容器已经启动
          接着,  启动容器中的各个组件: loader, logger, manager等等
          再接着,启动mapping组件。(注1)
          紧跟着,启动子容器。
          接下来,启动该容器的管道(pipline)
          然后,  触发启动中事件
          最后,  触发启动后事件。
     
          engin大致会这么做, host大致也会这么做, context大致还是会这么做。 那么很显然地, 我们需要在这里使用到代码复用的技术。 tomcat在处理这个问题的时候, 漂亮地使用了抽象类来处理。 containerbase. 最后使得这部分完成复杂功能的代码显得干净利落, 干练爽快, 实在是令人觉得叹为观止, 细细品来, 直觉如享佳珍, 另人齿颊留香, 留恋往返啊!
         
          engin的触发启动前事件里, 会激活绑定在engin上的唯一一个listener:enginconfig。
          这个enginconfig类基本上没有做什么事情, 就是把enginconfig的调试级别设置为和engin相当。 另外就是输出几行文本, 表示engin已经配置完毕, 并没有做什么实质性的工作。
          注1: mapping组件的用处是, 当一个需求将要从父容器传递到子容器的时候, 而父容器又有多个子容器的话, 那么应该选择哪个子容器来处理需求呢? 这个由mapping 组件来定夺。
       
       4. host
           同engin一样, 也是调用containerbase里面的start()方法, 不过之前做了些自个儿的任务,就是往host这个容器的通道(pipline)里面, 安装了一个叫做
     “org.apache.catalina.valves.errorreportvalve”的阀门。
           这个阀门的用处是这样的:  需求在被engin传递给host后, 会继续传递给context做具体的处理。 这里需求其实就是作为参数传递的request, response。 所以在context把需求处理完后, 通常会改动response。 而这个org.apache.catalina.valves.errorreportvalve的作用就是检察response是否包含错误, 如果有就做相应的处理。
       5. context
           到了这里, 就终于轮到了tomcat启动中真正的重头戏,启动context了。
     standardcontext.start() 这个启动context容器的方法被standardhost调用.
     5.1 webappresources 该context所指向的具体目录
     5.2 安装defaultcontex, defaultcontext 就是默认context。 如果我们在一个host下面安装了defaultcontext,而且defaultcontext里面又安装了一个数据库连接池资源的话。 那么其他所有的在该host下的context, 都可以直接使用这个数据库连接池, 而不用格外做配置了。
      5.3 指定loader. 通常用默认的org.apache.catalina.loader.webapploader这个类。   loader就是用来指定这个context会用到哪些类啊, 哪些jar包啊这些什么的。
     5.4 指定 manager. 通常使用默认的org.apache.catalina.session. standardmanager 。 manager是用来管理session的。
         其实session的管理也很好实现。 以一种简单的session管理为例。 当需求传递过来的时候, 在request对象里面有一个sessionid 属性。 ok, 得到这个sessionid后, 我们就可以把它作为map的key,而value我们可以放置一个hashmap. hashmap里边儿, 再放我们想放的东西。
     5.5 postworkdirectory (). tomcat下面有一个work目录。 我们把临时文件都扔在那儿去。 这个步骤就是在那里创建一个目录。 一般说来会在�talina_home%/work/standalone\localhost\ 这个地方生成一个目录。
    5.6  binding thread。到了这里, 就应该发生 class loader 互换了。 之前是看得见tomcat下面所有的class和lib. 接下来需要看得见当前context下的class。 所以要设置contextclassloader, 同时还要把旧的classloader记录下来,因为以后还要用的。
    5.7  启动 loader. 指定这个context具体要使用哪些classes, 用到哪些jar文件。 如果reloadable设置成了true, 就会启动一个线程来监视classes的变化, 如果有变化就重新启动context。
    5.8  启动logger
    5.9  触发安装在它身上的一个监听器。
     lifecycle.firelifecycleevent(start_event, null);
     作为监听器之一,contextconfig会被启动. contextconfig就是用来配置web.xml的。 比如这个context有多少servlet, 又有多少filter, 就是在这里给context装上去的。
     5.9.1 defaultconfig. 每个context都得配置 tomcat/conf/web.xml 这个文件。
     5.9.2 applicationconfig 配置自己的 web-inf/web.xml 文件
    5.9.3 validatesecurityroles 权限验证。 通常我们在访问/admin 或者/manager的时候,需要用户要么是admin的要么是manager的, 才能访问。 而且我们还可以限制那些资源可以访问, 而哪些不能。 都是在这里实现的。
    5.9.4 tldscan: 扫描一下, 需要用到哪些标签(tag lab)
    5.10 启动 manager
    5.11 postwelcomefiles() 我们通常会用到的3个启动文件的名称:
    index.html、index.htm、index.jsp 就被默认地绑在了这个context上
     5.12 listenerstart 配置listener
     5.13 filterstart 配置 filter
     5.14 启动带有1的servlet.
      顺序是从小到大: 1,2,3… 最后是0
      默认情况下, 至少会启动如下3个的servlet:
      org.apache.catalina.servlets.defaultservlet  
          处理静态资源的servlet. 什么图片啊, html啊, css啊, js啊都找他
      org.apache.catalina.servlets.invokerservlet
          处理没有做servlet mapping的那些servlet.
      org.apache.jasper.servlet.jspservlet
          处理jsp文件的.
           5.15  标识context已经启动完毕。
     走了多少个步骤啊, context总算是启动完毕喽。
        ok! 走到了这里, 每个容器以及组件都启动完毕。 tomcat终于不辞辛劳地为人民服务了!
    3. 参考文献:
        <>
        <>
       
    4. 后记
        这篇文章是讲解tomcat启动框架的,还有篇文章是讲解tomcat里面的消息处理流程的细节的。 文章内容已经写好了, 现在正在整理阶段。 相信很快就可以做出来, 大家共同研究共同进步。
        这篇文章是独自分析tomcat源码所写的, 所以一定有地方是带有个人主观色彩, 难免会有片面之处。若有不当之处敬请批评指教,这样不仅可以使刚开始研究tomcat的兄弟们少走弯路, 我也可以学到东西。
        email:

    5.



    林光炎 2009-08-26 20:49
    ]]>
    用digester简化xml文档处理http://www.blogjava.net/lingy/archive/2009/08/26/292713.html林光炎林光炎wed, 26 aug 2009 12:27:00 gmthttp://www.blogjava.net/lingy/archive/2009/08/26/292713.html 

    digester框架属于jakarta commons,它以规则和模式为基础处理xml文档。与sax和dom之类的标准api相比,digester不涉及太多的细节问题,非常适合于对xml文档进行简单的处理。

    在java和xml开发中,一个常见的任务是把xml文档转换成对应的java bean对象的层次结构。人们经常用标准的sax和dom api来完成这个任务。虽然这两种api都很强大和灵活,但对于某些简单的任务来说,它们显得操作层次太低,也就是说,涉及了太多的细节问题。jakarta digester框架能够很好地满足这类场合的需要。

    digester框架简介

    jakarta的digester框架从struts框架发展而来,原先被用来处理struts-config.xml配置文件,但很快人们认识到它有着更广泛的用途,把它转入了jakarta commons项目。jakarta commons的目标是提供一个“可重用java组件的仓库”。digester最新的版本是1.3,于2002年8月13日发布。

    digester框架允许开发者指定一组动作,当解析器在xml文档中发现某些特定的简单模式时动作被执行。digester框架带有10个预定义的规则(rule),涵盖了unmarshalling xml(例如创建bean或设置bean属性)的大多数需求( marshalling的原意是指“配制整齐,编组列车”,marshalling是在内存中为java对象生成xml描述文档的过程,而unmarshalling是指把xml形式的描述转换到可用java代码操作的对象的过程,我们称之为“反配制”),但必要时用户可以定义和实现自己的规则。

    在本文的例子中,我们将反配制下面这个xml文档:

    
                
                
                author 1
                title 1
                
                
                author 2
                his one book
                
                
                mag title 1
                
    some headline
    another headline
    author 2 his other book mag title 2
    second headline

    下面是bean的代码。注意使用digester框架时,bean类必须定义成public。

    import java.util.vector;
                public class catalog {
                private vector books;
                private vector magazines;
                public catalog() {
                books = new vector();
                magazines = new vector();
                }
                public void addbook( book rhs ) {
                books.addelement( rhs );
                }
                public void addmagazine( magazine rhs ) {
                magazines.addelement( rhs );
                }
                public string tostring() {
                string newline = system.getproperty( "line.separator" );
                stringbuffer buf = new stringbuffer();
                buf.append( "--- books ---" ).append( newline );
                for( int i=0; i

    1

    digester框架以模式(pattern)和规则(rule)为基础处理输入的xml。模式必须与xml元素匹配,包括其名字和在文档树内的位置。描述匹配模式的语法类似于xpath匹配模式,例如:catalog模式匹配顶层的元素,catalog/book模式匹配直接嵌套在元素内的元素(但不匹配文档内其他位置的元素)

    所有的模式都必须指定其完整名称——从根元素开始的完整路径。唯一的例外是包含通配符(“*”)的模式,例如*/name模式匹配xml文档内任何位置的元素。但是根元素不必特别指出,因为所有的路径都是从根元素开始的绝对路径。

    当digester发现一个指定的模式,它就执行关联的任务。由此可见,digester框架显然与sax解析器有着密切的关系(实际上,digester类实现了org.xml.sax.contenthandler,并维护着解析栈)。所有在digester中使用的规则必须扩展org.apache.commons.digester.rule,后者本身提供了一些类似于sax的contenthandler回调函数的方法。例如,当遇到匹配元素的开始标记和结束标记时,begin()方法和end()方法将分别被调用。

    一旦遇到匹配元素的内容,body()方法被调用;最后被调用的方法是finish(),这个方法在匹配元素的结束标记处理完毕之后被调用,用来执行可能需要的事后清理任务。然而,大多数时候我们不必关注这些方法,因为框架提供的标准规则很可能已经提供了所有必需的功能。

    要反配制一个文档,首先创建一个org.apache.commons.digester.digester类的实例,如果必要的话,进行一些配置操作,指定必需的模式和规则,最后向parse()方法传递一个xml文件的引用。下面的digesterdriver示范了这一处理过程(必须在命令行上指定输入xml文档的名称)。

    import org.apache.commons.digester.*;
                import java.io.*;
                import java.util.*;
                public class digesterdriver {
                public static void main( string[] args ) {
                try {
                digester digester = new digester();
                digester.setvalidating( false );
                digester.addobjectcreate( "catalog", catalog.class );
                digester.addobjectcreate( "catalog/book", book.class );
                digester.addbeanpropertysetter( "catalog/book/author", "author" );
                digester.addbeanpropertysetter( "catalog/book/title", "title" );
                digester.addsetnext( "catalog/book", "addbook" );
                digester.addobjectcreate( "catalog/magazine", magazine.class );
                digester.addbeanpropertysetter( "catalog/magazine/name", "name" );
                digester.addobjectcreate( "catalog/magazine/article", article.class );
                digester.addsetproperties( "catalog/magazine/article", "page", "page" );
                digester.addbeanpropertysetter( "catalog/magazine/article/headline" );
                digester.addsetnext( "catalog/magazine/article", "addarticle" );
                digester.addsetnext( "catalog/magazine", "addmagazine" );
                file input = new file( args[0] );
                catalog c = (catalog)digester.parse( input );
                system.out.println( c.tostring() );
                } catch( exception exc ) {
                exc.printstacktrace();
                }
                }
                }

    在上面的代码中,我们首先创建了digester类的一个实例digester,然后指定它不要用dtd验证xml文档的合法性——这是因为我们没有为xml文档定义dtd。接下来,我们指定了模式和关联的规则:objectcreaterule创建指定类的一个实例,并将它压入解析栈。setpropertiesrule把bean属性设置成当前xml元素的属性值——规则的第一个参数是xml属性的名称,第二个参数是bean属性的名称。

    setpropertiesrule获取的是xml属性的值,而beanpropertysetterrule获取的是位于当前元素内的原始字符数据值。使用beanpropertysetterrule时不必指定要设置的bean属性名字,默认是当前xml元素的名称。在上面的例子中,在匹配catalog/magazine/article/headline模式的规则定义中使用的就是默认值。最后,setnextrule弹出解析栈顶部的对象,并把该对象传递给它下面对象的指定名称的方法——通常用来把一个配置完毕的bean插入父对象。

    注意,我们可以为同一个模式注册多个规则。如果注册了多个规则,则这些规则按照它们被加入到digester的次序执行,例如,如果要处理catalog/magazine/article的

    元素,我们首先创建合适的article bean,然后设置page属性,最后弹出完成后的article bean,并把它插入magazine。
    调用任意方法

    我们不仅可以设置bean的属性,而且还可以调用堆栈内对象的任意方法。这通过callmethodrule完成,我们只需指定方法名字,如有必要,再说明调用的参数类型和数量。callparamrule用来定义传递给被调用函数的参数值,参数值可以从当前xml元素的命名的属性获取,也可以从当前元素包含的原始字符数据获取。例如,在前面实现digesterdriver的例子中,我们可以不用beanpropertysetterrule,而是通过显式调用属性的set方法达到同样的目的:

    digester.addcallmethod( "catalog/book/author", "setauthor", 1 );
                digester.addcallparam( "catalog/book/author", 0 );

    上面的第一行代码给出了要调用的方法(即setauthor()),以及该调用需要的参数数量(即1)。第二行代码的意思是从元素包含的字符数据获取函数参数的值,把它作为参数数组的第一个传入(即索引是0的数组元素)。如果我们指定了xml元素属性的名称(例如digester.addcallparam( "catalog/book/author", 0, "author" );),则参数值将从当前元素的相应属性值获取。

    这里必须注意的是,“digester.addcallmethod( "pattern", "methodname", 0 );”这个语句不是指定了一个不带参数的方法调用,而是指定了带有一个参数的方法调用,它的值就是当前xml元素的字符数据!这样,我们又有了另一种替代beanpropertysetterrule的办法:

    digester.addcallmethod( "catalog/book/author", "setauthor", 0 );

    如果要调用一个确实没有参数的方法,必须采用如下形式:digester.addcallmethod( "pattern", "methodname" );。

    标准规则概要

    下面简要说明所有标准规则。

    创建

    objectcreaterule:利用指定类的默认构造函数,创建该类的一个对象,并把对象压入栈。当元素处理结束时,对象被弹出。被实例化的类可通过class对象或类的全称给出。

    factorycreaterule:利用指定的工厂类创建一个对象,把对象压入栈。对于没有提供默认构造函数的类,这一规则很有用。用于该规则的工厂类必须实现org.apache.commons.digester.objectcreationfactory接口。

    设置属性

    setpropertiesrule:利用指定名称的xml元素属性值,设置顶层bean的一个或者多个指定名称的属性。xml元素的属性名称和bean的属性名称以string[]数组形式传入该规则(通常用来处理

    之类的结构)。

    beanpropertysetterrule:把顶层bean的指定名称的属性设置成当前xml元素包含的字符数据。(通常用来处理10之类的结构)。

    setpropertyrule:设置顶层bean的一个属性。无论是bean属性的名称,还是赋予该属性的值,都在当前xml元素中以属性的形式指定,例如:

    管理父/子关系

    setnextrule:弹出栈顶的对象,把它传递给紧接其下的另一个对象的指定名称的方法。通常用来把一个已经初始化的bean插入到父对象。

    settoprule:把栈里面上数第二的对象传递给顶层的对象。当子对象提供了一个setparenet方法时,这一规则很有用。

    setrootrule:调用栈底对象的一个方法,并把栈顶的对象作为参数传入。

    调用任意方法

    callmethodrule:调用顶层bean的指定名称的方法。被调用的方法可以有任意多个参数,参数的值通过后继的callparamrule给出。

    callparamrule:表示方法调用的参数。参数的值或者取自指定名称的xml元素的属性,或者是当前元素包含的原始字符数据。这个规则要求用一个整数指定它在参数列表中的位置。

    通过xml指定规则

    在前面的内容中,我们用程序代码的方式指定模式和规则,这些模式和规则都是在编译的时候就已经确定,虽然从概念上来讲比较简单,但却不能说尽善尽美:digester框架的总体目标是在运行时识别和处理各种数据结构,但如果我们用编程的方法指定模式和规则,则所有行为在编译时已经固定!如果java源程序中包含了大量固定的字符串,通常意味着程序在执行某些配置操作,这部分操作可以被(或许是应该被)延迟到运行时进行。

    org.apache.commons.digester.xmlrules包解决了这个问题。这个包提供了一个digesterloader类,它能够从xml文档读取模式/规则对,返回配置好的digester对象。用来配置digester对象的xml文档必须遵从digester-rules.dtd,这个dtd是xmlrules包的一部分。

    下面就是本文例子的配置文件rules.xml。有几点必须说明。

    首先,模式可以用两种方式指定:或者使用元素,或者通过代表规则的xml元素的属性。这两种办法可以混合使用,且元素是可以嵌套的。其次,元素和一起使用,用来把xml属性映射到bean属性。最后,就当前发行的digester软件包而言,我们不能在配置文件中指定beanpropertysetterrule,正如前面所介绍的,我们用callmethodrule来达到同样的目标。

    
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                
                

    现在,所有实际的操作都转移到了digester和digesterloader类,xmlrulesdriver类就变得相当简单。运行下面的xmlrulesdriver时,在第一个命令行参数中指定目录文档的名字,在第二个参数中指定rules.xml(注意,digesterloader不是从file或者org.xml.sax.inputsource读取rules.xml文件,而是要求指定一个url,因此,下面代码中file引用被转换成了等价的url)。

    import org.apache.commons.digester.*;
                import org.apache.commons.digester.xmlrules.*;
                import java.io.*;
                import java.util.*;
                public class xmlrulesdriver {
                public static void main( string[] args ) {
                try {
                file input = new file( args[0] );
                file rules = new file( args[1] );
                digester digester = digesterloader.createdigester( rules.to );
                catalog catalog = (catalog)digester.parse( input );
                system.out.println( catalog.tostring() );
                } catch( exception exc ) {
                exc.printstacktrace();
                }
                }
                }

    结束语:本文对jakarta commons digester的介绍就到这里结束。当然,还有许多内容这里尚未涉及。其中一个在这里忽略的主题是xml名称空间:digester允许把规则定义成只能对某一个名称空间内定义的元素起作用。

    另外,我们简单地提及了通过扩展rule类开发定制规则的问题。按照习惯,digester类提供了push()、peek()和pop()方法,使得开发者能够自由地直接操作解析栈。

    参考:

    jakarta commons digester homepage

    jakarta struts homepage



    林光炎 2009-08-26 20:27
    ]]>threadlocal与synchronized http://www.blogjava.net/lingy/archive/2009/08/25/292559.html林光炎林光炎tue, 25 aug 2009 12:36:00 gmthttp://www.blogjava.net/lingy/archive/2009/08/25/292559.html相关文章:  
    正确理解threadlocal
    threadlocal与synchronized

    推荐圈子: pipboy
    更多相关推荐
    昨天上java版块逛了一圈,一个2万5千人浏览的帖子引起了偶滴注意 threadlocal与synchronized ,9页以上的回复,足见大家对这个问题的兴趣。

    老实说,从看到这个帖子的题目开始,就觉得帖子的作者估计是在概念上有所混淆了,于是乎想写个咚咚,同大家分享一下自己的心得。

    帖子上,讨论的人很多,高手不乏,各抒己见,但不知新手们看明白没有,因此,这里偶以最简洁列表方式来说一说相关问题。

    1.区别threadlocal 与 synchronized

    threadlocal是一个线程隔离(或者说是线程安全)的变量存储的管理实体(注意:不是存储用的),它以java类方式表现;
    synchronized是java的一个保留字,只是一个代码标识符,它依靠jvm的锁机制来实现临界区的函数、变量在cpu运行访问中的原子性。
    两者的性质、表现及设计初衷不同,因此没有可比较性。

    2.理解threadlocal中提到的变量副本
    事实上,我们向threadlocal中set的变量不是由threadlocal来存储的,而是thread线程对象自身保存。当用户调用threadlocal对象的set(object o)时,该方法则通过thread.currentthread()获取当前线程,将变量存入thread中的一个map内,而map的key就是当前的threadlocal实例。请看源码,这是最主要的两个函数,能看出threadlocal与thread的调用关系:

    java代码
    public void set(t value) {   
            thread t = thread.currentthread();   
            threadlocalmap map = getmap(t);   
            if (map != null)   
                map.set(this, value);   
            else  
                createmap(t, value);   
    }   
      
    threadlocalmap getmap(thread t) {   
            return t.threadlocals;   
    }  
    public void set(t value) {
            thread t = thread.currentthread();
            threadlocalmap map = getmap(t);
            if (map != null)
                map.set(this, value);
            else
                createmap(t, value);
    }

    threadlocalmap getmap(thread t) {
            return t.threadlocals;
    }

    (有兴趣的朋友可以阅读java的threadlocal源码)因此,我们可以知道,所谓的变量副本,即是对object reference(对象引用)的拷贝。

    3.理解thread和 threadlocal对变量的引用关系
    实际上thread和threadlocal对变量引用关系就像是坐标系中的x轴和y轴,是从两个维度上来组织对变量的引用的。

    首先说thread。 我们知道一个threadone的执行会贯穿多个方法methoda、methodb、methodc这些方法可能分布于不同的类实例。假设,这些方法分别使用了threadlocala、threadlocalb、threadlocalc来保存线程本地变量,那么这些变量都存于threadone的map中,并使用各自的threadlocal实例作为key。 因此,可以认为,借助threanlocal的set方法,在x轴上,thread横向关联同一线程上下文中来自多个method的变量引用副本。


     


    接着说threadlocal。 一个methoda中的x变量将被多个线程threadone、threadtwo、threadthree所访问。假设methoda使用threadlocal存储x,通过set方法,以threadlocal作为key值,将不同线程来访时的不同的变量值引用保存于threadone、threadtwo、threadthree的各自线程上下文中,确保每个线程有自己的一个变量值。因此,可以认为,threadlocal是以method为y轴,纵向关联了处于同一方法中的不同线程上的变量。


     

    希望能对大家有所帮助,这样可以少走很多弯路哦。

    本文来自csdn博客,转载请标明出处:http://blog.csdn.net/yangairong1984/archive/2008/04/15/2294572.aspx



    林光炎 2009-08-25 20:36
    ]]>
    理解threadlocal http://www.blogjava.net/lingy/archive/2009/08/25/292558.html林光炎林光炎tue, 25 aug 2009 12:35:00 gmthttp://www.blogjava.net/lingy/archive/2009/08/25/292558.htmlthreadlocal是什么

    早在jdk 1.2的版本中就提供java.lang.threadlocal,threadlocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。

    threadlocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,threadlocal并不是一个thread,而是thread的局部变量,也许把它命名为threadlocalvariable更容易让人理解一些。

    当使用threadlocal维护变量时,threadlocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

    从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“local”所要表达的意思。

    线程局部变量并不是java的新发明,很多语言(如ibm ibm xl fortran)在语法层面就提供线程局部变量。在java中没有提供在语言级支持,而是变相地通过threadlocal的类提供支持。

    所以,在java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在java开发者中得到很好的普及。

    threadlocal的接口方法

    threadlocal类接口很简单,只有4个方法,我们先来了解一下:

    void set(object value)
    设置当前线程的线程局部变量的值。

    public object get()
    该方法返回当前线程所对应的线程局部变量。

    public void remove()
    将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是jdk 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。

    protected object initialvalue()
    返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(object)时才执行,并且仅执行1次。threadlocal中的缺省实现直接返回一个null。

    值得一提的是,在jdk5.0中,threadlocal已经支持泛型,该类的类名已经变为threadlocal。api方法也相应进行了调整,新版本的api方法分别是void set(t value)、t get()以及t initialvalue()。

    threadlocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在threadlocal类中有一个map,用于存储每一个线程的变量副本,map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:

    代码清单1 simplethreadlocal

    public class simplethreadlocal {

    private map valuemap = collections.synchronizedmap(new hashmap());

    public void set(object newvalue) {

    valuemap.put(thread.currentthread(), newvalue);①键为线程对象,值为本线程的变量副本

    }

    public object get() {

    thread currentthread = thread.currentthread();

    object o = valuemap.get(currentthread);②返回本线程对应的变量

    if (o == null && !valuemap.containskey(currentthread)) {③如果在map中不存在,放到map

    中保存起来。

    o = initialvalue();

    valuemap.put(currentthread, o);

    }

    return o;

    }

    public void remove() {

    valuemap.remove(thread.currentthread());

    }

    public object initialvalue() {

    return null;

    }

    }

    虽然代码清单9‑3这个threadlocal实现版本显得比较幼稚,但它和jdk所提供的threadlocal类在实现思路上是相近的。

    一个theadlocal实例

    下面,我们通过一个具体的实例了解一下threadlocal的具体使用方法。

    代码清单2 sequencenumber

    package com.baobaotao.basic;

    public class sequencenumber {

    ①通过匿名内部类覆盖threadlocal的initialvalue()方法,指定初始值

    private static threadlocal seqnum = new threadlocal(){

    public integer initialvalue(){

    return 0;

    }

    };

    ②获取下一个序列值

    public int getnextnum(){

    seqnum.set(seqnum.get() 1);

    return seqnum.get();

    }

    public static void main(string[] args)

    {

    sequencenumber sn = new sequencenumber();

    ③ 3个线程共享sn,各自产生序列号

    testclient t1 = new testclient(sn);

    testclient t2 = new testclient(sn);

    testclient t3 = new testclient(sn);

    t1.start();

    t2.start();

    t3.start();

    }

    private static class testclient extends thread

    {

    private sequencenumber sn;

    public testclient(sequencenumber sn) {

    this.sn = sn;

    }

    public void run()

    {

    for (int i = 0; i < 3; i ) {④每个线程打出3个序列值

    system.out.println("thread[" thread.currentthread().getname()

    "] sn[" sn.getnextnum() "]");

    }

    }

    }

    }

     
    通常我们通过匿名内部类的方式定义threadlocal的子类,提供初始的变量值,如例子中①处所示。testclient线程产生一组序列号,在③处,我们生成3个testclient,它们共享同一个sequencenumber实例。运行以上代码,在控制台上输出以下的结果:

    thread[thread-2] sn[1]

    thread[thread-0] sn[1]

    thread[thread-1] sn[1]

    thread[thread-2] sn[2]

    thread[thread-0] sn[2]

    thread[thread-1] sn[2]

    thread[thread-2] sn[3]

    thread[thread-0] sn[3]

    thread[thread-1] sn[3]

    考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个sequencenumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过threadlocal为每一个线程提供了单独的副本。

    thread同步机制的比较

    threadlocal和线程同步机制相比有什么优势呢?threadlocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

    在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

    而threadlocal则从另一个角度来解决多线程的并发访问。threadlocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。threadlocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进threadlocal。

    由于threadlocal中可以持有任何类型的对象,低版本jdk所提供的get()返回的是object对象,需要强制类型转换。但jdk 5.0通过泛型很好的解决了这个问题,在一定程度地简化threadlocal的使用,代码清单 9 2就使用了jdk 5.0新的threadlocal版本。

    概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而threadlocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

    spring使用threadlocal解决线程安全问题

    我们知道在一般情况下,只有无状态的bean才可以在多线程环境下共享,在spring中,绝大部分bean都可以声明为singleton作用域。就是因为spring对一些bean(如requestcontextholder、transactionsynchronizationmanager、localecontextholder等)中非线程安全状态采用threadlocal进行处理,让它们也成为线程安全的状态,因为有状态的bean就可以在多线程中共享了。

    一般的web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,如图9‑2所示:

     

    图1同一线程贯通三层

    这样你就可以根据需要,将一些非线程安全的变量以threadlocal存放,在同一次请求响应的调用线程中,所有关联的对象引用到的都是同一个变量。

    下面的实例能够体现spring对有状态bean的改造思路:

    代码清单3 topicdao:非线程安全

    public class topicdao {

    private connection conn;①一个非线程安全的变量

    public void addtopic(){

    statement stat = conn.createstatement();②引用非线程安全变量

    }

    }

    由于①处的conn是成员变量,因为addtopic()方法是非线程安全的,必须在使用时创建一个新topicdao实例(非singleton)。下面使用threadlocal对conn这个非线程安全的“状态”进行改造:

    代码清单4 topicdao:线程安全

    import java.sql.connection;

    import java.sql.statement;

    public class topicdao {

    ①使用threadlocal保存connection变量

    private static threadlocal connthreadlocal = new threadlocal();

    public static connection getconnection(){

    ②如果connthreadlocal没有本线程对应的connection创建一个新的connection,

    并将其保存到线程本地变量中。

    if (connthreadlocal.get() == null) {

    connection conn = connectionmanager.getconnection();

    connthreadlocal.set(conn);

    return conn;

    }else{

    return connthreadlocal.get();③直接返回线程本地变量

    }

    }

    public void addtopic() {

    ④从threadlocal中获取线程对应的connection

    statement stat = getconnection().createstatement();

    }

    }

    不同的线程在使用topicdao时,先判断connthreadlocal.get()是否是null,如果是null,则说明当前线程还没有对应的connection对象,这时创建一个connection对象并添加到本地线程变量中;如果不为null,则说明当前的线程已经拥有了connection对象,直接使用就可以了。这样,就保证了不同的线程使用线程相关的connection,而不会使用其它线程的connection。因此,这个topicdao就可以做到singleton共享了。

    当然,这个例子本身很粗糙,将connection的threadlocal直接放在dao只能做到本dao的多个方法共享connection时不发生线程安全问题,但无法和其它dao共用同一个connection,要做到同一事务多dao共享同一connection,必须在一个共同的外部类使用threadlocal保存connection。

    小结

    threadlocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,threadlocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。

     

    本文来自csdn博客,转载请标明出处:http://blog.csdn.net/qjyong/archive/2008/03/08/2158097.aspx



    林光炎 2009-08-25 20:35
    ]]>
    ownerhttp://www.blogjava.net/lingy/archive/2009/07/24/288258.html林光炎林光炎fri, 24 jul 2009 12:13:00 gmthttp://www.blogjava.net/lingy/archive/2009/07/24/288258.htmlowner_4
    客户服务人员

    应该是owner_2客服支持人员

    3是制作人员
    4是服务人员
    31没看到


    林光炎 2009-07-24 20:13
    ]]>
    ilevelhttp://www.blogjava.net/lingy/archive/2009/07/23/287998.html林光炎林光炎thu, 23 jul 2009 03:51:00 gmthttp://www.blogjava.net/lingy/archive/2009/07/23/287998.html
     
    ilevel 是1,2,3,4时分别是什么意思啊
    3--区域
    4--组管组
    1--
    2--?
     
     
    3 区域 和 大区
    1 全国 和 乱七八糟的 服务拍摄之类的
    2是论七八糟服务拍摄的下一级
     
    这个在那张表定义吗?我在ali_zeus_resourece表里找不到
     
    在ali_zeus_access_area


    林光炎 2009-07-23 11:51
    ]]>
    ilevelhttp://www.blogjava.net/lingy/archive/2009/07/23/287997.html林光炎林光炎thu, 23 jul 2009 03:51:00 gmthttp://www.blogjava.net/lingy/archive/2009/07/23/287997.html
     
    ilevel 是1,2,3,4时分别是什么意思啊
    3--区域
    4--组管组
    1--
    2--?
     
     
    3 区域 和 大区
    1 全国 和 乱七八糟的 服务拍摄之类的
    2是论七八糟服务拍摄的下一级
     
    这个在那张表定义吗?我在ali_zeus_resourece表里找不到
     
    在ali_zeus_access_area


    林光炎 2009-07-23 11:51
    ]]>
    execute immediate用法小解 http://www.blogjava.net/lingy/archive/2009/07/16/287019.html林光炎林光炎thu, 16 jul 2009 11:42:00 gmthttp://www.blogjava.net/lingy/archive/2009/07/16/287019.html 它解析并马上执行动态的sql语句或非运行时创建的pl/sql块.动态创建和执行sql语句性能超前,execute immediate的目标在于减小企业费用并获得较高的性能,较之以前它相当容易编码.尽管dbms_sql仍然可用,但是推荐使用execute immediate,因为它获的收益在包之上。


    -- 使用技巧


    1. execute immediate将不会提交一个dml事务执行,应该显式提交
    如果通过execute immediate处理dml命令,
    那么在完成以前需要显式提交或者作为execute immediate自己的一部分.
    如果通过execute immediate处理ddl命令,它提交所有以前改变的数据


    2. 不支持返回多行的查询,这种交互将用临时表来存储记录(参照例子如下)或者用ref cursors.


    3. 当执行sql语句时,不要用分号,当执行pl/sql块时,在其尾部用分号.


    4. 在oracle手册中,未详细覆盖这些功能。
    下面的例子展示了所有用到execute immediate的可能方面.希望能给你带来方便.


    5. 对于forms开发者,当在pl/sql 8.0.6.3.版本中,forms 6i不能使用此功能.


    execute immediate -- 用法例子


    1. 在pl/sql运行ddl语句


    begin
       execute immediate 'set role all';
    end;


    2. 给动态语句传值(using 子句)


    declare
       l_depnam varchar2(20) := 'testing';
       l_loc     varchar2(10) := 'dubai';
       begin
       execute immediate 'insert into dept values   (:1, :2, :3)'
         using 50, l_depnam, l_loc;
       commit;
    end;


    3. 从动态语句检索值(into子句)


    declare
       l_cnt     varchar2(20);
    begin
       execute immediate 'select count(1) from emp'
         into l_cnt;
       dbms_output.put_line(l_cnt);
    end;


    4. 动态调用例程.例程中用到的绑定变量参数必须指定参数类型.
    黓认为in类型,其它类型必须显式指定


    declare
       l_routin    varchar2(100) := 'gen2161.get_rowcnt';
       l_tblnam    varchar2(20) := 'emp';
       l_cnt       number;
       l_status    varchar2(200);
    begin
       execute immediate 'begin ' || l_routin || '(:2, :3, :4); end;'
         using in l_tblnam, out l_cnt, in out l_status;

       if l_status != 'ok' then
          dbms_output.put_line('error');
       end if;
    end;


    5. 将返回值传递到pl/sql记录类型;同样也可用%rowtype变量


    declare
       type empdtlrec is record (empno   number(4),
                                ename   varchar2(20),
                                deptno   number(2));
       empdtl empdtlrec;
    begin
       execute immediate 'select empno, ename, deptno ' ||
                        'from emp where empno = 7934'
         into empdtl;
    end;


    6. 传递并检索值.into子句用在using子句前


    declare
       l_dept     pls_integer := 20;
       l_nam      varchar2(20);
       l_loc      varchar2(20);
    begin
       execute immediate 'select dname, loc from dept where deptno = :1'
         into l_nam, l_loc
         using l_dept ;
    end;


    7. 多行查询选项.对此选项用insert语句填充临时表,
    用临时表进行进一步的处理,也可以用ref cursors纠正此缺憾.

    declare
       l_sal    pls_integer := 2000;
    begin
       execute immediate 'insert into temp(empno, ename) ' ||
                        '           select empno, ename from emp ' ||
                        '           where   sal > :1'
         using l_sal;
       commit;
    end;


            对于处理动态语句,execute immediate 比以前可能用到的更容易并且更高效.
    当意图执行动态语句时,适当地处理异常更加重要.应该关注于捕获所有可能的异常.
    我是无聊的人,所以做无聊的事情。


    林光炎 2009-07-16 19:42
    ]]>
    使用 truncate table 删除所有行http://www.blogjava.net/lingy/archive/2009/07/16/287018.html林光炎林光炎thu, 16 jul 2009 11:41:00 gmthttp://www.blogjava.net/lingy/archive/2009/07/16/287018.html
    sql server 2008 联机丛书(2009 年 5 月)
    使用 truncate table 删除所有行

    若要删除表中的所有行,则 truncate table 语句是一种快速、有效的方法。truncate table 与不含 where 子句的 delete 语句类似。但是,truncate table 速度更快,并且使用更少的系统资源和事务日志资源。

    与 delete 语句相比,truncate table 具有以下优点:

    • 所用的事务日志空间较少。
      delete 语句每次删除一行,并在事务日志中为所删除的每行记录一个项。truncate table 通过释放用于存储表数据的数据页来删除数据,并且在事务日志中只记录页释放。
    • 使用的锁通常较少。
      当使用行锁执行 delete 语句时,将锁定表中各行以便删除。truncate table 始终锁定表和页,而不是锁定各行。
    • 如无例外,在表中不会留有任何页。
      执行 delete 语句后,表仍会包含空页。例如,必须至少使用一个排他 (lck_m_x) 表锁,才能释放堆中的空表。如果执行删除操作时没有使用表锁,表(堆)中将包含许多空页。对于索引,删除操作会留下一些空页,尽管这些页会通过后台清除进程迅速释放。

    与 delete 语句相同,使用 truncate table 清空的表的定义与其索引和其他关联对象一起保留在数据库中。如果表中包含标识列,该列的计数器将重置为该列定义的种子值。如果未定义种子,则使用默认值 1。若要保留标识计数器,请使用 delete。

    microsoft sql server 引入一种功能,此功能可删除或截断超过 128 个区的表,而无需同时保留需要删除的所有区的锁。有关详细信息,请参阅。

    下面的示例删除 jobcandidate 表中的所有数据。在 truncate table 语句之前和之后使用 select 语句来比较结果。

    use adventureworks;
    go
    select count(*) as beforetruncatecount
    from humanresources.jobcandidate;
    go
    truncate table humanresources.jobcandidate;
    go
    select count(*) as aftertruncatecount
    from humanresources.jobcandidate;
    go
    

    概念




    其他资源




    帮助和信息



    林光炎 2009-07-16 19:41
    ]]>
    一个简单的逻辑备份策略学习 http://www.blogjava.net/lingy/archive/2009/07/10/286287.html林光炎林光炎fri, 10 jul 2009 08:58:00 gmthttp://www.blogjava.net/lingy/archive/2009/07/10/286287.html一个简单的逻辑备份策略学习
    需求:
    每天19:00备份一次业务用户
    每个备份最多保留7天
    每个备份用日期作为备份文件的名称

    [root@localhost opt]# cat /home/autobackup/ora_env
    export oracle_base=/opt/oracle
    export oracle_home=/opt/oracle/product/10.2.0
    export oracle_sid=staples
    export oracle_term=xterm
    #export nls_lang="american_america.zhs16gbk"
    export ora_nls33=$oracle_home/ocommon/nls/admin/data
    export classpath=$oracle_home/jre:$oracle_home/jlib:$oracle_home/rdbms/jlib
    export path=$path:$oracle_home/bin

    export tns_admin=$oracle_home/network/admin
    export ld_library_path=$oracle_home/lib:$oracle_home/jlib
    export java_home=$oracle_home/jdk
    export ora_nls10=$oracle_home/nls/data
    export lc_ctype=en_us.utf-8

    [root@localhost opt]# cat /opt/backup_exp.sh
    #!/bin/sh
    #creator:daimin
    #function:backup database with expdp
    #usage:crontab on linux
    #last modify:tuolei 2007-08-29 create
    #set environment variable
    . /home/autobackup/ora_env  #设置exp的环境变量
    #开始备份,假定目录backuppath已经创建,并且backuppath=/opt/oracle/backup
    backuppath='/opt/oracle/backup/'
    file=`date %y%m%d`'.dmp'
    logfile=`date %y%m%d`'.log'
    exp daimin/daimin@staples file=$backuppath$file.dmp log=$backuppath$logfile

    #删除以前过期的备份
    find /opt/oracle/backup/* -name "*.dmp" -mtime 4 -exec rm {} \;

     

    [root@localhost etc]# crontab -e
    0 19 * * * /opt/backup_exp.sh > /opt/oracle/backup/logs/backup_exp.log 2>&1
    ~
    "crontab.xxxxmfkudb" 1l, 76c written
    crontab: installing new crontab
    [root@localhost etc]# crontab -l
    0 19 * * * /opt/backup_exp.sh > /opt/oracle/backup/logs/backup_exp.log 2>&1

    注意:
    1、/opt/backup_exp.sh需要具有可执行权限,才可以被root用户执行
    [root@localhost backup]# chmod x /opt/backup_exp.sh
    否则会出现/bin/sh: /opt/backup_exp.sh: permission denied错误
    2、需要在脚本的第一行加#!/bin/sh
    否则会出现/bin/sh: /opt/backup_exp.sh: cannot execute binary file错误
    3、注意在执行exp命令之前,需要设置环境变量,所以在逻辑备份脚本中执行了. /home/autobackup/ora_env 命令;


    参考网页:
    http://blog.csdn.net/wzy0623/archive/2008/10/31/3193150.aspx
    http://www.linuxsir.org/main/?q=node/209


    本文来自csdn博客,转载请标明出处:http://blog.csdn.net/daimin1983/archive/2008/12/13/3511380.aspx



    林光炎 2009-07-10 16:58
    ]]>
    linux系统利用crontab命令实现定时重启http://www.blogjava.net/lingy/archive/2009/07/10/286193.html林光炎林光炎fri, 10 jul 2009 03:01:00 gmthttp://www.blogjava.net/lingy/archive/2009/07/10/286193.html  crontab是一个很方便的在unix/linux系统上定时(循环)执行某个任务的程序

      使用cron服务,用 service crond status 查看 cron服务状态,如果没有启动则 service crond start启动它,

      cron服务是一个定时执行的服务,可以通过crontab 命令添加或者编辑需要定时执行的任务:

      crontab -u //设定某个用户的cron服务,一般root用户在执行这个命令的时候需要此参数

      crontab -l //列出某个用户cron服务的详细内容

      crontab -r //删除没个用户的cron服务

      crontab -e //编辑某个用户的cron服务

      比如说root查看自己的cron设置:crontab -u root -l

      再例如,root想删除fred的cron设置:crontab -u fred -r

      在编辑cron服务时,编辑的内容有一些格式和约定,输入:crontab -u root -e

      进入vi编辑模式,编辑的内容一定要符合下面的格式:*/1 * * * * ls >> /tmp/ls.txt

      编辑/etc/crontab文件,在末尾加上一行: 30 5 * * * root init 6 这样就将系统配置为了每天早上5点30自动重新启动。

      需要将crond设置为系统启动后自动启动的服务,可以在/etc/rc.d/rc.local 中,在末尾加上

      service crond start

      如果还需要在系统启动十加载其他服务,可以继续加上其他服务的启动命令。

      比如: service mysqld start

      基本用法:

      1. crontab -l

      列出当前的crontab任务

      2. crontab -d

      删除当前的crontab任务

      3. crontab -e (solaris5.8上面是 crontab -r)

      编辑一个crontab任务,ctrl_d结束

      4. crontab filename

      以filename做为crontab的任务列表文件并载入

      crontab file的格式:

      crontab 文件中的行由 6 个字段组成,不同字段间用空格或 tab 键分隔。前 5 个字段指定命令要运行的时间

      分钟 (0-59)

      小时 (0-23)

      日期 (1-31)

      月份 (1-12)

      星期几(0-6,其中 0 代表星期日)

      第 6 个字段是一个要在适当时间执行的字符串

      例子:

      #min hour day month dayofweek command

      #每天早上6点10分

      10 6 * * * date

      #每两个小时

      0 */2 * * * date (solaris 5.8似乎不支持此种写法)

      #晚上11点到早上8点之间每两个小时,早上8点

      0 23-7/2,8 * * * date

      #每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点

      0 11 4 * mon-wed date

      #1月份日早上4点

      0 4 1 jan * date

      补充:在使用crontab的时候,要特别注意的是运行脚本中能够访问到的环境变量和当前测试环境中的环境变量未必一致,一个比较保险的做法是在运行的脚本程序中自行设置环境变量(export)

      (1)先建一个文件crond.txt如下, 每天早上5点36分重新启动

      36 5 * * * reboot

      (2)上传到/opt目录

      (3)运行命令

      crontab /opt/crond.txt

      crontab -l

      让配置文件生效:如果让配置文件生效,还得重新启动cron,切记,既然每个用户下的cron配置文件修改后。也要重新启动cron服务器。

      在fedora 和redhat中,我们应该用;

      [root@localhost ~]# /etc/init.d/crond restart

      如果让crond 在开机时运行,应该改变其运行级别;

      [root@localhost ~]# chkconfig --levels 35 crond on

      service crond status 查看 cron服务状态,如果没有启动则 service crond start启动它, cron服务是一个定时执行的服务,可以通过crontab 命令添加或者编辑需要定时执行的任务

    crontab文件的每一行由六个域(minutes、hours、day of month、month、day of week、 command)组 成,域之间用空格或tab分开,其中:

    minutes: 分钟域,值的范围是0到59

    hours: 小时域,值的范围是0到23

    day of month: 日期,值的范围是1到31

    month: 月份,值的范围是1到12

    day of week: 星期,值的范围是0到6,星期日值为0

    command: 所要运行的命令

    如果一个域是*,表明命令可以在该域所有可能的取值范围内执行。

    如果一个域是由连字符隔开的两个数字,表明命令可以在两个数字之间的范围内执行(包括两个数字 本身)。

    如果一个域是由逗号隔开的一系列值组成的,表明命令可以在这些值组成的范围内执行。

    如果日期域和星期域都有值,则这两个域都有效。

    编写一个文件,用以启动自动备份进程。

    cd /opt

    touch reboot.txt

    在reboot.txt中添加一下内容:

    0 4 * * * reboot

    crontab /opt/reboot.txt

    用crontab -e编辑定时操作,例如加入下行命令:

    用crontab -l命令来查看

    注意:需要启动服务(添加在rc.local中)

    重启crond任务

    /etc/init.d/cron restart (ubuntu下)

    第一种 在fedora或redhat 等以rpm包管理的系统中;

    [root@localhost ~]# /etc/init.d/crond start

    [root@localhost ~]# /etc/init.d/crond stop

    [root@localhost ~]# /etc/init.d/crond restart

    /etc/rc.d/init.d/crond restart

    命令简介

    crontab-操作每个用户的守护程序和该执行的时间表。

    部分参数说明

    crontab file [-u user]-用指定的文件替代目前的crontab。

    crontab-[-u user]-用标准输入替代目前的crontab.

    crontab-1[user]-列出用户目前的crontab.

    crontab-e[user]-编辑用户目前的crontab.

    crontab-d[user]-删除用户目前的crontab.

    crontab-c dir- 指定crontab的目录。

    crontab文件的格式:m h d m d cmd.

    m: 分钟(0-59)。

    h:小时(0-23)。

    d:天(1-31)。

    m: 月(1-12)。

    d: 一星期内的天(0~6,0为星期天)。

    cmd要运行的程序,程序被送入sh执行,这个shell只有user,home,shell这三个环境变量。

    下面是一个例子文件:

    #min hour day month dayofweek command
                #每天早上6点
                106* * * date
                #每两个小时
                0*/2* * * date
                #晚上11点到早上8点之间每两个小时,早上部点
                0 23-7/2,8* * * date
                #每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点
                0 11 4* mon-wed date
                #1月份日早上4点
                0 4 1 jan* date
                范例
                lark:~>crontab-1 列出用户目前的crontab.
                #min hour day month dayofweek command
                10 6* * * date
                0*/2* * * date
                0 23-7/2,8 * * * date
                lark:~>

    (责任编辑:云子)



    林光炎 2009-07-10 11:01
    ]]>
    linux定时任务系统cron入门http://www.blogjava.net/lingy/archive/2009/07/09/286135.html林光炎林光炎thu, 09 jul 2009 10:38:00 gmthttp://www.blogjava.net/lingy/archive/2009/07/09/286135.htmlcron是一个linux下的定时执行工具,可以在无需人工干预的情况下运行作业。由于cron 是linux的内置服务,但它不自动起来,可以用以下的方法启动、关闭这个服务:

     

    /sbin/service crond start //启动服务

    /sbin/service crond stop //关闭服务

    /sbin/service crond restart //重启服务

    /sbin/service crond reload //重新载入配置

     

     

    你也可以将这个服务在系统启动的时候自动启动:

     

    在/etc/rc.d/rc.local这个脚本的末尾加上:

    /sbin/service crond start

     

    现在cron这个服务已经在进程里面了,我们就可以用这个服务了,cron服务提供以下几种接口供大家使用:

     

    1.直接用crontab命令编辑

     

    cron服务提供crontab命令来设定cron服务的,以下是这个命令的一些参数与说明:

     

    crontab -u //设定某个用户的cron服务,一般root用户在执行这个命令的时候需要此参数

     

    crontab -l //列出某个用户cron服务的详细内容

     

    crontab -r //删除没个用户的cron服务

     

    crontab -e //编辑某个用户的cron服务

     

    比如说root查看自己的cron设置:crontab -u root -l

     

    再例如,root想删除fred的cron设置:crontab -u fred -r

     

    在编辑cron服务时,编辑的内容有一些格式和约定,输入:crontab -u root -e

     

    进入vi编辑模式,编辑的内容一定要符合下面的格式:*/1 * * * * ls >> /tmp/ls.txt

     

    这个格式的前一部分是对时间的设定,后面一部分是要执行的命令,如果要执行的命令太多,可以把这些命令写到一个脚本里面,然后在这里直接调用这个脚本就可以了,调用的时候记得写出命令的完整路径。时间的设定我们有一定的约定,前面五个*号代表五个数字,数字的取值范围和含义如下:

     

    分钟 (0-59)

     

    小時 (0-23)

     

    日期 (1-31)

     

    月份 (1-12)

     

    星期 (0-6)//0代表星期天

     

    除了数字还有几个个特殊的符号就是"*"、"/"和"-"、",",*代表所有的取值范围内的数字,"/"代表每的意思,"*/5"表示每5个单位,"-"代表从某个数字到某个数字,","分开几个离散的数字。以下举几个例子说明问题:

     

    每天早上6点

     

    0 6 * * * echo "good morning." >> /tmp/test.txt //注意单纯echo,从屏幕上看不到任何输出,因为cron把任何输出都email到root的信箱了。

     

    每两个小时

     

    0 */2 * * * echo "have a break now." >> /tmp/test.txt

     

    晚上11点到早上8点之间每两个小时,早上八点

     

    0 23-7/2,8 * * * echo "have a good dream:)" >> /tmp/test.txt

     

    每个月的4号和每个礼拜的礼拜一到礼拜三的早上11点

     

    0 11 4 * 1-3 command line

     

    1月1日早上4点

     

    0 4 1 1 * command line

     

    每次编辑完某个用户的cron设置后,cron自动在/var/spool/cron下生成一个与此用户同名的文件,此用户的cron信息都记录在这个文件中,这个文件是不可以直接编辑的,只可以用crontab -e 来编辑。cron启动后每过一份钟读一次这个文件,检查是否要执行里面的命令。因此此文件修改后不需要重新启动cron服务。

     

    2.编辑/etc/crontab 文件配置cron

     

    cron服务每分钟不仅要读一次/var/spool/cron内的所有文件,还需要读一次/etc/crontab,因此我们配置这个文件也能运用 cron服务做一些事情。用crontab配置是针对某个用户的,而编辑/etc/crontab是针对系统的任务。此文件的文件格式是:

     

    shell=/bin/bash

    path=/sbin:/bin:/usr/sbin:/usr/bin

    mailto=root //如果出现错误,或者有数据输出,数据作为邮件发给这个帐号

    home=/ //使用者运行的路径,这里是根目录

    # run-parts

    01 * * * * root run-parts /etc/cron.hourly //每小时执行/etc/cron.hourly内的脚本

    02 4 * * * root run-parts /etc/cron.daily //每天执行/etc/cron.daily内的脚本

    22 4 * * 0 root run-parts /etc/cron.weekly //每星期执行/etc/cron.weekly内的脚本

    42 4 1 * * root run-parts /etc/cron.monthly //每月去执行/etc/cron.monthly内的脚本

     

    大家注意"run-parts"这个参数了,如果去掉这个参数的话,后面就可以写要运行的某个脚本名,而不是文件夹名了



    林光炎 2009-07-09 18:38
    ]]>
    over partition by与group by 的区别http://www.blogjava.net/lingy/archive/2009/06/29/284658.html林光炎林光炎mon, 29 jun 2009 11:04:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/29/284658.html各位好!
    over partition by 与 group by 都是与统计类函数用,这两个有什么区别呢?
    目前我只知道一个这样的区别:
    比如有一张表saraly:create table salary as select 'a' name,10 dept,1000 salary from dual union all select 'b',10,2000 from dual union all select 'c' ,20,1500 from dual union all select 'd',20,3000 from dual union all
    select 'e',10,1000 from dual;
    name dept salary
    a         10     1000
    b         10     2000
    c         20     1500
    d         20     3000
    e         10     1000   
    用over partition by 我就可以查询到每位员工本来的具体信息和它所在部门的总工资:
    select name,dept,salary,sum(salary) over (partition by dept) total_salary from salary;  
    name       dept         salary      tatal_salary
    a        10        1000        4000
    b        10        2000        4000
    e        10        1000        4000
    c        20        1500        4500
    d        20        3000        4500

    用goup by 就没办法做到这点,只能查询到每个部门的总工资:
    select dept,sum(salary) total_salary from salary group by dept
    dept        total_salary
    10        4000
    20        4500
    另外over partion by 还可以做到查询每位员工占部门总工资的百分比:
    select name,dept,salary,salary*100/sum(salary) over (partition by dept) percent from salary;

    name       dept         salary     percent
    a        10        1000        25
    b        10        2000        50
    e        10        1000        25
    c        20        1500        33.3333333333333
    d        20        3000        66.6666666666667
    用group by 也没办法做到这个.不知道我的理解正不正确,请各位朋友指点,特别是over partition by 与group by 的更多区别请各位一起分享,谢谢!

    20        4500



    林光炎 2009-06-29 19:04
    ]]>
    oracle中的null(八)http://www.blogjava.net/lingy/archive/2009/06/29/284559.html林光炎林光炎mon, 29 jun 2009 02:59:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/29/284559.html最近在论坛上经常看到,很多人提出和null有关的问题。null其实是数据库中特有的类型,oracle中很多容易出现的错误都是和null有关的。

    打算简单的总结一下null的相关知识。

    这一篇描述一下在sql和plsql中一些处理null的一些问题。

    oracle中的null(一):

    oracle中的null(二):

    oracle中的null(三):

    oracle中的null(四):

    oracle中的null(五):

    oracle中的null(六):

    oracle中的null(七):


    8\![ q!x;]$yf0

    null的最大的特点就是两个null是不相等的。如果用等号来判断两个null是否相等得到的结果一定是null。从唯一约束的特点也可以看到,对于建立了唯一约束的列,oracle允许插入多个null值,这时因为oracle不认为这些null是相等的。

    sql> create table t (id number, constraint un_t unique(id));

    表已创建。

    sql> insert into t values (1);

    已创建 1 行。

    sql> insert into t values (1);
    | pw6i)a#p,x_0insert into t values (1)
    h:w|3~ n0*
    lcb-p#b{$c0error 位于第 1 行:
    f9i vj y%t0ora-00001: 违反唯一约束条件 (yangtk.un_t)


    &rsq _0p7qvyn0sql> insert into t values (null);

    已创建 1 行。

    sql> insert into t values (null);

    已创建 1 行。

    但是有的时候,oracle会认为null是相同的,比如在group by和distinct操作中。这个时候,oracle会认为所有的null都是一类的。

    还有一种情况,就是在decode函数中。如果表达式为decode(col, null, 0, 1),那么如果col的值为null,oracle会认为这种情况与第二个参数的null值相匹配,会返回0。不过这里只是给人感觉null值是相等的,oracle在实现decode函数的时候,仍然是通过is null的方式进行的判断。

    对于大多数的常用函数来说,如果输入为null,则输出也是null。nvl、nvl2、decode和||操作是个例外。他们在输入参数为null的时候,结果可能不是null。不过归结其原因是因为,这些函数都有多个参数,当多个参数不全为null时,结果可能不是null,如果输入参数均为null,那么得到的输出结果也是null。

    null还有一个特点,就是一般聚集函数不会处理null值。不管是max、min、avg还是sum,这些聚集函数都不会处理null。注意这里说的不会处理null,是指聚集函数会直接忽略null值记录的存在。除非是聚集函数处理的列中包含的全部记录都是null,这种情况下,上面这些聚集函数会返回null值。

    sql> delete t where id = 1;

    已删除 1 行。

    sql> select nvl(to_char(id), 'null') from t;

    nvl(to_char(id),'null')itpub个人空间 z,i pa;o~)t/tm7l
    ----------------------------------------
    l b d-f-a/o*c0nullitpub个人空间(c"}5q5a#l#t
    null

    sql> select max(id) from t;

    max(id)
    !c cs"ys5|'`0----------


    g_s.c d5mk)o#c0sql> select avg(id) from t;

    avg(id)itpub个人空间*o.ylk0ou(oy4dq$vqr
    ----------


    rff lf{] y0sql> insert into t values (1);

    已创建 1 行。

    聚集函数中比较特殊的是count,第一个特殊点是count不会返回null值,即使表中没有记录,或者count(col)中,col列的记录全为null,count也会返回0值而不是null。第二个特殊点就是count(*)或count(常量)的形式。这种形式使得count可以计算包含null记录在内的记录总数。

    sql> select count(*), count(1), count('a'), count(id), count(null) from t;

    count(*) count(1) count('a') count(id) count(null)itpub个人空间-nnoh2z
    ---------- ---------- ---------- ---------- -----------
    ;s t:o8t3o.y0t0 3 3 3 1 0

    最后简单说一下avg,avg(col)等价于sum(col)/count(col),不等价于sum(col)/count(*):

    sql> select avg(id), sum(id)/count(id), sum(id)/count(*) from t;

    avg(id) sum(id)/count(id) sum(id)/count(*)
    6~{9o*ml9c7c)p0---------- ----------------- ----------------
    6@:[s'l&k\&v9{vr0 1 1 .333333333




    林光炎 2009-06-29 10:59
    ]]>
    能否按照decode条件来count???http://www.blogjava.net/lingy/archive/2009/06/29/284556.html林光炎林光炎mon, 29 jun 2009 02:57:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/29/284556.html表a  
          dw               wp             bj  
          ----------------  
          a                 m1             y  
          b                 m2             n  
          c                 m3             y  
          d                 m1             y  
       
      我向统计bj是'y‘的dw有多少个和总dw多少个??  
        我用count(dw),count(decode(a.bj,'y',dw,0))  
      这样不行啊??  
      sql应该怎么写啊???不要用兼套语句,这样太罗嗦了 问题点数:50、回复次数:3

    visual_studio_net(打鼠英雄)回复于 2006-04-22 20:08:49 得分 15

    select   count(dw),sum(decode(bj,'y',1,0))   from   a

    chliang315()回复于 2006-04-22 20:11:32 得分 35

    可以的  
      count(dw),count(decode(a.bj,'y',dw,null))

    bbcboy(烦猪哥哥)回复于 2006-04-22 20:30:58 得分 0

    两位都是好人啊!!  
      非常感谢!!   
     



    林光炎 2009-06-29 10:57
    ]]>
    ant cactus tomcat5.5容器内单元测试http://www.blogjava.net/lingy/archive/2009/06/22/283575.html林光炎林光炎mon, 22 jun 2009 07:06:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/22/283575.html   下载地址为http://java.chinaitlab.com/tools/45970.html 。将cactus的lib目录下的cactus-ant-1.7.1.jar复制到ant的lib目录。
      二、配置cactus
      cactus的配置很简单,新建一个cactus.properties文件,并把它放在ant脚本中的cactus任务的classpath下,文件中包括如下内容
      cactus.sysproperties=cactus.contexturl
      #cactus-sample-servlet-cactified就是你的测试应用所在路径,8080是端口号
      cactus.contexturl = http://localhost:8080/cactus-sample-servlet-cactified
      cactus.servletredirectorname = servletredirector
      cactus.jspredirectorname = jspredirector
      cactus.filterredirectorname = filterredirector
      具体的做法结合ant脚本再进一步解释。
      三、运行ant脚本
       ant脚本主要执行以下任务
      1、设定classpath
      
      
      
      

      
      
      
      
      

      2、定义相关任务
      
      
      
      
      

      

      3、编译应用的类文件和测试的类文件
      4、打包整个应用为war文件
      需要注重的是,不仅要打包应用类,测试类也要打包
          description="generate the runtime war">
          webxml="${src.webapp.dir}/web-inf/web.xml">
      
      
      
      
      

      
      
      
      
       < lib dir="project.classpath"/>
      

      

      5、在应用的web.xml文件中添加测试所需的各种映射
      cactus提供了两个task来完成这个工作,cactifywar和webxmlmerge。
      cactifywar的功能是自动在已经打包的应用的web.xml文件中添加所需的映射。webxmlmerge是提供合并两个web.xml文件的功能。
          depends="war, compile.cactus, test.prepare.logging">
      
          destfile="${tomcat.home}/webapps/${project.name}-cactified.war"
       >
      
      
      
      

      

      6、运行测试
      cactus提供了cactus和runservertests两个task来运行测试。
      "cactus" task是通过复制容器服务器的最小文件并运行来运行测试,因此需要制定容器服务器的类型,启动速度稍快点,另外配置比较方便,但是无法测试象tomcat连接池等资源。另外对tomcat5.5的支持也不好。
      "runservertests"是通过直接启动容器服务起来运行测试,因此速度稍慢,且配置较麻烦,但能测试各种资源。
          description="run tests on tomcat ">
      
      
          testurl="http://localhost:8080/${project.name}-cactified/servletredirector?cactus_service=run_test"
       starttarget="_starttomcat"
       stoptarget="_stoptomcat"
       testtarget="_test"/>
      

      
      
      
      
      
      
      
      

      
      
      
      
      
      
      
      

      

      

      

      文章来源: baike.duba.net

    林光炎 2009-06-22 15:06
    ]]>
    给servlet写单元测试的总结http://www.blogjava.net/lingy/archive/2009/06/22/283551.html林光炎林光炎mon, 22 jun 2009 05:39:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/22/283551.html 给servlet写单元测试的总结收藏
    servlet的测试一般来说需要容器的支持,不是像通常的java类的junit测试一样简单,
     
    下面通过对helloworld代码的测试阐述了几种servlet测试方法。
     
    被测试的helloworld类的代码如下:
     
    /**
     * 被测试的servlet
     */

    import java.io.ioexception;
     
    import javax.servlet.http.httpservlet;
    import javax.servlet.http.httpservletrequest;
    import javax.servlet.http.httpservletresponse;
    import org.apache.cactus.webrequest;
    import org.apache.cactus.server.httpservletrequestwrapper;
     
    public class helloworld extends httpservlet{
     
     public void savetosession(httpservletrequest request) {

             request.getsession().setattribute("testattribute",request.getparameter("testparam"));

     }
     
     public void doget(httpservletrequest request,httpservletresponse response) throws ioexception{

             string username=request.getparameter("username");

             response.getwriter().write(username ":hello world!");
           
     }
     
     public boolean authenticate(){
            
            return true;
     
     }

    }
     
    以helloworld为例,我总结了servlet的多种测试方法如下:
     
    一.使用httpunit测试
     
    import com.meterware.httpunit.getmethodwebrequest;
    import com.meterware.httpunit.webrequest;
    import com.meterware.httpunit.webresponse;
    import com.meterware.servletunit.invocationcontext;
    import com.meterware.servletunit.servletrunner;
    import com.meterware.servletunit.servletunitclient;
    import junit.framework.assert;
    import junit.framework.testcase;
     
    public class httpunittesthelloworld extends testcase {
     
     protected void setup() throws exception {
      super.setup();
     }
     
     protected void teardown() throws exception {
      super.teardown();
     }
     
     public void testhelloworld() {
     
      try {

       // 创建servlet的运行环境

       servletrunner sr = new servletrunner();

       // 向环境中注册servlet

       sr.registerservlet("helloworld", helloworld.class.getname());
     
       // 创建访问servlet的客户端

       servletunitclient sc = sr.newclient();

       // 发送请求

       webrequest request = new getmethodwebrequest("http://localhost/helloworld");
       request.setparameter("username", "testuser");

       invocationcontext ic = sc.newinvocation(request);

       helloworld is = (helloworld) ic.getservlet();
     
       // 测试servlet的某个方法

       assert.asserttrue(is.authenticate());

       // 获得模拟服务器的信息

       webresponse response = sc.getresponse(request);

       // 断言

       assert.asserttrue(response.gettext().equals("testuser:hello world!"));

      } catch (exception e) {

       e.printstacktrace();

      }

     }
     
    }
     
    上述例子其实是junit的一个测试例子,在其中使用了httpunit模拟的servlet环境,使用上述方法测试
     
    servlet可以脱离容器,容易把该测试写入ant或maven脚本,让测试进行。
     
    httpunit网址:http://httpunit.sourceforge.net/
     
    使用该种方法测试的弱点就是:如果要使用request(response)的setcharercterencoding方法时,测试会出现一些问题,
     
    而且httpunit在测试servlet行为时,采用的是完全模拟浏览器,有时测试比较难写。
     
    二 使用cactus测试
     
    /**
     * cactus测试servlet的例子
     * 必须要有tomcat的支持
     *
     */
     
    import junit.framework.test;
    import junit.framework.testsuite;
    import org.apache.cactus.servlettestcase;
    import org.apache.cactus.webrequest;
    import org.apache.cactus.webresponse;
    public class cactushelloworld extends servlettestcase{
     
         helloworld servlet;
         public cactushelloworld(string thename) {
             super(thename);
         }
     
         protected void setup() throws exception {
             super.setup();
             servlet = new helloworld();
         }
     
         protected void teardown() throws exception {
             super.teardown();
         }
     
         /**
          * 测试方法测试参数在此设置
          *
          * @param webrequest
          */
     
         public void beginsavetosessionok(webrequest request) {
             request.addparameter("testparam", "it works!");
         }
        
         /**
          * 测试方法测试参数在此设置
          *
          * @param webrequest
          */
     
         public void begindoget(webrequest request) {
             request.addparameter("username", "testuser");
         }
     
         /**
          * 调用servlet的测试方法
          * 
          */

         public void testsavetosessionok() {
             servlet.savetosession(request);
             assertequals("it works!", session.getattribute("testattribute"));
         }
     
         public void testdoget() {
             try {
                 servlet.doget(request, response);
             } catch (exception e) {
                 e.printstacktrace();
             }
         }
     
         /**
          * 此方法可以判断测试方法的输出,会传递测试方法的reponse给end***,并且格式化为cactus
          * 的webresponse或者可以跟httpunit集成,格式化为httpunit的response
          *
          * @param response
          */

         public void enddoget(webresponse response) {
             string content;        
             content = response.gettext();
             assertequals("testuser:hello world!", content);
         }
    }
     
    cactus具备丰富灵活的测试功能,如要测试doget方法,分为begindoget(模拟测试参数设置)、doget(执行测试)、enddoget(状态结果验证)
     
    相比httpunit来说,写测试更为容易,测试servlet更为专业,流程更为清晰,但是cactus需要容器支持,使得测试不可以自动进行,但是
     
    如果使用一个嵌入式的容器,测试就可以自动了。
     
    cactus是一个servlet和jsp的测试框架:http://jakarta.apache.org/cactus/getting_started.html
     
    三 使用jetty作为嵌入式容器测试servlet.
     
    /**
     * 一个关于嵌入式jetty测试的例子,jetty作为stubs的一个例子
     *
     */
    package com.easyjf.testexample;
     
    import org.mortbay.jetty.connector;
    import org.mortbay.jetty.server;
    import org.mortbay.jetty.bio.socketconnector;
    import org.mortbay.jetty.servlet.servlethandler;
     
    import com.meterware.httpunit.webclient;
    import com.meterware.httpunit.webconversation;
    import com.meterware.httpunit.webresponse;
     
    import junit.framework.assert;
    import junit.framework.testcase;
     
    public class jettysampletest extends testcase {
     
     server server;
     protected void setup() throws exception {
          //通过代码设置并启动一个服务器,该服务器是servlet的测试容器
          super.setup();
          server = new server();
          connector connector=new socketconnector();
          connector.setport(80);
          server.setconnectors(new connector[]{connector});
          servlethandler handler=new servlethandler();
          server.sethandler(handler);
          handler.addservletwithmapping("helloworld", "/");
          server.start();
     }
     
     protected void teardown() throws exception {
      super.teardown();
      server.stop();
     }
     
     public void testhellworld() {
      try {
       webconversation wc = new webconversation();
       webresponse web = wc.getresponse("http://127.0.0.1/helloworld");
       string result=web.gettext();
       assert.assertequals(result,"it works!");
      } catch (exception e) {
       e.printstacktrace();
      }
     }
    }
     
    可以发现,jetty可以充当一个servlet的容器,方便的是,jetty支持嵌入式服务,即可以通过代码来启动,
     
    所以要写自动测试的例子很方便,可以结合httpunit或者cactus进行servlet测试。
     
    jetty凯发k8网页登录主页:http://docs.codehaus.org/display/jetty/embedding jetty


    四 使用mock对象,此处使用easymock
     
    import java.io.printwriter;
    import java.io.writer;
    import javax.servlet.servletconfig;
    import javax.servlet.servletcontext;
    import javax.servlet.http.httpservletrequest;
    import javax.servlet.http.httpservletresponse;
     
    import junit.framework.assert;
    import junit.framework.testcase;
    import static org.easymock.easymock.*;
    public class mocktestservlet extends testcase {
     
        public void testservice() throws exception {

            system.out.println("service");

            httpservletrequest request = createmock(httpservletrequest.class);

            httpservletresponse response = createmock(httpservletresponse.class);

            //creating the servletconfig mock here

            servletconfig servletconfig = createmock(servletconfig.class);

            //creating the servletcontext mock here

            servletcontext servletcontext = createmock(servletcontext.class);
           
            //create the target object 
         
            helloworld4 instance = new helloworld();

            //初始化servlet,一般由容器承担,一般调用servletconfig作为参数初始化,此处模拟容器行为

            instance.init(servletconfig);
     
            //在某些方法被调用时设置期望的返回值,如下这样就不会去实际调用servletconfig的getservletcontext方法,而是直接返回
     
            //servletcontext,由于servletconfig是mock出来的,所以可以完全控制。

            expect(servletconfig.getservletcontext()).andreturn(servletcontext).anytimes();

            expect(request.getparameter("username")).andreturn("testuser");

            printwriter pw=new printwriter(system.out,true);

            expect(response.getwriter()).andreturn(pw).anytimes();
           
            //以上均是录制,下面为重放,该种机制为easymock测试机制,要理解请看easymock测试的一些资料
            replay(request);
            replay(response);
            replay(servletconfig);
            replay(servletcontext);
     
            instance.doget(request, response);

            pw.flush();
           
     
            //验证结果是否预期,如果预期,则会在pw上写出testuser.
            verify(request);
            verify(response);
            verify(servletconfig);
            verify(servletcontext);
       }
    }
     
    mock测试注重行为,mock对象其实都是模拟的对象,方法一般直接给出一个返回值,没有具体的对象逻辑,mock对象
     
    是用来帮助测试要测试的类的。比如要测试servlet的内部行为,又不想要容器等环境,就可以采用mock测试。
     
    easymock是mock测试的一个框架:http://www.easymock.org/
     
    发表于 @ 2007年02月10日 22:13:00|评论(2)

    新一篇: 设计模式之创建模式 | 旧一篇: 服务定位器模式(service locator)wldandanpig 发表于2007年5月24日 10:09:40  ip:举报
    请问楼主
    public string saveinfo()
    {
    string reqinfo = request.getparameter("reqinfo");
    string sessinfo = (string)request.getsession().getattribute("sessinfo");

    request.setattribute("reqinfo" , "response:" reqinfo);
    request.getsession().setattribute("sessinfo", "response:" reqinfo);

    return "success";
    }
    这个方法怎么测试啊cz_hyf 发表于2007年5月24日 17:20:23  ip:举报
    如果用httpunit的话

    public void testhelloworld() {

    try {

    // 创建servlet的运行环境

    servletrunner sr = new servletrunner();

    // 向环境中注册servlet

    sr.registerservlet("helloworld", helloworld.class.getname());

    // 创建访问servlet的客户端

    servletunitclient sc = sr.newclient();

    // 发送请求

    webrequest request = new getmethodwebrequest("http://localhost/helloworld");
    request.setparameter("reqinfo", "......");

    invocationcontext ic = sc.newinvocation(request);

    helloworld is = (helloworld) ic.getservlet();

    // 测试servlet的某个方法

    assert.assertequals(is.saveinfo,"success");

    } catch (exception e) {

    e.printstacktrace();

    }

    }

    如果还不放心,不妨把request和request.session中的值取出来看看是否是你放进去的


    本文来自csdn博客,转载请标明出处:http://blog.csdn.net/cz_hyf/archive/2007/02/10/1507211.aspx



    林光炎 2009-06-22 13:39
    ]]>
    oracle trunc()函数的用法http://www.blogjava.net/lingy/archive/2009/06/19/283162.html林光炎林光炎fri, 19 jun 2009 01:39:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/19/283162.htmltrunc()函數分兩種

    1.trunc(for dates)
            trunc函数为指定元素而截去的日期值。
            其具体的语法格式如下:
            trunc(date[,fmt])
            其中:
            date        一个日期值
            fmt                日期格式,该日期将由指定的元素格式所截去。忽略它则由最近的日期截去
            下面是该函数的使用情况:
            trunc(to_date(’24-nov-1999 08:00 pm’,’dd-mon-yyyy hh:mi am’))
                    =’24-nov-1999 12:00:00 am’
            trunc(to_date(’24-nov-1999 08:37 pm’,’dd-mon-yyyy hh:mi am’,’hh’))        =’24-nov-1999 08:00:00 am’

    2.trunc(for number)
            trunc函数返回处理后的数值,其工作机制与round函数极为类似,只是该函数不对指定小数前或后的部分做相应舍入选择处理,而统统截去。
            其具体的语法格式如下
            trunc(number[,decimals])
            其中:
            number        待做截取处理的数值
            decimals        指明需保留小数点后面的位数。可选项,忽略它则截去所有的小数部分
            下面是该函数的使用情况:
            trunc(89.985,2)=89.98
            trunc(89.985)=89
            trunc(89.985,-1)=80
            注意:第二个参数可以为负数,表示为小数点左边指定位数后面的部分截去,即均以0记。


    林光炎 2009-06-19 09:39
    ]]>
    oracle merge into 的用法详解 实例 http://www.blogjava.net/lingy/archive/2009/06/18/283070.html林光炎林光炎thu, 18 jun 2009 08:10:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/18/283070.html
    oracle merge into 的用法详解 实例

    作用:merge into 解决用b表跟新a表数据,如果a表中没有,则把b表的数据插入a表;

    语法:

    merge into [your table-name] [rename your table here]

    using ( [write your query here] )[rename your query-sql and using just like a table]

    on ([conditional expression here] and [...]...)

    when mathed then [here you can execute some update sql or something else ]

    when not mathed then [execute something else here ! ]

    -------------------------------------实例-----------------------------------------------------------------

    merge into tfa_alarm_act_nms a
    using (select fp0,fp1,fp2,fp3,redefine_severity
    from tfa_alarm_status) b
    on (a.fp0=b.fp0 and a.fp1=b.fp1 and a.fp2=b.fp2 and a.fp3=b.fp3)
    when matched then update set a.redefine_severity=b.redefine_severity
    when not matched then insert (a.fp0,a.fp1,a.fp2,a.fp3,a.org_severity,a.redefine_severity,a.event_time
    ,a.int_id)
    values (b.fp0,b.fp1,b.fp2,b.fp3,b.redefine_severity,b.redefine_severity,sysdate,7777778);

    作用:利用表 tfa_alarm_status跟新表tfa_alarm_act_nms 的b.redefine_severity条件是a.fp0=b.fp0 and a.fp1=b.fp1 and a.fp2=b.fp2 and a.fp3=b.fp3,如果tfa_alarm_act_nms表中没有该条件的数据就插入。

    如果你的数据量很大,此sql效率非常高。



    林光炎 2009-06-18 16:10
    ]]>
    解析:怎样使用oracle的decode()函数 http://www.blogjava.net/lingy/archive/2009/06/18/283067.html林光炎林光炎thu, 18 jun 2009 08:08:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/18/283067.html 

    decode()函数,它将输入数值与函数中的参数列表相比较,根据输入值返回一个对应值。函数的参数列表是由若干数值及其对应结果值组成的若干序偶形式。当然,如果未能与任何一个实参序偶匹配成功,则函数也有默认的返回值。

    区别于sql的其它函数,decode函数还能识别和操作空值。

    语法:decode(control_value,value1,result1[,value2,result2…][,default_result]);

    control _value试图处理的数值。decode函数将该数值与后面的一系列的偶序相比较,以决定返回值。

    value1是一组成序偶的数值。如果输入数值与之匹配成功,则相应的结果将被返回。对应一个空的返回值,可以使用关键字null于之对应

    result1 是一组成序偶的结果值。

    default_result 未能与任何一个值匹配时,函数返回的默认值。

    例如:

    selectdecode( x , 1 , ‘x is 1 ’, 2 , ‘x is 2 ’, ‘others’) from dual

    当x等于1时,则返回‘x is 1’。

    当x等于2时,则返回‘x is 2’。

    否则,返回others’。

    需要,比较2个值的时候,可以配合sign()函数一起使用。

    select decode( sign(5 -6), 1 'is positive', -1, 'is nagative', 'is zero')

    同样,也可以用case实现:

    select case sign(5 - 6)
                when  1  then  'is positive'
                when -1 then  'is nagative'
                else 'is zero' end
                from dual

    此外,还可以在order by中使用decode。

    例如:表table_subject,有subject_name列。要求按照:语、数、外的顺序进行排序。这时,就可以非常轻松的使用decode完成要求了。

    select * from table_subject order by decode(subject_name, '语文', 1, '数学', 2, , '外语',3)(责任编辑:卢兆林)



    林光炎 2009-06-18 16:08
    ]]>
    怎样从一个过程返回一个结果集http://www.blogjava.net/lingy/archive/2009/06/18/283045.html林光炎林光炎thu, 18 jun 2009 06:22:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/18/283045.htmlcreate or replace procedure p_stu_lst(result out sys_refcursor) is
    begin
       open result for select * from test;
    end p_stu_lst;
    sql> select * from test;

    name       km                 cj
    ---------- ---------- ----------
    张三       语文               80
    张三       数学               86
    张三       英语               75
    李四       语文               78
    李四       数学               85
    李四       英语               78
    李四       物理               90

    已选择7行。

    sql> exec p_stu_lst(:aaa);

    pl/sql 过程已成功完成。

    sql> print aaa

    name       km                 cj
    ---------- ---------- ----------
    张三       语文               80
    张三       数学               86
    张三       英语               75
    李四       语文               78
    李四       数学               85
    李四       英语               78
    李四       物理               90

    已选择7行。

    林光炎 2009-06-18 14:22
    ]]>
    oracle pl/sql游标的学习http://www.blogjava.net/lingy/archive/2009/06/18/283042.html林光炎林光炎thu, 18 jun 2009 06:11:00 gmthttp://www.blogjava.net/lingy/archive/2009/06/18/283042.html 一 游标是什么

    游标字面理解就是游动的光标。

    用数据库语言来描述:游标是映射在结果集中一行数据上的位置实体,有了游标,用户就可以访问结果集中的任意一行数据了,将游标放置到某行后,即可对该行数据进行操作,例如提取当前行的数据等。

    二 游标的分类

    显式游标和隐式游标

    显式游标的使用需要4步:

    1. 声明游标

    cursor mycur(vartype number) is
                select emp_no,emp_zc from cus_emp_basic
                where com_no = vartype;


    2. 打开游标

    open mycur(000627)

    注:000627是参数

    3. 读取数据

    fetch mycur into varno, varprice;

    4. 关闭游标

    close mycur;

    三 游标的属性

    oracle 游标有4个属性:%isopen,%found,%notfound,%rowcount。

    %isopen判断游标是否被打开,如果打开%isopen等于true,否则等于false;

    %found %notfound判断游标所在的行是否有效,如果有效,则%foundd等于true,否则等于false;

    %rowcount返回当前位置为止游标读取的记录行数。

    四 示例

    set serveroutput on;
                declare
                varno varchar2(20);
                varprice varchar2(20);
                cursor mycur(vartype number) is
                select emp_no,emp_zc from cus_emp_basic
                where com_no = vartype;
                begin
                if mycur%isopen = false then
                open mycur(000627);
                end if;
                fetch mycur into varno,varprice;
                while mycur%found
                loop
                dbms_output.put_line(varno||','||varprice);
                if mycur%rowcount=2 then
                exit;
                end if;
                fetch mycur into varno,varprice;
                end loop;
                close mycur;
                end;


    pl/sql记录的结构和c语言中的结构体类似,是由一组数据项构成的逻辑单元。

    pl/sql记录并不保存在数据库中,它与变量一样,保存在内存空间中,在使用记录时候,要首先定义记录结构,然后声明记录变量。可以把pl/sql记录看作是一个用户自定义的数据类型。

    set serveroutput on;
                declare
                type person is record
                (
                empno cus_emp_basic.emp_no%type,
                empzc cus_emp_basic.emp_zc%type);
                person1 person;
                cursor mycur(vartype number)is
                select emp_no,emp_zc from cus_emp_basic
                where com_no=vartype;
                begin
                if mycur%isopen = false then
                open mycur(000627);
                end if;
                loop
                fetch mycur into person1;
                exit when mycur%notfound;
                dbms_output.put_line('雇员编号:'||person1.empno||',地址:'||person1.empzc);
                end loop;
                close mycur;
                end;


    典型游标for 循环

    游标for循环示显示游标的一种快捷使用方式,它使用for循环依次读取结果集中的行数据,当form循环开始时,游标自动打开(不需要open),每循环一次系统自动读取游标当前行的数据(不需要fetch),当退出for循环时,游标被自动关闭(不需要使用close)。使用游标for循环的时候不能使用open语句,fetch语句和close语句,否则会产生错误。

    set serveroutput on;
                declare
                cursor mycur(vartype number)is
                select emp_no,emp_zc from cus_emp_basic
                where com_no=vartype;
                begin
                for person in mycur(000627) loop
                dbms_output.put_line('雇员编号:'||person.emp_no||',地址:'||person.emp_zc);
                end loop;
                end;



    林光炎 2009-06-18 14:11
    ]]>
    网站地图