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