Using Gson instead of Jackson in Spring Webflux - spring

We have many Spring MVC projects already, which all use gson instead of jackson for response body encode. Our bean classes are all written based on gson annotation. Now I am setting up a Spring Webflux restful server. It would save a lot of work if we can use the old bean classes from our Spring MVC projects.
I have tried spring.http.converters.preferred-json-mapper=gson property to no avail.
I have tried HttpMessageConverter bean, which is included in webflux packages, but that does not work as in the Spring MVC projects.
I googled a lot and the only thing helpful is to implement org.springframework.http.codec.HttpMessageEncoder class and set it to WebFluxConfigurer.configureHttpMessageCodecs() method:
#Configuration
public class WebConfiguration implements WebFluxConfigurer {
#Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.customCodecs().decoder(new GsonHttpMessageDecoder());
configurer.customCodecs().encoder(new GsonHttpMessageEncoder());
}
private static class GsonHttpMessageEncoder implements HttpMessageEncoder {
...
}
private static class GsonHttpMessageDecoder implements HttpMessageDecoder {
...
}
}
I haven't try this out yet, since it is a little complex. Is there some easy way to replace jackson with gson in Spring Webflux?
Any help is appreciated.

Spring Framework doesn't support GSON as a WebFlux Encoder / Decoder for now. Feel free to follow up on the dedicated issue.
Note that as far as I know, GSON doesn't support non-blocking parsing so even if the support is implemented in Framework, it won't be complete and should not cover streaming input use cases.

Related

How to make WebFilter work in a non-WebFlux/non-reactive Spring Boot application?

I'm trying to solve this: How to rewrite URLs with Spring (Boot) via REST Controllers?
by creating some kind of "filter" which would be applied to every incoming HTTP request.
The matter is covered by some answers like for this question: Spring Boot Adding Http Request Interceptors
but interface HandlerInterceptor deals with javax' HttpServletRequest and HttpServletResponse which are not as practical as the new class introduced by Spring i.e. the ServerWebExchange (see the use of setLocation() in the code below) which appears in an interface whose name sounds promising, org.springframework.web.server.WebFilter:
So I ended with something like:
#Component
public class LegacyRestRedirectWebFilter implements WebFilter {
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
URI origin = exchange.getRequest().getURI();
String path = origin.getPath();
if (path.startsWith("/api/")) {
ServerHttpResponse response = exchange.getResponse();
response.setStatusCode(HttpStatus.MOVED_PERMANENTLY);
URI location = UriComponentsBuilder.fromUri(origin).replacePath(path.replaceFirst("/api/", "/rest/")).build().toUri();
response.getHeaders().setLocation(location);
}
return chain.filter(exchange);
}
}
...in the same way people are doing something similar like:
Spring WebFlux add WebFIlter to match specific paths
WebFilter in WebFlux application
Alas, my filter is never called!!!
The thing is: I am not in a "WebFlux" context (on the contrary to the questions above) because:
I don't need to, and
I tried and got the following problems:
Reactive Webfilter is not working when we have spring-boot-starter-web dependency in classpath (but no definitive answer); marked duplicate of:
Don't spring-boot-starter-web and spring-boot-starter-webflux work together?
Spring WebFlux with traditional Web Security (I have the "traditional" spring-boot-starter-security dependency in my pom.xml plus a #Configuration class extending WebSecurityConfigurerAdapter - but not willing to migrate it to... what by the way?)
Also I don't understand why would I need to be in a WebFlux context, because org.springframework.web.server.WebFilter neither deals with reactive nor Webflux, right? ..or does it? This is not very clear in the Javadoc.
In fact, I didn't find a way to make WebFilter work in a non-WebFlux context, but I could successfully implement such a filter, which both implements javax.servlet.Filter (non-reactive) AND org.springframework.web.server.WebFilter (reactive).
Here is my answer to the other related question: https://stackoverflow.com/a/63780659/666414

Spring dependency injection in a non-spring boot application

I writing a library which doesn't have a main and isn't a Spring Boot application but I'd still like to use Spring Framework's dependency injection to load the dependencies of my classes. I'd like this library to be available to everyone, even developers who aren't going to be using Spring.
Is there a way for me to take advantage of Spring Framework's dependency injection even if the users of my library won't be using Spring?
Currently I'm importing my library into another one of my apps but my dependencies are turning out as null. Is there a way to get around this?
#Component
public class Client
{
#Autowired
private ClientService clientService; // this is null
public static Client createClientWithCredentials(String clientId, String clientSecret)
{
return new Client(clientId, clientSecret);
}
private Client(String clientId, String clientSecret)
{
// ...
}
}
#Service
public class ClientService {
// ...
}
Someone using my library would use instantiate it like this.
...
// Non-Spring project
Client client = Client.createClientWithCredentials("id", "secret");
...
Unfortunately using client will at some point throw a NullPointerException since clientService is null. I'm also testing my app this way and this is why I know that I have null dependencies.
How can I take advantage of Spring's Dependency Injection if my app is never run but only used as a library?
Is this possible or do I need to use the old way of every class constructing it's own dependencies?

What Is the Correct Way To Use AbstractReactiveWebInitializer

I've got a Spring WebFlux application running successfully as a standalone spring boot application.
I am attempting to run the same application in a Tomcat container, and following the documentation, I've created a class that extends AbstractReactiveWebInitializer. The class requires that I implement a method getConfigClasses that would return classes normally annotated with #Configuration. If the working spring boot app started with a class called ApplicationInitializer, then the resulting implementations would look like this:
#SpringBootApplication(scanBasePackages = "my.pkg")
#EnableDiscoveryClient
#EnableCaching
public class ApplicationInitializer {
public static void main(String... args) {
SpringApplication.run(ApplicationInitializer.class, args);
}
}
and
public class ServletInitializer extends AbstractReactiveWebInitializer {
#Override
protected Class<?>[] getConfigClasses() {
return new Class[] {ApplicationInitializer.class};
}
}
When deployed, the only thing that starts is ApplicationInitializer, none of the autoconfigured Spring Boot classes (Cloud Config, DataSource, etc) ever kick off.
The documenation states this is the class I need to implement, I just expected the remainder of the spring environment to "just work".
How should I be using this class to deploy a Reactive WebFlux Spring Boot application to a Tomcat container ?
Edit:
After some additional research, I've narrowed it down to likely just Cloud Config. During bean post processing on startup, the ConfigurationPropertiesBindingPostProcessor should be enriched with additional property sources (from cloud config), but it appears to be the default Spring properties instead, with no additional sources.
The misisng properties is causing downstream beans to fail.
Spring Boot does not support WAR packaging for Spring WebFlux applications.
The documentation you're referring to is the Spring Framework doc; Spring Framework does support that use case, but without Spring Boot.
you can extend SpringBootServletInitializer, add add reactive servlet on onStartup method

How can I force #AutoConfigureJsonTesters to use HAL format from spring-hateoas?

I have a small module that should only contain the resource model of my REST service. I want to create a test in this module to ensure that the resource model serializes and deserializes appropriate HAL format.
I have a single test and this is the configuration:
#RunWith(SpringRunner::class
#SpringBootTest
#AutoConfigureJsonTesters
class MyTest {
#Autowired
private lateinit var collectionTester: JacksonTester<Resources<Entity>>
....
}
and a very simple configuration
#SpringBootConfiguration
class TestConfig
When calling collectionTester.write on a list of Entity (which extends ResourceSupport) I don't get an _embedded field, instead I get
{"links":[],"content":[...]}
which is not HAL format.
How can I force #AutoConfigureJsonTesters to give me a JacksonTester with an ObjectMapper configured for HAL?
Spring Boot 2.0.0.RELEASE
Thanks!
The auto-configured JacksonTester will use the context’s ObjectMapper which won’t have any of the Spring HATEOAS stuff configured on it. You might be better creating a JacksonTester yourself and passing it an appropriately configured ObjectMapper to use.
I believe Spring HATEOAS has a module that it applies to the ObjectMapper to configure it. If you get stuck with that, asking in gitter/spring-projects/spring-data is probably your best bet as Spring HATEOAS is maintained by the Data team due to it being used by Spring Data REST.

How to use Jersey 2 with Spring IoC container

What is the best way to enable injection of spring beans into Jersey 2? Jersey seems to not support this natively.
What is needed to wire the 2 frameworks together? In pom.xml and web.xml?
Jersey 2.3 has now spring support:
https://jersey.github.io/documentation/latest/user-guide.html#spring
As stated in the documentation
The Spring extension module configuration is based on annotations
So you have to tell spring to scan your classpath, for example:
<context:component-scan base-package="my.package.to.resources">
and annotate your resource class with a spring annotation (I advise to use #Component, and then specify the jersey resource scopes #Singleton/#PerLookup/#RequestScoped )
#Component
#Singleton
#Path("example")
public class Example {
//Spring beans can't be injected directly into JAX-RS classes by using Spring XML configuration
#Autowired
private MyOtherBean myOtherBean;
#GET #Path("hello")
public String hello() {
return myOtherBean.hello();
}
}
As of June 2013, Jersey 2.0 has no official Spring support. There are two options:
Use third party code from here https://github.com/marko-asplund/jersey/tree/master/ext/jersey-spring
Wait until HK2 spring bridge becomes stable and documented https://java.net/jira/browse/HK2-40
See also:
http://jersey.576304.n2.nabble.com/Spring-framework-support-for-Jersey-2-td7580673.html
EDIT: Jersey 2.3 has spring support now, see the answer by Fabio below
You should be able to annotate jersey components and then use annotations to inject the beans.
#Service //(or #Component)
public class MyJerseyService {
#Autowired
private MyObj mySpringBean
}

Resources