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));
>>>> }
>>>> 
>>>> }
>>>> 
>>> 
>> 
> 

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to