Response from Spring Async controller - java

I have wrote spring rest Async controller, that return String JSON response,
When I request the browser complete the response, while the controller have not completed the processing hence the response is not ready.
I am using Spring Boot, Apache as inbuilt server.
In EmbeddedServletContainerFactory I have set AsyncTimeout too.
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
connector.setAsyncTimeout(10000000);
}
});
so How I make the browser wait while the controller Async'ly complete the response?
And the controller is
#Async
#RequestMapping(value = "/id", method = RequestMethod.GET)
public String getDetails(#PathVariable("id") String id) {
// wrote logic for JSON response....
}

Related

How to use spring.security.oauth2.client with SOAP calls, initially sent by org.springframework.ws.client.core.WebServiceTemplate?

We have a Spring Boot microservice that does the SOAP call to the external system using org.springframework.ws.client.core.WebServiceTemplate.
Now the system would be protected with Keycloak, so all the request need to beak the auth token.
If it was a REST API, I would just replace the pre-existed RestTemplate with OAuth2RestTemplate. But how to instrument the calls initially done by the org.springframework.ws.client.core.WebServiceTemplate ?
So, I understand, I should put the authentication header manually with value 'Bearer ....token there...'. How I can retrieve that part manually to put it into the request?
you can get current request token using RequestContextHolder class and add into soap request header.
String token = ((ServletRequestAttributes)(RequestContextHolder.getRequestAttributes())).getRequest().getHeader("Authorization");
Also I would suggest use Webservice interecptor instead of adding header in each web service request call.
The problem was caused by
Existing library code, based on org.springframework.ws.client.core.WebServiceTemplate, so large and huge for rewriting it using WebClient, compatible with OAuth2 SpringSecurity or use depricated OAuth2RestTemplate
The webservice we previously communicated with, turns into protected with Gravitee and accepts queries with JWT tokens only. So, the only change here is to add the Authentication header with 'Bearer ....token there...'
We initiate the call from the scheduled jo in the microservice. So, it should be getting token from the Keycloak before the request and be able to update it with time. No one does the explicit authorization like in the frontend, so the OAuth2 client should use client-id and client-secret to connect with no human involved
The Solution
At the beginning, we define the Interceptor to the SOAP calls, that will pass the token as a header, via a Supplier function taking it wherever it can be taken:
public class JwtClientInterceptor implements ClientInterceptor {
private final Supplier<String> jwtToken;
public JwtClientInterceptor(Supplier<String> jwtToken) {
this.jwtToken = jwtToken;
}
#Override
public boolean handleRequest(MessageContext messageContext) {
SoapMessage soapMessage = (SoapMessage) messageContext. getRequest();
SoapHeader soapHeader = soapMessage.getSoapHeader();
soapHeader.addHeaderElement(new QName("authorization"))
.setText(String. format("Bearer %s", jwtToken.get()));
return true;
}
#Override
public boolean handleResponse(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public boolean handleFault(MessageContext messageContext) throws WebServiceClientException {
return true;
}
#Override
public void afterCompletion(MessageContext messageContext, Exception ex) throws WebServiceClientException {
}
}
Then pass it to the template in addition to other pre-existed interceptor to be called in config class:
protected WebServiceTemplate buildWebServiceTemplate(Jaxb2Marshaller marshaller,
HttpComponentsMessageSender messageSender, String uri, Supplier<String> jwtToken) {
WebServiceTemplate template = new WebServiceTemplate();
template.setMarshaller(marshaller);
template.setUnmarshaller(marshaller);
template.setMessageSender(messageSender);
template.setDefaultUri(uri);
ClientInterceptor[] clientInterceptors = ArrayUtils.addAll(template.getInterceptors(), new Logger(), new JwtClientInterceptor(jwtToken));
template.setInterceptors(clientInterceptors);
return template;
}
Then add the Spring Security Oath2 Client library
compile 'org.springframework.security:spring-security-oauth2-client:5.2.1.RELEASE'
We create OAuth2AuthorizedClientService bean, that uses a standard ClientRegistrationRepository (the repository is initiated through usage of #EnableWebSecurity annotation on the #Configuration class, but please double check about that)
#Bean
public OAuth2AuthorizedClientService oAuth2AuthorizedClientService(ClientRegistrationRepository clientRegistrationRepository) {
return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
}
Then create a OAuth2AuthorizedClientManager
#Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
Authentication authentication = new Authentication() {
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
GrantedAuthority grantedAuthority = new GrantedAuthority() {
#Override
public String getAuthority() {
return "take_a_needed_value_from_property";
}
};
return Arrays.asList(grantedAuthority);
}
#Override
public Object getCredentials() {
return null;
}
#Override
public Object getDetails() {
return null;
}
#Override
public Object getPrincipal() {
return new Principal() {
#Override
public String getName() {
return "our_client_id_from_properties";
}
};
}
#Override
public boolean isAuthenticated() {
return true;
}
#Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
}
#Override
public String getName() {
return "take_a_needed_name_from_properties";
}
};
//we need to emulate Principal there, as other classes relies on it. In fact, Principal isn't needed for the app which is a client and just do the call, as nothing is authorized in the app against this Principal itself
OAuth2AuthorizationContext oAuth2AuthorizationContext = OAuth2AuthorizationContext.withClientRegistration(clientRegistrationRepository.findByRegistrationId("keycloak")).
principal(authentication).
build();
oAuth2AuthorizationContext.getPrincipal().setAuthenticated(true);
oAuth2AuthorizationContext.getAuthorizedClient();
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder().
//refreshToken().
clientCredentials(). //- we use this one according to our set up
//authorizationCode().
build();
OAuth2AuthorizedClientService oAuth2AuthorizedClientService = oAuth2AuthorizedClientService(clientRegistrationRepository); //use the bean from before step here
AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
new AuthorizedClientServiceOAuth2AuthorizedClientManager(
clientRegistrationRepository, oAuth2AuthorizedClientService);
OAuth2AuthorizedClient oAuth2AuthorizedClient = authorizedClientProvider.authorize(oAuth2AuthorizationContext);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
oAuth2AuthorizedClientService.saveAuthorizedClient(oAuth2AuthorizedClient,
oAuth2AuthorizationContext.getPrincipal());
//this step is needed, as without explicit authorize call, the
//oAuth2AuthorizedClient isn't initialized in the service
return authorizedClientManager;
}
Provide a method for supplied function that can be called each time to retrieve the JWT token from the security stuff (repository and manager). Here it should be auto-updated, so we just call for retrieving it
public Supplier<String> getJwtToken() {
return () -> {
OAuth2AuthorizedClient authorizedClient = authorizedClientService.loadAuthorizedClient("keycloak", "we_havePout_realm_there_from_the_properties");
return authorizedClient.getAccessToken().getTokenValue();
};
}
Pass this Consumer to the #Bean, defining the WebServiceTemplate's
#Bean
public Client client(#Qualifier("Sender1") HttpComponentsMessageSender bnfoMessageSender,
#Qualifier("Sender2") HttpComponentsMessageSender uhMessageSender) {
WebServiceTemplate sender1= buildWebServiceTemplate(buildSender1Marshaller(), sender1MessageSender, properties.getUriSender1(),getJwtToken());
WebServiceTemplate sender2 = buildWebServiceTemplate(buildSender2Marshaller(), sender2MessageSender, properties.getUriSender2(),getJwtToken());
return buildClient(buildRetryTemplate(), sender1, sender2);
}
We add Spring Security Client values to application.yaml in order to configure it.
spring:
security:
oauth2:
client:
provider:
keycloak:
issuer-uri: https://host/keycloak/auth/realms/ourrealm
registration:
keycloak:
client-id: client_id
client-secret: client-secret-here
authorization-grant-type: client_credentials //need to add explicitly, otherwise would try other grant-type by default and never get the token!
client-authentication-method: post //need to have this explicitly, otherwise use basic that doesn't fit best the keycloak set up
scope: openid //if your don't have it, it checks all available scopes on url like https://host/keycloak/auth/realms/ourrealm/ .well-known/openid-configuration keycloak and then sends them as value of parameter named 'scope' in the query for retrieving the token; that works wrong on our keycloak, so to replace this auto-picked value, we place the explicit scopes list here

how to convert spark web api endpoint to spring boot endpoint properway

I have created a api endpoint using spark for education purpose. Now i need to convert this implementation to the springboot and need to consume the request parameter within the implementation.Following codes show how i have implemented both application...But how can i consume request from spring boot application. Following implementation wont give the request body.
package com.spark2springboot.application;
import static spark.Spark.*;
public class spark2SpringbootTest {
public static void main(String[] args) {
post("/api/spark2springboot/receive", (request, response) -> {
String str = new String(request.bodyAsBytes());
return str;
});
}
}
Spring boot implementation
#RestController
public class EmailMessageController {
#PostMapping(path = "/api/spark2springboot/receive", consumes = "application/json", produces = "application/json")
public void spark2springboot(HttpServletRequest request, HttpServletResponse response,) {
String str = CharStreams.toString(request.getReader());
return str;
}
}
Simply refer to 'https://www.baeldung.com/spring-request-response-body'. For more information, see the official documentation.

Get request attribute with Spring webflux and WebTestClient

I'm not able to bind an attribute that I'm setting from a WebTestClient into a RestController when using Spring WebFlux.
I tried the two ways I could think of.
First using the #RequestAttribute annotation and I got:
Failed to handle request [GET /attributes/annotation]: Response status 400 with reason "Missing request attribute 'attribute' of type String"
Then I tried with the ServerWebExchange and was null.
This is my controller:
#RestController
#RequestMapping("/attributes")
public class MyController {
#GetMapping("/annotation")
public Mono<String> getUsingAnnotation(#RequestAttribute("attribute") String attribute) {
return Mono.just(attribute);
}
#GetMapping("/exchange")
public Mono<String> getUsingExchange(ServerWebExchange exchange) {
return Mono.just(exchange.getRequiredAttribute("attribute"));
}
}
And this is my failing test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyControllerTest {
#Autowired
ApplicationContext context;
WebTestClient webClient;
#Before
public void setup() {
webClient = WebTestClient.bindToApplicationContext(context)
.configureClient()
.build();
}
#Test
public void testGetAttributeUsingAnnotation() {
webClient.get()
.uri("/attributes/annotation")
.attribute("attribute", "value")
.exchange()
.expectStatus()
.isOk();
}
#Test
public void testGetAttributeUsingExchange() {
webClient.get()
.uri("/attributes/exchange")
.attribute("attribute", "value")
.exchange()
.expectStatus()
.isOk();
}
}
In my real application I have a SecurityContextRepository that sets some attributes from a (decoded) header value and I'd like to get those attributes.
I've run into the same issue with a test which previously used MockMvc and then had to be converted to use WebClient. Like #jcfandino I was expecting the .attribute() methods on the WebClient to work similar to MockMvc's requestAttribute().
I haven't found out how .attribute() is meant to be used but I've bypassed the entire problem by adding a custom test filter. I'm not sure if this approach is correct but since this question has been unanswered the approach below may be of help for people running into the same issue.
#WebFluxTest(controllers = SomeController.class)
#ComponentScan({ "com.path1", "com.path2" })
class SomeControllerTest {
// define a test filter emulating the server's filter (assuming there is one)
private class AttributeFilter implements WebFilter {
String attributeValue;
public AttributeFilter(String filterAttributeValue) {
attributeValue = filterAttributeValue;
}
#Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
// add the desired attributes
exchange.getAttributes().put(SomeController.ATTR_NAME, attributeValue);
return chain.filter(exchange);
}
}
// mock the service the controller is dependend on
#MockBean
AppService appService;
// define the test where the controller handles a get() operation
#Test
void testMethod() {
// mock the app service
when(appService.executeService(anyString(), anyString())).thenAnswer(input -> {
// ... return some dummy appData
});
var testClient= WebTestClient.bindToController(new SomeController(appService))
.webFilter(new SomeControllerTest.AttributeFilter("someValue"))
.build();
try {
var response = testClient
.get()
.uri("someroute")
.accept(MediaType.APPLICATION_JSON)
.exchange()
.expectStatus().isOk()
.expectBody(AppData.class);
} catch (Exception e) {
fail("exception caught in testMethod", e);
}
}
}
Both on the server and client side, request attributes should be seen as Map-like data structures that can be used to transfer information within the client/server (for filters, codecs, etc).
That information is not sent over the network.
If you want to send that information from the client to the server, you should take a look at request params or the request body itself.

How to increase timeout AsyncRestTemplate class?

I have developed some async web services with spring framework and REST, I have consumed it from a client created with spring class AsyncRestTemplate. Class return an object ListenableFuture<ResponseEntity<T>> (with the method getForEntity), which brings the value returned by the web service (with the method .get():<T>) . It works fine, however when the web service takes a lot time the method isDone() of ListenableFuture class return a value true, even when the web service has not finished to work.
If I try to recover the web service response with the method get() in the client and It has late a lot of time, I always get the follows message:
"timestamp": "2018-05-29T22:42:26.978+0000",
"status": 500,
"error": "Internal Server Error",
"message": "java.util.concurrent.ExecutionException: org.springframework.web.client.HttpServerErrorException: 503 null",
"path": "/client/result"
Does someone knows how can i solve the problem?. I want the client shows me the web service response, even when the web service takes a lot time (I want to increase the timeout).
The server codes are the following:
Configuration Class:
#Configuration
#EnableAsync
public class ConfigurationClass {
#Bean
public Executor threadPoolTaskExecutor() {
return new ThreadPoolTaskExecutor();
}
}
Controller class:
#RestController
#RequestMapping("/server")
public class ControllerClass {
#GetMapping("/start")
#Async
public CompletableFuture<String> callService() throws InterruptedException{
Thread.sleep(100000L);
return CompletableFuture.completedFuture("OK");
}
}
The client code (consumer) is the following:
#RestController
#RequestMapping("/client")
public class ControllerClass {
private ListenableFuture<ResponseEntity<String>> entity;
#GetMapping("/start")
#Async
public void callService() throws InterruptedException {
AsyncRestTemplate restTemplate = new AsyncRestTemplate();
entity = restTemplate.getForEntity("http://localhost:8080/server/start",
String.class);
}
#GetMapping("/state")
public boolean getState() {
try {
return entity.isDone();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#GetMapping("/result")
public ResponseEntity<String> getResult() {
try {
return entity.get();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
I tried to increase the property timeout in application.property file, but it did not work.
# SPRING MVC (WebMvcProperties)
spring.mvc.async.request-timeout= 500000 # Amount of time before asynchronous request handling times out.
Thanks for your help, Regards.
For a better maintenance, you can config a AsyncRestTemplate bean:
#Bean
public AsyncRestTemplate asyncRestTemplate() {
SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
factory.setTaskExecutor(new SimpleAsyncTaskExecutor());
factory.setConnectTimeout(1000);//milliseconds
factory.setReadTimeout(2000);//milliseconds
return new AsyncRestTemplate(factory);
}
And then, autowired this bean:
#Autowired
private AsyncRestTemplate restTemplate;
After that, update your callService:
#GetMapping("/start")
public void callService() throws InterruptedException {
entity = restTemplate.getForEntity("http://localhost:8080/server/start",
String.class);
}
You can remove the #Async Annotation as the AsyncRestTemplate is Asynchronous.

How to access resource mapped with #RequestMapping with #RestController

I am working on the Spring Boot web application. I am running two different application one for service (Rest Resource) and other one is UI which show the request on HTML page on the bases of response got on the rest request.
My all rest services are created by
#Component
#Api(value = "/api/1/registration", description = "Access and manage your information")
#Path("/api/1/registration")
#Produces(MediaType.APPLICATION_JSON)
#Slf4j
public class RegistrationResource {
...
...
#ApiOperation(value = "Find user by id", notes = "Return a user by id", response = Registration.class)
#Path("/{id}")
#GET
#Timed
#Override
public Registration get(#PathParam("id") String id) {
// my logic
}
}
Restangular Request
Restangular.one("registration/1").get().then(function (data) {
...
},function(error) {
...
});
When I do restangular request from ui, its working fine. Now I need to have a servlet resource. For that I create new resource class
#Slf4j
#RestController
#RequestMapping("/api/1/test")
public class DownloadResource{
#RequestMapping(value = "/downloadtesting", method = RequestMethod.GET)
public void download(HttpServletResponse response, HttpServletRequest request){
// I need to call this method... How can I
}
}
FYI: All my resources are registered as following...
Reflections reflections = new Reflections("com.sample.resource");
Set<Class<? extends Object>> resources =
reflections.getTypesAnnotatedWith(Path.class); //
resources.forEach(r -> {
log.debug("Registering resource " + r);
register(beanFactory.getBean(r));
});
// Same I did for RequestMapping.class also but I am getting 404 error for downloadtesting api.
NOTE: If I try with following version for downloadtesting in RegistrationResource then I am getting HttpServletRequest / Response null.
public class RegistrationResource{
#Path("/downloadtesting")
public void download(HttpServletResponse response, HttpServletRequest request){
// I need to call this method... How can I
}
}
Can any one help me?

Categories

Resources