Webclient not wiring up kotlin Spring boot and returns null object - spring-boot

I have declared OAuthClientConfiguration setting for WebClient using ReactiveClientRegistrationRepository. But when I try to autowire the WebClient bean it does not pick up the configuration settings of OAuthClientConfiguration and returns null object.
OAuthClientConfiguration defines as follows:
#Configuration
class OAuthClientConfiguration {
#Bean
fun clientRegistrations(
#Value("\${spring.security.oauth2.client.provider.okta.access-token-url}") access_token: String?,
#Value("\${spring.security.oauth2.client.registration.okta.client-id}") client_id: String?,
#Value("\${spring.security.oauth2.client.registration.okta.client-secret}") client_secret: String?,
#Value("\${spring.security.oauth2.client.registration.okta.scope}") scope: String?,
#Value("\${spring.security.oauth2.client.registration.okta.authorization-grant-type}") authorizationGrantType: String?
): ReactiveClientRegistrationRepository? {
val registration = ClientRegistration
.withRegistrationId("okta")
.tokenUri(access_token)
.clientId(client_id)
.clientSecret(client_secret)
.scope(scope)
.authorizationGrantType(AuthorizationGrantType(authorizationGrantType))
.build()
return InMemoryReactiveClientRegistrationRepository(registration)
}
#Bean
fun webClient(clientRegistrations: ReactiveClientRegistrationRepository?): WebClient? {
val clientService = InMemoryReactiveOAuth2AuthorizedClientService(clientRegistrations)
val authorizedClientManager =
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(clientRegistrations, clientService)
val oauth = ServerOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager)
oauth.setDefaultClientRegistrationId("okta")
return WebClient.builder()
.filter(oauth)
.build()
}
}
And WebClient calling is defined as follows:
#Autowired
private var webClient: WebClient? = null
fun getAccessToken(): String? {
return webClient?.post()
?.retrieve()
?.bodyToFlux(String::class.java)
?.onErrorMap { e: Throwable? -> Exception("message", e) }
?.blockLast();
}
How to create WebClient with configuration defined in OAuthClientConfiguration class?
I want it to be wired up so that the configuration defined in OAuthClientConfiguration could be setup as I initialise it.

I believe the client is created correctly. However, the wiring seems wrong.
As a rule of thumb, it is better practice to wire on the constructor
#Service
class MyService(
val webClient: WebClient
)
Alternative, in Kotlin, I usually use:
#Autowired
lateinit var webClient: WebClient

Related

Spring Boot MockMVC test - enable pre & post annotations

I have a (kotlin) test that has this code with Spring Boot:
#Autowired
private lateinit var context: WebApplicationContext
#Autowired
private lateinit var entityManager: EntityManager
private var mockMvc: MockMvc? = null
#Autowired
lateinit var springSecurityFilterChain: Filter
#BeforeAll
fun setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.addFilters<DefaultMockMvcBuilder>(springSecurityFilterChain)
.apply<DefaultMockMvcBuilder>(springSecurity())
.build()
}
This method fails with 401:
#PutMapping("/register")
#ResponseStatus(HttpStatus.OK)
#PreAuthorize("permitAll()")
fun registerNewUser(#Valid #RequestBody userDTO: UserDTO): User {
val user = userDTO.toUser(passwordEncoder)
user.activationCode = generateActivationCode()
userRepository.save(user)
return user
}
I don't understand why my pre/post authorize is not running?
I have these defined in my app's src/main/:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
class MethodSecurityConfigurer : GlobalMethodSecurityConfiguration()
#Configuration
class SecurityConfiguration {
#Bean
fun getPasswordEncoder(): PasswordEncoder {
return Pbkdf2PasswordEncoder()
}
}
What do I need to do in MockMVC's setup to enable the pre/post annotations?
So I figured it out and thought I'd add the answer here.
It's not a MockMVC problem, and M. Deinum is correct in that the two annotations will work.
My issue was that #PreAuthorize("permitAll()") is a separate filter from the "normal" spring security filters, so I was not even getting to the method authorization aspect.
I configured Spring Security's HttpSecurity to allow the method, then the permitAll() starting working properly.
http.authorizeRequests().antMatchers(HttpMethod.PUT, '/register').permitAll()

kotlin.UninitializedPropertyAccessException: lateinit property has not been initialized

This is my main function
object Service {
fun getConfigMappings(client: RedissonClient, request: GetRequest): IndataType {
****
return obj
}
}
I calling it in my main class, and everything works good, I can get the response.
#Autowired
lateinit var client: RedissonClient
val indataObj = Service.getConfigMappings(client, request)
When I want to write a test for it, I got error "kotlin.UninitializedPropertyAccessException: lateinit property client has not been initialized", can anyone help me with that?
"
class ServiceTest {
#Autowired
lateinit var client: RedissonClient
#Test
fun `test1`() {
val request = GetRequest {
***
}
val indataObj = Service.getConfigMappings(client, request)
}
}
kotlin.UninitializedPropertyAccessException: lateinit property has not been initialized is occured because there is no configuration for AutoWired.
If you want to use AutoWired in the unit test, it needs annotation for configuration.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations="classpath:config/springbeans.xml")
public class BeanSpringTest {
if you want to use #Autowired, there must have a #Bean somewhere

Stop Spring #Controller at runtime

I've found Can a spring boot #RestController be enabled/disabled using properties? which addresses not starting a #Controller at boot time, but I'm looking for a way to stop a #Controller at runtime.
I would actually used the #RefreshScope Bean and then when you want to stop the Rest Controller at runtime, you only need to change the property of said controller to false.
SO's link referencing to changing property at runtime.
Here are my snippets of working code:
#RefreshScope
#RestController
class MessageRestController(
#Value("\${message.get.enabled}") val getEnabled: Boolean,
#Value("\${message:Hello default}") val message: String
) {
#GetMapping("/message")
fun get(): String {
if (!getEnabled) {
throw NoHandlerFoundException("GET", "/message", null)
}
return message
}
}
And there are other alternatives of using Filter:
#Component
class EndpointsAvailabilityFilter #Autowired constructor(
private val env: Environment
): OncePerRequestFilter() {
override fun doFilterInternal(
request: HttpServletRequest,
response: HttpServletResponse,
filterChain: FilterChain
) {
val requestURI = request.requestURI
val requestMethod = request.method
val property = "${requestURI.substring(1).replace("/", ".")}." +
"${requestMethod.toLowerCase()}.enabled"
val enabled = env.getProperty(property, "true")
if (!enabled.toBoolean()) {
throw NoHandlerFoundException(requestMethod, requestURI, ServletServerHttpRequest(request).headers)
}
filterChain.doFilter(request, response)
}
}
My Github explaining how to disable at runtime

Shall we make a bean and Autowire OkHttpClient

OkHttpClient client = new OkHttpClient();
MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");
RequestBody body = RequestBody.create(mediaType, "q=609&client=122&layer=explore&key=w3S4BEmDKd8Q3VCCO2OZTnI8sAQxIFwA&name=utkarsh%20sharma&password=utk&phone=1111111112");
Request request = new Request.Builder()
.url("http://explore-uat.mapmyindia.in/explore-api/v1.3/")
.post(body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.addHeader("Cache-Control", "no-cache")
.addHeader("Postman-Token", "44666246-b697-488f-9410-df09f7faa53a")
.build();
Response response = client.newCall(request).execute();
I'am using this code to make a post request to API.
I use this many times in my class.
Is it possible to make a bean of OKhttpClient and autowire in my class
Please reply!!Thnx in advance
You could do it by declaring a bean somewhere in your configuration:
#Configuration
public class HttpClientConfiguration {
#Bean
public OkHttpClient httpClient() {
return new OkHttpClient();
}
}
Also, if not declared otherwise, every spring bean is by default a singleton: https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html
Regarding the initial question. I think you should declare it as a spring bean. It should ease testing.
I would declare it as Spring bean, since that makes it much easier for testing compared to a Singleton solution. However, since you are using Spring boot, you could also just use RestTemplate, as described here: https://spring.io/guides/gs/consuming-rest/
A singleton as defined below should be sufficient:
public class OkHttpFactory {
private static OkHttpClient client = new OkHttpClient();
public OkHttpClient getClient() {
return client;
}
}

How to Create or configure Rest Template using #Bean in Spring Boot

I want to define RestTemplate as an application bean using #Bean annotation in my configuration class in a spring boot application.
I am calling 4 rest services in different places in my application flow. Currently I am creating RestTemplate every time every request. Is there a way I can define that as application bean using #Bean and inject that using #Autowired?
Main reason for this question is I can able to define RestTemplate using #Bean but when I inject it with #Autowired I am loosing all defined interceptors (Interceptors are not getting called.)
Configuration Class
#Bean(name = "appRestClient")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(new RestServiceLoggingInterceptor());
restClient.setInterceptors(interceptors);
return restClient;
}
Service Class
public class MyServiceClass {
#Autowired
private RestTemplate appRestClient;
public String callRestService() {
// create uri, method response objects
String restResp = appRestClient.getForObject(uri, method, response);
// do something with the restResp
// return String
}
}
It seems my Interceptors are not getting called at all with this configuration. But RestTemplate is able to make a call to the REST service and get a response.
Answer for Spring boot 2.*.* version.
I am using Spring boot 2.1.2.RELEASE and I also added RestTemplate in my project in a class where mail method exists.
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.setConnectTimeout(Duration.ofMillis(300000))
.setReadTimeout(Duration.ofMillis(300000)).build();
}
and Used in my service or other classes like this
#Autowired
RestTemplate res;
and in methods
HttpEntity<String> entity = new HttpEntity<>(str, headers);
return res.exchange(url, HttpMethod.POST, entity, Object.class);
Judging form the name of the interceptor, I'm guessing you're doing some logging in it? You could of missed logging level configuration. I created a small application to check weather your configuration works, using 1.3.6.RELEASE version.
In this class I define the RestTemplate bean and the interceptor with logging.
package com.example;
// imports...
#SpringBootApplication
public class TestApplication {
private static final Logger LOGGER = LoggerFactory.getLogger(TestApplication.class);
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Bean(name = "appRestClient")
public RestTemplate getRestClient() {
RestTemplate restClient = new RestTemplate(
new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
// Add one interceptor like in your example, except using anonymous class.
restClient.setInterceptors(Collections.singletonList((request, body, execution) -> {
LOGGER.debug("Intercepting...");
return execution.execute(request, body);
}));
return restClient;
}
}
For logging to work, I also have to set the correct debug level in application.properties.
logging.level.com.example=DEBUG
Then I create a service where I inject this RestTemplate.
#Service
public class SomeService {
private final RestTemplate appRestClient;
#Autowired
public SomeService(#Qualifier("appRestClient") RestTemplate appRestClient) {
this.appRestClient = appRestClient;
}
public String callRestService() {
return appRestClient.getForObject("http://localhost:8080", String.class);
}
}
And also an endpoint to test this out.
#RestController
public class SomeController {
private final SomeService service;
#Autowired
public SomeController(SomeService service) {
this.service = service;
}
#RequestMapping(value = "/", method = RequestMethod.GET)
public String testEndpoint() {
return "hello!";
}
#RequestMapping(value = "/test", method = RequestMethod.GET)
public String test() {
return service.callRestService();
}
}
By performing a GET request to http://localhost:8080/test I should expect to get the String hello! getting printed (the service makes a call to http://localhost:8080 which returns hello! and sends this back to me). The interceptor with logger also prints out Intercepting... in the console.
Edd's solution won't work if you're using Spring Boot 1.4.0 or later. You will have to use RestTemplateBuilder to get this working. Here is the example
#Bean(name="simpleRestTemplate")
#Primary
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder){
RestTemplate template = restTemplateBuilder.requestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()))
.interceptors(logRestRequestInterceptor) //This is your custom interceptor bean
.messageConverters(new MappingJackson2HttpMessageConverter())
.build();
return template;
}
Now you can autowire the bean into your service class
#Autowired
#Qualifier("simpleRestTemplate")
private RestTemplate simpleRestTemplate;
Hope this helps

Resources