Convert a Python function to Java anonymous class

When calling Java with Jython, anonymous inner class might be an issue because there is no such equivalent in Python.

In GUI programming, jython made additional effort on AWT event processing. You can pass a python function as some types of event listener.

def change_text(event):
    print 'Clicked!'

button = JButton('Click Me!', actionPerformed=change_text)
frame.add(button)

Described in the Definitive Guide of Jython:

This works because Jython is able to automatically recognize events in Java code if they have corresponding addEvent()* and *removeEvent() methods. Jython takes the name of the event and makes it accessible using the nice Python syntax as long as the event methods are public.

However, it does not work with Runnable, Callable and many other interfaces designed for anonymous usage.

To solve the incompatibility, I created a small decorator that converts Python function to a Java object.

Thanks to python’s dynamic magic, we can create class in runtime and assign modified method to a particular instance. The conversion is performed once the function loaded. Also, you can pass something as arguments to constructor.

With anonymous_class decorator, the example above can be written as:

from javax.swing import JButton, JFrame
from java.awt.event import ActionListener

frame = JFrame('Hello, Jython!',
            defaultCloseOperation = JFrame.EXIT_ON_CLOSE,
            size = (300, 300)
        )

@anonymous_class(ActionListener, "actionPerformed")
def change_text(dummy, event):
    print 'Clicked!'

button = JButton('Click Me!', actionPerformed=change_text)
frame.add(button)
frame.visible = True

GPars的Actor实现

Actor是一种Continuation技术,可以在少量的线程运行大量Actor对象。Actor对象之间通过消息机制进行交互。而Actor本身线程安全,这样的模型使并发编程的复杂度降低,同时也在一定的场景下实现了可扩展性。

gpars是Java和Groovy都可以使用的并行编程库,他实现了Actor、Agent、DataFlow等模型,旨在为Groovy提供高层的并行编程模型。以下分析gpars 0.12中非阻塞Actor的实现。

@Grab("org.codehaus.gpars:gpars:0.11")
import groovyx.gpars.actor.Actors

def worker = Actors.actor {
    loop {
        react {
            reply it.reverse()
        }
    }
}

def console = Actors.actor {
    worker << "Hello GPars"
    react {
        println it
    }
}

console.join()

首先,在工厂类中Actors里会初始化一个DefaultPGroup用来封装后台的线程池、管理actors。Actors默认使用ResizeablePool,他是JDK Concurrent Framework中的ThreadPoolExecutor的封装,coreSize和maxSize不同所以称Resizeable。

Actors的工厂方法actor生成的是DefaultActor,它是非阻塞actor的默认实现。(ActorGroup:67)

DefaultActor的构造方法接受一个Groovy的闭包对象,将其封装为DefaultActorClosure对象后,调用其父类AbstractLoopingActor的initialize方法(DefaultActor:73).

initialize方法创建一个Runnable对象AsyncMessagingCore,并将线程池传递给core对象。(AbstractLoopingActor:57)AsyncMessagingCore对象负责消息的传递和处理,是线程池处理的目标对象。

调用start启动actor后,actor会向自己发送一个start消息(AbstractLoopingActor:173).
core获得start消息后,调用DefaultActor覆盖的handleStart方法(DefaultActor:328)。

在handleStart中,actor会调用用户传入的闭包方法。上面的例子是一种典型的用法,loop是DefaultActor中的方法,loop也并不是无限空转的,他仅在收到消息被时被触发(DefaultActor:191)react也是DefaultActor中的方法,它将nextContinuation方法设为内部闭包对象,用来处理actor接收的消息。

向Actor发送消息,是通过actor的send方法和重载的leftShift运算符进行操作。(AbstractLoopingActor:236)actor调用core的store方法,将ActorMessage压入core的队列中。入队列之后,core会检查锁对象activeUpdater,判断当前core是否在线程处理中,如果不在,则将core加入线程池中处理。activeUpdater是一个AtomicIntegerFieldUpdater对象,他的compareAndSet可以保证原子性。而通过activeUpdater也可以保证同一时刻只有一个core被线程池处理,从而使actor的线程不安全代码也线程安全地运行。

进入线程池后,core首先将自己放进threadlocal对象中,并保存当前线程的引用。然后会循环消费MessageQueue中的消息直至Queue的可处理部分为空。(AsyncMessagingCore:126)。handleMessage在AbstractLoopingActor中被覆盖,会根据消息的类型进行分发调用(前面提到的start消息就是一种)。默认的业务消息,在DefaultActorClojure中调用DefaultActor的onMessage方法处理。

onMessage中,react的闭包会被调用来处理业务。之后nextContinuation被置为null,这时loop闭包被重新调用,react闭包重新被赋给nextContinuation。这部分代码就是前面所说的loop并非空转,而是在消息处理完成后重新准备而已。

此外,core的MessageQueue的实现是DefaultMessageQueue。它使用两个LinkedList作为输入(向actor输入)队列和输出队列,当输入队列为空时,通过同步方法swap交换输入输出队列。swap是整个actor系统里唯一一个同步方法。这样的机制保证actor的core在线程池中处理时,外界仍然可以向actor发送消息,消息会在actor被调度出线程池之前全部处理掉。不过,他的前提是只有一个线程读这个队列,这个条件在actor系统里,通过core对象的activeUpdater可以有效的保证。

Actor模式采用这种onDemand方式的线程使用,允许大量的actor共存,并只有活跃的actor会占用线程,非活跃状态的actor处在dettach状态,并不消耗计算资源,取消了空转的loop。

GPS Track of Train K221: Nanjing to Guangzhou

GPS Logger完成了它有生以来最长的一次旅行,1477分钟,行程1767公里,途径江苏、安徽、江西、湖南、广东,包含了宁芜线、浙赣线、京广线等。有了GPS终端,贴地的旅途就变成一件非常严肃的事情。这周末去西安,这个GPS也是16个小时上铺车程唯一的安慰了。
Railway-K221 - Viking

这个数据分享在OpenStreetMap上,也许你会感兴趣:
http://www.openstreetmap.org/user/Sunng/traces/996794

告别Ubuntu,迁移到Fedora 15,GNOME 3

Fedora15/Gnome3

昨晚终于下了决心告别Ubuntu的紫色,迎接新的GNOME3桌面。这个想法有一段时间了,Ubuntu从09年生日的时候安装在这台电脑上,已经将近两年的时间,经历了9.04, 9.10, 10.04, 10.10, 11.04五个版本,安装了GNOME, awesome, KDE, LXDE, Openbox, wmii, GNOME3, Ubuntu Netbook等等桌面环境,可以想想那种混乱。而且通过升级获得的新版本,并不能体会到快速开机这样的特性。这次unity发布以后,用了半个月,没有什么眼前一亮的感觉,操作也不太方便,于是换发行版的想法就更加强烈了。

我的系统之前有三个分区,分别挂载/,/home以及/opt。这次更新理想的方式就是格式化/,保留/home和/opt。之前比较讲究规范,所以/下面基本上没有什么需要备份的东西,配置文件都放在/home下面。这样用fedora的livecd安装程序手动配置磁盘分区,只格式化/就可以了。安装的过程非常顺利,重启以后就进入用户配置的界面了。

要把原先的/home/sun分配给新的sun用户,这里我走了一点弯路。系统可以把/home/sun目录配置给新的sun用户作为$HOME,但是目录的owner并不会根据用户名称来匹配。Ubuntu默认第一个用户的UID是1000,而Fedora创建的新用户默认是500. 我现在不确定如果把第一个用户的ID手动指定为1000会不会就直接成功。不过这个问题不大,只要启动之后用root用户把/home/sun目录chown给新的sun用户就可以了,当然文件很多是需要消耗一些时间的。

进入GNOME3桌面之后发现无线网络连不上,而且不弹出密码输入框。因为我最近改了无线的密码,而NetworkManager还是从gnome-keyring里去读旧的密码,所以总是连接失败。这时rm -r ~/.gnome-keyring/ 删除旧密码即可。

桌面的第二个问题是桌面背景仍然是旧的配置(只有颜色没有图片,因为GNOME3默认桌面不由Nautilus管理),这时要毫不留情地删掉所有GNOME2的配置: rm -rf ~/.gnome2 ~/.gconf ~/.gconfd

第三个问题,gnome的applition菜单非常混乱,还存着Ubuntu系统时候的内容。根据freedesktop的标准,用户的菜单配置保存在~/.config/menus里,而用户菜单会从~/.local/share/applications下读取所有的.desktop文件,甚至包括wine也会在这里创建自己的菜单。对付这些内容,一删了之: rm -rf ~/.local/share/applications/*。这样菜单就只会从/usr/share/applications/下读取.desktop文件,这些都是最新安装的。

然后,然后就没有了!就可以开始享受fresh install了,甚至你打开thunderbird,所有的邮件都在那里,什么都不用再做。
Window Selector
如果要比较gnome-shell和unity的话:

  1. gs的启动器比unity要好用,unity的启动器搜索程序之后必须点击图标才能启动,gs可以直接回车启动第一个
  2. gs的dock比unity要好用,其实都与windows7的任务栏类似,但是 unity里打开了一个终端之后,再点这个图标就转到终端,如果想新开一个就无奈了,右键点击也没有新开的选项。(难道是shift+click,来不及试了。。。)
  3. unity的indicator比gs的tray强大多了,这是ubuntu的强项,从10.04开始就用自己的indicator,到了11.04已经有成熟的API,除了官方的,还有很多程序支持,还有天气、系统监控的indicator。gs抛弃了原来的panel applets,现在看天气、看系统状况都无从谈起。尤其是看不到cpu使用情况,心里总是不踏实,天知道firefox+flash又把你的系统烧了多久。

总体来说这次重装效果非常好,基本上没有留下任何瑕疵。断断续续用了四年多的Ubuntu,再见了~