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