ajoo

2006年2月10日

用Neptune来交互执行ant任务
一般来说。ant的任务都是要写一个build.xml文件,然后删删改改,最后运行ant target。这自是正途。但是有时候,如果你需要的就是简单试验一下某个task,或者就想拿某个ant task当成shell命令的话,就不方便了。

在Neptune这个工具里面,附带了一个shell。这个shell允许交互执行Neptune的Command和Ant的Task。可以在这里下载:
http://docs.codehaus.org/display/JASKELL/Downloads

下面简要介绍一下怎么用:

启动Neptune shell很简单。按照要求安装完了之后(就是设置一个NEPTUNE_HOME和PATH环境变量),运行:
np -i

shell启动后,你可以看见:
Neptune Interactive
>
这个">"是命令提示符。现在你可以输入命令了。

先看看最简单的ant echo任务。xml语法是这样的:
<echo message="hello world"/>
对应的neptune语法是
> ant.echo{message="hello world"}->exec
>
starting command Echo ...
task Echo: hello world
command Echo executed.

稍微解释一下:
1。所有ant命令都在ant这个namespace里面。
2。xml的尖括号换成大括号。大括号在jaskell语言里面表示一个tuple。
3。exec函数用来执行一个命令。如果没有exec,ant.echo只是生成一个命令对象,但不执行。这样做得好处是,你可以用变量临时存储这些命令对象,可以把他们存在任意的对象或者数组中,等到真正需要执行的时候再运行。
4。"->"这个函数用来以先参数后函数的循序调用函数,相当于exec(ant.echo{...}),语法上有的时候显得更漂亮些。



再来看个复杂点的。前两天我发现需要把某个目录下所有反编译出来的jad文件改名成java文件。dos的rename命令居然不支持。于是我就祭起法宝:
> rename {dir,from,to} = ant.move{todir=dir}.with [
>   ant.types.fileset{dir}.with [
>     ant.types.include{name="**/$from"}
>   ],
>   ant.types.mapper{type="glob",from,to}
> ] ->exec
>
=> rename()

这个东西定义了一个函数,接下来,我就可以拿rename当作一个简洁的命令了:
> rename{dir="decompiled",from="*.jad",to="*.java"}
> 
starting command Move
command Move executed.
>


后来,我把这个rename函数稍微完善了一下,增加了一个recursive参数来控制是否处理子目录,缺省值是false,给dir设置当前目录为缺省值。代码放在一个可以重用的脚本文件里面。现在可以这样做了。先把函数加载进来:

> helper = import {resource="jfun/neptune/ant/anthelper.jsl"}
>


然后调用:
> helper.rename{from="*.jad",to="*.java",recursive=true}->exec
>

呵呵。

这个工具可以调用任意的ant任务。还有很多好用的函数。具体请见:
http://docs.codehaus.org/display/JASKELL/Neptune
posted @ 2006-02-10 13:06 ajoo 阅读(1281) | 评论 (0) | 编辑 收藏
 

2006年1月22日

一个方便的java shell
这两天完善了我的jaskell语言的一个shell。

这个shell虽然是jaskell的,但是也可以作为一个交互式执行java代码的解释器。对于想快速地试试某个api比较有用。

相比于eclipse scrapebook,它的好处是更方便,而且,jaskell的一些函数式的特性让你可以写出更加简洁的代码。

下面举几个例子:

轻松玩Swing

打开shell,它显示这样:

Jaskell Shell version 
0.5
>
然后假设你要运行一下javax.swing.JOptionPane.showInputDialog()函数的话,你可以这样写:
> javax.swing.JOptionPane.showInputDialog["your age?"]
>
回车两下(第一下,就是简单折行,因为你可以接着写下一行代码,只有连续两下折行,shell才认为你是要执行)
结果就会出现一个简单的swing对话框。在对话框里面输入年龄“13",回车,
shell里面就会显示:13。


这里面,一点需要注意的,java方法调用不用圆括号,而是方括号。你可以把这个理解为一个reflection调用,传递的永远都是一个数组。

下面,假设你想重复地使用JOptionPane这个类,使用showInputDialog, showConfirmDialog这类的方法,总这么写javax.swing.JOptionPane也够麻烦的。我们可以简化它:
> dialog = javax.swing.JOptionPane
>
=> class javax.swing.JOptionPane
当你回车两次后,shell在"=>"提示符后面自动显示这个表达式的值:"class javax.swing.JOptionPane"。

下面我们可以重复使用dialog变量了。在这之前,我们可能想看看JOptionPane到底都支持什么静态方法,我们可以用"?"来让shell告诉我们
> ? dialog
这个"?"不是jaskell语言的一部分,而是shell的命令,所以不需要回车两次。回车,shell就会把JOptionPane的所有方法都列出来。

然后假设我们选择showMessageDialog,可以这样写:
> dialog.showMessageDialog[null, "hello world"]
>
然后,一个"hello world"的对话框就弹了出来(看不见?找一找。它可能被藏在你的当前窗口后面了。)

更简化一点,假设我要重复showMessageDialog若干遍,我可以这样写:
> say msg = dialog.showMessageDialog[null, msg]
>
=> say()
这个表达式的值是一个接受一个参数的,叫做say的函数。

下面你可以say很多东西啦:
> say "how are you?"
>

> say "java sucks!"
>
等等等等。



傻瓜多线程

好,看过了JOptionPane,我们来看看多线程。下面是用这个语言怎么启动一个线程:
> Thread.new[(\_->System.out.println["hello world"]) `implements Runnable].start[]
>
hello world
这里面Thread.new大概不需要解释。这里对构造函数的调用是Ruby风格的ClassName.new,而不是java的new ClassName。

构造函数也需要用一个list来传递参数。我们这里传递的是一个Runnable对象。
用来实现Runnable接口的是一个匿名函数,这个函数不管参数是什么,一旦调用,就println一下。

implements 是一个函数,它负责用一个函数来动态生成一个实现某接口的proxy出来。它前面的那个反向单引号表示把一个函数以中缀语法调用,所以 (somefunction `implements Runnable)等价于implements(somefunction, Runnable)。

"\"符号是lamda函数表示法。"->"符号前面的是函数参数,后面的是函数体。这里因为我们不使用这个参数,所以用"_"这个通配符。

最后,我们调用start[]方法来执行这个线程。


我们还可以用标准的"const"函数来让代码更简短一点。"const x"语义上完全等价于"\_->x"。另外,我们也可以用java风格的new操作符函数来写,jaskell对两者都支持的:
> new Thread[const(System.out.println["hello world"]) `implements Runnable].start[]
>
hello world


然后,考虑到重用,我们可以这样,先把System.out.println搞短一点,每次敲这么长太麻烦:
> println msg = System.out.println[msg]
>
=> println()

(实际上,println函数是系统已经缺省就定义好的了。你完全没有必要自己定义println就可以直接用了。这里只是演示一下怎么自己定义函数)


然后,把那段启动线程的代码写成函数:
> run task = Thread.new[const task `implements Runnable].start[]
>
=> run()

好了,下面我们可以任意启动线程做事情了:
> run(println "hello world")
>
hello world

> run(println "pei!")
>
pei
!

> run (say "nice!")
>

最后一个say "nice!",如果不用单独线程的话,运行后这个对话框将阻塞当前线程。现在用run来运行它,就不会阻塞了。


今天你fp了吗?

最后再随便看看jaskell作为函数式语言的本分所能做的一些事。

foreach函数:

> foreach [1,2,"hello"] println
>
这个函数把1,2,"hello"三个都打印一遍。

> foreach(list 1 100, println)
>
这个函数把数字1到100按顺序打印一遍。

map函数:
> map (\x->Integer.parseInt[x]) ["1","2","3"]
>
=> [
1,2,3]
这个函数把一个字符串列表转换成一个整数列表。


filter函数:


> filter(\x->x<10, list 1 100)
>
=> [
1,2,3,4,5,6,7,8,9]
这个代码把1到100中所有小于10的整数都取出来。

filter函数的第一个参数是一个函数,这个函数对列表中的每一个元素都进行判断,返回true或者false。

find函数:

> find(3, list 1 100)
>
=> 2
这个代码在列表中寻找整数2,如果找到,返回找到的位置(0为第一个)


lookup函数:

> lookup(\x->x*x>x+50, list 1 100)
>
=> 7
这个代码在列表中寻找第一个符合x*x>x+50的元素,找到就返回位置。

@函数:

上面我们知道找到的数字是在位置7,可以用@来得到位置7的值:

> list 1 100 @ 7
>
=> 8
好,这个数是8。


sum函数:

> sum(list 1 100)
>
=> 5050


注意,最难的来了!
fold函数:

> fold (*) 1 [1,2,3,4,5]
>
=> 120
这个fold函数以1为初始值,然后对每个列表元素,把它和当前值做乘法,用结果更新当前值,最后把计算结果返回。所以这段代码实际上做的就是把从1到5乘起来。
(*)是一个乘法函数。

你也可以用自己写的:
>  fold (\x y->x*y) 1 [1,2,3,4,5]
一样的。


行了,差不多了。下载在:

http://dist.codehaus.org/yan/distributions/jaskell.zip

把jar文件都放到你的classpath里面,然后运行jfun.jaskell.shell.Shell类就行了。

posted @ 2006-01-22 02:13 ajoo 阅读(2514) | 评论 (0) | 编辑 收藏
 

2006年1月13日

一个使用Spring transaction template的小工具
昨天在千年妖精丽晶大宾馆,看见sparkle同学抱怨Spring的transaction template不好用。因为一些我没有问的原因,他们不能使用声明式事务,所以就只剩下两个选择:
1。直接用hibernate事务。
2。用spring的TransactionTemplate。

直接用hibernate事务有以下问题:
1。代码完全绑定在Hibernate上。
2。自己控制事务难度比较大,不容易处理得好。

对第二点,很多人可能都不以为然,不就是一个beginTransaction和一个commit(), rollback()么?太门缝里看人了吧?

我就举个sparkle同学的小弟写的代码吧:
try{
   PetBean pet 
= ;
   
   beginTransaction();
    ssn.delete();
    
    commit();
    petLog();
  }
  
catch(Exception e){
     rollback();
     
throw e;
  }

一个大的try-catch块,在出现任何异常的时候都rollback。

我想会这么写的人应该不在少数。同志们,革命不是请客吃饭那么简单地。

问题在哪?

1。最严重的。try里面一旦有Error抛出,rollback()就不会被执行。sparkle同学说,出了Error我们就不管了。可以,反正出Error的几率大概很小。所以你的软件可以说“大多数情况是可以工作地”。

2。这块代码最终抛出Exception!如果外面直接套一个函数的话,签名上就得写"throws Exception"。这种函数就一个字:“害群之马”。你让调用者根本不知道会出什么异常,笼统地告诉人家“什么情况都可能发生”可不是负责任的态度。

3。这个代码依赖于rollback()的特定实现。因为一旦exception是在beginTransaction()之前或者beginTransaction()时候抛出的,那么本来不应该调用rollback()的。调用 rollback()会出什么结果呢?如果rollback()不检查当前是否在事务中,就坏菜了。而且,就算rollback()做这个检查,嵌套事务 也会把一切搞乱。因为很有可能整块代码是处在另外一个大的事务中的。调用我们的代码在我们抛出异常的时候,也许会选择redo,或者修复一些东西,不见得总是选择回 滚它那一层的事务的,不分青红皂白地就rollback上层事务,这个代码的健壮性真的很差。

看,小小一段代码,bug和潜在问题如此之多。你还说自己写事务控制简单吗?

真正健壮的,不对外界和调用者有多余的假设依赖的代码,可以这样写:

 PetBean pet = ;
  
 beginTransaction();
 ok 
= false;
 
try{
    ssn.delete();
    
    ok 
= true;
    commit();
   
    petLog();
  }
  
finally{
     
if(!ok)
        rollback();
  }

放弃try-catch,改用try-finally。这样就不需要捕获异常再抛出那么麻烦。然后用一个bool变量来告诉finally块是否需要回滚。

这个代码不难理解,但是如果处处都用这个代码,也够丑陋的。

既然已经用了spring,为什么不用spring的TransactionTemplate呢?用Spring TransactionTemplate(下面简称tt)的好处如下:
1。事务代码不依赖hibernate,便于移植。
2。自动得到异常安全,健壮的事务处理。写代码的时候几乎可以完全忘记事务的存在。

当然,sparkle同学有他的道理。使用spring tt需要实现TransactionCallback接口。而java的匿名类语法非常繁琐。更可恨的是,匿名类只能引用定义成final的局部变量,这 样在从tt里面往外传递返回值的时候就非常不方便。我们可能需要这么写:
//xxx
  Object[] result = (Object[])tt.execute(new TransactionCallback() {
      
public Object doInTransaction(TransactionStatus status) {
          Object obj1 
= new Integer(resultOfUpdateOperation1());
          Object obj2 
= resultOfUpdateOperation2();
          
return new Objetct[]{obj1,obj2};
      }
  });
  System.out.println(((Integer)result[
0]).intValue()+1);
  System.out.println(result[
1]);

多么丑陋的result[0], result[1]呀。其它还有一些变体,比如每个结果用一个Object[],或者定义一个通用的Ref类来支持"get()"和"set()"。

可是,这么多的方案,sparkle同学都不满意。也是,这些方案都免不了类型不安全的down cast。而处理原始类型的结果还需要装箱!


因为这些原因,我构思了一个简单的spring tt的wrapper。一个Tx类。这个Tx类可以这么用:
//xxx
new Tx(){
  
private result0;
  
private String result1;
  
protected void run(){
      result0 
= resultOfUpdateOperation1();
      result1 
= resultOfUpdateOperation2();
  }
  
protected Object after(){
    System.out.println(result0
+1);
    System.out.println(result1);
    
return null;
  }
}.exec(tt);

通过把局部变量定义成Tx类的成员变量,我们绕过了downcast和原始类型装箱拆箱的麻烦。通过把事务之后要执行的动作封装在after()这个成员函数里面,我们可以方便地引用run()里面产生的结果。


下面看看Tx, TxBlock, TransactionBlockException这三个类的设计:

public abstract class TxBlock implements TransactionCallback{
  
protected void before()
  
throws Throwable{}

  
protected abstract void run(TransactionStatus status);
  
protected Object after()
  
throws Throwable{
    
return null;
  }
  
protected void lastly(){}
  
public final Object exec(TransactionTemplate tt){
    
try{
      before();
      tt.execute(
this);
      
return after();
    }
    
catch(RuntimeException e){
      
throw e;
    }
    
catch(Error e){
      
throw e;
    }
    
catch(Throwable e){
      
throw new TransactionBlockException(e);
    }
    
finally{
      lastly();
    }
  }
  
public Object doInTransaction(TransactionStatus status){
    run(status);
    
return null;
  }
}



public abstract class Tx extends TxBlock{
  
protected abstract void run();
  
protected void run(TransactionStatus status) {
    run();
  }
}

public class TransactionBlockException extends NestedRuntimeException {

  
public TransactionBlockException(String arg0, Throwable arg1) {
    
super(arg0, arg1);
  }

  
public TransactionBlockException(String arg0) {
    
super(arg0);
  }
  
public TransactionBlockException(Throwable e){
    
this("error in transaction block", e);
  }
}

所有的代码都在这了(除了import)。
这个小工具除了after(),还支持before(), lastly()。before()在事务开始前运行。after()在事务结束后运行。lastly()保证不管是否出现异常都会被执行。

如此,一个薄薄的封装,spring tt用起来庶几不会让sparkle再以头撞墙了。

posted @ 2006-01-13 07:49 ajoo 阅读(2632) | 评论 (0) | 编辑 收藏
 
仅列出标题  
 
<2025年7月>
日一二三四五六
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789

 导航

  • BlogJava
  • 首页
  • 发新随笔
  • 发新文章
  • 联系
  • 聚合
  • 管理

 统计

  • 随笔: 3
  • 文章: 11
  • 评论: 10
  • 引用: 0

常用链接

  • 我的随笔
  • 我的评论
  • 我的参与
  • 最新评论

留言簿(3)

  • 给我留言
  • 查看公开留言
  • 查看私人留言

随笔档案

  • 2006年2月 (1)
  • 2006年1月 (2)

文章分类

  • 名著类(11) (rss)

文章档案

  • 2006年1月 (11)

搜索

  •  

最新评论

  • 1. re: 论面向组合子程序设计方法 之一 创世纪[未登录]
  • 大神.我想转载 你这个文章到我qq 空间.. (非公开 非盈利.老衲空间只对亲人开放.233) 求准奏
  • --binbin
  • 2. re: 论面向组合子程序设计方法 之二 失乐园
  • gp应该是generic programming,泛型编程,用来做type abstraction的
  • --pianyu
  • 3. re: 论面向组合子程序设计方法 之二 失乐园
  • 哦,原来是上一行提到的PO = =
  • --bombless
  • 4. re: 论面向组合子程序设计方法 之二 失乐园
  • "他们,PO, OO,都是命令式的"此处疑为“GP”的笔误
  • --bombless
  • 5. re: 论面向组合子程序设计方法 之二 失乐园
  • 真知灼见,写得很棒!
  • --晨曦语晴

阅读排行榜

  • 1. 一个使用Spring transaction template的小工具(2632)
  • 2. 一个方便的java shell(2514)
  • 3. 用Neptune来交互执行ant任务(1281)

评论排行榜

  • 1. 用Neptune来交互执行ant任务(0)
  • 2. 一个方便的java shell(0)
  • 3. 一个使用Spring transaction template的小工具(0)

Powered by: 博客园
模板提供:沪江博客
Copyright ©2025 ajoo