001/*
002 * Copyright 2002-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.orm.jpa.support;
018
019import java.beans.PropertyDescriptor;
020import java.io.Serializable;
021import java.lang.reflect.AnnotatedElement;
022import java.lang.reflect.Field;
023import java.lang.reflect.Member;
024import java.lang.reflect.Method;
025import java.lang.reflect.Modifier;
026import java.util.LinkedList;
027import java.util.Map;
028import java.util.Properties;
029import java.util.concurrent.ConcurrentHashMap;
030import javax.persistence.EntityManager;
031import javax.persistence.EntityManagerFactory;
032import javax.persistence.PersistenceContext;
033import javax.persistence.PersistenceContextType;
034import javax.persistence.PersistenceProperty;
035import javax.persistence.PersistenceUnit;
036
037import org.springframework.beans.BeanUtils;
038import org.springframework.beans.BeansException;
039import org.springframework.beans.PropertyValues;
040import org.springframework.beans.factory.BeanCreationException;
041import org.springframework.beans.factory.BeanFactory;
042import org.springframework.beans.factory.BeanFactoryAware;
043import org.springframework.beans.factory.ListableBeanFactory;
044import org.springframework.beans.factory.NoSuchBeanDefinitionException;
045import org.springframework.beans.factory.annotation.InjectionMetadata;
046import org.springframework.beans.factory.config.ConfigurableBeanFactory;
047import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
048import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
049import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
050import org.springframework.beans.factory.config.NamedBeanHolder;
051import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
052import org.springframework.beans.factory.support.RootBeanDefinition;
053import org.springframework.core.BridgeMethodResolver;
054import org.springframework.core.Ordered;
055import org.springframework.core.PriorityOrdered;
056import org.springframework.jndi.JndiLocatorDelegate;
057import org.springframework.jndi.JndiTemplate;
058import org.springframework.orm.jpa.EntityManagerFactoryInfo;
059import org.springframework.orm.jpa.EntityManagerFactoryUtils;
060import org.springframework.orm.jpa.EntityManagerProxy;
061import org.springframework.orm.jpa.ExtendedEntityManagerCreator;
062import org.springframework.orm.jpa.SharedEntityManagerCreator;
063import org.springframework.util.ClassUtils;
064import org.springframework.util.ObjectUtils;
065import org.springframework.util.ReflectionUtils;
066import org.springframework.util.StringUtils;
067
068/**
069 * BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit}
070 * and {@link javax.persistence.PersistenceContext} annotations, for injection of
071 * the corresponding JPA resources {@link javax.persistence.EntityManagerFactory}
072 * and {@link javax.persistence.EntityManager}. Any such annotated fields or methods
073 * in any Spring-managed object will automatically be injected.
074 *
075 * <p>This post-processor will inject sub-interfaces of {@code EntityManagerFactory}
076 * and {@code EntityManager} if the annotated fields or methods are declared as such.
077 * The actual type will be verified early, with the exception of a shared ("transactional")
078 * {@code EntityManager} reference, where type mismatches might be detected as late
079 * as on the first actual invocation.
080 *
081 * <p>Note: In the present implementation, PersistenceAnnotationBeanPostProcessor
082 * only supports {@code @PersistenceUnit} and {@code @PersistenceContext}
083 * with the "unitName" attribute, or no attribute at all (for the default unit).
084 * If those annotations are present with the "name" attribute at the class level,
085 * they will simply be ignored, since those only serve as deployment hint
086 * (as per the Java EE specification).
087 *
088 * <p>This post-processor can either obtain EntityManagerFactory beans defined
089 * in the Spring application context (the default), or obtain EntityManagerFactory
090 * references from JNDI ("persistence unit references"). In the bean case,
091 * the persistence unit name will be matched against the actual deployed unit,
092 * with the bean name used as fallback unit name if no deployed name found.
093 * Typically, Spring's {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean}
094 * will be used for setting up such EntityManagerFactory beans. Alternatively,
095 * such beans may also be obtained from JNDI, e.g. using the {@code jee:jndi-lookup}
096 * XML configuration element (with the bean name matching the requested unit name).
097 * In both cases, the post-processor definition will look as simple as this:
098 *
099 * <pre class="code">
100 * &lt;bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/&gt;</pre>
101 *
102 * In the JNDI case, specify the corresponding JNDI names in this post-processor's
103 * {@link #setPersistenceUnits "persistenceUnits" map}, typically with matching
104 * {@code persistence-unit-ref} entries in the Java EE deployment descriptor.
105 * By default, those names are considered as resource references (according to the
106 * Java EE resource-ref convention), located underneath the "java:comp/env/" namespace.
107 * For example:
108 *
109 * <pre class="code">
110 * &lt;bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"&gt;
111 *   &lt;property name="persistenceUnits"&gt;
112 *     &lt;map/gt;
113 *       &lt;entry key="unit1" value="persistence/unit1"/&gt;
114 *       &lt;entry key="unit2" value="persistence/unit2"/&gt;
115 *     &lt;/map/gt;
116 *   &lt;/property&gt;
117 * &lt;/bean&gt;</pre>
118 *
119 * In this case, the specified persistence units will always be resolved in JNDI
120 * rather than as Spring-defined beans. The entire persistence unit deployment,
121 * including the weaving of persistent classes, is then up to the Java EE server.
122 * Persistence contexts (i.e. EntityManager references) will be built based on
123 * those server-provided EntityManagerFactory references, using Spring's own
124 * transaction synchronization facilities for transactional EntityManager handling
125 * (typically with Spring's {@code @Transactional} annotation for demarcation
126 * and {@link org.springframework.transaction.jta.JtaTransactionManager} as backend).
127 *
128 * <p>If you prefer the Java EE server's own EntityManager handling, specify entries
129 * in this post-processor's {@link #setPersistenceContexts "persistenceContexts" map}
130 * (or {@link #setExtendedPersistenceContexts "extendedPersistenceContexts" map},
131 * typically with matching {@code persistence-context-ref} entries in the
132 * Java EE deployment descriptor. For example:
133 *
134 * <pre class="code">
135 * &lt;bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"&gt;
136 *   &lt;property name="persistenceContexts"&gt;
137 *     &lt;map/gt;
138 *       &lt;entry key="unit1" value="persistence/context1"/&gt;
139 *       &lt;entry key="unit2" value="persistence/context2"/&gt;
140 *     &lt;/map/gt;
141 *   &lt;/property&gt;
142 * &lt;/bean&gt;</pre>
143 *
144 * If the application only obtains EntityManager references in the first place,
145 * this is all you need to specify. If you need EntityManagerFactory references
146 * as well, specify entries for both "persistenceUnits" and "persistenceContexts",
147 * pointing to matching JNDI locations.
148 *
149 * <p><b>NOTE: In general, do not inject EXTENDED EntityManagers into STATELESS beans,
150 * i.e. do not use {@code @PersistenceContext} with type {@code EXTENDED} in
151 * Spring beans defined with scope 'singleton' (Spring's default scope).</b>
152 * Extended EntityManagers are <i>not</i> thread-safe, hence they must not be used
153 * in concurrently accessed beans (which Spring-managed singletons usually are).
154 *
155 * <p>Note: A default PersistenceAnnotationBeanPostProcessor will be registered
156 * by the "context:annotation-config" and "context:component-scan" XML tags.
157 * Remove or turn off the default annotation configuration there if you intend
158 * to specify a custom PersistenceAnnotationBeanPostProcessor bean definition.
159 *
160 * @author Rod Johnson
161 * @author Juergen Hoeller
162 * @since 2.0
163 * @see javax.persistence.PersistenceUnit
164 * @see javax.persistence.PersistenceContext
165 */
166@SuppressWarnings("serial")
167public class PersistenceAnnotationBeanPostProcessor
168                implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor,
169                MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable {
170
171        /* Check JPA 2.1 PersistenceContext.synchronization() attribute */
172        private static final Method synchronizationAttribute =
173                        ClassUtils.getMethodIfAvailable(PersistenceContext.class, "synchronization");
174
175
176        private Object jndiEnvironment;
177
178        private boolean resourceRef = true;
179
180        private transient Map<String, String> persistenceUnits;
181
182        private transient Map<String, String> persistenceContexts;
183
184        private transient Map<String, String> extendedPersistenceContexts;
185
186        private transient String defaultPersistenceUnitName = "";
187
188        private int order = Ordered.LOWEST_PRECEDENCE - 4;
189
190        private transient ListableBeanFactory beanFactory;
191
192        private transient final Map<String, InjectionMetadata> injectionMetadataCache =
193                        new ConcurrentHashMap<String, InjectionMetadata>(256);
194
195        private final Map<Object, EntityManager> extendedEntityManagersToClose =
196                        new ConcurrentHashMap<Object, EntityManager>(16);
197
198
199        /**
200         * Set the JNDI template to use for JNDI lookups.
201         * @see org.springframework.jndi.JndiAccessor#setJndiTemplate
202         */
203        public void setJndiTemplate(Object jndiTemplate) {
204                this.jndiEnvironment = jndiTemplate;
205        }
206
207        /**
208         * Set the JNDI environment to use for JNDI lookups.
209         * @see org.springframework.jndi.JndiAccessor#setJndiEnvironment
210         */
211        public void setJndiEnvironment(Properties jndiEnvironment) {
212                this.jndiEnvironment = jndiEnvironment;
213        }
214
215        /**
216         * Set whether the lookup occurs in a J2EE container, i.e. if the prefix
217         * "java:comp/env/" needs to be added if the JNDI name doesn't already
218         * contain it. PersistenceAnnotationBeanPostProcessor's default is "true".
219         * @see org.springframework.jndi.JndiLocatorSupport#setResourceRef
220         */
221        public void setResourceRef(boolean resourceRef) {
222                this.resourceRef = resourceRef;
223        }
224
225        /**
226         * Specify the persistence units for EntityManagerFactory lookups,
227         * as a Map from persistence unit name to persistence unit JNDI name
228         * (which needs to resolve to an EntityManagerFactory instance).
229         * <p>JNDI names specified here should refer to {@code persistence-unit-ref}
230         * entries in the Java EE deployment descriptor, matching the target persistence unit.
231         * <p>In case of no unit name specified in the annotation, the specified value
232         * for the {@link #setDefaultPersistenceUnitName default persistence unit}
233         * will be taken (by default, the value mapped to the empty String),
234         * or simply the single persistence unit if there is only one.
235         * <p>This is mainly intended for use in a Java EE environment, with all lookup
236         * driven by the standard JPA annotations, and all EntityManagerFactory
237         * references obtained from JNDI. No separate EntityManagerFactory bean
238         * definitions are necessary in such a scenario.
239         * <p>If no corresponding "persistenceContexts"/"extendedPersistenceContexts"
240         * are specified, {@code @PersistenceContext} will be resolved to
241         * EntityManagers built on top of the EntityManagerFactory defined here.
242         * Note that those will be Spring-managed EntityManagers, which implement
243         * transaction synchronization based on Spring's facilities.
244         * If you prefer the Java EE server's own EntityManager handling,
245         * specify corresponding "persistenceContexts"/"extendedPersistenceContexts".
246         */
247        public void setPersistenceUnits(Map<String, String> persistenceUnits) {
248                this.persistenceUnits = persistenceUnits;
249        }
250
251        /**
252         * Specify the <i>transactional</i> persistence contexts for EntityManager lookups,
253         * as a Map from persistence unit name to persistence context JNDI name
254         * (which needs to resolve to an EntityManager instance).
255         * <p>JNDI names specified here should refer to {@code persistence-context-ref}
256         * entries in the Java EE deployment descriptors, matching the target persistence unit
257         * and being set up with persistence context type {@code Transaction}.
258         * <p>In case of no unit name specified in the annotation, the specified value
259         * for the {@link #setDefaultPersistenceUnitName default persistence unit}
260         * will be taken (by default, the value mapped to the empty String),
261         * or simply the single persistence unit if there is only one.
262         * <p>This is mainly intended for use in a Java EE environment, with all
263         * lookup driven by the standard JPA annotations, and all EntityManager
264         * references obtained from JNDI. No separate EntityManagerFactory bean
265         * definitions are necessary in such a scenario, and all EntityManager
266         * handling is done by the Java EE server itself.
267         */
268        public void setPersistenceContexts(Map<String, String> persistenceContexts) {
269                this.persistenceContexts = persistenceContexts;
270        }
271
272        /**
273         * Specify the <i>extended</i> persistence contexts for EntityManager lookups,
274         * as a Map from persistence unit name to persistence context JNDI name
275         * (which needs to resolve to an EntityManager instance).
276         * <p>JNDI names specified here should refer to {@code persistence-context-ref}
277         * entries in the Java EE deployment descriptors, matching the target persistence unit
278         * and being set up with persistence context type {@code Extended}.
279         * <p>In case of no unit name specified in the annotation, the specified value
280         * for the {@link #setDefaultPersistenceUnitName default persistence unit}
281         * will be taken (by default, the value mapped to the empty String),
282         * or simply the single persistence unit if there is only one.
283         * <p>This is mainly intended for use in a Java EE environment, with all
284         * lookup driven by the standard JPA annotations, and all EntityManager
285         * references obtained from JNDI. No separate EntityManagerFactory bean
286         * definitions are necessary in such a scenario, and all EntityManager
287         * handling is done by the Java EE server itself.
288         */
289        public void setExtendedPersistenceContexts(Map<String, String> extendedPersistenceContexts) {
290                this.extendedPersistenceContexts = extendedPersistenceContexts;
291        }
292
293        /**
294         * Specify the default persistence unit name, to be used in case
295         * of no unit name specified in an {@code @PersistenceUnit} /
296         * {@code @PersistenceContext} annotation.
297         * <p>This is mainly intended for lookups in the application context,
298         * indicating the target persistence unit name (typically matching
299         * the bean name), but also applies to lookups in the
300         * {@link #setPersistenceUnits "persistenceUnits"} /
301         * {@link #setPersistenceContexts "persistenceContexts"} /
302         * {@link #setExtendedPersistenceContexts "extendedPersistenceContexts"} map,
303         * avoiding the need for duplicated mappings for the empty String there.
304         * <p>Default is to check for a single EntityManagerFactory bean
305         * in the Spring application context, if any. If there are multiple
306         * such factories, either specify this default persistence unit name
307         * or explicitly refer to named persistence units in your annotations.
308         */
309        public void setDefaultPersistenceUnitName(String unitName) {
310                this.defaultPersistenceUnitName = (unitName != null ? unitName : "");
311        }
312
313        public void setOrder(int order) {
314          this.order = order;
315        }
316
317        @Override
318        public int getOrder() {
319          return this.order;
320        }
321
322        @Override
323        public void setBeanFactory(BeanFactory beanFactory) {
324                if (beanFactory instanceof ListableBeanFactory) {
325                        this.beanFactory = (ListableBeanFactory) beanFactory;
326                }
327        }
328
329
330        @Override
331        public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
332                if (beanType != null) {
333                        InjectionMetadata metadata = findPersistenceMetadata(beanName, beanType, null);
334                        metadata.checkConfigMembers(beanDefinition);
335                }
336        }
337
338        @Override
339        public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
340                return null;
341        }
342
343        @Override
344        public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
345                return true;
346        }
347
348        @Override
349        public PropertyValues postProcessPropertyValues(
350                        PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
351
352                InjectionMetadata metadata = findPersistenceMetadata(beanName, bean.getClass(), pvs);
353                try {
354                        metadata.inject(bean, beanName, pvs);
355                }
356                catch (Throwable ex) {
357                        throw new BeanCreationException(beanName, "Injection of persistence dependencies failed", ex);
358                }
359                return pvs;
360        }
361
362        @Override
363        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
364                return bean;
365        }
366
367        @Override
368        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
369                return bean;
370        }
371
372        @Override
373        public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
374                EntityManager emToClose = this.extendedEntityManagersToClose.remove(bean);
375                EntityManagerFactoryUtils.closeEntityManager(emToClose);
376        }
377
378        @Override
379        public boolean requiresDestruction(Object bean) {
380                return this.extendedEntityManagersToClose.containsKey(bean);
381        }
382
383
384        private InjectionMetadata findPersistenceMetadata(String beanName, final Class<?> clazz, PropertyValues pvs) {
385                // Fall back to class name as cache key, for backwards compatibility with custom callers.
386                String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
387                // Quick check on the concurrent map first, with minimal locking.
388                InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
389                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
390                        synchronized (this.injectionMetadataCache) {
391                                metadata = this.injectionMetadataCache.get(cacheKey);
392                                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
393                                        if (metadata != null) {
394                                                metadata.clear(pvs);
395                                        }
396                                        try {
397                                                metadata = buildPersistenceMetadata(clazz);
398                                                this.injectionMetadataCache.put(cacheKey, metadata);
399                                        }
400                                        catch (NoClassDefFoundError err) {
401                                                throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
402                                                                "] for persistence metadata: could not find class that it depends on", err);
403                                        }
404                                }
405                        }
406                }
407                return metadata;
408        }
409
410        private InjectionMetadata buildPersistenceMetadata(final Class<?> clazz) {
411                LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
412                Class<?> targetClass = clazz;
413
414                do {
415                        final LinkedList<InjectionMetadata.InjectedElement> currElements =
416                                        new LinkedList<InjectionMetadata.InjectedElement>();
417
418                        ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
419                                @Override
420                                public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
421                                        if (field.isAnnotationPresent(PersistenceContext.class) ||
422                                                        field.isAnnotationPresent(PersistenceUnit.class)) {
423                                                if (Modifier.isStatic(field.getModifiers())) {
424                                                        throw new IllegalStateException("Persistence annotations are not supported on static fields");
425                                                }
426                                                currElements.add(new PersistenceElement(field, field, null));
427                                        }
428                                }
429                        });
430
431                        ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
432                                @Override
433                                public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
434                                        Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
435                                        if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
436                                                return;
437                                        }
438                                        if ((bridgedMethod.isAnnotationPresent(PersistenceContext.class) ||
439                                                        bridgedMethod.isAnnotationPresent(PersistenceUnit.class)) &&
440                                                        method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
441                                                if (Modifier.isStatic(method.getModifiers())) {
442                                                        throw new IllegalStateException("Persistence annotations are not supported on static methods");
443                                                }
444                                                if (method.getParameterTypes().length != 1) {
445                                                        throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method);
446                                                }
447                                                PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
448                                                currElements.add(new PersistenceElement(method, bridgedMethod, pd));
449                                        }
450                                }
451                        });
452
453                        elements.addAll(0, currElements);
454                        targetClass = targetClass.getSuperclass();
455                }
456                while (targetClass != null && targetClass != Object.class);
457
458                return new InjectionMetadata(clazz, elements);
459        }
460
461        /**
462         * Return a specified persistence unit for the given unit name,
463         * as defined through the "persistenceUnits" map.
464         * @param unitName the name of the persistence unit
465         * @return the corresponding EntityManagerFactory,
466         * or {@code null} if none found
467         * @see #setPersistenceUnits
468         */
469        protected EntityManagerFactory getPersistenceUnit(String unitName) {
470                if (this.persistenceUnits != null) {
471                        String unitNameForLookup = (unitName != null ? unitName : "");
472                        if ("".equals(unitNameForLookup)) {
473                                unitNameForLookup = this.defaultPersistenceUnitName;
474                        }
475                        String jndiName = this.persistenceUnits.get(unitNameForLookup);
476                        if (jndiName == null && "".equals(unitNameForLookup) && this.persistenceUnits.size() == 1) {
477                                jndiName = this.persistenceUnits.values().iterator().next();
478                        }
479                        if (jndiName != null) {
480                                try {
481                                        return lookup(jndiName, EntityManagerFactory.class);
482                                }
483                                catch (Exception ex) {
484                                        throw new IllegalStateException("Could not obtain EntityManagerFactory [" + jndiName + "] from JNDI", ex);
485                                }
486                        }
487                }
488                return null;
489        }
490
491        /**
492         * Return a specified persistence context for the given unit name, as defined
493         * through the "persistenceContexts" (or "extendedPersistenceContexts") map.
494         * @param unitName the name of the persistence unit
495         * @param extended whether to obtain an extended persistence context
496         * @return the corresponding EntityManager, or {@code null} if none found
497         * @see #setPersistenceContexts
498         * @see #setExtendedPersistenceContexts
499         */
500        protected EntityManager getPersistenceContext(String unitName, boolean extended) {
501                Map<String, String> contexts = (extended ? this.extendedPersistenceContexts : this.persistenceContexts);
502                if (contexts != null) {
503                        String unitNameForLookup = (unitName != null ? unitName : "");
504                        if ("".equals(unitNameForLookup)) {
505                                unitNameForLookup = this.defaultPersistenceUnitName;
506                        }
507                        String jndiName = contexts.get(unitNameForLookup);
508                        if (jndiName == null && "".equals(unitNameForLookup) && contexts.size() == 1) {
509                                jndiName = contexts.values().iterator().next();
510                        }
511                        if (jndiName != null) {
512                                try {
513                                        return lookup(jndiName, EntityManager.class);
514                                }
515                                catch (Exception ex) {
516                                        throw new IllegalStateException("Could not obtain EntityManager [" + jndiName + "] from JNDI", ex);
517                                }
518                        }
519                }
520                return null;
521        }
522
523        /**
524         * Find an EntityManagerFactory with the given name in the current Spring
525         * application context, falling back to a single default EntityManagerFactory
526         * (if any) in case of no unit name specified.
527         * @param unitName the name of the persistence unit (may be {@code null} or empty)
528         * @param requestingBeanName the name of the requesting bean
529         * @return the EntityManagerFactory
530         * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
531         */
532        protected EntityManagerFactory findEntityManagerFactory(String unitName, String requestingBeanName)
533                        throws NoSuchBeanDefinitionException {
534
535                if (this.beanFactory == null) {
536                        throw new IllegalStateException("ListableBeanFactory required for EntityManagerFactory bean lookup");
537                }
538                String unitNameForLookup = (unitName != null ? unitName : "");
539                if ("".equals(unitNameForLookup)) {
540                        unitNameForLookup = this.defaultPersistenceUnitName;
541                }
542                if (!"".equals(unitNameForLookup)) {
543                        return findNamedEntityManagerFactory(unitNameForLookup, requestingBeanName);
544                }
545                else {
546                        return findDefaultEntityManagerFactory(requestingBeanName);
547                }
548        }
549
550        /**
551         * Find an EntityManagerFactory with the given name in the current
552         * Spring application context.
553         * @param unitName the name of the persistence unit (never empty)
554         * @param requestingBeanName the name of the requesting bean
555         * @return the EntityManagerFactory
556         * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
557         */
558        protected EntityManagerFactory findNamedEntityManagerFactory(String unitName, String requestingBeanName)
559                        throws NoSuchBeanDefinitionException {
560
561                EntityManagerFactory emf = EntityManagerFactoryUtils.findEntityManagerFactory(this.beanFactory, unitName);
562                if (this.beanFactory instanceof ConfigurableBeanFactory) {
563                        ((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName, requestingBeanName);
564                }
565                return emf;
566        }
567
568        /**
569         * Find a single default EntityManagerFactory in the Spring application context.
570         * @return the default EntityManagerFactory
571         * @throws NoSuchBeanDefinitionException if there is no single EntityManagerFactory in the context
572         */
573        protected EntityManagerFactory findDefaultEntityManagerFactory(String requestingBeanName)
574                        throws NoSuchBeanDefinitionException {
575
576                if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
577                        // Fancy variant with dependency registration
578                        ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) this.beanFactory;
579                        NamedBeanHolder<EntityManagerFactory> emfHolder = clbf.resolveNamedBean(EntityManagerFactory.class);
580                        clbf.registerDependentBean(emfHolder.getBeanName(), requestingBeanName);
581                        return emfHolder.getBeanInstance();
582                }
583                else {
584                        // Plain variant: just find a default bean
585                        return this.beanFactory.getBean(EntityManagerFactory.class);
586                }
587        }
588
589        /**
590         * Perform a JNDI lookup for the given resource by name.
591         * <p>Called for EntityManagerFactory and EntityManager lookup
592         * when JNDI names are mapped for specific persistence units.
593         * @param jndiName the JNDI name to look up
594         * @param requiredType the required type of the object
595         * @return the obtained object
596         * @throws Exception if the JNDI lookup failed
597         */
598        protected <T> T lookup(String jndiName, Class<T> requiredType) throws Exception {
599                return new LocatorDelegate().lookup(jndiName, requiredType);
600        }
601
602
603        /**
604         * Separate inner class to isolate the JNDI API dependency
605         * (for compatibility with Google App Engine's API white list).
606         */
607        private class LocatorDelegate {
608
609                public <T> T lookup(String jndiName, Class<T> requiredType) throws Exception {
610                        JndiLocatorDelegate locator = new JndiLocatorDelegate();
611                        if (jndiEnvironment instanceof JndiTemplate) {
612                                locator.setJndiTemplate((JndiTemplate) jndiEnvironment);
613                        }
614                        else if (jndiEnvironment instanceof Properties) {
615                                locator.setJndiEnvironment((Properties) jndiEnvironment);
616                        }
617                        else if (jndiEnvironment != null) {
618                                throw new IllegalStateException("Illegal 'jndiEnvironment' type: " + jndiEnvironment.getClass());
619                        }
620                        locator.setResourceRef(resourceRef);
621                        return locator.lookup(jndiName, requiredType);
622                }
623        }
624
625
626        /**
627         * Class representing injection information about an annotated field
628         * or setter method.
629         */
630        private class PersistenceElement extends InjectionMetadata.InjectedElement {
631
632                private final String unitName;
633
634                private PersistenceContextType type;
635
636                private boolean synchronizedWithTransaction = false;
637
638                private Properties properties;
639
640                public PersistenceElement(Member member, AnnotatedElement ae, PropertyDescriptor pd) {
641                        super(member, pd);
642                        PersistenceContext pc = ae.getAnnotation(PersistenceContext.class);
643                        PersistenceUnit pu = ae.getAnnotation(PersistenceUnit.class);
644                        Class<?> resourceType = EntityManager.class;
645                        if (pc != null) {
646                                if (pu != null) {
647                                        throw new IllegalStateException("Member may only be annotated with either " +
648                                                        "@PersistenceContext or @PersistenceUnit, not both: " + member);
649                                }
650                                Properties properties = null;
651                                PersistenceProperty[] pps = pc.properties();
652                                if (!ObjectUtils.isEmpty(pps)) {
653                                        properties = new Properties();
654                                        for (PersistenceProperty pp : pps) {
655                                                properties.setProperty(pp.name(), pp.value());
656                                        }
657                                }
658                                this.unitName = pc.unitName();
659                                this.type = pc.type();
660                                this.synchronizedWithTransaction = (synchronizationAttribute == null ||
661                                                "SYNCHRONIZED".equals(ReflectionUtils.invokeMethod(synchronizationAttribute, pc).toString()));
662                                this.properties = properties;
663                        }
664                        else {
665                                resourceType = EntityManagerFactory.class;
666                                this.unitName = pu.unitName();
667                        }
668                        checkResourceType(resourceType);
669                }
670
671                /**
672                 * Resolve the object against the application context.
673                 */
674                @Override
675                protected Object getResourceToInject(Object target, String requestingBeanName) {
676                        // Resolves to EntityManagerFactory or EntityManager.
677                        if (this.type != null) {
678                                return (this.type == PersistenceContextType.EXTENDED ?
679                                                resolveExtendedEntityManager(target, requestingBeanName) :
680                                                resolveEntityManager(requestingBeanName));
681                        }
682                        else {
683                                // OK, so we need an EntityManagerFactory...
684                                return resolveEntityManagerFactory(requestingBeanName);
685                        }
686                }
687
688                private EntityManagerFactory resolveEntityManagerFactory(String requestingBeanName) {
689                        // Obtain EntityManagerFactory from JNDI?
690                        EntityManagerFactory emf = getPersistenceUnit(this.unitName);
691                        if (emf == null) {
692                                // Need to search for EntityManagerFactory beans.
693                                emf = findEntityManagerFactory(this.unitName, requestingBeanName);
694                        }
695                        return emf;
696                }
697
698                private EntityManager resolveEntityManager(String requestingBeanName) {
699                        // Obtain EntityManager reference from JNDI?
700                        EntityManager em = getPersistenceContext(this.unitName, false);
701                        if (em == null) {
702                                // No pre-built EntityManager found -> build one based on factory.
703                                // Obtain EntityManagerFactory from JNDI?
704                                EntityManagerFactory emf = getPersistenceUnit(this.unitName);
705                                if (emf == null) {
706                                        // Need to search for EntityManagerFactory beans.
707                                        emf = findEntityManagerFactory(this.unitName, requestingBeanName);
708                                }
709                                // Inject a shared transactional EntityManager proxy.
710                                if (emf instanceof EntityManagerFactoryInfo &&
711                                                ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) {
712                                        // Create EntityManager based on the info's vendor-specific type
713                                        // (which might be more specific than the field's type).
714                                        em = SharedEntityManagerCreator.createSharedEntityManager(
715                                                        emf, this.properties, this.synchronizedWithTransaction);
716                                }
717                                else {
718                                        // Create EntityManager based on the field's type.
719                                        em = SharedEntityManagerCreator.createSharedEntityManager(
720                                                        emf, this.properties, this.synchronizedWithTransaction, getResourceType());
721                                }
722                        }
723                        return em;
724                }
725
726                private EntityManager resolveExtendedEntityManager(Object target, String requestingBeanName) {
727                        // Obtain EntityManager reference from JNDI?
728                        EntityManager em = getPersistenceContext(this.unitName, true);
729                        if (em == null) {
730                                // No pre-built EntityManager found -> build one based on factory.
731                                // Obtain EntityManagerFactory from JNDI?
732                                EntityManagerFactory emf = getPersistenceUnit(this.unitName);
733                                if (emf == null) {
734                                        // Need to search for EntityManagerFactory beans.
735                                        emf = findEntityManagerFactory(this.unitName, requestingBeanName);
736                                }
737                                // Inject a container-managed extended EntityManager.
738                                em = ExtendedEntityManagerCreator.createContainerManagedEntityManager(
739                                                emf, this.properties, this.synchronizedWithTransaction);
740                        }
741                        if (em instanceof EntityManagerProxy && beanFactory != null &&
742                                        beanFactory.containsBean(requestingBeanName) && !beanFactory.isPrototype(requestingBeanName)) {
743                                extendedEntityManagersToClose.put(target, ((EntityManagerProxy) em).getTargetEntityManager());
744                        }
745                        return em;
746                }
747        }
748
749}