Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Moved to Hibernate based listener to preserve previous state onUpdate

  • Loading branch information...
commit a7471e9a4611bd414b7276f37527a42fa227723c 1 parent 4def57f
Michael J Salera authored
20 BatesGrailsPlugin.groovy
... ... @@ -1,12 +1,13 @@
1 1 class BatesGrailsPlugin {
2 2 // the plugin version
3   - def version = "0.20"
  3 + def version = "0.23"
4 4
5 5 // the version or versions of Grails the plugin is designed for
6 6 def grailsVersion = "2.0 > *"
7 7
8 8 // the other plugins this plugin depends on
9   - def dependsOn = [mongodbMorphia: "0.8.2"]
  9 + def dependsOn = [mongodbMorphia: "0.8.2",
  10 + hibernate: "2.1.1"]
10 11 // executor: "0.3"]
11 12
12 13 // resources that are excluded from plugin packaging
@@ -58,19 +59,28 @@ build an Admin style front end to view and filter thru all log documents, for ex
58 59 // TODO Implement additions to web.xml (optional), this event occurs before
59 60 }
60 61
61   - def doWithSpring = {
62   - businessAuditLogService(com.freerangeconsultants.plugins.bates.core.BusinessAuditLogService)
63   - }
  62 +def doWithSpring = {
  63 + businessAuditLogService(com.freerangeconsultants.plugins.bates.core.BusinessAuditLogService)
  64 +
  65 + auditListener(com.freerangeconsultants.plugins.bates.listeners.BusinessAuditLogListener)
  66 + hibernateEventListeners(org.codehaus.groovy.grails.orm.hibernate.HibernateEventListeners) {
  67 + listenerMap = ['post-insert': auditListener,
  68 + 'pre-update': auditListener,
  69 + 'pre-delete': auditListener]
  70 + }
  71 +}
64 72
65 73 def doWithDynamicMethods = { ctx ->
66 74 // TODO Implement registering dynamic methods to classes (optional)
67 75 }
68 76
  77 +/*
69 78 def doWithApplicationContext = { applicationContext ->
70 79 application.mainContext.eventTriggeringInterceptor.datastores.each { k, datastore ->
71 80 applicationContext.addApplicationListener(new com.freerangeconsultants.plugins.bates.listeners.BusinessAuditLogListener(datastore))
72 81 }
73 82 }
  83 +*/
74 84
75 85 def onChange = { event ->
76 86 // TODO Implement code that is executed when any artefact that this plugin is
3  application.properties
... ... @@ -1,5 +1,6 @@
1 1 #Grails Metadata file
2   -#Sun Dec 02 18:43:39 PST 2012
  2 +#Mon Dec 03 22:54:39 PST 2012
3 3 app.grails.version=2.1.1
4 4 app.name=bates
  5 +plugins.hibernate=2.1.1
5 6 plugins.mongodb-morphia=0.8.2
6 grails-app/services/com/freerangeconsultants/plugins/bates/core/BusinessAuditLogService.groovy
@@ -2,7 +2,6 @@ package com.freerangeconsultants.plugins.bates.core
2 2
3 3 import com.freerangeconsultants.plugins.bates.domain.AuditLogEvent
4 4
5   -
6 5 /**
7 6 * Log audit events from any persistence object to MongoDB sharded cluster
8 7 *
@@ -19,7 +18,7 @@ class BusinessAuditLogService {
19 18 /**
20 19 * Use this method to return a list of the History of the given object id
21 20 *
22   - * @param clazz Class object for example of the Invoice.class
  21 + * @param clazz Class object
23 22 * @param persistedObjectId id of the object caller is looking for
24 23 * @return
25 24 */
@@ -42,10 +41,11 @@ class BusinessAuditLogService {
42 41 */
43 42 def recordLogEvent(String eventType, String className, persistedObjectId, oldeState, newState) {
44 43 //magic of the dataStore
45   - def auditEvent = new AuditLogEvent(eventName: eventType.toString(), className: className, persistedObjectId: persistedObjectId as String)
  44 + def auditEvent = new AuditLogEvent(eventName: eventType, className: className, persistedObjectId: persistedObjectId as String)
46 45 if (oldeState) { auditEvent.oldState = oldeState }
47 46 if (newState) { auditEvent.newState = newState }
48 47 auditEvent.save(flush: true)
  48 + println( "${eventType} for class ${className} Id -> ${persistedObjectId}" )
49 49 print('.')
50 50 true
51 51 }
BIN  grails-bates-0.20.zip
Binary file not shown
3  plugin.xml
... ... @@ -1,4 +1,4 @@
1   -<plugin name='bates' version='0.20' grailsVersion='2.0 &gt; *'>
  1 +<plugin name='bates' version='0.23' grailsVersion='2.0 &gt; *'>
2 2 <author>Michael Salera</author>
3 3 <authorEmail>mikesalera@mac.com</authorEmail>
4 4 <title>Business Audit Trail Events System (BATES) Plugin</title>
@@ -33,6 +33,7 @@ build an Admin style front end to view and filter thru all log documents, for ex
33 33 <plugins />
34 34 <runtimePluginRequirements>
35 35 <plugin name='mongodbMorphia' version='0.8.2' />
  36 + <plugin name='hibernate' version='2.1.1' />
36 37 </runtimePluginRequirements>
37 38 <behavior />
38 39 </plugin>
158 src/groovy/com/freerangeconsultants/plugins/bates/listeners/BusinessAuditLogListener.groovy
... ... @@ -1,23 +1,19 @@
1 1 package com.freerangeconsultants.plugins.bates.listeners
2 2
  3 +import com.freerangeconsultants.plugins.bates.core.BusinessAuditLogService
3 4
4 5 import grails.util.GrailsUtil
5 6 import org.apache.commons.logging.Log;
6 7 import org.apache.commons.logging.LogFactory
7 8 import org.codehaus.groovy.grails.commons.ApplicationHolder
8 9 import org.codehaus.groovy.grails.commons.GrailsApplication
9   -import org.grails.datastore.mapping.core.Datastore
10   -import org.grails.datastore.mapping.engine.EntityAccess;
11   -import org.grails.datastore.mapping.engine.event.*;
12   -import org.grails.datastore.mapping.engine.event.AbstractPersistenceEvent
13   -import org.grails.datastore.mapping.engine.event.AbstractPersistenceEventListener
14   -import org.grails.datastore.mapping.model.PersistentEntity;
15   -
  10 +import org.hibernate.HibernateException
  11 +import org.hibernate.cfg.Configuration
  12 +import org.hibernate.collection.PersistentList
  13 +import org.hibernate.collection.PersistentSet
  14 +import org.hibernate.event.*
  15 +import org.springframework.beans.factory.NoSuchBeanDefinitionException
16 16 import org.springframework.context.ApplicationContext
17   -import org.springframework.context.ApplicationEvent
18   -
19   -import com.freerangeconsultants.plugins.bates.core.BusinessAuditLogService
20   -
21 17
22 18 /**
23 19 * As of Grails 2.0 there is a new API for plugins and applications to register and listen for persistence events.
@@ -29,84 +25,99 @@ import com.freerangeconsultants.plugins.bates.core.BusinessAuditLogService
29 25 * @author Mike Salera
30 26 * @version 1.0
31 27 */
32   -class BusinessAuditLogListener extends AbstractPersistenceEventListener {
33   -
34   -// BusinessAuditLogService businessAuditLogService
35   - Log LOG = LogFactory.getLog(BusinessAuditLogListener.class)
  28 +//class BusinessAuditLogListener extends AbstractPersistenceEventListener {
36 29
37 30
38   - public BusinessAuditLogListener(final Datastore dataStore) {
39   - super(dataStore)
40   - println this.class.name
41   - println "Running on: " + dataStore.class.name
  31 +/**
  32 + * Or you could use the low-level Hibernate Events...
  33 + * Complete list of listeners/events can be obtained at <tt>org.hibernate.event.EventListeners</tt>.
  34 + *
  35 + * @see org.hibernate.event.EventListeners
  36 + * @author Mike Salera, Shawn Hartsock
  37 + *
  38 + */
  39 +class BusinessAuditLogListener
  40 + implements PostInsertEventListener, PreUpdateEventListener, PreDeleteEventListener, Initializable {
  41 +
  42 + def Log LOG = LogFactory.getLog(BusinessAuditLogListener.class)
  43 +
  44 + /**
  45 + * Log insertions made to the current model in the Audit Trail.
  46 + * new objects
  47 + *
  48 + * @param event
  49 + */
  50 + @Override
  51 + public void onPostInsert(PostInsertEvent event) {
  52 + handleEvent('onPostInsert', event)
  53 + }
42 54
43   - }
  55 + /**
  56 + * Log updates made to the current model in the Audit Trail.
  57 + * Return true if the operation should be vetoed
  58 + * @param event
  59 + */
  60 + @Override
  61 + public boolean onPreUpdate(PreUpdateEvent event) {
  62 + handleEvent('onPreUpdate', event)
  63 + return false
  64 + }
44 65
45   - @Override
46   - public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
47   - return true
48   - }
  66 + /**
  67 + * Log deletions made to the current model in the the Audit Trail.
  68 + * Return true if the operation should be vetoed
  69 + * @param event
  70 + */
  71 + @Override
  72 + public boolean onPreDelete(PreDeleteEvent event) {
  73 + handleEvent('onPreDelete', event)
  74 + return false
  75 + }
49 76
50   -@Override
51   -protected void onPersistenceEvent(AbstractPersistenceEvent event) {
52   - handleEvent(event)
53   -}
  77 + def handleEvent(String eventName, event) {
  78 + LOG.info("handleEvent: ${eventName}")
54 79
55   - def handleEvent(AbstractPersistenceEvent event) {
56   - def businessAuditLogService = ContextUtils.getBeanFromApplicationContext("businessAuditLogService")
  80 + def businessAuditLogService = ContextUtils.getBeanFromApplicationContext("businessAuditLogService")
57 81
58 82 def String className = null
59 83 def Object persistedObjectId = null
60 84 def oldState = null // for UPDATES
61 85 def newState = null
62 86
63   - // gotta have entity or entityName
64   - if (!event?.entityObject) { return false }
65   -
66 87 //skip if test mode
67   - if (!GrailsUtil.getEnvironment().equals(GrailsApplication.ENV_TEST)) {
68   - className = event?.entityObject?.class.name ?: 'Category'
69   - persistedObjectId = event?.entityObject?.id?.toString() ?: 1
70   -
71   - switch(event.eventType) {
72   - case EventType.PostInsert:
73   - println ''
74   - println this.class.name + " Got a Persistence event!"
75   - println "POST INSERT ${event.entityObject}"
76   -// newState = getStateMap(event?.persister?.propertyNames, event?.getState())
77   - businessAuditLogService.recordLogEvent('insert', className, persistedObjectId, null, newState)
  88 + if (!GrailsUtil.getEnvironment().equals(GrailsApplication.ENV_TEST) && isAuditableEntity(event?.entity)) {
  89 + className = event?.entity?.class.name
  90 + persistedObjectId = event?.entity?.id?.toString()
78 91
79   - break
80   -
81   - case EventType.PreUpdate:
82   - println ''
83   - println this.class.name + " Got a Persistence event!"
84   - println "PRE UPDATE ${event.entityObject}"
85   -// oldState = getStateMap(event?.persister?.propertyNames, event?.getOldState())
86   -// newState = getStateMap(event?.persister?.propertyNames, event?.getState())
  92 + if (eventName == 'onPostInsert') {
  93 + newState = getStateMap(event?.persister?.propertyNames, event?.getState())
  94 + businessAuditLogService.recordLogEvent('insert', className, persistedObjectId, null, newState)
  95 + }
  96 + else if (eventName == 'onPreUpdate') {
  97 + oldState = getStateMap(event?.persister?.propertyNames, event?.getOldState())
  98 + newState = getStateMap(event?.persister?.propertyNames, event?.getState())
87 99 businessAuditLogService.recordLogEvent('update', className, persistedObjectId, oldState, newState)
88   - break;
89   -
90   - case EventType.PreDelete:
91   - println ''
92   - println this.class.name + " Got a Persistence event!"
93   - println "PRE DELETE ${event.entityObject}"
94   -// oldState = getStateMap(event?.persister?.propertyNames, event?.getDeletedState())
  100 + }
  101 + else if (eventName == 'onPreDelete') {
  102 + oldState = getStateMap(event?.persister?.propertyNames, event?.getDeletedState())
95 103 businessAuditLogService.recordLogEvent('delete', className, persistedObjectId, oldState, null)
96   - break
97   -
98   - default:
99   - log.warn("Cannot support event: ${event.eventType}")
  104 + }
  105 + else {
  106 + throw new IllegalArgumentException("Cannot support event: ${eventName}")
100 107 }
101 108 }
102 109 }
103 110
104   -
105   - Boolean isAuditableEntity(entity) {
106   - entity?.metaClass.hasProperty(entity, 'auditable') && entity.auditable == Boolean.TRUE
  111 + @Override
  112 + public void initialize(Configuration cfg) {
  113 + LOG.info("initializing")
107 114 }
108 115
109 116
  117 + def Boolean isAuditableEntity(entity) {
  118 + entity?.metaClass.hasProperty(entity, 'auditable') && entity?.auditable == Boolean.TRUE
  119 + }
  120 +
110 121 private def getStateMap(String[] names, Object[] state) {
111 122 def map = [:]
112 123 for (int i = 0; i < names.length; i++) {
@@ -117,13 +128,12 @@ protected void onPersistenceEvent(AbstractPersistenceEvent event) {
117 128 sanitizeMap(map)
118 129 }
119 130
120   -
121 131 // propertyNames may have null values on the above call to put
122 132 //create Map with no missing teeth
123 133 private def sanitizeMap(aMap) {
124 134 def entriesToBeRemoved = []
125 135 aMap?.each { key, value ->
126   - if (key && value) {
  136 + if (value != null && !(value instanceof PersistentSet) && !(value instanceof PersistentList)) {
127 137 aMap[key] = value?.toString()
128 138 }
129 139 else { entriesToBeRemoved << key }
@@ -131,19 +141,19 @@ protected void onPersistenceEvent(AbstractPersistenceEvent event) {
131 141 entriesToBeRemoved.each { key -> aMap.remove(key) }
132 142 return aMap
133 143 }
134   -
135 144 }
136 145
  146 +
137 147 class ContextUtils {
138 148
139 149 def static getBeanFromApplicationContext(String beanName){
140   - ApplicationContext ctx = (ApplicationContext)ApplicationHolder.getApplication().getMainContext()
141   - def bean
  150 + ApplicationContext ctx = ApplicationHolder.getApplication().getMainContext() as ApplicationContext
  151 +
  152 + def bean = null
142 153 try{
143 154 bean = ctx.getBean(beanName)
144   - } catch (org.springframework.beans.factory.NoSuchBeanDefinitionException ex){
145   - //do nothing. this just means the requested bean doesn't exist and the method will return null
  155 + } catch (NoSuchBeanDefinitionException ex){
146 156 }
147 157 return bean
148 158 }
149   -}
  159 +}

0 comments on commit a7471e9

Please sign in to comment.
Something went wrong with that request. Please try again.