Skip to content

Commit abded22

Browse files
committed
HHH-19887 Construct FormatMapper with context for AggregatedClassLoader access
1 parent 8961238 commit abded22

File tree

5 files changed

+187
-37
lines changed

5 files changed

+187
-37
lines changed

hibernate-core/src/main/java/org/hibernate/boot/internal/SessionFactoryOptionsBuilder.java

Lines changed: 65 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.hibernate.boot.internal;
88

99
import java.lang.reflect.Constructor;
10+
import java.lang.reflect.InvocationTargetException;
1011
import java.time.ZoneId;
1112
import java.util.ArrayList;
1213
import java.util.Collections;
@@ -70,6 +71,7 @@
7071
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
7172
import org.hibernate.stat.Statistics;
7273
import org.hibernate.type.format.FormatMapper;
74+
import org.hibernate.type.format.FormatMapperCreationContext;
7375
import org.hibernate.type.format.jackson.JacksonIntegration;
7476
import org.hibernate.type.format.jakartajson.JakartaJsonIntegration;
7577
import org.hibernate.type.format.jaxb.JaxbXmlFormatMapper;
@@ -313,13 +315,23 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
313315
AvailableSettings.JPA_VALIDATION_FACTORY,
314316
configurationSettings.get( AvailableSettings.JAKARTA_VALIDATION_FACTORY )
315317
);
316-
this.jsonFormatMapper = determineJsonFormatMapper(
318+
319+
final var formatMapperCreationContext = new FormatMapperCreationContext() {
320+
@Override
321+
public BootstrapContext getBootstrapContext() {
322+
return context;
323+
}
324+
};
325+
jsonFormatMapper = jsonFormatMapper(
317326
configurationSettings.get( AvailableSettings.JSON_FORMAT_MAPPER ),
318-
strategySelector
327+
strategySelector,
328+
formatMapperCreationContext
319329
);
320-
this.xmlFormatMapper = determineXmlFormatMapper(
330+
331+
xmlFormatMapper = xmlFormatMapper(
321332
configurationSettings.get( AvailableSettings.XML_FORMAT_MAPPER ),
322-
strategySelector
333+
strategySelector,
334+
formatMapperCreationContext
323335
);
324336

325337
this.sessionFactoryName = (String) configurationSettings.get( SESSION_FACTORY_NAME );
@@ -827,7 +839,7 @@ private static Supplier<? extends Interceptor> interceptorSupplier(Class<? exten
827839
}
828840

829841
private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
830-
Map<String,Object> configurationSettings,
842+
Map<String, Object> configurationSettings,
831843
StandardServiceRegistry serviceRegistry) {
832844
final PhysicalConnectionHandlingMode specifiedHandlingMode = PhysicalConnectionHandlingMode.interpret(
833845
configurationSettings.get( CONNECTION_HANDLING )
@@ -840,36 +852,62 @@ private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
840852
return serviceRegistry.requireService( TransactionCoordinatorBuilder.class ).getDefaultConnectionHandlingMode();
841853
}
842854

843-
private static FormatMapper determineJsonFormatMapper(Object setting, StrategySelector strategySelector) {
844-
return strategySelector.resolveDefaultableStrategy(
845-
FormatMapper.class,
855+
private static FormatMapper jsonFormatMapper(Object setting, StrategySelector selector, FormatMapperCreationContext creationContext) {
856+
return formatMapper(
846857
setting,
847-
(Callable<FormatMapper>) () -> {
848-
final FormatMapper jsonJacksonFormatMapper = JacksonIntegration.getJsonJacksonFormatMapperOrNull();
849-
if (jsonJacksonFormatMapper != null) {
850-
return jsonJacksonFormatMapper;
851-
}
852-
else {
853-
return JakartaJsonIntegration.getJakartaJsonBFormatMapperOrNull();
854-
}
855-
}
858+
selector,
859+
() -> {
860+
final FormatMapper jsonJacksonFormatMapper = JacksonIntegration.getJsonJacksonFormatMapperOrNull( creationContext );
861+
return jsonJacksonFormatMapper != null
862+
? jsonJacksonFormatMapper
863+
: JakartaJsonIntegration.getJakartaJsonBFormatMapperOrNull();
864+
},
865+
creationContext
856866
);
857867
}
858868

859-
private static FormatMapper determineXmlFormatMapper(Object setting, StrategySelector strategySelector) {
860-
return strategySelector.resolveDefaultableStrategy(
861-
FormatMapper.class,
869+
private static FormatMapper xmlFormatMapper(Object setting, StrategySelector selector, FormatMapperCreationContext creationContext) {
870+
return formatMapper(
862871
setting,
863-
(Callable<FormatMapper>) () -> {
864-
final FormatMapper jacksonFormatMapper = JacksonIntegration.getXMLJacksonFormatMapperOrNull();
865-
if (jacksonFormatMapper != null) {
866-
return jacksonFormatMapper;
867-
}
868-
return new JaxbXmlFormatMapper();
869-
}
872+
selector,
873+
() -> {
874+
final FormatMapper jacksonFormatMapper = JacksonIntegration.getXMLJacksonFormatMapperOrNull( creationContext );
875+
return jacksonFormatMapper != null
876+
? jacksonFormatMapper
877+
: new JaxbXmlFormatMapper();
878+
},
879+
creationContext
870880
);
871881
}
872882

883+
private static FormatMapper formatMapper(Object setting, StrategySelector selector, Callable<FormatMapper> defaultResolver, FormatMapperCreationContext creationContext) {
884+
return selector.resolveStrategy( FormatMapper.class, setting, defaultResolver, strategyClass -> {
885+
try {
886+
final Constructor<? extends FormatMapper> creationContextConstructor =
887+
strategyClass.getDeclaredConstructor( FormatMapperCreationContext.class );
888+
return creationContextConstructor.newInstance( creationContext );
889+
}
890+
catch (NoSuchMethodException e) {
891+
// Ignore
892+
}
893+
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
894+
throw new StrategySelectionException(
895+
String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ),
896+
e
897+
);
898+
}
899+
try {
900+
return strategyClass.getDeclaredConstructor().newInstance();
901+
}
902+
catch (Exception e) {
903+
throw new StrategySelectionException(
904+
String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ),
905+
e
906+
);
907+
}
908+
} );
909+
}
910+
873911

874912
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
875913
// SessionFactoryOptionsState
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Hibernate, Relational Persistence for Idiomatic Java
3+
*
4+
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
5+
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
6+
*/
7+
package org.hibernate.type.format;
8+
9+
import org.hibernate.Incubating;
10+
import org.hibernate.boot.spi.BootstrapContext;
11+
12+
13+
/**
14+
* The creation context for {@link FormatMapper} that is passed as constructor argument to implementations.
15+
*/
16+
@Incubating
17+
public interface FormatMapperCreationContext {
18+
BootstrapContext getBootstrapContext();
19+
20+
}

hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonIntegration.java

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,17 @@
66
*/
77
package org.hibernate.type.format.jackson;
88

9+
import org.checkerframework.checker.nullness.qual.Nullable;
910
import org.hibernate.type.format.FormatMapper;
11+
import org.hibernate.type.format.FormatMapperCreationContext;
1012

1113
public final class JacksonIntegration {
1214

1315
// Implementation note: we rely on the following fields to be folded as constants
1416
// when GraalVM native image is initializing them.
1517
private static final boolean JACKSON_XML_AVAILABLE = ableToLoadJacksonXMLMapper();
1618
private static final boolean JACKSON_JSON_AVAILABLE = ableToLoadJacksonJSONMapper();
17-
private static final JacksonXmlFormatMapper XML_FORMAT_MAPPER = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper() : null;
18-
private static final JacksonJsonFormatMapper JSON_FORMAT_MAPPER = JACKSON_JSON_AVAILABLE ? new JacksonJsonFormatMapper() : null;
19+
1920

2021
private JacksonIntegration() {
2122
//To not be instantiated: static helpers only
@@ -29,12 +30,67 @@ private static boolean ableToLoadJacksonXMLMapper() {
2930
return canLoad( "com.fasterxml.jackson.dataformat.xml.XmlMapper" );
3031
}
3132

32-
public static FormatMapper getXMLJacksonFormatMapperOrNull() {
33-
return XML_FORMAT_MAPPER;
33+
/**
34+
* Checks that Jackson is available and that we have the Oracle OSON extension available
35+
* in the classpath.
36+
* @return true if we can load the OSON support, false otherwise.
37+
*/
38+
private static boolean ableToLoadJacksonOSONFactory() {
39+
return ableToLoadJacksonJSONMapper() &&
40+
canLoad( "oracle.jdbc.provider.oson.OsonFactory" );
41+
}
42+
43+
public static @Nullable FormatMapper getXMLJacksonFormatMapperOrNull(FormatMapperCreationContext creationContext) {
44+
return JACKSON_XML_AVAILABLE
45+
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonXmlFormatMapper", creationContext )
46+
: null;
3447
}
3548

36-
public static FormatMapper getJsonJacksonFormatMapperOrNull() {
37-
return JSON_FORMAT_MAPPER;
49+
public static @Nullable FormatMapper getJsonJacksonFormatMapperOrNull(FormatMapperCreationContext creationContext) {
50+
return JACKSON_JSON_AVAILABLE
51+
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonJsonFormatMapper", creationContext )
52+
: null;
53+
}
54+
55+
public static @Nullable FormatMapper getXMLJacksonFormatMapperOrNull(boolean legacyFormat) {
56+
if ( JACKSON_XML_AVAILABLE ) {
57+
try {
58+
final Class<?> formatMapperClass = JacksonIntegration.class.getClassLoader()
59+
.loadClass( "org.hibernate.type.format.jackson.JacksonXmlFormatMapper" );
60+
return (FormatMapper) formatMapperClass.getDeclaredConstructor( boolean.class )
61+
.newInstance( legacyFormat );
62+
}
63+
catch (Exception e) {
64+
throw new RuntimeException( "Couldn't instantiate Jackson XML FormatMapper", e );
65+
}
66+
}
67+
return null;
68+
}
69+
70+
public static @Nullable FormatMapper getJsonJacksonFormatMapperOrNull() {
71+
return JACKSON_JSON_AVAILABLE
72+
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonJsonFormatMapper", null )
73+
: null;
74+
}
75+
76+
private static FormatMapper createFormatMapper(String className, @Nullable FormatMapperCreationContext creationContext) {
77+
try {
78+
if ( creationContext == null ) {
79+
final Class<?> formatMapperClass = JacksonIntegration.class.getClassLoader()
80+
.loadClass( className );
81+
return (FormatMapper) formatMapperClass.getDeclaredConstructor().newInstance();
82+
}
83+
else {
84+
return (FormatMapper) creationContext.getBootstrapContext()
85+
.getClassLoaderAccess()
86+
.classForName( className )
87+
.getDeclaredConstructor( FormatMapperCreationContext.class )
88+
.newInstance( creationContext );
89+
}
90+
}
91+
catch (Exception e) {
92+
throw new RuntimeException( "Couldn't instantiate Jackson FormatMapper", e );
93+
}
3894
}
3995

4096
private static boolean canLoad(String name) {

hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonJsonFormatMapper.java

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@
66
*/
77
package org.hibernate.type.format.jackson;
88

9+
import com.fasterxml.jackson.databind.Module;
10+
11+
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
912
import org.hibernate.type.format.AbstractJsonFormatMapper;
1013

1114
import com.fasterxml.jackson.core.JsonProcessingException;
1215
import com.fasterxml.jackson.databind.ObjectMapper;
16+
import org.hibernate.type.format.FormatMapperCreationContext;
1317

1418
import java.lang.reflect.Type;
19+
import java.util.List;
1520

1621
/**
1722
* @author Christian Beikov
@@ -24,7 +29,20 @@ public final class JacksonJsonFormatMapper extends AbstractJsonFormatMapper {
2429
private final ObjectMapper objectMapper;
2530

2631
public JacksonJsonFormatMapper() {
27-
this(new ObjectMapper().findAndRegisterModules());
32+
this( ObjectMapper.findModules( JacksonJsonFormatMapper.class.getClassLoader() ) );
33+
}
34+
35+
public JacksonJsonFormatMapper(FormatMapperCreationContext creationContext) {
36+
this(
37+
creationContext.getBootstrapContext()
38+
.getServiceRegistry()
39+
.requireService( ClassLoaderService.class )
40+
.<List<Module>>workWithClassLoader( ObjectMapper::findModules )
41+
);
42+
}
43+
44+
private JacksonJsonFormatMapper(List<Module> modules) {
45+
this(new ObjectMapper().registerModules( modules ));
2846
}
2947

3048
public JacksonJsonFormatMapper(ObjectMapper objectMapper) {

hibernate-core/src/main/java/org/hibernate/type/format/jackson/JacksonXmlFormatMapper.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88

99
import java.io.IOException;
1010
import java.lang.reflect.Type;
11+
import java.util.List;
1112
import java.util.ArrayList;
1213

14+
import com.fasterxml.jackson.databind.Module;
15+
16+
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
1317
import org.hibernate.type.descriptor.WrapperOptions;
1418
import org.hibernate.type.descriptor.java.JavaType;
1519
import org.hibernate.type.format.FormatMapper;
@@ -24,6 +28,7 @@
2428
import com.fasterxml.jackson.databind.module.SimpleModule;
2529
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
2630
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
31+
import org.hibernate.type.format.FormatMapperCreationContext;
2732

2833
/**
2934
* @author Christian Beikov
@@ -35,17 +40,30 @@ public final class JacksonXmlFormatMapper implements FormatMapper {
3540
private final ObjectMapper objectMapper;
3641

3742
public JacksonXmlFormatMapper() {
38-
this( createXmlMapper() );
43+
this(
44+
createXmlMapper( XmlMapper.findModules( JacksonXmlFormatMapper.class.getClassLoader() ) )
45+
);
46+
}
47+
48+
public JacksonXmlFormatMapper(FormatMapperCreationContext creationContext) {
49+
this(
50+
createXmlMapper(
51+
creationContext.getBootstrapContext()
52+
.getServiceRegistry()
53+
.requireService( ClassLoaderService.class )
54+
.<List<Module>>workWithClassLoader( XmlMapper::findModules )
55+
)
56+
);
3957
}
4058

4159
public JacksonXmlFormatMapper(ObjectMapper objectMapper) {
4260
this.objectMapper = objectMapper;
4361
}
4462

45-
private static XmlMapper createXmlMapper() {
63+
private static XmlMapper createXmlMapper(List<Module> modules) {
4664
final XmlMapper xmlMapper = new XmlMapper();
4765
// needed to automatically find and register Jackson's jsr310 module for java.time support
48-
xmlMapper.findAndRegisterModules();
66+
xmlMapper.registerModules( modules );
4967
xmlMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
5068
xmlMapper.enable( ToXmlGenerator.Feature.WRITE_NULLS_AS_XSI_NIL );
5169
// Workaround for null vs empty string handling inside arrays,

0 commit comments

Comments
 (0)