Friday, October 10, 2008

MIDlet 在索爱手机上安装失败

最近在工作中,发现一部分索爱 (SonyEricsson) 手机,在安装 MIDlet 时发生“Downloading failed”错误。具体情况发生在手机通过 OTA 安装 MIDlet,Jar 文件下载完毕并开始安装后,系统询问是否允许应用程序自动启动,以及是否允许应用程序收发短信,接下来就出现“Downloading failed”。

在出现此问题时,使用的是 Orange France 的 SIM 卡,考虑到 GPRS 漫游可能会带来的问题,我们改用中国移动的 SIM 卡,则安装正常。看来和 SIM 卡有关系。但同时,在索爱手机上无法安装的 Orange France 的 SIM 卡,在其它一些索爱手机以及所有 NOKIA 手机上都能正确安装,所以并不是由 SIM 卡本身 GPRS 漫游引起的。

几个星期以来,一直被这个问题困扰,百思不得其解。今天修改一个关于 PushRegistry 的 bug,突然想起来可能和 PushRegistry 有关,又重新 Google 了一番,发现了索爱官方论坛上的这个帖子:

https://developer.sonyericsson.com/thread/17306

帖子的作者遇到和我们完全一致的问题,而最终的解决办法,由索爱的技术人员给出了答案。

这个无法安装的错误,确实和 PushRegistry 有关系。如果把 JAD 中“MIDlet-Push-*”的属性去掉,则可以正常安装。但导致出问题的最终原因,是手机短信选项设置中存在没有值得空选项,如“Service center”、“Message type”、“Validity period”。SIM 卡通常都会为这些选项自动设置,但如果某一个选项没有设置,成为空值,则会导致上述情况。

我仔细查看了我们有问题的手机,所有手机上的“Validity period”都为空。设置成“Network maximum”后,MIDlet 安装和 PushRegistry 注册就完全正常了。

另外,我们发现有问题的手机包括 K700、K500、W800、S700、K600 等,多为 JP-3、JP-5 平台的手机。

Labels: , , ,

Monday, September 01, 2008

用 Cobertura 测量测试覆盖率

单元测试相信大家都不陌生,测试先行编程(Test-First Programming)和测试驱动也不是什么新概念了。测试改进了代码质量,但这也只是针对实际测试到的那部分代码而言的。您需要有一个工具告诉您程序的哪些部分没有测试到,这样就可以针对这些部分编写测试代码并找出更多 bug。

现在,有 Cobertura (cobertura 在西班牙语是“覆盖”的意思)这个免费的 GPL 工具在 Java 领域来完成这个任务。Cobertura 通过在执行测试时,用额外的语句记录哪些行被测试到、哪些行没有被测试到,通过这种方式来度量字节码,以便对测试进行监视。然后它生成一个 HTML 或者 XML 格式的报告,指出代码中的哪些包、哪些类、哪些方法和哪些行没有测试到。可以针对这些特定的区域编写更多的测试代码,以发现所有隐藏的 bug。

先来看看生成报告的样子吧:

Cobertura 报告示例(点击放大)


可以看到报告列出了每个包的行覆盖率、分支覆盖率以及复杂度。点击进去,可以看到针对这个包里头每个类的报告,进而每行代码的测试情况,以及被测试到的次数。这里是 Cobertura 官方网站上给出的一个报告的 Sample。

在使用上,Cobertura 支持命令行操作,也可以结合到 Ant 任务。当然首先要定义任务:
<property name="cobertura.dir" value="C:/javastuff/cobertura" />

<path id="cobertura.classpath">
<fileset dir="${cobertura.dir}">
<include name="cobertura.jar" />
<include name="lib/**/*.jar" />
</fileset>
</path>

<taskdef classpathref="cobertura.classpath" resource="tasks.properties" />

然后,就可以用 cobertura-instrument 任务来统计测试目标:
<delete file="cobertura.ser" />

<cobertura-instrument todir="${instrumented.dir}">
<ignore regex="org.apache.log4j.*" />
<fileset dir="${classes.dir}">
<include name="**/*.class" />
<exclude name="**/*Test.class" />
</fileset>
<fileset dir="${guiclasses.dir}">
<include name="**/*.class" />
<exclude name="**/*Test.class" />
</fileset>
<fileset dir="${jars.dir}">
<include name="my-simple-plugin.jar" />
</fileset>
</cobertura-instrument>

之后,运行 Junit 测试(重要的部分用蓝色标出):
<junit fork="yes" dir="${basedir}" failureProperty="test.failed">
<!--
指定覆盖率统计的数据文件名,以下为默认值
-->
<sysproperty key="net.sourceforge.cobertura.datafile"
file="${basedir}/cobertura.ser" />


<!--
注意 classpath 的顺序:被处理过的 class 路径在为处理的
class 路径之前。这很重要!
-->
<classpath location="${instrumented.dir}" />
<classpath location="${classes.dir}" />

<!--
被处理过的 class 会在 Cobertura 运行时引用 Cobertura
的类库,因此这些类也必须在我们的 classpath 中。
-->
<classpath refid="cobertura_classpath" />

<formatter type="xml" />
<test name="${testcase}" todir="${reports.xml.dir}" if="testcase" />
<batchtest todir="${reports.xml.dir}" unless="testcase">
<fileset dir="${src.dir}">
<include name="**/*Test.java" />
</fileset>
</batchtest>
</junit>

最后,我们可以用 cobertura-report 任务来输出 HTML 格式或者 XML 格式的报告:
<cobertura-report format="html" destdir="${coveragereport.dir}"
srcdir="${src.dir}" />

也可以有更复杂的要求:
<cobertura-report format="html" destdir="${coveragereport.dir}" >
<fileset dir="${src.dir}">
<include name="**/*.java" />
<exclude name="**/*Stub.java" />
</fileset>
<fileset dir="${guisrc.dir}">
<include name="**/*.java" />
<exclude name="**/*RB.java" />
</fileset>
</cobertura-report>

另外,还可以用 cobertura-check 任务来检查统计出来的覆盖率是否达到预先的期望,如果没有达到则使得整个 Ant 任务失败;以及 cobertura-merge 任务用来合并多个测试的统计数据文件。

要特别注意的是,在这个 Ant 任务中,一定要保证数据文件 cobertura.ser 每次都被更新,并且在 instrument 的任务和后面做 Junit 的过程中,两者的路径一致,否则可能导致在输出的报告中,所有的测试结果都是 100% 或者 0%。

参考资料

Labels: , ,

Tuesday, February 13, 2007

MySQL 的自动断开连接问题

最近发现 MySQL 会自动断开 IDLE 时间超过 8 小时的数据库连接,从而导致一些基于数据库连接的应用程序,特别是 WEB 应用程序出错。

有两个办法可以解决这个问题:

第一个办法是修改 MySQL 的配置参数。这个参数的名称是 wait_timeout,其默认值为 28800(单位秒),刚好就是 8 小时。其意义为关闭一个连接之前在这个连接上等到行动的秒数,也就是说,如果一个连接闲置超过这个选项所设置的秒数,MySQL 会主动断开这个连接。

有实践表明,没有办法把这个值设置成无限大,即永久。因此如果你无法保证你的应用程序必定在设定的秒数内至少有一次操作,那么最好用第二个方法解决这个问题。

第二个办法是如下修改 JDBC 连接的 URL:
jdbc:mysql://hostaddress:3306/schemaname?autoReconnect=true

添加 autoReconnect=true 这个参数,即能解决这个问题。

另外,对于 Java 的应用程序,据说第三方的数据库连接池应用 Proxool 能够对断开的数据库连接发起自动重连,不过我没有用过,有兴趣的朋友可以尝试一下。

Labels: , , ,

Monday, November 13, 2006

升级后如何才能方便的发布评论?

升级到 Blogger Beta后,由于大家无法访问 beta.blogger.com 域名,因此出现了无法发布评论的问题,在上次的帖子中有所讨论。

为了解决这个问题,我自己写了个简单的 Servlet,来接受来自页面上的评论发布,并将这个请求转到 Blogger Beta 上处理评论发布的地址 https://beta.blogger.com/comment.do,因为我可以修改服务器上的 host 文件使其可以访问 beta.blogger.com,这样可以起到一个类似代理的作用,代替需要发布评论的朋友们访问这个不能直接访问的域名。

理论上是没有问题的,实践上我也几乎取得了成功。出现的问题是:评论被正常发布后,和评论相关的帖子页面没有重建!

我们知道,用 FTP 的方式发布 Blogger,任何对帖子的更改,都会导致 Blogger 自动重建页面并自动发布到 FTP 相关的目录下,以使得 blog 网站上的页面能够反应出最近的信息,这些操作包括新增/修改/删除帖子,以及添加/删除评论。正常情况下,一旦有评论发布,这个重建的过程就会发生,这样评论能够在最多几分钟的时间内出现在页面上。

可是现在,评论的内容都出现在 Blogger Beta 自己的评论页面上了,但是却没有触发页面重建。不过,如果直接把页面上评论表单的内容提交到 https://beta.blogger.com/comment.do,则一切正确;但是当然这样做没有意义,因为这要求发布评论的朋友能够直接访问 beta.blogger.com 域名。

现在出现的现象会是,大家发布的评论不会立即显示在帖子页面上,但是实际上是已经被正确的保存了。我看到电子邮件的提示后,就会尽快重建页面使得这些评论能够被显示出来。相信这个问题只是暂时的,总归是应该得到解决的。

这个问题琢磨了一晚上也没有得到结果,Google 上也搜不出什么有用的东西来。已经把这个问题发布到了 Google 上的 Blogger Data API 讨论组求助,希望有人遇到过类似的问题从而给我一些提示。

Updated on 2006/11/17:
有一个更科学的方法来提交评论,那就是用 GData API 向单贴评论的 Feed 地址提交,不过仍然有些问题。详见“通过 GData API 提交评论”。

Updated on 2006/12/21:
Blogger 的某次更新似乎已经解决了评论提交后页面不会重建的问题。现在在页内的评论表单中提交评论,页面会被立即重建,只是由于需要重建的页面和以往的老 Blogger 比起来要多一些,因此会慢一点。一般 5 分钟之内评论就会出现在页面上了。

Labels: , , , , ,

Thursday, November 17, 2005

Tomcat 5.5 无法编译 Java 1.5 语法 JSP

Apache Jakarta Tomcat 5.5.x (笔者在 5.5.9 和 5.5.12 下遇到同样问题) 在安装过后,用默认配置,无法自动编译带有 Java 1.5 语法的 JSP,经过多方求证,问题是由其自带的 eclipse 编译器造成的。文档 http://jakarta.apache.org/tomcat/tomcat-5.5-doc/jasper-howto.html 中说:
The Java compiler from Eclipse JDT in included as the default compiler. It is an advanced Java compiler which will load all dependencies from the Tomcat class loader, which will help tremendously when compiling on large installations with tens of JARs. On fast servers, this will allow sub-second recompilation cycles for even large JSP pages. This new compiler will be updated to support the Java 5 syntax as soon as possible.

Apache Ant, which was used in previous Tomcat releases, can be used instead instead of the new compiler by simply removing the common/lib/jasper-compiler-jdt.jar file, and placing the ant.jar file from the latest Ant distribution in the common/lib folder. If you do this, you also need to use the "javac" argument to catalina.sh.

由此可见,这个编译器 common/lib/jasper-compiler-jdt.jar 是不支持 Java 5 语法的,而 Apache Ant 工程中的编译器可以替代它解决这个问题,具体方法如下:
  • 将/common/lib/common/lib/jasper-compiler-jdt.jar 删除
  • 将 ant-1.6.x 发布的 ant.jar 拷贝到 /common/lib/
  • 修改 /conf/web.xml 文件,找到并修改如下内容,增加 compilerSourceVM 和 compilerTargetVM 两个设置:
<servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>

<init-param>
<param-name>compilerSourceVM</param-name>
<param-value>1.5</param-value>
</init-param>
<init-param>
<param-name>compilerTargetVM</param-name>
<param-value>1.5</param-value>
</init-param>

<init-param>
<param-name>fork</param-name>
<param-value>false</param-value>
</init-param>
<init-param>
<param-name>xpoweredBy</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>

Labels: , , , ,