Note that there are some explanatory texts on larger screens.

plurals
  1. POGrails custom listener, updating property generates other event?
    primarykey
    data
    text
    <p>I need to calculate coords from some address fields in a Client domain, so in order to decouple the domain class from this requirement I 've added a custom listener that uses Google Maps API to get the right values.</p> <p>Everything works, but debuggin' I've realized that updating domain properties inside the listener lauches another updating event and consecuently my listener <strong>calls the Google API twice</strong> for each update.</p> <p>Someone experiencing this issue?? What am I doing wrong??</p> <p><strong>Domain:</strong></p> <pre><code>class Client{ String address String city String country String postalCode double lat double lng .... } </code></pre> <p><strong>Service:</strong></p> <pre><code>class GoogleMapsService{ static transactional=false def grailsApplication def geocode(String address){ address=address.replaceAll(" ", "+") def urlApi=grailsApplication.config.googleMaps.apiUrl+"&amp;address=${address}" def urlJSON = new URL(urlApi) def geoCodeResultJSON = new JsonSlurper().parseText(urlJSON.getText()) return geoCodeResultJSON } } </code></pre> <p><strong>Event listener:</strong></p> <pre><code>class ClientListener extends AbstractPersistenceEventListener{ public boolean supportsEventType(Class&lt;? extends ApplicationEvent&gt; eventClass) { switch(eventClass){ case [PreInsertEvent, PreUpdateEvent, PostInsertEvent, PostUpdateEvent, PostDeleteEvent]: return true default: return false } } protected void onPersistenceEvent(AbstractPersistenceEvent event) { if(!(event.entityObject instanceof Client)){ return } switch(event.eventType) { //GOOGLE MAPS geocode case [EventType.PreInsert,EventType.PreUpdate]: this.updateCoords(event.entityObject) break //OTHER STUFF (notifications, no changing inside) case EventType.PostUpdate: //.... } } private String composeAddress(Client cli){ def resul=[cli.address,cli.city,cli.country,cli.postalCode] return resul.findAll{it}.join(",") } private void updateCoords(Client cli){ def fullAddress=this.composeAddress(cli) if(fullAddress){ def coords=googleMapsService.geocode(fullAddress) if (coords.status=="OK"){ //**IMPORTANT STUFF THESE TWO LINES RAISE AN EVENT cli.lat=coords.results.geometry.location.lat[0] cli.lng=coords.results.geometry.location.lng[0] } } } } </code></pre> <p><strong>Update:</strong></p> <p>Entity is null inside the listener so I cant make it work from preUpdate, Looks like there is an open issue (<a href="http://jira.grails.org/browse/GRAILS-9374" rel="nofollow">http://jira.grails.org/browse/GRAILS-9374</a>)</p> <p>Gonna try the SaveOrUpdate...</p> <hr> <p><strong>Big Update:</strong></p> <p>I'm getting closer but still cant avoid the duplicated call.</p> <p>Since I need a 'saveOrUpdate' listener and this one is a Hibernate specific listener I end up having two listeners:</p> <ul> <li>ClientMailListener (Grails custom listener, postinsert, postupdate, postdelete with no changes in the object)</li> <li>ClientGeoListener (Hibernate listener, mapped as 'save-update')</li> </ul> <p>The status with this combo is:</p> <ul> <li>Update: OK, both listeners are called just once</li> <li>Insert: KO!!, let's have deeper look.</li> </ul> <p><strong>1.</strong> It does an insert and calls the postInsert grails listener, <strong>Why????</strong></p> <pre><code>ClientMailListener.onPersistenceEvent(AbstractPersistenceEvent) line: 45 ClientMailListener(AbstractPersistenceEventListener).onApplicationEvent(ApplicationEvent) line: 46 SimpleApplicationEventMulticaster.multicastEvent(ApplicationEvent) line: 97 GrailsWebApplicationContext(AbstractApplicationContext).publishEvent(ApplicationEvent) line: 324 ClosureEventTriggeringInterceptor.publishEvent(AbstractEvent, AbstractPersistenceEvent) line: 163 ClosureEventTriggeringInterceptor.onPostInsert(PostInsertEvent) line: 129 EntityIdentityInsertAction.postInsert() line: 131 EntityIdentityInsertAction.execute() line: 90 ActionQueue.execute(Executable) line: 273 ClosureEventTriggeringInterceptor.performSaveOrReplicate(Object, EntityKey, EntityPersister, boolean, Object, EventSource, boolean) line: 250 ClosureEventTriggeringInterceptor(AbstractSaveEventListener).performSave(Object, Serializable, EntityPersister, boolean, Object, EventSource, boolean) line: 203 ClosureEventTriggeringInterceptor(AbstractSaveEventListener).saveWithGeneratedId(Object, String, Object, EventSource, boolean) line: 129 ClosureEventTriggeringInterceptor(DefaultSaveOrUpdateEventListener).saveWithGeneratedOrRequestedId(SaveOrUpdateEvent) line: 210 ClosureEventTriggeringInterceptor(DefaultSaveOrUpdateEventListener).entityIsTransient(SaveOrUpdateEvent) line: 195 ClosureEventTriggeringInterceptor(DefaultSaveOrUpdateEventListener).performSaveOrUpdate(SaveOrUpdateEvent) line: 117 ClosureEventTriggeringInterceptor(DefaultSaveOrUpdateEventListener).onSaveOrUpdate(SaveOrUpdateEvent) line: 93 ClosureEventTriggeringInterceptor.onSaveOrUpdate(SaveOrUpdateEvent) line: 108 SessionImpl.fireSaveOrUpdate(SaveOrUpdateEvent) line: 685 </code></pre> <p><strong>2.</strong> Then calls the save-update listener, and updates de object</p> <pre><code>ClientGeoListener.onSaveOrUpdate(SaveOrUpdateEvent) line: 34 SessionImpl.fireSaveOrUpdate(SaveOrUpdateEvent) line: 685 SessionImpl.saveOrUpdate(String, Object) line: 677 SessionImpl.saveOrUpdate(Object) line: 673 </code></pre> <p><strong>3.</strong> Consecuently the postUpdate grails listener <strong>is called again, :(</strong></p> <hr> <p><strong>Last Update</strong></p> <p>Finally try with just one listener to keep it simple. Looks like the whole point is that the hibernate 'save-update' listener is executed after the insert, details:</p> <p>If I disabled the custom listener leaving the Hibernate listener (GEO) this is what happens:</p> <p><strong>1.</strong> Insert into the client (blank coords) and calls the saveupdate Listener:</p> <pre><code>ClientGeoListener.onSaveOrUpdate(SaveOrUpdateEvent) line: 34 SessionImpl.fireSaveOrUpdate(SaveOrUpdateEvent) line: 685 SessionImpl.saveOrUpdate(String, Object) line: 677 </code></pre> <p><strong>2.</strong> Coords are updated and there is an update</p> <p>Sorry for this large update, Ideas??</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload