All Downloads are FREE. Search and download functionalities are using the official Maven repository.

au.net.causal.shoelaces.jersey.ShoelacesJerseyAutoConfiguration Maven / Gradle / Ivy

There is a newer version: 3.0
Show newest version
package au.net.causal.shoelaces.jersey;

import com.fasterxml.jackson.databind.AnnotationIntrospector;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.cfg.MapperConfig;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;
import org.glassfish.hk2.api.Rank;
import org.glassfish.jersey.jackson.JacksonFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jersey.ResourceConfigCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ClassUtils;

import javax.ws.rs.client.Client;
import javax.ws.rs.ext.ContextResolver;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Shoelaces custom configuration for Jersey that allows customization of the Jackson object mapper used by Jersey
 * through the use of {@link JerseyObjectMapperConfigurer}s.
 */
@AutoConfiguration
@ConditionalOnClass(Client.class)
public class ShoelacesJerseyAutoConfiguration
{
    //Mostly a copy from JerseyAutoConfiguration but with changes to support our own object mapper flavor

    /**
     * The Shoelaces custom context resolver is registered on the Jersey resource config with this priority to ensure
     * that it gets priority over the default Spring Boot Jersey one.
     * 

* * For user code or other libraries that want to use their own object mapper context resolver on the Jersey resource * config, use a priority higher than this value when registering your own context resolver. */ public static final int RESOURCE_CONFIG_CUSTOM_OBJECT_MAPPER_CONTEXT_RESOLVER_PRIORITY = 100; @ConditionalOnClass(JacksonFeature.class) @Configuration static class JacksonResourceConfigCustomizer { private static final String JAXB_ANNOTATION_INTROSPECTOR_CLASS_NAME = "com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector"; /** * Take the 'primary' object mapper bean from Spring Boot, copy it, and configure it with any available Jersey * object mapper configurer beans. * * @param objectMapperConfigurers all Jersey object mapper configurers available to Spring. * @param primaryObjectMapper the standard object mapper bean in Spring boot that is used for everything. * * @return a new object mapper to use specifically for Jersey, copied from the primary and configured with the * configurers. */ @Bean @JerseyConfigured public ObjectMapper customJerseyObjectMapper(Optional> objectMapperConfigurers, ObjectMapper primaryObjectMapper) { //Make a copy of the object mapper in case it's used in other things besides Jersey //The 'primary' object mapper being injected here is the default object mapper injected by Spring Boot //for *anything* that wants a Jackson ObjectMapper, be it Jersey or something else //By using a copy we configure Jersey's object mapper with custom stuff without affecting everything else ObjectMapper objectMapper = primaryObjectMapper.copy(); objectMapperConfigurers.orElse(List.of()).forEach(configurer -> configurer.configure(objectMapper)); return objectMapper; } /** * Customize the Jersey resource config by configuring our own custom object mapper into it instead of using * the default one. The custom object mapper will have been configured by any registered Jersey object mapper * configurer beans. * * @param objectMapper our custom object mapper. * * @return a Jersey resource config customizer that makes Jersey use our custom object mapper instead of the * default one. */ @Bean public ResourceConfigCustomizer shoelacesResourceConfigCustomizer(@JerseyConfigured ObjectMapper objectMapper) { addJaxbAnnotationIntrospectorIfPresent(objectMapper); return (ResourceConfig config) -> { config.register(JacksonFeature.class); //The map ensures our resolver is registered with higher priority than the default one that is //created in JerseyAutoConfiguration - this is important since we want Jersey to always use our one //instead of Spring Boot's default one config.register(new ObjectMapperContextResolver(objectMapper), Map.of(ContextResolver.class, RESOURCE_CONFIG_CUSTOM_OBJECT_MAPPER_CONTEXT_RESOLVER_PRIORITY)); }; } private void addJaxbAnnotationIntrospectorIfPresent(ObjectMapper objectMapper) { if (ClassUtils.isPresent(JAXB_ANNOTATION_INTROSPECTOR_CLASS_NAME, getClass().getClassLoader())) new ObjectMapperCustomizer().addJaxbAnnotationIntrospector(objectMapper); } } private static final class ObjectMapperCustomizer { private void addJaxbAnnotationIntrospector(ObjectMapper objectMapper) { JaxbAnnotationIntrospector jaxbAnnotationIntrospector = new JaxbAnnotationIntrospector(objectMapper.getTypeFactory()); objectMapper.setAnnotationIntrospectors(createPair(objectMapper.getSerializationConfig(), jaxbAnnotationIntrospector), createPair(objectMapper.getDeserializationConfig(), jaxbAnnotationIntrospector)); } private AnnotationIntrospector createPair(MapperConfig config, JaxbAnnotationIntrospector jaxbAnnotationIntrospector) { return AnnotationIntrospector.pair(config.getAnnotationIntrospector(), jaxbAnnotationIntrospector); } } //The @Rank is important to ensure that this object mapper is used before the default one from Spring Boot's JerseyAutoConfiguration //otherwise order is arbitrary and you'll get random object mappers being resolved each time the app is run @Rank(RESOURCE_CONFIG_CUSTOM_OBJECT_MAPPER_CONTEXT_RESOLVER_PRIORITY) private static class ObjectMapperContextResolver implements ContextResolver { private final ObjectMapper objectMapper; private ObjectMapperContextResolver(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public ObjectMapper getContext(Class type) { return this.objectMapper; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy