托朋友从米国终于带回了Kindle 2,加上皮套一共才2000 RMB,比国内各大品牌的阅读器都要便宜很多,而且可以直接免费使用3G网络,自带的浏览器能直接浏览网站(估计是联通WCDMA信号?确实不知道为啥能免费使用)。

不过Kindle 2仍然不支持中文,需要经过hack才能支持。在国内搜索了半天,发现中文支持最后都是从Blog Kindle这个国外牛人搞出来的升级包,在Unicode Font Hack一文里有详细的升级步骤和所有升级包下载,大概读了一下,基本步骤如下:

1.首先,根据Kindle 2的型号(从Kindle机器后面的序列号看,Kindle 2是B002开头,Kindle 2国际版是B003开头,Kindle 2 DX是B004开通,Kindle 2 DX国际版是B005开头)下载对应的升级包,作者已经做了好几种字体的升级包,对于中文用户,如果你不想自己从Windows上搞微软雅黑字体,推荐直接使用Droid字体的升级包,这是Android手机默认的中文字体,显示效果很不错,完全够用。例如,Kindle 2国际版的升级包就是:

http://blogkindle.com/wp-content/uploads/2009/11/update_ufh_droid_install-k2i.bin

2.把下载的update_uth_xxx.bin通过USB复制到Kindle的根目录,然后依次按Home,Menu,Settings,Menu,最后选择“Update Your Kindle”开始升级。

3.升级过程大概一分钟,然后Kindle会自动重启,重启后,复制几本中文PDF或TXT进去,能正常显示中文就大功告成!

升级注意要点:确保下载的bin文件和你的Kindle型号对应,升级前确保电池有足够的电量,升级过程中不要做任何操作。

用Kindle看电子书效果非常出色,和普通LCD屏幕相比更有纸质书的感觉。Kindle 2最新版本已经能直接支持PDF格式,不过,由于Kindle 2的屏幕还较小(Kindle 2 DX好点),而且不支持PDF重排和字体大小调整,而一般的PDF都是按照A4纸的尺寸排版的,即使是横屏看PDF也觉得字体太小,而中文的TXT格式我发现Kindle会有乱码和不能翻页的问题,估计是一些特殊字符造成的。Kindle支持的最佳格式是AZW,也就是Amazon自家的电子书格式,不但字体可缩放,而且翻页速度极快,因此,在Kindle上看电子书最好的办法就是把PDF和TXT等格式转换为AZW格式,如何批量转换?下次再继续讨论。

阅读全文 »

在使用Subversion进行版本管理时,有时候需要将其他资源库的一部分链接到我们自己资源库中,例如,本地已经存在的Project已经被Subversion管理了,但是,需要引入gdata等第三方库,当然可以直接将第三方发布的lib放到本地,但是,无法做到实时更新。由于Subversion支持external形式的外链,我们就可以将外部库的一部分当作本地Project的一部分。

以gdata为例,我们需要在本地的src目录下引入gdata的两个目录:

  • atom:http://gdata-python-client.googlecode.com/svn/trunk/src/atom
  • gdata:http://gdata-python-client.googlecode.com/svn/trunk/src/gdata

则需要通过svn命令实现。首先切换到src所在目录的父目录,新建文本文件externals.txt,内容如下:

atom http://gdata-python-client.googlecode.com/svn/trunk/src/atom
gdata http://gdata-python-client.googlecode.com/svn/trunk/src/gdata

给src目录加上svn:externals属性,使用以下命令:

C:\projects\my_project> svn propset svn:externals -F externals.txt src

-F参数告诉SVN属性"svn:externals"的内容从文件externals.txt中读取,然后,对src目录做update操作,就会看到类似如下的输出:

Updating external location at: C:/projects/my_project/src/atom
  ...
Updating external location at: C:/projects/my_project/src/gdata
  ...

最后,在src目录下,可以看到,自动创建了引入的两个外部资源库的目录:atom和gdata。

阅读全文 »

本文最早发表于IBM developerWorks:

http://www.ibm.com/developerworks/cn/java/j-lo-restmvc/

传统的JavaEE MVC框架如Struts等都是基于Action设计的后缀式映射,然而,流行的Web趋势是REST风格的架构。尽管使用Filter或者Apache mod_rewrite能够通过URL重写实现REST风格的URL,为什么不直接设计一个全新的REST风格的MVC框架呢?本文将讲述如何从头设计一个基于REST风格的Java MVC框架,配合Annotation,最大限度地简化Web应用的开发,您甚至编写一行代码就可以实现“Hello, world”。

Java开发者对MVC框架一定不陌生,从Struts到WebWork,Java MVC框架层出不穷。我们已经习惯了处理*.do或*.action风格的URL,为每一个URL编写一个控制器,并继承一个Action或者Controller接口。然而,流行的Web趋势是使用更加简单,对用户和搜索引擎更加友好的REST风格的URL。例如,来自豆瓣的一本书的链接是http://www.douban.com/subject/2129650/,而非http://www.douban.com/subject.do?id=2129650。

有经验的 Java Web 开发人员会使用 URL 重写的方式来实现类似的URL,例如,为前端Apache服务器配置mod_rewrite模块,并依次为每个需要实现URL重写的地址编写负责转换的正则表达式,或者,通过一个自定义的 RewriteFilter,使用Java Web服务器提供的Filter和请求转发(Forward)功能实现URL重写,不过,仍需要为每个地址编写正则表达式。

既然URL重写如此繁琐,为何不直接设计一个原生支持REST风格的MVC框架呢?

要设计并实现这样一个MVC框架并不困难,下面,我们从零开始,仔细研究如何实现REST风格的URL映射,并与常见的IoC容器如Spring框架集成。这个全新的MVC框架暂命名为 WebWind。

术语

MVC:Model-View-Controller,是一种常见的UI架构模式,通过分离Model(模型)、View(视图)和Controller(控制器),可以更容易实现易于扩展的UI。在Web应用程序中,Model 指后台返回的数据;View指需要渲染的页面,通常是JSP或者其他模板页面,渲染后的结果通常是HTML;Controller 指 Web 开发人员编写的处理不同URL的控制器(在Struts中被称之为 Action),而 MVC 框架本身还有一个前置控制器,用于接收所有的 URL 请求,并根据 URL 地址分发到 Web 开发人员编写的Controller中。 IoC:Invertion-of-Control,控制反转,是目前流行的管理所有组件生命周期和复杂依赖关系的容器,例如 Spring 容器。

Template:模板,通过渲染,模板中的变量将被Model的实际数据所替换,然后,生成的内容即是用户在浏览器中看到的 HTML。模板也能实现判断、循环等简单逻辑。本质上,JSP页面也是一种模板。此外,还有许多第三方模板引擎,如Velocity,FreeMarker等。

设计目标

和传统的Struts等MVC框架完全不同,为了支持REST风格的URL,我们并不把一个URL映射到一个Controller类(或者Struts的Action),而是直接把一个URL映射到一个方法,这样,Web开发人员就可以将多个功能类似的方法放到一个Controller中,并且,Controller没有强制要求必须实现某个接口。一个Controller通常拥有多个方法,每个方法负责处理一个URL。例如,一个管理Blog的Controller 定义起来就像清单1所示。

阅读全文 »

现在,Google Chrome浏览器已经有了unstable的Linux版本,而且是deb格式的!

dev.chromium.org/getting-involved/dev-channel这里下载32位或64位版本,我选择的是32位:

http://www.google.com/chrome/intl/en/eula_dev.html?dl=unstable_i386_deb

用dpkg -i xxx.deb安装,然后就可以直接启动google-chrome了:

显示的版本号是4.0.x:

阅读全文 »

最近才知道原来Eclipse还可以自己卸载已经安装的插件,方法是点击菜单“Help”,“Install New Software...”,在弹出的对话框中选择那个非常隐蔽的“already installed”链接:

然后就显示已经安装的插件:

现在就可以选择要卸载的插件,然后点“Uninstall...”把它卸载掉。

这个方法对Eclipse Galileo (3.5)有效,其他版本你需要自己试一下。

阅读全文 »

在JavaEE应用中,使用ORM操作数据库虽然简单快捷(参考“高效使用JavaEE ORM”),但是毕竟是对JDBC的封装,很多时候,ORM还是不能满足我们的需求,主要是两个问题:

1. 速度不如JDBC,毕竟是封装JDBC,有额外的开销;

2. ORM提供的xQL很多时候无法满足需求,还需要数据库相关的SQL,这时,必须使用JDBC。

使用JDBC虽然麻烦点,但是,按照软件设计的思想,一步一步封装必要的代码,还是可以做到性能与开发效率并存。

首先,要坚决避免的就是不断重复编写try... catch... finally...。对于查询、更新、插入和删除操作,每一种操作只允许编写一次try... catch... finally...。如何实现?有两个方法。

第一个办法是找一个现成的封装了这些JDBC操作的框架,最好的方案当然就是Spring的JDBC框架了,顺便可以参考JdbcTemplate的源码,以便提升自己的JavaEE功力。

如果不使用Spring,那就采用第二个办法,自己造轮子,封装一个JDBC框架。

很多人反对自己造轮子,原因不外乎费事。不过很多时候,造轮子并不麻烦,而且可以满足特定的需求。今天要造的轮子就是一个封装JDBC的框架:Express-Persist

Express-Persist是ExpressMe的持久化子项目,目标是封装JDBC并提供简单的数据库操作接口。

为什么不使用Spring JDBC呢?主要原因只有一个:Spring的JDBC目前还是1.4兼容的,不支持1.5的泛型。Express-Persist要提供的接口除了基本的数据库操作外,还要实现:

1. 简单的ORM映射,注意是简单的,没有Hibernate那样完整而强悍,本质上就是把ResultSet的每一条记录变成一个JavaBean,可以参考Spring JDBC的RowMapper,实现非常容易;

2. 充分利用Java 5泛型支持,都是类型安全的参数和返回值,不用做强制转化;

3. 利用Java 5的注解(Annotation)把SQL标记在接口方法上,比如:

@Query("select * from User where u.id=:id")
public User get(String id);

4. 最后,最重要的,只编写接口,没有实现类!

没有实现类,那JDBC代码写在哪?当然由Express-Persist框架自动生成了。如何自动生成?运行一个命令自动生成Java类?在“高效使用JavaEE ORM”一文中我们已经对JDO的这种静态增强方式表示了强烈的鄙视和唾弃,因此绝不可重蹈覆辙。Express-Persist会在启动时根据接口动态创建出类,不过我们不采用Hibernate使用的CGLIB库,而是直接通过JDK的动态代理功能实现动态类。

如何绑定SQL参数

DAO接口的方法参数要自动绑定到SQL参数中,由于方法参数的顺序与SQL参数的顺序可能不一致,因此,只能使用命名参数来绑定,即:SQL参数定义为:xxx,对应的方法参数用@Param("xxx")标记。

当SQL参数很多的时候(尤其是INSERT语句),方法参数也非常多,调用起来非常不方便,比如:

@Update("insert into User values(:id, :emai

阅读全文 »

虽然Java领域有无数的ORM框架,如HibernateiBatis,TopLink,JDO,JPA……但是这些ORM框架基本上大同小异。很多初学者对JDBC的复杂性望而却步,就简单认为使用ORM就会省时省力,结果恰恰相反,任何好的框架都是给专家准备的,任何急功近利试图偷懒的方法往往适得其反。要正确使用ORM还真不是一件简单的事情。本文仅简单整理一下ORM的原理,基本用法,以及如何避免各种陷阱的基本编程原则。

ORM的原理

先说ORM的实现原理。其实,要实现JavaBean的属性到数据库表的字段的映射,任何ORM框架不外乎是读某个配置文件把JavaBean的属性和数据库表的字段自动关联起来,当从数据库Query时,自动把字段的值塞进JavaBean的对应属性里,当做INSERT或UPDATE时,自动把JavaBean的属性值绑定到SQL语句中。但是,几乎所有的ORM都提供“按需读取”的功能,比如一个User有id,name,email和address这4个字段,但是address字段很少用,于是ORM只读取前3个字段,直到调用User的getAddress()方法时,才去数据库中读取address的值。这个功能显然不能通过User的get/set完成,因此,ORM需要采用某种方式生成一个User类的子类,并且覆写get/set方法,这样,才能在调用get方法时有机会从数据库中读取。类似的对User的修改检测也是这样实现的。

两种增强的方式

ORM为我们自己的JavaBean实现子类的方法很多,这个过程简单称之为“增强”,基本上有两种方法:Hibernate使用CGLIB在加载我们的User类时动态创建了一个子类,而JDO则要求编译完User类后再利用它提供的工具对User类进行改造,以便实现JDO需要的各种接口。请注意:就是这种极其变态的设计导致了使用JDO的极大困难,在我们编译完源码后,还需要额外执行一个增强命令,或者额外编写Ant任务,极大地影响了快速开发和单元测试,所以,凡是采用静态生成持久类的ORM,要在第一时间直接排除,切记!

理解持久和非持久状态

所有的ORM框架都有持久和非持久的概念。简单地说,当我们new一个User实例时,它是非持久对象,当我们调用ORM的save()之类的方法后,这个实例就变成持久对象了。当我们通过ORM从数据库读取到一个User对象时,这个对象是持久对象,当关闭当前的事务后,这个对象变成非持久对象。

虽然这个过程很容易理解,但是,难点在于当我们设计一个方法时,我们必须准确地知道当前操作的对象是持久对象还是非持久对象,否则,各种灵异事件会接踵而来,比如插入了重复记录等等。举例说明,当我们需要创建一个User对象时,save(User)方法必须传入非持久对象,当我们需要更新一个User对象时,update(User)方法必须传入一个持久对象,有些ORM比如Hibernate,为了方便用户,提供了saveOrUpdate()方法,自动判断是否是持久对象,是则更新,否则创建。我的建议是永远不要使用这些看上去很简单的方法,否则将很难判断ORM到底做了什么操作,也就很难追踪到逻辑错误。

正确使用CRUD

正因为我们需要时刻区分一个对象的持久化状态,所以,编写CRUD(Create,Retrieve,Update,Delete的缩写)要严格遵循以下原则:

Create:对于Create操作,传入的永远是非持久化对象,一旦调用了create方法,就变成持久化对象;

Retrieve:所有通过ORM从数据库读取的对象都是持久化对象,直到当前会话结束;

阅读全文 »

Windows虽然有简单易用的特点,不过,作为一名专业的软件开发人员,使用Linux作为开发平台,还是有很大的优越性的。

首先,Windows下的软件大多是收费的,虽然网上的破解也不少,不过,在公司使用说不定哪天就有麻烦了,而Linux下的软件基本上都是免费而且开源的,虽然公开源代码对我等只用不改造的用户来说意义不大,不过,免费却是实实在在的。

其次,作为一名软件开发人员,许多服务器软件只能在Linux下运行,有的虽然已经移植到了Windows上,运行效率和稳定性却要大打折扣。

不过,和Windows用户的担心一样,使用Linux,如果不能听MP3,不能看大片,仅仅在Linux上工作也不太爽,毕竟要劳逸结合嘛。好在Linux下的多媒体软件已经今非昔比了,今天,我们就一步一步打造一个工作+娱乐一体的Linux环境。

选择什么Linux?

Linux发行版众多,有商业公司支持的,也有开源社区支持的,不同的Linux发行版侧重也不同,有的Linux比如Gentoo,完全面向Linux发烧级用户的,从源代码编译开始。对于普通开发者而言,入门容易,安装软件快捷简便是最重要的两点。我的选择是Debian Linux,最新版本是5。相对于其他Linux版本,Debian的最大的优势就是软件众多,安装极为简单。有许多用户可能用过或听说过Red Hat的RPM软件包,不过,和Debian的DEB相比,RPM就差远了!

另外,Debian是一个社区维护的Linux发行版,和倾向于提供傻瓜式操作的Ubuntu相比,Debian显得更加“专业”一点,动手能力要求更高一点,便于和普通的Windows用户拉开更大的差距。

安装Debian Linux

闲话少说,要安装Debian Linux,先去Debian官方网站下载刻盘,根据计算机类型选相应的ISO,通常是i386,用AMD64处理器的可以选amd64,建议以HTTP/FTP方式下载第一张ISO光盘,比如i386对应的CD:

http://cdimage.debian.org/debian-cd/5.0.2a/i386/iso-cd/

选择netinst方式的ISO虽然下载较快,但是安装过程中需要联网,毕竟麻烦。

下载后刻盘,从光盘启动,安装过程很简单,主要是分区要注意,最好手动分区,然后把引导区安装到MBR上,Debian会自动发现已安装的Windows,双系统启动没有问题。

安装时会要求输入APT源,就是将来安装软件的下载地址了,我通常选择ftp.us.debian.org,速度那是非常地快。

安装过程中可以安装桌面,也可以不装。如果没有安装,用root登录后手动安装:

# apt-get install gnome

然后桌面就搞定了。

中文支持

使用Linux的第一个大问题就是要搞定中文。虽然Linux实际上完美支持各种语言,不过还是要稍微配置一下。在Debian中配置中文是相对简单的,运行命令

# dpkg-reconfigure locales

把以下的编码选中:

  • en_US.UTF-8 UTF-8
  • zh_CN GB2312
  • zh_CN.GB18030 GB18030
  • zh_CN.GBK GBK
  • zh_CN.UTF-8 UTF-8

也可以顺便把BIG5编码选上:

  • zh_TW BIG5
  • zh_TW.UTF-8 UTF-8

下一步是安装中文字体。Linux自带几种中文字体,输入以下命令安装Debian Linux默认的中文字体:

# apt-get install ttf-arphi

阅读全文 »

安装了Windows 7 Ultimate英文正式版后(注意是英文版!!!),发现很多中文软件显示为乱码,例如招商银行个人专业版,联系了他们的客服后,非常肯定地告诉我,目前专业版不支持Windows 7,!!! -_- !!!

然后在Google中搜索Windows 7乱码问题,发现好多网站把一篇改注册表的文章转来转去,结果我也被严重误导了!改完重启一点作用没有,最后终于找到简单而正确的方法:

打开Control Panel,Clock, Language and Region,选择Administrative:

选择Change system locale...按钮,把当前语言改为中文,重启即可:

所有中文软件均运行正常!

阅读全文 »

本文最早发表于IBM developerWorks:

http://www.ibm.com/developerworks/cn/java/j-lo-jeeflex/

传统的Java EE应用程序通常使用某种MVC框架(例如,Struts)作为前端用户界面,随着Flex的兴起,基于RIA的客户端能够给用户带来更酷的界面,更短的响应时间,以及更接近于桌面应用程序的体验。本文将讲述如何将Flex集成至一个现有的Java EE应用程序中,以及如何应用最佳实践高效率地并行开发Java EE和Flex。

开发环境

本文的开发环境为Windows 7 Ultimate,Eclipse 3.4,Flex Builder 3。Java EE服务器使用Resin 3.2,当然,您也可以使用Tomcat等其他Java EE服务器。

现有的Java EE应用

假定我们已经拥有了一个管理雇员信息的Java EE应用,名为EmployeeMgmt-Server,结构如图所示:

这是一个典型的Java EE应用,使用了流行的Spring框架。为了简化数据库操作,我们使用了内存数据库HSQLDB。对这个简单的应用,省略了DAO,直接在Façade中通过Spring的JdbcTemplate操作数据库。最后,EmployeeMgmt应用通过Servlet和JSP页面为用户提供前端界面:

该界面为传统的HTML页面,用户每次点击某个链接都需要刷新页面。由于Employee Management系统更接近于传统的桌面应用程序,因此,用Flex重新编写界面会带来更好的用户体验。

集成BlazeDS

如何将Flex集成至该Java EE应用呢?现在,我们希望用Flex替换掉原有的Servlet和JSP页面,就需要让Flex和Java EE后端通信。Flex支持多种远程调用方式,包括HTTP,Web Services和AMF。不过,针对Java EE开发的服务器端应用,可以通过集成BlazeDS,充分利用AMF协议并能轻易与Flex前端交换数据,这种方式是JavaEE应用程序集成Flex的首选。

BlazeDS是Adobe LifeCycle Data Services的开源版本,遵循LGPL v3授权,可以免费使用。BlazeDS为Flex提供了基于AMF二进制协议的远程调用支持,其作用相当于Java的RMI。有了BlazeDS,通过简单的配置,一个Java接口就可以作为服务暴露给Flex,供其远程调用。

尽管现有的EmployeeMgmt应用程序已经有了Façade接口,但这个接口是暴露给Servlet使用的,最好能再为Flex定义另一个接口FlexService,并隐藏Java语言的特定对象:

public interface FlexService {
    Employee createEmployee(String name, String title,

阅读全文 »

用WTK 2.5开发MIDP应用时,自己写了个冒泡排序,模拟器运行正常,真机上报NoClassDefFoundError,原来是没有java.lang.Comparable这个接口,但是WTK编译居然通过了!校验器也没验出任何问题。

解决办法:

自定义一个IsComparable接口,将要排序的类实现此接口:

public static void sort(Vector v) {
    int size = v.size();
    for (int i=0; i<size; i++) {
        for (int j=i+1; j<size; j++) {
            IsComparable o1 = (IsComparable) v.elementAt(i);
            Object o2 = v.elementAt(j);
            if (o1.compareTo(o2) > 0) {
                // swap:
                v.setElementAt(o1, j);
                v.setElementAt(o2, i);
            }
        }
    }
}

阅读全文 »

快速排序是一种基于分治的算法,其基本思想是将一个大数组按照一个基准数分成左右两份,左边的部份都不大于基准数,右边的部分都不小于基准数。然后,对这两份再分别应用快速排序,直到分到只剩2个数为止。

快速排序在通常情况下是最快的排序算法,以下是用Python实现的一个例子:

'''
qsort.py

Quick sort

Created on Jun 18, 2009

@author: Liao
'''

from random import Random

def quick_sort(arr):
    if len(arr) > 1:
        qsort(arr, 0, len(arr) - 1)

def qsort(arr, start, end):
    base = arr[start]
    pl = start
    pr = end
    while pl < pr:
        while pl < pr and arr[pr] >= base:
            pr -= 1
        if pl == pr:
            break
        else:
            arr[pl], arr[pr] = arr[pr], arr[pl]

        while pl < pr and arr[pl] <= base:
            pl += 1
        if pl == pr:
            break
        else:
            arr[pl], arr[pr] = arr[pr], arr[pl]
    # now pl == pr
    if pl - 1 > start:
        qsort(arr, start, pl - 1)
    if pr + 1 < end:
        qsort(arr, pr + 1, end)

r = Random()
a = []
for i in range(20):
    a.append(r.randint(0, 100))

print a
quick_sort(a)
print a

快速排序是一种不稳定排序,而冒泡排序则是稳定排序。

稳定排序是指如果排序前有两个相同的数,比如对[a=10, b=10, c=2]排序,a和b相等,排序前a在b的前面,稳定排序后结果为[c, a, b],a仍然在b的前面,而不稳定排序则不保证相等的两个数位置不会交换,排序结果可能变为[c, b, a]。

阅读全文 »

在Eclipse中,只需随时按住Ctrl并点击某个类名或方法名,即可跳转到相应的代码中。然而,如果引用一个开源的jar包,则会直 接打开其class的二进制码,这对于调试或研究代码内部流程颇为不便,尽管可以在Build Path中为每个jar指定源代码位置,但这样一来,对于同一个jar(例如spring.jar),每个工程都要指定,比较麻烦。

另一种更简单的方式是直接用WinZip或WinRAR之类的工具解开jar,再把源码也放进去,注意路径要正确,同一个Xxx.class和 Xxx.java应该在同一目录下,再用zip打包成jar包(jar格式其实就是zip格式),以后无论在哪个工程引用该jar包,Eclipse都可以直接从jar包中读出其对应的源代码,不必在Build Path中配置源代码位置,对于开源组件来说,大大方便了代码的跟踪和测试。

阅读全文 »

通过Google“博客搜索”Ping API, 用户可以程序化的方式将博客内容的更新通知给Google“博客搜索”引擎。这对于经常更新博客内容的用户尤其有用。博客服务提供商的管理人员也可以利用此API将其平台上的博客内容变化向Google通告,以便Google“博客搜索”及时抓取来自这一服务提供商的最新内容。

Google“博客搜索”支持XML-RPC客户端和REST客户端。使用XML-RPC时,需要构造一个XML,然后将其POST到Google的指定地址,比较麻烦,而REST则既简单又方便。

使用REST时,只需构造一个如下URL:

http://blogsearch.google.com/ping?name=xxx&url=xxx&changesURL=xxx

然后以GET发送,成功后会返回字符串“Thanks for the ping.”。

Google会根据url参数抓取blog页面并在最短的时间内索引。

阅读全文 »

Jetty是一个优秀的Web服务器,最大的特点是可嵌入应用程序,因此作为调试服务器非常方便,就像跟踪普通的main()方法一样可以在Eclipse中直接调试Web应用而无需远程连接。但是使用Jetty发现一个问题,即Windows上启动后Jetty会锁定已访问的静态文件,如HTML,CSS等,这给页面设计带来了不便。

其实Jetty官方站点对此问题已有回答,锁定文件据说是为了提高性能,但我觉得缓存也不一定需要长时间锁定文件:http://docs.codehaus.org/display/JETTY/Files+locked+on+Windows

其实可以修改Jetty默认的配置文件,在jetty-6.1.5.jar中找到org/mortbay/jetty/webapp/webdefault.xml,搜索useFileMappedBuffer:

<init-param>
    <param-name>useFileMappedBuffer</param-name>
    <param-value>true</param-value>
</init-param>

将param-value从true改为false即可。可以直接修改jar包内的这个文件,但是修改发行包毕竟不好,可以将此文件复制一份,在启动Jetty时用自己的这个webdefault.xml覆盖Jetty的设置即可。加上:

WebAppContext webapp = new WebAppContext();
webapp.setDefaultsDescriptor("./webdefault.xml");

重新启动后问题解决。

阅读全文 »

在开发web应用时,如果通过weblogic的控制台部署war包,则weblogic默认在运行期不会解压war,这对于调试jsp颇为不便。其实,只需一个简单的设置就可以强迫weblogic解开war,并且编辑jsp后weblogic会重新加载,方便调试。

以8.1 sp4为例,打开bea/user_projects/domains/<my-domain>/config.xml,找到相应的war包:

<Application Name="test"
    Path="C:\java\bea\user_projects\domains\mydomain\applications\test.war"
    StagingMode="nostage" TwoPhase="true">

将StagingMode由nostage改为stage,重启weblogic即可。解压后的目录在myserver目录下。

需要注意的是,一旦war包需要重新部署,除了更新war包外,还要删除bea/user_projects/domains/<my-domain>/myserver目录下的.wlnotdelete和stage目录,以便强迫weblogic重新解开最新的war包,否则将继续使用原来已解压的目录。

阅读全文 »

Subversion是新一代的开源版本控制系统,和CVS相比,Subversion最大的特点是支持事务,可以确保一个提交是原子操作。此外,Subversion还支持更多的协议,包括HTTP访问。在Eclipse中,使用Subverison和CVS一样简单,只需安装Subclipse插件就可以了。

本文以Eclipse 3.3为例,安装Subclipse非常容易,打开Eclipse,选择菜单Help->Software Updates->Find and Install…,在弹出的对话框中选择“Search for new features to install”,然后点击“New Remote Site…”,填入Subclipse的在线安装的URL:

按照提示安装完毕后,我们就可以打开Subversion的资源库了。选择Eclipse菜单Window->Show View->Other…,选择SVN->SVN Repository,然后添加一个新的资源库,例如http://livebookstore.googlecode.com/svn/trunk

添加完毕后,即可直接浏览SVN库的目录结构,然后通过右键菜单Checkout…检出为一个工程:

Eclipse提示将目录检出为一个工程:

点击Finish即完成。

阅读全文 »

线程的创建和启动

Java语言已经内置了多线程支持,所有实现Runnable接口的类都可被启动一个新线程,新线程会执行该实例的run()方法,当run()方法执行完毕后,线程就结束了。一旦一个线程执行完毕,这个实例就不能再重新启动,只能重新生成一个新实例,再启动一个新线程。

Thread类是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法:

Thread t = new Thread();
t.start();

start()方法是一个native方法,它将启动一个新线程,并执行run()方法。Thread类默认的run()方法什么也不做就退出了。注意:直接调用run()方法并不会启动一个新线程,它和调用一个普通的java方法没有什么区别。

因此,有两个方法可以实现自己的线程:

方法1:自己的类extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:

public class MyThread extends Thread {
    public run() {
        System.out.println("MyThread.run()");
    }
}

在合适的地方启动线程:new MyThread().start();

方法2:如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口:

public class MyThread extends OtherClass implements Runnable {
    public run() {
        System.out.println("MyThread.run()");
    }
}

为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:

MyThread myt = new MyThread();
Thread t = new Thread(myt);
t.start();

事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:

public void run() {
    if (target != null) {
        target.run();
    }
}

线程还有一些Name, ThreadGroup, isDaemon等设置,由于和线程设计模式关联很少,这里就不多说了。

线程的同步

由于同一进程内的多个线程共享内存空间,在Java中,就是共享实例,当多个线程试图同时修改某个实例的内容时,就会造成冲突,因此,线程必须实现共享互斥,使多线程同步。

最简单的同步是将一个方法标记为synchronized,对同一个实例来说,任一时刻只能有一个synchronized方法在执行。当一个方法正在执行某个synchronized方法时,其他线程如果想要执行这个实例的任意一个synchronized方法,都必须等待当前执行 synchronized方法的线程退出此方法后,才能依次执行。

但是,非synchronized方法不受影响,不管当前有没有执行synchronized方法,非synchronized方法都可以被多个线程同时执行。

此外,必须注意,只有同一实例的synchronized方法同一时间只能被一个线程执行,不同实例的synchronized方法是可以并发的。例如,class A定义了synchronized方法sync(),则不同实例a1.sync()和a2.sync()可以同时由两个线程来执行。

Java锁机制

多线程

阅读全文 »

概述

J2ME是Sun发布的运行在小型设备上的微型版Java的一系列标准,其中,最重要的标准便是运行在手机上的MIDP应用程序了。到目前为止,MIDP一共发布了两个版本:MIDP 1.0(JSR37)和MIDP 2.0(JSR118),2.0版本可以向后兼容1.0版本,也就是说,支持MIDP 2.0的手机可以同时运行MIDP 1.0和MIDP 2.0的应用程序。本文将重点讲述开发MIDP应用程序时非常有用的一些设计模式,开发技巧以及如何调试、优化J2ME应用程序。

本文将讨论J2ME开发的以下内容:

  • 如何自动适应用户手机配置
  • 如何在屏幕间导航
  • 如何实现一个灵活的联网应用
  • 如何实现一个灵活的RMS应用
  • 如何调试并优化J2ME程序

避免OutOfMemoryError

对于MIDP应用程序来说,由于手机设备上的资源非常有限,较弱的CPU计算能力,有限的内存(从几十KB到几百KB,虽然少数高端手机拥有超过1M的动态内存),很小的屏幕尺寸,因此,为了让一个MIDP应用程序能够不加改动地在多种不同手机上运行,程序必须有能力根据系统配置自动调整运行时的参数。比如,对于内存非常小的手机,如果从网络下载一幅较大的图像,需要分配巨大的缓冲区,就可能导致OutOfMemoryError错误,使应用程序直接终止,这会使用户感到不知所措,或者丢失用户的重要数据。因此,在试图分配一块大内存之前,首先使用System.gc()尝试让垃圾收集器释放无用对象占用的内存,然后,使用Runtime.getRuntime().freeMemory()方法获得可用的内存空间。如果可用空间太小,给用户一个“内存不足,无法完成操作”的Alert提示,从而尽可能地避免OutOfMemoryError错误。

// 示例代码:
System.gc();
int max_size = 102400; // 100KB
int free_size = (int)Runtime.getRuntime().freeMemory();
if(max_size>free_size*2/3) {
    // TODO: Alert!
}
else {
    byte[] buffer = new byte[max_size];
    // TODO: Download image...
}

减少图片以减小JAR文件大小

许多手机会因为JAR文件太大而无法运行MIDP应用程序,而减小JAR文件尺寸的有效方法之一是减少不必要的图片,例如,启动时的LOGO图片可以用文字来代替,列表项可以只显示文字而不显示图片。为了能适应不同配置的手机,我们的代码就应该编写得更加灵活。例如,从JAR包中加载图片时:

Image image = null;
try {
    image = Image.createImage("/logo.png");
}
catch(Exception ioe) {}
if(image==null) {
    g.setColor(0);
    g.drawString("info", getWidth()/2, getHeight()/2, Graphics.HCENTER|Graphics.BASELINE);
}
else {
    g.drawImage(image, getWidth()/2, getHeight()/2, Graphics.HCENTER|Graphics.VCENTER);
}

如果加载失败,程序会以文字方式显示,这样,对于低配置的手机,只需要把美化界面的图片删除掉,再重新打包即可得到一个可发布的尺寸较小的JAR包,同时应用程序的代码并没有改动。

类似的,在加载List之类的UI组件时:

Image image = null;
try {
    image = Image.createImage("/logo.png&

阅读全文 »

本文介绍如何在Resin中调试Web应用程序。测试环境为Windows 7 / Resin 3.2 / Eclipse 3.3

在Resin的resin.conf中找到<server-default>并添加加以下参数:

<resin xmlns="http://caucho.com/ns/resin"
       xmlns:resin="http://caucho.com/ns/resin/core">
    <log name="" level="info" path="stdout:"/>
    <cluster id="">
        <root-directory>.</root-directory>
        <server-default>
            <http server-id="" host="*" port="80"/>
            <jvm-arg>-Xmx128m</jvm-arg>
            <jvm-arg>-Xss1m</jvm-arg>
            <jvm-arg>-Xdebug</jvm-arg>
            <jvm-arg>-Xnoagent</jvm-arg>
            <jvm-arg>-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=12345</jvm-arg>

启动Resin后,打开Eclipse项目,选择 Run -> Debug... -> Remote Java Application -> New

新建一个Remote Java Application,填入Host: 127.0.0.1, Port: 12345, 注意这个Port就是Resin启动的address参数。

现在,就可以利用Eclipse强大而方便的调试界面对Web App断点调试并跟踪了!

阅读全文 »

分类