I am trying to store an instance or at least id of each domain that is auditable. For example, there is a domain 'Student' and is auditable. So, whenever the domain an instance of domain is created or edited, a new record is stored in audit log. What I want is to store the id of each domain that is saved or updated. How can it be done ?
Thanks in advance
You can use Grails Envers plugin that uses Hibernate Envers underneath. Plugin information can be found on plugins site here.
You can better refer to the url: http://grails.org/plugin/audit-logging
The above plugin works well for the domain class without collection, in case if you using collections in domain class, refer this stackoverflow link: How to determine Collection changes in a Hibernate PostUpdateEventListener?
Hope it will help a lot
I Wouldn't use the audit plugin , it has some issues when running your tests. This plugin in was created when the GORM didn't provide support for events. You can intercept the following events
beforeInsert - Executed before an object is initially persisted to the database
beforeUpdate - Executed before an object is updated
beforeDelete - Executed before an object is deleted
beforeValidate - Executed before an object is validated
afterInsert - Executed after an object is persisted to the database
afterUpdate - Executed after an object has been updated
afterDelete - Executed after an object has been deleted
onLoad - Executed when an object is loaded from the database
Hope this helps
You can use events with AuditLog plugin like below and then add what ever you would like too. Hope this makes sense
def onChange = { oldMap,newMap ->
println "Person was changed ..."
oldMap.each({ key, oldVal ->
if(oldVal != newMap[key]) {
println " * $key changed from $oldVal to " + newMap[key]
}
})
}
def onSave = {
println "new datainserted"
// may optionally refer to newState map
}
In any of these methods we can use
def event = new AuditLogEvent(
actor: actor,
className: className,
eventName: eventName,
persistedObjectId: objectId,
propertyName: propertyName,
newValue: newValue
)
if (event.validate()) {
auditLogListener.saveAuditLog(event)
log.info "Logged audit event [$event]"
}
Related
I have a grails domain object that I validate and then save as such:
if(foo.hasErrors()) {
transactionStatus.setRollbackOnly()
respond foo.errors, view: 'create'
return
}
foo.save flush:true, failOnError:true
println(foo)
There are no errors given on the save. But when I call the println, it says my object is unsaved. However, if I check the database, it has indeed been persisted. Is there something I can check to tell me why grails is telling me it is unsaved. The grails version is 3.0.9.
After more testing, it looks like it is related to the way I specify the key in my domain mapping.
static mapping = {
version false
autoTimestamp false
id name:'foo_id', generator:'increment'
}
If I remove the id field and let Grails handle it by default the object is saved properly. If I add the id field back, it has the issue described above. Is there a reason for it?
I wouldn't be too concerned about the format of toString(). You're calling save with failOnError: true, so it's safe to assume that an exception would be thrown if persistence fails. If you don't specify this argument, you can check that persistence succeeds like so
if (foo.save()) {
println 'it worked'
} else {
println 'if failed'
}
try
foo = foo.save flush:true, failOnError:true
println(foo)
I'm using grails 1.3.7 together with Oracle 11g and trying to manage inner transactions.
I have a bean Person that is passed to a transactional (Propagation.REQUIRED) service method who makes some modification. Then it is passed to another transactional (propagation = Propagation.REQUIRES_NEW) method that makes some other modification and then throws an Exception.
What I expected to see is the rollback of all the modification of the second service but still valid those of the first one. This is the situation:
//outer transaction
class MyService {
def nestedService
#Transactional(propagation = Propagation.REQUIRED)
public void testRequiredWithError(Person person) {
person.name = 'Mark'
try {
nestedService.testRequiresNewWithError(person)
} catch (RuntimeException e) {
println person.age //this prints 15
println e
}
}
}//end MyService
//inner transaction
class NestedService{
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void testRequiresNewWithError(Person person) {
person.age = 15 //expected after Exception will be discarded
throw new RuntimeException("Rollback this transaction!")
}
}
then I run grails console and check on the DB after it ends.
...
def p = Person.get(671)
def myService = ctx.myService
println p.name //'John'...from DB
println p.age //25...from DB
myService .testRequiredWithError(p)
println p.name // 'Mark'....correct
println p.age // 15....UNEXPECTED..
//same result checking on the DB after console ends and transaction flushes
I tried to use Propagation.NESTED after activating it in the bootstrap by transactionManager.setNestedTransactionAllowed(true)and use savepoints like in this post
grails transaction set savepoint
but still got same result.
What am I missing????
Thank you in advance.
I'm a little late to the party but in case you haven't found your answer I think I know why you're having issues with transactions.
I looked at your link to the discussion about your issues with savepoints. According to that discussion you are using MySQL as your datasource. MySQL does not support transactions by default and in order to make it do so, you need to create your tables in a special way. I have provided a link below that explains how you should create your tables in MySQL when you want to use transactions:
http://www.tutorialspoint.com/mysql/mysql-transactions.htm
EDIT: This article suggests setting the type of your table to InnoDB in order to support transactions. Here is an example of what that looks like:
mysql> create table tcount_tbl
-> (
-> tutorial_author varchar(40) NOT NULL,
-> tutorial_count INT
-> ) TYPE=InnoDB;
It might be worth noting that this is not the only type that supports transaction but is the most common. MySQL 5.5 and greater should create tables as type InnoDB automatically.
Hope this helps!
I'd test for p.isAttached() as transaction rollback detaches the domain object from the Hibernate session. Also in the test I'd reload the domain object from database - effectively p = Person.get(671) again to reload data from database.
I think the reason the age property is set to 15 after the test is that after the exception the domain object and the database are out of sync (and the domain object is detached).
For more see: https://weblogs.java.net/blog/blog/davidvc/archive2007/04/jpa_and_rollbac.html
I need to extend the listener class of audit-logging plugin, in order to provide different functionality on some scenarios.
For that purpose I have created this class:
class CustomAuditLogListener extends AuditLogListener{
public CustomAuditLogListener(Datastore datastore) {
super(datastore)
}
#Override
protected void onPreUpdate(event) {
. . .
}
}
Now, in order for the plugin to use this class instead of the default
AuditLogListener, within Bootstrap.groovy I try to remove AuditLogListener from the application listeners, and add the custom listener:
def applicationContext = servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT)
log.debug("Application listeners - PRE")
log.debug(applicationContext.getApplicationListeners())
// Remove all the listeners registered by the audit plugin.
applicationContext.getApplicationListeners.each { listener ->
if (listener.class == AuditLogListener.class) {
servletContext.getApplicationListeners.remove(listener)
log.debug("AuditLogListener removed ")
}
}
grailsApplication.mainContext.eventTriggeringInterceptor.datastores.each { key, datastore ->
// Don't register the listener if we are disabled
if (!grailsApplication.config.auditLog.disabled && !datastore.config.auditLog.disabled) {
def listener = new CustomAuditLogListener(datastore)
listener.with {
// some options
}
applicationContext.addApplicationListener(listener)
log.debug("Add new listener CustomAuditLogListener")
}
}
log.debug("Application listeners -POST")
log.debug(applicationContext.getApplicationListeners())
Application listeners - PRE returns empty list
Add new Listener CustomAudtilogListener is logged
Application Listeners - POST returns empty list
The way I register my listener is copied from the way the plugin registers the listener, seen in this class
To sum, what I need is replacing AuditLogListener, with my own CustomAuditLogListener. Currently the above process is not working. Any suggestions?
Hey sorry but the post does seem a little confusing, although I think I understand
I need to extend the listener class of audit-logging plugin.
Now, in order for the plugin to use this class instead of the default
AuditLogListener,
so you are trying to override the auditLogListener.
Your problem maybe that you are not declaring it as an override in your conf/spring/resources.groovy.
I have done a similar thing for a class rather than listener here:
https://github.com/vahidhedayati/testwschat/blob/master/test/unit/anythingbut/grails/plugin/wschat/MyOverrideServiceSpec.groovy
https://github.com/vahidhedayati/testwschat/blob/master/grails-app/conf/spring/resources.groovy
and something similar here:
grails kickstart plugin KickstartFilters how to prevent password information on logs
basically once you declare your bean
yourAppFilters(KickstartFilters)
This then tricks your grails-app and any plugins that maybe referring to KickstartFilters to now refer to yourAppFilters as the replacement
Try a bean like this: (ctrl shift o) to pull in CustomAuditLogListener
AuditLogListener(CustomAuditLogListener){
grailsApplication = ref('grailsApplication')
}
i'm displaying a server calculated value to the enduser by using propertyChanged event.
i was using breeze 1.4.8 and i'm using the productivity stack (ms sql, web api, ef)
It was working fine.
Recently i've updated to 1.4.12 and i recognized that this event doesn't get fired anymore.
The property "A_ProvisionTotal" gets calculated serverside only.
<snip>
var token = vm.transaction.entityAspect.propertyChanged.subscribe(propertyChanged);
function propertyChanged(propertyChangedArgs) {
var propertyName = propertyChangedArgs.propertyName;
if (vm.transaction.tblEmployees.CalculationMethod == "A" && propertyName == "A_ProvisionTotal")
logSuccess('Provision neuberechnet' + '<br/>' + 'Aktuell: ' + $filter('number')(vm.transaction.Provision, 2), true);
</snip>
Let me know if this is a known regression and if you need more snippets.
A couple of thoughts for how you could accomplish your desired functionality.
The entity could remember the last calculated value in a private field. Then whenever the recalculation gets triggered, you can compare the new value to the last calculated value and if there is no change, ignore the new calculated value.
Alternatively, you could define the properties involved in your calculation as ES5 properties in the entity ctor function and then trigger the calculation in the setter of the relevant properties, when they get set with a new value. More information here: http://www.breezejs.com/documentation/extending-entities#es5-property. ES5 properties are convenient if you want to build behavior such as your calculation into setters.
Update 3
This is not a bug - see the response to this post that describes this as a documented and deliberate behavior.
Update 2 June 2014
I overlooked a key fact in your question ... one that only became clear to me after I looked at the code you included in your comments. Let me extract the key pieces for other readers:
Your test issues a query, then saves an unrelated change to the server (where the property-of-interest is updated server-side), then checks if that telltale property-of-interest raises propertyChanged when the save result is merged back into cache.
var query = EntityQuery.from("Orders").where('id', 'eq', 10248);
query.using(em).execute().then(querySucceeded).then(checkPropertyChanged).fin(start);
// querySucceed receives order 10248, updates an unrelated property (so you can save),
// wires up a propertyChanged listener and saves, returning the saveChanges promise
function checkPropertyChanged(saveResults) {
var saved = saveResults.entities[0];
// this passes because the server-side change to `Freight` was returned
ok(saved && saved.Freight() === 1200.00,
"freight got changed serverside");
// this fails because Breeze did not notify you that the `Freight` had changed
ok(notifications[0].propertyName === "Freight",
"notified serverside change of Freight Property");
}
Summarizing, you expected that a property change on the server would trigger a propertyChanged notification on the client when the entity data are re-retrieved from the server as a by-product of saveChanges.
Do I have that right?
Our documentation was not clear on whether the merge of query, save, and import entity results would raise propertyChanged.
I discussed internally and confirmed that these operations SHOULD raise propertyChanged. I also wrote another (somewhat simpler) test that reveals the bug you discovered: that merged save results may not raise propertyChanged.
We'll look into it and tell you when we've fixed it. Thanks for discovering it.
Original
We have regression tests that show that the Breeze EntityAspect.propertyChanged event is raised in v.1.4.12. For example, you can see it at work in the DocCode sample, "basicTodoTests.js"; scroll to: "Breeze propertyChanged raised when any property changes".
Can you confirm that it really is a Breeze failure? Perhaps the property you are changing is not actually an entity property? Sometimes you think you are changing an entity (e.g, your Transaction entity) but the thing whose property you changed isn't actually an entity. Then the problem is that the data you thought would be mapped to a Transaction was not ... and you can start looking for that quite different problem.
In any case, I suggest that you write a small test to confirm your suspicion ... most importantly for yourself ... and then for us. That will help us discover what is different about your scenario from our scenarios. We'll fix it if you can find it. Thanks.
Actually, I'm not sure that this is a bug. Property change events DO get fired during a save merge but the property name parameter is documented as being 'null' when fired as a result of a save.
http://www.breezejs.com/sites/all/apidocs/classes/EntityAspect.html#event_propertyChanged
From the API Docs for the 'propertyName' parameter returned by EntityAspect.propertyChanged:
The name of the property that changed. This value will be 'null' for operations that replace the entire entity. This includes queries, imports and saves that require a merge. The remaining parameters will not exist in this case either.
What may have happened between 1.4.8 and 1.4.13 is that we actually implemented our design spec more carefully and probably introduced your breaking behavior. ( which we should have documented as such but likely missed).
Update by Ward
I updated the DocCode test which first confirmed the behavior described in your question and then confirmed the documented behavior.
We do regret that we apparently neglected to implement the documented behavior earlier and that we didn't mention the breaking change in our release notes (since updated).
Here's that test:
asyncTest("propertyChanged raised when merged save result changes a property", 3, function () {
var em = newTodosEm();
var todo = em.createEntity('TodoItem', {Description: "Saved description" });
em.saveChanges().then(saveSucceeded).catch(handleFail).finally(start);
ok(todo.entityAspect.isBeingSaved, "new todo is in the act of being saved");
// This change should be overwritten with the server value when the save result is returned
// even though the entity is in an Added state and the MergeStrategy is PreserveChanges
// because save expects to merge server values into an entity it is saving
todo.Description("Changed on client before save returns");
var descriptionChanged = false;
todo.entityAspect.propertyChanged.subscribe(function (changeArgs) {
// Watch carefully! The subscription is called twice during merge
// 1) propertyName === "Id" (assigned with permanent ID)
// 2) propertyName === null (WAT?)
// and not called with propertyName === "Description" as you might have thought.
// Actually 'null' means "merged a lot of properties"
// Documented: http://www.breezejs.com/sites/all/apidocs/classes/EntityAspect.html#event_propertyChanged
// The reason for this: don't want to fire a ton of events on whole entity load
// especially when merging many entities at the same time.
if (changeArgs.propertyName === null || changeArgs.propertyName === 'Description') {
descriptionChanged = true;
}
});
function saveSucceeded(saveResult) {
var saved = saveResult.entities[0];
// passes
equal(saved && saved.Description(), "Saved description",
"the merge after save should have restored the saved description");
// fails
ok(descriptionChanged,
"should have raised propertyChanged after merge/update of 'Description' property");
}
});
Updated post:
In a Controller if I do this:
def obj = new Test(name:"lol")
obj.save(flush:true)
obj.name = "lol2"
//a singleton service with nothing to do with obj
testService.dostuff()
/*
"obj" gets persisted to the database right here
even before the next println
*/
println "done"
Can anyone please explain me why is this happening with Grails 1.3.7 and not with Grails 2? What is the reason?
I know I could use discard() and basically restructure the code but I am interested in what and why is happening behind the scenes. Thanks!
Old post:
I have a test Grails application. I have one domain class test.Test:
package test
class Test {
String name
static constraints = {}
}
Also I have a service test.TestService:
package test
class TestService {
static scope = "singleton"
static transactional = true
def dostuff() {
println "test service was called"
}
}
And one controller test.TestController:
package test
class TestController {
def testService
def index = {
def obj = new Test(name:"lol")
obj.save(flush:true)
obj.name = "lol2"
testService.dostuff()
println "done"
}
}
So what I do:
Create a domain object
Change one of it's properties
Call a singleton service method
What I would expect:
Nothing gets persisted to the db unless I call obj.save()
What happens instead:
Right after the service call Grails will do an update query to the database.
I have tried the following configuration from this url: http://grails.1312388.n4.nabble.com/Turn-off-autosave-in-gorm-td1378113.html
hibernate.flush.mode="manual"
But it didn't help.
I have tested it with Grails 1.3.7, Grails 2.0.3 does not have this issue.
Could anyone please give me a bit more information on what is exactly going on? It seems like the current session has to be terminated because of the service call and because the object is dirty it is getting automatically persisted to the database after the service call. What I don't understand that even with the manual flush mode configuration in Hibernate does not help.
Thanks in advance!
I'm not sure what about that thread you linked to made you think it would work. They all said it wouldn't work, the ticket created has been closed as won't fix. The solution here is to use discard() as the thread stated.