Skip to content

Commit ecf2bc6

Browse files
committed
HHH-19887 Construct FormatMapper with context for AggregatedClassLoader access
1 parent 49c9369 commit ecf2bc6

File tree

6 files changed

+187
-29
lines changed

6 files changed

+187
-29
lines changed

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

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
package org.hibernate.boot.internal;
66

77
import java.lang.reflect.Constructor;
8+
import java.lang.reflect.InvocationTargetException;
89
import java.time.ZoneId;
910
import java.util.ArrayList;
1011
import java.util.Collections;
@@ -80,6 +81,7 @@
8081
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
8182
import org.hibernate.stat.Statistics;
8283
import org.hibernate.type.format.FormatMapper;
84+
import org.hibernate.type.format.FormatMapperCreationContext;
8385
import org.hibernate.type.format.jaxb.JaxbXmlFormatMapper;
8486

8587
import jakarta.persistence.criteria.Nulls;
@@ -294,18 +296,26 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
294296
settings.get( JAKARTA_VALIDATION_FACTORY )
295297
);
296298

299+
final var formatMapperCreationContext = new FormatMapperCreationContext() {
300+
@Override
301+
public BootstrapContext getBootstrapContext() {
302+
return context;
303+
}
304+
};
297305
jsonFormatMapper = jsonFormatMapper(
298306
settings.get( JSON_FORMAT_MAPPER ),
299307
!getBoolean( ORACLE_OSON_DISABLED, settings),
300-
strategySelector
308+
strategySelector,
309+
formatMapperCreationContext
301310
);
302311

303312
xmlFormatMapper = xmlFormatMapper(
304313
settings.get( XML_FORMAT_MAPPER ),
305314
strategySelector,
306315
xmlFormatMapperLegacyFormatEnabled =
307316
context.getMetadataBuildingOptions()
308-
.isXmlFormatMapperLegacyFormatEnabled()
317+
.isXmlFormatMapperLegacyFormatEnabled(),
318+
formatMapperCreationContext
309319
);
310320

311321
sessionFactoryName = (String) settings.get( SESSION_FACTORY_NAME );
@@ -852,39 +862,64 @@ private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
852862
.getDefaultConnectionHandlingMode();
853863
}
854864

855-
private static FormatMapper jsonFormatMapper(Object setting, boolean osonExtensionEnabled, StrategySelector selector) {
865+
private static FormatMapper jsonFormatMapper(Object setting, boolean osonExtensionEnabled, StrategySelector selector, FormatMapperCreationContext creationContext) {
856866
return formatMapper(
857867
setting,
858868
selector,
859869
() -> {
860870
// Prefer the OSON Jackson FormatMapper by default if available
861871
final FormatMapper jsonJacksonFormatMapper =
862872
osonExtensionEnabled && isJacksonOsonExtensionAvailable()
863-
? getOsonJacksonFormatMapperOrNull()
864-
: getJsonJacksonFormatMapperOrNull();
873+
? getOsonJacksonFormatMapperOrNull( creationContext )
874+
: getJsonJacksonFormatMapperOrNull( creationContext );
865875
return jsonJacksonFormatMapper != null
866876
? jsonJacksonFormatMapper
867877
: getJakartaJsonBFormatMapperOrNull();
868-
}
878+
},
879+
creationContext
869880
);
870881
}
871882

872-
private static FormatMapper xmlFormatMapper(Object setting, StrategySelector selector, boolean legacyFormat) {
883+
private static FormatMapper xmlFormatMapper(Object setting, StrategySelector selector, boolean legacyFormat, FormatMapperCreationContext creationContext) {
873884
return formatMapper(
874885
setting,
875886
selector,
876887
() -> {
877-
final FormatMapper jacksonFormatMapper =
878-
getXMLJacksonFormatMapperOrNull( legacyFormat );
888+
final FormatMapper jacksonFormatMapper = getXMLJacksonFormatMapperOrNull( creationContext );
879889
return jacksonFormatMapper != null
880890
? jacksonFormatMapper
881891
: new JaxbXmlFormatMapper( legacyFormat );
882-
}
892+
},
893+
creationContext
883894
);
884895
}
885896

886-
private static FormatMapper formatMapper(Object setting, StrategySelector selector, Callable<FormatMapper> defaultResolver) {
887-
return selector.resolveDefaultableStrategy( FormatMapper.class, setting, defaultResolver );
897+
private static FormatMapper formatMapper(Object setting, StrategySelector selector, Callable<FormatMapper> defaultResolver, FormatMapperCreationContext creationContext) {
898+
return selector.resolveStrategy( FormatMapper.class, setting, defaultResolver, strategyClass -> {
899+
try {
900+
final Constructor<? extends FormatMapper> creationContextConstructor =
901+
strategyClass.getDeclaredConstructor( FormatMapperCreationContext.class );
902+
return creationContextConstructor.newInstance( creationContext );
903+
}
904+
catch (NoSuchMethodException e) {
905+
// Ignore
906+
}
907+
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
908+
throw new StrategySelectionException(
909+
String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ),
910+
e
911+
);
912+
}
913+
try {
914+
return strategyClass.getDeclaredConstructor().newInstance();
915+
}
916+
catch (Exception e) {
917+
throw new StrategySelectionException(
918+
String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ),
919+
e
920+
);
921+
}
922+
} );
888923
}
889924

890925

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.type.format;
6+
7+
import org.hibernate.Incubating;
8+
import org.hibernate.boot.spi.BootstrapContext;
9+
10+
11+
/**
12+
* The creation context for {@link FormatMapper} that is passed as constructor argument to implementations.
13+
*/
14+
@Incubating
15+
public interface FormatMapperCreationContext {
16+
BootstrapContext getBootstrapContext();
17+
18+
}

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

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
*/
55
package org.hibernate.type.format.jackson;
66

7+
import org.checkerframework.checker.nullness.qual.Nullable;
78
import org.hibernate.type.format.FormatMapper;
9+
import org.hibernate.type.format.FormatMapperCreationContext;
810

911
public final class JacksonIntegration {
1012

@@ -13,10 +15,6 @@ public final class JacksonIntegration {
1315
private static final boolean JACKSON_XML_AVAILABLE = ableToLoadJacksonXMLMapper();
1416
private static final boolean JACKSON_JSON_AVAILABLE = ableToLoadJacksonJSONMapper();
1517
private static final boolean JACKSON_OSON_AVAILABLE = ableToLoadJacksonOSONFactory();
16-
private static final FormatMapper XML_FORMAT_MAPPER = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper() : null;
17-
private static final FormatMapper XML_FORMAT_MAPPER_PORTABLE = JACKSON_XML_AVAILABLE ? new JacksonXmlFormatMapper( false ) : null;
18-
private static final FormatMapper JSON_FORMAT_MAPPER = JACKSON_JSON_AVAILABLE ? new JacksonJsonFormatMapper() : null;
19-
private static final FormatMapper OSON_FORMAT_MAPPER = JACKSON_OSON_AVAILABLE ? new JacksonOsonFormatMapper() : null;
2018

2119

2220
private JacksonIntegration() {
@@ -41,15 +39,67 @@ private static boolean ableToLoadJacksonOSONFactory() {
4139
canLoad( "oracle.jdbc.provider.oson.OsonFactory" );
4240
}
4341

44-
public static FormatMapper getXMLJacksonFormatMapperOrNull(boolean legacyFormat) {
45-
return legacyFormat ? XML_FORMAT_MAPPER : XML_FORMAT_MAPPER_PORTABLE;
42+
public static @Nullable FormatMapper getXMLJacksonFormatMapperOrNull(FormatMapperCreationContext creationContext) {
43+
return JACKSON_XML_AVAILABLE
44+
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonXmlFormatMapper", creationContext )
45+
: null;
4646
}
4747

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

55105
/**

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66

77
import com.fasterxml.jackson.core.JsonGenerator;
88
import com.fasterxml.jackson.core.JsonParser;
9+
import com.fasterxml.jackson.databind.Module;
910
import org.hibernate.type.descriptor.WrapperOptions;
1011
import org.hibernate.type.descriptor.java.JavaType;
1112
import org.hibernate.type.format.AbstractJsonFormatMapper;
1213

1314
import com.fasterxml.jackson.core.JsonProcessingException;
1415
import com.fasterxml.jackson.databind.ObjectMapper;
16+
import org.hibernate.type.format.FormatMapperCreationContext;
1517

1618
import java.io.IOException;
1719
import java.lang.reflect.Type;
20+
import java.util.List;
1821

1922
/**
2023
* @author Christian Beikov
@@ -27,7 +30,19 @@ public final class JacksonJsonFormatMapper extends AbstractJsonFormatMapper {
2730
private final ObjectMapper objectMapper;
2831

2932
public JacksonJsonFormatMapper() {
30-
this( new ObjectMapper().findAndRegisterModules() );
33+
this( ObjectMapper.findModules( JacksonJsonFormatMapper.class.getClassLoader() ) );
34+
}
35+
36+
public JacksonJsonFormatMapper(FormatMapperCreationContext creationContext) {
37+
this(
38+
creationContext.getBootstrapContext()
39+
.getClassLoaderService()
40+
.<List<Module>>workWithClassLoader( ObjectMapper::findModules )
41+
);
42+
}
43+
44+
private JacksonJsonFormatMapper(List<Module> modules) {
45+
this( new ObjectMapper().registerModules( modules ) );
3146
}
3247

3348
public JacksonJsonFormatMapper(ObjectMapper objectMapper) {

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,18 @@
77
import com.fasterxml.jackson.core.JsonGenerator;
88
import com.fasterxml.jackson.core.JsonParser;
99
import com.fasterxml.jackson.core.JsonProcessingException;
10+
import com.fasterxml.jackson.databind.Module;
1011
import com.fasterxml.jackson.databind.ObjectMapper;
1112
import com.fasterxml.jackson.databind.SerializationFeature;
1213
import oracle.jdbc.provider.oson.OsonModule;
1314
import org.hibernate.type.descriptor.WrapperOptions;
1415
import org.hibernate.type.descriptor.java.JavaType;
1516
import org.hibernate.type.format.AbstractJsonFormatMapper;
17+
import org.hibernate.type.format.FormatMapperCreationContext;
1618

1719
import java.io.IOException;
1820
import java.lang.reflect.Type;
21+
import java.util.List;
1922

2023

2124
/**
@@ -34,7 +37,19 @@ public final class JacksonOsonFormatMapper extends AbstractJsonFormatMapper {
3437
* Creates a new JacksonOsonFormatMapper
3538
*/
3639
public JacksonOsonFormatMapper() {
37-
this( new ObjectMapper().findAndRegisterModules() );
40+
this( ObjectMapper.findModules( JacksonJsonFormatMapper.class.getClassLoader() ) );
41+
}
42+
43+
public JacksonOsonFormatMapper(FormatMapperCreationContext creationContext) {
44+
this(
45+
creationContext.getBootstrapContext()
46+
.getClassLoaderService()
47+
.<List<Module>>workWithClassLoader( ObjectMapper::findModules )
48+
);
49+
}
50+
51+
private JacksonOsonFormatMapper(List<Module> modules) {
52+
this( new ObjectMapper().registerModules( modules ) );
3853
}
3954

4055
public JacksonOsonFormatMapper(ObjectMapper objectMapper) {

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

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.util.Map;
1717

1818
import com.fasterxml.jackson.core.JsonGenerator;
19+
import com.fasterxml.jackson.databind.Module;
1920
import com.fasterxml.jackson.databind.SerializerProvider;
2021
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
2122
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
@@ -36,6 +37,7 @@
3637
import com.fasterxml.jackson.databind.module.SimpleModule;
3738
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
3839
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
40+
import org.hibernate.type.format.FormatMapperCreationContext;
3941
import org.hibernate.type.internal.ParameterizedTypeImpl;
4042

4143
/**
@@ -45,7 +47,7 @@
4547
public final class JacksonXmlFormatMapper implements FormatMapper {
4648

4749
public static final String SHORT_NAME = "jackson-xml";
48-
private boolean legacyFormat;
50+
private final boolean legacyFormat;
4951

5052
private final ObjectMapper objectMapper;
5153

@@ -54,18 +56,41 @@ public JacksonXmlFormatMapper() {
5456
}
5557

5658
public JacksonXmlFormatMapper(boolean legacyFormat) {
57-
this( createXmlMapper( legacyFormat ) );
58-
this.legacyFormat = legacyFormat;
59+
this(
60+
createXmlMapper( XmlMapper.findModules( JacksonXmlFormatMapper.class.getClassLoader() ), legacyFormat ),
61+
legacyFormat
62+
);
63+
}
64+
65+
public JacksonXmlFormatMapper(FormatMapperCreationContext creationContext) {
66+
this(
67+
createXmlMapper(
68+
creationContext.getBootstrapContext()
69+
.getClassLoaderService()
70+
.<List<Module>>workWithClassLoader( XmlMapper::findModules ),
71+
creationContext.getBootstrapContext()
72+
.getMetadataBuildingOptions()
73+
.isXmlFormatMapperLegacyFormatEnabled()
74+
),
75+
creationContext.getBootstrapContext()
76+
.getMetadataBuildingOptions()
77+
.isXmlFormatMapperLegacyFormatEnabled()
78+
);
5979
}
6080

6181
public JacksonXmlFormatMapper(ObjectMapper objectMapper) {
82+
this( objectMapper, false );
83+
}
84+
85+
public JacksonXmlFormatMapper(ObjectMapper objectMapper, boolean legacyFormat) {
6286
this.objectMapper = objectMapper;
87+
this.legacyFormat = legacyFormat;
6388
}
6489

65-
private static XmlMapper createXmlMapper(boolean legacyFormat) {
90+
private static XmlMapper createXmlMapper(List<Module> modules, boolean legacyFormat) {
6691
final XmlMapper xmlMapper = new XmlMapper();
6792
// needed to automatically find and register Jackson's jsr310 module for java.time support
68-
xmlMapper.findAndRegisterModules();
93+
xmlMapper.registerModules( modules );
6994
xmlMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
7095
xmlMapper.enable( ToXmlGenerator.Feature.WRITE_NULLS_AS_XSI_NIL );
7196
// Workaround for null vs empty string handling inside arrays,

0 commit comments

Comments
 (0)