1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.orm.jpa.persistenceunit;
18
19 import java.io.IOException;
20 import java.lang.annotation.Annotation;
21 import java.net.URL;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.LinkedHashSet;
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import javax.persistence.Embeddable;
30 import javax.persistence.Entity;
31 import javax.persistence.MappedSuperclass;
32 import javax.persistence.PersistenceException;
33 import javax.persistence.SharedCacheMode;
34 import javax.persistence.ValidationMode;
35 import javax.persistence.spi.PersistenceUnitInfo;
36 import javax.sql.DataSource;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40
41 import org.springframework.beans.factory.InitializingBean;
42 import org.springframework.context.ResourceLoaderAware;
43 import org.springframework.context.weaving.LoadTimeWeaverAware;
44 import org.springframework.core.io.Resource;
45 import org.springframework.core.io.ResourceLoader;
46 import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
47 import org.springframework.core.io.support.ResourcePatternResolver;
48 import org.springframework.core.io.support.ResourcePatternUtils;
49 import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
50 import org.springframework.core.type.classreading.MetadataReader;
51 import org.springframework.core.type.classreading.MetadataReaderFactory;
52 import org.springframework.core.type.filter.AnnotationTypeFilter;
53 import org.springframework.core.type.filter.TypeFilter;
54 import org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver;
55 import org.springframework.instrument.classloading.LoadTimeWeaver;
56 import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
57 import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
58 import org.springframework.jdbc.datasource.lookup.MapDataSourceLookup;
59 import org.springframework.util.ClassUtils;
60 import org.springframework.util.ObjectUtils;
61 import org.springframework.util.ResourceUtils;
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public class DefaultPersistenceUnitManager
87 implements PersistenceUnitManager, ResourceLoaderAware, LoadTimeWeaverAware, InitializingBean {
88
89 private static final String CLASS_RESOURCE_PATTERN = "/**/*.class";
90
91 private static final String PACKAGE_INFO_SUFFIX = ".package-info";
92
93 private static final String DEFAULT_ORM_XML_RESOURCE = "META-INF/orm.xml";
94
95 private static final String PERSISTENCE_XML_FILENAME = "persistence.xml";
96
97
98
99
100
101 public final static String DEFAULT_PERSISTENCE_XML_LOCATION = "classpath*:META-INF/" + PERSISTENCE_XML_FILENAME;
102
103
104
105
106
107 public final static String ORIGINAL_DEFAULT_PERSISTENCE_UNIT_ROOT_LOCATION = "classpath:";
108
109 public final static String ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME = "default";
110
111
112 private static final Set<TypeFilter> entityTypeFilters;
113
114 static {
115 entityTypeFilters = new LinkedHashSet<TypeFilter>(4);
116 entityTypeFilters.add(new AnnotationTypeFilter(Entity.class, false));
117 entityTypeFilters.add(new AnnotationTypeFilter(Embeddable.class, false));
118 entityTypeFilters.add(new AnnotationTypeFilter(MappedSuperclass.class, false));
119 try {
120 @SuppressWarnings("unchecked")
121 Class<? extends Annotation> converterAnnotation = (Class<? extends Annotation>)
122 ClassUtils.forName("javax.persistence.Converter", DefaultPersistenceUnitManager.class.getClassLoader());
123 entityTypeFilters.add(new AnnotationTypeFilter(converterAnnotation, false));
124 }
125 catch (ClassNotFoundException ex) {
126
127 }
128 }
129
130
131 protected final Log logger = LogFactory.getLog(getClass());
132
133 private String[] persistenceXmlLocations = new String[] {DEFAULT_PERSISTENCE_XML_LOCATION};
134
135 private String defaultPersistenceUnitRootLocation = ORIGINAL_DEFAULT_PERSISTENCE_UNIT_ROOT_LOCATION;
136
137 private String defaultPersistenceUnitName = ORIGINAL_DEFAULT_PERSISTENCE_UNIT_NAME;
138
139 private String[] packagesToScan;
140
141 private String[] mappingResources;
142
143 private SharedCacheMode sharedCacheMode;
144
145 private ValidationMode validationMode;
146
147 private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
148
149 private DataSource defaultDataSource;
150
151 private DataSource defaultJtaDataSource;
152
153 private PersistenceUnitPostProcessor[] persistenceUnitPostProcessors;
154
155 private LoadTimeWeaver loadTimeWeaver;
156
157 private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
158
159 private final Set<String> persistenceUnitInfoNames = new HashSet<String>();
160
161 private final Map<String, PersistenceUnitInfo> persistenceUnitInfos = new HashMap<String, PersistenceUnitInfo>();
162
163
164
165
166
167
168
169 public void setPersistenceXmlLocation(String persistenceXmlLocation) {
170 this.persistenceXmlLocations = new String[] {persistenceXmlLocation};
171 }
172
173
174
175
176
177
178
179
180 public void setPersistenceXmlLocations(String... persistenceXmlLocations) {
181 this.persistenceXmlLocations = persistenceXmlLocations;
182 }
183
184
185
186
187
188
189
190
191 public void setDefaultPersistenceUnitRootLocation(String defaultPersistenceUnitRootLocation) {
192 this.defaultPersistenceUnitRootLocation = defaultPersistenceUnitRootLocation;
193 }
194
195
196
197
198
199
200
201
202 public void setDefaultPersistenceUnitName(String defaultPersistenceUnitName) {
203 this.defaultPersistenceUnitName = defaultPersistenceUnitName;
204 }
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232 public void setPackagesToScan(String... packagesToScan) {
233 this.packagesToScan = packagesToScan;
234 }
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257 public void setMappingResources(String... mappingResources) {
258 this.mappingResources = mappingResources;
259 }
260
261
262
263
264
265
266 public void setSharedCacheMode(SharedCacheMode sharedCacheMode) {
267 this.sharedCacheMode = sharedCacheMode;
268 }
269
270
271
272
273
274
275 public void setValidationMode(ValidationMode validationMode) {
276 this.validationMode = validationMode;
277 }
278
279
280
281
282
283
284
285
286
287
288
289 public void setDataSources(Map<String, DataSource> dataSources) {
290 this.dataSourceLookup = new MapDataSourceLookup(dataSources);
291 }
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309 public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
310 this.dataSourceLookup = (dataSourceLookup != null ? dataSourceLookup : new JndiDataSourceLookup());
311 }
312
313
314
315
316
317
318 public DataSourceLookup getDataSourceLookup() {
319 return this.dataSourceLookup;
320 }
321
322
323
324
325
326
327
328
329
330
331 public void setDefaultDataSource(DataSource defaultDataSource) {
332 this.defaultDataSource = defaultDataSource;
333 }
334
335
336
337
338
339 public DataSource getDefaultDataSource() {
340 return this.defaultDataSource;
341 }
342
343
344
345
346
347
348
349
350
351
352 public void setDefaultJtaDataSource(DataSource defaultJtaDataSource) {
353 this.defaultJtaDataSource = defaultJtaDataSource;
354 }
355
356
357
358
359
360 public DataSource getDefaultJtaDataSource() {
361 return this.defaultJtaDataSource;
362 }
363
364
365
366
367
368
369
370 public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor... postProcessors) {
371 this.persistenceUnitPostProcessors = postProcessors;
372 }
373
374
375
376
377
378 public PersistenceUnitPostProcessor[] getPersistenceUnitPostProcessors() {
379 return this.persistenceUnitPostProcessors;
380 }
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400 @Override
401 public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
402 this.loadTimeWeaver = loadTimeWeaver;
403 }
404
405
406
407
408
409 public LoadTimeWeaver getLoadTimeWeaver() {
410 return this.loadTimeWeaver;
411 }
412
413 @Override
414 public void setResourceLoader(ResourceLoader resourceLoader) {
415 this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
416 }
417
418
419 @Override
420 public void afterPropertiesSet() {
421 if (this.loadTimeWeaver == null && InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
422 this.loadTimeWeaver = new InstrumentationLoadTimeWeaver(this.resourcePatternResolver.getClassLoader());
423 }
424 preparePersistenceUnitInfos();
425 }
426
427
428
429
430
431
432
433
434
435
436 public void preparePersistenceUnitInfos() {
437 this.persistenceUnitInfoNames.clear();
438 this.persistenceUnitInfos.clear();
439
440 List<SpringPersistenceUnitInfo> puis = readPersistenceUnitInfos();
441 for (SpringPersistenceUnitInfo pui : puis) {
442 if (pui.getPersistenceUnitRootUrl() == null) {
443 pui.setPersistenceUnitRootUrl(determineDefaultPersistenceUnitRootUrl());
444 }
445 if (pui.getJtaDataSource() == null) {
446 pui.setJtaDataSource(this.defaultJtaDataSource);
447 }
448 if (pui.getNonJtaDataSource() == null) {
449 pui.setNonJtaDataSource(this.defaultDataSource);
450 }
451 if (this.sharedCacheMode != null) {
452 pui.setSharedCacheMode(this.sharedCacheMode);
453 }
454 if (this.validationMode != null) {
455 pui.setValidationMode(this.validationMode);
456 }
457 if (this.loadTimeWeaver != null) {
458 pui.init(this.loadTimeWeaver);
459 }
460 else {
461 pui.init(this.resourcePatternResolver.getClassLoader());
462 }
463 postProcessPersistenceUnitInfo(pui);
464 String name = pui.getPersistenceUnitName();
465 if (!this.persistenceUnitInfoNames.add(name) && !isPersistenceUnitOverrideAllowed()) {
466 StringBuilder msg = new StringBuilder();
467 msg.append("Conflicting persistence unit definitions for name '").append(name).append("': ");
468 msg.append(pui.getPersistenceUnitRootUrl()).append(", ");
469 msg.append(this.persistenceUnitInfos.get(name).getPersistenceUnitRootUrl());
470 throw new IllegalStateException(msg.toString());
471 }
472 this.persistenceUnitInfos.put(name, pui);
473 }
474 }
475
476
477
478
479
480 private List<SpringPersistenceUnitInfo> readPersistenceUnitInfos() {
481 List<SpringPersistenceUnitInfo> infos = new LinkedList<SpringPersistenceUnitInfo>();
482 String defaultName = this.defaultPersistenceUnitName;
483 boolean buildDefaultUnit = (this.packagesToScan != null || this.mappingResources != null);
484 boolean foundDefaultUnit = false;
485
486 PersistenceUnitReader reader = new PersistenceUnitReader(this.resourcePatternResolver, this.dataSourceLookup);
487 SpringPersistenceUnitInfo[] readInfos = reader.readPersistenceUnitInfos(this.persistenceXmlLocations);
488 for (SpringPersistenceUnitInfo readInfo : readInfos) {
489 infos.add(readInfo);
490 if (defaultName != null && defaultName.equals(readInfo.getPersistenceUnitName())) {
491 foundDefaultUnit = true;
492 }
493 }
494
495 if (buildDefaultUnit) {
496 if (foundDefaultUnit) {
497 if (logger.isInfoEnabled()) {
498 logger.info("Found explicit default unit with name '" + defaultName + "' in persistence.xml - " +
499 "overriding local default unit settings ('packagesToScan'/'mappingResources')");
500 }
501 }
502 else {
503 infos.add(buildDefaultPersistenceUnitInfo());
504 }
505 }
506 return infos;
507 }
508
509
510
511
512
513 private SpringPersistenceUnitInfo buildDefaultPersistenceUnitInfo() {
514 SpringPersistenceUnitInfo scannedUnit = new SpringPersistenceUnitInfo();
515 scannedUnit.setPersistenceUnitName(this.defaultPersistenceUnitName);
516 scannedUnit.setExcludeUnlistedClasses(true);
517
518 if (this.packagesToScan != null) {
519 for (String pkg : this.packagesToScan) {
520 try {
521 String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
522 ClassUtils.convertClassNameToResourcePath(pkg) + CLASS_RESOURCE_PATTERN;
523 Resource[] resources = this.resourcePatternResolver.getResources(pattern);
524 MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
525 for (Resource resource : resources) {
526 if (resource.isReadable()) {
527 MetadataReader reader = readerFactory.getMetadataReader(resource);
528 String className = reader.getClassMetadata().getClassName();
529 if (matchesFilter(reader, readerFactory)) {
530 scannedUnit.addManagedClassName(className);
531 if (scannedUnit.getPersistenceUnitRootUrl() == null) {
532 URL url = resource.getURL();
533 if (ResourceUtils.isJarURL(url)) {
534 scannedUnit.setPersistenceUnitRootUrl(ResourceUtils.extractJarFileURL(url));
535 }
536 }
537 }
538 else if (className.endsWith(PACKAGE_INFO_SUFFIX)) {
539 scannedUnit.addManagedPackage(
540 className.substring(0, className.length() - PACKAGE_INFO_SUFFIX.length()));
541 }
542 }
543 }
544 }
545 catch (IOException ex) {
546 throw new PersistenceException("Failed to scan classpath for unlisted entity classes", ex);
547 }
548 }
549 }
550
551 if (this.mappingResources != null) {
552 for (String mappingFileName : this.mappingResources) {
553 scannedUnit.addMappingFileName(mappingFileName);
554 }
555 }
556 else if (useOrmXmlForDefaultPersistenceUnit()) {
557 scannedUnit.addMappingFileName(DEFAULT_ORM_XML_RESOURCE);
558 }
559
560 return scannedUnit;
561 }
562
563
564
565
566
567 private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
568 for (TypeFilter filter : entityTypeFilters) {
569 if (filter.match(reader, readerFactory)) {
570 return true;
571 }
572 }
573 return false;
574 }
575
576
577
578
579
580
581
582 private URL determineDefaultPersistenceUnitRootUrl() {
583 if (this.defaultPersistenceUnitRootLocation == null) {
584 return null;
585 }
586 try {
587 Resource res = this.resourcePatternResolver.getResource(this.defaultPersistenceUnitRootLocation);
588 return res.getURL();
589 }
590 catch (IOException ex) {
591 throw new PersistenceException("Unable to resolve persistence unit root URL", ex);
592 }
593 }
594
595
596
597
598
599
600
601 private boolean useOrmXmlForDefaultPersistenceUnit() {
602 Resource ormXml = this.resourcePatternResolver.getResource(
603 this.defaultPersistenceUnitRootLocation + DEFAULT_ORM_XML_RESOURCE);
604 if (ormXml.exists()) {
605 try {
606 Resource persistenceXml = ormXml.createRelative(PERSISTENCE_XML_FILENAME);
607 if (!persistenceXml.exists()) {
608 return true;
609 }
610 }
611 catch (IOException ex) {
612
613 return true;
614 }
615 }
616 return false;
617 }
618
619
620
621
622
623
624
625
626
627
628
629 protected final MutablePersistenceUnitInfo getPersistenceUnitInfo(String persistenceUnitName) {
630 PersistenceUnitInfo pui = this.persistenceUnitInfos.get(persistenceUnitName);
631 return (MutablePersistenceUnitInfo) pui;
632 }
633
634
635
636
637
638
639
640
641
642
643 protected void postProcessPersistenceUnitInfo(MutablePersistenceUnitInfo pui) {
644 PersistenceUnitPostProcessor[] postProcessors = getPersistenceUnitPostProcessors();
645 if (postProcessors != null) {
646 for (PersistenceUnitPostProcessor postProcessor : postProcessors) {
647 postProcessor.postProcessPersistenceUnitInfo(pui);
648 }
649 }
650 }
651
652
653
654
655
656
657 protected boolean isPersistenceUnitOverrideAllowed() {
658 return false;
659 }
660
661
662 @Override
663 public PersistenceUnitInfo obtainDefaultPersistenceUnitInfo() {
664 if (this.persistenceUnitInfoNames.isEmpty()) {
665 throw new IllegalStateException("No persistence units parsed from " +
666 ObjectUtils.nullSafeToString(this.persistenceXmlLocations));
667 }
668 if (this.persistenceUnitInfos.isEmpty()) {
669 throw new IllegalStateException("All persistence units from " +
670 ObjectUtils.nullSafeToString(this.persistenceXmlLocations) + " already obtained");
671 }
672 if (this.persistenceUnitInfos.size() > 1) {
673 return obtainPersistenceUnitInfo(this.defaultPersistenceUnitName);
674 }
675 PersistenceUnitInfo pui = this.persistenceUnitInfos.values().iterator().next();
676 this.persistenceUnitInfos.clear();
677 return pui;
678 }
679
680 @Override
681 public PersistenceUnitInfo obtainPersistenceUnitInfo(String persistenceUnitName) {
682 PersistenceUnitInfo pui = this.persistenceUnitInfos.remove(persistenceUnitName);
683 if (pui == null) {
684 if (!this.persistenceUnitInfoNames.contains(persistenceUnitName)) {
685 throw new IllegalArgumentException(
686 "No persistence unit with name '" + persistenceUnitName + "' found");
687 }
688 else {
689 throw new IllegalStateException(
690 "Persistence unit with name '" + persistenceUnitName + "' already obtained");
691 }
692 }
693 return pui;
694 }
695
696 }