1   /*
2    * Copyright 2002-2014 the original author or authors.
3    *
4    * Licensed under the Apache 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.apache.org/licenses/LICENSE-2.0
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  
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   * Default implementation of the {@link PersistenceUnitManager} interface.
65   * Used as internal default by
66   * {@link org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean}.
67   *
68   * <p>Supports standard JPA scanning for {@code persistence.xml} files,
69   * with configurable file locations, JDBC DataSource lookup and load-time weaving.
70   *
71   * <p>The default XML file location is {@code classpath*:META-INF/persistence.xml},
72   * scanning for all matching files in the classpath (as defined in the JPA specification).
73   * DataSource names are by default interpreted as JNDI names, and no load time weaving
74   * is available (which requires weaving to be turned off in the persistence provider).
75   *
76   * <p><b>NOTE: Spring's JPA support requires JPA 2.0 or higher, as of Spring 4.0.</b>
77   * Spring's persistence unit bootstrapping automatically detects JPA 2.1 at runtime.
78   *
79   * @author Juergen Hoeller
80   * @since 2.0
81   * @see #setPersistenceXmlLocations
82   * @see #setDataSourceLookup
83   * @see #setLoadTimeWeaver
84   * @see org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean#setPersistenceUnitManager
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  	 * Default location of the {@code persistence.xml} file:
99  	 * "classpath*:META-INF/persistence.xml".
100 	 */
101 	public final static String DEFAULT_PERSISTENCE_XML_LOCATION = "classpath*:META-INF/" + PERSISTENCE_XML_FILENAME;
102 
103 	/**
104 	 * Default location for the persistence unit root URL:
105 	 * "classpath:", indicating the root of the classpath.
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 			// JPA 2.1 API not available
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 	 * Specify the location of the {@code persistence.xml} files to load.
166 	 * These can be specified as Spring resource locations and/or location patterns.
167 	 * <p>Default is "classpath*:META-INF/persistence.xml".
168 	 */
169 	public void setPersistenceXmlLocation(String persistenceXmlLocation) {
170 		this.persistenceXmlLocations = new String[] {persistenceXmlLocation};
171 	}
172 
173 	/**
174 	 * Specify multiple locations of {@code persistence.xml} files to load.
175 	 * These can be specified as Spring resource locations and/or location patterns.
176 	 * <p>Default is "classpath*:META-INF/persistence.xml".
177 	 * @param persistenceXmlLocations an array of Spring resource Strings
178 	 * identifying the location of the {@code persistence.xml} files to read
179 	 */
180 	public void setPersistenceXmlLocations(String... persistenceXmlLocations) {
181 		this.persistenceXmlLocations = persistenceXmlLocations;
182 	}
183 
184 	/**
185 	 * Set the default persistence unit root location, to be applied
186 	 * if no unit-specific persistence unit root could be determined.
187 	 * <p>Default is "classpath:", that is, the root of the current classpath
188 	 * (nearest root directory). To be overridden if unit-specific resolution
189 	 * does not work and the classpath root is not appropriate either.
190 	 */
191 	public void setDefaultPersistenceUnitRootLocation(String defaultPersistenceUnitRootLocation) {
192 		this.defaultPersistenceUnitRootLocation = defaultPersistenceUnitRootLocation;
193 	}
194 
195 	/**
196 	 * Specify the name of the default persistence unit, if any. Default is "default".
197 	 * <p>Primarily applied to a scanned persistence unit without {@code persistence.xml}.
198 	 * Also applicable to selecting a default unit from several persistence units available.
199 	 * @see #setPackagesToScan
200 	 * @see #obtainDefaultPersistenceUnitInfo
201 	 */
202 	public void setDefaultPersistenceUnitName(String defaultPersistenceUnitName) {
203 		this.defaultPersistenceUnitName = defaultPersistenceUnitName;
204 	}
205 
206 	/**
207 	 * Set whether to use Spring-based scanning for entity classes in the classpath
208 	 * instead of using JPA's standard scanning of jar files with {@code persistence.xml}
209 	 * markers in them. In case of Spring-based scanning, no {@code persistence.xml}
210 	 * is necessary; all you need to do is to specify base packages to search here.
211 	 * <p>Default is none. Specify packages to search for autodetection of your entity
212 	 * classes in the classpath. This is analogous to Spring's component-scan feature
213 	 * ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
214 	 * <p>Such package scanning defines a "default persistence unit" in Spring, which
215 	 * may live next to regularly defined units originating from {@code persistence.xml}.
216 	 * Its name is determined by {@link #setDefaultPersistenceUnitName}: by default,
217 	 * it's simply "default".
218 	 * <p><p>Note: There may be limitations in comparison to regular JPA scanning.</b>
219 	 * In particular, JPA providers may pick up annotated packages for provider-specific
220 	 * annotations only when driven by {@code persistence.xml}. As of 4.1, Spring's
221 	 * scan can detect annotated packages as well if supported by the given
222 	 * {@link org.springframework.orm.jpa.JpaVendorAdapter} (e.g. for Hibernate).
223 	 * <p>If no explicit {@link #setMappingResources mapping resources} have been
224 	 * specified in addition to these packages, this manager looks for a default
225 	 * {@code META-INF/orm.xml} file in the classpath, registering it as a mapping
226 	 * resource for the default unit if the mapping file is not co-located with a
227 	 * {@code persistence.xml} file (in which case we assume it is only meant to be
228 	 * used with the persistence units defined there, like in standard JPA).
229 	 * @see #setDefaultPersistenceUnitName
230 	 * @see #setMappingResources
231 	 */
232 	public void setPackagesToScan(String... packagesToScan) {
233 		this.packagesToScan = packagesToScan;
234 	}
235 
236 	/**
237 	 * Specify one or more mapping resources (equivalent to {@code &lt;mapping-file&gt;}
238 	 * entries in {@code persistence.xml}) for the default persistence unit.
239 	 * Can be used on its own or in combination with entity scanning in the classpath,
240 	 * in both cases avoiding {@code persistence.xml}.
241 	 * <p>Note that mapping resources must be relative to the classpath root,
242 	 * e.g. "META-INF/mappings.xml" or "com/mycompany/repository/mappings.xml",
243 	 * so that they can be loaded through {@code ClassLoader.getResource}.
244 	 * <p>If no explicit mapping resources have been specified next to
245 	 * {@link #setPackagesToScan packages to scan}, this manager looks for a default
246 	 * {@code META-INF/orm.xml} file in the classpath, registering it as a mapping
247 	 * resource for the default unit if the mapping file is not co-located with a
248 	 * {@code persistence.xml} file (in which case we assume it is only meant to be
249 	 * used with the persistence units defined there, like in standard JPA).
250 	 * <p>Note that specifying an empty array/list here suppresses the default
251 	 * {@code META-INF/orm.xml} check. On the other hand, explicitly specifying
252 	 * {@code META-INF/orm.xml} here will register that file even if it happens
253 	 * to be co-located with a {@code persistence.xml} file.
254 	 * @see #setDefaultPersistenceUnitName
255 	 * @see #setPackagesToScan
256 	 */
257 	public void setMappingResources(String... mappingResources) {
258 		this.mappingResources = mappingResources;
259 	}
260 
261 	/**
262 	 * Specify the JPA 2.0 shared cache mode for all of this manager's persistence
263 	 * units, overriding any value in {@code persistence.xml} if set.
264 	 * @see javax.persistence.spi.PersistenceUnitInfo#getSharedCacheMode()
265 	 */
266 	public void setSharedCacheMode(SharedCacheMode sharedCacheMode) {
267 		this.sharedCacheMode = sharedCacheMode;
268 	}
269 
270 	/**
271 	 * Specify the JPA 2.0 validation mode for all of this manager's persistence
272 	 * units, overriding any value in {@code persistence.xml} if set.
273 	 * @see javax.persistence.spi.PersistenceUnitInfo#getValidationMode()
274 	 */
275 	public void setValidationMode(ValidationMode validationMode) {
276 		this.validationMode = validationMode;
277 	}
278 
279 	/**
280 	 * Specify the JDBC DataSources that the JPA persistence provider is supposed
281 	 * to use for accessing the database, resolving data source names in
282 	 * {@code persistence.xml} against Spring-managed DataSources.
283 	 * <p>The specified Map needs to define data source names for specific DataSource
284 	 * objects, matching the data source names used in {@code persistence.xml}.
285 	 * If not specified, data source names will be resolved as JNDI names instead
286 	 * (as defined by standard JPA).
287 	 * @see org.springframework.jdbc.datasource.lookup.MapDataSourceLookup
288 	 */
289 	public void setDataSources(Map<String, DataSource> dataSources) {
290 		this.dataSourceLookup = new MapDataSourceLookup(dataSources);
291 	}
292 
293 	/**
294 	 * Specify the JDBC DataSourceLookup that provides DataSources for the
295 	 * persistence provider, resolving data source names in {@code persistence.xml}
296 	 * against Spring-managed DataSource instances.
297 	 * <p>Default is JndiDataSourceLookup, which resolves DataSource names as
298 	 * JNDI names (as defined by standard JPA). Specify a BeanFactoryDataSourceLookup
299 	 * instance if you want DataSource names to be resolved against Spring bean names.
300 	 * <p>Alternatively, consider passing in a map from names to DataSource instances
301 	 * via the "dataSources" property. If the {@code persistence.xml} file
302 	 * does not define DataSource names at all, specify a default DataSource
303 	 * via the "defaultDataSource" property.
304 	 * @see org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup
305 	 * @see org.springframework.jdbc.datasource.lookup.BeanFactoryDataSourceLookup
306 	 * @see #setDataSources
307 	 * @see #setDefaultDataSource
308 	 */
309 	public void setDataSourceLookup(DataSourceLookup dataSourceLookup) {
310 		this.dataSourceLookup = (dataSourceLookup != null ? dataSourceLookup : new JndiDataSourceLookup());
311 	}
312 
313 	/**
314 	 * Return the JDBC DataSourceLookup that provides DataSources for the
315 	 * persistence provider, resolving data source names in {@code persistence.xml}
316 	 * against Spring-managed DataSource instances.
317 	 */
318 	public DataSourceLookup getDataSourceLookup() {
319 		return this.dataSourceLookup;
320 	}
321 
322 	/**
323 	 * Specify the JDBC DataSource that the JPA persistence provider is supposed to use
324 	 * for accessing the database if none has been specified in {@code persistence.xml}.
325 	 * This variant indicates no special transaction setup, i.e. typical resource-local.
326 	 * <p>In JPA speak, a DataSource passed in here will be uses as "nonJtaDataSource"
327 	 * on the PersistenceUnitInfo passed to the PersistenceProvider, provided that
328 	 * none has been registered before.
329 	 * @see javax.persistence.spi.PersistenceUnitInfo#getNonJtaDataSource()
330 	 */
331 	public void setDefaultDataSource(DataSource defaultDataSource) {
332 		this.defaultDataSource = defaultDataSource;
333 	}
334 
335 	/**
336 	 * Return the JDBC DataSource that the JPA persistence provider is supposed to use
337 	 * for accessing the database if none has been specified in {@code persistence.xml}.
338 	 */
339 	public DataSource getDefaultDataSource() {
340 		return this.defaultDataSource;
341 	}
342 
343 	/**
344 	 * Specify the JDBC DataSource that the JPA persistence provider is supposed to use
345 	 * for accessing the database if none has been specified in {@code persistence.xml}.
346 	 * This variant indicates that JTA is supposed to be used as transaction type.
347 	 * <p>In JPA speak, a DataSource passed in here will be uses as "jtaDataSource"
348 	 * on the PersistenceUnitInfo passed to the PersistenceProvider, provided that
349 	 * none has been registered before.
350 	 * @see javax.persistence.spi.PersistenceUnitInfo#getJtaDataSource()
351 	 */
352 	public void setDefaultJtaDataSource(DataSource defaultJtaDataSource) {
353 		this.defaultJtaDataSource = defaultJtaDataSource;
354 	}
355 
356 	/**
357 	 * Return the JTA-aware DataSource that the JPA persistence provider is supposed to use
358 	 * for accessing the database if none has been specified in {@code persistence.xml}.
359 	 */
360 	public DataSource getDefaultJtaDataSource() {
361 		return this.defaultJtaDataSource;
362 	}
363 
364 	/**
365 	 * Set the PersistenceUnitPostProcessors to be applied to each
366 	 * PersistenceUnitInfo that has been parsed by this manager.
367 	 * <p>Such post-processors can, for example, register further entity classes and
368 	 * jar files, in addition to the metadata read from {@code persistence.xml}.
369 	 */
370 	public void setPersistenceUnitPostProcessors(PersistenceUnitPostProcessor... postProcessors) {
371 		this.persistenceUnitPostProcessors = postProcessors;
372 	}
373 
374 	/**
375 	 * Return the PersistenceUnitPostProcessors to be applied to each
376 	 * PersistenceUnitInfo that has been parsed by this manager.
377 	 */
378 	public PersistenceUnitPostProcessor[] getPersistenceUnitPostProcessors() {
379 		return this.persistenceUnitPostProcessors;
380 	}
381 
382 	/**
383 	 * Specify the Spring LoadTimeWeaver to use for class instrumentation according
384 	 * to the JPA class transformer contract.
385 	 * <p>It is not required to specify a LoadTimeWeaver: Most providers will be able
386 	 * to provide a subset of their functionality without class instrumentation as well,
387 	 * or operate with their own VM agent specified on JVM startup. Furthermore,
388 	 * DefaultPersistenceUnitManager falls back to an InstrumentationLoadTimeWeaver
389 	 * if Spring's agent-based instrumentation is available at runtime.
390 	 * <p>In terms of Spring-provided weaving options, the most important ones are
391 	 * InstrumentationLoadTimeWeaver, which requires a Spring-specific (but very general)
392 	 * VM agent specified on JVM startup, and ReflectiveLoadTimeWeaver, which interacts
393 	 * with an underlying ClassLoader based on specific extended methods being available
394 	 * on it (for example, interacting with Spring's TomcatInstrumentableClassLoader).
395 	 * Consider using the {@code context:load-time-weaver} XML tag for creating
396 	 * such a shared LoadTimeWeaver (autodetecting the environment by default).
397 	 * @see org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver
398 	 * @see org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver
399 	 */
400 	@Override
401 	public void setLoadTimeWeaver(LoadTimeWeaver loadTimeWeaver) {
402 		this.loadTimeWeaver = loadTimeWeaver;
403 	}
404 
405 	/**
406 	 * Return the Spring LoadTimeWeaver to use for class instrumentation according
407 	 * to the JPA class transformer contract.
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 	 * Prepare the PersistenceUnitInfos according to the configuration
429 	 * of this manager: scanning for {@code persistence.xml} files,
430 	 * parsing all matching files, configuring and post-processing them.
431 	 * <p>PersistenceUnitInfos cannot be obtained before this preparation
432 	 * method has been invoked.
433 	 * @see #obtainDefaultPersistenceUnitInfo()
434 	 * @see #obtainPersistenceUnitInfo(String)
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 	 * Read all persistence unit infos from {@code persistence.xml},
478 	 * as defined in the JPA specification.
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 	 * Perform Spring-based scanning for entity classes.
511 	 * @see #setPackagesToScan
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 	 * Check whether any of the configured entity type filters matches
565 	 * the current class descriptor contained in the metadata reader.
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 	 * Try to determine the persistence unit root URL based on the given
578 	 * "defaultPersistenceUnitRootLocation".
579 	 * @return the persistence unit root URL to pass to the JPA PersistenceProvider
580 	 * @see #setDefaultPersistenceUnitRootLocation
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 	 * Determine whether to register JPA's default "META-INF/orm.xml" with
597 	 * Spring's default persistence unit, if any.
598 	 * <p>Checks whether a "META-INF/orm.xml" file exists in the classpath and
599 	 * uses it if it is not co-located with a "META-INF/persistence.xml" file.
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 				// Cannot resolve relative persistence.xml file - let's assume it's not there.
613 				return true;
614 			}
615 		}
616 		return false;
617 	}
618 
619 
620 	/**
621 	 * Return the specified PersistenceUnitInfo from this manager's cache
622 	 * of processed persistence units, keeping it in the cache (i.e. not
623 	 * 'obtaining' it for use but rather just accessing it for post-processing).
624 	 * <p>This can be used in {@link #postProcessPersistenceUnitInfo} implementations,
625 	 * detecting existing persistence units of the same name and potentially merging them.
626 	 * @param persistenceUnitName the name of the desired persistence unit
627 	 * @return the PersistenceUnitInfo in mutable form, or {@code null} if not available
628 	 */
629 	protected final MutablePersistenceUnitInfo getPersistenceUnitInfo(String persistenceUnitName) {
630 		PersistenceUnitInfo pui = this.persistenceUnitInfos.get(persistenceUnitName);
631 		return (MutablePersistenceUnitInfo) pui;
632 	}
633 
634 	/**
635 	 * Hook method allowing subclasses to customize each PersistenceUnitInfo.
636 	 * <p>The default implementation delegates to all registered PersistenceUnitPostProcessors.
637 	 * It is usually preferable to register further entity classes, jar files etc there
638 	 * rather than in a subclass of this manager, to be able to reuse the post-processors.
639 	 * @param pui the chosen PersistenceUnitInfo, as read from {@code persistence.xml}.
640 	 * Passed in as MutablePersistenceUnitInfo.
641 	 * @see #setPersistenceUnitPostProcessors
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 	 * Return whether an override of a same-named persistence unit is allowed.
654 	 * <p>Default is {@code false}. May be overridden to return {@code true},
655 	 * for example if {@link #postProcessPersistenceUnitInfo} is able to handle that case.
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 }