Yeah, I guess you’ll need to debug your ’setupRender’. 

I usually implement any security-related code in a servlet Filter that wraps 
around Tapestry filter (and intercepts all pages). This creates some 
inconvenience, as I can’t use injection outside Tapestry, but otherwise I can 
be 100% sure that security code is always executed. 

Perhaps you can do something similar using a custom T5 dispatcher: 
http://wiki.apache.org/tapestry/Tapestry5HowToCreateADispatcher

Andrus

On Aug 4, 2014, at 4:23 PM, D Tim Cummings <[email protected]> wrote:

> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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));
>>>>> }
>>>>> 
>>>>> }
>>>>> 
>>>> 
>>> 
>> 
> 

Reply via email to