Hi Tim, I noticed a while back that the links on the Tapestry Security site were broken, but fortunately the code is all on GitHub now, so you can browse it easily:
https://github.com/tynamo/tapestry-security mrg On Mon, Aug 4, 2014 at 6:33 PM, D Tim Cummings <[email protected]> wrote: > 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 <[email protected]> > 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 <[email protected]> > 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 <[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)); > >>>>>>> } > >>>>>>> > >>>>>>> } > >> > >
