日期:2014-05-16 浏览次数:20714 次
要做Audit Trail,即跟踪记录对数据的所有CRUD操作,实现方式大体分两种类型:
1. Brute Force型,即在代码中写大量的类似于这样的代码
new OpLog(
    // properties ...
).save()
2. 技术技巧型,即使用优雅的事件监听机制。
?
由于使用grails/groovy和db4o,实现Audit Trail显得尤为简洁。
?
首先,写一个简单得不能再简单的打入db4o内部的(不在乎侵入不侵入了,反正已经吊死在grails和db4o上了,反正大家都已经紧密地结合了,反正谁都离不开谁了)持久化事件引擎:
package com.db4o.internal
public class InvasivePersistentEventEngine {
	
	private static HANDLERS = [:]
	static on(evt, handler) {
	    if(HANDLERS[evt] == null) {
	        HANDLERS[evt] = new HashSet()
	    }
	    HANDLERS[evt] << handler
	}
	static fireEvent(evt, obj) {
	    if(!HANDLERS[evt]) {
	        return
	    }
	    for(h in HANDLERS[evt]) {
	        h(obj)
	    }
	}
}
?
然后,稍稍修改一下db4o的两个类(从代码可以看出,是受到db4o的Log Message的启发,只要在configure一下messageLevel,把它设置为自己指定的Integer.MIN_VALUE就可以了)
public abstract class ObjectContainerBase  implements TransientClass, Internal4, ObjectContainerSpec, InternalObjectContainer {
    //...	
    public final int store3(Transaction trans, Object obj, int updateDepth, boolean checkJustSet) {
        //。。。
        if (configImpl().messageLevel() > Const4.STATE) {
            message("" + ref.getID() + " new " + ref.classMetadata().getName());
        }
        //+S.C.
        else if (configImpl().messageLevel() == Integer.MIN_VALUE) {
            InvasivePersistentEventEngine.fireEvent("new", ref.getObject());
        }
	//。。。
    }
}
?
?
public class ObjectReference extends PersistentBase implements ObjectInfo, Activator {
    //...
    private void logEvent(ObjectContainerBase container, String event, final int level) {
        if (container.configImpl().messageLevel() > level) {
            container.message("" + getID() + " " + event + " " + _class.getName());
        }
        //+S.C.
        else if (container.configImpl().messageLevel() == Integer.MIN_VALUE) {
            if(event == "update") {
                InvasivePersistentEventEngine.fireEvent(event, getObject());     
            }
        }
    }
}
?
小小地测试一下,先注入handlers。在Web Console中执行
import com.db4o.internal.InvasivePersistentEventEngine
import framework.utils.GroovyDataUtils
InvasivePersistentEventEngine.on('new', {o->
    println "object created [${o.id}, ${o.version}]: " + GroovyDataUtils.getProperties(o)
})
InvasivePersistentEventEngine.on('update', {o->
    println "object ${o.deleted?'deleted':'updated'} [${o.id}, ${o.version}]: " + GroovyDataUtils.getProperties(o)
})
?
然后执行
new User(username:'t@t.cc', password:'**').save()
输出object created [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 0]: [username:t@t.cc, locked:null, password:**, profile:null, profileId:null]
继续执行
def user = User.find(username:'t@t.cc') user.password = 'password_changed' user.save()
输出object updated [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 1]: 
[password:password_changed, username:t@t.cc, locked:null, 
profile:null, profileId:null]
object created 
[16eb7e6c-8453-4e3d-8ddb-281470ed64f7_bak_0, 0]: [username:t@t.cc, 
locked:null, password:**, profile:null, profileId:null]
最后执行删除看一下
def user = User.find(username:'t@t.cc') user.delete()
输出object deleted [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 1]: [password:password_changed, username:t@t.cc, locked:null, profile:null, profileId:null]
?
入侵成功。