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