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