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)); >>>>>>> } >>>>>>> >>>>>>> } >>
signature.asc
Description: Message signed with OpenPGP using GPGMail