View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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  package org.kuali.rice.krad.data.jpa;
17  
18  import org.kuali.rice.core.api.config.property.ConfigContext;
19  import org.kuali.rice.krad.data.jpa.converters.BooleanYNConverter;
20  import org.springframework.beans.factory.BeanClassLoaderAware;
21  import org.springframework.beans.factory.BeanFactory;
22  import org.springframework.beans.factory.BeanFactoryAware;
23  import org.springframework.beans.factory.BeanNameAware;
24  import org.springframework.beans.factory.DisposableBean;
25  import org.springframework.beans.factory.FactoryBean;
26  import org.springframework.beans.factory.InitializingBean;
27  import org.springframework.context.ResourceLoaderAware;
28  import org.springframework.context.weaving.LoadTimeWeaverAware;
29  import org.springframework.core.io.Resource;
30  import org.springframework.core.io.ResourceLoader;
31  import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
32  import org.springframework.core.io.support.ResourcePatternResolver;
33  import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
34  import org.springframework.core.type.classreading.MetadataReader;
35  import org.springframework.core.type.classreading.MetadataReaderFactory;
36  import org.springframework.core.type.filter.AnnotationTypeFilter;
37  import org.springframework.core.type.filter.TypeFilter;
38  import org.springframework.dao.DataAccessException;
39  import org.springframework.dao.support.PersistenceExceptionTranslator;
40  import org.springframework.instrument.classloading.LoadTimeWeaver;
41  import org.springframework.orm.jpa.EntityManagerFactoryInfo;
42  import org.springframework.orm.jpa.JpaDialect;
43  import org.springframework.orm.jpa.JpaVendorAdapter;
44  import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
45  import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
46  import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
47  import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
48  import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
49  import org.springframework.util.ClassUtils;
50  
51  import javax.persistence.Converter;
52  import javax.persistence.EntityManager;
53  import javax.persistence.EntityManagerFactory;
54  import javax.persistence.PersistenceException;
55  import javax.persistence.spi.PersistenceProvider;
56  import javax.persistence.spi.PersistenceUnitInfo;
57  import javax.sql.DataSource;
58  import java.io.IOException;
59  import java.util.ArrayList;
60  import java.util.Arrays;
61  import java.util.HashMap;
62  import java.util.List;
63  import java.util.Map;
64  import java.util.Properties;
65  
66  /**
67   * A KRAD-managed {@link javax.persistence.EntityManagerFactory} factory bean which can be used to configure a JPA
68   * persistence unit using the Spring Framework.
69   *
70   * <p>
71   * This implementation does not support the use of a custom PersistenceUnitManager, but rather stores and manages one
72   * internally. This is intended to be an alternative to direct usage of Spring's
73   * {@link LocalContainerEntityManagerFactoryBean} in order to make JPA configuration with KRAD simpler.
74   * </p>
75   *
76   * <h1>Minimal Configuration</h1>
77   *
78   * <p>Minimal configuration of this factory bean will include the following:</p>
79   *
80   * <ul>
81   *     <li>{@code persistenceUnitName}</li>
82   *     <li>{@code dataSource} or {@code jtaDataSource}</li>
83   *     <li>{@code persistenceProvider} or {@code jpaVendorAdapter}</li>
84   * </ul>
85   *
86   * <p>
87   * Note that persistence unit names must be unique, so choose a name which is unlikely to clash with any potential
88   * persistence units configured within the runtime environment.
89   * </p>
90   *
91   * <h1>Behavior</h1>
92   *
93   * <p>
94   * When leveraging this class, persistence.xml files are not used. Rather, persistence unit configuration is loaded
95   * via the various settings provided on this factory bean which contain everything needed to create the desired
96   * persistence unit configuration in the majority of cases. If a KRAD application needs more control over the
97   * configuration of the persistence unit or wants to use persistence.xml and JPA's default bootstrapping and classpath
98   * scanning behavior, an EntityManagerFactory can be configured using the other classes provided by the Spring
99   * framework or by other means as needed. See {@link LocalContainerEntityManagerFactoryBean} for an alternative
100  * approach.
101  * </p>
102  *
103  * <p>
104  * Only one of JTA or non-JTA datasource can be set. Depending on which one is set, the underlying persistence unit
105  * will have it's {@link javax.persistence.spi.PersistenceUnitTransactionType} set to either RESOURCE_LOCAL or JTA. If
106  * both of these are set, then this class will throw an {@code IllegalStateException} when the
107  * {@link #afterPropertiesSet()} method is invoked by the Spring Framework.
108  * </p>
109  *
110  * <p>
111  * Elsewhere, this class delegates to implementations of {@link LocalContainerEntityManagerFactoryBean} and
112  * {@link DefaultPersistenceUnitManager}, so information on the specific behavior of some of the settings and methods on
113  * this class can be found on the javadoc for those classes as well.
114  * </p>
115  *
116  * <h1>JPA Property Defaults</h1>
117  *
118  * <p>
119  * When {@link #afterPropertiesSet()} is invoked, this class will scan the current {@link ConfigContext} for JPA
120  * properties and make them available to the persistence unit. It will combine these with any properties that were set
121  * directly on this factory bean via the {@link #setJpaProperties(java.util.Properties)} or
122  * {@link #setJpaPropertyMap(java.util.Map)} methods.
123  * </p
124  *
125  * <p>
126  * This scanning occurs in the following order, items later in the list will override any properties from earlier if
127  * they happen to set the same effective property value:
128  * </p>
129  *
130  * <ol>
131  *   <li>Scan ConfigContext for properties that begin with "rice.krad.jpa.global." For any found, strip off this prefix
132  *       prior to placing it into the JPA property map.</li>
133  *   <li>Scan ConfigContext for properties that being with "rice.krad.jpa.&lt;persistence-unit-name&gt;" where
134  *       "persistence-unit-name" is the configured name of this persistence unit. For any found, strip off this prefix
135  *       prior to placing it into the JPA property map.</li>
136  *   <li>Invoke {@link #loadCustomJpaDefaults(java.util.Map)} to allow for possible subclass customization of JPA
137  *       property defaults</li>
138  *   <li>Load the JPA properties configured via {@link #setJpaPropertyMap(java.util.Map)} and
139  *   {@link #setJpaProperties(java.util.Properties)}. It is potentially non-deterministic which of these setters will
140  *   take precedence over the other, so it is recommended to only invoke one of them on a given instance of this
141  *   factory bean.</li>
142  * </ol>
143  *
144  * <h1>Subclassing</h1>
145  *
146  * <p>
147  * This class can be subclassed to provide additional specialized implementations of this factory bean. A potential use
148  * for this might be to provide a factory bean that defaults certain values as part of it's default setup.
149  * </p>
150  *
151  * @author Kuali Rice Team (rice.collab@kuali.org)
152  */
153 public class KradEntityManagerFactoryBean implements FactoryBean<EntityManagerFactory>, BeanClassLoaderAware,
154         BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean, EntityManagerFactoryInfo,
155         PersistenceExceptionTranslator, ResourceLoaderAware, LoadTimeWeaverAware {
156 
157 	private static final org.apache.log4j.Logger LOG = org.apache.log4j.Logger
158 			.getLogger(KradEntityManagerFactoryBean.class);
159 
160     /**
161      * Prefix for property names that are passed to the JPA persistence context as JPA properties.
162      *
163      * <p>
164      * To use this, concatenate this prefix with the persistence context name. The total prefix (including the
165      * persistence context) will then be stripped before being passed to the JPA entity manager factory when using
166      * the {@link org.kuali.rice.krad.data.jpa.KradEntityManagerFactoryBean}.
167      * </p>
168      *
169      * @see org.kuali.rice.krad.data.jpa.KradEntityManagerFactoryBean
170      */
171     public static final String JPA_PROPERTY_PREFIX = "rice.krad.jpa.";
172 
173     /**
174      * Prefix for property names that are passed to *all* JPA persistence contexts as JPA properties.
175      *
176      * <p>
177      * The total prefix will then be stripped before being passed to all JPA entity manager factories that use the
178      * {@link org.kuali.rice.krad.data.jpa.KradEntityManagerFactoryBean}.
179      * </p>
180      *
181      * @see org.kuali.rice.krad.data.jpa.KradEntityManagerFactoryBean
182      */
183     public static final String GLOBAL_JPA_PROPERTY_PREFIX = JPA_PROPERTY_PREFIX + "global.";
184 
185     private static final boolean DEFAULT_EXCLUDE_UNLISTED_CLASSES = true;
186     private static final String DEFAULT_CONVERTERS_PACKAGE = BooleanYNConverter.class.getPackage().getName();
187 
188     private final DefaultPersistenceUnitManager persistenceUnitManager;
189     private final LocalContainerEntityManagerFactoryBean internalFactoryBean;
190 
191     private List<PersistenceUnitPostProcessor> persistenceUnitPostProcessors;
192     private List<String> managedClassNames;
193 	private List<String> converterPackageNames;
194 
195     /**
196      * Creates a default KRAD-managed factory bean.
197      */
198     public KradEntityManagerFactoryBean() {
199         this.persistenceUnitPostProcessors = new ArrayList<PersistenceUnitPostProcessor>();
200         this.managedClassNames = new ArrayList<String>();
201 		this.converterPackageNames = new ArrayList<String>();
202 		converterPackageNames.add(DEFAULT_CONVERTERS_PACKAGE); // set as default
203         this.persistenceUnitManager = createPersistenceUnitManager();
204         this.internalFactoryBean = createInternalFactoryBean(this.persistenceUnitManager);
205         this.internalFactoryBean.setJpaPropertyMap(createDefaultJpaProperties());
206     }
207 
208     /**
209      * Retrieve a reference to the internal {@link LocalContainerEntityManagerFactoryBean} which is used by this factory
210      * bean.
211      *
212      * <p>Primarily intended to allow subclasses to access this internal factory bean when needed.</p>
213      *
214      * @return the internal {@link LocalContainerEntityManagerFactoryBean} managed by this bean
215      */
216     protected LocalContainerEntityManagerFactoryBean getInternalFactoryBean() {
217         return this.internalFactoryBean;
218     }
219 
220     /**
221      * Creates a persistence unit manager.
222      *
223      * @return a persistence unit manager.
224      */
225     protected DefaultPersistenceUnitManager createPersistenceUnitManager() {
226         DefaultPersistenceUnitManager pum = new DefaultPersistenceUnitManager();
227         // IMPORTANT! - setting these to empty String arrays, this triggers the DefaultPersistenceUnitManager to
228         // behave appropriately and ignore persistence.xml files from META-INF/persistence.xml as well as allowing for
229         // an empty/minimal persistence unit to be created.
230         //
231         // Note that while Intellij complains about "Redundant array creation for calling varargs method", we really do
232         // need to pass an empty array here in order for this code to work properly.
233         pum.setPersistenceXmlLocations(new String[0]);
234         pum.setMappingResources(new String[0]);
235         pum.setPackagesToScan(new String[0]);
236         return pum;
237     }
238 
239     /**
240      * Creates a JPA-specific entity manager factory bean.
241      *
242      * @param manager the persistence unit manager to use.
243      * @return a JPA-specific entity manager factory bean.
244      */
245     protected LocalContainerEntityManagerFactoryBean createInternalFactoryBean(PersistenceUnitManager manager) {
246         LocalContainerEntityManagerFactoryBean delegate = new LocalContainerEntityManagerFactoryBean();
247         delegate.setPersistenceUnitManager(manager);
248         return delegate;
249     }
250 
251     /**
252      * {@inheritDoc}
253      */
254     @Override
255     public void afterPropertiesSet() throws PersistenceException {
256         if (persistenceUnitManager.getDefaultJtaDataSource() != null &&
257                 persistenceUnitManager.getDefaultDataSource() != null) {
258 			throw new IllegalStateException(getPersistenceUnitName() + ": " + getClass().getSimpleName()
259 					+ " was configured with both a JTA and Non-JTA "
260                 + " datasource. Must configure one or the other, but not both.");
261         }
262 
263         this.internalFactoryBean.setJpaPropertyMap(defaultAndMergeJpaProperties());
264         persistenceUnitManager.setPersistenceUnitPostProcessors(assemblePersistenceUnitPostProcessors());
265         persistenceUnitManager.afterPropertiesSet();
266         internalFactoryBean.afterPropertiesSet();
267     }
268 
269     /**
270      * Creates default JPA properties.
271      *
272      * @return a map of default JPA properties.
273      */
274     private Map<String, ?> createDefaultJpaProperties() {
275         Map<String, String> jpaProperties = new HashMap<String, String>();
276         loadGlobalJpaDefaults(jpaProperties);
277         loadPersistenceUnitJpaDefaults(jpaProperties);
278         loadCustomJpaDefaults(jpaProperties);
279 
280         return jpaProperties;
281     }
282 
283     /**
284      * Gets the default JPA properties and merges them with the configured JPA properties.
285      *
286      * @return a map of merged JPA properties.
287      */
288     private Map<String, ?> defaultAndMergeJpaProperties() {
289         Map<String, Object> jpaProperties = new HashMap<String, Object>(createDefaultJpaProperties());
290         Map<String, Object> configuredJpaPropertyMap = this.internalFactoryBean.getJpaPropertyMap();
291         jpaProperties.putAll(configuredJpaPropertyMap);
292 		if (LOG.isDebugEnabled()) {
293 			LOG.debug(getPersistenceUnitName() + ": JPA Properties Set:\n" + jpaProperties);
294 		}
295 
296         return jpaProperties;
297     }
298 
299     /**
300      * Allows for loading of custom JPA defaults by subclasses, default implementation does nothing so subclasses need
301      * not call super.loadCustomJpaDefaults.
302      *
303      * <p>
304      * Subclasses are free to override this method as they see fit. This method is executed after other defaults are
305      * loaded. A reference to the current Map of JPA properties is passed. Subclasses should take care if removing or
306      * overwriting any of the values which already exist in the given Map.
307      * </p>
308      *
309      * @param jpaProperties the current Map of JPA property defaults.
310      */
311     protected void loadCustomJpaDefaults(Map<String, String> jpaProperties) {
312         // subclass can override
313     }
314 
315     /**
316      * Loads the global JPA defaults from the config.
317      *
318      * @param jpaProperties the current Map of JPA property defaults.
319      */
320     protected void loadGlobalJpaDefaults(Map<String, String> jpaProperties) {
321         jpaProperties.putAll(ConfigContext.getCurrentContextConfig().getPropertiesWithPrefix(GLOBAL_JPA_PROPERTY_PREFIX,
322                 true));
323     }
324 
325     /**
326      * Loads the persistence unit JPA defaults from the config.
327      *
328      * @param jpaProperties the current Map of JPA property defaults.
329      */
330     protected void loadPersistenceUnitJpaDefaults(Map<String, String> jpaProperties) {
331         jpaProperties.putAll(ConfigContext.getCurrentContextConfig().getPropertiesWithPrefix(
332                 constructPersistenceUnitJpaPropertyPrefix(), true));
333     }
334 
335     /**
336      * Builds a persistence unit JPA property prefix.
337      * @return a persistence unit JPA property prefix.
338      */
339     protected String constructPersistenceUnitJpaPropertyPrefix() {
340         return JPA_PROPERTY_PREFIX + getPersistenceUnitName() + ".";
341     }
342 
343     /**
344      * Assembles the {@link PersistenceUnitPostProcessor}s into an array.
345      *
346      * @return an array of the {@link PersistenceUnitPostProcessor}s.
347      */
348     protected PersistenceUnitPostProcessor[] assemblePersistenceUnitPostProcessors() {
349         this.persistenceUnitPostProcessors = new ArrayList<PersistenceUnitPostProcessor>(this.persistenceUnitPostProcessors);
350         this.persistenceUnitPostProcessors.add(new InternalPersistenceUnitPostProcessor());
351         return this.persistenceUnitPostProcessors.toArray(new PersistenceUnitPostProcessor[this.persistenceUnitPostProcessors.size()]);
352     }
353 
354     /**
355      * Returns the list of all managed class names which have been configured on this factory bean.
356      *
357      * <p>This list is modifiable, so the returned list may be modified directly if desired.</p>
358      *
359      * @return list of all managed class names, may be an empty list but will never return null
360      */
361     public List<String> getManagedClassNames() {
362         return managedClassNames;
363     }
364 
365     /**
366      * Returns an array of the {@link PersistenceUnitPostProcessor} instances which have been configured on this
367      * factory bean.
368      *
369      * @return array of post processors, may be empty but will never return null
370      */
371     public PersistenceUnitPostProcessor[] getPersistenceUnitPostProcessors() {
372         return persistenceUnitPostProcessors.toArray(new PersistenceUnitPostProcessor[persistenceUnitPostProcessors.size()]);
373     }
374 
375     /**
376      * Returns a reference to the internal {@link DefaultPersistenceUnitManager} which is used by this factory bean.
377      *
378      * @return the internal persistence unit manager, will never return null
379      */
380     protected DefaultPersistenceUnitManager getPersistenceUnitManager() {
381         return persistenceUnitManager;
382     }
383 
384     /**
385      * {@inheritDoc}
386      */
387     @Override
388     public void destroy() {
389         internalFactoryBean.destroy();
390     }
391 
392     /**
393      * {@inheritDoc}
394      */
395     @Override
396     public Class<? extends EntityManagerFactory> getObjectType() {
397         return internalFactoryBean.getObjectType();
398     }
399 
400     /**
401      * {@inheritDoc}
402      */
403     @Override
404     public boolean isSingleton() {
405         return internalFactoryBean.isSingleton();
406     }
407 
408     /**
409      * {@inheritDoc}
410      */
411     @Override
412     public EntityManagerFactory getObject() {
413         return internalFactoryBean.getObject();
414     }
415 
416     /**
417      * {@inheritDoc}
418      */
419     @Override
420     public EntityManagerFactory getNativeEntityManagerFactory() {
421         return internalFactoryBean.getNativeEntityManagerFactory();
422     }
423 
424     /**
425      * {@inheritDoc}
426      */
427     @Override
428     public void setBeanName(String name) {
429         internalFactoryBean.setBeanName(name);
430     }
431 
432     /**
433      * {@inheritDoc}
434      */
435     @Override
436     public void setBeanFactory(BeanFactory beanFactory) {
437         internalFactoryBean.setBeanFactory(beanFactory);
438     }
439 
440     /**
441      * {@inheritDoc}
442      */
443     @Override
444     public ClassLoader getBeanClassLoader() {
445         return internalFactoryBean.getBeanClassLoader();
446     }
447 
448     /**
449      * {@inheritDoc}
450      */
451     @Override
452     public void setBeanClassLoader(ClassLoader classLoader) {
453         internalFactoryBean.setBeanClassLoader(classLoader);
454     }
455 
456     /**
457      * {@inheritDoc}
458      */
459     @Override
460     public Class<? extends EntityManager> getEntityManagerInterface() {
461         return internalFactoryBean.getEntityManagerInterface();
462     }
463 
464     /**
465      * {@inheritDoc}
466      */
467     @Override
468     public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
469         persistenceUnitManager.setLoadTimeWeaver(loadTimeWeaver);
470     }
471 
472     /**
473      * {@inheritDoc}
474      */
475     @Override
476     public void setResourceLoader(ResourceLoader resourceLoader) {
477         persistenceUnitManager.setResourceLoader(resourceLoader);
478     }
479 
480     /**
481      * {@inheritDoc}
482      */
483     @Override
484     public PersistenceUnitInfo getPersistenceUnitInfo() {
485         return internalFactoryBean.getPersistenceUnitInfo();
486     }
487 
488     /**
489      * {@inheritDoc}
490      */
491     @Override
492     public String getPersistenceUnitName() {
493         return internalFactoryBean.getPersistenceUnitName();
494     }
495 
496     /**
497      * {@inheritDoc}
498      */
499     @Override
500     public DataSource getDataSource() {
501         PersistenceUnitInfo pui = internalFactoryBean.getPersistenceUnitInfo();
502         if (internalFactoryBean.getPersistenceUnitInfo() != null) {
503             return (pui.getJtaDataSource() != null ?
504                     pui.getJtaDataSource() :
505                     pui.getNonJtaDataSource());
506         }
507         return (persistenceUnitManager.getDefaultJtaDataSource() != null ?
508                 persistenceUnitManager.getDefaultJtaDataSource() :
509                 this.persistenceUnitManager.getDefaultDataSource());
510     }
511 
512     /**
513      * {@inheritDoc}
514      */
515     @Override
516     public JpaDialect getJpaDialect() {
517         return internalFactoryBean.getJpaDialect();
518     }
519 
520     /**
521      * {@inheritDoc}
522      */
523     @Override
524     public PersistenceProvider getPersistenceProvider() {
525         return internalFactoryBean.getPersistenceProvider();
526     }
527 
528     /**
529      * {@inheritDoc}
530      */
531     @Override
532     public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
533         return internalFactoryBean.translateExceptionIfPossible(ex);
534     }
535 
536     /**
537      * Specifies a List of class names which should be loaded into the resulting EntityManagerFactory as managed
538      * JPA classes.
539      *
540      * @param managedClassNames the List of managed class names to set on this factory bean
541      */
542     public void setManagedClassNames(List<String> managedClassNames) {
543         if (managedClassNames == null) {
544             managedClassNames = new ArrayList<String>();
545         }
546         if (LOG.isInfoEnabled()) {
547             LOG.info(getPersistenceUnitName() + ": Setting Managed Class Names JPA:\n" + managedClassNames);
548         }
549         this.managedClassNames = managedClassNames;
550     }
551 
552     /**
553      * Specify the (potentially vendor-specific) EntityManager interface that this factory's EntityManagers are supposed
554      * to implement.
555      *
556      * <p>
557      * The default will be taken from the specific JpaVendorAdapter, if any, or set to the standard
558      * {@code EntityManager} interface else.
559      * </p>
560      *
561      * @param emInterface the {@link EntityManager} interface to use
562      *
563      * @see JpaVendorAdapter#getEntityManagerInterface()
564      * @see EntityManagerFactoryInfo#getEntityManagerInterface()
565      */
566     public void setEntityManagerInterface(Class<? extends EntityManager> emInterface) {
567         internalFactoryBean.setEntityManagerInterface(emInterface);
568     }
569 
570     /**
571      * Specify the (potentially vendor-specific) EntityManagerFactory interface that this EntityManagerFactory proxy is
572      * supposed to implement.
573      *
574      * <p>
575      * The default will be taken from the specific JpaVendorAdapter, if any, or set to the standard
576      * {@code EntityManagerFactory} interface else.
577      * </p>
578      *
579      * @param emfInterface the {@link EntityManagerFactory} interface to use
580      *
581      * @see JpaVendorAdapter#getEntityManagerFactoryInterface()
582      */
583     public void setEntityManagerFactoryInterface(Class<? extends EntityManagerFactory> emfInterface) {
584         internalFactoryBean.setEntityManagerFactoryInterface(emfInterface);
585     }
586 
587     /**
588      * Allow Map access to the JPA properties to be passed to the persistence provider, with the option to add or
589      * override specific entries.
590      *
591      * <p>Useful for specifying entries directly, for example via "jpaPropertyMap[myKey]".</p>
592      *
593      * @return the map of JPA properties
594      */
595     public Map<String, Object> getJpaPropertyMap() {
596         return internalFactoryBean.getJpaPropertyMap();
597     }
598 
599     /**
600      * Specify JPA properties as a Map, to be passed into {@code Persistence.createEntityManagerFactory} (if any).
601      *
602      * <p>Can be populated with a "map" or "props" element in XML bean definitions.</p>
603      *
604      * @param jpaProperties map of JPA properties to set
605      *
606      * @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
607      * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
608      */
609     public void setJpaPropertyMap(Map<String, ?> jpaProperties) {
610         internalFactoryBean.setJpaPropertyMap(jpaProperties);
611     }
612 
613     /**
614      * Specify JPA properties, to be passed into {@code Persistence.createEntityManagerFactory} (if any).
615      *
616      * <p>Can be populated with a String "value" (parsed via PropertiesEditor) or a "props" element in XML bean definitions.</p>
617      *
618      * @param jpaProperties the properties to set
619      *
620      * @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
621      * @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
622      */
623     public void setJpaProperties(Properties jpaProperties) {
624         internalFactoryBean.setJpaProperties(jpaProperties);
625     }
626 
627     /**
628      * Specify the name of the EntityManagerFactory configuration.
629      *
630      * <p>
631      * Default is none, indicating the default EntityManagerFactory configuration. The persistence provider will throw
632      * an exception if ambiguous EntityManager configurations are found.
633      * </p>
634      *
635      * @param persistenceUnitName the name of the persistence unit
636      *
637      * @see javax.persistence.Persistence#createEntityManagerFactory(String)
638      */
639     public void setPersistenceUnitName(String persistenceUnitName) {
640         internalFactoryBean.setPersistenceUnitName(persistenceUnitName);
641         persistenceUnitManager.setDefaultPersistenceUnitName(persistenceUnitName);
642     }
643 
644     /**
645      * Set whether to use Spring-based scanning for entity classes in the classpath instead of using JPA's standard
646      * scanning of jar files with {@code persistence.xml} markers in them.
647      *
648      * <p>
649      * In case of Spring-based scanning, no {@code persistence.xml} is necessary; all you need to do is to specify base
650      * packages to search here.
651      * </p>
652      *
653      * <p>
654      * Default is none. Specify packages to search for autodetection of your entity classes in the classpath. This is
655      * analogous to Spring's component-scan feature
656      * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
657      * </p>
658      *
659      * @param packagesToScan one or more base packages to search, analogous to Spring's component-scan configuration for
660      *        regular Spring components
661      *
662      * @see {@link DefaultPersistenceUnitManager#setPackagesToScan(String...)}
663      */
664     public void setPackagesToScan(String... packagesToScan) {
665         if (LOG.isInfoEnabled()) {
666             LOG.info(getPersistenceUnitName() + ": Setting Packages to Scan for JPA Annotations:\n"
667                 + Arrays.deepToString(packagesToScan));
668         }
669         persistenceUnitManager.setPackagesToScan(packagesToScan);
670 		converterPackageNames = Arrays.asList(packagesToScan);
671     }
672 
673     /**
674      * Specify one or more mapping resources (equivalent to {@code &lt;mapping-file&gt;} entries in
675      * {@code persistence.xml}) for the default persistence unit.
676      *
677      * <p>
678      * Can be used on its own or in combination with entity scanning in the classpath, in both cases avoiding
679      * {@code persistence.xml}.
680      * </p>
681      *
682      * <p>
683      * Note that mapping resources must be relative to the classpath root, e.g. "META-INF/mappings.xml" or
684      * "com/mycompany/repository/mappings.xml", so that they can be loaded through {@code ClassLoader.getResource}.
685      * </p>
686      *
687      * @param mappingResources one or more mapping resources to use
688      *
689      * @see {@link DefaultPersistenceUnitManager#setMappingResources(String...)}
690      */
691     public void setMappingResources(String... mappingResources) {
692         persistenceUnitManager.setMappingResources(mappingResources);
693     }
694 
695     /**
696      * Specify the JDBC DataSource that the JPA persistence provider is supposed to use for accessing the database.
697      *
698      * <p>
699      * This is an alternative to keeping the JDBC configuration in {@code persistence.xml}, passing in a Spring-managed
700      * DataSource instead.
701      * </p>
702      *
703      * <p>
704      * In JPA speak, a DataSource passed in here will be used as "nonJtaDataSource" on the PersistenceUnitInfo passed
705      * to the PersistenceProvider, as well as overriding data source configuration in {@code persistence.xml} (if any).
706      * Note that this variant typically works for JTA transaction management as well; if it does not, consider using the
707      * explicit {@link #setJtaDataSource} instead.
708      * </p>
709      *
710      * @param dataSource the DataSource to use for this EntityManagerFactory
711      *
712      * @see javax.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
713      * @see {@link DefaultPersistenceUnitManager#setDefaultDataSource(javax.sql.DataSource)}
714      */
715     public void setDataSource(DataSource dataSource) {
716         persistenceUnitManager.setDefaultDataSource(dataSource);
717     }
718 
719     /**
720      * Specify the JDBC DataSource that the JPA persistence provider is supposed to use for accessing the database.
721      *
722      * <p>
723      * This is an alternative to keeping the JDBC configuration in {@code persistence.xml}, passing in a Spring-managed
724      * DataSource instead.
725      * </p>
726      *
727      * <p>
728      * In JPA speak, a DataSource passed in here will be used as "jtaDataSource" on the PersistenceUnitInfo passed to
729      * the PersistenceProvider, as well as overriding data source configuration in {@code persistence.xml} (if any).
730      * </p>
731      *
732      * @param jtaDataSource the JTA-enabled DataSource to use for this EntityManagerFactory
733      *
734      * @see javax.persistence.spi.PersistenceUnitInfo#getJtaDataSource()
735      * @see {@link DefaultPersistenceUnitManager#setDefaultJtaDataSource(javax.sql.DataSource)}
736      */
737     public void setJtaDataSource(DataSource jtaDataSource) {
738         persistenceUnitManager.setDefaultJtaDataSource(jtaDataSource);
739     }
740 
741     /**
742      * Set the PersistenceProvider instance to use for creating the EntityManagerFactory.
743      *
744      * <p>
745      * If not specified, the persistence provider will be taken from the JpaVendorAdapter (if any) or determined by the
746      * persistence unit deployment descriptor (as far as possible).
747      * </p>
748      *
749      * @param persistenceProvider the PersistenceProvider to set
750      *
751      * @see JpaVendorAdapter#getPersistenceProvider()
752      * @see javax.persistence.spi.PersistenceProvider
753      * @see javax.persistence.Persistence
754      */
755     public void setPersistenceProvider(PersistenceProvider persistenceProvider) {
756         this.internalFactoryBean.setPersistenceProvider(persistenceProvider);
757     }
758 
759     /**
760      * Specify the vendor-specific JpaDialect implementation to associate with this EntityManagerFactory.
761      *
762      * <p>
763      * This will be exposed through the EntityManagerFactoryInfo interface, to be picked up as default dialect by
764      * accessors that intend to use JpaDialect functionality.
765      * </p>
766      *
767      * @param jpaDialect the JPA dialect to set
768      *
769      * @see EntityManagerFactoryInfo#getJpaDialect()
770      */
771     public void setJpaDialect(JpaDialect jpaDialect) {
772         this.internalFactoryBean.setJpaDialect(jpaDialect);
773     }
774 
775     /**
776      * Specify the JpaVendorAdapter implementation for the desired JPA provider, if any.
777      *
778      * <p>
779      * This will initialize appropriate defaults for the given provider, such as persistence provider class and
780      * JpaDialect, unless locally overridden in this FactoryBean.
781      * </p>
782      *
783      * @param jpaVendorAdapter the JpaVendorAdapter to set
784      */
785     public void setJpaVendorAdapter(JpaVendorAdapter jpaVendorAdapter) {
786         this.internalFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
787     }
788 
789     /**
790      * Set the PersistenceUnitPostProcessors to be applied to the * PersistenceUnitInfo used for creating this
791      * EntityManagerFactory.
792      *
793      * <p>
794      * Note that if executed before {@link #afterPropertiesSet()} then this factory bean may introduce its own post
795      * processor instances. If invoked after, then this method will override internally configured post processors.
796      * </p>
797      *
798      * <p>
799      * Such post-processors can, for example, register further entity classes and jar files, in addition to the metadata
800      * read from {@code persistence.xml}.
801      * </p>
802      *
803      * @param postProcessors one or more post processors to set
804      */
805     public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor... postProcessors) {
806         // persistence unit post processors will get applied to the internal factory bean during afterPropertiesSet(),
807         // this allows us to add our own internal post processor to the list)
808         this.persistenceUnitPostProcessors = new ArrayList<PersistenceUnitPostProcessor>(Arrays.asList(postProcessors));
809     }
810 
811     /**
812      * A {@link PersistenceUnitPostProcessor} to handle {@link Converter} annotations.
813      */
814     private final class InternalPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor {
815 
816 		private final TypeFilter converterAnnotationTypeFilter = new AnnotationTypeFilter(Converter.class);
817 		private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
818 		private static final String ENTITY_CLASS_RESOURCE_PATTERN = "/**/*.class";
819 
820         /**
821          * {@inheritDoc}
822          */
823         @Override
824         public void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
825             pui.setExcludeUnlistedClasses(DEFAULT_EXCLUDE_UNLISTED_CLASSES);
826 			processConverterPackages(pui);
827             for (String managedClassName : getManagedClassNames()) {
828                 pui.addManagedClassName(managedClassName);
829             }
830         }
831 
832         /**
833          * Determines whether the managed classes contain {@link Converter} annotations and adds them if necessary.
834          *
835          * @param pui the list of current list of managed classes.
836          */
837 		private void processConverterPackages(MutablePersistenceUnitInfo pui) {
838 			if (converterPackageNames != null) {
839 				for (String converterPackage : converterPackageNames) {
840 					// Code below lifted and modified from Spring's DefaultPersistenceUnitManager
841 					try {
842 						String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
843 								+ ClassUtils.convertClassNameToResourcePath(converterPackage)
844 								+ ENTITY_CLASS_RESOURCE_PATTERN;
845 						if (LOG.isInfoEnabled()) {
846 							LOG.info(getPersistenceUnitName() + ": Scanning for JPA @Converter annotations in: "
847 									+ pattern);
848 						}
849 						Resource[] resources = this.resourcePatternResolver.getResources(pattern);
850 						MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(
851 								this.resourcePatternResolver);
852 						for (Resource resource : resources) {
853 							if (!resource.isReadable()) {
854 								continue;
855 							}
856 							if (LOG.isDebugEnabled()) {
857 								LOG.debug(getPersistenceUnitName() + ": Found Matching Resource: " + resource);
858 							}
859 							MetadataReader reader = readerFactory.getMetadataReader(resource);
860 							String className = reader.getClassMetadata().getClassName();
861 							if (!pui.getManagedClassNames().contains(className)
862 									&& converterAnnotationTypeFilter.match(reader, readerFactory)) {
863 								pui.addManagedClassName(className);
864 								if (LOG.isDebugEnabled()) {
865 									LOG.debug(getPersistenceUnitName()
866 											+ ": Registering Converter in JPA Persistence Unit: " + className);
867 								}
868 							}
869 						}
870 					} catch (IOException ex) {
871 						throw new PersistenceException("Failed to scan classpath converters in package: "
872 								+ converterPackage, ex);
873 					}
874 				}
875 			}
876 		}
877     }
878 }