1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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);
160 this.persistenceUnitManager = createPersistenceUnitManager();
161 this.internalFactoryBean = createInternalFactoryBean(this.persistenceUnitManager);
162 this.internalFactoryBean.setJpaPropertyMap(createDefaultJpaProperties());
163 }
164
165
166
167
168
169
170
171 protected LocalContainerEntityManagerFactoryBean getInternalFactoryBean() {
172 return this.internalFactoryBean;
173 }
174
175 protected DefaultPersistenceUnitManager createPersistenceUnitManager() {
176 DefaultPersistenceUnitManager pum = new DefaultPersistenceUnitManager();
177
178
179
180
181
182
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
232
233
234
235
236
237
238 protected void loadCustomJpaDefaults(Map<String, String> jpaProperties) {
239
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
264
265
266
267
268 public List<String> getManagedClassNames() {
269 return managedClassNames;
270 }
271
272
273
274
275
276
277
278 public PersistenceUnitPostProcessor[] getPersistenceUnitPostProcessors() {
279 return persistenceUnitPostProcessors.toArray(new PersistenceUnitPostProcessor[persistenceUnitPostProcessors.size()]);
280 }
281
282
283
284
285
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
391
392
393
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
407
408
409
410
411
412
413
414
415
416
417
418 public void setEntityManagerInterface(Class<? extends EntityManager> emInterface) {
419 internalFactoryBean.setEntityManagerInterface(emInterface);
420 }
421
422
423
424
425
426
427
428
429
430
431
432
433 public void setEntityManagerFactoryInterface(Class<? extends EntityManagerFactory> emfInterface) {
434 internalFactoryBean.setEntityManagerFactoryInterface(emfInterface);
435 }
436
437
438
439
440
441
442
443
444
445
446 public Map<String, Object> getJpaPropertyMap() {
447 return internalFactoryBean.getJpaPropertyMap();
448 }
449
450
451
452
453
454
455
456
457
458
459
460 public void setJpaPropertyMap(Map<String, ?> jpaProperties) {
461 internalFactoryBean.setJpaPropertyMap(jpaProperties);
462 }
463
464
465
466
467
468
469
470
471
472
473
474
475 public void setJpaProperties(Properties jpaProperties) {
476 internalFactoryBean.setJpaProperties(jpaProperties);
477 }
478
479
480
481
482
483
484
485
486
487
488
489 public void setPersistenceUnitName(String persistenceUnitName) {
490 internalFactoryBean.setPersistenceUnitName(persistenceUnitName);
491 persistenceUnitManager.setDefaultPersistenceUnitName(persistenceUnitName);
492 }
493
494
495
496
497
498
499
500
501
502
503
504
505
506
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
519
520
521
522
523
524
525
526
527
528
529 public void setMappingResources(String... mappingResources) {
530 persistenceUnitManager.setMappingResources(mappingResources);
531 }
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548 public void setDataSource(DataSource dataSource) {
549 persistenceUnitManager.setDefaultDataSource(dataSource);
550 }
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565 public void setJtaDataSource(DataSource jtaDataSource) {
566 persistenceUnitManager.setDefaultJtaDataSource(jtaDataSource);
567 }
568
569
570
571
572
573
574
575
576
577
578
579
580 public void setPersistenceProvider(PersistenceProvider persistenceProvider) {
581 this.internalFactoryBean.setPersistenceProvider(persistenceProvider);
582 }
583
584
585
586
587
588
589
590
591
592
593 public void setJpaDialect(JpaDialect jpaDialect) {
594 this.internalFactoryBean.setJpaDialect(jpaDialect);
595 }
596
597
598
599
600
601
602
603
604 public void setJpaVendorAdapter(JpaVendorAdapter jpaVendorAdapter) {
605 this.internalFactoryBean.setJpaVendorAdapter(jpaVendorAdapter);
606 }
607
608
609
610
611
612
613
614
615
616
617
618
619 public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor... postProcessors) {
620
621
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
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 }