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