Thanks Andrus. I guess this is becoming a Tapestry question now. I tried calling setTheUserId in setupRender() and clearing it in cleanupRender() but AuditListener is running in a different thread to these two methods.
The following is logging from a single click of the "save" button when a new record is being created. There are 2 threads being used and AuditListener.doPrePersist is called in a different thread and prior to setupRender(). 2014-08-04 23:19:27,376 INFO [au.com.tramanco.chekway.cayenne.audit.AuditListener] doPrePersist() Thread qtp1880825967-22 au.com.tramanco.chekway.cayenne.TblPerson 2014-08-04 23:19:27,376 INFO [au.com.tramanco.chekway.cayenne.audit.AuditListener] getTheUser() Thread qtp1880825967-22 idUser == null 2014-08-04 23:19:27,376 INFO [au.com.tramanco.chekway.cayenne.audit.AuditListener] doPrePersist() Thread qtp1880825967-22 au.com.tramanco.chekway.cayenne.TblPerson 2014-08-04 23:19:27,376 INFO [au.com.tramanco.chekway.cayenne.audit.AuditListener] getTheUser() Thread qtp1880825967-22 idUser == null 2014-08-04 23:19:27,414 INFO [au.com.tramanco.chekway.base.TmcssComponent] setupRender() qtp1880825967-21 2014-08-04 23:19:27,414 INFO [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 21 qtp1880825967-21 setTheUserId 200 2014-08-04 23:19:27,448 INFO [au.com.tramanco.chekway.base.TmcssComponent] cleanupRender() qtp1880825967-21 2014-08-04 23:19:27,448 INFO [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 21 qtp1880825967-21 setTheUserId null Tim On 4 Aug 2014, at 16:04, Andrus Adamchik <and...@objectstyle.org> wrote: >> Maybe the request is running in a different thread each time. > > Of course. Jetty has a thread pool and each requests gets an available thread > from the pool semi-randomly. Note that qtp1248572294-23 and qtp1248572294-19 > are also Jetty threads, so they are called within a request. So make sure you > call ‘setTheUserId’ in every single request (and reset it to null at the end > of that request). > > Andrus > > On Aug 2, 2014, at 2:48 AM, D Tim Cummings <t...@triptera.com.au> wrote: > >> Hi Andrus >> >> Thanks for your help. Here are logs with thread names as well. I logged in >> in Thread 24 and created 5 new records. AuditListener is running in >> different threads. I am running this in Eclipse 4.3.2 with the RunJettyRun >> plugin running Jetty 8.1.2. Log statement is >> >> logger.info("Thread " + t.getId() + " " + t.getName() + " Looking for >> TblPerson " + idUser); >> //where t = Thread.currentThread() >> >> 2014-08-02 09:37:30,563 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 setTheUserId 220 >> 2014-08-02 09:38:14,064 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 Looking for TblPerson 220 >> 2014-08-02 09:38:14,064 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 Looking for TblPerson 220 >> 2014-08-02 09:38:14,064 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 Looking for TblPerson 220 >> 2014-08-02 09:38:14,065 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 Looking for TblPerson 220 >> 2014-08-02 09:38:31,045 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 23 >> qtp1248572294-23 idUser == null >> 2014-08-02 09:38:31,045 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 23 >> qtp1248572294-23 idUser == null >> 2014-08-02 09:38:31,045 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 23 >> qtp1248572294-23 idUser == null >> 2014-08-02 09:38:31,046 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 23 >> qtp1248572294-23 idUser == null >> 2014-08-02 09:38:57,932 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 19 >> qtp1248572294-19 idUser == null >> 2014-08-02 09:38:57,932 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 19 >> qtp1248572294-19 idUser == null >> 2014-08-02 09:38:57,932 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 19 >> qtp1248572294-19 idUser == null >> 2014-08-02 09:38:57,933 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 19 >> qtp1248572294-19 idUser == null >> 2014-08-02 09:39:16,048 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 23 >> qtp1248572294-23 idUser == null >> 2014-08-02 09:39:16,048 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 23 >> qtp1248572294-23 idUser == null >> 2014-08-02 09:39:16,048 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 23 >> qtp1248572294-23 idUser == null >> 2014-08-02 09:39:16,048 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 23 >> qtp1248572294-23 idUser == null >> 2014-08-02 09:39:41,670 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 Looking for TblPerson 220 >> 2014-08-02 09:39:41,670 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 Looking for TblPerson 220 >> 2014-08-02 09:39:41,670 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 Looking for TblPerson 220 >> 2014-08-02 09:39:41,670 INFO >> [au.com.tramanco.chekway.cayenne.audit.AuditListener] Thread 24 >> qtp1248572294-24 Looking for TblPerson 220 >> >> Maybe the request is running in a different thread each time. I will do some >> more checking. >> >> Tim >> >> >> On 1 Aug 2014, at 23:22, Andrus Adamchik <and...@objectstyle.org> wrote: >> >>> Hi Tim, >>> >>> Yes you are going in the right direction with AuditableProcessor. >>> >>>> If I save the user in a ThreadLocal in the data channel filter object, I >>>> get a different user each time (often blank) because the data channel >>>> filter seem to run in its own thread which changes each time. >>> >>> This seems suspect. In a typical web app, all processing happens in request >>> thread. Cayenne listeners are processed in the same thread as ObjectContext >>> commit, which is normally your request thread. Could you possibly print >>> thread names from within setTheUserId and doPrePersist methods ? Maybe that >>> will give you a hint. Request threads in Tomcat and Jetty have easily >>> identifiable names. >>> >>> Andrus >>> >>> >>> On Jul 20, 2014, at 2:23 AM, D Tim Cummings <t...@triptera.com.au> wrote: >>> >>>> Hi all >>>> >>>> I want to set up a simple audit trail which basically links who was the >>>> person to create a record to that record. I am using Cayenne 3.2M1 and >>>> Tapestry 5.3.7. I figure I need to set up a data channel filter to catch >>>> changes to that record and then save a link to the user who made the >>>> change. >>>> >>>> The problem is, if I save the user in the data channel filter object when >>>> someone logs in, then all created records link to the last logged in user. >>>> >>>> If I save the user in a ThreadLocal in the data channel filter object, I >>>> get a different user each time (often blank) because the data channel >>>> filter seem to run in its own thread which changes each time. >>>> >>>> I have been watching the excellent and now freely available podcast by >>>> Andrus Adamchik presented to WebObjects developers "Advanced Apache >>>> Cayenne" where he talks about lifecycle events, (callbacks, listeners), >>>> caching, data channel filters, clustering, in cayenne 3.2M1. >>>> https://itunes.apple.com/podcast/webobjects-podcasts/id270165303?mt=2# >>>> In Andrus's sample code he uses AuditableProcessor, but I couldn't think >>>> how to use it to solve this problem. >>>> >>>> Here is a copy of my listener/data channel filter with the ThreadLocal >>>> code. >>>> >>>> Thanks >>>> >>>> Tim >>>> >>>> >>>> public class AuditListener implements DataChannelFilter { >>>> >>>> private static final Logger logger = >>>> LoggerFactory.getLogger(AuditListener.class); >>>> >>>> private ThreadLocal<Integer> tlUserId; >>>> >>>> @PrePersist(entityAnnotations=TagCreateCancel.class) >>>> void doPrePersist(DataObject object) { >>>> if ( object instanceof AuditableCreateCancel ) { >>>> AuditableCreateCancel acc = (AuditableCreateCancel) object; >>>> TblPerson user = getTheUser(object.getObjectContext()); >>>> acc.setTblPersonCreate(user); >>>> } >>>> } >>>> >>>> private TblPerson getTheUser(ObjectContext oc) { >>>> Thread t = Thread.currentThread(); >>>> Integer idUser = tlUserId.get(); >>>> if ( idUser == null ) { >>>> logger.info("Thread " + t.getId() + " idUser == null "); >>>> return null; >>>> } >>>> logger.info("Thread " + t.getId() + " Looking for TblPerson " + idUser); >>>> TblPerson p = Cayenne.objectForPK(oc, TblPerson.class, idUser.intValue()); >>>> return p; >>>> } >>>> >>>> @Override >>>> public void init(DataChannel channel) { >>>> tlUserId = new ThreadLocal<Integer>(); >>>> } >>>> >>>> @Override >>>> public QueryResponse onQuery(ObjectContext originatingContext, Query >>>> query, DataChannelFilterChain filterChain) { >>>> return filterChain.onQuery(originatingContext, query); >>>> } >>>> >>>> @Override >>>> public GraphDiff onSync(ObjectContext originatingContext, GraphDiff >>>> changes, int syncType, DataChannelFilterChain filterChain) { >>>> try { >>>> return filterChain.onSync(originatingContext, changes, syncType); >>>> } finally { >>>> // >>>> } >>>> } >>>> >>>> public void setTheUserId(int idUser) { >>>> Thread t = Thread.currentThread(); >>>> logger.info("Thread " + t.getId() + " setTheUserId " + idUser); >>>> tlUserId.set(Integer.valueOf(idUser)); >>>> } >>>> >>>> } >>>> >>> >> >
signature.asc
Description: Message signed with OpenPGP using GPGMail