001/**
002 * Copyright 2005-2016 The Kuali Foundation
003 *
004 * Licensed under the Educational Community 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.opensource.org/licenses/ecl2.php
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 */
016package org.kuali.rice.krad.data.jpa;
017
018import org.kuali.rice.core.api.config.property.ConfigContext;
019import org.kuali.rice.krad.data.jpa.converters.BooleanYNConverter;
020import org.springframework.beans.factory.BeanClassLoaderAware;
021import org.springframework.beans.factory.BeanFactory;
022import org.springframework.beans.factory.BeanFactoryAware;
023import org.springframework.beans.factory.BeanNameAware;
024import org.springframework.beans.factory.DisposableBean;
025import org.springframework.beans.factory.FactoryBean;
026import org.springframework.beans.factory.InitializingBean;
027import org.springframework.context.ResourceLoaderAware;
028import org.springframework.context.weaving.LoadTimeWeaverAware;
029import org.springframework.core.io.Resource;
030import org.springframework.core.io.ResourceLoader;
031import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
032import org.springframework.core.io.support.ResourcePatternResolver;
033import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
034import org.springframework.core.type.classreading.MetadataReader;
035import org.springframework.core.type.classreading.MetadataReaderFactory;
036import org.springframework.core.type.filter.AnnotationTypeFilter;
037import org.springframework.core.type.filter.TypeFilter;
038import org.springframework.dao.DataAccessException;
039import org.springframework.dao.support.PersistenceExceptionTranslator;
040import org.springframework.instrument.classloading.LoadTimeWeaver;
041import org.springframework.orm.jpa.EntityManagerFactoryInfo;
042import org.springframework.orm.jpa.JpaDialect;
043import org.springframework.orm.jpa.JpaVendorAdapter;
044import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
045import org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager;
046import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo;
047import org.springframework.orm.jpa.persistenceunit.PersistenceUnitManager;
048import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor;
049import org.springframework.util.ClassUtils;
050
051import javax.persistence.Converter;
052import javax.persistence.EntityManager;
053import javax.persistence.EntityManagerFactory;
054import javax.persistence.PersistenceException;
055import javax.persistence.spi.PersistenceProvider;
056import javax.persistence.spi.PersistenceUnitInfo;
057import javax.sql.DataSource;
058import java.io.IOException;
059import java.util.ArrayList;
060import java.util.Arrays;
061import java.util.HashMap;
062import java.util.List;
063import java.util.Map;
064import java.util.Properties;
065
066/**
067 * A KRAD-managed {@link javax.persistence.EntityManagerFactory} factory bean which can be used to configure a JPA
068 * persistence unit using the Spring Framework.
069 *
070 * <p>
071 * This implementation does not support the use of a custom PersistenceUnitManager, but rather stores and manages one
072 * internally. This is intended to be an alternative to direct usage of Spring's
073 * {@link LocalContainerEntityManagerFactoryBean} in order to make JPA configuration with KRAD simpler.
074 * </p>
075 *
076 * <h1>Minimal Configuration</h1>
077 *
078 * <p>Minimal configuration of this factory bean will include the following:</p>
079 *
080 * <ul>
081 *     <li>{@code persistenceUnitName}</li>
082 *     <li>{@code dataSource} or {@code jtaDataSource}</li>
083 *     <li>{@code persistenceProvider} or {@code jpaVendorAdapter}</li>
084 * </ul>
085 *
086 * <p>
087 * Note that persistence unit names must be unique, so choose a name which is unlikely to clash with any potential
088 * persistence units configured within the runtime environment.
089 * </p>
090 *
091 * <h1>Behavior</h1>
092 *
093 * <p>
094 * When leveraging this class, persistence.xml files are not used. Rather, persistence unit configuration is loaded
095 * via the various settings provided on this factory bean which contain everything needed to create the desired
096 * persistence unit configuration in the majority of cases. If a KRAD application needs more control over the
097 * configuration of the persistence unit or wants to use persistence.xml and JPA's default bootstrapping and classpath
098 * scanning behavior, an EntityManagerFactory can be configured using the other classes provided by the Spring
099 * 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 */
153public 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}