/**
* 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();
}
}
}