- 解压缩含中文文件名的zip文件。
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipFile;
import java.io.InputStream;
import java.util.Enumeration;
... ...
private void extractSourceZipFile(ZipFile zipFile, File baseDir) throws IOException {
ZipEntry zipEntry = null;
Enumeration e = zipFile.getEntries();
while (e.hasMoreElements()) {
zipEntry = (ZipEntry) e.nextElement();
if (zipEntry.isDirectory()) {
new File(baseDir + File.separator + zipEntry.getName()).mkdirs();
} else {
new File(baseDir + File.separator + zipEntry.getName()).getParentFile().mkdirs();
log.info(baseDir + File.separator + zipEntry.getName());
FileOutputStream fos = new FileOutputStream(baseDir + File.separator + zipEntry.getName());
InputStream in = zipFile.getInputStream(zipEntry);
IOUtils.copy(in, fos);
in.close();
fos.close();
}
}
} - 压缩含中文文件名文件的目录
import org.apache.tools.zip.ZipEntry;
import org.apache.tools.zip.ZipOutputStream;
... ...
private void exportZipHelper(File fs, ZipOutputStream zos, String zePath) throws IOException {
File[] files = fs.listFiles();
for (int i=0; i<files.length; i++) {
if (files[i].isDirectory()) {
ZipEntry ze = new ZipEntry(zePath+"/"+files[i].getName()+"/");
zos.putNextEntry(ze);
zos.closeEntry();
exportZipHelper(files[i], zos, zePath+"/"+files[i].getName());
} else {
FileInputStream fis = new FileInputStream(files[i]);
ZipEntry ze = new ZipEntry(zePath+"/"+files[i].getName());
zos.putNextEntry(ze);
IOUtils.copy(fis, zos);
zos.closeEntry();
fis.close();
}
}
}
public void exportZip(FIle fs, String zePath){
ByteArrayOutputStream bais = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(bais);
// Prevents java.util.zip.ZipException: ZIP file must have at least one entry
ZipEntry ze = new ZipEntry(zePath+"/");
zos.putNextEntry(ze);
zos.closeEntry();
exportZipHelper(fs, zos, zePath);
zos.close();
}
2008年12月9日星期二
使用org.apache.tools.zip解决juva.util.Zip中不支持中文文件名操作的问题
jdk中的juva.util.Zip包处理有中文文件名的文件会出现问题,解压缩的时候就报异常,压缩后的文件用winzip打开就是乱码。原因是ZipOutputStream压缩和解压ZIP文件对文件名都是以UTF-8编码方式来处理的,而我们用winzip压缩文件对文件名只会以ASCII编码方式来处理.所以会出现编码不一致的问题。目前直到jdk6也没有解决这一问题,为解决非UTF-8文件名的问题,Apache Ant项目专门设计了替代java.util.Zip包的类。
2008年9月7日星期日
RHEL5安装Subversion
- 环境准备
安装包采用RHEL5光盘上自带的subversion-1.4.2-2.el5.rpm,注意不能安装最新的subversion-1.5.1版本,经测试与sventon-1.4.0不能兼容。
添加svn帐户svnroot,用于svn存储库。 - 配置svn存储库
切换到svnroot身份,创建目录/home/svnroot/repository
建立pal代码库,svnadmin create /home/svnroot/repository/pal
修改代码库配置文件,/home/svnroot/repository/pal/conf/svnserve.conf,内容如下:
[general]
anon-access = none
auth-access = write
password-db = /home/svnroot/repository/pwd.conf
authz-db = /home/svnroot/repository/authz.conf
realm = pal
删除/home/svnroot/repository/pal/conf/authz及/home/svnroot/repository/pal/conf/pwd两个文件。
- 建立访问用户及权限
建立用户及口令,创建文件/home/svnroot/repository/pwd.conf,内容如下:
[users]
svnclient = svnclient1234
testuser = testuser1234
建立访问权限,创建文件/home/svnroot/repository/authz.conf,内容如下:
[groups]
admin = svnclient
programer = testuser
[pal:/]
@admin = rw
@programer = r
[pal:/code]
@programer = r
[pal:/design]
@programer =
[pal:/document]
@programer =
[pal:/technical]
@programer =
以上的配置建立了两个用户组,admin和programer,给予admin组pal代码库根目录及其下子目录的读写权限,给予programer组pal代码库根目录和code子目录的只读权限,design,document,technical子目录拒绝访问。 - 将svn服务添加入xinetd
切换回root帐户,进入/etc/xinetd.d/,新建文件svn,内容为:
service svn
{
socket_type = stream
protocol = tcp
wait = no
user = root
server = /usr/bin/svnserve
server_args = -i -r /home/svnroot/repository
disable = no
}
重新启动xinetd,svn服务自动启动。 - sventon安装
sventon是一个Subversion的Web浏览工具,下载地址www.sventon.org,目前版本1.4.0。
先安装tomcat 5.5以上版本,本例使用6.0.18,修改server.xml中的8080 connector配置如下:
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
connectionTimeout="20000"
URIEncoding="UTF-8"
useBodyEncodingForURI="true"
redirectPort="8443" />
注意红色的部分,这样网页才能正常显示中文路径及文件名。
将svn.war部署到tomcat容器中,浏览器中打开http://<host>/svn,出现配置页面。注意sventon有两种浏览模式,一种是全局使用一个用户,所有人都可访问svn库中的内容,此模式支持路径及文件名的搜索;另一种是使用者输入自己的svn用户名及密码,只能访问自己有权限的内容,此模式不支持搜索功能。
2008年8月31日星期日
Mysql常用命令及配置
- 创建用户
格式:grant 权限1,权限2...(或*) on 数据库(或*).表名(或*) to 用户名@登录主机名("%"代表远程登录无限制) identified by '密码'
例子:
> grant * on *.* to xued@"%" identified by 'password' ;
> flush privileges;
注意以上方式创建的用户不能以localhost登录,必须使用mysql -u xued -h IP地址 -p 的方式登录。
修改密码:
> set password for 'xued'@'%' = password('********')
- 字符集及最大连接数
修改my.cnf或my.ini,在[mysqd]下添加
default-character-set=utf8
character_set_server=utf8
max_connections=100
保存后重启。
指定数据库的编码:
> create database test charset utf8;
jdbc的连接url参数添加字符集:jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=utf8
2008年5月26日星期一
Apache,GlasshFish与Comet HTTP streaming配置
使用Apache Httpd作为GlassFish的Load Balancer有两种方式:mod_jk与mod_proxy方式,由于mod_jk不支持Comet,所以如果应用程序使用了Comet,就只能使用mod_proxy方式了。此外Httpd应使用2.2.8以上版本,之前的版本mod_proxy不能对HTTP response chunk做立即转发,必须达到8k的chunk数据才会转发,这样在使用Http streaming的Comet应用中严重降低了实时性,2.2.8解决了这一问题。
经以上配置,即可实现通过访问80端口透明代理访问Comet Http streaming服务,响应无阻塞延迟。
- GlassFish配置。
进入$GLASSFISH_HOME/domains/domain1/config,编辑domain.xml,注意如果是集群或使用其他Glassfish的instance,目录会有所不同。
找到名称为http-listener-1的<http-listener>配置组,添加下面三项:
<property name="cometSupport" value="true"/>
<property name="authPassthroughEnabled" value="true"/>
<property name="proxyHandler" value="com.sun.enterprise.web.ProxyHandlerImpl"/>
第一行是启用Comet支持,后面两行是Apache SSL转发需要的。
此外找到<jvm-options>-client</jvm-options>,改为-server。 - 配置mod_proxy HTTP转发
打开httpd.conf文件,在末尾添加:
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
NameVirtualHost *:80
<VirtualHost *:80>
ServerName www.cometserver.com
ErrorLog logs/cometserver-error_log
CustomLog logs/cometserver-access_log common
ProxyRequests Off
ProxyPass /comet/ http://localhost:8080/comet/ max=1500
ProxyPassReverse /comet/ http://localhost:8080/comet/
</VirtualHost>
这里将mod_proxy配置加入了一个虚拟主机里面,只在 www.cometserver.com虚拟主机中生效。ProxyPass转发所有客户端http请求,ProxyPassReverse转发所有服务器端redirect请求。 - 调整Apache Httpd最大连接数
在httpd.conf中加入或修改如下配置:
<IfModule prefork.c>
StartServers 8
MinSpareServers 5
MaxSpareServers 20
ServerLimit 1500
MaxClients 1500
MaxRequestsPerChild 4000
</IfModule>
以上配置设置最大连接数为1500 - 配置mod_proxy HTTPS转发
打开ssl.conf或自定义的mod_ssl配置文件,在</VirtualHost>之前添加:
SSLProxyEngine on
ProxyRequests Off
ProxyPass /comet/ http://localhost:8080/comet/ max=1500
ProxyPassReverse /comet/ http://localhost:8080/comet/
配置基本上相同,只是多了SSLProxyEngine on开启SSLProxy。 - web.xml配置
在web.xml中配置CometServlet:
<servlet>
<servlet-name>QuotationServlet</servlet-name>
<servlet-class>com.xued.fxtrader.comet.QuotationServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>QuotationServlet</servlet-name>
<url-pattern>/quote</url-pattern>
</servlet-mapping> - Comet Servlet中response Header
response.addHeader("Expires", "0");
response.addHeader("Pragma", "no-cache");
response.addHeader("Cache-Control", "no-cache");
response.addHeader("Transfer-Encoding", "Chunked");
经以上配置,即可实现通过访问80端口透明代理访问Comet Http streaming服务,响应无阻塞延迟。
2008年5月25日星期日
OpenMq与Spring 2.5配置
在之前的一篇Blog中(http://herculesx.blogspot.com/2007/12/activemqspring25.html),记述了ActiveMq与Spring2.5的结合使用配置,OpenMq基本上与此类似,但有一点不同,即ConnectionFactory的获取。OpenMq支持两种生成ConnectionFactory实例的方式,一种是JNDI方式查找,相对简单,本文主要介绍第二种方式,即参数配置的方式。
OpenMqConnectionFactory是一个对com.sun.messaging.ConnectionFactory的装饰类,用于Spring的Bean管理,代码如下:
import com.sun.messaging.ConnectionConfiguration;
import com.sun.messaging.ConnectionFactory;
import javax.jms.Connection;
import javax.jms.JMSException;
public class OpenMqConnectionFactory implements javax.jms.ConnectionFactory{
private ConnectionFactory connectionFactory;
public OpenMqConnectionFactory(String brokerAddress) throws JMSException {
connectionFactory = new ConnectionFactory();
connectionFactory.setProperty(ConnectionConfiguration.imqAddressList, brokerAddress);
}
public Connection createConnection() throws JMSException {
return connectionFactory.createConnection();
}
public Connection createConnection(String userName, String password) throws JMSException {
return connectionFactory.createConnection(userName, password);
}
}
主要是connectionFactory.setProperty(ConnectionConfiguration.imqAddressList, brokerAddress);用来设置远程OpenMq Server的地址和端口,格式为"host1:port,host2:port...",多个主机用逗号隔开。
spring配置文件:
<bean id="jmsFactory" class="com.xued.fxtrader.jms.OpenMqConnectionFactory" >
<constructor-arg type="java.lang.String" value="10.100.1.156:7676" />
</bean>
这样在spring的配置文件中就可以使用OpenMq的ConnectionFactory了。
OpenMqConnectionFactory是一个对com.sun.messaging.ConnectionFactory的装饰类,用于Spring的Bean管理,代码如下:
import com.sun.messaging.ConnectionConfiguration;
import com.sun.messaging.ConnectionFactory;
import javax.jms.Connection;
import javax.jms.JMSException;
public class OpenMqConnectionFactory implements javax.jms.ConnectionFactory{
private ConnectionFactory connectionFactory;
public OpenMqConnectionFactory(String brokerAddress) throws JMSException {
connectionFactory = new ConnectionFactory();
connectionFactory.setProperty(ConnectionConfiguration.imqAddressList, brokerAddress);
}
public Connection createConnection() throws JMSException {
return connectionFactory.createConnection();
}
public Connection createConnection(String userName, String password) throws JMSException {
return connectionFactory.createConnection(userName, password);
}
}
主要是connectionFactory.setProperty(ConnectionConfiguration.imqAddressList, brokerAddress);用来设置远程OpenMq Server的地址和端口,格式为"host1:port,host2:port...",多个主机用逗号隔开。
spring配置文件:
<bean id="jmsFactory" class="com.xued.fxtrader.jms.OpenMqConnectionFactory" >
<constructor-arg type="java.lang.String" value="10.100.1.156:7676" />
</bean>
这样在spring的配置文件中就可以使用OpenMq的ConnectionFactory了。
2008年5月5日星期一
使用JWebUnit测试JavaScript弹出Alert窗口
JWebUnit提供了三个方法用于测试javascript弹出的Alert,Confirm和Prompt窗口,分别是:
- setExpectedJavaScriptAlert
- setExpectedJavaScriptConfirm
- setExpectedJavaScriptPrompt
public class TomcatHomeTest {
private WebTester tester;
public TomcatHomeTest() {
}
@BeforeClass
public static void setUpClass() throws Exception {
}
@AfterClass
public static void tearDownClass() throws Exception {
}
@Before
public void setUp() {
tester = new WebTester();
tester.setTestingEngineKey(TestingEngineRegistry.TESTING_ENGINE_HTMLUNIT);
tester.getTestContext().setBaseUrl("http://10.100.1.156:8080/");
}
@After
public void tearDown() {
}
@Test
public void testHomePage(){
tester.beginAt("/index.jsp");
tester.assertTitleEquals("Apache Tomcat/6.0.16");
//Link Test
tester.clickLinkWithText("Change Log");
tester.assertTitleEquals("Apache Tomcat 6.0 - Changelog");
//Javascript Alert Test
tester.setExpectedJavaScriptAlert("foo");
tester.clickLinkWithText("Alert");
tester.closeBrowser();
}
}
closeBrowser()方法执行时会去检查是否弹出了名为foo的Alert窗口,Confirm和Prompt窗口与此类似。
private WebTester tester;
public TomcatHomeTest() {
}
@BeforeClass
public static void setUpClass() throws Exception {
}
@AfterClass
public static void tearDownClass() throws Exception {
}
@Before
public void setUp() {
tester = new WebTester();
tester.setTestingEngineKey(TestingEngineRegistry.TESTING_ENGINE_HTMLUNIT);
tester.getTestContext().setBaseUrl("http://10.100.1.156:8080/");
}
@After
public void tearDown() {
}
@Test
public void testHomePage(){
tester.beginAt("/index.jsp");
tester.assertTitleEquals("Apache Tomcat/6.0.16");
//Link Test
tester.clickLinkWithText("Change Log");
tester.assertTitleEquals("Apache Tomcat 6.0 - Changelog");
//Javascript Alert Test
tester.setExpectedJavaScriptAlert("foo");
tester.clickLinkWithText("Alert");
tester.closeBrowser();
}
}
使用NetBeans与Ant实现Web项目自动集成测试
NetBeans创建Web项目缺省是以Ant为基础进行构建,可以自动生成基本的编译,打包等Ant脚本,省去了手工编辑的繁琐,另外预留了一些Ant任务可供覆写和扩展,本文的目的是在NetBeans生成的Ant脚本基础上进行扩展,以实现一个完整的自动构建集成测试过程。
一、环境准备
一、环境准备
- NetBeans 6.1
- 测试所需的jar包:
asm-2.2.1.jar
asm-tree-2.2.1.jar
cobertura.jar
commons-codec-1.3.jar
commons-collections.jar
commons-httpclient-3.1.jar
commons-io-1.3.1.jar
commons-lang-2.3.jar
cssparser-0.9.4.jar
dom4j-1.6.1.jar
hamcrest-core-1.1.jar
hamcrest-library-1.1.jar
htmlunit-1.14.jar
jaxen-1.1.1.jar
jdom-1.0.jar
jmock-2.4.0.jar
js-1.6R7.jar
junit-3.8.2.jar
jwebunit-core-1.4.1.jar
jwebunit-htmlunit-plugin-1.4.1.jar
nekohtml-0.9.5.jar
pmd-4.2.1.jar
regexp-1.3.jar
servlet-api.jar
strutstest-2.1.4.jar
xercesImpl-2.6.2.jar
xml-apis-1.3.02.jar
xmlParserAPIs-2.6.2.jar
xom-1.0.jar - Ant 1.7.0,此外需要activation.jar,commons-net-1.4.1.jar,jakarta-oro-2.0.8.jar,mail.jar。将以上jar包拷贝到ANT_HOME/lib目录下,设置路径加入ANT_HOME/bin,还有环境变量ANT_OPTS=-Xms512m -Xmx1024m
- 一台远程部署服务器,安装Tomcat 6.0.16和Apache Httpd 2.2.8
- 在NetBeans中创建一个Web工程,使用strutsTest和JMock编写单元测试,JWebUnit编写端对端测试,编译打包通过。
- 远程部署
<property file="build.properties" />
<!-- 覆写-post-dist任务,加构建版本号和时间 -->
<target name="-post-dist" description="Record build information">
<propertyfile file="build.info">
<entry default="0001" key="build.number" operation="+" pattern="0000" type="int" />
<entry default="now" key="build.time" pattern="yyyy.MM.dd HH:mm:ss" type="date" />
</propertyfile>
<copy file="build.info" tofile="${dist.dir}/build.info" />
<ftp server="${ftp.server}"
remotedir="${ftp.remotedir}"
userid="${ftp.user}"
password="${ftp.password}"
depends="true"
binary="true"
verbose="true"
ignorenoncriticalerrors="true">
<fileset dir="${dist.dir}" />
</ftp>
</target>
<!-- 取消远程部署的旧版本应用 -->
<target name="remove-remote-app" >
<property name="status.file" location="deploy-${tomcat.server}.txt" />
<get src="${tomcat.manager.url}/remove?path=/${webapp.name}"
dest="${status.file}"
username="${tomcat.username}"
password="${tomcat.password}" />
<loadfile property="deploy.result" srcfile="${status.file}" />
<echo>${deploy.result}</echo>
</target>
<!-- 部署新版本 -->
<target name="deploy-remote-server" depends="clean,dist,remove-remote-app">
<property name="install.file" location="deploy-remote-install.txt" />
<property name="redist.url" value="file://${target.dir}" />
<property name="target.url" value="path=/${webapp.name}&war=${redist.url}" />
<get src="${tomcat.manager.url}/install?${target.url}"
dest="${install.file}"
username="${tomcat.username}"
password="${tomcat.password}" />
<loadfile property="deploy.remote.result" srcfile="${install.file}" />
<echo>${deploy.remote.result}</echo>
</target>
build.properties为新建的资源文件,内容如下:
reports.dir=build/test/results
reports.xml.dir=${reports.dir}
reports.html.dir=${reports.dir}/junit-html
coverage.xml.dir=${reports.dir}/cobertura-xml
coverage.html.dir=${reports.dir}/cobertura-html
pmd.html.dir=${reports.dir}/pmd-html
instrumented.dir=${reports.dir}/cobertura-inmt
smtp.hostname=10.100.1.16
smtp.hostport=25
mail.user=xxxx
mail.password=xxxx
smtp.from=xxxx@xxxx.cn
smtp.tolist=xxxx@xxxx.cn
ftp.server=10.100.1.156
ftp.remotedir=dist
ftp.reportdir.junit=/home/httpd/html/htdocs/hosptest/junitreport
ftp.reportdir.cobertura=/home/httpd/html/htdocs/hosptest/coberturareport
ftp.reportdir.pmd=/home/httpd/html/htdocs/hosptest/pmdreport
ftp.user=deployer
ftp.password=deployer
tomcat.server=10.100.1.156
tomcat.manager.url=http://${tomcat.server}:8080/manager
tomcat.username=system
tomcat.password=manager
webapp.name=hosp
target.dir=/home/deployer/dist/hosp.war
report.url=http://10.100.1.156/hosptest/ - 运行测试,生成JUnit单元测试报告,Cobertura测试覆盖率报告及PMD代码质量检查报告
<!-- 装入Cobertura和PMD插件 -->
<path id="cobertura_classpath">
<fileset dir="lib">
<include name="test/cobertura.jar" />
<include name="test/asm-2.2.1.jar" />
<include name="test/asm-tree-2.2.1.jar" />
<include name="jakarta-oro-2.0.8.jar" />
<include name="log4j-1.2.14.jar" />
</fileset>
</path>
<taskdef classpathref="cobertura_classpath" resource="tasks.properties"/>
<path id="pmd.classpath">
<fileset dir="lib">
<include name="test/pmd-4.2.1.jar" />
<include name="test/asm-2.2.1.jar" />
<include name="test/jaxen-1.1.1.jar" />
</fileset>
</path>
<taskdef name="pmd" classname="net.sourceforge.pmd.ant.PMDTask" classpathref="pmd.classpath"/>
<target name="instrument" depends="init,compile">
<delete file="cobertura.ser"/>
<delete dir="${instrumented.dir}" />
<cobertura-instrument todir="${instrumented.dir}">
<ignore regex="org.apache.log4j.*" />
<fileset dir="${build.classes.dir}">
<include name="**/*.class" />
<exclude name="**/*Test.class" />
</fileset>
</cobertura-instrument>
</target>
<target name="-pre-test-run" depends="init" if="have.tests">
<mkdir dir="${reports.dir}"/>
<mkdir dir="${instrumented.dir}" />
<mkdir dir="${reports.html.dir}" />
<mkdir dir="${coverage.xml.dir}" />
<mkdir dir="${coverage.html.dir}" />
<mkdir dir="${pmd.html.dir}" />
</target>
<target name="coverage-test" depends="init,-pre-test-run,compile-test">
<junit dir="${basedir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" jvm="${platform.java}" showoutput="true">
<classpath location="${instrumented.dir}" />
<classpath location="${build.test.classes.dir}" />
<classpath location="${build.classes.dir}" />
<classpath path="${javac.test.classpath}" />
<formatter type="xml" />
<test name="${testcase}" todir="${reports.xml.dir}" if="testcase" />
<batchtest todir="${reports.xml.dir}" unless="testcase">
<fileset dir="${test.src.dir}">
<include name="**/*Test.java" />
<exclude name="com/xued/hosp/jwebunit/*Test.java" />
</fileset>
<fileset dir="${test.src.dir}">
<include name="com/xued/hosp/jwebunit/*Test.java" />
</fileset>
</batchtest>
</junit>
<junitreport todir="${reports.xml.dir}">
<fileset dir="${reports.xml.dir}">
<include name="TEST-*.xml" />
</fileset>
<report format="frames" todir="${reports.html.dir}" />
</junitreport>
</target>
<target name="coverage-check">
<cobertura-check branchrate="34" totallinerate="100" />
</target>
<target name="coverage-report">
<cobertura-report srcdir="${src.dir}" destdir="${coverage.xml.dir}" format="xml" />
</target>
<target name="alternate-coverage-report">
<cobertura-report destdir="${coverage.html.dir}">
<fileset dir="${src.dir}">
<include name="**/*.java"/>
</fileset>
</cobertura-report>
</target>
<!-- 运行所有测试,包括JUnit单元测试和JWebUnit端对端测试,生成测试报告 -->
<target name="coverage" depends="instrument,coverage-test,coverage-report,alternate-coverage-report" />
<!-- 运行PMD,生成代码质量报告 -->
<target name="pmd">
<pmd shortFilenames="true" classpath="." rulesetfiles="conf/pmd.xml">
<formatter type="html" toFile="${pmd.html.dir}/index.html" linkPrefix="http://pmd.sourceforge.net/xref/"/>
<fileset dir="src/java">
<include name="**/*.java"/>
</fileset>
</pmd>
</target>
pmd.xml指定了代码检查的规则集:
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="Custom ruleset"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0 http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="http://pmd.sf.net/ruleset_xml_schema.xsd">
<rule ref="rulesets/basic.xml"/>
<rule ref="rulesets/braces.xml"/>
<rule ref="rulesets/controversial.xml">
<exclude name="OnlyOneReturn" />
<exclude name="DataflowAnomalyAnalysis" />
<exclude name="AvoidFinalLocalVariable" />
</rule>
<rule ref="rulesets/codesize.xml/ExcessiveMethodLength"/>
<rule ref="rulesets/codesize.xml/ExcessiveParameterList"/>
<rule ref="rulesets/codesize.xml/CyclomaticComplexity" />
<rule ref="rulesets/design.xml">
<exclude name="ConfusingTernary" />
<exclude name="UncommentedEmptyConstructor" />
</rule>
<rule ref="rulesets/imports.xml"/>
<rule ref="rulesets/naming.xml"/>
<rule ref="rulesets/optimizations.xml">
<exclude name="MethodArgumentCouldBeFinal" />
<exclude name="LocalVariableCouldBeFinal" />
<exclude name="UseStringBufferForStringAppends" />
</rule>
<rule ref="rulesets/strictexception.xml">
<exclude name="SignatureDeclareThrowsException" />
</rule>
<rule ref="rulesets/strings.xml"/>
<rule ref="rulesets/typeresolution.xml">
<exclude name="SignatureDeclareThrowsException" />
</rule>
<rule ref="rulesets/unusedcode.xml"/>
</ruleset> - 将测试报告上传,并且发送Email
<!-- 删除旧的远程文件 -->
<target name="clean-remote-report">
<ftp server="${ftp.server}"
remotedir="${ftp.reportdir.junit}"
userid="${ftp.user}"
password="${ftp.password}"
action="del"
depends="true"
binary="true"
verbose="true"
ignorenoncriticalerrors="true">
<fileset includes="**/*" />
</ftp>
<ftp server="${ftp.server}"
remotedir="${ftp.reportdir.cobertura}"
userid="${ftp.user}"
password="${ftp.password}"
action="del"
depends="true"
binary="true"
verbose="true"
ignorenoncriticalerrors="true">
<fileset includes="**/*" />
</ftp>
<ftp server="${ftp.server}"
remotedir="${ftp.reportdir.pmd}"
userid="${ftp.user}"
password="${ftp.password}"
action="del"
depends="true"
binary="true"
verbose="true"
ignorenoncriticalerrors="true">
<fileset includes="**/*" />
</ftp>
</target>
<!-- 上传新的远程文件 -->
<target name="ftp-report" depends="clean-remote-report">
<ftp server="${ftp.server}"
remotedir="${ftp.reportdir.junit}"
userid="${ftp.user}"
password="${ftp.password}"
depends="true"
binary="true"
verbose="true"
ignorenoncriticalerrors="true">
<fileset dir="${reports.html.dir}" />
</ftp>
<ftp server="${ftp.server}"
remotedir="${ftp.reportdir.cobertura}"
userid="${ftp.user}"
password="${ftp.password}"
depends="true"
binary="true"
verbose="true"
ignorenoncriticalerrors="true">
<fileset dir="${coverage.html.dir}" />
</ftp>
<ftp server="${ftp.server}"
remotedir="${ftp.reportdir.pmd}"
userid="${ftp.user}"
password="${ftp.password}"
depends="true"
binary="true"
verbose="true"
ignorenoncriticalerrors="true">
<fileset dir="${pmd.html.dir}" />
</ftp>
</target>
<!-- 发送Email -->
<target name="notifyteam" depends="ftp-report">
<property file="build.info" />
<mail mailhost="${smtp.hostname}"
mailport="${smtp.hostport}"
user="${mail.user}"
password="${mail.password}"
subject="Test build #${build.number}"
from="${smtp.from}" tolist="${smtp.tolist}" messagemimetype="text/html">
<message>The build #${build.number} is now available.Please browse ${report.url} to see reports</message>
</mail>
</target> - 将所有任务集成
<target name="intergrate" depends="deploy-remote-server,coverage,pmd,notifyteam"/>
- 在命令行下输入ant intergrate即可完成所有任务,将此命令定制成自动执行脚本,即可实现自动集成。
2008年4月8日星期二
使用jMock2模拟对象进行单元测试
一、目的
在OO设计中,经常会出现一个对象拥有另一个对象的情况,而内部对象的行为依赖于容器,不能方便的进行脱离容器的单元测试。为了解决这个问题,就需要将内部对象用其他的对象来代替,使用jMock2来创建模拟对象可以避免在生产代码中创建大量的模拟类(只为了测试而存在),还能保证仿造的接口对象行为的一致性和统一性。
二、需要的jar包
jmock-2.4.0.jar
hamcrest-library-1.1.jar
junit.jar 3.8.2以上,本例使用4.4版本
三、代码
在OO设计中,经常会出现一个对象拥有另一个对象的情况,而内部对象的行为依赖于容器,不能方便的进行脱离容器的单元测试。为了解决这个问题,就需要将内部对象用其他的对象来代替,使用jMock2来创建模拟对象可以避免在生产代码中创建大量的模拟类(只为了测试而存在),还能保证仿造的接口对象行为的一致性和统一性。
二、需要的jar包
jmock-2.4.0.jar
hamcrest-library-1.1.jar
junit.jar 3.8.2以上,本例使用4.4版本
三、代码
- 要测试的类
public class DAOService {
private AnnotationDAO dao;
public DAOService() {
}
public DAOService(AnnotationDAO dao) {
this.dao = dao;
}
public void updateEntity(EntityDTO dto) {
dao.updateEntity(dto);
}
public int getRecordCount() {
return dao.getRecordCount();
}
}
- 使用的接口
public interface AnnotationDAO {
public int getRecordCount();
public void updateEntity(EntityDTO dto);
}
- 单元测试
public class DAOServiceMockTest {
private Mockery mocker;
private AnnotationDAO dao; //mock对象
private DAOService service;
public DAOServiceMockTest() {
}
@Before
public void setUp() {
mocker = new Mockery();
dao = mocker.mock(AnnotationDAO.class); //创建mock对象,注意AnnotationDAO是一个接口
service = new DAOService(dao); //用创建的mock对象实例创建测试对象
}
@After
public void tearDown() {
}
@Test
public void testUpdateEntity() {
final EntityDTO dto = new EntityDTO("1", "Tom", "Sawyer");
mocker.checking(new Expectations() {{ //定义预期
one (dao).updateEntity(dto); //one表示此mock对象方法只执行一次,void类型无返回值,注意可以传入参数
}});
service.updateEntity(dto);
mocker.assertIsSatisfied(); //将执行结果与预期比较
}
@Test
public void testGetRecordCount(){
mocker.checking(new Expectations() {{
one (dao).getRecordCount();
will(returnValue(15)); //执行此方法将返回整数15,returnValue参数值是一个对象,如果返回集合或抛出异常使用其他方法
}});
assertEquals(15, service.getRecordCount());
mocker.assertIsSatisfied();
}
}
2008年3月20日星期四
使用Axis生成Web Service客户端
- 需要的jar包:
axis.jar
j2ee.jar
wsdl4j-1.5.1.jar
commons-logging-1.1.jar
commons-discovery-0.4.jar - 根据wsdl生成骨架类文件:
java.exe -Djava.ext.dirs=./lib -cp ./axis.jar;./commons-logging-1.1.jar;./commons-discovery-0.4.jar;./j2ee.jar;./wsdl4j-1.5.1.jar org.apache.axis.wsdl.WSDL2Java http://10.100.1.156:8080/clustered-ejb/ExBean?wsdl
执行后生成相应的骨架类:
ExBean.java
ExBeanBindingStub.java
ExBeanService.java
ExBeanServiceLocator.java - 客户端代码:
package com.xued.axis;
import com.taifook.exbean.ExBean;
import com.taifook.exbean.ExBeanService;
import com.taifook.exbean.ExBeanServiceLocator;
/**
*
* @author xued
*/
public class AxisClient {
public static void main(String[] args) throws Exception {
ExBeanService service = new ExBeanServiceLocator();
ExBean exBean = service.getExBeanPort();
System.out.println("ExBean Web Service Return: "+exBean.invoke("herculesx"));
}
}
2008年3月18日星期二
Oracle中的索引
一、索引的分类
创建索引后分析要索引才能起作用:
要看索引是否被使用我们要借助Oracle的一个叫做AUTOTRACE功能,它显示了sql语句的执行路径,我们能看到Oracle内部是怎么执行sql的。
SQL> set autotrace on
SQL> select * from test;
三、索引使用注意事项
- B*Tree索引
B*Tree索引是最常见的索引结构,默认建立的索引就是这种类型的索引。B*Tree索引在检索高基数数据列(高基数数据列是指该列有很多不同的值)时 提供了最好的性能。当取出的行数占总行数比例较小时B-Tree索引比全表检索提供了更有效的方法。但当检查的范围超过表的10%时就不能提高取回数据的 性能。
create index index_name on table_name(field_name,...) - 反向索引
反向索引是B*Tree索引的一个分支,它的设计是为了运用在某些特定的环境下的。Oracle推出它的主要目的就是为了降低在并行服务器(Oracle Parallel Server)环境下索引叶块的争用。当B*Tree索引中有一列是由递增的序列号产生的话,那么这些索引信息基本上分布在同一个叶块,当用户修改或访问 相似的列时,索引块很容易产生争用。反向索引中的索引码将会被分布到各个索引块中,减少了争用。反向索引反转了索引码中每列的字节。
create index index_name on table_name(field_name,...) reverse - 降序索引
降序索引是B*Tree的另一个衍生物,它的变化就是列在索引中的储存方式从升序变成了降序,在某些场合下降序索引将会起作用,例如在查询条件中有order by语句。
create index index_name on table_name(field_name1 desc, field_name2 asc, ...) - 位图索引
位图索引主要用于决策支持系统或静态数据,不支持行级锁定。位图索引最好用于低基数列(即列的唯一值除以行数为一个很小的值,接近 零),例如一个"性别"列,列值有"Male","Female","Null"等3种,但一共有300万条记录,那么3/3000000约等于0,这 种情况下最适合用位图索引。
create bitmap index index_name on table_name(field_name,...) - 函数索引
基于函数的索引有索引计算列的能力,它易于使用并且提供计算好的值,在不修改应用程序的逻辑上提高了查询性能,适用于查询条件中包含函数运算项的情况。
create index index_name on table_name(UPPER(field_name))
创建索引后分析要索引才能起作用:
analyze table table_name compute statistics for all indexes;
或数据量大时使用 analyze table table_name estimate statistics sample 5 percent;
重建索引:
alter index idx_name rebuild nologging;
要看索引是否被使用我们要借助Oracle的一个叫做AUTOTRACE功能,它显示了sql语句的执行路径,我们能看到Oracle内部是怎么执行sql的。
- 由于AUTOTRACE自动为用户指定了Execution Plan,因此该用户使用AUTOTRACE前必须已经建立了PLAN_TABLE。如果没有的话,请运行utlxplan.sql脚本(它在$ORACLE_HOME/rdbms/admin目录中)。
- AUTOTRACE可以通过运行plustrce.sql脚本(它在$ORACLE_HOME/sqlplus/admin目录中)来设置,用sys用户登陆然后运行plustrce.sql后会建立一个PLUSTRACE角色,然后给相关用户授予PLUSTRACE角色,然后这些用户就可以使用AUTOTRACE功能了。
- 普通用户需要授予SELECT_CATALOG_ROLE权限使用autotrace。
SQL> GRANT SELECT_CATALOG_ROLE TO USER
- AUTOTRACE的默认使用方法是set autotrace on,但是这方法不总是适合各种场合,特别当返回行数很多的时候。Set autotrace traceonly提供了只查看统计信息而不查询数据的功能。
SQL> set autotrace on
SQL> select * from test;
三、索引使用注意事项
- 表未做statistics, 或者 statistics 陈旧,导致Oracle判断失误,未使用索引。
- 索引不是越多越好。特别是大量索引即会降低性能,而且在一个sql 中,Oracle几乎不用的索引,对系统只有损害。一般一个表不要超过 5个索引。
- 很多时候,单列索引不如复合索引有效率。但是查询条件中只有在使用到索引的前导索引时才可以使用组合索引
- 用于多表连结的字段,加上索引会很有作用。
- 使用外连接查询时,主表无法使用索引,因此外连接的效率要比内连接低得多。
- where 子句中的字段,不应该参与任何形式的计算,才会使用索引。
- 低基数值的列要建位图索引才有效果,如果是组合索引,则不应包含低基数值的列。
- 在建表时,把需要索引的列设成NOT NULL。如果被索引的列在某些行中存在NULL值,就不会使用这个索引(除非索引是一个位图索引)。
- 如果不使用基于函数的索引,那么在SQL语句的WHERE子句中对存在索引的列使用函数时,会使优化器忽略掉这些索引。
- 查询条件中使用不等于操作符(<>、!=)的字段不会使用索引。通过把不等于操作符改成OR条件,就可以使用索引,以避免全表扫描。
- 屏蔽索引,语句的执行计划中有不良索引时,可以人为地屏蔽该索引,方法:
数值型:在索引字段上加0,例如 select * from emp where emp_no+0 = v_emp_no;
字符型:在索引字段上加'',例如 select * from tg_cdr01 where msisdn''=v_msisdn;
2008年2月28日星期四
C3P0连接池配置选项
以spring中配置为例:
<bean id="cs-dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
<property name="jdbcUrl" value="jdbc://" />
<property name="user" value="username" />
<property name="password" value="password" />
<property name="minPoolSize" value="2" />
<property name="maxPoolSize" value="10" />
<property name="preferredTestQuery" value="SELECT SYSDATE FROM DUAL" /> <!-- 测试数据库连接SQL语句-->
<property name="breakAfterAcquireFailure" value="true" /> <!-- 取得连接失败后返回,非阻塞,自动重连所需参数 -->
<property name="connectionTesterClassName" value="com.mchange.v2.c3p0.impl.DefaultConnectionTester" /> <!-- 数据库连接测试类 -->
<property name="idleConnectionTestPeriod" value="1800" /> <!-- 空闲连接测试间隔,秒为单位-->
<property name="acquireIncrement" value="5" />
<property name="maxIdleTime" value="3600" /> <!-- 最长空闲时间 -->
<property name="maxIdleTimeExcessConnections" value="600" />
<property name="initialPoolSize" value="2" />
<property name="acquireRetryAttempts" value="0" /> <!-- 自动重连尝试次数,小于等于0表示无限制 -->
<property name="acquireRetryDelay" value="30000" /> <!-- 自动重连间隔时间,毫秒为单位 -->
<property name="testConnectionOnCheckout" value="false" />
<property name="testConnectionOnCheckin" value="true" /> <!--连接检出时进行测试 -->
<property name="checkoutTimeout" value="5000" />
</bean>
<bean id="cs-dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver" />
<property name="jdbcUrl" value="jdbc://" />
<property name="user" value="username" />
<property name="password" value="password" />
<property name="minPoolSize" value="2" />
<property name="maxPoolSize" value="10" />
<property name="preferredTestQuery" value="SELECT SYSDATE FROM DUAL" /> <!-- 测试数据库连接SQL语句-->
<property name="breakAfterAcquireFailure" value="true" /> <!-- 取得连接失败后返回,非阻塞,自动重连所需参数 -->
<property name="connectionTesterClassName" value="com.mchange.v2.c3p0.impl.DefaultConnectionTester" /> <!-- 数据库连接测试类 -->
<property name="idleConnectionTestPeriod" value="1800" /> <!-- 空闲连接测试间隔,秒为单位-->
<property name="acquireIncrement" value="5" />
<property name="maxIdleTime" value="3600" /> <!-- 最长空闲时间 -->
<property name="maxIdleTimeExcessConnections" value="600" />
<property name="initialPoolSize" value="2" />
<property name="acquireRetryAttempts" value="0" /> <!-- 自动重连尝试次数,小于等于0表示无限制 -->
<property name="acquireRetryDelay" value="30000" /> <!-- 自动重连间隔时间,毫秒为单位 -->
<property name="testConnectionOnCheckout" value="false" />
<property name="testConnectionOnCheckin" value="true" /> <!--连接检出时进行测试 -->
<property name="checkoutTimeout" value="5000" />
</bean>
订阅:
评论 (Atom)