/** * == @Spearal ==> * * Copyright (C) 2014 Franck WOLFF & William DRAI (http://www.spearal.io) * * Licensed 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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.spearal.jpa2.cdi; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Type; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Observes; import javax.enterprise.inject.spi.AfterDeploymentValidation; import javax.enterprise.inject.spi.AfterTypeDiscovery; import javax.enterprise.inject.spi.AnnotatedConstructor; import javax.enterprise.inject.spi.AnnotatedField; import javax.enterprise.inject.spi.AnnotatedMember; import javax.enterprise.inject.spi.AnnotatedMethod; import javax.enterprise.inject.spi.AnnotatedParameter; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.Bean; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.Extension; import javax.enterprise.inject.spi.InjectionPoint; import javax.enterprise.inject.spi.ProcessAnnotatedType; import javax.enterprise.inject.spi.ProcessProducer; import javax.enterprise.inject.spi.Producer; import javax.enterprise.util.AnnotationLiteral; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.PersistenceContext; import javax.persistence.PersistenceUnit; import org.spearal.SpearalFactory; import org.spearal.jpa2.EntityManagerWrapper; import org.spearal.jpa2.SpearalConfigurator; /** * @author Franck WOLFF * @author William DRAI */ public class SpearalExtension implements Extension { private final Logger log = Logger.getLogger(SpearalExtension.class.getName()); public void wrapEntityManager(@Observes ProcessProducer<?, EntityManager> event) { if (event.getAnnotatedMember().isAnnotationPresent(SpearalEnabled.class)) event.setProducer(new EntityManagerProducerWrapper(event.getProducer())); } public void wrapEntityManagerFactory(@Observes ProcessProducer<?, EntityManagerFactory> event, BeanManager beanManager) { if (event.getAnnotatedMember().isAnnotationPresent(SpearalEnabled.class)) event.setProducer(new EntityManagerFactoryProducerWrapper(event.getProducer(), beanManager)); } private Map<String, AnnotatedMember<?>> injectedPersistenceContexts = new HashMap<String, AnnotatedMember<?>>(); private Set<String> injectedPersistenceUnits = new HashSet<String>(); public <X> void processInjectedPersistenceResources(@Observes ProcessAnnotatedType<X> event) { for (AnnotatedMethod<? super X> method : event.getAnnotatedType().getMethods()) handleAnnotatedMember(method); for (AnnotatedField<? super X> field : event.getAnnotatedType().getFields()) handleAnnotatedMember(field); } private void handleAnnotatedMember(AnnotatedMember<?> member) { if (member.isAnnotationPresent(PersistenceContext.class)) { PersistenceContext persistenceContext = member.getAnnotation(PersistenceContext.class); injectedPersistenceContexts.put(persistenceContext.unitName(), member); } if (member.isAnnotationPresent(PersistenceUnit.class)) { PersistenceUnit persistenceUnit = member.getAnnotation(PersistenceUnit.class); injectedPersistenceUnits.add(persistenceUnit.unitName()); } } public void startup(@Observes AfterDeploymentValidation event, BeanManager beanManager) { // Force startup of SpearalFactory and EntityManagerFactory for (Bean<?> bean : beanManager.getBeans(SpearalFactory.class)) beanManager.getReference(bean, SpearalFactory.class, beanManager.createCreationalContext(bean)).toString(); for (Bean<?> bean : beanManager.getBeans(EntityManagerFactory.class)) beanManager.getReference(bean, EntityManagerFactory.class, beanManager.createCreationalContext(bean)).toString(); } public void produceMissingPersistenceUnits(@Observes AfterTypeDiscovery event, BeanManager beanManager) { for (String unitName : injectedPersistenceUnits) injectedPersistenceContexts.remove(unitName); for (AnnotatedMember<?> member : injectedPersistenceContexts.values()) { if (!member.isAnnotationPresent(SpearalEnabled.class)) continue; PersistenceContext persistenceContext = member.getAnnotation(PersistenceContext.class); try { final Set<Annotation> annotations = new HashSet<Annotation>(member.getAnnotations()); Iterator<Annotation> ia = annotations.iterator(); while (ia.hasNext()) { Annotation a = ia.next(); if (a.annotationType().equals(PersistenceContext.class)) ia.remove(); } PersistenceUnit persistenceUnit = new PersistenceUnitAnnotation(persistenceContext.name(), persistenceContext.unitName()); annotations.add(persistenceUnit); final AnnotatedType<PersistenceUnitProducer> annotatedPU = new AnnotatedPersistenceUnitProducerType(annotations); event.addAnnotatedType(annotatedPU, "org.spearal.jpa2.PersistenceUnit." + persistenceContext.unitName()); } catch (Exception e) { log.logp(Level.WARNING, SpearalExtension.class.getName(), "afterTypeDiscovery", "Could not setup PersistenceUnit integration {0}", new Object[] { persistenceContext.unitName() }); } } } private static class PersistenceUnitProducer { @SuppressWarnings("unused") private EntityManagerFactory persistenceUnit; } @SuppressWarnings("all") private class PersistenceUnitAnnotation extends AnnotationLiteral<PersistenceUnit> implements PersistenceUnit { private static final long serialVersionUID = 1L; private String name; private String unitName; public PersistenceUnitAnnotation(String name, String unitName) { this.name = name; this.unitName = unitName; } public String name() { return name; } public String unitName() { return unitName; } } private static final class EntityManagerProducerWrapper implements Producer<EntityManager> { private final Producer<EntityManager> wrappedProducer; public EntityManagerProducerWrapper(Producer<EntityManager> wrappedProducer) { this.wrappedProducer = wrappedProducer; } @Override public EntityManager produce(CreationalContext<EntityManager> ctx) { EntityManager entityManager = wrappedProducer.produce(ctx); if (entityManager instanceof EntityManagerWrapper) return entityManager; return new EntityManagerWrapper(entityManager); } @Override public void dispose(EntityManager entityManager) { if (entityManager instanceof EntityManagerWrapper) entityManager = ((EntityManagerWrapper)entityManager).getWrappedEntityManager(); wrappedProducer.dispose(entityManager); } @Override public Set<InjectionPoint> getInjectionPoints() { return wrappedProducer.getInjectionPoints(); } } private static final class EntityManagerFactoryProducerWrapper implements Producer<EntityManagerFactory> { private final Producer<EntityManagerFactory> wrappedProducer; private final BeanManager beanManager; public EntityManagerFactoryProducerWrapper(Producer<EntityManagerFactory> wrappedProducer, BeanManager beanManager) { this.wrappedProducer = wrappedProducer; this.beanManager = beanManager; } @Override public EntityManagerFactory produce(CreationalContext<EntityManagerFactory> ctx) { EntityManagerFactory entityManagerFactory = wrappedProducer.produce(ctx); Set<Bean<?>> beans = beanManager.getBeans(SpearalFactory.class); for (Bean<?> bean : beans) { SpearalFactory spearalFactory = (SpearalFactory)beanManager.getReference(bean, SpearalFactory.class, beanManager.createCreationalContext(bean)); SpearalConfigurator.init(spearalFactory, entityManagerFactory); } return entityManagerFactory; } @Override public void dispose(EntityManagerFactory entityManagerFactory) { wrappedProducer.dispose(entityManagerFactory); } @Override public Set<InjectionPoint> getInjectionPoints() { return wrappedProducer.getInjectionPoints(); } } public static class AnnotatedPersistenceUnitProducerType implements AnnotatedType<PersistenceUnitProducer> { private final Set<Annotation> annotations; private final Set<AnnotatedConstructor<PersistenceUnitProducer>> annotatedConstructors; private final Set<AnnotatedField<? super PersistenceUnitProducer>> annotatedFields = new HashSet<AnnotatedField<? super PersistenceUnitProducer>>(); public AnnotatedPersistenceUnitProducerType(Set<Annotation> annotations) throws Exception { this.annotations = annotations; this.annotatedConstructors = new HashSet<AnnotatedConstructor<PersistenceUnitProducer>>(); this.annotatedConstructors.add(new AnnotatedPersistenceUnitProducerConstructor()); } @Override public <T extends Annotation> T getAnnotation(Class<T> annotationType) { return null; } @Override public Set<Annotation> getAnnotations() { return Collections.emptySet(); } @Override public Type getBaseType() { return PersistenceUnitProducer.class; } @Override public Set<Type> getTypeClosure() { return Collections.singleton((Type)PersistenceUnitProducer.class); } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { return false; } @Override public Set<AnnotatedConstructor<PersistenceUnitProducer>> getConstructors() { return annotatedConstructors; } @Override public Set<AnnotatedField<? super PersistenceUnitProducer>> getFields() { return annotatedFields; } @Override public Class<PersistenceUnitProducer> getJavaClass() { return PersistenceUnitProducer.class; } @Override public Set<AnnotatedMethod<? super PersistenceUnitProducer>> getMethods() { return Collections.emptySet(); } public class AnnotatedPersistenceUnitProducerConstructor implements AnnotatedConstructor<PersistenceUnitProducer> { private final Constructor<PersistenceUnitProducer> persistenceUnitConstructor; public AnnotatedPersistenceUnitProducerConstructor() throws NoSuchMethodException { persistenceUnitConstructor = PersistenceUnitProducer.class.getDeclaredConstructor(); } @Override public List<AnnotatedParameter<PersistenceUnitProducer>> getParameters() { return Collections.emptyList(); } @Override public AnnotatedType<PersistenceUnitProducer> getDeclaringType() { return AnnotatedPersistenceUnitProducerType.this; } @Override public boolean isStatic() { return false; } @Override public <T extends Annotation> T getAnnotation(Class<T> annotationType) { return null; } @Override public Set<Annotation> getAnnotations() { return Collections.emptySet(); } @Override public Type getBaseType() { return PersistenceUnitProducer.class; } @Override public Set<Type> getTypeClosure() { return Collections.singleton((Type)PersistenceUnitProducer.class); } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { return false; } @Override public Constructor<PersistenceUnitProducer> getJavaMember() { return persistenceUnitConstructor; } } public class AnnotatedPersistenceUnitProducerField implements AnnotatedField<SpearalExtension.PersistenceUnitProducer> { private final Field persistenceUnitField; public AnnotatedPersistenceUnitProducerField() throws NoSuchFieldException { persistenceUnitField = PersistenceUnitProducer.class.getDeclaredField("persistenceUnit"); } @Override public AnnotatedType<PersistenceUnitProducer> getDeclaringType() { return AnnotatedPersistenceUnitProducerType.this; } @Override public boolean isStatic() { return false; } @SuppressWarnings("unchecked") @Override public <T extends Annotation> T getAnnotation(Class<T> annotationType) { for (Annotation annotation : AnnotatedPersistenceUnitProducerType.this.annotations) { if (annotation.annotationType().equals(annotationType)) return (T)annotation; } return null; } @Override public Set<Annotation> getAnnotations() { return annotations; } @Override public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) { for (Annotation annotation : AnnotatedPersistenceUnitProducerType.this.annotations) { if (annotation.annotationType().equals(annotationType)) return true; } return false; } @Override public Field getJavaMember() { return persistenceUnitField; } @Override public Type getBaseType() { return EntityManagerFactory.class; } @Override public Set<Type> getTypeClosure() { return Collections.singleton((Type)EntityManagerFactory.class); } } } }