Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package org.hibernate.boot.internal;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Collections;
Expand Down Expand Up @@ -70,6 +71,7 @@
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
import org.hibernate.stat.Statistics;
import org.hibernate.type.format.FormatMapper;
import org.hibernate.type.format.FormatMapperCreationContext;
import org.hibernate.type.format.jackson.JacksonIntegration;
import org.hibernate.type.format.jakartajson.JakartaJsonIntegration;
import org.hibernate.type.format.jaxb.JaxbXmlFormatMapper;
Expand Down Expand Up @@ -313,13 +315,23 @@ public SessionFactoryOptionsBuilder(StandardServiceRegistry serviceRegistry, Boo
AvailableSettings.JPA_VALIDATION_FACTORY,
configurationSettings.get( AvailableSettings.JAKARTA_VALIDATION_FACTORY )
);
this.jsonFormatMapper = determineJsonFormatMapper(

final var formatMapperCreationContext = new FormatMapperCreationContext() {
@Override
public BootstrapContext getBootstrapContext() {
return context;
}
};
jsonFormatMapper = jsonFormatMapper(
configurationSettings.get( AvailableSettings.JSON_FORMAT_MAPPER ),
strategySelector
strategySelector,
formatMapperCreationContext
);
this.xmlFormatMapper = determineXmlFormatMapper(

xmlFormatMapper = xmlFormatMapper(
configurationSettings.get( AvailableSettings.XML_FORMAT_MAPPER ),
strategySelector
strategySelector,
formatMapperCreationContext
);

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

private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
Map<String,Object> configurationSettings,
Map<String, Object> configurationSettings,
StandardServiceRegistry serviceRegistry) {
final PhysicalConnectionHandlingMode specifiedHandlingMode = PhysicalConnectionHandlingMode.interpret(
configurationSettings.get( CONNECTION_HANDLING )
Expand All @@ -840,36 +852,62 @@ private PhysicalConnectionHandlingMode interpretConnectionHandlingMode(
return serviceRegistry.requireService( TransactionCoordinatorBuilder.class ).getDefaultConnectionHandlingMode();
}

private static FormatMapper determineJsonFormatMapper(Object setting, StrategySelector strategySelector) {
return strategySelector.resolveDefaultableStrategy(
FormatMapper.class,
private static FormatMapper jsonFormatMapper(Object setting, StrategySelector selector, FormatMapperCreationContext creationContext) {
return formatMapper(
setting,
(Callable<FormatMapper>) () -> {
final FormatMapper jsonJacksonFormatMapper = JacksonIntegration.getJsonJacksonFormatMapperOrNull();
if (jsonJacksonFormatMapper != null) {
return jsonJacksonFormatMapper;
}
else {
return JakartaJsonIntegration.getJakartaJsonBFormatMapperOrNull();
}
}
selector,
() -> {
final FormatMapper jsonJacksonFormatMapper = JacksonIntegration.getJsonJacksonFormatMapperOrNull( creationContext );
return jsonJacksonFormatMapper != null
? jsonJacksonFormatMapper
: JakartaJsonIntegration.getJakartaJsonBFormatMapperOrNull();
},
creationContext
);
}

private static FormatMapper determineXmlFormatMapper(Object setting, StrategySelector strategySelector) {
return strategySelector.resolveDefaultableStrategy(
FormatMapper.class,
private static FormatMapper xmlFormatMapper(Object setting, StrategySelector selector, FormatMapperCreationContext creationContext) {
return formatMapper(
setting,
(Callable<FormatMapper>) () -> {
final FormatMapper jacksonFormatMapper = JacksonIntegration.getXMLJacksonFormatMapperOrNull();
if (jacksonFormatMapper != null) {
return jacksonFormatMapper;
}
return new JaxbXmlFormatMapper();
}
selector,
() -> {
final FormatMapper jacksonFormatMapper = JacksonIntegration.getXMLJacksonFormatMapperOrNull( creationContext );
return jacksonFormatMapper != null
? jacksonFormatMapper
: new JaxbXmlFormatMapper();
},
creationContext
);
}

private static FormatMapper formatMapper(Object setting, StrategySelector selector, Callable<FormatMapper> defaultResolver, FormatMapperCreationContext creationContext) {
return selector.resolveStrategy( FormatMapper.class, setting, defaultResolver, strategyClass -> {
try {
final Constructor<? extends FormatMapper> creationContextConstructor =
strategyClass.getDeclaredConstructor( FormatMapperCreationContext.class );
return creationContextConstructor.newInstance( creationContext );
}
catch (NoSuchMethodException e) {
// Ignore
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new StrategySelectionException(
String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ),
e
);
}
try {
return strategyClass.getDeclaredConstructor().newInstance();
}
catch (Exception e) {
throw new StrategySelectionException(
String.format( "Could not instantiate named strategy class [%s]", strategyClass.getName() ),
e
);
}
} );
}


// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SessionFactoryOptionsState
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.type.format;

import org.hibernate.Incubating;
import org.hibernate.boot.spi.BootstrapContext;


/**
* The creation context for {@link FormatMapper} that is passed as constructor argument to implementations.
*/
@Incubating
public interface FormatMapperCreationContext {
BootstrapContext getBootstrapContext();

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
*/
package org.hibernate.type.format.jackson;

import org.checkerframework.checker.nullness.qual.Nullable;
import org.hibernate.type.format.FormatMapper;
import org.hibernate.type.format.FormatMapperCreationContext;

public final class JacksonIntegration {

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


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

public static FormatMapper getXMLJacksonFormatMapperOrNull() {
return XML_FORMAT_MAPPER;
/**
* Checks that Jackson is available and that we have the Oracle OSON extension available
* in the classpath.
* @return true if we can load the OSON support, false otherwise.
*/
private static boolean ableToLoadJacksonOSONFactory() {
return ableToLoadJacksonJSONMapper() &&
canLoad( "oracle.jdbc.provider.oson.OsonFactory" );
}

public static @Nullable FormatMapper getXMLJacksonFormatMapperOrNull(FormatMapperCreationContext creationContext) {
return JACKSON_XML_AVAILABLE
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonXmlFormatMapper", creationContext )
: null;
}

public static FormatMapper getJsonJacksonFormatMapperOrNull() {
return JSON_FORMAT_MAPPER;
public static @Nullable FormatMapper getJsonJacksonFormatMapperOrNull(FormatMapperCreationContext creationContext) {
return JACKSON_JSON_AVAILABLE
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonJsonFormatMapper", creationContext )
: null;
}

public static @Nullable FormatMapper getXMLJacksonFormatMapperOrNull(boolean legacyFormat) {
if ( JACKSON_XML_AVAILABLE ) {
try {
final Class<?> formatMapperClass = JacksonIntegration.class.getClassLoader()
.loadClass( "org.hibernate.type.format.jackson.JacksonXmlFormatMapper" );
return (FormatMapper) formatMapperClass.getDeclaredConstructor( boolean.class )
.newInstance( legacyFormat );
}
catch (Exception e) {
throw new RuntimeException( "Couldn't instantiate Jackson XML FormatMapper", e );
}
}
return null;
}

public static @Nullable FormatMapper getJsonJacksonFormatMapperOrNull() {
return JACKSON_JSON_AVAILABLE
? createFormatMapper( "org.hibernate.type.format.jackson.JacksonJsonFormatMapper", null )
: null;
}

private static FormatMapper createFormatMapper(String className, @Nullable FormatMapperCreationContext creationContext) {
try {
if ( creationContext == null ) {
final Class<?> formatMapperClass = JacksonIntegration.class.getClassLoader()
.loadClass( className );
return (FormatMapper) formatMapperClass.getDeclaredConstructor().newInstance();
}
else {
return (FormatMapper) creationContext.getBootstrapContext()
.getClassLoaderAccess()
.classForName( className )
.getDeclaredConstructor( FormatMapperCreationContext.class )
.newInstance( creationContext );
}
}
catch (Exception e) {
throw new RuntimeException( "Couldn't instantiate Jackson FormatMapper", e );
}
}

private static boolean canLoad(String name) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@
*/
package org.hibernate.type.format.jackson;

import com.fasterxml.jackson.databind.Module;

import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.type.format.AbstractJsonFormatMapper;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.hibernate.type.format.FormatMapperCreationContext;

import java.lang.reflect.Type;
import java.util.List;

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

public JacksonJsonFormatMapper() {
this(new ObjectMapper().findAndRegisterModules());
this( ObjectMapper.findModules( JacksonJsonFormatMapper.class.getClassLoader() ) );
}

public JacksonJsonFormatMapper(FormatMapperCreationContext creationContext) {
this(
creationContext.getBootstrapContext()
.getServiceRegistry()
.requireService( ClassLoaderService.class )
.<List<Module>>workWithClassLoader( ObjectMapper::findModules )
);
}

private JacksonJsonFormatMapper(List<Module> modules) {
this(new ObjectMapper().registerModules( modules ));
}

public JacksonJsonFormatMapper(ObjectMapper objectMapper) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,12 @@

import java.io.IOException;
import java.lang.reflect.Type;
import java.util.List;
import java.util.ArrayList;

import com.fasterxml.jackson.databind.Module;

import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.format.FormatMapper;
Expand All @@ -24,6 +28,7 @@
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;
import org.hibernate.type.format.FormatMapperCreationContext;

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

public JacksonXmlFormatMapper() {
this( createXmlMapper() );
this(
createXmlMapper( XmlMapper.findModules( JacksonXmlFormatMapper.class.getClassLoader() ) )
);
}

public JacksonXmlFormatMapper(FormatMapperCreationContext creationContext) {
this(
createXmlMapper(
creationContext.getBootstrapContext()
.getServiceRegistry()
.requireService( ClassLoaderService.class )
.<List<Module>>workWithClassLoader( XmlMapper::findModules )
)
);
}

public JacksonXmlFormatMapper(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

private static XmlMapper createXmlMapper() {
private static XmlMapper createXmlMapper(List<Module> modules) {
final XmlMapper xmlMapper = new XmlMapper();
// needed to automatically find and register Jackson's jsr310 module for java.time support
xmlMapper.findAndRegisterModules();
xmlMapper.registerModules( modules );
xmlMapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
xmlMapper.enable( ToXmlGenerator.Feature.WRITE_NULLS_AS_XSI_NIL );
// Workaround for null vs empty string handling inside arrays,
Expand Down
Loading