星期一, 十二月 25, 2006

几个REST框架

Cetia4 REST Framework 优点在于可以和 Java Servlet-based 的程序无缝集成。

在Cetia4 REST 后继的讨论中又列出了下面两个不错的 REST 框架:

XX Framework (see www.xxframework.org)

http://www.restlet.org/ 这是个老牌的 REST 框架,这个框架的设计目的不是完全面向Web Application的,学习曲线比较高。

restlet 和 cetia4 的最大区别是 restlet 不是基于 java servlet 的,而后者是。当然 restlet 也可以在 servlet 容器中运行

星期三, 十二月 20, 2006

在Oracle下打开性能分析

conn sys/oracle@xmlindex as sysdba
grant plustrace to xdb;
set autot trace
set autot off

Oracle 连接字符串

1.thin:

jdbc:oracle:thin:@xedo 其中xedo在tns中configuration

jdbc:oracle:thin:@//152.69.90.48:1521/orcl

2.oci:

jdbc:oracle:oci8:@xedo 其中xedo在tns中configuration

jdbc:oracle:oci8:@//152.69.90.48:1521/orcl

3.sqlplus

sqlplus xedo/xedo@xedo 其中xedo在tns中configuration

sqlplus xedo/xedo@//152.69.90.48:1521/xbrl

Oracle Blob Clob Bfilename使用

1.建立一个目录别名

create DIRECTORY 'tmpdir' AS '/tmp';
GRANT READ ON DIRECTORY bfile_dir1 TO scott;

2.
建立一个含有bfile字段的表

create table bfiletest(id number(3), fname bfile);

建立一个含有BLOB字段的表

create table blobtest(id number(3),ablob blob);

3.插入数据
INSERT INTO bfiletest VALUES (1, BFILENAME ('XMLDIR', 'tmptest'));
4.bfile转blob

CREATE OR REPLACE PROCEDURE loadLOBFromBFILE_proc(
TID IN NUMBER,rfilename in varchar2,upmessage out varchar2)
AS
Dest_loc BLOB;
Src_loc BFILE;
BEGIN
INSERT INTO BLOBTEST(ID,ABLOB) VALUES(TID,EMPTY_BLOB()) RETURN ABLOB INTO DEST_LOC;
Src_loc := BFILENAME('XMLDIR',rfilename);
IF (DBMS_LOB.FILEEXISTS(Src_loc) != 0)
THEN
DBMS_LOB.OPEN(Src_loc, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(Dest_loc, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.LOADFROMFILE(Dest_loc, Src_loc,DBMS_LOB.GETLENGTH(Src_loc));
DBMS_LOB.CLOSE(Dest_loc);
DBMS_LOB.CLOSE(Src_loc);
COMMIT;
upmessage := '0';
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
upmessage := '操作失败';
END;

5.bfile转clob

create or replace function getDocument(filename varchar2)
return CLOB deterministic
is
file bfile := bfilename('XMLDIR',filename);
charContent CLOB := ' ';
targetFile bfile;
warning number;
begin
targetFile := file;
DBMS_LOB.fileopen(targetFile, DBMS_LOB.file_readonly);
DBMS_LOB.loadfromFile(charContent,targetFile, DBMS_LOB.getLength(targetFile),1,1);
DBMS_LOB.fileclose(targetFile);
return charContent;
end;
/

6.Select到LOB字段的定位器
对某一LOB字段进行选择,则返回的不是LOB的值,而是该LOB字段的定位器

DELCARE
AUDIO_INFO BLOB;
BENGIN
SELECT ablob INTO AUDIO_INFO FROM blobtest WHERE id=100;
END;

1.运行远程控制

如果是本机安装,运行xhost +x
如果是远程控制,运行export DISPLAY=152.69.90.92:0.0
测试运行xeyes,xclock,gnome-session,应该出现图形界面


1.1 安装数据库前,操作系统的安装应该是选择Full installation

如果没有的话:
gnome-session
找到系统安装的对话框,选择所有项进行安装,安装时会提示插入安装盘

1.4.创建用户和组

# su root
# /usr/sbin/groupadd oinstall
# /usr/sbin/groupadd dba
# /usr/sbin/useradd -g oinstall -G dba oracle
# passwd oracle

如果存在oracle用户: /usr/sbin/userdel oracle
可以通过查看或直接修改/etc/passwd和/etc/group来看是否生效

1.5 上传安装zip包(用oracle用户登录并上传)

su oracle
unzip 10201_database_linux32.zip
使用oracle用户upload和unzip有好处,因为oracle将拥有10201_database_linux32.zip的访问限制
如果没有权限:
chown -R oracle:dba database (optional)

1.7 安装路径设置

推荐安装在/home/oracle下面,因为oracle对该目录有访问权限,无须其它操作,如果想换地方,例如:

创建安装目录 mkdir /opt/oracle
注意如果待安装目录dir对于oracle用户而言没有权限,可以修改目录权限,使用如下命令:chown -R oracle:dba /opt/oracle (optional)
这样oracle就拥有了/opt/oracle的访问权限

2.修改核心参数

su root
vi /etc/sysctl.conf

kernel.shmall = 2097152
kernel.shmmax = 2147483648
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
fs.file-max = 65536
net.ipv4.ip_local_port_range = 1024 65000
net.core.rmem_default=262144
net.core.wmem_default=262144
net.core.rmem_max=262144
net.core.wmem_max=262144

命令操作来使我们所做的变更生效
/sbin/sysctl -p

3.检查并调整环境变量

vi /home/oracle/.bash_profile
添加如下内容,你的具体值应该不会和这个完全相同.

export ORACLE_BASE=/home/oracle
export ORACLE_HOME=$ORACLE_BASE/product/10.2.0/db_1
export ORACLE_SID=ORCL
export PATH=$PATH:$HOME/bin:$ORACLE_HOME/bin
export LD_LIBRARY_PATH=$ORACLE_HOME/lib:/usr/lib
export LC_CTYPE=en_US.UTF-8

5.开始安装10g

su - oracle

在安装文件目录下找到runInstaller文件

./runInstaller

之后就会出现安装向导,sid最好用小写,安装前把domain改成有意义的域名

用默认值下一步下一步就可以了

注意上面开始的检测提示,如果有不对的地方要退出进行修正,我在安装的时候就遇到glibc的版本不对,这时应该下载安装需要的版本进行升级,rpm命令详见rpm文章。

Oracle系统中用户权限的赋予,查看和管理

在Oracle数据库中,用户的权限分为两种(在这里我们不讨论dba或dbopr的权限,只考虑普通用户的权限),分别是System Privilege系统权限 和User Table Privilege用户数据表权限.
1.首先,创建用户,以下几条命令可以创建一个用户,前提是必须以DBA的身份登录(如果你不是DBA,不要看下去了):
create user DB_USER identified by DB_USER_PW '创建用户DB_USER,密码为DB_USER_PW
grant create session to DB_USER '给用户创建会话的权限
grant resource to DB_USER
2.当用户建立后,会自动在Oracle数据库系统中生成属于该用户的Scheme (可以理解为所有属于该用户的表,视图....等对象的集合).
该用户可以将对这些对象的访问权限赋予其它的系统用户.
3.该用户用sqlplus登录后,以下命令可以看到该用户的权限(该部分取自于CNOUG网站):
本用户读取其他用户对象的权限:
  select * from user_tab_privs;
本用户所拥有的系统权限:
  select * from user_sys_privs;

Oracle 用户管理 User

drop user username cascade;
create user username identified by my_password;
grant create any directory, drop any directory to username;
grant connect, resource to username;
grant dba to username;
grant all on emp to username; --把emp用户的权限都给username
ALTER USER scott ACCOUNT LOCK -- lock a user account
ALTER USER scott ACCOUNT UNLOCK; -- unlocks a locked users account
ALTER USER scott PASSWORD EXPIRE; -- Force user to choose a new password
ALTER USER <username> IDENTIFIED BY <new_password> --改密码

Oracle的SPFILE介绍和使用

在Oracle9i和10g中,一个新的文件被引入spfile,spfile用于服务器端,管理初始化参数。
在9i以前,Oracle使用pfile存储初始化参数设置,这些参数在实例启动时被读取,任何修改需要重起实例才能生效;使用spfile你可以使用ALTER SYSTEM或者ALTER SESSION来动态修改那些可动态修改的参数,所有更改可以立即生效
1.pfile和spfile 的系统缺省目录
Unix: $ORACLE_HOME/dbs; NT: $ORACLE_HOME\database
Oralce在缺省目录中搜索参数文件,先找spfile,再找pfile
2.创建SPFILE(spfile${ORACLE_SID}.ora)
SQL> CREATE SPFILE='E:\ora9i\database\SPFILEEYGLE.ORA' FROM PFILE='E:\ora9i\admin\eygle\pfile\init.ora';
2.创建pfile(init${ORACLE_SID}.ora)
SQL> CREATE SPFILE='E:\ora9i\database\SPFILEEYGLE.ORA' FROM PFILE='E:\ora9i\admin\eygle\pfile\init.ora';
3.使用pfile启动数据库
SQL> startup pfile='E:\Oracle\admin\eyglen\pfile\init.ora';
4.使用spfile启动数据库
你不能以同样的方式指定spfile,但是可以创建一个包含spfile参数的pfile文件,指向spfile.
我们修改PFILE文件内容如下:
#Pfile link to SPFILE
SPFILE= 'E:\Oracle\Ora9iR2\database\SPFILEEYGLEN.ORA'
log_archive_start = false
4.查看当前使用的spfile
SQL> SHOW parameter spfile
结果value列返回空值,那么说明你在使用pfile
5.导出SPFILE文件后手动编辑
SQL> create pfile='c:\initeyglen.ora' from spfile;
SQL> SHUTDOWN immediate
手动编辑c:\initeyglen.ora
SQL>STARTUP pfile='e:\initeyglen.ora'
SQL> CREATE spfile FROM pfile='e:\initeyglen.ora';
SQL> STARTUP
6.在线修改系统参数
ALTER SYSTEM SET {parameter}=TRUE SCOPE=BOTH;
其中{parameter}可以是select name from v$parameter;的其中一个name值
SCOPE参数有三个可选值:MEMORY(只改变当前实例运行) 、SPFILE (只改变SPFILE的设置)、BOTH(改变实例及SPFILE)。

Oracle 锁 使用

设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据的一致性和准确性
数据库锁方式有三种:共享锁,独占锁,共享更新锁
共享锁SHARE
特点:可在一个表上共存多个
效果: 所有用户只可查询
独占锁EXCLUSIVE
特点:在一个表上只能单独存在,在用户执行DML语句INSERT、UPDATE、DELETE时隐含获得
效果: 独占的用户可查可更新,其他用户只可查
共享更新锁(行锁)
特点:对一个表的一行或多行进行封锁
效果: 查询也可以更新被封锁的数据行,其它用户只能查询但不能更新被封锁的数据行.
释放锁: 执行COMMIT或ROLLBACK语句,退出数据库
ORACLE里锁有以下几种模式:
0:none
1:null 空
2:Row-S 行共享(RS):共享表锁
3:Row-X 行专用(RX):用于行的修改
4:Share 共享锁(S):阻止其他DML操作
5:S/Row-X 共享行专用(SRX):阻止其他事务操作
6:exclusive 专用(X):独立访问使用
数字越大锁级别越高, 影响的操作越多。
一般的查询语句如select ... from ... ;是小于2的锁, 有时会在v$locked_object出现。
select ... from ... for update; 是2的锁。
当对话使用for update子串打开一个游标时,
所有返回集中的数据行都将处于行级(Row-X)独占式锁定,
其他对象只能查询这些数据行,不能进行update、delete或select...for update操作。
insert / update / delete ... ; 是3的锁。
没有commit之前插入同样的一条记录会没有反应,
因为后一个3的锁会一直等待上一个3的锁, 我们必须释放掉上一个才能继续工作。
创建索引的时候也会产生3,4级别的锁。
locked_mode为2,3,4不影响DML(insert,delete,update,select)操作,
但DDL(alter,drop等)操作会提示ora-00054错误。
有主外键约束时 update / delete ... ; 可能会产生4,5的锁。
DDL语句时是6的锁。
1.查看当前数据库里锁的情况
select s.username,s.sid,s.serial#,object_name from v$locked_object l,v$session s ,dba_objects o
where l.session_id=s.sid and o.object_id=l.object_id order by s.logon_time;
select /*+ RULE */ ls.osuser os_user_name, ls.username user_name,
decode(ls.type, 'RW', 'Row wait enqueue lock', 'TM', 'DML enqueue lock', 'TX',
'Transaction enqueue lock', 'UL', 'User supplied lock') lock_type,
o.object_name object, decode(ls.lmode, 1, null, 2, 'Row Share', 3,
'Row Exclusive', 4, 'Share', 5, 'Share Row Exclusive', 6, 'Exclusive', null)
lock_mode, o.owner, ls.sid, ls.serial# serial_num, ls.id1, ls.id2
from sys.dba_objects o, ( select s.osuser, s.username, l.type,
l.lmode, s.sid, s.serial#, l.id1, l.id2 from v$session s,
v$lock l where s.sid = l.sid ) ls where o.object_id = ls.id1 and o.owner
<> 'SYS' order by o.owner, o.object_name
select *from v$lock;
2.杀掉不正常的锁
alter system kill session 'sid,serial#';

Oracle Directory 目录 创建和映射

select * from all_directories;
create or replace directory XMLDIR as '/home/oracle/rain'
drop directory XMLDIR
conn / as sysdba
grant read on directory XMLDIR to public;
grant write on directory XMLDIR to public;

Oracle Database File 文件操作

--从OS文件系统中读入文件到CLOB
create or replace function getDocument(filename varchar2)
return CLOB deterministic
is
file bfile := bfilename('XMLDIR',filename);
charContent CLOB := ' ';
targetFile bfile;
warning number;
begin
targetFile := file;
DBMS_LOB.fileopen(targetFile, DBMS_LOB.file_readonly);
DBMS_LOB.loadfromFile(charContent,targetFile, DBMS_LOB.getLength(targetFile),1,1);
DBMS_LOB.fileclose(targetFile);
return charContent;
end;
/
create or replace function getFileContent(file bfile)
return CLOB deterministic
is
charContent CLOB := ' ';
targetFile bfile;
warning number;
begin
targetFile := file;
DBMS_LOB.fileopen(targetFile, DBMS_LOB.file_readonly);
DBMS_LOB.loadfromFile(charContent,targetFile,
DBMS_LOB.getLength(targetFile),1,1);
DBMS_LOB.fileclose(targetFile);
return charContent;
end;
/

Oracle Database User 用户操作

drop user username cascade;
create user username identified by my_password;
grant create any directory, drop any directory to username;
grant connect, resource to username;
grant dba to username;
grant all on emp to username; --把emp用户的权限都给username
ALTER USER scott ACCOUNT LOCK -- lock a user account
ALTER USER scott ACCOUNT UNLOCK; -- unlocks a locked users account
ALTER USER scott PASSWORD EXPIRE; -- Force user to choose a new password
ALTER USER <username> IDENTIFIED BY <new_password> --改密码

VNC远程控制Linux主机

1.VNC 客户端
下载一个vnc-3.3.7-x86_win32_viewer.exe
2.在Linux上启动VNC服务
/>vncserver
然后设置vnc登陆密码
3.使用客户端访问

Linux 用户,文件和目录的权限管理

修改目录的拥有者
chown -R oracle:dba oracle
把oracle目录拥有者改为oracle,拥有组改为dba
-R代表所有目录中的文件都被修改

启动RedHat AS的FTP服务

RedHat AS中预装的是vsftpd
vsftpd 是一个基于GPL发布的类Unix类操作系统上运行的服务器的名字(是一种守护进程),它可以运行在诸如Linux、BSD、Solaris、HP-UX 以及IRIX上面。它支持很多其他传统的FTP服务器所不支持的特征。它具有如下特点:非常高的安全性、带宽限制、良好的扩展性、支持创建虚拟用户、支持IPv6、支持虚拟IP、高速、稳定。
启动vsftpd目前我知道一种方法:
在GNome中 开始->系统->服务设置->服务
在服务中选中vsftpd,点击启动便可以了

查詢linux版本

uname:查詢linux版本,包括kernel等
man uname 可以看到所有详细信息

Linux rpm 安装命令

rpm -ivh xorg-version.rpm // 安装
rpm -ivh --replacepkgs xorg-version.rpm // 强行重新安装
rpm -ivh --nodeps xorg-version.rpm // 强行安装
rpm -q xorg // 查询
rpm -e xorg // 删除
rpm -Uvh xorg-version.rpm // 升级安装

压缩 解压 命令大全

zip all.zip *.jpg
  这条命令是将所有.jpg的文件压缩成一个zip包
unzip all.zip
  这条命令是将all.zip中的所有文件解压出来
tar -cf all.tar *.jpg
  这条命令是将所有.jpg的文件打成一个名为all.tar的包。-c是表示产生新的包,-f指定包的文件名。
tar -rf all.tar *.gif
  这条命令是将所有.gif的文件增加到all.tar的包里面去。-r是表示增加文件的意思。
tar -uf all.tar logo.gif
  这条命令是更新原来tar包all.tar中logo.gif文件,-u是表示更新文件的意思。
tar -tf all.tar
  这条命令是列出all.tar包中所有文件,-t是列出文件的意思
tar -xf all.tar
  这条命令是解出all.tar包中所有文件,-t是解开的意思
tar调用gzip
  # tar -czf all.tar.gz *.jpg
  这条命令是将所有.jpg的文件打成一个tar包,并且将其用gzip压缩,生成一个gzip压缩过的包,包名为all.tar.gz
  # tar -xzf all.tar.gz
  这条命令是将上面产生的包解开。
tar调用bzip2
  # tar -cjf all.tar.bz2 *.jpg
  这条命令是将所有.jpg的文件打成一个tar包,并且将其用bzip2压缩,生成一个bzip2压缩过的包,包名为all.tar.bz2
  # tar -xjf all.tar.bz2
  这条命令是将上面产生的包解开。
tar调用compress
  # tar -cZf all.tar.Z *.jpg
  这条命令是将所有.jpg的文件打成一个tar包,并且将其用compress压缩,生成一个uncompress压缩过的包,包名为all.tar.Z
  # tar -xZf all.tar.Z
  这条命令是将上面产生的包解开
对于.tar结尾的文件
  tar -xf all.tar
对于.gz结尾的文件
  gzip -d all.gz
  gunzip all.gz
对于.tgz或.tar.gz结尾的文件
  tar -xzf all.tar.gz
  tar -xzf all.tgz
对于.bz2结尾的文件
  bzip2 -d all.bz2
  bunzip2 all.bz2
对于tar.bz2结尾的文件
  tar -xjf all.tar.bz2
对于.Z结尾的文件
  uncompress all.Z
对于.tar.Z结尾的文件
  tar -xZf all.tar.z
Rar文件
# rar a all *.jpg
  这条命令是将所有.jpg的文件压缩成一个rar包,名为all.rar,该程序会将.rar 扩展名将自动附加到包名后。
# unrar e all.rar
  这条命令是将all.rar中的所有文件解压出来

重新编译Linux Kernel

procedure:
1.enter /usr/src/linux
2.make mrproper
3.make dep
4.make clean
5.make bzImage
6.make modules
7.make module_install
8.make install

红旗redflag上启动telnet

1.set /etc/xinetd.d/telnet
2.startup the telnet
/etc/rc.d/init.d/xinetd restart

星期二, 十二月 19, 2006

vi command 使用

1.delete the line
dd
2.copy
Y
3.exit and save
:wq
4.exit and not save
:q!
5.import mode
i
6.find forward
/string
7.find backward
?stirng
8.copy line
yy
9.copy the position where the cursor is
p
10.replace
:s/old/new/g

Emacs命令小结

基本命令
C-x C-c : 退出Emacs
C-x C-f : 打开一个文件,如果文件不存在,则创建一个文件
C-g : 取消未完成的命令
编辑
C-z (redefined): Undo;原来C-z是挂起Emacs(然后用fg命令调出);C-x u 是默认的命令; 移动一下光标,再C-z就可以redo
M-d : 删除光标后的词语
移动光标
C-v : 向前翻页
M-v : 向后翻页
M-r : 将光标移动到屏幕中间那行
C-a : 移到行首
M-a : 移到句首,从行首到句首之间可能有空格
C-e : 移到行尾
M-e : 移到句尾
M-{ : 向上移动一段
M-} : 向下移动一段
C-right : 向前移动一个单词
C-left : 向后移动一个单词
C-up : 向前移动一段
C-down : 向后移动一段
M-< : 移到整个文本开头
M-> : 移到整个文本末尾
C-u 数字 命令 : 执行多次(数字表示次数)该命令;“M-数字 命令” 也可以
M-x goto-line : 移动到某一行
C-l : 重绘屏幕,效果就是当前编辑行移动窗口中央
Buffer 相关
C-x k : 关闭当前buffer
C-x b : 切换到前一个编辑的buffer
C-x C-b : 列出当前所有buffer
C-x C-s : 保存当前buffer
C-x s : 保存所有未保存的buffer,会提示你是否需要保存
C-x C-w : 文件另存为
拷贝与粘贴
M-space (redefined): 设置mark; C-@ 是默认命令
C-w (redefined) : 剪切一块区域;如果没有设置mark,则是剪切一行
M-w (redefined) : 拷贝一块区域;如果没有设置mark, 则是拷贝一行
C-k : 从当前位置剪切到行尾
C-y : 粘贴
M-y : 用C-y拉回最近被除去的文本后,换成 M-y可以拉回以前被除去的文本。键入多次的M-y可以拉回更早以前被除去的文本。
C-x r k : 执行矩形区域的剪切
C-x r y : 执行矩形区域的粘贴
窗口操作
C-x 0 : 关闭当前窗口
C-x 1 : 将当前窗口最大化
C-x 2 : 垂直分割窗口
C-x 3 : 水平分割窗口
M-o (redefined) : 在窗口之间切换; C-x o 是默认命令
C-x 5 1/2/3/0 : 对frame类似的操作
C-x < : 窗口内容右卷
C-x > : 窗口内容左卷(这两个命令在垂直分割窗口后比较有用)
(C-u) C-x ^ : 加高当前窗口,如果有C-u,则每次加高4行
(C-u) C-x } : 加宽当前窗口
(C-u) C-x { : 压窄当前窗口
ESC C-v : 在其它窗口进行卷屏操作
搜索和替换
C-s : 向前搜索(增量式搜索);连续C-s,跳到下一个搜索到的目标
C-s RET : 普通搜索
C-r : 向前搜索
C-s RET C-w : 按单词查询
M-% : 查询替换,也就是替换前会询问一下
M-x replace-string : 普通替换
Tags
M-! etags .c .h : 创建TAGS文件
M-. : 跳到tag所在位置
M-x list-tags : 列出tags
Bookmark
C-x r m : 设置书签bookmark
C-x r b : 跳到bookmark处
帮助
C-h ? : 查看帮助信息
C-h f : 查看一个函数
C-h v : 查看一个变量
C-h k : 查看一个键绑定 (C-h c 也是查看键绑定,但是信息较简略)
C-h C-f : 查看一个函数的Info,非常有用
C-h i : 看Info
其它
C-M-\ : 对选中区域,按照某种格式(比如C程序)进行格式化
C-x h : 全部选中
M-! : 执行外部shell命令
M-x shell : 模拟shell的buffer
M-x term : 模拟terminal, C-c k 关闭terminal
C-x C-q : 修改buffer的只读属性

Xwindows远程控制Linux主机

1、安装Xmanager或XWin32软件。
2、启动telnet登陆到待远程控制的主机
1)在用户的目录下找到文件.bash_profile或profile,用vi对其进行编辑。加入下列命令行:
DISPLAY=192.168.0.11:0.0;export DISPLAY
2)保存,退出。
3)如果只想临时在客户端用一下图形界面,无需进行第4、5步,直接在xterm界面的命令行中输入:
export DISPLAY=192.168.0.11:0.0
注:192.168.0.11是你本机IP
3、然后就可以在命令行中运行图形界面程序了,测试。
运行xeyes,应该出现图形界面。
4、redhat下运行gnome-session,启动图形用户界面

Linux下应用程序不随Shell关闭而关闭

if you want to run jdev in the current directory
./jdev& : if the shell is closed, the jdev application will be closed followed by the shell being closed.
nohup ./jdev : if the shell is closed, the jdev application won't be closed followed by the shell being closed.

cpio 解压命令

cpio -idmv < ***.cpio

星期一, 十二月 18, 2006

Linux下Mount其他机器目录

Samba: http://www.e2web.cn/temp/0005/20066142358339294.htm
mount -t smbfs -o username=abc,password=abc //lily/download /mnt/download


Cifs : sudo mount -t cifs //10.182.112.107/public /mnt/rhost/arctic/pub -o uid=1000,gid=1000,username=spsz,password=showmethemoney,codepage=utf8,iocharset=utf8

星期日, 十二月 10, 2006

JDK1.5 加解密例子 zz

本文主要談一下密碼學中的加密和數字簽名,以及其在java中如何進行使用。對密碼學有興趣的夥伴,推薦看Bruce Schneier的著作:Applied Crypotography。在jdk1.5的發行版本中安全性方面有了很大的改進,也提供了對RSA算法的直接支持,現在我們從實例入手解決問題(本文僅是作爲簡單介紹):

  一、密碼學上常用的概念 

  1)消息摘要:

  這是一種與消息認證碼結合使用以確保消息完整性的技術。主要使用單向散列函數算法,可用于檢驗消息的完整性,和通過散列密碼直接以文本形式保存等,目前廣泛使用的算法有MD4、MD5、SHA-1,jdk1.5對上面都提供了支持,在java中進行消息摘要很簡單, java.security.MessageDigest提供了一個簡易的操作方法:
/**
*MessageDigestExample.java
*Copyright 2005-2-16
*/
import java.security.MessageDigest;
/**
*單一的消息摘要算法,不使用密碼.可以用來對明文消息(如:密碼)隱藏保存
*/
public class MessageDigestExample{
 public static void main(String[] args) throws Exception{
  if(args.length!=1){
   System.err.println("Usage:java MessageDigestExample text");
   System.exit(1);
  }

  byte[] plainText=args[0].getBytes("UTF8");

  //使用getInstance("算法")來獲得消息摘要,這裏使用SHA-1的160位算法
  MessageDigest messageDigest=MessageDigest.getInstance("SHA-1");

  System.out.println("\n"+messageDigest.getProvider().getInfo());
  //開始使用算法
  messageDigest.update(plainText);
  System.out.println("\nDigest:");
  //輸出算法運算結果
  System.out.println(new String(messageDigest.digest(),"UTF8"));
 }
}

  還可以通過消息認證碼來進行加密實現,javax.crypto.Mac提供了一個解決方案,有興趣者可以參考相關API文檔,本文只是簡單介紹什麽是摘要算法。

  2)私鑰加密:

  消息摘要只能檢查消息的完整性,但是單向的,對明文消息並不能加密,要加密明文的消息的話,就要使用其他的算法,要確保機密性,我們需要使用私鑰密碼術來交換私有消息。

  這種最好理解,使用對稱算法。比如:A用一個密鑰對一個文件加密,而B讀取這個文件的話,則需要和A一樣的密鑰,雙方共享一個私鑰(而在web環境下,私鑰在傳遞時容易被偵聽):

  使用私鑰加密的話,首先需要一個密鑰,可用javax.crypto.KeyGenerator産生一個密鑰 (java.security.Key),然後傳遞給一個加密工具(javax.crypto.Cipher),該工具再使用相應的算法來進行加密,主要對稱算法有:DES(實際密鑰只用到56位),AES(支持三種密鑰長度:128、192、256位),通常首先128位,其他的還有DESede等, jdk1.5種也提供了對對稱算法的支持,以下例子使用AES算法來加密:
/**
*PrivateExmaple.java
*Copyright 2005-2-16
*/
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import java.security.Key;

/**
*私?加密,保證消息機密性
*/
public class PrivateExample{
 public static void main(String[] args) throws Exception{
  if(args.length!=1){
   System.err.println("Usage:java PrivateExample ");
   System.exit(1);
  }
  byte[] plainText=args[0].getBytes("UTF8");

  //通過KeyGenerator形成一個key
  System.out.println("\nStart generate AES key");
  KeyGenerator keyGen=KeyGenerator.getInstance("AES");
  keyGen.init(128);
  Key key=keyGen.generateKey();
  System.out.println("Finish generating DES key");

  //獲得一個私?加密類Cipher,ECB是加密方式,PKCS5Padding是填充方法
  Cipher cipher=Cipher.getInstance("AES/ECB/PKCS5Padding");
  System.out.println("\n"+cipher.getProvider().getInfo());

  //使用私?加密
  System.out.println("\nStart encryption:");
  cipher.init(Cipher.ENCRYPT_MODE,key);
  byte[] cipherText=cipher.doFinal(plainText);
  System.out.println("Finish encryption:");
  System.out.println(new String(cipherText,"UTF8"));

  System.out.println("\nStart decryption:");
  cipher.init(Cipher.DECRYPT_MODE,key);
  byte[] newPlainText=cipher.doFinal(cipherText);
  System.out.println("Finish decryption:");

  System.out.println(new String(newPlainText,"UTF8"));

 }
}

  3)公鑰加密:

  上面提到,私鑰加密需要一個共享的密鑰,那麽如何傳遞密鑰呢?web環境下,直接傳遞的話很容易被偵聽到,幸好有了公鑰加密的出現。公鑰加密也叫不對稱加密,不對稱算法使用一對密鑰對,一個公鑰,一個私鑰,使用公鑰加密的數據,只有私鑰能解開(可用于加密);同時,使用私鑰加密的數據,只有公鑰能解開(簽名)。但是速度很慢(比私鑰加密慢100到1000倍),公鑰的主要算法有RSA,還包括Blowfish,Diffie-Helman 等,jdk1.5種提供了對RSA的支持,是一個改進的地方:
/**
*PublicExample.java
*Copyright 2005-2-16
*/
import java.security.Key;
import javax.crypto.Cipher;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
/**
*一個簡單的公?加密例子,Cipher類使用KeyPairGenerator生成的公?和私?
*/
public class PublicExample{
 public static void main(String[] args) throws Exception{
  if(args.length!=1){
   System.err.println("Usage:java PublicExample ");
   System.exit(1);
  }

  byte[] plainText=args[0].getBytes("UTF8");
  //構成一個RSA密鑰
  System.out.println("\nStart generating RSA key");
  KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA");
  keyGen.initialize(1024);
  KeyPair key=keyGen.generateKeyPair();
  System.out.println("Finish generating RSA key");

  //獲得一個RSA的Cipher類,使用公?加密
  Cipher cipher=Cipher.getInstance("RSA/ECB/PKCS1Padding");
  System.out.println("\n"+cipher.getProvider().getInfo());

  System.out.println("\nStart encryption");
  cipher.init(Cipher.ENCRYPT_MODE,key.getPublic());
  byte[] cipherText=cipher.doFinal(plainText);
  System.out.println("Finish encryption:");
  System.out.println(new String(cipherText,"UTF8"));

  //使用私?解密
  System.out.println("\nStart decryption");
  cipher.init(Cipher.DECRYPT_MODE,key.getPrivate());
  byte[] newPlainText=cipher.doFinal(cipherText);
  System.out.println("Finish decryption:");
  System.out.println(new String(newPlainText,"UTF8"));
 }
}

  4)數字簽名:

  數字簽名,它是確定交換消息的通信方身份的第一個級別。上面A通過使用公鑰加密數據後發給B,B利用私鑰解密就得到了需要的數據,問題來了,由于都是使用公鑰加密,那麽如何檢驗是A發過來的消息呢?上面也提到了一點,私鑰是唯一的,那麽A就可以利用A自己的私鑰進行加密,然後B再利用A的公鑰來解密,就可以了;數字簽名的原理就基于此,而通常爲了證明發送數據的真實性,通過利用消息摘要獲得簡短的消息內容,然後再利用私鑰進行加密散列數據和消息一起發送。java中爲數字簽名提供了良好的支持,java.security.Signature類提供了消息簽名:
/**
*DigitalSignature2Example.java
*Copyright 2005-2-16
*/
import java.security.Signature;
import java.security.KeyPairGenerator;
import java.security.KeyPair;
import java.security.SignatureException;

/**
*數字簽名,使用RSA私鑰對對消息摘要簽名,然後使用公?驗證 測試
*/
public class DigitalSignature2Example{
 public static void main(String[] args) throws Exception{
  if(args.length!=1){
   System.err.println("Usage:java DigitalSignature2Example ");
   System.exit(1);
  }

  byte[] plainText=args[0].getBytes("UTF8");
  //形成RSA公鑰對
  System.out.println("\nStart generating RSA key");
  KeyPairGenerator keyGen=KeyPairGenerator.getInstance("RSA");
  keyGen.initialize(1024);

  KeyPair key=keyGen.generateKeyPair();
  System.out.println("Finish generating RSA key");
  //使用私?簽名
  Signature sig=Signature.getInstance("SHA1WithRSA");
  sig.initSign(key.getPrivate());
  sig.update(plainText);
  byte[] signature=sig.sign();
  System.out.println(sig.getProvider().getInfo());
  System.out.println("\nSignature:");
  System.out.println(new String(signature,"UTF8"));

  //使用公?驗證
  System.out.println("\nStart signature verification");
  sig.initVerify(key.getPublic());
  sig.update(plainText);
  try{
   if(sig.verify(signature)){
    System.out.println("Signature verified");
   }else System.out.println("Signature failed");
   }catch(SignatureException e){
    System.out.println("Signature failed");
   }
  }
}

  5)數字證書。

  還有個問題,就是公鑰問題,A用私鑰加密了,那麽B接受到消息後,用A提供的公鑰解密;那麽現在有個討厭的C,他把消息攔截了,然後用自己的私鑰加密,同時把自己的公鑰發給B,並告訴B,那是A的公鑰,結果....,這時候就需要一個中間機構出來說話了(相信權威,我是正確的),就出現了 Certificate Authority(也即CA),有名的CA機構有Verisign等,目前數字認證的工業標准是:CCITT的X.509:
數字證書:它將一個身份標識連同公鑰一起進行封裝,並由稱爲認證中心或 CA 的第三方進行數字簽名。

  密鑰庫:java平台爲你提供了密鑰庫,用作密鑰和證書的資源庫。從物理上講,密鑰庫是缺省名稱爲 .keystore 的文件(有一個選項使它成爲加密文件)。密鑰和證書可以擁有名稱(稱爲別名),每個別名都由唯一的密碼保護。密鑰庫本身也受密碼保護;您可以選擇讓每個別名密碼與主密鑰庫密碼匹配。

  使用工具keytool,我們來做一件自我認證的事情吧(相信我的認證):

  1、創建密鑰庫keytool -genkey -v -alias feiUserKey -keyalg RSA 默認在自己的home目錄下(windows系統是c:\documents and settings\<你的用戶名> 目錄下的.keystore文件),創建我們用 RSA 算法生成別名爲 feiUserKey 的自簽名的證書,如果使用了-keystore mm 就在當前目錄下創建一個密鑰庫mm文件來保存密鑰和證書。

  2、查看證書:keytool -list 列舉了密鑰庫的所有的證書

  也可以在dos下輸入keytool -help查看幫助。


  二、JAR的簽名

  我們已經學會了怎樣創建自己的證書了,現在可以開始了解怎樣對JAR文件簽名,JAR文件在Java中相當于 ZIP 文件,允許將多個 Java 類文件打包到一個具有 .jar 擴展名的文件中,然後可以對這個jar文件進行數字簽名,以證實其來源和真實性。該 JAR 文件的接收方可以根據發送方的簽名決定是否信任該代碼,並可以確信該內容在接收之前沒有被篡改過。同時在部署中,可以通過在策略文件中放置訪問控制語句根據簽名者的身份分配對機器資源的訪問權。這樣,有些Applet的安全檢驗訪問就得以進行。

  使用jarsigner工具可以對jar文件進行簽名:

  現在假設我們有個Test.jar文件(可以使用jar命令行工具生成):

  jarsigner Test.jar feiUserKey (這裏我們上面創建了該別名的證書) ,詳細信息可以輸入jarsigner查看幫助

  驗證其真實性:jarsigner -verify Test.jar(注意,驗證的是jar是否被修改了,但不檢驗減少的,如果增加了新的內容,也提示,但減少的不會提示。)

  使用Applet中: 然後浏覽器就會提示你:准許這個會話-拒絕-始終准許-查看證書等。

  三、安全套接字層(SSL Secure Sockets Layer)和傳輸層安全性(TLS Transport Layer Security)

  安全套接字層和傳輸層安全性是用于在客戶機和服務器之間構建安全的通信通道的協議。它也用來爲客戶機認證服務器,以及(不太常用的)爲服務器認證客戶機。該協議在浏覽器應用程序中比較常見,浏覽器窗口底部的鎖表明 SSL/TLS 有效:

  1)當使用 SSL/TLS(通常使用 https:// URL)向站點進行請求時,從服務器向客戶機發送一個證書。客戶機使用已安裝的公共 CA 證書通過這個證書驗證服務器的身份,然後檢查 IP 名稱(機器名)與客戶機連接的機器是否匹配。

  2)客戶機生成一些可以用來生成對話的私鑰(稱爲會話密鑰)的隨機信息,然後用服務器的公鑰對它加密並將它發送到服務器。服務器用自己的私鑰解密消息,然後用該隨機信息派生出和客戶機一樣的私有會話密鑰。通常在這個階段使用 RSA 公鑰算法。

  3)客戶機和服務器使用私有會話密鑰和私鑰算法(通常是 RC4)進行通信。使用另一個密鑰的消息認證碼來確保消息的完整性。

  java中javax.net.ssl.SSLServerSocketFactory類提供了一個很好的SSLServerSocker 的工廠類,熟悉Socket編程的讀者可以去練習。當編寫完服務器端之後,在浏覽器上輸入https://主機名:端口就會通過SSL/TLS進行通話了。注意:運行服務端的時候要帶系統環境變量運行:javax.net.ssl.keyStore=密鑰庫(創建證書時,名字應該爲主機名,比如localhost)和javax.net.ssl.keyStorePassword=你的密碼。

星期一, 十二月 04, 2006

Tomcat5 中文乱码问题 解决方案

问题描述:

1 表单提交的数据,用request.getParameter(“xxx”)返回的字符串为乱码或者??
2 Tomcat5下直接通过url如http://localhost/a.jsp?name=中国,这样的get请求在服务端用request. getParameter(“name”)时返回的是乱码;按tomcat4的做法设置Filter也没有用或者用 request.setCharacterEncoding("GBK");也不管用

原因:
1 tomcat5的j2ee实现对表单提交即post方式提示时处理参数采用缺省的iso-8859-1来处理
2 tomcat5对get方式提交的请求对query-string 处理时采用了和post方法不一样的处理方式。(与tomcat4不一样,所以设置setCharacterEncoding(“gbk”))不起作用。

1 post方式的解决办法

1)首先所有的jsp文件都加上:
<%@ page contentType="text/html; charset=GBK"%>

2)实现一个Filter.设置处理字符集为GBK。(在tomcat的webapps/servlet-examples目录有一个完整的例子。请参考web.xml和SetCharacterEncodingFilter的配置。)
把%TOMCAT安装目录%/ webapps\servlets-examples\WEB-INF\classes\filters\SetCharacterEncodingFilter.class 文件拷到你的webapp目录/filters下,如果没有filters目录,就创建一个。

3)在你的web.xml里加入如下几行:
<filter>
<filter-name>Set Character Encoding</filter-name>
<filter-class>filters.SetCharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>GBK</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Set Character Encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

2 get方式的解决办法
1) 打开tomcat的server.xml文件,找到区块,加入如下一行:
URIEncoding=”GBK”
完整的应如下:
<Connector port="80" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" debug="0" connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="GBK"/>

當 Tomcat 發現 QueryString 並沒有設定 encode 時,並非像文件中所說預設採用 ISO-8859-1 的編碼,而是用一段 fast conversion 來處理,才會造成中文問題,所以,還是必須在 Server.xml 中,加上 URLEncoding 的參數設定才行哦。

星期日, 十二月 03, 2006

Using DWR DWRUtil 使用

Ajax是一种提高web站点吸引力和实用性的书写web页面的方法。它从服务器端更新web页面的特殊区域,从而增强用户的交互性。它允许信息在短时间的延迟或不用刷新页面的情况下更新。
DWR减少了开发时间,也减少了一些可能的错误,这些错误是在提供常用的方法函数并消除一些与高交互性web站点有关的重复性代码的时候产生的。
DWR是作为开源软件(ASL verssion 2.0)而可以免费得到的。它凭借它的广阔的库、例子和指南非常易于实现。把它结合到一个现有的站点是非常简单的,同样它也可以简单地与大多数java框架结合。


util.js
util.js包含了一些使用的方法,从而帮助你利用j avascript(可能)从服务器端更新你的web数据。
你可以在DWR之外的地方使用它,因为它并不依赖与DWR而实现。
它包含四个页面处理函数:getValue[s]()、setValue[s]()作用于除tables、lists和images以外的大多数html元素。getText()作用于select lists。
addRows()和removeAllRows()用于编辑tables。addOptions()和removeAllOptions()用于编辑lists(如:select lists、ul、ol)。


$()
$函数(在j avascript中,他的名字是合法的)的思想是从prototype引进的。一般说来,$ = document.getElementById。在以后你花大量时间进行ajax编程的时候,在合适的地方使用这种格式是很有益的。
'$'通过给定的ID在当前HTML页面找到元素,如果多于一个的参数被提交,它就会返回一个包含已找到元素的数组。这个函数从prototype的library中得到的灵感,而且,它还能更好的工作在不同的浏览器中。


Generating Lists
DWR的一个功能可以给一个下拉列表(select list)添加选项,只需使用DWRUtil.addOptions()。
如果你在更新列表之前,希望保留一些选项,你需要写以下一些代码:
var sel = DWRUtil.getValue(id);
DWRUtil.removeAllOptions(id);
DWRUtil.addOptions(id, ...);
DWRUtil.setValue(id, sel);
如果你想有个初始化选项,如:“please select”,你可以直接使用:
DWRUtil.addOptions(id, ["Please select"]);
DWRUtil.addOptions 有5种调用方法:Array: DWRUtil.addOptions(selectid, array) 。selectid为目标ID,array为每一项的text。
Array of objects (option text = option value): DWRUtil.addOptions(selectid, data, prop) 用text和value的集合来为每一个数组元素创建一个选项,pro参数指定text和value的值。
Array of objects (with differing option text and value): DWRUtil.addOptions(selectid, array, valueprop, textprop) 用text和value的集合来为每一个数组元素创建一个选项,valueprop确定value,textprop确定text。
Object: DWRUtil.addOptions(selectid, map, reverse) 为map中每一个属性(property)创建一个选项,属性名作为选项的value,属性的value作为选项的text。这样做看起来是错的,但实际上这种做法的确是正确的。如果reverse参数被设置为true,则属性的value还是被用做选项的value。
Map of objects: DWRUtil.addOptions(selectid, map, valueprop, textprop) 为map中的每一个对象创建一个选项,valueprop指定选项的value,textprop指定选项的text。


Generating Tables
DWRUtil.addRows() 从一个数组(第二个参数)取得值,创建table的每一行。从另一个数组(第三个参数)去得值,为table的每一行创建若干列。
DWRUtil.addRows()的用法:
DWRUtil.addRows(”items”,items,cellFunctions);
第一个items,应该是table的id,第2个items,是远程返回的结果集,cellFunctions,对表格行填充的回调函数一类的。
var cellFunctions = [ function(item) { return item.name; },
function(item) { return item.description; },
function(item) { return item.formattedPrice; },
function(item) { var btn = document.createElement(”button”); btn.innerHTML = “Add to cart”; btn.itemId = item.id; btn.onclick = addToCartButtonHandler; return btn; }
应该是一个function(item)就代表一列,return的就是传入的结果集的某列的内容。那么这个表格就应该有4列。最后一列是静态的html代码了。不过没研究一下,return回去动静混合的内容,就是一个字串会如何。我猜应该也可以吧。

DWRUtil.getText(id)
可以根据id取得text的值,这个方法只能用于select list
DWRUtil.getValue(id)
可以根据id取得value,使用这个方法,你不必在意div和select list的不同。
DWRUtil.getValues()
getValues() is similar to getValue() except that the input is a Javascript object that contains name/value pairs. The names are assumed to be the IDs of HTML elements, and the values are altered to reflect the contents of those IDs. This method does not return the object in question, it alters the value that you pass to it.
这个方法和getValue()一样,只是它传入的是一个包含名字和数值的j avascript对象.这个名字就是HTML元素的ID。这个方法不会返回任何对象,它只会将ID的value映射给传入的value。例:
function doGetValues() {
var text= "{
div:null,
textarea:null,
select:null,
text:null,
password:null,
formbutton:null,
button:null
}";
var object = objectEval(text); //j avascript对象
DWRUtil.getValues(object);
var reply = DWRUtil.toDescriptiveString(object, 2); //toString
reply = reply.replace(/n/g, "<br/>"); //转意
DWRUtil.setValue("getvaluesret", reply); //显示
}


DWRUtil.onReturn
贴一段代码,暂时不理解,用onReturn和不用有什么区别
<script>
function submitFunction()
{
$("alert").style.display = "inline";
setTimeout("unsubmitFunction();", 1000);
}
function unsubmitFunction()
{
$("alert").style.display = "none";
}
</script>
<p><input type="text" onkeydown="DWRUtil.onReturn(event, submitFunction)"/>
<input type="button" onclick="submitFunction()" value="GO"/>
<span id="alert" style="display:none; background:#FFFFDD; font-weight:bold;">submitFunction called</span>
</p>


DWRUtil.selectRange
在一个input box里选一个范围
DWRUtil.selectRange("sel-test", $("start").value, $("end").value);
DWRUtil.setValue(id, value)
用ID找到元素,并更新value
DWRUtil.setValues()
和setValue(id,value)一样,只是它需要的参数是个j avascript对象,如:
DWRUtil.setValues({
div: "new div content",
password: "1234567890"
});


DWRUtil.toDescriptiveString
带debug信息的toString,第一个为将要debug的对象,第二个参数为处理等级。等级如下:
0: Single line of debug 单行调试
1: Multi-line debug that does not dig into child objects 不分析子元素的多行调试
2: Multi-line debug that digs into the 2nd layer of child objects 最多分析到第二层子元素的多行调试
And so on. Level 2 and greater probably produce too much output.
总结:DWR不但屏蔽了许多client与server交互的重复且复杂的代码,而且还提供了一些常用的方法,一些思想还是从prototype继承而来,并有一定的改进。同时,它也考虑到了与struts、hibernate、spring的结合问题。
需要注意的是,DWR是一种把服务器端的java代码通过j avascript直接从浏览器调用的方法(DWR is a way of calling Java code on the server directly from Javascript in the browser.),而不是一个j avascript的库(Generally speaking DWR is not a generic JavaScript library so it does not attempt to provide fill this need. However this is one of these really useful functions to have around if you are doing Ajax work.)能做到怎么多,已经很难得了。
DWR自04年11月草案提出到现在的Version 1.1 beta 3(2005-12-29),已经更新发布了20多次了,但愿在ajax的发展大路上,能始终看见DWR的身影。

JDBCTemplate 事务处理 Transaction

  這邊以一個簡單的例子來示範Spring的事務管理,對於事務管理定義,我們使用Spring提供的DefaultTransactionDefinition。

  以JDBC DataSource為例,我們可以使用org.springframework.jdbc.datasource.DataSourceTransactionManager(PlatformTransactionManager的一個實現)作為我們的事務管理員,我們在Bean定義檔中配置,並將DataSource注入給它,例如:

 代碼:

程序代码:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans public "-//SPRING/DTD BEAN/EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName">
<value>com.mysql.jdbc.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://localhost:3306/TestDB</value>
</property>
<property name="username">
<value>root</value>
</property>
<property name="password">
<value></value>
</property>
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>

<bean id="userDAO" class="onlyfun.caterpillar.UserDAO">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
</bean>
</beans>


  我們定義了一個簡單的DAO物件,將transactionManager與dataSource注入至其中,UserDAO如下:

 代碼:

UserDAO.java

程序代码:
package onlyfun.caterpillar;

import javax.sql.DataSource;
import org.springframework.jdbc.core.*;
import org.springframework.transaction.*;
import org.springframework.transaction.support.*;
import org.springframework.dao.*;

public class UserDAO {
private DataSource dataSource;
private PlatformTransactionManager transactionManager;

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}

public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}

public void insertUser(User user) {
....
}
}


  insertUser()方法中,我們將實現User的存儲,並使用事務管理,首先看看直接使用trransactionManager如何實現事務,我們需要事務定義,作為示範,這邊先使用Spring預設的即可:DefaultTransactionDefinition,我們的程式示範如下:

 代碼:

程序代码:
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("INSERT INTO USER VALUES('Spring008', 'caterpillar', 'M', 29)");
jdbcTemplate.update("INSERT INTO USER VALUES('Spring009', 'momor', 'F', 26)");
jdbcTemplate.update("INSERT INTO USER VALUES('Spring010, 'beckyday', 'F', 35)");

} catch (DataAccessException ex) {
transactionManager.rollback(status); // 也可以執行status.setRollbackOnly();
throw ex;
}
transactionManager.commit(status);



  在上面的程式中,我們照常執行我們的SQL(為了簡化,我們直接將資料寫在SQL中了,而不是從User物件中取得),注意到我們故意在最後一行SQL中Spring010少寫了一個 ' ,這使得SQL句子不合語法,因而造成DataAccessException(封裝了SQLException),這個例外被catch區塊捕捉,因而執行rollback()取消之後的SQL,如果沒有發生錯誤,則最後我們使用commit()來提交操作。

  另一個簡便的方法是使用TransactionTemplate,它封裝了上面的事務流程,透過callback來完成事務,例如:

 代碼:

程序代码:
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
public void doInTransactionWithoutResult(TransactionStatus status) {
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
jdbcTemplate.update("INSERT INTO USER VALUES('Spring008', 'caterpillar', 'M', 29)");
jdbcTemplate.update("INSERT INTO USER VALUES('Spring009', 'momor', 'F', 26)");
jdbcTemplate.update("INSERT INTO USER VALUES('Spring010, 'beckyday', 'F', 35)");
}
});


  這邊使用TransactionCallbackWithoutResult,並在它的doInTransactionWithoutResult()中進行SQL操作,如果執行中發生例外,則之前所有的操作取消,否則最後自動提交操作。如果操作完成您想要返回結果物件,則可以使用TransactionCallback,並實現其doInTransaction(),例如:

 代碼:

程序代码:
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
Object result = transactionTemplate.execute(
new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
// 作一些操作
// 收集為結果物件 resultObject

return resultObject;
});

Using JDBCTemplate 使用

  您可以使用JdbcTemplate的execute()方法執行SQL陳述,例如:

 代碼:

jdbcTemplate.execute("CREATE TABLE USER (user_id integer, name varchar(100))");

  如果是UPDATE或INSERT,您可以使用update()方法,一個基本的INSERT例子是:


 代碼:



jdbcTemplate.update("INSERT INTO USER VALUES('"+ user.getId() + "', '"                + user.getName() + "', '"
+ user.getSex() + "', '"
+ user.getAge() + "')");

  使用UPDATE時,您可以在欄位上使用 "?",並指定參數陣列給對應的欄位,例如:


 代碼:



jdbcTemplate.update("UPDATE USER SET name = ? WHERE user_id = ?", new Object[] {name, id});

  在上例中所給定的name與id是String物件,它會取代之前對應順序的 "?",然後執行SQL更新。


  使用參數陣列來指定SQL中的變量相當方便,上例中的INSERT範例也可以這麼作,減輕撰寫SQL與Java物件結合時的負擔:


 代碼:



jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
new Object[] {user.getId(), user.getName(), user.getSex(), user.getAge()});

  使用JdbcTemplate進行查詢時,我們可以使用queryForXXX()等方法,例如下面使用queryForInt()方法傳回USER表格中的資料筆數:


 代碼:



int count = jdbcTemplate.queryForInt("SELECT COUNT(*) FROM USER");

  您也可以使用queryForObject()傳回一個查詢後的結果物件,例如下例傳回一個String物件:


 代碼:



String name = (String) jdbcTemplate.queryForObject("SELECT name FROM USER WHERE user_id = ?", new Object[] {id}, java.lang.String.class);

  上面兩個例子傳回的都是單一筆資料,如果傳回多筆資料,則可以使用queryForList()方法,例如:


 代碼:



List rows = jdbcTemplate.queryForList("SELECT * FROM USER");

  傳回的List中包括的是Map物件,每個Map物件代表查詢結果中的一筆資料,每筆資料包括多個欄位內容,要取得欄位中的值,要使用欄位名稱作為key值,例如:


 代碼:



List rows = jdbcTemplate.queryForList("SELECT * FROM USER");
Iterator it = rows.iterator();

while(it.hasNext()) {
Map userMap = (Map) it.next();
System.out.print(userMap.get("user_id") + "\t");
System.out.print(userMap.get("name") + "\t");
System.out.print(userMap.get("sex") + "\t");
System.out.println(userMap.get("age") + "\t");
}

  事實上,JdbcTemplate只是將我們使用JDBC的流程封裝起來而已,包括了例外捕捉、SQL的執行、查詢結果的轉換與傳回等等,之前所用到的參數設定,也只是在方法中幫您作SQL語句組合的動作而已,Spring大量使用Template Method模式來封裝固定流程的動作,XXXTemplate等類別都是基於這種方式實現的。


  除了大量使用Template Method來封裝一些低層操作細節,Spring也大量使用callback方式來呼叫相關類別之方法以提供傳統JDBC相關類別的功能,使得傳統JDBC的使用者也能清楚瞭解Spring所提供的相關封裝類別方法之使用。


  例如JDBC的PreparedStatement,我們可以實作PreparedStatementSetter介面來提供:


 代碼:



final String id = user.getId();

final String name = user.getName();
final String sex = user.getSex() + "";
final int age = user.getAge();

jdbcTemplate.update("INSERT INTO USER VALUES(?, ?, ?, ?)",
new PreparedStatementSetter() {
public void setValues(PreparedStatement ps) throws SQLException {
ps.setString(1, id);
ps.setString(2, name);
ps.setString(3, sex);
ps.setInt(4, age);
}
});

  您可以實作RowCallbackHandler介面,在查詢到資料之後先作一些處理再傳回,例如下面的例子實現了手動的O/R Mapping:


 代碼:



final User user = new User();
jdbcTemplate.query("SELECT * FROM USER WHERE user_id = ?",
new Object[] {id},
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));
}
});

  您更可以實作RowMapper介面將手動的O/R Mapping程式碼獨立撰寫,共用在不同的DAO方法之中:


 代碼:



class UserRowMapper implements RowMapper {
public Object mapRow(ResultSet rs, int index) throws SQLException {
User user = new User();

user.setId(rs.getString("user_id"));
user.setName(rs.getString("name"));
user.setSex(rs.getString("sex").charAt(0));
user.setAge(rs.getInt("age"));

return user;
}
}

  在findAllByRowMapperResultReader()裡面使用UserRowMapper:


 代碼:



public List findAllByRowMapperResultReader() {
String sql = "SELECT * FROM USER";

return jdbcTemplate.query(sql, new RowMapperResultReader(new UserRowMapper()));
}

  在getUser(id)裡面使用UserRowMapper:


 代碼:



public User getUser(final String id) throws DataAccessException {
String sql = "SELECT * FROM USER WHERE user_id=?";
final Object[] params = new Object[] { id };
List list = jdbcTemplate.query(sql, params, new RowMapperResultReader(new UserRowMapper()));

return (User) list.get(0);
}

星期六, 十二月 02, 2006

Using JSTL 使用

此文中,我们将更详细地讲述不同标记描述文件(Tag Library Descriptor,TLD)里的各种标记。我们会通过例子去说明条件、迭代、URL、SQL及XML等标记的使用。文章的目的就是展示JSTL的主要功能,说明如何使用JSTL以及说明如何使用JSTL去改善JSP的开发。

简单来说,JSTL就是一套可以用在多个方面的自定义功能,JAVA标准组织(Java Community Process,JCP)在JSR-52里面定义到,JSTL提供表达式语言(expression language),流程控制及数据检校等功能,JSTL规范的最后版本可以在JCP的网站上找得到。

JSTL要求运行在支持JSP 1.2版的服务器上,JSTL的主旨就是简化JSP的开发以及提供访问、操作数据的简便方式,使用JSTL可以使用大家的工作更轻松。

JSTL包含了多个TLD和JAR文件,这些TLD覆盖很多功能,我们将一个个说明。首先,我们要重点说一下JSTL最突出的功能,表达式语言。

实际上,表达式语言是由JSR-152专家组所提供的,也就是定义JSP 2.0的专家组。JSR-152和JSR-52很多时是在一起工作的,更甚的是,表达式语言已经成为JSP 2.0的一部分。表达式语言提供访问数据的一种简便方法,它支持运算,支持bean,支持集合,同时也支持类型转换和属性的缺省值。

使用表达式语言

表达式语言是通过 ${expression} 这样的形式来调用的,并且它只能用在属性里面,例如:

<c:if test="${product.price >= customer.limit}">
...
</c:if>


在上面的例子,我们使用表达式语言做一个比较运算,它也可以混合一些静态文本,就象下面的例子:

<c:forEach var="current">
<c:out value="Product-${current}"/>
</c:forEach>


在例子里面,我们枚举出一个集合里面的元素,之后将它加在一个字符串后面输出,结果就象:

Product-1
Product-2
Product-3...

从例子里可以看出,相对于以前的代码,使用JSTL之后,在整个过程变得简单清晰。在以前,你需要定义一个对象,要知道对象的类型,并且要掌握一些脚本语言,而这只是去完成一个简单的操作。

现在,有了JSTL,我们可以使用简单的语法去访问数据,表达式语言是非常适合做这样的工作的。

再举一个例子,我们可以将:

<jsp:useBean id="customer" type="sample.Customer" scope="request"/> ...
Customer Name: <%=customer.getName()%>
...
<% if (customer.getState().equals("CO")){ %>
...
<%}%>


转换成:

Customer Name: ${ customer. name}
<c:if test="${customer. state == param. state}">
...
</c:if>


表达式语言允许直接访问JSP中任何作用域的变量,比如可以用${foo}来代替pageContext.findAttributes("foo")。还有可以能过点或索引来访问bean以及它们的属性,如:

${user.address.city}
${products.[product.id]}
${products.["Part-010"]}


表达式语言提供所有你使用到的运算:==, !=, <,>, <=, >=, &&, ||, ! 。这表达式语言中,它们表示为lt, gt, le, ge, eq, ne,这样可以避免与XML的语法重复。表达式语言也有普通的算术运算同布尔运算。另外,表达式语言还具有自动类型转换的功能,如int value = "${request.myValue}"会自动转换。

在表达式语言是可以提供属性的缺省值的,使用缺省值是为了避免空指针异常,下面是一个例子,

<c:set var="city" value="${user.address.city}" default="N/A" />


现在,我们已经对表达式语言有所了解,下面,让我们看一下与EL相关的操作。

核心操作

在核心标记库里使用到表达式语言。<c:out>标记将表达式语言所计算出的值输出到当前的JSP输出流。这个与JSP中的<%= scripting exp %>相似。比如:

<c:out value="${customer.name}"  default="N/A" />


核心标记库里还可以设置和清除变量。变量的缺省作用域是PAGE。例如我们可以用<c:set var="customer" value=${customer}" />设置一个页面变量customer,再用一个<c:remove var="customer" />去清除它。

现在,能过JSTL,我们可以使用一个标记去捕捉java.lang.Throwable,例如:<c:catch var="myError" />。使用这个标记,可以统一页面的异常处理,但这不意味着代替JSP的错误页面机制。通过使用<c:catch>标记,就可以在页面上控制特定的异常,而不用转到错误页面,其实并不是所有异常都需要转到错误页面。通过使用<c:catch>标记,与用户的交互也变得更友好。

条件操作

在条件操作中使用EL是简化JSP的一种强有力的手段。<c:if>标记,它可以构造简单的条件表达式。下面的例子,访问了一个对象的属性:

<c:if test="${user.visitCount == 1}"
Welcome back!
</c:if>


当然,有IF就一定会有ELSE。如果是"if/then/else"结构的,则使用<c:choose>, <c:when>和<c:otherwise>标记。

看一看下面的例子,我们对一个查询结果集做过了一些处理,之后使用标记去显示正确的信息。

<c:choose>
<c:when test="${count == 0}">
No records matched your selection.
</c:when>
<c:otherwise>
<c:out value="${count}"/> records matched your selection.
</c:otherwise>
</c:choose>


迭代操作

在整个JSTL里面,可能最有用的操作就是迭代操作了。迭代操作的标记有<c:forEach>,<c:forToken>和<x:forEach>。最后一个是XML的迭代操作。下面我们会说到XML的操作,但现在我们还是继续核心操作。

迭代操作支持所有的J2SE集合类型,包括List, LinkedList, ArrayList, Vector, Stack, 和Set。还有java.util.Map对象,如HashMap, Hashtable, Properties, Provider, 和Attributes。还有数组。当使用基本数据类型的数组时,它会包装成对应的基本类。在迭代操作中,会输出两个东西,当前的数据项和迭代的状态。看看下面的例子:

<table>
<c:forEach var="product"
items="${products}"
varStatus="status">
<tr>
<td><c:out value="${status.count}"/></td>
<td><c:out value="${product.name}"/></td>
</tr>
</c:forEach>
</table>


例子中,products是一个集合,当前的数据项入在变量product里面,变量status是当前的迭代状态。是不是很简单。

URL操作

除了迭找操作外,核心标记库里也提供了URL相关的操作,它包括超链接,引入和重定向。可以使用<c:url>去设定一个URL。假如我们想指定一个带参数的URL,并在链接中使用,那就象下面的例子:

<c:url=http://mysite.com/register var="myUrl">
<c:param name="name" value="${param.name}"/>
</c:url>
<a href='<c:out value="${myUrl}"/>'>Register</a>


JSTL中有很强大的资源引入机制,它可以指定绝对的URL,在同一应用的相对的URL,不同应用的相对的URL,还有FTP资源。下面给出一些例子:

绝对URL:<c:import url="http://sample.com/Welcome.html"/>
同一应用中的相对的URL:<c:import url="/copyright.html"/>
不同应用中的相对的URL:<c:import url="/myLogo.html" context="/common"/>
FTP资源:<c:import url="ftp://ftp.sample.com/myFile"/>

通过上面的说明可以看出,<c:import>比<jsp:include>强大得多,但是,也有其它理由去使用<jsp:include>的。在JSTL中,对于引入的资源使用了缓存,但有时候缓存会是没用的。如果你使用<jsp:include>,资源的内容会被读入并写到当前的JspWriter,而且是每访问一次就重读一次。而使用<c:import>,资源的内容则只会读取一次。

本地化操作

JSTL的另一样重要功能就是本地化操作,通过当前请求的,或者环境配置中的参数,就可以简便地实现本地化操作。这个操作是使用J2SE中的ResourceBundle机制去存放各种译文的。JSTL通过设定的区域,去找到并使用相应的ResourceBundle。用<fmt:setLocale>去设置区域,如<fmt:setLocale value="es_Es"/>,value属性为语言代码和国家代码。也可以直接指定一个ResourceBundle:<fmt:bundle basename="ApplicationResource_fr"/>。

一旦设置了区域或者绑定特定的信息,那么<fmt:message>标记就会自动地选择正确的ResourceBundle,使用以下的形式就可以输出正确的信息:

<fmt:message key="welcome">
<fmt:param value="${visitCount}" />
<fmt:message/>


你也可以直接使用< fmt:requestEncoding/>标记去设置当前请求的字符集。

获取和显示文字信息只是本地化操作的一半,而另一半就是格式化同解释日期和数字,不同的地域会有不同的日期和数字格式的。使用<fmt:formatNumber>和<fmt:parseNumber>去格式化数字,金额,百分比数,而且还可以指定格式,就如<fmt:formatNumber value="12.3" pattern=".00"/>会输出"12.30"。

日期和时间的处理使用<fmt:formatDate>, <fmt:timeZone>, <fmt:setTimeZone>, 和 <fmt:parseDate>。

SQL操作

SQL操作允许你直接操作数据源,在MVC模式里面是不提倡这样做的,我更是彻底反对在一个正式产品里这样做。它们只适用于快速开发,原型开发或者是一些小的应用里面,它们是不应该用在一些大型的应用上面的。不过也有许多开发人员想使用它,所以它保留在标准里面。下面我们来看一下SQL操作。

SQL标记可以用来设置数据源,执行查询,访问查询结果,执行更新等。所有的SQL操作都是基于某一数据源的。有几种方式可以设置数据源:在配置文件里设置
sql.datasource参数,在程序中直接设置,或者使用<sql:setDataSource>标记。如下面就设置了一个MySql的数据源:

<sql:setDataSource var="datasource"  driver="org.gjt.mm.mysql.driver" url="jdbc:mysql://localhost/db" />


与JDBC中的DriverManager相似,<sql:setDataSource>也只是一个包装。数据源的属性,它可以是一个JNDI资源,也可以是一个JDBC参数。用<sql:query datasource="${datasource}" ... />这样的方式来访问数据。

我们把这些东西都放在一起,它是会是这样的:

<sql:query var="customer" datasource="${datasource}"
SELECT * FROM customers WHERE state = 'CO' ORDER BY city
</sql:query>
<table>
<c:forEach var="row" items="${customers.row}">
<tr>
<td><c:out value="${row.custName}" /></td>
<td><c:out value="${row.address}" /></td>
</tr>
</c:forEach>
</table>


使用事务和执行更新一样也很简单。例如,我们建立一个事务,并执行几个更新,那就是下面代码:

<sql:transaction dataSource="${dataSource}">
<sql:update>
UPDATE account SET Balance =Balance -? WHERE accountNo = ?
<sql:param value="${transferAmount}"/>
<sql:param value="${accountFrom}"/>
</sql:update>
</sql:transaction>


用<sql:dateParam>标记来设定SQL语句中的类型为日期型的参数的值。

XML操作

JSTL中最后一类操作是XML操作,XML操作也可细分成核心操作,流程控制操作和转换操作。JSTL中的XML操作是基于Xpath的,Xpath是XML操作专用的表达式语言。JSTL所有的XML操作中,用"select"属性去指定XPath表达式,这些信息将由XPath引擎解释。

XML核心操作与JSTL的核心操作相似,它包含<x:out>, <x:set>, 和<x:parse>标记。<x:parse>标记提供将XML文档转换成结构化数据的功能,之后,这些数据就能被XPath引擎解释了。例如,有一个关于书籍的XML文档,我们就可以解释它,并打印出来:

<c:import url="http://oreilly.com/book?id=1234" var="xml"/>
<x:parse source="${xml}" var="bookInfo"/>
<x:out select="$bookInfo/title"/>
<x:out select="$bookInfo/author"/>


XML类流程控制操作与核心类的流程控制操作一样,包括if, choose, when, otherwise, 和 forEach 这些标记,区别只是它们使用XPath表达式语言。当表达式计算出来后,根据一些规则,结果将转换成一个布尔值。这些规则如下:

A number is true if and only if it is neither positive or negative zero nor NaN.
一个数字当且仅当它不是一个正数,也不是一个负零,更不是一个NaN(非数字)时,表达式的值为真。
A node-set is true if and only if it is non-empty.
一个节点当且仅当它不是空节点时,表达式的值为真。
A string is true if and only if its length is non-zero.
一个字符串当且仅当它的长度不为0是,表达式的值为真。

XML转换操作,是通过XSL样式表来转换XML文档,转换的结果输出到当前页面,另一方面也可以其中的结果保存到变量中。完成一个转换就是导入一个XML文档和XSL样式表,之后做一个转换那么简单:

<c:import url="/books" var="xml"/>
<c:import url="/WEB-INF/xslt/bookDisplay.xsl" var="xslt"/>
<x:transform source="${xml}" xslt="${xslt}"/>


如果你在转换中使用到参数,你可以用<x:param>指定参数名称及参数值。

小结

现在,你已经学会了使用JSTL中的标记,这样,开发JSP会更容易更快捷。留意JSTL的发展,当JSTL的正式版本发布以后,JSP服务器的提供厂商就会对此优化。你可以在http://jakarta.apache.org/taglibs/doc/standard-doc/intro.html中得到JSTL的最后实现版本。