/** * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file * distributed with this work for additional information regarding copyright ownership. Apereo * licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use * this file except in compliance with the License. You may obtain a copy of the License at the * following location: * * <p>http://www.apache.org/licenses/LICENSE-2.0 * * <p>Unless required by applicable law or agreed to in writing, software distributed under the * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing permissions and * limitations under the License. */ package org.apereo.portal.jpa; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceException; import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.framework.ProxyFactory; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.ApplicationEventPublisherAware; import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; /** * Wraps the created {@link EntityManagerFactory} so that create/close of {@link EntityManager} * instances results in spring events being dispatched */ public class EventingLocalContainerEntityManagerFactoryBean extends LocalContainerEntityManagerFactoryBean implements ApplicationEventPublisherAware, ApplicationListener<ContextRefreshedEvent> { private static final AtomicLong EVENT_ID = new AtomicLong(); private final AtomicBoolean contextReady = new AtomicBoolean(false); private ApplicationEventPublisher applicationEventPublisher; @Override public void onApplicationEvent(ContextRefreshedEvent event) { contextReady.set(true); } @Override public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) { this.applicationEventPublisher = applicationEventPublisher; } @Override protected EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException { final String persistenceUnitName = this.getPersistenceUnitName(); //Create actual EMF final EntityManagerFactory nativeEntityManagerFactory = super.createNativeEntityManagerFactory(); //Add a proxy to the EMF which results in events final ProxyFactory proxyFactory = new ProxyFactory(nativeEntityManagerFactory); proxyFactory.addAdvice( new EventingEntityMangerFactoryInterceptor( applicationEventPublisher, contextReady, persistenceUnitName)); return (EntityManagerFactory) proxyFactory.getProxy(); } /** * Interceptor that fires a {@link EntityManagerCreatedEvent} event when {@link * EntityManagerFactory#createEntityManager()} or {@link * EntityManagerFactory#createEntityManager(java.util.Map)} is called */ private static final class EventingEntityMangerFactoryInterceptor implements MethodInterceptor { private final ApplicationEventPublisher applicationEventPublisher; private final AtomicBoolean contextReady; private final String persistenceUnitName; public EventingEntityMangerFactoryInterceptor( ApplicationEventPublisher applicationEventPublisher, AtomicBoolean contextReady, String persistenceUnitName) { this.applicationEventPublisher = applicationEventPublisher; this.contextReady = contextReady; this.persistenceUnitName = persistenceUnitName; } @Override public Object invoke(MethodInvocation invocation) throws Throwable { if ("createEntityManager".equals(invocation.getMethod().getName()) && contextReady.get()) { final EntityManager entityManager = (EntityManager) invocation.proceed(); final long entityManagerId = EVENT_ID.getAndIncrement(); final EntityManagerCreatedEvent entityManagerCreatedEvent = new EntityManagerCreatedEvent( this, entityManagerId, persistenceUnitName, entityManager); applicationEventPublisher.publishEvent(entityManagerCreatedEvent); //Add a proxy to the EMF which results in events final ProxyFactory proxyFactory = new ProxyFactory(entityManager); proxyFactory.addAdvice( new EventingEntityMangerInterceptor( applicationEventPublisher, contextReady, entityManagerId, persistenceUnitName, entityManager)); return (EntityManager) proxyFactory.getProxy(); } return invocation.proceed(); } } /** * Interceptor that fires a {@link EntityManagerClosingEvent} event when {@link * EntityManager#close()} is called */ private static final class EventingEntityMangerInterceptor implements MethodInterceptor { private final ApplicationEventPublisher applicationEventPublisher; private final AtomicBoolean contextReady; private final long entityManagerId; private final String persistenceUnitName; private final EntityManager entityManager; public EventingEntityMangerInterceptor( ApplicationEventPublisher applicationEventPublisher, AtomicBoolean contextReady, long entityManagerId, String persistenceUnitName, EntityManager entityManager) { this.applicationEventPublisher = applicationEventPublisher; this.contextReady = contextReady; this.entityManagerId = entityManagerId; this.persistenceUnitName = persistenceUnitName; this.entityManager = entityManager; } @Override public Object invoke(MethodInvocation invocation) throws Throwable { if ("close".equals(invocation.getMethod().getName()) && contextReady.get()) { final EntityManagerClosingEvent entityManagerClosingEvent = new EntityManagerClosingEvent( this, this.entityManagerId, this.persistenceUnitName, this.entityManager); this.applicationEventPublisher.publishEvent(entityManagerClosingEvent); } return invocation.proceed(); } } }