Skip to content

Commit 3c624da

Browse files
committed
HHH-19918 Avoid reflection when instantiating known FormatMapper
1 parent 6896e17 commit 3c624da

File tree

3 files changed

+36
-63
lines changed

3 files changed

+36
-63
lines changed

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

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

9+
import java.util.List;
10+
11+
import com.fasterxml.jackson.databind.Module;
12+
import com.fasterxml.jackson.databind.ObjectMapper;
913
import org.checkerframework.checker.nullness.qual.Nullable;
14+
15+
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
1016
import org.hibernate.type.format.FormatMapper;
1117
import org.hibernate.type.format.FormatMapperCreationContext;
1218

@@ -30,69 +36,24 @@ private static boolean ableToLoadJacksonXMLMapper() {
3036
return canLoad( "com.fasterxml.jackson.dataformat.xml.XmlMapper" );
3137
}
3238

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-
4339
public static @Nullable FormatMapper getXMLJacksonFormatMapperOrNull(FormatMapperCreationContext creationContext) {
4440
return JACKSON_XML_AVAILABLE
45-
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonXmlFormatMapper", creationContext )
41+
? new JacksonXmlFormatMapper( creationContext )
4642
: null;
4743
}
4844

4945
public static @Nullable FormatMapper getJsonJacksonFormatMapperOrNull(FormatMapperCreationContext creationContext) {
5046
return JACKSON_JSON_AVAILABLE
51-
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonJsonFormatMapper", creationContext )
47+
? new JacksonJsonFormatMapper( creationContext )
5248
: null;
5349
}
5450

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-
7051
public static @Nullable FormatMapper getJsonJacksonFormatMapperOrNull() {
7152
return JACKSON_JSON_AVAILABLE
72-
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonJsonFormatMapper", null )
53+
? new JacksonJsonFormatMapper()
7354
: null;
7455
}
7556

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-
}
94-
}
95-
9657
private static boolean canLoad(String name) {
9758
try {
9859
//N.B. intentionally not using the context classloader
@@ -106,4 +67,28 @@ private static boolean canLoad(String name) {
10667
return false;
10768
}
10869
}
70+
71+
static List<Module> loadModules(FormatMapperCreationContext creationContext) {
72+
final ClassLoader classLoader = JacksonIntegration.class.getClassLoader();
73+
final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
74+
if ( contextClassLoader != null && classLoader != contextClassLoader ) {
75+
try {
76+
// The context class loader represents the application class loader in a Jakarta EE deployment.
77+
// We have to check if the ObjectMapper that is visible to Hibernate ORM is the same that is visible
78+
// to the application class loader. Only if it is, we can use the application class loader or rather
79+
// our AggregatedClassLoader for loading Jackson Module via ServiceLoader, as otherwise the loaded
80+
// Jackson Module instances would have a different class loader, leading to a ClassCastException.
81+
if ( ObjectMapper.class == contextClassLoader.loadClass( "com.fasterxml.jackson.databind.ObjectMapper" ) ) {
82+
return creationContext.getBootstrapContext()
83+
.getServiceRegistry()
84+
.requireService( ClassLoaderService.class )
85+
.<List<Module>>workWithClassLoader( ObjectMapper::findModules );
86+
}
87+
}
88+
catch (ClassNotFoundException | LinkageError e) {
89+
// Ignore if the context/application class loader doesn't know Jackson classes
90+
}
91+
}
92+
return ObjectMapper.findModules( classLoader );
93+
}
10994
}

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import com.fasterxml.jackson.databind.Module;
1010

11-
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
1211
import org.hibernate.type.format.AbstractJsonFormatMapper;
1312

1413
import com.fasterxml.jackson.core.JsonProcessingException;
@@ -33,12 +32,7 @@ public JacksonJsonFormatMapper() {
3332
}
3433

3534
public JacksonJsonFormatMapper(FormatMapperCreationContext creationContext) {
36-
this(
37-
creationContext.getBootstrapContext()
38-
.getServiceRegistry()
39-
.requireService( ClassLoaderService.class )
40-
.<List<Module>>workWithClassLoader( ObjectMapper::findModules )
41-
);
35+
this( JacksonIntegration.loadModules( creationContext ) );
4236
}
4337

4438
private JacksonJsonFormatMapper(List<Module> modules) {

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

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313

1414
import com.fasterxml.jackson.databind.Module;
1515

16-
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
1716
import org.hibernate.type.descriptor.WrapperOptions;
1817
import org.hibernate.type.descriptor.java.JavaType;
1918
import org.hibernate.type.format.FormatMapper;
@@ -41,18 +40,13 @@ public final class JacksonXmlFormatMapper implements FormatMapper {
4140

4241
public JacksonXmlFormatMapper() {
4342
this(
44-
createXmlMapper( XmlMapper.findModules( JacksonXmlFormatMapper.class.getClassLoader() ) )
43+
createXmlMapper( ObjectMapper.findModules( JacksonXmlFormatMapper.class.getClassLoader() ) )
4544
);
4645
}
4746

4847
public JacksonXmlFormatMapper(FormatMapperCreationContext creationContext) {
4948
this(
50-
createXmlMapper(
51-
creationContext.getBootstrapContext()
52-
.getServiceRegistry()
53-
.requireService( ClassLoaderService.class )
54-
.<List<Module>>workWithClassLoader( XmlMapper::findModules )
55-
)
49+
createXmlMapper( JacksonIntegration.loadModules( creationContext ) )
5650
);
5751
}
5852

0 commit comments

Comments
 (0)