Keep simple but not too simple

发布新日志

  • 【转】程序设计中的一些感悟

    2007-06-26 17:55:11Top 1 Digest 1


    1)学习应该从基础打起,不要一开始就尝试最高深的技术。
    2
    )每看一本书,不要说这章我以前学习过了,也掌握的很好,因此我可以跳过这一章看更重要的了。
    3
    )对于作业,遇到不会的尽量不要立刻向别人请教。如果实在解决不了的问题,可以先完成你会的,然后把一些特别的难点提炼出来,向高手请教。
    4
    )不要指望书本和行家能帮你解决一切问题,因为并不是所有问题都能由别人教给你。
    5
    )向别人请教问题应该把问题说明白。对于错误提示信息应该原样提供出来,不要按自己理解的信息提供。因为既然你自己做不了,说明你理解一般都有问题。
    6
    )问问题最好能带代码。
    7
    )不要说编译通过,可是运行时...",因为编译错误和运行错误可能根本没有关系。一般来说,编译是语法问题,而运行是逻辑问题。
    8)
    书看千遍不如做程序一遍,应该尽量尝试去写程序。
    9
    )做程序千个不如做好程序一个。应该尽量完善你现在做的程序,而不要不断开新的计划,而每个计划都虎头蛇尾。
    10
    )要想到你不是一个人写程序,而是和大家一起写程序。
    11
    )高深的技巧虽然显示了高深的本领,但是对于合作往往是有害的,应该尽量写出简单易读的代码。
    12
    )编制程序应该尽量做到自注释,即代码本身一读就懂,好象自己在说明自己的逻辑一样。
    13
    )复杂的代码如果实在做不到自注释,应该给出适量的注释。
    14
    )注释在修改代码的时候应该相应修改,不能用陈旧的注释去误导别人。
    15)
    代码应该尽量可重用,相同功能的代码应该由相同的函数完成,重要函数应该给出调试信息,以便调试时及早发现问题。
    16
    )应该尽量写小函数,每个函数尽量不要超过40行或者更少。这样不用滚动屏幕也许就可以读完整个函数。
    17
    )对于switch语句,尽量不要有过多的分支,如果分支太多,可以考虑用跳转表。
    18
    )尽量少使用一些有争议的语句,如goto和三目运算符,既然有争议,它肯定有一定的缺点。
    19
    )对于goto,许多工程师技术高到可以合理使用,而不至于导致问题。但是你的程序并不一定给你同水平的人看和修改,他们可不能保证合理的读和修改这些相关代码。
    20)
    代码编写时应该有一定的格式,其基本要求是对理解代码有一定帮助。
    21
    )如果数据是多个模块共有的,应该提供一个封装的类来管理它,并提供一个合适的接口给各个模块。这样,如果数据内容有重大修改,则只要接口不变,基本上可以保证程序不要很复杂的修改。
    22
    )应该尽量考虑到数据的并发控制。
    23
    )数据的并发控制应该封装在接口内,而不要暴露给其他模块,这样可以减少因为并发原因导致的程序死锁。
    24
    )数据本身结构不可以太复杂。应该尽量把不相关的数据分割成为两组数据。
    25
    )对于数据量比较大的情况,应该考虑数据库。
    26
    )数据库接口应该采用标准ODBC或者ADO接口,尽量不要根据实际数据库DBMS提供的接口来处理,因为你可能在实际使用中更换DBMS
    27
    )小的数据可以考虑文件,文件路径应该必须设计成相对路径。
    28
    )在一个函数中,应该尽量打开文件后使用完后立刻关闭,这样其他程序可能使用文件。
    29
    )不要尝试把文件全部读到内存中,应该分次处理大文件。
    30
    )编写程序应该提供相关的测试程序,以提供测试手段。
    31
    )应该考虑代码、函数的使用情况,不要超越函数可以使用的范围使用之。

  • 详解Linux下svn命令[转]

    2010-05-20 16:35:10

     本文讲述了Linux命令行下常用svn命令的使用方法,希望对您有所帮助。

      1、Linux命令行下将文件checkout到本地目录

      svn checkout path(path是服务器上的目录)

      例如:svn checkout svn://192.168.1.1/pro/domain

      简写:svn co

      2、Linux命令行下往版本库中添加新的文件

      svn add file

      例如:svn add test.php(添加test.php)

      svn add *.php(添加当前目录下所有的php文件)

      3、Linux命令行下将改动的文件提交到版本库

      svn commit -m “LogMessage“ [-N] [--no-unlock] PATH(如果选择了保持锁,就使用–no-unlock开关)

      例如:svn commit -m “add test file for my test“ test.php

      简写:svn ci

      4、Linux命令行下的加锁/解锁

      svn lock -m “LockMessage“ [--force] PATH

      例如:svn lock -m “lock test file“ test.php

      svn unlock PATH

      5、Linux命令行下更新到某个版本

      svn update -r m path

      例如:

      svn update如果后面没有目录,默认将当前目录以及子目录下的所有文件都更新到最新版本。

      svn update -r 200 test.php(将版本库中的文件test.php还原到版本200)

      svn update test.php(更新,于版本库同步。如果在提交的时候提示过期的话,是因为冲突,需要先update,修改文件,然后清除svn resolved,最后再提交commit)

      简写:svn up

      6、Linux命令行下查看文件或者目录状态

      1)svn status path(目录下的文件和子目录的状态,正常状态不显示)

      【?:不在svn的控制中;M:内容被修改;C:发生冲突;A:预定加入到版本库;K:被锁定】

      2)svn status -v path(显示文件和子目录状态)

      第一列保持相同,第二列显示工作版本号,第三和第四列显示最后一次修改的版本号和修改人。

      注:svn status、svn diff和 svn revert这三条命令在没有网络的情况下也可以执行的,原因是svn在本地的.svn中保留了本地版本的原始拷贝。

      简写:svn st

      7、Linux命令行下删除文件

      svn delete path -m “delete test fle“

      例如:svn delete svn://192.168.1.1/pro/domain/test.php -m “delete test file”

      或者直接svn delete test.php 然后再svn ci -m ‘delete test file‘,推荐使用这种

      简写:svn (del, remove, rm)

      8、Linux命令行下查看日志

      svn log path

      例如:svn log test.php 显示这个文件的所有修改记录,及其版本号的变化

      9、Linux命令行下查看文件详细信息

      svn info path

      例如:svn info test.php

      10、Linux命令行下比较差异

      svn diff path(将修改的文件与基础版本比较)

      例如:svn diff test.php

      svn diff -r m:n path(对版本m和版本n比较差异)
    例如:svn diff -r 200:201 test.php

      简写:svn di

      11、Linux命令行下将两个版本之间的差异合并到当前文件

      svn merge -r m:n path

      例如:svn merge -r 200:205 test.php(将版本200与205之间的差异合并到当前文件,但是一般都会产生冲突,需要处理一下)

      12、Linux命令行下SVN 帮助

      svn help

      svn help ci

      以上是常用命令,下面写几个不经常用的

      13、Linux命令行下版本库下的文件和目录列表

      svn list path

      显示path目录下的所有属于版本库的文件和目录

      简写:svn ls

      14、Linux命令行下创建纳入版本控制下的新目录

      svn mkdir: 创建纳入版本控制下的新目录。

      用法: 1、mkdir PATH…

      2、mkdir URL…

      创建版本控制的目录。

      1、每一个以工作副本 PATH 指定的目录,都会创建在本地端,并且加入新增调度,以待下一次的提交。

      2、每个以URL指定的目录,都会透过立即提交于仓库中创建.在这两个情况下,所有的中间目录都必须事先存在。

      15、Linux命令行下恢复本地修改

      svn revert: 恢复原始未改变的工作副本文件 (恢复大部份的本地修改)。revert:

      用法: revert PATH…

      注意: 本子命令不会存取网络,并且会解除冲突的状况。但是它不会恢复被删除的目录

      16、Linux命令行下代码库URL变更

      svn switch (sw): 更新工作副本至不同的URL。

      用法: 1、switch URL [PATH]

      2、switch –relocate FROM TO [PATH...]

      1、更新你的工作副本,映射到一个新的URL,其行为跟“svn update”很像,也会将服务器上文件与本地文件合并。这是将工作副本对应到同一仓库中某个分支或者标记的方法。

      2、改写工作副本的URL元数据,以反映单纯的URL上的改变。当仓库的根URL变动(比如方案名或是主机名称变动),但是工作副本仍旧对映到同一仓库的同一目录时使用这个命令更新工作副本与仓库的对应关系。

      17、Linux命令行下解决冲突

      svn resolved: 移除工作副本的目录或文件的“冲突”状态。

      用法: resolved PATH…

      注意: 本子命令不会依语法来解决冲突或是移除冲突标记;它只是移除冲突的相关文件,然后让 PATH 可以再次提交。

      18、Linux命令行下输出指定文件或URL的内容。

      svn cat 目标[@版本]…如果指定了版本,将从指定的版本开始查找。

      svn cat -r PREV filename > filename (PREV 是上一版本,也可以写具体版本号,这样输出结果是可以提交的)

      以上是Linux命令行下常用svn命令的使用方法。 google_protectAndRun("render_ads.js::google_render_ad", google_handleError, google_render_ad);
  • 【转】CentOS 修改IP地址, DNS, 网关

    2010-05-20 16:28:28

    一、CentOS 修改IP地址

    修改对应网卡的IP地址的配置文件
    # vi /etc/sysconfig/network-scripts/ifcfg-eth0

    修改以下内容

    DEVICE=eth0 #描述网卡对应的设备别名,例如ifcfg-eth0的文件中它为eth0
    BOOTPROTO=static #设置网卡获得ip地址的方式,可能的选项为static,dhcp或bootp,分别对应静态指定的 ip地址,通过dhcp协议获得的ip地址,通过bootp协议获得的ip地址
    BROADCAST=192.168.0.255 #对应的子网广播地址
    HWADDR=00:07:E9:05:E8:B4 #对应的网卡物理地址
    IPADDR=12.168.1.2 #如果设置网卡获得 ip地址的方式为静态指定,此字段就指定了网卡对应的ip地址
    IPV6INIT=no
    IPV6_AUTOCONF=no
    NETMASK=255.255.255.0 #网卡对应的网络掩码
    NETWORK=192.168.1.0 #网卡对应的网络地址
    ONBOOT=yes #系统启动时是否设置此网络接口,设置为yes时,系统启动时激活此设备

    二、CentOS 修改网关
    修改对应网卡的网关的配置文件
    [root@centos]# vi /etc/sysconfig/network

    修改以下内容
    NETWORKING=yes(表示系统是否使用网络,一般设置为yes。如果设为no,则不能使用网络,而且很多系统服务程序将无法启动)
    HOSTNAME=centos(设置本机的主机名,这里设置的主机名要和/etc/hosts中设置的主机名对应)
    GATEWAY=192.168.1.1(设置本机连接的网关的IP地址。例如,网关为10.0.0.2)

    三、CentOS 修改DNS

    修改对应网卡的DNS的配置文件
    # vi /etc/resolv.conf
    修改以下内容

    nameserver 8.8.8.8 #google域名服务器
    nameserver 8.8.4.4 #google域名服务器

    四、重新启动网络配置
    # service network restart

    # /etc/init.d/network restart

    修改 IP 地址
    即时生效:
    # ifconfig eth0 192.168.0.2 netmask 255.255.255.0
    启动生效:
    修改 /etc/sysconfig/network-scripts/ifcfg-eth0

    修改网关 Default Gateway
    即时生效:
    # route add default gw 192.168.0.1 dev eth0
    启动生效:
    修改 /etc/sysconfig/network

    修改 DNS
    修改/etc/resolv.conf
    修改后可即时生效,启动同样有效

    修改 host name
    即时生效:
    # hostname centos1
    启动生效:
    修改/etc/sysconfig/network

    注:原链接 http://www.21andy.com/blog/20100227/1717.html

  • 系统管理员职责

    2008-09-01 11:17:08

    系统管理员职责

      

        系统管理员主要负责整个集团内部网络和服务器系统的设计、安装、配置、管理和维护工作,为内部网的安全运行做技术保障。,服务器是网络应用系统的核心,由系统管理员专门负责管理。

    1、提供网络运行保障,维持网络和服务器系统的稳定、正常运转,及时解决网络和服务器系统故障,故障解决时间一般不得超过2小时。确保网络内用户能安全、高效的使用网络办公和学习。

    2、网络系统的管理

    网络设备是整个网络运转的核心,系统管理员必须保证网络核心交换机、二级交换机、路由器和防火墙等主干设备的正常运转。由于网络设备的特殊重要性,网络设备的配置管理由单一系统管理员完成,不设AB角,其他任何人不得改动设备配置,为了保证特殊情况下的接管工作,系统管理员必须做好网络设备的配置记录,对每次的配置改动作纪录,并备份设备的配置文档,记录配置时间。

    3、服务器系统的管理。服务器系统的管理采用AB角制度,A管理员负责服务器日常的的管理工作,B管理员应掌握服务器的知识,当A管理员外出的时候担负管理服务器的职责。主要包括以下工作:

    a、做好服务器配置、安装和改动记录,编写内部网络和系统运行日志,内容要详尽、科学。和服务器的配置的每次改动都要做记录,包括时间、原因、配置记录文件等。如果发生故障,就必须记录故障发生的时间、故障情况、处理方法,及预防措施等。

    b、系统管理员要定期对硬盘进行整理,清除缓存或垃圾文件。

    c、定期保存系统日志。

    d、做好系统的硬件维护,对设备定期检查,定期清洁、除尘,保持设备正常运行。

    e、网络设备或服务器的性能测试或系统软件的升级。

    4、用户的管理。

    服务器超级用户的密码要定期更换,密码设定要有一定的规定,不能少于八位,系统管理员不得对任何无关人员泄露。知道超级用户和密码的人员不得超过两人。

    对服务器用户的权限进行严格、详细的审核,对废弃的用户要及时进行删除。用户要记录进数据库,以便查询,用户密码的设定不得少于六位字母或数字。系统管理员要严守保密制度,不得泄漏用户密码。

    5、为了保证应用系统的正常运转,单一服务器上原则上提供单一应用服务,不得在单一服务器上同时提供两种应用服务(系统相互备份例外)。

    6、为了保证系统的正常运转,系统管理员不得在应用服务器上做软件或系统功能试验,不得在应用服务器上随意安装与应用无关的软件,不得在服务器上安装盗版软件。基本保证单服务器单应用。

    12、网络安全:按照《网络安全管理制度》严格执行。系统管理员要定期安装系统软件公司发布的补丁程序。

    13、防病毒:网络内所有的服务器必须安装网络防病毒软件,并及时升级病毒定义文件。定期对服务器进行全面的病毒检测。对检测出的病毒要做病毒记录。

    14、备份。

    系统备份,对重要的应用服务器,要做双机备份(有条件的话),必须保证一旦一台服务器出现故障,另一台服务器能在最短的时间内切换使用。主要包括:邮件服务、Internet服务、DNS服务。

    数据备份,做好网络内所有系统数据和应用数据的定期自动备份,定期做人工备份,确保数据的安全,要采用多种备份形式。

    15、数据保密工作,对在IntranetInternet上发布的信息,需要做保密处理的,必须进行密码或用户验证服务等处理,并对密码进行严格的管理。

    16、系统管理员应努力学习、积极进取,不断学习新的网络和服务器系统技术,不断提高自我

  • [转]tomcat

    2007-06-26 18:12:44

    转载 http://blog.csdn.net/DarkXie/archive/2004/10/25/TOMCATAPP.aspx
    谨以此文送给所有正在使用TOMCAT或者打算使用的人们,向TOMCAT的所有开发人员致敬!



    一、小猫TOMCAT其实很可爱

    2003年底,我换公司了,同样也换了WEBAPP,TOMCAT出现在我的面前(以前使用weblogic),我有点茫然,免费的东西真的能用的好么?担心ING……(其实是在火星呆太久)出门一打听,原来此猫出自名门-jakarta项目,2001年度最具创新的java产品(Most Innovative Java Product),又有JAVA的老大SUN的力捧(官方推荐的servlet和jsp容器),以后就靠它吃饭了。不说二话,搞起来先:

    1、 安装

    TOMCAT最新版本是5.0.29(http://jakarta.apache.org/site/binindex.cgi)

    如果在WINDOWS下它可以自动找到你的JDK或者set JAVA_HOME=c:/jdk

    在LINUX下需要先解压,然后设置JAVA_HOME

    export JAVA_HOME=/usr/local/jdk

    2、 RUN

    设置完毕后就可以运行tomcat服务器了,进入tomcat的bin目录,WINDOWS下用startup启动tomcat,linux下用startup.sh,相应的关闭tomcat的命令为shutdown和shutdown.sh。

    启动服务后在浏览器里输入http://localhost:8080来测试一下

    3、 目录结构

    Bin:存放启动和关闭tomcat脚本。

    Conf:包含不同的配置文件,server.xml(Tomcat的主要配置文件)。

    Work:存放jsp编译后产生的class文件。

    Webapp:存放应用程序示例,以后你要部署的应用程序也要放到此目录。

    Logs:存放日志文件

    Comm./server/shared:这三个文件夹下的LIB文件夹放jar文件。

    1、 配置server.xml文件

    没有什么好说的,看TOMCAT的文档比较有用,这里提供一些主要的东西吧。

    元素名
    属性
    解释

    server
    port
    指定一个端口,这个端口负责监听关闭tomcat的请求

    shutdown
    指定向端口发送的命令字符串

    service
    name
    指定service的名字

    Connector(表示客户端和service之间的连接)
    port
    指定服务器端要创建的端口号,并在这个断口监听来自客户端的请求

    minProcessors
    服务器启动时创建的处理请求的线程数

    maxProcessors
    最大可以创建的处理请求的线程数

    enableLookups
    如果为true,则可以通过调用request.getRemoteHost()进行DNS查询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip地址

    redirectPort
    指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的端口号

    acceptCount
    指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理队列中的请求数,超过这个数的请求将不予处理

    connectionTimeout
    指定超时的时间数(以毫秒为单位)

    Engine(表示指定service中的请求处理机,接收和处理来自Connector的请求)
    defaultHost
    指定缺省的处理请求的主机名,它至少与其中的一个host元素的name属性值是一样的

    Context(表示一个web应用程序,通常为WAR文件,关于WAR的具体信息见servlet规范)
    docBase
    应用程序的路径或者是WAR文件存放的路径

    path
    表示此web应用程序的url的前缀,这样请求的url为http://localhost:8080/path/****

    reloadable
    这个属性非常重要,如果为true,则tomcat会自动检测应用程序的/WEB-INF/lib 和/WEB-INF/classes目录的变化,自动装载新的应用程序,我们可以在不重起tomcat的情况下改变应用程序

    host(表示一个虚拟主机)
    name
    指定主机名

    appBase
    应用程序基本目录,即存放应用程序的目录

    unpackWARs
    如果为true,则tomcat会自动将WAR文件解压,否则不解压,直接从WAR文件中运行应用程序

    Logger(表示日志,调试和错误信息)
    className
    指定logger使用的类名,此类必须实现org.apache.catalina.Logger 接口

    prefix
    指定log文件的前缀

    suffix
    指定log文件的后缀

    timestamp
    如果为true,则log文件名中要加入时间,如下例:localhost_log.2001-10-04.txt

    Realm(表示存放用户名,密码及role的数据库)
    className
    指定Realm使用的类名,此类必须实现org.apache.catalina.Realm接口

    Valve(功能与Logger差不多,其prefix和suffix属性解释和Logger 中的一样)
    className
    指定Valve使用的类名,如用org.apache.catalina.valves.AccessLogValve类可以记录应用程序的访问信息

    directory
    指定log文件存放的位置

    pattern
    有两个值,common方式记录远程主机名或ip地址,用户名,日期,第一行请求的字符串,HTTP响应代码,发送的字节数。combined方式比common方式记录的值更多




    2、 管理

    TOMCAT管理能力很强大,进入http://localhost:8080/,自己慢慢管吧。实践出真知,我喜欢这样搞:

    ^_^,一切尽在掌握http://localhost:8080/manager/html 。



    一、让数据库连接池转起来

    作为一个J2EE程序员大家手上可能会有现成的JDBC 数据库连接池,其实这没有太大的必要,因为象weblogic……企业级WEBAPP都有自己的连接池,大家不要费力直接使用吧,效率也很不错,再也不用羡慕.NET的ADO了(以前作MS从来不担心数据连接,ADO确实用起来很爽),如果想实现一个 JDBC connection pool 的注意事项有:

    1. 有一个简单的函数从连接池中得到一个 Connection。

    2. close 函数必须将 connection 放回 数据库连接池。

    3. 当数据库连接池中没有空闲的 connection, 数据库连接池必须能够自动增加 connection 个数。

    4. 当数据库连接池中的 connection 个数在某一个特别的时间变得很大,但是以后很长时间只用其中一小部分,应该可以自动将多余的 connection 关闭掉。

    5. 如果可能,应该提供debug 信息报告没有关闭的 new Connection 。

    网上有各种各样的连接池代码,抄过来改改吧,嘿嘿~



    这里介绍如何配置TOMCAT的连接池,以SQLSERVER为例:

    步骤1:安装SQLSERVER的JDBC驱动

    SQLSERVER的JDBC驱动其实就是三个JAR文件,msbase.jar/mssqlserver.jar/msutil.jar,将这三个文件拷贝到你的/tomcat_home/common/lib目录下去就可以了。

    步骤2:修改server.xml文件

    具体代码如下:

    <Context path="test" docBase="F:\yourroot" debug="5" reloadable="true" crossContext="true">

    <Logger className="org.apache.catalina.logger.FileLogger" prefix="localhost_DBTest_log." suffix=".txt" timestamp="true"/>

    <Resource name="jdbc/SqlServerDB" auth="Container" type="javax.sql.DataSource"/>

    <ResourceParams name="jdbc/SqlServerDB">

    <parameter>

    <name>factory</name>

    <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>

    </parameter>

    <!-- Maximum number of dB connections in pool. Make sure you configure your mysqld max_connections large enough to handle all of your db connections. Set to 0 for no limit.-->

    <parameter>

    <name>maxActive</name>

    <value>50</value>

    </parameter>

    <!-- Maximum number of idle dB connections to retain in pool. Set to 0 for no limit.-->

    <parameter>

    <name>maxIdle</name>

    <value>20</value>

    </parameter>

    <!-- Maximum time to wait for a dB connection to become available in ms, in this example 0.5 seconds. An Exception is thrown if this timeout is exceeded. Set to -1 to wait indefinitely. -->

    <parameter>

    <name>maxWait</name>

    <value>500</value>

    </parameter>

    <!-- msSQL dB username and password for dB connections -->

    <parameter>

    <name>username</name>

    <value>sa</value>

    </parameter>



    <parameter>

    <name>password</name>

    <value>sa</value>

    </parameter>

    <!-- Class name for SQLServer2000 JDBC driver -->

    <parameter>

    <name>driverClassName</name>

    <value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>

    </parameter>

    <!-- The JDBC connection url for connecting to your MS SQL Server dB.The autoReconnect=true argument to the url makes sure that the mm.Sql Server JDBC Driver will automatically reconnect if mysqld closed the connection. mysqld by default closes idle connections after 8 hours.-->

    <parameter>

    <name>url</name>

    <value>jdbc:microsoft:sqlserver://10.0.254.11:1433;databaseName=yourdb</value>

    <!--must use & not use & -->

    </parameter>

    </ResourceParams>

    </Context>

    步骤三:程序调用

    package dbmanage;



    import java.sql.CallableStatement;

    import java.sql.Connection;

    import java.sql.Date;

    import java.sql.PreparedStatement;

    import java.sql.ResultSet;

    import java.sql.SQLException;

    import java.sql.Statement;

    import java.util.Enumeration;

    import java.util.Hashtable;

    import java.util.Vector;

    import javax.naming.Context;

    import javax.naming.InitialContext;

    import javax.sql.DataSource;



    import util.smartDateFormat;





    public class dbManager {

    /************************************

    * @param static private boolean VERBOSE ;

    * @param Statement theStatement;

    * @param PreparedStatement thePstmt;

    * @param Connection theConnection;

    ************************************/

    final static private boolean VERBOSE = true; //打印控制台控制



    //static Logger logger = Logger.getLogger(dbManager.class.getName());

    private Context initCtx = null;

    private Context ctx = null;

    private DataSource ds = null;



    private long timeout = 5000;



    private Statement theStatement = null;

    private PreparedStatement thePstmt = null;



    /************************************

    * 初试化initCtx

    * 取得数据源对象

    ************************************/

    public

    dbManager() {

    try {

    initCtx = new InitialContext();

    //init context,read config web.xml

    if (initCtx == null) {

    throw new Exception("Initial Failed!");

    }

    ctx = (Context) initCtx.lookup("java:comp/env");

    //find "jdbc/SqlServerDB" object this configruation in the SERVER.XML of Tomcat

    if (ctx != null) {

    ds = (DataSource) ctx.lookup("jdbc/SqlServerDB");

    }

    if (ds == null) {

    throw new Exception("Look up DataSource Failed!");

    }

    }

    catch (Exception e) {

    log(e, "Can't get the Context!");

    }

    }



    /************************************

    * get Connection

    * @return Connection

    ************************************/

    public synchronized

    Connection getConnection() {

    //get connection and set to delay time

    long startTime = new java.util.Date().getTime();

    Connection con = null;

    while (con == null) {

    con = newConnection();

    if (con != null) {

    //log("Create New Connection!");

    break;

    }

    try {

    log("连接超时,重新连接,等待" + timeout + "ms");

    wait(timeout);

    }

    catch (InterruptedException e) {

    log(e, "连接超时!");

    }

    if ( (new java.util.Date().getTime() - startTime) >= timeout) {

    log("Connection timeout!");

    break;

    }

    }

    return con;

    }



    private

    Connection newConnection() {

    Connection con = null;

    try {

    con = ds.getConnection();

    if (con == null) {

    throw new Exception("Create Connection Failed!");

    }

    }

    catch (Exception e) {

    log("Create Connection Failed!");

    System.out.println(e.getMessage());

    }

    return con;

    }



    /************************************

    * release the connection

    * @param conn Connection

    * @param stmt Statement

    * @param pstmt PreparedStatement

    ************************************/

    public synchronized

    void freeConnection(Connection conn,

    Statement stmt,

    PreparedStatement pstmt) {

    try {

    //close Statement

    if (stmt != null) {

    stmt.close();

    stmt = null;

    //log("Close Statement......");

    }

    //close PreparedStatement

    if (pstmt != null) {

    pstmt.close();

    pstmt = null;

    //log("Close PreparedStatement......");

    }

    }

    catch (Exception e) {

    System.out.println(e.getMessage());

    }

    try {

    //close Connection

    if (conn != null) {

    conn.close();

    conn = null;

    //log("Close Connection......");

    }

    }

    catch (SQLException e) {

    log(e, "释放资源出错!");

    }

    }





    /************************************

    * write log file.

    * @param s String

    ************************************/

    private

    void log(String s) {

    if (VERBOSE) {

    System.out.println(new java.util.Date() + ":" + s);

    //logger.info(new java.util.Date()+s);

    }

    }



    /************************************

    * write log file.

    * @param ex Object

    ************************************/

    private

    void logerr(Object ex) {

    if (VERBOSE) {

    //System.out.println(new java.util.Date()+":"+s);

    //logger.error(ex);

    }

    }



    /************************************

    * write log file.

    * @param e Throwable

    * @param msg String

    ************************************/

    private

    void log(Throwable e, String msg) {

    System.out.println(new java.util.Date() + ": " + msg);

    //logger.info(new java.util.Date() + ": " + msg, e);

    }

    ……

    }



    OK,你现在可以方便的使用连接池了,想要一个得一个,记得要释放哦,连接池的数量总是有限的。





    二、中文问题照样很简单

    每个国家(或区域)都规定了计算机信息交换用的字符编码集,如美国的扩展 ASCII码, 中国的 GB2312-80,日本的 JIS 等,作为该国家/区域内信息处理的基础,有着统一编码的重要作用。字符编码集按长度分为 SBCS(单字节字符集),DBCS(双字节字符集)两大类。早期的软件(尤其是操作系统),为了解决本地字符信息的计算机处理,出现了各种本地化版本(L10N),为了区分,引进了 LANG, Codepage 等概念。但是由于各个本地字符集代码范围重叠,相互间信息交换困难;软件各个本地化版本独立维护成本较高。因此有必要将本地化工作中的共性抽取出来,作一致处理,将特别的本地化处理内容降低到最少。这也就是所谓的国际化(I18N)。各种语言信息被进一步规范为 Locale 信息。处理的底层字符集变成了几乎包含了所有字形的 Unicode。

    现在大部分具有国际化特征的软件核心字符处理都是以 Unicode 为基础的,在软件运行时根据当时的 Locale/Lang/Codepage 设置确定相应的本地字符编码设置,并依此处理本地字符。在处理过程中需要实现 Unicode 和本地字符集的相互转换,甚或以 Unicode 为中间的两个不同本地字符集的相互转换。这种方式在网络环境下被进一步延伸,任何网络两端的字符信息也需要根据字符集的设置转换成可接受的内容。

    Java 语言内部是用 Unicode 表示字符的,遵守 Unicode V2.0。Java 程序无论是从/往文件系统以字符流读/写文件,还是往 URL 连接写 HTML 信息,或从 URL 连接读取参数值,都会有字符编码的转换。这样做虽然增加了编程的复杂度,容易引起混淆,但却是符合国际化的思想的。从理论上来说,这些根据字符集设置而进行的字符转换不应该产生太多问题。而事实是由于应用程序的实际运行环境不同,Unicode 和各个本地字符集的补充、完善,以及系统或应用程序实现的不规范,转码时出现的问题时时困扰着程序员和用户。

    其实解决 JAVA 程序中的汉字编码问题的方法往往很简单,但理解其背后的原因,定位问题,还需要了解现有的汉字编码和编码转换。相信这样的东西大家都见过了

    new String(request.getParameter("test").getBytes("iso-8859-1"),"GBK")

    但这样的代码相信不是一个解决的办法,这样会增加程序的复杂度,写数据库,提交表单,URL中传中文参数,到处都是中文问题!作为一个连走路都要算计最短距离的懒人,当然不愿天天叨念着new String(request.getParameter("test").getBytes("iso-8859-1"),"GBK"),然汉战战兢兢的处理各种字符转换的问题,我跋山涉水,翻山越岭,终于找到了完美的解决方式,在TOMCAT中只需要简单的配置,引入2个文件就可以轻松搞定。

    前提条件,每个页面使用

    <%@ page contentType="text/html; charset=GBK" language="java" import="java.sql.*" errorPage="" %>

    <meta http-equiv="Content-Type" content="text/html; charset=GBK">

    地球人都知道的东西。

    步骤1:添加过滤器

    在TOMCAT中找到这2个文件RequestDumperFilter.java,SetCharacterEncodingFilter.java,他们位于D:\Tomcat5.0.27\webapps\jsp-examples\WEB-INF\classes\filters,加到你的工程文件里去,编译他们。

    步骤2:配置WEB.XML

    在web.xml里加入这一段

    ……

    <filter>

    <filter-name>Set Character Encoding</filter-name>

    <filter-class>filters.SetCharacterEncodingFilter</filter-class>

    <init-param>

    <param-name>encoding</param-name>

    <param-value>GBK</param-value>

    </init-param>

    </filter>

    <filter-mapping>

    <filter-name>Set Character Encoding</filter-name>

    <url-pattern>/*</url-pattern>

    </filter-mapping>

    ……

    看到没有?这样你就不用写那些麻烦的转换代码了,当然这样还不足以解决问题。

    步骤3:修改server.xml

    在server.xml修改2个地方

    <Connector port="8080"

    maxThreads="150" minSpareThreads="25" maxSpareThreads="75"

    enableLookups="false" redirectPort="8443" acceptCount="100"

    debug="0" connectionTimeout="20000"

    disableUploadTimeout="true" URIEncoding='GBK'/>

    <Connector className="org.apache.coyote.tomcat5.CoyoteConnector"

    port="8009" minProcessors="5" maxProcessors="75"

    enableLookups="true" redirectPort="8443"

    acceptCount="10" debug="0" connectionTimeout="0"

    useURIValidationHack="false" protocol="AJP/1.3"

    protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"

    URIEncoding='GBK'/>

    OK,搞定!



    三、APACHE和TOMCAT他们俩关系非同一般

    Apache和tomcat都是很优秀的软件,更可贵的是它们是免费的。其实他们2个都是jakarta项目的重要组成部分。按辈分来讲,TOMCAT是APACHE的儿子,APACHE的专长是解析静态文件,CGI,PHP……图片……,儿子当然不能抢了老爹的饭碗,所以TOMCAT只有在J2EE这个上面发愤图强,其实TOMCAT并非不能干他老爹的活,只是稳定性差点而已(偶没有明显的感觉,可能是商业炒作吧),现在大家明白为什么把他们2个扯一起了吧,上阵还靠父子兵呢~

    把2个家伙整一起有大致有2种方法,一种是利用mod_jk2.so,一种是利用mod_jk_1.2.5_2.0.47.dll。这2个东东叫联接器(TOMCAT就是通过这家伙与apache勾搭上的)

    1、 利用mod_jk_1.2.5_2.0.47.dll在WINDOWS下整合

    步骤1:准备材料

    apache2.0.52

    http://apache.te8.com/dist/httpd/binaries/win32/apache_2.0.52-win32-x86-no_ssl.msi

    tomcat5.0.27

    http://apache.linuxforum.net/dist/jakarta/tomcat-5/v5.0.19/bin/jakarta-tomcat-5.0.27.exe

    JDK(这个不用说了吧^_^)

    mod_jk_1.2.5_2.0.47.dll(关键是这个东东啊,找了我N久),据说在下面连接可以下到,最后在我同事那找到的。

    http://apache.linuxforum.net/dist/jakarta/tomcat-connectors/jk/binaries/win32/mod_jk_1.2.5_2.0.47.dll

    安装apache\ tomcat\JDK。



    步骤2:安装后设置环境变量

    设置我的电脑\属性\高级\环境变量\新建系统变量 变量名:JAVA_HOME 变量值:C:\JBuilderX\jdk1.4 (指向JDK的实际安装路径);TOMCAT_HMOM 变量值:Tomcat5.0.27;lasspath 编辑变量值中加上 ……;%JAVA_HOME%\bin;%JAVA_HOME%\lib;%TOMCAT_HOME%\bin;.;

    测试一下,访问http://localhost和http://localhost:8080,默认安装是不会有什么错误的^_^

    把连接器mod_jk_1.2.5_2.0.47.dll COPY到D:\Apache2\modules\下。



    步骤3:apache配置

    在d:\Apache2\conf下找到httpd.conf,找到DirectoryIndex,在index.html后添加index.jsp;查找“listen”用于本机测试时:Listen 127.0.0.1:80,我的是这样设置的Listen *:80

    查找AddDefaultCharset设置为AddDefaultCharset off,这样APACHE将以你页面定义的字符集解析页面。

    在最后添加如下代码:

    <VirtualHost *:80> #localhost为本机,你可用本机ip

    ServerAdmin darkxie@hotmail.com #你的mail地址

    DocumentRoot F:/uutang/uutang #你的项目组根目录

    ServerName dark #你的服务名,若你的机器有域名,设为域名

    ErrorLog logs/ErrorLog.txt #错误日志

    CustomLog logs/CustomLog.txt common #访问日志

    JkMount /servlet/* ajp13 #让Apache支持对servlet传送,用以Tomcat解析

    JkMount /*.jsp ajp13 #让Apache支持对jsp传送,用以Tomcat解析

    JkMount /*.do ajp13 #让Apache支持对struts的action传送,用以Tomcat解析

    </VirtualHost>

    LoadModule jk_module modules/mod_jk_1.2.5_2.0.47.dll

    JkWorkersFile "D:/Tomcat5.0.27/conf/workers.properties"

    JkLogFile "D:/Tomcat5.0.27/logs/mod_jk2.log"

    JkLogLevel info



    步骤4:tomcat配置

    在d:\Tomcat5\conf下新建一个workers.properties文件 .内容如下:

    workers.tomcat_home=d:\Tomcat5 #让mod_jk模块知道Tomcat

    workers.java_home=d:\jdk1.3 #让mod_jk模块知道j2sdk

    ps=\

    worker.list=ajp13 #模块版本,现有ajp13了,不要修改

    worker.ajp13.port=8009 #工作端口,若没占用则不用修改

    worker.ajp13.host=localhost #主机,若上面的Apache主机不为localhost,作相应修改

    worker.ajp13.type=ajp13 #类型

    worker.ajp13.lbfactor=1 #代理数,不用修改

    修改TOMCAT的server.xml文件:

    <!-- Define a Coyote/JK2 AJP 1.3 Connector on port 8009 -->

    <Connector className="org.apache.coyote.tomcat5.CoyoteConnector"

    port="8009" minProcessors="5" maxProcessors="75"

    enableLookups="true" redirectPort="8443"

    acceptCount="10" debug="0" connectionTimeout="0"

    useURIValidationHack="false" protocol="AJP/1.3"

    protocolHandlerClassName="org.apache.jk.server.JkCoyoteHandler"

    URIEncoding='GBK'/>

    让TOMCAT知道ajp13协议,apache和tomcat俩父子间靠这个协议沟通。

    测试一下,访问http://localhost和http://localhost:8080,看到相同的页面没有?细心点,其实很简单,看看E文的帮助,搞定不成问题。



    2、 利用mod_jk2.so(也叫JK2)整合

    jk2是一个jakarta-tomcat-connectors-jk2.0.4-win32-apache2.0.49.zip文件,主要用的是其中的mod_jk2.so。其实利用mod_jk2.so整合和利用mod_jk_1.2.5_2.0.47.dll整合大同小异,只是换了个联接器而已,现在一步一步整起来~

    步骤1:没有多说的,安装好TOMCAT和APACHE

    下载jakarta-tomcat-connectors-jk2.0.4-win32-apache2.0.49.zip,解压,将mod_jk2放到apache的安装文件夹下的modules文件夹中。

    步骤2:apache配置

    在/conf中加入一个work.properties文件,其内容如下:

    <!--这个文件的作用不是很清楚,总之路径设置正确就行了。我的apache装在D:/Apache2,根据情况自己修改。-->

    [shm]

    file=D:/ /Apache2/logs/shm.file

    size=1048576

    <!--这个socket channel是必须的,port和host对应于tomcat端的设置。-->

    #The socket channel

    [channel.socket:localhost:8009]

    port=8009

    host=localhost

    <!--worker,必须的。-->

    #define the worker

    [ajp13:localhost:8009]

    channel=channel.socket:localhost:8009

    <!--url mapping,我的主要是.jsp和struts的.do,servlet的话设置成[uri:/xxx/*]之类的。-->

    #uri mapping

    [uri:/*] #和第一种方式一样吧^_^

    [uri:/*.jsp]

    [uri:/*.do]

    worker=ajp13:localhost:8009

    在httpd.conf中,在LoadModule那里加入这句:

    LoadModule jk2_module modules/mod_jk2.so

    在最后加入这句:

    JkSet config.file "conf/work.properties"

    这是告诉apache去哪里找jk的配置的,根据具体情况修改。

    还要修改一下DirectoryIndex,DirectoryIndex index.html index.html.var index.jsp查找“listen”用于本机测试时:Listen 127.0.0.1:80,我的是这样设置的Listen *:80。

    当然还有我们的虚拟目录:

    <VirtualHost *:80>

    ServerAdmin darkxie@hotmail.com

    DocumentRoot F:/uutang/uutang

    ServerName dark

    ErrorLog logs/ErrorLog.txt

    CustomLog logs/CustomLog.txt common

    #JkMount /servlet/* ajp13

    #JkMount /*.jsp ajp13

    #JkMount /*.do ajp13

    </VirtualHost>



    步骤3:tomcat配置

    Tomcat的端口设置为8080。

    在/conf文件夹加入jk2.properties文件,其内容如下:

    # Set the desired handler list

    handler.list=apr,request,channelSocket

    #

    # Override the default port for the socketChannel

    channelSocket.port=8009

    TOMCAT自己已经生成了这个文件,找到相关的地方把注视去掉改一下就成。
  • 知道手机号码为什么以13开头吗?

    2007-03-01 17:12:45

    大家都知道以前电信移动邮政还没有分家阿,邮电资源是邮电部(现信息产业部)统一调度的。
    在分配号段的时候,做了一些细致的规划,大体是这样:
    10开头,电信服务号码,如103国际半自动挂号,108国际对方付费电话,1000电信服务中心,1001联通服务中心等等。
    11开头,赋予特种服务号码,如110匪警,111电信内部测试,112报修,113、115国内人工长途挂号,114查号台,116国内人工长途查询,117报时,119火警等,
    12开头,赋予民用特殊号码,如120(医院),121(天气预报),122交通事故告警,126、127、128、129寻呼台(BP机时代)。

    所以分配到手机用户时,以13开始做号段。后来分配130~133为联通,134~139为移动。

     

    其他的特殊号段号码有:


    16,声讯类,如160中国电信工人信息服务接入码,166语音信箱业务,167吉通计算机互联网业务接入码,168声讯服务,中国电信公众多媒体网接入码等
    17,长途电话服务,如170国内长途全自动话费查询台,173国内立接制长途半自动挂号台,176国内长途半自动查询台,177国内长途半自动班长台,179IP语音服务接入码等
    18,部分服务台,如180邮政服务,184邮政编码查询接入码,185国家邮政局电话信息服务接入码,186移动服务中心,188固定电话交费台,189中国电信业务受理特服台等。
    19,寻呼接入,191联通无线电人工寻呼接入码,192联通无线电自动寻呼接入码等等
    20,30,电话卡服务
    另外,8、9开头的号段部分也作电信及民用服务号码

    以上号码均作分配,内容太多未详细列出,部分号码目前已经升级,如电信服务台,天气预报,邮政服务台等等

  • 进销存系统的报表测试

    2007-02-27 16:38:06

    文适合有过MIS系统报表测试经验,或者有关进销存系统测试经验的朋友参考。

    报表功能的基本要求,就是通过查询/统计/分析,提供用户所需的准确的数据。如果无法实现这个基本功能,则报表完全失去意义。

    对于用户来说,报表可以直接影响到他们的决策,例如可能因为报表对销售和库存情况反映的不准确,导致错误的大量进货;或者因为报表对应收应付金额计算的不准确,而导致企业对资金占用情况做出错误的估计,

    从而导致错误的决策,最终造成用户在经营上的损失。诸如此类,相信只要大家留心,还可以找出很多这样的例子。

    进销存系统中的报表多如牛毛,而且各种不同行业的进销存系统中的业务有区别,报表也有些区别,因此不太可能对各种报表逐个讲解,而主要是把一些报表测试的经验总结成了十几条可以在各种行业的报表测试中应用的“最佳实践”,来跟大家一起分享。希望下面的这十几条像一招招简单实用的“擒拿手”,可以供正在进行报表测试或者准备开始作报表测试的朋友随手拈来,见招拆招,轻松应对这项工作。


    <!--[if !supportLists]-->(1)       提高对业务的熟悉程度<!--[endif]-->

        其实对任何一个软件进行测试,都必须要熟悉它的业务,包括业务流程和业务规则。但是报表同一般的业务功能还是有些区别的。例如对于单据的增、删、改,通过对界面的浏览和探索性的操作,大概都可以弄明白它的业务流程和业务规则,因为这些内容比较直观,而且在不同的行业中也差不了太多。但是在报表中,是很难直观的看到我们所需要了解的内容的。例如报表中的某个数据项,它的算法或者说数据来源,恐怕是比较难看出来的——即使是很类似的一个数据项,在不同行业的实际业务中,它的算法和数据来源也可以能完全不同的。

    所以对于报表业务的熟悉,主要是两个方面:数据项的算法和数据来源,也就是说要明白一个数据项同具体的业务有什么关系,单据的增、删、改或者状态的变化,对报表中各个数据项的计算会产生什么不同的影响。如果不知道到这些,那么就无法验证报表中的数据是否准确,也无法通过报表去检查业务系统的正确与否。

    <!--[if !supportLists]-->(2)       覆盖所有可能的查询统计方式,而不是以自己的使用习惯为准<!--[endif]-->

        对于报表的使用者来说——一般是企业的中层或高层领导,他们对于报表的要求可能会是多方面的,例如在进销存系统中,可能需要按不同商品进行分类统计,也可能是按供应商分类统计,这些都是由用户在实际工作中的需要来决定的,所以假如一个报表提供了多种查询统计的方法,那么在测试时,只要时间充分,就应该覆盖这些所有可能被用到的查询统计方法,而不是以自己的使用习惯为测试的依据。

    <!--[if !supportLists]-->(3)       使用或构造受控的数据环境<!--[endif]-->

        数据对于报表测试来说是一个非常非常重要的问题。因为上面说到,报表的基本功能就是通过各种查询统计分析的方法,为用户提供准确的数据,帮助用户做出决策。那么那些用来进行测试的数据从哪里来呢?

        首先,应该保证准备足够多的有效的数据。前面一条也提到了,在实际测试报表时,应当尽可能的覆盖到报表所提供的各种查询统计方法,因此至少应该保证每一种查询统计方法都应该有对应的数据,得到的结果都不会是0,否则等于没有覆盖到这个被测的查询统计算法。当然数据也不是越多越好,能保证全部覆盖,并且刚好够用就可以了,因为数据的准备和生成也是很花时间的。

        其次,要保证数据的可控。数据并不是随意生成的,如果使用通过自动化工具或者通过业务测试时随意的输入的数据来进行报表测试,一般来说是不太可能的。因为如果无法控制数据来源,那么即使知道报表中每个数据项的算法,也无法最终验证报表的查询统计结果是否正确。例如,系统的会有不同类型的单据,每种单据又会有不同的状态,某个报表的统计中,可能会涉及到多种类型和状态的单据,那么在准备数据时,就要充分考虑到这一点,准备各种不同的单据来满足测试的要求。又比如,如果整个系统中只有一个供应商,一个商品,那么测试按供应商分类统计或者按商品分类统计的报表时,意义也就不大了。

        所以如果希望高有效、更高质量的完成报表的测试,那么就要重视并增加对于数据准备工作的关注:用于验证报表功能的数据,一定是专门为报表准备的,并且是经过精心设计,要分析影响数据项算法的各种因素,以及每个因素可能出现的不同变化,这样才有可能覆盖各种查询统计方法;同时,才能保证无论使用哪个数据项的算法进行计算,其结果都是可以预知的——因为数据来源已经被我们控制了。

        特别是对于算法比较复杂,又提供了多种查询统计方式的报表,如果想完整的测试,就需要准备大量的数据。而如果想高效、高质量的完成这项功能,就一定要理解数据准备工作的重要性。

        经过精心设计的数据还有一个好处,就是当在进行业务功能的测试时,不再需要使用一些随意的数据,而是可以通过业务测试的过程,把报表测试所需要的数据输入到系统中。并根据报表对单据类型和状态的需要,进行相应的操作。

        如果留心,你也会发现报表测试同其他业务功能测试的有个区别。业务功能(例如单据的新增、审核等)的测试用例设计,通常需要考虑的是对各种正常的、异常的业务流程和业务规则的组合的遍历或覆盖;而对于报表功能,虽然没有太复杂的业务流程和规则,但是算法更加复杂,同时报表功能本身就是一种对数据的加工处理,因此会更偏重于对于各种数据来源和算法的遍历或覆盖,也就是要准备各种正常的、异常的数据,来验证报表是否取到的该取的数据、没有取不该取的数据,并且最后计算出了正确的结果。

    <!--[if !supportLists]-->(4)       特征性数据的准备<!--[endif]-->

        这又是一个同数据准备有关的问题,也是一个解决实际问题的经验。如果由多人同时对一个系统进行测试,虽然大家各自使用的数据都是经过精心设计的,但是在实际进行报表测试时,还是很难保证其他人的数据不会对自己的测试结果产生影响,最明显的一个问题就是原来自己对结果是可以预知的——因为数据是经过精心设计的,是可控的,但是现在掺杂了别人的数据,就需要花时间去区分这种“假”的错误和真的错误。

    有一个经验是可以借鉴的,就是在初期,团队内对数据的准备达成一直,使数据中的某一项具有特征性,例如分别使用不同的供应商,或者使用不同的商品。最后测试报表时,通过限定选取的数据来源,来保证相互之间尽可能的没有影响。

    <!--[if !supportLists]-->(5)       做好数据环境的备份和维护<!--[endif]-->

    虽然数据是经过精心准备的,但是难免在操作过程中因为误操作导致环境的变化,例如不小心把一张单据变成了另外一种状态,或者某个类型的单据多做了一张。对于这种情况,一个简单的方法就是去维护你的数据文档——一般我们都可以用EXCEL表来保存我们事先准备的数据,可以一个文件保存一个类型的单据,例如采购单、入库单、出库单等等,文件中的每张表用来保存不同状态的单据,例如已经审核过的入库单,没有审核过的入库单,全部入库的入库单和部分入库的入库单,等等。假如你一不小心,把一张本来准备入一半的入库单全入了,那也不要惊慌,去重新调整一下你的数据文档,在相应的类型相应的状态的单据列表中新增一张就行了。

    而如果想减少回归测试的工作量,那么应该考虑在一些关键的“点”上备份测试数据。例如所有的基础单据已经输入完成,但是还都没有开始审核或者出入库,那么可以备份一下,下次再测的时候可以直接在数据库中恢复这部分原始数据。

    <!--[if !supportLists]-->(6)       在业务功能测试通过之后才开始<!--[endif]-->

    这一点相信应该不难理解,如果业务功能本身存在缺陷,导致的数据不准,那么最后进行报表测试也就没有什么意义了。所以,应该在保证各项同报表有关的业务的功能测试通过之后,才开始考虑对报表进行测试。

    <!--[if !supportLists]-->(7)       寻求开发人员的协助<!--[endif]-->

        在报表测试中很常见的一个问题,是需求文档中可能没有定义报表的各个数据项的算法,这时候你需要找开发人员帮忙,向他们了解准确的算法和相应的公式。

    <!--[if !supportLists]-->(8)       多个报表相互对照<!--[endif]-->

    这是一项“高级”的报表测试技能,需要对整个系统中的各种业务的熟悉程度达到一种炉火纯青的地步。除了可以准确的说出各个业务的处理过程对每张报表的影响之外,还能够进行横向的联系,知道不同报表之间存在的关系。例如,一个简单的例子,库存报表中,你可以看到商品的出入库情况,而在销售报表中,你可以看到商品的销售金额和销售成本金额,在应收应付报表中,你又可以看到不同供应商或客商之间的应收应付金额。那么这几张报表之间,是否存在一些关联呢?是否会存在一些可以相互验证的地方呢?这个问题,留给大家来思考 ^_^

    <!--[if !supportLists]-->(9)       着重对那些算法复杂、与业务功能关联较多的报表的测试<!--[endif]-->

        如果只是简单的把某个日期范围内的所有入库情况统计出来,可能不会出错;但是如果还要考虑按照供应商或商品汇总,同时要选取特定的类型或状态的单据,再进行一些响应的计算,恐怕就很难保证开发人员永远不会出错了。这就像业务功能的测试一样,越是复杂的业务,越有可能出错。

    <!--[if !supportLists]-->(10)   留意四舍五入对报表数据的影响<!--[endif]-->

    从这一条开始,后面的内容可以说也是一些在实际测试时要注意的事项。

    这也是一个常见的问题。在一般的进销存系统中,都会存在这种情况,无论小数点后保留几位,总是难以避免明细和汇总之间的差别。原因可能是因为采购和销售的包装不一致,因为拆零引起的,例如10/30*30≠10;或者由于毛利率、税率等因素导致的不一致。我们曾经试过在保留4位小数的情况下还是无法避免这种情况。

    <!--[if !supportLists]-->(11)   留意进/存/销时使用不同单位对报表数据的影响<!--[endif]-->

    例如采购时是5箱,每箱有100盒,而销售单位是盒,入库之后,可能会要求按照销售单位来统计,这时要注意开发人员是否会选择了错误的单位,把500盒弄成5盒。

    <!--[if !supportLists]-->(12)   留意业务单据中存在多个日期字段时对报表数据的影响<!--[endif]-->

        一般来说,一张单据上都会有多个日期字段,比如采购单就有采购日期、单据日期、审核日期,而入库单也会有单据日期、入库日期,诸如此类。那么在测试时,一定要留意,开发人员是否按照要求选择了正确的日期,包括日期选取的一致性——是否存在这边取采购日期,那边取审核日期的情况。

    <!--[if !supportLists]-->(13)   留意是否存在遗漏的单据类型<!--[endif]-->

        例如像出入库的报表,入库方向的,除了最主要的采购入库外,可能还会包括退货入库、盘盈入库、报溢入库;出库方向的,除了最常见的销售出库,还会包括盘亏出库、报损出库。那么在具体测试时,一定要准备充分这些相应类型的单据,并且要留意开发人员是否遗漏了相应的单据类型。

    <!--[if !supportLists]-->(14)   留意不同状态的单据对报表数据的影响<!--[endif]-->

    例如采购单,当采购单发出后,供应商会开始送货,可能第一批之送来了一半的商品,那么这时采购单的收货状态是“未完成” ;当供应商把商品送齐了以后,采购数量=收货数量,则采购单的收货状态变为“已完成”。那这时留意,开发人员在采购报表中,是包含所需要的状态的单据,还是只包含了一部分?

    <!--[if !supportLists]-->(15)   留意那些被当做默认规则的因素<!--[endif]-->

    有些规则——例如单据类型或者单据状态——是作为默认规则写死在SQL语句或者数据库的存储过程里面,这些规则不会体现在界面,也不会由用户选择决定。但是这些规则恰恰是最容易被忽略的部分。所以,一定要同开发人员反复确认,保证自己已经了解了同报表各数据项计算有关的各个因素。

    <!--[if !supportLists]-->(16)   保证测试人员可以通过UI 找到自己所需的所有原始数据<!--[endif]-->

    在进行系统测试时,无论是报表,还是一般的业务功能测试,都不要去直接通过SQL语句查询数据库中的内容,而是通过UI来输入,再通过UI体现处理的结果进行验证——因为这是系统测试,不是集成测试,将来用户是决不会去直接查数据库的。因此,如果需要对报表的结果进行验证,应该通过其他的功能模块,去查询业务单据,或者其他报表,根据UI体现的结果,来进行确认。

    <!--[if !supportLists]-->(17)   检查大数据量对报表的影响<!--[endif]-->

        报表测试也会涉及到性能测试,主要是在大数据量查询统计的测试。大数据量一是说原始数据多,二是被操作、计算的数据多,三是某个数据项被是经过多次计算得出的。特别是对于一些算法比较复杂的报表,10万条数据和100万条数据时的响应时间将表现出巨大的差别。

    <!--[if !supportLists]-->(18)   不要遗漏权限控制和访问安全性的测试<!--[endif]-->

    这里说的权限控制不是谁可以访问某个模块,谁不可以访问某个模块,而且数据的计算也没有直接的关系,而是侧重于报表设计的测试。我们都知道不同的报表是设计给不同的人看的,例如出入库报表是给仓库管理人员看的,里面不会包含商品的价格,而只会包含数量;而财务报表中,只会包含采购、销售的金额,而不会包含数量,这样才能保证可以相互对照,不会出现营私舞弊的行为。那么在测试时,应当考虑报表是否泄漏了不该泄漏的信息。当然,这里对业务的熟悉程度就是更高的要求了。

    又如,不同的业务员只能看到同自己有关的业务,但是领导可以看到所有业务员的业务——例如不同的业务员分管不同的客户或者地域,他们之间的销售情况是互相保密的。

    还有一种情况,系统的用户可能会为他的供应商提供一个专门的程序或者Web页面,供其对其供应的商品的销售、库存情况进行查看。那么对于这种情况,一方面是要保证某个供应商只能看到他所供应的商品的销售、库存情况,如果某个商品由多个供应商同时提供,那么其中一个供应商应该只能看到他提供的那部分,而看不到其他供应商提供的同一商品的情况。当然,这种功能一般都是通过外网(internet)来访问的,所以也还要考虑相应的访问安全性测试,以免泄漏重要的商业信息。
  • 转贴:好的测试工程师应具备的素质

    2007-02-24 10:43:06

    人测试工作中最有价值也是最重要的资源,没有一个合格的、积极的测试小组,测试就不可能实现。然而,在软件开发产业中有一种非常普遍习惯,那就是让那些经验最少的新手、没有效率的开发者或不适合干其他工作的人去做测试工作。这绝对是一种目光短浅的行为,对一个系统进行有效的测试所需要的技能绝对不比进行软件开发需要的少,事实上,测试者将获得极其广泛的经验,他们将遇到许多开发者不可能遇到的问题。

    沟通能力 一名理想的测试者必须能够同测试涉及到的所有人进行沟通,具有与技术(开发者)和非技术人员(客户,管理人员)的交流能力。既要可以和用户谈得来,又能同开发人员说得上话,不幸的是这两类人没有共同语言。和用户谈话的重点必须放在系统可以正确地处理什么和不可以处理什么上。而和开发者谈相同的信息时,就必须将这些活重新组织以另一种方式表达出来,测试小组的成员必须能够同等地同用户和开发者沟通。


    移情能力 和系统开发有关的所有人员都处在一种既关心又担心的状态之中。用户担心将来使用一个不符合自己要求的系统,开发者则担心由于系统要求不正确而使他不得不重新开发整个系统,管理部门则担心这个系统突然崩溃而使它的声誉受损。测试者必须和每一类人打交道,因此需要测试小组的成员对他们每个人都具有足够的理解和同情,具备了这种能力可以将测试人员与相关人员之间的冲突和对抗减少到最低程度。

    技术能力 就总体言,开发人员对那些不懂技术的人持一种轻视的态度。一旦测试小组的某个成员作出了一个错误的断定,那么他们的可信度就会立刻被传扬了出去。一个测试者必须既明白被测软件系统的概念又要会使用工程中的那些工具。要做到这一点需要有几年以上的编程经验,前期的开发经验可以帮助对软件开发过程有较深入的理解,从开发人员的角度正确的评价测试者,简化自动测试工具编程的学习曲线。

    自信心 开发者指责测试者出了错是常有的事,测试者必须对自己的观点有足够的自信心。如果容许别人对自己指东指西,就不能完成什么更多的事情了。  

    外交能力 当你告诉某人他出了错时,就必须使用一些外交方法。机智老练和外交手法有助于维护与开发人员的协作关系,测试者在告诉开发者他的软件有错误时,也同样需要一定的外交手腕。如果采取的方法过于强硬,对测试者来说,在以后和开发部门的合作方面就相当于“赢了战争却输了战役”。

    幽默感 在遇到狡辩的情况下,一个幽默的批评将是很有帮助的。


    很强的记忆力 一个理想的测试者应该有能力将以前曾经遇到过的类似的错误从记忆深处挖掘出来,这一能力在测试过程中的价值是无法衡量的。因为许多新出现的问题和我们已经发现的问题相差无几。

    耐心 一些质量保证工作需要难以置信的耐心。有时你需要花费惊人的时间去分离、识别和分派一个错误。这个工作是那些坐不住的人无法完成的。

    怀疑精神 可以预料,开发者会尽他们最大的努力将所有的错误解释过去。测式者必须听每个人的说明,但他必须保持怀疑直到他自己看过以后。

    自我督促 干测试工作很容易使你变得懒散。只有那些具有自我督促能力的人才能够使自己每天正常地工作。


    洞察力 一个好的测试工程师具有“测试是为了破坏”的观点,捕获用户观点的能力,强烈的质量追求,对细节的关注能力。应用的高风险区的判断能力以便将有限的测试针对重点环节。

  • 录制脚本随感

    2006-12-29 14:59:37

    哈哈,博客开通好多天了,一直不知道该写些什么。。。实在不忍心让她一直挂着,翻啊找啊,终于找到了n年前(嘿嘿~其实是n个月前^_^)写的东东。。。。记得写的时候是很认真的,只是现在好像懒了很多,好久都没有写了,哈哈~

    录制脚本似乎不是难事,可是要录制一个比较好的能让人满意的脚本是有一定困难的。因为这不仅有技术问题,录制环境问题,还有兴趣问题。如果你对这个象放电影一样的操作很感兴趣,那么即使你没有基础,你也能做的很好。
    以前在学校学校Functional Tester时,总觉得很好玩,但也没有去深入学习。只知道录制回放,插入验证点,进行数据驱动,当然那时候进行的都是最最基本的操作,谈不上什么修改和优化脚本。只要回放通过,一切OK。但是录制脚本远不只这些--我们需要根据自己的需要去修改并优化脚本。你可以让原本回放通不过的脚本回放成功,也可以让原本回放通过的脚本回放失败;可以在脚本中添加java循环、判断、输入输出语句,也可以添加数据驱动,延迟和注释。至于怎样添加,添加在哪里,这就看你的需要了。呵呵,现在是不是在想脚本录制之前需要做一个简单的录制计划啊?这就对了,我们在录制脚本之前最好能有个大概的思路,做好能拟一个小提纲,写出该编辑的数据池(如果用专用数据池录制的时后可以不编辑;若用公用数据池,最好先编辑,在录制脚本),该插入的验证点,在哪插入验证点等。下一步就可以录制了,
    1、录制脚本时动作最好适中,不要太快,也不要太慢。
    2、在用数据驱动某行表格的某个单元格时,先用鼠标选中该单元格,再拖动手形选择
    3、回放脚本时尽量不要覆盖日志,这样便于分析错误原因。
    4、修改脚本时最好对选择操作添加延迟,比如在部门选择框中选择采购部,我们可以对此延迟几秒钟。这是因为脚本回放多次过后,速度变得很快,有可能没有选中要选项,从而导致回放失败。
    5、不管回放成功与否最好都看看一下日志。若失败了,看失败在哪里,然后修改脚本或者在下一次录制在该地方留意一点。
    6、如果要录制的是一个流程,我们可以一个功能点,一个功能点去录。先将这个框架录完再去细化脚本。这样可以保证每一个功能点都实现了,即使某个功能点在第一次录制时没有成功,你可以修改脚本使它成功,也可以重新再录一次使它成功

Open Toolbar