1 Reply Latest reply on Nov 22, 2011 12:00 AM by lightguard

    How to intercept methods of EntityManager with Seam 3?

    aogier.netangel+seamframework.gmail.com

      I'm trying to intercept the method persist and update of javax.persistence.EntityManager in a Seam 3 project.


      In a previous version (Seam 2) of the micro-framework I'm trying to make, I did this using an implementation of org.hibernate.Interceptor and declaring it in the persistence.xml.


      But I want something more CDI-like now we are in a JEE6 environment.


      I want that just before entering in a EntityManager.persist call, an event @BeforeTrackablePersist is thrown. The same way, I want an event @BeforeTrackableUpdate to be thrown before entering in a EntityManager.merge call. Trackable is an interface which some of my Entitys could implement in order to be intercepted before persist or merge.


      I'm using Seam 3 (3.1.0.Beta3) Extended Persistence Manager :


      public class EntityManagerHandler {
           @SuppressWarnings("unused")
           @ExtensionManaged
           @Produces
           @PersistenceUnit
           private EntityManagerFactory entityManagerFactory;
      }
      



      So I've made a javax.enterprise.inject.spi.Extension, and tryied many ways to do that :


      public class TrackableExtension implements Extension {
      
           @Inject @BeforeTrackablePersisted
           private Event<Trackable> beforeTrackablePersistedEvent;
      
           @Inject @BeforeTrackableMerged
           private Event<Trackable> beforeTrackableMergedEvent;
      
           @SuppressWarnings("unchecked")
           public void processEntityManagerTarget(@Observes final ProcessInjectionTarget<EntityManager> event) {
                final InjectionTarget<EntityManager> injectionTarget = event.getInjectionTarget();
                final InjectionTarget<EntityManager> injectionTargetProxy = (InjectionTarget<EntityManager>) Proxy.newProxyInstance(event.getClass().getClassLoader(), new Class[] {InjectionTarget.class}, new InvocationHandler() {
                     @Override
                     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
                          if ("produce".equals(method.getName())) {
                               final CreationalContext<EntityManager> ctx = (CreationalContext<EntityManager>) args[0];
                               final EntityManager entityManager = decorateEntityManager(injectionTarget, ctx);
                               return entityManager;
                          } else {
                               return method.invoke(injectionTarget, args);
                          }
                     }
                });
                event.setInjectionTarget(injectionTargetProxy);
           }
      
           public void processEntityManagerType(@Observes final ProcessAnnotatedType<EntityManager> event) {
                final AnnotatedType<EntityManager> type = event.getAnnotatedType();
                final AnnotatedTypeBuilder<EntityManager> builder = new AnnotatedTypeBuilder<EntityManager>().readFromType(type);
      
               for (final AnnotatedMethod<? super EntityManager> method : type.getMethods()) {
                    final String name = method.getJavaMember().getName();
                     if (StringUtils.equals(name, "persist") || StringUtils.equals(name, "merge")) {
                         builder.addToMethod(method, TrackableInterceptorBindingLiteral.INSTANCE);
                    }
               }
      
               event.setAnnotatedType(builder.create());
           }
      
           public void processEntityManagerBean(@Observes final ProcessBean<EntityManager> event) {
                final AnnotatedType<EntityManager> annotatedType = (AnnotatedType<EntityManager>)event.getAnnotated();
      //          not even called
           }
      
           public void processEntityManager(@Observes final ProcessProducer<?, EntityManager> processProducer) {
                processProducer.setProducer(decorate(processProducer.getProducer()));
           }
      
           private Producer<EntityManager> decorate(final Producer<EntityManager> producer) {
                return new Producer<EntityManager>() {
                     @Override
                     public EntityManager produce(final CreationalContext<EntityManager> ctx) {
                          return decorateEntityManager(producer, ctx);
                     }
                     @Override
                     public Set<InjectionPoint> getInjectionPoints() {
                          return producer.getInjectionPoints();
                     }
                     @Override
                     public void dispose(final EntityManager instance) {
                          producer.dispose(instance);
                     }
                };
           }
      
           private EntityManager decorateEntityManager(final Producer<EntityManager> producer, final CreationalContext<EntityManager> ctx) {
                final EntityManager entityManager = producer.produce(ctx);
                return (EntityManager) Proxy.newProxyInstance(entityManager.getClass().getClassLoader(), new Class[] {EntityManager.class}, new InvocationHandler() {
                     @Override
                     public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
                          final String methodName = method.getName();
                          if (StringUtils.equals(methodName, "persist")) {
                               fireEventIfTrackable(beforeTrackablePersistedEvent, args[0]);
                          } else if (StringUtils.equals(methodName, "merge")) {
                               fireEventIfTrackable(beforeTrackableMergedEvent, args[0]);
                          }
                          return method.invoke(entityManager, args);
                     }
      
                     private void fireEventIfTrackable(final Event<Trackable> event, final Object entity) {
                          if (entity instanceof Trackable) {
                               event.fire(Reflections.<Trackable>cast(entity));
                          }
                     }
                });
           }
      }
      



      In all those observer methods, only the second one (processEntityManagerType(@Observes ProcessAnnotatedType<EntityManager>)) is called ! And even with that binding addition to methods persist and merge, my Interceptor is never called (I've of course enabled it with the correct lines in beans.xml, and enabled my extension with the services/javax.enterprise.inject.spi.Extension file).


      Something I've thought simple with CDI seems to be actually really hard at last... or perhaps Seam 3 does something which prevent this code from executing correctly...


      Does someone know how to handle that ?


      (I've posted the same question in stackoverflow here : http://stackoverflow.com/questions/8213285/how-to-intercept-methods-of-entitymanager-with-seam-3)