Thanks Robert and Andrus for your help. I will definitely look at Dispatcher 
and tapestry-security. I had started to look at tapestry-security before but 
all the links to sample code were broken.

And thanks to Michael explaining why my POST was generating 2 threads.

Cheers

Tim


On 5 Aug 2014, at 0:09, Robert Zeigler <robert.zeig...@roxanemy.com> wrote:

> Dispatcher is a great place for security code, as long as you don't have 
> application components that live outside tapestry which also need the 
> security constraints. 
> 
> You might consider looking into the tapestry-security module from the tynamo 
> project, which integrates shiro.  A little time reading the docs will likely 
> save you a lot if coding time. 
> 
> Robert
> 
> GATAATGCTATTTCTTTAATTTTCGAA
> 
>> On Aug 4, 2014, at 9:02 AM, Andrus Adamchik <and...@objectstyle.org> wrote:
>> 
>> 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 <t...@triptera.com.au> 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 <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