HHH-18231 Take provided classloader/classloaderservice into account in PersistenceXmlParser

This commit is contained in:
Yoann Rodière 2024-06-05 15:14:09 +02:00 committed by Steve Ebersole
parent 2863e43467
commit 93ea757382
6 changed files with 115 additions and 132 deletions

View File

@ -78,7 +78,8 @@ public class HibernatePersistenceProvider implements PersistenceProvider {
final Map<?,?> integration = wrap( properties ); final Map<?,?> integration = wrap( properties );
final List<ParsedPersistenceXmlDescriptor> units; final List<ParsedPersistenceXmlDescriptor> units;
try { try {
units = PersistenceXmlParser.locatePersistenceUnits( integration ); units = PersistenceXmlParser.create( integration, providedClassLoader, providedClassLoaderService )
.locatePersistenceUnits();
} }
catch (Exception e) { catch (Exception e) {
log.debug( "Unable to locate persistence units", e ); log.debug( "Unable to locate persistence units", e );

View File

@ -12,11 +12,10 @@ import java.net.URL;
import java.net.URLConnection; import java.net.URLConnection;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.xml.transform.stream.StreamSource; import javax.xml.transform.stream.StreamSource;
import org.hibernate.boot.archive.internal.ArchiveHelper; import org.hibernate.boot.archive.internal.ArchiveHelper;
@ -51,20 +50,80 @@ public class PersistenceXmlParser {
private static final EntityManagerMessageLogger LOG = messageLogger( PersistenceXmlParser.class ); private static final EntityManagerMessageLogger LOG = messageLogger( PersistenceXmlParser.class );
/** /**
* Find all persistence-units from all accessible {@code META-INF/persistence.xml} resources * @return A {@link PersistenceXmlParser} using no settings at all.
* */
public static PersistenceXmlParser create() {
return new PersistenceXmlParser( Map.of(), null, null );
}
/**
* @param integration The Map of integration settings * @param integration The Map of integration settings
* @return A {@link PersistenceXmlParser} using the provided settings.
*/
public static PersistenceXmlParser create(Map<?,?> integration) {
return new PersistenceXmlParser( integration, null, null );
}
/**
* @param integration The Map of integration settings
* @param providedClassLoader A class loader to use.
* Resources will be retrieved from this classloader in priority,
* before looking into classloaders mentioned in integration settings.
* @param providedClassLoaderService A class loader service to use.
* Takes precedence over classloading settings in {@code integration}
* and over {@code providedClassLoader}.
* @return A {@link PersistenceXmlParser} using the provided settings and classloading configuration.
*/
public static PersistenceXmlParser create(Map<?,?> integration, ClassLoader providedClassLoader,
ClassLoaderService providedClassLoaderService) {
return new PersistenceXmlParser( integration, providedClassLoader, providedClassLoaderService );
}
private final Map<?, ?> integration;
private final ClassLoaderService classLoaderService;
protected PersistenceXmlParser(Map<?, ?> integration, ClassLoader providedClassLoader,
ClassLoaderService providedClassLoaderService) {
this.integration = integration;
if ( providedClassLoaderService != null ) {
classLoaderService = providedClassLoaderService;
}
else {
final List<ClassLoader> providedClassLoaders = new ArrayList<>();
if ( providedClassLoader != null ) {
providedClassLoaders.add( providedClassLoader );
}
@SuppressWarnings("unchecked")
final Collection<ClassLoader> classLoaders =
(Collection<ClassLoader>) integration.get( AvailableSettings.CLASSLOADERS );
if ( classLoaders != null ) {
providedClassLoaders.addAll( classLoaders );
}
classLoaderService = new ClassLoaderServiceImpl(
providedClassLoaders,
TcclLookupPrecedence.from( integration, TcclLookupPrecedence.AFTER )
);
}
}
/**
* Find all persistence-units from all accessible {@code META-INF/persistence.xml} resources
* *
* @return List of descriptors for all discovered persistence-units. * @return List of descriptors for all discovered persistence-units.
*/ */
public static List<ParsedPersistenceXmlDescriptor> locatePersistenceUnits(Map<?,?> integration) {
@SuppressWarnings("removal") @SuppressWarnings("removal")
final PersistenceXmlParser parser = new PersistenceXmlParser( public List<ParsedPersistenceXmlDescriptor> locatePersistenceUnits() {
fromConfigSettings( integration ), Map<String, ParsedPersistenceXmlDescriptor> persistenceUnits = new HashMap<>();
PersistenceUnitTransactionType.RESOURCE_LOCAL final List<URL> xmlUrls = classLoaderService.locateResources( "META-INF/persistence.xml" );
); if ( xmlUrls.isEmpty() ) {
parser.doResolve( integration ); LOG.unableToFindPersistenceXmlInClasspath();
return new ArrayList<>( parser.persistenceUnits.values() ); }
else {
parsePersistenceXml( persistenceUnits, xmlUrls, PersistenceUnitTransactionType.RESOURCE_LOCAL );
}
return new ArrayList<>( persistenceUnits.values() );
} }
/** /**
@ -72,13 +131,12 @@ public class PersistenceXmlParser {
* persistence-unit. * persistence-unit.
* *
* @param persistenceXmlUrl The {@code persistence.xml} URL * @param persistenceXmlUrl The {@code persistence.xml} URL
* @param integration The Map of integration settings
* *
* @return The single persistence-unit descriptor * @return The single persistence-unit descriptor
*/ */
@SuppressWarnings("removal") @SuppressWarnings("removal")
public static ParsedPersistenceXmlDescriptor locateIndividualPersistenceUnit(URL persistenceXmlUrl, Map<?,?> integration) { public ParsedPersistenceXmlDescriptor locateIndividualPersistenceUnit(URL persistenceXmlUrl) {
return locateIndividualPersistenceUnit( persistenceXmlUrl, PersistenceUnitTransactionType.RESOURCE_LOCAL, integration ); return locateIndividualPersistenceUnit( persistenceXmlUrl, PersistenceUnitTransactionType.RESOURCE_LOCAL );
} }
/** /**
@ -87,25 +145,17 @@ public class PersistenceXmlParser {
* *
* @param persistenceXmlUrl The {@code persistence.xml} URL * @param persistenceXmlUrl The {@code persistence.xml} URL
* @param transactionType The specific PersistenceUnitTransactionType to incorporate into the persistence-unit descriptor * @param transactionType The specific PersistenceUnitTransactionType to incorporate into the persistence-unit descriptor
* @param integration The Map of integration settings
* *
* @return The single persistence-unit descriptor * @return The single persistence-unit descriptor
*/ */
public static ParsedPersistenceXmlDescriptor locateIndividualPersistenceUnit( public ParsedPersistenceXmlDescriptor locateIndividualPersistenceUnit(
URL persistenceXmlUrl, URL persistenceXmlUrl,
@SuppressWarnings("removal") @SuppressWarnings("removal")
PersistenceUnitTransactionType transactionType, PersistenceUnitTransactionType transactionType) {
Map<?,?> integration) { Map<String, ParsedPersistenceXmlDescriptor> persistenceUnits = new HashMap<>();
final PersistenceXmlParser parser = new PersistenceXmlParser( parsePersistenceXml( persistenceUnits, persistenceXmlUrl, transactionType );
fromConfigSettings( integration ), assert persistenceUnits.size() == 1;
transactionType return persistenceUnits.values().iterator().next();
);
parser.parsePersistenceXml( persistenceXmlUrl, integration );
assert parser.persistenceUnits.size() == 1;
return parser.persistenceUnits.values().iterator().next();
} }
/** /**
@ -116,22 +166,9 @@ public class PersistenceXmlParser {
* *
* @return The matching persistence-unit descriptor * @return The matching persistence-unit descriptor
*/ */
public static ParsedPersistenceXmlDescriptor locateNamedPersistenceUnit(URL persistenceXmlUrl, String name) {
return locateNamedPersistenceUnit( persistenceXmlUrl, name, Collections.emptyMap() );
}
/**
* Parse a specific {@code persistence.xml} and return the descriptor for the persistence-unit with matching name
*
* @param persistenceXmlUrl The {@code persistence.xml} URL
* @param name The PU name to match
* @param integration The Map of integration settings
*
* @return The matching persistence-unit descriptor
*/
@SuppressWarnings("removal") @SuppressWarnings("removal")
public static ParsedPersistenceXmlDescriptor locateNamedPersistenceUnit(URL persistenceXmlUrl, String name, Map<?,?> integration) { public ParsedPersistenceXmlDescriptor locateNamedPersistenceUnit(URL persistenceXmlUrl, String name) {
return locateNamedPersistenceUnit( persistenceXmlUrl, name, PersistenceUnitTransactionType.RESOURCE_LOCAL, integration ); return locateNamedPersistenceUnit( persistenceXmlUrl, name, PersistenceUnitTransactionType.RESOURCE_LOCAL );
} }
/** /**
@ -140,39 +177,20 @@ public class PersistenceXmlParser {
* @param persistenceXmlUrl The {@code persistence.xml} URL * @param persistenceXmlUrl The {@code persistence.xml} URL
* @param name The PU name to match * @param name The PU name to match
* @param transactionType The specific PersistenceUnitTransactionType to incorporate into the persistence-unit descriptor * @param transactionType The specific PersistenceUnitTransactionType to incorporate into the persistence-unit descriptor
* @param integration The Map of integration settings
* *
* @return The matching persistence-unit descriptor * @return The matching persistence-unit descriptor
*/ */
public static ParsedPersistenceXmlDescriptor locateNamedPersistenceUnit( public ParsedPersistenceXmlDescriptor locateNamedPersistenceUnit(
URL persistenceXmlUrl, URL persistenceXmlUrl,
String name, String name,
@SuppressWarnings("removal") @SuppressWarnings("removal")
PersistenceUnitTransactionType transactionType, PersistenceUnitTransactionType transactionType) {
Map<?,?> integration) {
assert StringHelper.isNotEmpty( name ); assert StringHelper.isNotEmpty( name );
final PersistenceXmlParser parser = new PersistenceXmlParser( Map<String, ParsedPersistenceXmlDescriptor> persistenceUnits = new HashMap<>();
fromConfigSettings( integration ), parsePersistenceXml( persistenceUnits, persistenceXmlUrl, transactionType );
transactionType assert persistenceUnits.containsKey( name );
); return persistenceUnits.get( name );
parser.parsePersistenceXml( persistenceXmlUrl, integration );
assert parser.persistenceUnits.containsKey( name );
return parser.persistenceUnits.get( name );
}
/**
* Intended only for use by Hibernate tests!
* <p>
* Parses a specific persistence.xml file...
*/
@SuppressWarnings("removal")
public static Map<String, ParsedPersistenceXmlDescriptor> parse(
URL persistenceXmlUrl,
PersistenceUnitTransactionType transactionType) {
return parse( persistenceXmlUrl, transactionType, Collections.emptyMap() );
} }
/** /**
@ -181,69 +199,30 @@ public class PersistenceXmlParser {
* *
* @param persistenceXmlUrl The URL of the {@code persistence.xml} to parse * @param persistenceXmlUrl The URL of the {@code persistence.xml} to parse
* @param transactionType The specific PersistenceUnitTransactionType to incorporate into the persistence-unit descriptor * @param transactionType The specific PersistenceUnitTransactionType to incorporate into the persistence-unit descriptor
* @param integration The Map of integration settings
* *
* @return Map of persistence-unit descriptors keyed by the PU name * @return Map of persistence-unit descriptors keyed by the PU name
*/ */
public static Map<String, ParsedPersistenceXmlDescriptor> parse( public Map<String, ParsedPersistenceXmlDescriptor> parse(
URL persistenceXmlUrl, URL persistenceXmlUrl,
@SuppressWarnings("removal") @SuppressWarnings("removal")
PersistenceUnitTransactionType transactionType, PersistenceUnitTransactionType transactionType) {
Map<?,?> integration) { Map<String, ParsedPersistenceXmlDescriptor> persistenceUnits = new HashMap<>();
PersistenceXmlParser parser = new PersistenceXmlParser( parsePersistenceXml( persistenceUnits, persistenceXmlUrl, transactionType );
fromConfigSettings( integration ), return persistenceUnits;
transactionType
);
parser.parsePersistenceXml( persistenceXmlUrl, integration );
return parser.persistenceUnits;
} }
private final ClassLoaderService classLoaderService; private void parsePersistenceXml(Map<String, ParsedPersistenceXmlDescriptor> persistenceUnits,
List<URL> xmlUrls,
@SuppressWarnings("removal") @SuppressWarnings("removal")
private final PersistenceUnitTransactionType defaultTransactionType; PersistenceUnitTransactionType defaultTransactionType) {
private final Map<String, ParsedPersistenceXmlDescriptor> persistenceUnits;
@SuppressWarnings("removal")
protected PersistenceXmlParser(ClassLoaderService classLoaderService, PersistenceUnitTransactionType defaultTransactionType) {
this.classLoaderService = classLoaderService;
this.defaultTransactionType = defaultTransactionType;
this.persistenceUnits = new ConcurrentHashMap<>();
}
private static ClassLoaderServiceImpl fromConfigSettings(Map<?,?> configValues) {
final List<ClassLoader> providedClassLoaders = new ArrayList<>();
@SuppressWarnings("unchecked")
final Collection<ClassLoader> classLoaders =
(Collection<ClassLoader>) configValues.get( AvailableSettings.CLASSLOADERS );
if ( classLoaders != null ) {
providedClassLoaders.addAll( classLoaders );
}
return new ClassLoaderServiceImpl(
providedClassLoaders,
TcclLookupPrecedence.from( configValues, TcclLookupPrecedence.AFTER )
);
}
private void doResolve(Map<?,?> integration) {
final List<URL> xmlUrls = classLoaderService.locateResources( "META-INF/persistence.xml" );
if ( xmlUrls.isEmpty() ) {
LOG.unableToFindPersistenceXmlInClasspath();
}
else {
parsePersistenceXml( xmlUrls, integration );
}
}
private void parsePersistenceXml(List<URL> xmlUrls, Map<?,?> integration) {
for ( URL xmlUrl : xmlUrls ) { for ( URL xmlUrl : xmlUrls ) {
parsePersistenceXml( xmlUrl, integration ); parsePersistenceXml( persistenceUnits, xmlUrl, defaultTransactionType );
} }
} }
protected void parsePersistenceXml(URL xmlUrl, Map<?,?> integration) { @SuppressWarnings("removal")
protected void parsePersistenceXml(Map<String, ParsedPersistenceXmlDescriptor> persistenceUnits,
URL xmlUrl, PersistenceUnitTransactionType defaultTransactionType) {
if ( LOG.isTraceEnabled() ) { if ( LOG.isTraceEnabled() ) {
LOG.tracef( "Attempting to parse persistence.xml file : %s", xmlUrl.toExternalForm() ); LOG.tracef( "Attempting to parse persistence.xml file : %s", xmlUrl.toExternalForm() );
} }
@ -267,7 +246,7 @@ public class PersistenceXmlParser {
// per JPA spec, any settings passed in to PersistenceProvider bootstrap methods should override // per JPA spec, any settings passed in to PersistenceProvider bootstrap methods should override
// values found in persistence.xml // values found in persistence.xml
applyIntegrationOverrides( integration, persistenceUnitDescriptor ); applyIntegrationOverrides( integration, defaultTransactionType, persistenceUnitDescriptor );
persistenceUnits.put( persistenceUnitDescriptor.getName(), persistenceUnitDescriptor ); persistenceUnits.put( persistenceUnitDescriptor.getName(), persistenceUnitDescriptor );
} }
@ -316,8 +295,9 @@ public class PersistenceXmlParser {
return false; return false;
} }
@SuppressWarnings("deprecation") @SuppressWarnings({"deprecation", "removal"})
private void applyIntegrationOverrides(Map<?,?> integration, ParsedPersistenceXmlDescriptor persistenceUnitDescriptor) { private void applyIntegrationOverrides(Map<?,?> integration, PersistenceUnitTransactionType defaultTransactionType,
ParsedPersistenceXmlDescriptor persistenceUnitDescriptor) {
if ( integration.containsKey( AvailableSettings.JAKARTA_PERSISTENCE_PROVIDER ) ) { if ( integration.containsKey( AvailableSettings.JAKARTA_PERSISTENCE_PROVIDER ) ) {
persistenceUnitDescriptor.setProviderClassName( (String) integration.get( AvailableSettings.JAKARTA_PERSISTENCE_PROVIDER ) ); persistenceUnitDescriptor.setProviderClassName( (String) integration.get( AvailableSettings.JAKARTA_PERSISTENCE_PROVIDER ) );
} }
@ -364,14 +344,15 @@ public class PersistenceXmlParser {
persistenceUnitDescriptor.setNonJtaDataSource( integration.get( AvailableSettings.JAKARTA_NON_JTA_DATASOURCE ) ); persistenceUnitDescriptor.setNonJtaDataSource( integration.get( AvailableSettings.JAKARTA_NON_JTA_DATASOURCE ) );
} }
applyTransactionTypeOverride( persistenceUnitDescriptor ); applyTransactionTypeOverride( persistenceUnitDescriptor, defaultTransactionType );
final Properties properties = persistenceUnitDescriptor.getProperties(); final Properties properties = persistenceUnitDescriptor.getProperties();
ConfigurationHelper.overrideProperties( properties, integration ); ConfigurationHelper.overrideProperties( properties, integration );
} }
@SuppressWarnings("removal") @SuppressWarnings("removal")
private void applyTransactionTypeOverride(ParsedPersistenceXmlDescriptor persistenceUnitDescriptor) { private void applyTransactionTypeOverride(ParsedPersistenceXmlDescriptor persistenceUnitDescriptor,
PersistenceUnitTransactionType defaultTransactionType) {
// if transaction type is set already, use that value // if transaction type is set already, use that value
if ( persistenceUnitDescriptor.getTransactionType() == null ) { if ( persistenceUnitDescriptor.getTransactionType() == null ) {
// else // else

View File

@ -67,7 +67,7 @@ public final class Bootstrap {
PersistenceUnitTransactionType transactionType, PersistenceUnitTransactionType transactionType,
Map integration) { Map integration) {
return new EntityManagerFactoryBuilderImpl( return new EntityManagerFactoryBuilderImpl(
PersistenceXmlParser.parse( persistenceXmlUrl, transactionType, integration ).get( persistenceUnitName ), PersistenceXmlParser.create( integration ).parse( persistenceXmlUrl, transactionType ).get( persistenceUnitName ),
integration integration
); );
} }

View File

@ -71,7 +71,8 @@ public class JakartaXmlSmokeTests {
public void testLoadingPersistenceXml(ServiceRegistryScope scope) { public void testLoadingPersistenceXml(ServiceRegistryScope scope) {
final ClassLoaderService cls = scope.getRegistry().getService( ClassLoaderService.class ); final ClassLoaderService cls = scope.getRegistry().getService( ClassLoaderService.class );
final URL url = cls.locateResource( "xml/jakarta/simple/persistence.xml" ); final URL url = cls.locateResource( "xml/jakarta/simple/persistence.xml" );
final ParsedPersistenceXmlDescriptor descriptor = PersistenceXmlParser.locateIndividualPersistenceUnit( url, Collections.emptyMap() ); final ParsedPersistenceXmlDescriptor descriptor = PersistenceXmlParser.create()
.locateIndividualPersistenceUnit( url );
assertThat( descriptor.getName() ).isEqualTo( "defaultpar" ); assertThat( descriptor.getName() ).isEqualTo( "defaultpar" );
assertThat( descriptor.getManagedClassNames() ).contains( "org.hibernate.jpa.test.pack.defaultpar.Lighter" ); assertThat( descriptor.getManagedClassNames() ).contains( "org.hibernate.jpa.test.pack.defaultpar.Lighter" );
} }

View File

@ -63,7 +63,7 @@ public class DuplicatePersistenceUnitNameTest extends BaseUnitTestCase {
public void testDuplicatePersistenceUnitNameLogAWarnMessage() { public void testDuplicatePersistenceUnitNameLogAWarnMessage() {
final Map<String, Object> properties = new HashMap<String, Object>(); final Map<String, Object> properties = new HashMap<String, Object>();
properties.put( AvailableSettings.CLASSLOADERS, Arrays.asList( new TestClassLoader() ) ); properties.put( AvailableSettings.CLASSLOADERS, Arrays.asList( new TestClassLoader() ) );
PersistenceXmlParser.locatePersistenceUnits( properties ); PersistenceXmlParser.create( properties ).locatePersistenceUnits();
assertTrue( "The warn HHH015018 has not been logged ", triggerable.wasTriggered() ); assertTrue( "The warn HHH015018 has not been logged ", triggerable.wasTriggered() );
} }

View File

@ -51,8 +51,8 @@ public class ExcludeUnlistedClassesTest extends BaseUnitTestCase {
final Map<String, Object> properties = new HashMap<String, Object>(); final Map<String, Object> properties = new HashMap<String, Object>();
properties.put( AvailableSettings.CLASSLOADERS, Arrays.asList( new TestClassLoader() ) ); properties.put( AvailableSettings.CLASSLOADERS, Arrays.asList( new TestClassLoader() ) );
final List<ParsedPersistenceXmlDescriptor> parsedDescriptors = PersistenceXmlParser.locatePersistenceUnits( final List<ParsedPersistenceXmlDescriptor> parsedDescriptors = PersistenceXmlParser.create( properties )
properties ); .locatePersistenceUnits();
doTest( parsedDescriptors, "ExcludeUnlistedClassesTest1", false ); doTest( parsedDescriptors, "ExcludeUnlistedClassesTest1", false );
doTest( parsedDescriptors, "ExcludeUnlistedClassesTest2", true ); doTest( parsedDescriptors, "ExcludeUnlistedClassesTest2", true );