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;
018
019import java.io.IOException;
020import java.io.NotSerializableException;
021import java.io.ObjectInputStream;
022import java.io.ObjectStreamException;
023import java.io.Serializable;
024import java.lang.reflect.InvocationHandler;
025import java.lang.reflect.InvocationTargetException;
026import java.lang.reflect.Method;
027import java.lang.reflect.Proxy;
028import java.util.HashMap;
029import java.util.LinkedHashSet;
030import java.util.Map;
031import java.util.Properties;
032import java.util.Set;
033import java.util.concurrent.ExecutionException;
034import java.util.concurrent.Future;
035
036import javax.persistence.EntityManager;
037import javax.persistence.EntityManagerFactory;
038import javax.persistence.PersistenceException;
039import javax.persistence.Query;
040import javax.persistence.SynchronizationType;
041import javax.persistence.spi.PersistenceProvider;
042import javax.persistence.spi.PersistenceUnitInfo;
043import javax.sql.DataSource;
044
045import org.apache.commons.logging.Log;
046import org.apache.commons.logging.LogFactory;
047
048import org.springframework.beans.BeanUtils;
049import org.springframework.beans.factory.BeanClassLoaderAware;
050import org.springframework.beans.factory.BeanFactory;
051import org.springframework.beans.factory.BeanFactoryAware;
052import org.springframework.beans.factory.BeanNameAware;
053import org.springframework.beans.factory.DisposableBean;
054import org.springframework.beans.factory.FactoryBean;
055import org.springframework.beans.factory.InitializingBean;
056import org.springframework.core.task.AsyncTaskExecutor;
057import org.springframework.dao.DataAccessException;
058import org.springframework.dao.support.PersistenceExceptionTranslator;
059import org.springframework.lang.Nullable;
060import org.springframework.util.Assert;
061import org.springframework.util.ClassUtils;
062import org.springframework.util.CollectionUtils;
063
064/**
065 * Abstract {@link org.springframework.beans.factory.FactoryBean} that creates
066 * a local JPA {@link javax.persistence.EntityManagerFactory} instance within
067 * a Spring application context.
068 *
069 * <p>Encapsulates the common functionality between the different JPA bootstrap
070 * contracts (standalone as well as container).
071 *
072 * <p>Implements support for standard JPA configuration conventions as well as
073 * Spring's customizable {@link JpaVendorAdapter} mechanism, and controls the
074 * EntityManagerFactory's lifecycle.
075 *
076 * <p>This class also implements the
077 * {@link org.springframework.dao.support.PersistenceExceptionTranslator}
078 * interface, as autodetected by Spring's
079 * {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor},
080 * for AOP-based translation of native exceptions to Spring DataAccessExceptions.
081 * Hence, the presence of e.g. LocalEntityManagerFactoryBean automatically enables
082 * a PersistenceExceptionTranslationPostProcessor to translate JPA exceptions.
083 *
084 * @author Juergen Hoeller
085 * @author Rod Johnson
086 * @since 2.0
087 * @see LocalEntityManagerFactoryBean
088 * @see LocalContainerEntityManagerFactoryBean
089 */
090@SuppressWarnings("serial")
091public abstract class AbstractEntityManagerFactoryBean implements
092                FactoryBean<EntityManagerFactory>, BeanClassLoaderAware, BeanFactoryAware, BeanNameAware,
093                InitializingBean, DisposableBean, EntityManagerFactoryInfo, PersistenceExceptionTranslator, Serializable {
094
095        /** Logger available to subclasses. */
096        protected final Log logger = LogFactory.getLog(getClass());
097
098        @Nullable
099        private PersistenceProvider persistenceProvider;
100
101        @Nullable
102        private String persistenceUnitName;
103
104        private final Map<String, Object> jpaPropertyMap = new HashMap<>();
105
106        @Nullable
107        private Class<? extends EntityManagerFactory> entityManagerFactoryInterface;
108
109        @Nullable
110        private Class<? extends EntityManager> entityManagerInterface;
111
112        @Nullable
113        private JpaDialect jpaDialect;
114
115        @Nullable
116        private JpaVendorAdapter jpaVendorAdapter;
117
118        @Nullable
119        private AsyncTaskExecutor bootstrapExecutor;
120
121        private ClassLoader beanClassLoader = getClass().getClassLoader();
122
123        @Nullable
124        private BeanFactory beanFactory;
125
126        @Nullable
127        private String beanName;
128
129        /** Raw EntityManagerFactory as returned by the PersistenceProvider. */
130        @Nullable
131        private EntityManagerFactory nativeEntityManagerFactory;
132
133        /** Future for lazily initializing raw target EntityManagerFactory. */
134        @Nullable
135        private Future<EntityManagerFactory> nativeEntityManagerFactoryFuture;
136
137        /** Exposed client-level EntityManagerFactory proxy. */
138        @Nullable
139        private EntityManagerFactory entityManagerFactory;
140
141
142        /**
143         * Set the PersistenceProvider implementation class to use for creating the
144         * EntityManagerFactory. If not specified, the persistence provider will be
145         * taken from the JpaVendorAdapter (if any) or retrieved through scanning
146         * (as far as possible).
147         * @see JpaVendorAdapter#getPersistenceProvider()
148         * @see javax.persistence.spi.PersistenceProvider
149         * @see javax.persistence.Persistence
150         */
151        public void setPersistenceProviderClass(Class<? extends PersistenceProvider> persistenceProviderClass) {
152                this.persistenceProvider = BeanUtils.instantiateClass(persistenceProviderClass);
153        }
154
155        /**
156         * Set the PersistenceProvider instance to use for creating the
157         * EntityManagerFactory. If not specified, the persistence provider
158         * will be taken from the JpaVendorAdapter (if any) or determined
159         * by the persistence unit deployment descriptor (as far as possible).
160         * @see JpaVendorAdapter#getPersistenceProvider()
161         * @see javax.persistence.spi.PersistenceProvider
162         * @see javax.persistence.Persistence
163         */
164        public void setPersistenceProvider(@Nullable PersistenceProvider persistenceProvider) {
165                this.persistenceProvider = persistenceProvider;
166        }
167
168        @Override
169        @Nullable
170        public PersistenceProvider getPersistenceProvider() {
171                return this.persistenceProvider;
172        }
173
174        /**
175         * Specify the name of the EntityManagerFactory configuration.
176         * <p>Default is none, indicating the default EntityManagerFactory
177         * configuration. The persistence provider will throw an exception if
178         * ambiguous EntityManager configurations are found.
179         * @see javax.persistence.Persistence#createEntityManagerFactory(String)
180         */
181        public void setPersistenceUnitName(@Nullable String persistenceUnitName) {
182                this.persistenceUnitName = persistenceUnitName;
183        }
184
185        @Override
186        @Nullable
187        public String getPersistenceUnitName() {
188                return this.persistenceUnitName;
189        }
190
191        /**
192         * Specify JPA properties, to be passed into
193         * {@code Persistence.createEntityManagerFactory} (if any).
194         * <p>Can be populated with a String "value" (parsed via PropertiesEditor) or a
195         * "props" element in XML bean definitions.
196         * @see javax.persistence.Persistence#createEntityManagerFactory(String, Map)
197         * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, Map)
198         */
199        public void setJpaProperties(Properties jpaProperties) {
200                CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap);
201        }
202
203        /**
204         * Specify JPA properties as a Map, to be passed into
205         * {@code Persistence.createEntityManagerFactory} (if any).
206         * <p>Can be populated with a "map" or "props" element in XML bean definitions.
207         * @see javax.persistence.Persistence#createEntityManagerFactory(String, Map)
208         * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, Map)
209         */
210        public void setJpaPropertyMap(@Nullable Map<String, ?> jpaProperties) {
211                if (jpaProperties != null) {
212                        this.jpaPropertyMap.putAll(jpaProperties);
213                }
214        }
215
216        /**
217         * Allow Map access to the JPA properties to be passed to the persistence
218         * provider, with the option to add or override specific entries.
219         * <p>Useful for specifying entries directly, for example via
220         * "jpaPropertyMap[myKey]".
221         */
222        public Map<String, Object> getJpaPropertyMap() {
223                return this.jpaPropertyMap;
224        }
225
226        /**
227         * Specify the (potentially vendor-specific) EntityManagerFactory interface
228         * that this EntityManagerFactory proxy is supposed to implement.
229         * <p>The default will be taken from the specific JpaVendorAdapter, if any,
230         * or set to the standard {@code javax.persistence.EntityManagerFactory}
231         * interface else.
232         * @see JpaVendorAdapter#getEntityManagerFactoryInterface()
233         */
234        public void setEntityManagerFactoryInterface(Class<? extends EntityManagerFactory> emfInterface) {
235                this.entityManagerFactoryInterface = emfInterface;
236        }
237
238        /**
239         * Specify the (potentially vendor-specific) EntityManager interface
240         * that this factory's EntityManagers are supposed to implement.
241         * <p>The default will be taken from the specific JpaVendorAdapter, if any,
242         * or set to the standard {@code javax.persistence.EntityManager}
243         * interface else.
244         * @see JpaVendorAdapter#getEntityManagerInterface()
245         * @see EntityManagerFactoryInfo#getEntityManagerInterface()
246         */
247        public void setEntityManagerInterface(@Nullable Class<? extends EntityManager> emInterface) {
248                this.entityManagerInterface = emInterface;
249        }
250
251        @Override
252        @Nullable
253        public Class<? extends EntityManager> getEntityManagerInterface() {
254                return this.entityManagerInterface;
255        }
256
257        /**
258         * Specify the vendor-specific JpaDialect implementation to associate with
259         * this EntityManagerFactory. This will be exposed through the
260         * EntityManagerFactoryInfo interface, to be picked up as default dialect by
261         * accessors that intend to use JpaDialect functionality.
262         * @see EntityManagerFactoryInfo#getJpaDialect()
263         */
264        public void setJpaDialect(@Nullable JpaDialect jpaDialect) {
265                this.jpaDialect = jpaDialect;
266        }
267
268        @Override
269        @Nullable
270        public JpaDialect getJpaDialect() {
271                return this.jpaDialect;
272        }
273
274        /**
275         * Specify the JpaVendorAdapter implementation for the desired JPA provider,
276         * if any. This will initialize appropriate defaults for the given provider,
277         * such as persistence provider class and JpaDialect, unless locally
278         * overridden in this FactoryBean.
279         */
280        public void setJpaVendorAdapter(@Nullable JpaVendorAdapter jpaVendorAdapter) {
281                this.jpaVendorAdapter = jpaVendorAdapter;
282        }
283
284        /**
285         * Return the JpaVendorAdapter implementation for this EntityManagerFactory,
286         * or {@code null} if not known.
287         */
288        @Nullable
289        public JpaVendorAdapter getJpaVendorAdapter() {
290                return this.jpaVendorAdapter;
291        }
292
293        /**
294         * Specify an asynchronous executor for background bootstrapping,
295         * e.g. a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}.
296         * <p>{@code EntityManagerFactory} initialization will then switch into background
297         * bootstrap mode, with a {@code EntityManagerFactory} proxy immediately returned for
298         * injection purposes instead of waiting for the JPA provider's bootstrapping to complete.
299         * However, note that the first actual call to a {@code EntityManagerFactory} method will
300         * then block until the JPA provider's bootstrapping completed, if not ready by then.
301         * For maximum benefit, make sure to avoid early {@code EntityManagerFactory} calls
302         * in init methods of related beans, even for metadata introspection purposes.
303         * @since 4.3
304         */
305        public void setBootstrapExecutor(@Nullable AsyncTaskExecutor bootstrapExecutor) {
306                this.bootstrapExecutor = bootstrapExecutor;
307        }
308
309        /**
310         * Return the asynchronous executor for background bootstrapping, if any.
311         * @since 4.3
312         */
313        @Nullable
314        public AsyncTaskExecutor getBootstrapExecutor() {
315                return this.bootstrapExecutor;
316        }
317
318        @Override
319        public void setBeanClassLoader(ClassLoader classLoader) {
320                this.beanClassLoader = classLoader;
321        }
322
323        @Override
324        public ClassLoader getBeanClassLoader() {
325                return this.beanClassLoader;
326        }
327
328        @Override
329        public void setBeanFactory(BeanFactory beanFactory) {
330                this.beanFactory = beanFactory;
331        }
332
333        @Override
334        public void setBeanName(String name) {
335                this.beanName = name;
336        }
337
338
339        @Override
340        public void afterPropertiesSet() throws PersistenceException {
341                JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
342                if (jpaVendorAdapter != null) {
343                        if (this.persistenceProvider == null) {
344                                this.persistenceProvider = jpaVendorAdapter.getPersistenceProvider();
345                        }
346                        PersistenceUnitInfo pui = getPersistenceUnitInfo();
347                        Map<String, ?> vendorPropertyMap = (pui != null ? jpaVendorAdapter.getJpaPropertyMap(pui) :
348                                        jpaVendorAdapter.getJpaPropertyMap());
349                        if (!CollectionUtils.isEmpty(vendorPropertyMap)) {
350                                vendorPropertyMap.forEach((key, value) -> {
351                                        if (!this.jpaPropertyMap.containsKey(key)) {
352                                                this.jpaPropertyMap.put(key, value);
353                                        }
354                                });
355                        }
356                        if (this.entityManagerFactoryInterface == null) {
357                                this.entityManagerFactoryInterface = jpaVendorAdapter.getEntityManagerFactoryInterface();
358                                if (!ClassUtils.isVisible(this.entityManagerFactoryInterface, this.beanClassLoader)) {
359                                        this.entityManagerFactoryInterface = EntityManagerFactory.class;
360                                }
361                        }
362                        if (this.entityManagerInterface == null) {
363                                this.entityManagerInterface = jpaVendorAdapter.getEntityManagerInterface();
364                                if (!ClassUtils.isVisible(this.entityManagerInterface, this.beanClassLoader)) {
365                                        this.entityManagerInterface = EntityManager.class;
366                                }
367                        }
368                        if (this.jpaDialect == null) {
369                                this.jpaDialect = jpaVendorAdapter.getJpaDialect();
370                        }
371                }
372
373                AsyncTaskExecutor bootstrapExecutor = getBootstrapExecutor();
374                if (bootstrapExecutor != null) {
375                        this.nativeEntityManagerFactoryFuture = bootstrapExecutor.submit(this::buildNativeEntityManagerFactory);
376                }
377                else {
378                        this.nativeEntityManagerFactory = buildNativeEntityManagerFactory();
379                }
380
381                // Wrap the EntityManagerFactory in a factory implementing all its interfaces.
382                // This allows interception of createEntityManager methods to return an
383                // application-managed EntityManager proxy that automatically joins
384                // existing transactions.
385                this.entityManagerFactory = createEntityManagerFactoryProxy(this.nativeEntityManagerFactory);
386        }
387
388        private EntityManagerFactory buildNativeEntityManagerFactory() {
389                EntityManagerFactory emf;
390                try {
391                        emf = createNativeEntityManagerFactory();
392                }
393                catch (PersistenceException ex) {
394                        if (ex.getClass() == PersistenceException.class) {
395                                // Plain PersistenceException wrapper for underlying exception?
396                                // Make sure the nested exception message is properly exposed,
397                                // along the lines of Spring's NestedRuntimeException.getMessage()
398                                Throwable cause = ex.getCause();
399                                if (cause != null) {
400                                        String message = ex.getMessage();
401                                        String causeString = cause.toString();
402                                        if (!message.endsWith(causeString)) {
403                                                ex = new PersistenceException(message + "; nested exception is " + causeString, cause);
404                                        }
405                                }
406                        }
407                        if (logger.isErrorEnabled()) {
408                                logger.error("Failed to initialize JPA EntityManagerFactory: " + ex.getMessage());
409                        }
410                        throw ex;
411                }
412
413                JpaVendorAdapter jpaVendorAdapter = getJpaVendorAdapter();
414                if (jpaVendorAdapter != null) {
415                        jpaVendorAdapter.postProcessEntityManagerFactory(emf);
416                }
417
418                if (logger.isInfoEnabled()) {
419                        logger.info("Initialized JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'");
420                }
421                return emf;
422        }
423
424        /**
425         * Create a proxy for the given {@link EntityManagerFactory}. We do this to be able to
426         * return a transaction-aware proxy for an application-managed {@link EntityManager}.
427         * @param emf the EntityManagerFactory as returned by the persistence provider,
428         * if initialized already
429         * @return the EntityManagerFactory proxy
430         */
431        protected EntityManagerFactory createEntityManagerFactoryProxy(@Nullable EntityManagerFactory emf) {
432                Set<Class<?>> ifcs = new LinkedHashSet<>();
433                Class<?> entityManagerFactoryInterface = this.entityManagerFactoryInterface;
434                if (entityManagerFactoryInterface != null) {
435                        ifcs.add(entityManagerFactoryInterface);
436                }
437                else if (emf != null) {
438                        ifcs.addAll(ClassUtils.getAllInterfacesForClassAsSet(emf.getClass(), this.beanClassLoader));
439                }
440                else {
441                        ifcs.add(EntityManagerFactory.class);
442                }
443                ifcs.add(EntityManagerFactoryInfo.class);
444
445                try {
446                        return (EntityManagerFactory) Proxy.newProxyInstance(this.beanClassLoader,
447                                        ClassUtils.toClassArray(ifcs), new ManagedEntityManagerFactoryInvocationHandler(this));
448                }
449                catch (IllegalArgumentException ex) {
450                        if (entityManagerFactoryInterface != null) {
451                                throw new IllegalStateException("EntityManagerFactory interface [" + entityManagerFactoryInterface +
452                                                "] seems to conflict with Spring's EntityManagerFactoryInfo mixin - consider resetting the "+
453                                                "'entityManagerFactoryInterface' property to plain [javax.persistence.EntityManagerFactory]", ex);
454                        }
455                        else {
456                                throw new IllegalStateException("Conflicting EntityManagerFactory interfaces - " +
457                                                "consider specifying the 'jpaVendorAdapter' or 'entityManagerFactoryInterface' property " +
458                                                "to select a specific EntityManagerFactory interface to proceed with", ex);
459                        }
460                }
461        }
462
463        /**
464         * Delegate an incoming invocation from the proxy, dispatching to EntityManagerFactoryInfo
465         * or the native EntityManagerFactory accordingly.
466         */
467        Object invokeProxyMethod(Method method, @Nullable Object[] args) throws Throwable {
468                if (method.getDeclaringClass().isAssignableFrom(EntityManagerFactoryInfo.class)) {
469                        return method.invoke(this, args);
470                }
471                else if (method.getName().equals("createEntityManager") && args != null && args.length > 0 &&
472                                args[0] == SynchronizationType.SYNCHRONIZED) {
473                        // JPA 2.1's createEntityManager(SynchronizationType, Map)
474                        // Redirect to plain createEntityManager and add synchronization semantics through Spring proxy
475                        EntityManager rawEntityManager = (args.length > 1 ?
476                                        getNativeEntityManagerFactory().createEntityManager((Map<?, ?>) args[1]) :
477                                        getNativeEntityManagerFactory().createEntityManager());
478                        return ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, true);
479                }
480
481                // Look for Query arguments, primarily JPA 2.1's addNamedQuery(String, Query)
482                if (args != null) {
483                        for (int i = 0; i < args.length; i++) {
484                                Object arg = args[i];
485                                if (arg instanceof Query && Proxy.isProxyClass(arg.getClass())) {
486                                        // Assumably a Spring-generated proxy from SharedEntityManagerCreator:
487                                        // since we're passing it back to the native EntityManagerFactory,
488                                        // let's unwrap it to the original Query object from the provider.
489                                        try {
490                                                args[i] = ((Query) arg).unwrap(null);
491                                        }
492                                        catch (RuntimeException ex) {
493                                                // Ignore - simply proceed with given Query object then
494                                        }
495                                }
496                        }
497                }
498
499                // Standard delegation to the native factory, just post-processing EntityManager return values
500                Object retVal = method.invoke(getNativeEntityManagerFactory(), args);
501                if (retVal instanceof EntityManager) {
502                        // Any other createEntityManager variant - expecting non-synchronized semantics
503                        EntityManager rawEntityManager = (EntityManager) retVal;
504                        retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this, false);
505                }
506                return retVal;
507        }
508
509        /**
510         * Subclasses must implement this method to create the EntityManagerFactory
511         * that will be returned by the {@code getObject()} method.
512         * @return the EntityManagerFactory instance returned by this FactoryBean
513         * @throws PersistenceException if the EntityManager cannot be created
514         */
515        protected abstract EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException;
516
517
518        /**
519         * Implementation of the PersistenceExceptionTranslator interface, as
520         * autodetected by Spring's PersistenceExceptionTranslationPostProcessor.
521         * <p>Uses the dialect's conversion if possible; otherwise falls back to
522         * standard JPA exception conversion.
523         * @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
524         * @see JpaDialect#translateExceptionIfPossible
525         * @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible
526         */
527        @Override
528        @Nullable
529        public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
530                JpaDialect jpaDialect = getJpaDialect();
531                return (jpaDialect != null ? jpaDialect.translateExceptionIfPossible(ex) :
532                                EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex));
533        }
534
535        @Override
536        public EntityManagerFactory getNativeEntityManagerFactory() {
537                if (this.nativeEntityManagerFactory != null) {
538                        return this.nativeEntityManagerFactory;
539                }
540                else {
541                        Assert.state(this.nativeEntityManagerFactoryFuture != null, "No native EntityManagerFactory available");
542                        try {
543                                return this.nativeEntityManagerFactoryFuture.get();
544                        }
545                        catch (InterruptedException ex) {
546                                Thread.currentThread().interrupt();
547                                throw new IllegalStateException("Interrupted during initialization of native EntityManagerFactory", ex);
548                        }
549                        catch (ExecutionException ex) {
550                                Throwable cause = ex.getCause();
551                                if (cause instanceof PersistenceException) {
552                                        // Rethrow a provider configuration exception (possibly with a nested cause) directly
553                                        throw (PersistenceException) cause;
554                                }
555                                throw new IllegalStateException("Failed to asynchronously initialize native EntityManagerFactory: " +
556                                                ex.getMessage(), cause);
557                        }
558                }
559        }
560
561        @Override
562        @Nullable
563        public PersistenceUnitInfo getPersistenceUnitInfo() {
564                return null;
565        }
566
567        @Override
568        @Nullable
569        public DataSource getDataSource() {
570                return null;
571        }
572
573
574        /**
575         * Return the singleton EntityManagerFactory.
576         */
577        @Override
578        @Nullable
579        public EntityManagerFactory getObject() {
580                return this.entityManagerFactory;
581        }
582
583        @Override
584        public Class<? extends EntityManagerFactory> getObjectType() {
585                return (this.entityManagerFactory != null ? this.entityManagerFactory.getClass() : EntityManagerFactory.class);
586        }
587
588        @Override
589        public boolean isSingleton() {
590                return true;
591        }
592
593
594        /**
595         * Close the EntityManagerFactory on bean factory shutdown.
596         */
597        @Override
598        public void destroy() {
599                if (this.entityManagerFactory != null) {
600                        if (logger.isInfoEnabled()) {
601                                logger.info("Closing JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'");
602                        }
603                        this.entityManagerFactory.close();
604                }
605        }
606
607
608        //---------------------------------------------------------------------
609        // Serialization support
610        //---------------------------------------------------------------------
611
612        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
613                throw new NotSerializableException("An EntityManagerFactoryBean itself is not deserializable - " +
614                                "just a SerializedEntityManagerFactoryBeanReference is");
615        }
616
617        protected Object writeReplace() throws ObjectStreamException {
618                if (this.beanFactory != null && this.beanName != null) {
619                        return new SerializedEntityManagerFactoryBeanReference(this.beanFactory, this.beanName);
620                }
621                else {
622                        throw new NotSerializableException("EntityManagerFactoryBean does not run within a BeanFactory");
623                }
624        }
625
626
627        /**
628         * Minimal bean reference to the surrounding AbstractEntityManagerFactoryBean.
629         * Resolved to the actual AbstractEntityManagerFactoryBean instance on deserialization.
630         */
631        @SuppressWarnings("serial")
632        private static class SerializedEntityManagerFactoryBeanReference implements Serializable {
633
634                private final BeanFactory beanFactory;
635
636                private final String lookupName;
637
638                public SerializedEntityManagerFactoryBeanReference(BeanFactory beanFactory, String beanName) {
639                        this.beanFactory = beanFactory;
640                        this.lookupName = BeanFactory.FACTORY_BEAN_PREFIX + beanName;
641                }
642
643                private Object readResolve() {
644                        return this.beanFactory.getBean(this.lookupName, AbstractEntityManagerFactoryBean.class);
645                }
646        }
647
648
649        /**
650         * Dynamic proxy invocation handler for an {@link EntityManagerFactory}, returning a
651         * proxy {@link EntityManager} (if necessary) from {@code createEntityManager} methods.
652         */
653        @SuppressWarnings("serial")
654        private static class ManagedEntityManagerFactoryInvocationHandler implements InvocationHandler, Serializable {
655
656                private final AbstractEntityManagerFactoryBean entityManagerFactoryBean;
657
658                public ManagedEntityManagerFactoryInvocationHandler(AbstractEntityManagerFactoryBean emfb) {
659                        this.entityManagerFactoryBean = emfb;
660                }
661
662                @Override
663                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
664                        try {
665                                if (method.getName().equals("equals")) {
666                                        // Only consider equal when proxies are identical.
667                                        return (proxy == args[0]);
668                                }
669                                else if (method.getName().equals("hashCode")) {
670                                        // Use hashCode of EntityManagerFactory proxy.
671                                        return System.identityHashCode(proxy);
672                                }
673                                else if (method.getName().equals("unwrap")) {
674                                        // Handle JPA 2.1 unwrap method - could be a proxy match.
675                                        Class<?> targetClass = (Class<?>) args[0];
676                                        if (targetClass == null) {
677                                                return this.entityManagerFactoryBean.getNativeEntityManagerFactory();
678                                        }
679                                        else if (targetClass.isInstance(proxy)) {
680                                                return proxy;
681                                        }
682                                }
683                                return this.entityManagerFactoryBean.invokeProxyMethod(method, args);
684                        }
685                        catch (InvocationTargetException ex) {
686                                throw ex.getTargetException();
687                        }
688                }
689        }
690
691}