Nirvana Studio » Nicholas Ding :: 分享知识,传播技术

Author Archive

Ubuntu 完美中文字体

Posted by Nicholas Ding on 26th 四月 2007

Ubuntu 每次升级几乎都会调整 fonts.conf 的内容,所以 wiki.ubuntu.org.cn 上提供的 VeraSansYuanTi 的字体配置就不能够直接替换系统默认的 fonts.conf 文件。

圆体的下载地址和参考配置在 Ubuntu Wiki 上的 使用圆体美化系统

这里提供了一份 Nicholas 手工合并过的版本,适合 Ubuntu 7.04 Feisty Fawn,完美显示圆体效果。需要替换 /etc/fonts.conf 和另外两个文件 /etc/fonts/conf.avail/40-generic.conf 和 /etc/fonts/conf.avail/60-latin.conf

下载 fonts.conf 40-generic.conf 60-latin.conf

效果图

效果图

Posted in Ubuntu, 新闻 | 3 Comments »

如何给 Solaris 添加新 IDE 硬盘

Posted by Nicholas Ding on 6th 四月 2007

以 root 身份登录,键入以下命令:


touch /reconfigure

shutdown -y -i6

Posted in Solaris | 3 Comments »

FireBug 控制台函数说明

Posted by Nicholas Ding on 20th 九月 2006

原文地址:http://www.joehewitt.com/software/firebug/docs.php

FireBug 是一个非常实用的JavaScript以及DOM查看调试工具,是 Firefox 的一个插件。使用 FireBug 调试 AJAX 应用非常方便,终于可以告别 alert 时代了!

Console Logging 函数

FireBug 为所有 Web 页面提供了一个 console 对象。这个对象有以下函数:

Logging 基础

console.log(”message” [,objects]) - 将一个字符串打印到控制台。字符串可以包含任何“String Formatting”小节描述的模式。字符串后面的对象应该用来取代之前字符串中的模式。(译者注:大家用过C里面 printf 吧,效果基本是一样的。)

Logging 等级

通常根据不同的等级来区分Logging的严重程度是很有帮助的。FireBug 提供了4个等级。为了达到视觉分离的效果,这些函数与 log 不同的地方就是它们在被调用的时候会自动包含一个指向代码行数的链接。

console.debug(”message” [,objects]) - 记录一个 debug 消息。
console.info(”message” [,objects]) - 记录一个信息.
console.warn(”message” [,objects]) - 记录一个警告.
console.error(”message” [,objects]) - 记录一个错误.

断言

断言是一条确保代码规则的非常好的途径。console 对象包含了一系列各种类型的断言函数,并且允许你编写自己的断言函数。

console.assert(a, “message” [,objects]) - Asserts that an a is true.
console.assertEquals(a, b, “message” [,objects]) - Asserts that a is equal to b.
console.assertNotEquals(a, b, “message” [,objects]) - Asserts that a is not equal to b.
console.assertGreater(a, b, “message” [,objects]) - Asserts that a is greater than b.
console.assertNotGreater(a, b, “message” [,objects]) - Asserts that a is not greater than b.
console.assertLess(a, b, “message” [,objects]) - Asserts that a is less than b.
console.assertNotLess(a, b, “message” [,objects]) - Asserts that a is not less than b.
console.assertContains(a, b, “message” [,objects]) - Asserts that a is in the array b.
console.assertNotContains(a, b, “message” [,objects]) - Asserts that a is not in the array b.
console.assertTrue(a, “message” [,objects]) - Asserts that a is equal to true.
console.assertFalse(a, “message” [,objects]) - Asserts that a is equal to false.
console.assertNull(a, “message” [,objects]) - Asserts that a is equal to null.
console.assertNotNull(a, “message” [,objects]) - Asserts that a is not equal to null.
console.assertUndefined(a, “message” [,objects]) - Asserts that a is equal to undefined.
console.assertNotUndefined(a, “message” [,objects]) - Asserts that a is not equal to undefined.
console.assertInstanceOf(a, b, “message” [,objects]) - Asserts that a is an instance of type b.
console.assertNotInstanceOf(a, b, “message” [,objects]) - Asserts that a is not an instance of type b.
console.assertTypeOf(a, b, “message” [,objects]) - Asserts that the type of a is equal to the string b.
console.assertNotTypeOf(a, b, “message” [,objects]) - Asserts that the type of a is not equal to the string b.

测量(Measurement)

下面的一些函数可以让你方便的测量你的一些代码。

console.trace() - 记录执行点的堆栈信息。
console.time(”name”) - 根据 name 创建一个唯一的计时器。
console.timeEnd(”name”) - 根据 name 停止计时器,并且记录消耗的时间,以毫秒为单位。
console.count(”name”) - 记录该行代码执行的次数。

字符串格式化

所有 console 的 logging 函数都可以通过以下模式格式化字符串:

%s - 将对象格式化为字符串。
%d, %i, %l, %f - 将对象格式化为数字。
%o - 将对象格式化成一个指向 inspector 的超链接。
%1.o, %2.0, etc.. - 将对象格式化成包含自己属性的可交互的表格。
%.o - 将对象格式化成具有自身属性的一个数组。
%x - 将对象格式化成一个可交互的 XML 树形结构。
%1.x, %2.x, etc.. - 将对象格式化成一个可交互的 XML 数型结构,并且展开 n 层节点。

如果你需要一个真实的 % 符号,你可以通过一个转移符号就像这样 “\%”。

命令行函数

内建的命令行函数可以通过以下命令行使用:

$(”id”) - document.getElementById() 的简写。(译者注:跟 prototype.js 学来的吧?)
$$(”css”) - 返回一个符合 CSS 选择器的元素数组。
$x(”xpath”) - 返回一个符合 XPath 选择器的元素数组。
$0 - 返回最近被检查(inspected)的对象。
$1 - 返回最近被检查(inspected)的下一个对象。
$n(5) - 返回最近被检查的第n个对象。
inspect(object) - 将对象显示在 Inspector 中。
dir(object) - 返回一个对象的属性名数组。(译者注:跟 Python 学的?)
clear() - 清除控制台信息。

Posted in Ajax, JavaScript, Web?? | 7 Comments »

编写自己的dojo扩展

Posted by Nicholas Ding on 12th 八月 2006

前言

dojo是时下非常流行的javascript框架,它在自己的Wiki上给自己下了一个定义,dojo是一个用JavaScript编写的开源的DHTML工具箱。

dojo很想做一个“大一统”的工具箱,不仅仅是浏览器层面的,野心还是很大的。不过dojo带来了JavaScript编程的一些新想法,其中引入包机制进行动态加载是一个不错的概念。

理解dojo的包机制

其实dojo只需要一些很小的加载代码就可以用来加载它的各种包,它的官方站点上提供的dojo-ajax下载中包含的dojo.js体积还是比较庞大的,因为它将一些常用的包都包含在了js中,

但是很多时候我们并不需要这么多功能,还是按需加载比较好。

幸好在http://download.dojotoolkit.org/这个地址中我们还可以下载到dojo的各个自定义版本,其实包含的组件都是一样的,只不过dojo.js的大小有很大不同,那么,我们就从minimal版本下手。

下载之后会发现minimal版本包含的dojo.js只有18kb,里面仅仅包含了加载机制,非常不错。这样,我们就可以开始编写自己的dojo扩展。

dojo代码结构

解压缩后的目录里面包含src目录,src目录下存放有dojo的各个组件包,我们在这里面新建一个hello目录。

新建一个名为__package__.js文件,很类似Python的模块命名,这个__package__.js定义了在引入这个命名空间的时候默认导入多少类,以及这个命名空间的名字。

我们的目的是做一个dojo.hello.Echo扩展,那么在__package__.js中的代码应该这样:

// kwCompoundRequire 的作用是当你导入整个dojo.hello包的时候需要默认加载多少类
// 这些定义就在这个函数里面,common在这里表示默认的加载,这个参数不是固定的
// dojo希望自己是一个“大一统”的实现,所以考虑了非浏览器情况,可以有别的,譬如rhino
dojo.kwCompoundRequire({
	common: [
		"dojo.hello.Echo"
	]
});
// 这个定义了包,默认这么写 | 原因嘛,当然是有的,看你的悟性了:-)
dojo.provide("dojo.hello.*");

我们指定了默认加载的类Echo,那么我们就去写Echo类,在hello目录中新建Echo.js,代码如下:

// 类名定义,JavaScript写的变扭,其实就是直接定义类名
dojo.provide("dojo.hello.Echo");
 
// 类定义部分,非常熟悉的代码
dojo.hello.Echo = function() {
	this.name = "dojo.hello.Echo";
	this.sayHello = function(greeting) {
		return greeting;
	}
}

扩展写好了,很简单,接下来就是掉用了,index.html如下。

<html>
<head>
<script language="javascript" src="dojo.js"></script>
<script language="javascript">
dojo.require("dojo.hello.*");
var echo = new dojo.hello.Echo();
document.write(echo.sayHello("Hello World"));
</script>
</head>
<body>

</body>
</html>

注意dojo.require(”dojo.hello.*”)回去请求两个文件,首先是__package__.js,这样一来就得到了之前在dojo.kwCompoundRequire里面指定的类列表,然后去加载Echo.js。你也可以直

接去加载Echo.js,只需要变成dojo.require("dojo.hello.Echo")

更多内容

这个例子非常简单的介绍了一下dojo的包加载机制,当然这个包中的类并没有引用其它类,dojo还允许在代码中动态加载其它类,当然了,这些都是通过XmlHttp来实现的,因为是同步模式,所以请求的类比较多并且都没有包含在dojo.js中的时候会有页面停顿的现象,这点还是需要注意的。

文中的代码下载:,dojo-hello.tar.gz

一些dojo的资源:

Posted in Ajax, JavaScript, Web?? | No Comments »

cglib 指南

Posted by Nicholas Ding on 6th 六月 2006

cglib,全称是Code Generation Library,它可以用来动态继承Java类或者实现接口,很多知名的开源项目中用到了它,譬如Hibernate,Spring之类用它来实现动态代理。

增强一个已有类

public class MyClass {
 
	public void method() {
		System.out.println("MyClass.method()");
	}
}

import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;
 
public class Main {
 
	public static void main(String[] args) {
 
		Enhancer enhancer = new Enhancer();
 
		enhancer.setSuperclass(MyClass.class);
		enhancer.setCallback( new MethodInterceptorImpl() );
 
 
		MyClass my = (MyClass)enhancer.create();
 
		my.method();
	}
 
	private static class MethodInterceptorImpl implements MethodInterceptor {
		
		public Object intercept(Object obj, 
                                Method method, 
                                Object[] args, 
                                MethodProxy proxy) throws Throwable {
 
			System.out.println(method);
 
			proxy.invokeSuper(obj, args);
 
			return null;
		}
	}
}

执行结果:

public void cglib_test.MyClass.method()
MyClass.method()

使用CallbackFilter

public class MyClass {
 
	public void method() {
		System.out.println("MyClass.method()");
	}
 
	public void method2() {
		System.out.println("MyClass.method2()");
	}
}

import java.lang.reflect.Method;
 
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.NoOp;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
 
 
public class Main {
 
	public static void main(String[] args) {
 
		Callback[] callbacks =
			new Callback[] { new MethodInterceptorImpl(),  NoOp.INSTANCE };
 
		Enhancer enhancer = new Enhancer();
 
		enhancer.setSuperclass(MyClass.class);
		enhancer.setCallbacks( callbacks );
		enhancer.setCallbackFilter( new CallbackFilterImpl() );
 
 
		MyClass my = (MyClass)enhancer.create();
 
		my.method();
		my.method2();
	}
 
	private static class CallbackFilterImpl implements CallbackFilter {
 
		public int accept(Method method) {
 
			if ( method.getName().equals("method2") ) {
				return 1;
 
			} else {
				return 0;
			}
		}
	}
 
	private static class MethodInterceptorImpl implements MethodInterceptor {
		
		public Object intercept(Object obj, 
                                Method method, 
                                Object[] args, 
                                MethodProxy proxy) throws Throwable {
 
			System.out.println(method);
 
			return proxy.invokeSuper(obj, args);
		}
	}
}

执行结果:

public void cglib_test.MyClass.method()
MyClass.method()
MyClass.method2()

使用Mixin

public interface MyInterfaceA {
 
	public void methodA();
}
 
public interface  MyInterfaceB {
	public void methodB();
}
 
public class MyInterfaceAImpl implements MyInterfaceA {
 
	public void methodA() {
		System.out.println("MyInterfaceAImpl.methodA()");
	}
}
 
public class MyInterfaceBImpl implements MyInterfaceB {
 
	public void methodB() {
		System.out.println("MyInterfaceBImpl.methodB()");
	}
}

import net.sf.cglib.proxy.Mixin;
 
public class Main {
 
	public static void main(String[] args) {
 
		Class[] interfaces =
			new Class[] { MyInterfaceA.class, MyInterfaceB.class };
		
		Object[] delegates =
			new Object[] { new MyInterfaceAImpl(), new MyInterfaceBImpl() };
			
		
		Object obj = Mixin.create(interfaces, delegates);
 
 
		MyInterfaceA myA = (MyInterfaceA)obj;
		myA.methodA();
 
 
		MyInterfaceB myB = (MyInterfaceB)obj;
		myB.methodB();
	}
}

执行结果:

MyInterfaceAImpl.methodA()
MyInterfaceBImpl.methodB()

Posted in Java | 4 Comments »

Don’t repeat the DAO!

Posted by Nicholas Ding on 1st 六月 2006

译者:Nicholas @ Nirvana Studio

原文地址:http://www-128.ibm.com/developerworks/java/library/j-genericdao.html

使用Hibernate和Spring AOP购建一个范型类型安全的DAO

2006年五月12日

在采用了Java 5的范型之后,要实现一个基于范型类型安全的数据访问对象(DAO)就变得切实可行了。在这篇文章里,系统架构师Per Mellqvist展示了一个基于Hibernate的范型DAO实现。然后将介绍如何使用Spring AOP的introduction为一个类增加一个类型安全的接口以便于执行查询。

对于大多数开发者来说,在系统中为每一个DAO编写几乎一样的代码已经成为了一种习惯。同时大家也都认可这种重复就是“代码的味道”,我们中的大多数已经习惯如此。当然也有另外的办法。你可以使用很多ORM工具来避免代码的重复编写。举个例子,用Hibernate,你可以简单的使用session操作直接控制你的持久化领域对象。这种方式的负面影响就是丢失了类型安全。

为什么你的数据访问代码需要一个类型安全的接口?我认为它减少了编程错误,提高了生产率,尤其是在使用现代高级IDE的时候。首先,一个类型安全的接口清晰的制定了哪些领域对象具有持久化功能。其次,它消除了类型转换带来的潜在问题。最后,它平衡了IDE的自动完成功能。使用自动完成功能是最快的方式来记住对于适当的领域类哪些查询是可用的。

在这篇文章里,我将展示给大家如何避免一次次地重复编写DAO代码,但同时还收益于类型安全的接口。事实上,所有内需要编写的是为新的DAO编写一个Hibernate映射文件,一个POJO的Java接口,并且10行Spring配置文件。

DAO实现

DAO模式对于任何Java开发人员来说都是耳熟能详的。这个模式的实现相当多,所以让我们仔细推敲一下我这篇文章里面对于DAO实现的一些假设:

  • 所有系统中的数据库访问都是通过DAO来完成封装
  • 每一个DAO实例对一个主要的领域对象或者实体负责。如果一个领域对象具有独立的生命周期,那么它需要具有自己的DAO。
  • DAO具有CRUD操作
  • DAO可以允许基于criteria方式的查询而不仅仅是通过主键查询。我将这些成为finder方法或者finders。这个finder的返回值通常是DAO所负责的领域对象的集合。

范型DAO接口

范型DAO的基础就是CRUD操作。下面的接口定义了范型DAO的方法:

public interface GenericDao <T, PK extends Serializable> {
 
    /** Persist the newInstance object into database */
    PK create(T newInstance);
 
    /** Retrieve an object that was previously persisted to the database using
     *   the indicated id as primary key
     */
    T read(PK id);
 
    /** Save changes made to a persistent object.  */
    void update(T transientObject);
 
    /** Remove an object from persistent storage in the database */
    void delete(T persistentObject);
}

实现这个接口

使用Hibernate实现上面的接口是非常简单的。也就是调用一下Hibernate的方法和增加一些类型转换。Spring负责session和transaction管理。

public class GenericDaoHibernateImpl <T, PK extends Serializable>
    implements GenericDao<T, PK>, FinderExecutor {
    private Class<T> type;
 
    public GenericDaoHibernateImpl(Class<T> type) {
        this.type = type;
    }
 
    public PK create(T o) {
        return (PK) getSession().save(o);
    }
 
    public T read(PK id) {
        return (T) getSession().get(type, id);
    }
 
    public void update(T o) {
        getSession().update(o);
    }
 
    public void delete(T o) {
        getSession().delete(o);
    }
 
    // Not showing implementations of getSession() and setSessionFactory()
}

Spring 配置

最后,Spring配置,我创建了一个GenericDaoHibernateImpl的实例。GenericDaoHibernateImpl的构造器必须被告知领域对象的类型,这样DAO实例才能为之负责。这个同样需要Hibernate运行时知道这个对象的类型。下面的代码中,我将领域类Person传递给构造器并且将Hibernate的session工厂作为一个参数用来实例化DAO:

<bean id="personDao" class="genericdao.impl.GenericDaoHibernateImpl">
        <constructor-arg>
            <value>genericdaotest.domain.Person</value>
        </constructor-arg>
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
</bean>

可用的范型DAO

我还没有全部完成,但我现在已经有了一个可供作的代码。下面的代码展示了范型DAO如何使用:

public void someMethodCreatingAPerson() {
    ...
    GenericDao dao = (GenericDao)
     beanFactory.getBean("personDao"); // This should normally be injected
 
    Person p = new Person("Per", 90);
    dao.create(p);
}

这时候,我有一个范型DAO有能力进行类型安全的CRUD操作。同时也有理由编写GenericDaoHibernateImpl的子类来为每个领域对象增加查询功能。但是这篇文章的主旨在于展示如何完成这项功能而不是为每个查询编写明确的代码,然而,我将会使用多个工具来介绍DAO的查询,这就是Spring AOP和Hibernate命名查询。

Spring AOP介绍

你可以使用Spring AOP提供的introduction功能将一个现存的对象包装到一个代理里面来增加新的功能,定义它需要实现的新接口,并且将之前所有不支持的方法委派到一个处理机。在我的DAO实现里面,我用introduction将一定数量的finder方法增加到现存的范型DAO类里面。因为finder方法针对特定的领域对象,所以它们被应用到表明接口的范型DAO中。

<bean id="finderIntroductionAdvisor" class="genericdao.impl.FinderIntroductionAdvisor"/>
 
<bean id="abstractDaoTarget"
        class="genericdao.impl.GenericDaoHibernateImpl" abstract="true">
        <property name="sessionFactory">
            <ref bean="sessionFactory"/>
        </property>
</bean>
 
<bean id="abstractDao"
        class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true">
        <property name="interceptorNames">
            <list>
                <value>finderIntroductionAdvisor</value>
            </list>
        </property>
</bean>

在上面的配置中,我定义了三个Spring bean,第一个bean,FinderIntroductionAdvisor,处理那些introduce到DAO中但是不属于GenericDaoHibernateImpl类的方法。一会我再介绍Advisor bean的详细情况。

第二个bean定义为“abstract”。在Spring中,这个bean可以被其他bean重用但是它自己不会被实例化。不同于抽象属性,bean的定义简单的指出了我需要一个GenericDaoHibernateImpl的实例同时需要一个SessionFactory的引用。注意GenericDaoHibernateImpl类只定义了一个构造器接受领域类作为参数。因为这个bean是抽象的,我可以无限次的重用并且设定合适的领域类。

最后,第三个,也是最有意思的是bean将GenericDaoHibernateImpl的实例包装进了一个代理,给予了它执行finder方法的能力。这个bean定义同样是抽象的并且没有指定任何接口。这个接口不同于任何具体的实例。

扩展通用DAO

每个DAO的接口,都是基于GenericDAO接口的。我需要将为特定的领域类适配接口并且将其扩展包含我的finder方法。

public interface PersonDao extends GenericDao<Person, Long> {
    List<Person> findByName(String name);
}

上面的代码清晰的展示了通过用户名查找Person对象列表。所需的Java实现类不需要包含任何的更新操作,因为这些已经包含在了通用DAO里。

配置PersonDao

因为Spring配置依赖之前的那些抽象bean,所以它变得很紧凑。我需要指定DAO负责的领域类,并且我需要告诉Spring我这个DAO需要实现的接口。

<bean id="personDao" parent="abstractDao">
    <property name="proxyInterfaces">
        <value>genericdaotest.dao.PersonDao</value>
    </property>
    <property name="target">
        <bean parent="abstractDaoTarget">
            <constructor-arg>
                <value>genericdaotest.domain.Person</value>
            </constructor-arg>
        </bean>
    </property>
</bean>

你可以这样使用:

public void someMethodCreatingAPerson() {
    ...
    PersonDao dao = (PersonDao)
     beanFactory.getBean("personDao"); // This should normally be injected
 
    Person p = new Person("Per", 90);
    dao.create(p);
 
    List<Person> result = dao.findByName("Per"); // Runtime exception
}

上面的代码是使用类型安全接口PersonDao的一种正确途径,但是DAO的实现并没有完成。当调用findByName()的时候导致了一个运行时异常。这个问题是我还没有findByName()。剩下的工作就是指定查询语句。要完成这个,我使用Hibernate命名查询。

Hibernate命名查询

使用Hibernate,你可以定义任何HQL查询在映射文件里,并且给它一个名字。你可以在之后的代码里面方便的通过名字引用这个查询。这么做的一个优点就是能够在部署的时候调节查询而不需要改变代码。正如你一会将看到的,另一个好处就是实现一个“完整”的DAO而不需要编写任何Java实现代码。

<hibernate-mapping package="genericdaotest.domain">
     <class name="Person">
         <id name="id">
             <generator class="native"/>
         </id>
         <property name="name" />
         <property name="weight" />
     </class>
 
     <query name="Person.findByName">
         <![CDATA[select p from Person p where p.name = ? ]]>
     </query>
</hibernate-mapping>

上面的代码定义了领域类Person的Hibernate映射文件,有两个属性:name和weight。Person是一个具有上面属性的简单的POJO。这个文件同时包含了一个查询,通过提供的name属性从数据库查找Person实例。Hibernate为命名查询提供了不真实的命名空间功能。为了便于讨论,我将所有的查询名字的前缀变成领域类的的名称。在现实场景中,使用完整的类名,包含包名,是一个更好的主意。

总览

你已经看到了为任何领域对象创建并配置DAO的所需步骤了。这三个简单的步骤就是:

  1. 定义一个接口继承GenericDao并且包含任何所需的finder方法
  2. 在映射文件中为每个领域类的finder方法增加一个命名查询。
  3. 为DAO增加10行Spring配置

可重用的DAO类

Spring advisor和interceptor的功能比较琐碎,事实上他们的工作都引用回了GenericDaoHibernateImpl类。所有带有“find”开头的方法都被传递给DAO的单一方法executeFinder()。

public class FinderIntroductionAdvisor extends DefaultIntroductionAdvisor {
    public FinderIntroductionAdvisor() {
        super(new FinderIntroductionInterceptor());
    }
}
 
public class FinderIntroductionInterceptor implements IntroductionInterceptor {
 
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
 
        FinderExecutor genericDao = (FinderExecutor) methodInvocation.getThis();
 
        String methodName = methodInvocation.getMethod().getName();
        if (methodName.startsWith("find")) {
            Object[] arguments = methodInvocation.getArguments();
            return genericDao.executeFinder(methodInvocation.getMethod(), arguments);
        } else {
            return methodInvocation.proceed();
        }
    }
 
    public boolean implementsInterface(Class intf) {
        return intf.isInterface() && FinderExecutor.class.isAssignableFrom(intf);
    }
}

executeFinder() 方法

上面的代码唯一缺的就是executeFinder的实现。这个代码观察被调用的类的名字和方法,并且将他们与Hibernate的查询名相匹配。你可以使用一个FinderNamingStrategy来激活其他方式的命名查询。默认的实现查找一个名为“ClassName.methodName”的查询,ClassName是除包名之外的类名。

public List<T> executeFinder(Method method, final Object[] queryArgs) {
     final String queryName = queryNameFromMethod(method);
     final Query namedQuery = getSession().getNamedQuery(queryName);
     String[] namedParameters = namedQuery.getNamedParameters();
     for(int i = 0; i < queryArgs.length; i++) {
             Object arg = queryArgs[i];
             Type argType =  namedQuery.setParameter(i, arg);
      }
      return (List<T>) namedQuery.list();
}
 
public String queryNameFromMethod(Method finderMethod) {
     return type.getSimpleName() + "." + finderMethod.getName();
}

总结

在Java 5之前,Java语言并不支持代码同时具有类型安全和范性的特性;你不得不二者选一。在这篇文章里,你可以看到使用Java 5范型支持并且结合Spring和Hibernate(和AOP)一起来提高生产力。一个范型类型安全的DAO类非常容易编写,所有你需要做的就是一个接口,一些命名查询,并且10行Spring配置,并且可以极大的减少错误,同时节省时间。

代码下载:
j-genericdao.zip

Posted in Java | No Comments »

jBPM 流程部署文件研究

Posted by Nicholas Ding on 29th 五月 2006

jBPM 为流程定义及其相关文件专门使用了一种打包机制,就是.par文件,似乎JBoss很喜欢这样的形式,之前还有为Hibernate提供的.har包。这个.par被称为Process Archive,故名思义,里面包含了流程需要的所有信息。

其实.par文件就是一个简单的zip格式的压缩包。里面的核心文件是processdefinition.xml这个流程定义,当然用Eclipse jBPM插件制作的流程还含有一个流程图片,可以使用jBPM提供的webapp动态标示当前所执行的流程。除次之外,classes这个目录以内的文件都会被动态加载到内存,因为流程里面定义的Action和Task等的实现类都需要去Classpath找,jBPM会在部署.par包的时候用自己的Class Loader加载进去。(PS:也可以直接放在上层Classpath里面,只要能够加载到就可以)

流程部署详解

如果认为一定要使用Eclipse jBPM插件来部署流程的话,那就错了,jBPM插件从一定程度上简化了jBPM开发,尤其是Deployment功能为大家省了不少事情,但是如果要手工部署,怎么做呢?接下来就要研究一下到底部署这个.par文件的时候做了哪些事情。

首先要让Eclipse jBPM的部署功能有效,那么要确保服务器使用jBPM提供的webapp,并且让起Context位于/jbpm这个位置。例如http://localhost:8080/jbpm,那么在jBPM插件里面写上localhost,端口8080,测试一下连接就可以了。那么我们分析一下webapp,发现原来是org.jbpm.webapp.servlet.UploadServlet这个类在起作用。

看一下UploadServlet的代码,看handleRequest里面的内容,用Commons Fileupload做的文件上传,如果文件小直接加载到内存,文件大会用磁盘的临时空间(Fileupload的文档上有解释)。文件上传完毕,那么就执行doDeployment操作。这个doDeployment才是部署的关键入口。

ZipInputStream zipInputStream = new ZipInputStream(fileItem.getInputStream());

这行代码解释了如何加载.par文件,首先作为Zip格式读取,然后得到jbpmContext来进行流程部署。这个JbpmContext也是采用了ThreadLocal,感觉原理上和Hibernate用的差不多(到这篇文章为止,我在Weblogic还没法成功使用这个得到jbpmContext,还只能用jbpmConfiguration来获取)。
JbpmContext jbpmContext = JbpmContext.getCurrentJbpmContext();
ProcessDefinition processDefinition = ProcessDefinition.parseParZipInputStream(zipInputStream);
jbpmContext.deployProcessDefinition(processDefinition);

这样以来,一个流程就这么简单的部署上去了。当然了,如果不想Upload,还可以使用本地文件系统直接部署,需要提供一个URL就可以了,请参考DeployServlet,还可以使用ant进行部署。
这么看来,部署一个流程就变得非常方便了。

部署文件怎么就消失了,到底去了哪里?

在部署完.par流程包之后,仿佛就不再需要这个.par文件了,但是是什么原因能够让服务器不必知道.par的位置而又能每次正常运行这个流程呢?这个问题我诼磨了很久,一个zip文件不可能凭空消失啊,至少他应该存在于引擎可以找到的地方。而这个地方,正是数据库!

看一下jbpm生成的数据库,包含两张比较特别的表jbpm_bytearray和jbpm_byteblock,正是这两张表纯储了.par文件的内容。可以说,他是将zip里面的内容拆开存到了数据库。

mysql> select * from jbpm_bytearray;
+-----+------------------------------------------------------+-----------------+

| ID_ | NAME_                                                | FILEDEFINITION_ |

+-----+------------------------------------------------------+-----------------+

|   1 | processimage.jpg                                     |               1 |

|   2 | gpd.xml                                              |               1 |

|   3 | processimage.jpg                                     |               4 |

|   4 | gpd.xml                                              |               4 |

|   5 | classes/com/sample/action/MessageActionHandler.class |               4 |

+-----+------------------------------------------------------+-----------------+

jbpm_bytearray这张表把.par文件目录存了进去,jbpm_byteblock则是将二进制内容存了进去。可以说如果你的.par文件里面含有Java Bytecode,那么引擎会从数据库读出byte[]数组然后作为类加载,如果你的类存在于引擎可见的Classpath,那么他会从那里面加载。

总结

jBPM在流程的部署上着实下了不少功夫,从流程的部署上可以看到jBPM引擎的一些工作方式,这也有点类似IoC的概念,本身jBPM提供了基于有限状态机的编程模型,这一模型大大的简化了编程难度,同时将流程的定义和实现分离出来,使得可以在流程实现的功能子集定义新的流程。
在流程部署上提供了版本机制,即连续部署两个相同的流程会出现版本增量,总是新建高版本的流程,但是低版本的流程在执行过程中不会因为高版本的部署而自动取消,直到运行完毕。

Posted in Java | No Comments »

AJAX 入门

Posted by Nicholas Ding on 17th 五月 2006

AJAX in Action

像其他人一样,当我看到一下RIA应用,例如Google Maps和Google Suggest的时候我都非常惊讶。我希望知道是如何实现的。现在,谜底揭开了,那就是AJAX。这是在我花了一段时间研究AJAX之后才知晓的。这里有一个很好的例子让我们知道AJAX是如何很好的应用在 JavaRSS.com 里面的。

什么是AJAX:
AJAX 是一个架构(architecture)并不是一种技术。AJAX代表异步的JavaScript和XML。
妙语(Punch Line):
延迟加载
问题:
当JavaRSS.com首页加载时,他同时加载了所有条目的介绍(如果你在设置中激活了)。这些介绍只有当你鼠标移动到该条目的上面的时候才显示。

现在的问题是用户不可能是鼠标移过所有的条目,所以预先加载所有的介绍不是个好主意。

解决方案: 使用AJAX,当鼠标移过的时候从服务器动态加载条目的介绍。

这么做可以使初始页的加载大小减小一半甚至更多,这样一来页面加载就更快,就内能得到一个更好的用户体验。

时序图:

AJAX Sequence Diagram

我们首先会在onmouseover事件中调用JavaScript函数getDescription。下面是html代码:

<a href="/" onmouseover="getDescription(3,1)">Java & J2EE News</a>

下面是 getDescription 函数的代码:

var url = 'http://localhost:8080/getDescription.jsp?channelId=' + channelId + '&itemId=' + itemId;
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }
    req.onreadystatechange = processRequest;
    req.open("GET", url, true);
    req.send(null);

XMLHttpRequest 对象将用来进行http连接并取回xml文档。我们需要检测一下是否是IE并且创建 XMLHttpRequest 对象。

if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }

设置回调函数,并且发送”GET”请求至服务器接收xml文档:

req.onreadystatechange = processRequest;
    req.open("GET", url, true);
    req.send(null);

JSP将根据适当的条目编号创建具有相应介绍的xml文档。

<%
String channelId = request.getParameter("channelId");
String itemId = request.getParameter("itemId");
//String description = new Channel(channelId).getItemDescription(itemId);
String description = "This is the description for the channelId: " + channelId + "and itemId: " + itemId;
 
if (description != null) {
   response.setContentType("text/xml");
   response.setHeader("Cache-Control", "no-cache");
   response.getWriter().write("<description>" + description.toString() + "</description>");
} else {
   //nothing to show
   response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
%>

检测HTTP请求返回状态码,状态为200,即OK。

function processRequest() {
    if (req.readyState == 4) {
        if (req.status == 200) {
          parseMessages();
        } else {
  alert ( "Not able to retrieve description" );
        }
    }
}

readyState = 4 的情况下文档被加载。

readyState Status Codes:

  • 0 = uninitialized
  • 1 = loading
  • 2 = loaded
  • 3 = interactive
  • 4 = complete

最后,我们解析XML文档并显示介绍。

问题: 唯一的问题就是我遭遇到的 “&” 字符。 “&” 在XML文档里面不是一个有效字符。所以我需要将他转换成 “&amp;”。

function parseMessages() {
	response  = req.responseXML.documentElement;
	itemDescription = response.getElementsByTagName('description')[0].firstChild.data;
	alert(itemDescription);
}

下面是所有的代码:

HTML Code:

<a href="/" onmouseover="getDescription(3,1)">Java & J2EE News<a>

JavaScript Code:
function getDescription(channelId,itemId) {
    var url = 'http://localhost:8080/getDescription.jsp?channelId=' + channelId + '&itemId=' + itemId;
    if (window.XMLHttpRequest) {
        req = new XMLHttpRequest();
    } else if (window.ActiveXObject) {
        req = new ActiveXObject("Microsoft.XMLHTTP");
    }
    req.onreadystatechange = processRequest;
    req.open("GET", url, true);
    req.send(null);
}
 
function processRequest() {
    if (req.readyState == 4) {
        if (req.status == 200) {
          parseMessages();
        } else {
          alert ( "Not able to retrieve description" );
				}
    }
}
 
function parseMessages() {
	response  = req.responseXML.documentElement;
	itemDescription = response.getElementsByTagName('description')[0].firstChild.data;
	alert ( itemDescription );
}

JSP Code:
<%
String channelId = request.getParameter("channelId");
String itemId = request.getParameter("itemId");
description = "This is the description for the channelId: " + channelId + "and itemId: " + itemId;
 
if (description != null) {
   response.setContentType("text/xml");
   response.setHeader("Cache-Control", "no-cache");
   response.getWriter().write("<description>" + description.toString() + "</description>");
} else {
   //nothing to show
   response.setStatus(HttpServletResponse.SC_NO_CONTENT);
}
%>

资源:

使用AJAX的Google站点:

关于作者:

  • Jay 具有10年以上的IT工作经验,并且自从Java & J2EE诞生那天起就开始接触他们了。
  • 译者Nicholas@NirvanaStudio

Posted in Ajax | No Comments »

关注 JBossWeb

Posted by Nicholas Ding on 29th 四月 2006

介绍

JBossWeb 是 JBoss 2006 年新产品线中的一个非常重要的部分。他基于 Apache Tomcat 开发而成,但是 JBoss 对 Tomcat 进行了额外的开发。Tomcat 是一个纯 Java 实现的 Web 服务器,想必大家在实际部署的时候不单单是只部署 Tomcat,还需要同时部署 Apache Web Server,通过mod_jk 来整合 Tomcat,这个配置相当繁琐,而且性能未必优越,jk、jk2 的站点似乎很长时间没有再更新了。JBoss 对 Tomcat 的调整力度非常大,JBossWeb 已经不是简单的包含了 Tomcat,而是对 Tomcat 做了本地化,使用了Apache APR,即Apache Portable Runtime 对 Tomcat 底层 API 做了一个本地化实现,下面一副图引子 JBossWeb 的站点,对 JBossWeb 和 Tomcat 做了一个性能比较。

JBossWeb

架构

Tomcat 以内嵌的方式集成到 JBoss 中,基于 JBoss Microkernel 架构,这样以来,在 Tomcat 上就可以使用到更全面的功能,譬如JBoss提供的数据库连接池(感觉比Tomcat配置方便多了),真正的 JNDI 支持,当然还有 JTA 等以来 JNDI 基础设施的服务。

JBossWebArch

JBossWeb 不仅仅支持 JSP 等 Java 技术,同时他的架构还支持其他 Web 技术的集成,譬如 PHP、.NET 两大阵营的正和。到目前为止,JBossWeb 提供了一个与 PHP 集成的例子,通过 JNI 库调用 PHP,可以通过 servlet-mapping 调用 PHP 处理引擎处理 PHP,在此之前,要做到这一点需要配置 Apache 服务器,但现在,JBoss 做到了这一点,而且非常方便。

JBossWebBlock

下面的图介绍了Tomcat Native的基础架构,依仗于 APR 的支持,可以像 Eclipse 一样运行于各种平台,同时又可以提升性能。

TNative

URL 重写

URL 重写是 Apache 一个非常重要的部分,mod_rewrite 模块起到了非常大的作用。现在 JBossWeb 同样支持了这个功能,基本配置和 mod_rewrite 几乎一样,而且同样支持全局配置和针对每个Context进行配置。

Posted in Java | 2 Comments »

理解 ANTLR 语法文件

Posted by Nicholas Ding on 26th 四月 2006

理解 ANTLR 语法文件

译者:Nicholas @ NirvanaStudio

原文出处:http://www.placidsystems.com/articles/article-grammarlayout/grammarLayout.htm

你是否被Antlr语法文件的不同部分搞的晕头转向呢,你是否很想知道这些部分的含义呢?我们在这里将从另一个方面来了解它们,这里我们使用Antlr Studio来演示。

不管你信不信,Antlr语法文件在某种程度上和Java源代码很类似。什么,你看不出任何与Java代码相似的地方?好,就让我给你展示……

语法(Grammar)

1

有放在header部分的内容将会出现在Antlr生成的Java代码的头部。站在Java文件的层次上思考,你通常在这个部分里面放置包定义。你还可以把一部分import的内容放在这里。

2这个部分的代码对于文件中的每个语法来说是唯一的。这个部分会被放在类声明之前。以上的例子将只对CalcParser引入ArrayList和MyClass这两个类。

3 width="20" />然后我们开始定义语法,这看起来像是在声明一个类。

4 width="20" />在这个options部分,你可以制定语法生成的一些参数。在Antlr Studio中你可以使用Ctrl+Space看看哪些选项可用。

5 width="20" />Token部分用来制定“假想的”记号,那些通常没有声明在lexer中。这些是在TreeParsers中使用的“假想的”记号。

6 width="20" />另一个行为部分。这个部分的内容被放在类内部。你可以为你的解析器定义一些自定义方法。

规则(RULES)

7 width="746" />

Antlr语法文件中的规则定义对应生成的Java代码中的一个方法定义。

1 width="20" />2 width="20" />3 width="20" />4 width="20" />正如你所看到的,这里我们可以在规则中做任何事,这些可以通过一个函数完成。我们可以为规则指定参数,上面的例子显示了这一点,甚至可以指定返回值和异常。

5 width="20" />这个options部分允许你指定一些可选参数。

7 width="20" />我们可以指定自定义异常处理机。

Java and all Java-based trademarks and logos are
trademarks or registered trademarks of Sun Microsystems, Inc. in the United
States, other countries, or both.

Posted in 编译器 | 1 Comment »