日期:2014-05-16 浏览次数:20715 次
要做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]
?
入侵成功。