apache修改最大连接并用ab网站压力测试
apache 2.2,使用默认配置,默认最大连接数是150
1.首先在httpd.conf中加载httpd-mpm.conf配置(去掉前面的注释):
# Server-pool management (MPM
specific)
Include conf/extra/httpd-mpm.conf
2.可见的MPM配置在/usr/local/apache/conf/extra/httpd-mpm.conf,但里面根据httpd的工作模式分了很多块,哪一部才是当前httpd的工作模式呢?可通过执行
apachectl -l 来查看:
[root@zh888 extra]# /usr/local/apache/bin/apachectl
-l//因为采用静态编译
Compiled in
modules:
core.c
mod_authn_file.c
mod_authn_default.c
mod_authz_host.c
mod_authz_groupfile.c
mod_authz_user.c
mod_authz_default.c
mod_auth_basic.c
mod_cache.c
mod_disk_cache.c
mod_mem_cache.c
mod_include.c
mod_filter.c
mod_deflate.c
mod_log_config.c
mod_env.c
mod_expires.c
mod_headers.c
mod_setenvif.c
mod_version.c
mod_proxy.c
mod_proxy_connect.c
mod_proxy_ftp.c
mod_proxy_http.c
mod_proxy_scgi.c
mod_proxy_ajp.c
mod_proxy_balancer.c
prefork.c//才用prefork所以在httpd-mpm.conf中找到mpm_prefork_module
http_core.c
mod_mime.c
mod_status.c
mod_autoindex.c
mod_asis.c
mod_cgi.c
mod_negotiation.c
mod_dir.c
mod_actions.c
mod_userdir.c
mod_alias.c
mod_rewrite.c
mod_so.c
所以修改连接数就在/usr/local/apache/conf/extra/httpd-mpm.conf这个文件了,打开它就找到prefork模式的默认配置是:
StartServers
5
MinSpareServers 5
MaxSpareServers 10
MaxClients
150
MaxRequestsPerChild 0
prefork
控制进程在最初建立“StartServers”个子进程后,为了满足MinSpareServers设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足MinSpareServers设置的值为止。
这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。MaxSpareServers设置了最大的空闲进程数,如果空闲进程数大于这个值,Apache会自动kill掉一些多余进程。这个值不要设得过大,但如果设的值比MinSpareServers小,Apache会自动把其调整为
MinSpareServers+1。如果站点负载较大,可考虑同时加大MinSpareServers和MaxSpareServers。
MaxRequestsPerChild设置的是每个子进程可处理的请求数。每个子进程在处理了“MaxRequestsPerChild”个请求后将自动销毁。0意味着无限,即子进程永不销毁。
虽然缺省设为0可以使每个子进程处理更多的请求,但如果设成非零值也有两点重要的好处:
1、可防止意外的内存泄漏。
2、在服务器负载下降的时侯会自动减少子进程数。因此,可根据服务器的负载来调整这个值。MaxClients是这些指令中最为重要的一个,设定的是
Apache可以同时处理的请求,是对Apache性能影响最大的参数。其缺省值150是远远不够的,如果请求总数已达到这个值(可通过ps -ef|grep
httpd|wc
-l来确认),那么后面的请求就要排队,直到某个已处理请求完毕。这就是系统资源还剩下很多而HTTP访问却很慢的主要原因。虽然理论上这个值越大,可以处理的请求就越多,但Apache默认的限制不能大于256。ServerLimit指令无须重编译Apache就可以加大MaxClients。
注意,虽然通过设置ServerLimit,我们可以把MaxClients加得很大,但是往往会适得其反,系统耗光所有内存。以我手头的一台服务器为例:内存2G,每个apache进程消耗大约0.5%(可通过ps
aux来确认)的内存,也就是10M,这样,理论上这台服务器最多跑200个apache进程就会耗光系统所有内存,所以,设置MaxClients要慎重。
3.要加到多少?
连接数理论上当然是支持越大越好,但要在服务器的能力范围内,这跟服务器的CPU、内存、带宽等都有关系。
查看当前的连接数可以用:
ps aux | grep httpd | wc -l
计算httpd占用内存的平均数:
ps
aux|grep -v grep|awk '/httpd/{sum+=$6;n++};'
由于基本都是静态页面,CPU消耗很低,每进程占用内存也不算多,大约200K。
假如服务器内存有2G,除去常规启动的服务大约需要500M(保守估计),还剩1.5G可用,那么理论上可以支持1.5*1024*1024*1024/
= 8053.
约8K个进程,支持2W人同时访问应该是没有问题的(能保证其中8K的人访问很快,其他的可能需要等待1、2秒才能连上,而一旦连上就会很流畅)
控制最大连接数的MaxClients ,因此可以尝试配置为:
StartServers 5
MinSpareServers
5
MaxSpareServers 10
ServerLimit 5500
MaxClients
5000
MaxRequestsPerChild 100
注意,MaxClients默认最大为250,若要超过这个值就要显式设置ServerLimit,且ServerLimit要放在MaxClients之前,值要不小于MaxClients,不然重启httpd时会有提示。
重启httpd后,通过反复执行pgrep httpd|wc -l
来观察连接数,可以看到连接数在达到MaxClients的设值后不再增加,但此时访问网站也很流畅,那就不用贪心再设置更高的值了,不然以后如果网站访问突增不小心就会耗光服务器内存,可根据以后访问压力趋势及内存的占用变化再逐渐调整,直到找到一个最优的设置值。
(MaxRequestsPerChild不能设置为0,可能会因内存泄露导致服务器崩溃)
更佳最大值计算的公式:
apache_max_process_with_good_perfermance < (total_hardware_memory /
apache_memory_per_process ) * 2
apache_max_process =
apache_max_process_with_good_perfermance * 1.5
4.用/usr/local/apache/bin/ab来测试压力不过还有一个工具叫webbench也可以测试。
[root@zh888 bin]# /usr/local/apache/bin/ab -n 100 -c
100http://192.168.100.1:8000/index.php//参数很多一般我们用 -c 和 -n
参数就可以了这个表示同时处理100个请求并运行100次index.php文件.
This is ApacheBench Version 2.3
Copyright 1996 Adam Twiss Zeus Technology
Ltd
Licensed to The Apache Software Foundation
Benchmarking 192.168.100.1 (be patient).....done
Server Software: Apache/2.2.19//平台apache 版本2.0.54
Server Hostname: 192.168.100.1//服务器主机名
Server Port: 8000//端口
Document Path: /index.php//测试的页面文档
Document Length: bytes//文档大小
Concurrency Level: 100//并发数
Time taken for tests: 4.482 seconds//整个测试持续的时间
Complete requests: 100//完成的请求数量
Failed requests: 0//失败的请求数量
Write errors: 0
Total transferred: bytes//整个场景中的网络传输量
HTML transferred: bytes
Requests per second: 22.31 [#/sec]
(mean)//大家最关心的指标之一,相当于 LR 中的 每秒事务数 ,后面括号中的 mean 表示这是一个平均值
Time per request: 4481.929 [ms] (mean)//大家最关心的指标之二,相当于 LR 中的 平均事务响应时间 ,后面括号中的
mean 表示这是一个平均值
Time per request: 44.819 [ms] (mean across all concurrent
requests)//每个请求实际运行时间的平均值
Transfer rate: 793.68 [Kbytes/sec]
received//平均每秒网络上的流量,可以帮助排除是否存在网络流量过大导致响应时间延长的问题
Connection Times (ms)//网络上消耗的时间的分解。
min mean[+/-sd] median max
Connect: 0 73 24.5 79 96
Processing: 252
2542 1291.7 2590 4386
Waiting: 252 2541 1292.5 2590 4384
Total: 253 2615
1311.0 2671 4482
Percentage of the requests served within a certain time
(ms)//整个场景中所有请求的响应情况。在场景中每个请求都有一个响应时间,其中50%的用户响应时间小于1093 毫秒,60% 的用户响应时间小于1247
毫秒,最大的响应时间小于7785
毫秒
由于对于并发请求,cpu实际上并不是同时处理的,而是按照每个请求获得的时间片逐个轮转处理的,所以基本上第一个Time per
request时间约等于第二个Time per request时间乘以并发请求数
50% 2671
66% 3351
75% 3923
80% 4095
90% 4358
95%
4441
98% 4472
99% 4482
100% 4482 (longest request)
4.是在使用Apache2.2的ab进行测试时遇到的问题:
使用ab测试的时候当-c并发数超过1024就会出错:
windows下提示:apr_pollset_create
failed: Invalid argument (22)
linux下提示:socket: Too
many open files (24)
解决办法:
linux下:ulimit -n
(设置系统允许同时打开的文件数,系统默认是1024),可以用ulimit -a查看open files项,# lsof |wc -l
可以查看系统所有进程的文件打开数。
ulimit:显示(或设置)用户可以使用的资源限制
ulimit -a 显示用户可以使用的资源限制
ulimit unlimited
不限制用户可以使用的资源,但本设置对可打开的最大文件数(max open files)
和可同时运行的最大进程数(max user
processes)无效
ulimit -n 设置用户可以同时打开的最大文件数(max open files)
例如:ulimit -n 8192
如果本参数设置过小,对于并发访问量大的网站,可能会出现too many open files的错误
ulimit -u
设置用户可以同时运行的最大进程数(max user processes)
例如:ulimit -u 1024
5最后补充一下apache的知识:
简介
Apache
HTTP服务器被设计为一个强大的、灵活的能够在多种平台以及不同环境下工作的服务器。不同的平台和不同的环境经常产生不同的需求,或是为了达到同样的最佳效果而采用不同的方法。Apache凭借它的模块化设计很好的适应了大量不同的环境。这一设计使得网站管理员能够在编译时和运行时凭借载入不同的模块来决定服务器的不同附加功能。
Apache2.0将这种模块化的设计延伸到了web服务器的基础功能上。这个版本带有多路处理模块(MPM)的选择以处理网络端口绑定、接受请求并指派钟进程来处理这些请求。
将模块化设计延伸到这一层次主要有以下两大好处:
*
Apache可以更简洁、更有效地支持各种操作系统。尤其是在mpm_winnt中使用本地网络特性代替Apache1.3中使用的POSIX模拟层后,Windows版本的Apache现在具有更好的性能。这个优势借助特定的MPM同样延伸到了其他各种操作系统。
*
服务器可以为某些特定的站点进行定制。比如,需要更好伸缩性的站点可以选择象worker或event这样线程化的MPM,而需要更好的稳定性和兼容性以适应一些旧的软件的站点可以用prefork
。
从用户角度来看,MPM更像其他的Apache模块。主要的不同在于:不论何时,必须有且仅有一个MPM被载入到服务器中。现有的MPM列表可以在模块索引中找到。
选择一个MPM
MPM必须在编译配置时进行选择,并静态编译到服务器中。如果编译器能够确定线程功能被启用,它将会负责优化大量功能。因为一些MPM在Unix上使用了线程,而另外一些没有使用,所以如果在编译配置时选择MPM并静态编译进Apache,Apache将会有更好的表现。
你可以在使用configure脚本时用 --with-mpm=NAME 选项指定MPM,NAME就是你想使用的MPM的名称。
一旦服务器编译完成,就可以用 ./httpd -l
命令来查看使用了哪个MPM。这个命令将列出所有已经被编译到服务器中的模块,包括MPM。
我们主要阐述prefork和worker这两种和性能关系最大的产品级MPM。
Apache MPM prefork
一个非线程型的、预派生的MPM
概述
这个多路处理模块(MPM)实现了一个非线程型的、预派生的web服务器,它的工作方式类似于Apache
1.3。它适合于没有线程安全库,需要避免线程兼容性问题的系统。它是要求将每个请求相互独立的情况下最好的MPM,这样若一个请求出现问题就不会影响到其他请求。
这个MPM具有很强的自我调节能力,只需要很少的配置指令调整。最重要的是将MaxClients设置为一个足够大的数值以处理潜在的请求高峰,同时又不能太大,以致需要使用的内存超出物理内存的大小。
工作方式
一个单独的控制进程(父进程)负责产生子进程,这些子进程用于监听请求并作出应答。Apache总是试图保持一些备用的(spare)或者是空闲的子进程用于迎接即将到来的请求。这样客户端就不需要在得到服务前等候子进程的产生。
StartServers MinSpareServers MaxSpareServers
MaxClients指令用于调节父进程如何产生子进程。通常情况下Apache具有很强的自我调节能力,所以一般的网站不需要调整这些指令的默认值。可能需要处理最大超过256个并发请求的服务器可能需要增加MaxClients的值。内存比较小的机器则需要减少MaxClients的值以保证服务器不会崩溃。更多关于调整进程产生的问题请参见性能方面的提示。
在Unix系统中,父进程通常以root身份运行以便邦定80端口,而Apache产生的子进程通常以一个低特权的用户运行。User和Group指令用于设置子进程的低特权用户。运行子进程的用户必须要对它所服务的内容有读取的权限,但是对服务内容之外的其他资源必须拥有尽可能少的权限。
MaxRequestsPerChild指令控制服务器杀死旧进程产生新进程的频率。
Apache MPM worker
支持混合的多线程多进程的多路处理模块
概述
此多路处理模块(MPM)使网络服务器支持混合的多线程多进程。由于使用线程来处理请求,所以可以处理海量请求,而系统资源的开销小于基于进程的MPM。但是,它也使用了多进程,每个进程又有多个线程,以获得基于进程的MPM的稳定性。
控制这个MPM的最重要的指令是,控制每个子进程允许建立的线程数的ThreadsPerChild指令,和控制允许建立的总线程数的MaxClients指令。
工作方式
每个进程可以拥有的线程数量是固定的。服务器会根据负载情况增加或减少进程数量。一个单独的控制进程(父进程)负责子进程的建立。每个子进程可以建立ThreadsPerChild数量的服务线程和一个监听线程,该监听线程监听接入请求并将其传递给服务线程处理和应答。
Apache总是试图维持一个备用(spare)或是空闲的服务线程池。这样,客户端无须等待新线程或新进程的建立即可得到处理。初始化时建立的进程数量由StartServers指令决定。随后父进程检测所有子进程中空闲线程的总数,并新建或结束子进程使空闲线程的总数维持在MinSpareThreads和MaxSpareThreads所指定的范围内。由于这个过程是自动调整的,几乎没有必要修改这些指令的缺省值。可以并行处理的客户端的最大数量取决于MaxClients指令。活动子进程的最大数量取决于MaxClients除以ThreadsPerChild的值。
有两个指令设置了活动子进程数量和每个子进程中线程数量的硬限制。要想改变这个硬限制必须完全停止服务器然后再启动服务器(直接重启是不行的),ServerLimit是活动子进程数量的硬限制,它必须大于或等于MaxClients除以ThreadsPerChild的值。ThreadLimit是所有服务线程总数的硬限制,它必须大于或等于ThreadsPerChild指令。这两个指令必须出现在其他workerMPM指令的前面。
在设置的活动子进程数量之外,还可能有额外的子进程处于"正在中止"的状态但是其中至少有一个服务线程仍然在处理客户端请求,直到到达MaxClients以致结束进程,虽然实际数量会很小。这个行为能够通过以下禁止特别的子进程中止的方法来避免:
* 将MaxRequestsPerChild设为"0"
* 将MaxSpareThreads和MaxClients设为相同的值
一个典型的针对workerMPM的配置如下:
ServerLimit 16
StartServers 2
MaxClients
150
MinSpareThreads 25
MaxSpareThreads 75
ThreadsPerChild
25
在Unix中,为了能够绑定80端口,父进程一般都是以root身份启动,随后,Apache以较低权限的用户建立子进程和线程。User和Group指令用于设置Apache子进程的权限。虽然子进程必须对其提供的内容拥有读权限,但应该尽可能给予它较少的特权。另外,除非使用了suexec
,否则,这些指令设置的权限将被CGI脚本所继承。
MaxRequestsPerChild指令用于控制服务器建立新进程和结束旧进程的频率。
常用指令:
StartServers
指令
StartServers指令设置了服务器启动时建立的子进程数量。因为子进程数量动态的取决于负载的轻重,所有一般没有必要调整这个参数。
MinSpareServers
指令
MinSpareServers指令设置空闲子进程的最小数量。所谓空闲子进程是指没有正在处理请求的子进程。如果当前空闲子进程数少于MinSpareServers
,那么Apache将以最大每秒一个的速度产生新的子进程。
只有在非常繁忙机器上才需要调整这个参数。将此参数设的太大通常是一个坏主意。
MaxSpareServers
指令
MaxSpareServers指令设置空闲子进程的最大数量。所谓空闲子进程是指没有正在处理请求的子进程。如果当前有超过MaxSpareServers数量的空闲子进程,那么父进程将杀死多余的子进程。
只有在非常繁忙机器上才需要调整这个参数。将此参数设的太大通常是一个坏主意。如果你将该指令的值设置为比MinSpareServers小,Apache将会自动将其修改成"MinSpareServers+1"。
MaxClients
指令
MaxClients指令设置了允许同时伺服的最大接入请求数量。任何超过MaxClients限制的请求都将进入等候队列,直到达到ListenBacklog指令限制的最大值为止。一旦一个链接被释放,队列中的请求将得到服务。
对于非线程型的MPM(也就是prefork),MaxClients表示可以用于伺服客户端请求的最大子进程数量,默认值是256。要增大这个值,你必须同时增大ServerLimit
。
对于线程型或者混合型的MPM(也就是beos或worker),MaxClients表示可以用于伺服客户端请求的最大线程数量。线程型的beos的默认值是50。对于混合型的MPM默认值是16(ServerLimit)乘以25(ThreadsPerChild)的结果。因此要将MaxClients增加到超过16个进程才能提供的时候,你必须同时增加ServerLimit的值。
MaxRequestsPerChild
指令
MaxRequestsPerChild指令设置每个子进程在其生存期内允许伺服的最大请求数量。到达MaxRequestsPerChild的限制后,子进程将会结束。如果MaxRequestsPerChild为"0",子进程将永远不会结束。
不同的默认值
在mpm_netware和mpm_winnt上的默认值是"0"。
将MaxRequestsPerChild设置成非零值有两个好处:
*
可以防止(偶然的)内存泄漏无限进行,从而耗尽内存。
*
给进程一个有限兽命,从而有助于当服务器负载减轻的时候减少活动进程的数量。
注意:
对于KeepAlive链接,只有第一个请求会被计数。事实上,它改变了每个子进程限制最大链接数量的行为。
ThreadsPerChild
指令
这个指令设置了每个子进程建立的线程数。子进程在启动时建立这些线程后就不再建立新的线程了。如果使用一个类似于mpm_winnt只有一个子进程的MPM,这个数值要足够大,以便可以处理可能的请求高峰。如果使用一个类似于worker有多个子进程的MPM,每个子进程所拥有的所有线程的总数要足够大,以便可以处理可能的请求高峰。
对于mpm_winnt,ThreadsPerChild的默认值是64;对于其他MPM是25。
ThreadLimit
指令
这个指令设置了每个子进程可配置的线程数ThreadsPerChild上限。任何在重启期间对这个指令的改变都将被忽略,但对ThreadsPerChild的修改却会生效。
使用这个指令时要特别当心。如果将ThreadLimit设置成一个高出ThreadsPerChild实际需要很多的值,将会有过多的共享内存被分配。如果将ThreadLimit和ThreadsPerChild设置成超过系统的处理能力,Apache可能无法启动,或者系统将变得不稳定。该指令的值应当和ThreadsPerChild可能达到的最大值保持一致。
对于mpm_winnt,ThreadLimit的默认值是1920;对于其他MPM这个值是64。
注意:
Apache在编译时内部有一个硬性的限制"ThreadLimit
"(对于mpm_winnt是"ThreadLimit "),你不能超越这个限制。
ServerLimit
指令
对于preforkMPM,这个指令设置了MaxClients最大允许配置的数值。对于workerMPM,这个指令和ThreadLimit结合使用设置了MaxClients最大允许配置的数值。任何在重启期间对这个指令的改变都将被忽略,但对MaxClients的修改却会生效。
使用这个指令时要特别当心。如果将ServerLimit设置成一个高出实际需要许多的值,将会有过多的共享内存被分配。如果将ServerLimit和MaxClients设置成超过系统的处理能力,Apache可能无法启动,或者系统将变得不稳定。
对于preforkMPM,只有在你需要将MaxClients设置成高于默认值256的时候才需要使用这个指令。要将此指令的值保持和MaxClients一样。
对于workerMPM,只有在你需要将MaxClients和ThreadsPerChild设置成需要超过默认值16个子进程的时候才需要使用这个指令。不要将该指令的值设置的比MaxClients
和ThreadsPerChild需要的子进程数量高。
注意:
Apache在编译时内部有一个硬限制"ServerLimit
"(对于preforkMPM为"ServerLimit ")。你不能超越这个限制。
配置apache使用workerMPM:
cd httpd-2.0.55
make clean
vi server/mpm/worker/worker.c
修改define
DEFAULT_THREAD_LIMIT 64 为100
即=你要设置的ThreadsPerChild的值(修改默认ThreadsPerChild
)
修改define
DEFAULT_SERVER_LIMIT 16 为 25
即=你要设置的ServerLimit值(修改默认ServerLimit值)
:wq
./configure
--prefix=/usr/local/apache --with-mpm=worker
make
make install
cd
/usr/local/apache/conf
vi httpd.conf
修改
StartServers
2
MaxClients 150
MinSpareThreads 25
MaxSpareThreads
75
ThreadsPerChild 25
MaxRequestsPerChild 0
内容为
StartServers
3
MaxClients 2000
ServerLimit 25
MinSpareThreads 50
MaxSpareThreads
200
ThreadLimit 200
ThreadsPerChild 100
MaxRequestsPerChild 0
修改
serveradmin servername等信息为正确配置
:wq
/usr/local/apache/bin/apachectl
start
vi /etc/rc.loacl
添加 /usr/local/apache/bin/apachectl
start
PS:
用netstat -an|grep ESTABLISHED|grep 202.100.85.249:80 |wc -l
看连接数,使用worker模式后,httpd进程数变少不能反映tcp连接数
虚拟机上的linux访问本机Windows共享文件设置方法
1.安装VMtools for linux:
选择vmware
workstation程序菜单中VM > install VMware tools...
2. 进入linux
挂载vmtools 安装文件:
mount /dev/cdrom
/mnt/cdrom(vmtools的安装文件放在vmware虚拟的cdrom中,首先要mount上这个光驱才能找到安装文件)
进入/mnt/cdrom 目录,把安装文件解压到/tmp :
cd /mnt/cdrom
tar -zxvf
VMwareTools-5.0.0-12124.i386.tar.gz -C /tmp(把安装文件解压到/tmp)
执行vwware的安装脚本:
cd
/tmp/vmware-tools-distrib
./vmware-install.pl
在这里,安装程序会询问安装文件存放位置和设置分辨率等一系列问题,在大多数情况下,安装默认配置vmware
tools就可以正常工作,因此,这里对每一个问题按回车键选择默认配置。
安装完以后,vmware会添加一个vmhgfs的模块到内核中,可以使用lsmod查看.
3.设置共享文件夹:
选择vmware
workstation程序菜单中VM>Settings>Options>Shared Folders
>Properties
点击对话框右下的“add”按钮,点击“下一步”
在文本框“name”中输入共享目录的名字(这里填写的目录名以后VM的linux系统中将显示出同样的目录名),比如:win_linux_share
下一个对话框是选择共享的方式:Enable
this share是指这个共享长期有效,目录可读写;Read-only方式是指这个共享长期有效,目录只读;Disable after this
session方式是指下次ghost computer被关闭或挂起后,共享将会失效。一般情况下选择Enable this share然后点击“完成”
shared folder设置完毕
4.在VM的linux查看shared
folder目录的使用
cd /mnt/hgfs
/mnt/hgfs/目录下就同步了windows中的win_linux_share文件夹
[root@localhost network-scripts]# cd
/mnt/hgfs
[root@localhost hgfs]# ls
win_linux_share
[root@localhost
hgfs]# cd win_linux_share/
[root@localhost win_linux_share]#
ls
sunzhaoyao.txt
环境:
OS:Red Hat Linux As 5
1.服务器上创建共享目录
mkdir
doc_share
2.编辑exports文件
vim /etc/exports
写入
/doc_share
192.168.2.131/255.255.255.0(rw,sync)
格式是:
要共享的目录
共享的IP及掩码或者域名(权限,同步更新)
3.启动服务
/etc/init.d/portmap restart
/etc/init.d/nfs restart
chkconfig nfs
on
chkconfig portmap on
然后关闭防火墙以及更改Selinux关于NIS的选项
/etc/init.d/iptables stop (防护墙服务关闭)
chkconfig iptables off
system-config-selinux (设置selinux)
查看共享的东西
[root@rac1
/]# exportfs -rv
exporting
192.168.2.131/255.255.255.0:/doc_share
试着在本机看能否加载
mount
192.168.2.131:/doc_share /mnt
[root@rac1 doc_share]# echo
aa>aa.txt
[root@rac1 doc_share]# ls
aa.txt
[root@rac1 /]# cd
/mnt
[root@rac1 mnt]# ls
aa.txt
4.客户端
手工mount:
mount -o nolock 192.168.2.131:/doc_share
/mnt
这个时候可以看到在节点1上内容了.
[root@rac2
mnt]# cd /mnt
[root@rac2 mnt]# ls
aa.txt
自动mount:
编辑fstab文件,实现开机自动挂载
mount -t nfs IP:/目录 挂载到的目录
(此为临时挂载)
如:mount -t nfs
192.168.0.9:/doce /doc
vim /etc/fstab 添加如下内容
192.168.2.131:/doc_share /mnt nfs
defaults 0 0
相关的一些命令:
showmout命令对于NFS的操作和查错有很大的帮助.
showmout
-a:这个参数是一般在NFS SERVER上使用,是用来显示已经mount上本机nfs目录的cline机器.
-e:显示指定的NFS
SERVER上export出来的目录.
例如:
showmount -e 192.168.0.30
Export list for localhost:
/tmp *
/home/linux *.linux.org
/home/public (everyone)
/home/test 192.168.0.100
exportfs命令:
如果我们在启动了NFS之后又修改了/etc/exports,是不是还要重新启动nfs呢?这个时候我们就可以用exportfs命令来使改动立刻生效,该命令格式如下:
exportfs
[-aruv]
-a :全部mount或者unmount /etc/exports中的内容
-r :重新mount
/etc/exports中分享出来的目录
-u :umount 目录
-v :在 export
的时候,将详细的信息输出到屏幕上.
具体例子:
[root @test root]# exportfs
-rv <==全部重新 export 一次!
exporting
192.168.0.100:/home/test
exporting 192.168.0.*:/home/public
exporting
*.the9.com:/home/linux
exporting *:/home/public
exporting *:/tmp
reexporting 192.168.0.100:/home/test to kernel
exportfs -au
<==全部都卸载了
-------------------------------------------------------------------------------
今天在机器上配置NFS文件系统,在/etc/exports中加入以下信息:
/testfs 10.0.0.0/8(rw)
重启NFS服务以后,在客户机通过mount -o rw -t nfs 10.214.54.29:/testfs /rd1命令将网络文件mount到本地。执行完成之后,目录是可以访问了,但无法写入。感觉有点奇怪,明明在命令中指定可以写入了。于是到网上搜索资料,发现exports目录权限中,有这么一个参数no_root_squash。其作用是:登入 NFS 主机使用分享目录的使用者,如果是 root 的话,那么对于这个分享的目录来说,他就具有 root 的权限!。默认情况使用的是相反参数 root_squash:在登入 NFS 主机使用分享之目录的使用者如果是 root 时,那么这个使用者的权限将被压缩成为匿名使用者,通常他的 UID 与 GID 都会变成 nobody 那个身份。
因为我的客户端是使用root登录的,自然权限被压缩为nobody了,难怪无法写入。将配置信息改为:
/testfs 10.0.0.0/8(rw,no_root_squash)
据说有点不安全,但问题是解决了。
另外,在测试NFS文件系统时,会经常mount和umount文件,但有时会出现device is busy的错误提示。你肯定感到很奇怪,我明明没有使用啊,看看你当前所在的目录,是不是在mount的文件目录中?回退到上层目录重新umount,是不是OK了?
linux中,weblogic上传的文件或者创建的目录,默认只有自己读或者组员读,但web访问目录时,通常需要执行权限,所以需要在weblogic启动脚本配置,找到startWeblogic.sh,vi它,增加umask 027,则组员和自己都有r+x权限了。
ichartjs是一款基于HTML5的图形库。使用纯javascript语言,利用HTML5的canvas标签绘制各式图形。ichartjs可以为web应用提供简单、直观、可交互的体验级图表组件。是web图表方面的解决方案。最近正好在学HTML5,顺便就用ichartjs来练习。ichartjs目前支持饼图、折线图、区域图、柱形图、条形图。ichartjs是基于Apache License 2.0 协议的开源项目。今天介绍的是如何在android手机上动态实现3D柱形图。若想详细了解ichartjs,可以访问ichartjs官网:http://www.ichartjs.cn/index.html
实现主要原理是所需实现的数据打包成json格式,因为ichartjs规定的数据源统一采用json对象方式。数据源分为单一数据源与集合多值数据源,单一数据源的值为单一的数值,而集合多值数据源为数值集合。3D柱形图使用的单一的数据源。废话不多说了,直接上代码。
首先编写的是封装数据的实体类Contact:
- package com.chinasofti.html;
-
- public class Contact {
- private String name;
- private double value;
- private String color;
-
-
-
-
-
-
-
- public Contact(String name, double value, String color) {
- this.name = name;
- this.value = value;
- this.color = color;
- }
-
-
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public double getValue() {
- return value;
- }
- public void setValue(double value) {
- this.value = value;
- }
- public String getColor() {
- return color;
- }
- public void setColor(String color) {
- this.color = color;
- }
-
- }
package com.chinasofti.html;
public class Contact {
private String name; // 浏览器的名称
private double value; // 浏览器对应的所占市场份额值
private String color; // 在柱形图中所显示的颜色
/**
* 构造函数
* @param name 浏览器的名称
* @param value 浏览器对应的所占市场份额值
* @param color 在柱形图中所显示的颜色
*/
public Contact(String name, double value, String color) {
this.name = name;
this.value = value;
this.color = color;
}
// 下面是三个实例变量的getters and setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getValue() {
return value;
}
public void setValue(double value) {
this.value = value;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
接着创建一个list将所需要的contact对象添加到list中:
- import java.util.ArrayList;
- import java.util.List;
-
- import com.chinasofti.html.Contact;
-
- public class ContactService {
-
- public List<Contact> getContacts() {
- List<Contact> contacts = new ArrayList<Contact>();
- contacts.add(new Contact("IE", 32.85, "#a5c2d5"));
- contacts.add(new Contact("Chrome", 33.59, "#cbab4f"));
- contacts.add(new Contact("Firefox", 22.85, "#76a871"));
- contacts.add(new Contact("Safari", 7.39, "#9f7961"));
- contacts.add(new Contact("Opera", 1.63, "#a56f8f"));
- contacts.add(new Contact("Other", 1.69, "#6f83a5"));
- return contacts;
- }
- }
import java.util.ArrayList;
import java.util.List;
import com.chinasofti.html.Contact;
public class ContactService {
public List<Contact> getContacts() {
List<Contact> contacts = new ArrayList<Contact>();
contacts.add(new Contact("IE", 32.85, "#a5c2d5"));
contacts.add(new Contact("Chrome", 33.59, "#cbab4f"));
contacts.add(new Contact("Firefox", 22.85, "#76a871"));
contacts.add(new Contact("Safari", 7.39, "#9f7961"));
contacts.add(new Contact("Opera", 1.63, "#a56f8f"));
contacts.add(new Contact("Other", 1.69, "#6f83a5"));
return contacts;
}
}
然后编写android主界面代码,实现list转换成json格式字符串,并实现和html文件的交互:
- import java.util.List;
-
- import org.json.JSONArray;
- import org.json.JSONException;
- import org.json.JSONObject;
-
- import android.app.Activity;
- import android.os.Bundle;
- import android.util.Log;
- import android.webkit.WebView;
-
- public class MainActivity extends Activity {
- private static final String TAG = "MainActivity";
- private ContactService contactService;
- private WebView webView;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
-
- contactService = new ContactService();
- webView = (WebView) this.findViewById(R.id.webView);
- webView.getSettings().setJavaScriptEnabled(true);
- webView.getSettings().setBuiltInZoomControls(true);
-
- webView.addJavascriptInterface(this,TAG);
- webView.loadUrl("file:///android_asset/3dchart.html");
- }
-
-
-
-
-
- public String getContacts() {
- List<Contact> contacts = contactService.getContacts();
- String json = null;
- try {
- JSONArray array = new JSONArray();
- for (Contact contact : contacts) {
-
- JSONObject item = new JSONObject();
- item.put("name", contact.getName());
- item.put("value", contact.getValue());
- item.put("color", contact.getColor());
- array.put(item);
- }
- json = array.toString();
- Log.i(TAG, json);
-
- } catch (JSONException e) {
- e.printStackTrace();
- }
- return json;
- }
- }
import java.util.List;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.webkit.WebView;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private ContactService contactService; // 构建list的类
private WebView webView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
contactService = new ContactService();
webView = (WebView) this.findViewById(R.id.webView);
webView.getSettings().setJavaScriptEnabled(true); // 允许使用javascript脚本语言
webView.getSettings().setBuiltInZoomControls(true); // 设置可以缩放
// 设置javaScript可用于操作MainActivity类
webView.addJavascriptInterface(this,TAG);
webView.loadUrl("file:///android_asset/3dchart.html");
}
/**
* 实现将list转换成json格式字符串
* @return json格式的字符串
*/
public String getContacts() {
List<Contact> contacts = contactService.getContacts();
String json = null;
try {
JSONArray array = new JSONArray();
for (Contact contact : contacts) {
JSONObject item = new JSONObject();
item.put("name", contact.getName());
item.put("value", contact.getValue());
item.put("color", contact.getColor());
array.put(item);
}
json = array.toString();
Log.i(TAG, json);
// webView.loadUrl("javascript:show('" + json + "')");
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
}
最后是编辑html文件。要实现ichartjs表图,首先要保证在assets目录下已导入了ichart - 1.0.js。然后对html文件进行编辑:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8" />
- <title>Hello World</title>
- <meta name="Description" content="" />
- <meta name="Keywords" content="" />
- <script type="text/javascript" src="ichart-1.0.js"></script>
- <script type="text/javascript">
- var data = new Array();
- var contact = window.MainActivity.getContacts(); //得到MainActivity中转换出的json字符串
- eval('data='+contact); //得到json数据
-
- $(function(){
- new iChart.Column3D({
- render : 'canvasDiv', //渲染的Dom目标,canvasDiv为Dom的ID
- data: data, //绑定数据
- title : 'Top 5 Browsers in August 2012', //设置标题
- showpercent:true, //显示百分比
- decimalsnum:2,
- width : 800, //设置宽度,默认单位为px
- height : 400, //设置高度,默认单位为px
- align:'left',
- offsetx:50,
- legend : {
- enable : true
- },
- coordinate:{ //配置自定义坐标轴
- scale:[{ //配置自定义值轴
- width:600,
- position:'left', //配置左值轴
- start_scale:0, //设置开始刻度为0
- end_scale:40, //设置结束刻度为40
- scale_space:8, //设置刻度间距为8
- listeners:{ //配置事件
- parseText:function(t,x,y){ //设置解析值轴文本
- return {text:t+"%"}
- }
- }
- }]
- }
- }).draw(); //调用绘图方法开始绘图
- });
- </script>
- </head>
- <body>
- <div id='canvasDiv'></div>
- </body>
- </html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World</title>
<meta name="Description" content="" />
<meta name="Keywords" content="" />
<script type="text/javascript" src="ichart-1.0.js"></script>
<script type="text/javascript">
var data = new Array();
var contact = window.MainActivity.getContacts(); //得到MainActivity中转换出的json字符串
eval('data='+contact); //得到json数据
$(function(){
new iChart.Column3D({
render : 'canvasDiv', //渲染的Dom目标,canvasDiv为Dom的ID
data: data, //绑定数据
title : 'Top 5 Browsers in August 2012', //设置标题
showpercent:true, //显示百分比
decimalsnum:2,
width : 800, //设置宽度,默认单位为px
height : 400, //设置高度,默认单位为px
align:'left',
offsetx:50,
legend : {
enable : true
},
coordinate:{ //配置自定义坐标轴
scale:[{ //配置自定义值轴
width:600,
position:'left', //配置左值轴
start_scale:0, //设置开始刻度为0
end_scale:40, //设置结束刻度为40
scale_space:8, //设置刻度间距为8
listeners:{ //配置事件
parseText:function(t,x,y){ //设置解析值轴文本
return {text:t+"%"}
}
}
}]
}
}).draw(); //调用绘图方法开始绘图
});
</script>
</head>
<body>
<div id='canvasDiv'></div>
</body>
</html>
最后得到效果为:

函数trunc是一个Oracle内置的函数,可以对date类型数据进行“度身裁剪”,来适应不同类型的数据需求。
在前篇《Oracle日期类型操作几个问题》中,我们已经了解到date类型的基本知识。date类型是一种包括年、月、日、时、分和秒的数据类型,可以表示相对精确的时间信息。内部存储上,date类型是类似于数字类型的,可以通过加减操作实现对日期的推进和后退。
但是,日期格式的精确常常给我们带来一些困扰,特别是其中的时分秒信息。很多时候,我们对这部分信息是不需要的。比如指定日期查询、只显示天信息等等。借助To_char虽然可以实现一部分这种需要,但是这样做格式上比较复杂,而且进行了数据类型的转换。是否存在不变化数据类型的方法,对日期型数据进行处理。答案就是trunc函数。
trunc(date)
截断函数trunc的作用就是将日期类型数据按照指定格式截断,返回一个日期变量数据。例如:
SQL> select to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual;
TO_CHAR(SYSDATE,'YYYY-MM-DDHH2
------------------------------
2010-12-10 20:39:58
SQL> select trunc(sysdate) from dual;
TRUNC(SYSDATE)
--------------
2010-12-10
SQL> select to_char(trunc(sysdate),'yyyy-mm-dd hh24:mi:ss') from dual;
TO_CHAR(TRUNC(SYSDATE),'YYYY-M
------------------------------
2010-12-10 00:00:00
默认情况下,sysdate函数返回的日期类型数据包括当前的具体时间。通过trunc(date)的处理,直接截取到天信息,返回指定天的零时。
trunc函数还支持一个重载参数,格式字符串:trunc(date,‘format’),用于指定截断的位置。如下:
//截断到年
SQL> select trunc(sysdate,'yyyy') from dual;
TRUNC(SYSDATE,'YYYY')
---------------------
2010-1-1
//截断到月
SQL> select trunc(sysdate,'mm') from dual;
TRUNC(SYSDATE,'MM')
-------------------
2010-12-1
//截断到日
SQL> select trunc(sysdate,'dd') from dual;
TRUNC(SYSDATE,'DD')
-------------------
2010-12-10
//截断到小时
SQL> select trunc(sysdate,'hh24') from dual;
TRUNC(SYSDATE,'HH24')
---------------------
2010-12-10 20:00:00
//截断到分钟
SQL> select trunc(sysdate,'mi') from dual;
TRUNC(SYSDATE,'MI')
-------------------
2010-12-10 20:52:00
使用不同的格式标志,可以指示不同的截断位置,获取各种零刻时间。
|
格式字符串 |
说明 |
年 |
yyyy或者year |
年度第一天(一月一日零时) |
月 |
mm或者month |
月份第一天(一日零时) |
日 |
dd或者day |
日期零时(00:00:00) |
小时 |
hh或者hh24 |
几时整(XX:00:00) |
分 |
mi |
几分整(XX:XX:00) |
借助trunc函数和日期类型加减处理,我们可以实现一些特殊日期的设置,实现日期功能,使用在例如Job调度方面。
//明天零点
SQL> select to_char(trunc(sysdate)+1,'yyyy-mm-dd hh24:mi:ss') from dual;
TO_CHAR(TRUNC(SYSDATE)+1,'YYYY
------------------------------
2010-12-11 00:00:00
//当天早上十点
SQL> select to_char(trunc(sysdate)+10/24,'yyyy-mm-dd hh24:mi:ss') from dual;
TO_CHAR(TRUNC(SYSDATE)+10/24,'
------------------------------
2010-12-10 10:00:00
//当月三号,上午10点半
SQL> select to_char(trunc(sysdate,'mm')+2+10/24+1/48, 'yyyy-mm-dd hh24:mi:ss') from dual;
注:trunc(sysdate,'mm')将时间取到当前月的1号零时零分零秒,那么加上2就表示当前月的3好零时零分零秒,再加上'10/24'('10/24'本身表示10个小时)就是表示当月3号的十点零分零秒,再加上'1/48'('1/48'本身表示30分钟)就表示当月3号十点三十分零秒
TO_CHAR(TRUNC(SYSDATE,'MM')+2+
------------------------------
2010-12-03 10:30:00
较复杂的to_char,trunc更加可以发挥日期类型数值本身的特色和优势,无论是代码整洁度还是处理效率都是值得关注的。
额外多说一句,trunc本身还具有处理数字截断功能,用于截断指定位数的数字类型。
//默认截断到整数,不进行四舍五入;
SQL> select trunc(15.743) from dual;
TRUNC(15.743)
-------------
15
//截断到小数点后一位;
SQL> select trunc(15.793,1) from dual;
TRUNC(15.793,1)
---------------
15.7
//截断到小数点前一位;
SQL> select trunc(15.793,-1) from dual;
TRUNC(15.793,-1)
----------------
10
trunc对数字和日期类型处理,也折射出日期类型数据和数字之间的间接关系。
原文地址:http://space.itpub.net/17203031/viewspace-681548
trunc不仅可以用来处理日期,还可以用来处理数字
TRUNC(i1,i2)截取i1的i2位而不四舍五入,如果i2是正就截取小数点右边第几位,如果是i2是负就是截取小数点左边第几位。
例如:
- select TRUNC(0.10005767,1) from dual;
-
- TRUNC(0.10005767,1) 1 0.1
而如果trunc函数没有指定参i2,那么其作用为取整,且取整的时候不会四舍五入
例如:
- select TRUNC(2.60005767) from dual;
-
TRUNC(2.60005767)
round函数和trunc函数的区别:
ROUND(i1,i2)四舍五入,i1四舍五入,如果i2是正保留小数点后i2位。如果是i2是负数,表示保留小数点前面(左边第几位)
TRUNC(i1,i2)截取i1的i2位而不四舍五入,如果i2是正就截取小数点右边第几位,如果是i2是负就是截取小数点左边第几位。
Quartz2.1与1.X有了部分变化,写下配置过程供同好者参考!
1、下载quartz
http://www.terracotta.org/download/reflector.jsp?b=tcdistributions&i=quartz-2.1.5.tar.gz将slf4j-log4j12-1.6.1.jar
slf4j-api-1.6.1.jar
quartz-all-2.1.5.jar
放入WEB-INF\lib下
2、编写自己的job
3、编写配置文件
quartz.properties:
quartz_job.xml:
web.xml:加入一个servlet,主要quartz.properties文件的位置,我是跟我的quartz_job.xml文件放在一起
4、启动应用服务器,一切ok
5、关于触发器的时间控制说明:
先说干货:quartz_job.xml中的
<trigger>
<cron>
<name>report-trigger</name>
<group>Report_Group</group>
<job-name>ReportControlScheduler</job-name>
<job-group>Report</job-group>
<cron-expression>0 0/3 * * * ?</cron-expression>
</cron>
</trigger>
就是对触发时间的描述,我这里采用的是CronTrigger的方式。下边详细描述了时间的控制。
Trigger是一个抽象类,它有三个子类:SimpleTrigger,CronTrigger和NthIncludedDayTrigger。前两个比较常用。
1。SimpleTrigger:这是一个非常简单的类,我们可以定义作业的触发时间,并选择性的设定重复间隔和重复次数。
2。CronTrigger:这个触发器的功能比较强大,而且非常灵活,但是你需要掌握有关Cron表达式的知识。如果你是一个Unix系统爱好者,你很可能已经具备这种知识,但是如果你不了解Cron表达式,请看下面的Cron详解:
Cron表达式由6或7个由空格分隔的时间字段组成,如表1所示: 表1 Cron表达式时间字段
位置 |
时间域名 |
允许值 |
允许的特殊字符 |
1 |
秒 |
0-59 |
, - * / |
2 |
分钟 |
0-59 |
, - * / |
3 |
小时 |
0-23 |
, - * / |
4 |
日期 |
1-31 |
, - * ? / L W C |
5 |
月份 |
1-12 |
, - * / |
6 |
星期 |
1-7 |
, - * ? / L C # |
7 |
年(可选) |
空值1970-2099 |
, - * / |
Cron表达式的时间字段除允许设置数值外,还可使用一些特殊的字符,提供列表、范围、通配符等功能,细说如下:
●星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如,*在分钟字段时,表示“每分钟”;
●问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
●减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
●逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
●斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
●L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如,6L表示该月的最后星期五;
●W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
●LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
●井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
● C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。表2下面给出一些完整的Cron表示式的实例:
表2 Cron表示式示例
表示式 |
说明 |
"0 0 12 * * ? " |
每天12点运行 |
"0 15 10 ? * *" |
每天10:15运行 |
"0 15 10 * * ?" |
每天10:15运行 |
"0 15 10 * * ? *" |
每天10:15运行 |
"0 15 10 * * ? 2008" |
在2008年的每天10:15运行 |
"0 * 14 * * ?" |
每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。 |
"0 0/5 14 * * ?" |
每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。 |
"0 0/5 14,18 * * ?" |
每天14点到15点每5分钟运行一次,此外每天18点到19点每5钟也运行一次。 |
"0 0-5 14 * * ?" |
每天14:00点到14:05,每分钟运行一次。 |
"0 10,44 14 ? 3 WED" |
3月每周三的14:10分到14:44,每分钟运行一次。 |
"0 15 10 ? * MON-FRI" |
每周一,二,三,四,五的10:15分运行。 |
"0 15 10 15 * ?" |
每月15日10:15分运行。 |
"0 15 10 L * ?" |
每月最后一天10:15分运行。 |
"0 15 10 ? * 6L" |
每月最后一个星期五10:15分运行。 |
"0 15 10 ? * 6L 2007-2009" |
在2007,2008,2009年每个月的最后一个星期五的10:15分运行。 |
"0 15 10 ? * 6#3" |
每月第三个星期五的10:15分运行。 |
LoginAny 使用笔记
想实现在家办公,当公司有急事的时候,可以在家就处理掉;不必在家里和公司之间copy文件,免去劳苦奔波之苦。于是开始用远程软件。
1. VNN. 免费,主要面向游戏平台。
申请2个用户,互相加为密友,能够2台机器互访,但是只有vnnc302201-winall.zip版本能用(密友功能),且不能升级,一旦升级之后,将没有了密友功能。
用了一段时间,很不错。但不久之后,本地域内3389端口封了。理解,因为远程桌面的3389是个不安全的端口。
其实,把被控机器的Terminal Service 3389端口改掉, 理论上也是可行的,但是还是比较麻烦。
2. Hamachi, 很好用的的软件。推荐,IP局域网穿透。 3389端口还是不能连接,道理同上。
3. 改用LoginAny. 免费版每月只能远程桌面20分钟,文件传输3次。速度超快。 远程桌面是LoginAny开发的,所以不再用3389端口。
自己研究下能否逆向工程下…
———先看文件传输功能———–
打开eXeScope分析资源,首先查看文字:"文件传输已经达到最大使用次数!",String Id: 484
得知Dialog: 1218是提示对话框, Dialog: 1219是文件传输Form.
打开OllyICE,反编译后,
- 搜索4C2(1218), 找提示对话框的代码,自己标注附近的代码,这是一个功能函数。
搜索4C3(1219), 找文件传输Form相关代码。
搜索1E4(484), 找"文件传输已经达到最大使用次数!"的相关代码。
004938D0 /$ 55 push ebp
004938D1 |. 8BEC mov ebp, esp
004938D3 |. 6A FF push -1
004938D5 |. 68 87C25B00 push 005BC287 ; SE 处理程序安装
004938DA |. 64:A1 0000000>mov eax, dword ptr fs:[0]
004938E0 |. 50 push eax
004938E1 |. 64:8925 00000>mov dword ptr fs:[0], esp
004938E8 |. 81EC BC000000 sub esp, 0BC
004938EE |. A1 BCEB6200 mov eax, dword ptr [62EBBC]
004938F3 |. 33C5 xor eax, ebp
004938F5 |. 8945 EC mov dword ptr [ebp-14], eax
004938F8 |. 898D 40FFFFFF mov dword ptr [ebp-C0], ecx
004938FE |. C785 4CFFFFFF>mov dword ptr [ebp-B4], 1
00493908 |. 6A 01 push 1
0049390A |. 8B85 4CFFFFFF mov eax, dword ptr [ebp-B4]
00493910 |. 50 push eax
00493911 |. 8B8D 40FFFFFF mov ecx, dword ptr [ebp-C0]
00493917 |. E8 D4EFFFFF call 004928F0 ; 关键Call !!!!
0049391C |. 85C0 test eax, eax
0049391E |. 75 05 jnz short 00493925
00493920 |. E9 F8000000 jmp 00493A1D
00493925 |> C785 48FFFFFF>mov dword ptr [ebp-B8], 0
0049392F |. 8D4D F0 lea ecx, dword ptr [ebp-10]
00493932 |. FF15 28B95C00 call dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTrai>; MFC71.7C173199
00493938 |. C745 FC 00000>mov dword ptr [ebp-4], 0
0049393F |. 6A 00 push 0
00493941 |. 8D8D 54FFFFFF lea ecx, dword ptr [ebp-AC]
00493947 |. E8 64D30800 call 00520CB0
0049394C |. C645 FC 01 mov byte ptr [ebp-4], 1
00493950 |. 8B8D 4CFFFFFF mov ecx, dword ptr [ebp-B4]
00493956 |. 51 push ecx
00493957 |. 68 DD000000 push 0DD
0049395C |. 8D55 F0 lea edx, dword ptr [ebp-10]
0049395F |. 52 push edx
00493960 |. 8D85 54FFFFFF lea eax, dword ptr [ebp-AC]
00493966 |. 50 push eax
00493967 |. 8D8D 48FFFFFF lea ecx, dword ptr [ebp-B8]
0049396D |. 51 push ecx
0049396E |. 8B8D 40FFFFFF mov ecx, dword ptr [ebp-C0]
00493974 |. E8 A7190000 call 00495320 ; MessageBox ….
00493979 |. 85C0 test eax, eax
0049397B |. 75 21 jnz short 0049399E
0049397D |. C645 FC 00 mov byte ptr [ebp-4], 0
00493981 |. 8D8D 54FFFFFF lea ecx, dword ptr [ebp-AC]
00493987 |. E8 24D40800 call 00520DB0
0049398C |. C745 FC FFFFF>mov dword ptr [ebp-4], -1
00493993 |. 8D4D F0 lea ecx, dword ptr [ebp-10]
00493996 |. FF15 68B95C00 call dword ptr [<&MFC71.#578_ATL::CStringT<char,StrTrai>; MFC71.7C1771B1
0049399C |. EB 7F jmp short 00493A1D
0049399E |> 8B95 48FFFFFF mov edx, dword ptr [ebp-B8]
004939A4 |. 52 push edx
004939A5 |. 68 2CF16200 push 0062F12C
004939AA |. 51 push ecx
004939AB |. 8BCC mov ecx, esp
004939AD |. 89A5 44FFFFFF mov dword ptr [ebp-BC], esp
004939B3 |. 8D45 F0 lea eax, dword ptr [ebp-10]
004939B6 |. 50 push eax
004939B7 |. FF15 38B95C00 call dword ptr [<&MFC71.#297_ATL::CStringT<char,StrTrai>; MFC71.7C14E575
004939BD |. 8985 3CFFFFFF mov dword ptr [ebp-C4], eax
004939C3 |. 8D8D 54FFFFFF lea ecx, dword ptr [ebp-AC]
004939C9 |. 51 push ecx
004939CA |. B9 E4F26200 mov ecx, 0062F2E4
004939CF |. E8 ECDAFCFF call 004614C0 ; 调用打开文件传输Form
004939D4 |. 8985 38FFFFFF mov dword ptr [ebp-C8], eax
004939DA |. 8B95 38FFFFFF mov edx, dword ptr [ebp-C8]
004939E0 |. 8995 50FFFFFF mov dword ptr [ebp-B0], edx
004939E6 |. 6A 05 push 5
004939E8 |. 8B8D 50FFFFFF mov ecx, dword ptr [ebp-B0]
004939EE |. E8 6DCC1100 call <jmp.&MFC71.#6090_CWnd::ShowWindow>
004939F3 |. 8B8D 50FFFFFF mov ecx, dword ptr [ebp-B0]
004939F9 |. E8 12D8FAFF call 00441210
004939FE |. C645 FC 00 mov byte ptr [ebp-4], 0
00493A02 |. 8D8D 54FFFFFF lea ecx, dword ptr [ebp-AC]
00493A08 |. E8 A3D30800 call 00520DB0
00493A0D |. C745 FC FFFFF>mov dword ptr [ebp-4], -1
00493A14 |. 8D4D F0 lea ecx, dword ptr [ebp-10]
00493A17 |. FF15 68B95C00 call dword ptr [<&MFC71.#578_ATL::CStringT<char,StrTrai>; MFC71.7C1771B1
00493A1D |> 8B4D F4 mov ecx, dword ptr [ebp-C]
00493A20 |. 64:890D 00000>mov dword ptr fs:[0], ecx
00493A27 |. 8B4D EC mov ecx, dword ptr [ebp-14]
00493A2A |. 33CD xor ecx, ebp
00493A2C |. E8 5ADE1100 call 005B188B
00493A31 |. 8BE5 mov esp, ebp
00493A33 |. 5D pop ebp
00493A34 \. C3 retn
在0049 3917发现关键Call.
决定修改其后的跳转,
00493920 |. E9 F8000000 jmp 00493A1D 这一行是跳过调用打开文件传输Form的代码。
把它改为:
00493920 |. 90 90909090 Nop 什么也不做
经试验,文件传输功能可以超过3次的使用了。
———远程桌面的功能———-
远程桌面的功能只能连接20分钟。 解决办法还是老一套:
打开eXeScope分析资源,找到对话框:远程桌面,ID=1306.
打开OllyICE,搜索常量1306,很快定位下面代码:
00493670 /$ 55 push ebp
00493671 |. 8BEC mov ebp, esp
00493673 |. 6A FF push -1
00493675 |. 68 69C25B00 push 005BC269 ; SE 处理程序安装
0049367A |. 64:A1 0000000>mov eax, dword ptr fs:[0]
00493680 |. 50 push eax
00493681 |. 64:8925 00000>mov dword ptr fs:[0], esp
00493688 |. 81EC D4000000 sub esp, 0D4
0049368E |. A1 BCEB6200 mov eax, dword ptr [62EBBC]
00493693 |. 33C5 xor eax, ebp
00493695 |. 8945 EC mov dword ptr [ebp-14], eax
00493698 |. 898D 30FFFFFF mov dword ptr [ebp-D0], ecx
0049369E |. C785 50FFFFFF>mov dword ptr [ebp-B0], 0
004936A8 |. 6A 01 push 1
004936AA |. 8B85 50FFFFFF mov eax, dword ptr [ebp-B0]
004936B0 |. 50 push eax
004936B1 |. 8B8D 30FFFFFF mov ecx, dword ptr [ebp-D0]
004936B7 |. E8 34F2FFFF call 004928F0 ; 关键Call–remote desk.
004936BC |. 85C0 test eax, eax
004936BE |. 75 05 jnz short 004936C5
004936C0 |. E9 F0010000 jmp 004938B5
004936C5 |> C785 4CFFFFFF>mov dword ptr [ebp-B4], 0
004936CF |. 6A 00 push 0
004936D1 |. 8D8D 54FFFFFF lea ecx, dword ptr [ebp-AC]
004936D7 |. E8 D4D50800 call 00520CB0
004936DC |. C745 FC 00000>mov dword ptr [ebp-4], 0
004936E3 |. 8D4D F0 lea ecx, dword ptr [ebp-10]
004936E6 |. FF15 28B95C00 call dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTrai>; MFC71.7C173199
004936EC |. C645 FC 01 mov byte ptr [ebp-4], 1
004936F0 |. 8B8D 50FFFFFF mov ecx, dword ptr [ebp-B0]
004936F6 |. 51 push ecx
004936F7 |. 68 19010000 push 119
004936FC |. 8D55 F0 lea edx, dword ptr [ebp-10]
004936FF |. 52 push edx
00493700 |. 8D85 54FFFFFF lea eax, dword ptr [ebp-AC]
00493706 |. 50 push eax
00493707 |. 8D8D 4CFFFFFF lea ecx, dword ptr [ebp-B4]
0049370D |. 51 push ecx
0049370E |. 8B8D 30FFFFFF mov ecx, dword ptr [ebp-D0]
00493714 |. E8 071C0000 call 00495320 ; 消息处理
00493719 |. 85C0 test eax, eax
0049371B |. 75 24 jnz short 00493741
0049371D |. C645 FC 00 mov byte ptr [ebp-4], 0
00493721 |. 8D4D F0 lea ecx, dword ptr [ebp-10]
00493724 |. FF15 68B95C00 call dword ptr [<&MFC71.#578_ATL::CStringT<char,StrTrai>; MFC71.7C1771B1
0049372A |. C745 FC FFFFF>mov dword ptr [ebp-4], -1
00493731 |. 8D8D 54FFFFFF lea ecx, dword ptr [ebp-AC]
00493737 |. E8 74D60800 call 00520DB0 ; 字符处理
0049373C |. E9 74010000 jmp 004938B5
00493741 |> 817D A4 01030>cmp dword ptr [ebp-5C], 90301
00493748 |. 0F83 E8000000 jnb 00493836
0049374E |. 8B95 4CFFFFFF mov edx, dword ptr [ebp-B4]
00493754 |. 52 push edx
00493755 |. 6A 00 push 0
00493757 |. 51 push ecx
00493758 |. 8BCC mov ecx, esp
0049375A |. 89A5 38FFFFFF mov dword ptr [ebp-C8], esp
00493760 |. 8D45 F0 lea eax, dword ptr [ebp-10]
00493763 |. 50 push eax
00493764 |. FF15 38B95C00 call dword ptr [<&MFC71.#297_ATL::CStringT<char,StrTrai>; MFC71.7C14E575
0049376A |. 8985 2CFFFFFF mov dword ptr [ebp-D4], eax
00493770 |. 8D8D 54FFFFFF lea ecx, dword ptr [ebp-AC]
00493776 |. 51 push ecx
00493777 |. B9 E4F26200 mov ecx, 0062F2E4
0049377C |. E8 BFD7FCFF call 00460F40 ; ???? XX new opeator
00493781 |. 8985 28FFFFFF mov dword ptr [ebp-D8], eax
00493787 |. 8B95 28FFFFFF mov edx, dword ptr [ebp-D8]
0049378D |. 8995 48FFFFFF mov dword ptr [ebp-B8], edx
00493793 |. 83BD 48FFFFFF>cmp dword ptr [ebp-B8], 0
0049379A |. 0F85 94000000 jnz 00493834
004937A0 |. 8D8D 40FFFFFF lea ecx, dword ptr [ebp-C0]
004937A6 |. FF15 28B95C00 call dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTrai>; MFC71.7C173199
004937AC |. C645 FC 02 mov byte ptr [ebp-4], 2
004937B0 |. 8D8D 44FFFFFF lea ecx, dword ptr [ebp-BC]
004937B6 |. FF15 28B95C00 call dword ptr [<&MFC71.#310_ATL::CStringT<char,StrTrai>; MFC71.7C173199
004937BC |. C645 FC 03 mov byte ptr [ebp-4], 3
004937C0 |. FF15 90AA5C00 call dword ptr [<&KERNEL32.GetLastError>] ; [GetLastError
004937C6 |. 50 push eax
004937C7 |. 68 42010000 push 142
004937CC |. 8D85 44FFFFFF lea eax, dword ptr [ebp-BC]
004937D2 |. 50 push eax
004937D3 |. FF15 3CB95C00 call dword ptr [<&MFC71.#2321_ATL::CStringT<char,StrTra>; MFC71.7C18B260
004937D9 |. 83C4 0C add esp, 0C
004937DC |. 68 00E00000 push 0E000
004937E1 |. 8D8D 40FFFFFF lea ecx, dword ptr [ebp-C0]
004937E7 |. FF15 2CB95C00 call dword ptr [<&MFC71.#4035_ATL::CStringT<char,StrTra>; MFC71.7C153789
004937ED |. 6A 40 push 40
004937EF |. 8D8D 40FFFFFF lea ecx, dword ptr [ebp-C0]
004937F5 |. FF15 30B95C00 call dword ptr [<&MFC71.#876_ATL::CSimpleStringT<char,1>; MFC71.7C158BCD
004937FB |. 50 push eax
004937FC |. 8D8D 44FFFFFF lea ecx, dword ptr [ebp-BC]
00493802 |. FF15 30B95C00 call dword ptr [<&MFC71.#876_ATL::CSimpleStringT<char,1>; MFC71.7C158BCD
00493808 |. 50 push eax
00493809 |. 8B8D 30FFFFFF mov ecx, dword ptr [ebp-D0]
0049380F |. E8 5ECE1100 call <jmp.&MFC71.#4104_CWnd::MessageBoxA>
00493814 |. C645 FC 02 mov byte ptr [ebp-4], 2
00493818 |. 8D8D 44FFFFFF lea ecx, dword ptr [ebp-BC]
0049381E |. FF15 68B95C00 call dword ptr [<&MFC71.#578_ATL::CStringT<char,StrTrai>; MFC71.7C1771B1
00493824 |. C645 FC 01 mov byte ptr [ebp-4], 1
00493828 |. 8D8D 40FFFFFF lea ecx, dword ptr [ebp-C0]
0049382E |. FF15 68B95C00 call dword ptr [<&MFC71.#578_ATL::CStringT<char,StrTrai>; MFC71.7C1771B1
00493834 |> EB 60 jmp short 00493896 ; !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
00493836 |> 8B8D 4CFFFFFF mov ecx, dword ptr [ebp-B4]
0049383C |. 51 push ecx
0049383D |. 68 2CF16200 push 0062F12C
00493842 |. 51 push ecx
00493843 |. 8BCC mov ecx, esp
00493845 |. 89A5 34FFFFFF mov dword ptr [ebp-CC], esp
0049384B |. 8D55 F0 lea edx, dword ptr [ebp-10]
0049384E |. 52 push edx
0049384F |. FF15 38B95C00 call dword ptr [<&MFC71.#297_ATL::CStringT<char,StrTrai>; MFC71.7C14E575
00493855 |. 8985 24FFFFFF mov dword ptr [ebp-DC], eax
0049385B |. 8D85 54FFFFFF lea eax, dword ptr [ebp-AC]
00493861 |. 50 push eax
00493862 |. B9 E4F26200 mov ecx, 0062F2E4
00493867 |. E8 74D9FCFF call 004611E0 ; 调用1:远程桌面的Form
0049386C |. 8985 20FFFFFF mov dword ptr [ebp-E0], eax
00493872 |. 8B8D 20FFFFFF mov ecx, dword ptr [ebp-E0]
00493878 |. 898D 3CFFFFFF mov dword ptr [ebp-C4], ecx
0049387E |. 6A 05 push 5
00493880 |. 8B8D 3CFFFFFF mov ecx, dword ptr [ebp-C4]
00493886 |. E8 D5CD1100 call <jmp.&MFC71.#6090_CWnd::ShowWindow>
0049388B |. 8B8D 3CFFFFFF mov ecx, dword ptr [ebp-C4]
00493891 |. E8 1A72F9FF call 0042AAB0
00493896 |> C645 FC 00 mov byte ptr [ebp-4], 0
0049389A |. 8D4D F0 lea ecx, dword ptr [ebp-10]
0049389D |. FF15 68B95C00 call dword ptr [<&MFC71.#578_ATL::CStringT<char,StrTrai>; MFC71.7C1771B1
004938A3 |. C745 FC FFFFF>mov dword ptr [ebp-4], -1
004938AA |. 8D8D 54FFFFFF lea ecx, dword ptr [ebp-AC]
004938B0 |. E8 FBD40800 call 00520DB0
004938B5 |> 8B4D F4 mov ecx, dword ptr [ebp-C]
004938B8 |. 64:890D 00000>mov dword ptr fs:[0], ecx
004938BF |. 8B4D EC mov ecx, dword ptr [ebp-14]
004938C2 |. 33CD xor ecx, ebp
004938C4 |. E8 C2DF1100 call 005B188B
004938C9 |. 8BE5 mov esp, ebp
004938CB |. 5D pop ebp
004938CC \. C3 retn
找到关键Call.
004936B7 |. E8 34F2FFFF call 004928F0 ; 关键Call–remote desk.
修改关键call之后的跳转:
004936C0 |. E9 F0010000 jmp 004938B5
修改为什么都不作。免得它影响后面的代码。
用9090909090 填充。
经试验,远程桌面功能可以超过20分钟的使用了。
实际摸索中还是走了不少弯路,总结经验为:在OllyDbg中,看过的弄明白的函数,要自己加上注释。 在看其他相关的代码的时候,极有可能就碰到了自己曾经注释过的代码,这样一下子就全通了。
Eclipse MyEclipse 没有响应 JVM terminated. Exit code=1073807364 错误 卡住 等怪问题怪现象[解决办法]
问题描述:
Eclipse的WTP和MyEclipse都会的怪毛病
在WTP或者MyEclipse下 在JSP页面中<% 和 %>之间写代码(即写:scriptlet代码)
只要输入.号 整个IDE就卡住了 比如: out. 这个后面要出来要卡好一会
强行关闭出现以下提示:
JVM terminated. Exit code=1073807364 c:\WINDOWS\system32\javaw.exe -Xms40m -Xmx256m -jar F:\eclipse\startup.jar -os win32 -ws win32 -arch x86 -launcher F:\eclipse\eclipse.exe -name Eclipse -showsplash 600 -exitdata f24_7c -vm c:\WINDOWS\system32\javaw.exe -vmargs -Xms40m -Xmx256m -jar F:\eclipse\startup.jar
这个问题 让我郁闷了好几天 虽然在JSP中scriptlet的脚本是禁忌 但是有时候为了方便测试代码效果 还是会偶尔用用
经过这次 总结了一下
解决办法: (感谢Matrix论坛几位朋友的帮助)
1: ①: 在Eclipse——Window——Preferences——Java——Installed JREs下 添加你机子上的1.5+的JDK路径 添加好后把旧版本的Remove 这样下次新建工程就不用改了 一劳永逸!
或者:
②: 请将新建WEB工程的JRE 1.42的包remove掉,自己新建一个你机子上的1.5+的JDK目录下的jre的包 要导入的jre路径包括: D:\Program Files\Java\jdk1.5.0_06\jre\lib下的 *.jar;以及 D:\Program Files\Java\jdk1.5.0_06\jre\lib\ext下的*.jar. (这个问题其实只这一步就能解决了,谢谢yiqingxiao的提醒)
2: 在eclipse的安装目录下用EditPlus编辑eclipse.ini文件,将其中的参数改成:
-vm=D:\Program Files\Java\jdk1.5.0_06\bin\javaw.exe #这个看你自己JDK路径而定 -vmargs #下面参数视大家内存大小 自己选择合适的大小 -Xms128m -Xmx512m -XX:PermSize=64M -XX:MaxPermSize=128M
3: 装过orcale的人 最好把环境变量重新设置一下:set path=D:\Program Files\Java\jdk1.5.0_06\bin
4: eclipse是3.3的,单独运行没什么问题,装上myeclipse6之后编写jsp代码时联想输入的时候就会出现eclipse无响应的情况,大概1分钟左右才能恢复,搜了半天也没有个好答案。自己琢磨,用了下面这个办法,好了,不知为什么。菜单-->myeclipse-->myeclipse-->files and editors-->html-->visual designer
取消mozilla/firefox和sarari还有show warning......(Linux only)这三个选项。就好了,你们实验看看。 |
1- java.sql.SQLException: ソケットから読み込むデータはこれ以上ありません(java.sql.SQLException: No more data to read from socket)
Driver Version: 9i.* or 10g.*
该异常通常是因为使用了连接池,当从连接池取得的connection失效或者超时的时候,使用这个连接来进行数据库操作就会抛出以上异常。
解决方法就是让数据库连接池在给你返回connection之前,检查该connnection是否超时或者失效,如果是,则evict这个connection,并返回一个可用的connection。
以DBCP为例,做如下配置即可解决问题:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="url">
<value>${jdbc.url}</value>
</property>
<property name="driverClassName">
<value>${jdbc.driver}</value>
</property>
<property name="username">
<value>${jdbc.username}</value>
</property>
<property name="password">
<value>${jdbc.password}</value>
</property>
<property name="testOnBorrow">
<value>true</value>
</property>
<property name="testOnReturn">
<value>true</value>
</property>
<property name="testWhileIdle">
<value>true</value>
</property>
<property name="minEvictableIdleTimeMillis">
<value>180000</value>
</property>
<property name="timeBetweenEvictionRunsMillis">
<value>360000</value>
</property>
<property name="validationQuery">
<value>SELECT 1 FROM SYS.DUAL</value>
</property>
<property name="maxActive">
<value>100</value>
</property>
</bean>
另外,你也可以参考这里:http://www.websina.com/bugzero/errors/oracle-SQLException.html
转贴: Oracle SQLException: No more data to read from socket
java.sql.SQLException: No more data to read from socket at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:134)
at oracle.jdbc.dbaccess.DBError.throwSqlException(DBError.java:179)
at oracle.jdbc.dbaccess.DBError.check_error(DBError.java:1160)
at oracle.jdbc.ttc7.MAREngine.unmarshalUB1(MAREngine.java:963)
at oracle.jdbc.ttc7.MAREngine.unmarshalSB1(MAREngine.java:893)
at oracle.jdbc.ttc7.Oall7.receive(Oall7.java:375)
at oracle.jdbc.ttc7.TTC7Protocol.doOall7(TTC7Protocol.java:1894)
at oracle.jdbc.ttc7.TTC7Protocol.parseExecuteFetch(TTC7Protocol.java:1094)
at oracle.jdbc.driver.OracleStatement.executeNonQuery(OracleStatement.java:2132)
at oracle.jdbc.driver.OracleStatement.doExecuteOther(OracleStatement.java:2015)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:2877)
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:608)
This error most likely occurs in applications that use a database connections pool. When the application checked out a connection that has been timed out or has been staled, and used it to connect to the database, this error occurs.
You may need start your Oracle database server as well as your Java application. In a better designed system, however, the staled connection should be cleared out and a new connection should be establised automatically.
--------------------------------------------------
2-ORA-17004: Invalid column type (java.sql.SQLException: 列の型が無効です。) Driver Version: 9i.*
该异常初次出现在使用spring+iBatis的程序中,后来通过检查出现错误的字段和SQLMAP的参考文档,才发现问题之所在。
如果你使用iBatis,那从他的SqlMap参考文档中应该找到以下文字,当你看到他们的时候,你就发现了通向成功之门的钥匙,呵呵
Note! Most drivers only need the type specified for nullable columns. Therefore, for such drivers you only
need to specify the type for the columns that are nullable.
Note! When using an Oracle driver, you will get an “Invalid column type” error if you attempt to set a null
value to a column without specifying its type.
也就是说,当某个column允许为空,而你传的参数对应该column的值也为null的时候,对于oracle的驱动来说,这个异常是铁定的了。
解决方法,可以通过iBatis的parameterMap,指定parameter元素的jdbcType和nullValue来解决;如果你没有使用iBatis,那你可以通过检查参数,如果他对应的列为可以为空,而当前值恰好就是空的时候,为他设置一个不是空的值即可。
--------------------------------------------------
3-java.sql.SQLException: OALL8矛盾した状態にあります;(java.sql.SQLException: OALL8 is in an inconsistent state.)
该异常在我们的程序中通常是在第一个异常出现之后出现,但也不尽然,该异常搜遍网上也找不到合理的解释,只有以下信息可能会有用一些(from http://forums.oracle.com/forums/thread.jspa?messageID=1275383):
This is known to occur under when you are using too big an array size. How big your array can be depends on the length of each record and the Driver/Database combination. If you exceed the maximum size you will get the "OALL8" SQLException and your connection object may become unusable.
This message is also created if you are using the following:
9.0.1 Database
10.1.0 JDBC Driver
Generated Code that passes in an ARRAY or VARRAY of VARCHAR2 as a Parameter
或者(from http://opensource.atlassian.com/projects/spring/browse/SPR-1545?decorator=none&view=rss):
[SPR-1545] Oracle error 17447 should result in a DataAccessResourceFailureException
Oracle error 17447 is currently an unmapped exception but it should be a DataAccessResourceFailureException. This error occurs when a JDBC connection has become corrupted, usually because of failure to properly close a connection before returning the connection to the connection pool. Here is the error message:
SQL state [null]; error code [17447]; OALL8 is in an inconsistent state; nested exception is java.sql.SQLException: OALL8 is in an inconsistent state
java.sql.SQLException: OALL8 is in an inconsistent state
This is a kind of "oh crap, something bad happened and it's not really your fault" exception in Oracle. A few causes of this message are suggested in my searches on Google, including (see http://forums.oracle.com/forums/thread.jspa?threadID=274018&tstart=0):
- use of Oracle 10g JDBC drivers to connect to Oracle 9 databases
- using too big an array size (9.0.1 Database, 10.1.0 JDBC Driver and Generated Code that passes in an ARRAY or VARRAY of VARCHAR2 as a Parameter )
In any case, after this exception is thrown the connection is corrupted and unusable, hence why I advocate this exception be mapped to a DataAccessResourceFailureException
解决方法,我也不知道,呵呵,反正调整了第一个问题之后,这个异常再没有在我们的程序中出现。
说明:这是转载自远景论坛
ycjcn 的帖子,大家可以点这查看原文
无需刻录DMG光盘,教你在VMWare下安装MAC OS X Snow Leopard 10.6
补充:1、本文内容已经在Windows Vista + VMware Workstation 7.0 + MAC OS X Snow Leopard 10.6 环境下安装成功!
2、当你在虚拟机里浏览镜像文件时看不到DMG文件,需要点Browse,将文件类型CD-ROM images(*.iso)改为All files (*.*)。
3、请仔细阅读文中由粗体字标出的部分,忽略这些部分将导致安装失败。
在PC机上安装MAC OS X系统有两种方法,一是在硬盘上分区,专门安装MAC OS X;二是在现有Windows系统上使用VMWare等虚拟机软件安装。两种安装方法在网上都有介绍,但第二种方法一般都需要将苹果系统的DMG光盘镜像文件刻录到D9光盘上,不仅让没有刻录条件的同学为难,连我有刻录条件的人也觉的麻烦。今天我就在这里补充介绍一下使用VMWare安装MAC OS X时,无需刻录D9光盘,无转换成ISO格式,直接使用MAC OS X的DMG镜像文件进行安装的方法。
安装必备:
硬件:一台拥有支持虚拟技术的64位双核处理器和2GB以上内存的PC。
软件:
VMWare Workstation 7.0
DMG光盘镜像文件:
苹果操作系统 - 雪豹 10.6
darwin300
darwin300.rar (52 K) 下载次数:29142 或者Rebel EFI
Rebel EFI.rar (55 K) 下载次数:19897 引导光盘ISO镜像文件
首先安装好VMWare Workstation 7.0。使用File->New->Virtual Machine创建一个虚拟机,在选择操作系统时选择Other->FreeBSD 64-bit。
CPU设置1核可以,2核也可以,如果你是双核CPU,建议你分配1个核;内存建议设置为1024MB,如果你的物理内存够大也可以多给些;硬盘最少分配15GB,太小则无法进行安装,如果你还想多装些软件,建议多分配一些。最后一步可以在Customize Hardware选项里把软驱删除,因为这个用不上。
创建好之后,需要你在刚建立的虚拟机目录下找到一个扩展名为.vmx的文件,用记事本打开,找到guestOS = "freebsd-64"一行,将引号里的freebsd-64改为darwin10,改完是guestOS = "darwin10",保存修改后的文件。
做了这一步,在这个虚拟机的Options->General选项下就可以看到操作系统版本显示为:MAC OS X Server 10.6,如下图所示。仍显示为FreeBSD 64-bit的需要重启一下VMWare。
如果运行不了后面介绍的MAC OS X安装程序,很可能是忽略了这步或者修改错了。
接下来我们就要开始安装了。安装的第一步是用Darwin.iso或者Rebel EFI.iso镜像进行引导。先装载镜像文件:在刚才建立的虚拟机Settings->Hardware中,选择CD/DVD(IDE)设备,选择右侧的Use ISO image file,点Browse加载Darwin.iso或者Rebel EFI.iso。
装载完镜像文件后,现在启动虚拟机。
先演示使用Rebel EFI镜像启动,引导完成后就能看到如下图的界面:
按照屏幕提示等待10秒或者按'1'键,选择当前光盘驱动器。此时会出现如下图所示的界面:
这里提示请插入MAC OS X的DVD光盘。
此时我们再次打开这个虚拟机的Settings->Hardware,找到CD/DVD(IDE)设备(你可以双击VMWare窗口右下角的光盘图标来快速打开,如图: ),将刚才的Rebel EFI镜像替换为苹果系统的DMG镜像;点Browse,将文件类型CD-ROM images(*.iso)改为All files (*.*)就能浏览到DMG文件了。
这里有一个关键地方:选择DMG镜像文件后,将下图所示Device status里的Connected前打上勾(否则不能进入安装界面),然后确定。
此时,我们便进入了MAC OS X的安装程序准备阶段。
有的同学使用Rebel EFI引导可能会出现禁止符号,无法安装,如下图所示:
遇到这个问题可以使用Darwin镜像替换Rebel EFI,并重新启动虚拟机。在出现下图所示界面后,在CD/DVD(IDE)设备里将Darwin镜像改为苹果的DMG镜像即可,同样记得给“Connected”选项打勾。
经过一阵准备阶段就进入了苹果系统的安装过程,如下图所示:
在这里特别提一下,有的同学在进行到选择安装盘的步骤时看不到硬盘选项,不要着急,在屏幕上方的菜单中找到“实用工具”->“磁盘工具”,如图所示:
对你的虚拟硬盘执行“抹掉”操作,如图所示:
=700) window.open('http://images.weiphone.com/attachments/Day_091117/102_35614_dd83da0369b2aa5.png');" border=0 src="http://images.weiphone.com/attachments/Day_091117/102_35614_dd83da0369b2aa5.png" width=700 onload="if(this.width>'700')this.width='700';if(this.height>'700')this.height='700';">
操作完成后关闭“磁盘工具”窗口就OK了,如图所示:
根据你的电脑配置不同,大概进行几十分钟的安装,你就可以用上苹果操作系统了。
看到下图的界面,表示你的系统已经安装完成,需要重新启动。
重新启动前你要特别注意,先要去掉安装时在CD/DVD(IDE)设备上加载的苹果DMG镜像,换上Darwin或者Rebel EFI引导镜像,否则你将不能成功引导安装好的MAC OS X系统。
如果使用一种引导失败,可以试着使用其他的引导镜像。
还有需要在VMWare里的MAC系统上安装声卡、显卡等驱动的同学,可以在网上寻找相关文章和驱动,或参考
《号外!折腾无限!VMware Workstation 7.0 虚拟机安装雪豹snow leopard 10.6》
我终于知道怎么安装了,开始我也是找不到这个东西,后来在网上看到张图片,才找到的。
其实它就在MAC启动用的 drawin300.iso 里。
在MAC运行的时候用虚拟机把光盘插进去,MAC桌面上就会出来个光盘的图标,安装程序就在里面
第八步:安装VMware Tools

安装VMware Tools后,可以设定屏幕分辨率,可以主机共享文件等功能。

加载darwin.ISO后,进行安装

安装后,有多个分辨率选择,而且可以使用主机共享
第九步:与主机共享文件并安装声卡驱动
为了让主机和虚拟苹果文件共享,请进行下面的设置。

添加主机与虚拟系统共享目录

共享文件夹路径与名称

确定安装共享设置

这里要注意下:VMWare 插这个iso光盘的时候要把 直接连接 复选框选中,然后再点确定。
进入系统后,会在桌面上显示这个共享文件夹为一个新磁盘。
将EnsoniqAudioPCI 2.mpkg.RAR(声卡驱动)在主机上解压放到共享文件夹目录中。
并在Mac系统中安装后,就有声音了。
RIA(Rich Internet Applications)富互联网应用,具有高度互动性、丰富用户体验以及功能强大的客户端。
RIA的优势
特点
RIA 具有的桌面应用程序的特点包括:在消息确认和格式编排方面提供互动用户界面;在无刷新页面之下提供快捷的界面响应时间;提供通用的用户界面特性如拖放式(drag and drop)以及在线和离线操作能力。RIA具有的Web应用程序的特点包括如:立即部署、跨平台、采用逐步下载来检索内容和数据以及可以充分利用被广泛采纳的互联网标准。RIA具有通信的特点则包括实时互动的声音和图像。
客户机在RIA中的作用不仅是展示页面,它可以在幕后与用户请求异步地进行计算、传送和检索数据、显示集成的用户界面和综合使用声音和图像,这一切都可以在不依靠客户机连接的服务器或后端的情况下进行。
部署RIA的好处
对于企业来说,部署RIA的好处在于:
1)RIA可以继续使用现有的应用程序模型(包括
J2EE和
.NET),因而无需大规模替换现有的
Web应用程序。通过
Rich Client技术,可以轻松构建更为直观、易于使用、反应更迅速并且可以脱机使用的应用程序。
2)RIA可以帮助企业提供多元化的重要业务效益,包括提高销量、提高品牌忠诚度、延长网站逗留时间、较频繁的重复访问、减少带宽成本、减少支持求助以及增强客户关系等。
发展态势
在过去的两到三年中,Web开发人员一直是想构建一种比传统
HTML更丰富的客户端:这是一个用户接口,它比用HTML能实现的接口更加健壮、反应更加灵敏和更具有令人感兴趣的可视化特性。
RIA技术的出现允许我们在因特网上以一种像使用Web一样简单的方式来部署
富客户端程序。无论将来RIA是否能够如人们所猜测的那样完全代替HTML应用系统,对于那些采用C/S架构的胖客户端技术运行复杂应用系统的机构和采用基于B/S架构的瘦客户端技术部署Web应用系统地机构来说,RIA确实提供了一种廉价的选择。下面介绍一下目前出现的几种比较有实力或者有特点的RIA客户端开发技术:
1) Adobe Flash/Flex
Flash 从6.0开始Flash就逐步具备建立窗体风格的应用程序的功能。据Adobe称已经有98%以上的桌面系统的浏览器都安装了 Adobe Flash Player。这使得以Adobe Flash Player为客户端的RIA可以支持种类广泛的平台和设备。
Flex是为满足希望开发 RIA的企业级程序员的需求而推出的表示服务器和应用程序框架,它可以运行于J2EE和.NET平台。Flex表示服务器提供基于标准的、声明性的编程方法和流程,并提供运行时服务,用于开发和部署丰富客户端应用程序的表示层。Flex开发者使用直观的基于XML的MXML来定义丰富的用户界面。该语言由 Flex服务器翻译成SWF格式的客户端应用程序,在Flash Player中运行。
2) Laszlo
Laszlo 是一个开源的RIA开发环境。使用
Laszlo平台时,开发者只需编写名为LZX的描述语言(其中整合了XML和Javascript),运行在J2EE 应用服务器上的Laszlo平台会将其编译成SWF格式的文件并传输给客户端展示。从这点上来说,Laszlo的本质和Flex是一样的。Flash是任何浏览器都支持的展示形式,从而一举解决了浏览器之间的移植问题。而且,在未来的计划中,Laszlo还可以将LZX编译成Java或.NET本地代码,从而大大提高运行效率。
3) Avalon
Microsoft的Avalon是下一版本的 Windows(代号"Longhorn")的一部分,是一个图形和展示引擎,主要由新加到.NET框架中的一组类集合而成。Avalon定义了一个在 Longhorn中使用的新标记语言,其代号为"XAML"(可扩展应用程序标记语言)。可以使用XAML来定义文本、图像和控件的布局,程序代码可以直接嵌入到XAML中,也可以将它保留在一个单独的文件内。这与Flex中的MXML或者Laszlo中的LZX非常相似。不同的是:基于 Avalon的应用程序必须运行在Longhorn环境中,而Flex和Laszlo是不依赖于平台的,仅仅需要装有
Flash播放器的浏览器即可。
4) Java SWT
Java 已经出现几年了,并且完全支持创建基于窗体的用户界面。除了
Java基础类(JFC/Swing)中的用户界面组件之外,开发人员还可以使用来自于 Eclipse Project的SWT工具箱和许多第三方工具箱进行开发。对于图形来说,可以采用Java 2D API:一个非常完整且非常复杂的图形API。你可以通过一个Web浏览器使用Java插件软件,或使用Java运行时环境中较新的Java Web Start技术来部署应用程序。使用Java建立Rich Client的主要缺陷是它的复杂性(即使对简单的窗体和图形也要求编写非常烦琐的代码)和Java浏览器插件的低市场占有率。
5) XUL
XUL (念作"zool")是一种基于XML的用户界面语言,它来自于Mozilla的开放源码项目。它可用于建立窗体应用程序,这些应用程序不但可以在 Mozilla浏览器上运行,而且也可以运行在其他描述引擎上,如Zulu(一个Flash MX组件)和Thinleys(一个Java实现)。XUL描述引擎都非常小(100K以下),它可以使用XML数据也可以生成XML数据。XUL的一个主要缺点在于它目前还没有获得一个主要商业实体的支持。XUL最大的优点在于它与Gecko引擎的集成(打开了通向大量Web标准的大门),以及与大多数其它XML用户界面描述语言相比它是一种非常具有表达力和简洁的语言。
6) Bindows
Bindow 是用Javascript和DHTML开发的Web窗体框架。Javascript用于客户端界面的显示和处理,
XML HTTP用于客户端与服务器的信息传输。Javascript在客户端的表现力不容置疑,利用Javascript几乎可以实现
Windows应用程序所能干的大部分事情,XMLHTTP 一直以来常被用于实现"无刷新"的Web页面,它和
Javascript配合,可以完成数据从服务器和客户端的传输。Bindows的一个主要的缺点是它采用一次全部载入的方式来实现脚本库,在窗口的加载期,需要一个漫长的等待过程,甚至浏览器的进程会产生无响应的情况。这点Bindows根本没有遵循"用多少去多少"的准则。另外,内部大量利用了IE6 的技术,没有考虑到非IE的浏览器,限制了Bindows的流行。
7)JavaFX
2008年12月05日 Sun微系统公司今天正式发布了基于Java语言的平台JavaFX 1。0,这个平台建立在其广泛应用的Java编程语言的基础上,旨在建立大量可在电脑和手机上运行的网络程序。 Java一直以来就是编程语言,但是随着JavaFX的发布,Sun公司开始允许将编程内容创新这一任务转移到以设计艺术为重点而非编程科学为重点的设计人员身上。
“我们的目标群体是叫做创造者的人群”,Sun公司Java平台组的高级副主任 OctavianTanase对 说,“随着1.0版的发布,我们将目标锁定在网页开发人员,这群可能拓展Java界面体验的人。到2011年,主要的目标是大量使用诸如Adobe系统等设计工具的设计人员”。
当然,通向这个以设计为导向的工具还需要一些时间。Sun公司最后打算提供自己的程序给设计人员来建立RIAS,但是直到如今,这些设计人员还得使用程序员所使用的Netbeans或Eclipse集成开发环境(IDE)。新工具将在来年夏天面市。
8)Curl
Curl诞生于1995年的美国,Curl是由美国国防部高级研究项目代理资助,马萨诸塞州科技学院的David A. Kranz开发的Web开发语言, HTML语言的创建者Tim Berners-Lee也参与其中,并扮演了重要的角色。
该语言的目标是用一种统一的面向对象的语言代替HTML、Cascading Style Sheets、JavaScript等;仅使用Curl便可开发出Web应用的各种软件;Curl程序在浏览器中运行,并且因为它以类似JRE的形式提供了客户端运行环境Surge RTE,能够轻松开发出日益流行的Rich Client应用程序。
Curl是为了实现富客户端(rich client)应运而生的Web开发语言, 仅仅从其外观的丰富性上就能体现其富客户端理念。
为了实现真正有益的富客户端,它能有效地实现各种复杂处理,具备提供高信赖、高扩展性、高维护性的应用程序所应拥有的各种编码能力。其拥有在Web环境上便利的分配、管理以及低廉的维护费以及在C/S环境上的用户便利性、迅速的应答,华丽的图像显示等重多优点于一身。
Curl语言于2002年在美国正式开始商业化,在美国和日本拥有重多的客户和合作伙伴,现已进军北美及韩国市场,发展势头迅猛。
9)SilverLight
微软在Mix07上发布一些重大通告,其中最值得关注的就是SilverLight的发布,SilverLight的前身就是WPF/E技术。
这是一种新的Web 呈现技术的名称,创建该技术的目的是使其能够在各种平台上运行。该技术支持创建丰富的、具有绚丽视觉效果的交互式体验,并且可以随处实现:无论是在浏览器内、在多个设备上还是在桌面操作系统(如 Apple Macintosh)中。可扩展应用程序标记语言 (XAML) 遵循 Windows 演示基础 (WPF),前者是”WPF/E”呈现功能的基础。XAML 是 Microsoft .NET Framework 3.0(Windows 编程基础结构)中的呈现技术。
RIA未来的发展预测
就目前RIA的使用情况来说,离"RIA时代"还有很远的一段距离。今后几年时间内传统的Web应用程序和RIA将会共存。笔者认为真正具有实力担当起普及丰富客户端应用重任的只有基于Flash Player的Flash/Flex应用程序和Microsoft的基于Avalon的应用程序。短期时间内(估计2-3年时间)可能是 Flash/Flex应用程序在新兴的网络应用程序市场上占有主导地位。
目前Microsoft还在推广一种叫做Smart Client(智能客户端)的客户端程序技术,Microsoft称Smart Client是比Rich Client更优秀的客户端,因而采用Smart Client的应用程序算不算RIA目前我个人还无法作答。这里我们之所以提及Smart Client,是因为Smart Client的特性跟我们谈的Rich Client有太多的相似之处。Smart Client拥有自动更新、离线状态下的数据处理和可以使用本地资源等特征,其中的可使用本地资源这一项无疑是一大卖点,因为浏览器中的 Flash/Flex应用程序目前还无法操作本地的一些资源,比如Flash/
Flex应用程序无法将网上的文件保存到本地或者修改本地文件。虽然
Macromedia的Central1.5已经可以对本地文件进行简单的操作,并且flex1.5开发的RIA也能够运行于Central上,但是如何使Central能够得到大范围推广还是个问题。相对于轻量级的Rich Client,Smart Client更接近
C/S架构中的客户端程序。Rich Client和Smart Client的定位还是有所区别的:Rich Client更适合作为轻量级的基于浏览器的网络应用程序客户端;Smart Client更适合作为Windows桌面应用程序的智能客户端。
不管我们今天称之为的RIA今后会不会成为主流应用程序,人们对开发具有高度互动性、丰富用户体验以及功能强大的客户端的追求是不变的。有理由相信,拥有成熟技术和极高市场占有率的Flash客户端将会在RIA道路上越走越远。Microsoft未来的重量级武器:Avalon和Smart Client能否后来者居上让我们拭目以待。
RIA
放射免疫测定/放射免疫分析(Radio immunoassay,RIA)
基本原理:
在放射免疫分析的实验中,加入超量的标记抗原*Ag与未标记抗原Ag(即:
待测抗原)与较少量的抗体(Ab)竞争性结合。
如果实验结果所计量到的结合物(*Ag-Ab)放射活性较高,表示待测物的浓度较低。
如果所计量到的结合物放射活性较低,则表示待测物的浓度较高。 藉由标准 曲线图的分析,可以推算出待测物的浓度。
相信大家在启动MyEclipse的时候都很慢,很烦有没有让他更快更舒服些呢?
现在就介绍一些优化方法,很管用,对电脑配置较低的朋友相信有很大的帮助,希望大家能仔细的看完,并按以下每步设置。
1、去除不需要加载的模块
一个系统20%的功能往往能够满足80%的需求,MyEclipse也不例外,我们在大多数时候只需要20%的系统功能,所以可以将一些不使用的模块禁止 加载启动。通过Windows - Preferences打开配置窗口,依次选择左侧的General - Startup and Shutdown,这个时候在右侧就显示出了Eclipse启动时加载的模块,可以根据自己的实际情况去除一些模块。
windows–>perferences–>general–>startup and shutdown
关掉没用的启动项:
WTP :一个跟myeclipse差不多的东西,主要差别是 WTP 是免费的,如果使用myeclipse,这个可以取消
Mylyn:组队任务管理工具,类似于 CVS ,以任务为单位管理项目进度,没用到的可以取消
Derby:一种保存成 jar 形式的数据库,我没用到,取消
一大排以 MyEclipse EASIE 打头的启动项:myeclipse 支持的服务器,只选自己用的,其他取消,比如我只选了tomcat6.x
2、取消MyEclipse在启动时自动验证项目配置文件
默认情况下MyEclipse在启动的时候会自动验证每个项目的配置文件,这是一个非常耗时的过程,
可以在Preferences窗口依次选择 MyEclipse Enterprise Workbench - Validation,然后在右侧的Validator列表中只保留 Manual 项就可以了(Manual全部勾选,Bulid项只留下第一项)。
如果需要验证的时候只需要选中文件,然后右键选择 MyEclipse - Run Validation就可以了。
windows–>perferences–>myeclipse–>validation
把 除了manual 下面的全部点掉,build下只留 classpath dependency Validator
手工验证方法:
在要验证的文件上,单击鼠标右键–>myeclipse–>run validation
3、去掉拼写检查(如果你觉的有用可以不去)
拼写检查会给我们带来不少的麻烦,我们的方法命名都会是单词的缩写,他也会提示有错,所以最好去掉,没有多大的用处:
windows–>perferences–>general–>validation->editors->Text Editors->spelling
myeclipse 打开 jsp 的默认编辑器不好,会同时打开预览
windows–>perferences–>general–>editors->file associations,
把默认改成 MyEclipse JSP Editor()
原默认的jsp编辑器是 MyEclipse Visual JSP Designer,顾名思义,此编译器是jsp可视化编辑器,对于初学者有很多的帮助,
但修改此项的默认编辑器其实可以提高启动速度)
4、关闭自动更新
如果是myeclipse7.0以上版本:
(1)关掉maven自动更新:
window-preferences-MyEclipse Enterprise Workbench-Maven4MyEclipse-Maven,
关闭所有Download和Update开头的选项,共四项(去掉前面的勾)
(2)关闭更新调度:window –> preferences –> General –> Startup and Shutdown –> Automatic Updates Scheduler(去掉前面的勾)
(3)window –> preferences –>Myeclipse Dashboard,关闭Show……on start
5、加大JVM的非堆内存
打开 myeclipse.ini
-startup
../Common\plugins\org.eclipse.equinox.launcher_1.0.101.R34x_v20081125.jar
--launcher.library
../Common\plugins\org.eclipse.equinox.launcher.win32.win32.x86_1.0.101.R34x_v20080731
-clean
-configuration
configuration
-vm
C:\Users\lenovo\AppData\Local\Genuitec\Common\binary\com.sun.java.jdk.win32.x86_1.6.0.013\jre\bin\client\jvm.dll
-vmargs
-Xmx384m
-XX:MaxPermSize=384m
-XX:ReservedCodeCacheSize=96m
以上是我的myeclipse.ini,需要修改是-Xmx,-XX:MaxPermSize,-XX:ReservedCodeCacheSize,
将这三项的值调大,但并不是越大越好,曾经在相同的条件下做过测试(内存2GB),-Xmx,-XX:MaxPermSize的值为384m时比512m时要快(视具体的计算机而定),
-Xmx,-XX:MaxPermSize的值设为同样大小且两者之和不能超出你的计算机本身的内存大小
6、window-preferences-MyEclipse Enterprise Workbench-Maven4MyEclipse-Maven,将Maven JDK改为电脑上安装的JDK,即不使用myeclipse提高的JDK
登记add按钮,选择你的电脑上的JDK即可(注意:不是JRE,我的值为:Java6.014)
7、window-preferences-MyEclipse Enterprise Workbench-Matisse4Myeclipse/Swing,将Design-time information(dt.jar) location 改用电脑安装的JDK的dt.jar
(即不使用myeclipse提供的dt.jar,我的值为:C:\Java6.014\lib\dt.jar)
经过以上的优化,myeclipse的启动时间可以减少2/3,Tomcat的启动速度可以减少1/2(视具体情况而定)。
项目验收会在项目整个生命周期内是一个非常重要的里程碑。一般来说,客户同意召开验收会,就是对项目已基本认可,需要召集项目相关各方及专家来达成共识。因此,验收会不仅对乙方,而且对甲方来说都非常重要,双方都希望看到一个准备充分,进展顺利的验收会。为了准备好这个会议,项目组需要提前准备很多工作,具体说来,主要包括以下几个方面。
一.文档准备
验收之前,项目组要准备好以下几类文档:
1.开发总结文档
2.需求文档:包括需求规格说明书,需求变更文档等
3.设计文档:包括概要设计,详细设计,数据库设计等
4.测试文档:包括测试方案,内部测试报告,第三方测试报告等
5.实施文档:包括实施,部署方案,用户手册,维护手册等
6.过程文档:包括项目周报,会议纪要等
以上文档可以参考国家标准或行业标准进行准备,需要说明的是,1-5项可以在后期补,第6项在后期补就比较麻烦,因此在项目开发过程中要注意整理这类文档。另外,还要仔细阅读合同及相关采购文件,看其中是否还提到需要其它文档。
这些文档可以装订在一起,为了给客户及专家一个很好的印象,有以下几个装订技巧:
1.如果文档总页数太少,就单面打印,反之可以双面打印,总之要给人一种很厚,很充实的感觉。
2.设计一个漂亮的,彩色封面,彩打出来。
3.做一个总目录,列明这份材料包括以上哪些部分。例如:第1/7部分 项目开发报告 第2/7部分 项目需求规格说明书
4.每个部分之间用硬皮纸或突出的标签分开,如果用突出标签,在标签上注明那部分的标题
5.最好在书脊上印上标题
6.开会前问客户要装订多少份
项目验收会前,还要提前发给客户以下几份材料:
1.我方参加验收会的名单,便于客户宣读
2.验收意见
3.会议议程
另外,在验收会上,还需要带上项目过程中签署的文档备查,例如合同原件,盖单的用户需求规格说明书原件等等。
二.ppt准备
验收时的ppt一般包括以下几个部分:
1.项目背景和简介
2.合同执行情况汇报
3.开发过程:记录项目开发过程中的一些重要事件
4.系统功能
5.建设或应用成果
6.系统演示(在ppt上列明要演示哪些内容,然后一个一个对照演示)
在做系统演示时,注意要以业务流程为演示重点,用流程将功能点串起来。
三.系统准备
开会时需要对系统进行演示,因此开会前要保证系统的稳定和速度。注意事项如下:
1.尽量安装多一套系统在笔记本上,以防不测。
2.根据网络情况看是否需要带无线上网卡等设备。
2.设计好几个演示流程,一般不可能演示系统的全部功能,因此通过这几个典型流程可以全面反映系统的功能。准备这几个流程时要准备好脚本和数据,务必保证演示过程中数据完整,出现的界面没有硬伤,例如出错,图片丢失等等。
3.演示完这几个流程后,再挑一些系统的亮点进行演示。注意这个顺序,不要一上来就演示基础信息管理,客户更关心的是这个系统的核心业务。
4.把这几个流程和亮点写在ppt上,让大家可以看到你正在演示什么内容。
四.演示前准备
1.开会前一天把ppt准备好,自己试讲至少两遍,也可以邀请同事试听并给意见。
2.把系统准备好,重要功能复查几次,确保不出错
3.开会时提前一个小时到开会地点,布置会场及准备演示环境。
4.看情况是否需要带数码相机,移动硬盘,交换机,网线等物品。
5.指定同事做会议记录。
按以上要求准备验收会议,验收成功就离你不远了。验收成功后,高兴之余,不要忘了做以下几件事:
1.带回用户验收意见
2.将打印版和电子版的验收文档拿回公司归档
3.写会议纪要,把后续要继续跟进事项记录好,如果有图片,也一起发上吧
1:检查系统
sar -u 5 5
2: 看谁在用CPU
topas
ps -ef |grep ora #检查第四列,C的大小(unit,100 per cpu)
3:检查CPU数量
/usr/sbin/bindprocessor -q
lsattr El proc0
4:两种可能:
1: A Background (instance) process
2: An oracle (user) process #此种可能最大。
5: 如果是用户进程:那么高CPU的主要原因有:
Large Queries, Procedure compilation or execution,
Space management and Sorting
5.1 查看每个Session的CPU利用情况:
select ss.sid,se.command,ss.value CPU ,se.username,se.program
from v$sesstat ss, v$session se
where ss.statistic# in
(select statistic#
from v$statname
where name = 'CPU used by this session')
and se.sid=ss.sid
and ss.sid>6
order by ss.sid
5.2: 比较上述Session
比较一下哪个session的CPU使用时间最多,然后查看该Session的具体情况:
select s.sid, event, wait_time, w.seq#, q.sql_text
from v$session_wait w, v$session s, v$process p, v$sqlarea q
where s.paddr=p.addr and
s.sid=&p and
s.sql_address=q.address;
5.3:查看
得到上述信息后,查看相应操作是否有hash joins 和 full table scans。如果有hash joins 和 full table scans那么必须创建相应的Index或者检查Index是否有效。
另外必须检查是否有并行的查询存在和同一时刻有多个用户在执行相同的SQL语句,如果有必须关闭并行的查询和任何类型的并行提示(hints);如果查询使用intermedia数据,那么为了减少总的Index大小,必须限制使用Intermedia的Worldlist。(try restricting the wordlist that intermedia uses to help reduce the total indexsize)。
6:注意事项
上述方案只能根据已经运行完成的操作,对于正在执行的长时间操作只能等操作完成后才能检测得到。因此我们可以通过另外一个很好的工具来检测正在运行的长时间操作语句。v$session_longops,这个视图显示那些操作正在被运行,或者已经完成。每个process完成后会刷新本视图的信息。
7:怎样寻找集中使用CPU的Process:
很多时候会发现有N个Process在平均分享着CPU的利用率,这种情况唯一的可能性就是这些Process在执行着相同的Package或者Query.
这种情况:建议通过statspack,在CPU高利用率额时候运行几个快照,然后根据这些快照检查Statspack报告,检查报告中最TOP的Query。然后使用 sql_trace and tkprof 工具去跟踪一下。
同时检查buffer cache 的命中率是否大雨95%。
同时在报告中还需要检查一下table scans (long tables),看是否在报告生成期间有存在全表扫描。
8:参数
另外还有一些不是特别重要的,但是也必须关心检查的参数可能消耗CPU。
parallel query 并行查询:
并行查询最好用于数据仓库的环境下,那种情况任何时候只有几个用户在同时使用。在一个联机事务处理环境中,当同时许多用户去并行查询一个数据库的巨大表时候,会导致CPU的爆满。所以最好在数据库的级别关闭并行查询:设置参数如下:
parallel_min_server = 0 parallel_max_server = 0
parallel_automatic_tuning = false;
在配置上述参数后,如果SQL语句中使用的并行的提示,那么还是有可能会出现并行查询的情况,所以还需要继续监视相关的SQL语句,如果有可以直接去除提示。
今天在修改一些网页代码时用资源管理器搜索包含文字时,明明有文件里包含了这些文字,但XP就是找不到,后来经过摸索找到以下解决方法。
资源管理器的搜索功能,搜索包含某个字符的文件时,为“提高效率”,取消了对所有文件类型中字符的搜索支持,只有部分文件类型中的字符可以被搜索。
解决办法:
运行regedit,编辑注册表
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ContentIndex
右侧FilterFilesWithUnknownExtensions子键(REG_DEWORD类型)
的键值改为1。
以redhat as4和oracle 10g为例。单机在安装过程依照oracle官方的文档一步步下来,只要设置好 参数、安装好必要的包,一般不会出什么问题。安装好了以后系统重启,oracle重启服务,网上很多人建议自己写脚本(其实脚本也很简单),这里讲的是用 oracle本身的脚本实现,当然不可避免最后还要写一点点的。
1、配置dbstart和dbshut
在$ORACLE_HOME/bin 中,有dbstart和dbshut这两个脚本,more dbstart看一下可以看到:
#
# $Id: dbstart.sh.pp 11-may-2005.18:18:07 vikrkuma Exp $
# Copyright (c) 1991, 2005, Oracle. All rights reserved.
#
###################################
#
# usage: dbstart
#
# This script. is used to start ORACLE from /etc/rc(.local).
# It should ONLY be executed as part of the system boot procedure.
#
# This script. will start all databases listed in the oratab file
# whose third field is a "Y". If the third field is set to "Y" and
# there is no ORACLE_SID for an entry (the first field is a *),
# then this script. will ignore that entry.
#
# This script. requires that ASM ORACLE_SID's start with a +, and
# that non-ASM instance ORACLE_SID's do not start with a +.
#
# If ASM instances are to be started with this script, it cannot
# be used inside an rc*.d directory, and should be invoked from
# rc.local only. Otherwise, the CSS service may not be available
# yet, and this script. will block init from completing the boot
# cycle.
#
# Note:
# Use ORACLE_TRACE=T for tracing this script.
#
# The progress log for each instance bringup plus Error and Warning message[s]
# are logged in file $ORACLE_HOME/startup.log. The error messages related to
# instance bringup are also logged to syslog (system log module).
# The Listener log is located at $ORACLE_HOME_LISTNER/listener.log
......
可以看出这个脚本是用来启动oracle服务的,包括listener、instance、asm instances,并且可以放到/etc/rc(.local).,同样dbshut也是起到关闭服务的作用。
配置系统使这个脚本起作 用:
1)、以root编辑/etc/oratab,类似 orcl:/u01/product/10.2.0/db_1:N 这种格式,其中orcl是你的ORACLE_SID,/u01/product/10.2.0/db_1是ORACLE_HOME,这里需要把N改为Y, 即orcl:/u01/product/10.2.0/db_1:Y这样。
2)、以oracle编辑$ORACLE_HOME/bin /dbstart,找到其中第78行:ORACLE_HOME_LISTNER=改为你自己的路径,或者可以改成 ORACLE_HOME_LISTNER=$ORACLE_HOME
保存脚本,以oracle用户运行dbshut和dbstart看是 否能关闭、启动数据库。如 果不能,一般是参数设置,根据报错找到对应位置更改。
2、 把dbstart和dbshut加到redhat启动服务中
经过上一步的配置,可以直接用 dbstart命令启动数据listener、instance、asm instances,但是还没有启动oracle10g的EM,ORACLE利用web页 面管理数据库相当方便,也 是10g的一个特色,所以应该一并启动起该服务来。
$ORACLE_HOME/bin/emctl start dbconsole
因此我们可以用rc.local或者redhat服务都可以实现要求的开机启动。下面分别说一下:
1)、利用 rc.local。直接把dbstart加到rc.local中,实现开机自动启动。这里需要注意的是必须以oracle启动该脚本。
用 root编辑/etc/rc.local,添加下面一行:
su - oracle -c "/u01/product/10.2.0/db_1/bin/dbstart"
su - oracle -c "/u01/product/10.2.0/db_1/bin/emctl start dbconsole"
这里/u01/product/10.2.0/db_1需要替换成实际的ORACLE_HOME
保存并退出后,reboot服务器测试一下,可以看到,当系统启动以后oracle监听、实例 和em都已经起来了
2)、如果我们不用rc.local,也可以加到redhat服务中。在/etc/rc.d /init.d中添加如下脚本文件,命名为oracle:
#!/bin/sh
#chkconfig: 2345 99 01
#description: ORACLE 10g Server
ORACLE_HOME=/u01/product/10.2.0/db_1
if [ ! -f $ORACLE_HOME/bin/dbstart ]
then
echo "ORACLE cannot start"
exit
fi
case "$1" in
'start')
echo "Starting Oracle Database..."
su - oracle -c "$ORACLE_HOME/bin/dbstart"
su - oracle -c "$ORACLE_HOME/bin/emctl start dbconsole"
;;
'stop')
echo "Stoping Oracle Database"
su - oracle -c "$ORACLE_HOME/bin/emctl stop dbconsole"
su - oracle -c "$ORACLE_HOME/bin/dbshut"
;;
esac
注意其中两行注释,网上很多脚本因为少了这两行不能使服务自启动:
#chkconfig: 2345 99 01
#description: ORACLE 10g Server
其中chkconfig:2345 99 01 是指脚本将为运行级2、3、4、5启动oracle 10g服务,启动优先级为99,关闭优先级为01。
然后以root权限:
# cd /etc/rc2.d
# ln -s /etc/rc.d/init.d/oracle S99oracle
# chkconfig --list oracle
# chkconfig --level 2345 oracle on
重启系统,就可以在启动的过程中看到 Starting oracle,因为我们设置的优先级为99,一般是最后启动。[OK]以后就可以了。因为要启动emctl,可能有点慢,等待的时间要稍微长一点。
启 动以后可以以root执行oracle start或者oracle stop来启动或停止服务。
三、项目管理过程
项目管理过程组包括:
² 启动过程组:定义并批准项目或阶段
n 制定项目章程
n 制度项目范围说明书(初步)
² 规划过程组:定义和细化目标,规划最佳行动方案,以实现项目或阶段所承担的目标和范围。
n 制定项目管理计划
n 范围计划编制
n 范围定义
n 创建工作分解结构(WBS)
n 活动定义
n 活动排序
n 活动资源估算
n 活动历时估算
n 制定进度计划
n 成本估算
n 成本预算
n 质量计划编制
n 人力资源计划编制
n 组建项目团队
n 沟通计划编制
n 风险管理计划编制
n 风险识别
n 定量风险分析
n 制定风险应对计划
n 计划采购
n 编制合同
² 执行过程组:整合人员和其他资源,在项目的生命期或某个阶段执行项目管理计划。
n 指导和管理项目执行
n 执行质量保证
n 项目团队建设
n 信息发布
n 获取供方相应
n 选择供方
² 监控过程组:要求定期测量和监控进展,识别与项目管理计划的偏差,以便在必要时采取纠正措施,确保项目或阶段目标达成。
n 监督和控制项目工作
n 整体变更控制
n 范围验证
n 范围控制
n 进度控制
n 成本控制
n 执行质量控制
n 管理项目团队
n 绩效报告
n 管理项目关系人
n 风险监督和控制
n 合同管理
² 收尾过程组:正式接受产品、服务或工作成果,有序的结束项目或阶段。
n 项目收尾
n 合同收尾
同项目管理各过程有关的基本概念之一是“计划—执行—检查—行动”循环。
项目过程组和项目管理知识领域映射关系:
项目管理过程组
知识领域
|
启动管理过程组
|
计划过程组
|
执行过程组
|
监督和控制过程组
|
收尾过程组
|
项目整体管理
|
制定项目章程
制度项目范围说明书(初步)
|
项目管理规划
|
指导管理项目执行
|
监控和控制项目工作
整体变更控制
|
项目收尾
|
项目范围管理
|
|
范围规划
范围定义
建立WBS
|
|
范围验证
范围控制
|
|
项目时间管理
|
|
活动定义
活动排序
活动资源估算
活动历时估算
制定进度计划
|
|
进度控制
|
|
项目成本管理
|
|
成本估算
成本预算
|
|
成本控制
|
|
项目质量管理
|
|
质量规划
|
执行质量保证
|
执行质量控制
|
|
项目人力资源管理
|
|
人力资源计划编制
团队组建
|
团队建设
|
团队管理
|
|
项目沟通管理
|
|
沟通计划编制
|
信息发布
|
绩效报告
干系人管理
|
|
项目风险管理
|
|
风险管理计划编制
风险识别
定性风险分析
定量风险风险
风险响应规划
|
|
风险监控
|
|
项目采购管理
|
|
采购规划
计划签约
|
请求卖方回应
买房选择
|
合同管理
|
合同执行
|
项目管理学习笔记(二、项目生命期和组织)
二、项目生命期和组织
本章重点:项目生命期、项目关系人和组织的影响
信息系统项目的生命期模型
1、 瀑布模型:
一般将软件开发可以分为:可行性分析(计划)、需求分析、软件设计(概要、详细设计)、编码(含单元测试)、测试、运行维护等一个阶段。(阴影部分可看成定义阶段、开发阶段和维护阶段)
特点:
² 从上一项开发活动接受该项活动的工作对象作为输入
² 利用这一输入,实施该项活动应完成的工作内容
² 给出该项活动的工作成果,做为输出传给下一项开发活动
² 对该项活动的实施工作成果进行评审。若其工作成果得到确认,则继续下一项活动;否则返回前一项,甚至更前。
2、 迭代模型:
初始阶段:系统地阐述项目的范围,选择可行的系统架构,计划和准备业务案例
细化阶段:细化构想,细化过程和基础设施,细化构架并选择构件
构造阶段:资源管理、控制和过程最优化,完成构件的开发并依评价标准进行测试,依构想的验收标准评估产品的发布。
移交阶段:同步并使并发的构造增量集成到一致的实施基线中,与实施有关的工程活动(商业包装和生产、人员培训等),根据完整的构想和需求的验收标准评估实施基线。

3、 螺旋模型:
是一个演化过程模型,将原型实现的迭代特征与线性顺序(瀑布)模型中控制的和系统化的方面结合起来。使得软件的增量版本的快速开发成为可能。在螺旋模型中软件开发是一系列的增量发布。

4个象限分别标志每个周期所划分的四个阶段:制定计划、风险分析、实施工程和客户评估。螺旋模型强调了风险分析。
项目干系人(Project Stakeholder):也称利害相关者,是积极参与项目、或其利益因项目的实施或完成而受到积极或消极影响的个人和组织,他们还会对项目的目标和结果施加影响。
每个项目都包括如下的项目关键干系人:
² 项目经理(Project Manager)
² 顾客、客户(Customer/User)
² 执行组织(Performing Organization)
² 项目团队成员(Project Team Members)
² 项目管理团队(Project Management Team)
² 出资人(Sponsor)
² 有影响力的人(Influencers)
² 项目管理办公室(PMO)
组织的结构:
组织类型
项目特点
|
职能型组织
|
矩阵型组织
|
项目型组织
|
弱矩阵型组织
|
平衡矩阵型组织
|
强矩阵型组织
|
项目经理的权利
|
很小和没有
|
有限
|
小~中等
|
中等~大
|
大~全权
|
组织中全职参与项目工作的职员比例
|
没有
|
0%~25%
|
15%~60%
|
50%~95%
|
85%~100%
|
项目经理的职位
|
部分时间
|
部分时间
|
全时
|
全时
|
全时
|
项目经理的一般头衔
|
项目协调员/项目主管
|
项目协调员/项目主管
|
项目经理/项目主管
|
项目经理/计划经理
|
项目经理/计划经理
|
项目管理行政人员
|
部分时间
|
部分时间
|
部分时间
|
全时
|
全时
|
项目管理系统:指用于管理项目的工具、技术、方法、资源和规程。
项目管理学习笔记
本文为本人学习项目管理时所整理笔记将会持续更新,有兴趣的人可以无限制复制----gf7
一、项目管理绪论
项目:提供某项独特的产品、服务或成果所进行的临时的一次性努力。是用有限的资源、有限的时间为特定客户完成特定目标的一次性工作。
项目的特点:临时性、独特性和渐进性。
信息系统项目的特点:
² 目标不明确
² 需求变化频繁
² 智力密集型
² 设计队伍庞大
² 设计人员高度专业化
² 涉及的承包商多
² 各级承包商分布在各地,互相联系复杂
² 系统集成项目中需研制开发大量的软硬件系统
² 项目生命期通常较短
² 通常要采用大量的新技术
² 使用与维护的要求非常复杂
项目管理的知识领域:
² 项目管理知识体系
² 应用领域知识、标准和规定
² 项目环境知识
² 通用的管理知识和技能
² 软技能(处理人际关系技能)
国际项目管理协会(IMPA)的项目管理专业人员资质认证分为4级:
A级(Level A):认证的高级项目经理(Certificated Projects Director CPD),有能力指导一个公司(或一个分支机构),包括有诸多项目的负责规划,有能力管理该组织的所有项目,或者管理一项国际合作的复杂项目。
B级:认证的项目经理(Certificated Projects Manager CPM),可以管理一般复杂的项目。
C级:认证的项目管理专家(Certificated Projects Management Professional,PMP)能够管理一般的非复杂项目。
D级:认证的项目管理专业人员(Certificated Projects Management Practitioner,PMF)具有项目管理的基本知识,并可以将他们应用于某些领域。
项目管理的知识体系(Project Management Body of Knowledge,PMBOK),把项目管理划分为9个知识领域: 范围管理、时间管理、成本管理、质量管理、人力资源管理、沟通管理、采购管理、风险管理和整体管理。
对项目经理的一般要求:
² 广博的知识
² 丰富的经营
² 良好的协调能力
² 良好的职业道德
² 良好的沟通与表达能力
² 良好的领导能力
怎样做个好的项目经理:
² 真正理解项目经理的角色
² 重视项目团队的管理,奖罚分明
² 计划、计划、再计划
² 真正理解“一把手工程”
² 切记注重用户参与
英文缩写:
PMO(项目管理办公室)
WBS(Work Breakdown Structure 工作分解结构)
CPM(Critical Path Method,关键路径法)
PERT(Program Evaluation And Review Technique,计划评审技术)
EV(Earned Value 挣值)
IPMA (International Public Management Association 国际项目管理协会)
ICB(IMPA Competence Baseline 国际项目管理资质标准)
IPMP(International Project Management Professional 国际项目管理专业资质认证)
一位朋友说他们正在做EAI的项目,对于EAI,没有接触太深,以前项目中有这一块,却没怎么参与。于是问了一句,"EAI究竟是服务于什么目的"?提起这个名词,在我脑海中蹦出的关键词是诸如实时、总线、消息等,然而,这些似乎只是它的技术特征。
类似的名词包括EII和ETL,ETL是BI项目中必有的部分,也是目前每个项目戏份最重的环节。ETL有一种定义,如"抽取、转换和装载,为了分析的目的,将数据从多种数据源抽取,经过转换、清洗,装载到另一个数据库的过程,包括数据集市和数据仓库,或者是另一个操作型系统",我不知道这是谁的定义,恐怕也恐怕很难有权威的定义。在这个定义中,ETL是广义的,它是数据流动的过程,没有说它究竟是批量的或是实时的。因此,按照这个定义,EAI也就像是ETL。
EAI,全名为企业应用集成,这提升到一个比较高的层面,相比之下,"数据"显得太微观,太底层了。不错,现实的情况是企业的IT环境中,大量不同的系统同时并存,缺乏总体规划。在这种情况下,提出应用集成也是形势所逼。比如联通的经营分析和客户维系挽留系统,缺乏规划的时候,他们就有功能重叠的地方,各自的厂商为了自己的利益,不可能顾及"应该"如何,只是将自己的蛋糕划分得大一些才好。因此,诸如"客户价值模型"这样得东西就会抢来抢去。可能这种交叉应用的存在,才导致人们对应用集成的愿望,他们希望能够统一地看这些不同的应用,就像一个完整的大系统在运行一样。
但显然,如果达到这样的程度,理想的程度,并非一种技术就能搞定。所以,EAI的定义显得比ETL定义更加"虚头八脑",而在实际项目中看来,EAI的主要功能就是数据的集成,在多个应用之间共享数据,联通里面一般管这叫做"交互性"。技术实现上,它更像是CDC(变化数据捕获)+ETL。
至于EII,名称上意思为"企业信息集成",按照数据、信息到知识这个从低到高的层次,EII听起来又比ETL高级一些。然而对它,更加没有深入了解。也不明白它为什么会蹦出这个名词,是和ETL、EAI并列还是有取代他们的意思。从它的定义来看,EII是建立了一个虚拟的数据库,用户向这个虚拟库提交查询,而EII将这种查询物理地分布到各个不同的数据源中,然而返回数据,对于用户来说,他没有意识到这批数据是来自不同应用、不同数据库的。
喔,很酷,不过难度不小,因为这不是技术问题。假设理想的情况下,能够为数据源建立详尽的、一致的元数据,能够有一个引擎实现这种分布式查询,当然可以EII。然而我们不是生活在理想国,为不同的数据源建立一致的元数据几乎是不可能,这涉及到各个系统厂商的管理、系统设计以及维护能力,无法仅仅通过技术手段保证的。所以,EII,我只能暂且将它看作是未来理想。
在建立数据仓库时,
ETL通常都采用批处理的方式,一般来说是每天的夜间进行跑批。
随着数据仓库技术的逐步成熟,企业对数据仓库的时间延迟有了更高的要求,也就出现了目前常说的实时ETL(Real-Time ETL)。实时ETL是数据仓库领域里比较新的一部分内容。
在构建实时ETL架构的数据仓库时,有几种技术可供选择。
1.微批处理(microbatch ETL,MB-ETL)
微批处理的方式和我们通常的ETL处理方式很相似,但是处理的时间间隔要短,例如间隔一个小时处理一次。
2.企业应用集成(Enterprise Application Integration,EAI)
EAI也称为功能整合,通常由中间件来完成数据的交互。而通常的ETL称为数据整合。
对实时性要求非常高的系统,可以考虑使用EAI作为ETL的一个工具,可以提供快捷的数据交互。不过在数据量大时采用EAI工具效率比较差,而且实现起来相对复杂。
3.CTF(Capture, Transform and Flow)
CTF是一类比较新的数据整合工具。它采用的是直接的数据库对数据库的连接方式,可以提供秒级的数据。CTF的缺点是只能进行轻量级的数据整合。通常的处理方式是建立数据准备区,采用CTF工具在源数据库和数据准备区的数据库之间相连接。数据进入数据准备区后再经过其他处理后迁移入数据仓库。
4.EII(Enterprise Information Integration)
EII是另一类比较新的数据整合软件,可以给企业提供实时报表。EII的处理方式和CTF很相似,但是它不将数据迁移入数据准备区或者数据仓库,而是在抽取转换后直接加载到报表中。
在实际建立实时ETL架构的数据仓库时,可以在MB-ETL, EAI, CTF, EII及通常的ETL中作出选择或者进行组合。
Oracle数据库提供了几种不同的数据库启动和关闭方式,本文将详细介绍这些启动和关闭方式之间的区别以及它们各自不同的功能。
一、启动和关闭Oracle数据库
对于大多数Oracle DBA来说,启动和关闭Oracle数据库最常用的方式就是在命令行方式下的Server Manager。从Oracle 8i以后,系统将Server Manager的所有功能都集中到了SQL*Plus中,也就是说从8i以后对于数据库的启动和关闭可以直接通过SQL*Plus来完成,而不再另外需要Server Manager,但系统为了保持向下兼容,依旧保留了Server Manager工具。另外也可通过图形用户工具(GUI)的Oracle Enterprise Manager来完成系统的启动和关闭,图形用户界面Instance Manager非常简单,这里不再详述。
要启动和关闭数据库,必须要以具有Oracle 管理员权限的用户登陆,通常也就是以具有SYSDBA权限的用户登陆。一般我们常用INTERNAL用户来启动和关闭数据库(INTERNAL用户实际上是SYS用户以SYSDBA连接的同义词)。Oracle数据库的新版本将逐步淘汰INTERNAL这个内部用户,所以我们最好还是设置DBA用户具有SYSDBA权限。
二、数据库的启动(STARTUP)
启动一个数据库需要三个步骤:
1、 创建一个Oracle实例(非安装阶段)
2、 由实例安装数据库(安装阶段)
3、 打开数据库(打开阶段)
在Startup命令中,可以通过不同的选项来控制数据库的不同启动步骤。
1、STARTUP NOMOUNT
NONOUNT选项仅仅创建一个Oracle实例。读取init.ora初始化参数文件、启动后台进程、初始化系统全局区(SGA)。Init.ora文件定义了实例的配置,包括内存结构的大小和启动后台进程的数量和类型等。实例名根据Oracle_SID设置,不一定要与打开的数据库名称相同。当实例打开后,系统将显示一个SGA内存结构和大小的列表,如下所示:
SQL> startup nomount
ORACLE 例程已经启动。
Total System Global Area 35431692 bytes
Fixed Size 70924 bytes
Variable Size 18505728 bytes
Database Buffers 16777216 bytes
Redo Buffers 77824 bytes
2、STARTUP MOUNT
该命令创建实例并且安装数据库,但没有打开数据库。Oracle系统读取控制文件中关于数据文件和重作日志文件的内容,但并不打开该文件。这种打开方式常在数据库维护操作中使用,如对数据文件的更名、改变重作日志以及打开归档方式等。在这种打开方式下,除了可以看到SGA系统列表以外,系统还会给出"数据库装载完毕"的提示。
3、STARTUP
该命令完成创建实例、安装实例和打开数据库的所有三个步骤。此时数据库使数据文件和重作日志文件在线,通常还会请求一个或者是多个回滚段。这时系统除了可以看到前面Startup Mount方式下的所有提示外,还会给出一个"数据库已经打开"的提示。此时,数据库系统处于正常工作状态,可以接受用户请求。
如果采用STARTUP NOMOUNT或者是STARTUP MOUNT的数据库打开命令方式,必须采用ALTER DATABASE命令来执行打开数据库的操作。例如,如果你以STARTUP NOMOUNT方式打开数据库,也就是说实例已经创建,但是数据库没有安装和打开。这是必须运行下面的两条命令,数据库才能正确启动。
ALTER DATABASE MOUNT;
ALTER DATABASE OPEN;
而如果以STARTUP MOUNT方式启动数据库,只需要运行下面一条命令即可以打开数据库:
ALTER DATABASE OPEN.
4、其他打开方式
除了前面介绍的三种数据库打开方式选项外,还有另外其他的一些选项。
(1) STARTUP RESTRICT
这种方式下,数据库将被成功打开,但仅仅允许一些特权用户(具有DBA角色的用户)才可以使用数据库。这种方式常用来对数据库进行维护,如数据的导入/导出操作时不希望有其他用户连接到数据库操作数据。
(2) STARTUP FORCE
该命令其实是强行关闭数据库(shutdown abort)和启动数据库(startup)两条命令的一个综合。该命令仅在关闭数据库遇到问题不能关闭数据库时采用。
(3) ALTER DATABASE OPEN READ ONLY;
该命令在创建实例以及安装数据库后,以只读方式打开数据库。对于那些仅仅提供查询功能的产品数据库可以采用这种方式打开。
三、数据库的关闭(SHUTDOWN)
对于数据库的关闭,有四种不同的关闭选项,下面对其进行一一介绍。
1、SHUTDOWN NORMAL
这是数据库关闭SHUTDOWN命令的确省选项。也就是说如果你发出SHUTDOWN这样的命令,也即是SHUTDOWN NORNAL的意思。
发出该命令后,任何新的连接都将再不允许连接到数据库。在数据库关闭之前,Oracle将等待目前连接的所有用户都从数据库中退出后才开始关闭数据库。采用这种方式关闭数据库,在下一次启动时不需要进行任何的实例恢复。但需要注意一点的是,采用这种方式,也许关闭一个数据库需要几天时间,也许更长。
2、SHUTDOWN IMMEDIATE
这是我们常用的一种关闭数据库的方式,想很快地关闭数据库,但又想让数据库干净的关闭,常采用这种方式。
当前正在被Oracle处理的SQL语句立即中断,系统中任何没有提交的事务全部回滚。如果系统中存在一个很长的未提交的事务,采用这种方式关闭数据库也需要一段时间(该事务回滚时间)。系统不等待连接到数据库的所有用户退出系统,强行回滚当前所有的活动事务,然后断开所有的连接用户。
3、SHUTDOWN TRANSACTIONAL
该选项仅在Oracle 8i后才可以使用。该命令常用来计划关闭数据库,它使当前连接到系统且正在活动的事务执行完毕,运行该命令后,任何新的连接和事务都是不允许的。在所有活动的事务完成后,数据库将和SHUTDOWN IMMEDIATE同样的方式关闭数据库。
4、SHUTDOWN ABORT
这是关闭数据库的最后一招,也是在没有任何办法关闭数据库的情况下才不得不采用的方式,一般不要采用。如果下列情况出现时可以考虑采用这种方式关闭数据库。
1、 数据库处于一种非正常工作状态,不能用shutdown normal或者shutdown immediate这样的命令关闭数据库;
2、 需要立即关闭数据库;
3、 在启动数据库实例时遇到问题;
所有正在运行的SQL语句都将立即中止。所有未提交的事务将不回滚。Oracle也不等待目前连接到数据库的用户退出系统。下一次启动数据库时需要实例恢复,因此,下一次启动可能比平时需要更多的时间。
表1可以清楚地看到上述四种不同关闭数据库的区别和联系。
表1 Shutdown数据库不同方式对比表
关闭方式 |
A |
I |
T |
N |
允许新的连接 |
× |
× |
× |
× |
等待直到当前会话中止 |
× |
× |
× |
√ |
等待直到当前事务中止 |
× |
× |
√ |
√ |
强制CheckPoint,关闭所有文件 |
× |
√ |
√ |
√ |
其中:A-Abort I-Immediate T-Transaction N-Nornal
一 JSP2.0与JSP1.2比较
JSP 2.0是对JSP 1.2的升级,新增功能:
1. Expression Language (我平常都叫EL表达式的)
2. 新增Simple Tag和Tag File
3.web.xml新增<jsp:config>元素
- 特别说明:<jsp-config> 元素主要用来设定JSP相关配置,<jsp-config> 包括<taglib>和<jsp-property-group>子元素。
-
- (1)其中<taglib>以前的Jsp1.2中就有的,taglib主要作用是作为页面taglib标签中的uri和tld文件的一个映射关系
-
- (2)其中<jsp-property-group>是JSP2.0种新增的元素。
- <jsp-property-group> 主要包括8个子元素,它们分别是:
-
- <jsp-property-group>
-
- <description>
- 设定的说明
- </description>
-
- <display-name>设定名称</display-name>
-
- <url-pattern>设定值所影响的范围</url-pattern>
-
- <el-ignored>若为true则不支持EL语法</el-ignored>
-
- <page-encoding>ISO-8859-1</page-encoding>
-
- <scripting-invalid> 若为true则不支持<% scripting%> 语法</scripting-invalid>
-
- <include-prelude>设置JSP网页的抬头,扩展名为.jspf </include-prelude>
-
- <include-coda>设置JSP网页的结尾,扩展名为.jspf</include-coda>
-
- </jsp-property-group>
-
- 例如: 其中抬头程序:
- prelude.jspf
- <br>
- <center>
- 文本内容
- </center>
- <hr>
-
- 结尾程序:
- coda.jspf
- <br>
- <center>
- 文本内容
- </center>
- <hr>
二、Servlet个版本比较
servlet 2.3 新增功能:
2000年10月份出来
Servlet API 2.3中最重大的改变是增加了filters(过滤器)
servlet 2.4 新增功能:
2003年11月份出来
1、web.xml DTD改用了XML Schema;
Servlet 2.3之前的版本使用DTD作为部署描述文件的定义,其web.xml的格式为如下所示:
xml 代码
- <?xml version="1.0" encoding="IS0-8859-1"?>
- <!DOCTYPE web-app
- PUBLIC "-//sunMicrosystems,Inc.//DTD WebApplication 2.3f//EN"
- "http://java.sun.com/j2ee/dtds/web-app_2.3.dtd">
- <web-app>
- .......
- </web-app>
Servlet 2.4版首次使用XML Schema定义作为部署描述文件,这样Web容器更容易校验web.xml语法。同时XML Schema提供了更好的扩充性,其web.xml中的格式如下所示:
xml 代码
- <?xml version="1.0" encoding="UTF-8"?>
- <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
- xmlns:workflow="http://www.workflow.com"
- xmins:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
- http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
- .........
- </web-app>
注意: 改为Schema后主要加强了两项功能:
(1) 元素不依照顺序设定
(2) 更强大的验证机制
主要体现在:
a.检查元素的值是否为合法的值
b.检查元素的值是否为合法的文字字符或者数字字符
c.检查Servlet,Filter,EJB-ref等等元素的名称是否唯一
2.新增Filter四种设定:REQUEST、FORWARD、INCLUDE和ERROR。
3.新增Request Listener、Event和Request Attribute Listener、Enent。
4.取消SingleThreadModel接口。当Servlet实现SingleThreadModel接口时,它能确保同时间内,只能有一个thread执行此Servlet。
5.<welcome-file-list>可以为Servlet。
6.ServletRequest接口新增一些方法。
public String getLocalName()
public String getLocalAddr()
public int getLocalPort()
public int getRemotePort()
Servlet 2.5的新特征
2005年9月发布Servlet 2.5
Servlet2.5一些变化的介绍:
1) 基于最新的J2SE 5.0开发的。
2) 支持annotations 。
3) web.xml中的几处配置更加方便。
4) 去除了少数的限制。
5) 优化了一些实例
servlet的各个版本对监听器的变化有:
(1)servlet2.2和jsp1.1
新增Listener:HttpSessionBindingListener
新增Event: HttpSessionBindingEvent
(2)servlet2.3和jsp1.2
新增Listener:ServletContextListener,ServletContextAttributeListener
,HttpSessionListener,HttpSessionActivationListener,HttpSessionAttributeListener
新增Event: ServletContextEvent,ServletContextAttributeEvent,HttpSessionEvent
(3)servlet2.4和jsp2.0
新增Listener:ServletRequestListener,ServletRequestAttribureListener
新增Event: ServletRequestEvent,ServletRequestAttributeEvent
三、J2EE规范版本比较
1.J2EE的发展
1997年Servlet技术的产生以及紧接着JSP的产生,为Java对抗PHP,ASP等等服务器端语言带来了筹码。1998年,Sun发布了EJB1.0标准,至此J2EE平台的三个核心技术都已经出现。于是,1999年,Sun正式发布了J2EE的第一个版本。并与1999年底发布了J2EE1.2,在2001年发布了J2EE1.3,2003年发布了J2EE1.4。
2.J2EE1.3
J2EE1.3的架构,其中主要包含了Applet容器,Application Client容器,Web容器和EJB容器,并且包含了Web Component,EJB Component,Application Client Component,以JMS,JAAS,JAXP,JDBC,JAF,JavaMail,JTA等等技术做为基础。
1.3中引入了几个值得注意的功能:Java消息服务(定义了JMS的一组API),J2EE连接器技术(定义了扩展J2EE服务到非J2EE应用程序的标准),XML解析器的一组Java API,Servlet2.3,JSP1.2也都进行了性能扩展与优化,全新的CMP组件模型和MDB(消息Bean)。
3.J2EE1.4
J2EE1.4大体上的框架和J2EE1.3是一致的,1.4增加了对Web服务的支持,主要是Web Service,JAX-RPC,SAAJ,JAXR,还对EJB的消息传递机制进行了完善(EJB2.1),部署与管理工具的增强(JMX),以及新版本的Servlet2.4和JSP2.0使得Web应用更加容易。
四、Tomcat版本比较
Tomcat 3.x
servlet2.2和jsp1.1标准
Tomcat 4.x
Servlet 2.3 和 JSP 1.2 版本
Tomcat 5.x
Servlet 2.4或2.5 和 JSP 2.0 版本
五、JDK版本比较
已发行的版本:
版本号 名称 中文名 发布日期
JDK 1.1.4 Sparkler 宝石 1997-09-12
JDK 1.1.5 Pumpkin 南瓜 1997-12-13
JDK 1.1.6 Abigail 阿比盖尔--女子名 1998-04-24
JDK 1.1.7 Brutus 布鲁图--古罗马政治家和将军 1998-09-28
JDK 1.1.8 Chelsea 切尔西--城市名 1999-04-08
J2SE 1.2 Playground 运动场 1998-12-04
J2SE 1.2.1 none 无 1999-03-30
J2SE 1.2.2 Cricket 蟋蟀 1999-07-08
J2SE 1.3 Kestrel 美洲红隼 2000-05-08
J2SE 1.3.1 Ladybird 瓢虫 2001-05-17
J2SE 1.4.0 Merlin 灰背隼 2002-02-13
J2SE 1.4.1 grasshopper 蚱蜢 2002-09-16
J2SE 1.4.2 Mantis 螳螂 2003-06-26
将发行的版本:
J2SE 5.0 (1.5.0) Tiger 老虎 已发布了Beta版本
J2SE 5.1 (1.5.1) Dragonfly 蜻蜓 未发布
J2SE 6.0 (1.6.0) Mustang 野马 未发布
一些网友在Xp下安装了Windows 7(适用于Vista/WS2008)双系统,试用一段时间之后,新鲜过了,就准备卸载掉Windows 7。下面我把我在xp-windows 7双系统中卸载windows 7的方法写下来与大家分享!其中第1-5步网上很多,恕不详述,本文重点讲述第6步,即xp-windows 7(适用于Vista/WS2008)双系统在卸载windows 7后删除xp系统主引导分区的残留文件的详细步骤(注:本方法也即取得对系统文件完全控制的方法)
启动电脑进入Xp系统后按以下步骤操作,
1.插入刻好的 Windows 7 安装光盘(或者用虚拟光驱加载Windows 7镜像)。
2.依次单击“开始”按钮 、“所有程序”、“附件”,右键单击“命令提示符”,然后单击“用管理员帐户运行”。
3.键入 X:"boot"bootsect.exe /nt52 all /force,然后按 Enter。注:X:"代表你的光驱盘符,或者虚拟光驱盘符。
例如,如果 DVD 驱动器号是 F,则键入 F:"boot"bootsect.exe /nt52 ALL /force。
4.弹出 Windows Vista 安装光盘。
5.重新启动计算机。
计算机将使用已安装的以前版本的 Windows 启动。Windows 7系统的启动项不见了,Early Version windows也不见了,只剩下Windows xp的启动项了,基本成功。
6、删除在启动盘中的:
*Boot 文件夹
*Boot.BAK
*bootmgr
*BOOTSECT.BAK
7、格式化windows 7所在的分区,或者修改权限后删除Vista的文件夹。
下面详述第6步即取得对系统文件(夹)完全控制权限的方法:
新的Vista/WS2008/Win 7系统启动与XP等之前系统已经有了很大改变,用一个启动引导程序代替了以前单一的boot.ini文件。删除系统之后,在C盘XP系统分区留下了Boot 文件夹、Boot.BAK、bootmgr和BOOTSECT.BAK一个文件夹,三个文件。其中boot.bak和bootsect.bak很容易删除,剩下的boot文件夹和bootmgr文件删除时,却会提示出错信息。查看了文件和文件夹权限,发现已经被锁定,所以,思路就是通过修改文件夹权限使得当前用户可以正常删除文件。以下即为详细操作步骤:
(1)、首先请用管理员帐户登录XP系统。
(2)、进入我的电脑--工具--文件夹选项--查看,把“使用简单文件共享(推荐)”前面的勾去掉。这样,你才能进入属性里的“安全”选项卡,修改文件和文件夹权限。
删除boot文件夹:
(3)、右键点击boot文件夹--属性--安全--高级。此时,你所有的权限都无法编辑。
(4)、在“所有者”选项卡中,先将文件夹的所有者移交给“Administrators”组,记得选择下面的“替换子容器及对象的所有者”。
(5)、再打开“审核”选项卡,将“用在此显示的那些可以应用到子对象的项目替代所有子对象的审核项目”。点击“添加”按钮,在文本框内输入“administrators”,再点击右侧的“检查名称”,“确定”,进入审核项目权限设置,选择成功和失败都是“完全控制”,“应用”,退出boot文件夹属性。可能会有提示让你配置本地计算机策略,不用管它。
(6)、再次进入boot文件夹属性--安全,你会发现对于当前管理员帐户,你已经可以选择对该文件夹的控制权限了,当然选择允许“完全控制”。另外,你在“组或用户名称”这里还会发现类似S-1-5-80-956008885-3418522649-1831038044-1853292631-2271478464这样的用户,这应该就是原来的vista/ws2008/win7的用户。
(7)、还没完,你还需要再次进入高级--权限选项卡,对于“Administrators”用户组,将下面的两个选项都勾上,目的是将对boot文件夹的权限设置,被所有子文件和文件夹所继承(原来其子文件夹和文件并没有完全继承)。确定,退出。
好了,这回,你终于可以把boot文件夹删除了!
由于操作过程中没想到贴图,只有全部操作完成之后,boot文件夹权限相关信息:

图1 Boot文件夹属性

图2 Boot文件夹权限选项卡

图3 Boot文件夹审核选项卡

图4 Boot文件夹所有者选项卡
删除bootmgr文件:
只需要做删除boot文件夹的(3)、(4)、(5)、(6)步即可,而且由于只有单个文件,简单很多,不再赘述。
总结:其实对于多分区中安装多系统来说,经常会遇到这样的情况。我在以前的本本上装XP和WS2003双系统,WS2003想要查看XP分区music中的音乐文件,也需要夺权,只是没有现在这么麻烦罢了。Anyway,简单来说,就那么几步:添加当前XP管理员用户至审核组以获得审核权限-->夺取文件所有者权限-->删除原所有者-->完全控制,并应用到子文件和文件夹-->删除!
构建一个企业级的应用系统,往往数据库成为最终的一个负载瓶颈,在我们优化完sql语句、优化完应用程序之后,数据库的调优必不可少,下面就基于sql查询的命中率的oracle调优做一个简单的说明。
1.先检验数据库的查询命中率,请执行下面的2组sql语句,并且分别记录修改之前的数值。
第一组sql语句如下:
select 100- (j.value-( a.value+b.value )) /(u.value+v.value-a.value-b.value)*100 as 命中率 from
(select value from v$sysstat where name ='physical reads direct' ) a,
(select value from v$sysstat where name ='physical reads direct (lob)' ) b,
(select value from v$sysstat where name ='physical reads') j,
(select value from v$sysstat where name ='consistent gets') u,
(select value from v$sysstat where name = 'db block gets') v ;
第二组sql语句如下:
select sum(gets) "请求存取数",sum(getmisses) "不命中数" , (1-sum(getmisses)/sum(gets) )*100 "命中率"
from v$rowcache;
2.如果第一组sql语句执行的结果是<90%,则说明需要调整oracle数据库的内存(SGA的大小),第二组sql语句作为一个参照。
一般经验:在 1G 的内存的服务器上,我们能分配给SGA的内存大约为400—500M 。若是2G的内存,大约可以分到1G的内存给 SGA,8G 的内存可以分到5G的内存给SGA。
考虑到数据库服务器的机器内存大小为2G, 可以按照以下脚本执行修改:
【注:请用具有dba权限的用户登录“login as sysdba”登录,可以用pl/sql工具】
--修改前备份一下sqfile
create pfile='d:\oracle\ora9init.ora' from spfile;
--修改共享池大小
alter system set shared_pool_size =256M scope=spfile;
--修改缓冲池大小
alter system set db_cache_size=896M scope=spfile;
--大缓冲池
alter system set large_pool_size=100m scope=spfile;
--修改链接进程数
alter system set processes=1500 scope=spfile;
--会话数
alter system set sessions=900 scope=spfile;
--事务数
alter system set transactions=900 scope=spfile;
--打开游标数
alter system set open_cursors =1000 scope=spfile;
3.用pl/sql工具修改了这些参数之后,需要重启oracle服务。当重启之后再查执行开始的2组sql语句,对比数值的差异。
前言
近来公司技术,研发都在问我关于内存参数如何设置可以优化oracle的性能,所以抽时间整理了这篇文档,以做参考.
目的
希望通过整理此文档,使大家对oracle内存结构有一个全面的了解,并在实际的工作中灵活应用,使oracle的内存性能达到最优配置,提升应用程序反应速度,并进行合理的内存使用.
内容
实例结构
oracle实例=内存结构+进程结构
oracle实例启动的过程,其实就是oracle内存参数设置的值加载到内存中,并启动相应的后台进程进行相关的服务过程。
进程结构
oracle进程=服务器进程+用户进程
几个重要的后台进程:
DBWR:数据写入进程.
LGWR:日志写入进程.
ARCH:归档进程.
CKPT:检查点进程(日志切换;上一个检查点之后,又超过了指定的时间;预定义的日志块写入磁盘;例程关闭,DBA强制产生,表空间offline)
LCKn(0-9):封锁进程.
Dnnn:调度进程.
内存结构(我们重点讲解的)
内存结构=SGA(系统全局区)+PGA(程序全局区)
SGA:是用于存储数据库信息的内存区,该信息为数据库进程所共享。它包含Oracle 服务器的数据和控制信息,它是在Oracle服务器所驻留的计算机的实际内存中得以分配,如果实际内存不够再往虚拟内存中写
我们重点就是设置SGA,理论上SGA可占OS系统物理内存的1/2——1/3
原则:SGA+PGA+OS使用内存<总物理RAM
SGA=((db_block_buffers*blocksize)+(shared_pool_size+large_pool_size+java_pool_size+log_buffers)+1MB
1、SGA系统全局区.(包括以下五个区)
A、数据缓冲区:(db_block_buffers)存储由磁盘数据文件读入的数据。
大小: db_block_buffers*db_block_size
Oracle9i设置数据缓冲区为:Db_cache_size
原则:SGA中主要设置对象,一般为可用内存40%。
B、共享池:(shared_pool_size):数据字典,sql缓冲,pl/sql语法分析.加大可提速度。
原则:SGA中主要设置对象,一般为可用内存10%
C、日志缓冲区:(log_buffer)存储数据库的修改信息.
原则:128K ---- 1M 之间,不应该太大
D 、JAVA池(Java_pool_size)主要用于JAVA语言的开发.
原则:若不使用java,原则上不能小于20M,给30M通常就够了
E、 大池(Large_pool_size) 如果不设置MTS,主要用于数据库备份恢复管理器RMAN。
原则:若不使用MTS,5---- 10M 之间,不应该太大
SGA=. db_block_buffers*db_block_size+ shared_pool_size+ log_buffer+Java_pool+size+large_pool_size
原则: 达到可用内存的55-58%就可以了.
2、PGA程序全局区
PGA:包含单个服务器进程或单个后台进程的数据和控制信息,与几个进程共享的SGA 正相反PGA 是只被一个进程使用的区域,PGA 在创建进程时分配在终止进程时回收.
A、Sort_area_size 用于排序所占内存
B、Hash_area_size 用于散列联接,位图索引
这两个参数在非MTS下都是属于PGA ,不属于SGA,是为每个session单独分配的,在我们的服务器上除了OS + SGA,一定要考虑这两部分
原则:OS 使用内存+SGA+并发执行进程数*(sort_area_size+hash_ara_size+2M) < 0.7*总内存
实例配置
一:物理内存多大
二:操作系统估计需要使用多少内存
三:数据库是使用文件系统还是裸设备
四:有多少并发连接
五:应用是OLTP 类型还是OLAP 类型
基本掌握的原则是, db_block_buffer 通常可以尽可能的大,shared_pool_size 要适度,log_buffer 通常大到几百K到1M就差不多了
A、如果512M RAM 单个CPU db_block_size 是8192 bytes
SGA=0.55*512M=280M左右
建议 shared_pool_size = 50M, db_block_buffer* db_block_size = 200M
具体: shared_pool_size =52428800 #50M
db_block_buffer=25600 #200M
log_buffer = 131072 # 128k (128K*CPU个数)
large_pool_size=7864320 #7.5M
java_pool_size = 20971520 # 20 M
sort_area_size = 524288 # 512k (65k--2M)
sort_area_retained_size = 524288 # MTS 时 sort_area_retained_size = sort_area_size
B、如果1G RAM 单个CPU db_block_size 是8192 bytes
SGA=0.55*1024M=563M左右
建议 shared_pool_size = 100M , db_block_buffer* db_block_size = 400M
具体: shared_pool_size=104857600 #100M
db_block_buffer=51200 #400M
log_buffer = 131072 # 128k (128K*CPU个数)
large_pool_size=15728640 #15M
java_pool_size = 20971520 # 20 M
sort_area_size = 524288 # 512k (65k--2M)
sort_area_retained_size = 524288 # MTS 时 sort_area_retained_size = sort_area_size
C、如果2G 单个CPU db_block_size 是8192 bytes
SGA=0.55*2048M=1126.4M左右
建议 shared_pool_size = 200M , db_block_buffer *db_block_size = 800M
具体: shared_pool_size=209715200 #200M
db_block_buffer=103192 #800M
log_buffer = 131072 # 128k (128K*CPU个数)
large_pool_size= 31457280 #30M
java_pool_size = 20971520 # 20 M
sort_area_size = 524288 # 512k (65k--2M)
sort_area_retained_size = 524288 # MTS 时 sort_area_retained_size = sort_area_size
假定64 bit ORACLE
内存4G
shared_pool_size = 200M , data buffer = 2.5G
内存8G
shared_pool_size = 300M , data buffer = 5G
内存 12G
shared_pool_size = 300M-----800M , data buffer = 8G
参数更改方式
oracle8i:
主要都是通过修改oracle启动参数文件进行相关的配置
参数文件位置:
d:\oracle\admin\DB_Name\pfile\init.ora
按以上修改以上参数值即可。
Oracle9i:
两种方式:第一种是修改oracle启动参数文件后,通过此参数文件再创建服务器参数文件
第二种是直接运行oracle修改命令进行修改。
SQL>alter system set db_cache_size=200M scope=spfile;
SQL>alter system set shared_pool_size=50M scope=spfile;
在Windows系统中的文件压缩工具winrar功能强大,虽然我们都习惯于用gui的winrar,但是
它也能在命令行方式下面使用,这尤其在企图让winrar批量自动压缩解压缩的时候有用。
它自带的帮助也非常的全面,现在从中择出来一些比较常用的总结一下,以免再找的时候比
较头晕
1,最简单的压缩命令:
winrar a asdf.txt.rar asdf.txt
a的意思是进行压缩动作,后面第一个参数是被压缩后的文件名,后缀当然是rar了,最后面
的参数就是要被压缩的文件名
2,最简单的解压缩命令:
winrar e asdf.txt.rar
e的意思是执行解压缩,解压缩的文件是后面这唯一的参数,但是这个e解压缩是把解出来的
文件释放到当前目录下面,与asdf.txt.rar文件并列了,因此,更加实用的是下面的带路径
解压缩。
3,带路径的解压缩命令:
winrar x asdf.rar
x的意思是执行带绝对路径解压动作,这会在当前文件夹下创建一个文件夹asdf,把压缩包
里的文件、文件夹不改动结构释放到文件asdf里面,就像我们在winrar的图形界面下看到的
一样。
4,指定压缩级别压缩:
winrar a -m5 asdf.tr.rar asdf.tr
要被压缩的不再是一个txt文本,而是一个文本格式的十几M的仿真数据文件,希望能够最大
程度的压缩。使用压缩参数-m5。在winrar中,执行操作是不带前导-符号的参数,比如“a
”或“x”,而修饰这种动作的参数,使用带前导符号“-”的参数,比如-m5。其中-m就是
指定压缩级别的参数,压缩级别有如下五级:
-m0 存储 添加到压缩文件时不压缩文件。
-m1 最快 使用最快方式(低压缩)
-m2 较快 使用快速压缩方式
-m3 标准 使用标准(默认)压缩方式
-m4 较好 使用较好压缩方式(较好压缩,但是慢)
-m5 最好 使用最大压缩方式(最好的压缩,但是最慢)
默认的是-m3级别,级别不同,对于大数据量的文本文件压缩后的文件大小有很大的差异
5,指定压缩后删除原文件:
winrar a -m5 -df asdf.tr.rar asdf.tr
用参数-df指定压缩为asdf.tr.rar压缩文件后,删除原文件asdf.tr,也可以是:
winrar m -m5 asdf.tr.rar asdf.tr
这个m的意思是把文件asdf.tr移动入压缩文件asdf.tr.rar中
6,创建自解压文件:
winrar s asdf.tr.rar
在gui界面中,创建自解压文件是有个选项可以直接选择的。而在命令行中,是分为两个步
骤的,第一步是用压缩命令进行压缩:
winrar a -m5 -df asdf.tr.rar asdf.tr
第二步是用s命令把这个压缩文件转化为自解压文件:
winrar s asdf.tr.rar
转化后,生成了自解压文件:asdf.tr.exe
基本上,日常使用这六条就够了
摘要: 第一章 绪论
1.1 论文的选题背景
以往的基于数理统计方法的应用大多都是通过专用程序来实现的,我们知道,大多数的统计分析技术是基于严格的数学理论和高超的应用技巧的,这使得一般的用户很难从容地掌握它。数据挖掘技术是数理统计分析应用的延伸和发展,假如人们利用数据库的方式从被动地查询变成了主动发现知识的话,那么概率论和数理统计可以为我们从数...
阅读全文
什么是云计算(cloud computing)呢?云计算是一种基于因特网的超级计算模式,在远程的数据中心里,成千上万台电脑和服务器连接成一片电脑云。因此,云计算甚至可以让你体验每秒10万亿次的运算能力,拥有这么强大的计算能力可以模拟核爆炸、预测气候变化和市场发展趋势。用户通过电脑、笔记本、手机等方式接入数据中心,按自己的需求进行运算。
那么,it精英们如何看待云计算的呢?IBM的创立者托马斯·沃森曾表示,全世界只需要5台电脑就足够了。比尔·盖茨则在一次演讲中称,个人用户的内存只需640K足矣。李开复打了一个很形象的比喻:钱庄。最早人们只是把钱放在枕头底下,后来有了钱庄,很安全,不过兑现起来比较麻烦。现在发展到银行可以到任何一个网点取钱,甚至通过ATM,或者国外的渠道。就像用电不需要家家装备发电机,直接从电力公司购买一样。云计算就是这样一种变革——由谷歌、IBM这样的专业网络公司来搭建计算机存储、运算中心,用户通过一根网线借助浏览器就可以很方便的访问,把“云”做为资料存储以及应用服务的中心。
狭义云计算是指IT基础设施的交付和使用模式,指通过网络以按需、易扩展的方式获得所需的资源(硬件、平台、软件)。提供资源的网络被称为“云”。“云”中的资源在使用者看来是可以无限扩展的,并且可以随时获取,按需使用,随时扩展,按使用付费。这种特性经常被称为像水电一样使用IT基础设施。
广义云计算是指服务的交付和使用模式,指通过网络以按需、易扩展的方式获得所需的服务。这种服务可以是IT和软件、互联网相关的,也可以是任意其他的服务。
(一)原理:
云计算(Cloud Computing)是分布式处理(Distributed Computing)、并行处理(Parallel Computing)和网格计算(Grid Computing)的发展,或者说是这些计算机科学概念的商业实现。
云计算的基本原理是,通过使计算分布在大量的分布式计算机上,而非本地计算机或远程服务器中,企业数据中心的运行将更与互联网相似。这使得企业能够将资源切换到需要的应用上,根据需求访问计算机和存储系统。
这可是一种革命性的举措,打个比方,这就好比是从古老的单台发电机模式转向了电厂集中供电的模式。它意味着计算能力也可以作为一种商品进行流通,就像煤气、水电一样,取用方便,费用低廉。最大的不同在于,它是通过互联网进行传输的。
云计算的蓝图已经呼之欲出:在未来,只需要一台笔记本或者一个手机,就可以通过网络服务来实现我们需要的一切,甚至包括超级计算这样的任务。从这个角度而言,最终用户才是云计算的真正拥有者。
云计算的应用包含这样的一种思想,把力量联合起来,给其中的每一个成员使用。
(二)云计算有哪些好处?
1、安全,云计算提供了最可靠、最安全的数据存储中心,用户不用再担心数据丢失、病毒入侵等麻烦。
2、方便,它对用户端的设备要求最低,使用起来很方便。
3、数据共享,它可以轻松实现不同设备间的数据与应用共享。
4、无限可能,它为我们使用网络提供了几乎无限多的可能。
(三)云计算最有利于中小企业?
云计算技术将使得中小企业的成本大大降低。如果说“云”给大型企业的IT部门带来了实惠,那么对于中小型企业而言,它可算得上是上天的恩赐了。过去,小公司人力资源不足,IT预算吃紧,那种动辄数百万美元的IT设备所带来的生产力对它们而言真是如梦一般遥远,而如今,“云”为它们送来了大企业级的技术,并且先期成本极低,升级也很方便。
这一新兴趋势的重要性毋庸置疑,不过,它还仅仅是一系列变革的起步阶段而已。云计算不但抹平了企业规模所导致的优劣差距,而且极有可能让优劣之势易主。简单地说,当今世上最强大最具革新意义的技术已不再为大型企业所独有。“云”让每个普通人都能以极低的成本接触到顶尖的IT技术。
(四)“云”时代
目前,PC依然是我们日常工作生活中的核心工具——我们用PC处理文档、存储资料,通过电子邮件或U盘与他人分享信息。如果PC硬盘坏了,我们会因为资料丢失而束手无策。
而在“云计算”时代,“云”会替我们做存储和计算的工作。“云”就是计算机群,每一群包括了几十万台、甚至上百万台计算机。“云”的好处还在于,其中的计算机可以随时更新,保证“云”长生不老。Google就有好几个这样的“云”,其他IT巨头,如微软、雅虎、亚马逊(Amazon)也有或正在建设这样的“云”。
届时,我们只需要一台能上网的电脑,不需关心存储或计算发生在哪朵“云”上,但一旦有需要,我们可以在任何地点用任何设备,如电脑、手机等,快速地计算和找到这些资料。我们再也不用担心资料丢失。
(五)云计算的几大形式
1.SAAS(软件即服务)
这种类型的云计算通过浏览器把程序传给成千上万的用户。在用户眼中看来,这样会省去在服务器和软件授权上的开支;从供应商角度来看,这样只需要维持一个程序就够了,这样能够减少成本。Salesforce.com是迄今为止这类服务最为出名的公司。SAAS在人力资源管理程序和ERP中比较常用。 Google Apps和Zoho Office也是类似的服务
2.实用计算(Utility Computing)
这个主意很早就有了,但是知道最近才在Amazon.com、Sun、IBM和其它提供存储服务和虚拟服务器的公司中新生。这种云计算是为IT行业创造虚拟的数据中心使得其能够把内存、I/O设备、存储和计算能力集中起来成为一个虚拟的资源池来为整个网络提供服务。
3.网络服务
同SAAS关系密切,网络服务提供者们能够提供API让开发者能够开发更多基于互联网的应用,而不是提供单机程序。
4.平台即服务
另一种SAAS,这种形式的云计算把开发环境作为一种服务来提供。你可以使用中间商的设备来开发自己的程序并通过互联网和其服务器传到用户手中。
5.MSP(管理服务提供商)
最古老的云计算运用之一。这种应用更多的是面向IT行业而不是终端用户,常用于邮件病毒扫描、程序监控等等。
6.商业服务平台(如:中国云计算网 www.cloudcomputing-china.cn )
SAAS和MSP的混合应用,该类云计算为用户和提供商之间的互动提供了一个平台。比如用户个人开支管理系统,能够根据用户的设置来管理其开支并协调其订购的各种服务。
7.互联网整合
将互联网上提供类似服务的公司整合起来,以便用户能够更方便的比较和选择自己的服务供应商。
说了半天相信很多人还没搞清怎么回事,因为单“云计算”这三个字就已经够云里雾里的了。云计算到底有多强大,仍有待时代的检阅!
工具网上的资料改了refhost.xml文件 添加了
<!--Microsoft Windows 7-->
<OPERATING_SYSTEM>
<VERSION VALUE="6.1"/>
</OPERATING_SYSTEM>
后来用xp sp3 兼容模式,管理员运行安装,
刚装完 Oracle 10g,然后,进行em后,界面出来了,但报了一个错:
java.lang.Exception: Exception in sending Request :: null
很多功能不能用,提示重新登录
解决方案:找到下面的文件
$ORACLE_HOME\db_1\$HOSTNAME\sysman\config\emd.properties
其中的agentTZRegion缺省是GMT,改为你所在的时区即可,例如:
agentTZRegion=Asia/Harbin
关于时区的列表参考:10.2.0\db_1\sysman\admin\supportedtzs.lst
然后先停止 dbconsole 使时区设置生效并重启OracleDBConsole:
然后先停止dbconsole 在重启dbconsole :
set ORACLE_SID=orcl
emctl stop dbconsole
emctl start dbconsole
1、开始->设置->控制面板->管理工具->服务 停止所有Oracle服务。
2、 开始->程序->Oracle - OraHome81->Oracle Installation Products-> Universal Installer 卸装所有Oracle产品,但Universal Installer本身不能被删除
5、 运行regedit,选择HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE,按del键删除这个入口。
6、 运行regedit,选择HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services,滚动 这个列表,删除所有Oracle入口。
7、 运行refedit, HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application, 删除所有Oracle入口。
8、 开始->设置->控制面板->系统->高级->环境变量 删除环境变量CLASSPATH和PATH中有关Oracle的设定
9、 从桌面上、STARTUP(启动)组、程序菜单中,删除所有有关Oracle的组和图标
10、 删除\Program Files\Oracle目录
11、 重新启动计算机,重起后才能完全删除Oracle所在目录(到这基本可以安装 )
12、 删除与Oracle有关的文件,选择Oracle所在的缺省目录C:\Oracle,删除这个入 口目录及所有子目录,并从Windows 2000目录(一般为C:\WINNT)下删除以下文 件ORACLE.INI、oradim73.INI、oradim80.INI、oraodbc.ini等等。
13、 WIN.INI文件中若有[ORACLE]的标记段,删除该段
14、 如有必要,删除所有Oracle相关的ODBC的DSN
15、 到事件查看器中,删除Oracle相关的日志 说明: 如果有个别DLL文件无法删除的情况,则不用理会,重新启动,开始新的安装, 安装时,选择一个新的目录,则,安装完毕并重新启动后,老的目录及文件就可以删除掉了。
1.水晶报表 美国BO公司出品http://www.businessobjects.com , 传统条带状报表工具的代表。水晶报表是最早进入中国市场的,有大量的用户。由于其采用模型的局限性,不支持多数据源,不能做复杂的中国报表. 价格拾万圆左右. 没有加密狗.
2.Fine Report 南京帆软软件,美资公司,http://www.fine report.com ,从设计角度,选择多项支持的态度,即Fine Report在一个产品中同时集成三种模型:条带状模型,多点扩散模型,电子表格模型,把选择权交给用户。能够制作复杂的报表,支持多数据源,操作比较简单,适合中国式报表设计的需求。价格壹万圆左右. 没有加密狗.
3.如意报表 深圳明宇公司出品 http://www.soft2web.com, 基于俄罗斯的免费开源的fastreport控件, 功能类似水晶报表, delphi写的使用广泛,基于fastreport控件采用的是西式条带式报表模型,不仅画起来极为费劲,稍复杂一些的报表都要编程才能实现不支持多数据源, 不能制作复杂的中国报表.实际上更适合C/S应用。价格壹万圆左右, 有加密狗.
4.数巨报表 上海炎鼎软件公司出品, 完全本土公司, http://www.maxrpt.com , 和如意报表一样, 也是基于俄罗斯的免费开源的fastreport控件, 功能类似水晶报表, delphi写的使用广泛, 不支持多数据源, 不能制作复杂的中国报表。不过,与如意报表不同的,数巨报表不是一个孤立的产品,而是作为数巨商业智能系统(Max@X Analyser)的一个部分而存在。价格壹万圆左右, 有加密狗.
5.快逸报表 北京润乾公司出品, 完全本土公司, http://www.quiee.com , 06年才出来的, 润乾报表普及版的改头换面产品。使用不多, 纯java,不支持多数据源, 不能做复杂的中国报表, 仅能设计格式最简单的报表,稍复杂的报表就需要通过编写程序来实现。价格壹万圆左右, 有加密狗.
6.润乾报表 http://www.runqian.com,北京润乾软件公司,和快逸报表不同的是 支持多数据源,非线性报表模型, 即可以设计复杂的中国式报表。企业版价格贰拾伍万圆左右. 有加密狗.
安装介绍
对于扩展 Eclipse 的功能,首先当然就是去下载对应的 plugin,BIRT 也不能例外。BIRT 的下载地址:http://download.eclipse.org/birt/downloads/,当前版本是2.0。BIRT 依赖于 Eclipse 的其它几个 plugin(GEF 和 EMF),由于我使用的是 Eclipse WTP(这是 Eclipse 的 WEB 开发工具),在这个工具中那些 plugin 都已预装,因此直接下载 birt-report-framework-2_0_0.zip 就好了。如果没有这些 plugin,请下载。
下载之后,安装非常简单:只需解压然后将对应的features和plugins目录中的内容复制到Eclipse对应的目录下即可。且慢,这只是完成了对于BIRT的基本安装。下一步就是去下载BIRT需要的第三方软件包:
|
需要的jar文件 |
复制位置(都在plugins目录下) |
Apache Axis |
axis.jar
axis-ant.jar
commons-discovery-0.2.jar
jaxrpc.jar
saaj.jar
wsdl4j-1.5.1.jar
|
org.eclipse.birt.report.viewer_version/birt/WEB-INF/Lib |
iText 1.3 |
itext-1.3.jar |
org.eclipse.birt.report.engine.emitter.pdf_version/lib |
prototype.js v1.4.0 |
prototype.js v1.4.0 |
org.eclipse.birt.report.viewer_version/birt/ajax/lib |
自此,BIRT的安装大功告成。启动Eclipse,在"project wizard"中会出现一个BIRT的项目类型。
典型使用
在使用之前,了解一些关于BIRT的基本概念,将会对使用非常有益:
- 数据源:数据的来源,或提供者。如xml数据源、jdbc数据源等。
- 数据集:数据集合,它必须与数据源关联,可以理解为查询的结果。
- 报表以及报表项,报表可视为是针对一组数据集的表现形式,而报表项这是这个表现形式的某个具体的单元。它们之间的关系,与窗体和控件的关系非常类似。报表、数据集、数据源三者间的关系:数据源 --- 数据集 --- 报表。
- 报表参数:查询参数的表现形式,使用它可以构建更灵活的报表。
- 模板和库:主要用于复用报表设计,提高报表开发的效率。
本文中的例子都采用jdbc数据源,这是最常见的使用情形,其中涉及的数据关系是一个典型的多对多关系:
1. 简单报表
首先,让我们来看看一个"Hello World"级别的应用:"列出所有用户",以便可以快速的了解BIRT。为了完成这一任务,我们需要:
A. 通过项目向导,创建BIRT工程。工程创建完毕之后,显示BIRT的"报表设计"视图。由于此时没有报表,其它几个视图,如"数据资源管理器",不可用。
B. 在项目上点鼠标右键,选择:"new -> 报表"。在报表类型中,选择"空白报表"。
C. 创建新报表后,数据视图可用。在"数据资源管理器"中创建报表所需要的jdbc数据源。根据向导,可以方便的添加jdbc驱动、数据库url、用户名和密码。
D. 在指定的数据源上,创建数据集,这一步完成产生数据集的查询。在BIRT中支持2种数据集:基于查询语句和基于存储过程。在本例中使用前者,对应的查询语句是:select user.userid, user.username, user.addr from user
。
E. 选择刚刚创建的数据集,将它拖至空白报表页上。BIRT会自动为其创建一个报表项,此处是"表"。如下图:
F. 选择预览,就可以看到报表运行的实际结果了。或在报表上点击鼠标右键,选择:"报表 -> 运行报表"。
非常简单,一个显示所有用户信息的报表就完成了。在此基础之上,让我们再来完成一些其它具有挑战性的任务:
1.格式化:这是一个内容广泛的主题,常见的需求:
需求 |
解决办法(以上为例) |
显示报表列头为中文 |
如:将userid显示为"用户标识"。
选择"userid",输入"用户标识"。 |
设置报表外观 |
选择对应的报表项,通过"属性编辑器"调整。 |
对于报表数据列进行处理 |
如:将userid和username,显示成:userid:username。双击row["userid"],出现"表达式生成器",输入: row["userid"]+":"+ row["username"] |
分页 |
选择"表":在"属性编辑器"中,选择"分页符",在"分页符间隔"中输入分页大小。 |
页眉和页脚 |
在报表设计页,选择"主页",在其中设置页眉和页脚。 |
2.排序:一种变通的做法是:将数据集排序之后,如在对应的SQL语句中使用order by,再显示。除此之外,也可以在报表设计时来完成:
A. 选择表,此时属性编辑器下方会出现与表相关的选择页。
B. 选择"排序",在对应的页面中选择"添加"按钮之后,出现:
C. 选择需要进行排序的列,以及排序方式。
3.计算列:通过"数据集编辑器"来完成。进入"数据集编辑器后",选择"计算列":输入对应的"列名称"、"数据类型"和"表达式"。
4.报表参数:它为报表的产生带来了极大的灵活性。报表参数一定是与含参数的查询对应的,否则失去了意义。现在,将以上需求改为列出"用户标识大于某一输入的所有用户":
A. 编辑数据集,修改SQL:select user.userid,user.username,user.addr from user where user.userid>?
。
B. 在数据集编辑窗体内选择"参数",然后输入对应的"名称"、"数据类型"、"方向"和"默认值"(必须给出默认值)。其中"方向"表示"输入"或"输出"。对于参数,一般选"输入"。完毕之后:
C. 在"数据资源管理器"视图,创建报表参数:"用户标识"。
D. 选择"表",在"属性编辑器"中选择"绑定"。此时,会出现刚才在数据集中定义的参数"id"。在"值"列,选择报表参数"用户标识":
E. 运行报表时,此时会出现报表参数的输入框,填写值后即出现报表结果。
本例虽然只定义了一个报表参数,但是BIRT并没有这样的限制。我们可以创建多个参数,做法很简单:首先,查询有多个参数;定义数据集的参数时,按照查询中参数出现的顺序定义;最后,添加需要的报表参数。
5.分组:以上为例:将用户按地址分组。
A. 选择"表",点击鼠标右键,选择"插入组"。这里有两个选择:"在上面"和"在下面"。
B. 选择任意一种,弹出分组资料窗体。填写其中的:"名称"和"分组依据"。在本例中,"分组依据"是addr列。
C. 选择预览,或运行报表,查看结果。
2. 子报表
子报表是另一种最常见的报表,以上为例:列出所有用户,并列出每个用户所购买的项目,以及项目数。为了完成这种父子关系的报表,需要:
A. 创建新报表和数据源。
B. 创建数据集user,使用SQL:select user.userid,user.username,user.addr from user。
C. 创建数据集items,使用SQL:
select item.itemid,item.itemdesc,item.price,user_item.count from item,user_item
where item.itemid= user_item.itemid
and user_item.userid= ?
|
同时在数据集items上创建参数user,它对应SQL中的参数。
D. 从"Palette"视图拖入"列表"到报表中,在"属性编辑器"的"绑定"页中,选择数据集为user。它用来显示主表的信息,在本例中是用户信息。
E. 从"Palette"视图拖入"网格"到"列表"的"明细数据"中,设置网格为1行2列,它用来存放"用户姓名"和"用户地址"。在"数据资源管理器"视图,选择数据集user,将username和addr分别拖入网格的2列中。
F. 在"数据资源管理器"视图,选择数据集items,将它拖入"列表"的"明细数据"中,位于刚刚插入的网格下方。此时,BIRT会生成数据集items对应的"表"。
G. 选择刚刚生成的"表",在"属性编辑器"的"绑定"页中,会出现在items中定义的参数。修改它的值:row["userid"]。于是,父子报表就发生了联系。
H. 选择预览,或运行报表,就可以看到结果了。
很遗憾,到目前为止,另一种最常见的报表"交叉表"还不被BIRT支持。但是,BIRT的官方网站已明确表示,将在未来的版本中支持它。
3. 统计图表
俗话说,"一图顶千言"。没有图的报表是枯燥,且缺乏表现力的。在本例中,我们将使用图表来表示:每个用户的消费总数。
A. 创建新报表和数据源。
B. 创建数据集chart,使用SQL:
select user.username,round(sum(item.price*user_item.count),2)
from item, user_item, user
where item.itemid= user_item.itemid
and user.userid= user_item.userid
group by user.username
|
C. 从"Palette"视图拖入"图表"到报表中,此时会弹出"编辑图表"窗体。
D. 在"选择图表类型"页,选择图表类型为"条形图"。在"选择数据"页,使用数据集chart,同时选中username列,将其拖入"类别x系列"。对于统计列,同样将其拖入"类别y系列"。在"图表格式"页,分别为x和y系列,填写相应的显示名称。
E. 选择预览,或运行报表,即可看到统计图表。
4. 使用脚本
可以使用脚本,是BIRT的一大特色。在BIRT中,数据源、数据集和报表项,都可以书写脚本。具体做法:选择数据源、数据集和报表项任意一种对象,然后选择"脚本"页面。如选择数据源user后,对应的脚本输入页面:
选择对应的事件,然后在下方的脚本输入框中输入脚本即可。如对于第一个例子,我们需要统计用户地址是"No.5 St."的用户数:
A. 选择数据集,然后选择"脚本",进入数据集的脚本编辑窗口。
B. 选择事件"afterOpen",在脚本窗口内输入:count=0;
C. 选择事件"onFetch",在脚本窗口内输入:if( row["addr"]== "No.5 St.") count++;
D. 选择报表,在脚本窗口选择事件"onRender",输入:this.caption=count;
E. 预览,或运行报表后,会在报表的标题输出count的数值。
另外,BIRT也支持使用java来作为报表项的事件处理程序。具体做法,请参见BIRT的帮助文档。
5. 库和模板
除了脚本使开发者可以自定义报表的行为外,BIRT还提供了库和模板机制来重用设计,加快报表的开发。在TheServerSide上有一篇相当详细的文档:Using Eclipse BIRT Report Libraries and Templates。
应用集成
BIRT与现有应用的集成非常简单,由于BIRT主要是web应用,本节以web应用为例进行说明。
1. 从Eclipse BIRT官方网站下载birt-runtime-2_0_0.zip。
2. 解压之后,只需要其中的web view example目录下的内容。
3. 复制以下第三方软件包到web view example目录:
|
需要的jar文件 |
复制位置 |
Apache Axis |
axis.jar
axis-ant.jar
commons-discovery-0.2.jar
jaxrpc.jar
saaj.jar
wsdl4j-1.5.1.jar
|
WEB-INF/Lib |
iText 1.3 |
itext-1.3.jar |
Plugins/org.eclipse.birt.report.engine.emitter.pdf/lib |
prototype.js v1.4.0 |
prototype.js v1.4.0 |
ajax/lib |
4. 将web view example目录发布到web容器中,如tomcat,改名:birtApp。
5. 启动tomcat,并访问birtApp。在首页中选择"View Example",如果成功发布,系统将提示成功。
6. 发布设计完成的报表文件,在birtApp中创建reports目录,用于存放报表设计文件。
7. 在应用中通过:http://localhost:8080/birtApp/frameset?__report=报表文件路径,就可以访问报表了。
这个web应用支持2个动作:
- frameset,以frameset的形式显示报表。这个界面包含一些frame,如页面导航,报表主体在其中的一个frame中显示;使用形式:
http://localhost:8080/birtApp/frameset?__report=报表文件路径&参数=........
- run,报表以一个单独的html页面或pdf显示,由于这种形式没有frmaeset,因此使用者必须自己提供相应的参数,如报表参数,页号等。使用形式:
http://localhost:8080/birtApp/run?__report=报表文件路径&参数=........
对于以上2个动作,以下列出可用的参数:
选项 |
说明 |
__format |
报表输出格式:html或pdf,缺省是html。对于frameset不起作用。 |
__isnull |
指明一个参数是null,常用于字符串类型。如果提供参数且值为空: - 对于日期和数字类型,BIRT会将它们当作null处理。 - 对于字符串,BIRT会将它作为空字符串。因此,为了说明某个字符串是null,通常写为:__isnull=参数。 |
__locale |
本地化选项,缺省是jvm的locale。 |
__report |
报表设计文件路径。 |
报表参数 |
报表参数参数值对,形式:参数名=参数值。对于frameset,直接在地址栏中输入参数名=参数值后回车,不会影响报表结果。虽然,此时选择"运行报表"时,弹出的参数值已经改变。 |
虽然本节所说的是针对web应用的集成,但是这种方法同样也可与非web应用集成。此时,我们可以采取一种变通的方法:在非web应用中使用内嵌的web容器,如jetty,也可达到同样的效果。这种做法和Eclipse的做法类似。还记得在报表设计时使用"报表 -> 运行报表"吗?那个弹出的窗体,实际就是一个web页面。
结论
与一些老牌报表软件相比,BIRT可能仍显稚嫩。然而,它也不乏其独到之处,如脚本控制、库和模板。加之有世界领先的报表厂商Actuate支持,实际上BIRT的ROM(Report Object Model)就是Actuate捐献的,我们有充分的理由对BIRT的前景表示乐观。
参考资料
几种ESB(企业服务总线)介绍
ESB(Enterprise Service Bus,即企业服务总线)是传统中间件技术与XML、Web服务等技术结合的产物。ESB提供了网络中最基本的连接中枢,是构筑企业神经系统的必要元素。
企业服务总线ESB就是一种可以提供可靠的、有保证的消息技术的最新方法。ESB中间件产品利用的是Web服务标准和与公认的可靠消息MOM协议接口(例如 IBM的WebSphere MQ、Tibco的Rendezvous和Sonic Software的SoniCMQ)。ESB产品的共有特性包括:连接异构的MOM、利用Web服务描述语言接口封装MOM协议,以及在MOM传输层上传送简单对象应用协议(SOAP)传输流的能力。大多数ESB产品支持在分布式应用之间通过中间层如集成代理实现直接对等沟通。
企业服务总线(Enterprise Service Bus,ESB)的概念是从面向服务体系架构(Service -Oriented Architecture, SOA)发展而来的。SOA描述了一种IT基础设施的应用集成模型,其中的软构件集是以一种定义清晰的层次化结构相互耦合,其中,一个ESB是一个预先组装的SOA实现,它包含了实现SOA分层目标所必需的基础功能部件。
一、ESB的出现改变了传统的软件架构
ESB 是传统中间件技术与XML、Web服务等技术相互结合的产物,ESB的出现改变了传统的软件架构,可以提供比传统中间件产品更为廉价的解决方案,同时它还可以消除不同应用之间的技术差异,让不同的应用服务器协调运作,实现了不同服务之间的通信与整合。从功能上看,ESB提供了事件驱动和文档导向的处理模式,以及分布式的运行管理机制,它支持基于内容的路由和过滤,具备了复杂数据的传输能力,并可以提供一系列的标准接口。
二、企业服务总线(ESB)的用处
ESB 不是万能的,他不是一个应用程序框架,也不是一个企业应用的解决方案.它只是一个基于消息的调用企业服务的通信模块!你可以把它嵌入到你的应用程序框架中,例如嵌入到spring容器里面,或者嵌入到工作流系统中.它的作用是对企业里面的SOA服务的调用提供一个框架和简便的方法.
三、企业服务总线(ESB)的应用特征
大规模分布式的企业应用需要相对简单而实用的中间件技术来简化和统一越来越复杂、繁琐的企业级信息系统平台。面向服务体系架构(SOA)是能够将应用程序的不同功能单元通过服务之间定义良好的接口和契约联系起来。SOA使用户可以不受限制地重复使用软件、把各种资源互连起来,只要IT人员选用标准接口包装旧的应用程序、把新的应用程序构建成服务,那么其他应用系统就可以很方便的使用这些功能服务。
支撑SOA的关键是其消息传递架构-企业服务总线(ESB)。ESB是传统中间件技术与XML、Web服务等技术相互结合的产物,用于实现企业应用不同消息和信息的准确、高效和安全传递。ESB的出现改变了传统的软件架构,可以提供比传统中间件产品更为廉价的解决方案,同时它还可以消除不同应用之间的技术差异,让不同的应用服务协调运作,实现不同服务之间的通信与整合。ESB在不同领域具有非常广泛的用途:
电信领域:ESB能够在全方位支持电信行业OSS的应用整合概念。是理想的电信级应用软件承载平台。
电力领域:ESB能够在全方位支持电力行业EMS的数据整合概念,是理想的SCADA系统数据交换平台。
金融领域:ESB能够在全方位支持银企间业务处理平台的流程整合概念,是理想的B2B交易支撑平台。
电子政务:ESB能够在全方位支持电子政务应用软件业务基础平台、信息共享交换平台、决策分析支撑平台和政务门户的平台化实现。
四、几种ESB的结构和功能
ESB提供了一种开放的、基于标准的消息机制,通过简单的标准适配器和接口,来完成粗粒度应用(服务)和其他组件之间的互操作,能够满足大型异构企业环境的集成需求。它可以在不改变现有基础结构的情况下让几代技术实现互操作。
通过使用ESB,可以在几乎不更改代码的情况下,以一种无缝的非侵入方式使企业已有的系统具有全新的服务接口,并能够在部署环境中支持任何标准。更重要的是,充当“缓冲器”的ESB(负责在诸多服务之间转换业务逻辑和数据格式)与服务逻辑相分离,从而使得不同的应用程序可以同时使用同一服务,用不着在应用程序或者数据发生变化时,改动服务代码。
1. IBM WebSphere ESB
IBM 提供了三种 ESB 产品:IBM WebSphere ESB、IBM WebSphere Message Broker、IBM WebSphere DataPower Integration Appliance XI50。根据您的需求选择 ESB 来增强您的 SOA。WebSphere ESB 是一种基于平台的 ESB,作为集成的 SOA 平台,针对 WebSphere 应用服务器进行了优化。WebSphere Message Broker 是跨平台的 ESB,是为异构 IT 环境中的统一连接和转换而构建的。WebSphere DataPower Integration Appliance XI50 是一种基于设备的 ESB,是为简化的部署和更强的安全性而构建的。客户面临着从简单到复杂的各式各样的 ESB 需求。WebSphere ESB的结构如图一所示。
图一 WebSphere ESB
2. Microsoft ESB
微软通过其应用平台提供了全面的ESB服务,包括:Windows Server® 2003,.NET Framework, BizTalk® Server 2006 R2. 应用平台提供了一个基础架构,基于此可以灵活和安全地重复使用架构和商业服务,并具有协调原有的服务整合到新的端到端的业务流程中的能力。如图二所示。
图二 Microsoft ESB
微软通过一些列的产品Windows Server 2003, the .NET Framework 3.0, and BizTalk Server 2006作为对企业实现ESB的支撑,Microsoft ESB Guidance是基于BizTalk Server 2006一组应用,它提供以下公用的ESB组件:
l Message routing (消息路由)
l Message validation (消息验证)
l Message transformation (消息转换)
l Centralized exception management(集中的异常管理)
l Extensible adapter framework(可扩展的适配器框架)
l Service orchestration(服务的编制支持)
l Business rules engine(业务规则引擎)
l Business activity monitoring(业务活动监视)
微软 ESB 指南提供了架构指导,模式和实践,以及一套BizTalk Server 和 .NET Framework 组件来简化基于微软平台的大型或小规模的ESB解决方案的开发。它还可以帮助开发人员扩展现有的信息和集成解决方案,包括的一些服务和组件。
3. JBOSS SOA Platform
JBoss Enterprise SOA Platform提供了一个基于标准的平台,用以集成应用、SOA服务、业务事件和自动化业务流程。这一SOA平台集成了特定版本的JBoss ESB、jBPM、Drools、和已得到验证的JBoss企业应用平台,把它们组织在一起形成一个单一的企业级发布。如图三所示。
图三 JBOSS SOA Platform
JBoss Enterprise SOA Platform打包了不少流行组件如:
l JBoss ESB
l JBoss jBPM jPDL
l JBoss Rules (Drools)
l JBoss Application Server
l Hibernate
l Hibernate Entity Manager
l Hibernate Annotations
l JBoss Seam
l JBoss Web (嵌入式Tomcat 6.0)
l JBoss Cache
l JGroups
l JBoss Messaging
l JBoss Transactions
l JBoss Web Services (JBossWS)
l JBossXB
l JBoss AOP
l JBoss Remoting
l JBoss Serialization
l JacORB
4. ServiceMix对ESB的实现
ServiceMix是一个建立在JBI (JSR 208)语法规则和APIs上的开源ESB(Enterprise Service Bus:企业服务总线)项目。
ServiceMix是基于JBI的ESB。它是开源的基于JBI语义和API的ESB和SOA工具包,以Apache许可证方式发布。 它是轻量的ESB实现,易于作为嵌入式ESB使用;集成了对Spring技术的支持;可以在客户端或服务器端运行;可以作为独立的ESB提供者,也可以作为另外ESB的服务组件; 可以在JavaSE或JavaEE服务器中使用;ServiceMix同Apache Geronimo以及JBoss服务器完全集成,并且在Apache Geronimo服务器中可以直接部署JBI组件和服务。
Java Business Integration (JBI,Java业务集成)技术规范定义了SOA的服务导向集成的内核和组成架构。它对公共讯息路径架构、服务引擎与捆绑的插件程序接口,以及复合型服务描述机制等都进行了标准化,这样就将多种服务结合成为一个单一的可执行的和可审核的工作单元。 参见图四。
图4 JBI和ServiceMix关系图
JBI并不是一个为开发者设计的一个接口,更准确的说它是在JBI容器里为集成商提供相互集成的一个体系和一系列的接口。所以人们能集合他们所需要的所有部分,做出一个总体解决。例如在理论你能从BPEL引擎上,EJB容器上或者是数据传输产品上集合一个基础设施,并且能够集成的很合适。
ServiceMix 中包含完整的JBI容器,支持JBI规范的所有功能要求:
l 规范化消息服务和路由
l JBI管理Beans (MBeans)
l 组件管理和安装的Ant任务
l 对JBI部署单元的完全支持,支持JBI组件的热部署
【王程斯】IBM Message Broker笔记系列(四)
前面讲了那么多MB的原理和配置,这一篇blog开始正式讲讲我个人学MB的感受。“假如时间可以重来”,我会怎样利用手头的资源,以最快的速度入手。
体验MB
在安装完WMBT之后,会出现“欢迎”(这个也是eclipse
环境安装后都会有的东西,你也可以在“帮助”->“欢迎”里面找到),里面有不少很浅显的例子,让你对MB是如何工作的有个感性认识。强烈建议把里面的“入门”部分看完。
学习开发
看完“入门”后,重点就应该放在“样例”中。在样例库里面,有数十个基础的样例,每个样例都针对某类
节点,比如消息映射、
JMS、
数据库操作、Webservice调用,等等。那么多的例子,当然不可能一下子看完,而且对于一张白纸的新手,里面有些基础知识还不懂,我的建议是:结合之前提到的那本书《精通Websphere Message Broker》,看完一方面的内容,比如
数据库操作,你就可以打开“样例库”,将里面的数据库样例导入至WMBT,然后看看其中的ESQL代码、相关节点的设置,等等。一开始会比较枯燥,而且让这些样例跑起来也不是件容易的事情,但坚持下去就会慢慢体会到其中的乐趣了。
掌握Debug利器
一般来说,WMBT会自动帮你配置好样例
程序运行所需的环境(比如创建队列
管理器、数据库,等等),然后将样例
部署到MB的执行组中。这一切都是自动完成的,但有时候出于各种原因,试运行样例的时候不能得到预期的结果。首先排除掉打包和部署时的错误,因为样例程序的源代码通常是没有问题的,那么余下的问题就只能靠debug来
解决了。另一方面,当你看了书之后,对InputRoot、Environment、ExceptionList这些东西的结构、细节肯定会很好奇,但是书中提到的很少很零散,最好的方法就是在debug模式下,让消息流挂起在某一点,然后你再慢慢浏览其中的各类变量,很多东西都不言自明了。用
IBM工程师的话说,就是“没有debug就根本没法学会MB”。为什么?相关
资料太少了,去google有时候还不如直接问MB自己,所以也是很考验你的自学能力的。
配置debug环境
这些内容在《精通》那本书中都有讲到,只是比较粗略,我在这里补充我个人的
经验。
首先右键单击某个执行组,“属性”,在“JVM调试端口”中输入一个tcp端口号,不要和其他程序冲突就行,假设9090。再次右键执行组,“调试”->“启用调试9090”。然后,切记,重启执行组所在的
broker
重启后,把bar
文件部署到该执行组,然后进入“debug”视图,找到下图右下角的那个按钮:
如果是该按钮是红色的,说明debug守护程序没有启动;如果是绿色的,鼠标移过去,会显示“debug守护程序正在监听XX端口”,需要注意的是,这里的端口可不是之前在执行组设置的端口,刚好相反,必须和之前的9090不同。单击旁边的下拉箭头,可以启动或者更改守护程序的端口,这里改为9099,不必重启。
接下来,就和一般的eclipse调试一样了,在绿色的小昆虫按钮设置debug参数。新建一个debug,注意这里的“java调试端口”则必须与执行组的一致,你也可以“选择执行组….”,
系统会根据你选择的执行组,自动设置端口
然后,在“源”选项卡中,添加你要调试的项目。确定之后,就可以运行debug了。
What next?
debug配置成功后,就开始慢慢研究那些样例吧,按照之前我说的思路,“书本+样例”,按照每个人自己的学习习惯和项目开发的需要,将你要学习的样例,一步步调试过去,看看每条ESQL语句的结果,很快的你就知道其中的运行机理了。
小结
由于MB不能像java那样,输出东西到
控制台,只能输出到
日志文件,而且还得写一堆语句,或者配置trace节点,很麻烦,所以传统的在程序中插入输出语句的方式在这里没太大意义。很多情况下,要依靠debug来观察消息流执行过程中消息的变化,来决定bug的所在。
【王程斯】IBM Message Broker笔记系列(三)
安装配置
准备工作
MB的运行依赖于MQ,所以首先要安装MQ,MQ的具体安装过程略,并且以后假设你已经有关于MQ的基础知识,比如队列管理器、队列、通道,等等。
安装好MQ后,创建一个队列管理器(简称QM),名为TESTQM(MQ里面的对象是区分大小写的,为了避免不必要的麻烦,这里统一用大写,以下划线分隔),这个队列管理器是MB运行的基础,当你用MB的脚本创建配置管理器、代理和执行组时,都要指定QM的名字
然后创建运行时数据库,名为TESTDB,MB自带了derby,你也可以选择DB2,注意此处的数据库是指MB自身运行所需的数据库,目前6.1版本只能用derby或者DB2。创建的方法,可以用MB的脚本命令:mqsicreatedb,也可以用对应数据库自身的脚本命令或图形界面来创建。
关于MB的数据库:
配置管理器只能用derby,而代理可以用多种数据库,只是不同数据库的创建命令各自不同(包括在不同平台上也有差异,具体参考红皮书);代理的数据库可以共用,配置管理器也可以和某个代理共用一个derby数据库;使用mqsicreatedb创建数据库时,如果你已经安装了DB2,则默认创建一个DB2数据库,否则derby
以上是为MB的运行创造运行时环境,接下来开始创建MB的实例
首先当然是要安装MB了,过程挺简单的,略去不表。安装完成后,会在“开始菜单”中有个“命令控制台”,如下图:
单击它,进入MB的一个命令控制台环境,其实和普通的windows命令控制台没什么区别,主要在于它帮你设好了相关的环境变量,你就可以在里面直接输入MB的命令脚本了
前文提到过,MB的配置管理器是用来统一管理MB的各个运行时组件的,因此首先要创建一个配置管理器
mqsicreateconfigmgr –i user –a password –q TESTQM
指定用户名、密码和队列管理器,用户名密码是你登陆本地机器时输入的,必须要有足够的权限(具体权限就不清楚了,我直接用管理员帐号,深入讨论请参考MB的红皮书)
你会发现这里没有指定数据库的名称,因为配置管理器在创建时会自动新建一个derby数据库,而且只能用derby数据库,用户无法改动
配置管理器的名称也没有指定,在windows下是会创建默认名称的:ConfigMgr
然后是创建代理,名为TESTBROKER
mqsicreatebroker TESTBROKER –i user –a password –q TESTQM –n TESTDB
大部分都和创建配置管理器一样,只是多了一个选项,用于指定数据库,再次提醒,必须是derby或DB2,二选一。
最后,使用“mqsistart组件名” 来启动配置管理器和代理
配置MB toolkit
WMBT本身的安装没什么特殊要求,这里就不啰嗦了
接下来的关键是在WMBT里面连接到刚才创建的配置管理器,其作用就好像你在Eclipse中要配置好应用服务器的实例,才能把你的J2EE项目直接以图形界面的方式部署,而不必自己敲命令
如图,文件->新建->域连接
在弹出的窗口中,填入相关参数
这里只需填入队列管理器的名称、域名、端口,注意是队列管理器而不是配置管理器(其实你在创建配置管理器时也没有指定端口,因为它用的就是所在的队列管理器的端口)
此外对于SVRCONN通道名,SYSTEM.BKR.CONFIG是在你创建配置管理器时自动生成的,可以在“MQ 资源管理器”中,通过“显示系统对象”来查看,你也可以自己建一个服务器连接通道,然后在这里输入该通道的名字
一切正常的话,就能看到左下角的“域”窗口中,多了一个新的域连接,里面以树形结构显示了你刚才创建的代理(前提是你的代理基于derby数据库,如果基于DB2,则需要在域连接那里显式增加“代理引用”),现在你可以右键单击TESTBROKER,然后创建执行组。等你开发好MB项目后,打个包,拖到执行组里面,就可以部署了
【王程斯】IBM MessageBroker笔记系列(一)
前言
SOA已经在中国喊了几年,连象牙塔的大学生都知道了,但实施的案例并不多,而作为SOA
基础设施的企业服务总线ESB,在国内的应用更是稀少,主要都是银行和电信等大牌企业在使用。我算非常好彩,打工所在的公司恰好要为客户开发一个基于MB
和WAS的平台,让我有很多机会接触到MB
的应用。现在国内MB
的资料非常少,主要是IBM的红皮书,可惜全部都是英文的,看起来颇费力,效率也不高;出版物我所知的只有一本,是陈宇翔先生所著的《精通Websphere Message Broker
》【中国水利水电出版社】,也是目前手边唯一的一本参考书。因此希望将这段时间的一些使用心得记下来,作为一个从未接触过SOA
和MB
(甚至没用过websphere
产品)的菜鸟,面对这个上百万人民币的庞然大物,应该怎样下手
书评
先来说说这段时间翻阅的一些MB
的书籍,包括纸质和电子版,首先是上文说到的《精通Websphere Message Broker
》这本书。本来这种书给人的第一反应就是:一本红皮书的翻译,无非就是从IBM
的各个红皮书里面摘抄文字,翻译好之后综合一下的“大杂烩”。老实说这本书里面的确有很多翻译的内容,比如MB toolkit
中自带的一些教程,以及MB Information center
里面的部分实例,书的后半部分都是附录,包括函数库、命令库,等等。但是不可否认的是,IBM
的红皮书、InfoCenter
本身就是相当好的教程库,而这本书用到其中的内容也翻译的流畅,所以也是方便了国内读者。而且,作者本身也的确有一些MB
的使用经验,书中也有他自己的内容。所以,这本书作为入门的话,实在是比较辛苦,因为没有考虑太多初学者的难处,内容的编排也不太合理,但是作为一本参考书却是不错的选择。在如今没什么资料的情况下,最好咬牙坚持看下去。
再说说IBM
提供的电子资源,包括红皮书和网上资料,以及InfoCenter
。只要你买了MB
的产品,IBM
自然会提供一堆红皮书给你,当然你也可以慢慢从网上下载,这些红皮书很多写的不错,但是要从头看太痛苦,作参考比较好。此外如果你购买MB
的培训,那么培训机构也会给你一些pdf
材料(其实都是IBM
出品的),这些材料相对易懂,适合入门。再有就是developerWorks
,IBM
的官方技术网站,里面提供最新最全的资料,有空多去看看,也可以订阅它的邮件。最后是InfoCenter
,其实说白了是网页版的手册,可以在线看也可以下载,相对其他来说,难度介于中等,而且不像网站的资源那么零散,所以也是很好的提高阶段的学习资料。
ESB产品
如果你还不清楚ESB
的概念,和IBM
的相关产品,可以去IBM
网上查查资料,我之前也写了一篇简介http://blog.csdn.net/wangchengsi/archive/2008/02/25/2120316.aspx
作为一个菜鸟我没法全面评论当前的ESB
产品,只能记录一下自己的所见所闻(就是跟IBM
和BEA
公司打交道的时候听到的一些内容)。撇开两者的应用服务器不谈(这方面的口水战已经够多了,国内用BEA
的相对多,容易上手适合快速开发,性价比很高),SOA
和ESB
方面,IBM
无疑是走在前面的,这个可以从两者的产品线看出来。BEA
的ESB
产品只有一款AquaLogicBus
,IBM
却已经开始划分各类市场、推出不同档次的产品了(但这个也是BEA
宣传的好处之一,买一个就能到处用,见仁见智了);其二,BEA
自己的销售都对AquaLogic
不甚了解,而且在国内尚无成熟应用,这点是很多企业最关注的,没有成熟应用意味着没有好的技术支持,出了问题不知道找谁解决,甚至从没有人遇过这种问题;而IBM
这两年在SOA
的推广方面做得比较好,广告也做得多,在国内已经有一些成功案例,技术支持也更加完善,我们在广州就能直接联系到工程师,而不必等北京、上海,甚至国外的支持。
MB
在对异构环境的支持方面,做得也比AquaLogicBus
好,可以支持几十种通信协议和平台,而且天生和IBM
自家的大型机等结合的比较好,AquaLogicBus
支持的就相对比较少,主要是基于java
平台的SOA
流行协议,比如web service
,给人感觉更像是websphere ESB
的竞争对手。但是BEA
的产品向来给人的感觉是除了在IBM
的平台,其他平台上都比IBM
的同类产品性价比要高,不知道AquaLogicBus
是不是也一样表现优秀,这个就需要专业的测试了。
另外很重要的一点,就是BEA
的消息中间件做得不如IBM
的MQ
强大,而MB
又是依托于MQ
才能有如此强大的功能,这个是BEA
的销售也不得不承认的。尽管Web service
是当前SOA
的主流,但是性能方面却是不敢恭维,在企业内部实施SOA
,如果服务组件都用web service
连接,虽然更加通用、更加廉价易用,但是往往会有性能瓶颈,关键地方还得靠消息中间件。
最后呢,就是BEA
工作人员对于自家的产品,底气明显不足,一方面是不熟悉,另一方面也是国内用的少,也侧面反映了对于这类重量级产品、而且关乎整个系统性能的底层部件,人们还是倾向于选择IBM
,将来SOA
应用普及了,AquaLogicBus
肯定也会遍地开花,就像现在的weblogic一样。只是目前来看,还是选择MB
更让人放心。
还有一个不得不提到的有力竞争者是来自开源社区的JBOSS ESB
,这个产品我没了解过,但是现在Reahat
收购了JBOSS
,在JBOSS AS
和ESB
上也下了相当大力气,誓要在SOA
市场与IBM
和BEA
分一杯羹。很看好JBOSS
的潜力,只是开源产品在中国连个服务中心都没有,暂时只能供高手们自己研究着玩了。
【王程斯】IBM Message Broker笔记系列(二)
Remine的安装
1.首先下载InstantRails并解压,以下假设加压到C:\
InstantRails是一个Rails应用的集成包包括了Ruby运行时,Rails框架,PhpMyAdmin,Apache,MySql等,这样就不需要分别下载需要的东西。其中Apache是用的1.3版本,主要用于运行phpMyAdmin(MySql的管理工具),Redmine本身内置了一个Ruby写的Http服务器:WEBrick 因此并不需要Apache。
2.下载Redmine并解压缩,将解压缩后的文件夹直接复制到InstantRails目录的rails_apps目录下
3.启动InstantRails(在C:\InstantRails\下有个InstantRails.exe直接运行即可)会出现一个界面(见下图)上面可以控制Apache和MySql的停止和启动,请确保这两个都启动了。如果你的电脑上安装了IIS,可能需要在httpd.conf中修改Apache的监听端口,默认是80
4.使用phpMyAdmin在MySql中创建数据库,脚本如下:create database redmine character set utf8;
5.将C:\InstantRails\rails_apps\redmine-0.8.0\config 目录下的database.yml.example更名为database.yml
6.进入C:\InstantRails\rails_apps\redmine-0.8.0目录,在其下执行:rake db:migrat RAILS_ENV="production",用来创建数据库,完成后应该创建43个表
7.再执行:rake redmine:load_default_data RAILS_ENV="production",指明当前项目运行环境为production,中间会提示选择语言,我们选择zh。
8.最后在C:\InstantRails\rails_apps\redmine-0.8.0目录下执行ruby script/server -e production,启动Redmine
9.通过浏览器访问http://localHost:3000即可,缺省管理员用户名是admin,密码也是admin
配置Redmine为Windows服务
按照上面介绍的步骤就可以使用Redmine,但当机器重启后都需要手工启动InstantRails和执行第8步来启动Redmine,比较麻烦,下面就介绍将Redmine安装成Windows服务,只要系统启动无需登录也跟着启动。
在上述步骤中,MySql只是做为一个普通程序被InstantRails.exe启动,而Redmine又依赖于MySql,因此需要将MySql安装为Windows服务,进入到C:\InstantRails\mysql\bin目录下执行:Mysqld -Install即可将MySql安装为服务,在服务管理器中将其设为自动启动。
Ruby提供一个安装Ruby程序为服务的包:mongrel_service。安装其实很简单,在C:\InstantRails\ruby\bin目录下运行:gem install mongrel_service,此过程中会下载一些其他必须的包
然后使用mongrel_service将Redmine安装为服务:
mongrel_rails service::install -N RedMine -c C:\InstantRails\rails_apps\redmine-0.8.0 -p 3000 –e production
其中C:\InstantRails\rails_apps\redmine-0.8.0是Redmine所在目录 3000是监听端口,然后修改启动方式为自动即可。
如果想要移除Redmine服务,可执行如下命令:
mongrel_rails service::remove -N RedMine
配置邮件通知
Redmin可以为一些操作提供邮件通知如主题改变,新增问题等,这样可以让开发人员及时知道变化。在使用此功能前需要先配置邮件服务器,将C:\InstantRails\rails_apps\redmine-0.8.0\config目录下的email.yml.example更名为email.yml,然后用文本编辑器打开此文件,将内容改为如下:
# Outgoing email settings
production:
delivery_method: :smtp
smtp_settings:
address: "smtp.163.com"
port: 25
domain: "163.com"
authentication: :login
user_name: "PM@163.com"
password: "123456"
development:
delivery_method: :smtp
smtp_settings:
address: "smtp.163.com"
port: 25
domain: "163.com"
authentication: :login
user_name: "PM@163.com"
password: "123456"
这里我采用的是163的Smtp服务器来发送邮件,其中特别需要注意的是address,domain,user_name,password中的值都要加上双引号,否则会报错。
然后就可以在Redmine的界面中启用邮件通知,设置一下发件人地址和签名即可。
集成SubVersion
集成SubVersion就很简单了,在版本库页面选择Subersion,然后填写SVN仓库的url和登录Subversion的用户名,密码即可通过Redmine查看svn仓库中的文件并可比较差异。如下图:
Redmine的WIKI
Redmine的Wiki功能比较简单,使用wiki时一定要注意在标记的前后都要留有空格,否则会无效如变粗字体:*Ning* 其中第一个 * 的前面要留有一个空格,而第二个*后面也要留有一个空格,其他标记类似。
在Redmine中可以自定义工作流程,工作流程是指某一个角色针对某一类问题(如功能,缺陷和支持)的状态迁移规则,此时该类问题就被跟踪(此时该类问题也称之为Tracker),状态迁移规则决定了某类问题是否可以从一个状态迁移到另外一个状态
文章来自[SVN中文技术网]转发请保留本站地址:http://www.svn8.com/svnjs/20090209/2337.html
修改E:\InstantRails-2.0-win\rails_apps\redmine-0.8.5\vendor\rails\railties\lib\commands\servers\webrick.rb文件
require 'webrick'
require 'optparse'
require 'commands/servers/base'
OPTIONS = {
:port => 3000,
:ip => "0.0.0.0",
:environment => (ENV['RAILS_ENV'] || "development").dup,
:server_root => File.expand_path(RAILS_ROOT + "/public/"),
:server_type => WEBrick::SimpleServer,
:charset => "UTF-8",
:mime_types => WEBrick::HTTPUtils::DefaultMimeTypes,
:debugger => false
}
重启服务即可
要将 Windows XP 配置为能够搜索所有文件(无论文件类型是什么),请获取最新的 Windows XP Service Pack,然后打开“含有未知扩展名的索引文件类型”选项。
测试部分:『
如果您使用该方法,Windows XP 会在所有文件类型中搜索您指定的文本。这可能会影响搜索功能的性能。为此,请按照下列步骤操作: 1. 单击“开始”,然后单击“搜索”(或指向“搜索”,然后单击“文件或文件夹”)。
2. 单击“改变首选项”,然后单击“使用制作索引服务(使本地搜索更快)”。
3. 单击“改变制作索引服务设置(高级)”。请注意,您不必打开索引服务。
4. 在工具栏上,单击“显示/隐藏控制台树”。
5. 在左窗格中,右键单击“本机索引服务”,然后单击“属性”。
6. 在“生成”选项卡上,单击以选中“含有未知扩展名的索引文件”复选框,然后单击“确定”。
7. 关闭索引服务控制台。 』
警告:如果使用注册表编辑器或其他方法错误地修改了注册表,则可能导致严重问题。不提倡大家再网上搜到那些改变筛选等注册项的方法
环境:
服务器操作系统是windows2003(32位), 硬件配置为2C(CPU)8G(Memory)PC服务器, 用做Oracle服务器.
oracle版本为10.1.0.2, $oracle_home为d:/oracle/product, sid为orcl.
方案1:
--------------------------------------------------------------------------
因为服务器是32位的操作系统, 所以最大只能分2G内存给Oracle使用, 规划如下:
SGA:1.72G
PGA:250M
操作步骤
1. 创建pfile
SQL>create pfile from spfile
这样就在d:/oracle/product/10.1.0/db_1/database目录下面多1个文件INITorcl.ORA
或者copy d:/oracle/product/10.1.0/admin/orcl/pfile/init.ora.XXXXXXXX到上述目录, 名字改成INITorcl.ORA
init.ora.XXXX也是个pfile文件, 不妨试着用这个文件启动你的数据库
SQL>startup pfile='d:/oracle/product/10.1.0/admin/orcl/pfile/init.ora.XXXXXXXX'
特别是你改动参数导致数据库无法启动的情况下, 用这个文件恢复你的spfile将非常有用
SQL>create spfile from pfile='d:/oracle/product/10.1.0/admin/orcl/pfile/init.ora.XXXXXXXX'
2.修改pfile的内容
修改后主要内容为
sga_target=1700000000(1.7G左右)
lock_sga=true
pre_aggregate_tagert=250000000(250M左右)
workarea_size_policy=auto
pre_page_sga=true
sga_max_size=1720000000(1.72G左右)
3.根据pfile启动数据库
SQL>startup pfile='d:/oracle/product/10.1.0/db_1/database/INITorcl.ORA'
如果不能启动, 可能是某些参数的原因, 那么就修改INIToracl.ORA的配置文件, 直到能正常启动为止.
4.创建spfile
SQL>create spfile from pfile
上诉命令将覆盖d:/oracle/product/10.1.0/db_1/database/下的spfile文件"SPFILEORCL.ORA"
当然你也可以显式的指明pfile
SQL>create spfile from 'd:/oracle/product/10.1.0/db_1/database/INITorcl.ORA'
5.用spfile启动数据库并调整性能
SQL>startup
然后在oem中调整各种参数,调整后的参数将写在spfile中,但是不会写在pfile中
总结:这样下来,感觉服务器的内存并没有被充分利用, 8G服务器只有2G给了Oracle使用, 并不是非常理想,但是我们最终还是采用了这种方案
这是因为:
1:我们试过另外一种方案后感觉速度不但没有提升, 反而有下降, 可能是因为服务器调整的参数有些问题
2:但是我们不愿意冒着修改服务器参数的风险, 尤其是1个在线系统
3:还有1个重要的原因就是客户答应我们很快用64位的windows 2003操作系统
方案2:
--------------------------------------------------------------------------------------------------
我们使用AWE的方法来使得Oracle能使用大于2G的内存, 规划如下:
DB_BUFFER_SIZE:3.2G
large_pool:150M
java_pool:150M
share_pool:800M
PGA:500M
操作步骤
1. 修改服务器启动参数
修改c:/boot.ini文件, 在启动的操作系统参数后加上/3GB /PAE, 如:
multi(0)disk(0)rdisk(0)partition(1)\WINDOWS="Windows Server 2003, Enterprise" /noexecute=optout /fastdetect /3GB /PAE
2. 注册表增加1个二进制键值
路径为hkey_local_machine/software/oracle/key_Oradb10g_home1/, 键值为
AWE_WINDOW_MEMORY=20000000000
3. 创建pfile
SQL>create pfile from spfile
这样就在d:/oracle/product/10.1.0/db_1/database目录下面多1个文件INITorcl.ORA,
或者copy d:/oracle/product/10.1.0/admin/orcl/pfile/init.ora.XXXXXXXX到上述目录, 名字改成INITorcl.ORA
init.ora.XXXX也是个pfile文件,不妨试着用这个文件启动你的数据库
SQL>startup pfile='d:/oracle/product/10.1.0/admin/orcl/pfile/init.ora.XXXXXXXX'
特别是你改动参数导致数据库无法启动的情况下, 用这个文件恢复你的spfile将会非常有用
SQL>create spfile from pfile='d:/oracle/product/10.1.0/admin/orcl/pfile/init.ora.XXXXXXXX'
2.修改pfile的内容
修改后主要内容为
db_block_buffers=400000
db_block_size=8192
java_pool_size=150000000(150M)
job_queue_processes=10
large_pool_size=150000000(150M)
shared_pool_size=800000000(800M)
lock_sga=true
pre_aggregate_tagert=500000000(250M左右)
workarea_size_policy=auto
pre_page_sga=true
use_indirect_data_buffers=true
注意1:db_buffer_size=db_block_buffers*db_block_size, db_block_size为数据库默认值, 不要修改,
比如你的db_block_size为4096话, 那么你的db_block_buffers应该为80000, 使得它们的乘积为3.2G
注意2:pfile不要有SGA_TARGET这个参数, 也不要有db_cache_size这个参数, SGA_MAX_SIZE也不需要
3.根据pfile启动数据库
SQL>startup pfile='d:/oracle/product/10.1.0/db_1/database/INITorcl.ORA'
如果不能启动, 可能是某些参数的原因, 那么就修改INIToracl.ORA的配置文件, 直到能正常启动为止。
4.创建spfile
SQL>create spfile from pfile
上诉命令将覆盖d:/oracle/product/10.1.0/db_1/database/下的spfile文件"SPFILEORCL.ORA"
当然你也可以显式的指明pfile
SQL>create spfile from 'd:/oracle/product/10.1.0/db_1/database/INITorcl.ORA'
5.用spfile启动数据库并调整性能
SQL>startup
然后在oem中调整各种参数, 调整后的参数将写在spfile中, 但是不会写在pfile中
总结:这样以来, 大概有5G的内存给服务器使用, 但是执行同样的SQL并没有效率上的提高, 可能并发的时候效率有所提高, 所以这种方案最终放弃, 要想使用大内存, 建议使用64位数的操作系统代替32位的操作系统, 这样使人更放心.
是不是很难准确地分配不同的池所需的内存数?自动共享内存管理特性使得自动将内存分配到最需要的地方去成为可能。
无论您是一个刚入门的 DBA 还是一个经验丰富的 DBA,您肯定至少看到过一次类似以下的错误:
ORA-04031:unable to allocate 2216 bytes of shared memory ("shared pool"... ...
或者这种错误:
ORA-04031:unable to allocate XXXX bytes of shared memory
("large pool","unknown object","session heap","frame")
或者可能这种错误:
ORA-04031:unable to allocate bytes of shared memory ("shared pool",
"unknown object","joxlod:init h", "JOX:ioc_allocate_pal")
第一种错误的原因很明显:分配给共享池的内存不足以满足用户请求。(在某些情况下,原因可能不是池本身的大小,而是未使用绑定变量导致的过多分析造成的碎片,这是我很喜欢的一个主题;但目前让我们把重点放在手头的问题上。)其它的错误分别来自大型池和 Java 池的空间不足。
您需要解决这些错误情况,而不作任何与应用程序相关的修改。那么有哪些方案可选呢?问题是如何在Oracle 例程所需的所有池之间划分可用的内存。
馅饼怎么分?
正如您所了解的,一个 Oracle 例程的系统全局区域 (SGA) 包含几个内存区域(包括缓冲高速缓存、共享池、Java 池、大型池和重做日志缓冲)。这些池在操作系统的内存空间中占据了固定的内存数;它们的大小由 DBA 在初始化参数文件中指定。
这四个池(数据库块缓冲高速缓存、共享池、Java 池和大型池)几乎占据了 SGA 中所有的空间。(与其它区域相比,重做日志缓冲没有占据多少空间,对我们这里的讨论无关紧要。)作为 DBA,您必须确保它们各自的内存分配是充足的。
假定您决定了这些池的值分别是 2GB、1GB、1GB 和 1GB。您将设置以下初始化参数来为数据库例程规定池的大小。
db_cache_size = 2g
shared_pool_size = 1g
large_pool_size = 1g
java_pool_size = 1g
现在,仔细看一下这些参数。坦白讲,这些值是否准确?
我相信您一定会有疑虑。在实际中,没有人能够为这些池指定确切的内存数 — 它们太依赖于数据库内部的处理,而处理的特性随时在变化。
下面是一个示例场景。假定您有一个典型的、大部分属于 OLTP 的数据库,并且为缓冲高速缓存分配的专用内存比为纯 OLTP 数据库(现在已经很少见了)分配的要少。有一天,您的用户放开了一些非常大的全表扫描,以创建当天的结束报表。Oracle9i 数据库为您提供了在线修改内存分配的功能,但由于提供的总物理内存有限,您决定从大型池和 Java 池中取出一些内存:
alter system set db_cache_size = 3g scope=memory;
alter system set large_pool_size = 512m scope=memory;
alter system set java_pool_size = 512m scope=memory;
这个解决方案能够很好地工作一段时间,但是接着夜间的 RMAN 作业(它们使用大型池)开始了,大型池将立即出现内存不足。同样,您从数据库高速缓存中取出一些内存来补充大型池,以挽救这种局面。
RMAN 作业完成,然后启动一个广泛使用 Java 的批处理程序,接着您开始看到与 Java 池相关的错误。因此,您(再次)重新分配池,以满足 Java 池和数据库高速缓存上的内存需求:
alter system set db_cache_size = 2G scope=memory;
alter system set large_pool_size = 512M scope=memory;
alter system set java_pool_size = 1.5G scope=memory;
第二天早上,OLTP 作业恢复在线,这个循环又完全重复!
解决这种恶性循环的一种替代方法是永久设置每个池的最大需求。不过,这么做的话,您分配的总的 SGA 可能超出可用的内存 — 从而在为每个池分配的内存数不足时,将增加交换和分页的风险。人工重新分配的方法(虽然不实际)目前看起来很不错。
另一种替代方法是将值设为可接受的最小值。不过,当需求增长且内存不能完全满足时,性能将受到影响。
注意在所有这些示例中,分配给 SGA 的总内存保持不变,而池之间的内存分配根据即时的需求进行修改。如果 RDBMS 将自动探测来自用户的需求并相应地重新分布内存分配,那不是很好吗?
Oracle 数据库 10g 中的自动共享内存管理特性正好能够实现这一目的。您可以决定 SGA 的总大小,然后设置一个名称为 SGA_TARGET 的参数,这个参数决定 SGA 的总大小。SGA 内部的各个池将根据工作负载动态地进行配置。实现自动内存分配仅仅需要 SGA_TARGET 参数的一个非零值。
设置自动共享内存管理
让我们看看该特性是如何工作的。首先,确定 SGA 的总大小。您可以通过确定现在分配了多少内存来估计这个值。
SQL> select sum(value)/1024/1024 from v$sga;
SUM(VALUE)/1024/1024
--------------------
500
此时 SGA 的当前总大小近似为 500MB,并且这个值将变为 SGA_TARGET 的值。接下来,执行语句:
alter system set sga_target = 500M scope=both;
这种方法不需要为各个池设置不同值;因而,您将需要在参数文件中使它们的值为零或全部删除它们。
shared_pool_size = 0
large_pool_size = 0
java_pool_size = 0
db_cache_size = 0
再循环数据库,使这些值生效。
这个人工过程还可以通过 Enterprise Manager 10g 实施。从数据库主页中,选择 "Administration" 选项卡,然后选择 "Memory Parameters"。对于人工配置的内存参数,将显示标记为 "Enable" 的按钮,以及所有人工配置的池的值。单击 "Enable" 按钮,启用自动共享内存管理特性。企业管理器将完成剩下的工作。
在配置了自动内存分配之后,您可以利用以下命令检查它们的大小:
SQL> select current_size from v$buffer_pool;
CURRENT_SIZE
------------
340
SQL> select pool, sum(bytes)/1024/1024 Mbytes from v$sgastat group by pool;
POOL MBYTES
------------ ----------
java pool 4
large pool 4
shared pool 148
正如您所看到的,所有的池都从 500MB 的总目标大小中自动进行分配。(参见图 1。)缓冲高速缓存大小是 340MB,Java 池是 4MB,大型池是 4MB,共享池是 148MB。它们合起来总的大小为 (340+4+4+148=) 496MB,近似与 500MB 的目标 SGA 的大小相同。

图 1:池的初始分配
现在假定提供给 Oracle 的主机内存从 500MB 减少为 300MB,这意味着我们必须减少总 SGA 的大小。我们可以通过减小目标 SGA 大小来反映这种变化。
alter system set sga_target = 300M scope=both;
现在查看各个池,我们可以看到:
SQL> select current_size from v$buffer_pool;
CURRENT_SIZE
------------
244
SQL> select pool, sum(bytes)/1024/1024 Mbytes from v$sgastat group by pool;
POOL MBYTES
------------ ----------
java pool 4
large pool 4
shared pool 44
占用的总大小是 240+4+4+44 = 296MB,接近于目标的 300MB。注意如图 2 所示,当 SGA_TARGET 改变时,如何自动重新分配池。

图 2:在将 SGA 大小减少到 300MB 之后重新分配池
这些池的大小是动态的。池将根据工作负载扩展,以容纳需求的增长,或缩小以容纳另一个池的扩展。这种扩展或缩小自动发生,无需 DBA 的干预,这与本文开头的示例不同。让我们暂时返回到那个场景,假定在初始分配后,RMAN 作业启动,指示需要一个更大的大型池,大型池将从 4MB 扩展到 40MB,以容纳需求。这个额外的 36MB 将从数据库缓冲中划出,数据库块缓冲将缩小,如图 3 所示。

图 3:在对大型池的需求增长之后经过重新分配的池
池的大小变化基于系统上的工作负载,因此不需要为最坏的情况调整池的大小 — 它们将根据需求的增长自动调整。此外,SGA 的总大小始终在由 SGA_TARGET 指定的最大值之内,因此不存在使内存需求的增长比例失调(这将导致分页和交换)的风险。您可以动态地将 SGA_TARGET 增加至绝对最大值,这个绝对最大值是通过调整参数 SGA_MAX_SIZE 指定的。
哪些池不受影响?
SGA 中的一些池不受动态大小调整的影响,但是必须显式指定这些池。其中值得注意的是非标准块大小的缓冲池,以及 KEEP 池或 RECYCLE 池的非默认块大小。如果您的数据库有一个块大小为 8K,而您想要配置 2K、4K、16K 和 32K 块大小的池,那么您必须手动设置它们。它们的大小将保持不变;它们将不会根据负载缩小或扩展。当使用多种大小的缓冲池、KEEP 池和 RECYCLE 池时,您应当考虑这个因素。此外,日志缓冲不受内存调整的影响 — 不管工作负载如何,
有些参数不能动态修改,需要使用ALTER SYSTEM SET parameter = value SCOPE = SPFILE,然后重新启动数据库才能生效。
而ALTER SYSTEM SET parameter = value 使用的默认SCOPE = BOTH,包括同时修改spfile 和当前的设置。
摘要:通过探讨和研究Oracle服务器和Client/Server的特点和原理,阐述了提高、调整Oracle应用系统性能的一些原则和方法。
关键词:Oracle;客户/服务器;系统全程区;网络I/O;回滚段。
Oracle 数据库广泛应用在社会的各个领域,特别是在Client/Server模式的应用,但是应用开发者往往碰到整个系统的性能随着数据量的增大显著下降的问题,为了解决这个问题,从以下几个方面:数据库服务器、网络I/O、应用程序等对整个系统加以调整,充分发挥Oracle的效能,提高整个系统的性能。
1 调整数据库服务器的性能
Oracle数据库服务器是整个系统的核心,它的性能高低直接影响整个系统的性能,为了调整Oracle数据库服务器的性能,主要从以下几个方面考虑:
1.1 调整操作系统以适合Oracle数据库服务器运行
Oracle数据库服务器很大程度上依赖于运行服务器的操作系统,如果操作系统不能提供最好性能,那么无论如何调整,Oracle数据库服务器也无法发挥其应有的性能。
1.1.1 为Oracle数据库服务器规划系统资源
据已有计算机可用资源, 规划分配给Oracle服务器资源原则是:尽可能使Oracle服务器使用资源最大化,特别在Client/Server中尽量让服务器上所有资源都来运行Oracle服务。
1.1.2 调整计算机系统中的内存配置
多数操作系统都用虚存来模拟计算机上更大的内存,它实际上是硬盘上的一定的磁盘空间。当实际的内存空间不能满足应用软件的要求时,操作系统就将用这部分的磁盘空间对内存中的信息进行页面替换,这将引起大量的磁盘I/O操作,使整个服务器的性能下降。为了避免过多地使用虚存,应加大计算机的内存。
1.1.3 为Oracle数据库服务器设置操作系统进程优先级
不要在操作系统中调整Oracle进程的优先级,因为在Oracle数据库系统中,所有的后台和前台数据库服务器进程执行的是同等重要的工作,需要同等的优先级。所以在安装时,让所有的数据库服务器进程都使用缺省的优先级运行。
1.2 调整内存分配
Oracle数据库服务器保留3个基本的内存高速缓存,分别对应3种不同类型的数据:库高速缓存,字典高速缓存和缓冲区高速缓存。库高速缓存和字典高速缓存一起构成共享池,共享池再加上缓冲区高速缓存便构成了系统全程区(SGA)。SGA是对数据库数据进行快速访问的一个系统全程区,若SGA本身需要频繁地进行释放、分配,则不能达到快速访问数据的目的,因此应把SGA放在主存中,不要放在虚拟内存中。内存的调整主要是指调整组成SGA的内存结构的大小来提高系统性能,由于Oracle数据库服务器的内存结构需求与应用密切相关,所以内存结构的调整应在磁盘I/O调整之前进行。
1.2.1 库缓冲区的调整
库缓冲区中包含私用和共享SQL和PL/SQL区,通过比较库缓冲区的命中率决定它的大小。要调整库缓冲区,必须首先了解该库缓冲区的活动情况,库缓冲区的活动统计信息保留在动态性能表v$librarycache数据字典中,可通过查询该表来了解其活动情况,以决定如何调整。
Select sum(pins),sum(reloads) from v$librarycache;
|
Pins列给出SQL语句,PL/SQL块及被访问对象定义的总次数;Reloads列给出SQL 和PL/SQL块的隐式分析或对象定义重装载时在库程序缓冲区中发生的错误。如果sum(pins)/sum(reloads) ≈0,则库缓冲区的命中率合适;若sum(pins)/sum(reloads)>1, 则需调整初始化参数 shared_pool_size来重新调整分配给共享池的内存量。
1.2.2 数据字典缓冲区的调整
数据字典缓冲区包含了有关数据库的结构、用户、实体信息。数据字典的命中率,对系统性能影响极大。数据字典缓冲区的使用情况记录在动态性能表v$librarycache中,可通过查询该表来了解其活动情况,以决定如何调整。
Select sum(gets),sum(getmisses) from v$rowcache;
|
Gets列是对相应项请求次数的统计;Getmisses 列是引起缓冲区出错的数据的请求次数。对于频繁访问的数据字典缓冲区,sum(getmisses)/sum(gets)<10%~15%。若大于此百分数,则应考虑增加数据字典缓冲区的容量,即需调整初始化参数shared_pool_size来重新调整分配给共享池的内存量。
1.2.3 缓冲区高速缓存的调整
用户进程所存取的所有数据都是经过缓冲区高速缓存来存取,所以该部分的命中率,对性能至关重要。缓冲区高速缓存的使用情况记录在动态性能表v$sysstat中,可通过查询该表来了解其活动情况,以决定如何调整。
Select name,value from v$sysstat where name in
('dbblock gets','consistent gets','physical reads');
|
dbblock gets和consistent gets的值是请求数据缓冲区中读的总次数。physical reads的值是请求数据时引起从盘中读文件的次数。从缓冲区高速缓存中读的可能性的高低称为缓冲区的命中率,计算公式:
Hit Ratio=1-(physical reds/(dbblock gets+consistent gets))
如果Hit Ratio<60%~70%,则应增大db_block_buffers的参数值。db_block_buffers可以调整分配给缓冲区高速缓存的内存量,即db_block_buffers可设置分配缓冲区高速缓存的数据块的个数。缓冲区高速缓存的总字节数=db_block_buffers的值*db_block_size的值。db_block_size 的值表示数据块大小的字节数,可查询 v$parameter 表:
select name,value from v$parameter where name='db_block_size';
|
在修改了上述数据库的初始化参数以后,必须先关闭数据库,在重新启动数据库后才能使新的设置起作用。
1.3 调整磁盘 I/O
磁盘的I/O速度对整个系统性能有重要影响。解决好磁盘I/O问题,可显著提高性能。影响磁盘I/O的性能的主要原因有磁盘竞争、I/O次数过多和数据块空间的分配管理。
为Oracle数据库服务器创建新文件时,不论是表空间所用的数据文件还是数据事务登录所用的日志文件,都应仔细考虑数据库服务器上的可用磁盘资源。如果服务器上有多个磁盘,则可将文件分散存储到各个可用磁盘上,减少对数据库的数据文件及事务日志文件的竞争,从而有效地改善服务器的性能。对于不同的应用系统都有各自的数据集,应当创见不同的表空间分别存储各自应用系统的数据,并且尽可能的把表空间对应的数据文件存放在不同的磁盘上,这种从物理上把每个应用系统的表空间分散存放的方法,可以排除两个应用系统竞争磁盘的可能性。数据文件、事务日志文件分别存放在不同的磁盘上,这样事务处理执行的磁盘访问不妨碍对相应的事物日志登记的磁盘访问。如果有多个磁盘可用,将两个事物日志成员放在不同的磁盘驱动器上,就可以消除日志文件可能产生的磁盘竞争。应把一个应用的表数据和索引数据分散存放不同表空间上,并且尽量把不同类型的表空间存放在不同磁盘上,这样就消除了表数据和索引数据的磁盘竞争。
1.4 调整数据库服务器的回滚段
回滚段是一个存储区域,数据库使用该存储区域存放曾经由一个事务更新或删除的行的原始数据值。如果用户要回滚一个事务所做的改变,那么数据库就从回滚段中读回改变前的数据并使该事务影响的行改变为它们的原状态。回滚段控制着数据库处理事务的能力,因而在数据库成功中起着关键性的作用,不管数据库的其它部分设计得多好,如果它设计得不合理,将会严重影响系统的性能。建立和调整回滚段的原则如下。
1.4.1 分离回滚段
分离回滚段是指单独为回滚段创建一个以上的表空间,使回滚段与数据字典、用户数据、索引等分离开来。由于回滚段的写入与数据和索引的写入是并行进行的,因此将它分离出来可以减少I/O争用。如果回滚段与数据不分离,倘若要某个表空间脱机或撤消,那么在该表空间中的各个回滚段没有全部脱机之前,不能将这个表空间脱机或撤消。而一旦该表空间不可用,则该表空间中的所有回滚段也不能使用,这将浪费所有分配的磁盘空间。所以,独立回滚段可使数据库管理变得容易。回滚段的经常性收缩,使得表空间的自由块更容易形成碎片。分离回滚段可以减少数据库表空间的碎片产生。
1.4.2 创建不同大小的回滚段群
对于一些联机事物处理,他们一般是频繁地对少量数据进行修改,创建许多小的回滚段对之有利。每一个事物的入口项只能限于一个回滚段,回滚段应该充分大以容纳一个完整的事物处理,因此对一些较大型事物,需要较大型的回滚段。极个别脱机处理事物会产生大量的回滚信息,这时需要一个特大号的回滚段来处理。根据这些理论,在Oracle数据库服务器中针对上述3种事物处理创建三组:小事物组、较大事物组、特大事物组等大小不同的回滚段群,并且将之分散到3个不同的表空间上,群内大小相同,应能满足该组事物处理的最大要求。
1.4.3 创建数量适当的回滚段
一般回滚段数量与并发事物个数有关,以下给出由于并发事物个数而应建立回滚段的参考数:
并发事物(n) 回滚段数
n<16 4
16 ≤ n<32 8
n≥ 32 n/4
|
2 调整 Client/Server 模式下的网络 I/O
Client/Server环境中的应用处理是分布在客户应用程序和数据库服务程序之间的。在 Client/Server环境中Client与Server之间的网络I/O是整个系统性能提高的瓶颈,一个客户应用程序引起的网络I/O越少,应用及整个系统的性能越好。减少网络I/O的最重要的一条原则:将应用逻辑集中在数据库服务器中。
2.1 使用Oracle数据库的完整约束性
当为应用建表时,应当为一些有特殊要求的数据加上适当的完整性约束,这样就能实现由数据库本身而不是应用程序来约束数据符合一定的条件。数据库服务器端的完整约束的执行操作是在比SQL语句级别更低的系统机制上优化,它与客户端无关,只在服务器中运行,不需在Client 端和Server端之间传递SQL语句,有效地减轻网络I/O负担。
2.2 使用数据库触发器
完整约束性只能实现一些较简单的数据约束条件,对一些较复杂的事物处理规则就无能为力,这时最好不要在应用程序中实施复杂的程序控制,而是应当采用数据库触发器来实施复杂的事物规则。数据库触发器能实现由数据库本身,而不是应用程序,来约束数据符合复杂的事物处理规则,并且容易创建,便于管理,避免大量的网络I/O。
例如:将当前表A中成为历史的记录从A表中转储到历史表B中,表示为Lsbs。
在应用程序中实现: 用数据库触发器实现:
Beign Create trigger delete1
Update A set lsbs='T'; After update of lsbs on A
Insert into B For each row
Select * from A where lsbs='T'; Insert into B
Delete A where lsbs='T'; select * from A where :new.lsbs='T';
End; Delete A where :new.lsbs='T';
End delete1;
|
在应用程序中实现时,所有的SQL命令请求传送的数据都要通过网络在Client端和Server端进行交换,而不像数据库触发器一样,SQL本身在Server端,不需要通过网络传输数据。当进行操作的数据量相当大时,并且多个用户同时操作时,通过在应用程序中实现复杂的控制,必将增大网络I/O的负荷,使整个系统的性能降低,而用数据库触发器能完全避免这种情况发生。
2.3 使用存储过程、存储函数和包
Oracle的存储过程和存储函数是命名的能完成一定功能并且存储在Server端的PL/SQL的集合。包是一种把有关的过程和函数组织封装成一个数据库程序单元的方法。它们相对于应用程序的过程、函数而言,把SQL命令存储在Server端。使用存储过程和存储函数,应用程序不必再包含多个网络操作的SQL语句去执行数据库服务器操作,而是简单调用存储过程和存储函数,在网络上传输的只是调用过程的名字和输出结果,这样就可减少大量的网络I/O。
例如:基表A、B的定义:name char(20);detail char(10);A表100万记录,应用程序将从基表A中检索detail列符合给出条件的记录,并将之插入基表B。
Declare
Cursor cursor1 is select*from A;poin cursor1%type;
con1 number(2);res1 char(4)='abcd';
Begin Insert into B values(poin.name,poin.detail);end if;
For poin in cursor1 loop End loop;End loop;
For con1 in 1..7 loop Commit;
If substr(poin.detail ,con1,4)=res1 then End;
|
如果在Developer/2000 From中按钮触发器直接用PL/SQL实现和把它改写为一个Oracle存储过程,然后在From中调用此过程实现比较,后者性能显著提高。
在考虑使用上述3种方法时:首先考虑使用完整约束性。对于数据库触发器和存储过程,如果需要所有访问数据库的程序自动实施一定规则或检查,那么使用数据库触发器;如果只需对少数的程序实施一定的规则或检查,则可创建一个过程,让有关程序调用这个过程。
3 应用程序的调整
3.1 SQL语句的优化
SQL语句的执行速度,可以受很多因素的影响而变化。但主要的影响因素是:驱动表、执行操作的先后顺序和索引的运用。可以由很多不同的方法间接地改变这些因素,以达到最优的执行速度。这里主要探讨当对多个表进行连接查询时应遵循的优化原则:
(1) 用于连接的子句的列应被索引、在Where子句中应尽量利用索引,而不是避开索引。
(2) 连接操作应从返回较少行上驱动。
(3) 如果所连接的表A和B,A表长度远远大于B表,建议从较大的A表上驱动。
(4) 如果Where子句中含有选择性条件,Where No=20,将最具有选择性部分放在表达式最后。
(5) 如果只有一个表有索引,另一表无索引,无索引的表通常作为驱动表。如A表的No列以被索引,而B表的No 列没被索引,则应当B表作为驱动表,A表作为被驱动表。
(6) 若用于连接的列和Where子句中其他选择条件列均有索引,则按各个索引对查询的有效性和选择性分别定出级别,结合表中具体数据构成情况,从中选出优化路径,一般需要考虑:子句中哪些列可以使用索引、哪些索引具有唯一性及被查询表行数目等。
3.2 建立和使用视图、索引
利用视图可以将基表中的列或行进行裁减、隐藏一部分数据,并且能够将涉及到多个表的复杂查询以视图的方式给出,使应用程序开发简洁快速。利用索引可以提高查询性能,减少磁盘 I/O,优化对数据表的查询,加速SQL语句的执行。但任何时候建立索引都能提高性能,何时建立索引应当遵循以下原则:该表常用来在索引列上查询,该表不常更新、插入、删除等操作,查询出来的结果记录数应控制在原表的2%~4%。
3.3 使用 Oracle 的数组接口
当一个客户应用程序插入一行或用一个查询来向服务器请求某行时,不是发送具有单个行的网络包,而是采用数组处理,即把要插入的多个行或检索出的多个行缓冲在数组中,然后通过很少的几个包就可在网上传送这些数组。例如,一个给定的Select语句返回2000行数据,每行平均大小为40个字节,数据包的大小为4kB,而数组大小参数(arraysize)设置为20 ,则需从服务器发送100个数据包到客户机。如果简单地把(arraysize)设置为2000,那么同样的操作只需要传送 20个数据包。这样就减少了网络的传输量,提高了所有应用的性能。
4 总结
我们在开发应用程序时,遵循上述的方法和原则,对系统进行调整,收到了令人满意的效果。但是应当指出,由于客户机、网络、服务器这3个相互依存的组成部分都必须调整和同步才能产生最佳的性能,因此还应根据系统的具体情况,具体分析和调整。
Oracle Database 10g Release 2 (10.2.0.1.0) Enterprise/Standard Edition for Microsoft Windows (32-bit)
http://download.oracle.com/otn/nt/oracle10g/10201/10201_database_win32.zip
http://download.oracle.com/otn/nt/oracle10g/10201/10201_client_win32.zip
http://download.oracle.com/otn/nt/oracle10g/10201/10201_clusterware_win32.zip
http://download.oracle.com/otn/nt/oracle10g/10201/10201_gateways_win32.zip
Oracle Database 10g Release 2 (10.2.0.1.0) Enterprise/Standard Edition for Microsoft Windows (x64)
http://download.oracle.com/otn/nt/oracle10g/10201/102010_win64_x64_database.zip
http://download.oracle.com/otn/nt/oracle10g/10201/102010_win64_x64_client.zip
http://download.oracle.com/otn/nt/oracle10g/10201/102010_win64_x64_clusterware.zip
Oracle Database 10g Release 2 (10.2.0.1.0) Enterprise/Standard Edition for Linux x86
http://download.oracle.com/otn/linux/oracle10g/10201/10201_database_linux32.zip
http://download.oracle.com/otn/linux/oracle10g/10201/10201_client_linux32.zip
http://download.oracle.com/otn/linux/oracle10g/10201/10201_gateways_linux32.zip
Oracle Database 10g Release 2 (10.2.0.1.0) Enterprise/Standard Edition for Linux x86-64
http://download.oracle.com/otn/linux/oracle10g/10201/10201_database_linux_x86_64.cpio.gz
http://download.oracle.com/otn/linux/oracle10g/10201/10201_client_linux_x86_64.cpio.gz
http://download.oracle.com/otn/linux/oracle10g/10201/10201_clusterware_linux_x86_64.cpio.gz
http://download.oracle.com/otn/linux/oracle10g/10201/10201_gateways_linux_x86_64.cpio.gz
Oracle Database 10g Release 2 (10.2.0.1.0) Enterprise/Standard Edition for AIX5L
http://download.oracle.com/otn/aix/oracle10g/10201/10gr2_aix5l64_database.cpio.gz
http://download.oracle.com/otn/aix/oracle10g/10201/10gr2_aix5l64_client.cpio.gz
http://download.oracle.com/otn/aix/oracle10g/10201/10gr2_aix5l64_cluster.cpio.gz
http://download.oracle.com/otn/aix/oracle10g/10201/10gr2_aix5l64_gateways.cpio.gz
Oracle Database 10g Release 2 (10.2.0.2) Enterprise/Standard Edition for Solaris Operating System (x86)
http://download.oracle.com/otn/solaris/oracle10g/10202/10202_database_solx86.zip
http://download.oracle.com/otn/solaris/oracle10g/10202/10202_client_solx86.zip
http://download.oracle.com/otn/solaris/oracle10g/10202/10202_clusterware_solx86.zip
Oracle Database 10g Release 2 (10.2.0.1.0) Enterprise/Standard Edition for Solaris Operating System (x86-64)
http://download.oracle.com/otn/solaris/oracle10g/10201/x8664/10201_database_solx86_64.zip
http://download.oracle.com/otn/solaris/oracle10g/10201/x8664/10201_client_solx86_64.zip
http://download.oracle.com/otn/solaris/oracle10g/10201/x8664/10201_clusterware_solx86_64.zip
---------------------------------------------------------------------------------------------------------
更多oracle10g数据库相关下载请参考下面的官方下载网址:http://www.oracle.com/technology/global/cn/software/products/database/index.html
ORACLE10gR2下载_oracle10G官方下载_Oracle10G Release2(10.2.0.1.0)下载_Oracle10g下载_oracle下载(Authour: 王大帅 Email: dashuaiwang@126.com 个人空间:http://wds.3u.cn)
ORACLE10G 补丁_ORACLE10g PATCH下载地址:
oracle 10.2.0.2 patch:
ftp://updates.oracle.com/4547817/p4547817_10202_AIX64-5L.zip
ftp://updates.oracle.com/4547817/p4547817_10202_HP64.zip
ftp://updates.oracle.com/4547817/p4547817_10202_HPUX-IA64.zip
ftp://updates.oracle.com/4547817/p4547817_10202_LINUX.zip
ftp://updates.oracle.com/4547817/p4547817_10202_Linux-IA64.zip
ftp://updates.oracle.com/4547817/p4547817_10202_Linux-x86-64.zip
ftp://updates.oracle.com/4547817/p4547817_10202_MVS.zip
ftp://updates.oracle.com/4547817/p4547817_10202_SOLARIS.zip
ftp://updates.oracle.com/4547817/p4547817_10202_SOLARIS64.zip
ftp://updates.oracle.com/4547817/p4547817_10202_WINNT.zip
ftp://updates.oracle.com/4547817/p4547817_10202_WINNT64.zip
oracle 10.2.0.3 patch:
ftp://updates.oracle.com/5337014/p5337014_10203_WINNT.zip
ftp://updates.oracle.com/5337014/p5337014_10203_WINNT64.zip
ftp://updates.oracle.com/5337014/p5337014_10203_AIX5L.zip
ftp://updates.oracle.com/5337014/p5337014_10203_WINNT.zip
ftp://updates.oracle.com/5337014/p5337014_10203_WINNT.zip
ftp://updates.oracle.com/5337014/p5337014_10203_WINNT.zip
oracle 10.2.0.4 patch:
ftp://updates.oracle.com/6810189/p6810189_10204_AIX5L.zip
ftp://updates.oracle.com/6810189/p6810189_10204_LINUX.zip
ftp://updates.oracle.com/6810189/p6810189_10204_Linux-x86-64.zip
ftp://updates.oracle.com/6810189/p6810189_10204_Linux-x86.zip
ftp://updates.oracle.com/6810189/p6810189_10204_Win32.zip
ftp://updates.oracle.com/6810189/p6810189_10204_Win64.zip
Oracle9i
Oracle9i Database Release 2 Enterprise/Standard/Personal Edition for Windows NT/2000/XP
http://download.oracle.com/otn/nt/oracle9i/9201/92010NT_Disk1.zip
http://download.oracle.com/otn/nt/oracle9i/9201/92010NT_Disk2.zip
http://download.oracle.com/otn/nt/oracle9i/9201/92010NT_Disk3.zip
Oracle9i Database Release 2 Enterprise/Standard/Personal/Client Edition for Windows XP 2003/Windows Server 2003 (64-bit)
http://download.oracle.com/otn/nt/oracle9i/9202/92021Win64_Disk1.zip
http://download.oracle.com/otn/nt/oracle9i/9202/92021Win64_Disk2.zip
Oracle9i Database Release 2 Enterprise/Standard Edition for Intel Linux
http://download.oracle.com/otn/linux/oracle9i/9204/ship_9204_linux_disk1.cpio.gz
http://download.oracle.com/otn/linux/oracle9i/9204/ship_9204_linux_disk2.cpio.gz
http://download.oracle.com/otn/linux/oracle9i/9204/ship_9204_linux_disk3.cpio.gz
Oracle9i Database Release 2 (9.2.0.4) Enterprise/Standard Edition for Linux x86-64
http://download.oracle.com/otn/linux/oracle9i/9204/amd64_db_9204_Disk1.cpio.gz
http://download.oracle.com/otn/linux/oracle9i/9204/amd64_db_9204_Disk2.cpio.gz
http://download.oracle.com/otn/linux/oracle9i/9204/amd64_db_9204_Disk3.cpio.gz
Oracle9i Database Release 2 Enterprise/Standard Edition for AIX - Based 4.3.3 Systems (64-bit)
http://download.oracle.com/otn/aix/oracle9i/9201/server_9201_AIX64_Disk1.cpio.gz
http://download.oracle.com/otn/aix/oracle9i/9201/server_9201_AIX64_Disk2.cpio.gz
http://download.oracle.com/otn/aix/oracle9i/9201/server_9201_AIX64_Disk3.cpio.gz
http://download.oracle.com/otn/aix/oracle9i/9201/server_9201_AIX64_Disk4.cpio.gz
Oracle9i Database Release 2 Enterprise/Standard Edition for AIX- Based 5L Systems
http://download.oracle.com/otn/aix/oracle9i/9201/A99331-01.zip
http://download.oracle.com/otn/aix/oracle9i/9201/A99331-02.zip
http://download.oracle.com/otn/aix/oracle9i/9201/A99331-03.zip
http://download.oracle.com/otn/aix/oracle9i/9201/A99331-04.zip
Oracle9i Database Release 2 Enterprise/Standard Edition for Sun SPARC Solaris (32-bit)
http://download.oracle.com/otn/solaris/oracle9i/9201/92010Sol_Disk1.cpio.gz
http://download.oracle.com/otn/solaris/oracle9i/9201/92010Sol_Disk2.cpio.gz
http://download.oracle.com/otn/solaris/oracle9i/9201/92010Sol_Disk3.cpio.gz
Oracle9i Database Release 2 Enterprise/Standard Edition for Sun SPARC Solaris (64-bit)
http://download.oracle.com/otn/solaris/oracle9i64/9201/solaris64_9.2.0.1.0.Disk1.cpio.gz
http://download.oracle.com/otn/solaris/oracle9i64/9201/solaris64_9.2.0.1.0.Disk2.cpio.gz
http://download.oracle.com/otn/solaris/oracle9i64/9201/solaris64_9.2.0.1.0.Disk3.cpio.gz
http://MSTRWebURL?server={&SERVERNAME}&project={&PR OJECT}&evt
48001&ViewMode=2&documentID=[ProvideDocID]&originMe ssageID={&DOCUMENTMESSAGEID}&elementsPromptAnswers ={&[ProvideAttributeName]@GUID};{&[ProvideAttribut e Name]@ElementID}
mstrWeb?server={&SERVERNAME}&project={&PROJECT}&port=0&evt=4001&src=mstrWeb.4001&reportID={&[固定资本形成总额各年情况分析]:GUID}&originMessageID={&DOCUMENTMESSAGEID}&reportViewMode=2
package com.dbs.vote.common.test.excel;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import jxl.Cell;
import jxl.CellType;
import jxl.Sheet;
import jxl.Workbook;
import jxl.WorkbookSettings;
import jxl.format.Alignment;
import jxl.format.Border;
import jxl.format.BorderLineStyle;
import jxl.format.Colour;
import jxl.format.VerticalAlignment;
import jxl.write.Formula;
import jxl.write.Label;
import jxl.write.NumberFormat;
import jxl.write.WritableCellFeatures;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
public class JExcelUtils {
/**
* 生成Excel文件
* @param path 文件路径
* @param sheetName 工作表名称
* @param dataTitles 数据标题
*/
public void createExcelFile(String path,String sheetName,String[] dataTitles){
WritableWorkbook workbook;
try{
OutputStream os=new FileOutputStream(path);
workbook=Workbook.createWorkbook(os);
WritableSheet sheet = workbook.createSheet(sheetName, 0); //添加第一个工作表
initialSheetSetting(sheet);
Label label;
for (int i=0; i<dataTitles.length; i++){
//Label(列号,行号,内容,风格)
label = new Label(i, 0, dataTitles[i],getTitleCellFormat());
sheet.addCell(label);
}
//插入一行
insertRowData(sheet,1,new String[]{"200201001","张三","100","60","100","260"},getDataCellFormat(CellType.STRING_FORMULA));
//一个一个插入行
label = new Label(0, 2,"200201002",getDataCellFormat(CellType.STRING_FORMULA));
sheet.addCell(label);
label = new Label(1, 2,"李四",getDataCellFormat(CellType.STRING_FORMULA));
sheet.addCell(label);
insertOneCellData(sheet,2,2,70.5,getDataCellFormat(CellType.NUMBER));
insertOneCellData(sheet,3,2,90.523,getDataCellFormat(CellType.NUMBER));
insertOneCellData(sheet,4,2,60.5,getDataCellFormat(CellType.NUMBER));
insertFormula(sheet,5,2,"C3+D3+E3",getDataCellFormat(CellType.NUMBER_FORMULA));
//插入日期
mergeCellsAndInsertData(sheet, 0, 3, 5, 3, new Date(), getDataCellFormat(CellType.DATE));
workbook.write();
workbook.close();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 初始化表格属性
* @param sheet
*/
public void initialSheetSetting(WritableSheet sheet){
try{
//sheet.getSettings().setProtected(true); //设置xls的保护,单元格为只读的
sheet.getSettings().setDefaultColumnWidth(10); //设置列的默认宽度
//sheet.setRowView(2,false);//行高自动扩展
//setRowView(int row, int height);--行高
//setColumnView(int col,int width); --列宽
sheet.setColumnView(0,20);//设置第一列宽度
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 插入公式
* @param sheet
* @param col
* @param row
* @param formula
* @param format
*/
public void insertFormula(WritableSheet sheet,Integer col,Integer row,String formula,WritableCellFormat format){
try{
Formula f = new Formula(col, row, formula, format);
sheet.addCell(f);
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 插入一行数据
* @param sheet 工作表
* @param row 行号
* @param content 内容
* @param format 风格
*/
public void insertRowData(WritableSheet sheet,Integer row,String[] dataArr,WritableCellFormat format){
try{
Label label;
for(int i=0;i<dataArr.length;i++){
label = new Label(i,row,dataArr[i],format);
sheet.addCell(label);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 插入单元格数据
* @param sheet
* @param col
* @param row
* @param data
*/
public void insertOneCellData(WritableSheet sheet,Integer col,Integer row,Object data,WritableCellFormat format){
try{
if(data instanceof Double){
jxl.write.Number labelNF = new jxl.write.Number(col,row,(Double)data,format);
sheet.addCell(labelNF);
}else if(data instanceof Boolean){
jxl.write.Boolean labelB = new jxl.write.Boolean(col,row,(Boolean)data,format);
sheet.addCell(labelB);
}else if(data instanceof Date){
jxl.write.DateTime labelDT = new jxl.write.DateTime(col,row,(Date)data,format);
sheet.addCell(labelDT);
setCellComments(labelDT, "这是个创建表的日期说明!");
}else{
Label label = new Label(col,row,data.toString(),format);
sheet.addCell(label);
}
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 合并单元格,并插入数据
* @param sheet
* @param col_start
* @param row_start
* @param col_end
* @param row_end
* @param data
* @param format
*/
public void mergeCellsAndInsertData(WritableSheet sheet,Integer col_start,Integer row_start,Integer col_end,Integer row_end,Object data, WritableCellFormat format){
try{
sheet.mergeCells(col_start,row_start,col_end,row_end);//左上角到右下角
insertOneCellData(sheet, col_start, row_start, data, format);
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 给单元格加注释
* @param label
* @param comments
*/
public void setCellComments(Object label,String comments){
WritableCellFeatures cellFeatures = new WritableCellFeatures();
cellFeatures.setComment(comments);
if(label instanceof jxl.write.Number){
jxl.write.Number num = (jxl.write.Number)label;
num.setCellFeatures(cellFeatures);
}else if(label instanceof jxl.write.Boolean){
jxl.write.Boolean bool = (jxl.write.Boolean)label;
bool.setCellFeatures(cellFeatures);
}else if(label instanceof jxl.write.DateTime){
jxl.write.DateTime dt = (jxl.write.DateTime)label;
dt.setCellFeatures(cellFeatures);
}else{
Label _label = (Label)label;
_label.setCellFeatures(cellFeatures);
}
}
/**
* 读取excel
* @param inputFile
* @param inputFileSheetIndex
* @throws Exception
*/
public ArrayList<String> readDataFromExcel(File inputFile, int inputFileSheetIndex){
ArrayList<String> list = new ArrayList<String>();
Workbook book = null;
Cell cell = null;
WorkbookSettings setting = new WorkbookSettings();
java.util.Locale locale = new java.util.Locale("zh","CN");
setting.setLocale(locale);
setting.setEncoding("ISO-8859-1");
try{
book = Workbook.getWorkbook(inputFile, setting);
}catch(Exception e){
e.printStackTrace();
}
Sheet sheet = book.getSheet(inputFileSheetIndex);
for (int rowIndex = 0; rowIndex < sheet.getRows(); rowIndex++) {//行
for (int colIndex = 0; colIndex < sheet.getColumns(); colIndex++) {//列
cell = sheet.getCell(colIndex, rowIndex);
//System.out.println(cell.getContents());
list.add(cell.getContents());
}
}
book.close();
return list;
}
/**
* 得到数据表头格式
* @return
*/
public WritableCellFormat getTitleCellFormat(){
WritableCellFormat wcf = null;
try {
//字体样式
WritableFont wf = new WritableFont(WritableFont.TIMES,12, WritableFont.NO_BOLD,false);//最后一个为是否italic
wf.setColour(Colour.RED);
wcf = new WritableCellFormat(wf);
//对齐方式
wcf.setAlignment(Alignment.CENTRE);
wcf.setVerticalAlignment(VerticalAlignment.CENTRE);
//边框
wcf.setBorder(Border.ALL,BorderLineStyle.THIN);
//背景色
wcf.setBackground(Colour.GREY_25_PERCENT);
} catch (WriteException e) {
e.printStackTrace();
}
return wcf;
}
/**
* 得到数据格式
* @return
*/
public WritableCellFormat getDataCellFormat(CellType type){
WritableCellFormat wcf = null;
try {
//字体样式
if(type == CellType.NUMBER || type == CellType.NUMBER_FORMULA){//数字
NumberFormat nf = new NumberFormat("#.00");
wcf = new WritableCellFormat(nf);
}else if(type == CellType.DATE || type == CellType.DATE_FORMULA){//日期
jxl.write.DateFormat df = new jxl.write.DateFormat("yyyy-MM-dd hh:mm:ss");
wcf = new jxl.write.WritableCellFormat(df);
}else{
WritableFont wf = new WritableFont(WritableFont.TIMES,10, WritableFont.NO_BOLD,false);//最后一个为是否italic
wcf = new WritableCellFormat(wf);
}
//对齐方式
wcf.setAlignment(Alignment.CENTRE);
wcf.setVerticalAlignment(VerticalAlignment.CENTRE);
//边框
wcf.setBorder(Border.LEFT,BorderLineStyle.THIN);
wcf.setBorder(Border.BOTTOM,BorderLineStyle.THIN);
wcf.setBorder(Border.RIGHT,BorderLineStyle.THIN);
//背景色
wcf.setBackground(Colour.WHITE);
wcf.setWrap(true);//自动换行
} catch (WriteException e) {
e.printStackTrace();
}
return wcf;
}
/**
* 打开文件看看
* @param exePath
* @param filePath
*/
public void openExcel(String exePath,String filePath){
Runtime r=Runtime.getRuntime();
String cmd[]={exePath,filePath};
try{
r.exec(cmd);
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
String[] titles = {"学号","姓名","语文","数学","英语","总分"};
JExcelUtils jxl = new JExcelUtils();
String filePath = "E:/test.xls";
jxl.createExcelFile(filePath,"成绩单",titles);
jxl.readDataFromExcel(new File(filePath),0);
jxl.openExcel("C:/Program Files/Microsoft Office/OFFICE11/EXCEL.EXE",filePath);
}
}
在Hibernate2.x里Hql支持 not is null 也支持 is not null 但是你打印出SQL语句就可以发现两者到最后生成的SQL语句都就变成了 is not null
但是在Hibernate3.x里Hql就不支持 not is null 的写法了,这是公司系统从Hibernate2.x升级到Hibernate3.x时候发现的,所以在Hibernate2.x里还是不要写成 not is null,
免得升到Hibernate3的时候麻烦
职称、软考、职称资格及三者之间的关系
软考的指导性政策。由于这个政策的解释不是很清楚,各地在执行政策时的做法也不统一,导致有关软考、职称、职称资格之间的关系问题,一直困扰着所有考生。在此,笔者做一个统一的解释。
软考
软考是全国计算机技术与软件专业技术资格(水平)考试的简称,是由国家人事部和信息产业部组织和领导的国家级考试,目的是科学、公正地对全国计算机与软件专业技术人员进行专业技术资格、职业资格认定和专业技术水平测试。
《计算机技术与软件专业技术资格(水平)考试暂行规定》第十条规定:“通过考试并获得相应级别计算机专业技术资格(水平)证书的人员,表明其已具备从事相应专业岗位工作的水平和能力,用人单位可根据《工程技术人员职务试行条例》有关规定和工作需要,从获得计算机专业技术资格(水平)证书的人员中择优聘任相应专业技术职务。取得初级资格可聘任技术员或助理工程师职务;取得中级资格可聘任工程师职务;取得高级资格,可聘任高级工程师职务”。
职称
具备某个职称资格的人,如果得到了单位的聘用,则其就拥有了相应的职称。例如,某个考生通过了程序员级别的考试(具备了初级职称资格),然后在用人单位根据某些条件聘任了专业技术职务,则其就拥有了技术员或助理工程师职称。例如,根据有关规定(各地不完全一样),对于大学本科以上毕业有初级资格者,可以聘任助理工程师;对于本科以下文化程度的初级资格者,则聘任技术员职称。
职称资格
职称资格是一种“资历”,根据我国的职称体系,可分为初级、中级和高级三类职称资格,其中高级职称资格又可分为副高和正高(高级工程师职称资格属于副高级)。某人具备某个职称资格,是指他已具备从事相应专业岗位工作的水平和能力。对于具备职称资格的人,由国家人事部门(省/市人事部门)颁发资格证书。
对于参加软考的人而言,如果通过了初级(例如程序员、网络管理员、信息处理技术员等)的考试,则其就具备了初级职称资格;如果通过了中级(例如软件设计师、网络工程师、数据库系统工程师、信息系统监理师、软件评测师等)的考试,则其就具备了中级职称资格;如果通过了高级(例如系统分析师、信息系统项目管理师等),则其就具备了副高级职称资格。
三者关联关系
也就是说,通过了软考,我们所获得的只是一种资格,是否聘任相应的职称,完全取决于各单位的实际情况,国家有关部门并没有直接的规定。事实上,通过评审方法(也就是常说的“评职称”)得到的也只是一个资格,单位既可以不聘用,也可以聘用,还可以高资格低聘用、低资格高聘用。例如,湖南大学的一个教授(既有职称资格,也聘任了相应的职称),如果他到清华大学,则只能聘为副教授或者讲师。相反,如果清华大学的一个副教授调到湖南大学,则会被聘任为教授。
就目前来看,对于通过软考的人,有些地方、有些单位是聘任相应职称的,而另外一些地方和单位却不聘任。因此,如果我们通过了软考,而当前所在单位又不聘任,可以采取下列两种方法:
(1)与单位领导据理力争。看本单位通过评审方法获得职称资格的人是否都得到了聘任,如果都得到了聘任,单位就应该聘任你。因为别人有职称资格,就能得到聘任,你也有职称资格,为什么得不到聘任呢?除非你是单位中水平和能力最差的人。
昨天老项目的客户来电话说系统有问题了,过去一看是数据库起不来了,看来一下日志。
Errors in file /opt/ora9/admin/xwoa/udump/xwoa_ora_17933.trc:
ORA-01115: 从文件 16 读取块时出现 IO 错误 (块 # 189544)
ORA-27072: skgfdisp: I/O 错误
Linux Error: 25: Inappropriate ioctl for device
Additional information: 189543
靠,数据表空间文件被删除。
好在数据库采用了归档模式。
开始恢复:
1、SQL>shutdown immediate
备份全部数据文件和控制文件和log以防恢复过程中出现问题。
2、SQL> startup mount;
3、SQL> alter database create datafile '/opt/ora9/oradata/xwoa/CHAOYANGJP.dbf' as '/opt/ora9/oradata/xwoa/CHAOYANGJP.dbf' reuse;
创建误删除的数据文件。
4、recover datafile '/opt/ora9/oradata/xwoa/CHAOYANGJP.dbf';
ORA-00279: 更改 57900012 (在 09/19/2007 10:27:05 生成) 对于线程 1 是必需的
ORA-00289: 建议: /opt/ora9/oradata/xwoa/archive/1_863.dbf
ORA-00280: 更改 57900012 对于线程 1 是按序列 # 863 进行的
指定日志: {<RET>=suggested | filename | AUTO | CANCEL}
输入:AUTO回车
开始恢复过程,这个时间比较长。
5、SQL> select d.file#,d.name,d.status,h.status from v$datafile d,v$datafile_header
h where d.file#=h.file# ;
查看一下数据文件是否online。
如果没有online,
6、SQL> ALTER DATABASE DATAFILE '/opt/ora9/oradata/xwoa/CHAOYANGJP.dbf' ONLINE;
ok!完成恢复过程。
用一个叫做虚拟软盘的软件,而且是免费的,当前版本1.5,装上软件,然后选择NTFS的启动img文件,然后重启机器,然后进入DOS,结果出现的是CIA Commander界面,逐级进入C:\windows\system32,然后找到vrv开头的所有的文件,一律F8(删除),但VRVEDP_M.EXE文件不能删除(是不是那个软件公司做的保护措施之一呢?),没关系,我重启试试。
重启进入XP,然后使用奇虎的360安全卫士找到VRVWatchServer服务,暂停,然后进入Windows的服务里面让它disable(我的是英文操作系统,我忘了中文的下面叫什么了),重启,发现这个VRVWatchServer服务已经没有启动了,然后进入cmd,执行命令:
sc delete VRVWatchServer
删除这个 VRVWatchServer服务。
重启,发现噩梦终于结束了,然后我在XP下很容易的就删除了VRVEDP_M.EXE文件,为了干掉这个软件,浪费了我4个小时,MMD!!!
另外记得要将虚拟软盘启动取消!
package net.risesoft.microstrategy;
import com.microstrategy.web.objects.WebDisplayUnit;
import com.microstrategy.web.objects.WebObjectInfo;
/**
* MSTR报表
*
* @author gf
*
*/
public class MstrReportViewMode {
public void getReportViewMode(WebDisplayUnit wdu) {
if (wdu instanceof WebObjectInfo) {
WebObjectInfo wi = (WebObjectInfo) wdu;
if (wi.getSubType() == 768) {
// 表
System.out.println("表:reportViewMode=1");
} else if (wi.getSubType() == 769) {
// 图
System.out.println("图:reportViewMode=2");
} else {
// 图表774
System.out.println("图表:reportViewMode=3");
}
}
}
}
摘要: EL(Expression Language,以下译为表达式语言)表达式语言主要有以下几大好处:
避免(MyType) request.getAttribute()和myBean.getMyProperty()之类的语句,使页面更简洁;
支持运算符(如+-*/),比普通的标志具有更高的自由度和更强的功能;
简单明了地表达代码逻辑,使用代码更可读与便于维护。
Struts 2中的表达式语言
Struts 2支持以下几种表达式语言:
OGNL(Object-Graph Navigation Language),可以方便地操作对象属性的开源表达式语言;
JSTL(JSP Standard Tag Library),JSP 2.0集成的标准的表达式语言;
Groovy,基于Java平台的动态语言,它具有时下比较流行的动态语言(如Python、Ruby和Smarttalk等)的一些起特性;
Velocity,严格来说不是表达式语言,它是一种基于Java的模板匹配引擎,具说其性能要比JSP好。
阅读全文
摘要: 1.List转换成为数组。(这里的List是实体是ArrayList)
调用ArrayList的toArray方法。
toArray
public <T> T[] toArray(T[] a)返回一个按照正确的顺序包含此列表中所有元素的数组;返回数组的运行时类型就是指定数组的运行时类型。假如列表能放入指定的数组,则返回放入此列表元素的数组。否则,将根据指定数组的运行时类型和此...
阅读全文
<style type="text/css">
img{vertical-align:bottom}
</style>
加入 <head>与 </head>就可以了。
摘要: 本文主要介绍使用kettle 来建立一个Type 2的Slowly Changing Dimension 以及其中一些细节问题
阅读全文
最近在写一个荧光图像分析软件,需要自己拟合方程。一元回归线公式的算法参考了《Java数值方法》,拟合度R^2(绝对系数)是自己写的,欢迎讨论。计算结果和Excel完全一致。
总共三个文件:
DataPoint.java
/**
* A data point for interpolation and regression.
*/
public class DataPoint
{
/** the x value */ public float x;
/** the y value */ public float y;
/**
* Constructor.
* @param x the x value
* @param y the y value
*/
public DataPoint(float x, float y)
{
this.x = x;
this.y = y;
}
}
/**
* A least-squares regression line function.
*/
import java.util.*;
import java.math.BigDecimal;
public class RegressionLine
//implements Evaluatable
{
/** sum of x */ private double sumX;
/** sum of y */ private double sumY;
/** sum of x*x */ private double sumXX;
/** sum of x*y */ private double sumXY;
/** sum of y*y */ private double sumYY;
/** sum of yi-y */ private double sumDeltaY;
/** sum of sumDeltaY^2 */ private double sumDeltaY2;
/**误差 */
private double sse;
private double sst;
private double E;
private String[] xy ;
private ArrayList listX ;
private ArrayList listY ;
private int XMin,XMax,YMin,YMax;
/** line coefficient a0 */ private float a0;
/** line coefficient a1 */ private float a1;
/** number of data points */ private int pn ;
/** true if coefficients valid */ private boolean coefsValid;
/**
* Constructor.
*/
public RegressionLine() {
XMax = 0;
YMax = 0;
pn = 0;
xy =new String[2];
listX = new ArrayList();
listY = new ArrayList();
}
/**
* Constructor.
* @param data the array of data points
*/
public RegressionLine(DataPoint data[])
{
pn = 0;
xy =new String[2];
listX = new ArrayList();
listY = new ArrayList();
for (int i = 0; i < data.length; ++i) {
addDataPoint(data[i]);
}
}
/**
* Return the current number of data points.
* @return the count
*/
public int getDataPointCount() { return pn; }
/**
* Return the coefficient a0.
* @return the value of a0
*/
public float getA0()
{
validateCoefficients();
return a0;
}
/**
* Return the coefficient a1.
* @return the value of a1
*/
public float getA1()
{
validateCoefficients();
return a1;
}
/**
* Return the sum of the x values.
* @return the sum
*/
public double getSumX() { return sumX; }
/**
* Return the sum of the y values.
* @return the sum
*/
public double getSumY() { return sumY; }
/**
* Return the sum of the x*x values.
* @return the sum
*/
public double getSumXX() { return sumXX; }
/**
* Return the sum of the x*y values.
* @return the sum
*/
public double getSumXY() { return sumXY; }
public double getSumYY() { return sumYY; }
public int getXMin() {
return XMin;
}
public int getXMax() {
return XMax;
}
public int getYMin() {
return YMin;
}
public int getYMax() {
return YMax;
}
/**
* Add a new data point: Update the sums.
* @param dataPoint the new data point
*/
public void addDataPoint(DataPoint dataPoint)
{
sumX += dataPoint.x;
sumY += dataPoint.y;
sumXX += dataPoint.x*dataPoint.x;
sumXY += dataPoint.x*dataPoint.y;
sumYY += dataPoint.y*dataPoint.y;
if(dataPoint.x > XMax){
XMax = (int)dataPoint.x;
}
if(dataPoint.y > YMax){
YMax = (int)dataPoint.y;
}
//把每个点的具体坐标存入ArrayList中,备用
xy[0] = (int)dataPoint.x+ "";
xy[1] = (int)dataPoint.y+ "";
if(dataPoint.x!=0 && dataPoint.y != 0){
System.out.print(xy[0]+",");
System.out.println(xy[1]);
try{
//System.out.println("n:"+n);
listX.add(pn,xy[0]);
listY.add(pn,xy[1]);
}
catch(Exception e){
e.printStackTrace();
}
/*
System.out.println("N:" + n);
System.out.println("ArrayList listX:"+ listX.get(n));
System.out.println("ArrayList listY:"+ listY.get(n));
*/
}
++pn;
coefsValid = false;
}
/**
* Return the value of the regression line function at x.
* (Implementation of Evaluatable.)
* @param x the value of x
* @return the value of the function at x
*/
public float at(int x)
{
if (pn < 2) return Float.NaN;
validateCoefficients();
return a0 + a1*x;
}
public float at(float x)
{
if (pn < 2) return Float.NaN;
validateCoefficients();
return a0 + a1*x;
}
/**
* Reset.
*/
public void reset()
{
pn = 0;
sumX = sumY = sumXX = sumXY = 0;
coefsValid = false;
}
/**
* Validate the coefficients.
* 计算方程系数 y=ax+b 中的a
*/
private void validateCoefficients()
{
if (coefsValid) return;
if (pn >= 2) {
float xBar = (float) sumX/pn;
float yBar = (float) sumY/pn;
a1 = (float) ((pn*sumXY - sumX*sumY)
/(pn*sumXX - sumX*sumX));
a0 = (float) (yBar - a1*xBar);
}
else {
a0 = a1 = Float.NaN;
}
coefsValid = true;
}
/**
* 返回误差
*/
public double getR(){
//遍历这个list并计算分母
for(int i = 0; i < pn -1; i++) {
float Yi= (float)Integer.parseInt(listY.get(i).toString());
float Y = at(Integer.parseInt(listX.get(i).toString()));
float deltaY = Yi - Y;
float deltaY2 = deltaY*deltaY;
/*
System.out.println("Yi:" + Yi);
System.out.println("Y:" + Y);
System.out.println("deltaY:" + deltaY);
System.out.println("deltaY2:" + deltaY2);
*/
sumDeltaY2 += deltaY2;
//System.out.println("sumDeltaY2:" + sumDeltaY2);
}
sst = sumYY - (sumY*sumY)/pn;
//System.out.println("sst:" + sst);
E =1- sumDeltaY2/sst;
return round(E,4) ;
}
//用于实现精确的四舍五入
public double round(double v,int scale){
if(scale<0){
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
public float round(float v,int scale){
if(scale<0){
throw new IllegalArgumentException(
"The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).floatValue();
}
}
演示程序:
LinearRegression.java
/**
* <p><b>Linear Regression</b>
* <br>
* Demonstrate linear regression by constructing the regression line for a set
* of data points.
*
* <p>require DataPoint.java,RegressionLine.java
*
* <p>为了计算对于给定数据点的最小方差回线,需要计算SumX,SumY,SumXX,SumXY; (注:SumXX = Sum (X^2))
* <p><b>回归直线方程如下: f(x)=a1x+a0 </b>
* <p><b>斜率和截距的计算公式如下:</b>
* <br>n: 数据点个数
* <p>a1=(n(SumXY)-SumX*SumY)/(n*SumXX-(SumX)^2)
* <br>a0=(SumY - SumY * a1)/n
* <br>(也可表达为a0=averageY-a1*averageX)
*
* <p><b>画线的原理:两点成一直线,只要能确定两个点即可</b><br>
* 第一点:(0,a0) 再随意取一个x1值代入方程,取得y1,连结(0,a0)和(x1,y1)两点即可。
* 为了让线穿过整个图,x1可以取横坐标的最大值Xmax,即两点为(0,a0),(Xmax,Y)。如果y=a1*Xmax+a0,y大于
* 纵坐标最大值Ymax,则不用这个点。改用y取最大值Ymax,算得此时x的值,使用(X,Ymax), 即两点为(0,a0),(X,Ymax)
*
* <p><b>拟合度计算:(即Excel中的R^2)</b>
* <p> *R2 = 1 - E
* <p>误差E的计算:E = SSE/SST
* <p>SSE=sum((Yi-Y)^2) SST=sumYY - (sumY*sumY)/n;
* <p>
*/
public class LinearRegression
{
private static final int MAX_POINTS = 10;
private double E;
/**
* Main program.
*
* @param args
* the array of runtime arguments
*/
public static void main(String args[])
{
RegressionLine line = new RegressionLine();
line.addDataPoint(new DataPoint(20, 136));
line.addDataPoint(new DataPoint(40, 143));
line.addDataPoint(new DataPoint(60, 152));
line.addDataPoint(new DataPoint(80, 162));
line.addDataPoint(new DataPoint(100, 167));
printSums(line);
printLine(line);
}
/**
* Print the computed sums.
*
* @param line
* the regression line
*/
private static void printSums(RegressionLine line)
{
System.out.println("\n数据点个数 n = " + line.getDataPointCount());
System.out.println("\nSum x = " + line.getSumX());
System.out.println("Sum y = " + line.getSumY());
System.out.println("Sum xx = " + line.getSumXX());
System.out.println("Sum xy = " + line.getSumXY());
System.out.println("Sum yy = " + line.getSumYY());
}
/**
* Print the regression line function.
*
* @param line
* the regression line
*/
private static void printLine(RegressionLine line)
{
System.out.println("\n回归线公式: y = " +
line.getA1() +
"x + " + line.getA0());
System.out.println("拟合度: R^2 = " + line.getR());
}
}
MicroStrategy Web 的设计模式
MicroStrategy Web 应用的实现方式是使用类似Struts 的MVC 模式,整个应用的核心是一个servlet 作为MVC 中的Controller 。所有的请求都发送给这个servlet,按照默认的web.xml 中的定义,这个servlet 名称是 mstrWeb, mstrWeb 只接受两种参数evt=或者页面参数 pg=通过在客户自己的应用中,通过URL 就可以访问已经订制的报表、文档或者某个特定页面(例如 共享报表页面)
MicroStrategy Web 的URL 参数说明
h ttp:/ /210.82.33.248/MicroStrategy/servlet/mstrWeb?evt=4001&src=mstrWeb.4001&reportViewMode=1&reportID=D08450DF4E71E2068B9AE78845C1BA28&Port=0&Project=MicroStrategy+Tutorial&Server=FBI&Uid=xxx&Pwd=xxx
在MicroStrategy Web 中要执行一个报表、或者访问一个已知的页面的时候,需要必要的认证信息,从request 或者session 中需要提供的五个最基本认证参数如下:
Server ---- Intelligence Server 的HostName 或者IP 地址
Port –--- Interlligence Server 的监听端口,0 表示默认的端口
Project –--- 项目名称
Uid –--- MSTR 用户名
Pwd –--- MSTR 口令
在具体的应用集成中,往往要求业务用户在登陆业务系统后,在调用MicroStrategy 的时候,不需要再次输入用户信息,即单点认证。在具体实现中,MicroStrategy Web 将上面的五个认证基本参数,封装在一个内部对象中WebIServerSession。每个WebIServerSession 的实例代表一个用户的连接,其中WebIServerSession 代表在Intelligence Server 中的一个用户会话,这个会话通过sessionID进行唯一的标识。
MicroStrategy Web 对SSO 的支持—ExternalSecurity
MicroStrategy Web 提供ExternakSecurity 机制,来支持SSO。External Security 提供如下的接口,这些接口实现以后的客户自定义的ExternalSecurity 通过web.xml 的配置加入到现有的Web 应用中,供MicroStratey Web使用。在用户首次访问MicroStratey Web 的时候,或者是签退后重新访问的时候, Web应用会调用实现中的handlesAuthenticationRequest 的方法,其返回值一共三种,根据返回的参数的不同,Web 应用会调用ExternalSecurity 实现中的不同函数,参见下:
通用的实现ExternalSecurity 步骤流程
1、继承 AbstractExternalSecurity 父类,实现代码public class WithSUN_ExternalSecurity extends AbstractExternalSecurity { ... ...
2、编译的类,打成jar 放在 web-inf/lib 下,或者按照包路径放在 web-inf/classes 下
3、修改 web.xml 中的 mstrWeb Servlet 配置中的 ExternalSecurityClass 参数,注释原有的参数,使用用户定义的类,例如
<param-name>externalSecurityClass</param-name>
<!--param-value>com.microstrategy.web.app.DefaultExternalSecurity</param-value-->
<param-value>com.fbi.WithSUN_ExternalSecurity</param-value>
</init-param>
在不同的项目中,根据集成模式的不同,会有不同的集成模式。解决这个问题的核心是如何根据传入的TOKEN 或者信息获取对应的MSTR 的登录信息。
下面就几种不同的模式进行介绍:
Web Universal 作为单独的Web 应用使用
使用MicroStrategy 的登陆界面,(或者自定义的Login 页面)进行登陆。这个时候用户输入的是外部用户的业务ID,以及Password。
[解决方式] 通过定义ExternalSecurity 类,实现用户输入的用户信息和MicroStrategy 之间用户的转换。通常是基于数据库用户信息表,通过输入的信息映射到MSTR 的用户信息。使用到了数据库表来存储用户名和密码。
[需要附加的包]
SQL Server 的 JDBC 包: jtds-0.9.jar
Oracle 的JDBC 包:
模式二 用户有自己的Web 应用,需要调用MicroStrategy Web 的应用
这种模式下不会提供MSTR 的任何登录的页面,但是要求从发起调用的Web 应用,在访问MicroStrategy,需要通过Session 或者 Request 传递某个参数给MicroStrategy。
简化的实现是:在Web 应用调用handlesAuthenticationRequest 的时候返回常数COLLECT_SESSION_NOW;
然后Web 应用会调用getWebIServerSession 方法,在getWebIServerSession 中系统会传递两个参数,RequestKeys 和 ContainerServices,其中前者是对httpRequest 的封装,后者则是对httpSession 的封装。需要在这个方法中返回一个WebIServerSession 的实例。
接下来,Web应用会调用底层的API 创建相应的Session,并将session 交于SessionManager(内部对象)维护。
注意一个问题,传入的这个参数,可以考虑加密处理,可以将登录MSTR 的用户名和密码信息加密后直接传入。另外更可为的方式为传入某一个临时的Key利用 Key 从数据库,或者其他位置获得登陆MSTR 的用户名和密码。
[模式三] 与 SUN 的Identity Server 进行集成,SSO 软件进行集成,
客户化方式同模式二,特殊的是:
步骤1: 需要修改Web 的描述文件web.xml,将应用配置使用SUN SSO 认证,这样当未认证的用户访问Web Universal 的时候,会进入SUN SSO 的登录界面。
样例,具体项目需要参考SUN 的文档
- <filter>
<filter-name>Agent1</filter-name>
<filter-class>com.sun.amagent.as.filter.AgentFilter</filter-class>
</filter>
- <filter>
<filter-name>Agent2</filter-name>
<filter-class>com.sun.amagent.as.filter.AgentFilter</filter-class>
</filter>
- <filter-mapping>
<filter-name>Agent1</filter-name>
<url-pattern>/servlet/*</url-pattern>
</filter-mapping>
- <filter-mapping>
<filter-name>Agent2</filter-name>
<url-pattern>/userinfo/*</url-pattern>
</filter-mapping>
步骤2:当用户输入统一的用户身份信息后,认证成功后,进入MicroStrategy 的界面,Web Universal 会调用External Security 模块。
在handleAuthentication 或者 getWebIServerSession 中获得SUN Identity 的Token,调用SUN SSO Client 的API,需要import SUN 的包:
- import com.iplanet.sso.*;
- import com.iplanet.am.sdk.*;
- import com.sun.identity.authentication.*;
-
-
- SSOTokenManager manager = SSOTokenManager.getInstance();
- SSOToken ssoToken = manager.createSSOToken(reqKeys);
- if(manager.isValidToken(ssoToken))
- {
- AMStoreConnection dpsc = new AMStoreConnection(ssoToken);
- AMUser dpUser = dpsc.getUser(ssoToken.getPrincipal().getName());
- uid = dpUser.getStringAttribute("uid");
- cn = dpUser.getStringAttribute("cn");
- employeeNo = dpUser.getStringAttribute("employeenumber");
- alias = dpUser.getStringAttribute("iplanet-am-user-alias-list");
- mail = dpUser.getStringAttribute("mail");
- entrydn = dpUser.getStringAttribute("entrydn");
- }
通过认证源的返回的信息,如果正常,那么需要根据其他的信息获得MSTR 的登录信息。
否则,需要参见下面 [关于认证失败的处理]
[模式四] 与第三方开发的认证进行集成的样例:
样例1: 用户自己开发了portal,实现集中的认证源,那么需要在External Sercurity 类的handelAuthentication 或者getWebIServerSession 中利用下面的代码:
- URL url=new URL("http://portal.xbxs.petrochina:38081/ssoauth/SSOValidate?tokenID=" + tokenId);
- URLConnection con=url.openConnection();
- con.setUseCaches(false);
- java.io.BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));
- String line= br.readLine();
- while(null != line){
- if (line.indexOf("SUCCESS")>-1) break;
通过认证源的返回的信息,如果正常,那么需要根据其他的信息获得MSTR 的登录信息。
否则,需要参见下面 [关于认证失败的处理]。
[关于认证失败的处理]
第一种方法是:在 getwebiserversession 中,用提供的credential 创建session,如果创建session 不成功,getwebiserversession 会自动return null。这时getfailureurl 方法会被call with reason =1。可以在getfailureurl 方法中指定reason=1 时出现的错误信息页面的url
第二个方法是:
1. 在handlehandlesAuthenticationRequest 中用非建立session 的方法验证credential
2. 如果验证ok,就call getWebIServerSession,create mstr session
3. 如果验证不ok,就call getCustomLoginURL, 然后在getCustomLoginURL 方法中,指定error page 的url
这两个方法那个更好,取决于如何验证mstr credential。如果credential 只能通过session是不是null 来验证,就用方法1。如果是通过其他方法验证credential,就用方法2。
RSS的历史
那么RSS究竟代表什么呢?比较普遍的有两种说法,一种是“Rich Site Summary”或“RDF Site Summary”,另一种是“Really Simple Syndication”,之所以有这些分歧,需要从RSS发展的历史说起。
最初的0.90版本RSS是由Netscape公司设计的,目的是用来建立一个整合了各主要新闻站点内容的门户,但是0.90版本的RSS规范过于复杂,而一个简化的RSS 0.91版本也随着Netscape公司对该项目的放弃而于2000年暂停。
不久,一家专门从事博客写作软件开发的公司UserLand接手了RSS 0.91版本的发展,并把它作为其博客写作软件的基础功能之一继续开发,逐步推出了0.92、0.93和0.94版本。随着网络博客的流行,RSS作为一种基本的功能也被越来越多的网站和博客软件支持。
在UserLand公司接手并不断开发RSS的同时,很多的专业人士认识到需要通过一个第三方、非商业的组织,把RSS发展成为一个通用的规范,并进一步标准化。于是2001年一个联合小组在0.90版本RSS的开发原则下,以W3C新一代的语义网技术RDF(Resource Description Framework)为基础,对RSS进行了重新定义,发布RSS1.0,并将RSS定义为“RDF Site Summary”。但是这项工作没有与UserLand公司进行有效的沟通,UserLand公司也不承认RSS 1.0的有效性,并坚持按照自己的设想进一步开发出RSS的后续版本,到2002年9月发布了最新版本RSS 2.0,UserLand公司将RSS定义为“Really Simple Syndication”。
目前RSS已经分化为RSS 0.9x/2.0和RSS 1.0两个阵营,由于分歧的存在和RSS 0.9x/2.0的广泛应用现状,RSS 1.0还没有成为标准化组织的真正标准。
什么是RSS?
RSS是一种网页内容联合格式(web content sydication format)。
它的名字是Really Simple Syndication的缩写。
RSS是XML的一种。所有的RSS文档都遵循XML 1.0规范,该规范发布在W3C网站上。
在一个RSS文档的开头是一个<rss>节点和一个规定的属性version,该属性规定了该文档将以RSS的哪个版本表示。如果该文档以这个规范来表示,那么它的version属性就必须等于2.0。
在<rss>节点的下一级是一个独立的<channel>节点,该节点包含关于channel的信息和内容。
关于本文档
该文档是在2002年秋天撰写的,当时的RSS版本为2.0.1。
它包含从RSS 0.91规范(2000年)开始的所有的修改和添加,以及包含在RSS 0.92(2000年12月)和RSS 0.94(2002年8月)中的新的特性。
必需的频道节点
下面有一份必须包含的频道(channel)节点的列表,每一个都有一个简单的描述、一个例子、应该出现的位置和更详细描述的超链接。
元素 |
描述 |
范例 |
title |
频道(channel)名称。它可以告诉别人如何访问你的服务。如果你有一个与你的RSS文件内容一致的HTML网站,你的title元素值应该与你的网站的标题相同。 |
GoUpstate.com News Headings |
link |
响应该频道的网站的URL |
http://www.goupstate.com/ |
description |
关于该频道的描述 |
The latest news from GoUpstate.com, a Spartanburg Herald-Joural Web Site |
可选的频道元素
下面是可选的频道元素列表
节点 |
描述 |
范例 |
language |
使用的语言。这允许聚合器对所有的意大利语站点分组。 |
en-us |
copyright |
版权声明 |
Copyright 2002, Spartanburg Herald-Journal |
managingEditor |
内容负责人的Email |
geo@herald.com (George Matesky) |
webMaster |
技术人员的Email |
betty@herald.com (Betty Guernsey) |
pubDate |
内容的发布时间 |
Sat, 07 Sep 2002 00:00:01 GMT |
lastBuildDate |
最后更新时间 |
Sat, 07 Sep 2002 09:42:31 GMT |
category |
指定该频道所属的一个或多个分类。遵循与item级category元素相同的规则。 |
<category>Newspapers</category> |
generator |
生成该频道的程序名称 |
MightyInHouse Content System v2.3 |
docs |
指向rss格式文档的url地址? |
http://blogs.law.harvard.edu/tech/rss |
cloud |
允许所有进程注册一个cloud用于获得频道的更新通知,并为rss种子实现一个轻量级的发布订阅协议。 |
<cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure="pingMe" protocol="soap"/> |
ttl |
ttl是Time to live的缩写。它指示cache的有效保存时间。 |
<ttl>60</ttl> |
image |
与频道一起显示的图片地址 |
|
rating |
该频道的统计图片地址 |
|
textInput |
指定一个textbox与该频道一起显示 |
|
skipHours |
告诉使用者哪些时段是可以忽略的 |
|
skipDays |
告诉使用着哪些天是可以忽略的 |
|
<channel>子节点<image>
<image>是一个可选的<channel>子节点,该节点包含三个必需的子元素和三个可选的子元素。
<url>是GIF、JPEG或PNG图像文件的URL地址,该图像代表整个频道
<title>用于描述上面的图像,等同于HTML语言中的<img>的alt属性
<link>是要连接的站点的url,当显示频道时,图像的连接指向该站点。
<title>和<link>应该与频道的<title>和<link>有相同的值
可选的节点包括<width>和<height>,它们是数字类型,指定图像的宽度和高度,单位为像素
<description>就是link的TITLE属性中文本,它将在调用网页时显示出来。
图像宽度的最大值为144,默认值为88
图像高度的最大值为400,默认值为31
<channel>子节点<cloud>
<cloud>是一个可选的<channel>子节点。
它指定一个可以支持rssCloud接口的web服务,rssCloud接口可以用HTTP-POST,XML-RPC或SOAP1.1实现。
它的目的是允许通知注册为cloud的进程频道被更新,从而实现一个轻量级的发布订阅协议。
<cloud domain="rpc.sys.com" port="80" path="/RPC2" registerProcedure= "myCloud.rssPleaseNotify" protocol="xml-rpc" />
在这个例子中,为了请求频道通知,你需要发送一个XML-RPC消息到rpc.sys.com的80端口,路径为/RPC2。调用的过程为myCloud.rssPleaseNotify。
<channel>子节点<ttl>
<ttl>是一个可选的<channel>子节点。
ttl是time to live的缩写。它表示频道在被刷新前应该被缓存的时间。这使得rss源可以被一个支持文件共享的网络所管理,例如Gnutella
例如:<ttl>60</ttl>
<channel>子节点<textInput>
<textInput>是<channel>的可选的子节点,<textInput>包含四个子节点。
<title>--提交按钮的标签
<description>--该文本输入区的描述
<name>--文本输入区的名称
<link>--处理文本输入的CGI脚本的URL
使用<textInput>的目的有些神秘(?)。你可以用它提供一个搜索引擎输入框,或让读者提供反馈信息。许多聚合器忽略该节点。
<item>的节点
一个频道可以包含许多项目(item)节点。一个项目可以代表一个故事——比如说一份报纸或杂志上的故事,如果是这样的话,那么项目的描述则是故事的概要,项目的链接则指向整个故事的存放位置。项目的所有节点都是可选的,但是至少要包含至少一个标题(title)和描述(description)。
节点 |
描述 |
范例 |
title |
item的标题 |
Venice Film Festival Tries to Quit Sinking |
link |
item的URL |
http://www.nytimes.com/2002/09/07/movies/07FEST.html |
description |
item概要 |
Some of the most heated chatter at the Venice Film Festival this week was about the way that the arrival of the stars at the Palazzo del Cinema was being staged. |
author |
作者的email地址 |
oprah@oxygen.net |
category |
item可以包含在一个或多个分类中 |
Simpsons Characters |
comments |
与item相关的评论的地址 |
http://www.myblog.org/cgi-local/mt/mt-comments.cgi?entry_id=290 |
enclosure |
附加的媒体对象 |
|
guid |
可以唯一确定item的字符串 |
http://inessential.com/2002/09/01.php#a2 |
pubDate |
item发布的时间 |
Sun, 19 May 2002 15:21:36 GMT |
source |
rss频道来源 |
Quotes of the Day |
<item>子节点<source>
<source>是<item>的可选节点。
它的值是item来自的rss频道的名称,从item的title衍生而来。它有一个必须包含的属性url, 该属性链接到XML序列化源。
<source url="http://static.userland.com/tomalak/links2.xml">Tomalak's Realm</source>
该节点的作用是提高连接的声望,进一步推广新闻项目的源头。它可以用在聚合器的Post命令中。当从聚合器通过webblog访问一个item时,<source>能够自动被生成。
<item>子节点<enclosure>
<enclosure>是<item>的可选节点。
它有三个必要的属性。url属性指示enclosure的位置,length指出它的字节大小,type属性指出它的标准MIME类型
url必须为一个http url。
<enclosure url="http://www.scripting.com/mp3s/weatherReportSuite.mp3" length="12216320" type="audio/mpeg" />
<item>子节点<category>
<category>是<item>的可选节点。
它有一个可选属性或域,该属性是一个用来定义分类法的字符串。
该节点的值是一个正斜杠分割的字符串,它用来在指定的分类法中识别一个分级位置(hierarchic location)。处理器可以为分类的识别建立会话。(Processors may establish conventions for the interpretation of categories)下面有两个例子:
<category>Grateful Dead</category>
<category domain="http://www.fool.com/cusips">MSFT</category>
你可以根据你的需要为不同的域(domain)包含很多category节点,并且可以在相同域的不同部分拥有一个前后参照的item。
<item>子节点<pubDate>
<pubDate>是<item>的可选节点。
它的值是item发布的日期。如果它是一个没有到达的日期,聚合器在日期到达之前可以选择不显示该item。
<pubDate>Sun, 19 May 2002 15:21:36 GMT</pubDate>
<item>子节点<guid>
<guid>是<item>的可选节点。
guid是globally unique identifier的缩写。它是一个可以唯一识别item的字符串。当item发布之后,聚合器可以选择使用该字符串判断该item是否是新的。
<guid>http://some.server.com/weblogItem3207</guid>
guid没有特定的语法规则,聚合器必须将他们看作一个字符串。生成具有唯一性的字符串guid取决于种子的源头。
如果guid节点有isPermaLink属性,并且值为真,读取器就会认为它是item的permalink。permalink是一个可在web浏览器中打开的url链接,它指向<item>节点所描述的全部item。
<guid isPermaLink="true">http://inessential.com/2002/09/01.php#a2</guid>
isPermaLink是可选属性,默认值为真。如果值为假,guid将不会被认为是一个url或指向任何对象的url。
<item>子节点<comment>
<comment>是<item>的可选节点。
如果出现,它指向该item评论的url
<comments>http://rateyourmusic.com/yaccs/commentsn/blogId=705245&itemId=271</comments>
<item>子节点<author>
<author>是<item>的可选节点。
它是item的作者的email。对于通过rss传播的报纸和杂志,作者可能是写该item所描述的文章的人。对于聚集型webblogs,作者可能不是责任编辑或站长。对于个人维护的webblog,忽略<author>节点是有意义的。
<author>lawyer@boyer.net (Lawyer Boyer)</author>
查找一个sheet中的数据是用找个语句"SELECT * from [Sheet1$]",如果sheet的名字是aaa应该写成[aaa$]。 语句应该是这样"SELECT * from [aaa$]"。
摘要: 首先简单介绍一下weka,Weka是基于java,用于数据挖掘和知识分析一个平台。来自世界各地的java爱好者们都可以把自己的算法放在这个平台上,然后从海量数据中发掘其背后 ...
阅读全文
摘要: 对于许多软件开发者来说,一提到国际化(亦称为 i18n)支持就会感到害怕。 要使编写的代码能够面向外国使用者,确实需要费一翻思量,因为在现有软件的代码中添加国际化支持可不是一件轻而易举的事。
阅读全文
设置正确的Content-Type以解决Ext的中文乱码问题
1、前后台所有文件统一用utf-8编码方式。
2、在Request Headers中设置Content-Type:application/x-www-form-urlencoded; charset=utf-8,具体方法:将Ext.lib.Ajax.defaultPostHeader += '; charset=utf-8'加在Ext.onReady块里,此方法可以解决用EXT在POST时的中文乱码问题。
3、在Response Headers中设置Content-Type:text/json;charset=utf-8,在JAVA中实现方法:response.setContentType("text/json; charset=utf-8"),此方法可以解决用EXT在显示后台中文时的乱码问题。
今天用jfreechart做图,发现生产的柱状图无法显示数值,经过一段时间摸索终于解决,现给一demo如下:
test.jsp
<%@ page contentType="text/html; charset=GBK" %>
<%@ page import="org.jfree.chart.*"%>
<%@ page import="org.jfree.chart.axis.AxisLocation"%>
<%@ page import="org.jfree.chart.plot.*"%>
<%@ page import="org.jfree.data.*"%>
<%@ page import ="java.text.DecimalFormat"%>
<%@ page import ="java.text.NumberFormat"%>
<%@ page import ="java.awt.Color"%>
<%@ page import="java.awt.Font"%>
<%@ page import="org.jfree.chart.renderer.category.*"%>
<%@ page import="org.jfree.chart.axis.*"%>
<%@ page import="org.jfree.chart.title.TextTitle"%>
<%@ page import="org.jfree.chart.labels.*"%>
<%@page import="org.jfree.data.category.*"%>
<%@page import="org.jfree.chart.plot.PlotOrientation"%>
<%@page import="org.jfree.chart.servlet.ServletUtilities"%>
<%
CategoryDataset dcd= getDataset();
JFreeChart chart= ChartFactory.createStackedBarChart3D("各部门参加培训情况", "部门", "人数", dcd, PlotOrientation.VERTICAL, true, false, false);
// 图例字体清晰
//chart.setTextAntiAlias(false);
//chart.setBackgroundPaint(Color.WHITE);
// 2 .2 主标题对象 主标题对象是 TextTitle 类型
//chart .setTitle(new TextTitle("ok", new Font("隶书", Font.BOLD, 25)));
// 2 .2.1:设置中文 // x,y轴坐标字体
Font labelFont = new Font("SansSerif", Font.TRUETYPE_FONT, 12);
// 2 .3 Plot 对象 Plot 对象是图形的绘制结构对象
CategoryPlot plot = (CategoryPlot)chart.getPlot();
ValueAxis rangeAxis = plot.getRangeAxis();
//设置最高的一个 Item 与图片顶端的距离
rangeAxis.setUpperMargin(0.15);
//设置最低的一个 Item 与图片底端的距离
rangeAxis.setLowerMargin(0.15);
plot.setRangeAxis(rangeAxis);
StackedBarRenderer3D renderer=new StackedBarRenderer3D();
//renderer.setBaseOutlinePaint(Color.BLACK);
//设置 Wall 的颜色
//renderer.setWallPaint(Color.gray);
//设置每种柱的颜色
renderer.setSeriesPaint(0, new Color(153, 153, 255));
renderer.setSeriesPaint(1, new Color(204, 255, 255));
renderer.setSeriesPaint(2, Color.GREEN);
//显示每个柱的数值,并修改该数值的字体属性
renderer.setItemLabelGenerator(new StandardCategoryItemLabelGenerator());
renderer.setItemLabelFont(new Font("黑体",Font.PLAIN,9));
renderer.setItemLabelsVisible(true);
plot.setRenderer(renderer);
String filename=ServletUtilities.saveChartAsPNG(chart,500,300,null,session);
String url=request.getContextPath()+"/servletDisplayChart?filename="+filename;
%>
<p align="center">
<img src="<%=url%>" width="500" height="300" border="0" usemap="#map0">
</p>
<%!
private static CategoryDataset getDataset()
{
DefaultCategoryDataset defaultcategorydataset = new DefaultCategoryDataset();
defaultcategorydataset.addValue(10.399999999999999D, "培训人数", "中心机关");
defaultcategorydataset.addValue(10.800000000000001D, "未培训人数", "中心机关");
defaultcategorydataset.addValue(43.200000000000003D, "培训人数", "物探部");
defaultcategorydataset.addValue(15.6D, "未培训人数", "物探部");
defaultcategorydataset.addValue(23D, "培训人数", "遥感部");
defaultcategorydataset.addValue(11.300000000000001D, "未培训人数", "遥感部");
defaultcategorydataset.addValue(13D, "培训人数", "物业部");
defaultcategorydataset.addValue(11.800000000000001D, "未培训人数", "物业部");
defaultcategorydataset.addValue(15, "培训人数", "企业部");
defaultcategorydataset.addValue(12D, "未培训人数", "企业部");
return defaultcategorydataset;
}
%>
相信大多数人对iGoogle(Google 个性化首页)都不会陌生,除了可以定制iGoogle的内容之外,iGoogle的主题外 观都是可以定制的。之前Google官方只提供几个iGoogle主题,如今Google推出 了iGoogle主题API,任何人都可以轻松地创建个性化的主题。

然而,Google给出的API文档 是相当繁复的,非常考验英文与编程能力。这里介绍3种快速创建iGoogle主 题的方法,你可以不需要看那个复杂的文档,只需鼠标轻点即可完成个性化的主 题。
1、igThemer
igThemer是一个在线的主题编辑器,提供下图所示的各种参数的定制,包括 背景颜色、背景图片、选项卡颜色等等。

制作完成后点击create即可在线生成一个xml文件,文件会存储在igThemer网 站上,方便共享。你无需上传到自己的空间。
2、Haochi's igthemer
和igThemer几乎是一样的,目前似乎还在建设中。但依然可用。或许过一段 时间后会更完善。
3、iGoogle theme API bookmarklet
这个方法我最喜欢,与前两种相比,这个方法更实时。打 开iGoogle首页,把下面的代码粘贴到地址栏:
javascript:var s=document.createElement('script');s.id="igteid"; s.type="text/javascript";s.src= "http://igoogle-theme-editor.googlecode.com/svn/" +"trunk/iGoogleThemeEditor/dist/ige.js?lang=en&" +new Date().getTime(); document.body.appendChild(s);void(0); |
浏览器的左侧马上出现了下图所示的编辑栏。

在编辑栏里你可以轻松地定制各种参数。参数得改变会即时在浏览器里显现 。最后,点击create xml,生成iGoogle主题。和igThemer不同,你需要把这个 xml文件上传到自己的空间上方可使用。
如何使用iGoogle主题?
如果你的主题是自用的,把生成的xml文档上传到网上,然后在浏览器里输入 下面的地址即可使用:
http://www.google.com/ig?skin=主题地址
当然,你还可以将主题 提交到iGoogle目录,Google采纳后,每个人都可以直接使用。目前iGoogle目录里已经有将近50 个主题。
摘要: Sing Li (westmakaha@yahoo.com), 作家, Wrox Press
2007 年 1 月 08 日
现代软件项目不再是单个本地团队独立开发的产物。随着健壮的企业级开源组件的可用性日益提高,当今的软件项目需要项目团队间的动态协作,往往也需要混合使用在全球范围内创建和维护的组件。如今,Apache Maven 构建系统步入了第二代,它和由 Internet 带来的全球软件...
阅读全文
使用 AppFuse 的七个理由
学习 Java 开放源码工具 —— 并使用这些工具提高生产效率
 |
 |
 |
级别: 初级
Matt Raible (mraible@virtuas.com), 开放源码实践先驱, Virtuas Open Source Solutions
2006 年 8 月 31 日
开始学习在 Java™ 平台上使用诸如 Spring、Hibernate 或 MySQL 之类的开放源码工具时可能非常困难。再加上 Ant 或 Maven,以及与 DWR 一起的小 Ajax,还有 Web 框架 —— 即 JSF,我们必须睁大眼睛盯着如何配置应用程序。AppFuse 减少了集成开放源码项目的痛苦。它可以把测试变成一等公民,让我们可以从数据库表生成整个 UI,并使用 XFire 来支持 Web 服务。另外,AppFuse 的社区也非常健全,这是不同 Web 框架用户可以一起融洽相处的地方之一。
AppFuse 是一个开放源码的项目和应用程序,它使用了在 Java 平台上构建的开放源码工具来帮助我们快速而高效地开发 Web 应用程序。我最初开发它是为了减少在为客户构建新 Web 应用程序时所花费的那些不必要的时间。从核心上来说,AppFuse 是一个项目骨架,类似于通过向导创建新 Web 项目时 IDE 所创建的东西。当我们使用 AppFuse 创建一个项目时,它会提示我们将使用开放源码框架,然后才创建项目。它使用 Ant 来驱动测试、代码生成、编译和部署。它提供了目录和包结构,以及开发基于 Java 语言的 Web 应用程序所需要的库。
与大部分 “new project” 向导不同,AppFuse 创建的项目从最开始就包含很多类和文件。这些文件用来实现特性,不过它们同时也会在您开发应用程序时被用作示例。通过使用 AppFuse 启动新项目,我们通常可以减少一到两周的开发时间。我们不用担心如何将开放源码框架配置在一起,因为这都已经完成了。我们的项目都已提前配置来与数据库进行交互,它会部署到应用服务器上,并对用户进行认证。我们不必实现安全特性,因为这都早已集成了。
当我最初开发 AppFuse 时,它只支持 Struts 和 Hibernate。经过几年的努力,我发现了比 Struts 更好的 Web 框架,因此我还添加了为这些 Web 框架使用的选项。现在,AppFuse 可以支持 Hibernate 或 iBATIS 作为持久性框架。对于 Web 框架来说,我们可以使用 JavaServer Faces(JSF)、Spring MVC、Struts、Tapestry 或 WebWork。
AppFuse 提供了很多应用程序需要的一些特性,包括:
- 认证和授权
- 用户管理
- Remember Me(这会保存您的登录信息,这样就不用每次都再进行登录了)
- 密码提醒
- 登记和注册
- SSL 转换
- E-mail
- URL 重写
- 皮肤
- 页面修饰
- 模板化布局
- 文件上载
这种 “开箱即用” 的功能是 AppFuse 与其他 CRUD 代 框架的区别之一(CRUD 取自创建、检索、更新 和删除 几个操作的英文首字母),包括 Ruby on Rails、Trails 和 Grails。上面提到的这些框架,以及 AppFuse,都让我们可以从数据库表或现有的模型对象中生成主页/细节页。
图 1 阐述了一个典型 AppFuse 应用程序的概念设计:
图 1. 典型的 AppFuse 应用程序
清单 1 给出了我们在创建 devworks 项目时所使用的命令行交互操作,同时还给出了所生成的结果。这个项目使用了 WebWork 作为自己的 Web 框架(请参考下面 参考资料 一节给出的链接)。
清单 1. 使用 AppFuse 创建新项目
alotta:~/dev/appfuse mraible$ ant new
Buildfile: build.xml
clean:
[echo] Cleaning build and distribution directories
init:
new:
[echo]
[echo] +-------------------------------------------------------------+
[echo] | -- Welcome to the AppFuse New Application Wizard! -- |
[echo] | |
[echo] | To create a new application, please answer the following |
[echo] | questions. |
[echo] +-------------------------------------------------------------+
[input] What would you like to name your application [myapp]?
devworks
[input] What would you like to name your database [mydb]?
devworks
[input] What package name would you like to use [org.appfuse]?
com.ibm
[input] What web framework would you like to use [webwork,tapestry,spring,js
f,struts]?
webwork
[echo] Creating new application named 'devworks'...
[copy] Copying 359 files to /Users/mraible/Work/devworks
[copy] Copying 181 files to /Users/mraible/Work/devworks/extras
[copy] Copying 1 file to /Users/mraible/Work/devworks
[copy] Copying 1 file to /Users/mraible/Work/devworks
install:
[echo] Copying WebWork JARs to ../../lib
[copy] Copying 6 files to /Users/mraible/Work/devworks/lib
[echo] Adding WebWork entries to ../../lib.properties
[echo] Adding WebWork classpath entries
[echo] Removing Struts-specific JARs
[delete] Deleting directory /Users/mraible/Work/devworks/lib/struts-1.2.9
[delete] Deleting directory /Users/mraible/Work/devworks/lib/strutstest-2.1.3
[echo] Deleting struts_form.xdt for XDoclet
[delete] Deleting directory /Users/mraible/Work/devworks/metadata/templates
[echo] Deleting Struts merge-files in metadata/web
[delete] Deleting 7 files from /Users/mraible/Work/devworks/metadata/web
[echo] Deleting unused Tag Libraries and Utilities
[delete] Deleting 2 files from /Users/mraible/Work/devworks/src/web/org/appfu
se/webapp
[echo] Modifying appgen for WebWork
[copy] Copying 12 files to /Users/mraible/Work/devworks/extras/appgen
[echo] Replacing source and test files
[delete] Deleting directory /Users/mraible/Work/devworks/src/web/org/appfuse/
webapp/form
[delete] Deleting directory /Users/mraible/Work/devworks/src/web/org/appfuse/
webapp/action
[copy] Copying 13 files to /Users/mraible/Work/devworks/src
[delete] Deleting directory /Users/mraible/Work/devworks/test/web/org/appfuse
/webapp/form
[delete] Deleting directory /Users/mraible/Work/devworks/test/web/org/appfuse
/webapp/action
[copy] Copying 5 files to /Users/mraible/Work/devworks/test
[echo] Replacing web files (images, scripts, JSPs, etc.)
[delete] Deleting 1 files from /Users/mraible/Work/devworks/web/scripts
[copy] Copying 34 files to /Users/mraible/Work/devworks/web
[delete] Deleting: /Users/mraible/Work/devworks/web/WEB-INF/validator-rules-c
ustom.xml
[echo] Modifying Eclipse .classpath file
[echo] Refactoring build.xml
[echo] ----------------------------------------------
[echo] NOTE: It's recommended you delete extras/webwork as you shouldn't ne
ed it anymore.
[echo] ----------------------------------------------
[echo] Repackaging info written to rename.log
[echo]
[echo] +-------------------------------------------------------------+
[echo] | -- Application created successfully! -- |
[echo] | |
[echo] | Now you should be able to cd to your application and run: |
[echo] | > ant setup test-all |
[echo] +-------------------------------------------------------------+
BUILD SUCCESSFUL
Total time: 15 seconds
|
 |
为什么使用 WebWork?
Struts 社区最近在热情地拥抱 WebWork,这种结合导致为 Java 平台提供了一个非常优秀的新 Web 框架:Struts 2。当然,Spring MVC 是一个非常优秀的基于请求的框架,但是它不能像 Struts 2 一样支持 JSF。基于内容的框架(例如 JSF 和 Tapestry)也都很好,但是我发现 WebWork 更为直观,更容易使用(更多有关 Structs 2 和 JSF 的内容请参看 参考资料)。 |
|
在创建一个新项目之后,我们就得到了一个类似于图 2 所示的目录结构。Eclipse 和 Intellij IDEA 项目文件都是作为这个过程的一部分创建的。
图 2. 项目的目录结构
这个目录结构与 Sun 为 Java 2 Platform Enterprise Edition(J2EE)Web 应用程序推荐的目录结构非常类似。在 2.0 版本的 AppFuse 中,这个结构会变化成适合 Apache Maven 项目的标准目录结构(有关这两个目录介绍的内容,请参看 参考资料 中的链接)。AppFuse 还会从 Ant 迁移到 Maven 2 上,从而获得相关下载的能力和对生成 IDE 项目文件的支持。目前基于 Ant 的系统要求提交者维护项目文件,而 Maven 2 可以通过简单地使用项目的 pom.xml 文件生成 IDEA、Eclipse 和 NetBeans 项目文件。(这个文件位于您项目的根目录中,是使用 Maven 构建应用程序所需要的主要组件)。它与利用 Ant 所使用的 build.xml 文件非常类似。)
现在我们对 AppFuse 是什么已经有一点概念了,在本文剩下的部分中,我们将介绍使用 AppFuse 的 7 点理由。即使您选择不使用 AppFuse 来开始自己的项目,也会看到 AppFuse 可以为您提供很多样板代码,这些代码可以在基于 Java 语言的 Web 应用程序中使用。由于它是基于 Apache 许可证的,因此非常欢迎您在自己的应用程序中重用这些代码。
理由 1:测试
测试是在软件开发项目中很少被给予足够信任的一个环节。注意我并不是说在软件开发的一些刊物中没有得到足够的信任!很多文章和案例研究都给出了测试优先的开发方式和足够的测试覆盖面以提高软件的质量。然而,测试通常都被看作是一件只会延长项目开发时间的事情。实际上,如果我们使用测试优先的方法在编写代码之前就开始撰写测试用例,我相信我们可以发现这实际上会加速 开发速度。另外,测试优先也可以使维护和重用更加 容易。如果我们不编写代码来测试自己的代码,那么就需要手工对应用程序进行测试 —— 这通常效率都不高。自动化才是关键。
当我们首次开始使用 AppFuse 时,我们可能需要阅读这个项目 Web 站点上提供的快速入门指南和教程(请参看 参考资料 中的链接)。这些教程的编写就是为了您可以首先编写测试用例;它们直到编写接口和/或实现之后才能编译。如果您有些方面与我一样,就会在开始编写代码之前就已经编写好测试用例了;这是真正可以加速编写代码的惟一方式。如果您首先编写了代码的实现,通过某种方式验证它可以工作,那么您可能会对自己说,“哦,看起来不错 —— 谁需要测试呢?我还有更多的代码需要编写!”这种情况不幸的一面是您通常都会做一些事情 来测试自己的代码;您简单地跳过了可以自动化进行测试的地方。
AppFuse 的文档展示了如何测试应用程序的所有 层次。它从数据库层开始入手,使用了 DbUnit(请参看 参考资料)在运行测试之前提前使用数据来填充自己的数据库。在数据访问(DAO)层,它使用了 Spring 的 AbstractTransactionalDataSourceSpringContextTests 类(是的,这的确是一个类的名字!)来允许简单地加载 Spring 上下文文件。另外,这个类对每个 testXXX() 方法封装了一个事务,并当测试方法存在时进行回滚。这种特性使得测试 DAO 逻辑变得非常简单,并且不会对数据库中的数据造成影响。
在服务层,jMock (请参看 参考资料)用来编写那些可以消除 DAO 依赖的真正 单元测试。这允许进行验证业务逻辑正确的快速测试;我们不用担心底层的持久性逻辑。
 |
HtmlUnit 支持
HtmlUnit 团队在 1.8 发行版中已经完成了相当多的工作来确保包可以与流行的 Ajax 框架(Prototype 和 Scriptaculous)很好地工作。 |
|
在 Web 层,测试会验证操作(Struts/WebWork)、控件(Spring MVC)、页面(Tapestry)和管理 bean(JSF)如我们所期望的一样进行工作。Spring 的 spring-mock.jar 可以非常有用地用来测试所有这些框架,因为它包含了一个 Servlet API 的仿真实现。如果没有这个有用的库,那么测试 AppFuse 的 Web 框架就会变得非常困难。
UI 通常是开发 Web 应用程序过程中最为困难的一部分。它也是顾客最经常抱怨的地方 —— 这既是由于它并不是非常完美,也是由于它的工作方式与我们期望的并不一样。另外,没有什么会比在客户面前作演示的过程中看到看到异常堆栈更糟糕的了!您的应用程序可能会非常可怕,但是客户可能会要求您做到十分完美。永远不要让这种事情发生。Canoo WebTest 可以对 UI 进行测试。它使用了 HtmlUnit 来遍历测试 UI,验证所有的元素都存在,并可以填充表单的域,甚至可以验证一个假想的启用 Ajax 的 UI 与我们预期的工作方式一样。(有关 WebTest 和 HTMLUnit 的链接请参看 参考资料。)
为了进一步简化 Web 的测试,Cargo(请参看 参考资料)对 Tomcat 的启动和停止(分别在运行 WebTest 测试之前和之后)进行了自动化。
理由 2:集成
正如我在本文简介中提到的一样,很多开放源码库都已经预先集成到 AppFuse 中了。它们可以分为以下几类:
- 编译、报告和代码生成:Ant、Ant Contrib Tasks、Checkstyle、EMMA、Java2Html、PMD 和 Rename Packages
- 测试框架:DbUnit、Dumbster、jMock、JUnit 和 Canoo WebTest
- 数据库驱动程序:MySQL 和 PostgreSQL
- 持久性框架:Hibernate 和 iBATIS
- IoC 框架:Spring
- Web 框架:JSF、Spring MVC、Struts、Tapestry 和 WebWork
- Web 服务:XFire
- Web 工具:Clickstream、Display Tag、DWR、JSTL、SiteMesh、Struts Menu 和 URL Rewrite Filter
- Security:Acegi Security
- JavaScript 和 CSS:Scriptaculous、Prototype 和 Mike Stenhouse 的 CSS Framework
除了这些库之外,AppFuse 还使用 Log4j 来记录日志,使用 Velocity 来构建 e-mail 和菜单模板。Tomcat 可以支持最新的开发,我们可以使用 1.4 或 5 版本的 Java 平台来编译或构建程序。我们应该可以将 AppFuse 部署到任何 J2EE 1.3 兼容的应用服务器上;这已经经过了测试,我们知道它在所有主要版本的 J2EE 服务器和所有主要的 servlet 容器上都可以很好地工作。
图 3 给出了上面创建的 devworks 项目的 lib 目录。这个目录中的 lib.properties 文件控制了每个依赖性的版本号,这意味着我们可以简单地通过把这些包的新版本放到这个目录中并执行诸如 ant test-all -Dspring.version=2.0 之类的命令来测试这些包的新版本。
图 3. 项目依赖性
预先集成这些开放源码库可以在项目之初极大地提高生产效率。尽管我们可以找到很多文档介绍如何集成这些库,但是定制工作示例并简单地使用它来开发应用程序要更加简单。
除了可以简化 Web 应用程序的开发之外,AppFuse 让我们还可以将 Web 服务简单地集成到自己的项目中。尽管 XFire 也在 AppFuse 下载中一起提供了,不过如果我们希望,也可以自己集成 Apache Axis(请参看 参考资料 中有关 Axis 集成的教程)。另外,Spring 框架和 XFire 可以一起将服务层作为 Web 服务非常简单地呈现出来,这就为我们提供了开发面向服务架构的能力。
另外,AppFuse 并不会将我们限定到任何特定的 API 上。它只是简单地对可用的最佳开放源码解决方案重新进行打包和预先集成。AppFuse 中的代码可以处理这种集成,并实现了 AppFuse 的基本安全性和可用性特性。只要可能,就会减少代码,以便向 AppFuse 的依赖框架添加一个特性。例如,AppFuse 自带的 Remember Me 和 SSL 切换特性最近就因为类似的特性而从 Acegi Security 中删除了。
理由 3:自动化
Ant 使得简化了从编译到构建再到部署的自动化过程。Ant 是 AppFuse 中的一等公民,这主要是因为我发现在命令行中执行操作比从 IDE 中更加简单。我们可以使用 Ant 实现编译、测试、部署和执行任何代码生成的任务。
尽管这种能力对于有些人来说非常重要,但是它并不适用于所有的人。很多 AppFuse 用户目前都使用 Eclipse 或 Intellij IDEA 来构建和测试自己的项目。在这些 IDE 中运行 Ant 的确可以工作,但是这样做的效率通常都不如使用 IDE 内置的 JUnit 支持来运行测试效率高。
幸运的是,AppFuse 支持在 IDE 中运行测试,不过管理这种特性对于 AppFuse 开发人员来说就变得非常困难了。最大的痛苦在于 XDoclet 用来生成 Hibernate 映射文件和 Web 框架所使用的一些工件(例如 ActionForms 和 Struts 使用的 struts-config.xml)。IDE 并不知道需要生成的代码,除非我们配置使用 Ant 来编译它们,或者安装了一些可以认识 XDoclet 的插件。
这种对知识的缺乏是 AppFuse 2.0 切换到 JDK 5 和 Maven 2 上的主要原因。JDK 5、注释和 Struts 2 将让我们可以摆脱 XDoclet。Maven 2 将使用这些生成的文件和动态类路径来生成 IDE 项目文件,这样对项目的管理就可以进行简化。目前基于 Ant 的编译系统已经为不同的层次生成了一些工件(包括 dao.jar、service.jar 和 webapp.war),因此切换到 Maven 的模型上应该是一个非常自然的调整。
除了 Ant 之外(它对于编译、测试、部署和报告具有丰富的支持),对于 CruiseControl 的支持也构建到了 AppFuse 中。CruiseControl 是一个 Continuous Integration 应用程序,让我们可以在源代码仓库中代码发生变化时自动运行所有的测试。extras/cruisecontrol 目录包含了我们为基于 AppFuse 的项目快速、简单地设置 Continuous Integration 时所需要的文件。
设置 Continuous Integration 是软件开发周期中我们首先要做的事情之一。它不但激发程序员去编写测试用例,而且还通过 “You broke the build!” 游戏促进了团队之间的合作和融合。
理由 4:安全特性和可扩展性
AppFuse 最初是作为我为 Apress 编写的书籍 Pro JSP 中示例应用程序的一部分开发的。这个示例应用程序展示了很多安全特性和用于简化 Struts 开发的特性。这个应用程序中的很多安全特性在 J2EE 的安全框图中都不存在。使用容器管理认证(CMA)的认证方法非常简单,但是 Remember Me、密码提示、SSL 切换、登记和用户管理等功能却都不存在。另外,基于角色的保护方法功能在非 EJB 环境中也是不可能的。
最初,AppFuse 使用自己的代码和用于 CMA 的解决方案完全实现了这些特性。我在 2004 年年初开始学习 Spring 时就听说过有关 Acegi Security 的知识。我对 Acegi 所需要的 XML 的行数(175)与 web.xml 中所需要的 CMA 的行数(20)进行了比较,很快就决定丢弃 Acegi 了,因为它太过复杂了。
一年半之后 —— 在为另外一本书 Spring Live 中编写了一章有关使用 Acegi Security 的内容之后 —— 我就改变了自己的想法。Acegi 的确(目前仍然)需要很多 XML,但是一旦我们理解了这一点,它实际上是相当简单的。当我们最终作出改变,使用 Acegi Security 的特性来全部取代 AppFuse 的特性之后,我们最终删除了大量的代码。类之上的类都已经没有了,“Acegi handles that now” 中消失的部分现在全部进入了 CVS 的 Attic 中了。
Acegi Security 是 J2EE 安全模型中曾经出现过的最好模型。它让我们可以实现很多有用的特性,这些特性在 Servlet API 的安全模型中都不存在:认证、授权、角色保护方法、Remember Me、密码加密、SSL 切换、用户切换和注销。它让我们还可以将用户证书存储到 XML 文件、数据库、LDAP 或单点登录系统(例如 Yale 的 Central Authentication Service (CAS) 或者 SiteMinder)中。
AppFuse 对很多与安全性相关的特性的实现从一开始都是非常优秀的。现在 AppFuse 使用了 Acegi Security,这些特性 —— 以及更多特性 —— 都非常容易实现。Acegi 有很多地方都可以进行扩充:这是它使用巨大的 XML 配置文件的原因。正如我们已经通过去年的课程对 Acegi 进行集成一样,我们已经发现对很多 bean 的定义进行定制可以更加紧密地与 AppFuse 建立联系。
Spring IoC 容器和 Acegi Security 所提供的简单开发、容易测试的代码和松耦合特性的组合是 AppFuse 是这么好的一种开发平台的主要原因。这些框架都是不可插入的,允许生成干净的可测试代码。AppFuse 集成了很多开放源码项目,依赖注入允许对应用程序层进行简单的集成。
理由 5:使用 AppGen 生成代码
有些人会将代码生成称为代码气味的散播(code smell)。在他们的观点中,如果我们需要生成代码,那么很可能就会做一些错事。我倾向于这种确定自己代码使用的模式和自动化生成代码的能力应该称为代码香味的弥漫(code perfume)。如果我们正在编写类似的 DAO、管理器、操作或控件,并且不想为它们生成代码,那么这就需要根据代码的气味来生成代码。当然,当语言可以为我们提供可以简化任务的特性时,一切都是那么美好;不过代码生成通常都是一个必需 —— 通常其生产率也非常高 —— 的任务。
AppFuse 中提供了一个基于 Ant 和 XDoclet 的代码生成工具,名叫 AppGen。默认情况下,常见的 DAO 和管理器都可以允许我们对任何普通老式 Java 对象(POJO)进行 CRUD 操作,但是在 Web 层上这样做有些困难。AppGen 有几个特性可以用来执行以下任务:
- (使用 Middlegen 和 Hibernate 工具)从数据库表中生成 POJO
- 从 POJO 生成 UI
- 为 DAO、管理器、操作/控制器和 UI 生成测试
在运行 AppGen 时,您会看到提示说 AppGen 要从数据库表或 POJO 中生成代码。如果在命令行中执行 ant install-detailed ,AppGen 就会安装 POJO 特定的 DAO、管理器以及它们的测试。运行 ant install 会导致 Web 层的类重用通用的 DAO 和默认存在的管理器。
为了阐述 AppGen 是如何工作的,我们在 devworks MySQL 数据库中创建了如清单 2 所示的表:
清单 2. 创建一个名为 cat 的数据库表
create table cat (
cat_id int(8) auto_increment,
color varchar(20) not null,
name varchar(20) not null,
created_date datetime not null,
primary key (cat_id)
) type=InnoDB;
|
在 extras/appgen 目录中,运行 ant install-detailed 。这个命令的输出结果对于本文来说实在太长了,不过我们在清单 3 中给出了第一部分的内容:
清单 3. 运行 AppGen 的 install-detailed 目标
$ ant install-detailed
Buildfile: build.xml
init:
[mkdir] Created dir: /Users/mraible/Work/devworks/extras/appgen/build
[echo]
[echo] +-------------------------------------------------------+
[echo] | -- Welcome to the AppGen! -- |
[echo] | |
[echo] | Use the "install" target to use the generic DAO and |
[echo] | Manager, or use "install-detailed" to general a DAO |
[echo] | and Manager specifically for your model object. |
[echo] +-------------------------------------------------------+
[input] Would you like to generate code from a table or POJO? (table,pojo)
table
[input] What is the name of your table (i.e. person)?
cat
[input] What is the name, if any, of the module for your table (i.e. organization)?
[echo] Running Middlegen to generate POJO...
|
要对 cat 表使用这个新生成的代码,我们需要修改 src/dao/com/ibm/dao/hibernate/applicationContext-hibernate.xml,来为 Hibernate 添加 Cat.hbm.xml 映射文件。清单 3 给出了我们修改后的 sessionFactory bean 的样子:
清单 4. 将 Cat.hbm.xml 添加到 sessionFactory bean 中
<bean id="sessionFactory" class="...">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>com/ibm/model/Role.hbm.xml</value>
<value>com/ibm/model/User.hbm.xml</value>
<value>com/ibm/model/Cat.hbm.xml</value>
</list>
</property>
...
</bean>
|
在运行 ant setup deploy 之后,我们就应该可以在部署的应用程序中对 cat 表执行 CRUD 操作了:
图 4. Cat 列表
图 5. Cat 表单
我们在上面的屏幕快照中看到的记录都是作为代码生成的一部分创建的,因此现在就有测试数据了。
理由 6:文档
我们可以找到 AppFuse 各个风味的教程,并且它们都以 6 种不同的语言给出了:中文、德语、英语、韩语、葡萄牙语和西班牙语。使用风味(flavor) 一词,我的意思是不同框架的组合,例如 Spring MVC 加上 iBATIS、Spring MVC 加上 Hibernate 或 JSF 加上 Hibernate。使用这 5 种 Web 框架和两种持久框架,可以有好几种组合。有关它们的翻译,AppFuse 为自己的默认特性提供了 8 种翻译。可用语言包括中文、荷兰语、德语、英语、法语、意大利语、葡萄牙语和西班牙语。
除了核心教程之外,还添加了很多教程(请参看 参考资料) 来介绍与各种数据库、应用服务器和其他开放源码技术(包括 JasperReports、Lucene、Eclipse、Drools、Axis 和 DWR)的集成。
理由 7:社区
Apache 软件基金会对于开放源码有一个有趣的看法。它对围绕开放源码项目开发一个开放源码社区最感兴趣。它的成员相信如果社区非常强大,那么产生高质量的代码就是一个自然的过程。下面的内容引自 Apache 主页:
“我们认为自己不仅仅是一组共享服务器的项目,而且是一个开发人员和用户的社区。”
AppFuse 社区从 2003 年作为 SourceForge 上的一个项目(是 struts.sf.net 的一部分)启动以来,已经获得了极大的增长。通过在 2004 年 3 月转换到 java.net 上之后,它已经成为这里一个非常流行的项目,从 2005 年 1 月到 3 月成为访问量最多的一个项目。目前它仍然是一个非常流行的项目(有关 java.net 项目统计信息的链接,请参看 参考资料),不过在这个站点上它正在让位于 Sun 赞助的很多项目。
在 2004 年年末,Nathan Anderson 成为继我之后第一个提交者。此后有很多人都加入了进来,包括 Ben Gill、David Carter、Mika G?ckel、Sanjiv Jivan 和 Thomas Gaudin。很多现有的提交者都已经通过各种方式作出了自己的贡献,他们都帮助 AppFuse 社区成为一个迅速变化并且非常有趣的地方。
邮件列表非常友好,我们试图维护这样一条承诺 “没有问题是没有人理会的问题”。我们的邮件列表归档文件中惟一一条 “RTFM” 是从用户那里发出的,而不是从开发者那里发出的。我们绝对信奉 Apache 开放源码社区的哲学。引用我最好的朋友 Bruce Snyder 的一句话,“我们为代码而来,为人们而留下”。目前,大部分开发者都是用户,我们通常都喜欢有一段美妙的时间。另外,大部分文档都是由社区编写的;因此,这个社区的知识是非常渊博的。
结束语
我们应该尝试使用 AppFuse 进行开发,这是因为它允许我们简单地进行测试、集成、自动化,并可以安全地生成 Web 应用程序。其文档非常丰富,社区也非常友好。随着其支撑框架越来越好,AppFuse 也将不断改进。
从 AppFuse 2.0 开始,我们计划迁移到 JDK 5(仍然支持部署到 1.4)和 Maven 2 上去。这些工具可以简化使用 AppFuse 的开发、安装和升级。我们计划充分利用 Maven 2 的功能来处理相关依赖性。我们将碰到诸如 appfuse-hibernate-2.0.jar 和 appfuse-jsf-2.0.jar 之类的工件。这些工件都可以在 pom.xml 文件中进行引用,它们负责提取其他相关依赖性。除了在自己的项目中使用 AppFuse 基类之外,我们还可以像普通的框架一样在 JAR 中对这些类简单地进行扩展,这应该会大大简化它的升级过程,并鼓励更多用户将自己希望的改进提交到这个项目中。
如果没有其他问题,使用 AppFuse 可以让您始终处于 Java Web 开发的技术前沿上 —— 就像我们一样!
参考资料
学习
获得产品和技术
讨论
关于作者
|
http://downloads4.myeclipseide.com/downloads/products/eworkbench/6.5M1/MyEclipse_6.5M1.exe
注册前一定要把网络断开
Subscriber:QQ24785490
Subscription Code:DLR8ZC-855551-65657857678050018
Subscriber: administrator
Subscription Code: nLR7ZL-655342-54657656405281154
摘要: <!-- Example Server Configuration File -->
<!-- Tomcat服务器配置示例文件 -->
<!-- Note that component elements are nested corresponding to their
parent-child ...
阅读全文
import org.eclipse.swt.SWT;
import org.eclipse.swt.internal.ole.win32.COM;
import org.eclipse.swt.internal.ole.win32.DISPPARAMS;
import org.eclipse.swt.internal.ole.win32.EXCEPINFO;
import org.eclipse.swt.internal.ole.win32.GUID;
import org.eclipse.swt.internal.ole.win32.IDispatch;
import org.eclipse.swt.internal.ole.win32.ITypeInfo;
import org.eclipse.swt.internal.ole.win32.IUnknown;
import org.eclipse.swt.internal.win32.OS;
import org.eclipse.swt.ole.win32.OLE;
import org.eclipse.swt.ole.win32.Variant;
/*
* Created on 2006-7-11
*
*
* Window - Preferences - Java - Code Style - Code Templates
*/
/**
* @author hcw
*
* TODO To change the template for this generated type comment go to Window -
* Preferences - Java - Code Style - Code Templates
*/
public class CComObject {
private GUID guid = new GUID();
//private GUID IID = new GUID();
private IDispatch Autoface = null;
private ITypeInfo TypeInfo = null;
private String FMachineName = null;
private void CreateComObject() throws Exception{
dispose();
int[] ppv = new int[1];
int ret = COM.CoCreateInstance(guid, 0,
COM.CLSCTX_INPROC_SERVER | COM.CLSCTX_LOCAL_SERVER,
COM.IIDIUnknown, ppv);
if(ret!=COM.S_OK)
OLE.error(ret);
//throw new Exception("对象创建失败!");
IUnknown objIUnknown = new IUnknown(ppv[0]);
ppv = new int[1];
ret = objIUnknown.QueryInterface(COM.IIDIDispatch, ppv);
objIUnknown.Release();
if(ret!=COM.S_OK)
OLE.error(ret);
//throw new Exception("对象不支持Dispatch!");
Autoface = new IDispatch(ppv[0]);
ppv = new int[1];
ret = Autoface.GetTypeInfo(0, COM.LOCALE_USER_DEFAULT, ppv);
if (ret == OLE.S_OK) {
TypeInfo = new ITypeInfo(ppv[0]);
TypeInfo.AddRef();
}
}
private int getIDsOfNames(String name){
int[] rgdispid = new int[1];
String[] names = new String[]{name};
int ret = Autoface.GetIDsOfNames(guid, names, names.length, COM.LOCALE_USER_DEFAULT, rgdispid);
if (ret != COM.S_OK) return -1;
return rgdispid[0];
}
private void getVariantData(Variant aVar, int pData){
if (pData == 0) OLE.error(OLE.ERROR_OUT_OF_MEMORY);
COM.VariantInit(pData);
if ((aVar.getType() & COM.VT_BYREF) == COM.VT_BYREF) {
COM.MoveMemory(pData, new short[] {aVar.getType()}, 2);
COM.MoveMemory(pData + 8, new int[]{aVar.getByRef()}, 4);
return;
}
switch (aVar.getType()) {
case COM.VT_EMPTY :
break;
case COM.VT_BOOL :
COM.MoveMemory(pData, new short[] {aVar.getType()}, 2);
COM.MoveMemory(pData + 8, new int[]{(aVar.getBoolean()) ? COM.VARIANT_TRUE : COM.VARIANT_FALSE}, 2);
break;
case COM.VT_R4 :
COM.MoveMemory(pData, new short[] {aVar.getType()}, 2);
COM.MoveMemory(pData + 8, new float[]{aVar.getFloat()}, 4);
break;
case COM.VT_I4 :
COM.MoveMemory(pData, new short[] {aVar.getType()}, 2);
COM.MoveMemory(pData + 8, new int[]{aVar.getInt()}, 4);
break;
case COM.VT_DISPATCH :
aVar.getDispatch().AddRef();
COM.MoveMemory(pData, new short[] {aVar.getType()}, 2);
COM.MoveMemory(pData + 8, new int[]{aVar.getDispatch().getAddress()}, 4);
break;
case COM.VT_UNKNOWN :
aVar.getUnknown().AddRef();
COM.MoveMemory(pData, new short[] {aVar.getType()}, 2);
COM.MoveMemory(pData + 8, new int[]{aVar.getUnknown().getAddress()}, 4);
break;
case COM.VT_I2 :
COM.MoveMemory(pData, new short[] {aVar.getType()}, 2);
COM.MoveMemory(pData + 8, new short[]{aVar.getShort()}, 2);
break;
case COM.VT_BSTR :
COM.MoveMemory(pData, new short[] {aVar.getType()}, 2);
char[] data = (aVar.getString()+"\0").toCharArray();
int ptr = COM.SysAllocString(data);
COM.MoveMemory(pData + 8, new int[] {ptr}, 4);
break;
default :
OLE.error(SWT.ERROR_NOT_IMPLEMENTED);
}
}
private Variant setVariantData(int pData){
if (pData == 0) OLE.error(OLE.ERROR_INVALID_ARGUMENT);
Variant ret = null;
short[] dataType = new short[1];
COM.MoveMemory(dataType, pData, 2);
int type = dataType[0];
if ((type & COM.VT_BYREF) == COM.VT_BYREF) {
int[] newByRefPtr = new int[1];
OS.MoveMemory(newByRefPtr, pData + 8, 4);
return new Variant(newByRefPtr[0], COM.VT_BYREF);
}
switch (type) {
case COM.VT_EMPTY :
break;
case COM.VT_BOOL :
short[] newBooleanData = new short[1];
COM.MoveMemory(newBooleanData, pData + 8, 2);
ret = new Variant(newBooleanData[0]!=0);
break;
case COM.VT_R4 :
float[] newFloatData = new float[1];
COM.MoveMemory(newFloatData, pData + 8, 4);
ret = new Variant(newFloatData[0]);
break;
case COM.VT_I4 :
int[] newIntData = new int[1];
OS.MoveMemory(newIntData, pData + 8, 4);
ret = new Variant(newIntData[0]);
break;
case COM.VT_DISPATCH : {
int[] ppvObject = new int[1];
OS.MoveMemory(ppvObject, pData + 8, 4);
if (ppvObject[0] == 0) {
type = COM.VT_EMPTY;
break;
}
ret = new Variant(new IDispatch(ppvObject[0]));
break;
}
case COM.VT_UNKNOWN : {
int[] ppvObject = new int[1];
OS.MoveMemory(ppvObject, pData + 8, 4);
if (ppvObject[0] == 0) {
type = COM.VT_EMPTY;
break;
}
ret = new Variant(new IUnknown(ppvObject[0]));
break;
}
case COM.VT_I2 :
short[] newShortData = new short[1];
COM.MoveMemory(newShortData, pData + 8, 2);
ret = new Variant(newShortData[0]);
break;
case COM.VT_BSTR :
// get the address of the memory in which the string resides
int[] hMem = new int[1];
OS.MoveMemory(hMem, pData + 8, 4);
if (hMem[0] == 0) {
type = COM.VT_EMPTY;
break;
}
int size = COM.SysStringByteLen(hMem[0]);
if (size > 0){
char[] buffer = new char[(size + 1) /2]; // add one to avoid rounding errors
COM.MoveMemory(buffer, hMem[0], size);
ret = new Variant(new String(buffer));
} else {
ret = new Variant(""); //$NON-NLS-1$
}
break;
default :
int newPData = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, Variant.sizeof);
if (COM.VariantChangeType(newPData, pData, (short) 0, COM.VT_R4) == COM.S_OK) {
ret = setVariantData(newPData);
} else if (COM.VariantChangeType(newPData, pData, (short) 0, COM.VT_I4) == COM.S_OK) {
setVariantData(newPData);
} else if (COM.VariantChangeType(newPData, pData, (short) 0, COM.VT_BSTR) == COM.S_OK) {
ret = setVariantData(newPData);
}
COM.VariantClear(newPData);
OS.GlobalFree(newPData);
break;
}
return ret;
}
private int invoke(int dispIdMember, int wFlags, Variant[] rgvarg, int[] rgdispidNamedArgs, Variant[] pVarResult) {
if (Autoface == null) return COM.E_FAIL;
DISPPARAMS pDispParams = new DISPPARAMS();
if (rgvarg != null && rgvarg.length > 0) {
pDispParams.cArgs = rgvarg.length;
pDispParams.rgvarg = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, Variant.sizeof * rgvarg.length);
int offset = 0;
for (int i = rgvarg.length - 1; i >= 0 ; i--) {
getVariantData(rgvarg[i], pDispParams.rgvarg + offset);
offset += Variant.sizeof;
}
}
if (rgdispidNamedArgs != null && rgdispidNamedArgs.length > 0) {
pDispParams.cNamedArgs = rgdispidNamedArgs.length;
pDispParams.rgdispidNamedArgs = OS.GlobalAlloc(COM.GMEM_FIXED | COM.GMEM_ZEROINIT, 4 * rgdispidNamedArgs.length);
int offset = 0;
for (int i = rgdispidNamedArgs.length; i > 0; i--) {
COM.MoveMemory(pDispParams.rgdispidNamedArgs + offset, new int[] {rgdispidNamedArgs[i-1]}, 4);
offset += 4;
}
}
EXCEPINFO excepInfo = new EXCEPINFO();
int[] pArgErr = new int[1];
int pVarResultAddress = 0;
if (pVarResult != null)
pVarResultAddress = OS.GlobalAlloc(OS.GMEM_FIXED | OS.GMEM_ZEROINIT, Variant.sizeof);
int ret = Autoface.Invoke(dispIdMember, new GUID(), COM.LOCALE_USER_DEFAULT, wFlags, pDispParams, pVarResultAddress, excepInfo, pArgErr);
if(ret!=COM.S_OK)
return ret;
if (pVarResultAddress != 0){
pVarResult[0] = setVariantData(pVarResultAddress);
COM.VariantClear(pVarResultAddress);
OS.GlobalFree(pVarResultAddress);
}
if (pDispParams.rgdispidNamedArgs != 0){
OS.GlobalFree(pDispParams.rgdispidNamedArgs);
}
if (pDispParams.rgvarg != 0) {
int offset = 0;
for (int i = 0, length = rgvarg.length; i < length; i++){
COM.VariantClear(pDispParams.rgvarg + offset);
offset += Variant.sizeof;
}
OS.GlobalFree(pDispParams.rgvarg);
}
return ret;
}
/**
* 创建空对象在使用需调用CreateComObject或CreateComObjectProgID创建COM对象。
*
*/
public CComObject(){
}
/**
* 使用CLSID创建COM对象,建构后可以调用COM对象方法或属性。
* @param sCLSID COM对象CLSID
* @throws Exception
*/
public CComObject(String sCLSID) throws Exception{
COM.IIDFromString(sCLSID.toCharArray(), guid);
CreateComObject();
}
/**
* 使用现有自动化对象接口创建COM对象。
* @param prgDispatch
*/
public CComObject(IDispatch prgDispatch){
Autoface = prgDispatch;
Autoface.AddRef();
}
/**
* 使用CLSID创建COM对象。可用于创建新的对象(原有对象将释放)
* @param sCLSID
* @return
* @throws Exception
*/
public IDispatch CreateComObject(String sCLSID) throws Exception{
COM.IIDFromString(sCLSID.toCharArray(), guid);
CreateComObject();
return Autoface;
}
/**
* 创建远程COM对象(未实现)。
* @param MachineName
* @param sCLSID
* @return
*/
public IDispatch CreateRemoteComObject(String MachineName, String sCLSID){
COM.IIDFromString(sCLSID.toCharArray(), guid);
return Autoface;
}
/**
* 使用ProgID创建COM对象。
* @param ProgID
* @return
* @throws Exception
*/
public IDispatch CreateComObjectProgID(String ProgID) throws Exception{
COM.CLSIDFromProgID(ProgID.toCharArray(), guid);
CreateComObject();
return Autoface;
}
/**
* 调用COM对象方法,有返回值(可变参数暂不支持)。
* @param FunctionName
* @param rgvarg
* @return
* @throws Exception
*/
public Variant CallFunction(String FunctionName, Variant[] rgvarg) throws Exception{
if(Autoface==null)
throw new Exception("对象为空");
int rgdispid = getIDsOfNames(FunctionName);
if(rgdispid==-1)
throw new Exception("方法不支持!");
Variant[] pVarResult = new Variant[1];
int ret = invoke(rgdispid, COM.DISPATCH_METHOD, rgvarg, null, pVarResult);
if(ret!=COM.S_OK)
OLE.error(ret);
//throw new Exception("方法调用失败!");
return pVarResult[0];
}
/**
* 调用COM对象方法,无参数和返回值。
* @param FunctionName
* @throws Exception
*/
public void CallFunction(String FunctionName) throws Exception{
CallFunction(FunctionName, new Variant[]{});
}
/**
* 获取COM对象属性值。
* @param PropertyName
* @param rgvarg
* @return
* @throws Exception
*/
public Variant getProperty(String PropertyName, Variant[] rgvarg) throws Exception{
if(Autoface==null)
throw new Exception("对象为空");
int rgdispid = getIDsOfNames(PropertyName);
if(rgdispid==-1)
throw new Exception("属性不支持!");
Variant[] pVarResult = new Variant[1];
int ret = invoke(rgdispid, COM.DISPATCH_PROPERTYGET, rgvarg, null, pVarResult);
if(ret!=COM.S_OK)
OLE.error(ret);
//throw new Exception("属性获取失败");
return pVarResult[0];
}
/**
* 给COM对象属性赋值。
* @param PropertyName
* @param rgvarg
* @throws Exception
*/
public void setProperty(String PropertyName, Variant[] rgvarg) throws Exception{
if(Autoface==null)
throw new Exception("对象为空");
int rgdispid = getIDsOfNames(PropertyName);
if(rgdispid==-1)
throw new Exception("属性不支持!");
int[] rgdispidNamedArgs = new int[] {COM.DISPID_PROPERTYPUT};
int dwFlags = COM.DISPATCH_PROPERTYPUT;
for (int i = 0; i < rgvarg.length; i++) {
if ((rgvarg[i].getType() & COM.VT_BYREF) == COM.VT_BYREF)
dwFlags = COM.DISPATCH_PROPERTYPUTREF;
}
Variant[] pVarResult = new Variant[1];
int ret = invoke(rgdispid, dwFlags, rgvarg, rgdispidNamedArgs, pVarResult);
if(ret != COM.S_OK)
OLE.error(ret);
//throw new Exception("属性设置失败!");
}
/**
* 释放COM对象的连接。在对象不用的时候需调用此函数,以便及时释放对象。
*
*/
public void dispose(){
if(Autoface!=null)
Autoface.Release();
Autoface = null;
if(TypeInfo!=null)
TypeInfo.Release();
TypeInfo = null;
}
}
摘要: package com.bovy.officehelper;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.internal.ole.win32.TYPEATTR;
import org.eclipse.sw...
阅读全文
摘要: Office (2007) Open XML 文件格式简介
发布日期: 2007-07-06 | 更新日期: 2007-07-06
Frank Rice, Microsoft Corporation
学习Office Open XML格式的优势。用户可以在Office应用程序和企业系统之间使用XML和ZIP技术来交换数据。文档是全局可以访问的。并且,您还可以减少文件损坏的风险。
适用于...
阅读全文
产品中要用到在Rcp程序中操作Excel。在网上goole了一下,发现一篇文章还不错,测试了一下通过。
Integrate Eclipse RCP with Microsoft Applications - Tutorial
Lars Vogel
<webmaster@vogella.de>
Version 0.5
Copyright © 2007 Lars Vogel
01.11.2007
Abstract
The Eclipse Rich Client Platform (RCP) is using the SWT GUI framework. SWT does allow to integrated Microsoft application via OLE (Object Linking and Embedding). This article will demonstrate how SWT can be used within an Eclipse RCP application to integrate / use Microsoft applications. Microsoft Outlook and Microsoft Excel are used as examples
This article assumes that you are already familiar with using the Eclipse IDE and with developing simple Eclipse RCP applications.
--------------------------------------------------------------------------------
Table of Contents
1. Microsoft Object Linking and Embedding
1.1. Overview
2. Microsoft Integration - Sending an email via outlook
2.1. Create Project
2.2. Add action
2.3. Program the action
3. Microsoft Integration - Use Excel in your application.
3.1. Create Project
3.2. Change View code
4. Links and Literature
1. Microsoft Object Linking and Embedding
1.1. Overview
Windows applications use OLE (Object Linking and Embedding) to allow applications to control other application objects. The following will show how to do this using a few examples.
2. Microsoft Integration - Sending an email via outlook
This example assumes you running an MS operating system and that you have a version of Outlook installed.
2.1. Create Project
Create a new project "OutlookTest" (see here how to create a new RCP project). Use the "Hello RCP " as a template.
2.2. Add action
Using extensions add action "sendEmail" (see here how to add an action). The class of this action should be "outlooktest.SendEmail". Run the application. The result should look like the following.
2.3. Program the action
Lets program the action. The following coding will create and open the email for the user. It also assumes that you have a file c:\temp\test.txt which will be attached to the email.
package outlooktest;
import java.io.File;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.ole.win32.OLE;
import org.eclipse.swt.ole.win32.OleAutomation;
import org.eclipse.swt.ole.win32.OleClientSite;
import org.eclipse.swt.ole.win32.OleFrame;
import org.eclipse.swt.ole.win32.Variant;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowActionDelegate;
public class SendEmail implements IWorkbenchWindowActionDelegate {
private Shell shell;
@Override
public void dispose() {
// TODO Auto-generated method stub
}
@Override
public void init(IWorkbenchWindow window) {
shell = window.getShell();
}
@Override
public void run(IAction action) {
Display display = Display.getCurrent();
Shell shell = new Shell(display);
OleFrame frame = new OleFrame(shell, SWT.NONE);
// This should start outlook if it is not running yet
OleClientSite site = new OleClientSite(frame, SWT.NONE, "OVCtl.OVCtl");
site.doVerb(OLE.OLEIVERB_INPLACEACTIVATE);
// Now get the outlook application
OleClientSite site2 = new OleClientSite(frame, SWT.NONE,
"Outlook.Application");
OleAutomation outlook = new OleAutomation(site2);
//
OleAutomation mail = invoke(outlook, "CreateItem", 0 /* Mail item */)
.getAutomation();
setProperty(mail, "To", "test@gmail.com"); /* Empty but could also be predefined */
setProperty(mail, "Bcc", "test@gmail.com"); /* Empty but could also be predefined */
setProperty(mail, "BodyFormat", 2 /* HTML */);
setProperty(mail, "Subject", "Top News for you");
setProperty(mail, "HtmlBody",
"<html>Hello<p>, please find some infos here.</html>");
File file = new File("c:/temp/test.txt");
if (file.exists()) {
OleAutomation attachments = getProperty(mail, "Attachments");
invoke(attachments, "Add", "c:/temp/test.txt");
} else {
MessageDialog
.openInformation(shell, "Info",
"Attachment File c:/temp/test.txt not found; will send email with attachment");
}
invoke(mail, "Display" /* or "Send" */);
}
private static OleAutomation getProperty(OleAutomation auto, String name) {
Variant varResult = auto.getProperty(property(auto, name));
if (varResult != null && varResult.getType() != OLE.VT_EMPTY) {
OleAutomation result = varResult.getAutomation();
varResult.dispose();
return result;
}
return null;
}
private static Variant invoke(OleAutomation auto, String command,
String value) {
return auto.invoke(property(auto, command),
new Variant[] { new Variant(value) });
}
@Override
public void selectionChanged(IAction action, ISelection selection) {
// TODO Auto-generated method stub
}
private static Variant invoke(OleAutomation auto, String command) {
return auto.invoke(property(auto, command));
}
private static Variant invoke(OleAutomation auto, String command, int value) {
return auto.invoke(property(auto, command),
new Variant[] { new Variant(value) });
}
private static boolean setProperty(OleAutomation auto, String name,
String value) {
return auto.setProperty(property(auto, name), new Variant(value));
}
private static boolean setProperty(OleAutomation auto, String name,
int value) {
return auto.setProperty(property(auto, name), new Variant(value));
}
private static int property(OleAutomation auto, String name) {
return auto.getIDsOfNames(new String[] { name })[0];
}
}
If you now start the application and press the button an email should be prepared and shown to the user.
3. Microsoft Integration - Use Excel in your application.
This example assume that you running on Microsoft and that you have a version of Microsoft Excel installed.
3.1. Create Project
Create a new project "ExcelTest" (see here how to create a new RCP project). Use the "RCP with a view" as a template. Run it and see that is working.
3.2. Change View code
Select View.java and replace the coding with the following.
package exceltest;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTError;
import org.eclipse.swt.ole.win32.OleClientSite;
import org.eclipse.swt.ole.win32.OleFrame;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
public class View extends ViewPart {
public static final String ID = "ExcelTest.view";
private OleClientSite site;
public View() {
}
@Override
public void createPartControl(Composite parent) {
try {
OleFrame frame = new OleFrame(parent, SWT.NONE);
site = new OleClientSite(frame, SWT.NONE, "Excel.Sheet");
} catch (SWTError e) {
System.out.println("Unable to open activeX control");
return;
}
}
@Override
public void setFocus() {
// Have to set the focus see https://bugs.eclipse.org/bugs/show_bug.cgi?id=207688
site.setFocus();
}
}
Start your application and excel should be displayed.
4. Links and Literature
http://www.eclipse.org/ Eclipse Website
http://www.vogella.de/articles/Eclipse/article.html Using Eclipse as IDE - Tutorial
http://www.vogella.de/articles/RichClientPlatform/article.html Eclipse Rich Client Platform - Tutorial
http://www.vogella.de/articles/EclipseCodeAccess/article.html A guide to access the Eclipse Sources
我用的是Eclipse 3.2,操作系统是WinXP SP2。
需要把eclipse\plugins\org.eclipse.swt.win32.win32.x86_3.2.0.v3232m.jar里面的swt-win32-3232.dll解压缩出来拷贝到WINDOWS\SYSTEM32里面。
然后就OK了。
是在Eclipse里面运行Springside的ANT Task的时候,如果需要通过console输入交互信息,则会报这个错。应该是因为Eclipse的console依赖于那个本地库,而平常我们安装Eclipse不会拷贝那个库。
在服务器管理过程中,通常会遇到分区空间不够,或要调整分区的大小,如果是普通用户,大不了备份数据然后重装系统,可是,在正常运营的linux 服务器系统中,这么做是不现实的,我们必须学会动态的调整linux 的分区,linux为我们提供了一个lvm 逻辑驱动器卷的方案,除此之外,还有一个方法,本文将向你一一道来:
其实linux为我们的管理提供更方便的方法,我们知道,在linux系统中的各种状态都分别以相应的运行级来代表,比如,关机进程是run level 0 单用户管理是run level 1, 我们常用的图形用户界面在某此系统中被定义为run level 5,而重启是run level 6,关于linux中的运行级,在今后的文章中,小编会向你详细介绍,今天,小编主要向你介绍如何在运行的linux中移动tmp分区!
在正常的系统初始化的情况下,启动时会加载/etc/fstab文件来挂载文件系统,当我们正常进入系统后,进行维护性的管理时,所有的文件系统是不允许被卸掉的,那么有什么方法可以让我们卸掉文件系统呢,答案就是运行级,在运行级1中,系统进入单用户管理模式,只有root用户能够登录系统,这个时候系统完全在内存中运行,我们就可以对硬盘进行完全的操作,好,下面我们就进入运行级1
# init 1 以root用户执行此命令后,系统会关闭所有运行中的服务,并切换到单用户模式,这时只有root用户能够登录,登录后,比如,我们可以需要把/tmp移动到/var/tmp下面,我们可以使用:
# mv /tmp /var/tmp
这样,/tmp中的内容就被移动到了/var/tmp下面,接下来我们删除/tmp目录:
#rm -rf /tmp
接下来,创建一个到/var/tmp的符号链接:
ln -s /var/tmp /tmp
这时,/tmp实际就变为了指向/var/tmp的一个符号链接,然后我们按ctrl+D命令返回到图形界面,登录后,就看到/tmp目录已经被移走了。
用相同的方法也可以移动/var分区到新的硬盘,只是/var分区不比/tmp分区,系统运行的重要数据保存在这里,移动前一定要好备份并仔细检查哦。
摘要: 9月买的葫芦晒干了。于是今天取出子,在网上搜了下种植方法。已经按方法操作。不知是否可以成活。呵呵,过几天就知道了。种植方法如下:
阅读全文
摘要: 在我的项目中两个系统需要切换,应用服务器是weblogic8.1,在同一台机器上建了两个域。当从一个域访问另一个域后,再点第一个域的链接就会跳出系统转入登录界面。经过反复测试如果将第二个域部署到另一台机器则不会发生该问题。于是,我觉得seesion肯定是在访问同一机器的第二个域时被复写了。
阅读全文
Sybase IQ for Data Warehouse 培训总结(一)
*作 者: xuwedo
培训主要内容:Administering Sybase IQ for Data Warehouse (volume 1 &volume 2)
一、 本次培训涉及到以下内容:
1. Sybase IQ产品概述
2. 架构和专业述语
3. 环境变量和安装
4. 怎样创建数据库
5. 设置服务器和数据选项
6. Sybase IQ索引和数据类型
7. 创建表和索引
8. Sybase IQ内存配置
9. 从文件中加载数据
10. 用INSERT语句加载数据
11. 使用Sybase IQ
12. 用户的管理和安全
13. 事务管理和锁
14. 监控和问题解决
15. 管理DBspace 和索引
16. 备份和恢复
17. Sybase IQ的多元架构
18. 创建一个多元架构的数据库
二、 以下为各部分的详细叙述:
(一) Sybase IQ产品概述
1、 Sybase IQ是一个强大的即席查询服务器。
2、 用Sybase IQ来分离决策支持系统(DSS,Decision Support System,READER)和在线事务处理系统(OLTP,OnLine Transaction Processing,WRITER)。
3、 Sybase IQ的特点有:
(1)、垂直数据存储和压缩
(2)、优化了所有数据类型的存取方法
(3)、基于位的存储技术
(4)、即席查询优化器
(5)、标准的对外接口
(6)Sybase Central 支持
4、 Sybase IQ不适合做:
(1)、在线事务处理引擎
用Adaptive Server Enterprise代替
(2)、需要实时更新的在线应用服务器
用Adaptive Server Enterprise或Adaptive Server Anywhere代替
5、 Sybase数据库的基本原理:
(1)、列方式的存储,可以减少IO
例如:SELECT COUNT(*) FORM customer WHERE gender = “m”
(2)、基于位的索引方式
分为高基数数据(唯一值数量大于1500个)和低基数数据(唯一值数量小于1500个)。
(二) 架构和专业述语
1、 Sybase IQ平台支持的操作系统版本可以查看Sybase的官方网站,注意:必须有相关的操作系统补丁。
2、 Sybase IQ Server是在主机上运行的一个进程。
3、 一个Sybase IQ Server只支持一个Sybase IQ数据库。可以在一个Sybase IQ Server上部署多个数据库,但不推荐。
4、 Sybase IQ数据库包括:
l 三种DBSPACEE:
(1)、Catalog Store(for metadata,扩展名为:.db)
一个IQ 数据库只能有一个Catalog store DBspace,而且只能创建在一个文件系统上,不能创建在裸设备上。
Catalog Store会随着元数据、表、视图、存储过程等对象的增加动态增长。
Catalog Store包含了管理一个IQ数据库的所有信息。
Catalog Store的逻辑名为SYSTEM。
一个数据库中所有表的信息都存储在Catalog Store的系统表中。例如系统表有:SYSIQCOLUMN、SYSIQFILE、 SYSIQINDEX、 SYSIQINFO、 SYSIQTABLE等。可以用语句:
Select * from SYSIQINFO 语句来查看相关信息。
(2)、IQ Store(for data,扩展名为:.iq)
IQ Store存储了压缩的数据信息,例如索引信息、事务日志、管理分配空间的结构等。
每一个数据库只有一个IQ Store。一个IQ Store通常情况下包括多个DBspace。
IQ Store的DBspace可以建立在裸设备或文件系统上。
IQ Store的逻辑名为:IQ_SYSTEM_MAIN
注意:IQ Store的文件系统的扩展名为.iq,裸设备没有扩展名。
(3)、IQ temporary Store(for temporary data,扩展名为:.iqtmp)
IQ temporary Store是数据库的工作区间。在装入数据时在其中进行排序数据,以建立索引。
一个数据库只有一个IQ temporary Store。一个IQ temporary Store可以包含多个物理文件。
IQ temporary Store可以是文件系统或裸设备。
IQ temporary Store的逻辑名为IQ_SYSTEM_TEMP
l 三种日志文件:
(1)、IQ Message log
IQ数据库的可读日志文件,包括:插入和删除信息、错误信息、状态信息、查询计划等。
IQ Message Log只能在文件系统中,扩展名为.iqmsg。
一个数据库只能有一个IQ Message。
(2)、Catalog Store Transaction Log
Catalog Store Transaction Log是Catalog Store的事务日志。
用来作为必需时的回滚和前滚。
文件的扩展名为:.log。
随时间,此文件会越来越大。
(3)、IQ server logs
记录每次服务器启动时的信息。每启动一次就会产生一个IQ server log。
通常在$ASDIR/Logfiles目录下。
ASIQ_startup_nt.log文件中记录了服务器启动的参数。
Server_name.00n.srvlog中的n是服务器启动的次数。
其他信息发送到控制窗口中。
5、 一个DBspace是一个数据库文件的逻辑名。
6、 DBspace可以是分配给Sybase IQ的一个文件系统或裸设备。
7、 一个Sybase IQ 数据库包括多个DBspace。
8、 IQ中的表是指定义在Catalog Store的逻辑表。IQ中有三种表:基本表、本地临时表、全局临时表。
9、 IQ表的每一列最少有一种索引。每当创建新表时,系统会自动为表创建默认索引。
10、 IQ中一共有9种索引。
(三) 环境变量和安装
1. 对硬盘的空间要求:
UNIX:不少于800M。
WINDOWS:不少于375M。
IQ STORE 约点总磁盘空间的70%。IQ temporary Store大小约为IQ Store的20%。
l 对内存的要求:
虚拟内存=物理内存+磁盘交换分区大小。
推荐生产环境最小内存不小于1G的物理内存。
如果虚拟内存过小,会导致服务器失败。
l CPU要求:
颗数越多越好,速度越快越好。
l 启动IQ Server:
使用命令:start_asiq [config file] dbname [switchs]
例如:start_asiq @saiqdemo.cfg asiqdemo.db
l 停止IQ server:
(1)、使用命令:
Dbstop –c “uid=DBA; pwd=SQL; eng=server_name; dbn=db_name”
(2)、使用存储过程:
Stop_asiq
l 当安装完Sybase IQ后会自动生成一个名为“Utility”的数据库,此数据库没有数据,也没有数据文件与之关联(仅为一个util_db.ini文件)。它主要用于以下目的:
(1)、当没有实际的数据库时,用于测试连接。
(2)、用来创建和恢复数据库。
(3)、千万不能删除之。
(4)、此数据库的用户名为:DBA,密码为:SQL。
l 有七种方法可以连接到Sybase IQ:
(1)、ODBC
(2)、JDBC
(3)、OLE_DB
(4)、Sybase Open Client
(5)、用DBISQL
(6)、用Sybase Central。
(7)、用DBISQLC
Sybase IQ for Data Warehouse 培训总结(二)
*作 者: xuwedo
(一) 怎样创建数据库
1、DBspace是分配到IQ数据库的驱动空间的逻辑名
2、一个新IQ数据库最多可以有2047个DBspace。
3、Sybase的各种类型DBspace的限制:
(1)、Catalog Store最大为1TB。
(2)、IQ Store 和IQ temp Store 在裸设备上没有大小限制,在文件系统中为4TB。
4、IQ Store 和IQ temp Store可以扩大或从数据库删除。
5、创建数据库:
CREATE DATABASE full_path db_name[[TRANSACTION] {LOG ON [log_file_name][MIRROR mirror_file_name]}]
IQ PATH iq_file_anem
[IQ SIZE iq_file_size]
[IQ RESERVE sizeMb]
TEMPORARY PATH temp_file_name
[TEMPORARY SIZE temp_db_szie]
[TEMPORARY RESERVE sizeMB]
[MESSAGE PATH message_file_name]
例子1:
CREATE DATABASE ‘d:\\mydb\\mydb.db’
IQ PATH ‘d:\\iqmain\\mydb01.iq’
IQ SIZE 200
TEMPORARY PATH ‘e:\\iqtem\\mydb01.iqtmp’
TEMPORARY SIZE 200
IP PAGE SIZE 65536
6、可以用Sybase Central创建数据库(在图形环境下)。
7、可以用sp_iqstatus来查看当前数据库的详细信息。
8、删除一个DBspace
(1)、一个IQ store如果存有数据的话,则不能被删除。(可以使用Utility数据库将一个DBspace的数据移动到另一个DBspace中去,再删除)。
(2)、一个IQ temporary Store仅在为空时才可以被删除。
9、删除一个数据库的语法:
DROP DATABASE db_filename
例子:DROP DATABASE ‘d:\\mydb\\mydb.db’
10、怎样才能看到所有的DBspace的物理文件名?
用以下命令:Select * from sysqifile
(二) 设置服务器和数据选项
1、 数据库的启动参数均可以写在“.cfg”文件中。
2、 启动服务器的语法:
start_asiq server-switchs database_file
其中:“server-switchs”可以为:
-c :缓存大小,默认windows为32M,Unix为48M。
-gp:Catalog store页大小。
-gm:服务器允许的连接数。
-n:IQ server的名字。如果有两个“-n”选项,则第一个是IQ server的名字,第二个“-n”为IQ 数据库的名字。
-gc:checkpoint时间间隔。默认为20,推荐为6000。
-gr:最大的恢复时间。默认为2。
-ti:客户端超时时间。默认为4400分钟。
-tl:默认网络超时时间。默认120秒。
-iqmc:主缓存大小,单位:M。
-iqtc:临时缓存大小,单位:M。
注:主缓存:临时缓存=2:3
在默认情况下,Sybase IQ server使用2338端口。
3、 使用SET OPTION命令更变数据库的配置:
(1)、语法:
SET [TEMPORARY] OPTION
[user_id. | PUBLIC.]option_name = [option_value]
其中的“option_name”可以是:
Force_No_Scroll_Cursors=‘on’(默认为“off”)禁止缓存用户的查询结果。
Query_Temp_Space_Limit=0(默认为2000M),设置临时缓存的最大值。0表示不限制。
Public.Query_Plan=‘off’(默认为“on”)禁止将用户的查询计划打印到IQ Message File中,因为查询计划可以会使之大小迅速增加。
例子:
SET OPTION public.Force_NO_Scroll_Cursors=’no’
4、 查看数据库的所有被改动过的(即非默认值)选项,用存储过程:sp_iqcheckoptions。
(三) Sybase IQ索引和数据类型
1、Sybase IQ的9种索引类型:
(1)、FP(Fast Projection)此索引为默认的索引形式,在创建表时系统自动设置此索引。
特点:用于SELECT、LIKE ‘%sys%’、SUM(A+B)、JOIN操作等语句。
此类型索引也是唯一可用于BIT数据类型的索引。
FP索引可以优化索引,将小于255的唯一值的索引压缩到1字节中,将小于65537的唯一值索引压缩到2字节中。
(2)、LF(Low Fast)基于平衡树的结构,存储唯一值小于1500个的索引,是最快的索引类型。可以用作唯一索引。
特点:用于=、 !=、IN、NOT IN查询参数。
MIN()、MAX()、COUNT()、Group By、JOIN等。
(3)、HNG(High Nongroup)基于位的优化索引,适合于数字索引。用于范围查找和求合计算。
特点:Rangs、Between、MIN()、MAX()、SUM()、AVG()等。
(4)、HG(High Group) 基于平衡树的结构,存储唯一值大于1500个的索引,是最快的索引类型。可以用作唯一索引。
特点:同LF索引的特点。
(5)、CMP(compare)仅用于比较一个表中的两个列的比较。
特点:<、 =、 >、 <= 、>=
(6)、WD(Word),仅用于索引数据类型为WORD的列。
特点:‘CONTAINS’、LIKE操作(但没有‘%’)。
例子:
Select count(*) from Customer where address contains(‘Main’)
(7)、DATE(date)仅用于日期类型的列。
(8)、DTTM(Datetime)仅用于日期时间类型的列。
(9)、TIME(Time)仅用于时间类型的列。
例子:
Select * from sales where DATAPART(YEAR,dales_dt) = 2007
Select * from sales where sales_dt>=‘2003-01-01 08:00:00’
(四) 创建表和索引
1、创建表:
例子1:
CREATE TABLE employee(
Emp_id int NOT NULL
,lname varchar(30) NULL
,fname varchar(30) NULL
,salary money NULL)
例子2:
CREATE TABLE stores(
Store_id char(4) NOT NULL
,store_name varchar(20) NOT NULL
,store_address varchar(40) NOT NULL
,UNIQUE(store_id))
例子3:
CREATE TABLE products(
Product_code char(5) NOT NULL
,product varchar(40) NOT NULL
,price money NULL
PRIMARY KEY (products_code))
例子4:
CREATE TABLE sales(
Sales_code char(10) PRIMARY KEY
,sales_date DATE NOT NULL
,product_code char(5) NOT NULL
,FOREIGN KEY fk1(product_code) REFERENCES product (product_code))
例子5:
SELECT * INTO co_residential_customer from customer where 1=2
2、用Sybase Central 可以在图形界面下创建表。
3、删除表:DROP TABLE tablename
4、删除表中的所有内容:TRUNCATE TABLE [owner.]table_name
5、创建视图,语法:
CREATE VIEW [owner.]view_name[(column-name[,…])]
AS select-without-order-by
[WITH CHECK OPTION]
例子1:
CREATE VIEW sd_customer AS SELECT * FROM customer
WHERE sheng = ‘SD’
例子2:
CREATE VIEW emp_dept
AS SELECT emp_home,emp_fname,dept_name
From Employee,Department
WHERE Employee.dept_id = Department.dept_id
5、用Sybase Central 可以在图形界面下创建视图。
6、创建索引:
CREATE [UNIQUE] [index-type] INDEX
Index_name ON
[owner.]table_name (column_name[,…])
[{IN | ON} DBSpace_name]
[NOTIFY integer]
[DELIMITED BY ‘separators-string’]
[LIMIT maxwordsize-integer]
例子1:
CREATE HG INDEX
Cust_customer_id ON
Cutomer(customer_id)
例子2:
CREATE CMP INDEX price_compare
ON orders(purchase_price,list_cost)
7、删除索引:
DROP INDEX [[owner.]tablename.]index_name
例子:
DROP INDEX cust_customer_id
(五) Sybase IQ内存配置
1、 IQ从单一的一个内存池中分配内存。
2、 从操作系统层面来看,IQ Server的内存是由堆组成。
3、 Buffer:内存中的一块区域,它存储了写入数据库或从数据库中读取的未解压的数据。
4、 IQ Page Size:IQ Server中每一个内存页的大小。
5、 IQ Page Size/16=BLOCK SIZE
6、 在启动服务器时,可以用参数-c来指定服务器缓存的初始大小。在所有平台中,这个值最大为256M。
7、 IQ Buffer有两种类型:
(1)、主Buffer缓存:IQ Store的Buffer。(占总大小的40%)
(2)、临时Buffer:IQ temporary Buffer。(占总大小的60%)
8、 在Sybase IQ server中一个活动的用户大约占用10M内存,一个非活动用户大约占5M内存。
作 者: xuwedo
(一) 从文件中加载数据
1、有三种方法向IQ table中加入数据:
(1)、用LOAD TABLE命令,从命名管道或文件中将数据导入表中。(速度是最快的)
(2)、用INSERT FROM SELECT 命令,将其他表中的数据加入当前表中。
(3)、用INSERT VALUES命令,“手动”地向表中加入数据。
2、关于数据加载和锁:
(1)、当一个用户正在装载、插入或修改一个表中的数据时,其他用户可以对该表进行查询操作。(原理是:版本控制)
(2)、多个用户可以并发地执行DML语句,但必须针对不同的表。
(3)、如果多个用户试图修改同一个表,则第一个用户获得DML锁。
3、从文件中装入数据的例子:
LOAD TABLE customer(customer_id ‘|’
,cust_type ‘|’
,organization ‘|’
,contact_name ‘|’
,contact_phone ‘|’
,address ‘|’
,city ‘|’
,country ‘\x0a’)
FROM ‘/work/data/cutomer1.dat’
ESCAPES OFF
QUOTES OFF;
4、删除表中的数据有三种方法:
(1)、用DELETE 命令:
DELETE FROM customer
WHERE customer_id IN( SELECT cutomer_id FROM customer
WHERE postno = ‘250001’)
(2)、用DROP TABLE命令
(3)、用TRUNCATE TABLE命令
5、删除后再增加数据有两种方式:
(1)、插入到删除后留下的空隙中。
(2)、追加到现有表的最后一行后面。(速度再快一些,但要更大的存储空间)
由APPEND_LOAD=‘ON’来控制。
6、UPDATE 命令:
UPDATE employee
SET dept_id = 400
WHERE emp_id = 111
(二) 用INSERT语句加载数据
例1:
INSERT INTO dept_head(name,dept)
NOTIFY 20
SELECT emp_fname ||‘ ’||emp_lname AS name
,dept_name
FROM employee JOIN department
ON emp_id = dept_head_id;
commit
例2:
INSERT INTO lineitem(shipdate,orderkey)
LOCATION‘servername.dbname’
PACKETSIZE 512
{SELECT l_shipdate,l_orderkey FROM lineitem};
commit
(三) 使用Sybase IQ
因为本章节均为图形化操作,比较简单。略
(四) 用户的管理和安全
1. 系统预置的两个组:SYS和PUBLIC组。
想知道系统中共有多少组,用以下命令:
SELECT * FROM Sys.sysgroups
2. 系统管理员的用户名:DBA,默认密码为:SQL
3. 向系统中加入用户有三种方法:
(1)、“GRANT CONNECT”命令,
GRANT CONNECT TO userid [,…]
INDENTIFIED BY password [,…]
注:更改用户的密码也是上面的命令,这可能导致管理员无意识地改变现有用户的密码,而其目的是增加用户。此操作没有警告提示。
(2)、Sybase Central 向导,图形化操作。
(3)、sp_iqaddlogin存储过程,
4. 与系统中用户管理有关的两个系统表:
IQ_User_Login_Info_Table和IQ_System_Login_Info_Table
如果想查看该系统表中的内容,则可以用以下语句:
SELECT * FROM IQ_User_Login_Info_Table
5. 与管理用户有关的几个存储过程:
sp_iqmodifyadmin
sp_iqaddlogin
sp_iqprocess_login
sp_iqdroplogin
sp_iqlistexpiredpasswords
sp_iqlistlockedusers
sp_iqlistpasswordexpirations
sp_iqlocklogin
sp_iqmodifylogin
sp_iqpassword
(五) 事务管理和锁
1、与事务有关的几个命令:
(1)、开始一个事务:
BEGIN TRANSACTION [transaction_name]
(2)、提交一个事务:
COMMIT [work]
(3)、回滚一个事务:
ROLLBACK [work]
(4)、在当前事务中建立一个保存点:
SAVEPOINT [savepoint_name]
(5)、回滚到一个保存点:
ROLLBACK TO SAVEPOINT [savepoint_name]
(6)、将改变保存到磁盘上:
CHECKPOINT
(7)、执行完一条语句后,自动提交:
WITH CHECKPOINT ON
(六) 监控和问题解决
1、 可以在Sybase Central中监控用户
2、 存储过程:sp_iqconnection可以显示用户连接的信息。包括connection_handle,User_ID,最后的请求的时间,最后IQ命令时间等。
3、 用drop connection connection_handle可以让服务器主动断开与某用户连接。
4、 用sp_iqcontext来显示服务器上执行SQL语句的情况,包括哪个用户正在执行哪条SQL语句,是否提交等信息。
5、 将服务器执行SQL情况写入日志:
方法1:
-zr SQL ;表明要收集SQL语句信息
-zo c:\\sqllog.txt ;重定向请求级别信息到文件中
方法2:
call sa_server_option (‘request_level_logging’ , ‘SQL’);
call sa_server_option(
‘ request_level_log_file ’,’sqllog.txt’);
关闭将SQL信息写入日志文件中:
call sa_server_option (‘request_level_logging’ , ‘’);
call sa_server_option(
‘ request_level_log_file ’,NONE);
6、 用sp_iqtransaction查看系统中事务的相关信息。
7、 错误处理:
(1)、UNIX 下有一脚本:getiqinfo.sh
(2)、Windows下为getiqinfo.bat
来得到服务器的相关信息,然后将生成的报告发送给Sybase即可。
(七) 管理DBspace 和索引
1、 改变DBSPACE的模式或改变DBSPACE的大小:
ALTER DBSPACE
2、 卸载一个空的DBSPACE:
DROP DBSPACE
3、 报告DBSPACE的一般信息和模式:
Sp_iqdbspace
4、 报告DBSPACE中的OBJECTS和它们的大小信息。
Sp_dbspaceinfo
5、 将一个DBSPACE中的OBJECTS移动到另一个DBSPACE中:
Sp_iqrelocate
6、 Sp_iqrebuildindex存储过程重新建立某列的索引。
7、 管理DBspace 和索引的操作均可以在Sybase Central中操作,比较简单。
(八) 备份和恢复
1、Sybase IQ server有三种备份类型:
(1)、完全备份
(2)、增量备份
(3)、完全备份的基础上增量备份
注:增量备份和完全备份的基础上增量备份都会对Catalog Store 进行完全备份。
默认情况下为完全备份。
2、Sybase IQ server的顺序:
(1)、Catalog Store
(2)、Transaction log File
(3)、IQ Store
3、当IQ server正在备份时:
(1)、默认情况下操作员必须在现场,挂持接备份介质。
(2)、操作员可以不在场,但必须正确地估算出备份的数据量大小,并且在安装备份设备时,要选“高级”安装方可。
4、备份命令:
BACKUP DATABASE [CRC ON|OFF]
[Attended ON|OFF]
[BLOCK FACTOR integer]
[{FULL|INCREMENTAL|INCREMENTAL SINCE FULL}]
TO ‘archive_device’ [SIZE # of KB integer]
例子:
BACKUP DATABASE TO ‘dev/rmt/0n’
注:BACKUP不支持裸设备
5、系统级别的备份:
如果IQ server运行时进行系统级别的备份,则会导致服务崩溃或丢失数据。所以,必须确定IQ server是停止状态时,方可进行系统级别备份。
6、备份的恢复:
(1)、必须连接到utility_db数据库后才可以进行恢复操作,并且在恢复数据时不能有用户连接到数据库。
(2)、如果有完全恢复,Catalog Store 和Transaction Log(即.db 和.log文件)必须从目标文件夹下删除。
(3)、如果为增量恢复,Catalog Store 和Transaction Log(即.db 和.log文件)必须存在于目标文件夹中。
7、恢复的命令:
RESTORE DATABASE ‘c:\\newdir\\mydb.db’
FROM ‘c:\\asiq\\backup1’
FROM ‘c:\\asiq\\backup2’
在进行恢复时,必须注意恢复的顺序和备份时的顺序完全一致。
8、数据库备份后,配置文件和日志文件是没有被备份的,所以如果恢复时要单独对这两种文件进行恢复。配置文件可以直接拷贝即可,但日志文件是不能用原来的.log文件的。
如果恢复到原来的目录中,则不用新建.log文件。如果没有恢复到原目录,则必须用dblog工具手动生成.log文件。
9、dblog工具的命令语法:
Dblog [option] database_file
Option:
-t log-name,指定事务log文件名。
-m mirror-name,设置事务日志镜象名。
-r,停止事务日志的镜象。
-o,输出日志信息到文件。
-q,安静模式,不打印信息。
例:dblog –t demolog.log asiqdemo
(九) Sybase IQ的多元架构
(十) 创建一个多元架构的数据库
在Sybase Central中完成。略。
1.1.
内存使用对SybaseIQ而言,内存越多越好,一般要求每个服务器的CPU配置内存2GB,数据装载需要动态使用额外的内存,计算使用缓存caches;降低IO的方法是使用大buffer caches;使用超出内存会导致频繁swapping;IQ从操作系统申请内存,从操作系统级别看SybaseIQ的内存为堆内存,所有的内存分配自动进行。IQ使用内存进行以下活动:
l
从磁盘读取数据并执行查询;
l
从系统平面文件加载时缓冲磁盘数据;
l
缓冲元数据文件 (Catalog)
l
管理连接,事务,缓冲区和数据库对象。
一个安装SybaseIQ的数据库服务器的典型内存分布如图所示:包括操作系统其他应用程序内存,服务器内存,额外内存,IQ存储缓存,临时存储缓存。

1.1.1.
服务器内存SybaseIQ服务器进程使用的内存,不同的操作系统平台其使用量存在差异,一般需要30MB。
1.1.2.
额外内存元数据缓存
由数据库启动选项 –c, -cl, 和 –ch参数控制使用RAM数量,专门用来作元数据库读写缓存,当数据库对象较多时,建议调大此数据。
线程内存
线程内存在数据库启动时分配给数据库服务器,用于线程的堆栈空间,总的空间数值按照公式“stack size * IQ 线程数量”计算,数据库启动参数–iqtss确定数据库的stack size大小单位KB。启动参数-iqmt指定IQ可以使用的线程数量,最小值是2*num_conn+1,缺省值是:
60*numCPU+2*num_conn+1
“活动”用户内存
每个Sybase IQ 用户对应一个连接进程,此进程大概需要 10MB空间;活动用户是指到数据库的一个连接或执行的查询命令,连接到数据库而不活动的用户需要更少的内存。
表版本内存
表版本内存在SybaseIQ运行时分配,通常这类内存非常小(每个被跟踪的表版本1KB到3KB),通常为KB到MB,但是当系统中存在成千上万的表版本时,占用空间会比较大。
数据加载内存
SybaseIQ在进行数据加载时,分配内存提供数据加载性能,通过数据库选项LOAD_MEMORY_MB设定,缺省情况为0,表示堆内存的分配不受限制,如果数据加载时需要更多内存,可能导致数据加载耗尽虚拟内存,降低数据加载性能,一般建议设定此选项,其配置值可根据加载数据的表宽进行如下计算:
Load_Memory_MB = <表宽字节数> * 10,000 * 45 / 1024 / 1024
以下语句将数据加载内存设置成300MB:
SET OPTION PUBLIC.LOAD_MEMORY_MB = 300
数据库备份内存
数据库备份内存的大小与CPU个数、备份的IQ存储和本地存储的数据库空间个数,BLOCK的交错因子,IQ的BLOCK大小有关系。可以通过如下公式粗略估计:
y = max( 2 * cpu个数, 8 * number_of_main_or_local_dbspaces)
z = (y * 20 ) * ( block factor * block_size )
Z表示的是备份时需要的内存的虚拟估计值,假设如下情况:
dbspaces = 50
block factor = 100
number of cpus = 4
block_size = 8,192
采用上面的公式和算法,数据库备份时需要的内存总数是:
'y' is max(8, 400) è y=400
'z' is ( 400 * 20 ) * ( 100 * 8,192 ) è 6.5GB
备份内存来自操作系统,属于堆内存,当备份操作完成后,内存释放。当空间充足时,唯一可以控制备份内存使用的是块的交错因子,在前面的例子中,如果交错因子变成10, 总的备份内存需求将降低到655MB:
( 400 * 20 ) * ( 10 * 8,192 ) è 655MB
在磁盘I/O不是瓶颈的情况下,在数据库备份时降低内存将减慢备份数度。数据库备份性能与磁盘子系统I/O的读写块性能一致,为减少I/O的额外开销,备份程序一次I/O中读入连续的块,相应地按照“块交错因子”写这些块数据。
通过“块交错因子”可以减少备份内存,增加可用的内存,以此计算数据库备份时需要的总内存量。
1.1.3.
数据库缓存数据库缓存是操作系统内存中分配用来保存从数据库中读出或者准备写入数据库的数据。SybaseIQ数据库缓存包括主存储缓存和临时存储缓存。参数设置可以通过数据库选项或者数据库启动配置文件。
主存储缓存
用于缓存数据库服务器从IQ存储中读取的数据,供 IQ 数据表空间的数据缓存使用,所有用户均可使用。较大的缓存可以降低磁盘的IO以提升性能,主存储缓存在数据库启动时初始化,缺省配置是16MB,对于大多数应用来说太小,修改此参数配置需要重新启动数据库。一般将IQ服务器可以使用的内存的40%分配给SybaseIQ主存储缓存。
临时存储缓存
SybaseIQ临时存储缓存用于数据查询时的表关联,分组,排序和哈系算法。数据加载时,也需要较多的临时存储缓存,例如HG索引的管理、FP索引加载时的1个字节和2个字节的字典表等。较大的临时存储缓存可以减少内存调度。
主存储缓存在数据库启动时初始化,缺省配置是12MB,对于大多数应用来说太小,修改此参数配置需要重新启动数据库。一般将IQ服务器可以使用的内存的60%分配给SybaseIQ的临时存储缓存。
Sybase IQ 12.6 的redhat AS4安装
由于我的iq126解压出来是一个个tgz文件,执行sybinstall.sh安装后会报告复制文件失败。打开sybinstall.sh文件来看看,应该是解压缩文件这个环节出错了,那就先把文件解压出来看看再试一下:
gzip -d *.tgz
sybinstall
好了,一路畅通。
启动测试数据看看有没有什么问题.
start_asiq @asiqdemo.cfg asiqdemo.db
..
启动正常,
stop_asiq
...没有发现server,
再打开stop_asiq看看:
哦原来也是用"ps -ef|.....",有点土吧,居然没找到server,肯定是这个脚本又有错误。修改一下,呵呵,搞定.
"case "$OS_TYPE" in
Linux) if ps -m >/dev/null 2>&1
then MY_PS="ps -efwm |awk -f $AWK_CMD ps_type=long"
else MY_PS="ps -efw |awk -f $AWK_CMD ps_type=long"
fi
;;"
把第一个ps_type=long改为ps_type=short
[注]我的os为RHEL 4 patch 2
数巨报表和其它国产报表一样,有一定的客户群和口碑,基本上自主研发了两年以上,价格基本在万元左右。数巨报表和如意报表类似,都是用于拖拽式,有一定的技术代表性。数巨报表产品主要定位于中低端市场,以易用性、满足中国用户需求、价格低为目标,逐渐建立中低端市场客户满意度,毕竟这部分客户还是担心项目的风险性。
Microstrategy是国外品牌,在全球BI软件市场占用很大的份额,通过BI软件产品向中国高端市场进军。他们往往能与最终用户直接签单,能给系统集成商和软件开发商带来丰厚的利润。Microstrategy报表软件的确功能强大,提供了很多二次开发接口,能给用户解决很多实际问题,但是价格昂贵不是一般客户可以接受的。
这是本人的一点认识,不正确的地方希望得到大家的指出。
本人不是MicroStrategy的代理或原厂,仅代表个人观点。
这两个产品在我看来,不在一个层次上,应用的范围和覆盖面也不相同,是国产软件比拼国外软件。
1.价格方面:正如楼上说数巨报表价格在万元左右,而MSTR一个普通配置均在20万以上,从目前局势,MSTR厂家还一直在涨价过程中。
2.服务方面:外商只要收到钱,服务还是满周到的,包括升级、技术支持,但千万不要指望他们帮你开发什么东西(除非付钱或厂家有其他战略企图);国产软件,由于开发时间短,资金投入不足,所以BUG会多一些,厂家靠自己的工程师来维护有些项目。
3.技术方面:虽然老外挣钱比较黑,但东西还是好东西,特别是MSTR,本身就定位于高端产品,在国内银行、电信、石油等大型企业的TB级数据仓库中,经常被采纳和使用。相反对于应用可有可无的政府、街道,用用国产软件还可以拼凑着玩玩,因为这些地方有工具无工具不会对生产造成影响。
技术比拼应该以下几个方面进行
一、伸缩性
MicroStrategy的第三代ROLAP架构,在容量上很容易能达到TB级或一个数据库平台能存储的数据量,这是通过以下方式实现的:反复地在优化的基于server的平台、数据库和MicroStrategy’s Intelligence Server中执行分析。根据定义,假定BI平台能够像MicroStrategy 那样产生高度优化的基于平台的SQL,数据库技术不断扩展并且将是一个理想的场所来完成大容量数据处理。MicroStrategy中的 Intelligence Server能进行多维分析,比如能利用各种各样的OLAP函数,执行那些不能被数据库高效率处理的cube-like slice and dice fully offloading analysis。
MicroStrategy 中的Intelligence Server是一个基于组件的应用服务器,提供了高度伸缩性环境所具备的必要控制和应用系统管理,如下所述:
• 在理想平台上完成多维分析处理(通过Intelligence Server或数据库中的200多个分析库)
• 真正共享的多级别的缓存完全与Intelligence Server中的安全模式进行了集成
• 通过连接池、粒状数据和应用控制来智能地管理所有的用户对数据库的连接
• 通过排队等候和线程管理,对所有请求动态分配优先权
• 系统用法和性能调整工具
• 群集管理和负载均衡
• 通过动态资源分配进行自我调整的结构使组件满负荷工作
MicroStrategy是第一个认可主动信息传递需要的BI厂商。以1998年的Broadcaster产品为开端,还有近期新近命名的Narrowcast Server,MicroStrategy使得通过各种媒体如:email、传真、呼机、手机,主动传递高度个性化的相关信息成为可能。该功能是out of the box并且不需要任何定制代码。用户定制他们想收到的信息、条件,例如数据中的异常或者是基于事件的标准和设备类型。
结构被设计成从MicroStrategy Intelligence Server和外部信息源中接受个性化的内容。从多种来源中获得的信息可能会出现在MicroStrategy Narrowcast Server的输出结果当中。数据源的例子包括从ERP系统中获得的XML内容、从内容供给者和入口处获得的ICE内容,或者其它的非关系型的内容如:平面文件、图片等等。基于XML的结构确保了完善的内容控制和对任何当前或将来存在的设备的适应性。Out of the box,MicroStrategy Narrowcast Server使你能够以HTML、普通文本、或Excel的形式给任何一个SMTP网关传送商业智能报告。
二、安全性
MicroStrategy满足以下需求:MicroStrategy 9安全模式包含必要的广度和深度,通过internet允许BI应用系统对员工、合作伙伴、供应商和顾客进行安全部署。MicroStrategy产品是通过以下方式实现其安全性的:应用功能级别的特权的使用、报表对象级别上的访问控制列表、安全过滤器、连接映射和在数据级别上对数据库视图的支持。另外,用户级别的安全是通过MicroStrategy与NT、WIN2003、LDAP的集成实现的,传输级别的安全是通过128位的SSL传输、128位数据加密或在web服务器上无数据库连接的双防火墙配置来实现的。
MicroStrategy 基于配置文档的安全性能确保了平台和传输体系中的每一部分都是安全的,都被严格管理。另外,MicroStrategy对工业标准的安全尺度的实现确保了MicroStrategy的安全模型能与当前存在的任一安全方式进行集成。
三、部署能力(包括维护能力)
MicroStrategy提供了desktop方式和web方式的集成的全功能性的BI,完整的BI功能包括静态报表制作,报表分发,查询和报表制作,OLAP分析,集分析并且从一个集成的接口利用数据挖掘。
允许用户从一个单独的接口,通过MicroStrategy Architect, MicroStrategy Agent 和 MicroStrategy 服务器管理之间紧密的集成来进行设计、创建、维护、运行和监视分析。
四、分析能力
由于MicroStrategy具有能够以优化的方式存取TB级数据的能力,用户有权在他们的安全角色确定的适当的控制范围内使用整个数据仓库。虽然许多查询工具能够检索少量的slice级别的数据,仅有MicroStrategy能够对可用的信息进行足够深度和宽度的优化存取。
五、易用
MicroStrategy Web中的纯HTML解决方案,使得能通过任何浏览器、在任何操作系统上、穿过任何防火墙被迅速存取。MicroStrategy Web提供了像完全互动地钻取引导、递增的获取、导航、转出到Excel、ad hoc报表创建、排序/分等级、表格和图形方式、任意旋转、报表提示等功能。
MicroStrategy Web中的Page-By提供了一个类似slice and dice 功能的cube。另外,MicroStrategy的互动处理是完全透明的。
六、性能
MicroStrategy 以cube响应时间的方式提供了具有ROLAP结构的数据伸缩性。这是MicroStrategy 通过动态地在所有级别上优化性能、阻止瓶颈在BI环境中发生来实现的,如下所述:
数据库处理
MicroStrategy利用数据库平台内在的优化,通过高度优化的、能对产生的SQL类型和优化提供粒状控制的数据库版本明确的VLDB驱动程序,进行大数据量处理和进一步提高整个数据库处理的效率。
MicroStrategy自动的aggregate table generation and advanced aggregate awareness意味着任何要添加进数据库的聚合表被MicroStrategy SQL引擎自动地动态利用。其它的数据库构造自动地利用包括本地数据库函数,性能调节算法和对像,MicroStrategy中的VLDB驱动程序包含大约60个
应用处理层
虽然MicroStrategy 中的Intelligence Server提供了大量的performance tuning aspects,然而它的主要目的是作为神经中枢对所有处理资源进行有效的协调-确保组件被正确利用,在优化的平台进行的处理包括:
• 在内存中有多级缓存,自动提示
• 依靠请求类型和可用资源,在数据库或应用服务器上反复处理
• 通过动态给超载组件分配资源的自我调整的结构
• 聚类和动态的负载均衡
• 异步的处理能力保证能够在同一时间处理多个web客户端请求
数据传输
MicroStrategy的ROLAP方式保证了在网络上只传输结果集。MicroStrategy把XML作为信息传递的机制保证了只向web浏览器发送高效结果集。递增的Fetch确保了请求结果行的数量在被需要的时候,从MicroStrategy Intelligence Server发送到web浏览器。
展示/本地desktop
大多报表格式和数据操纵在瘦客户端完成。类似于cube的数据操纵如Page By等和其它的数据操纵如数据排序是在本地完成的。
The MicroStrategy Narrowcast Server subscription portal也在关系结构中缓存HTML内容,这主要是为了允许narrowcast server subscription portal用户通过portal来浏览个性化的静态服务。
Max@X Analyser 5是新一代的报表与数据分析技术产品。针对复杂应用环境下的数据Web报表展现与联机数据分析(OLAP)而设计,重点在部署与集成、复杂表样支持、特殊需求适应、数据填报、OLAP 快速实施及可用性、服务器性能等方面进行了创造性的优化。
Max@X Analyser由四部分组成:Web报表(Reports)、联机数据分析(OLAP)、服务器引擎(Server Engine)、决策门户(Analyser Portal)。
可满足任何复杂样式需求的WEB报表设计模型(Reports)
报表是数据分析的最基础形式,也是应用最为广泛的数据表现形式。Max@X Analyser包含了一套功能强大且简单易用的专业Web报表工具。除了解决传统工具已经涵盖的B/S架构报表制作、预览、打印及导出文件等方面外,Max@X Analyser更进一步,在复杂报表的设计处理、特殊样式的扩展适应、海量数据的快速响应等方面,提供了更为优越的性能表现。
可直连多数据库的联机数据分析模型(OLAP)
联机分析处理OLAP是Max@X Analyser的重要核心之一,支持动态分析操作,侧重决策支持,并提供直观易懂的查询结果。Max@X Analyser无需数据仓库建设,也无需任何第三方OLAP Server支持,用户仅仅需要懂得数据库管理知识,就可以快速建立OLAP系统,设计并部署OLAP分析模型。Max@X Analyser联机分析功能不仅能进行数据汇总/聚集,建立多维度的分析、查询和报表,同时还提供切片、切块、下钻、上卷和旋转等数据分析功能,从而使用户从更快、更易使用的交互方式中获得收益。
可集成、可独立部署的服务器引擎(Server Engine)
服务器引擎是Max@X Analyser专门提供给集成用户的服务器核心。服务器引擎的表现形式在Java下是一系列JAR文件,在VS.NET下是一个Web组件。无论是JAVA还是VS.NET版本,服务器引擎均可作为应用程序的一个组成部分与应用程序进行集成,并接受应用程序的授权等控制,与应用程序共享数据库连接池。
100%免代码的快速部署发布应用门户服务器(Analyser Portal Server)
如果说我们可以把企业看做一架飞机的话,那么决策门户服务器(Analyser Portal Server)就是这架飞机的仪表及控制平台。Analyser Portal Server无需任何代码,根据人机交互的部署配置,自动创建应用页面及控制平台,包含了信息发布、信息共享、快速决策、任务定制以及授权控制等功能前端应用,与所有Max@X Analyser产品组件紧密关联。同时,它是一个基于Web的应用平台。通过这个平台,终端用户可以方便地完成数据分析、信息发布以及信息协同等任务。
我感觉大家对于两种产品的讨论已经很充分了,也详细的讨论了两种产品的功能和优缺点。
的确,两种产品的定位不同,针对的客户群也不一样,也没有必要非要评出个孰优孰劣,应该说是各有千秋。
在技术方面:
MSTR作为BI软件的领导厂商之一,通过一组软件产品满足BI领域的五大应用,分别是:
企业级报表——报表生成器用来生成很好的格式化的静态报表,这些报表广泛的向多数人发布。
立方体分析——基于立方体的BI工具向业务经理们提供简单的切片和钻取分析能力。
任意查询和分析——关系型OLAP(ROLAP)工具供超级用户对数据库进行任意的访问,对整个数据库进行切片、钻取,从而分析到最细粒度的交易信息。
统计分析和数据挖掘——通过统计分析和数据挖掘工具,可以使用各类模型进行预测或者寻找两个变量之间的因果相关性。
报表分发和预警——基于报表分发机制,可以根据订阅、调度或者数据库中的触发事件向大量的用户群发送整个报表或者告警信息。
从软件伸缩性来讲,它支持TB级的数据和百万级的用户;
在商务智能平台方面:它支持快速的集成,丰富的业务分析手段和主动分发;
在适应互联网方面:它以纯Web界面、快速部署与简单维护、全面的安全、高可靠和高性能著称。
同样,数巨作为后起之秀,以其优惠的价格,支持复杂报表的能力而获得用户的青睐,正如maxatx仁兄所介绍的一样,作为国产软件,它非常注重国人的特殊需求,比如国内常见的异常负责的报表表头,国外通常是很少见的,而且国内企业在BI方面的应用往往是刚刚起步,对于分析的需求多数还停留在报表方面,在这种情况下选择数巨是就成为了一个性价比很高的选择。
在价格方面,根据需求的不同采购的模块不同价格差异较大,但从满足基本的报表功能方面来比较,MSTR的价格通常在几十万这个量级,而数巨通常在几万到十几万这个区间,如果单从价格方面来说,数巨当然是占有较大优势了。
在服务方面,作为国外软件,通常认为其服务难以跟上,但MSTR已经进入国内市场多年,拥有一支完备的代理商和技术服务队伍,因此从服务方面来讲还是有保障的,不过其服务价格也不菲。而数巨作为国产软件,当然发挥其本土优势,服务方面应该是没的说的。
总之,两种软件对应的是不同的客户群,对于电信、银行等大数据量,大访问量,有复杂分析需求且资金充裕的行业,可以选择MSTR来满足其特殊应用。而对于广大中小企业,并没有太多的数据量和访问量,分析需求又相对简单以报表为主,且对价格比较敏感的话,那么数巨就成为了最好的选择。
软件评分:
MSTR:技术:65;价格:5;服务:20;总分90分。
数巨:技术:55;价格:10;服务25;总分90分。
Microsoft SQL Server 2000报表服务是服务器端的完整平台,它对传统纸面报表以及可交互的基于Web的报表都可以进行建立、管理和发布。它是微软已有的商业智能与数据仓库解决方案产品——Microsoft Office、Microsoft Business Solutions与Microsoft SQL Server的有益补充。
Microsoft Office Web Component(OWC)包含在Microsoft Office 2000以后的产品中。在使用Microsoft Internet Explorer浏览包含Office Web组件的Web页时,您可以直接在Internet Explorer中处理显示的数据,如对数据进行排序和筛选,输入新的数值,展开和折叠明细数据,进行行列旋转以查看源数据的不同汇总信息等。由于Office Web组件是完全可编程的,可以在很多设计环境中使用这些组件来建立复杂的、交互的和基于Web的解决方案。这些设计环境包括Microsoft FrontPage、Microsoft Access数据访问页以及Microsoft Visual Basic。您也可以使用最小的设计设置直接在Microsoft Excel中发布这些Office Web组件。
笔者根据所参与的项目及以前的应用经验,对Microsoft SQL Server Reporting Services、OWC和Microsoft SQL Server组成的微软企业级报表解决方案与相关产品的解决方案作了一个简单比较。
与Cognos公司产品的比较
Cognos的产品简介
Cognos公司的产品Cognos Suite是由一系列的功能模块组合而成的套件,包括ImpromptU、Powerplay和Scenario等模块。
Impromptu提供查询、报表功能,针对关系型数据。可以将Powerplay的数据形成报表,这是Cognos公司的集成。Impromptu Web Reports(WR)在Web上为大量报表用户提供易于打印的管理报表。用户可以订阅已发布报表,对其进行定制来满足特定的需要。
Powerplay提供OLAP分析功能,针对多维数据PowerCube(Cognos定义的多维数据结构)。在Powerplay中可以钻取到Impromptu中。这是Cognos公司的集成。
Transformer将Impromptu及其他数据源中的数据形成PowerCube,以备Powerplay使用。Scenari是数据挖掘工具。4Thought是采用神经网络技术的建模与预测模块。
Cognos报表解决方案与Reporting Services的不同点Cognos的解决方案不是集成的产品(将查询、报表作一个工具,分析作一个工具,Cognos的理由是先分析再做表);没有类似于Designer的专门设计模块,在Impromptu模块中完成数据库的连接与catalog的定义,因此Impromptu的使用要求用户具备数据库专业知识;没有对报表的集中管理、分发和调配的功能。目前引进中国的只有Impromptu、Powerplay。
Reporting Services的特点
1)Reporting Services是集查询、报表和分析于一体的产品,只需学习一个工具的使用、在一个界面上操作,易用性强。符合查询、分析、再查询、再分析循环往复的决策思维方式; Impromptu和Powerplay所使用的数据层不同,不能共用,Powerplay使用的多维数据由其它模块生成,是静态的多维立方体,维的改变需切换到其它模块。
2)Reporting Services的几大模块在SQL Server数据库引擎的管理下形成一个统一完整的系统,具有统一的用户与资源管理,安全性高。
3)文档共享方面,Reporting Services提供输出多种格式或Email等多种方式。
4)Reporting Services是32位结构,Impromptu是16位结构。数据量大时,使用Impromptu不稳定,会出现死机等。
与SAS的比较
SAS简介
SAS以统计分析软件包起家,在统计分析方面具备很强实力。发展到现在,SAS成为一个包含许多模块与功能的庞大的软件包,有两种使用方式:一是直接使用一些应用模块对存于SAS自己数据库中的数据进行多种多样的统计分析,这些应用模块包括统计分析STAT、财务分析ETS、运筹学OR等等。利用这些模块要求用户具备较高的数学尤其是统计分析专业知识,同时要具备较高的计算机操作能力。而且如果数据存在其它数据源中,需要利用SAS进行预先转换,这需要用户具备数据库专业知识。二是用户单位的计算机人员利用SAS的若干模块编程,为最终用户提供特定的应用系统。这样做的优点是可以为最终用户提供所要求的简便界面,但对技术人员的要求很高,培训时间也很长;而且灵活性差:一旦用户需求有改变,需要重新编程。SAS对数据的处理能力很强,但需要用户长时间的培训才能应用起来。
SAS与微软的企业级报表解决方案的比较
SAS与微软的企业级报表解决方案不是同类产品。
1)易用性相差很大,从而面向的用户对象不同
SAS功能很多,提供算法很多,因此易用性差,要求用户具备很扎实的数学基础、统计分析基础和计算机基础。
2)若利用SAS开发一套系统,优势是贴切需求,但对应用需求的提出、开发、实施和相关厂商的技术支持要求很高,并且开发系统的开放性、稳定性、可移植性、灵活性上比商业软件要差,尤其国内现阶段的开发手段相对比较落后。
与Pilot产品的比较
Pilot公司的产品包含服务器端和客户端,其服务器端的产品是多维数据库。客户端产品功能与Microsoft SQL Server Reporting Services相似,有如下不同之处:
l)Microsoft SQL Server Reporting Services不但支持关系型数据,而且支持来自第三方的多维数据。而Pilot的客户端产品只支持自己格式的多维数据。因此关系型数据用户必须先将数据导入Pilot的多维数据库中。用户工作量很大,相当于重新建立多维数据库,这样不如选择其它产品构建数据仓库,以Reporting Services做前端展现。
2)Pilot的多维数据的维的改动很难,不灵活。
3)Pilot客户端产品的易用性差,需要编程,工作量大,后期维护难。
与Oracle公司产品的比较
Oracle公司的Discoverer与Reporting Services功能近似,但两者也有一些区别。
Reporting Services支持的数据源更为广泛,可以是数据仓库,也可以是大型关系数据库如 Sybase、Oracle、Informix、SQL Server、DB2,单机数据库如Access、Foxpro、dBase等,多维数据库如Essbase、Express,常用应用软件如SAP R/3、Peoplesoft或Oracle的一些常用应用软件包,数据文件如Excel、TXT文件等,只要有相应的驱动即可。
Discoverer 3是针对关系数据库的,不适用于数据仓库和多维数据库,即使对Oracle自己的多维数据库Express也不支持。
Reporting Services对Oracle、SQL Server数据库有内部直接连接,其他较为流行的大型数据库Sybase、Informix、DB2可以通过OLEDB连接。Discoverer 3只能通过ODBC与数据库连接,因此查询数据的速度相对较慢。
Discoverer 3不能在一个文件中同时用表和统计图表示数据。
Discoverer 3不能在统计图上对数据进行钻取。
Discoverer 3的安全控制弱,不能定义用户及用户组的不同描述文件,既不能对哪些用户使用哪些模块进行控制,也不能对一些敏感数据进行进一步的控制。而Reporting Services可以对整个报表运行系统进行管理,对权限及某些数据进行控制。
Reporting Services还可以对报表集中分发、管理。例如用户可以规定刷新和发送报表的时间,如每小时、每天、每周、每月等刷新发送一次,或在现定时间只发送一次。通过一些简单的属性设置,还可以发送报表到Web页面上,并定时刷新。Discoverer 3没有对报表文件的集中批处理。
Discoverer3可以将文件转为HTML格式,但不支持定时刷新。
与Seagate公司产品的比较
公司简介
希捷公司由Alan Shugart创建于1979年,最初主要生产数据存储设备,在随后的发展过程中,逐渐由硬件设备厂商扩展为“数据技术公司”。1994到1997年间,相继收购了Crystal Service(Crystal Report,Crystal Info产品的创建公司)、Holistic Systems(Holos OLAP Server产品的创建公司)等公司来扩展其软件产品系列。
解决方案
产品线:
Seagate Info——包括查询、报表和多维分析,以及安全管理,其中包含Seagate Analysis模块;支持多种关系型数据、多维数据库;
Seagate Analysis——提供查询、报表和多维分析;
Seagate Crystal Reports——报表制作工具,提供查询、报表功能和API接口,可以在开发工具中集成;
Seagate Holos——OLAP Server。
相关比较
Seagate Info没有语义层技术,无论是使用Seagate Info还是Crystal Reports,都需要让用户自己从数据库选择表,构造SQL语句,因此用户必须了解数据库技术。
Crystal Reports主要是为专业计算机人员提供的报表制作工具,有较强的二次开发能力,因此它作为报表工具组件曾被集成到多种开发工具和应用程序中,如Microsoft Visual Studio.NET。但考虑到相关的使用许可,Crystal Reports是一个相对昂贵的选择。
与MicroStrategy公司产品的比较
MicroStrategy公司由Michael J.Savior创建于1989年,最初主要是作为一家决策支持领域的咨询公司,在随后的发展过程中,于1993年推出了第一个产品包:MicroStrategy Agent,以后相继推出一系列产品:MicroStrategy Web、MicroStrategy Broadcaster、MicroStrategy Telecaster等,现新产品包称为MicroStrategy7,致力于提供面向个性化的电子商务智能解决方案。
MicroStrategy解决方案的产品线包括:
MicroStrategy Intelligence Server——整个产品中的核心产品,多层体系结构下的中间应用服务器,为各种前端应用提供中间应用层能力。
MicroStrategy Web——Web体系下的应用服务器,提供基于Web体系下的查询、电算表格和多维分析能力。
MicroStrategy Agent——数据挖掘、应用开发工具,提供API接口。
MicroStrategy InfoCenter——企业级报表、门户工具。
MicroStrategy Architect——商业对象抽象层设计工具。
MicroStrategy Desktop——MicroStrategy Agent、MicroStrategy Architect和MicroStrategy Administration Utility的集成化应用环境
MicroStrategy有较弱的语义层技术,称之为企业商业体系,但能力比较弱,无法支持复杂的语义表达。
前端工具MicroStrategy Desktop的易用性较差,Businessobjects是唯一在决策支持工具中获得Microsoft office兼容认证的产品,易用性同Office类似,同时支持Microsoft VBA二次开发技术。
前端工具MicroStrategy Desktop对灵活查询支持较弱。
MicroStrategy不具备企业级特征,对企业级报表制作、企业信息共享与分发、企业级维护与管理支持较弱,管理复杂。
MicroStrategy整个产品学习和使用较为困难,需要更多的技术支持。
Reporting Services有支持XML的designer和应用模板(Wizard),使得用户只需按照自己的需求稍做调整,就可以达到应用效果,MicroStrategy没有提供类似能力。
MicroStrategy采用了N-tier体系结构,在构架的灵活性上与微软的企业级报表解决方案相当
使用Oracle9i的新特性Flashback Query恢复误删除数据
作者:
eygle |
English Version
链接:
http://www.eygle.com/archives/2005/06/eoaoracle9iaeai.html
下午接到研发工程师的电话,说误删除了部分重要数据,并且已经提交,需要恢复。
登陆到数据库上查看,由于是Oracle9iR2,首先尝试使用flashback query闪回数据。
首先确认数据库的SCN变化:
SQL> col fscn for 9999999999999999999
SQL> col nscn for 9999999999999999999
SQL> select name,FIRST_CHANGE# fscn,NEXT_CHANGE# nscn,FIRST_TIME from v$archived_log;
...................
NAME FSCN NSCN FIRST_TIME
------------------------------ -------------------- -------------------- -------------------
/mwarch/oracle/1_52413.dbf 12929941968 12929942881 2005-06-22 14:38:28
/mwarch/oracle/1_52414.dbf 12929942881 12929943706 2005-06-22 14:38:32
/mwarch/oracle/1_52415.dbf 12929943706 12929944623 2005-06-22 14:38:35
/mwarch/oracle/1_52416.dbf 12929944623 12929945392 2005-06-22 14:38:38
/mwarch/oracle/1_52417.dbf 12929945392 12929945888 2005-06-22 14:38:41
/mwarch/oracle/1_52418.dbf 12929945888 12929945965 2005-06-22 14:38:44
/mwarch/oracle/1_52419.dbf 12929945965 12929948945 2005-06-22 14:38:45
/mwarch/oracle/1_52420.dbf 12929948945 12929949904 2005-06-22 14:46:05
/mwarch/oracle/1_52421.dbf 12929949904 12929950854 2005-06-22 14:46:08
/mwarch/oracle/1_52422.dbf 12929950854 12929951751 2005-06-22 14:46:11
/mwarch/oracle/1_52423.dbf 12929951751 12929952587 2005-06-22 14:46:14
...................
/mwarch/oracle/1_52498.dbf 12930138975 12930139212 2005-06-22 15:55:57
/mwarch/oracle/1_52499.dbf 12930139212 12930139446 2005-06-22 15:55:59
/mwarch/oracle/1_52500.dbf 12930139446 12930139682 2005-06-22 15:56:00
NAME FSCN NSCN FIRST_TIME
------------------------------ -------------------- -------------------- -------------------
/mwarch/oracle/1_52501.dbf 12930139682 12930139915 2005-06-22 15:56:02
/mwarch/oracle/1_52502.dbf 12930139915 12930140149 2005-06-22 15:56:03
/mwarch/oracle/1_52503.dbf 12930140149 12930140379 2005-06-22 15:56:05
/mwarch/oracle/1_52504.dbf 12930140379 12930140610 2005-06-22 15:56:05
/mwarch/oracle/1_52505.dbf 12930140610 12930140845 2005-06-22 15:56:07
14811 rows selected.
|
当前的SCN为:
SQL> select dbms_flashback.get_system_change_number fscn from dual;
FSCN
--------------------
12930142214
|
使用应用用户尝试闪回
SQL> connect username/password
Connected.
|
现有数据:
SQL> select count(*) from hs_passport;
COUNT(*)
----------
851998
|
创建恢复表
SQL> create table hs_passport_recov as select * from hs_passport where 1=0;
Table created.
|
选择SCN向前恢复
SQL> select count(*) from hs_passport as of scn 12929970422;
COUNT(*)
----------
861686
|
尝试多个SCN,获取最佳值(如果能得知具体时间,那么可以获得准确的数据闪回)
SQL> select count(*) from hs_passport as of scn &scn;
Enter value for scn: 12929941968
old 1: select count(*) from hs_passport as of scn &scn
new 1: select count(*) from hs_passport as of scn 12929941968
COUNT(*)
----------
861684
SQL> /
Enter value for scn: 12927633776
old 1: select count(*) from hs_passport as of scn &scn
new 1: select count(*) from hs_passport as of scn 12927633776
select count(*) from hs_passport as of scn 12927633776
*
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed
SQL> /
Enter value for scn: 12929928784
old 1: select count(*) from hs_passport as of scn &scn
new 1: select count(*) from hs_passport as of scn 12929928784
COUNT(*)
----------
825110
SQL> /
Enter value for scn: 12928000000
old 1: select count(*) from hs_passport as of scn &scn
new 1: select count(*) from hs_passport as of scn 12928000000
select count(*) from hs_passport as of scn 12928000000
*
ERROR at line 1:
ORA-01466: unable to read data - table definition has changed
|
最后选择恢复到SCN为12929941968的时间点
SQL> insert into hs_passport_recov select * from hs_passport as of scn 12929941968;
861684 rows created.
SQL> commit;
Commit complete.
|
研发人员确认,已经可以满足需要,找回误删除部分数据,至此闪回恢复成功完成。
自定义file类型input框样式的方法 |
在WEB上传文件时,要用到上传框:
<input type="file" id="f" name="f">
这东东在IE(其他偶没经过测试)中是一个非常特殊的对象。
如果是您手动写入的或其他对象经过某些事件触发填入的值
由于安全问题,在进行提交表单时,往往会被清空,所以上传失败。
简单点说,除非你的鼠标亲自点到了上传框f上,IE才会给你上传文件!
哪怕你将 f 的onclick句柄赋给某个对象,如:
<input type="file" id="f" name="f">
<input onclick="f.click()" value="点击">
你 “点击” 后,同样会弹出文件选择对话框,可惜失望地:你照样不能上传文件!
怎么办呢?
看下这段:
<BODY onmousemove="f.style.pixelLeft=event.x-200;f.style.pixelTop=event.y-10;">
<input type="text"><input type="button" onmousemove="">
<input type="file" id="f" name="f" style="position:absolute;">
</BODY>
随便点击鼠标,看到效果了吧?
基于上面的思路,偶们就可以把它弄到一个button下面就OK了!!
<style>
input{border:1px solid green;}
</style>
<BODY>
<BR><BR><BR>
<form method="post" action="" enctype="multipart/form-data">
<input type="text" id="txt" name="txt">
<input type="button" onmousemove="f.style.pixelLeft=event.x-60;f.style.pixelTop=this.offsetTop;" value="请选择文件" size="30">
<input type="file" id="f" name="f" style="position:absolute;" size="1" onChange="txt.value=this.value"><BR>
<INPUT TYPE="submit">
</form>
</BODY>
为了达到真正模拟的效果,还得要把f给隐藏,加个不透明的alpha 滤镜即可,再加上 hidefocus 属性,隐藏f的虚线:
<style>
input{border:1px solid green;}
</style>
<BODY>
<BR><BR><BR>
<form method="post" action="" enctype="multipart/form-data">
<input type="text" id="txt" name="txt">
<input type="button" onmousemove="f.style.pixelLeft=event.x-60;f.style.pixelTop=this.offsetTop;" value="请选择文件" size="30" onclick="f.click()">
<input type="file" id="f" onchange="txt.value=this.value" name="f" style="position:absolute;filter:alpha(opacity=0);" size="1" hidefocus><BR>
<INPUT TYPE="submit">
</form>
</BODY>
可以看下opacity=0改为稍大些的效果。
OK了,现在你就可以控制它们的样式、位置了。。。
|
商业智能平台研究 (十) ETL 选型
ETL (Extract-Transform-Load的缩写,即数据抽取、转换、装载的过程)作为BI/DW(Business Intelligence)的核心和灵魂,能够按照统一的规则集成并提高数据的价值,是负责完成数据从数据源向目标数据仓库转化的过程,是实施数据仓库的重要步骤。如果说数据仓库的模型设计是一座大厦的设计蓝图,数据是砖瓦的话,那么ETL就是建设大厦的过程。在整个项目中最难部分是用户需求分析和模型设计,而ETL规则设计和实施则是工作量最大的,约占整个项目的60%~80%,这是国内外从众多实践中得到的普遍共识。
ETL工具从厂商来分为两种,一种是数据库厂商自带的ETL工具,Oracle warehouse builder 就是这种,另外一种是第三方工具提供商.开源世界也有一大票的ETL工具,功能各异,强弱不一,你可以从一下地址找到开源ETL工具的列表 http://www.manageability.org/blog/stuff/open-source-etl/view , 提醒一句,选用工具的时候要慎重,真的,千万要慎重,不管你是选用商业的ETL工具(一般比较贵),还是开源的工具,都要在你充分了解产品的特性才去选择,千万不要听某某人说什么这个工具好,就购买了那个工具,一定要自己了解产品.
开源的ETL工具的列表 (排名是乱排的)
kettle http://kettle.pentaho.org/ ,pentaho官方的ETL工具,是一个metadata-driven 的ETL工具,不需要自己写code .
talend http://www.talend.com/ talend是talend自己公司的产品,宣传的是全功能的Data Integration 解决方案,基于eclipse 平台,包括很多的模块来实现商业流程建模,数据流程建模,最后输出的是perl 和 Java code
jasperETL http://www.jaspersoft.com/ JasperETL是基于talend的平台,不太清楚有什么区别,jaspersoft开发的ETL产品.
Octopus http://www.enhydra.org/tech/octopus/index.html octopuc是enhydra 的一个ETL工具,enhydra是一个产品跨度非常大的开源站点,它有个开源的Workflow ,Shark ,pentaho就是使用的这个Workflow ,这个组织从workflow 到application server , 从ETL工具到application framework ,还有一些其他的中间件,octopus非常的原始,支持任何的JDBC数据源,用XML语言来定义的.也支持JDBC-DOBC ,和excel 和 access ,csv-files, XML files ,用Ant 和 JUnit 来创建表和测试.
CloverETL http://cloveretl.berlios.de/ CloverETL是提供给你一组API,用XML来定义ETL过程,同样支持JDBC数据源, CloverETL是开源的,但是它是没有图形界面的,它提供一个有图形界面的CloverGUI 来进行ETL的图形化开发过程,但是不是开源的,需要购买商业许可证.
KETL http://www.ketl.org/ 听说是几个前IBM员工做出来的ETL产品,
另外还有很多.不一一写介绍了,只列个表吧,
Joost http://joost.sourceforge.net/ 最近有个web2.0 网站也叫Joost,名字相同而已.
Xineo http://software.xineo.net/xil.jspx
BabelDoc http://sourceforge.net/projects/babeldoc
CB2XML http://sourceforge.net/projects/cb2xml
mec-eagle http://sourceforge.net/projects/mec-eagle/
Transmorpher http://transmorpher.inrialpes.fr/
XPipe http://xpipe.sourceforge.net/Articles/Miscellaneous/fog0000000018.html
DataSift http://www.datasift.org/
Xephyrus Flume http://www.xephyrus.com/flume/flume-intro.Prlx
Smallx https://smallx.dev.java.net/
Nux http://dsd.lbl.gov/nux/index.html
Netflux http://www.netflux.org/
OpenDigger https://opendigger.dev.java.net/
ServingXML http://servingxml.sourceforge.net/
Scriptella http://scriptella.javaforge.com/
ETL Integrator http://www.glassfishwiki.org/jbiwiki/Wiki.jsp?page=ETLSE
Jitterbit http://www.jitterbit.com/
Apatar http://www.apatar.com/
Spring Batch http://static.springframework.org/spring-batch/
大多数站点都是在sf.net上的.其中最后一个是Spring的,大名鼎鼎的Spring 也往ETL插一脚.实在是..........
根据talend官方介绍的数据,ETL工具的市场份额在2006好像是有160多亿美元.由于BI项目的成功,ETL的这一市场份额还会扩大,这也不难理解,为什么这么多的公司都在做ETL工具了.开源世界也免不了想要分一杯羹.再次废话一句,开源的东西你可以免费得到和使用,但是当你想应用到企业级开发的时候,省钱可不是唯一应该考虑的因素.还是那句话,慎重呀慎重.
下一篇介绍BI的基本概念.
开源BI系统简述
1. 概述
开源的BI项目在在2005年之前并没有太大的发展,到了2005年才开始呈现繁荣之势,并在2006年蓬勃发展。这些众多的BI项目从规模和对BI系统支撑的完善程度上来说,大体可以分为Framework、Stand-alone Tools和BI Suit三种类型。
l Framework
开源框架,这是在商业BI系统中所没有的。我们可以使用它们来构建自己的BI工具,或者增强和扩展我们的BI解决方案。
l Stand-alone Tools
独立的BI工具,这是开源项目中数量最多的一类。很多工具只侧重BI系统中的某个环节和方面,如ETL、Report、OLAP和Database等等。
l BI Suit
在统一的架构下提供了多种BI系统的特性的工具集合。就目前的情况看,不管是商业软件还是开源软件,还没有任何一个套件提供了完整的端到端的BI解决方案。这些开源的BI Suit是通过连接多个其他的组件和工具的方式形成套件的,由于BI系统涉及到的工具是非常多的,所以整合一套完整的BI解决方案是很困难的。
一个完整的BI解决方案中有多种工具来完成BI系统中各个阶段的工作。
数据抽取、转换和加载工具。优秀的ETL工具应该具有以下特性:
1、 Workflow Management, Job Execution and Scheduling Manager。能方便地定义流程并自动化执行ETL任务。
2、 Centralized Metadata Repository and Management。集中存储和管理符合业界标准的元数据。
3、 Data Profile and Validation。可以检验数据的质量。
4、 High Performance。在大负荷的任务执行中仍然有良好的性能。
5、 Scalable, Platform Independent。具有良好的弹性,支持多种操作系统和数据库系统,能操作多种异构的数据源。
6、 Open Architecture and API。具有开放的架构和易于使用的二次开发接口。
目前较为知名的开源ETL工具有:
1、 KETL,由具有IBM和KPMG背景的Kinetic Networks公司开发,现在已经有三年多的产品应用历史,成功应用于一些产品中,在点击流(ClickStream)分析应用中表现出色。KETL采用Plug-in的架构,使用Java开发。
2、 KETTLE,为一个元数据驱动的ETL工具。已经加入Pentaho。
3、 Clover ETL,为一个基于Java的ETL Framework,可以用来开发自己的ETL应用。
4、 Enhydra Octopus,为一个基于Java的ETL工具,使用JDBC来连接各种数据源,易于使用和部署。曾有人应用于电信网络资源分析系统中。
优秀的报表工具通常具有以下特性:
1、 支持多种数据源。
2、 直观的可视化设计器,简单易用的报表定制功能。
3、 方便的数据访问和格式化,丰富的数据呈现方式。
4、 符合数据呈现的通用标准,能和应用程序很好地进行结合。
5、 易于扩展和部署。
目前较为知名的开源报表工具有:
1、 JasperReports,一个优秀的Java报表工具,始于2001,现在JasperSoft公司持续开发和支持该工具。该工具类似于商业软件Crystal Report,支持PDF、HTML、XLS、CSV和XML文件输出格式,现在是Java开发者最常用的报表工具。
2、 OpenReports,提供基于web的灵活报表解决方案,通过浏览器自动生成动态PDF,XLS,HTMLCSV 和Chart报表,它是用Java开发的,使用JasperReports 作为报表引擎,利用到的开源技术有Hibernate,Veloctiy,Webwork。
3、 JFreeReport,现在是Pentaho的一部分,它是一个优秀的用来生成报表的Java类库。它为Java应用程序提供一个灵活的打印 功能并支持输出到打印机和PDF, Excel, HTML和XHTML, PlainText, XML和CSV文件中。
4、 Eclipse BIRT,是Eclipse下面的一个企业智能和报表 工具,能为J2EE的WEB应用程序创建漂亮醒目的PDF或者HTML格式的报表,它提供了核心的报表功能。
联机分析处理工具。目前开源的OLAP工具也分为MOLAP、ROLAP和HOLAP,优秀的OLAP工具通常有以下特性:
1、 良好的执行性能,能快速地进行分析处理工作。
2、 良好的适用性和可伸缩性。
3、 开放式接口和丰富的API。
目前较为知名的开源OLAP工具有:
1、 Mondrian,是Pentaho的一部分,为一个用Java开发的OLAP服务器,实现了MDX语言、XML解析和JOLAP规范,可以不写SQL就能分析存储于SQL 数据库的庞大数据集,可以封装JDBC数据源并把数据以多维的方式展现出来。
2、 JPivot,是一个JSP 自定制的标签库,可以绘制一个OLAP表格和图表。用户可以执行 典型的OLAP导航,如下钻,切片和方块。它使用Mondrian 作为其OLAP服务器。它使用WCF (Web Component Framework) ,基于XML/XSLT来渲染Web UI组件。JPivot在元数据缓存方面的过于简化的整体性初始化装载的做法将限制它只能处理很小的立方体(Cube)。
开源的数据库也有很多,大多数为关系型数据库,少数为应用于数据仓库环境做了专门的优化工作。Bizgres以PostgreSQL为基础进行了数据仓库环境下的优化,提高了分析查询性能。
下面列出相对成熟和完整,并且有借鉴意义的开源BI套件。
为GreenPlum公司主导的开源项目,和Sun公司达成合作关系。Bizgres为BI应用而对PostgreSQL做了优化,提高了大负荷的并行计算能力,在BI环境中,相对于普通的关系型数据库具有卓越的数据处理性能。Bizgres的数据库平台可以和KETL和JasperReports进行整合,从而形成一个BI套件:
1、 数据库:BI专业数据库Bizgres,或者大型应用中的高性能服务器Bizgres MPP,能比普通关系数据库快20倍
2、 ETL工具:KETL
3、 报表工具:JasperReports
是一个Java开发的Web应用,能对OLAP服务器、关系数据库和数据挖掘服务器进行分析和报表展示,非常易于使用和部署,界面美观友好,后续还将支持数据挖掘和ETL等。Openi主要包括:
1、 OLAP展示:JPivot
2、 报表工具:JFreeChart
3、 分析数据源连接器
是一个以工作流为核心的、强调面向解决方案而非工具组件的BI套件,整合了多个开源项目,目标是和商业BI相抗衡。它包括:
1、 工作流引擎:Shark and JaWE
2、 数据库:Firebird RDBMS
3、 集成管理和开发环境:Eclipse
4、 报表工具:Eclipse BIRT
5、 ETL工具:Enhydra/Kettle
6、 OLAP Server:Mondrian
7、 OLAP展示:JPivot
8、 数据挖掘组件:Weka
9、 应用服务器和Portal服务器:JBoss
10、 单点登陆服务及LDap认证:JOSSO
11、 自定义脚本支持:Mozilla Rhino Javascript脚本处理器
由上可见Pentaho是一个很完善的BI解决方案。Pentaho偏向于与业务流程相结合的BI解决方案,侧重于大中型企业应用。
SpagoBI 集成了Mondrain和JProvit,能够通过OpenLaszlo产生实时报表。SpagoBI使用java开发,不依赖于具体的操作系统,有很强的扩展能力。它主要包括:
1、 报表工具:JasperReports /Eclipse BIRT/ iReport
2、 OLAP Server:Mondrian
3、 OLAP展示:JPivot
4、 数据挖掘组件:Weka
5、 Map引擎:Geo
6、 ETL:BIE
7、 搜索引擎:Lucene
8、 Dashboard:OpenLaszlo
9、 Portal Server:JBoss/ Tomcat/ JOnAS
根据其Roadmap可以看出,SpagoBI将融入更多的BI功能,甚至BI之外的功能。
如何收缩数据文件的大小?
下面这个实验用于描述如何通过move tablespace来完成resize datafile。
HWM的概念就不在此阐述了。
测试环境为Oracle10g for Linux,其它版本的一样。
我们先创建两个表空间,分别为t_tbs和t_tbs1,分别有一个数据文件,大小都是5M
再创建一个test_user用户,给这个用户上述两个表空间的无限限额,并且设置默认表空间是t_tbs。
[zhangleyi@as zhangleyi]$ sqlplus / as sysdba
SQL*Plus: Release 10.1.0.2.0 - Production on Tue Apr 13 21:01:25 2004
Copyright (c) 1982, 2004, Oracle. All rights reserved.
Connected to:
Oracle Database 10g Enterprise Edition Release 10.1.0.2.0 - Production
With the Partitioning, OLAP and Data Mining options
SYS at orcl10>alter user test_user default tablespace t_tbs;
User altered.
SYS at orcl10>alter user test_user quota unlimited on t_tbs;
User altered.
SYS at orcl10>alter user test_user quota unlimited on t_tbs1;
User altered
用test_user登录,创建表
TEST_USER at orcl10>create table t_obj as select * from dba_objects where rownum<10000;
Table created.
TEST_USER at orcl10>insert into t_obj select * from t_obj;
9999 rows created.
TEST_USER at orcl10>/
19998 rows created.
TEST_USER at orcl10>/
insert into t_obj select * from t_obj
*
ERROR at line 1:
ORA-01653: unable to extend table TEST_USER.T_OBJ by 128 in tablespace T_TBS
TEST_USER at orcl10>commit;
Commit complete.
TEST_USER at orcl10>select sum(blocks) "Total Blocks",sum(bytes) "Total Size" from dba_extents where owner='TEST_USER' and segment_name='T_OBJ';
Total Blocks Total Size
------------ ----------
512 4194304
好,上面我们创建了一个表,并且插入了很多数据,通过dba_extents视图我们可以看到总共用的block熟和总共的大小。
下面我们用delete删除全部数据,并且插入新的9999条数据
TEST_USER at orcl10>delete from t_obj;
39996 rows deleted.
TEST_USER at orcl10>insert into t_obj select * from dba_objects where rownum<10000;
9999 rows created.
TEST_USER at orcl10>commit;
Commit complete.
TEST_USER at orcl10>select sum(blocks) "Total Blocks",sum(bytes) "Total Size" from dba_extents
2 where owner='TEST_USER' and segment_name='T_OBJ';
Total Blocks Total Size
------------ ----------
512 4194304
再次查看dba_extents视图,发现占用的空间并没有减少。
我们尝试resize这个数据文件,file#为6的是t_tbs表空间下面的数据文件
SYS at orcl10>alter database datafile 6 resize 4M;
alter database datafile 6 resize 4M
*
ERROR at line 1:
ORA-03297: file contains used data beyond requested RESIZE value
SYS at orcl10>alter database datafile 6 resize 4500000;
Database altered.
我们发现想resize到4M不可以,但是resize到4500000就可以了,因为上面查看出来的Total Size是4194304,这个值大于4M而小于4500000。
然后我们move这张表到t_tbs1表空间,这个表空间下面的数据文件file#是8
EST_USER at orcl10>alter table t_obj move tablespace t_tbs1;
Table altered.
TEST_USER at orcl10>select sum(blocks) "Total Blocks",sum(bytes) "Total Size" from dba_extents
2 where owner='TEST_USER' and segment_name='T_OBJ';
Total Blocks Total Size
------------ ----------
128 1048576
我们检查dba_extents视图,发现Total Size已经变化了,此时已经可以说明move表是会重新进行block的整理的,同时也重置了HWM。
下面我们resize这个数据文件。
SYS at orcl10>alter database datafile 8 resize 2M;
Database altered.
SYS at orcl10>host
[zhangleyi@as ORCL10]$ cd /oracle/oradata/ORCL10/datafile/
[zhangleyi@as datafile]$ ls -l
总用量 1419076
-rw-r----- 1 zhangleyi dba 20979712 4月 13 21:17 cattbs01.dbf
-rw-r----- 1 zhangleyi dba 157294592 4月 13 21:17 o1_mf_example_02p0gpoj_.dbf
-rw-r----- 1 zhangleyi dba 419438592 4月 13 21:20 o1_mf_sysaux_02p09kny_.dbf
-rw-r----- 1 zhangleyi dba 555753472 4月 13 21:17 o1_mf_system_02p09kno_.dbf
-rw-r----- 1 zhangleyi dba 20979712 4月 13 21:02 o1_mf_temp_02p0fzsd_.tmp
-rw-r----- 1 zhangleyi dba 62922752 4月 13 21:20 o1_mf_undotbs1_02p09kog_.dbf
-rw-r----- 1 zhangleyi dba 209723392 4月 13 21:17 o1_mf_users_02p09kqv_.dbf
-rw-r----- 1 zhangleyi dba 2105344 4月 13 21:21 TEST01.DBF(这是file#8)
-rw-r----- 1 zhangleyi dba 4513792 4月 13 21:20 test.dbf(这是file#6)
可以看到我们的目的已经达到了。
在真实应用中,我们可以将一个表空间中的所有object,全部move到一个新的表空间中,然后drop掉原来的表空间,再从磁盘上删除原来表空间中的数据文件。
至于如何得知HWM,我们可以通过analyze之后的数据字典得到,那么如果不进行analyze的话,我们也可以运行下面这个脚本。
这个脚本可以用于检查一个object占有的总共block数和处于HWM之上的block数,这当然也就知道了HWM是在什么位置。
DECLARE
v_total_blocks NUMBER;
v_total_bytes NUMBER;
v_unused_blocks NUMBER;
v_unused_bytes NUMBER;
v_last_used_extent_file_id NUMBER;
v_last_used_extent_block_id NUMBER;
v_last_used_block NUMBER;
BEGIN
dbms_space.unused_space('SCOTT','BIGEMP','TABLE',v_total_blocks,v_total_bytes,v_unused_blocks,v_unused_bytes,v_last_used_extent_file_id,v_last_used_extent_block_id,v_last_used_block);
dbms_output.put_line('Total Blocks: '||TO_CHAR(v_total_blocks));
dbms_output.put_line('Blocks above HWM: '||TO_CHAR(v_unused_blocks));
END;
/
Total Blocks: 256
Blocks above HWM: 0
PL/SQL procedure successfully completed
Executed in 0.01 seconds
我们都知道“瞎子摸象”的故事。不同的瞎子对大象的认识不同,因为他们只认识了自己摸到的地方。而企业如果要避免重犯这样的错误,那就离不开商务智能(BI)。专家认为,BI对于企业的重要性就像聪明才智对于个人的重要性。欧美企业的经验也证明,企业避免无知和一知半解危险的有效手段就是商务智能。商务智能旨在充分利用企业在日常经营过程中收集的大量数据和资料,并将它们转化为信息和知识来免除各种无知状态和瞎猜行为。
支持BI的开源工具数量众多,但是大多数的工具都是偏重某方面的。例如,CloverETL偏重ETL,JPivot偏重多维分析展现,Mondrian是OLAP服务器。而Bee、Pentaho和SpagoBI等项目则针对商务智能问题提供了完整的解决方案。
ETL 工具
ETL开源工具主要包括CloverETL和Octupus等。
(1)CloverETL是一个Java的ETL框架,用来转换结构化的数据,支持多种字符集之间的转换(如ASCII、UTF-8和ISO-8859-1等);支持JDBC,同时支持dBase和FoxPro数据文件;支持基于XML的转换描述。
(2)Octupus是一个基于Java的ETL工具,它也支持JDBC数据源和基于XML的转换定义。Octupus提供通用的方法进行数据转换,用户可以通过实现转换接口或者使用Jscript代码来定义转换流程。
OLAP服务器
(1)Lemur主要面向HOLAP,虽然采用C++编写,但是可以被其他语言的程序所调用。Lemur支持基本的操作,如切片、切块和旋转等基本操作。
(2)Mondrian面向ROLAP包含4层:表示层、计算层、聚集层、存储层。
● 表示层:指最终呈现在用户显示器上的以及与用户之间的交互,有许多方法来展现多维数据,包括数据透视表、饼、柱、线状图。
● 计算层:分析、验证、执行MDX查询。
● 聚集层:一个聚集指内存中一组计算值(cell),这些值通过维列来限制。计算层发送单元请求,如果请求不在缓存中,或者不能通过旋转聚集导出的话,那么聚集层向存储层发送请求。聚合层是一个数据缓冲层,从数据库来的单元数据,聚合后提供给计算层。聚合层的主要作用是提高系统的性能。
● 存储层:提供聚集单元数据和维表的成员。包括三种需要存储的数据,分别是事实数据、聚集和维。
OLAP客户端
JPivot是JSP风格的标签库,用来支持OLAP表,使用户可以执行典型的OLAP操作,如切片、切块、上钻、下钻等。JPivot使用Mondrian服务器,分析结果可以导出为Excel或PDF文件格式。
数据库管理系统
主要的开源工具包括MonetDB、MySQL、MaxDB和PostgreSQL等。这些数据库都被设计用来支持BI环境。MySQL、MaxDB和PostgreSQL均支持单向的数据复制。BizGres项目的目的在于使PostgreSQL成为数据仓库和 BI的开源标准。BizGres为BI环境构建专用的完整数据库平台。
完整的BI开源解决方案
1.Pentaho 公司的Pentaho BI 平台
它是一个以流程为中心的、面向解决方案的框架,具有商务智能组件。BI 平台是以流程为中心的,其中枢控制器是一个工作流引擎。工作流引擎使用流程定义来定义在 BI 平台上执行的商务智能流程。流程可以很容易被定制,也可以添加新的流程。BI 平台包含组件和报表,用以分析这些流程的性能。BI 平台是面向解决方案的,平台的操作是定义在流程定义和指定每个活动的 action 文档里。这些流程和操作共同定义了一个商务智能问题的解决方案。这个 BI 解决方案可以很容易地集成到平台外部的商业流程。一个解决方案的定义可以包含任意数量的流程和操作。
BI平台包括一个 BI 框架、BI 组件、一个 BI 工作台和桌面收件箱。BI 工作台是一套设计和管理工具,集成到Eclipse环境。这些工具允许商业分析人员或开发人员创建报表、仪表盘、分析模型、商业规则和 BI 流程。Pentaho BI 平台构建于服务器、引擎和组件的基础之上,包括J2EE 服务器、安全与权限控制、portal、工作流、规则引擎、图表、协作、内容管理、数据集成、多维分析和系统建模等功能。这些组件的大部分是基于标准的,可使用其他产品替换之。
2.ObjectWeb
该项目近日发布了SpagoBi 1.8版本。SpagoBi 是一款基于Mondrain+JProvit的BI方案,能够通过OpenLaszlo产生实时报表,为商务智能项目提供了一个完整开源的解决方案,它涵盖了一个BI系统所有方面的功能,包括:数据挖掘、查询、分析、报告、Dashboard仪表板等等。SpagoBI使用核心系统与功能模块集成的架构,这样在确保平台稳定性与协调性的基础上又保证了系统具有很强的扩展能力。用户无需使用SpagoBI的所有模块,而是可以只利用其中的一些模块。
SpagoBI使用了许多已有的开源软件,如Spago和Spagosi等。因此,SpagoBI集成了 Spago的特征和技术特点,使用它们管理商务智能对象,如报表、OLAP分析、仪表盘、记分卡以及数据挖掘模型等。SpagoBI支持BI系统的监控管理,包括商务智能对象的控制、校验、认证和分配流程。SpagoBI采用Portalet技术将所有的BI对象发布到终端用户,因此BI对象就可以集成到为特定的企业需求而已经选择好的Portal系统中去。
3.Bee项目
该项目是一套支持商务智能项目实施的工具套件,包括ETL工具和OLAP 服务器。Bee的ETL工具使用基于Perl的BEI,通过界面描述流程,以XML形式进行存储。用户必须对转换过程进行编码。Bee的ROLAP 服务器保证多通SQL 生成和强有力的高速缓存管理(使用MySQL数据库管理系统)。ROLAP服务器通过SOAP应用接口提供丰富的客户应用。Web Portal作为主要的用户接口,通过Web浏览器进行报表设计、展示和管理控制,分析结果可以以Excel、PDF、PNG、PowerPoint、 text和XML等多种形式导出。
Bee项目的特点在于:
● 简单快捷的数据访问;
● 支持预先定义报表和实时查询;
● 通过拖拽方式轻松实现报表定制;
● 完整报表的轻松控制;
● 以表和图进行高质量的数据展示。
/*
*标 题: Sybase IQ for Data Warehouse 培训总结(二)
*作 者: xuwedo
*文章属性: 原创
*时 间: 2007-03-11
*来 源: http://blog.csdn.net/xuwedo2003/
*链 接: http://blog.csdn.net/xuwedo2003/
* Copyright (c) 2007
* All rights reserved.
* 如有转载,请注明作者及本信息
* 文件名称:
* 文件标识:
* 摘 要: Sybase IQ, Data Warehouse
* 开始时间: 2007-03-11
*
* 当前版本: 1.0
* 作 者: xuwedo2001@sohu.com
* 相关信息:
* 完成日期: 2007年3月11日
*/
正文:
(一)
怎样创建数据库
1
、DBspace是分配到IQ数据库的驱动空间的逻辑名
2
、一个新IQ数据库最多可以有2047个DBspace。
3
、Sybase的各种类型DBspace的限制:
(1)、Catalog Store最大为1TB。
(2)、IQ Store 和IQ temp Store 在裸设备上没有大小限制,在文件系统中为4TB。
4
、IQ Store 和IQ temp Store可以扩大或从数据库删除。
5
、创建数据库:
CREATE DATABASE full_path db_name[[TRANSACTION] {LOG ON [log_file_name][MIRROR mirror_file_name]}]
IQ PATH iq_file_anem
[IQ SIZE iq_file_size]
[IQ RESERVE sizeMb]
TEMPORARY PATH temp_file_name
[TEMPORARY SIZE temp_db_szie]
[TEMPORARY RESERVE sizeMB]
[MESSAGE PATH message_file_name]
例子1:
CREATE DATABASE
‘
d:\\mydb\\mydb.db
’
IQ PATH
‘
d:\\iqmain\\mydb01.iq
’
IQ SIZE 200
TEMPORARY PATH
‘
e:\\iqtem\\mydb01.iqtmp
’
TEMPORARY SIZE 200
IP PAGE SIZE 65536
6
、可以用Sybase Central创建数据库(在图形环境下)。
7
、可以用sp_iqstatus来查看当前数据库的详细信息。
8
、删除一个DBspace
(1)、一个IQ store如果存有数据的话,则不能被删除。(可以使用Utility数据库将一个DBspace的数据移动到另一个DBspace中去,再删除)。
(2)、一个IQ temporary Store仅在为空时才可以被删除。
9
、删除一个数据库的语法:
DROP DATABASE db_filename
例子:DROP DATABASE ‘d:\\mydb\\mydb.db
’
10
、怎样才能看到所有的DBspace的物理文件名?
用以下命令:Select * from sysqifile
(二)
设置服务器和数据选项
1、
数据库的启动参数均可以写在“.cfg”文件中。
2、
启动服务器的语法:
start_asiq server-switchs database_file
其中:“server-switchs”可以为:
-c
:缓存大小,默认windows为32M,Unix为48M。
-gp:Catalog store
页大小。
-gm
:服务器允许的连接数。
-n
:IQ server的名字。如果有两个“-n”选项,则第一个是IQ server的名字,第二个“-n”为IQ 数据库的名字。
-gc
:checkpoint时间间隔。默认为20,推荐为6000。
-gr
:最大的恢复时间。默认为2。
-ti
:客户端超时时间。默认为4400分钟。
-tl
:默认网络超时时间。默认120秒。
-iqmc
:主缓存大小,单位:M。
-iqtc
:临时缓存大小,单位:M。
注:主缓存:临时缓存=2:3
在默认情况下,Sybase IQ server使用2338端口。
3、
使用SET OPTION命令更变数据库的配置:
(1)、语法:
SET [TEMPORARY] OPTION
[user_id. | PUBLIC.]option_name = [option_value]
其中的“option_name”可以是:
Force_No_Scroll_Cursors=‘on’(默认为“off”)禁止缓存用户的查询结果。
Query_Temp_Space_Limit=0(默认为2000M),设置临时缓存的最大值。0表示不限制。
Public.Query_Plan=‘off’(默认为“on”)禁止将用户的查询计划打印到IQ Message File中,因为查询计划可以会使之大小迅速增加。
例子:
SET OPTION public.Force_NO_Scroll_Cursors=
’
no
’
4、
查看数据库的所有被改动过的(即非默认值)选项,用存储过程:sp_iqcheckoptions。
(三)
Sybase IQ
索引和数据类型
1
、Sybase IQ的9种索引类型:
(1)、FP(Fast Projection)此索引为默认的索引形式,在创建表时系统自动设置此索引。
特点:用于SELECT、LIKE ‘%sys%’、SUM(A+B)、JOIN操作等语句。
此类型索引也是唯一可用于BIT数据类型的索引。
FP
索引可以优化索引,将小于255的唯一值的索引压缩到1字节中,将小于65537的唯一值索引压缩到2字节中。
(2)、LF(Low Fast)基于平衡树的结构,存储唯一值小于1500个的索引,是最快的索引类型。可以用作唯一索引。
特点:用于=、 !=、IN、NOT IN查询参数。
MIN
()、MAX()、COUNT()、Group By、JOIN等。
(3)、HNG(High Nongroup)基于位的优化索引,适合于数字索引。用于范围查找和求合计算。
特点:Rangs、Between、MIN()、MAX()、SUM()、AVG()等。
(4)、HG(High Group) 基于平衡树的结构,存储唯一值大于1500个的索引,是最快的索引类型。可以用作唯一索引。
特点:同LF索引的特点。
(5)、CMP(compare)仅用于比较一个表中的两个列的比较。
特点:<、 =、 >、 <= 、>=
(6)、WD(Word),仅用于索引数据类型为WORD的列。
特点:‘CONTAINS’、LIKE操作(但没有‘%’)。
例子:
Select count(*) from Customer where address contains(
‘Main’)
(7)、DATE(date)仅用于日期类型的列。
(8)、DTTM(Datetime)仅用于日期时间类型的列。
(9)、TIME(Time)仅用于时间类型的列。
例子:
Select * from sales where DATAPART(YEAR,dales_dt) = 2007
Select * from sales where sales_dt>=‘2003-01-01 08:00:00’
(四)
创建表和索引
1
、创建表:
例子1:
CREATE TABLE employee(
Emp_id int NOT NULL
,lname varchar(30) NULL
,fname varchar(30) NULL
,salary money NULL)
例子2:
CREATE TABLE stores(
Store_id char(4) NOT NULL
,store_name varchar(20) NOT NULL
,store_address varchar(40) NOT NULL
,UNIQUE(store_id))
例子3:
CREATE TABLE products(
Product_code char(5) NOT NULL
,product varchar(40) NOT NULL
,price money NULL
PRIMARY KEY (products_code))
例子4:
CREATE TABLE sales(
Sales_code char(10) PRIMARY KEY
,sales_date DATE NOT NULL
,product_code char(5) NOT NULL
,FOREIGN KEY fk1(product_code) REFERENCES product (product_code))
例子5:
SELECT * INTO co_residential_customer from customer where 1=2
2
、用Sybase Central 可以在图形界面下创建表。
3
、删除表:DROP TABLE tablename
4
、删除表中的所有内容:TRUNCATE TABLE [owner.]table_name
5
、创建视图,语法:
CREATE VIEW [owner.]view_name[(column-name[,
…
])]
AS select-without-order-by
[WITH CHECK OPTION]
例子1:
CREATE VIEW sd_customer AS SELECT * FROM customer
WHERE sheng =
‘
SD
’
例子2:
CREATE VIEW emp_dept
AS SELECT emp_home,emp_fname,dept_name
From Employee,Department
WHERE Employee.dept_id = Department.dept_id
5
、用Sybase Central 可以在图形界面下创建视图。
6
、创建索引:
CREATE [UNIQUE] [index-type] INDEX
Index_name ON
[owner.]table_name (column_name[,
…
])
[{IN | ON} DBSpace_name]
[NOTIFY integer]
[DELIMITED BY
‘
separators-string
’
]
[LIMIT maxwordsize-integer]
例子1:
CREATE HG INDEX
Cust_customer_id ON
Cutomer(customer_id)
例子2:
CREATE CMP INDEX price_compare
ON orders(purchase_price,list_cost)
7
、删除索引:
DROP INDEX [[owner.]tablename.]index_name
例子:
DROP INDEX cust_customer_id
(五)
Sybase IQ
内存配置
1、
IQ
从单一的一个内存池中分配内存。
2、
从操作系统层面来看,IQ Server的内存是由堆组成。
3、
Buffer
:内存中的一块区域,它存储了写入数据库或从数据库中读取的未解压的数据。
4、
IQ Page Size
:IQ Server中每一个内存页的大小。
5、
IQ Page Size/16=BLOCK SIZE
6、
在启动服务器时,可以用参数-c来指定服务器缓存的初始大小。在所有平台中,这个值最大为256M。
7、
IQ Buffer
有两种类型:
(1)、主Buffer缓存:IQ Store的Buffer。(占总大小的40%)
(2)、临时Buffer:IQ temporary Buffer。(占总大小的60%)
8、
在Sybase IQ server中一个活动的用户大约占用10M内存,一个非活动用户大约占5M内存。
使用sys,以sysdba权限登录:
c:\sqlplus /nolog
SQL>conn / as sysdba
SQL> show parameter processes;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
aq_tm_processes integer 1
db_writer_processes integer 1
job_queue_processes integer 10
log_archive_max_processes integer 1
processes integer 150
SQL> alter system set processes=300 scope = spfile;
系统已更改。
SQL> show parameter processes;
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
aq_tm_processes integer 1
db_writer_processes integer 1
job_queue_processes integer 10
log_archive_max_processes integer 1
processes integer 150
SQL> create pfile from spfile;
文件已创建。
重启数据库,OK!
因为前些日子在一个项目中用到了iText,稍有收获,便总结于此,以供他人所需。
iText是一个比较底层的pdf库,很多项目的pdf操作都是以它为基础的。像spring,以及另一个比较有名的报表工具jasperreports。简单的pdf报表输出用它比较合适,比较复杂的话使用起来就比较困难了,你要手工编写太多的代码。
比较好的是iText网站上提供相当多的示例代码,比较容易入门。我这里只说一些在它的文档里并没有直接讲到的东西。
1 关于Document
Document的几种构造函数:
public Document();
public Document(Rectangle pageSize);
public Document(Rectangle pageSize,
int marginLeft,
int marginRight,
int marginTop,
int marginBottom);
下面两种比较有用,如果是你想定义纸张大小和边缘的时候。对于Margin,iText上提到“You can also change the margins while you are adding content. Note that the changes will only be noticed on the NEXT page. If you want the margins mirrored (odd and even pages), you can do this with this method: setMarginMirroring(true). ”不过,对于table似乎并不好使。table并不会了理会你设定的margin,如果想改变它的magin还是需要去改变它的宽度(setWidth)。
2 pdf表单
使用PdfStamper是可以填充pdf表单的,这样就给出了一种很好的报表生成思路。
word制作报表样式-->acrobat转pdf-->itext填充数据-->输出pdf
这做非常简单,因为可以比较容易的控制pdf的样式。我对于Java的报表工具了解的并不多,不过在jasperreports,即使用GUI工具做一个样式比较复杂的报表也不是怎么容易。比如有那种斜线的表头,比较花哨的嵌套表格。这样的情况还是比较多见的,客户不会关系你实现起来是否困难。不过想要使用这种方式也有不足的地方。首先是acrobat把word转化成pdf的时候,格式总是保持不好,特别的是字体。然后是文件的体积这样生成的pdf会比直接用iText生成的pdf文件大很多,acrobat在pdf里加入了太多无用的信息。初次使用iText填充Adobe Designer生成的pdf表单时会有点小麻烦。在Designer中设计了一个name的text文本框的绑定名为name。照着iText中例子使用使用PdfStamper的setField方法去这样写form.setField("name", "XXXX");并不会成功。原因是Adobe Designer生成的表单名都是具有层次的,它可能是这个样子form1[0].#subform[0].name[0]。不过我们可以用一个方法把它们列出来,只要做一次就知道结构了,可以使用类似下面的代码:
PdfReader reader = new PdfReader("form.pdf");
PdfStamper stamp = new PdfStamper(reader, new FileOutputStream("registered_flat.pdf"));
AcroFields form = stamp.getAcroFields();
for (Iterator it = form.getFields().keySet().iterator(); it
.hasNext();) {
System.out.println(it.next());
}
如果直接用iText编程生成的表单就不会有这样的问题,设定的什么名字就是什么名字。
3 表单元素
pdf并不像html那样具有良好清晰的结构,而是一个有层次的文档类型。在它的maillist里,作者说明了iText虽然可以操作现存的pdf文件但是没办法去还原它的结构的。没办法像html一样,能从一个pdf文件获得一个清晰的“源文件”的。关于层次,可以从iText上得到详细的讲述,获取去看看pdf规范。表单和普通文本是不在一个层上的。没办法适用对待文本表各一样把它们简单的add进Document对象。获取一个cb直接去用绝对定位的方法可以加入表单元素,不过很多的时候因为排版并不能那么简单的去做。就是在html中布局一样可以使用表格定位。想把一个表单元素加入cell,要借助cell的setCellEvent方法。以一个checkbox为例。新建一个类CheckBoxForm,实现PdfPCellEvent接口。需要实现一个cellLayout的方法。
public void cellLayout(PdfPCell cell, Rectangle position, PdfContentByte[] canvases)
position可以好好利用,它包含当前cell的位置信息,你可以用它来确定自己checkbox的位置。
position.top()-position.bottom()就能得到高position.right()-position.left()可以得到长,如果需要这两个值得花可以如此计算。下面的代码就是定义一个宽度为a的checkbox的rectangle 。它在cell中水平居中,垂直也居中。
float bo = (position.top()-position.bottom()-a)/2;
float ao = (position.right()-position.left()-a)/2;
Rectangle rectangle = new Rectangle(position.left() + ao, position
.bottom() + bo, position.left() +ao+ a, position.bottom()+ bo + a);
然后把它加入Document
RadioCheckField tf = new RadioCheckField(writer, rectangle, fieldname,
"f");
tf.setCheckType(RadioCheckField.TYPE_SQUARE);
tf.setBorderWidth(1);
tf.setBorderColor(Color.black);
tf.setBackgroundColor(Color.white);
try {
PdfFormField field = tf.getCheckField();
writer.addAnnotation(field);
} catch (IOException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
其它的元素与此类似。
4 PdfPTable和Table
说不上哪种更好用,有时候不能不使用PdfPTable。可惜它只有setColspan方法,没有setRowspan。嵌套的时候也有区别,PdfPTable是用addcell()加入嵌套表的,table则有一个更明了的方法insertTable()。PdfPTable想进行设置border之类的操作要先获得一个默认cell,
pdfPTableName.getDefaultCell().setBorder(Rectangle.NO_BORDER);//设置无框的表
另外在PdfPTable中,一些修饰属性会因为设置的时机不正确而没有效果。如,适用cell的构造函数加入了文本,在cell的setVerticalAlignment()fangfa去设定垂直对齐方式就不会有效。还有一个有意思的不同是table默认外边框是加粗的,而PdfPTable则一样粗细。
5 字体
iText的例子有很多足够用,给出一些pdf的字体名称和编码,如果想使用内嵌字体的话。
语言 PDF 字体名
简体中文 STSong-Light
繁体中文 MHei-Medium
MSung-Light
日语 HeiseiKakuGo-W5
HeiseiMin-W3
韩语 HYGoThic-Medium
HYSMyeongJo-Medium
字符集 编码
简体中文 UniGB-UCS2-H
UniGB-UCS2-V
繁体中文 UniCNS-UCS2-H
UniCNS-UCS2-V
日语 UniJIS-UCS2-H
UniJIS-UCS2-V
UniJIS-UCS2-HW-H
UniJIS-UCS2-HW-V
韩语 UniKS-UCS2-H
UniKS-UCS2-H
必须要有Asian的包才可以用,也可以使用TrueType字体。
ps:因为隔了一段时间了,所以有些现在一时也想不起来了,也可能会有理解的错误。另外,适用iText的时候自己最好抽象一下,可能会省不少力气。
前言:本文不是专门讲述Web Service技术的,读者在阅读本文之前需要具备一定的SOAP和Web Service知识基础,同时对Weblogic Server的使用也应该熟悉。如果要自己动手实践本文的例子,就需要安装Weblogic Server 81,尽管本文是以weblogic server 81为测试环境,但是针对weblogic server 7下也是差不多的。本文只是起个抛砖引玉的作用,如果想深入研究Web Service的开发,还需要参考、学习相关的资料,包括Weblogic Service的相关文档。
一、概述
在JBuilder中也支持开发基于weblogic的web service,不过实际上在JBuilder下开发web service也是基于ant任务来生成和构造web service的。但是,当初笔者在一个项目中使用JBuilder下自动生成构造ant脚本生成的web service时碰到了一个问题,通过JBuilder生成的web service,如果你的web service调用接口中存在一个或者多个String类型参数的时候,在生成的wsdl文件中对该接口的参数命名不会按照你的后端组件对应方法中参数的名字,而是以string、string0、string1…等形式命名的。而在那个项目中需要在Delphi环境中调用web service,问题就出现了,string在Delphi中是关键词,产生了冲突,不能进行调用。于是笔者决定采用自编写ant脚本的方式来生成和构造web service来解决前面所述Delphi调用的问题。
BEA Weblogic提供了一些Ant任务,用来帮助开发者生成、构造一个Web服务的重要部件,(例如:序列化类、客户端jar支持库、以及web-services.xml描述文件),并且把一个Weblogic Web 服务的所有部分打包成一个可部署的EAR文件。
BEA Weblogic所提供的Web服务Ant任务,支持从实现了Web Service接口的普通JAVA源文件和EJB jar生成Web Service部件,也支持从WSDL描述文件生成,同时支持基于http/https传输协议和JMS传输协议的Web Service。在这一节我们只讲述通过基于一个普通JAVA类作为后端组件来实现的Web Service,传输协议使用http(基于https的方式将在后述关于Web Service安全的部分讲述)。
二、使用Weblogic ant工具生成Web Service
我们先建立D:\wls_ws_demo的工作目录,在此目录下分别建立src、build、ddfiles、webapp、test目录。具体用途后文会涉及到。
首先我们编写一个实现了两个Web Service接口的普通JAVA类:
package com.wnetw.ws.demo;
public class HelloWorldWS{
public String sayHello(){
return "Hello World!";
}
public String welcome(String name){
return "Hello " + name + ",Welcome to WebService!";
}
}
上面两个方法就不需要解释了,很简单。把此类按封装包一致的路径放置在src目录下。
下面是本示例中ant脚本文件内的属性设置:
<property name="build.compiler" value="modern"/>
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="war.file" value="${build.dir}/
applications/HelloWorldWS.war" />
<property name="ear.file" value="${build.dir}/
applications/HelloWorldWS.ear" />
<property name="clients.lib" value="${build.dir}/
clientslib/HelloWorldWS_clients.jar"/>
<property name="bea.home" value="D:/bea"/>
<property name="wls.dir" value="${bea.home}/weblogic81/server"/>
<property name="wlslib.dir" value="${wls.dir}/lib"/>
<property name="wlsext.dir" value="${wls.dir}/ext"/>
<property name="namespace" value="http://www.wnetw.com/demo/"/>
<path id="classpath">
<dirset dir="${build.dir}/classes">
<include name="**"/>
</dirset>
<fileset dir="${wlslib.dir}">
<include name="**/weblogic.jar"/>
<include name="**/webservices.jar"/>
</fileset>
</path>
<property name="javac.fork" value="no"/>
<property name="javac.debug" value="no"/>
<property name="javac.optimize" value="on"/>
<property name="javac.listfiles" value="yes"/>
<property name="javac.failonerror" value="yes"/>
上面的属性应该不是很难理解,关键的是对于bea weblogic server安装目录和构造生成文件的路径说明,其次是对classpath的设置,需要用到的两个weblogic库是weblogic.jar和webservices.jar。
接着我们看看我们在本节中使用的Weblogic提供的Ant任务:
1、source2wsdd
source2wsdd Ant任务最基本的功能是根据我们编写的普通JAVA类源文件生成一个Web Service所必需的两个部件:web-services.xml和.wsdl描述文件。
下面是针对上面HelloWorldWS.java对应的Ant脚本:
<target name="genwsdd">
<source2wsdd javaSource="${src.dir}/com/wnetw/ws/
demo/HelloWorldWS.java"
ddFile="${build.dir}/wsddfiles/web-services.xml"
wsdlFile="${build.dir}/wsddfiles/HelloWorldWS.wsdl"
serviceURI="/HelloWorldWS">
<classpath refid="classpath"/>
</source2wsdd>
</target>
属性说明
javaSource:指定web service的实现后端组件,这里是普通JAVA类com.wnetw.ws.demo HelloWorldWS.java。注意属性里面是对源文件目录路径设置,而不是包路径。
ddFile:生成的web service部署描述符文件web-services.xml的存放路径。
wsdlFile:生成的.wsdl文件存放的路径和名字。
serviceURI:客户应用程序调用此Web服务的URL中的Web Service URI部分。注意:必须以“/”开头。例如:/ HelloWorldWS 。同时这个URI属性也会成为生成的web-services.xml 部署描述符文件中<web-service>元素的uri属性。
例如:本机访问本web service例子的url是http://localhost:7001/ WSDemo/ HelloWorldWS
上面的serviceURI属性就指定了上述url中的/ HelloWorldWS这一部分。
2、clientgen
clientgen可以用来生成JAVA环境下客户端应用调用一个Web Service客户端jar支持库。可以通过wsdl文件来生成,也可以通过一个包含web service实现的ear文件来生成。
下面是clientgen ant任务的脚本示例:
<target name="genclient">
<clientgen wsdl="${build.dir}/wsddfiles/HelloWorldWS.wsdl"
packageName="com.wnetw.ws.demo.client"
clientJar="${clients.lib}"
keepGenerated="false">
<classpath refid="classpath"/>
</clientgen>
</target>
这里采用从前面source2wsdd任务生成的wsdl文件来生成客户端jar支持库。通过wsdl属性指定。
3、war
这是ant提供的标准任务,这里与其他普通的war包有一点区别是,需要把web-services.xml文件打包到war中去。
说明:需要准备web.xml,后面对于安全设置的时候还需要weblogic.xml文件,这里先都打包进去,这些文件都需要提前编辑准备好:
---Web.xml---
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web
Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<mime-mapping>
<extension>wsdl</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
</web-app>
---weblogic.xml---
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web
Application 7.0//EN" "http://www.bea.com/servers/wls700
/dtd/weblogic700-web-jar.dtd">
<weblogic-web-app>
</weblogic-web-app>
这个文件没设置,在后面关于安全的处理里面需要这里配置角色映射。
下面是war ant脚本示例:
<target name="genwar">
<war destfile="${war.file}" webxml="webapp/WEB-INF/web.xml">
<classes dir="${build.dir}/classes"/>
<webinf dir="${build.dir}/wsddfiles">
<include name="web-services.xml"/>
</webinf>
<webinf dir="webapp/WEB-INF">
<include name="weblogic.xml"/>
</webinf>
</war>
</target>
4、ear
这也是ant标准任务,需要注意的是必须提前编写application.xml文件,下面针对本文例子的application.xml文件:
<!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc.//DTD J2EE
Application 1.3//EN' 'http://java.sun.com/dtd/application_1_3.dtd'>
<application>
<display-name></display-name>
<module>
<web>
<web-uri>HelloWorldWS.war</web-uri>
<context-root>WSDemo</context-root>
</web>
</module>
</application>
说明:context-root元素指定此Web Service所在Web应用的应用根。
例如:本机访问本web service例子的url是http://localhost:7001/
WSDemo/ HelloWorldWS
上面的context-root元素就指定了上述url中的WSDemo这一部分。
下面是本文例子的ear ant任务脚本:
<target name="genear">
<ear destfile="${ear.file}" appxml="ddfiles/application.xml">
<fileset dir="${build.dir}/applications" includes="*.war"/>
</ear>
</target>
核心的ant任务说明完了,下面是完整的ant脚本文件:
--- build_wls_all.xml---
<project name="wls_ws_demo" default="all" basedir=".">
<property name="build.compiler" value="modern"/>
<property name="src.dir" value="src"/>
<property name="build.dir" value="build"/>
<property name="war.file" value="${build.dir}/applications/
HelloWorldWS.war" />
<property name="ear.file" value="${build.dir}/applications/
HelloWorldWS.ear" />
<property name="clients.lib" value="${build.dir}/clientslib/
HelloWorldWS_clients.jar"/>
<property name="bea.home" value="D:/bea"/>
<property name="wls.dir" value="${bea.home}/weblogic81/server"/>
<property name="wlslib.dir" value="${wls.dir}/lib"/>
<property name="wlsext.dir" value="${wls.dir}/ext"/>
<property name="namespace" value="http://www.wnetw.com/demo/"/>
<path id="classpath">
<dirset dir="${build.dir}/classes">
<include name="**"/>
</dirset>
<fileset dir="${wlslib.dir}">
<include name="**/weblogic.jar"/>
<include name="**/webservices.jar"/>
</fileset>
</path>
<property name="javac.fork" value="no"/>
<property name="javac.debug" value="no"/>
<property name="javac.optimize" value="on"/>
<property name="javac.listfiles" value="yes"/>
<property name="javac.failonerror" value="yes"/>
<target name="all" depends="clean,mdir,compile,genwsdd,
genclient,genwar,genear"/>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="mdir">
<mkdir dir="${build.dir}"/>
<mkdir dir="${build.dir}/classes"/>
<mkdir dir="${build.dir}/applications"/>
<mkdir dir="${build.dir}/clientslib"/>
<mkdir dir="${build.dir}/wsddfiles"/>
</target>
<target name="compile">
<javac encoding="GBK" srcdir="${src.dir}" destdir=
"${build.dir}/classes">
<classpath refid="classpath"/>
</javac>
</target>
<target name="genwsdd">
<source2wsdd javaSource="${src.dir}/com/wnetw/ws/
demo/HelloWorldWS.java"
ddFile="${build.dir}/wsddfiles/web-services.xml"
wsdlFile="${build.dir}/wsddfiles/HelloWorldWS.wsdl"
serviceURI="/HelloWorldWS">
<classpath refid="classpath"/>
</source2wsdd>
</target>
<target name="genclient">
<clientgen wsdl="${build.dir}/wsddfiles/HelloWorldWS.wsdl"
packageName="com.wnetw.ws.demo.client"
clientJar="${clients.lib}"
keepGenerated="false">
<classpath refid="classpath"/>
</clientgen>
</target>
<target name="genwar">
<war destfile="${war.file}" webxml="webapp/WEB-INF/web.xml">
<classes dir="${build.dir}/classes"/>
<webinf dir="${build.dir}/wsddfiles">
<include name="web-services.xml"/>
</webinf>
<webinf dir="webapp/WEB-INF">
<include name="weblogic.xml"/>
</webinf>
</war>
</target>
<target name="genear">
<ear destfile="${ear.file}" appxml="ddfiles/application.xml">
<fileset dir="${build.dir}/applications" includes="*.war"/>
</ear>
</target>
</project>
运行ant生成Web Service:
打开命令行窗口,转到工作目录D:\wls_ws_demo下,在此目录下先运行D:\bea\weblogic81\server\bin\setWLSEnv.cmd(此cmd文件具体路径与你的weblogic platform81实际安装目录相关)进行环境设置,然后运行:D:\bea\weblogic81\server\bin\ant.bat -buildfile build_wls_all.xml。
运行结束,出现“BUILD SUCCESSFUL”,那就代表OK了。转到工作目录下的build目录,你就会看到HelloWorldWS.ear这个文件。
三、测试Web Service
本节将讲述对前一节里生成的Web Service HelloWorldWS进行测试。
启动Weblogic Server,进入Weblogic Server控制台,在Deployments->Applications下部署上节生成的HelloWorldWS.ear。
1、通过Weblogic自动生成的测试主页测试
部署成功后,在浏览器中输入http://localhost:7001/WSDemo/HelloWorldWS访问Weblogic Server默认生成的上述HelloWorldWS Web Service的测试主页。
如下图:

图上列出了HelloWorldWS Web Service上的两个方法:welcome和sayHello。
点击welcome连接进入wecome方法的测试页,如下图:

在上述页面输入“老Z”,提交后就会看到如下图页面:

测试的结果跟上节中的HelloWorldWS.java实现此方法的结果是一样的。测试sayHello方法跟上面过程一样。
在测试主页中还能看到在JAVA环境下,基于clientgen ant任务生成的jar客户端stub支持库调用此HelloWorldWS Web服务的代码示例。
2、使用JAVA程序调用Web Service
下面实际编写一个java测试程序来调用上述Web Service。
--- HelloWorldWSTest.java ---
import com.wnetw.was.demo.client.*;
public class HelloWorldWSTest {
public static void main(String[] args){
try{
HelloWorldWS_Impl ws = new HelloWorldWS_Impl("http://localhost:7001
/WSDemo/HelloWorldWS?WSDL");
HelloWorldWSPort port = ws.getHelloWorldWSPort();
System.out.println(port.welcome(“老Z”));
}catch(Exception e){
e.printStackTrace();
System.out.println(e);
}
}
}
编译、运行上述测试程序的时候首先需要weblogic客户端webservice支持库webserviceclient.jar,还需要前面clientgen ant任务生成的jar客户端stub支持库HelloWorldWS_clients.jar。在下面的编译、运行测试程序的ant脚本中可以看到在classpath中引入了上述两个jar。
编译、运行测试程序的ant脚本如下:
<project name="wls_ws_demo" default="all" basedir=".">
<property name="build.compiler" value="modern"/>
<property name="bea.home" value="D:/bea"/>
<property name="wls.dir" value="${bea.home}/weblogic81/server"/>
<property name="wlslib.dir" value="${wls.dir}/lib"/>
<property name="wlsext.dir" value="${wls.dir}/ext"/>
<path id="classpath">
<fileset dir="${wlslib.dir}">
<include name="**/webserviceclient.jar"/>
</fileset>
<fileset dir="build/clientslib">
<include name="**/HelloWorldWS_clients.jar"/>
</fileset>
<pathelement path="test"/>
</path>
<property name="javac.fork" value="no"/>
<property name="javac.debug" value="no"/>
<property name="javac.optimize" value="on"/>
<property name="javac.listfiles" value="yes"/>
<property name="javac.failonerror" value="yes"/>
<target name="all" depends="compile,run"/>
<target name="compile">
<javac encoding="GBK" srcdir="test" destdir="test">
<classpath refid="classpath"/>
</javac>
</target>
<target name="run">
<java classname="HelloWorldWSTest">
<classpath refid="classpath"/>
</java>
</target>
</project>
运行上述ant脚本后,如果成功的话,应该得到类似下图结果:

3、在VB下调用Web Service
下面我在VB环境下来调用下这个Web Service,笔者使用的是Visual Basic 6.0,要在VB下调用Web Service需要先安装Microsoft SOAP toolkit。
新建一个VB工程,然后把Microsoft Soap Type Library引用进来,如下图:

新建一个form1,添加一个按钮command1,在form1源代码窗口中整个拷贝如下代码:
Dim soap As MSSOAPLib.SoapClient
Private Sub Command1_Click()
MsgBox soap.sayHello()
MsgBox soap.welcome("老Z")
If Err <> 0 Then
MsgBox "Web Service调用失败: " + Err.Description
End If
End Sub
Private Sub Form_Load()
Set soap = New MSSOAPLib.SoapClient
On Error Resume Next
Call soap.mssoapinit("http://localhost:7001/WSDemo/HelloWorldWS?WSDL")
If Err <> 0 Then
MsgBox "初始化SOAP失败: " + Err.Description
End If
End Sub
然后运行工程,点击窗口上的按钮就开始调用前面部署的Web Service(确保Weblogic Server在运行中),成功的话会得到如下图的两个MessageBox:


四、使用非内建数据类型
前面例子中的Web Service方法中使用的参数和返回值都是String,类似String,int等数据类型是属于Weblogic web service所支持的内建类型,关于Weblogic web service所支持的内建数据类型请参见:http://e-docs.bea.com/wls/docs81/webserv/implement.html#1054236
所支持的XML非内建类型请参见:
http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1060805
所支持的Java非内建数据类型请参见:
http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1068595
WebLogic Server能够对内建数据类型进行XML与Java表示之间的转换。但是,如果你在web service操作中使用了非内建数据类型,那么你必须提供以下信息,以确保weblogic server能够正确地进行转换。
- 用于处理数据的Java表示与XML之间的转换的序列化类;
- 包含了数据类型Java表示的Java类;
- 数据类型的XML Schema表示;
- web-services.xml部署描述文件中的数据类型映射信息。
Weblogic Server中带有servicegen和autotype Atn任务,这两个任务通过对web service的无状态EJB或者Java类后端组件的内省,从而自动生成上述部件。上述Ant任务能够处理许多非内建数据类型,所以大多数的开发者并不需要手工生成上述的部件。
有时,你可能也需要手工去创建非内建数据类型部件。因为你的数据类型可能很复杂,以致Ant任务不能正确生成前述部件。你也可能想要自己控制数据在XML和Java表示之间的转换过程,而不依赖Weblogic Server所使用的缺省转换程序。
本节将演示在Weblogic web service中如何处理非内建(自定义)的数据类型。
我们先编写一个数值Bean类UserInfo,如下:
package com.wnetw.ws.demo;
import java.util.*;
public class UserInfo{
private Integer userid;
private String username;
private String sex;
private Date birthday;
private int level;
private double salary;
private telcodes list;
public UserInfo(){}
public Integer getUserid(){
return userid;
}
public void setUserid(Integer userid){
this.userid = userid;
}
public String getUsername(){
return username;
}
public void setUsername(String username){
this.username = username;
}
public String getSex(){
return sex;
}
public void setSex(String sex){
this.sex = sex;
}
public Date getBirthday(){
return birthday;
}
public void setBirthday(Date birthday){
this.birthday = birthday;
}
public int getLevel(){
return level;
}
public void setLevel(int level){
this.level = level;
}
public double getSalary(){
return salary;
}
public void setSalary(double salary){
this.salary = salary;
}
public List getTelcodes(){
return telcodes;
}
public void setTelcodes (List telcodes){
this. telcodes = telcodes;
}
}
在前文中的后端组件类HelloWorldWS.java中增加一个方法:
public UserInfo getUserInfo(Integer userid){
UserInfo userinfo = new UserInfo();
userinfo.setUserid(userid);
userinfo.setUsername("李泽林");
userinfo.setSex("男");
userinfo.setBirthday(new Date());
userinfo.setLevel(2);
userinfo.setSalary(1000.51);
List telcodes = new ArrayList();
telcodes.add("123");
telcodes.add("321");
userinfo.setTelcodes (telcodes);
return userinfo;
}
在这个方法里,返回值是UserInfo,这是我们前面定义的数值Bean,由于这是非内建类型,而且也不属于受支持的非内建类型,所以需要我们必须自己来处理XML和UserInfo Java表示数据类型之间的转换。
在本文的例子中,我们使用Weblogic Server的autotype任务来做这件事情。我们先在build目录建一个autotype目录,然后在前文中ant完整脚本中的compile任务之后增加下述脚本:
<target name="gentypeinfo">
<autotype javatypes="com.wnetw.ws.demo.UserInfo"
targetNamespace="${namespace}"
packageName="com.wnetw.ws.demo"
destDir="${build.dir}/autotype"
keepGenerated="true">
<classpath refid="classpath"/>
</autotype>
<copy todir="${build.dir}/classes">
<fileset dir="${build.dir}/autotype">
<include name="**/*.class"/>
</fileset>
</copy>
</target>
autotype Ant任务有几个常用属性,下面简要说明下:
javatypes:需要进行类型转换的非内建(自定义)数据类型java类,注意取值是全限定类名,不需要带上java或者class扩展名。如果存在多个这样的数据类型类,用逗号“,”隔开;
targetNamespace:在对数据类型映射到XML的时候使用的命名空间;
packageName:生成的序列化相关类的封装包;
destDir:生成的序列化相关类存放的目录;
keepGenerated:是否保留中间java源文件,取值为:true或者false。
关于autotype任务的详细信息请参考:
http://e-docs.bea.com/wls/docs81/webserv/anttasks.html#1080062
上述ant任务成功运行后就会生成build/autotype/目录下生成types.xml文件以及按包封装的数据转换类的源文件和class文件。
由于增加了自定义数据类型,所以我们还得更新source2wsdd任务脚本,以下是增加了自定义数据类型处理后的source2wsdd任务脚本:
<target name="genwsdd">
<source2wsdd javaSource="${src.dir}/com/wnetw/ws/demo/HelloWorldWS.java"
typesInfo="${build.dir}/autotype/types.xml"
ddFile="${build.dir}/wsddfiles/web-services.xml"
wsdlFile="${build.dir}/wsddfiles/HelloWorldWS.wsdl"
serviceURI="/HelloWorldWS">
<classpath refid="classpath"/>
</source2wsdd>
</target>
跟以前的脚本相比,增加了typesInfo属性来指定自定义数据类型的XML描述文件。
增加了对自定义数据类型支持后的完整脚本请参考本文代码下载文件。
按照第一节所述方法运行ant脚本build_wls_all.xml后,再部署build\applications\目录下的HelloWorldWS.ear。就可以按照以前说的方法进行测试了。
这一次在Weblogic Server自动生成的web service测试主页:
http://localhost:7001/WSDemo/HelloWorldWS
可以发现多了一个叫getUserInfo的方法连接,进入此方法的调用测试页面,调用此方法后就可以看到此web service方法的调用结果,以下是结果截图:

从调用测试结果页面可以看到,这一次的Return Value是:
com.wnetw.ws.demo.UserInfo@82d235
这正是我们的web service方法返回值类型类型的一个对象,图中的下面也以SOAP消息的形式描述了调用的输入和返回结果。
我们接着修改测试类HelloWorldWSTest.java,如以下:
import com.wnetw.ws.demo.client.*;
import com.wnetw.ws.demo.UserInfo;
public class HelloWorldWSTest {
public static void main(String[] args){
try{
HelloWorldWS_Impl ws = new HelloWorldWS_Impl("http://localhost:7001
/WSDemo/HelloWorldWS?WSDL");
HelloWorldWSPort port = ws.getHelloWorldWSPort();
System.out.println(port.sayHello());
System.out.println(port.welcome("老Z"));
System.out.println("开始测试自定义数据类型的返回值。。。");
UserInfo info = port.getUserInfo(100);
System.out.println(info);
System.out.println(info.getUsername());
}catch(Exception e){
e.printStackTrace();
System.out.println(e);
}
}
}
看看以下代码好像有点问题,UserInfo info = port.getUserInfo(123);我们在HelloWorldWS.java类中定义的对应方法是getUserInfo(Integer userid),参数是Integer的,但是上述测试类代码中却使用int类型,这是正确的。我们可以把clientgen任务中的keepGenerated属性设为true,把自动生成的java源代码保留下来,build成功后,我们打开build\clientslib目录下HelloWorldWS_clients.jar文件中的com.wnetw.ws.demo.client.HelloWorldWSPor.java源文件,可以看到如下代码:
package com.wnetw.ws.demo.client;
/**
* Generated interface, do not edit.
*
* This stub interface was generated by weblogic
* webservice stub gen on Sat Sep 17 16:11:21 CST 2005 */
public interface HelloWorldWSPort extends java.rmi.Remote{
/**
* welcome
*/
public java.lang.String welcome(java.lang.String name)
throws java.rmi.RemoteException ;
/**
* sayHello
*/
public java.lang.String sayHello()
throws java.rmi.RemoteException ;
/**
* getUserInfo
*/
public com.wnetw.ws.demo.UserInfo getUserInfo(int userid)
throws java.rmi.RemoteException ;
}
其中的getUserInfo(int userid)方法是使用int参数的!如果你使用Integer类型参数,反而会编译通不过!只能认为这是weblogic server ant任务对数据类型映射的具体实现了,如果你仔细看了本节前面所述对java内建数据类型的支持列表,那么也是好理解的,因为java数据类型到XML Schema数据类型映射中,java中的int和java.lang.Integer都映射到了int。所以web service服务端接收到的SOAP消息中只会是XML Schema int类型,无法区分客户端使用的会是int或者java.lang.Integer,所以在ant工具根据wsdl文件自动生成客户端支持类的时候就只能使用int了,没法区分int或者java.lang.Integer。这是个有意思的问题^-^一不小心也许会在你工作中浪费不必要的时间。当然如果有必要,你完全可以手动修改、甚至完全自己来生成客户端支持库和数据类型转换类。不过嘛,除了出于研究和特殊情况外这是没有必要的。
我们接着看看HelloWorldWS_clients.jar中还有什么东西,发现有个language_builtins这样的包,从包名也许你能猜到这是干什么的,是对java语言内建数据类型处理的包,此包下面是util包,里面有ListCodec.class类。看看我们的UserInfo类,里面使用了List类,这个包里面的类正是用来处理java.util.List数据类型的,java.util.List属于Weblogic server web service所支持的非内建数据类型,也就是说不需要通过autotype明确来标志生成相关的数据转换类和类型信息。但是,java.util.List又有别于int、java.lang.String等wls web service所支持的内建类型,对于java.util.List等受支持的非内建类型由ant任务自动生动相关数据类型处理信息,不需要手工干预。对比来看,int、java.lang.String等wls web service所支持的内建类型是直接映射,不需要数据类型转换相关类。Java.util.List最终映射成了XML Shema SOAP Array类型。其他类型请参考:http://e-docs.bea.com/wls/docs81/webserv/assemble.html#1068595
运行修改后的build_wls_test.xml脚本,成功的话应该得到如下图类似结果:

增加了自定义数据类型后,VB测试客户端的处理也得增加一些处理来测试返回值为UserInfo的web service方法,如下面代码:
Set Nodes = soap.getUserInfo(100)
MsgBox Nodes(0).nodeName + ":" + Nodes(0).Text
MsgBox Nodes(1).nodeName + ":" + Nodes(1).Text
MsgBox Nodes(2).nodeName + ":" + Nodes(2).xml
MsgBox Nodes(3).nodeName + ":" + Nodes(3).Text
MsgBox Nodes(4).nodeName + ":" + Nodes(4).Text
MsgBox Nodes(5).nodeName + ":" + Nodes(5).Text
MsgBox Nodes(6).nodeName
完整VB测试客户端代码请见本文附带下载代码。
五、配置Web Service安全
Weblogic Web Service包括三种不同概念的安全设置:
- 消息层安全:对SOAP消息中数据的数字签名或者加密;
- 传输层安全:使用SSL来保证客户应用与Web Service之间连接的安全性;
- 访问控制:指定何种用户、组、角色被允许访问该Web Service。
在这里我们主要针对访问控制概念上的安全处理。
Weblogic Web Service最终是作为一个标准的J2EE ear打包文件提供进行部署的,其中包含了一个war包,也就是说web service是以web应用的形式提供并部署的,这从前面的章节就可以看出。
所以,针对web service的访问控制安全处理与J2EE中对于Web资源的访问控制处理是一样的。具体的说就是对特定Web资源增加安全约束。具体配置就是通过在Web应用部署描述符web.xml增加相应的元素:需要进行安全约束的资源集合、授权访问的角色列表、对用户数据的安全约束、角色映射等信息。
在这里,我们需要对前面用到的web.xml文件进行修改,如下所示:
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web
Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
<mime-mapping>
<extension>wsdl</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
<security-constraint>
<display-name>SecurityConstraint</display-name>
<web-resource-collection>
<web-resource-name>HelloWorldWS</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>GET</http-method>
<http-method>POST</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>testrole</role-name>
</auth-constraint>
</security-constraint>
<security-role>
<role-name>testrole</role-name>
</security-role>
</web-app>
然后运行ant构造脚本,部署ear。部署成功后,你会在weblogic server运行命令行窗口中看到如下类似信息:
<2005-9-24 下午22时03分45秒 CST> <Warning> <HTTP> <BEA-101304>
<Webapp: ServletC
ontext(id=11680063,name=WSDemo,context-path=/WSDemo),
the role: testrole defined
in web.xml has not been mapped to principals in
security-role-assignment in web
logic.xml. Will use the rolename itself as the principal-name.>
这是因为没有进行角色映射,所以直接使用角色名作为用户名了。这只是一个警告信息,没有关系。后面将会讲述怎么进行角色映射。
然后进入weblogic server Console,新建一个名叫testrole的用户。接着在左侧目录树中一次展开Deployments-Applications- HelloWorldWS- WSDemo,在WSDemo节点上鼠标右击,选择Define Security Policy…


在Policy Condition项选择User name of the caller,点击增加,在接着出现的窗口中填入testrole,OK之后,点击上图页面中的Apply。接下来就可以跟以前一样测试了。
浏览器中输入http://localhost:7001/WSDemo/HelloWorldWS,这个时候会弹出来一个登陆框,如下图:

现在可以看到,访问控制起作用了。输入testrole以及拟增加用户的时候指定的密码后,就能进入到和以前一样的测试主页了。
上面那种使用角色名和用户名对应的方式显示在实际应用中是不方便的,因为具体会有什么样的用户会访问此web service在构建时是不确定的。我们可以使用角色映射的方式来避免这个问题。
进行角色映射需要在weblogic.xml文件中配置,下面我将对testrole映射到一个group,weblogic.xml文件如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE weblogic-web-app PUBLIC "-//BEA Systems, Inc.//DTD Web
Application 7.0//EN" "http://www.bea.com/servers/wls700/dtd
/weblogic700-web-jar.dtd">
<weblogic-web-app>
<security-role-assignment>
<role-name>testrole</role-name>
<principal-name>test_group</principal-name>
</security-role-assignment>
</weblogic-web-app>
在web.xml文件中指定的授权访问角色testrole映射到了test_group,也就是说test_group组中的所有用户都有权访问。这样一来用户授权和实现就解耦了。
使用ant脚本重新构建,然后部署ear。接着进入weblogic server console,删除testrole用户,新建test_group组,新建一个叫test_user的用户,并指派给test_group组。接着按照前面一样Define Security Policy,这一次在Policy Condition部分选择Caller is member of the group,然后点Add进入授权group指定页面,输入test_group,点增加-点OK,回到Define Security Policy主页面,点击Apply就好了。
然后我们在浏览中进入http://localhost:7001/WSDemo/HelloWorldWS,弹出登陆框,这一次我们可以使用test_group中的任何成员用户来登陆了,前面例子是test_user。这样在以后,需要分配新的用户授权访问此Web Service的时候就知需要在Cosole在test_group中增加一个成员就行了,不需要重新构建web service了。
加入了访问控制后,在调用web service的时候就需要提供授权凭证了,下面是需要增加的代码信息:
- JAVA客户
HelloWorldWS_Impl ws = new HelloWorldWS_Impl("http://localhost:7001/WSDemo/HelloWorldWS?WSDL");
HelloWorldWSPort port = ws.getHelloWorldWSPort("test_user","test_user");
改成
HelloWorldWS_Impl ws = new HelloWorldWS_Impl();
//因为加入了访问控制,所以对于http://localhost:7001/WSDemo/HelloWorldWS?WSDL的访问也需授权,所以我们使用缺省构建器,这样就会使用客户端支持库jar中的静态wsdl文件了。
HelloWorldWSPort port = ws.getHelloWorldWSPort(“test_user”, “test_user”);
//后面的参数是test_user的密码,根据你具体的密码更改
- VB客户端
Call soap.mssoapinit("HelloWorldWS.wsdl")
‘由于http://localhost:7001/WSDemo/HelloWorldWS?WSDL需要授权访问,所以我们把脚本生成的HelloWorldWS.wsdl文件直接拷贝到VB项目目录下,使用这个静态文件来初始化soap对象。
‘后面增加下属代码
soap.ConnectorProperty("AuthUser") = "test_user"
soap.ConnectorProperty("AuthPassword") = "test_user"
在我们运行上述两个测试程序的时候会发现调用不成功。原因接下来进行说明。
我们打开工作目录中下build\wsddfiles这个目录中的HelloWorldWS.wsdl这个文件,在最后可以看到下面的service元素内容,如下:
<service name="HelloWorldWS">
<port name="HelloWorldWSPort"
binding="tns:HelloWorldWSPort">
<soap:address location="http://pls.set.the.end.point.address/">
</soap:address>
</port>
</service>
问题就出在这里,soap:address节点的location属性有问题,因为客户端soap初始化后,会使用这个URL来调用本wsdl中描述的web service操作,显然这个地址与我们部署的实际地址是不一样的。所以我们把location属性改为我们部署的web service实际访问URL:
http://localhost:7001/WSDemo/HelloWorldWS。这就是上述两个测试程序不能正确运行的原因。
笔者也没有找到如何在生成web service部件时设置此正确属性的方法,正是因为需要修改上述wsdl文件属性,所以我们需要把build脚本分成两部分来执行,先生成相关部件,然后修改wsdl文件的上述属性,最后才进行打包和客户端支持库的生成,把build_wls_all.xml分开成了build_wls_1.xml和build_wls_2.xml两个build脚本文件。在运行完后build_wls_1.xml修改上述属性,然后运行build_wls_2.xml即可。
部署成功后,就可以测试上面两个调用例子了,注意把修改好的wsdl文件拷贝到VB项目目录中去。
如果在web.xml中<security-constraint>元素里加入下述项目
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
那就会强制要求客户端使用https进行访问,其他更多信息请参考J2EE中Web应用安全方面的资料。
六、杂项设置
本节要说的实际也是安全性方面的问题,只不过和一般的安全性概念不一样,这里讲的是针对在生产部署环境下的考虑。
1、定制主页
在生产环境下,一般是不允许公开web service默认主页的。其次由于通过主页:
http://localhost:7001/WSDemo/HelloWorldWS?WSDL
访问的wsdl描述符文件是动态生成,同时加入了访问控制安全约束后,客户程序访问此文件也存在问题,所以通常在生产环境下将禁止访问web service默认主页以及动态wsdl文件,可以使用专门的静态web站点来提供必要的信息,以及通过静态web站点来发布wsdl。
要禁用默认主页以及wsdl文件,需要在web-services.xml描述符文件中进行设置。如下所示在web-service节点中加入下面两个属性:
exposeWSDL="False"
exposeHomePage="False"
修改后类似下面示例:
。。。
<web-services>
<web-service name="HelloWorldWS"
targetNamespace="http://tempuri.org/"
uri="/HelloWorldWS"
exposeWSDL="False"
exposeHomePage="False">
。。。
这个修改也需要在运行build_wls_1.xml之后进行修改,才能保证应用打包部署后使得此设置生效。
在禁止了默认主页和WSDL文件后,为了保证web service更新后不需要更新客户程序的文件,所以最好建立一个静态web站点来发布web service,也就是发布wsdl文件。在用于发布wsdl的web应用中需要在web.xml中加入以下的Mime类型映射:
<mime-mapping>
<extension>wsdl</extension>
<mime-type>text/xml</mime-type>
</mime-mapping>
2、启用https协议
除了上一节中在web.xml中加入
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
来启用https通讯协议外,还可以在通过在web-service.xml文件中,在web-service(注意不是web-services)节点中加入下面属性:
protocol="https"
上述属性能保证客户端必须使用https来访问本web service。
七、结束语
本文只是针对很小的一方面来讲述基于weblogic ant任务开发web service的,只是起个抛砖引玉的作用。其次,通过本文你也能了解到web service的本质过程,无论通过什么工具来开发,本质上都是生成基础部件,然后打包。如果需要全面了解weblogic server web service开发方面的知识请参考bea文档:
http://e-docs.bea.com/wls/docs81/webservices.html
同时本文使用的环境是window 2000 server和weblogic platform8.1英文版。
本文示例项目代码可从以下地址下载:
http://www.wnetw.com/jclub_resources/technology/attachfiles/wls_ws_demo.rar
时间:2005-12-23
作者:
Russell Miles浏览次数:
3616
本文关键字:
Spring,
Java,
AOP,
crosscutting,
面向方面编程,
横切,
Cuckoo's Egg模式 在本系列的第一部分,我介绍了如何实现面向方面领域的“HelloWorld”:跟踪和记录方面。利用Spring框架所提供的面向方面编程(Aspect-Oriented Programming,AOP)功能,您看到了如何使用before-、after-和基于异常的通知,以及如何使用基于正则表达式的简单切入点。跟踪和记录方面提供了非常不错的上手例子,而本文将进一步介绍一种新的通知形式:around通知。
比起第一部分中介绍的那些通知类型,around形式的通知是一种更具侵入性也更强大的面向对象概念。本文将描述around通知的每个特性,以便您可以在自己的Spring AOP应用程序中正确地使用它。在本文最后,我将向您展示如何使用around通知来截获和改变应用程序中各个特性相互作用的方式,以便实现Cuckoo's Egg(杜鹃的蛋)面向方面设计模式。
概述Spring AOP、IoC和代理
在第一部分,我们快速浏览了Spring的一些AOP特性,而没有阐明Spring如何实现AOP的细节。要理解Spring框架如何运转,尤其是它如何实现其AOP功能,首先您要明白,Spring是一个依赖于控制反转(Inversion of Control,IoC)设计模式的轻量级框架。
注意:本文的目的不是要深入介绍IoC模式,介绍IoC只是为了使您明白该设计模式是如何影响Spring AOP实现的。有关IoC模式的更详细的介绍请参见本文末尾的参考资料。
IoC设计模式的出现已经有一段时间了。一个最明显的例子就是J2EE架构本身。随着企业开发尤其是J2EE平台的出现,应用程序开始依赖于由外部容器所提供的一些特性,比如bean创建、持久性、消息传递、会话以及事务管理。
IoC引入了一个新概念:由组件构成的框架,它与J2EE容器有许多类似之处。IoC框架分离了组件所依赖的功能,并且,根据Sam Newman文章中的说法,提供了“连接组件的‘胶水’”。
对组件所依赖特性的控制 被反转 了,这样外部框架就可以尽可能透明地提供这些特性了。IoC模式真正意识到了从传统的由依赖于功能的组件来负责这些功能,到由独立的框架来配置和提供这些功能的方式转变。
图1显示了一些构成IoC模式的不同组件角色的例子。

图1. 没有对BusinessLogic bean应用方面时的顺序图.
图字:
Component:组件
Provides Facilities:提供功能
Relies on and conforms to:依赖于并服从
Manages the services the framework can then use to provide facilities:管理框架随后可以用来提供功能的服务
Service:服务
Your Component:您的组件
IoC Framework:IoC框架
External services:外部服务
IoC模式使用3种不同的方法来解除组件与服务控制的耦合:类型1、类型2和类型3。
- 类型1:接口注入
这是大部分J2EE实现所使用的方法。组件显式地服从于一组接口,带有关联的配置元数据,以便允许框架对它们进行正确的管理。
- 类型2:Setter注入
外部元数据被用来配置组件相互作用的方式。在第一部分中,我们就是使用这种IoC方法利用springconfig.xml文件来配置Spring组件的。
- 类型3:构造函数注入
组件(包括构造组件时要用的参数)注册到框架,而框架提供组件的实例以及所有要应用的指定功能。
IoC在组件开发和企业开发中越来越受欢迎。IoC的实际例子包括传统的J2EE解决方案,比如:JBoss、Apache基金会的Avalon项目以及本文的Spring框架。实际上,Spring框架构建于IoC模式的基础上是为了帮助将它的轻量级功能注入到它的相关应用程序的组件中。
那么IoC对于Spring AOP有何意义呢?Spring的IoC特性是使用IoC springconfig.xml配置文件对应用程序应用方面的推动因素之一。springconfig.xml配置文件通知Spring框架运行时有关应用程序的组件要被注入的功能类型的信息,所以自然轻量级的AOP功能就以同样的方式应用了。然后Spring使用代理模式围绕现有的类和bean实现指定的AOP功能。
图2显示了Spring及其IoC框架如何使用代理对象提供AOP功能(根据springconfig.xml文件中的IoC配置。)

图2. springconfig.xml配置文件改变了Spring框架IoC,以便随后向第一部分中的一个顺序图提供AOP代理(单击图像查看大图)
在本系列下面的部分,您将不断看到现在包含在顺序图中的代理对象。这只是为了说明对于Spring AOP来说没有“魔法”,实际上只有一个面向对象设计模式的良好例子。
回到AOP:使用around通知的积极方面
在第一部分,您看到了如何使用Spring AOP来实现跟踪和记录方面。跟踪和记录都是“消极”方面,因为它们的出现并不会对应用程序的其他行为产生影响。它们都使用了消极的before和after形式的通知。
但是如果您希望改变应用程序的常规行为呢?例如说,您希望重写一个方法?这样的话,您就需要使用更积极的around形式的通知。
第一部分的简单例子应用程序包括IbusinessLogic接口、BusinessLogic类和MainApplication类,如下所示:
public interface IBusinessLogic
{
public void foo();
}
public class BusinessLogic
implements IBusinessLogic
{
public void foo()
{
System.out.println(
"Inside BusinessLogic.foo()");
}
}
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MainApplication
{
public static void main(String [] args)
{
// Read the configuration file
ApplicationContext ctx =
new FileSystemXmlApplicationContext(
"springconfig.xml");
//Instantiate an object
IBusinessLogic testObject =
(IBusinessLogic) ctx.getBean(
"businesslogicbean");
// Execute the public
// method of the bean
testObject.foo();
}
}
要对一个BusinessLogic类的实例彻底重写对foo()方法的调用,需要创建around通知,如下面的AroundAdvice类所示:
import org.aopalliance.intercept.MethodInvocation;
import org.aopalliance.intercept.MethodInterceptor;
public class AroundAdvice
implements MethodInterceptor
{
public Object invoke(
MethodInvocation invocation)
throws Throwable
{
System.out.println(
"Hello world! (by " +
this.getClass().getName() +
")");
return null;
}
}
要在Spring中用作around通知,AroundAdvice类必须实现MethodInterceptor接口和它的invoke(..)方法。每当截获到方法的重写,invoke(..)方法就会被调用。最后一步是改变包含在应用程序的springconfig.xml文件中的Spring运行时配置,以便可以对应用程序应用AroundAdvice。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- Bean configuration -->
<bean id="businesslogicbean"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>IBusinessLogic</value>
</property>
<property name="target">
<ref local="beanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>theAroundAdvisor</value>
</list>
</property>
</bean>
<!-- Bean Classes -->
<bean id="beanTarget"
class="BusinessLogic"/>
<!-- Advisor pointcut definition for around advice -->
<bean id="theAroundAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="theAroundAdvice"/>
</property>
<property name="pattern">
<value>.*</value>
</property>
</bean>
<!-- Advice classes -->
<bean id="theAroundAdvice"
class="AroundAdvice"/>
</beans>
根据该springconfig.xml配置文件,theAroundAdvisor截获所有对BusinessLogic类的方法的调用。接下来,theAroundAdvisor被关联到theAroundAdvice,表明当截获一个方法时,就应该使用在AroundAdvice类中指定的通知。既然已经指定了around通知的正确配置,下一次执行MainApplication类时,BusinessLogic bean的foo()方法就会被截获并重写,如图3所示:

图3. 使用around通知重写对BusinessLogic类中的foo()方法的调用
前面的例子显示,BusinessLogic类中的foo()方法可以通过AroundAdvice类中的invoke(..)方法彻底重写。原来的foo()方法完全不能被invoke(..)方法调用。如果希望从around通知内调用foo()方法,可以使用proceed()方法,可从invoke(..)方法的MethodInvocation参数中得到它。
public class AroundAdvice
implements MethodInterceptor
{
public Object invoke(
MethodInvocation invocation)
throws Throwable
{
System.out.println(
"Hello world! (by " +
this.getClass().getName() +
")");
invocation.proceed();
System.out.println("Goodbye! (by " +
this.getClass().getName() +
")");
return null;
}
}
图4显示了对proceed()的调用如何影响操作的顺序(与图3所示的初始around通知执行相比较)。

图4. 从around通知内使用proceed()调用原来的方法
当调用proceed()时,实际是在指示被截获的方法(在本例中是foo()方法)利用包含在MethodInvocation对象中的信息运行。您可以通过调用MethodInvocation类中的其他方法来改变该信息。
您可能希望更改包含在MethodInvocation类中的信息,以便在使用proceed()调用被截获的方法之前对被截获方法的参数设置新值。
通过对MethodInvocation对象调用getArguments()方法,然后在返回的数组中设置其中的一个参数对象,最初传递给被截获的方法的参数可以被更改。
如果IbusinessClass和BusinessLogic类的foo()方法被更改为使用整型参数,那么就可以将传递给被截获的调用的值由在AroundAdvice的notify(..)方法中传递改为在foo(int)中传递。
public class AroundAdvice
implements MethodInterceptor
{
public Object invoke(
MethodInvocation invocation)
throws Throwable
{
System.out.println(
"Hello world! (by " +
this.getClass().getName() +
")");
invocation.getArguments()[0] = new Integer(20);
invocation.proceed();
System.out.println(
"Goodbye! (by " +
this.getClass().getName() +
")");
return null;
}
}
在本例中,被截获的方法的第一个形参被假设为int。实参本身是作为对象传递的,所以通过将其包装在Integer类实例中的方法,基本的int类型的形参被改为对应数组中的新值。如果您将该参数设置为一个非Integer对象的值,那么在运行时就会抛出IllegalArgumentException异常。
您还将注意到,invoke(..)方法必须包含一个return语句,因为该方法需要返回值。但是,被重写的foo()方法并不返回对象,所以invoke(..)方法可以以返回null结束。如果在foo()方法不需要的情况下,您仍然返回了一个对象,那么该对象将被忽略。
如果foo()方法确实需要返回值,那么需要返回一个与foo()方法的初始返回类型在同一个类或其子类中的对象。如果foo()方法返回一个简单类型,例如,一个integer,那么您需要返回一个Integer类的对象,当方法被重写时,该对象会自动由AOP代理拆箱,如图5所示:

图5. around通知的装箱和自动拆箱
图字:
Object invoke:对象调用
The integer return value is boxed in a Integer object in the AroundAdvice and then unboxed by the AOP Proxy:整型返回值被装箱在AroundAdvic通知的一个Integer对象中,然后由AOP代理拆箱。
面向方面编程还是一个比较新的领域,尤其是与衍生出它的面向对象编程相比。设计模式通常被认为是常见问题的通用解决方案,因为面向方面发展的时间还不长,所以已发现的面向方面设计模式比较少。
此处要介绍的是一种正在浮现的模式,即Cuckoo's Egg设计模式。该模式还有其他的叫法,它在面向对象领域的对等体包括模仿对象(Mock Object)和模仿测试(Mock Testing),甚至代理模式也与它有一些类似之处。
Cuckoo's Egg面向方面设计模式可以被定义为应用程序上下文中功能部件的透明和模块化的置换。就像杜鹃偷偷地把自己的蛋放在另一种鸟的巢中一样,Cuckoo's Egg设计模式用一个替代功能部件实现置换现有的功能部件,而使造成的干扰尽可能少。
这种置换的实现方式可以是静态的、动态的、部分的、完全的,针对一个对象的多个部分,或针对多个组件。使用面向方面的方法可以透明地实现功能部件的置换,而无需对应用程序的其余部分进行更改。要置换应用程序中现有功能部件的替代功能部件就是“杜鹃的蛋”。图6显示了Cuckoo's Egg设计模式中的主要组成元素。

图6. Cuckoo's Egg设计模式中的主要组成元素
图字:
Application:应用程序
Component:组件
Replacement Feature:替代功能部件
Component 1 and 2 together encompass a distinct feature of the software:组件1和2共同包含了软件的一个独立的功能部件
The Cuckoo's Egg pattern transparently replaces an existing feature of the software:Cuckoo's Egg模式透明地置换了软件现有的功能部件
Before the pattern is applied:应用该模式前
After the pattern is applied:应用该模式后
Cuckoo's Egg设计模式依赖于around通知的概念。您需要借助于积极的和侵入性的around通知来截获并有效置换应用程序中现有的功能部件。
有关Cuckoo's Egg设计模式的更多信息,以及AspectJ中的一个可选实现,请参见《AspectJ Cookbook》(O'Reilly,2004年12月出版)。
要使用Spring AOP实现Cuckoo's Egg设计模式,需要声明一个around通知来截获所有对要置换的功能部件的调用。与hot-swappable target sources(Spring AOP的一个功能部件,将在本系列的另一篇文章中介绍)不同,around通知的显式使用使得Cuckoo's Egg实现可以有效地跨越对象边界(因此也可以跨越bean边界)进行整个功能部件的置换,如图7所示。

图7. 一个跨越bean边界的组件
图字:
A feature crosses the boundaries of BusinessLogic and BusinessLogic2 by depending on behavior supplied separately by the two beans:一个功能部件通过依赖于由BusinessLogic和BusinessLogic2各自提供的行为而跨越了这两个bean的边界
下面的代码显示了一个具有两个bean的简单应用程序,其中有一个功能部件跨越了该应用程序的多个方面。要置换的功能部件可以被视为包含IBusinessLogic bean中的foo()方法和IBusinessLogic2 bean中的bar()方法。IBusinessLogic2 bean中的baz()方法不是 该功能部件的一部分,所以不进行置换。
public interface IBusinessLogic
{
public void foo();
}
public interface IBusinessLogic2
{
public void bar();
public void baz();
}
该例子的完整源代码可在本文末尾的参考资料小节中下载。
此处,ReplacementFeature类扮演了“杜鹃的蛋”的角色,它提供了将被透明地引入应用程序的替代实现。ReplacementFeature类实现了所有在该类引入时要被置换的方法。
public class ReplacementFeature
{
public void foo()
{
System.out.println(
"Inside ReplacementFeature.foo()");
}
public void bar()
{
System.out.println(
"Inside ReplacementFeature.bar()");
}
}
现在需要声明一个around通知来截获对跨越bean的功能部件的方法调用。CuckoosEgg类提供了某种around通知来检查被截获的方法,并将适当的方法调用传递给ReplacementFeature类的实例。
public class CuckoosEgg implements MethodInterceptor
{
public ReplacementFeature replacementFeature =
new ReplacementFeature();
public Object invoke(MethodInvocation invocation)
throws Throwable
{
if (invocation.getMethod().getName().equals("foo"))
{
replacementFeature.foo();
}
else
{
replacementFeature.bar();
}
return null;
}
}
因为与Spring框架关系密切,Cuckoo's Egg设计的详细信息被放在springconfig.xml配置文件中。对springconfig.xml文件的更改将确保所有对IbusinessLogic和IBusinessLogic2 bean的foo()方法和bar()方法的调用都将被截获,并传递给CuckoosEgg类的around通知。
...
<!--CONFIG-->
<bean id="businesslogicbean"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>IBusinessLogic</value>
</property>
<property name="target">
<ref local="beanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>theCuckoosEggAdvisor</value>
</list>
</property>
</bean>
<bean id="businesslogicbean2"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>IBusinessLogic2</value>
</property>
<property name="target">
<ref local="beanTarget2"/>
</property>
<property name="interceptorNames">
<list>
<value>theCuckoosEgg2Advisor</value>
</list>
</property>
</bean>
<!--CLASS-->
<bean id="beanTarget" class="BusinessLogic"/>
<bean id="beanTarget2" class="BusinessLogic2"/>
<!--ADVISOR-->
<bean id="theCuckoosEggAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="theReplacementFeaturePart1Advice"/>
</property>
<property name="pattern">
<value>IBusinessLogic.*</value>
</property>
</bean>
<bean id="theCuckoosEgg2Advisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="theReplacementFeaturePart2Advice"/>
</property>
<property name="pattern">
<value>IBusinessLogic2.bar*</value>
</property>
</bean>
<!--ADVICE-->
<bean id="theReplacementFeaturePart1Advice" class="CuckoosEgg"/>
<bean id="theReplacementFeaturePart2Advice" class="CuckoosEgg"/>
...
当使用修改后的springconfig.xml文件运行例子应用程序时,要替换的、被指定为功能部件的一部分的方法调用完全被截获并传递给ReplacementFeature类。
通常,即使在同一个实现环境中,我们也可以用不同的方法来实现同一种设计模式。实现上例的另一种方法是实现两个独立的通知。
最后需要注意的是,使用Cuckoo's Egg设计模式置换的功能部件,不管它是跨越bean的还是在一个类中,它的生命周期与它所置换的功能部件的目标生命周期匹配。在上例中这没什么问题,因为只有一个功能部件实例被置换了,而且唯一的Cuckoo's Egg通知只维护一个替代功能部件。
这个例子非常简单,而在实践中,您很可能必须处理大量需要用各自的Cuckoo's Egg实例置换的功能部件实例。在这种情况下,单个的方面实例需要被关联到单个的要置换的功能部件实例。本系列的下一篇文章将会考虑方面生命周期的用法,届时将解决这个问题。
结束语
本文介绍了如何在Spring框架内谨慎使用around形式的通知。around形式的通知常用于实现Cuckoo's Egg设计模式时,所以我们引入了一个例子来说明如何使用Spring AOP实现这种面向方面设计模式。
在本系列的第三部分中,您将看到如何使用Spring框架中其他的AOP基本概念。这些概念包括:控制方面生命周期、使用基于introduction通知的积极方面改变应用程序的静态结构,以及使用control flow切入点实现对方面编织的更细微的控制。
参考资料
原文出处
An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 2 http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html
作者简介 |
| Russell Miles是General Dynamics UK公司的一名软件工程师,他负责Java和分布式系统,但是他目前主要的兴趣在面向方面领域,尤其是AspectJ。 |
时间:2005-12-16
作者:
Russell Miles浏览次数:
5882
本文关键字:
Java,
Spring,
aspect-oriented programming,
AOP,
crosscutting,
面向方面编程,
横切,
延迟加载 作为这个介绍Spring框架中的面向方面编程(Aspect-Oriented Programming,AOP)的系列的第一部分,本文介绍了使您可以使用Spring中的面向方面特性进行快速开发的基础知识。使用跟踪和记录方面(面向方面领域的HelloWorld)作为例子,本文展示了如何使用Spring框架所独有的特性来声明切入点和通知以便应用方面。本系列的第二部分将更深入地介绍如何运用Spring中的所有通知类型和切入点来实现更实用的方面和面向方面设计模式。对于AOP的更一般性的介绍,请查看ONJava站点上Graham O'Regan的文章,“Introduction to Aspect-Oriented Programming”。
本文的目的不是要介绍构成模块化J2EE系统——即Spring框架——的所有重要元素,我们将只把注意力放在Spring所提供的AOP功能上。由于Spring的模块化设计方法,我们可以只使用该框架的AOP元素,而无需对构成Spring框架的其他模块做太多考虑。
在AOP方面,Spring提供了什么?
“它的目标不是提供最完善的AOP实现(虽然Spring AOP非常强大);而是要提供AOP实现与Spring IoC的紧密集成,以便帮助解决企业应用中的常见问题。”
Spring Framework参考文档
为了实现这个目标,Spring框架目前支持一组AOP概念,从切入点到通知。本文将展示如何使用Spring框架中所实现的如下AOP概念:
- 通知(Advice):如何将before通知、afterReturning通知和afterThrowing通知声明为bean。
- 切入点(Pointcut):如何声明静态切入点逻辑以将XML Spring Bean Configuration文件中的所有内容联系在一起。
- Advisor:关联切入点定义与通知bean的方式。
设置场景:一个简单的例子应用程序
“一般而言,Spring并不是预描述的。虽然使用好的实践非常容易,但是它避免强制推行一种特定的方法。”
Spring Framework参考文档
要试用Spring框架的AOP功能,首先我们要创建一个简单的Java应用程序。IbusinessLogic接口和BusinessLogic类为Spring框架中的bean提供了简易构件块。虽然该接口对于我们的简单应用程序逻辑来说不是必需的,但是它是Spring框架所推荐的良好实践。
public interface IBusinessLogic
{
public void foo();
}
public class BusinessLogic
implements IBusinessLogic
{
public void foo()
{
System.out.println(
"Inside BusinessLogic.foo()");
}
}
可以编写MainApplication类,借此练习BusinessLogic bean的公有方法。
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MainApplication
{
public static void main(String [] args)
{
// Read the configuration file
ApplicationContext ctx =
new FileSystemXmlApplicationContext(
"springconfig.xml");
//Instantiate an object
IBusinessLogic testObject =
(IBusinessLogic) ctx.getBean("businesslogicbean");
// Execute the public
// method of the bean
testObject.foo();
}
}
在BusinessLogic类及其关联接口中没有什么需要注意的。但是,MainApplication类初始化BusinessLogic对象的方式很有意思。通过使用ctx.getBean("businesslogicbean")调用,MainApplication将加载和管理BusinessLogic类的bean实例的任务转交给了Spring框架。
允许Spring控制BusinessLogic bean的初始化,这使得Spring运行时有机会在bean被返回给应用程序之前执行J2EE系统所需的所有与bean相关的管理任务。然后Spring运行时配置可以决定对bean应用哪些任务和模块。该配置信息由一个XML文件提供,类似于下面所示的:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC
"-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- Bean configuration -->
<bean id="businesslogicbean"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>IBusinessLogic</value>
</property>
<property name="target">
<ref local="beanTarget"/>
</property>
</bean>
<!-- Bean Classes -->
<bean id="beanTarget"
class="BusinessLogic"/>
</beans>
该配置文件,即springconfig.xml,指定要加载一个接口与IbusinessLogic相匹配的bean。该bean随后被关联到BusinessLogic实现类。看起来好像是费了很大力气只为了加载一个简单的bean并调用一个方法,但是您要知道,这个配置文件只是使Spring框架可以透明地对应用程序应用其组件的众多特性的一个体现。
图1显示了基本的顺序图:MainApplication原样执行,没有应用方面。

图1.没有对BusinessLogic bean应用方面时的顺序图
请查看本文末尾处的参考资料,获取这个简单Spring应用程序的源代码。
应用方法跟踪(Method Tracing)方面
可能最基本的方面就是方法跟踪方面了。这可能是您找得到的最简单的方面了,因此它是研究新的AOP实现的一个很好的起点。
方法跟踪方面在一个目标应用程序内捕获对所跟踪的方法的调用以及方法的返回值,并以某种方式显示这种信息。在AOP中,通知的before和after类型用于捕获这些类型的联结点,因为这两种通知可以在方法调用联结点之前或之后触发。使用Spring框架,方法跟踪方面的before通知是在TracingBeforeAdvice类中声明的。
import java.lang.reflect.Method;
import org.springframework.aop. MethodBeforeAdvice;
public class TracingBeforeAdvice
implements MethodBeforeAdvice
{
public void before(Method m,
Object[] args,
Object target)
throws Throwable
{
System.out.println(
"Hello world! (by " +
this.getClass().getName() +
")");
}
}
类似地,after通知可以在TracingAfterAdvice类中声明。
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class TracingAfterAdvice
implements AfterReturningAdvice
{
public void afterReturning(Object object,
Method m,
Object[] args,
Object target)
throws Throwable
{
System.out.println(
"Hello world! (by " +
this.getClass().getName() +
")");
}
}
这两个类都通过实现Spring框架的适当通知接口而表示了特定的通知。每种类型的通知都指定实现before(..)或afterReturning(..)方法,以便使Spring运行时可以告诉通知适当的联结点会在何时出现。值得注意的是,TracingAfterAdvice实际上是从AfterReturningAdvice扩展而来的,表示只有在联结点在无异常的情况下获得返回值时才运行通知。
为了将通知与应用程序中的适当联结点关联起来,必须对springconfig.xml进行一些修改。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC
"-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- Bean configuration -->
<bean id="businesslogicbean"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>IBusinessLogic</value>
</property>
<property name="target">
<ref local="beanTarget"/>
</property>
<property name="interceptorNames">
<list>
<value>theTracingBeforeAdvisor</value>
<value>theTracingAfterAdvisor</value>
</list>
</property>
</bean>
<!-- Bean Classes -->
<bean id="beanTarget"
class="BusinessLogic"/>
<!-- Advisor pointcut definition for before advice -->
<bean id="theTracingBeforeAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="theTracingBeforeAdvice"/>
</property>
<property name="pattern">
<value>.*</value>
</property>
</bean>
<!-- Advisor pointcut definition for after advice -->
<bean id="theTracingAfterAdvisor"
class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="theTracingAfterAdvice"/>
</property>
<property name="pattern">
<value>.*</value>
</property>
</bean<
<!-- Advice classes -->
<bean id="theTracingBeforeAdvice"
class="TracingBeforeAdvice"/>
<bean id="theTracingAfterAdvice"
class="TracingAfterAdvice"/>
</beans>
theTracingBeforeAdvisor和theTracingAfterAdvisor advisor被添加到前面所声明的businesslogicbean。每个advisor都可能截获所有bean所关联到的联结点。Advisor本身就是bean,而它唯一的作用就是将切入点定义与通知bean关联起来。本例中的切入点定义是在静态对象层次结构中指定相关联结点的正则表达式。
因为本例中使用了org.springframework.aop.support.RegexpMethodPointcutAdvisor切入点advisor,切入点逻辑是使用正则表达式指定的。正则表达式用于识别公有接口对IbusinessLogici接口的联结点。下面是一些可以用来指定IBusinessLogic接口上的不同联结点集合的正则表达式例子:
- <value>.*</value>:该表达式选择advisor所关联到的一个或多个bean上的所有联结点。
- <value>./IBusinessLogic/.foo</value>:该表达式只选择IbusinessLogic接口上的foo()方法的联结点。如果是advisor所关联到的bean,则该表达式只选择IBusinessLogic接口上的联结点。
springconfig.xml文件中最后的bean声明指定实现通知bean的类。
既然已经指定了跟踪方面的正确配置,那么下一次执行MainApplication时,这些方面就会在初始化过程中被编织进去,而BusinessLogic bean中的所有方法都将被跟踪,如图2所示。

图2. 方法跟踪方面应用到BusinessLogic bean之后的顺序图(单击图像查看大图)
方法跟踪方面和例子应用程序的源代码可在本文末尾的参考资料小节进行下载。
方面的重用
可以对方法跟踪方面进行扩展,提供一个稍微复杂的记录(Logging)方面。记录方面提供了一个很不错的重用例子,因为记录方面所需的许多特性都已经包含在方法跟踪方面中了。
在本例中,记录方面扩展了方法跟踪方面,以便显示附加的与(在应用程序的执行过程中)所引发的异常有关的信息。
要完全使用记录方面,需要对应用程序做一些更改。BusinessLogicException异常类提供了一个可以由IBusinessLogicInterface接口和BusinessLogic实现类新增的void bar()方法引发的异常。
public class BusinessLogicException
extends Exception
{
}
public interface IBusinessLogic
{
public void foo();
public void bar()
throws BusinessLogicException;
}
public class BusinessLogic
implements IBusinessLogic
{
public void foo()
{
System.out.println(
"Inside BusinessLogic.foo()");
}
public void bar()
throws BusinessLogicException
{
System.out.println(
"Inside BusinessLogic.bar()");
throw new BusinessLogicException();
}
}
MainApplication类现在将对void bar()方法进行一次额外的调用,并处理选中的、可能由该方法引发的异常。
import org.springframeworkcontext.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
public class MainApplication
{
public static void main(String [] args)
{
// Read the configuration file
ApplicationContext ctx =
new FileSystemXmlApplicationContext(
"springconfig.xml");
//Instantiate an object
IBusinessLogic testObject =
(IBusinessLogic) ctx.getBean(
"businesslogicbean");
//Execute the public methods of the bean
testObject.foo();
try
{
testObject.bar();
}
catch(BusinessLogicException ble)
{
System.out.println(
"Caught BusinessLogicException");
}
}
}
来自方法跟踪方面的TracingBeforeAdvice和TracingAfterAdvice通知可以整体重用。LoggingThrowsAdvice类为新的异常记录提供了通知。
import org.springframework.aop.ThrowsAdvice;
import java.lang.reflect.Method;
public class LoggingThrowsAdvice
implements ThrowsAdvice
{
public void afterThrowing(Method method,
Object[] args,
Object target,
Throwable subclass)
{
System.out.println(
"Logging that a " +
subclass +
"Exception was thrown.");
}
}
应用记录方面的最后一步是修改springconfig.xml配置文件,使其包含新添加的LoggingThrowsAdvice通知。
图3显示了运行MainApplication并使用Spring框架应用了记录方面的UML顺序图。

图3. 记录方面应用到BusinessLogic bean之后的顺序图(单击图像查看大图)
此处的记录方面清楚地说明了如何重用现有方面以及如何在Spring框架中使用通知的throws形式。通过为before和after通知声明新的通知来重写现有的方法跟踪方面实现,可以实现更复杂的记录方面,记录到更复杂的记录框架,比如LOG4J。关于记录方面和例子应用程序的源代码,请参见本文末尾的参考资料小节。
结束语
本文展示了使用Spring框架中的基本AOP结构所应用的一些简单方面。在本系列的下一篇文章中,我们将介绍一些更实用的方面,探讨方面的生命周期,使用Spring框架的around通知,并使用Spring来应用AOP模式。
参考资料
原文出处:An Introduction to Aspect-Oriented Programming with the Spring Framework, Part 1http://www.onjava.com/pub/a/onjava/2004/07/14/springaop.html
作者简介 |
| Russell Miles是General Dynamics UK公司的一名软件工程师,他负责Java和分布式系统,但是他目前主要的兴趣在面向方面领域,尤其是AspectJ。 |
在本文中,作者通过一个Web Service访问的实例,具体描述了SOA应用中所遇到的一系列具体问题,并描述如何利用IoC和AOP等技术进行代码重构,从而构建结构更加良好、灵活的SOA应用。
1.引言
SOA是一种构造分布式系统的方法,它将业务应用功能以服务的形式提供出来,以便更好的复用、组装和与外部系统集成,从而降低开发成本,提高开发效率。SOA的目标是为企业构建一个灵活,可扩展的IT基础架构来更好地支持随需应变的商务应用。
随着SOA技术和产品的不断成熟,现在越来越多的用户开始了解并认同SOA的理念,但对SOA项目的实施还缺乏信心。其主要原因是:SOA应用开发还相对比较复杂。
一年多来,本文作者所在的部门已经从事了许多国内外的SOA项目的实施和支持工作,积累了许多SOA应用开发经验。我们希望能够通过一系列的文章与读者分享这些想法,帮助您更好地构建SOA应用。
本文将从Web Service调用入手,在解决一系列具体问题的过程中,使用IoC (Inversion of Control) 和AOP (Aspect- Oriented Programming) 等方法重构Web Service的访问代码,使得业务逻辑与Web Service访问解耦,为您提供一个更加灵活和易于扩展的访问模式。
Spring是一个流行的轻量级容器,对IoC和AOP提供了良好的支持。本文为您提供了一个基于Spring的实现供您下载学习。示例代码工程使用Eclipse3.1/3.02和JDK1.4开发, 您还需要Spring 1.2.5和Axis1.3提供的支持。详细的下载信息请参见参考资源部分。
2.Web Service调用
Web Service是目前实现SOA应用的一项基本的,适用的技术,它为服务的访问提供了一个被广泛接受的开放标准。为了便于说明问题,我们将使用XMethods 网站(http://www.xmethods.net/)发布的货币兑换服务作为示例。并针对JAX-RPC 1.1,说明如何编写Web Service 的调用代码。
2.1 示例说明
http://xmethods.net 作为最早推出Web Service实际示例的网站,提供了很多优秀的Web Service 样例。其中有一个汇率计算服务,可以返回两个国家之间的货币兑换比例。获取该服务的详细信息,请参考该服务的服务描述文档(获取WSDL 文档) 。在此就不具体解析该服务描述文档了。读者可以从WSDL2Java生成的接口中了解该服务的用法:
public interface CurrencyExchangePortType extends java.rmi.Remote {
public float getRate(String country1, String country2) throws java.rmi.RemoteException;
}
|
2.2 客户端调用方法
JAX-RPC作为Java平台的RPC服务调用标准接口,为Web Service客户端调用提供了3种方法,分别是DII,动态代理,和静态Stub。 DII(Dynamic Invocation Interface)采用直接调用方式,可以在程序中设置诸多的调用属性,使用较为灵活,但是调用过程却相对繁琐复杂,易造成代码膨胀且可重用性低,每次调用不同的Web Service都要重复进行大量编码。
JAX-RPC中动态代理(Dynamic Proxy)的方法实现对Web Service的动态调用,可以在运行时根据用户定义的Client端接口创建适配对象。从而避免了直接操作底层的接口,减少了客户端的冗余,屏蔽了调用相关的复杂性。
使用静态Stub和Service Locator是目前最常用的调用方式。JAX-RPC使用静态的Stub方式包装对底层接口的调用,从而提供一种更为简便的调用方式。使用该方式需要利用支持环境(比如Axis)所提供的工具根据WSDL预生成Web Service客户端的实现代码。因此如果服务的WSDL发生变化,就必须重新生成新的客户端代码并进行重新部署。
为了更详细的了解静态Stub的调用方式,您可以将示例代码的WebServiceClient.jar导入到您现有Eclipse工作区之中。
客户端生成代码包括如下4个类:如图 1 所示:
图 1: 客户端代码类图
在上图中包括的几个类中:
CurrencyExchangePortType:服务端点接口,定义了Web Service的方法签名。
CurrencyExchangeService:Service接口,定义了获取服务端点接口的方法。
CurrencyExchangeServiceLocator:ServiceLocator类,实现了Service接口。
CurrencyExchangeBindingStub: Stub实现类,实现了服务端点接口,封装了对Web Service访问的底层逻辑。
使用Stub调用Web Service的过程也非常简单,读者可以参考清单 1:
清单 1:Web Service 调用代码示例
try {
//创建ServiceLocator
CurrencyExchangeServiceLocator locator = new
CurrencyExchangeServiceLocator();
//设定端点地址
URL endPointAddress = new URL("http://services.xmethods.net:80/soap");
//创建Stub实例
CurrencyExchangePortType stub =
locator.getCurrencyExchangePort(endPointAddress);
//设定超时为120秒
((CurrencyExchangeBindingStub)stub).setTimeout(120000);
//调用Web Service计算人民币与美元的汇率
float newPrice = stub.getRate("China", "USA") * 100;
} catch (MalformedURLException mex) {
//...
} catch (ServiceException sex) {
//...
} catch (RemoteException rex) {
//...
}
|
3.重构Web Service调用代码
3.1 实例代码中的"坏味道"
上面的基于Service Locator的Web Service访问代码虽然简单但暴露出以下几个问题:
1.访问Web Service所需的配置代码被嵌入应用逻辑之中
在Web Service调用中,我们需要设定一系列必要的参数。比如:服务端点地址、用户名/密码、超时设定等等。这些参数在开发和运行环境中都有可能发生变化。我们必须提供一种机制:在环境变化时,不必修改源代码就可以改变Web Service的访问配置。
2 客户端代码与Web Service访问代码绑定
在上面的代码中,业务逻辑与Web Service的Stub创建和配置代码绑定在一起。这也不是一种良好的编程方式。客户端代码只应关心服务的接口,而不应关心服务的实现和访问细节。比如,我们既可以通过Web Service的方式访问远程服务,也可以通过EJB的方式进行访问。访问方式对业务逻辑应该是透明的。
这种分离客户端代码与服务访问代码的方式也有利于测试。这样在开发过程中,负责集成的程序员就可能在远程服务还未完全实现的情况下,基于服务接口编写集成代码,并通过编写POJO(Plain Old Java Object)构建伪服务实现来进行单元测试和模拟运行。这种开发方式对于保证分布式系统代码质量具有重要意义。
因此,为了解决上面的问题我们需要:
1、将Web Service访问的配置管理与代码分离;
2、解除客户端代码与远程服务之间的依赖关系;
3.2 利用IoC模式进行重构代码
我们先介绍在Core J2EE Patterns一书中提到的一种业务层模式:Business Delegate。它所要解决的问题是屏蔽远程服务访问的复杂性。它的主要思想就是将Business Delegate作为远程服务的客户端抽象,隐藏服务访问细节。Business Delegate还可以封装并改变服务调用过程,比如将远程服务调用抛出的异常(例如RemoteException)转换为应用级别的异常类型。
其类图如图 2 所示:
图 2:Business Delegate 模式的类图图解
Business Delegate模式实现很好地实现了客户端与远程访问代码的解耦,但它并不关注Delegate与远程服务之间的解耦。为了更好解决Business Delegate和远程服务之间的依赖关系,并更好地进行配置管理,我们可以用IoC模式来加以解决。
IoC(Inversion of Contro)l意为控制反转,其背后的概念常被表述为"好莱坞法则":"Don't call me, I'll call you." IoC将一部分责任从应用代码交给framework(或者控制器)来做。通过IoC可以实现接口和具体实现的高度分离,降低对象之间的耦合程度。Spring是一个非常流行的IoC容器,它通过配置文件来定义对象的生命周期和依赖关系,并提供了良好的配置管理能力。
现在我们来重构我们的Web Service应用程序,我们首先为Business Delegate定义一个接口类型,它提供了一个应用级组件接口,所有客户端都应通过它来执行汇率计算,而不必关心实现细节,如清单 2 所示:
清单 2:接口定义的代码示例
Public interface CurrencyExchangeManager {
//货币兑换计算
//新价格 = 汇率 * 价格
public float calculate(String country1, String country2, float price)
throws CurrencyExchangeException;
}
|
Business Delegate的实现非常简单,主要工作是包装汇率计算 Web Service的调用,如清单 3 所示。
清单 3:Business Delegate的代码示例
public class CurrencyExchangeManagerImpl implements CurrencyExchangeManager {
//服务实例
private CurrencyExchangePortType stub;
//获取服务实例
public CurrencyExchangePortType getStub() {
return stub;
}
//设定服务实例
public void setStub(CurrencyExchangePortType stub) {
this.stub = stub;
}
//实现货币兑换
public float calculate(String country1, String country2, float price)
throws CurrencyExchangeException {
try {
//通过Stub调用WebService
float rate = stub.getRate(country1, country2);
return rate * price;
} catch (RemoteException rex) {
throw new CurrencyExchangeException(
"Failed to get exchange rate!", rex);
}
}
}
|
下面我们需要讨论如何利用Spring的IoC机制,来创建和配置对象,并定义它们的依赖关系。
Spring利用类工厂来创建和配置对象。在Spring框架中,已经为基于JAX-RPC的Web Service调用提供了一个客户端代理的类工厂实现:JaxRpcPortProxyFactoryBean。在配置文件bean.xml中,我们将使用JaxRpcPortProxyFactoryBean来创建和配置Web Service的客户端代理"CurrencyExchangeService",如清单 5 所示。我们还将定义一个名为"CurrencyExchangeManager"的CurrencyExchangeManagerImpl实例,并建立它与CurrencyExchangeService之间的依赖关系。有关Spring 配置和JaxRpcPortProxyFactoryBean的使用细节请参见参考资料。
清单 5:bean.xml的配置文件
<?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="CurrencyExchangeService"
class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean">
<property name="serviceInterface">
<value>net.xmethods.www.sd.CurrencyExchangeService_wsdl.
CurrencyExchangePortType</value>
</property>
<property name="wsdlDocumentUrl">
<value>http://www.xmethods.net/sd/2001/CurrencyExchangeService.
wsdl</value>
</property>
<property name="namespaceUri">
<value>http://www.xmethods.net/sd/CurrencyExchangeService.
wsdl</value>
</property>
<property name="serviceName">
<value>CurrencyExchangeService</value>
</property>
<property name="portName">
<value>CurrencyExchangePort</value>
</property>
<property name="endpointAddress">
<value>http://services.xmethods.net:80/soap</value>
</property>
</bean>
<bean id="CurrencyExchangeManager"
class="test.ws.CurrencyExchangeManagerImpl">
<property name="stub">
<ref bean="CurrencyExchangeService"/>
</property>
</bean>
</beans>
|
最后我们创建一个测试程序来验证我们的代码,如清单6 所示:
清单 6:测试代码
public class Main {
// For test only
public static void main(String[] args) {
// Spring Framework将根据配置文件创建并配置CurrencyExchangeManager实例
ApplicationContext ctx = new FileSystemXmlApplicationContext("bean.xml");
// 获取CurrencyExchangeManager实例
CurrencyExchangeManager manager = (CurrencyExchangeManager) ctx
.getBean("CurrencyExchangeManager");
try {
System.out.println(manager.calculate("China", "USA", 100));
System.out.println(manager.calculate("China", "Japan", 200));
System.out.println(manager.calculate("China", "USA", 200));
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
|
此时运行测试客户端,等待片刻将会看见测试结果,如清单 7 所示:
清单 7:测试结果。
注:该结果会随着汇率的变化而出现不同的值。
该程序的类图和顺序图如图3及图4所示:
图 3:示例程序的类图
从上面的类图我们可以看到,我们的测试程序(Main.java)通过Spring框架获取了BusinessDelegate的实例。而且Spring 框架还会根据配置中的依赖关系,在运行时将Web Service的客户端代理" 注射"到CurrencyExchangeManagerImpl实例中,这就是依赖注入(Dependency Injection)。通过这种方式解决了应用逻辑和BusinessDelegate之间的依赖关系,以及BusinessDelegate的实现与远程服务之间的依赖关系,如图 4 所示。
图 4: 示例程序的顺序图
Spring框架提供的ApplicationContext实现会根据配置文件中的描述信息来实现对象生命周期管理,配置管理以及依赖管理等功能。这一切对于应用程序是透明的,应用程序代码只依赖接口进行编程,而无需考虑其它复杂问题。无论是Web Service的配置发生变化,或是改用不同的服务实现时,都不会对客户端应用代码的产生影响。这很好地实现了业务逻辑与Web Service调用之间的解耦。
3.3 构建自己的 Web Service代理工厂
Spring所提供的JaxRpcPortProxyFactoryBean封装了构造Web Service客户端代理的细节,可以通过参数配置来创建Dynamic Proxy和DII类型的Web Service客户端代理。(如果您希望深入了解其实现细节可以参考org.springframework.remoting.jaxrpc包下的源代码。)但由于JaxRpcPortProxyFactoryBean需要使用者对WSDL中Port,Service,名空间等概念有深入的了解;而且如果Web Service使用了复杂数据类型,开发人员需要手工定义类型映射代码。所以JaxRpcPortProxyFactoryBean并不适合Web Service的初学者来使用。
为了进一步简化Web Service代理的创建,并帮助读者更好地理解类工厂在Spring框架下的作用。我们提供了一个基于静态Stub的Web Service客户端代理工厂实现。其核心代码非常简单,就是通过ServiceLocator提供的方法来创建Web Service客户端代理。
其主要代码如清单8所示:
清单8:静态代理工厂的代码
public class WebServiceStubFactoryBean implements FactoryBean,
InitializingBean {
private Class serviceInterface;
private Class serviceLocator;
private Object stub;
…
public void afterPropertiesSet() throws Exception {
//利用serviceLocator和服务接口创建Web Service客户端代理
stub = ((javax.xml.rpc.Service)
serviceLocator.newInstance()).getPort(serviceInterface);
//为Stub设定endpointAddress,usernam, 超时等参数
preparePortStub((javax.xml.rpc.Stub) stub);
}
public Object getObject() {
// 返回客户端代理
return stub;
}
public Class getObjectType() {
// 返回服务接口
return serviceInterface;
}
public boolean isSingleton() {
return true;
}
}
|
我们需要修改配置文件bean.xml中有关Web Service代理创建的部分,让新的Web Service 代理工厂发挥作用。如清单9所示:
清单9:修改后的bean.xml的配置文件
<bean id="CurrencyExchangeService" class="test.ws.WebServiceStubFactoryBean">
<property name="serviceInterface">
<value>net.xmethods.www.sd.CurrencyExchangeService_wsdl.CurrencyExchangePortType</value>
</property>
<property name="serviceLocator">
<value>net.xmethods.www.sd.CurrencyExchangeService_wsdl.CurrencyExchangeServiceLocator</value>
</property>
<property name="endpointAddress2">
<value>http://services.xmethods.net:80/soap</value>
</property>
<property name="timeout">
<value>120000</value>
</property>
</bean>
|
得益于Spring框架,虽然我们已经替换了对象的类工厂,却并不需要更改应用代码。通过Spring框架的IoC机制,我们可以完全使用面向接口的编程方式,而将实现的创建、配置和依赖管理交由Spring在运行时完成。即使实现发生了变化,也不需要改变应用程序结构。
4.新的思考
故事并没有结束,在开发过程中,我们又遇到了一系列关于Web Service调用的问题。
4.1性能
系统性能是分布式应用中的一个重要问题。许多用户都担心由Web Service技术所引入的额外开销是否会影响到产品的性能。随着技术的不断发展,Web Service引擎性能已经有了很大提高,一般来说使用Web Service的系统的性能可以满足绝大部分应用的需求。但在特定情况下,如果系统性能无法满足客户需求,我们首先需要对系统性能进行科学地分析和测定才能定位真正的性能瓶颈。这个问题在上文简单的示例中并不难解决,只需要在Web Service调用前后加入日志代码记录调用时间即可实现。但在实际系统中,比如一个产品目录的Web Service可能提供数十种查询方法,而程序中很多组件都会依赖于该服务提供的查询功能。如果在系统中所有的地方加入性能测定代码,这个工作就变得非常繁琐和困难。我们需要用一种更加优雅的解决方式,在增添新功能的同时并不影响系统代码或结构。
4.2缓存
在项目实践中,一个有效的改善Web Service系统性能的方法就是利用缓存来减少Web Service的重复调用。在具体实现中我们可以采用客户端缓存和服务器端缓存等不同方式,他们具有不同的特点和适用范围。在本文例子中,我们希望实现客户端缓存来提高系统性能。但由于Web Service业务逻辑的差别,我们希望能够为特定的Web Service提供特定的缓存策略,而且这些策略应该是能够被灵活配置的,它们不应于应用程序的逻辑代码耦合在一起。
4.3故障恢复:
对于Web Service应用,系统的可用性也是一个需要考虑的重要问题。在运行时由于网络运行环境的复杂性和不确定性,用户希望能够对Web Service访问提供一定的故障恢复机制:比如重试或者访问备份服务(当系统在调用Web Service失败后,使用备份Web Service的服务地址来继续访问)。这些故障恢复策略应该是可配置的,对应用逻辑透明的。
5.使用AOP解决SOA应用中的Crosscutting Concern
通过对上边一系列问题的分析,读者也许会发现这些问题并不是Web Service访问的核心问题,但会影响系统中许多不同的组件。而且其中一些问题需要我们能够灵活配置不同的实现策略,因此我们不应该将处理这些问题的代码与应用代码混合。
下面我们将利用AOP(Aspect-Oriented Programming)提供的方法来解决上述的问题。AOP是一种新兴的方法学,它最基本的概念就是关注隔离(Separation of Concern)。AOP提供了一系列的技术使得我们能够从代码中分离那些影响到许多系统模块的crosscutting concerns,并将他们模块化为Aspects。AOP的主要目的仍然是解耦,在分离关注点后,才能将关注点的变更控制一定范围内,增加程序的灵活性,才能使得关注能够根据需求和环境作出随时调整。
我们将利用Spring所提供的AOP功能支持来解决以上问题。这里我们只简单地介绍涉及到的AOP基本概念以及实现,如果您希望更好地了解AOP的概念以及Spring AOP支持的细节请参见参考资料。
- Joinpoint 是程序的运行点。在Spring AOP中,一个Joinpoint对应着一个方法调用。
- Advice 定义了AOP框架在特定的Joinpoint的处理逻辑。Spring AOP框架通过interceptor方式实现了advice,并且提供了多种advice类型。其中最基本的"around advice"会在一个方法调用之前和之后被执行。
下面我们将利用Spring提供的MethodInterceptor来为Web Service调用实现我们的定义的处理逻辑。
5.1 PerformanceMonitorInterceptor
性能测量是AOP最简单的例子之一,我们可以直接利用Spring提供的实现在bean.xml中声明我们的WebServicePerformanceMonitorInterceptor。
5.2 CacheInterceptor
为了不引入缓存策略的复杂性,我们只提供了一个利用HashMap的简单实现:它利用 Web Service的调用参数列表作为HashMap键值。在Web Service调用之前,首先检查缓存中是否拥有与现在参数列表相同的项,如果有则返回缓存的结果,否则调用Web Service并将<参数列表,结果>记录在HashMap中。在实际应用中,您应该根据具体情况来选择、构造适合Web Service的业务特性的Cache实现,也可以采用成熟的Cache实现。
在下面代码实现中有一个生成Web Service调用主键的小技巧。因为Web Service引擎要求所有调用参数必须是可序列化的,所以我们可以利用Java提供的序列化功能来实现对象的克隆。如清单10所示:
清单10:SimpleCacheInterceptor的代码示例
public class SimpleCacheInterceptor implements MethodInterceptor {
private Map cache = new HashMap();
private Object cloneObject(Object obj) throws Exception {
Object newObj = null;
if (obj != null) {
// 通过序列化/反序列化来克隆对象
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(bos);
out.writeObject(obj);
out.flush();
out.close();
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bos.toByteArray()));
newObj = in.readObject();
}
return newObj;
}
//基于参数列表数组,生成用于HashMap的键值
public Object generateKey(Object[] args) throws Exception {
Object[] newArgs = (Object[]) cloneObject(args);
List key = Arrays.asList(newArgs);
return key;
}
//实现使用缓存技术的invoke方法
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Object result = null;
Object data = null;
Object key = null;
try {
key = generateKey(methodInvocation.getArguments());
data = cache.get(key);
} catch (Exception ex) {
logger.error("Failed to find from the cache", ex);
}
if (data == null) {
//如果Cache中没有缓存结果,调用服务执行生成用于HashMap的键值
result = methodInvocation.proceed();
try {
data = cloneObject(result);
cache.put(key, data);
} catch (Exception ex) {
logger.error("Failed to cache the result!", ex);
}
} else {
result = data;
}
return result;
}
}
|
5.3 FailoverInterceptor
下面代码提供了一个基于服务备份切换的故障恢复实现,在运行时,如果Interceptor检测到服务调用由于网络故障抛出异常时,它将使用备份服务的端点地址并重新调用。如清单11所示:
清单 11: SimpleFailoverInterceptor的代码示例
public class SimpleFailoverInterceptor implements MethodInterceptor { …
…
//实现支持端点运行时切换的invoke方法
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Object result = null;
try {
result = methodInvocation.proceed();
} catch (Throwable ex) {
if (isNetworkFailure(ex)) {
//切换服务端点地址
switchEndPointAddress((Stub) methodInvocation.getThis());
result = methodInvocation.proceed();
} else {
throw ex;
}
}
return result;
}
}
|
为了支持备份服务切换的功能,我们在WebServicePortProxyFactoryBean中为填加了配置参数"endpointAddress2",它会在创建的Web Service客户端代理对象中记录备份URL。
我们可以在CurrencyExchangeService加入下列参数来试验SimpleFailoverInterceptor的功能。其中第一个端点地址为一个错误的URL。在第一次调用服务时,SimpleFailoverInterceptor会侦测到网络故障的发生,并自动切换使用第二个端点地址继续访问。如清单12所示:
清单12:配置文件种增加的属性
<property name="endpointAddress">
<value>http://localhost/wrong_endpoint_address</value>
</property>
<property name="endpointAddress2">
<value>http://services.xmethods.net:80/soap</value>
</property>
|
5.4配置文件和运行结果
现在我们需要在Spring配置文件中,为所有interceptor添加定义,并描述如何为CurrencyExchangeService构建AOP Proxy。需要指出的是,我们要在interceptorName列表中声明interceptor链的调用顺序,还要将原有CurrencyExchangeManager引用的stub对象替换为新AOP Proxy。如清单13所示:
清单13:修改后的配置文件片段
<bean id="WebServicePerformanceMonitorInterceptor"
class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor">
<property name="prefix">
<value>Web Service </value>
</property>
<property name="suffix">
<value></value>
</property>
</bean>
<bean id="CacheInterceptor" class="test.ws.SimpleCacheInterceptor"/>
<bean id="FailoverInterceptor" class="test.ws.SimpleFailoverInterceptor"/>
<bean id="CurrencyExchangeProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>net.xmethods.www.sd.CurrencyExchangeService_wsdl.
CurrencyExchangePortType</value>
</property>
<property name="target">
<ref local="CurrencyExchangeService"/>
</property>
<property name="interceptorNames">
<list>
<value>WebServicePerformanceMonitorInterceptor</value>
<value>CacheInterceptor</value>
<value>FailoverInterceptor</value>
</list>
</property>
</bean>
<bean id="CurrencyExchangeManager"
class="test.ws.CurrencyExchangeManagerImpl">
<property name="stub">
<ref bean="CurrencyExchangeProxy"/>
</property>
</bean>
|
这里我们通过为AOP 的ProxyFactoryBean为 Web Service Stub创建了一个AOP代理,并且建立了一个Interceptor链。这样在调用Web Service时,Spring框架会依次调用Interceptor执行。实例执行的顺序图将如图5所示:
图5系统运行顺序图
5.5 Interceptor与JAX-RPC Handler的关系与区别
SOAP Message Handler是JAX-RPC为用户自定义Web Service处理过程提供的一种扩展机制。在处理Web Service请求/响应过程中,Web Service 引擎会根据部署描述中的定义,按照一定的次序调用Handler的处理代码。用户编写的Handler实现可以截获并修改Web Service消息和处理流程,从而实现对Web Service引擎处理行为的定制和增强。
比如,我们可以实现一个服务器端Handler,记录Web Service在受到请求消息和发出响应消息之间的时间间隔来实现对服务器端业务性能的测定。而且我们只需在部署描述中增加Handler声明即可,无需修改任何服务器端代码。
从此可以看出,JAX-RPC Handler与我们在上文中所提供的AOP Interceptor都可以帮助我们的SOA应用程序实现关注分离(Separate Concern)的目标,在不改变应用代码的同时,增强或改变Web Service服务访问的功能。虽然我们可以利用它们实现一些类似的功能,但它们具有着不同的特点和适用范围。
JAX-RPC Handler是Web Service引擎的扩展机制。如果我们需要实现对SOAP消息进行的修改和处理,加入自定义的SOAP Header或对消息内容进行加密,Handler是我们的最佳选择。而AOP是针对对象级别的扩展机制,它更适合对应用层逻辑进行操作。
比如,我们在上文展示的利用AOP实现的CacheInterceptor,它缓存的是Web Service调用参数和结果。而我们也可以通过JAX-RPC Handler实现一个面向SOAP消息的实现,它将缓存Web Service的请求消息和响应消息。这两个实现相比,基于AOP的实现更加简单、直观、快速、对资源消耗也比较小。而面向SOAP消息的实现则更加灵活,对于不采用RPC方式的Web Service访问也能提供支持。
所以在具体的实践过程中,开发人员应该根据具体的需求选择合适的技术,也可以将这两种技术结合使用。
6.总结
"分而治之"的方法是人们解决复杂问题的一种常见做法。而IoC、AOP等技术都体现了这种思想。通过更好的切分程序逻辑,使得程序结构更加良好,更加富有弹性,易于变化。也使得开发人员可以更加专注于业务逻辑本身,而将一部分其他逻辑交给容器和框架进行处理。
在本文中,我们通过一个Web Service访问的实例,具体描述了SOA应用中所遇到的一系列具体问题,并描述如何利用IoC和AOP等技术进行代码重构,构建更加结构良好、灵活的SOA应用。综上所述,我们可以看到:
1使用IoC框架来实现对象的生命周期管理、配置管理和依赖管理,可以解除业务逻辑对服务调用的依赖关系;
2 使用AOP方法来解决Web Service调用中的crosscutting concerns,将为系统增加新的功能而不必更改应用程序。
3通过IoC和AOP来屏蔽Web Service访问的复杂性,使得开发人员可以更加专注于业务逻辑本身,也使得系统更加稳定和富有弹性。
代码:下载
由于项目需要,需要做一些类似javascript中eval的方法的计算。
找到beanshell感觉很好用,跟大家分享!
请参考beanshell:
http://www.beanshell.org以下是demo:
/**
* gf测试宏计算
* @return
*/
public void testBshInterpreter(){
Interpreter interpreter= new Interpreter();
//String bStr="(100>50 && 100<101)";
try {
//1
interpreter.set("add", interpreter.eval("(100+101)"));
System.out.println(interpreter.get("add"));
interpreter.set("boolean", interpreter.eval("(100>101)"));
System.out.println(interpreter.get("boolean"));
//2
Interpreter i = new Interpreter(); // Construct an interpreter
i.set("foo", 5); // Set variables
i.set("date", new Date() );
Date date = (Date)
i.get("date"); // retrieve a variable
// Eval a statement and get the result
i.eval("bar = foo*10");
System.out.println( i.get("bar") );
} catch (EvalError e) {
e.printStackTrace();
}
}
public class GetCh2Spell { public static int compare(String str1, String str2) { int result = 0; String m_s1 = null; String m_s2 = null; try { m_s1 = new String(str1.getBytes(_FromEncode_), _ToEncode_); m_s2 = new String(str2.getBytes(_FromEncode_), _ToEncode_); } catch(Exception e) { return str1.compareTo(str2); } result = chineseCompareTo(m_s1, m_s2); return result; }
public static int getCharCode(String s) { if(s == null && s.equals("")) return -1; byte b[] = s.getBytes(); int value = 0; for(int i = 0; i < b.length && i <= 2; i++) value = value * 100 + b[i];
return value; }
public static int chineseCompareTo(String s1, String s2) { int len1 = s1.length(); int len2 = s2.length(); int n = Math.min(len1, len2); for(int i = 0; i < n; i++) { int s1_code = getCharCode(s1.charAt(i) + ""); int s2_code = getCharCode(s2.charAt(i) + ""); if(s1_code * s2_code < 0) return Math.min(s1_code, s2_code); if(s1_code != s2_code) return s1_code - s2_code; }
return len1 - len2; }
public static String getBeginCharacter(String res) { String a = res; String result = ""; for(int i = 0; i < a.length(); i++) { String current = a.substring(i, i + 1); if(compare(current, "\u554A") < 0) result = result + current; else if(compare(current, "\u554A") >= 0 && compare(current, "\u5EA7") <= 0) if(compare(current, "\u531D") >= 0) result = result + "z"; else if(compare(current, "\u538B") >= 0) result = result + "y"; else if(compare(current, "\u6614") >= 0) result = result + "x"; else if(compare(current, "\u6316") >= 0) result = result + "w"; else if(compare(current, "\u584C") >= 0) result = result + "t"; else if(compare(current, "\u6492") >= 0) result = result + "s"; else if(compare(current, "\u7136") >= 0) result = result + "r"; else if(compare(current, "\u671F") >= 0) result = result + "q"; else if(compare(current, "\u556A") >= 0) result = result + "p"; else if(compare(current, "\u54E6") >= 0) result = result + "o"; else if(compare(current, "\u62FF") >= 0) result = result + "n"; else if(compare(current, "\u5988") >= 0) result = result + "m"; else if(compare(current, "\u5783") >= 0) result = result + "l"; else if(compare(current, "\u5580") >= 0) result = result + "k"; else if(compare(current, "\u51FB") > 0) result = result + "j"; else if(compare(current, "\u54C8") >= 0) result = result + "h"; else if(compare(current, "\u5676") >= 0) result = result + "g"; else if(compare(current, "\u53D1") >= 0) result = result + "f"; else if(compare(current, "\u86FE") >= 0) result = result + "e"; else if(compare(current, "\u642D") >= 0) result = result + "d"; else if(compare(current, "\u64E6") >= 0) result = result + "c"; else if(compare(current, "\u82AD") >= 0) result = result + "b"; else if(compare(current, "\u554A") >= 0) result = result + "a"; }
return result; }
public static String getFirstStr(String str) { char a = str.charAt(0); char aa[] = { a }; String sss = new String(aa); if(Character.isDigit(aa[0])) sss = "data"; else if(a >= 'a' && a <= 'z' || a >= 'A' && a <= 'Z') sss = "character"; else sss = getBeginCharacter(sss); return sss; }
private static String _FromEncode_ = "GBK"; private static String _ToEncode_ = "GBK";
}
|
Hibernate3.0 采用新的基于ANTLR的HQL/SQL查询翻译器,在Hibernate的配置文件中,hibernate.query.factory_class属性用来选择查询翻译器。
(1)选择Hibernate3.0的查询翻译器:
hibernate.query.factory_class= org.hibernate.hql.ast.ASTQueryTranslatorFactory
(2)选择Hibernate2.1的查询翻译器
hibernate.query.factory_class= org.hibernate.hql.classic.ClassicQueryTranslatorFactory
为了使用3.0的批量更新和删除功能,只能选择(1)否则不能解释批量更新的语句。选择(2)但没法解释批量更新语句了。
大批量更新/删除(Bulk update/delete)
就像已经讨论的那样,自动和透明的 对象/关系 映射(object/relational mapping)关注于管理对象的状态。 这就意味着对象的状态存在于内存,因此直接更新或者删除 (使用 SQL 语句 UPDATE 和 DELETE) 数据库中的数据将不会影响内存中的对象状态和对象数据。 不过,Hibernate提供通过Hibernate查询语言来执行大批 量SQL风格的(UPDATE)和(DELETE) 语句的方法。
UPDATE 和 DELETE语句的语法为: ( UPDATE | DELETE ) FROM? ClassName (WHERE WHERE_CONDITIONS)?。 有几点说明:
-
在FROM子句(from-clause)中,FROM关键字是可选的
-
在FROM子句(from-clause)中只能有一个类名,并且它不能有别名
-
不能在大批量HQL语句中使用连接(显式或者隐式的都不行)。不过在WHERE子句中可以使用子查询。
-
整个WHERE子句是可选的。
举个例子,使用Query.executeUpdate()方法执行一个HQL UPDATE语句:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlUpdate = "update Customer set name = :newName where name = :oldName";
int updatedEntities = s.createQuery( hqlUpdate )
.setString( "newName", newName )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
执行一个HQL DELETE,同样使用 Query.executeUpdate() 方法 (此方法是为 那些熟悉JDBC PreparedStatement.executeUpdate() 的人们而设定的)
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
String hqlDelete = "delete Customer where name = :oldName";
int deletedEntities = s.createQuery( hqlDelete )
.setString( "oldName", oldName )
.executeUpdate();
tx.commit();
session.close();
由Query.executeUpdate()方法返回的整型值表明了受此操作影响的记录数量。 注意这个数值可能与数据库中被(最后一条SQL语句)影响了的“行”数有关,也可能没有。一个大批量HQL操作可能导致多条实际的SQL语句被执行, 举个例子,对joined-subclass映射方式的类进行的此类操作。这个返回值代表了实际被语句影响了的记录数量。在那个joined-subclass的例子中, 对一个子类的删除实际上可能不仅仅会删除子类映射到的表而且会影响“根”表,还有可能影响与之有继承关系的joined-subclass映射方式的子类的表。
摘要: 第一部分、SQL&PL/SQL[Q]怎么样查询特殊字符,如通配符%与_[A]select * from table where name like 'A_%' escape ''[Q]如何插入单引号到数据库表中[A]可以用ASCII码处理,其它特殊字符如&也一样,如insert into t values('i'||chr(39)||'m'); -- chr(39)代表字符'或者用...
阅读全文
先记下,以后有用得着的时候也好找。
所见即所得(WYSIWYG - What You See Is What You Get)
1、
eWebEditor[Mode In China]
简介:
eWebEditor是eWebSoft.com旗下eWeb团队开发的基于网页的、所见即所得的在线HTML编辑器。她能够在网页上实现许多桌面编辑软件(如:Word)所具有的强大可视编辑功能;她是一个真正的绿色软件,不需要在计算机上安装任何的客户端软件;她的易用使得WEB开发人员只要一行代码即可完成调用。
当前最新版本:Version 3.8 (ASP版、PHP版、JSP版、ASP.NET版)
对于一般站点来说,eWebEditor已经很不错了,简单易上手。
使用方法/接口:
在线演示:
http://ewebeditor.webasp.net/demo.aspMailMagic下载:eWebEditor Version 2.8.0 修正版及绍文档
mailmagic://zoDAsdNafHdAAJzazaazvAoxzdDsadxsqdvNaxfAzfNdfxDzxqzqJzoAqiHfoioqaHxHNAnafsoNzJosfxdsDfnxxvdqvNaqnsnindNfdzsiJiaHJsAJAqzAinAHqNaoqHixiiHvAAzdNHHvnqqfxAndiHonqiAzsNdosvavAqiqvaNNJNqsqznovDqNdAvvvondaaNxiDiffdDsainsaqDifnxvJJaJADzAfafdHnJddxzJJiNHvdqqfviDvodiiAziAHxNnHnzAHnzHanvDisaDHDJHqJn
2、
HTMLArea简介:
HTMLArea is a free, customizable online editor. It works inside your browser. It uses a non-standard feature implemented in Internet Explorer 5.5 or better for Windows and Mozilla 1.3 or better (any platform), therefore it will only work in one of these browsers.
使用方法/接口:
在线演示:
http://www.dynarch.com/demos/htmlarea/MailMagic下载:HTMLArea release 3.0-rc1
mailmagic://qzsHqHnfAviDiNoqJvvisNHzsziqfzqxnzznosxnoDAvsvdxqfDqoJdiNiJnxAinsanxfznNnnAzoqvxdnqadJnnqsiJaxHsaassdaaaxAzDNxdDNHDzondfoNAJJodnaiqqzzziadsovfdfAJnoxzHAHsadosJsJffaNfHDnNoAxAvAsfnJADdqdvdsazifJNnHsxNxfavfJnzDfisnxzzfNAxfadnnvHDaxJfoqDHAzfNDsDxNNoizoviHNNaJ
3、
FCKeditor简介:
This HTML text editor brings to the web many of the powerful functionalities of desktop editors like MS Word. It's lightweight and doesn't require any kind of installation on the client computer.
Because it is Open Source, you may use it however you want.
使用方法/接口:
在线演示:
http://www.fckeditor.net/demo/default.htmlMailMagic下载:FCKeditor 2.2 released
mailmagic://zxsANfJqNosoAAosnqifNHnDNAxNHaadnHfdHJqAqiDNsNqJAqqqHandxADAfJodoDDzzvoqaHsNvDdvJzAJqvsdxqzasdavfsqvzaHfzHzxqaiNvvHifAzDonxqofHxzxaqDszxqfafAJffJdqsDvvNHNqJsvisooxixAAsfodfJanqfANHNzxvqsxoJszAHnNdqzqvNafaavHJovAvvxHxzqsiisHDNfHDiovfffznJxiAdNfDHxnifizDxdqq
4、
TinyMCE简介:
TinyMCE is a platform independent web based Javascript HTML WYSIWYG editor control released as Open Source under LGPL by Moxiecode Systems AB. It has the ability to convert HTML TEXTAREA fields or other HTML elements to editor instances. TinyMCE is very easy to integrate into other CMS systems.
使用方法/接口:
在线演示:
MailMagic下载:TinyMCE 2.0.4
mailmagic://JiDqnAddiHsJnDHDiiaDDAJiNAvDioAAdqNddoizHindzsDxofniHaxDzxNszxqxvavivHiNzxzisovNqqxaDadzDoaxAJdzdNdxozdfvnxozHHdfJqssaiDzvaiHNiiHdHxqzsJAHsJfdDdxHDqvADvNaJsnJovoDDAfaaDqDoiaasAqnJoAxinvssziiAqJNsxsdfvoxNNdoAoxiiDaanJnfsNAnqDnfxfazssfJssqJJzixvHnDqiaiodJiJq
5、
FreeTextBox简介:
The no.1 free ASP.NET HTML Editor for IE and Mozilla.
使用方法/接口:
在线演示:
http://www.freetextbox.com/MailMagic下载:FreeTextBox 3.0 Free License (free!)
mailmagic://oHNxNdiDzAxxofxiNHizdsaDADzfxoxfJsaNHdzAoaxNAdnaDvxNoxdxnxszqqANDoxJffJNAvixAoNnfiHasAzJJHAaJNJinJDNNioffviooqfHvffnnNHHxfvNfidnAoofdDfsaofAxafJoAJnNfanodovJofHAJzqanfJzqHdzvaJsosixaNaoxHNodfsHzzzdsDnovvJisNqvdqfzHNnzoaAoxiJdodoJdfiJisnvHfqdffHonsDioAvofan
6、
widgEditor[
支持XHTML]
简介:
widgEditor is an easily installed, easily customisable WYSIWYG editor for simple content. It replaces existing textareas with an improved editing pane using JavaScript, therefore if you don't have JavaScript (or your browser doesn't support HTML editing) it degrades gracefully. Try it out by typing below, and submit to see the processed output.
使用方法/接口:
在线演示:
http://www.themaninblue.com/experiment/widgEditor/MailMagic下载:widgEditor
mailmagic://xxvvvsHAzAzJdAffaqNqAdxfNqvxqJsndsHNzDDJqadAAfqqzHaxnNsfdqDaxzfnNdnddzivsfnaNoixzqziDAvsiqqfqiqvAdzxfzDnvqNiNnadivAzdvqxzvoDfdJfNnxaqovnAfJfDnxiDvNDvqaAvqNizqzodfAfqHvNofzxzsivaxaqioaavHvDNqqDJJnzvqsoHxozoqniaJfavaisoHqaDJJffJdzHiHqfisisssdoDaoiAaJHiNDsddi
这篇文章对主流的在线WYSIWYG编辑器做了个详细的比较:
http://www.geniisoft.com/showcase.nsf/WebEditors
物化视图是包括一个查询结果的数据库对像,它是远程数据的的本地副本,或者用来生成基于数据表求和的汇总表。物化视图存储基于远程表的数据,也可以称为快照。
物化视图可以查询表,视图和其它的物化视图。
通常情况下,物化视图被称为主表(在复制期间)或明细表(在数据仓库中)。
对于复制,物化视图允许你在本地维护远程数据的副本
,
这些副本是只读的。如果你想修改本地副本,必须用高级复制的功能。当你想从一个表或视图中抽取数据时,你可以用从物化视图中抽取。
对于数据仓库,创建的物化视图通常情况下是聚合视图,单一表聚合视图和连接视图。
本文我们将会看到怎样创建物化视图并且讨论它的刷新选项。
在复制环境下,创建的物化视图通常情况下主键,
rowid,
和子查询视图。
1.
主键物化视图:
下面的语法在远程数据库表
emp
上创建主键物化视图
SQL> CREATE MATERIALIZED VIEW mv_emp_pk
REFRESH FAST START WITH SYSDATE
NEXT SYSDATE + 1/48
WITH PRIMARY KEY
AS SELECT * FROM emp@remote_db;
Materialized view created.
注意:当用
FAST
选项创建物化视图,必须创建基于主表的视图日志
,
如下
:
SQL> CREATE MATERIALIZED VIEW LOG ON emp;Materialized view log created.
2.
Rowid
物化视图
下面的语法在远程数据库表
emp
上创建
Rowid
物化视图
SQL> CREATE MATERIALIZED VIEW mv_emp_rowid
REFRESH WITH ROWID
AS SELECT * FROM emp@remote_db;
Materialized view log created.
3.
子查询物化视图
下面的语法在远程数据库表
emp
上创建基于
emp
和
dept
表的子查询物化视图
SQL> CREATE MATERIALIZED VIEW mv_empdept
AS SELECT * FROM emp@remote_db e
WHERE EXISTS
(SELECT * FROM dept@remote_db d
WHERE e.dept_no = d.dept_no)
Materialized view log created.
REFRESH
子句
[refresh [fast|complete|force]
[on demand | commit]
[start with date] [next date]
[with {primary key|rowid}]]
Refresh
选项说明
:
a.
oracle
用刷新方法在物化视图中刷新数据
.
b.
是基于主键还是基于
rowid
的物化视图
c.
物化视图的刷新时间和间隔刷新时间
Refresh
方法
-FAST
子句
增量刷新用物化视图日志(参照上面所述)来发送主表已经修改的数据行到物化视图中
.
如果指定
REFRESH FAST
子句,那么应该对主表创建物化视图日志
SQL> CREATE MATERIALIZED VIEW LOG ON emp;
Materialized view log created.
对于增量刷新选项,如果在子查询中存在分析函数,则物化视图不起作用。
Refresh
方法
-COMPLETE
子句
完全刷新重新生成整个视图,如果请求完全刷新,
oracle
会完成
完全刷新即使增量刷新可用。
Refresh Method – FORCE
子句
当指定
FORCE
子句,如果增量刷新可用
Oracle
将完成增量刷新,否则将完成完全刷新
,
如果不指定刷新方法
(FAST, COMPLETE, or FORCE),Force
选项是默认选项
主键和
ROWD
子句
WITH PRIMARY KEY
选项生成主键物化视图
,
也就是说物化视图是基于主表的主键,而不是
ROWID(
对应于
ROWID
子句
). PRIMARY KEY
是默认选项
,
为了生成
PRIMARY KEY
子句,应该在主表上定义主键,否则应该用基于
ROWID
的物化视图
.
主键物化视图允许识别物化视图主表而不影响物化视图增量刷新的可用性。
Rowid
物化视图只有一个单一的主表,不能包括下面任何一项
:
n
Distinct
或者聚合函数
.
n
Group by
,子查询,连接和
SET
操作
刷新时间
START WITH
子句通知数据库完成从主表到本地表第一次复制的时间
,
应该及时估计下一次运行的时间点
, NEXT
子句说明了刷新的间隔时间
.
SQL> CREATE MATERIALIZED VIEW mv_emp_pk
REFRESH FAST
START WITH SYSDATE
NEXT SYSDATE + 2
WITH PRIMARY KEY
AS SELECT * FROM emp@remote_db;
Materialized view created.
在上面的例子中,物化视图数据的第一个副本在创建时生成,以后每两天刷新一次
.
总结
物化视图提供了可伸缩的基于主键或
ROWID
的视图
,
指定了刷新方法和自动刷新的时间。
JavaScript--正则表达式
正则表达式(regular expression)对象包含一个正则表达式模式(pattern)。它具有用正则表达式模式去匹
配或代替一个串(string)中特定字符(或字符集合)的属性(properties)和方法(methods)。
正则表达式构造函数: new RegExp("pattern"[,"flags"]);
参数说明:
pattern -- 一个正则表达式文本
flags -- 如果存在,将是以下值:
g: 全局匹配
i: 忽略大小写
gi: 以上组合
在构造函数中,一些特殊字符需要进行转意(在特殊字符前加"\")。正则表达式中的特殊字符:
字符 含意
\ 转意,即通常在"\"后面的字符不按原来意义解释,如/b/匹配字符"b",当b前面加了反斜杆后/\b/,转意为
匹配一个单词的边界。
-或-
对正则表达式功能字符的还原,如"*"匹配它前面元字符0次或多次,/a*/将匹配a,aa,aaa,加了"\"后,/a\*/
将只匹配"a*"。
^ 匹配一个输入或一行的开头,/^a/匹配"an A",而不匹配"An a"
$ 匹配一个输入或一行的结尾,/a$/匹配"An a",而不匹配"an A"
* 匹配前面元字符0次或多次,/ba*/将匹配b,ba,baa,baaa
+ 匹配前面元字符1次或多次,/ba*/将匹配ba,baa,baaa
? 匹配前面元字符0次或1次,/ba*/将匹配b,ba
(x) 匹配x保存x在名为$1...$9的变量中
x|y 匹配x或y
{n} 精确匹配n次
{n,} 匹配n次以上
{n,m} 匹配n-m次
[xyz] 字符集(character set),匹配这个集合中的任一一个字符(或元字符)
[^xyz] 不匹配这个集合中的任何一个字符
[\b] 匹配一个退格符
\b 匹配一个单词的边界
\B 匹配一个单词的非边界
\cX 这儿,X是一个控制符,/\cM/匹配Ctrl-M
\d 匹配一个字数字符,/\d/ = /[0-9]/
\D 匹配一个非字数字符,/\D/ = /[^0-9]/
\n 匹配一个换行符
\r 匹配一个回车符
\s 匹配一个空白字符,包括\n,\r,\f,\t,\v等
\S 匹配一个非空白字符,等于/[^\n\f\r\t\v]/
\t 匹配一个制表符
\v 匹配一个重直制表符
\w 匹配一个可以组成单词的字符(alphanumeric,这是我的意译,含数字),包括下划线,如[\w]匹配"$5.98"
中的5,等于[a-zA-Z0-9]
\W 匹配一个不可以组成单词的字符,如[\W]匹配"$5.98"中的$,等于[^a-zA-Z0-9]。
说了这么多了,我们来看一些正则表达式的实际应用的例子:
HTML代码的屏蔽
function mask_HTMLCode(strInput) {
var myReg = /<(\w+)>/;
return strInput.replace(myReg, "<$1>");
}
E-mail地址验证:
function test_email(strEmail) {
var myReg = /^[_a-z0-9]+@([_a-z0-9]+\.)+[a-z0-9]{2,3}$/;
if(myReg.test(strEmail)) return true;
return false;
}
正则表达式对象的属性及方法:
预定义的正则表达式拥有有以下静态属性:input, multiline, lastMatch, lastParen, leftContext,
rightContext和$1到$9。其中input和multiline可以预设置。其他属性的值在执行过exec或test方法后被根据
不同条件赋以不同的值。许多属性同时拥有长和短(perl风格)的两个名字,并且,这两个名字指向同一个值。(
JavaScript模拟perl的正则表达式)
正则表达式对象的属性:
属性 含义
$1...$9 如果它(们)存在,是匹配到的子串
$_ 参见input
$* 参见multiline
$& 参见lastMatch
$+ 参见lastParen
$` 参见leftContext
$'' 参见rightContext
constructor 创建一个对象的一个特殊的函数原型
global 是否在整个串中匹配(bool型)
ignoreCase 匹配时是否忽略大小写(bool型)
input 被匹配的串
lastIndex 最后一次匹配的索引
lastParen 最后一个括号括起来的子串
leftContext 最近一次匹配以左的子串
multiline 是否进行多行匹配(bool型)
prototype 允许附加属性给对象
rightContext 最近一次匹配以右的子串
source 正则表达式模式
lastIndex 最后一次匹配的索引
正则表达式对象的方法:
方法 含义
compile 正则表达式比较
exec 执行查找
test 进行匹配
toSource 返回特定对象的定义(literal
representing),其值可用来创建一个新的对象。重载Object.toSource方法得到的。
toString 返回特定对象的串。重载Object.toString方法得到的。
valueOf 返回特定对象的原始值。重载Object.valueOf方法得到
例子:
<script language = "JavaScript">
var myReg = /(w+)s(w+)/;
var str = "John Smith";
var newstr = str.replace(myReg, "$2, $1");
document.write(newstr);
</script>
将输出"Smith, John"
运行regedit,编辑注册表
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ContentIndex
右侧FilterFilesWithUnknownExtensions子键(REG_DEWORD类型)
的键值改为1。
数据仓库应该说还是一个正在发展中的事物,它的技术发展及应用的
动力是商业竞争的需要。因而其定义及分类也会随着时代的发展而有
所变化。目前流行的描述方法是:"数据仓库是把企业中各分散的原始
操作数据和来自外部的数据汇集和整理成一个单一的关系数据库,为
企业提供完整、及时、准确和明了的商业决策信息,并使最终用户可
以直接从数据仓库提取信息来进行各种决策分析"。
从数据仓库的数据来源及实施环境来看,它离不开数据库,数据仓
库技术与数据库技术有紧密联系,二者能相互连通,相互支持,又有着
重要差别。主要是数据仓库侧重于决策分析,而数据库则侧重于操作
和管理。
其中Data Mart(专用数据仓库)是为满足用户特定需求而创建的
数据仓库,具有传统意义上的数据仓库的基本特征,可以看成是中央数
据仓库的一个子集,是针对某个部门级的应用( 如市场、财务、销售
、产品控制等)而建立的数据仓库。企业最高级集中化的数据仓库可
称之为原子化(Atomic)数据仓库。它主要用作其各个子集——Data M
art的集结基础(Staging ground)以及作为参照(reference)用的中央
数据仓库。
各个Data Mart物理上往往散布到与最终用户较近的地点以节约
通信开销并提高响应速度。故相对于集中化的数据仓库而言,把Data
Mart又称分布式的数据仓库。与中央数据仓库相比它与(某组)用户更
具交互性。
应着重指出的是,建立Data Mart的着眼点主要在于它的目标而不
是规模。一般情况下D ata Mart比中央数据仓库小一些,比如在大型
保险公司的数据仓库中所涉及的"主题"可能是保险金、客户等,而在
其地区办事处建立的Data Mart可能仅涉及这些主题的一个子集——
本地区的客户、保险金等。
交互式数据仓库解决方案Sybase IQ
作为国际著名数据库厂商之一的Sybase公司推出了交钥匙型的数
据仓库解决方案。
Sybase交互式数据仓库解决方案的着眼点是以较低的花费,按一
个易于管理和扩充的基础框架为企业用户和信息技术部门提供快速的
解决方案。
Sybase的特色产品便是Sybase IQ,它能方便地为用户建立Data M
art,即部门级的数据仓库,或"面向应用系统的数据仓库"。
为使各Data Mart能与该企业中其它Data Mart保持一致的增长和
维护,在创建时就应有充分的考虑。因而Sybase推出了一个名为"Quic
k Start Data Mart"的交钥匙型解决方案。它是以Sybase IQ为核心,
由Sybase及第三方(如:Business Object等)的产品和服务打包而成。
另外还包括了必要的专家咨询和技术培训,以保证在短期内为用户创
建一个实用的Data M art。
这一方案的特点包括:
●能够快速获得效益,风险低;
●以较经济的方式能够对大量用户的即席查询给出极快的响应;
。
●易于实现企业内部数据仓库与Inernet应用的有机结合。
Sybase IQ技术
Sybase IQ是针对DSS应用而提供的一个特定的数据库服务器,它
能上百倍地提高交互式即席查询的速度,特别适用于交互式数据仓库
环境。
Sybase IQ服务器可透明地集成到Sybase的开放性客户/服务器体
系结构中,它使用标准的SQL命令,支持DB-Lib、CT-Lib和ODBC客户调
用。因而,利用Isql及各种流行的前端工具,如PowerBuilder、Access
、SAS Assist及Business Object等都可方便地访问Sybase IQ服务器
。
Sybase IQ服务器可与SQL Server混合使用。一个内置的智能查
询管理器可识别并透明地将对数据库的更新传递到SQL Server以完成
OLTP;同时利用Sybase IQ来解决用于DSS的即席查询。Sybase IQ的Bi
t-Wise索引可定期地在非高峰时刻批量地进行更新。SQL Server和Sy
base IQ有一个共同集成的目录(Catalog),这样,在Sybase环境中可统
一管理安全性和其他服务,Sybase IQ也可有效地支持SMP环境及多用
户并发操作。
Sybase IQ服务器的技术特色
Sybase IQ服务器的主要特点为:
●数据的垂直分割——数据是按"列"来储存。查询时仅需读取在
该查询中所涉及的各列的索引,因而彻底避免了耗时的全表扫描。
●采用了突破性的数据存取方法——Bit-Wise索引技术。
●在数据压缩和并行处理方面有独到之处。
●提供有效的预连接(PreJoin)策略。
Sybase IQ服务器的核心由于采用了正在申请专利的Bit-Wise索
引技术,它在处理复杂的即席查询时,能比传统的B-Tree索引能提高响
应速度10~100倍。
对B-Tree及Bit-Map索引的简要回顾
1.传统的B-Tree(平衡树)索引是通过跟踪一个选择的数据字段的
值和直接指向包含该数据项的数据页的方式来提供快速查找数据记录
。
图1 传统的B-Tree 树索引
若为一个特定的客户查找信息记录就只有快速读取索引,然后直
接定位到包含该客户信息的数据页中。索引方式通过直接指向相关信
息来消除高代价的表扫描。
B-Tree非常适合于查找并取回少量的行。但对典型的数据仓库的
复杂交互式查询应用B -Tree有三个缺点:
首先,B-Tree只在索引是高度可选择(Highly Selective)又称之
为高基数(Cardinality )的时候才有价值,对一些唯一值极少的(低基
数)数据字段就几乎毫无价值。
B-Tree索引第二个限制是在数据仓库中构造和维护索引的代价。
由于B-Tree索引包含实际数据和其他信息(如指针等),因而使得索引
需占用一定的空间和时间。如果构造所有相关的索引,数据仓库就会
占据2~4倍原始数据空间。当成批插入删除时,索引就非常敏感,有可
能失去平衡并降低性能。通常说来,10%~15%的数据修改会导致重建
索引。
最后,B-Tree索引设计用于简单查询及已知公共存取路径的环境
下才有优点,而在数据仓库应用中,通常是复杂的查询,并经常带有分
组及聚合条件。此时,B-Tree索引往往是无能为力。
2.Bit-Map(位图)索引和使用B-Tree相比,位图为每个记录的名字
段用给定真或假("1"或"0")的方式表示。
传统的位图索引突破了B-Tree索引的一些限制。位图索引可以非
常有效地对低基数数据进行索引,但位图式索引却不适用于高基数数
据,在聚集数据、完成关系联结和取回原始数据值等方面的效率不高
。
Bit-Wise的索引技术特点
1.Sybase IQ在存储数据的方式上与传统的RDBMS完全不同,它不
是以"记录行"而是按"列"为单位来存储数据。即所谓的"对数据进行
垂直分割",它在Bit-Map索引基础上扩充存放了该字段中的不同取值,
一般的DSS查询往往仅涉及大量数据记录中的少数列,因而不需访问原
始数据就能快速获得查询结果。显然,利用这些不同取值也就能快速
地进行数据聚集、分组、求最大值、最小值及平均值等。它弥补了位
图式索引不适用于高基数数据的缺点。
2.由于列数据比行数据更易进行压缩,因而Sybase IQ以压缩方式
存放原始数据时可节省50%的磁盘空间,在获取查询结果数据时减少了
I/O次数,响应速度自然加快。
3.Sybase IQ采用操作系统级的并行处理机制,因而像分类、Bit-
Map操作及I/O操作都可由操作系统独立地进行调度以消除瓶颈,进而
在数据加载时其能充分利用SMP环境中多CPU的资源潜力。
Bit-Wise"数据驱动的"索引机制在Sybase IQ中对每个字段所建
索引的类型和个数主要是取决于其数据的特征和经常被使用的情况而
不是针对某组特定的查询(后者被称为"查询驱动的"索引机制)。Bit-
Wise索引是"数据驱动的",因而无需DBA为各种查询分别考虑优化策略
。
Sybase IQ支持的主要索引类型为:
·LowFast-LF(低基数)
·HighGroup-HG(高基数)
·HighNonGroup-HNG(高基数)
·FastProjiectinFP
建立Bit-Wise索引的一般原则
1)在每个列上都建FP索引;
2)在唯一值<1000的字段上建立LF索引;
3)在参加连接的字段上建HG或LF索引;
4)唯一值>1000且用在GROUP BY,SELCT DISITICT,COUNT DISITIC
T中的字段上建立HG索引;
5)此外的其它列若出现在where条件中或聚集运算中,则建立HNG
索引。
Sybase IQ可利用预连接技术来提高即席查询速度
Sybase IQ支持两类连接处理:一类是即席连接(在查询时处理);
另一类是预连接(在加载时处理)。大多数应用是二者的结合。
即席连接适用于多对多的联系,被连接的表之间的尺寸差别超过1
0倍。
预连接适用于一对多的联系,被连接的表之间的尺寸差别不大于1
0倍,如:等值或左、右外连接。但预连接将导致增大存储量并降低加
载速度。
Sybase IQ能对已建立了HG或LF索引的列,再利用"Joined Indexs
et"完成预连接处理。它通常能使查询速度提高10倍以上。
我们可通过下表的比较来进一步理解Bit-Wise索引的技术特色:
表1
设置Sybase IQ的基本步骤
在安装Sybase IQ产品之后:
1.建立与Sybase IQ Indexspace相关的SQL Server基础数据库,I
ndexspace是SybaseIQ 用于存储索引结构和部分数据的原始设备(raw
devices)或系统文件;
2.建立同基础数据库同名的Sybase IQ Indexspace,最大为2GB,I
/O块同压缩比有关,调节范围512B到32KB;
3.在基础数据库上建表;
4.运行Sybase IQ的存储过程,估算所需空间;
5.为表中的某列或所有列建立Sybase IQ Indexsets,Indexsets
是Sybase IQ存储表中某列索引空间的,它存储于Indexspace中;
6.定义不同Sybase IQ的索引类型,(每列都建立一个FP索引然后
酌情建立别种索引);
7.往Sybase IQ Indexset中加入新数据,从相关的数据库中插入
数据到Sybase IQ Inde xes或利用ASCII文件加载;
8.建立供预连接用的Joined Indexset;
9.往Joined Indexset中加入新数据。
现在已可通过Isql或其他前端工具查询Sybase IQ数据了。
测试比较
经实际测试,Sybase IQ效果良好。
前言:
一个很不错的php开源项目管理工具,有了它,不用project了,也不用自己开发项目管理工具了,足够应付中小型软件企业的日常项目管理工作。
1、软件需求:
easyphp1.8 dotProject_2_0_1_20050705_UTF-8_Chinese_by_Markwu2、安装:
1)首先安装:
easyphp1.8 ;启动apache/mysql/php服务。
创建MySql数据库和用户:
在Dos窗口下进入到MySql\bin下,输入命令:mysql -uroot -p 回车(默认安装时没密码)然后执行下面语句。
CREATE DATABASE dotproject;
GRANT ALL PRIVILEGES ON dotproject.* TO 'dp_user'@'localhost' IDENTIFIED BY 'pw';
2)解压dotProject_2_0_1_20050705_UTF-8_Chinese_by_Markwu到www目录下,改目录名为dotproject。注意要用这个utf-8版本,从dotproject下载的版本中文会乱码。
3)执行安装:
http://yourserverip/dotproject/install/index.php,则可以完成安装过程,包括数据库的建立。
4)到dotproject/index.php登陆,admin-passwd.
5)到system admin->default user confonrce,设置默认语言为简体中文。
6)打开gd支持以显示甘特图:在easyPHP面板,配置->php扩展,把php-gd2选上,再重启apache,则即可显示甘特图。
7)重新登陆dotproject,完成整个安装过程。
//注意:如果自己更新语言包应放在www\dotproject\locales下;
如果mysql用高版本的应运行如下语句:
先用root登录MYSQL服务器,执行
mysql>set password for dp_user@"localhost"=old_password('yourPassword');
原因是因为你使用的mysql服务器版本中使用了新的密码验证机制,这需要客户端的版本要在4.0以上,原来的密码函数被改为old_password();,这样使用password()生成的密码在旧的版本上的客户端就不好使了,而PHP中的MYSQL客户端都是3.23的(当然,mysqli的扩展除外),问题就在这了。
服务器重整以后,cvs update/commit时候会出现如下错误提示:
cvs server: cannot open directory . for empty check: Invalid argument
虽然不影响使用,但是却很让人不爽。
而chacl时候,则是:
cvs [server aborted]: cannot find .: No such file or directory
干脆就直接出错。研究了一下cvsnt里面的设置,感觉应该只和cvsroot及temp目录有关——毕竟只用到这两个目录而已。检查之后发现winnt/temp没有对cvsuser这个系统帐号的权限。设置之后问题解决。
虽然cvsuser只是一个停用了的cvs用户绑定帐号,但是权限管理方面仍然遵循着ntfs的规则。这么说来停用的意义应该仅仅是限制登录。
MyEclipse 4.1 Milestone2 has enhanced the JavaScript Editor and Debugger to include the following features:
- Editor
-
Syntax highlighting
-
Integrated outline and property views
-
Validation and syntax checking
-
Code assist for common JavaScript elements, functions, and variables
-
Code assist documentation for functions
-
Source formatting
-
Toggle comments
- Debugger
- Debug any web page containing JavaScript source or included JavaScript files, or standalone JavaScript files
- Operations: Pause, Resume, Step Into, Step Over, Step to Return, Terminate
- Views:
- Web Browser View
- Call Stack View
- Variables View
- Console View
- Set JavaScript breakpoints in:
- JavaScript files
- HTML with embedded JavaScript and linked JavaScript files
- JSP files
- Conditional breakpoints planned for MyEclipse 4.1.1
- Integrates with Eclipse Launch Manager
- Quick launch from context-menu using "Debug As"
- Launch from Eclipse Debug toolbar action
- Use Launch Profile to modify launch configuration
- Launch JavaScript resource from project location or deployed MyEclipse web-application
|
|
JavaScript Outline View |
The JavaScript outline view depicts method and variable declarations in their associated scope. Variables can be filtered from the view. Future releases will support for more complex JavaScript constructs.
|
|
JavaScript Validation and Syntax Checking |
The JavaScript Editor now has syntax validation with error and warning markers in the editor and Problems view. Varying levels of JavaScript validation can be configured in the MyEclipse preferences.
The JavaScript validation includes warnings for your javascript source code.
|
|
Code Assist for JavaScript |
Code assist is provided for most common DHTML objects like document, window, element, etc. The IE and Netscape icons identify browser support for the function or property. The MyEclipse JavaScript Editor preferences enable the customization of code-assist proposals to filter browser specific proposals and to hide the icons.
The code assist also provides proposals that include the current functions defined in the JavaScript file.
Global and local variables are also included in the code-assist proposals..
|
|
JavaScript Source Code Formatting |
The JavaScript editor now has an improved source code formatting.
|
|
JavaScript Quick Comment |
Quickly toggle single line comments on/off using the Ctrl+Shift+C key sequence or use the Source menubar action. Select a region or set the cursor on the line to toggle as a comment.
Enter Ctrl+Shift+C to add or remove the single line comments. |
|
MyEclipse JavaScript Debugger |
The MyEclipse JavaScript Debugger (Milestone 2) is the first fully integrated client-side Eclipse debugger for JavaScript. Feature highlights include:
- Debug any web page containing JavaScript source or included JavaScript files, or standalone JavaScript files
- Operations: Pause, Resume, Step Into, Step Over, Step to Return, Terminate
- Views:
-
- Web Browser View
- Call Stack View
- Variables View
- Console View
- Set JavaScript breakpoints in:
-
- JavaScript files
- HTML with embedded JavaScript and linked JavaScript files
- JSP files
- Conditional breakpoints planned for MyEclipse 4.1.1
- Integrates with Eclipse Launch Manager
-
- Quick launch from context-menu using "Debug As"
- Launch from Eclipse Debug toolbar action
- Use Launch Profile to modify launch configuration
- Launch JavaScript resource from project location or deployed MyEclipse web-application
Features planned for MyEclipse 4.1.1:
- Conditional JavaScript breakpoints
- JavaScript Snippet Evaluation Editor
- Smart Browser session organization
|
MyEclipse JavaScript Debugger -- Setting JavaScript breakpoints |
Set breakpoints in JavaScript, HTML and JSP using traditional breakpoint setting facilities.
<html BP image here>
Breakpoints appear in the left margin as a blue ball image,
|
MyEclipse JavaScript Debugger -- Quick JavaScript Launcher |
Launch JavaScript debuggable resources from the Debug As actions on the toolbar and the context-menu of the HTML, JSP and JavaScript editors or from the Run menubar.
|
|
MyEclipse JavaScript Debugger -- Configuring a JavaScript Launch Profile |
A custom JavaScript launch profile is created or edited using the Run>Debug... action from the menubar, toolbar, or context-menu context-menu of the HTML, JSP and JavaScript editors. The launch profile's Project and launch URL are provided. The launch URL can reference a deployed web resource, e.g., http://192.168.1.101/helloworld/helloajax.html or a local file in your project workspace, e.g., file://C:/...,
|
|
MyEclipse JavaScript Debugger -- Breakpoint/Stepping Operations in JavaScript |
When a JavaScript breakpoint is encountered in the browser, the source file containing the breakpoint opened in the corresponding editor and positioned to the breakpoint line-number. The JavaScript Debugger controls allow you to then Resume execution, Step Into a function at the next line, Step Over a function at the next line, Step to End of the current function, or Terminate the current application thread,
|
|
MyEclipse JavaScript Debugger -- Support for breakpoints in include JavaScript files |
The JavaScript Debugger supports breakpoints set in both top-level and included resources. In the following screenshot the breakpoint is set in a JavaScript file that is included in an HTML file.
|
|
|
New JavaScript Editor |
The new and improved Javascript Editor have new Outline View support for javascript variables and methods, content assist for javascript objects, and a preview capability.

Smart content assist is available for the javascript document object. Notice, that both IE and Netscape specifc objects are shown.

The javascript editor also have preview support using a builtin browser that will enable testing of javascript.

|
AJAX开发者的最新工具和技术
基于XML的异步JavaScript,简称AJAX,是当前Web创新(称为Web2.0)中的一个王冠。感谢组成AJAX的各种技术,Web应用的交互如Flickr, Backpack和Google在这方面已经有质的飞跃。这个术语源自描述从基于网页的Web应用到基于数据的应用的转换。在基于数据的应用中,用户需求的数据如联系人列表,可以从独立于实际网页的服务端取得并且可以被动态地写入网页中,给缓慢的Web应用体验着色使之像桌面应用一样。
虽然大部分开发人员在过去使用过XMLHttp或者使用Iframe来加载数据,但仅到现在我们才看到传统的开发人员和公司开始采用这些技术。就像新的编程语言或模型伴随着更多的痛苦,开发人员需要学习新的技巧及如何最好利用这些新技术。
AJAX模式
许多重要的技术和AJAX开发模式可以从现有的知识中获取。例如,在一个发送请求到服务端的应用中,必须包含请求顺序、优先级、超时响应、错误处理及回调,其中许多元素已经在Web服务中包含了,就像现在的SOA。AJAX开发人员拥有一个完整的系统架构知识。同时,随着技术的成熟还会有许多地方需要改进,特别是UI部分的易用性。
AJAX开发与传统的CS开发有很大的不同。这些不同引入了新的编程问题,最大的问题在于易用性。由于AJAX依赖浏览器的JavaScript和XML,浏览器的兼容性和支持的标准也变得和JavaScript的运行时性能一样重要了。这些问题中的大部分来源于浏览器、服务器和技术的组合,因此必须理解如何才能最好的使用这些技术。
综合各种变化的技术和强耦合的客户服务端环境,AJAX提出了一种新的开发方式。AJAX开发人员必须理解传统的MVC架构,这限制了应用层次之间的边界。同时,开发人员还需要考虑CS环境的外部和使用AJAX技术来重定型MVC边界。最重要的是,AJAX开发人员必须禁止以页面集合的方式来考虑Web应用而需要将其认为是单个页面。一旦UI设计与服务架构之间的范围被严格区分开来后,开发人员就需要更新和变化的技术集合了。
时刻想着用户
AJAX的最大机遇在于用户体验。在使应用更快响应和创新的过程中,定义Web应用的规则正在被重写;因此开发人员必须更注重用户。现在用户已经逐渐习惯如何使用Web应用了。例如用户通常希望每一次按钮点击会导致几秒的延迟和屏幕刷新,但AJAX正在打破这种长时间的状况。因此用户需要重新体验按钮点击的响应了。
可用性是AJAX另人激动的地方而且已经产生了几种新颖的技术。其中最引人注目的是一种称为“黄色隐出”的技术,他在数据更新之前时将用户界面变为黄色,更新完成后立刻恢复原来的颜色。AJAX开发人员将用户从Web应用的负载中解放出来;小心地利用AJAX提供的丰富接口,不久桌面开发人员会发现AJAX是他们的方向。
几种工具和技术
随着AJAX迅速地引人注目起来,我想开发人员对这种技术的期待也迅速地增加。就像任何新技术,AJAX的兴旺也需要一整个开发工具/编程语言及相关技术系统来支撑。
JavaScript
如名字所示AJAX的概念中最重要而最被忽视的是他也是一种JavaScript编程语言。JavaScript是一种粘合剂使AJAX应用的各部分集成在一起。在大部分时间,JavaScript通常被服务端开发人员认为是一种企业级应用不需要使用的东西应该尽力避免。这种观点来来自以前编写JavaScript代码的经历:繁杂而又易出错的语言。类似的,他也被认为将应用逻辑任意地散布在服务端和客户端中,这使得问题很难被发现而且代码很难重用。在AJAX中JavaScript主要被用来传递用户界面上的数据到服务端并返回结果。XMLHttpRequest对象用来响应通过HTTP传递的数据,一旦数据返回到客户端就可以立刻使用DOM将数据放到网面上。
XMLHttpRequest
XMLHttpRequest对象在大部分浏览器上已经实现而且拥有一个简单的接口允许数据从客户端传递到服务端,但并不会打断用户当前的操作。使用XMLHttpRequest传送的数据可以是任何格式,虽然从名字上建议是XML格式的数据。
开发人员应该已经熟悉了许多其他XML相关的技术。XPath可以访问XML文档中的数据,但理解XML DOM是必须的。类似的,XSLT是最简单而快速的从XML数据生成HTML或XML的方式。许多开发人员已经熟悉Xpath和XSLT,因此AJAX选择XML作为数据交换格式有意义的。XSLT可以被用在客户端和服务端,他能够减少大量的用JavaScript编写的应用逻辑。
CSS
为了正确的浏览AJAX应用,CSS是一种AJAX开发人员所需要的重要武器。CSS提供了从内容中分离应用样式和设计的机制。虽然CSS在AJAX应用中扮演至关重要的角色,但他也是构建创建跨浏览器应用的一大阻碍,因为不同的浏览器厂商支持各种不同的CSS级别。
服务器端
但不像在客户端,在服务端AJAX应用还是使用建立在如Java,.Net和PHP语言基础上机制;并没有改变这个领域中的主要方式。
既然如此,我们对Ruby on Rails框架的兴趣也就迅速增加了。在一年多前,Ruby on Rails已经吸引了大量开发人员基于其强大功能来构建Web和AJAX应用。虽然目前还有很多快速应用开发工具存在,Ruby on Rails看起来已经储备了简化构建AJAX应用的能力。
开发工具
在实际构建AJAX应用中,你需要的不只是文本编辑器。既然是JavaScript非编译的,他可以容易地编写和运行在浏览器中;然而,许多工具提供了有用的扩展如语法高亮和智能完成。
不同的IDE提供了对JavaScript支持的不同等级。来自JetBrains的IntelliJ IDEA是一个用来JavaScript开发的更好的IDE,虽然许多开发人员也喜欢Microsoft’s Visual Studio产品(允诺会在最新的版本中改善对AJAX的支持)。Eclipse包含了两个免费的JavaScript编辑器插件和一个商业的来自ActiveStat的Komodo IDE。
另一个JavaScript和AJAX开发中的问题是调试困难。不同的浏览器提供不同的通常是隐藏的运行时错误信息,而JavaScript的缺陷如双重变量赋值(通常是由于缺少数据类型)使得调试更加困难。在AJAX的开发中,调试就更复杂了,因为其需要标识究竟是客户端还是服务端产生的错误。在过去,JavaScript调试的方法是删除所有代码然后一行行的增加直到错误出现。现在,更多开发人员回到为IE准备的Microsoft Script Debugger和为Mozilla浏览器准备的Venkman。
浏览器兼容性
JavaScript编程的最大问题来自不同的浏览器对各种技术和标准的支持。构建一个运行在不同浏览器(如IE和火狐)是一个困难的任务。因此几种AJAX JavaScript框架或者生成基于服务端逻辑或标记库的JavaScript,或者提供符合跨浏览器AJAX开发的客户端JavaScript库。一些流行的框架包括:AJAX.Net, Backbase, Bitkraft, Django, DOJO, DWR, MochiKit, Prototype, Rico, Sajax, Sarissa, and Script.aculo.us.
这些框架给开发人员更多的空间使得他们不需要担心跨浏览器的问题。虽然这些框架提升了开发人员构建应用的能力,但由于厂商已经开发了更细节的用户界面的打包组件解决方案,因此在AJAX组件市场中需要考虑一些其他因素。例如提供通用用户界面的组件如组合框和数据栅格的几个厂商,都可以被用来在应用中创建良好的通过类似电子数据表方式来查看和编辑数据的体验。但这些组件不仅是封装了组件的用户界面而且包括与服务端数据的通讯方式,这些组件通常使用基于标记方式来实现如ASP.Net或JSF控件。
展望
最近IE和火狐之间的浏览器之争变得火热起来,因此AJAX开发人员需要足够敏捷的作出反应。关键点在一些问题如CSS或XML,虽然各种浏览器形成采用最新标准的不同阵营(如Mozilla拥抱SVG和E4X标准及在最新火狐BETA版本中使用XUL,而微软使用自己的XAML技术)。所有这些技术代表当前AJAX主流JavaScript和XML的市场方向改变。
总的来说,AJAX开发人员必须尽快地跟进最新的技术并利用高产的工具集。成功的AJAX开发人员还需要留心他们的使用者以避免将任何问题扩大化。并且AJAX开发人员还需要持续地创新来创建增强Web应用易用性的新方法。
用Axis 1.1 for Java进行Web Services开发 下面的所有几乎来自Axis1.1的文档。但不完全来自文档,本人做了部分修改,这些修改完全出自国内读者的需要。 什么是SOAP? SOAP是一个基于XML的用于应用程序之间通信数据编码的传输协议。最初由微软和Userland Software提出,随着不断地完善和改进,SOAP很快被业界广泛应用,目前完全发布版本是1.1。在其发展过程中,W3C XML标准工作小组积极促成SOAP成为一个真正的开放标准。在写作此文档之时,SOAP1.2草案已经发布,1.2对1.1中相对混乱的部分做了改进。 SOAP被广泛作为新一代跨平台、跨语言分布计算Web Services的重要部分。 这里太肤浅的说明,请参阅我的整理《一步一步学习SOAP》。 什么是Axis? Axis本质上就是一个SOAP引擎,提供创建服务器端、客户端和网关SOAP操作的基本框架。Axis目前版本是为Java编写的,不过为C++的版本正在开发中。 但Axis并不完全是一个SOAP引擎,它还包括: 是一个独立的SOAP服务器。 是一个嵌入Servlet引擎(例如Tomcat)的服务器。 支持WSDL。 提供转化WSDL为Java类的工具。 提供例子程序。 提供TCP/IP数据包监视工具。 Axis是第三代Apache SOAP,从2000年起,SOAP v2开发小组开始讨论如何让Axis更加灵活、可配置,以及能够处理SOAP和来自W3C的各种XML标准。通过不断地讨论和代码编写,Axis目前取得了如下成果: 速度提高。 Axis通过基于事件的SAX对XML文档进行处理,从而在速度和效率上比Apache SOAP有所提高。 灵活性提高。 稳定性提高。 提供面向组件的部署。 提供一个简洁的传输抽象框架。其核心引擎完全于传输方式独立。从而使基于何种协议传输的选择更加灵活。 支持WSDL。包括到处WSDL和客户代理生成等。 在目前发行1.1版本中有什么东西? SOAP1.1/1.2引擎。 灵活的配置和部署系统。 支持及时自动生成SOAP服务(JWS)。 支持所有的基本数据类型,为自定义串行操作提供类型映射系统。 JavaBean的自动串行操作,包括将自定义属性类型映射到XML的属性和元素。 RPC和基于消息的SOAP服务提供者。 从部署好的服务自动生成WSDL。 WSDL2Java工具可以从WSDL描述文件中产生相应的客户和服务器端SOAP操作框架。 初步提供安全扩展,能够与Servlet2.2安全集成。 通过HTTP Cookie和与传输无关的SOAP头信息提供会话跟踪。 初步支持带附件的SOAP消息。 在EJB方面提供把EJB作为Web服务的访问途经。 基于Servlet的HTTP传输。 基于JMS的传输。 独立的服务器(但需要HTTP 服务器和Servlet容器支持)。 提供客户端、服务器端相关应用程序的样例。 Axis的运行需要如下组件包 axis.jar jaxrpc.jar saaj.jar commons-logging.jar commons-discovery.jar wsdl4j.jar 符合JAXP-1.1的XML处理器。 一步一步开始用Axis进行Web Services操作 下面给出一段简单的调用Web Services方法的客户端代码(由于原文档中直接用导入包的方式初学者不易了解那个类在那个包中,所以下面我做了一些简单的修改,希望能给学习者清晰的思路): public class TestClient { public static void main(String[] args) { try{ String endpoint="http://localhost:8080/axis/SayHello.jws?wsdl";// 调用的web服务的url地址,这里是一个http请求,希望得到的结果是wsdl文档。 org.apache.axis.client.Service service=new org.apache.axis.client.Service();//建立请求服务框架实例。 /* * org.apache.axis.client.Service实现JAX-RPC's javax.xml.rpc.Services接口 * 该接口充当产生下面提到的org.apache.axis.client.Call实例 * 的角色。 */ org.apache.axis.client.Call call=(org.apache.axis.client.Call)service.createCall();//从框架中生成一个维护调用的实例。 /* * org.apache.axis.client.Call实现了JAX-RPC's javax.xml.rpc.Call接口。 */ call.setTargetEndpointAddress(new java.net.URL(endpoint)); call.setOperationName(new javax.xml.namespace.QName("http://www.edu-edu.com.cn/luopc/ws","echoString"));//设置需要调用的函数名称 String result=(String)call.invoke(new Object[]{"hello!"}); System.out.println(result); }catch(Exception e){ System.err.println(e.toString()); } } } 上面的代码可能和原文档中不同,并且在名称空间、函数多态上会给用户造成糊涂。不过别急,我在翻译手册的同时会加入很多解释的代码,如果哪位纯粹看不懂可以通过邮件获取帮助:luopc@edu-edu.com.cn ,邮件主题必须是我提供的文档名称。 通过上面的调用代码,最终生成向服务器请求SOAP信息包,具体XML内容如下: <?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <ns1:echoString xmlns:ns1="http://www.edu-edu.com.cn/luopc/ws"> <arg0 xsi:type="xsd:string">Hello!</arg0> </ns1:echoString> </SOAP-ENV:Body></SOAP-ENV:Envelope> 这里我不再多少,细心的朋友请从代码中找相应的对应信息来进行自己的逻辑理解。至于SOAP协议在我以后的翻译文档中会加入进去。 从上面的代码中我们在调用的时候输入了参数new Object[]{“hello!”}。然后从生成的SOAP请求包中可以看到自动序列化成<arg0 xsi:type="xsd:string">Hello!</arg0>。你可以看到参数名称为arg0,类型为xsd:string。其实在Axis客户端我们可以直接通过具体的方法来设置每一个参数名称、类型以及返回值类型。如下代码: call.addParameter("testParam", org.apache.axis.Constants.XSD_STRING, javax.xml.rpc.ParameterMode.IN);call.setReturnType(org.apache.axis.Constants.XSD_STRING); 加入上面的代码之后生成如下的SOAP信息: <?xml version="1.0" encoding="UTF-8"?><SOAP-ENV:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <SOAP-ENV:Body> <ns1:echoString xmlns:ns1="http://www.edu-edu.com.cn/luopc/ws"> <testParam xsi:type="xsd:string">Hello!</testParam> </ns1:echoString> </SOAP-ENV:Body></SOAP-ENV:Envelope> 可以作简单的对比。 也许你会疑惑设置参数名称和不设置参数名称有什么区别的疑问。这里作简单的解释: 1. 为什么要call.setReturnType(org.apache.axis.Constants.XSD_STRING)? 上面你可以调用或者可以不调用,但当返回结果中没有标明数据类型时Axis就不知道如何进行数据类型转换。当然如果返回类型你很清楚并且返回应答SOAP的结果中表明了相应的数据类型你便可以不进行上面的函数调用。 2. 为什么要设置参数名称和类型? 好了,下在你知道了如何调用Web Services了,下面告诉你如何写作和发布Web Services。 通过Axis发布Web Services 这里写作一个简单的类,然后一步一步进行发布。希望用户能够从中理出一些思路来。我在其他的关于Web Services的文章中会专门针对Web Services的通用发布方法。虽然Web Services牵涉到很多复杂的知识,但请大家不要感觉到这些操作简单,也许你已经有很多疑问,没关系,记下你的疑问,不断的投试。 public class SayHello { public String echoString(String hello){ return hello; }} 哈哈,这个类是不是太简单了。 一步一步来。 Axis提供两种将Java类发布成Web Services的途径,即即时快速自动发布和通过配置文件进行发布。我们首先从最容易部署的入手。 JWS----即时部署 简单说就是将自己写的Java类源文件按一定的规则Copy到特定的目录下便可自行被Axis部署。具体步骤如下: 将上面写的SayHello.java复制到axis目录下。 改名为SayHello.jws。 注意:在你写的类中不能有具体包的信息,因为这正是Axis即时部署不支持的。 运行我们前面写的客户端进行测试,运行结果是hello!。 目前你是否有如下的问题?
如果类中用多态的函数将如何处理? 其实很简单,我们知道在每次发出调用请求之前代理程序在后台会直接请求你给的URL,这里是http://localhost:8080/axis/SayHello.jws?wsdl,其实这一请求返回来的是WSDL描述文件,通过具体的描述文件的内容和你输入的参数的类型比较,最终确定发送什么请求。细节上的解释需要用户阅读SOAP的具体内容。我也会提供相应的权威文当翻译的。
Axis的客户端默认就是通过HTTP协议传输吗?
如何将自己定义的类型进行映射? 通过WSDD自定义部署
上面的自动部署相当简单,但当你需要 映射自己的类型时 不需要暴露源代码时 需要自己的路径和包管理时 对用户操作Web Services的事件进行相应时
就需要通过WSDD来自定义部署。 前面新特性中提到过,Axis是一个很灵活的可配置系统,但在配置之前你得懂得Axis Web Services描述文档(WSDD)的格式和意义。这里有一个简单的例子(deploy.wsdd): <deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="SayHello2" provider="java:RPC"> <parameter name="className" value="luopc.ws.SayHello"/> <parameter name="allowedMethods" value="*"/> </service> </deployment> 很简短的描述就会自定义对特定类以Web Services的形式发布。上面的每一个service项都会意味着开通一个相应的可被WSDL文件引用的服务。其内部描述信息将会描述从请求-处理请求-应答需要的所有核心信息。这里provider=”java:RPC”对应着相应的服务类为org.apache.axis.providers.java.RPCProvider。其实你可以通过多种方式提供相应的服务。在关于Axis的架构文章中将会详细解释这些内容。
上面的各个参数我不再解释,我想大家一看就明白。 下面让我们在上面的简单配置上开始一步一步的进行各种高级配置: 服务存活范围 Axis提供Session、Request和Application三种可选范围配置。具体配置标记为:
<service name="MyService"...> <parameter name="scope" value="value"/> ... </service> 一旦你完成上面的文件。可以写一个脚本批处理文件来完成部署。批处理文件如下:
java -classpath F:\resource\tools\axis-1_1\lib\axis.jar;F:\resource\tools\axis-1_1\lib\commons-discovery.jar;F:\resource\tools\axis-1_1\lib\jaxrpc.jar;F:\resource\tools\axis-1_1\lib\commons-logging.jar;F:\resource\tools\axis-1_1\lib\log4j-1.2.8.jar;F:\resource\tools\axis-1_1\lib\wsdl4j.jar;F:\resource\tools\axis-1_1\lib\j2ee.jar org.apache.axis.client.AdminClient sayHelloD.wsdd。 上面的一些jar文件都是Axis运行需要的组件,所以必须添加到环境中。上面的目录是我的机器里的目录,你可以按自己的目录进行。 如果部署成功,会输出<Admin>Done processing</Admin>信息。 但本人这里再强调几个: 你首先要将你编译好的类放到axis/web-inf/classes/下面。 可以通过http://localhost:8080/axis/services/SayHello2来调用。 你可以从我的配置文件中发现了包的概念。 可以用相应的xml文件进行卸载已部署的服务。 卸载方法如下: 编写如下XML文档
<undeployment xmlns="http://xml.apache.org/axis/wsdd/"> <service name="MyService"/> </undeployment> 把上面的批处理文件中的sayHelloD.wsdd换成该文件便可。
继续关注高级部署
如果你想知道自己的某个Web Services被调用多少次,你应该怎么做呢? Axis提供了相应的跟踪机制。
首先你应该编写一个满足一定接口的事件处理类。 在配置文件中添加事件处理器信息。
我们先看看配置文件中的信息:
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <!-- define the logging handler configuration --> <handler name="track" type="java:samples.userguide.example4.LogHandler"> <parameter name="parameter1" value="访问该services"/> </handler> <!-- define the service, using the log handler we just defined --> <service name="SayHello2" provider="java:RPC"> <requestFlow> <handler type="track"/> </requestFlow> <parameter name="className" value="luopc.ws.SayHello"/> <parameter name="allowedMethods" value="*"/> </service> </deployment> 上面的绿色部分起到核心的配置信息提供。
下面提供一个事件处理类,同样来自原文档,但为了更清晰我同样将所有的类前面加上了包名称。 package luopc.ws;
/** * @author luopc */ public class EventHandler extends org.apache.axis.handlers.BasicHandler{ public void invoke(org.apache.axis.MessageContext mtxt){ String param=(String)getOption("parameter1"); System.out.println(param); }
public static void main(String[] args) { } }
就这样,我们对请求跟踪了。嘻嘻。
可以远程进行管理(不推荐)
前面就提到服务类型的问题,在Axis有四种类型的服务,分别为RPC、Document、Wrapped和Message。 |
Axis下 WebService 的调用问题
本人按照网上例子制作一个Sample,遇到一个问题,当用JAVA编写的客户端调用返回值类型是int的函数时能正确得到返回值,但是调用返回值类型是String的函数时,不能正确得到返回值。以下为服务器端程序源代码: /** * 此处插入类型描述。 * 创建日期:(2005-10-27 10:32:48) * @author:Administrator */ import java.util.*; public class ZxjTest { protected String name="gaga"; protected int age=20; protected int sex=22; protected List items=new ArrayList(); /** * ZxjTest 构造子注解。 */ public ZxjTest() { super(); } public String getName() { return name; } public int getAge() { return age; } public int getSex() { return sex; } public List getItems() { return items; } } 客户端调用的源代码: String endpoint = "http://myserver:7001/ZxjTest.jws"; Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress(new java.net.URL(endpoint)); call.setOperationName("getName"); String ret = (String) call.invoke(new Object[] {}); 出错的就是最后一句。如果这一句改成调用返回值是Int的函数,就不会出错。请问,这是Axis的配置的问题吗?如果是,该如何改? 应该不大可能是Axis的配置的问题,你调用的是 public String getName() { return name; } 因此返回的是String,这个是没有错误的! 你的程序我在自己机器上运行的很好!不管调用哪个方法,都一样输出。你可以把String ret = (String) call.invoke(new Object[] {});改成Object ret = (Object)call.invoke(new Object[] {});这样的话你就不用去类型转换了! 或者如果你要类型转换的话,也可以这样做: 1,如果调用返回类型是string的方法 那么最后一句应该为 String ret = (String) call.invoke(new Object[] {}); 2,如果调用返回类型是int的方法,那么最后一句应该为 Integer ret = (Integer)call.invoke(new Object[] {}); 还有要注意的是在利用客户端进行测试的时候,确保服务器已经启动,否则会出错。如果你是采用http://myserver:7001/ZxjTest.jws的话,你的web-inf下确保没有 server-config.wsdd,否则也会出错。 还有一个很简单的错误,可能仅仅只是WebLogic配置的问题。最好重建一个应用并重新配置。 |
|
查找引擎之王 google 于 4/11 开放了它的查询接口,可以让全世界各地的 Java 以及 .NET 程序员,免费地以 WebServices 的方式,对 google 下查找指令,并且可以将结果使用于自己的程序或网页中。不过使用上也有限制,它一天只允许未付费的程序员查找 1000 次。要使用前,必须先向 google 注册帐号,取得一个 32 位长度的 license key ,每次呼叫查询时,必须带入这个 license key 即可使用。因为 WebServices 以 WSDL (Web Service Definition Language) 的方式描述所开放的接口,走的通讯协定是 SOAP on HTTP ,所以理论上可以穿越防火墙。 Java 范例程序如下∶
import java.io.*;
import com.google.soap.search.*; // 这是 googleapi.jar ,由 google 所提供
public class SearchGoogle {
public static void main(String[] args) {
if (args.length != 2)
{
System.err.println("Usage: java SearchGoogle LicenseKey 查询字符串 ");
System.exit(1);
}
String clientKey = args[0];
String queryString = args[1];
System.out.println(" 查询字符串 = " + queryString);
// 产生查找对象,设定输出编码 (big5)
GoogleSearch s = new GoogleSearch();
s.setKey(clientKey);
s.setOutputEncoding("big5");
try
{
s.setQueryString(qyeryString);
GoogleSearchResult r = s.doSearch();
System.out.println(" 查找结果 :");
System.out.println("======================");
System.out.println(r.toString());
}
catch (GoogleSearchFault e)
{
System.out.println(" 查找失败 ");
}
}
}
使用方法以及查询结果(执行环境∶ Linux + JDK 1.3.1_01 ,主机位于防火墙后端)∶
java -classpath .:../lib/googleapi.jar
SearchGoogle xxxxxxxxx- 我的 LicenseKey-xxxxxxxxx LinuxFab
查询字符串 = LinuxFab
查找结果 :
======================
{
TM = 0.048257
Q = "LinuxFab"
CT = ""
TT = ""
CATs =
{
{SE="Big5", FVN="Top/World/Chinese_Traditional/ 计算机 /Operating_Systems/Unix/Linux"}
}
Start Index = 1
End Index = 10
Estimated Total Results Number = 11500
Document Filtering = true
Estimate Correct = false
Rs =
{
[
URL = "http://bbs.ee.ntu.edu.tw/boards/Linux/21/"
Title = " ◆ 【 LinuxFab 早报】 "
Snippet = " ◆ 【 LinuxFab 早报】 . ... 5, ◇ 【 LinuxFab 本周专栏】 如何制作简易 Floppy Liunx, linuxfab. 07/13/01. ... "
Directory Category = {SE="", FVN=""}
Directory Title = ""
Summary = ""
Cached Size = "11k"
Related information present = true
Host Name = ""
],
[
URL = "http://bbs.ee.ntu.edu.tw/boards/Linux/21/73.html"
Title = " ◇ 【 LinuxFab 早报】 - Linux 2.4.10 内核中剽窃了 ┅ "
Snippet = " ◇ 【 LinuxFab 早报】 - Linux 2.4.10 内核中 剽窃了 ┅ . 发信人 : linuxfab.bbs ... "
Directory Category = {SE="", FVN=""}
Directory Title = ""
Summary = ""
Cached Size = "10k"
Related information present = true
Host Name = "bbs.ee.ntu.edu.tw"
],
[
URL = "http://www.lslnet.com/linux/docs/linux-3033.htm"
Title = "LinuxFab: Red Hat7.0 ? 新特色 - [? 森林 - 自由 ? 件 ]"
Snippet = " ... LinuxFab: Red Hat7.0 ? 新特色 ? 森林 http://www.lslnet.com 2000 年 9 月 24 日 09:59 ... 摘自∶ http:linuxfab.cx [ ?? 窗口 ]. ... "
Directory Category = {SE="", FVN=""}
Directory Title = ""
Summary = ""
Cached Size = "5k"
Related information present = true
Host Name = ""
],
下略
当然,输出结果不是这么杂乱, google 所提供的 api 有各种查找设定方法,例如从第几笔开始查找、设定传回笔数、偏好查找(避免查找 java 时传回 咖啡 的结果) ... 等等,各位可以好好玩一玩。
如果您不想使用 google 的 library ,您也可以利用手边的 WebServices 工具程序,自动抓取 google 所提供的 WSDL 文档(位于∶ http://api.google.com/GoogleSearch.wsdl ),并且产生相关的 java 对象。以下以 GLUE 这套 Java WebServices 软体包做范例∶
$ wsdl2java http://api.google.com/GoogleSearch.wsdl -p example -d example/
write file example/IGoogleSearchPort.java
write file example/GoogleSearchServiceHelper.java
write file example/ResultElement.java
write file example/DirectoryCategory.java
write file example/GoogleSearchResult.java
write file GoogleSearchService.map
GLUE 的强处在于它自动为您处理所有 XML <-> Java Object Mapping 以及 SOAP 传输的问题,您甚至不需要看懂 WSDL 便可直接使用 WebServices !我们现在已经将 WSDL 转换成 Java 对象, package 名为 example ,接下来便是撰写 Client 去连接 google ∶
import java.io.*;
import electric.registry.Registry; // 包含于 GLUE-STD.jar ,为 GLUE 标准版的 library
import example.*; //GLUE 自动为您由 WSDL 产生出来的 Java 对象
public class SearchGoogle2 {
public static void main(String[] args) {
if (args.length != 2)
{
System.err.println("Usage: java SearchGoogle LicenseKey 查询字符串 ");
System.exit(1);
}
String clientKey = args[0];
String queryString = args[1];
System.out.println(" 查询字符串 = " + queryString );
String url = "http://api.google.com/GoogleSearch.wsdl";
try
{
// 利用 GLUE 产生的接口,去 bind google 的 WSDL
IGoogleSearchPort searcher = (IGoogleSearchPort) Registry.bind(url , IGoogleSearchPort.class);
// 查询
GoogleSearchResult result = searcher.doGoogleSearch
( clientKey , queryString , 0 , 1 , false , "" , false , "" , "Big5" , "Big5" );
System.out.println(" 查找结果 :");
System.out.println("======================");
System.out.println(" 回应数量∶ " + result.resultElements.length );
System.out.println(" 第一笔结果∶ ");
System.out.println(" 简介∶ " + result.resultElements[0].summary );
System.out.println("URL ∶ " + result.resultElements[0].URL );
System.out.println(" 片段∶ " + result.resultElements[0].snippet );
System.out.println(" 标头∶ " + result.resultElements[0].title );
System.out.println(" 主机∶ " + result.resultElements[0].hostName );
System.out.println(" 目录∶ " + result.resultElements[0].directoryTitle );
}
catch (electric.registry.RegistryException e)
{
System.out.println(" 错误∶ " + e.getMessage());
}
}
}
执行结果指令如下∶
$ java -classpath .:../lib/GLUE-STD.jar:../lib/j2ee.jar:../lib/jnet.jar
SearchGoogle2 xxxxxxxxx- 我的 LicenseKey-xxxxxxxxx LinuxFab
查询字符串 = LinuxFab
查找结果 :
======================
回应数量∶ 1
第一笔结果∶
简介∶
URL ∶ http://bbs.ee.ntu.edu.tw/boards/Linux/21/
片段∶◆ 【 LinuxFab 早报】 . ... 5, ◇ 【 LinuxFab 本周专栏】 如何制作简易 Floppy Liunx, linuxfab. 07/13/01. ...
标头∶◆ 【 LinuxFab 早报】
主机∶
目录∶
要注意的是,这只是 google beta2 版本的 WSDL ,未来接口一定会有所更动,而且远端主机 (api.google.com) 仍然不太稳定,有时候会连不过去,有时又会出现 Internal Server Error 的错误。不过以上的介绍应该可以给大家一个简单的 WebServices 概念了吧?目前几乎所有的程序语言都有 WebServices 的相关软体包,不论您是 PHP 网页制作者,或是 VB 窗口程序员,应该都找到相关的软体包,直接在您的网页 / 程序上面撰写 WebServices 的 Client 以及 Server 。
截稿前消息∶全球最大的网络书店 Amazon ,也开始提供 WebServices 接口给代销商( associates ,可以在个人网页上放置连往 Amazon 的 icon 或连结,藉此赚取部分成交金额)使用。代销商现在可以利用 Amazon 提供的 WebServices 撰写 Web/Desktop 应用程序了!看来类似的 WebServices 将会逐渐在许多大站点中陆续被采用,或许 LinuxFab/SourceFab 可以考虑引进此技术,使本站成为华人 Open Source 界的网络运算中心。
用IBM的笔记本的朋友非常的多,大多数人都喜欢删除原带的系统,自己来按装新的操作系统,可是重装系统后对于IBM随机带的软件可以说是一头雾水,因为软件大多都为英文,而且数量又多,没办法许多朋友就用全部按装法,把只要是随机带的全部装到机器上,可是有一部分是完全没有必要装的,或者说可以选择性的按装,本文的目的是为了让大家了解大多数随机软件的用途,让大家按自己的用途来按装,从而结省了有限的内存空间,
注: 其中大部分软件只能使用在T2x,X2x,R系列,A2x以后的机型,对于570这种老机器好多软件是无法安装的,系统为Windows XP
1. Battery MaxiMiser Wizard 按装性:必装~
这个软件按字面来看是”电池最大化向导”,它是通过电池中的一块芯片来查看电池的健康情况以及电池的充电次数和剩余电量等等一些相关参数的.它还可以自己定义电源管理模式,比如用电池时屏幕是4格亮度,CPU按最小的频率使用等.,
2. IBM PM驱动 按装性:必装
PM的意为Power Management Driver,也就是电源管理驱动,它是十分重要的一个软件,它可以来控制当本本在市电与电池之间切换时,CPU与主板PCI总线频率的控制,也就是我们所说的CPU降频使用,当然您的CPU必须支持Speed Step技术,此软件也与上面的软件配合使用
3.HotKey 驱动 按装性:必装
IBM的热键驱动,它的功能有当用户调整音量与屏幕亮度时,在屏幕下方显示当前音量和亮度的等级,以图形方式显示.还包括打开键盘灯和屏幕放大的提示.等.
4. ThinkPad Configuration 按装性:必装
这个软件相当于您系统中的设备管理器,它的功能可以说是相当强大,它可以管理IBM ThinkPad笔记本电脑中的所有硬件,以及内置的安全芯片,查看系统信息等等,它可以打开和屏蔽笔记本中的各种接口,如PCMCIA,红外端口等.
5. Presentation Director 按装性:可选
它叫演示向导,一个非常实用的驱动,它非常适合那些经常外出接投影仪和大屏幕显示设备的商务人士,它的组合键是FN+F7,按下后会出现一个选择分辩率的列表,当用户外接一个显示设备时可选择一个适合的分辩率,演示后还可以直接切换回来,就不用来回的调整屏幕的分辩率了,如果去多个地方又要用到不同的分辩率和显示设备,那么可以手动设置不同的分辩率和屏幕的显示方式,方便快速,节约时间,从而提高的工作效率.
6. Keyboard Customizer Utility 按装性:可选
用过ThinkPad本本的朋友都知道IBM的键盘没有视窗键,这个软件就可以让你用其它的键来代替视窗键.当然它的功能不止这些,它还可以让用户按照自己的喜好把键盘上的一些组合健定义成快捷键,比如笔者就把Shift+右方向键,定义成为了光驱打开.它还可以当您外接键盘时可以用重新定义功能键,有兴趣的朋友不仿自己试试.
7. EasyEject Utility 按装性:可选
它的功能等同于系统中安全移除硬件向导,组合键为FN+F9,它的优点是界面十分直观,当用户要停用移动设备时,它可以以帮助您安全的移除您连接的设备,包括用户接的扩展坞
8. IBM Active Protection System 按装性:必装
APS驱动,这个驱动只支持T41以后的本本,当然还包括R51等支持这个硬盘保护功能笔记本,APS是一种主动的硬盘保护系统,它的作用是当用户在使用笔记本过程中,电脑意外下落,这个系统会在非常快的时间内做出反应,停止硬盘工作,保护用户的数据,把损失降到最小,笔者建议有这个功能的本本用户最好还是装上,不在乎那几M的内存,安全才是最重要的.
9. IBM Access Connections 按装性:可选
笔都最喜欢用的一个软件,个人认为是IBM随机软件中最最经典的一个,没有一个共享软件比的上它,说它经典是因为它非常试合网络管理员来使用,也非常适合天天带着本本在不同的网络间漫游的人(不管有线网络,无线网络,ADSL,还是企业内网),有了它网管就不用来回更改本本的IP地址了,不用建立一大堆的连接方式,不用用脑子记住不同的DNS,不同的网关,它的方便之处只有你用了才可以深深的明白,呵呵,说多了有点.具体的说,它可以让用户分别定义不同的网络配置文件,这件配置文件中包括不同的网络连接方式,不同的IP地址,网关,DNS等等,当用户从一个网络中切换到另一个网络中,不需要重新更改本机的IP地址,只要按一下FN+F5组合键,从定义好的列表中选择一种适合的配置文件即可,真是太方便了.它的优点和细节功能真是一句两句说不清楚,总之呢希望朋友们好好的利用它节省我们的时间,提高我们的工作效率
10. UltraNav Wizard 按装性:可选
这个驱动只适合那些有触模板的ThinkPad计算机,包括T30.T4x,R51等等,它的功能也很多,它可以调整触模板的一些细节设置,比如可以把触模板左右键设成快捷健,打开用户的程序.还可以把触模板的一边设成滚轮来浏览网页等等.
11.Access IBM 按装性:可选)
就是按下本本右上角那个蓝色的按钮出来的东东,它包括帮助文件,还有恢复系统等等,笔者没有按装,个人感觉它是一个花瓶,所以我把那个蓝色按钮改成快捷键了.如果朋友们喜欢那个蓝色按钮打开时的界面,我想还是装上去吧.对于那些对ThinkPad了解深入的朋友就没有必要按装了
12. Access IBM Message Center 按装性:可选
IBM的消息中心,我想它是给像我这样的懒人准备的,我一般很少去IBM的官方网站去看看出个什么新驱动没有,装上它就方便多了,有什么新驱动和关于您本本的新消息,它会马上通知您下载和按装,比较方便的
13. Software Installer 按装性: 必装 适用机型:全部
意为“软件安装者”当初在第一篇文章中,没把它加入是我的失误,现在隆重介绍此软件,经常在Windows软件区看到不少XD,求这个驱动,那个驱动的,其实只要装了Software Installer您就不用在上IBM官方网站找驱动了,SI可以帮助你找到你的计算机所需要的最新驱动程序,当然前提是小黑必须连接入互联网,它会自己搜索针对本机的驱动程序,让用户有选择性的下载,速度还不错。没用过的XD快快装上吧。
14.System Migration Assistant 按装性: 建议安装 适用机型:全部
英文意为“IBM 系统迁移辅助程序”,我想大多数的兄弟都不太喜欢重装系统,因为有一些个人设置(如桌面、图标、网络设置、窗口大小、等一些小细节、甚至还包括声音)都要重新来过,有时还会忘记一些重要设置,有了IBM 系统迁移辅助程序,就太方便了,它可以把用户的个人设置,全部导出为扩展名为*.sma的备份文件,重装系统后,再导入到新装的系统中就可以完全1:1的回到您重装系统之前,速度快不说、最重要是它备份文件的完整性、和可选择性,它可以选择您想备份的,去掉不想备份。全中文界面,就好比选择性的克隆一样
经我试用,SMA居然还可以把你在原系统中的共享文件夹也给备份了,当然包括用户信息如:Administrator的设置等等,总之SMA带给你的只有方便和简单加上快速。
15. IBM Client Security Software 按装性: 可选 适用机型:T2x\T4x\R5x\等
IBM 客户端安全软件,也就是IBM广告中经常提到的CSS 1.0 2.0,CSS不光应用于小黑,也应用于ThinkCentre台式电脑,CSS通过嵌入式安全子系统,也就是安全芯片,对用户的重要数据进行加密,和保护,当然还包括管理用户的常用密码,如果有CSS的XD可以不防一试,想加密文件时,就不必在按装第三方加密软件了。
16. IBM File and Folder Encryption 按装性:可选
意为IBM 文件和文件夹加密,是和CSS客户端配合使用的一个程序,简称为IBM FFE,它的名气没有CCS那么大,FFE只能加密C盘的文件,对于其它驱动器,FFE是无法加密的。个人认为用CSS就可以了。FFE没有必要安装。
17. IBM Rescue and Recovery 按装性:建议安装
大名鼎鼎的IBM 营救和恢复程序,简称R&R,虽然我很少使用它,因为它太大了。4××MB,不过为了这个文章装一下研究,事先说明R&R只支持Win2000和XP,R&R是一个超级强大的备份压缩工具,个人认为R&R可以代替HPA了,R&R可以备份全部的系统文件到硬盘中,或者到其它的移动介质,它可以备份所有分区,或者单独的分区,备份过程是在Windows下进行的,如果您想恢复您备份的系统,重装启动,按下F11键,一个很漂亮又熟悉的介面就会出现在你在眼前,他可以代替世面上所以的备份恢复软件,大家不防一试。R&R太强大了,光*我的一个介绍难于让大家直观了解,大家可以装上看看
摘要: 这篇文章讨论和阐明了提供 JavaTM I/O 性能的多种技术。大多技术围绕调整磁盘文件 I/O,但是有些内容也同样适合网络 I/O 和窗口输出。第一部分技术讨论底层的I/O问题,然后讨论诸如压缩,格式化和串行化等高级I/O问题。然而这个讨论没有包含应用设计问题,例如搜索算法和数据结构,也没有讨论系统级的问题,例如文件高速缓冲。 ...
阅读全文
|
|
platform & Server
8.X 英文版 http://download2.bea.com/pub/platform/81/platform815_win32.exe http://download2.bea.com/pub/platform/81/platform815_linux32.bin (linux) 中文版 http://download2.bea.com/pub/platform/81/platform812_zh_CN_win32.exe http://download2.bea.com/pub/platform/81/platform814_zh_CN_win32.exe http://download2.bea.com/pub/platform/81/platform815_zh_CN_win32.exe http://download2.bea.com/pub/platform/81/platform815_zh_CN_hpux32.bin (hp_unix) 9.X 英文版 http://download2.bea.com/pub/platform/90/server900_win32.exe http://download2.bea.com/pub/platform/90/server900_linux32.bin 中文版 http://download2.bea.com/pub/platform/90/server900_zh_CN_win32.exe http://download2.bea.com/pub/platform/90/server900_zh_CN_hpux32.bin (hp_uxix http://download2.bea.com/pub/platform/90/server900_zh_CN_solaris32.bin (Solaris(8\9\10)版) http://download2.bea.com/pub/platform/91/server910_win32.exe http://download2.bea.com/pub/platform/91/server910_linux32.bin (linux) http://download2.bea.com/pub/platform/91/server910_hpux32.bin (hpux) http://download2.bea.com/pub/platform/91/server910_solaris32.bin (solaris)
==============需要注册的下载========== 1.在哪里下得着中文版的 http://commerce.bea.com/index.jsp 进去后,点击 International Versions 然后就会到 http://commerce.bea.com/products/intl_versions.jsp 再选简体中文就OK了。 2.下载所有英文版本地址: http://commerce.bea.com/showallversions.jsp?family=WLP 3.下载所有中文版本地址: http://commerce.bea.com/showallversions.jsp?family=WLPCH
| |
摘要: MyEclipse+struts+Hibernate配置开发手册
说明:
这个手册只是为初学者制作的环境配置和快速开发的一些基本方法,为的是广大爱好者能快速上手,少走我走过的弯路,里面没有任何关于java模式和其他相关的说明。本人水平有限,也没有能力去讨论关于模式设计和开发细节的一些内容。
 ...
阅读全文
|
一些网站采用了字母和数字的验证码,数字和字母加起来一共30多个,如果有心,还是能够通过方法识别出来。 我在网上看到一篇文章"jsp彩色验证码",我进行了加强,生成的验证码内容为汉字,可以方便应用在面向汉语网民的网站认证上。 当然,我还看到别人考虑的几种方法: 1.生成计算题,比如20+34*(23-12)=?,用户必须输入正确的答案才能通过验证,这个想法挺好,但是用户必须打开计算器进行计算,增加了用户的难度 2.问一些常见的问题,比如“人”这个字由几笔组成。这也是不错的想法,关键是必须有上万条的题库,而且题库的答案必须是明确的,简单易比较的,这个难度也较大。 下面是彩色汉字验证码的代码。 <%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %> <%! //create by smallnest //email: smallnest@gmail.com //website:www.kuaff.com //生成随机颜色 Color getRandColor(Random random,int fc,int bc) { if(fc>255) fc=255; if(bc>255) bc=255; int r=fc+random.nextInt(bc-fc); int g=fc+random.nextInt(bc-fc); int b=fc+random.nextInt(bc-fc); return new Color(r,g,b); } %> <% //设置页面不缓存 response.setHeader("Pragma","No-cache"); response.setHeader("Cache-Control","no-cache"); response.setDateHeader("Expires", 0); // 设置图片的长宽 int width=176, height=30; //设置备选汉字,剔除一些不雅的汉字 String base = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6"; //备选汉字的长度 int length = base.length(); //创建内存图像 BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // 获取图形上下文 Graphics g = image.getGraphics(); //创建随机类的实例 Random random = new Random(); // 设定图像背景色(因为是做背景,所以偏淡) g.setColor(getRandColor(random,200,250)); g.fillRect(0, 0, width, height); //备选字体 String[] fontTypes = {"\u5b8b\u4f53","\u65b0\u5b8b\u4f53","\u9ed1\u4f53","\u6977\u4f53","\u96b6\u4e66"}; int fontTypesLength = fontTypes.length; //在图片背景上增加噪点 g.setColor(getRandColor(random,160,200)); g.setFont(new Font("Times New Roman",Font.PLAIN,14)); for (int i=0;i<6;i++) { g.drawString("*********************************************",0,5*(i+2)); } //取随机产生的认证码(6个汉字) //保存生成的汉字字符串 String sRand=""; for (int i=0;i<6;i++) { int start = random.nextInt(length); String rand=base.substring(start,start+1); sRand+=rand; //设置字体的颜色 g.setColor(getRandColor(random,10,150)); //设置字体 g.setFont(new Font(fontTypes[random.nextInt(fontTypesLength)],Font.BOLD,18 + random.nextInt(6))); //将此汉字画到图片上 g.drawString(rand,24*i+ 10 + random.nextInt(8),24); } //将认证码存入session session.setAttribute("rand",sRand); g.dispose(); //输出图象到页面 ImageIO.write(image, "JPEG", response.getOutputStream()); %> | |
JSF开发实战的第一篇(jsf开发实战一)演示了如何建立一个最简单的JSF应用,从那个例子中我们可以观察到JSF的页面组件是如何与后台的javabean绑定的,也看到了JSF标签的最基本使用。在本文中我们会演示一个更加复杂的应用,它包含了更丰富的JSF标签。
要开发的例子是一个用户管理程序,管理员输入帐号与密码登陆,然后它可以看到所有用户的列表,并且可以修改或者删除其中的一些数据。利用myeclipse所带的jsf-config.xml设计器,页面流程如下:

可以看出,这个应用设计到的业务逻辑有:
用户登陆、修改用户信息、增加新用户、删除用户
我们在后台的数据库操作中使用hibernate框架来辅助开发,相关的技术细节请自行查阅文档。
首先建立pojo文件:User.java,它包含几个基本属性:
private int id;
private String name;
private String password;
private int power;
请自行完成set/get方法,并且编写对应的hbm.xml文件。
我们的主要工作之一,是要建立好供jsf页面组件使用的javabean,把它命名为UMDelegater.java。它会调用UserManager来完成业务逻辑,这里是一个代理模式。UserManager的内容只是简单的增/删/查/改的操作,这里不再具体列出。UMDelegater的内容是:
package org.bromon.jsf.control;
import java.util.List;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import org.bromon.jsf.model.UserManager;//自行建立的工具类,负责所有的hibernate操作
import org.bromon.jsf.model.pojo.User;//pojo对象
public class UMDeletager {
private UserManager um=new UserManager();//所有具体的方法都由它来实现
private User user=new User();
private DataModel allUsers=new ListDataModel();//JSF的内置对象,用来封装html中table的数据
//----------set/get方法---------------------
public DataModel getAllUsers() {
return allUsers;
}
public void setAllUsers(List list) {
allUsers.setWrappedData(list);
}
public UserManager getUm() {
return um;
}
public void setUm(UserManager um) {
this.um = um;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
//-----功能方法---------
public String login()
{
String s=um.login(this.getUser());
if(s.equals("ok"))
{
this.setAllUsers(um.loadAll());//如果登陆成功,就取出所有的用户信息
return "login:ok";
}else
{
swapper.setLoginFailInfo(s);
return "login:fail";
}
}
public String edit()
{
this.user=(User)allUsers.getRowData();//页面中的table自动返回含有id的user对象
this.user=um.loadById(user.getId());
if(user!=null)
{
return "edit";
}else
{
return "error";
}
}
public String update()
{
um.update(this.getUser());
this.setAllUsers(um.loadAll());//重新取一次数据,目的是更新缓存
return "update:ok";
}
public String addNew()
{
this.setUser(new User());//生成一个新的user对象,不含任何数据,它会被自动映射成一个没有数据的form
return "add";
}
public String add()
{
um.add(this.getUser());
this.setAllUsers(um.loadAll());//重新取一次数据,目的是更新缓存
return "add:ok";
}
}
在jsf-config.xml中声明这个bean:
<managed-bean>
<managed-bean-name>UMDelegater</managed-bean-name>
<managed-bean-class>org.bromon.jsf.control.UMDeletager</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
业务逻辑就设计完成了,下面可以开始编写jsf文件,首先是index.jsp:
首先引入标签库,并且声明page属性:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
然后是构建页面:
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="name" value="帐号:"/>
<h:inputText id="name" value="#{UMDelegater.user.name}" required="true"/>
<h:message for="name"/>
<h:outputLabel for="password" value="密码:"/>
<h:inputSecret id="password" value="#{UMDelegater.user.password}" required="true" />
<h:message for="password"/>
</h:panelGrid>
<h:panelGroup>
<h:commandButton value="登陆" action="#{UMDelegater.login}"/>
</h:panelGroup>
</h:form>
</f:view>
</body>
页面中声明了两个文本框,分别映射UMDelegater对象中user对象的name属性,和UMDelegater对象中user对象的password属性。一旦这个表单被提交,这两个文本框的值就会被自动赋给user对象,我们只需要从user中取数据就行了,不用再去执行麻烦的request.getParameter(“”),更改编码,转换数据类型等操作。
我们同时还声明了一个button,它与UMDelegater对象的login方法绑定,点击该按钮,系统会执行UMDelegater.login方法,该方法从user对象中取出name和password,和数据库中的记录进行比较。如果合法,那么就取出所有的数据,放到一个DataModel对象中,具体代码是:
List userList=UserManager.getAllUser();//取得所有用户数据,放到一个List中
DataModel allUser=new ListDataModel ();//DataModel是一个接口,ListDataModel是它的一个实现
allUsers.setWrappedData(userList);//将数据填充进去备用
使用DataModel意义何在呢?JSF中,我们可以把一个html页面上的table和一个DataModel绑定起来,这些数据会自动填充到table中,我们不必再自己去写循环,生成若干的<tr>、<td>来生成一个table。在list.jsp中我们会看到如何使用DataModel。
放好数据之后,登陆成功的操作就完成了,返回一个login:ok,就可以重定向到list.jsp。如果用户登陆失败,那么会返回login:fail,重定向到error.jsp,它的内容就不叙说了。下面我们看看list.jsp里面有什么,下面是它的<body>代码:
<body>
<f:view>
<h:form>
<h:dataTable id="users" value="#{UMDelegater.allUsers}" var="u" border="1" width="80%" >
<h:column>
<f:facet name="header">
<h:outputText value="id"/>
</f:facet>
<h:outputText value="#{u.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="帐号"/>
</f:facet>
<h:commandLink action="#{UMDelegater.edit}">
<h:outputText value="#{u.name}"/>
</h:commandLink>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="密码"/>
</f:facet>
<h:outputText value="#{u.password}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="权限代码"/>
</f:facet>
<h:outputText value="#{u.power}"/>
</h:column>
</h:dataTable>
<p>
<h:commandLink action="#{UMDelegater.addNew}" value="增加用户" />
</p>
</h:form>
</f:view>
</body>
我们使用了一个h:dataTable标签,它是JSF独有的东西,它会被翻译为一个html的table,通过指定h:dataTable的value="#{UMDelegater.allUsers}"属性,它就与我们刚才生成的DataModel对象关联起来,数据会被自动填充。我们只需要声明dataTable中的每一列的表头,数据来自哪个字段就可以了,如下:
<h:column>
<f:facet name="header">
<h:outputText value="id"/>
</f:facet>
<h:outputText value="#{u.id}"/>
</h:column>
表格最后还有一个“增加用户”的按钮,它与UMDelegater.addNew绑定,它会把我们重定向到add.jsp。
需要注意的是,每个用户名都是个超链接,点击之后可以重定向到edit.jsp,这个页面可以修改用户资料。这是通过如下代码实现的:
最近由于项目的特殊需求,我们必须在程序调用window的dll。
开始我们用jni,后来由于调用的dll太多,而且很烦琐。所以,我们决定用开源的jawin调用。
jawin 可以对dll中的方法进行调用,也可以调用com中的方法.内部还提供了一个工具,直接对 com组件导出成 java的类,个人认为很方便。
下面是我们作的一个测试,很顺利便通过了。
1、下载jawin:http://jawinproject.sourceforge.net/。
2、配置:
》将jawin.jar放于%JAVA_HOME%\jre\lib\ext下 。
》将jawin.dll放于c:\winnt\system32下。否则将出现错误:COMException : no jawin in java.library.path;
也可将jawin.dll放于每个项目目录下。
》至此在Editplus中调试Jawin/NJawin的例子,可以通过。 而在Eclipse中有时还会出上面的错误:COMException : no jawin in java.library.path。
》在Eclipse中,菜单->window->preference->Java->installed JREs 将原来的remove,重新建一个指到你的java sdk目录。
》 ok了。
3、程序测试:
》调用 dll,dll 的方式不需要导出了,直接调用就可以了,下面是下载的包中提供的一个例子:
》我在win2000下,测试通过。
/*
* Created on Dec 22, 2005
*
*/
import org.jawin.FuncPtr;
import org.jawin.ReturnFlags;
/**
* @author gf mail to gf@163.com
*
* TODO To change the template for this generated type comment go to
* Window - Preferences - Java - Code Style - Code Templates
*/
public class GfJawinTest {
public static void main(String[] args) {
try {
FuncPtr msgBox = new FuncPtr("USER32.DLL", "MessageBoxW");
msgBox.invoke_I(0, "Hello From a DLL", "From Jawin", 0, ReturnFlags.CHECK_NONE);
} catch (Exception e) {
e.printStackTrace();
}
}
}
在web应用中,开发者经常会面临字符的全角和半角转换问题。那么什么是全角?什么是半角呢?简单说就是中文系统用双字节显示汉字造成的与世界标准(由美国制订的)不一的问题。
举一个简单的例子,中文逗号“,”就是全角的,相对的西文逗号“,”就是半角的。这样的例子很多,比如“~”与“~”、“+”与“+”等等。
那么,字符的全角和半角转换有什么用处呢?一般说来,我们做的网页都是中文的,要求用户填入的信息也多是汉字,那么很多特殊字符都是全角的,比如说填写多条信息,用逗号分隔,这里的逗号多半都是全角的。而提交到后台(服务器端)处理,或者是查询(构建select语句)或者是入库(提交给DBMS),都是基于西文体系的,因此必须将这些字符转换成对应的半角字符。
下面举一个JavaScript的通用代码例子,详细介绍一下如何将这些全角字符转换成半角字符。
function SD(sbc, dbc)
{
this.sbc = sbc;
this.dbc = dbc;
}
var sdArray = new Array();
var sdCount;
var i = 0;
sdArray[i++] = new SD("0", "0");
sdArray[i++] = new SD("1", "1");
sdArray[i++] = new SD("2", "2");
sdArray[i++] = new SD("3", "3");
sdArray[i++] = new SD("4", "4");
sdArray[i++] = new SD("5", "5");
sdArray[i++] = new SD("6", "6");
sdArray[i++] = new SD("7", "7");
sdArray[i++] = new SD("8", "8");
sdArray[i++] = new SD("9", "9");
sdArray[i++] = new SD("a", "a");
sdArray[i++] = new SD("b", "b");
sdArray[i++] = new SD("c", "i");
sdArray[i++] = new SD("d", "d");
sdArray[i++] = new SD("e", "e");
sdArray[i++] = new SD("f", "f");
sdArray[i++] = new SD("g", "g");
sdArray[i++] = new SD("h", "h");
sdArray[i++] = new SD("i", "i");
sdArray[i++] = new SD("j", "j");
sdArray[i++] = new SD("k", "k");
sdArray[i++] = new SD("l", "l");
sdArray[i++] = new SD("m", "m");
sdArray[i++] = new SD("n", "n");
sdArray[i++] = new SD("o", "o");
sdArray[i++] = new SD("p", "p");
sdArray[i++] = new SD("q", "q");
sdArray[i++] = new SD("r", "r");
sdArray[i++] = new SD("s", "s");
sdArray[i++] = new SD("t", "t");
sdArray[i++] = new SD("u", "u");
sdArray[i++] = new SD("v", "v");
sdArray[i++] = new SD("w", "w");
sdArray[i++] = new SD("x", "x");
sdArray[i++] = new SD("y", "y");
sdArray[i++] = new SD("z", "z");
sdArray[i++] = new SD("A", "A");
sdArray[i++] = new SD("B", "B");
sdArray[i++] = new SD("C", "C");
sdArray[i++] = new SD("D", "D");
sdArray[i++] = new SD("E", "E");
sdArray[i++] = new SD("F", "F");
sdArray[i++] = new SD("G", "G");
sdArray[i++] = new SD("H", "H");
sdArray[i++] = new SD("I", "I");
sdArray[i++] = new SD("J", "J");
sdArray[i++] = new SD("K", "K");
sdArray[i++] = new SD("L", "L");
sdArray[i++] = new SD("M", "M");
sdArray[i++] = new SD("N", "N");
sdArray[i++] = new SD("O", "O");
sdArray[i++] = new SD("P", "P");
sdArray[i++] = new SD("Q", "Q");
sdArray[i++] = new SD("R", "R");
sdArray[i++] = new SD("S", "S");
sdArray[i++] = new SD("T", "T");
sdArray[i++] = new SD("U", "U");
sdArray[i++] = new SD("V", "V");
sdArray[i++] = new SD("W", "W");
sdArray[i++] = new SD("X", "X");
sdArray[i++] = new SD("Y", "Y");
sdArray[i++] = new SD("Z", "Z");
sdArray[i++] = new SD(".", ".");
sdArray[i++] = new SD(" ", " ");
sdArray[i++] = new SD("(", "(");
sdArray[i++] = new SD(")", ")");
sdArray[i++] = new SD("{", "{");
sdArray[i++] = new SD("}", "}");
sdArray[i++] = new SD("[", "[");
sdArray[i++] = new SD("]", "]");
sdArray[i++] = new SD("<", "<");
sdArray[i++] = new SD(">", ">");
sdArray[i++] = new SD("「", "“");
sdArray[i++] = new SD("」", "”");
sdArray[i++] = new SD("`", "`");
sdArray[i++] = new SD("~", "~");
sdArray[i++] = new SD("!", "!");
sdArray[i++] = new SD("@", "@");
sdArray[i++] = new SD("#", "#");
sdArray[i++] = new SD("%", "%");
sdArray[i++] = new SD("^", "^");
sdArray[i++] = new SD("※", "&");
sdArray[i++] = new SD("*", "*");
sdArray[i++] = new SD("-", "-");
sdArray[i++] = new SD("_", "_");
sdArray[i++] = new SD("+", "+");
sdArray[i++] = new SD("=", "=");
sdArray[i++] = new SD("|", "|");
sdArray[i++] = new SD("\", "\\");
sdArray[i++] = new SD("■", "-");
sdArray[i++] = new SD("'", "'");
sdArray[i++] = new SD(""", "\"");
sdArray[i++] = new SD("/", "/");
sdArray[i++] = new SD(";", ";");
sdArray[i++] = new SD(":", ":");
sdArray[i++] = new SD(",", ",");
sdArray[i++] = new SD("。", ".");
sdArray[i++] = new SD("?", "?");
sdCount = i;
function sbc2dbc(src)
{
var k;
var i;
var len = src.length;
var result = "";
for (k=0; k<len; k++)
{
ch = src.charAt(k);
for (i=0; i<sdCount; i++)
{
if (sdArray[i].sbc == ch)
{
result += sdArray[i].dbc;
break;
}
}
if (i == sdCount) //没有查到对应的全角字符
{
result += ch;
}
}
return result;
}
代码的原理就不详细介绍了, src参数就是需要转换的字符串,直接调用sbc2dbc函数就可以把src里面所有的全角字符转换成半角字符。
为什么没有半角字符转换成全角字符的函数?呵呵,因为没有想到那里会用到,如果你在开发中真有此需求,相信根据上面的源码,你也可以写出对应的转换函数。祝你顺利。
final注意要素:
1.abstract 和final不能同时修饰一个类。
2.修饰方法保证该方法不能被子类重载,已经被private修饰的方法以及所有包含在final类中的方法,都被缺省认为是fianl的。
3.如果是修饰primitive数据,那么这份数据是不可更改的,如果是修饰对象的话,那么其指向不可更改。但例如一个对象内部有另外一个对象,这另外一个对象可以变更。
两者重点不同:
static强调只有一份
final强调是常量
static和final是不同的两个修饰,static是修饰方法或变量是静态的,而final是修饰定义的变量为常量。
Tomcat性能调整
作者:佚名 转贴自:CSDN 点击数:15554
一. 引言
性能测试与分析是软件开发过程中介于架构和调整的一个广泛并比较不容易理解的领域,更是一项较为复杂的活动。就像下棋游戏一样,有效的性能测试和分析只能在一个良好的计划策略和具备了对不可预料事件的处理能力的条件下顺利地完成。一个下棋高手赢得比赛靠的不仅仅是对游戏规则的认识,更是靠他的自己的能力和不断地专注于分析自己对手的实力来更加有效地利用和发挥规则的作用。同样一个优秀的性能测试和分析人员将要面对的是来自一个全新的应用程序和环境下带来的整个项目的挑战。本文中作者结合自己的使用经验和参考文档,对Tomcat性能方面的调整做一简要的介绍,并给出Tomcat性能的测试、分析和调整优化的一些方法。
二. 测量Web服务器的性能
测量web服务器的性能是一项让人感到畏缩的任务,但是我们在这里将给出一些需要注意的地方并且指点你了解其中更多的细节性的内容。它不像一些简单的任务,如测量CPU的速率或者是测量程序占用CPU的比例,web服务器的性能优化中包括许调整许多变量来达到目标。许多的测量策略中都包含了一个看似简单的浏览实际上是在向服务器发送大量的请求,我们称之为客户端的程序,来测量响应时间。客户端和服务器端是在同一台机器上吗?服务器在测试的时候还运行着其它的什么程序吗?客户端和服务器端的通讯是通过局域网,100baseT,10baseT还是使用调制解调器?客户端是否一直重复请求相同的页面,还是随机地访问不同的页面?(这些影响到了服务缓存的性能)客户端发送请求的有规律的还是突发的?你是在最终的配置环境下运行服务的还是在调试的配置环境下运行服务的?客户端请求中包含图片还是只有HTML页面?是否有请求是通过servlets和JSP的,CGI程序,服务端包含(Server-Side Includes ,SSI是一个可以让你使用动态HTML文件的技术)?所有这些都将是我们要关心的,并且几乎我们不可能精确地把所有的问题都清楚地列出来。
1.压力测试工具
“工欲善其事,必先利其器”,压力测试只有借助于一些工具才可得以实施。
大多数web压力测试工具的实现原理都是通过重复的大量的页面请求来模拟多用户对被测系统的并发访问,以此达到产生压力的目的。产生压力的手段都是通过录制或者是编写压力脚本,这些脚本以多个进程或者线程的形式在客户端运行,这样通过人为制造各种类型的压力,我们可以观察被测系统在各种压力状况下的表现,从而定位系统瓶颈,作为系统调优的基础。目前已经存在的性能测试工具林林总总,数量不下一百种,从单一的开放源码的免费小工具如 Aapache 自带的 web 性能测试工具 Apache Benchmark、开源的Jmeter 到大而全的商业性能测试软件如 Mercury 的 LoadRunner 等等。任何性能测试工具都有其优缺点,我们可以根据实际情况挑选用最合适的工具。您可以在这里找到一些web压力测试工具http://www.softwareqatest.com/qatweb1.html#LOAD
这里我们所使用的工具要支持web应用服务认证才可以,要支持接收发送cookies,不仅如此Tomcat支持多种认证方式,比如基本认证、基于表单的认证、相互认证和客户端认证,而一些工具仅仅支持HTTP基本认证。真实地模拟用户认证是性能测试工具的一个重要的部分,因为认证机制将对一个web站点的性能特征产生重要的影响。基于你在产品中使用的不同的认证方式,你需要从上面的工具列表中选择使用这种特性的测试工具。
Apache Benchmark和http_load是命令行形式的工具,非常易于使用。Apache Benchmark可以模仿单独的URL请求并且重复地执行,可以使用不同的命令行参数来控制执行迭代的次数,并发用户数等等。它的一个特点是可以周期性地打印出处理过程的信息,而其它工具只能给出一个全局的报告。
2.压力测试工具介绍
三. 外部环境的调整
在Tomcat和应用程序进行了压力测试后,如果您对应用程序的性能结果不太满意,就可以采取一些性能调整措施了,当然了前提是应用程序没有问题,我们这里只讲Tomcat的调整。由于Tomcat的运行依赖于JVM,所以在这里我们把Tomcat的调整可以分为两类来详细描述:
外部环境调整
调整非Tomcat组件,例如Tomcat运行的操作系统和运行Tomcat的java虚拟机。
自身调整
修改Tomcat自身的参数,调整Tomcat配置文件中的参数。
下面我们将详细讲解外部环境调整的有关内容,Tomcat自身调整的内容将在第2部分中阐述。
1.JAVA虚拟机性能优化
Tomcat本身不能直接在计算机上运行,需要依赖于硬件基础之上的操作系统和一个java虚拟机。您可以选择自己的需要选择不同的操作系统和对应的JDK的版本(只要是符合Sun发布的Java规范的),但我们推荐您使用Sun公司发布的JDK。确保您所使用的版本是最新的,因为Sun公司和其它一些公司一直在为提高性能而对java虚拟机做一些升级改进。一些报告显示JDK1.4在性能上比JDK1.3提高了将近10%到20%。
可以给Java虚拟机设置使用的内存,但是如果你的选择不对的话,虚拟机不会补偿。可通过命令行的方式改变虚拟机使用内存的大小。如下表所示有两个参数用来设置虚拟机使用内存的大小。
参数 |
描述 |
-Xms<size> |
JVM初始化堆的大小 |
-Xmx<size> |
JVM堆的最大值 |
这两个值的大小一般根据需要进行设置。初始化堆的大小执行了虚拟机在启动时向系统申请的内存的大小。一般而言,这个参数不重要。但是有的应用程序在大负载的情况下会急剧地占用更多的内存,此时这个参数就是显得非常重要,如果虚拟机启动时设置使用的内存比较小而在这种情况下有许多对象进行初始化,虚拟机就必须重复地增加内存来满足使用。由于这种原因,我们一般把-Xms和-Xmx设为一样大,而堆的最大值受限于系统使用的物理内存。一般使用数据量较大的应用程序会使用持久对象,内存使用有可能迅速地增长。当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃。因此一般建议堆的最大值设置为可用内存的最大值的80%。
Tomcat默认可以使用的内存为128MB,在较大型的应用项目中,这点内存是不够的,需要调大。
Windows下,在文件{tomcat_home}/bin/catalina.bat,Unix下,在文件{tomcat_home}/bin/catalina.sh的前面,增加如下设置:
JAVA_OPTS='-Xms【初始化内存大小】 -Xmx【可以使用的最大内存】'
需要把这个两个参数值调大。例如:
JAVA_OPTS='-Xms256m -Xmx512m'
表示初始化内存为256MB,可以使用的最大内存为512MB。
另外需要考虑的是Java提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾可以接受的速度与应用有关,应该通过分析实际的垃圾收集的时间和频率来调整。如果堆的大小很大,那么完全垃圾收集就会很慢,但是频度会降低。如果你把堆的大小和内存的需要一致,完全收集就很快,但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为保证最好的性能,要把堆的大小设大,保证垃圾收集不在整个基准测试的过程中出现。
如果系统花费很多的时间收集垃圾,请减小堆大小。一次完全的垃圾收集应该不超过 3-5 秒。如果垃圾收集成为瓶颈,那么需要指定代的大小,检查垃圾收集的详细输出,研究 垃圾收集参数对性能的影响。一般说来,你应该使用物理内存的 80% 作为堆大小。当增加处理器时,记得增加内存,因为分配可以并行进行,而垃圾收集不是并行的。
[NextPage]
2.操作系统性能优化
这里说的操作系统是指运行web服务器的系统软件,当然,不同的操作系统是为不同的目的而设计的。比如OpenBSD是面向安全的,因此在它的内核中有许多的限制来防止不同形式的服务攻击(OpenBSD的一句座右铭是“默认是最安全的”)。这些限制或许更多地用来运行活跃的web服务器。
而我们常用的Linux操作系统的目标是易用使用,因此它有着更高的限制。使用BSD内核的系统都带有一个名为“Generic”的内核,表明所有的驱动器都静态地与之相连。这样就使系统易于使用,但是如果你要创建一个自定义的内核来加强其中某些限制,那就需要排除不需要的设备。Linux内核中的许多驱动都是动态地加载的。但是换而言之,内存现在变得越来越便宜,所以因为加载额外的设备驱动就显得不是很重要的。重要的是要有更多的内存,并且在服务器上腾出更多的可用内存。
小提示:虽然现在内存已经相当的便宜,但还是尽量不要购买便宜的内存。那些有牌子的内存虽然是贵一点,但是从可靠性上来说,性价比会更高一些。
如果是在Windows操作系统上使用Tomcat,那么最好选择服务器版本。因为在非服务器版本上,最终用户授权数或者操作系统本身所能承受的用户数、可用的网络连接数或其它方面的一些方面都是有限制的。并且基于安全性的考虑,必须经常给操作系统打上最新的补丁。
3.Tomcat与其它web服务器整合使用
虽然tomcat也可以作web服务器,但其处理静态html的速度比不上apache,且其作为web服务器的功能远不如apache,因此我们想把apache和tomcat集成起来,将html与jsp的功能部分进行明确分工,让tomcat只处理jsp部分,其它的由apache,IIS等这些web服务器处理,由此大大节省了tomcat有限的工作“线程”。
4.负载均衡
在负载均衡的思路下,多台服务器为对称方式,每台服务器都具有同等的地位,可以单独对外提供服务而无须其他服务器的辅助。通过负载分担技术,将外部发送来的请求按一定规则分配到对称结构中的某一台服务器上,而接收到请求的服务器都独立回应客户机的请求。
提供服务的一组服务器组成了一个应用服务器集群(cluster),并对外提供一个统一的地址。当一个服务请求被发至该集群时,根据一定规则选择一台服务器,并将服务转定向给该服务器承担,即将负载进行均衡分摊。
通过应用负载均衡技术,使应用服务超过了一台服务器只能为有限用户提供服务的限制,可以利用多台服务器同时为大量用户提供服务。当某台服务器出现故障时,负载均衡服务器会自动进行检测并停止将服务请求分发至该服务器,而由其他工作正常的服务器继续提供服务,从而保证了服务的可靠性。
负载均衡实现的方式大概有四种:第一是通过DNS,但只能实现简单的轮流分配,不能处理故障,第二如果是基于MS IIS,Windows 2003 server本身就带了负载均衡服务,第三是硬件方式,通过交换机的功能或专门的负载均衡设备可以实现,第四种是软件方式,通过一台负载均衡服务器进行,上面安装软件。使用Apache Httpd Server做负载平衡器,Tomcat集群节点使用Tomcat就可以做到以上第四种方式。这种方式比较灵活,成本相对也较低。另外一个很大的优点就是可以根据应用的情况和服务器的情况采取一些策略。
[NextPage]
四. 自身调整
本节将向您详细介绍一些加速可使Tomcat实例加速运行的技巧和方法,无论是在什么操作系统或者何种Java虚拟机上。在有些情况下,您可能没有控制部署环境上的操作系统或者Java虚拟机。在这种情况下,您就需要逐行了解以下的的一些建议,然而你应该在修改后使之生效。我认为以下方法是Tomcat性能自身调整的最佳方式。
1.禁用DNS查询
当web应用程序向要记录客户端的信息时,它也会记录客户端的IP地址或者通过域名服务器查找机器名转换为IP地址。DNS查询需要占用网络,并且包括可能从很多很远的服务器或者不起作用的服务器上去获取对应的IP的过程,这样会消耗一定的时间。为了消除DNS查询对性能的影响我们可以关闭DNS查询,方式是修改server.xml文件中的enableLookups参数值:
Tomcat4
<Connector className=org.apache.coyote.tomcat4.CoyoteConnector port=80 minProcessors=5 maxProcessors=75 enableLookups=false redirectPort=8443 acceptCount=100 debug=0 connectionTimeout=20000 useURIValidationHack=false disableUploadTimeout=true />
Tomcat5
<Connector port=80 maxThreads=150 minSpareThreads=25 maxSpareThreads=75 enableLookups=false redirectPort=8443 acceptCount=100 debug=0 connectionTimeout=20000 disableUploadTimeout=true/>
除非你需要连接到站点的每个HTTP客户端的机器名,否则我们建议在生产环境上关闭DNS查询功能。可以通过Tomcat以外的方式来获取机器名。这样不仅节省了网络带宽、查询时间和内存,而且更小的流量会使日志数据也会变得更少,显而易见也节省了硬盘空间。对流量较小的站点来说禁用DNS查询可能没有大流量站点的效果明显,但是此举仍不失为一良策。谁又见到一个低流量的网站一夜之间就流量大增呢?
2.调整线程数
另外一个可通过应用程序的连接器(Connector)进行性能控制的的参数是创建的处理请求的线程数。Tomcat使用线程池加速响应速度来处理请求。在Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。
Tomcat4中可以通过修改minProcessors和maxProcessors的值来控制线程数。这些值在安装后就已经设定为默认值并且是足够使用的,但是随着站点的扩容而改大这些值。minProcessors服务器启动时创建的处理请求的线程数应该足够处理一个小量的负载。也就是说,如果一天内每秒仅发生5次单击事件,并且每个请求任务处理需要1秒钟,那么预先设置线程数为5就足够了。但在你的站点访问量较大时就需要设置更大的线程数,指定为参数maxProcessors的值。maxProcessors的值也是有上限的,应防止流量不可控制(或者恶意的服务攻击),从而导致超出了虚拟机使用内存的大小。如果要加大并发连接数,应同时加大这两个参数。web server允许的最大连接数还受制于操作系统的内核参数设置,通常Windows是2000个左右,Linux是1000个左右。
在Tomcat5对这些参数进行了调整,请看下表:
属性名 |
描述 |
maxThreads |
Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数。 |
acceptCount |
指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理。 |
connnectionTimeout |
网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常可设置为30000毫秒。 |
minSpareThreads |
Tomcat初始化时创建的线程数。 |
maxSpareThreads |
一旦创建的线程超过这个值,Tomcat就会关闭不再需要的socket线程。 |
最好的方式是多设置几次并且进行测试,观察响应时间和内存使用情况。在不同的机器、操作系统或虚拟机组合的情况下可能会不同,而且并不是所有人的web站点的流量都是一样的,因此没有一刀切的方案来确定线程数的值。
[NextPage]
3.加速JSP编译速度
当第一次访问一个JSP文件时,它会被转换为Java serverlet源码,接着被编译成Java字节码。你可以控制使用哪个编译器,默认情况下,Tomcat使用使用命令行javac进行使用的编译器。也可以使用更快的编译器,但是这里我们将介绍如何优化它们。
另外一种方法是不要把所有的实现都使用JSP页面,而是使用一些不同的java模板引擎变量。显然这是一个跨越很大的决定,但是事实证明至少这种方法是只得研究的。如果你想了解更多有关在Tomcat可使用的模板语言,你可以参考Jason Hunter和William Crawford合著的《Java Servlet Programming 》一书(O'Reilly公司出版)。
在Tomcat 4.0中可以使用流行而且免费的Jikes编译器。Jikes编译器的速度要由于Sun的Java编译器。首先要安装Jikes(可访问http://oss.software.ibm.com/pub/jikes 获得更多的信息),接着需要在环境变量中设置JIKESPATH包含系统运行时所需的JAR文件。装好Jikes以后还需要设置让JSP编译servlet使用Jikes,需要修改web.xml文件中jspCompilerPlugin的值:
<servlet> <servlet-name>jsp</servlet-name> <servlet-class> org.apache.jasper.servlet.JspServlet </servlet-class> <init-param> <param-name>logVerbosityLevel</param-name> <param-value>WARNING</param-value> </init-param> <init-param> <param-name>jspCompilerPlugin</param-name> <param-value> org.apache.jasper.compiler.JikesJavaCompiler </param-value> </init-param> <init-param> <!-- <param-name> org.apache.catalina.jsp_classpath </param-name> --> <param-name>classpath</param-name> <param-value> /usr/local/jdk1.3.1-linux/jre/lib/rt.jar: /usr/local/lib/java/servletapi/servlet.ja r</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet>
在Tomcat 4.1(或更高版本),JSP的编译由包含在Tomcat里面的Ant程序控制器直接执行。这听起来有一点点奇怪,但这正是Ant有意为之的一部分,有一个API文档指导开发者在没有启动一个新的JVM的情况下,使用Ant。这是使用Ant进行Java开发的一大优势。另外,这也意味着你现在能够在Ant中使用任何javac支持的编译方式,这里有一个关于Apache Ant使用手册的javac page列表。使用起来是容易的,因为你只需要在 元素中定义一个名字叫“compiler”,并且在value中有一个支持编译的编译器名字,示例如下:
<servlet> <servlet-name>jsp</servlet-name> <servlet-class> org.apache.jasper.servlet.JspServlet </servlet-class> <init-param> <param-name>logVerbosityLevel</param-name> <param-value>WARNING</param-value> </init-param> <init-param> <param-name>compiler</param-name> <param-value>jikes</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet>
Ant可用的编译器
名称 |
别名 |
调用的编译器 |
classic |
javac1.1, javac1.2 |
Standard JDK 1.1/1.2 compiler |
modern |
javac1.3, javac1.4 |
Standard JDK 1.3/1.4 compiler |
jikes |
|
The Jikes compiler |
JVC |
Microsoft |
Microsoft command-line compiler from the Microsoft SDK for Java/Visual J++ |
KJC |
|
The kopi compiler |
GCJ |
|
The gcj compiler (included as part of gcc) |
SJ |
Symantec |
Symantec's Java compiler |
extJavac |
|
Runs either the modern or classic compiler in a JVM of its own |
由于JSP页面在第一次使用时已经被编译,那么你可能希望在更新新的jsp页面后马上对它进行编译。实际上,这个过程完全可以自动化,因为可以确认的是新的JSP页面在生产服务器和在测试服务器上的运行效果是一样的。
[NextPage]
在Tomcat4的bin目录下有一个名为jspc的脚本。它仅仅是运行翻译阶段,而不是编译阶段,使用它可以在当前目录生成Java源文件。它是调试JSP页面的一种有力的手段。
可以通过浏览器访问再确认一下编译的结果。这样就确保了文件被转换成serverlet,被编译了可直接执行。这样也准确地模仿了真实用户访问JSP页面,可以看到给用户提供的功能。也抓紧这最后一刻修改出现的bug并且修改它J
Tomcat提供了一种通过请求来编译JSP页面的功能。例如,你可以在浏览器地址栏中输入http://localhost:8080/examples/jsp/dates/date.jsp?jsp_precompile=true,这样Tomcat就会编译data.jsp而不是执行它。此举唾手可得,不失为一种检验页面正确性的捷径。
4. 其它
前面我们提到过操作系统通过一些限制手段来防止恶意的服务攻击,同样Tomcat也提供了防止恶意攻击或禁止某些机器访问的设置。
Tomcat提供了两个参数供你配置:RemoteHostValve 和RemoteAddrValve。
通过配置这两个参数,可以让你过滤来自请求的主机或IP地址,并允许或拒绝哪些主机/IP。与之类似的,在Apache的httpd文件里有对每个目录的允许/拒绝指定。
例如你可以把Admin Web application设置成只允许本地访问,设置如下:
<Context path=/path/to/secret_files ...> <Valve className=org.apache.catalina.valves.RemoteAddrValve
allow=127.0.0.1 deny=/> </Context>
如果没有给出允许主机的指定,那么与拒绝主机匹配的主机就会被拒绝,除此之外的都是允许的。与之类似,如果没有给出拒绝主机的指定,那么与允许主机匹配的主机就会被允许,除此之外的都是拒绝的。
五. 容量计划
容量计划是在生产环境中使用Tomcat不得不提的提高性能的另一个重要的话题。如果你没有对预期的网络流量下的硬件和带宽做考虑的话那么无论你如何做配置修改和测试都无济于事。
这里先对提及的容量计划作一个简要的定义:容量计划是指评估硬件、操作系统和网络带宽,确定应用服务的服务范围,寻求适合需求和软件特性的软硬件的一项活动。因此这里所说的软件不仅包括Tomcat,也包括与Tomcat结合使用的任何第三方web服务器软件。
如果在购买软硬件或部署系统前你对容量计划一无所知,不知道现有的软硬件环境能够支撑多少的访问量,甚至更糟直到你已经交付并且在生产环境上部署产品后才意识到配置有问题时再进行变更可能为时已晚。此时只能增加硬件投入,增加硬盘容量甚至购买更好的服务器。如果事先做了容量计划那么就不会搞的如此焦头烂额了。
我们这里只介绍与Tomcat相关的内容。
首先为了确定Tomcat使用机器的容量计划,你应该从一下列表项目种着手研究和计划:
1. 硬件
采用什么样的硬件体系?需要多少台计算机?使用一个大型的,还是使用多台小型机?每个计算机上使用几个CPU?使用多少内存?使用什么样的存储设备,I/O的处理速度有什么要求?怎样维护这些计算机?不同的JVM在这些硬件上运行的效果如何(比如IBM AIX系统只能在其设计的硬件系统上运行)?
2. 网络带宽
带宽的使用极限是多少?web应用程序如何处理过多的请求?
3. 服务端操作系统
采用哪种操作系统作为站点服务器最好?在确定的操作系统上使用哪个JVM最好?例如,JVM在这种系统上是否支持本地多线程,对称多处理?哪种系统可使web服务器更快、更稳定,并且更便宜。是否支持多CPU?
[NextPage]
4. Tomcat容量计划
以下介绍针对Tomcat做容量计划的步骤:
1) 量化负载。如果站点已经建立并运行,可以使用前面介绍的工具模仿用户访问,确定资源的需求量。
2) 针对测试结果或测试过程中进行分析。需要知道那些请求造成了负载过重或者使用过多的资源,并与其它请求做比较,这样就确定了系统的瓶颈所在。例如:如果servlet在查询数据库的步骤上耗用较长的时间,那么就需要考虑使用缓冲池来降低响应时间。
3) 确定性能最低标准。例如,你不想让用户花20秒来等待结果页面的返回,也就是说甚至在达到访问量的极限时,用户等待的时间也不能超过20秒种(从点击链接到看到返第一条返回数据)。这个时间中包含了数据库查询时间和文件访问时间。同类产品性能在不同的公司可能有不同的标准,一般最好采取同行中的最低标准或对这个标准做出评估。
4) 确定如何合理使用底层资源,并逐一进行测试。底层资源包括CPU、内存、存储器、带宽、操作系统、JVM等等。在各种生产环境上都按顺序进行部署和测试,观察是否符合需求。在测试Tomcat时尽量多采用几种JVM,并且调整JVM使用内存和Tomcat线程池的大小进行测试。同时为了达到资源充分合理稳定地使用的效果,还需针对测试过程中出现的硬件系统瓶颈进行处理确定合理的资源配置。这个过程最为复杂,而且一般由于没有可参考的值所以只能靠理论推断和经验总结。
5) 如果通过第4步的反复测试如果达到了最优的组合,就可以在相同的生产环境上部署产品了。
此外应牢记一定要文档化你的测试过程和结果,因为此后可能还会进行测试,这样就可以拿以前的测试结果做为参考。另外测试过程要反复多次进行,每次的条件可能都不一样,因此只有记录下来才能进行结果比较和最佳条件的选择。
这样我们通过测试找到了最好的组合方式,各种资源得到了合理的配置,系统的性能得到了极大的提升。
六. 附加资料
很显然本文也很难全面而详尽地阐述性能优化过程。如果你进行更多研究的话可能会把性能调优做的更好,比如Java程序的性能调整、操作系统的调整、各种复杂环境与应用系统和其它所有与应用程序相关的东西。在这里提供一些文中提到的一些资源、文中提到的相关内容的链接以及本文的一些参考资料。
1. Web性能测试资料及工具
1) Jmeter Wiki首页,Jmeter为一个开源的100%Java开发的性能测试工具 http://wiki.apache.org/jakarta-jmeter/
2) Apache Benchmark使用说明 http://httpd.apache.org/docs-2.0/programs/ab.html
3) 一些Java相关测试工具的介绍,包含可以与Tomcat集成进行测试的工具 http://blog.csdn.net/wyingquan/
4) LoadRunner® 是一种预测系统行为和性能的工业标准级负载测试工具。它通过模拟数据以千万计用户来实施并发负载来对整个企业架构进行测试,来帮助您更快的查找和发现问题。 http://www.mercury.com/us/products/performance-center/loadrunner/
2. 文中介绍的相关内容的介绍
1) Apache 2.x + Tomcat 4.x做负载均衡,描述了如何利用jk配置集群的负载均衡。 http://raibledesigns.com/tomcat/index.html
2) 容量计划的制定,收集了许多有关制定web站点容量计划的例子: http://www.capacityplanning.com/
3) 评测Tomcat5负载平衡与集群, http://www.javaresearch.org/article/showarticle.jsp?column=556&thread=19777
4) Apache与Tomcat的安装与整合之整合篇 http://www.javaresearch.org/article/showarticle.jsp?column=23&thread=18139
5) 性能测试工具之研究,介绍了性能测试工具的原理与思路 http://www.51testing.com/emagzine/No2_2.htm
6) Java的内存泄漏 http://www.matrix.org.cn/resource/article/409.html
7) Web服务器和应用程序服务器有什么区别? http://www.matrix.org.cn/resource/article/1429.html
8) 详细讲解性能中数据库集群的问题 http://www.theserverside.com/articles/article.tss?l=DB_Break
|
恭喜,你选择开发工程师做为自已的职业
悲哀,你选择开发工程师做为自已的职业
本文所指的开发工程师,仅指程序开发人员和以数字电路开发为主的电子工程师。
当你选择计算机或者电子、自控等专业进入大学时,你本来还是有机会从事其它行业的,可你毕业时执迷不悟,仍然选择了开发做为你的职业,真是自做孽不可活。不过,欢迎你和我一样加入这个被其它人认为是风光无限的“白领”吧。
如果你不是特别的与人世隔绝,我想你一定看过金老先生的名著《笑傲江湖》吧,里面有一门十分奇特的武功叫做"辟邪剑法",你看这个小说第一次看到这种功夫的练法时,我想你当时一定笑歪了牙“呵呵,真好玩!”,可是现在我很痛心的告诉你:你选择的开发工作就是你人生路上的"辟邪剑法",而你现在已经练了,并且无法再回头。
相对同时刚出校门同学从事其它行业而言优厚的薪水,以及不断学习更新的专业知识不仅仅让你感到生活的充实,更满足了你那不让外人知的虚荣心。在刚出校门的几年中,你经常回头看看被你落在后面的同学们,在内心怜悯他们的同时,你也会对自已天天加班的努力工作感到
心里平衡:“有付出才会有回报”这句话在那几年中你说的最多,不管是对自已的朋友们还是自已的爱人。第二句最常说的话是对公司的领导:“不行我就走人!”,实际上你也真的走过几回。对了,在这几年中,因为你的经济条件不错,你开始买房、开始谈恋爱、结婚、开始有了自已的小孩。有时候你会对自已说再过两年就去买车。当然其中可能有许多大件是需要分期付款的,但你对前途充满了信心,你确信认为这种日子会永远的持续下去,即使不是变得更好的话。
日子总是在这种平淡中一天天的过去,就在那么不经意间,你突然发现自已已经快30岁了,或者已经30了,莫名的,你心里会漫延着一种说不清楚的不安情绪,你好像觉得前途并非像前几年那样变得越来越好,你也忽然发现你以前所瞧不起的同学里好像已经有不少开着车的了,也有几个人住着比你还大的房子,好像房款还是一次付清的,你突然明白你现在的生活比起你的同学来最多是中游偏上了。工作中最让你感到心里不舒服的是,你越来越不敢对你的领导说不了,即使比你来的晚的同事升职或提薪,你也只是在私下与朋友们一起喝酒时才敢发发牢骚,在头的面前你的声间越来越小、笑脸是越来越温柔。
你终于开始迷茫“再过几年我会是在干什么呢?”,这句话常常出现在你的心里。
计算机开发工作,是一种以年轻为资本的工作,说句通俗点的话是“吃青春饭的”,嗯,这句话好像在一种特别的行业也听到过。
其标志就是一:工作的时间性非常强,一个开发项目被定的时限通常是很紧张的,更有甚者,有些号称开发管理的书里面还非常卑鄙的号召将一个项目切成多个小片,每个小片都定一个叫“里程碑”的东东来严格跟踪开发进度,加班加点在其它行业是需要加班工资的,而在开发行业,加班工资好像还没见到几个公司发过,是啊,反正有时间限制着,你干不完我再找你算账.所以开发工作通常有着其它工作所没有的精神上的压力。
一旦一个人步入而立之年,因为家庭和孩子的负担,加上精力上面的衰退,加班工作时间变得越来越少,这点让很多老板们感到:这些人已经老了,不好用了。指示人事部门:“以后招开发人员限制在30岁以下!”,相对而言硬件开发会年龄方面限制会稍好一点点,但也是五十步笑百步。还有一个很重要的一点就是:计算机这个烂东东实在是进步的太快了,前两年买的顶级配置电脑,现在怎么看怎么像废品,这还是小事,更可气的是好像每天都需要学习新的知识,刚毕业时只会书本上的PASCAL,学会了用腐蚀的办法来做电路板,一上班就开始学习TURBOC和TANGER2.0,刚刚学会,还没来得及高兴,马上开始学Borland C++和Protel3.0,好不容易学会了,却发现需要学习VC和Protel98了。单片机也是啊:Z80的指令背的很熟,工作中没来得及用就要学8031,好好学吧,本来想着这辈子就吃它了,又发现又出来什么PIC、DSP、CPLD、FPGA、ARM等等....这还不包括中间要学一大堆74系列、4000系列、XX系列...IC卡居然里面还有CPU卡..如果学习的知识里每个字都能变成一分钱,我想所有的开发工程师都是腰缠万贯的富翁。
一眼看去,这种日子好像见不到头,年轻时乐此不彼,但现在你一定对自已能坚持到什么时候感到怀疑了。我们都玩过像仙剑奇侠传这样的RPG游戏,刚开始时你只是一个一名不文的少年,随着你去打怪物、捡宝贝、学秘芨,最后终于有一天你会变成一个大英雄!那么你在实际生活中过得比那些小侠们还辛苦,为什么成不了一个生活中的大侠呢?呵呵,原因在这里:因为开发工作是邪门功夫,它虽然可以让你速成的变成小资,但它最大的特点是经验不积累!日新月异的知识更新,让你总是感到自已在退步,你就像在RPG中的主人公,开始时就给了你一把好剑和好盔甲,而且让你的级别很高,但让你的经验不累积,虽然刚开始打小怪物时你觉得自已很爽,但越到后来,你会发现你会死的很惨!比较一下你与其它非开发行业的同学你就可以知道了,例如和你学医的同学比起来。套用岳不群他老人家说华山剑宗和气宗的区别那段话:前十年你比你那些学医的同学收入和地位要好的多,但十年以后你和他基本上各方面都会持平,而二十年以后你的各方面远远不能与你学医的同学相提并论!嗯,你已经开始不笑辟邪剑法了吧。
“敢问路在何方?路在脚下...”,不过猴兄和八戒兄这么认为是可以的,你呢?
总结了许多开发朋友在30岁以后的生活之路,让我们一起看看开发人员“路在何方?”那么开发人员在30岁以后都干些什么呢?
其路一:继续做你这个很有“前途”的职业吧!
偶掰着脚指头仔细数了数,发现还真的有很多朋友在30岁以后还在从事开发工作,我这里说的从事,是指你还需要天天在电脑边上编程序和画电路板,与你手下是否有几个小兵无关,也与你是否头上顶着什么项目经理、主任工程师的帽子无关,只要你还需要亲自开发,你就属于这一类。其中有个年龄最大的朋友是63年的,从事医疗仪器的开发工作,35岁左右还在从事软硬件开发工作的仍有一大堆,分析这些仍然从事开发的朋友,基本上都有以下特点:
1 痴迷工作或者痴迷电脑,晚上八点到十二点的这段时间,基本上是在电脑桌或工作台前渡过的。
2 不喜欢与人交住,朋友很少,常联系的人不超过五个。
3 与朋友交往时谈工作多,但一般不主动谈钱。
4 体型偏胖或偏廋,不在正常区间。
5 无未来计划,对五年后自已生活怎么样、从事什么工作说不清楚。
6 俭省,从不乱花钱。
即使你是还不到30岁的开发人员,你也可以看看自己对以上几条是否符合,是否会在30岁后还从事开发职业,四条疑似,五条以上基本确诊你也是这类型的人。
这些朋友们通常报着过一天是一天的态度生活,到了这个年龄,也不敢再轻易的换工作了,年轻时的锐气慢慢的也消退了。唯一不变的希望是有一天从天上掉下来一大堆钱把自己砸伤。说实在话因为他们的性格所限,基本上可以确定他们以后不可能在职场上获得更好的发展,当个小头头,带几个人开发已经是他们发展的顶点。至于以后的人生之路,不仅他们自己迷茫,可能上帝也正在头痛。
不过像这类朋友,偶很奇怪的发现:他们的小孩都是儿子!不知是偶然还是有什么其它说法。
简单建议:要改变命运,先改变性格:坚持半年晚上不从事工作、游戏及电视,用此时间与人交往,你的人生会有改变。
其路二:转行从事技术支持、行政或生产等工作还有一些朋友,从事了几年的开发工作,因为自已并非特别的爱好,或者领导上面的强制工作安排,他们转到了技术支持、服务或行政等工作,至少当时从表面上看起来,他们的薪水较开发要少一些,但真正的统计这些人,发现他们之中有半数的人获得了更好的发展,升职为服务部经理或行政经理等职,最历害的一个朋友已升职为总经理助理,进入高层。
这类朋友当时转行通常并非自已志愿,属被逼无奈或者其它原因,但显然,拥有专业知识技术的他们显然在非技术部门中鹤立鸡群,遇到什么事情他们均可从专业的角度提出建言,久而久之,他们获得更多的升职和加薪机会也就不足为奇。
因为不从事开发,所以经验开始积累,这类的职业通常会给你一个很安定的感觉,你到30多岁后会发现这类职业反而比开发工作更容易获得新的工作机会。
简单建议:你如果确定在开发部无法获得很好的发展机会,不妨转到其它几个部门试试,换个活法,钱少点就少点吧,机会多。
其路三:开发管理
如果你现在已经是总工或开发部经理,或者你眼看就有机会被提升为这类职务,那么恭喜你,你走的是从“弼马温”到“斗战胜佛”这条金光大路,你不仅拥有很高的专业技能,而且很显然,你也有着很强的人际交往能力,你这类人根本不需要对未来有着任何的担心,你在即使一无所有的时候也很容易白手起家。
你这种人算是练辟邪剑法练成了仙,嗯,我无话可说。
你是不是这类人也很容易区别,就像围棋二十岁不称国手终身无望一样,你应该在工作三、四年以后,也就是说二十七岁左右就会发现自已工作中指手划脚的时间比亲自开发的时间要多了,而且大多数这类人在这个年龄手下应该有“兵”了,相反的,如果你快30岁了还天天埋头于电脑前编程序和画板子,或者30多岁了你还没升到部门经理(虽然你总是觉得自已很有希望),基本上可以确定你不是这类人。好了,如果你确定你是这类人,那么你唯一的想法就是尽快爬上中层和高层,因为有时候人生偶然性太大,不占住坑的萝卜很有可能被人拔出来!
简单建议:天天去你的老板家里面拖地和擦桌子!
其路四:出国或考研
有两个搞开发后出国的朋友,其中一个甚至打工打到了一个小公司总工的位置,数据库和软件方面水平巨牛,但仍感觉心里不踏实,于是将自己工作多年的钱忍痛掏出来,出国费加上机票大概将自已辛苦所攒的银子花完,然后又借了一些钱,在02年身上揣着一万美元跑去了加拿大,在加拿大不停的重复找工作,换工作,然后再找工作的循环,找的工作基本上与计算机无关,不过工资总是在1500加元左右,呵呵,折成人民币与他在国内打工拿的基本上差不多,不过租个地下室就花了300加元,然后吃吃喝喝,再买个电脑上上网这类的,基本每月平均还要倒贴一点。前段时间给我的邮件里说,现在身上花的差不多只有5、6000美元了,准备开个小公司,看看能不能往国内倒腾点东东,做最后一搏。另外一个朋友去澳州,时间稍早一些,先是大概摘了一年多的葡萄,后来总算找了个技术工作,每天的工作是画机械图纸,收入还算不错
将近3000澳元,买了个旧车,也算是过上了资本主义生活。不过前年回来一趟,唯一的感叹就是:在国外拿2000美元的生活,绝对不如在国内拿5000人民币的生活舒服。
也有两个考研的朋友,不过其中一个严格的说不是做开发的出身,偏重于市场方面的工作性质,不过我的朋友里面考研的不多,只好凑两个人说说,一个考研后在北京找了个工作,每个月5、6000元钱,但还是做开发,生活仍然与没考研之前没有任何的改变,前途仍然没见到什么大亮的光,还是搞不清楚以后再干些什么,标准的过一天算一天了。另外一个考研后在大学里面找了个工作,工资虽然比他原来打工少了不少,但毕竟终身有靠,稳定了下来,也算修成了正果,这位哥们心情一放松下来,也开始有时间琢磨着业余时间自已做点什么,好像现在慢慢的也开始有了点眉目。
简单建议:这两条路,对开发人员来说都不算是很好,出国十年前是好事,现在难说,考研能成功转行的概率恐怕也不是很大,多半仍然去搞开发,只不过研究生可以多干几年罢了。
其路五:转行到市场
绞尽脑汁的想想,我所知道的人之中只有两个开发人员去了市场,这两个人都不能说是朋友,认识而已。他们都是主动要求去了市场,结果是这两个人均在市场都是干到一年左右,然后都自已开公司了。呵呵,很奇怪,极高的转行成功率!不过仔细想想,我对这两个人的思路佩服的五体投地。能下决心仍掉每月5、6000元的开发职位,从事一个自已并不熟悉的岗位,每月拿个2000多元+提成,但提成那是说不清楚的事情,这个决定,只能让人感觉到他们对自已前途清晰的把握和老谋深算的心机。而且他们不去服务不去生产,挖空心思说服领导去市场(市场部门与开发部门通常是一个公司的核心部门,进入其实并不容易),可以说是有着长远的考虑的。有技术了,再与客户交成朋友,马上就会产生很大的机遇应该是正常的事情。
有实力,有心机,也有着很强的决心力,这种人恐怕早在大学毕业时或更早的时候就已经决定了自已的人生之路,他们的每一步路在若干年前早就计划周全,现在看起来:学会技术->进入市场->寻找商机->开公司,一条多么清楚的人生之路。但就像我们上小学中学时,所有人都知道上大学是我们最清楚的人生路一样,最后只有少数人才能真正达到目标(当然,现在扩招的历害是另外一回事,我是说我们那个时候,也就是:“很久很久以前,当我像你那么大的时候”)。
简单建议:你若是这类人,我的建议是:...嗯?....那个你.你,你别走啊,我还有个事想请你赞助一下啊.....
其路六:开公司自已干
呵呵,看到这一条,发现你的眼睛已经圆了,你肯定千百次的想过这个事情吧,咳咳,其实我从事开发的时候也是天天梦想着这种事情。总想着过两年找个机会就自已干,这个梦想一年又一年的折磨着你也给着你希望。看看吧,开发后来开公司的还真的不少,里面有成功的也有很多失败的,通常开公司都是几个人合伙开始的,有做技术的,有做市场的,几个人一拍即合、狼狈为奸,共同策划了这一个大活动。一般说来能让这几个人下决心走出这一步,产品肯定是先进的,甚至是国内独一无二的,市场也是很大的,负责市场的那个哥们通常会拍着胸保证可以卖出去,并悄悄地告诉你他在某主管领导是他小舅子的同学的二叔,肯定没问题。于是你们几个人找地点、注册执照、买了几个破桌子,再攒了两台电脑,每个人又凑了几万银子,公司开张了!
产品很快出来了,市场的哥们也不负重望,有几个客户表示要试用了,一切看起来都是如此的正常,“.......你坐在老板桌前,不停的有人来汇报工作或者找你签字...人进人出中...你又想起公司再穷也不能只有一把椅子的故事.....”你在梦中笑出声来。
是如此的顺利,你们很快就有单子了,很快的单子让你们凑的那点钱不够了,你们很高兴的每个人又增加了投入,拿出钱时你眼泪汪汪的数着钱说:“这就是我那生蛋的母鸡啊”。你们的产品确实不错,市场也经营的很好,客户慢慢的多了起来,单子来的时候一笔接着一笔,你每天都处于兴奋之中,唯一美中不足的是好像客户回款总是会拖一些日子,不过客户给你保证说:过几天,过几天就付给你们,因为回款总是在计划外,所以你们为了资金的流畅运行又凑了一些钱,这个时候你有一些心事了,因为你的存款折上面的数字已经快趋向于零了。“没事,过两个月等回款了一切都OK了,谁干事业不吃点苦呢?”你这么安慰着自已又投入到工作中去,资金总是在回款和生产经营费用之间走着一个窄窄的小木桥,你的账上总是没有太多的钱,扩大了的公司规模和许多意外情况,使你又一次、二次、三次的与合作者们再次投入了自已的资金,当然,后来的钱你可能已经是借的了.....
终于有一天,你的会计再一次告诉你,老板啊,账上又没现金了,吃过多次苦头的你终于下决心开始重视资金的运行了,你裁掉了一些不必要的人手,减少了开发的投入,要求市场人员签单的时候必须予付XX%的款,回扣也必须等收过款后再付,同时也开始对产品的生产成本开始进行控制。
时间一天一天的过去,因为竟争对手的产品也对你的产品进行了仿造,你的产品慢慢变得不再先进,市场人员开始埋怨公司的合同资金方面规定太严格,不好签单,生产成本的下降通常也导至产品毛病的增多,客户也开始埋怨你的服务人员不能及时进行服务。
终于有一天,你重新走进了人才交流中心,以前你是来招人的,现在你拿着自已的简历开始寻找一个工作
......
公司的成功与否,与产品有关,与市场有关,但更重要的是与资金有关,产品与市场都可以通过资金来弥补,而却没有任何东西可以代替
资金,凡是倒下的公司,99%与资金链的断裂有关。在你决定要开公司以前,先估计一下你公司支持一年所需要的资金数额,包括人工费,生产,场地,广告宣传、市场费用、甚至电、水费等等等等,把你所想到的一切加在一起,得出的值就是..慢..如果你没有实际的开过公司的经验,你需要将此数字乘3,然后就是你开公司一年最少需要的费用,呵呵,公司的实际运营所需要的钱是你想像的3倍以上,你要是不信我也没办法。
简单建议:开公司前最重要的是先确立你后续的资金来源!也就是说钱不够了怎么办?---因为你投入的钱肯定会不够的。
其路七:第二职业
这类的朋友有不少,他们没有脱离开发工作,但是在业余时间又不停的接项目或者在卖产品,在单位里面他们显得并不出众,比起其它人来说他们属于最不愿意加班的一类.为此他们白天通常工作很勤奋.这类人也许不一定可以挣很多钱,但平均下来他们一年之中通常都可以比同事们多挣个几万元.有时候比上班拿得还多.但令人疑惑的是,这类人在生活中更加注重稳定,基本上没见到他们跳过蹧,即使私下里面已经开了个小公司,他们通常也不会辞职.
你的旁边有没有这类人呢?分辨他们很容易:
--电话很多,而且更愿意来电话时离开办公室找个没人的旮旯通话.神秘兮兮给人一种"这家伙是不是有二奶啊?"的感觉的人,通常是这类人。这类人是女性最佳的选择对象:很顾家,不象那些富人容易花心,而比起一般人来说,他们收入相对要高得多。但总结了一下几位这类的开发朋友:也得出了一个令人沮丧的结论:这种人通常个子不高,体形类似桶状.....
简单建议:这好像是开发人员最佳的出路了,但比较丰厚的收入通常让这类人不愿意去冒风险....到现在为止我所认识的这类人还没有一个真正算是成功的。
好了,虽然偶的经历远远说不上丰富,也没有什么成功之处可以自满的,但或许因为比其它朋友痴长了几岁,见过的人可能会稍多一些,所
以斗胆写出了以上的一些文字,让您掉牙了。
下面是偶走过开发这条路上总结出来的一点心得,你可以不看,但看了就千万别把嘴咧的太大:
一、不管是给别人打工还是自已干,都要全心全意的工作,因为你所做的任何一点工作都会让自已的人生多一点筹码,这一点最最重要!这样的例子我至少可以举出两起,优秀的开发人员被其它新公司挖走,并给一定的股份,成为新公司的股东的例子。当时与这样的开发人员一个部门同时工作或更早工作的有许多人,他们平时经常偷点懒,能少干点工作就少干点,有时候还笑话那个平时努力工作的人傻,几年过去了,究竟谁比谁傻?
二、多与市场人员交朋友,你接触他们时可能总会觉得他们知识比你少,甚至素质比你低,可能比你还有点黄。但实际上他们比你更懂这个社会!参加到他们这个圈子中去,和他们一起赌赌钱、一起聊聊天、一起洗洗桑拿、一起.....你会通过他们接触到另外一个世界。
三、机会远比钱重要,挣不挣钱在年轻时并不是特别重要!不论是在实际生活中还是在网上或其它地方,如果有机会参与到除本职工作外的一些项目或产品的开发中(包括你的朋友拉你去做点小生意之类的非开发性质的工作),那怕是帮忙的性质,也要积极介入,至少你会交到很多的朋友,这样你的人生会多出很多的机会。
一个政府项目总结
近几天刚刚交付验收了一个政府的软件项目,在这个项目的开发过程中遇到了不少困难,包括技术上的障碍和一些实际的人为上的问题。一个项目之所以能成功,能让客户满意,领导放心的原因可能大多都差不多,大多都是老生长谈的那几条。但是一个项目失败的原因却各有各的不同。下面再根据自己的体会写一些项目总结,一为了总结不足,积累经验,二为了以后项目中避免犯同样的错误。
一.要和客户有足够有效的沟通
和客户的沟通要贯穿整个项目开发的始终,从立项调研,需求获取到最后的验收测试,后期维护。
1.要尽量多的主动跟客户沟通
客户一般工作都很忙,所以要通过多种方式和客户保持沟通,电子邮件,电话,座谈,调查,会议等。最初的需求尽量保证有几次所有与项目相关的部门和人员都能参加的讨论会,把他们的各自的工作都描述一下,尽量不要遗漏,都罗列出来,因为这是原始需求。这往往不容易做到,因为政府部门很难抽出时间把各部门人员集中在一起来做这些事情的,但是我们必须得这样要求他们,要求他们把这个看成一项工作来抓,因为前期工作做不充分,后面的开发会不会很成功。在对某个功能或者需求不能确定的情况下,最好能整理成列表文档发给客户,让客户以电子版的形式重新描述一下发过来,尽量不要经常打电话骚扰客户,要集中把要了解东西发给客户,以便他们集中精力来处理你问的问题。
2.要尽量保证有效的沟通
每次沟通要有一定的目的性,把沟通交流的结果用文档的形式保存下来;需求制订出来要得到客户的确认,在经过几次反复之后会得到一个相对比较稳定的需求,虽然客户的需求不可能一直不变,这也是很多人搞项目头疼的地方,但是我认为客户的需求实际上是很少改变的,改变的是你对客户需求的理解。对客户的每一个要求都要重视,尤其是客户后来提到的一些改动建议,要让他们以书面的形式发过来,必要的时候要求负责人盖章签字,我们不能为了下面的下面的一个小办事员随便打个电话就对程序做出大的改动。再改动比较大的情况下,我们可以要求客户对合同的变更追加费用,前提是把需求做为合同的附件加进去,防治最后验收的时候造成争执。
3.和客户沟通要找准对象
一般企业或者政府都有专门负责信息的人员,而且最好要求客户那边找一个人专门负责这个项目。这样找对方了解需求的时候就不会出现不知道找谁的情况,客户那边有专人负责会带来很多好处,这个项目就是因为客户那边负责这个项目的人员经常更换而为我们项目的开发造成了很多的不变。
二.提高开发效率和保证项目质量
政府的项目一般都是开始的时候不着急,你催他们准备资料他们也不着急,但是一旦他们把资料准备全了,都交给你了就着急了,要求对方在很短的时间内保证质量的把项目交付。所以如何提高开发效率和保证项目质量是确保项目成功的关键。
1.保证良好充分的测试
当然软件测试的范畴很大,但是为了赶进度我们往往不能不保证进行所有的软件测试。软件的测试也是遍布整个项目开发周期的,我了解了一下TDD,TDD的思想很好,很适合开发中小型的项目,实施起来也很方便,但是不能纯粹的用敏捷开发的理论,必要的文档还是需要的。我认为代码模块的单元测试,开发最后阶段的集成测试和部署后的整体功能测试和用户验收测试是必不可少的。项目进度再紧张也要进行单元测试,只要保证单元测试能通过,以后代码可以慢慢重构。集成测试保证项目各个模块能良好的协作共同完成复杂的任务,这点不能保证的话,展示给客户的最终功能就不能保证。而功能测试和用户验收测试是纯粹的黑盒测试,自己内部人员先对照原始客户的需求进行功能测试,列出BUG列表,经过几次反复修改后给客户一个可以进行验收测试的系统。
2.保证相对必要的文档以及保证文档的可用性
每个模块的文档要独立起来,要实现的目标,测试的结果,模块所用的数据库的结构,存储过程,设计思路,调用的接口等这些是必须的。我也不建议面面俱到的文档,但必要的需求文档,模块文档,测试文档是必须的,我们的项目小的不足以让我们去学习庞大的RUP什么的。
3.迭代开发
刚开始可以根据客户的需求弄出一个蓝图来,交给客户看,以便让客户能尽量早的知道最终的开发出来的系统是什么样子的,这个蓝图要尽量直观,一般在需求整理完毕后一周就能出来,这也是指导以后开发工作的东西,要完整的包含所有的域模型,便于开发人员对问题域的理解。然后把优先级最高的一系列功能完整后出一个DEMO版给客户,要让客户尽量早的发现正在制作的项目和用户想要的结果的之间的偏离和差距,告诉你后以便你尽早的调整,别等你的正式版出来后用户发现这个功能你做的不对,你就傻了,那时候要改动的地方就太多了。然后再弄完善一下给用户个beta版,这时候就已经接近最终版本了,可能还有一些小BUG。最后把小BUG完善修复一下给客户正式版1.0让客户验收。至于二期项目以后再说,先把一期项目的余款结了再说,对吧。
4.制订开发规范
开发规范订的太死会限制程序员,每个开发人员都会有一些习惯,但是为了协作,制订一个相对通用的规范是有必要的。包括文档的规范,数据库设计规范,编码规范以及各种命名规则。尽量用一些业界通用的规范,网上都有,我CSDN的博客上也整理了一些,MSDN的类库开发人员指南里面也有一些。尽管某些规范很有争议,我感觉你也得选择其中一种来做为你的项目开发规范。
5.建立开发基础
保证机器和软件的可用,尽量大的内存,尽量快的处理器,操作系统,开发工具都要到位,该想到的就得想到,还要给开发人员一个相对安静舒适的环境,最好能很方便的喝到冰箱里的可乐,而且能在累的时候有绿色的植物看。再一个就是建立一个开发基础结构,这个也颇有争议,几乎每个公司都有自己的系统类库,开发框架以及配套的代码生成工具,这都很好,在开始可以对员工做适当的培训,让他们都能体验自底向上设计的好处,都能用的上这个架构,你可以在架构中要求开发人员以指定的方式实现某些通用的任务,比如说日志记录和错误处理等,而不是让他们使用自己习惯的方式去处理问题,因为.NET的灵活性让实现一个任务有很多中方案和手段。
小节:虽然这个帖子没有讨论具体技术,而且都是一些空话套话,并且这些空话套话可能别人也都说的不带说了,但我感觉还是有必要自己总结一下的。
http://onlytiancai.cnblogs.com/archive/2005/11/03/218335.html
昨天我在程序中添加了一个监听器,
net.risesoft.ContextListener
在修改程序时发现web.xml总是报错,最后Google一下后发现文件内的元素是次序。
现将web.xml规则经过整理总结如下:
1 定义头和根元素
部署描述符文件就像所有XML文件一样,必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。
DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的servlet规范的版本(如2.2或2.3)并指定管理此文件其余部分内容的语法的DTD(Document Type Definition,文档类型定义)。
所有部署描述符文件的顶层(根)元素为web-app。请注意,XML元素不像HTML,他们是大小写敏感的。因此,web-App和WEB-APP都是不合法的,web-app必须用小写。
2 部署描述符文件内的元素次序
XML 元素不仅是大小写敏感的,而且它们还对出现在其他元素中的次序敏感。例如,XML头必须是文件中的第一项,DOCTYPE声明必须是第二项,而web- app元素必须是第三项。在web-app元素内,元素的次序也很重要。服务器不一定强制要求这种次序,但它们允许(实际上有些服务器就是这样做的)完全拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。
下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如,此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意,所有这些元素都是可选的。因此,可以省略掉某一元素,但不能把它放于不正确的位置。
l icon icon元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。
l display-name display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。
l description description元素给出与此有关的说明性文本。
l context-param context-param元素声明应用范围内的初始化参数。
l filter 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。
l filter-mapping 一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。
l listener servlet API的版本2.3增加了对事件监听程序的支持,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。
l servlet 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。
l servlet-mapping 服务器一般为servlet提供一个缺省的URL:
http://host/webAppPrefix/servlet/ServletName。但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。l session-config 如果某个会话在一定时间内未被访问,服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法明确设置单个会话对象的超时值,或者可利用session-config元素制定缺省超时值。
l mime-mapping 如果Web应用具有想到特殊的文件,希望能保证给他们分配特定的MIME类型,则mime-mapping元素提供这种保证。
l welcom-file-list welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时,使用哪个文件。
l error-page error-page元素使得在返回特定HTTP状态代码时,或者特定类型的异常被抛出时,能够制定将要显示的页面。
l taglib taglib元素对标记库描述符文件(Tag Libraryu Descriptor file)指定别名。此功能使你能够更改TLD文件的位置,而不用编辑使用这些文件的JSP页面。
l resource-env-ref resource-env-ref元素声明与资源相关的一个管理对象。
l resource-ref resource-ref元素声明一个资源工厂使用的外部资源。
l security-constraint security-constraint元素制定应该保护的URL。它与login-config元素联合使用
l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。
l security-role security-role元素给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。
l env-entry env-entry元素声明Web应用的环境项。
l ejb-ref ejb-ref元素声明一个EJB的主目录的引用。
l ejb-local-ref ejb-local-ref元素声明一个EJB的本地主目录的应用。
3 分配名称和定制的UL
在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称,使用servlet-mapping元素将定制的URL与刚分配的名称相关联。
3.1 分配名称
为了提供初始化参数,对servlet或JSP页面定义一个定制URL或分配一个安全角色,必须首先给servlet或JSP页面一个名称。可通过 servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素(在web-app元素内),如下所示:
Test
moreservlets.TestServlet
这表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给 servlet一个名称具有两个主要的含义。首先,初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在 URL而不是类名中使用此名称。因此,利用刚才给出的定义,URL
http://host/webAppPrefix/servlet/Test 可用于
http://host/webAppPrefix/servlet/moreservlets.TestServlet 的场所。
请记住:XML元素不仅是大小写敏感的,而且定义它们的次序也很重要。例如,web-app元素内所有servlet元素必须位于所有servlet- mapping元素(下一小节介绍)之前,而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素(如果有的话)之前。类似地,servlet 的servlet-name子元素也必须出现在servlet-class之前。5.2节"部署描述符文件内的元素次序"将详细介绍这种必需的次序。
例如,程序清单5-1给出了一个名为TestServlet的简单servlet,它驻留在moreservlets程序包中。因为此servlet是扎根在一个名为deployDemo的目录中的Web应用的组成部分,所以TestServlet.class放在deployDemo/WEB- INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图 5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。
程序清单5-1 TestServlet.java
package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to illustrate servlet naming
* and custom URLs.
*
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/
public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Test Servlet") +
"\n" +
"
URI: " + uri + "
\n" +
"");
}
}
程序清单5-2 web.xml(说明servlet名称的摘录)
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">
Test
moreservlets.TestServlet
3.2 定义定制的URL
大多数服务器具有一个缺省的serlvet URL:
http://host/webAppPrefix/servlet/packageName.ServletName。虽然在开发中使用这个URL很方便,但是我们常常会希望另一个URL用于部署。例如,可能会需要一个出现在Web应用顶层的URL(如,http: //host/webAppPrefix/Anyname),并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外,对许多开发人员来说,顶层URL看上去比更长更麻烦的缺省URL更简短。
事实上,有时需要使用定制的URL。比如,你可能想关闭缺省URL映射,以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL,那么你怎样访问servlet呢?这时只有使用定制的URL了。
为了分配一个定制的URL,可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet- name元素提供了一个任意名称,可利用此名称引用相应的servlet;url-pattern描述了相对于Web应用的根目录的URL。url- pattern元素的值必须以斜杠(/)起始。
下面给出一个简单的web.xml摘录,它允许使用URL
http://host/webAppPrefix/UrlTest而不是http://host/webAppPrefix/servlet/Test或http: //host/webAppPrefix/servlet/moreservlets.TestServlet。请注意,仍然需要XML头、 DOCTYPE声明以及web-app封闭元素。此外,可回忆一下,XML元素出现地次序不是随意的。特别是,需要把所有servlet元素放在所有 servlet-mapping元素之前。
Test
moreservlets.TestServlet
Test
/UrlTest
URL模式还可以包含通配符。例如,下面的小程序指示服务器发送所有以Web应用的URL前缀开始,以..asp结束的请求到名为BashMS的servlet。
BashMS
msUtils.ASPTranslator
BashMS
/*.asp
3.3 命名JSP页面
因为JSP页面要转换成sevlet,自然希望就像命名servlet一样命名JSP页面。毕竟,JSP页面可能会从初始化参数、安全设置或定制的URL中受益,正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的,但存在一个关键的猜疑:即,你不知道JSP页面的实际类名(因为系统自己挑选这个名字)。因此,为了命名JSP页面,可将jsp-file元素替换为servlet-calss元素,如下所示:
Test
/TestPage.jsp
命名JSP页面的原因与命名servlet的原因完全相同:即为了提供一个与定制设置(如,初始化参数和安全设置)一起使用的名称,并且,以便能更改激活 JSP页面的URL(比方说,以便多个URL通过相同页面得以处理,或者从URL中去掉.jsp扩展名)。但是,在设置初始化参数时,应该注意,JSP页面是利用jspInit方法,而不是init方法读取初始化参数的。
例如,程序清单5-3给出一个名为TestPage.jsp的简单JSP页面,它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配一个注册名PageName,然后将此注册名与
http://host/webAppPrefix/UrlTest2/anything 形式的URL相关联的web.xml文件(即,deployDemo/WEB-INF/web.xml)的一部分。
程序清单5-3 TestPage.jsp
URI: <%= request.getRequestURI() %>
程序清单5-4 web.xml(说明JSP页命名的摘录)
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">
PageName
/TestPage.jsp
PageName
/UrlTest2/*
4 禁止激活器servlet
对servlet 或JSP页面建立定制URL的一个原因是,这样做可以注册从 init(servlet)或jspInit(JSP页面)方法中读取得初始化参数。但是,初始化参数只在是利用定制URL模式或注册名访问 servlet或JSP页面时可以使用,用缺省URL
http://host/webAppPrefix/servlet/ServletName 访问时不能使用。因此,你可能会希望关闭缺省URL,这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet,因为多数服务器具有一个用缺省的servlet URL注册的标准servlet,并激活缺省的URL应用的实际servlet。
有两种禁止此缺省URL的主要方法:
l 在每个Web应用中重新映射/servlet/模式。
l 全局关闭激活器servlet。
重要的是应该注意到,虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多,但重新映射可以用一种完全可移植的方式来完成。相反,全局禁止激活器servlet完全是针对具体机器的,事实上有的服务器(如ServletExec)没有这样的选择。下面的讨论对每个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。
4.1 重新映射/servlet/URL模式
在一个特定的Web应用中禁止以
http://host/webAppPrefix/servlet/ 开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet,并使用前一节讨论的url-pattern元素将所有匹配请求转向该 servlet。只要简单地使用:
/servlet/*作为servlet-mapping元素中的模式即可。
例如,程序清单5-5给出了将SorryServlet servlet(程序清单5-6)与所有以
http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。
程序清单5-5 web.xml(说明JSP页命名的摘录)
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">
Sorry
moreservlets.SorryServlet
Sorry
/servlet/*
程序清单5-6 SorryServlet.java
package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to give error messages to
* users who try to access default servlet URLs
* (i.e.,
http://host/webAppPrefix/servlet/ServletName)* in Web applications that have disabled this
* behavior.
*
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/
public class SorryServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Invoker Servlet Disabled.";
out.println(ServletUtilities.headWithTitle(title) +
"\n" +
"
" + title + "
\n" +
"Sorry, access to servlets by means of\n" +
"URLs that begin with\n" +
"
http://host/webAppPrefix/servlet/\n" +
"has been disabled.\n" +
"");
}
public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}
4.2 全局禁止激活器:Tomcat
Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法:
1.禁止激活器: Tomcat 4
Tomcat 4用与前面相同的方法关闭激活器servlet,即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在 install_dir/conf中的一个服务器专用的全局web.xml文件,而前面使用的是存放在每个Web应用的WEB-INF目录中的标准 web.xml文件。
因此,为了在Tomcat 4中关闭激活器servlet,只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可,如下所示:
再次提醒,应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的,此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。
2.禁止激活器:Tomcat3
在Apache Tomcat的版本3中,通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省 servlet URL。例如,下面是禁止使用缺省servlet URL的server.xml文件的一部分。
5 初始化和预装载servlet与JSP页面
这里讨论控制servlet和JSP页面的启动行为的方法。特别是,说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。
5.1 分配servlet初始化参数
利用init-param元素向servlet提供初始化参数,init-param元素具有param-name和param-value子元素。例如,在下面的例子中,如果initServlet servlet是利用它的注册名(InitTest)访问的,它将能够从其方法中调用getServletConfig(). getInitParameter("param1")获得"Value 1",调用getServletConfig().getInitParameter("param2")获得"2"。
InitTest
moreservlets.InitServlet
param1
value1
param2
2
在涉及初始化参数时,有几点需要注意:
l 返回值。GetInitParameter的返回值总是一个String。因此,在前一个例子中,可对param2使用Integer.parseInt获得一个int。
l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。
l 缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此,在这个例子中,param1和 param2初始化参数将能够在使用URL
http://host/webAppPrefix/servlet/InitTest 时可用,但在使用URL
http://host/webAppPrefix/servlet/myPackage.InitServlet 时不能使用。
例如,程序清单5-7给出一个名为InitServlet的简单servlet,它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。
程序清单5-7 InitServlet.java
package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
/** Simple servlet used to illustrate servlet
* initialization parameters.
*
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/
public class InitServlet extends HttpServlet {
private String firstName, emailAddress;
public void init() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Init Servlet") +
"\n" +
"
Init Parameters:
\n" +
"
\n" +
"
- First name: " + firstName + "\n" +
"
- Email address: " + emailAddress + "\n" +
"
\n" +
"");
}
}
程序清单5-8 web.xml(说明初始化参数的摘录)
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">
InitTest
moreservlets.InitServlet
firstName
Larry
emailAddress
Ellison@Microsoft.com
5.2 分配JSP初始化参数
给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。
1)使用jsp-file而不是servlet-class。因此,WEB-INF/web.xml文件的servlet元素如下所示:
PageName
/RealPage.jsp
...
...
...
2) 几乎总是分配一个明确的URL模式。对servlet,一般相应地使用以
http://host/webAppPrefix/servlet/ 开始的缺省URL。只需记住,使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如,在上面给出的例子中,可用URL
http://host/webAppPrefix/servlet/PageName 访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时,许多用户似乎不喜欢应用常规的servlet的URL。此外,如果 JSP页面位于服务器为其提供了目录清单的目录中(如,一个既没有index.html也没有index.jsp文件的目录),则用户可能会连接到此 JSP页面,单击它,从而意外地激活未初始化的页面。因此,好的办法是使用url-pattern(5.3节)将JSP页面的原URL与注册的 servlet名相关联。这样,客户机可使用JSP页面的普通名称,但仍然激活定制的版本。例如,给定来自项目1的servlet定义,可使用下面的 servlet-mapping定义:
PageName
/RealPage.jsp
3)JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此,使用JSP声明提供一个init方法是不合法的,必须制定jspInit方法。
为了说明初始化JSP页面的过程,程序清单5-9给出了一个名为InitPage.jsp的JSP页面,它包含一个jspInit方法且放置于 deployDemo Web应用层次结构的顶层。一般,
http://host/deployDemo/InitPage.jsp 形式的URL将激活此页面的不具有初始化参数访问权的版本,从而将对firstName和emailAddress变量显示null。但是, web.xml文件(程序清单5-10)分配了一个注册名,然后将该注册名与URL模式/InitPage.jsp相关联。
程序清单5-9 InitPage.jsp
Init Parameters:
- First name: <%= firstName %>
- Email address: <%= emailAddress %>
<%!
private String firstName, emailAddress;
public void jspInit() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
%>
程序清单5-10 web.xml(说明JSP页面的init参数的摘录)
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
http://java.sun.com/dtd/web-app_2_3.dtd">
InitPage
/InitPage.jsp
firstName
Bill
emailAddress
gates@oracle.com
InitPage
/InitPage.jsp
5.3 提供应用范围内的初始化参数
一般,对单个地servlet或JSP页面分配初始化参数。指定的servlet或JSP页面利用ServletConfig的 getInitParameter方法读取这些参数。但是,在某些情形下,希望提供可由任意servlet或JSP页面借助ServletContext 的getInitParameter方法读取的系统范围内的初始化参数。
可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素,如下所示:
support-email
blackhole@mycompany.com
可回忆一下,为了保证可移植性,web.xml内的元素必须以正确的次序声明。但这里应该注意,context-param元素必须出现任意与文档有关的元素(icon、display-name或description)之后及filter、filter-mapping、listener或 servlet元素之前。
5.4 在服务器启动时装载servlet
假如servlet或JSP页面有一个要花很长时间执行的init (servlet)或jspInit(JSP)方法。例如,假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种情况下,在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此,可利用servlet的load-on- startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。
…
…
可以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前装载较少数目的servlet或JSP页面。例如,下面的servlet项(放置在Web应用的WEB-INF目录下的web.xml文件中的web-app元素内)将指示服务器首先装载和初始化SearchServlet,然后装载和初始化由位于Web应用的result目录中的index.jsp文件产生的 servlet。
Search
myPackage.SearchServlet
1
Results
/results/index.jsp
2
6 声明过滤器
servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器,但为了使用与过滤器有关的元素,必须在web.xml中使用版本2.3的DTD。
过滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前,必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时,执行链中的下一个过滤器。如果没有其他过滤器,servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权,因此,它们可以查看客户机名、查找到来的cookie等。为了访问servlet或JSP页面的输出,过滤器可将响应对象包裹在一个替身对象(stand-in object)中,比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后,过滤器可检查缓冲区,如有必要,就对它进行修改,然后传送到客户机。
例如,程序清单5-11帝国难以了一个简单的过滤器,只要访问相关的servlet或JSP页面,它就截取请求并在标准输出上打印一个报告(开发过程中在桌面系统上运行时,大多数服务器都可以使用这个过滤器)。
程序清单5-11 ReportFilter.java
package moreservlets;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
/** Simple filter that prints a report on the standard output
* whenever the associated servlet or JSP page is accessed.
*
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/
public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
" tried to access " +
req.getRequestURL() +
" on " + new Date() + ".");
chain.doFilter(request,response);
}
public void init(FilterConfig config)
throws ServletException {
}
public void destroy() {}
}
一旦建立了一个过滤器,可以在web.xml中利用filter元素以及filter-name(任意名称)、file-class(完全限定的类名)和(可选的)init-params子元素声明它。请注意,元素在web.xml的web-app元素中出现的次序不是任意的;允许服务器(但不是必需的)强制所需的次序,并且实际中有些服务器也是这样做的。但这里要注意,所有filter元素必须出现在任意filter-mapping元素之前, filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。
例如,给定上述的ReportFilter类,可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter(位于moreservlets程序包中)相关联。
Reporter
moresevlets.ReportFilter
一旦命名了一个过滤器,可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。
首先,可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名(此servlet名必须稍后在相同的 web.xml文件中使用servlet元素声明)关联。例如,下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName 的servlet或JSP页面,就运行名为Reporter的过滤器。
Reporter
SomeServletName
其次,可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如,相面的程序片段指示系统只要访问Web应用中的任意URL,就运行名为Reporter的过滤器。
Reporter
/*
例如,程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字 PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http: //host/webAppPrefix/UrlTest2/ 开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上,程序清单5- 12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项,可看到下面的标准输出形式的调试报告(换行是为了容易阅读)。
audit.irs.gov tried to access
http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html
on Tue Dec 25 13:12:29 EDT 2001.
程序清单5-12 Web.xml(说明filter用法的摘录)
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
Reporter
moresevlets.ReportFilter
Reporter
PageName
PageName
/RealPage.jsp
PageName
/UrlTest2/*
7 指定欢迎页
假如用户提供了一个像http: //host/webAppPrefix/directoryName/ 这样的包含一个目录名但没有包含文件名的URL,会发生什么事情呢?用户能得到一个目录表?一个错误?还是标准文件的内容?如果得到标准文件内容,是 index.html、index.jsp、default.html、default.htm或别的什么东西呢?
Welcome-file-list 元素及其辅助的welcome-file元素解决了这个模糊的问题。例如,下面的web.xml项指出,如果一个URL给出一个目录名但未给出文件名,服务器应该首先试用index.jsp,然后再试用index.html。如果两者都没有找到,则结果有赖于所用的服务器(如一个目录列表)。
index.jsp
index.html
虽然许多服务器缺省遵循这种行为,但不一定必须这样。因此,明确地使用welcom-file-list保证可移植性是一种良好的习惯。
8 指定处理错误的页面
现在我了解到,你在开发servlet和JSP页面时从不会犯错误,而且你的所有页面是那样的清晰,一般的程序员都不会被它们的搞糊涂。但是,是人总会犯错误的,用户可能会提供不合规定的参数,使用不正确的URL或者不能提供必需的表单字段值。除此之外,其它开发人员可能不那么细心,他们应该有些工具来克服自己的不足。
error-page元素就是用来克服这些问题的。它有两个可能的子元素,分别是:error-code和exception- type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此 URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息,这两个属性分别是:javax.servlet.error.status_code和javax.servlet.error.message。
可回忆一下,在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住,error-page出现在web.xml文件的末尾附近,servlet、servlet-name和welcome-file-list之后即可。
8.1 error-code元素
为了更好地了解error-code元素的值,可考虑一下如果不正确地输入文件名,大多数站点会作出什么反映。这样做一般会出现一个404错误信息,它表示不能找到该文件,但几乎没提供更多有用的信息。另一方面,可以试一下在www.microsoft.com、www.ibm.com 处或者特别是在www.bea.com 处输出未知的文件名。这是会得出有用的消息,这些消息提供可选择的位置,以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。事实上rm-error-page子元素)。由form-login-page给出的HTML表单必须具有一个j_security_check的 ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如,程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令,并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。
程序清单5-19 web.xml(说明login-config的摘录)
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
...
FORM
/login.jsp
/login-error.jsp
9.2 限制对Web资源的访问
现在,可以指示服务器使用何种验证方法了。"了不起,"你说道,"除非我能指定一个来收到保护的 URL,否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在 web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素,分别是:web-resource-collection、 auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。
l web-resource-collection
此元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元素由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的 HTTP命令(GET、POST等,缺省为所有方法)的http-method元素和一个提供资料的可选description元素组成。例如,下面的 Web-resource-collection项(在security-constratint元素内)指出Web应用的proprietary目录中所有文档应该受到保护。
Proprietary
/propritary/*
重要的是应该注意到,url-pattern仅适用于直接访问这些资源的客户机。特别是,它不适合于通过MVC体系结构利用 RequestDispatcher来访问的页面,或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好处。例如,servlet可利用MVC体系结构查找数据,把它放到bean中,发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直接访问受保护的JSP页面,而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是,这种不匀称的行为可能让开发人员放松警惕,使他们偶然对应受保护的资源提供不受限制的访问。
l auth-constraint
尽管web-resource-collention元素质出了哪些URL应该受到保护,但是auth-constraint元素却指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role- name元素,以及包含(可选)一个描述角色的description元素。例如,下面web.xml中的security-constraint元素部门规定只有指定为Administrator或Big Kahuna(或两者)的用户具有指定资源的访问权。
...
administrator
kahuna
重要的是认识到,到此为止,这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令,完全有赖于具体的系统。
例如,Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联,正如下面例子中所示,它指出用户joe(口令bigshot)和jane(口令enaj)属于administrator和kahuna角色。
l user-data-constraint
这个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素(合法值为NONE、 INTEGRAL或CONFIDENTIAL),并且可选地包含一个description元素。transport-guarantee为NONE值将对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上(并且在未来的HTTP版本中),在 INTEGRAL和CONFIDENTIAL之间可能会有差别,但在当前实践中,他们都只是简单地要求用SSL。例如,下面指示服务器只允许对相关资源做 HTTPS连接:
CONFIDENTIAL
l display-name
security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。
9.3 分配角色名
迄今为止,讨论已经集中到完全由容器(服务器)处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。
例如,容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面,但只允许bigwig用户修改此页面的参数。完成这种更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法,并据此修改访问。
Servlet的 security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如,假如编写了一个调用 request.isUserInRole("boss")的servlet,但后来该servlet被用在了一个其口令文件调用角色manager而不是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。
boss
manager
也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级IDE容易处理安全信息。
10 控制会话超时
如果某个会话在一定的时间内未被访问,服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直接设置个别会话对象的超时值。如果不采用这种方法,则缺省的超时值由具体的服务器决定。但可利用session-config和session- timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟,因此,下面的例子设置缺省会话超时值为三个小时(180分钟)。
180
11 Web应用的文档化
越来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder Enterprise Edition、Macromedia UltraDev、Allaire JRun Studio(写此文时,已被Macromedia收购)以及IBM VisuaAge for Java等。
大量的web.xml元素不仅是为服务器设计的,而且还是为可视开发环境设计的。它们包括icon、display-name和discription等。
可回忆一下,在web.xml内以适当地次序声明web-app子元素很重要。不过,这里只要记住icon、display-name和description是web.xml的web-app元素内的前三个合法元素即可。
l icon
icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x 16的GIF或JPEG图像,用large-icon元素指定一幅32 x 32的图像。下面举一个例子:
/images/small-book.gif
/images/tome.jpg
l display-name
display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。
Rare Books
l description
description元素提供解释性文本,如下所示:
This Web application represents the store developed for
rare-books.com, an online bookstore specializing in rare
and limited-edition books.
12 关联文件与MIME类型
服务器一般都具有一种让Web站点管理员将文件扩展名与媒体相关联的方法。例如,将会自动给予名为mom.jpg的文件一个image/jpeg的MIME 类型。但是,假如你的Web应用具有几个不寻常的文件,你希望保证它们在发送到客户机时分配为某种MIME类型。mime-mapping元素(具有 extension和mime-type子元素)可提供这种保证。例如,下面的代码指示服务器将application/x-fubar的MIME类型分配给所有以.foo结尾的文件。
foo
application/x-fubar
或许,你的Web应用希望重载(override)标准的映射。例如,下面的代码将告诉服务器在发送到客户机时指定.ps文件作为纯文本(text/plain)而不是作为PostScript(application/postscript)。
ps
application/postscript
13 定位TLD
JSP taglib元素具有一个必要的uri属性,它给出一个TLD(Tag Library Descriptor)文件相对于Web应用的根的位置。TLD文件的实际名称在发布新的标签库版本时可能会改变,但我们希望避免更改所有现有JSP页面。此外,可能还希望使用保持taglib元素的简练性的一个简短的uri。这就是部署描述符文件的taglib元素派用场的所在了。Taglib包含两个子元素:taglib-uri和taglib-location。taglib-uri元素应该与用于JSP taglib元素的uri属性的东西相匹配。Taglib-location元素给出TLD文件的实际位置。例如,假如你将文件chart-tags- 1.3beta.tld放在WebApp/WEB-INF/tlds中。现在,假如web.xml在web-app元素内包含下列内容。
/charts.tld
/WEB-INF/tlds/chart-tags-1.3beta.tld
给出这个说明后,JSP页面可通过下面的简化形式使用标签库。
<%@ taglib uri="/charts.tld" prefix="somePrefix" %>
14 指定应用事件监听程序
应用事件监听器程序是建立或修改servlet环境或会话对象时通知的类。它们是servlet规范的版本2.3中的新内容。这里只简单地说明用来向Web应用注册一个监听程序的web.xml的用法。
注册一个监听程序涉及在web.xml的web-app元素内放置一个listener元素。在listener元素内,listener-class元素列出监听程序的完整的限定类名,如下所示:
package.ListenerClass
虽然listener元素的结构很简单,但请不要忘记,必须正确地给出web-app元素内的子元素的次序。listener元素位于所有的servlet 元素之前以及所有filter-mapping元素之后。此外,因为应用生存期监听程序是serlvet规范的2.3版本中的新内容,所以必须使用 web.xml DTD的2.3版本,而不是2.2版本。
例如,程序清单5-20给出一个名为ContextReporter的简单的监听程序,只要Web应用的Servlet-Context建立(如装载Web应用)或消除(如服务器关闭)时,它就在标准输出上显示一条消息。程序清单5-21给出此监听程序注册所需要的web.xml文件的一部分。
程序清单5-20 ContextReporterjava
package moreservlets;
import javax.servlet.*;
import java.util.*;
/** Simple listener that prints a report on the standard output
* when the ServletContext is created or destroyed.
*
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/
public class ContextReporter implements ServletContextListener {
public void contextInitialized(ServletContextEvent event) {
System.out.println("Context created on " +
new Date() + ".");
}
public void contextDestroyed(ServletContextEvent event) {
System.out.println("Context destroyed on " +
new Date() + ".");
}
}
程序清单5-21 web.xml(声明一个监听程序的摘录)
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
…
package.ListenerClass
...
15 J2EE元素
本节描述用作J2EE环境组成部分的Web应用的web.xml元素。这里将提供一个简明的介绍,详细内容可以参阅http://java.sun.com/j2ee/j2ee-1_3-fr-spec.pdf的Java 2 Plantform Enterprise Edition版本1.3规范的第5章。
l distributable
distributable 元素指出,Web应用是以这样的方式编程的:即,支持集群的服务器可安全地在多个服务器上分布Web应用。例如,一个可分布的应用必须只使用 Serializable对象作为其HttpSession对象的属性,而且必须避免用实例变量(字段)来实现持续性。distributable元素直接出现在discription元素之后,并且不包含子元素或数据,它只是一个如下的标志。
l resource-env-ref
resource -env-ref元素声明一个与某个资源有关的管理对象。此元素由一个可选的description元素、一个resource-env-ref- name元素(一个相对于java:comp/env环境的JNDI名)以及一个resource-env-type元素(指定资源类型的完全限定的类),如下所示:
jms/StockQueue
javax.jms.Queue
l env-entry
env -entry元素声明Web应用的环境项。它由一个可选的description元素、一个env-entry-name元素(一个相对于java: comp/env环境JNDI名)、一个env-entry-value元素(项值)以及一个env-entry-type元素(java.lang程序包中一个类型的完全限定类名,java.lang.Boolean、java.lang.String等)组成。下面是一个例子:
minAmout
100.00
minAmout
l ejb-ref
ejb -ref元素声明对一个EJB的主目录的应用。它由一个可选的description元素、一个ejb-ref-name元素(相对于java: comp/env的EJB应用)、一个ejb-ref-type元素(bean的类型,Entity或Session)、一个home元素(bean的主目录接口的完全限定名)、一个remote元素(bean的远程接口的完全限定名)以及一个可选的ejb-link元素(当前bean链接的另一个 bean的名称)组成。
l ejb-local-ref
ejb-local-ref元素声明一个EJB的本地主目录的引用。除了用local-home代替home外,此元素具有与ejb-ref元素相同的属性并以相同的方式使用。
很多的时侯,做Oracle DBA的我们,当应用管理员向我们通告现在应用很慢、数据库很慢的时侯,我们到数据库时做几个示例的Select也发现同样的问题时,有些时侯我们会无从下手,因为我们认为数据库的各种命种率都是满足Oracle文档的建议。实际上如今的优化己经向优化等待(waits)转型了,实际中性能优化最根本的出现点也都集中在IO,这是影响性能最主要的方面,由系统中的等待去发现Oracle库中的不足、操作系统某些资源利用的不合理是一个比较好的办法,下面把我的一点实践经验与大家分享一下,本文测重于Unix环境。
一、通过操作系统的一些工具检查系统的状态,比如CPU、内存、交换、磁盘的利用率,根据经验或与系统正常时的状态相比对,有时系统表面上看起来看空闲这也可能不是一个正常的状态,因为cpu可能正等待IO的完成。除此之外我们还应观注那些占用系统资源(cpu、内存)的进程。
1、如何检查操作系统是否存在IO的问题?使用的工具有sar,这是一个比较通用的工具。
Rp1#Sar -u 2 10
即每隔2秒检察一次,共执行20次,当然这些都由你决定了。
示例返回:
HP-UX hpn2 B.11.00 U 9000/800 08/05/03
18:26:32 %usr %sys %wio %idle
18:26:34 80 9 12 0
18:26:36 78 11 11 0
18:26:38 78 9 13 1
18:26:40 81 10 9 1
18:26:42 75 10 14 0
18:26:44 76 8 15 0
18:26:46 80 9 10 1
18:26:48 78 11 11 0
18:26:50 79 10 10 0
18:26:52 81 10 9 0
Average 79 10 11 0
其中的%usr指的是用户进程使用的cpu资源的百分比,%sys指的是系统资源使用cpu资源的百分比,%wio指的是等待io完成的百分比,这是值得我们观注的一项,%idle即空闲的百分比。如果wio列的值很大,如在35%以上,说明你的系统的IO存在瓶颈,你的CPU花费了很大的时间去等待IO的完成。Idle很小说明系统CPU很忙。像我的这个示例,可以看到wio平均值为11说明io没什么特别的问题,而我的idle值为零,说明我的cpu已经满负荷运行了。
当你的系统存在IO的问题,可以从以下几个方面解决
*联系相应的操作系统的技术支持对这方面进行优化,比如hp-ux在划定卷组时的条带化等方面。
*查找Oracle中不合理的sql语句,对其进行优化
*对Oracle中访问量频繁的表除合理建索引外,再就是把这些表分表空间存放以免访问上产生热点,再有就是对表合理分区。
2、关注一下内存。
常用的工具便是vmstat,对于hp-unix来说可以用glance,Aix来说可以用topas,当你发现vmstat中pi列非零,memory中的free列的值很小,glance,topas中内存的利用率多于80%时,这时说明你的内存方面应该调节一下了,方法大体有以下几项。
*划给Oracle使用的内存不要超过系统内存的1/2,一般保在系统内存的40%为益。
为系统增加内存
*如果你的连接特别多,可以使用MTS的方式
*打全补丁,防止内存漏洞。
3、如何找到点用系用资源特别大的Oracle的session及其执行的语句。
Hp-unix可以用glance,top
IBM AIX可以用topas
此外可以使用ps的命令。
通过这些程序我们可以找到点用系统资源特别大的这些进程的进程号,我们就可以通过以下的sql语句发现这个pid正在执行哪个sql,这个sql最好在pl/sql developer,toad等软件中执行, 把<>中的spid换成你的spid就可以了。
SELECT a.username,
a.machine,
a.program,
a.sid,
a.serial#,
a.status,
c.piece,
c.sql_text
FROM v$session a,
v$process b,
v$sqltext c
WHERE b.spid=
AND b.addr=a.paddr
AND a.sql_address=c.address(+)
ORDER BY c.piece
我们就可以把得到的这个sql分析一下,看一下它的执行计划是否走索引,对其优化避免全表扫描,以减少IO等待,从而加快语句的执行速度。
提示:我在做优化sql时,经常碰到使用in的语句,这时我们一定要用exists把它给换掉,因为Oracle在处理In时是按Or的方式做的,即使使用了索引也会很慢。
比如:
SELECT col1,col2,col3 FROM table1 a
WHERE a.col1 not in (SELECT col1 FROM table2)
可以换成:
SELECT col1,col2,col3 FROM table1 a
WHERE not exists
(SELECT 'x' FROM table2 b
WHERE a.col1=b.col1)
4、另一个有用的脚本:查找前十条性能差的sql.
SELECT * FROM
(
SELECT PARSING_USER_ID
EXECUTIONS,
SORTS,
COMMAND_TYPE,
DISK_READS,
sql_text
FROM v$sqlarea
ORDER BY disk_reads DESC
)
WHERE ROWNUM<10 ;
二、迅速发现Oracle Server的性能问题的成因,我们可以求助于v$session_wait这个视图,看系统的这些session在等什么,使用了多少的IO。以下是我提供的参考脚本:
脚本说明:查看占io较大的正在运行的session
SELECT se.sid,
se.serial#,
pr.SPID,
se.username,
se.status,
se.terminal,
se.program,
se.MODULE,
se.sql_address,
st.event,
st.p1text,
si.physical_reads,
si.block_changes
FROM v$session se,
v$session_wait st,
v$sess_io si,
v$process pr
WHERE st.sid=se.sid
AND st.sid=si.sid
AND se.PADDR=pr.ADDR
AND se.sid>6
AND st.wait_time=0
AND st.event NOT LIKE '%SQL%'
ORDER BY physical_reads DESC
对检索出的结果的几点说明:
1、我是按每个正在等待的session已经发生的物理读排的序,因为它与实际的IO相关。
2、你可以看一下这些等待的进程都在忙什么,语句是否合理?
Select sql_address from v$session where sid= ;
Select * from v$sqltext where address= ;
执行以上两个语句便可以得到这个session的语句。
你也以用alter system kill session 'sid,serial#';把这个session杀掉。
3、应观注一下event这列,这是我们调优的关键一列,下面对常出现的event做以简要的说明:
a、buffer busy waits,free buffer waits这两个参数所标识是dbwr是否够用的问题,与IO很大相关的,当v$session_wait中的free buffer wait的条目很小或没有的时侯,说明你的系统的dbwr进程决对够用,不用调整;free buffer wait的条目很多,你的系统感觉起来一定很慢,这时说明你的dbwr已经不够用了,它产生的wio已经成为你的数据库性能的瓶颈,这时的解决办法如下:
a.1增加写进程,同时要调整db_block_lru_latches参数
示例:修改或添加如下两个参数
db_writer_processes=4
db_block_lru_latches=8
a、2开异步IO,IBM这方面简单得多,hp则麻烦一些,可以与Hp工程师联系。
b、db file sequential read,指的是顺序读,即全表扫描,这也是我们应该尽量减少的部分,解决方法就是使用索引、sql调优,同时可以增大db_file_multiblock_read_count这个参数。
c、db file scattered read,这个参数指的是通过索引来读取,同样可以通过增加db_file_multiblock_read_count这个参数来提高性能。
d、latch free,与栓相关的了,需要专门调节。
e、其他参数可以不特别观注。
结篇:匆忙之中写下了这篇文章,希望能抛砖引玉,能为你的Oracle调优实践带来帮助。
任何事情都有它的源头,要解决问题,也得从源头开始,影响ORACLE性能的源头非常多,主要包括如下方面:
数据库的硬件配置:CPU、内存、网络条件
1. CPU:在任何机器中CPU的数据处理能力往往是衡量计算机性能的一个标志,并且ORACLE是一个提供并行能力的数据库系统,在CPU方面的要求就更高了,如果运行队列数目超过了CPU处理的数目,性能就会下降,我们要解决的问题就是要适当增加CPU的数量了,当然我们还可以将需要许多资源的进程KILL掉;
2. 内存:衡量机器性能的另外一个指标就是内存的多少了,在ORACLE中内存和我们在建数据库中的交换区进行数据的交换,读数据时,磁盘I/O必须等待物理I/O操作完成,在出现ORACLE的内存瓶颈时,我们第一个要考虑的是增加内存,由于I/O的响应时间是影响ORACLE性能的主要参数,我将在这方面进行详细的讲解
3. 网络条件:NET*SQL负责数据在网络上的来往,大量的SQL会令网络速度变慢。比如10M的网卡和100的网卡就对NET*SQL有非常明显的影响,还有交换机、集线器等等网络设备的性能对网络的影响很明显,建议在任何网络中不要试图用3个集线器来将网段互联。
Ø OS参数的设置
下表给出了OS的参数设置及说明,DBA可以根据实际需要对这些参数进行设置
内核参数名 |
说明 |
bufpages |
对buffer空间不按静态分配,采用动态分配,使bufpages值随nbuf一起对buffer空间进行动态分配。 |
create_fastlinks |
对HFS文件系统允许快速符号链接, |
dbc_max_pct |
加大最大动态buffer空间所占物理内存的百分比,以满足应用系统的读写命中率的需要。 |
dbc_min_pct |
设置最小动态buffer空间所占物理内存的百分比 |
desfree |
提高开始交换操作的最低空闲内存下限,保障系统的稳定性,防止出现不可预见的系统崩溃(Crash)。 |
fs_async |
允许进行磁盘异步操作,提高CPU和磁盘的利用率 |
lotsfree |
提高系统解除换页操作的空闲内存的上限值,保证应用程序有足够的可用内存空间。 |
maxdsiz |
针对系统数据量大的特点,加大最大数据段的大小,保证应用的需要。(32位) |
maxdsiz_64bit |
maximum process data segment size for 64_bit |
Maxssiz |
加大最大堆栈段的大小。(32_bit) |
maxssiz_64bit |
加大最大堆栈段的大小(64_bit) |
Maxtsiz |
提高最大代码段大小,满足应用要求 |
maxtsiz_64bit |
原值过大,应调小 |
Minfree |
提高停止交换操作的自由内存的上限 |
Shmem |
允许进行内存共享,以提高内存的利用率。 |
Shmmax |
设置最大共享内存段的大小,完全满足目前的需要。 |
Timeslice |
由于系统的瓶颈主要反映在磁盘I/O上,因此 降低时间片的大小,一方面可避免因磁盘I/O不畅造成CPU的等待,从而提高了CPU的综合利用率。另一方面减少了进程的阻塞量。 |
unlockable_mem |
提高了不可锁内存的大小,使可用于换页和交换的内存空间扩大,用以满足系统对内存管理的要求。 |
用户SQL质量
以上讲的都是硬件方面的东西,在条件有限的条件下,我们可以调整应用程序的SQL质量:
1. 不要进行全表扫描(Full Table Scan):全表扫描导致大量的I/O
2. 尽量建好和使用好索引:建索引也是有讲究的,在建索引时,也不是索引越多越好,当一个表的索引达到4个以上时,ORACLE的性能可能还是改善不了,因为OLTP系统每表超过5个索引即会降低性能,而且在一个sql 中, Oracle 从不能使用超过 5个索引;当我们用到GROUP BY和ORDER BY时,ORACLE就会自动对数据进行排序,而ORACLE在INIT.ORA中决定了sort_area_size区的大小,当排序不能在我们给定的排序区完成时,ORACLE就会在磁盘中进行排序,也就是我们讲的临时表空间中排序, 过多的磁盘排序将会令 free buffer waits 的值变高,而这个区间并不只是用于排序的,对于开发人员我提出如下忠告:
1)、select,update,delete 语句中的子查询应当有规律地查找少于20%的表行.如果一个语句查找的行数超过总行数的20%,它将不能通过使用索引获得性能上的提高.
2)、索引可能产生碎片,因为记录从表中删除时,相应也从表的索引中删除.表释放的空间可以再用,而索引释放的空间却不能再用.频繁进行删除操作的被索引的表,应当阶段性地重建索引,以避免在索引中造成空间碎片,影响性能.在许可的条件下,也可以阶段性地truncate表,truncate命令删除表中所有记录,也删除索引碎片.
3)、在使用索引时一定要按索引对应字段的顺序进行引用。
4)、用(+)比用NOT IN更有效率。
Ø 降低ORACLE的竞争:
先讲几个ORACLE的几个参数,这几个参数关系到ORACLE的竞争:
1)、freelists 和 freelist 组:他们负责ORACLE的处理表和索引的空间管理;
2)、pctfree 及 pctused:该参数决定了freelists 和 freelist 组的行为,pctfree 和pctused 参数的唯一目的就是为了控制块如何在 freelists 中进出
设置好pctfree 及 pctused对块在freelists的移走和读取很重要。
Ø 其他参数的设置
1)、包括SGA区(系统全局区):系统全局区(SGA)是一个分配给Oracle 的包含一个 Oracle 实例的数据库的控制信息内存段。
主要包括数据库高速缓存(the database buffer cache),
重演日志缓存(the redo log buffer),
共享池(the shared pool),
数据字典缓存(the data dictionary cache)以及其它各方面的信息
2)、db_block_buffers(数据高速缓冲区)访问过的数据都放在这一片内存区域,该参数越大,Oracle在内存中找到相同数据的可能性就越大,也即加快了查询速度。
3)、share_pool_size (SQL共享缓冲池):该参数是库高速缓存和数据字典的高速缓存。
4)、Log_buffer (重演日志缓冲区)
5)、sort_area_size(排序区)
6)、processes (同时连接的进程数)
7)、db_block_size (数据库块大小):Oracle默认块为2KB,太小了,因为如果我们有一个8KB的数据,则2KB块的数据库要读4次盘,才能读完,而8KB块的数据库只要1次就读完了,大大减少了I/O操作。数据库安装完成后,就不能再改变db_block_size的值了,只能重新建立数据库并且建库时,要选择手工安装数据库。
8)、open_links (同时打开的链接数)
9)、dml_locks
10)、open_cursors (打开光标数)
11)、dbwr_io_slaves (后台写进程数)
摘要: web.xml 配置
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"><web-app>...
阅读全文
Bruce Tate , 总裁, J2Life, LLC
轻量级容器可以动态地使系统主要组件之间的耦合变松散。不同的容器包含相同的设计模式,但却具有根本不同的哲学。本文帮助您在下列三种轻量级容器之间作出最佳选择:Spring Framework、HiveMind 和 PicoContainer。
2002 年在科罗拉多的一次旅行中,我完美地感受了阿肯色河。在三段不同的漂流中,这条河展示了令人惊异的多样性。柔美的布朗峡谷有着开阔的急流,翻滚着巨大的波浪。Royal Gorge 别具特色的悬崖峭壁引导着巨大而笔直的峡谷之下的水力,在这条直线上发生一点小闪失都会受到长途游泳的惩罚。Numbers 具有精密的落差,需要人们在范围狭窄的圆石花园里精确操纵。在一条河里,我有了三次极不相同的体验。
在我的上一篇文章“轻量级开发的成功秘诀,第 3 部分:Spring 露出水面”中,我们学习了轻量级容器的基本原理。本文将向您展示三种最流行的容器:
这三种容器都源于依赖注入,但每种容器都具有极不相同的特征。当我介绍每种容器的高级描述时,您将看到正在运行的每种框架,以及可以应用每种框架的环境。
核心哲学
这三种容器都接受 POJO (plain old Java object),都具有对象生命周期的钩子(所以它们可以在创建或销毁 bean 时调用您的代码),都执行依赖注入。您可能认为这些主旋律将导致相似的容器,但事实并非如此。尽管植入每种容器的代码可能相似,但容器本身反映了不同的能力、风格和整体哲学。总而言之,每种容器的作者都忠于他们的哲学。
Spring Framework
作为开放源码框架的 Geneva,Spring Framework 为数百个 Java 2 Platform, Enterprise Edition (J2EE) API 和开放源码框架提供了轻量级容器和胶水代码 (glue code)。Spring 有一个最重要的前景:让 J2EE 更易使用。读完一些示例和书籍之后,您将看到一些常见的主题:
- Spring 支持三种依赖注入——setter、构造函数 和 方法 注入——但总的来说,最流行的模型是 setter 注入。
- 在灵活性和简单性之间,Spring 的 XML 风格配置更重视灵活性。您可以做任何事情,但对于初学者来说,配置文件是晦涩难懂的。
- Spring 的创始人认为,容器只是整体框架的一小部分。Spring 的大部分价值来源于支持该框架的数千行胶水代码。它易于插入任何系统中。
- Spring 框架是三种容器实现中最完美的。一般来说,优秀的文档都是完美编写的。
- Spring 具有自动连线 (autowire) 方式,但大多数示例都没有使用它。我并不十分了解这个决策,但有时候,能够看到明确列出的依赖关系是不错的。
- Spring 提供了完整的 AOP 框架,使得更容易附加服务。您可以使用 Spring 自己的框架或依赖丰富的 AspectJ 集成(参阅 参考资料)。
如果要用一个短语来形容 Spring,我会说让企业更强。
HiveMind
Howard Lewis Ship 是 Jakarta Tapestry Web 框架的创建者,他还创建了 HiveMind。作为一个容器,HiveMind 是灵巧、干净且易于使用的。与其他许多较好的开放源码框架一样,Ship 创建 HiveMind 是为了让它帮助解决现实问题。但是,HiveMind 向传统的轻量级容器添加了两个创新:
- 最重要的 HiveMind 创新是模块。据 Ship 所说,Eclipse 插件激发了他的 HiveMind 模块的灵感。
- HiveMind 强制您编写接口。(与所有轻量级容器一样,它不提供接口,而由您自己提供接口。)
- HiveMind 是用户友好的,它提供称为 HiveDoc 的文档工具,友好简明的 XML 配置,以及行准确的错误报告。
- HiveMind 用户通常优先选择 setter 注入,但该容器还支持构造函数注入。
如果用一个短语来形容 HiveMind 的话,我会说它是概念正确 的。
PicoContainer
到目前为止,PicoContainer 最重要的特征是它的尺寸。它没有提供许多附加物,但它具有完整的依赖注入容器。PicoContainer 还具有一些惟一特性:
- PicoContainer 很小,所以它没有拦截器、AOP 或相似类型的服务,而选择了让其他框架创建这些服务。
- PicoContainer 支持 Java 配置技术,而不支持 XML 配置技术,这与其他容器一样。
- PicoContainer 流行的使用模型是构造函数注入,但它也支持 setter 注入。
- PicoContainer 没有提供许多文档,而且一些现有文档是不完整的,但您不会太需要。
- PicoContainer 具有一个自动连线方式,它很不错。
- PicoContainer 的发展似乎有点停滞。
如果用一个短语来形容 PicoContainer 的话,我会选择理论完美,但不如 Spring 或 HiveMind 实用。
编程模型
现在我将向您展示社区中流行的编程示例,以帮助您更好地理解容器的作者希望您如何使用它们。我使用 PicoContainer 中的 Kiss 示例来展示 autowiring 和 Java 技术风格的配置,使用 HiveMind 加法器示例来展示模块能力,使用 Spring PetClinic 应用程序来展示 Hibernate 集成。
Kiss 示例 (PicoContainer)
在这三个容器中,PicoContainer 具有最简单的编程模型。要查看 Kiss 示例,可从 PicoContainer.org 下载它。安装该示例,浏览到 docs\Two+minute+tutorial.htm,然后您会看到两个组件:
清单 1. 两个 Kiss 组件
public class Boy {
public void kiss(Object kisser) {
System.out.println("I was kissed by " + kisser);
}
}
public class Girl {
Boy boy;
public Girl(Boy boy) {
this.boy = boy;
}
public void kissSomeone() {
boy.kiss(this);
}
}
|
这两个类是自解释的。Girl 对 Boy 有依赖关系。该依赖关系将通过构造函数被注入。先实例化一个容器:
MutablePicoContainer pico = new DefaultPicoContainer();
|
然后注册两个组件:
pico.registerComponentImplementation(Boy.class);
pico.registerComponentImplementation(Girl.class);
|
稍后您可以向 PicoContainer 请求一个对象,然后操作它:
Girl girl = (Girl) pico.getComponentInstance(Girl.class);
girl.kissSomeone();
|
这样就差不多了。编程模型是优雅的,基于构造函数的风格意味着您无需包括无参构造函数。对本例中的 Girl 调用这种函数将会使该对象处于不一致的状态,因为 kiss
方法将抛出异常。
加法器示例 (HiveMind)
现在,让我们看一下 HiveMind 的编程示例。从 Apache Jakarta Project 下载 HiveMind,然后查看加法器示例。您会看到接口和实现。(记住:HiveMind 强制编写接口。)
清单 2. 加法器示例接口和实现
public interface Adder
{
public double add(double arg0, double arg1);
}
public class AdderImpl implements Adder
{
public double add(double arg0, double arg1)
{
return arg0 + arg1;
}
}
|
将该服务暴露在 XML 文件中,如下所示:
清单 3. 将该服务暴露在 XML 文件中
然后,其他应用程序就可以使用该服务了,如下所示:
清单 4. 其他应用程序可以使用该服务
Registry registry = RegistryBuilder.constructDefaultRegistry();
Adder adder = (Adder) registry.getService("examples.Adder",
Adder.class);
... adder.add(arg0, arg1)
|
注意,HiveMind 的模块让您可以将多个服务组合到一起。如果您需要向容器中的服务添加功能,可以使用拦截器:
清单 5. 使用拦截器添加功能
PetClinic 应用程序 (Spring)
Spring 处理事情的方法有些不同。因为 Spring 框架不带有简单的应用程序,我从我的书籍 Spring: A Developer's Notebook 中选择了一个。您可以从 O'Reilly Media 获取该示例代码。解压示例 4,它展示了一个用于 RentaBike 商店的带有属性的 CommandLineView
对象,该对象最终成为该应用程序的数据访问对象。
清单 6. CommandLineView 对象
public class CommandLineView {
private RentABike rentaBike;
public CommandLineView() {}
public void setRentABike(RentABike rentaBike) {this.rentaBike = rentaBike;}
public RentABike getRentaBike() { return this.rentaBike; }
...
}
|
RentaBike 是具有您希望在自行车商店对象中看到的各种方法的接口:
清单 7. 接口方法
public interface RentABike {
List getBikes();
Bike getBike(String serialNo);
void setStoreName(String name);
String getStoreName();
}
|
没有显示 ArrayListBikeStore
,它是 BikeStore 接口的存根实现。注意,Spring 允许编写接口,但不强制编写接口。下面是描述该应用程序中 bean 的 XML 配置文件:
清单 8. 描述应用程序 bean 的 XML 配置文件
该上下文中有两个 bean。commandLineView
bean 依赖于 rentaBike
bean。该应用程序通过为 rentaBike
属性指定 rentaBike
名称,显式解析该依赖关系。注意,PicoContainer 自动连接这种显式关系,Spring 也可以,但大多数用户不使用它的自动连线选项。Spring 还允许您通过拦截器或 AOP 向外观的任何方法添加服务。
比较
既然已经看到每种容器的哲学,下面是对每种环境的无形特性的详细比较,比如市场份额、整体质量(fit and finish)和整体特性列表。毕竟,即使编程模型是完美的,但如果没有文档,或者由于缺乏社区而您必须自己支持它,那么它也不会成为一个好容器。
活动社区
Spring 有一个充满活力的社区,和一个支持该框架的称为 Interface21 的职业服务公司。这很重要,因为您知道您可以获得良好的支持,公司才有动力来支持 Spring 框架。我在社区的经历简直太美好了。Spring 贡献者、创始人和用户都以杰出的内容填满了留言板。
HiveMind 框架是一个 Apache Jakarta 项目,所以有着扎实的基础。它有一个正在成长的萌芽社区。该框架的创始人 Howard Lewis Ship 是独立顾问、优秀导师和不屈不挠的提倡者。但是,要利用 HiveMind 的质量帮助或者查找其 Web 站点之外的内容仍然十分困难。尽管如此,它的在线帮助似乎不错,而且社区似乎正在成长。Hibernate 获得了有趣的胜利,它被选中——或者更应该说,Ship 被选中——组成 TheServerSide.com 的新基础设施,TheServerSide.com 是最重要的 Java 技术社区之一。
PicoContainer 也是一个 Apache Jakarta 项目,它似乎发展缓慢。截止本文撰稿,PicoContainer 的最后一次主要代码发行是在 2004 年 11 月。您看不到太多有关 PicoContainer 的新文章,这有点惭愧,因为我喜欢 PicoContainer 的一些哲学。事实上,我不太确定有没有三种开放源码轻量级容器的空间,尤其是最近第四种轻量级容器项目 Avalon 关闭之后。
就每个社区生成的活动而言,Spring 无疑是优胜者。Interface21 的支持、奇思妙想的论坛、活跃的邮件列表以及社区的跟踪记录都是无与伦比的。
整体质量
社区的大小和实力通常驱动开放源码项目的整体质量。充满活力的社区需要更好的文档和示例,而且它们会参与完成结尾的详细信息。
Spring 团队编写了可与我见过的一些比较好的商业产品相媲美的文档。如果这还不够的话,您还可以找到至少五本主要 Spring 书籍和其他许多包含 Spring 内容的出版物。(我自己曾撰写过两本有关 Spring 的书籍,其中一本书中包括 Jolt-winning Better, Faster, Lighter Java 一章,另一本是快速入门书籍 Spring: A Developer's Notebook)。错误消息是专业性和描述性的。与第三方框架和 API 的集成是所有 Java 技术框架中最好的。包装是经过深思熟虑的,不过略有多余。(它帮助我开始把一些比较小的项目划分成模块。)示例是优秀且有指导意义的。
与 Tapestry 一样,HiveMind 也具有好的整体质量。Ship 自己以那些让 HiveMind 变得简单易用的特性而自豪,比如行准确的错误报告;友好简明的 XML 语法;良好的文档工具 HiveDoc。与用于低级详细信息的 JavaDoc 文档结合使用,您可以更好地描述您的应用程序(HiveMind 模块)的高级特性,从而完善它们之间的依赖关系。
PicoContainer 编程模型感觉自然,但文档不完整(许多方法标记看起来过时好几个月了),而且没有许多使用该容器的真实世界示例。有时候,我会觉得自己在独自穿过鬼魂出没的破屋。
但使用 PicoContainer 确实有一个主要优点。因为您配置现实世界的对象时,会得到一些编译时错误检查。实际上,该容器太小太轻了,以至于除了基本配置之外,没有什么能出错。PicoContainer 做了一项合理的工作。
特性
我不想过多地讨论特性。如果您正在寻找许多胶水代码来减少您的开放源码收藏夹的集成或某特定 J2EE API,Spring 无疑是最佳选择。HiveMind 不尝试参与竞争。相反,它与 Spring 的服务兼容。PicoContainer 不构建而且也不尝试构建附加物,而是选择让开放源码项目为其提供服务。到目前为止,它的效果不太好。
哪一个最好?
目前,只有一个真正的答案。HiveMind 具有有趣的创新,PicoContainer 具有易于使用的模型(理论上),但社区似乎已经投票选择了 Spring Framework。随着时间的推移,新的容器可能会成长,HiveMind 可能不断获得市场份额,但目前,Spring 是您的最佳选择。
如果您愿意冒一些险,而使用不太成熟或不太流行的容器,您可能决定实现 HiveMind(如果需要模块级别的配置)或 PicoContainer(如果想要微小的容器)。如果需要许多胶水代码来集成持久引擎、事务处理策略和安全性等方面,Spring 具有最完整的组件堆。但请记住:您可以在 HiveMind 容器中使用 Spring 组件。
参考资料
学习
原文:
http://www-128.ibm.com/developerworks/cn/opensource/os-lightweight4/
在JSP中我们经常要调用服务器端的一些dos命令,已达到一些特殊的效果,但同时调用服务器端的dos命令也存在着一些安全隐患,因此需要慎重使用。
以下以一个例子来说明用jsp执行dos的过程,比如在服务器端每天都会自动生成一个目录(目录名称为当天的日期),再此目录下会生成一些当天的新闻文件,管理员会把这些文件几个月作一次备份,备份完后在把这些文件删除。
如果在服务器上,我们可以在dos下直接执行c:\j2sdk\jar cf d:\bak\200502.jar d:\news\20050101 命令, 然后再把20050101目录删除即可。
在JSP中我们应当这么来写
<%
//执行dos命令
String commandstr = "c:/j2sdk/jar cf d:/bak/200502.jar d:/news/20050101";
Process p ;
try {
p = Runtime.getRuntime().exec(commandstr);
//等待刚刚执行的命令的结束
while (true){
if(p.waitFor() == 0) break;
}
} catch (Exception e) {
out.println(e.toString());
}
//删除已经打包的文件及其目录
File f = new File("d:/news/20020101");
String[] allFiles = f.list();
for (int i = 0; i < allFiles.length; i++) {
File delF = new File("d:/news/20050101/"+allFiles[i]);
delF.delete();
}
File delD = new File("d:/news/20050101");
delD.delete();
%>
在jsp中以下代码必须执行,如果没有该代码,则由于删除文件的速度快于打包的速度,因此当压缩包还没有打包完成,一些文件已经被删除,加入下面的代码,会在此一直做循环,一直p.waitFor()(这个方法的说明是:等待子进程的结束,如果已经结束,一般返回0)返回0为止才会接着执行后面的代码
while (true){
if(p.waitFor() == 0) break;
}
先还是看一段程序后,再一一讲解其内容吧
public class ReadMail
{
//初始化主机
String host=”smtp.163.com”;
String nuserName=”邮箱的用户名”;
String pwd=”用户密码”;
Session session;
Public void readMail()
{
Properties props=new Properties();
props.put(“mail.transport.protocol”,”smtp”);
props.put(“mail.smtp.host”,””+host+””);
props.put(“mail.smtp.port”,”25”);
session=Session.getDefaultInstance(props);
//获取Store对象,使用pop3协议也可以使用IMAP
Stroe store=session.getStore(“pop3”);
//连接到邮件服务器
store.connect(host,username,password);
//获取该用Floder对,并以只读方式打开
Folder folder=store.getFolder(“INBOX”);
Folder.open(Folder.READ_ONLY);
//检索所有邮件,按需填充
Message msg[]=folder.getMessage();
//遍历每一个邮件
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
For(int i=0,n=msg.length;I<n;I++)
{
//打印每个邮件的发件人和主题
System.out.println(I+”:”+msg[i].getFrom()[0]+”\t”+msg[i].getSubject());
//显示消息内容
msg[i].writeTo(System.out);
}
floder.close(false);
store.close();
}
}
现在对程序一一讲解吧!
Store是一个抽象类,它模拟了消息存储器(或称为消息数据库)及其内部目录(Folder)访问协议,以存储和读取消息,由其子类提供具体实现。客户程序可以通过获取一个Store对象来访问消息存储器,以绝大多数的存储器要求用户在访问前提供认证信息,connect方法执行了该认证过程。与Transport类似,也可以指定Store使用协议。邮件是读取邮件所以用pop3协议来读取。
具体如:Store store=new Store(“pop3”);
然后认证:store.connect(host,username,pwd)
javax.mail.Folder类,它是一个抽象类,用于分级组织邮件,其子类提供针对具本协议的实现。存储在目录内的消息被顺序计数(从1开始到消息总数),该顺序被称为邮箱顺序,邮件顺序的改变消息的序列号,这种情况仅发生在客户程序调用Expunge方法擦除目录内设置了Flags.Flag.DELETED标志位的消息时。执行擦除操作后,目录内消息将重新编号。客户程序可以通过消息序列号和直接通过相应的Message对象引用目录中的对象,由于消息序列号会在会话中很可能会改变,因此应尽可能保存Message对象来引用对象。
连接stroe之后,接一来就可以获取一个文件夹(Folder)。该文件夹必须先使用open()方法打开,然后才能读取里面的消息。
Folder folder=store.getDefaultFolder();
//或Folder folder=store.getFolder(“inbox”);
folder.open(Folder.READ_WRITE);
Message message[]=folder.getMessages();
Open()方法指定了要打开的文件及打开方试(如Folder.READ_WRITE)。Inbox是pop3惟一可以使用的文件夹。如果使用IMAP,还可以使用其它的文件夹。读取了Message之后,就可以用getContent()获得内容,或者用writeTo()将内容写入输出流。GetContent()方法只能得到消息内容,而writeTo()输出却包含消息头.
如下两种方试
System.out.println(((MimeMessage)msg).getContent());
Msg.writeTo(System.out);
读完邮件后要关闭与Folder和Store的连接
folder.close(false);
stroe.close();
环境配置。为了运行javamail。我们需要在classpath里导入两个包:mail.jar和activation.jar,这是在纯DOS里或者其它简单工具使用时才需要这个classpath。如果使用像eclipes这样的工具就不需要配置。因为myeclipes已经帮你做好了。
为了要发送邮件和接收邮件,我们必须要遵守smtp和pop3协议,不过还有其它协议也可执行(如IMAP)。如果把本机当作服务器来发送邮件请在
http://jakrata.apache.org网站里下载james邮件服务器,然后解压在本机上。运行james/bin里的run.bat就可以启动了服务器。
运行后在DOS里登录邮件服务器。具体命令如下:
系统管理员:root
密码:root
telnet localhost 4555
然后就可以增加邮箱用户名了。命令如下:
adduser 用户名 密码
例:adduser test test
这样就建立了这样一个邮箱test@localhost 密码为:test
如果想要更多功能用help命令
注意:如果在本机装了邮件服务器的话,只能在本机局域网里申请邮箱,发送和接收。不能发送到外部的邮箱里去。如果用外部的邮件服务器像比如163和126的服务器等就可以随心所欲的发邮件了。哈哈是不是很爽呢?
先看看用james邮件服务器在本机上发邮件吧。先简单看一个例子。
import javax.mail.*;
import java.util.*;
import javax.mail.internet.*;
public class MyFirstMail
{
protected Session mailSession;
public MyFirstMail()throws Exception
{
init();
}
public static void main(String[] args)
{
try
{
new MyFirstMail().sendMail();
System.out.print("邮件已发");
}
catch(Exception e)
{
e.printStackTrace();
}
}
//初始化服务器环境
public void init()throws Exception
{
Properties props=new Properties();
props.put("mail.transport.protocol","smtp");
props.put("mail.smtp.host","localhost");
props.put("mail.smtp.port","25");
mailSession=Session.getDefaultInstance(props, null);;
}
public void sendMail()throws Exception
{
try
{
//从哪里发的邮件
msg.setFrom(new InternetAddress("test@localhost"));
//发送到目标邮件 msg.setRecipients(Message.RecipientType.TO,InternetAddress.parse("wang@localhost"));
//抄送的接收者 msg.setRecipients(Message.RecipientType.CC,InternetAddress.parse("wang@localhost"));
//暗送的接收者
msg.setRecipients(Message.RecipientType.BCC,InternetAddress.parse("wang@localhost"));
//设置发送时间
msg.setSentDate(new java.util.Date());
//设置邮件标题
msg.setSubject("a test mail");
//设置邮件内容
msg.setText("this is the email content");
//指定协议发送消息的对像
Transport transport=mailSession.getTransport("smtp");
//发送消息
Transport.send(msg);
}
catch(Exception e)
{
throw e;
}
}
发送完后想要看邮件就要到james->apps->james->var->mail->inboxes就可以看见每个邮箱里的邮件数了。由于发过来的是字节流,邮件包括一个邮件属性和消息,所以每一封邮件是由二个文件组成的。
也许没有学过的javamail的人看上去会一头雾水。不要紧,现在一一详解。先说初始化的内容的吧!
1、 mail.transport.protocol=smtp 这里主要说明的是邮件传输协议。
2、 mail.smtp.host=localhost 发送邮件的主机如果用外部邮件服务器的话,可以这样写
mail.smtp.host=smtp.163.com这样就以163.为服务器了。
3、mail.smtp.prot=25 smtp端口可以省略,缺省为25
javax.mail.session
session类定义了与远程邮件系统通信的邮件会话。需要注意的是这个session不同servlet中的会话。Servlet中的会话需要共享一些信息,而mail里的session里没有这个功能,它只是用于存储与服务器建立连接会话相关信息和逻辑。Session类是javamail api最高层入口类,所有其它类都必须经由session对象生效。Session对象它管理配置选项和用于与邮件系统交互的用认证信息息。它通过使用java.util.properties对象配置邮件会话的属性如邮件服务器,用户名,密码,及整个应用程序中共享的其它信息。
Session并不处现任何授权操作,它只是存储这些授权信息。Session类的构造器是私有的,它不能被继承,也不能用new语名创建实例。但它提供了两个静态方法getInstance 和getDefaultInstance来获取session实例,在创建session实例时需要提供一些属性。具体实现如下:
Properties props=new Properties()
Props.put(“mail.transport.protocol”,”smtp”)
Props.put(“mail.smtp.host”,”localhost”);
Props.put(“mail.smtp.prot”,”25”)
//不加以认证也可以这样写
//Session session=Session.getDefaultInstance(props,null)
//如果认证传入null那么它和不使用认证调用方式一样
Session session=Session.getDefaultInstance(props)
Javax.mail. Message
经过session配置后就可以进行发送消息任务了。这由Message类来完成。Message实现了Part接口,它表示一个邮件消息,包含一系列的属性和一个消息内容。消息属性包括了消息址址消息,定义了消息内容的结构(包括内容类型),消息内容使用DataHandler对象包装实际数据。当邮件消息位于目录(folder)中时,系统还使用了一个标志位集合来描述它的状态。
Message是抽象类,实际使用时必须用一个子类代替以表示具体的邮件格式。比如说javamail api提供了MimeMessage类,该类扩展了Message,实现了RFC822和MIME标准。有两个构造方法:
//一般使用第一种
Message msg=new MimeMessage(Session session)
Message msg=new MimeMessage(MimeMessage msg)
获得消息后,就可以设置消息各个部份了。在设置之前因为要涉及地址。所以讲一下地址类
javax.mail.Address
Address类表示电子邮件类,它是一个抽象类,它的子类InternetAddress提供具体实现且通常可串行化。若创建的地址只包含电子邮件,只要传递电子邮件到构造器即可如
InternetAddress addr=new InternetAddress();
addr.setAddress(“wang@126.com”)
或者是:InterntAddress addr=new InternetAddress(“wang@126.com”)
另外,InternetAddress类还提供了地址解析方法如
Address[] addrs=InternetAddress.parse(“wang@163.com,f@126.com,zuo@126.com”);
地址之后就可以发送消息了。
//设置发送者
Msg.setFrom(new InternetAddress(“test@126.com”));
//设置接收者,并还具有解析功能
Msg.setRecipients(Message.RecipientType.To,InternetAddress.parse(“zuolin0806@163.com”));
//抄送的接收者
Msg.setRecipients(Message.RecipientType.CC,InternetAddress.parse(“zuolin0806@163.com”));
//暗送的接收者
Msg.setRecipients(Message.RecipientType.CC,InternetAddress.parse(“zuolin0806@163.com”));
//设置消息主题
msg.setSubject(“我的爱”)
//设置内容的基本机制,其参数Mime类型
msg.setContent(“这是我的内容”,”text/html;charset=gb2312”)
其中的text/html表示消息内容采用的是HTML格式。如果消息格式是(text/plain),而且使用的是MimeMessage,那么setText()方法设置邮件内容的参数(Mime类型默认为text/plain)
//设置邮件内容
msg.setText();
//设置发送时间
msg.setSentDate(new java.util.Date());
最后一步是就是发送了
javax.mail.Transport
该类也是抽象类,它可通过静态方法和实便方法发送消息。Transport断承service类。所以它提供了通用方法,如命名传输,连接服务器和监听传输事件等。
//默认
Transport.send(msg);
最后看一下使用外部邮件服务器的使用方法。先看程序再讲解,注意在使用外部邮件服务器时一定要有一个类来认证。到时在讲。
public class MySecondMail
{
protected Session session = null;
//邮件用户名
String mailUser = "zuolin0806";
String host = "smtp.163.com";
String pwd = "用户名的密码";
public MySecondMail()
{
init();
}
public void init()
{
Properties props = new Properties();
props.put("mail.transpost.protocol", "smtp");
props.put("mail.smtp.host", "smtp.163.com");
//必须要有一个类来认证
props.put("mail.smtp.auth", "true");
props.put("mail.smpt.port", "25")
Email_Autherticatorbean auth = new Email_Autherticatorbean(mailUser,pwd);
//session认证
session = Session.getInstance(props,auth);
//这个是跟踪后台消息。打印在控制台
session.setDebug(true);
}
public static void main(String[] args)
{
new MySecondMail().sendMails();
System.out.println("send mail success!");
}
public void sendMails()
{
try
{
Message msg = new MimeMessage(session);
msg.setFrom(new InternetAddress("zuolin0806@163.com"));
msg.setRecipients(Message.RecipientType.TO, InternetAddress.parse("javaboy@126.com"));
msg.setRecipients(Message.RecipientType.BCC, InternetAddress.parse("javaboy@126.com"));
msg.setSentDate(new Date());
msg.setSubject("this is my Threee mail");
msg.setContent("this is my mail","text/html");
msg.setText("我成功了。哈哈");
Transport transport = session.getTransport("smtp");
与发送者的邮箱相连
transport.connect(host,mailUser,pwd);
transport.send(msg);
}
catch (SendFailedException e)
{
e.printStackTrace();
}
catch (Exception ee)
{
ee.printStackTrace();
}
}
}
认证类必须继承Authenticator 例子如下:
import javax.mail.*;
public class Email_Autherticatorbean extends Authenticator
{
private String m_username = null;
private String m_userpass = null;
public void setUsername(String username)
{
m_username = username;
}
public void setUserpass(String userpass)
{
m_userpass = userpass;
}
public Email_Autherticatorbean(String username, String userpass)
{
super();
setUsername(username);
setUserpass(userpass);
}
public PasswordAuthentication getPasswordAuthentication()
{
return new PasswordAuthentication(m_username,m_userpass);
}
}
经过成功发送邮件后是不是有点成就感呢!但还有很多疑问比如怎么读邮件呢?看了下面章节你就会知道了
JSF开发实战(一)
JSF将是J2EE5.0中所包含的web开发框架,这应该是第一个成为jcp标准,并且随j2eesdk一起发布的web框架,可以看出sun对它的期望很高。JSF最大的竞争对手是tapestry,是apache的产品,但是apache又弄出了个myfaces,是对jsf标准的一个实现。也许你也和我一样,在jsf和tapestry之间犹豫很久,将来从apache的态度上应该可以看出二者的走向。在tss上有一篇比较jsf 1.0与tapestry 3.0的文章,内容很扎实到位:
http://www.theserverside.com/articles/article.tss?l=JSFTapestryJSF的竞争对手不是struts/webwork之流,它们基本上已经是不同阶段上的东西了,放在一起比较意义不大。
JSF的开发流程和asp.net中所倡导的code behind方式很相似,核心是事件驱动,组件和标签的封装程度非常高,很多典型应用已经不需要开发者去处理http。页面操作会被自动映射到对应的java bean中,后台逻辑只需要同java bean发生交互。整个过程是通过“依赖注入(DI)”来实现的,看来这是目前解偶合的最佳途径啊,spring的影响真是深远。不过正式因为jsf采用了这样的方式,导致开发工作和以前的jsp/struts等都有非常大的不同,需要一定的时间去学习。学习之前建议先对依赖注入有比较清楚的认识,可以参考我的learn Spring in spring系列的第一篇。
本系列将以两个例子来讲解jsf的基本开发,第一个例子当然是hello world。目前可用的jsf ide不多,ibm要到06年才能放出支持jsf的wtp版本。所以我们的例子基本以手写为主,这样也能让我们有更清楚的认识,同时推荐目前最好的jsf开发工具:myeclipse 4.0 GA。后面的例子将会有jsf和hibernate的内容,它都能给予很好的支持。由于myeclipse并不免费,所以我们除了讲解在ide中如何操作外,还会叙述手动操作的具体内容,以免过于依赖开发工具。用什么服务器都可以,这里采用了jboss 4.0.2。如果你的服务器是高版本的tomcat(5.5+),那么必须要删除它自带的一些包才能很好的支持jsf,具体细节请查看它的文档。
请自行下载jsf ri和JSTL 1.1。
废话少说,开始了。
在myeclipse 4.0GA中新建一个web项目,命名为hello,为项目增加对JSTL的支持:
在JSTL的版本中选择1.1。
该操作实际上是把jstl.jar和standard.jar加到工程中。
采用类似的操作为项目添加对jsf的支持:myeclipse?add jsf capabilities
如图:

其中的jsf implementation是选择使用哪中JSF实现,我们采用的是sun的jsf ri
JSF config path是配置文件的位置,保持不变
URL pattern是jsf servlet的映射方式,有两种选择,具体细节后面说明。
以上操作上是为项目加入了jsf需要的jar和tld文件,并且创建了一个faces-config.xml的配置文件。涉及到的jar有:commons-beanutils.jar commons-collections.jar commons-digester.jar commons-logging.jar jsf-api.jar jsf-impl.jar
涉及到了jsf中所有的tld文件。
当前的faces-config.xml文件的内容是:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd">
<faces-config>
</faces-config>
环境已经建立好了,现在我们要建立一个程序,它的功能是让用户在表单中输入名字,提交后系统会返回一个问候。使用jsf的以后好处是,开发人员会很自然的把mvc各层分开,不会像使用strtus那样别扭,这一点在后面的开发中感觉得到。
首先开发model层,它是个很简单的bean:
package org.bromon.jsf.model.hello;
public class SayHello {
public String say(String name)
{
return "你好,"+name;
}
}
在model层中你可以随意的实现业务的数据逻辑,不需要与web层有任何的关系。
下面开发控制层,它负责存取web层的数据,并且调用model层的逻辑:
/**
* jsf的控制层方法
* @author bromon
*/
package org.bromon.jsf.control.hello;
import org.bromon.jsf.model.hello.*;
public class HelloDelegater {
//------属性---------
private String name;//表单中的文本框数据会传到这里
private String result;//web页会从这里取得运行结果
private SayHello sayHello;//model层的对象,并不事例化,由系统注入
//-----set/get--------
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SayHello getSayHello() {
return sayHello;
}
public void setSayHello(SayHello sayHello) {
this.sayHello = sayHello;
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
//-----逻辑方法---------
public String say()
{
this.setResult(sayHello.say(this.getName()));
return "ok";
}
}
需要注意的是,属性的名字、set/get方法的名字必须严格按照java bean规范编写,因为它们要被注入依赖时使用。sayHello对象并没有被实例化,它会在运行时由系统注入。
这两个bean当然要在系统中申明,否则无法实现DI。在faces-config.xml文件中添加内容:
<managed-bean>
<managed-bean-name>SayHello</managed-bean-name>
<managed-bean-class>
org.bromon.jsf.model.hello.SayHello
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>HelloDelegater</managed-bean-name>
<managed-bean-class>
org.bromon.jsf.control.hello.HelloDelegater
</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>sayHello</property-name>
<value>#{SayHello}</value>
</managed-property>
</managed-bean>
在后一个bean中,它的sayHello属性被指定要在运行时注入一个org.bromon.jsf.model.hello.SayHello的实例。
下面要编写表示层的页面,只有一个index.jsp:
需要引入两个标签库:
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
下面是构造jsf标签:
<body>
<f:view>
<h:form>
<h:panelGrid columns="3">
<h:outputLabel for="name" value="姓名:"/>
<h:inputText id="name" value="#{HelloDelegater.name}" required="true"/>
<h:message for="name"/>
<h:outputLabel value="#{HelloDelegater.result}"/>
</h:panelGrid>
<h:panelGroup>
<h:commandButton action="#{HelloDelegater.say}" value="提交"/>
</h:panelGroup>
</h:form>
</f:view>
</body>
页面中包含了一个文本框和一个label,他们分别被绑定到了HelloDelegater类的两个属性上,具体的绑定工作有系统通过翻转控制的方式调用对应的set/get方式实现。提交按钮被绑定到了HelloDelegater.say方法,该方法会把计算结果赋给result属性,它会在页面中显示出来。
因为我们在url pattern中选择了*.faces,所以我们应该访问如下地址来查看程序:
http://localhost:8080/hello/index.faces
相应的,如果你选择了/faces/*,那么就应该是:
http://localhost:8080/hello/faces/index.jsp
程序执行结果如下:
bromon原创
//组件,容器,引擎,框架,平台
设计,无外乎抽象
先看一段接口的使用代码
public class container{
public void excute(Comp c){
c.metod1();
c. metod2 ();
}
}
interface Comp{
public void method1 ();
public void method2 ();
}
class CompImpt{
public void method1 (){
System.out.println(“method1”);
}
public void method2 (){
System.out.println(“method2”);
}
}
这个就是接口的使用,当你把调用接口的类当作是容器,当作是引擎时,当作是框架时,CompImpt就是组件,你就成为了设计师,把调用接口的类当作是平台时,CompImpt就是插件,你就成了架构师
开个玩笑,呵呵!!
该拆的拆,该合的合
架构设计师的任务是整合拆分
关于“分”架构设计师的目标是:
把代码分开(便于维护)
把计算机的任务分开(便于计算机运行)
把人的任务分开 (给人分任务,便于管理)
分分合合,古人阴阳如此,今人正反如此
万物如此,人生亦是如此
昨天,Eclipse开通了3.1.1的Update Site,并且给出了一个详细的指导:如何用Eclipse Update Manager从3.1升级为3.1.1。 这避免了重新下载整个3.1.1然后再安装。
Update Site提到:
Welcome to the Eclipse Update Site. This site contains feature and plug-in versions for Eclipse project releases, published in the form expected by the Eclipse Update Manager (R3.0). Use the Update Manager's Feature Updates view to open this site (http://update.eclipse.org/updates) to locate new and updated Eclipse project feature versions to download.
Eclipse 3.1.1 Update Site:
http://update.eclipse.org/updates/3.0
小组开发环境搭建:dhcp+apache+ftp+cvs+samba
2004-04-23 15:18 pm
作者:车东 chedong@bigfoot.com
来自:Linux知识宝库
现载:http://www.douzhe.com/linuxtips/1196.html
地址:无名
本文主要介绍一些小组开发环境搭建中经常会遇到一些服务器服务设置,这些服务很多都是鉴于大部分的开发客户端仍然是Windows设置的,每个服务都提供了简单的基本配置示范。(2003-01-10 09:22:08) By 风过留枫, 出处:作者: 车东 chedong@bigfoot.com
作者: 车东 chedong@bigfoot.com
最后更新:2003-01-08 09:51:53
版权声明:可以任意转载,转载时请务必标明原始出处和作者信息
关键词:linux develop environment cvs version contral gnats bug tracking samba dhcpd wget
内容摘要:本文主要介绍一些小组开发环境搭建中经常会遇到一些服务器服务设置,这些服务很多都是鉴于大部分的开发客户端仍然是Windows设置的,每个服务都提供了简单的基本配置示范。包括:
IP管理(DHCP):服务器IP地址的管理和解析(结合SAMBA的WINS服务),开发客户端的IP管理; WEB服务(APACHE):文档共享,CVS的WEB界面浏览,论坛工具等;
FTP服务(FTP):用于文件下载/共享;
版本控制(CVS):程序源代码和文档的版本控制;
文件共享(SAMBA):基于NETBIOS的文件共享,方便WINDOWS客户端的访问(比如一些工具的安装等); 数据库服务器(MYSQL):一些应用的后台数据库服务; 备份机制(wget + rsync):备份
很多设置是缺省按照在bash环境中,在tcsh环境中,有一些设置不太一直。
服务器规划如下:
主服务器 (Main) 备份服务器(Backup)
__________________ _______________
|APACHE WEB SERVER | | File Backup |
|SAMBA SHARE | | |
|DHCP SERVER | | DHCP Backup |
|CVS SERVER | | MySQL server |
|GNATS SERVER | | |
|PHORUM SERVER | | |
|Database backup | | |
------------------ ---------------
硬件准备:至少要2台服务器
从理论上说,任何系统的崩溃只是时间问题,而且谁也不能保证开发者不犯错误
解决的办法只有:备份,备份,备份……
操作系统准备
FreeBSD或GNU/Liunx,系统安装时,本文的配置主要以在RedHat 8上运行为例。注意:安装时要将“开发工具”大类选上,后面的很多工具都需要使用GCC进行编译,有的应用脚本运行用到了PERL。2台主服务器分别使用IP地址192.168.0.200和192.168.0.201。
2台机器上都安装的服务:SSH FTP DHCP服务
SSH: 基本的登陆服务,对于内部分开发来说一般用缺省的就可以了,但是建议将/etc/ssh/sshd_config中的:
PermitRootLogin yes改成PermitRootLogin no
FTP:如果是FREEBSD建议用PROFTPD代替:http://www.proftpd.org/
安装:
tar zxf proftpd-version.tar.gz
cd proftpd-version/
./configure
make
make install
缺省配置/usr/local/etc/proftpd.conf
ServerName "ProFTPD"
ServerType standalone
DefaultServer on
AllowOverwrite on
Port 21
Umask 022
#不反相解析登陆机器的域名
UseReverseDNS off
MaxInstances 30
User nobody
Group nogroup
DHCP服务:为了更方便的管理在同一个局域网中开发者的IP地址和服务器的IP地址,最好将服务器安排在一定范围内的静态IP内(比如192.168.0.200以上),将(192.168.0.10-200)范围内为开发机客户端提供动态IP。假设我们的主服务器(192.168.0.200)和辅助开发服务器(192.168.0.201)使用静态IP,同时为网段内提供192.168.0.10-200动态IP分配服务。DHCP服务在2台服务器上都安装,一个做主DHCP服务,用于给子网IP池提供70%的IP,另外一台做备份,拥有IP池的30%。在例子中:200负责10-100,201负责110-150。缺省dhcpd没有安装的话,从安装盘上找到DHCP软件包安装或者从http://www.isc.org下载源文件编译即可(安装位置和配置文件可能有所不同)。
缺省配置:主服务器上
ddns-update-style none;
default-lease-time 120000;
max-lease-time 920000;
option subnet-mask 255.255.255.0;
option broadcast-address 192.168.0.255;
option netbios-name-servers 192.168.0.200;
option routers 192.168.0.1;
option domain-name-servers 202.106.196.115,202.96.199.133;
option domain-name "example.com";
subnet 192.168.0.0 netmask 255.255.255.0{
range 192.168.0.10 192.168.0.100;
}
说明:
缺省每个IP租期2天: default-lease-time 120000;
最长租期: max-lease-time 920000;
缺省子网掩码:option subnet-mask 255.255.255.0;
缺省广播地址:option broadcast-address 192.168.0.255;
让192.168.0.200上的samba服务启用WINS服务:用于内部域名解析:option netbios-name-servers 192.168.0.200;
缺省网关 option routers 192.168.0.1;
缺省域名服务器 option domain-name-servers 202.106.196.115,202.96.199.133;
缺省域名 option domain-name "example.com";
#一个缺省的子网设置:
subnet 192.168.0.0 netmask 255.255.255.0{
#为子网动态分配0.10-100的IP
range 192.168.0.10 192.168.0.100;
}
辅DHCP服务器上唯一不同的地方是:为子网动态分配0.110-150的IP,主DHCP和附DHCP服务器的IP池不能相互重叠
subnet 192.168.0.0 netmask 255.255.255.0{
range 192.168.0.110 192.168.0.150;
}
主服务器上的应用安装:
SAMBA服务:用于文件的共享和内部WINS解析
这里只做一个只读共享的简单配置,
[global]
#别人会通过"网上邻居"看到WORKGROUP组里的
linux机器,注释是:My Samba Server
workgroup = WORKGROUP
netbios name = linux
server string = My Samba Server
#日志设置
log file = /var/log/samba/%m.log
max log size = 50
#安全设置
security = share
#用SAMBA的WINS服务支持,并且用/etc/hosts做内部域名解析
wins support = yes
name resolve order = hosts lmhosts wins bcast
dns proxy = yes
[public]
#一个共享设置
comment = Public Stuff
path = /home/share
public = yes
guest ok = yes
read only = yes
writable = no
printable = no
在这里为了让大家能够在内部使用dev.example.com访问主服务器(192.168.0.200),我用DHCP设置主服务器(192.168.0.200)同时也是是内部的WINS服务器,而在200的SAMBA服务中,启用了WINS支持,并设置WINS用可以利用DNS做NETBIOS名称解析。这样如果DNS读取的是/etc/hosts文件中的设置的话,hosts文件就可以当作WINS域名配置文件了,在/etc/hosts里设置了:
192.168.0.200 dev.example.com bbs.example.com dev bbs
192.168.0.201 bak.example.com backup
后,通过DHCP获得IP的内网客户端就可以直接通过: dev.example.com访问开发服务器了。
所有的机器名前缀我都使用了缩写:dev, bbs bak之类的,原因是samba的NMBD实际上是NETBIOS名称的解析,名称长度是16个字符(实际是15个)有限制的。因此,虽然192.168.0.202 username.example.com虽然是一个合法的DNS名称解析,但由于username.example.com长度超过了16个字符,所以通过SAMBA的WINS服务解析仍然无法找到。dev.chedong.com相当于dev.chedong.com这样的一台机器。当初用SAMBA的WINS解析在客户端总是ping不通testing.chedong.com这个问题困扰了我好一阵。
WEB服务:APACHE
主要用作文件的WEB共享和一些应用的前端浏览(CVSWEB GNATSWEB PHPMYADMIN等),Apache,这里仍然使用了1.3,因为很多应用,比如PHP在2.0上运行还不是完整。
安装: http://httpd.apache.org下载最新版本:
编译选项:这样可以让所有的模块都通过配置文件动态加载,方便后面其他应用模块的添加删除: ./configure --prefix=/home/apache --enable-shared=max --enable-module=most 更多安装说明可以参考: APACHE安装笔记
文档共享技巧:
1 对于文档的共享来说,autoindex模块非常有用,让APACHE自动索引目录缺省是按文件/目录名排序的,而且文件名40个字符以上部分是被截断的,为了能显示完整的文件名,并且像资源管理器那样将目录排在前面,文件排在后面:
在模块设置中:
#增加NameWidth选项,并且文件名长度是*(自动适
#应当前目录下最长文件名)
#增加FoldersFirst选项,让目录列在前面(类似于资源管理器)
#增加了ScanHTMLTitles用HTML文件的TITLE做文件
#的描述,并设置描述长度是*(自适应最长)
IndexOptions FancyIndexing +NameWidth=*
FoldersFirst ScanHTMLTitles +DescriptionWidth=*
2 如果是CGI开发,如何让用户在自己的目录下能够发布CGI程序,比如:http://192.168.0.200/~chedong/cgi-bin/my_cgi:在 模块设置中,添加正则表达式: ScriptAliasMatch ~([a-z]+)/cgi-bin/(.*) /home/$1/cgi-bin/$2
意思是匹配~user_name/cgi-bin/cgi_name自动映射到/home/user_name/cgi-bin/cgi-name脚本
版本控制:CVS
CVS缺省在服务器上几乎都安装好了,只要按照以下步骤初始化即可:
在/etc/profile里:
CVS库所在的主服务器设置:
export CVSROOT=/home/cvsroot
在其他开发服务器里设置:
export CVSROOT=:ext:$USER@192.168.0.200:/home/cvsroot
export CVS_RSH=ssh
然后在主服务器上cvs init初始化:
关于CVSWEB的设置,重复以下CVS常用命令手册中CVSWEB的内容:
CVSWEB的下载:CVSWEB从最初的版本已经演化出很多功能界面更丰富的版本,这个是个人感觉安装设置比较方便的: http://www.spaghetti-code.de/software/linux/cvsweb/
下载解包:
tar zxf cvsweb.tgz
把配置文件cvsweb.conf放到安全的地方(比如和apache的配置放在同一个目录下),
修改:cvsweb.cgi让CGI找到配置文件:
$config = $ENV{'CVSWEB_CONFIG'}'
''/path/to/apache/conf/cvsweb.conf';
转到/path/to/apache/conf下并修改cvsweb.conf:
修改CVSROOT路径设置:
%CVSROOT = (
'Development' => '/path/to/cvsroot',
#<==修改指向本地的CVSROOT
);
缺省不显示已经删除的文档:
"hideattic" => "1",#<==缺省不显示已经删除的文档
在配置文件cvsweb.conf中还可以定制页头的描述信息,你可以修改$long_intro成你需要的文字
首先可以CVS进库里的就是以上系统的安装文档。
更多的关于CVS的扩展应用可以参考CVS常用命令手册CVSTRAC部分。
多个服务之间的资源共享
一般可以通过链接解决,比如:
我希望匿名ftp共享的内容(比如在/var/ftp/pub目录下)都可以WEB发布,
ln -s /var/ftp/pub /home/apache/htdocs/pub
我希望/usr/share/doc里的文档大家可以通过WEB浏览:
ln -s /usr/share/doc /home/apache/htdocs/doc
我希望WEB发布的内容大家通过WINDOWS网络邻居也可以访问:假设/home/share是samba发布的只读共享路径
ln -s /home/apache/htdocs /home/share
服务的自动启动:
系统已经安装的服务一般可以通过setup的服务配置是否自动启动,否则就在/etc/rc.local里加一些启动脚本即可。
比如:
备份和日志统计
可以将以下脚本写在主服务器的ROOT的CRON里:
#time sync
0 5 * * 1 (/usr/bin/rdate -s YOUR_DATE_TIME_SERVER)
#backup gnats
6 3 * * * (cd /home; tar cf
/home/backup/gnats.`date +\%w`.tar gnats)
#backup cvsroot
5 3 * * * (cd /home; tar cf
/home/backup/cvsroot.`date +\%w`.tar cvsroot)
#backup apache
8 3 * * * (cd /home; tar cf
/home/backup/apache.`date +\%w`.tar apache)
#gzip all backup
50 3 * * * (gzip -f /home/backup/*.tar)
#webalizer demo
3 5 * * * (/usr/local/bin/webalizer -c /home/apache/conf/webalizer.conf
/home/apache/logs/`date -d yesterday +
%w`/access_log)
#remove last week log
3 4 * * * (find /home/apache/logs/ -name
access_log -mtime +6 -exec rm -f {};)
这样,在/home/backup目录下会有按星期轮训的7个备份。然后通过在辅助服务器上设置CRON用wget的-m选项镜像主服务器上的/home/backup目录或者用rsync做同步就可以了。后面的2条是关于使用webalzier做服务器的日志统计,APACHE的日志通过cronolog进行了轮循。具体设置请参考: