Как перехватить методы EntityManager с помощью Seam 3? - PullRequest
0 голосов
/ 21 ноября 2011

Я пытаюсь перехватить метод persist и update из javax.persistence.EntityManager в проекте Seam 3 .

В предыдущей версии (Seam 2)микро-фреймворк, который я пытаюсь сделать, я сделал это, используя реализацию org.hibernate.Interceptor и объявив ее в persistence.xml.

Но я хочу что-то более "CDI-подобное", теперь мы находимся вСреда JEE6.

Я хочу, чтобы перед входом в вызов EntityManager.persist генерировалось событие @BeforeTrackablePersist.Точно так же я хочу, чтобы событие @BeforeTrackableUpdate генерировалось перед входом в вызов EntityManager.merge.Trackable - это интерфейс, который некоторые из моих Entity могут реализовать для перехвата перед сохранением или слиянием.

Я использую Seam 3 (3.1.0.Beta3) Extended Persistence Manager:

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

Итак, я сделал javax.enterprise.inject.spi.Extension и попробовал много способов сделать это:

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));
                }
            }
        });
    }
}

Во всех этих методах наблюдателя, только второй (processEntityManagerType(@Observes ProcessAnnotatedType<EntityManager>))называется !И даже с этим обязательным дополнением к методам persist и merge мой Interceptor никогда не вызывается (я, конечно, включил его с правильными строками в beans.xml и включил свое расширение с файлом services/javax.enterprise.inject.spi.Extension).

Что-то, что я считаю простым с CDI, кажется, наконец-то действительно очень сложным ... или, возможно, Seam 3 делает что-то, что мешает правильному выполнению этого кода ...

Кто-то знаеткак справиться с этим?

Ответы [ 2 ]

1 голос
/ 22 ноября 2011

Я думаю, вы делаете это немного сложнее, чем нужно.Во-первых, хотя интеграция JPA и CDI не очень хороша в Java EE 6, мы очень надеемся, что изменения в Java EE 7 и JPA 2.1.

Что вы захотите сделать, так это создать свой собственныйпроизводитель EntityManager, который будет делегировать фактический экземпляр EntityManager, но также будет запускать ваши собственные события при вызове интересующих вас методов. Посмотрите на источник Seam Persistence, чтобы увидеть один из способов, которым это можно сделать.

0 голосов
/ 03 января 2012

Поскольку мой маленький патч для сохраняемости шва был применен в SEAMPERSIST-75 , теоретически можно будет сделать это, расширив org.jboss.seam.persistence.HibernatePersistenceProvider и переопределив метод proxyEntityManager(EntityManager).

...