1 /*
2 * Copyright 2002-2008 the original author or authors.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package org.springframework.orm.jpa.support;
18
19 import java.beans.PropertyDescriptor;
20 import java.io.Serializable;
21 import java.lang.reflect.AnnotatedElement;
22 import java.lang.reflect.Field;
23 import java.lang.reflect.Member;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
26 import java.util.Map;
27 import java.util.Properties;
28 import java.util.concurrent.ConcurrentHashMap;
29
30 import javax.naming.NamingException;
31 import javax.persistence.EntityManager;
32 import javax.persistence.EntityManagerFactory;
33 import javax.persistence.PersistenceContext;
34 import javax.persistence.PersistenceContextType;
35 import javax.persistence.PersistenceProperty;
36 import javax.persistence.PersistenceUnit;
37
38 import org.springframework.beans.BeanUtils;
39 import org.springframework.beans.BeansException;
40 import org.springframework.beans.PropertyValues;
41 import org.springframework.beans.factory.BeanCreationException;
42 import org.springframework.beans.factory.BeanFactory;
43 import org.springframework.beans.factory.BeanFactoryAware;
44 import org.springframework.beans.factory.BeanFactoryUtils;
45 import org.springframework.beans.factory.ListableBeanFactory;
46 import org.springframework.beans.factory.NoSuchBeanDefinitionException;
47 import org.springframework.beans.factory.annotation.InjectionMetadata;
48 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
49 import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
50 import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;
51 import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
52 import org.springframework.beans.factory.support.RootBeanDefinition;
53 import org.springframework.core.Ordered;
54 import org.springframework.core.PriorityOrdered;
55 import org.springframework.jndi.JndiLocatorSupport;
56 import org.springframework.orm.jpa.EntityManagerFactoryInfo;
57 import org.springframework.orm.jpa.EntityManagerFactoryUtils;
58 import org.springframework.orm.jpa.EntityManagerProxy;
59 import org.springframework.orm.jpa.ExtendedEntityManagerCreator;
60 import org.springframework.orm.jpa.SharedEntityManagerCreator;
61 import org.springframework.util.ClassUtils;
62 import org.springframework.util.ObjectUtils;
63 import org.springframework.util.ReflectionUtils;
64
65 /**
66 * BeanPostProcessor that processes {@link javax.persistence.PersistenceUnit}
67 * and {@link javax.persistence.PersistenceContext} annotations, for injection of
68 * the corresponding JPA resources {@link javax.persistence.EntityManagerFactory}
69 * and {@link javax.persistence.EntityManager}. Any such annotated fields or methods
70 * in any Spring-managed object will automatically be injected.
71 *
72 * <p>This post-processor will inject sub-interfaces of <code>EntityManagerFactory</code>
73 * and <code>EntityManager</code> if the annotated fields or methods are declared as such.
74 * The actual type will be verified early, with the exception of a shared ("transactional")
75 * <code>EntityManager</code> reference, where type mismatches might be detected as late
76 * as on the first actual invocation.
77 *
78 * <p>Note: In the present implementation, PersistenceAnnotationBeanPostProcessor
79 * only supports <code>@PersistenceUnit</code> and <code>@PersistenceContext</code>
80 * with the "unitName" attribute, or no attribute at all (for the default unit).
81 * If those annotations are present with the "name" attribute at the class level,
82 * they will simply be ignored, since those only serve as deployment hint
83 * (as per the Java EE 5 specification).
84 *
85 * <p>This post-processor can either obtain EntityManagerFactory beans defined
86 * in the Spring application context (the default), or obtain EntityManagerFactory
87 * references from JNDI ("persistence unit references"). In the bean case,
88 * the persistence unit name will be matched against the actual deployed unit,
89 * with the bean name used as fallback unit name if no deployed name found.
90 * Typically, Spring's {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean}
91 * will be used for setting up such EntityManagerFactory beans. Alternatively,
92 * such beans may also be obtained from JNDI, e.g. using the <code>jee:jndi-lookup</code>
93 * XML configuration element (with the bean name matching the requested unit name).
94 * In both cases, the post-processor definition will look as simple as this:
95 *
96 * <pre class="code">
97 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/></pre>
98 *
99 * In the JNDI case, specify the corresponding JNDI names in this post-processor's
100 * {@link #setPersistenceUnits "persistenceUnits" map}, typically with matching
101 * <code>persistence-unit-ref</code> entries in the Java EE deployment descriptor.
102 * By default, those names are considered as resource references (according to the
103 * Java EE resource-ref convention), located underneath the "java:comp/env/" namespace.
104 * For example:
105 *
106 * <pre class="code">
107 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
108 * <property name="persistenceUnits">
109 * <map/gt;
110 * <entry key="unit1" value="persistence/unit1"/>
111 * <entry key="unit2" value="persistence/unit2"/>
112 * </map/gt;
113 * </property>
114 * </bean></pre>
115 *
116 * In this case, the specified persistence units will always be resolved in JNDI
117 * rather than as Spring-defined beans. The entire persistence unit deployment,
118 * including the weaving of persistent classes, is then up to the Java EE server.
119 * Persistence contexts (i.e. EntityManager references) will be built based on
120 * those server-provided EntityManagerFactory references, using Spring's own
121 * transaction synchronization facilities for transactional EntityManager handling
122 * (typically with Spring's <code>@Transactional</code> annotation for demarcation
123 * and {@link org.springframework.transaction.jta.JtaTransactionManager} as backend).
124 *
125 * <p>If you prefer the Java EE server's own EntityManager handling, specify entries
126 * in this post-processor's {@link #setPersistenceContexts "persistenceContexts" map}
127 * (or {@link #setExtendedPersistenceContexts "extendedPersistenceContexts" map},
128 * typically with matching <code>persistence-context-ref</code> entries in the
129 * Java EE deployment descriptor. For example:
130 *
131 * <pre class="code">
132 * <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">
133 * <property name="persistenceContexts">
134 * <map/gt;
135 * <entry key="unit1" value="persistence/context1"/>
136 * <entry key="unit2" value="persistence/context2"/>
137 * </map/gt;
138 * </property>
139 * </bean></pre>
140 *
141 * If the application only obtains EntityManager references in the first place,
142 * this is all you need to specify. If you need EntityManagerFactory references
143 * as well, specify entries for both "persistenceUnits" and "persistenceContexts",
144 * pointing to matching JNDI locations.
145 *
146 * <p><b>NOTE: In general, do not inject EXTENDED EntityManagers into STATELESS beans,
147 * i.e. do not use <code>@PersistenceContext</code> with type <code>EXTENDED</code> in
148 * Spring beans defined with scope 'singleton' (Spring's default scope).</b>
149 * Extended EntityManagers are <i>not</i> thread-safe, hence they must not be used
150 * in concurrently accessed beans (which Spring-managed singletons usually are).
151 *
152 * <p>Note: A default PersistenceAnnotationBeanPostProcessor will be registered
153 * by the "context:annotation-config" and "context:component-scan" XML tags.
154 * Remove or turn off the default annotation configuration there if you intend
155 * to specify a custom PersistenceAnnotationBeanPostProcessor bean definition.
156 *
157 * @author Rod Johnson
158 * @author Juergen Hoeller
159 * @since 2.0
160 * @see javax.persistence.PersistenceUnit
161 * @see javax.persistence.PersistenceContext
162 */
163 public class PersistenceAnnotationBeanPostProcessor extends JndiLocatorSupport
164 implements InstantiationAwareBeanPostProcessor, DestructionAwareBeanPostProcessor,
165 MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware, Serializable {
166
167 private transient Map<String, String> persistenceUnits;
168
169 private transient Map<String, String> persistenceContexts;
170
171 private transient Map<String, String> extendedPersistenceContexts;
172
173 private transient String defaultPersistenceUnitName = "";
174
175 private int order = Ordered.LOWEST_PRECEDENCE - 4;
176
177 private transient ListableBeanFactory beanFactory;
178
179 private transient final Map<Class<?>, InjectionMetadata> injectionMetadataCache =
180 new ConcurrentHashMap<Class<?>, InjectionMetadata>();
181
182 private final Map<Object, EntityManager> extendedEntityManagersToClose =
183 new ConcurrentHashMap<Object, EntityManager>();
184
185
186 public PersistenceAnnotationBeanPostProcessor() {
187 setResourceRef(true);
188 }
189
190
191 /**
192 * Specify the persistence units for EntityManagerFactory lookups,
193 * as a Map from persistence unit name to persistence unit JNDI name
194 * (which needs to resolve to an EntityManagerFactory instance).
195 * <p>JNDI names specified here should refer to <code>persistence-unit-ref</code>
196 * entries in the Java EE deployment descriptor, matching the target persistence unit.
197 * <p>In case of no unit name specified in the annotation, the specified value
198 * for the {@link #setDefaultPersistenceUnitName default persistence unit}
199 * will be taken (by default, the value mapped to the empty String),
200 * or simply the single persistence unit if there is only one.
201 * <p>This is mainly intended for use in a Java EE 5 environment, with all
202 * lookup driven by the standard JPA annotations, and all EntityManagerFactory
203 * references obtained from JNDI. No separate EntityManagerFactory bean
204 * definitions are necessary in such a scenario.
205 * <p>If no corresponding "persistenceContexts"/"extendedPersistenceContexts"
206 * are specified, <code>@PersistenceContext</code> will be resolved to
207 * EntityManagers built on top of the EntityManagerFactory defined here.
208 * Note that those will be Spring-managed EntityManagers, which implement
209 * transaction synchronization based on Spring's facilities.
210 * If you prefer the Java EE 5 server's own EntityManager handling,
211 * specify corresponding "persistenceContexts"/"extendedPersistenceContexts".
212 */
213 public void setPersistenceUnits(Map<String, String> persistenceUnits) {
214 this.persistenceUnits = persistenceUnits;
215 }
216
217 /**
218 * Specify the <i>transactional</i> persistence contexts for EntityManager lookups,
219 * as a Map from persistence unit name to persistence context JNDI name
220 * (which needs to resolve to an EntityManager instance).
221 * <p>JNDI names specified here should refer to <code>persistence-context-ref</code>
222 * entries in the Java EE deployment descriptors, matching the target persistence unit
223 * and being set up with persistence context type <code>Transaction</code>.
224 * <p>In case of no unit name specified in the annotation, the specified value
225 * for the {@link #setDefaultPersistenceUnitName default persistence unit}
226 * will be taken (by default, the value mapped to the empty String),
227 * or simply the single persistence unit if there is only one.
228 * <p>This is mainly intended for use in a Java EE 5 environment, with all
229 * lookup driven by the standard JPA annotations, and all EntityManager
230 * references obtained from JNDI. No separate EntityManagerFactory bean
231 * definitions are necessary in such a scenario, and all EntityManager
232 * handling is done by the Java EE 5 server itself.
233 */
234 public void setPersistenceContexts(Map<String, String> persistenceContexts) {
235 this.persistenceContexts = persistenceContexts;
236 }
237
238 /**
239 * Specify the <i>extended</i> persistence contexts for EntityManager lookups,
240 * as a Map from persistence unit name to persistence context JNDI name
241 * (which needs to resolve to an EntityManager instance).
242 * <p>JNDI names specified here should refer to <code>persistence-context-ref</code>
243 * entries in the Java EE deployment descriptors, matching the target persistence unit
244 * and being set up with persistence context type <code>Extended</code>.
245 * <p>In case of no unit name specified in the annotation, the specified value
246 * for the {@link #setDefaultPersistenceUnitName default persistence unit}
247 * will be taken (by default, the value mapped to the empty String),
248 * or simply the single persistence unit if there is only one.
249 * <p>This is mainly intended for use in a Java EE 5 environment, with all
250 * lookup driven by the standard JPA annotations, and all EntityManager
251 * references obtained from JNDI. No separate EntityManagerFactory bean
252 * definitions are necessary in such a scenario, and all EntityManager
253 * handling is done by the Java EE 5 server itself.
254 */
255 public void setExtendedPersistenceContexts(Map<String, String> extendedPersistenceContexts) {
256 this.extendedPersistenceContexts = extendedPersistenceContexts;
257 }
258
259 /**
260 * Specify the default persistence unit name, to be used in case
261 * of no unit name specified in an <code>@PersistenceUnit</code> /
262 * <code>@PersistenceContext</code> annotation.
263 * <p>This is mainly intended for lookups in the application context,
264 * indicating the target persistence unit name (typically matching
265 * the bean name), but also applies to lookups in the
266 * {@link #setPersistenceUnits "persistenceUnits"} /
267 * {@link #setPersistenceContexts "persistenceContexts"} /
268 * {@link #setExtendedPersistenceContexts "extendedPersistenceContexts"} map,
269 * avoiding the need for duplicated mappings for the empty String there.
270 * <p>Default is to check for a single EntityManagerFactory bean
271 * in the Spring application context, if any. If there are multiple
272 * such factories, either specify this default persistence unit name
273 * or explicitly refer to named persistence units in your annotations.
274 */
275 public void setDefaultPersistenceUnitName(String unitName) {
276 this.defaultPersistenceUnitName = (unitName != null ? unitName : "");
277 }
278
279 public void setOrder(int order) {
280 this.order = order;
281 }
282
283 public int getOrder() {
284 return this.order;
285 }
286
287 public void setBeanFactory(BeanFactory beanFactory) {
288 if (beanFactory instanceof ListableBeanFactory) {
289 this.beanFactory = (ListableBeanFactory) beanFactory;
290 }
291 }
292
293
294 public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
295 if (beanType != null) {
296 InjectionMetadata metadata = findPersistenceMetadata(beanType);
297 metadata.checkConfigMembers(beanDefinition);
298 }
299 }
300
301 public Object postProcessBeforeInstantiation(Class beanClass, String beanName) throws BeansException {
302 return null;
303 }
304
305 public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
306 InjectionMetadata metadata = findPersistenceMetadata(bean.getClass());
307 try {
308 metadata.injectFields(bean, beanName);
309 }
310 catch (Throwable ex) {
311 throw new BeanCreationException(beanName, "Injection of persistence fields failed", ex);
312 }
313 return true;
314 }
315
316 public PropertyValues postProcessPropertyValues(
317 PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
318
319 InjectionMetadata metadata = findPersistenceMetadata(bean.getClass());
320 try {
321 metadata.injectMethods(bean, beanName, pvs);
322 }
323 catch (Throwable ex) {
324 throw new BeanCreationException(beanName, "Injection of persistence methods failed", ex);
325 }
326 return pvs;
327 }
328
329 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
330 return bean;
331 }
332
333 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
334 return bean;
335 }
336
337 public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
338 EntityManager emToClose = this.extendedEntityManagersToClose.remove(bean);
339 EntityManagerFactoryUtils.closeEntityManager(emToClose);
340 }
341
342
343 private InjectionMetadata findPersistenceMetadata(final Class clazz) {
344 // Quick check on the concurrent map first, with minimal locking.
345 InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
346 if (metadata == null) {
347 synchronized (this.injectionMetadataCache) {
348 metadata = this.injectionMetadataCache.get(clazz);
349 if (metadata == null) {
350 final InjectionMetadata newMetadata = new InjectionMetadata(clazz);
351 ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
352 public void doWith(Field field) {
353 PersistenceContext pc = field.getAnnotation(PersistenceContext.class);
354 PersistenceUnit pu = field.getAnnotation(PersistenceUnit.class);
355 if (pc != null || pu != null) {
356 if (Modifier.isStatic(field.getModifiers())) {
357 throw new IllegalStateException("Persistence annotations are not supported on static fields");
358 }
359 newMetadata.addInjectedField(new PersistenceElement(field, null));
360 }
361 }
362 });
363 ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
364 public void doWith(Method method) {
365 PersistenceContext pc = method.getAnnotation(PersistenceContext.class);
366 PersistenceUnit pu = method.getAnnotation(PersistenceUnit.class);
367 if (pc != null || pu != null &&
368 method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
369 if (Modifier.isStatic(method.getModifiers())) {
370 throw new IllegalStateException("Persistence annotations are not supported on static methods");
371 }
372 if (method.getParameterTypes().length != 1) {
373 throw new IllegalStateException("Persistence annotation requires a single-arg method: " + method);
374 }
375 PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
376 newMetadata.addInjectedMethod(new PersistenceElement(method, pd));
377 }
378 }
379 });
380 metadata = newMetadata;
381 this.injectionMetadataCache.put(clazz, metadata);
382 }
383 }
384 }
385 return metadata;
386 }
387
388 /**
389 * Return a specified persistence unit for the given unit name,
390 * as defined through the "persistenceUnits" map.
391 * @param unitName the name of the persistence unit
392 * @return the corresponding EntityManagerFactory,
393 * or <code>null</code> if none found
394 * @see #setPersistenceUnits
395 */
396 protected EntityManagerFactory getPersistenceUnit(String unitName) {
397 if (this.persistenceUnits != null) {
398 String unitNameForLookup = (unitName != null ? unitName : "");
399 if ("".equals(unitNameForLookup)) {
400 unitNameForLookup = this.defaultPersistenceUnitName;
401 }
402 String jndiName = this.persistenceUnits.get(unitNameForLookup);
403 if (jndiName == null && "".equals(unitNameForLookup) && this.persistenceUnits.size() == 1) {
404 jndiName = this.persistenceUnits.values().iterator().next();
405 }
406 if (jndiName != null) {
407 try {
408 return (EntityManagerFactory) lookup(jndiName, EntityManagerFactory.class);
409 }
410 catch (NamingException ex) {
411 throw new IllegalStateException("Could not obtain EntityManagerFactory [" + jndiName + "] from JNDI", ex);
412 }
413 }
414 }
415 return null;
416 }
417
418 /**
419 * Return a specified persistence context for the given unit name, as defined
420 * through the "persistenceContexts" (or "extendedPersistenceContexts") map.
421 * @param unitName the name of the persistence unit
422 * @param extended whether to obtain an extended persistence context
423 * @return the corresponding EntityManager, or <code>null</code> if none found
424 * @see #setPersistenceContexts
425 * @see #setExtendedPersistenceContexts
426 */
427 protected EntityManager getPersistenceContext(String unitName, boolean extended) {
428 Map<String, String> contexts = (extended ? this.extendedPersistenceContexts : this.persistenceContexts);
429 if (contexts != null) {
430 String unitNameForLookup = (unitName != null ? unitName : "");
431 if ("".equals(unitNameForLookup)) {
432 unitNameForLookup = this.defaultPersistenceUnitName;
433 }
434 String jndiName = contexts.get(unitNameForLookup);
435 if (jndiName == null && "".equals(unitNameForLookup) && contexts.size() == 1) {
436 jndiName = contexts.values().iterator().next();
437 }
438 if (jndiName != null) {
439 try {
440 return (EntityManager) lookup(jndiName, EntityManager.class);
441 }
442 catch (NamingException ex) {
443 throw new IllegalStateException("Could not obtain EntityManager [" + jndiName + "] from JNDI", ex);
444 }
445 }
446 }
447 return null;
448 }
449
450 /**
451 * Find an EntityManagerFactory with the given name in the current Spring
452 * application context, falling back to a single default EntityManagerFactory
453 * (if any) in case of no unit name specified.
454 * @param unitName the name of the persistence unit (may be <code>null</code> or empty)
455 * @param requestingBeanName the name of the requesting bean
456 * @return the EntityManagerFactory
457 * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
458 */
459 protected EntityManagerFactory findEntityManagerFactory(String unitName, String requestingBeanName)
460 throws NoSuchBeanDefinitionException {
461
462 if (this.beanFactory == null) {
463 throw new IllegalStateException("ListableBeanFactory required for EntityManagerFactory bean lookup");
464 }
465 String unitNameForLookup = (unitName != null ? unitName : "");
466 if ("".equals(unitNameForLookup)) {
467 unitNameForLookup = this.defaultPersistenceUnitName;
468 }
469 if (!"".equals(unitNameForLookup)) {
470 return findNamedEntityManagerFactory(unitNameForLookup, requestingBeanName);
471 }
472 else {
473 return findDefaultEntityManagerFactory(requestingBeanName);
474 }
475 }
476
477 /**
478 * Find an EntityManagerFactory with the given name in the current
479 * Spring application context.
480 * @param unitName the name of the persistence unit (never empty)
481 * @param requestingBeanName the name of the requesting bean
482 * @return the EntityManagerFactory
483 * @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
484 */
485 protected EntityManagerFactory findNamedEntityManagerFactory(String unitName, String requestingBeanName)
486 throws NoSuchBeanDefinitionException {
487
488 EntityManagerFactory emf = EntityManagerFactoryUtils.findEntityManagerFactory(this.beanFactory, unitName);
489 if (this.beanFactory instanceof ConfigurableBeanFactory) {
490 ((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName, requestingBeanName);
491 }
492 return emf;
493 }
494
495 /**
496 * Find a single default EntityManagerFactory in the Spring application context.
497 * @return the default EntityManagerFactory
498 * @throws NoSuchBeanDefinitionException if there is no single EntityManagerFactory in the context
499 */
500 protected EntityManagerFactory findDefaultEntityManagerFactory(String requestingBeanName)
501 throws NoSuchBeanDefinitionException{
502
503 String[] beanNames =
504 BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, EntityManagerFactory.class);
505 if (beanNames.length == 1) {
506 String unitName = beanNames[0];
507 EntityManagerFactory emf = (EntityManagerFactory) this.beanFactory.getBean(unitName);
508 if (this.beanFactory instanceof ConfigurableBeanFactory) {
509 ((ConfigurableBeanFactory) this.beanFactory).registerDependentBean(unitName, requestingBeanName);
510 }
511 return emf;
512 }
513 else {
514 throw new NoSuchBeanDefinitionException(
515 EntityManagerFactory.class, "expected single bean but found " + beanNames.length);
516 }
517 }
518
519
520 /**
521 * Class representing injection information about an annotated field
522 * or setter method.
523 */
524 private class PersistenceElement extends InjectionMetadata.InjectedElement {
525
526 private final String unitName;
527
528 private PersistenceContextType type;
529
530 private Properties properties;
531
532 public PersistenceElement(Member member, PropertyDescriptor pd) {
533 super(member, pd);
534 AnnotatedElement ae = (AnnotatedElement) member;
535 PersistenceContext pc = ae.getAnnotation(PersistenceContext.class);
536 PersistenceUnit pu = ae.getAnnotation(PersistenceUnit.class);
537 Class resourceType = EntityManager.class;
538 if (pc != null) {
539 if (pu != null) {
540 throw new IllegalStateException("Member may only be annotated with either " +
541 "@PersistenceContext or @PersistenceUnit, not both: " + member);
542 }
543 Properties properties = null;
544 PersistenceProperty[] pps = pc.properties();
545 if (!ObjectUtils.isEmpty(pps)) {
546 properties = new Properties();
547 for (int i = 0; i < pps.length; i++) {
548 PersistenceProperty pp = pps[i];
549 properties.setProperty(pp.name(), pp.value());
550 }
551 }
552 this.unitName = pc.unitName();
553 this.type = pc.type();
554 this.properties = properties;
555 }
556 else {
557 resourceType = EntityManagerFactory.class;
558 this.unitName = pu.unitName();
559 }
560 checkResourceType(resourceType);
561 }
562
563 /**
564 * Resolve the object against the application context.
565 */
566 @Override
567 protected Object getResourceToInject(Object target, String requestingBeanName) {
568 // Resolves to EntityManagerFactory or EntityManager.
569 if (this.type != null) {
570 return (this.type == PersistenceContextType.EXTENDED ?
571 resolveExtendedEntityManager(target, requestingBeanName) :
572 resolveEntityManager(requestingBeanName));
573 }
574 else {
575 // OK, so we need an EntityManagerFactory...
576 return resolveEntityManagerFactory(requestingBeanName);
577 }
578 }
579
580 private EntityManagerFactory resolveEntityManagerFactory(String requestingBeanName) {
581 // Obtain EntityManagerFactory from JNDI?
582 EntityManagerFactory emf = getPersistenceUnit(this.unitName);
583 if (emf == null) {
584 // Need to search for EntityManagerFactory beans.
585 emf = findEntityManagerFactory(this.unitName, requestingBeanName);
586 }
587 return emf;
588 }
589
590 private EntityManager resolveEntityManager(String requestingBeanName) {
591 // Obtain EntityManager reference from JNDI?
592 EntityManager em = getPersistenceContext(this.unitName, false);
593 if (em == null) {
594 // No pre-built EntityManager found -> build one based on factory.
595 // Obtain EntityManagerFactory from JNDI?
596 EntityManagerFactory emf = getPersistenceUnit(this.unitName);
597 if (emf == null) {
598 // Need to search for EntityManagerFactory beans.
599 emf = findEntityManagerFactory(this.unitName, requestingBeanName);
600 }
601 // Inject a shared transactional EntityManager proxy.
602 if (emf instanceof EntityManagerFactoryInfo &&
603 ((EntityManagerFactoryInfo) emf).getEntityManagerInterface() != null) {
604 // Create EntityManager based on the info's vendor-specific type
605 // (which might be more specific than the field's type).
606 em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties);
607 }
608 else {
609 // Create EntityManager based on the field's type.
610 em = SharedEntityManagerCreator.createSharedEntityManager(emf, this.properties, getResourceType());
611 }
612 }
613 return em;
614 }
615
616 private EntityManager resolveExtendedEntityManager(Object target, String requestingBeanName) {
617 // Obtain EntityManager reference from JNDI?
618 EntityManager em = getPersistenceContext(this.unitName, true);
619 if (em == null) {
620 // No pre-built EntityManager found -> build one based on factory.
621 // Obtain EntityManagerFactory from JNDI?
622 EntityManagerFactory emf = getPersistenceUnit(this.unitName);
623 if (emf == null) {
624 // Need to search for EntityManagerFactory beans.
625 emf = findEntityManagerFactory(this.unitName, requestingBeanName);
626 }
627 // Inject a container-managed extended EntityManager.
628 em = ExtendedEntityManagerCreator.createContainerManagedEntityManager(emf, this.properties);
629 }
630 if (em instanceof EntityManagerProxy &&
631 beanFactory != null && !beanFactory.isPrototype(requestingBeanName)) {
632 extendedEntityManagersToClose.put(target, ((EntityManagerProxy) em).getTargetEntityManager());
633 }
634 return em;
635 }
636 }
637
638 }