Spring Cloud Stream validation - java

How to perform validation with Spring Cloud Stream framework in message listeners using standard Spring annotation based validation?
Tried different cases, with #Valid #Payloadfor incoming object, tried method validation post processor with #Validated on entity, but it didn't help.
#StreamListener(MediaItemStream.ITEM_LIKED_CHANNEL)
public void handleLikeMessage(#Valid #Payload LikeInputDto like) {...
and
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
The best approach for now is just using of custom service for validation, but it looks not as wanted..
#Log4j2
#Service
#AllArgsConstructor
public class LikeStreamHandler {
private MediaEventMessagingService mediaEventMessagingService;
private ValidationService validationService;
#StreamListener(MediaItemStream.ITEM_LIKED_CHANNEL)
public void handleLikeMessage(LikeInputDto like) {
validationService.validate(like);
log.debug("Handling LIKE message: {}", like);
mediaEventMessagingService.processLikeEvent(like);
}
}

This is a new Feature of Spring Cloud Stream v2.1.0: Issue on GitHub: "Add (javax.)Validation Support for Stream Listener"

Related

Can we use #RestClientTest when the rest template has interceptors using Spring boot 1.5.x?

I am using Spring Boot 1.5.x (Spring 4.2.x), and I created a RestClientSdk spring component class as shown here:
#Component
public class RestClientSdkImpl implements RestClientSdk {
private RestTemplate restTemplate;
#Autowired
public RestClientSdkImpl(RestTemplateBuilder restTemplateBuilder) {
this.restTemplate = restTemplateBuilder.build();
}
...
//other methods kept out for brevity
}
I have also defined a DefaultRestTemplateCustomizer spring component as shown here:
#Component
public class DefaultRestTemplateCustomizer implements RestTemplateCustomizer {
private LogClientHttpRequestInterceptor logClientHttpRequestInterceptor;
#Autowired
public DefaultRestTemplateCustomizer(LogClientHttpRequestInterceptor logClientHttpRequestInterceptor) {
this.logClientHttpRequestInterceptor = logClientHttpRequestInterceptor;
}
#Override
public void customize(RestTemplate restTemplate) {
restTemplate.getInterceptors().add(logClientHttpRequestInterceptor);
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
}
}
With that, I've defined a test class as shown below that uses the #RestClientTest annotation as shown below.
#RunWith(SpringRunner.class)
#RestClientTest(RestClientSdk.class)
#ActiveProfiles("test")
/*
* The RestClientTest only includes the most minimal configuration to include a rest template builder,
* so we include the rest sdk auto config within the scope of the test
*/
#ImportAutoConfiguration(RestSdkAutoConfiguration.class)
public class RestClientApplicationBehaviourTest{
#Autowired
private RestClientSdk restClientSdk;
#Autowired
private MockRestServiceServer mockRestServiceServer;
/**
* A simple Http Get that retrieves a JSON document from a rest server and
* produces a plain old java object as a response.
*/
#Test
public void testPlainHttpGet() throws IOException{
//ARRANGE
RestClientDto<?> simpleGetRequest = simpleHttpGet();
mockRestServiceServer.expect(once(), requestTo("http://localhost:8080/account/1234567890"))
.andRespond(withSuccess(IOUtils.getInputAsString("/stubs/account.json"),MediaType.APPLICATION_JSON));
//ACT
Account account = restClientSdk.send(simpleGetRequest, Account.class);
//ASSERT
mockRestServiceServer.verify();
Assert.assertNotNull(account);
Assert.assertNotNull(account.getAccountId());
Assert.assertNotNull(account.getFirstName());
Assert.assertNotNull(account.getLastName());
}
...
//not including other methods for brevity
}
PROBLEM
Because the MockRestServiceServer builder overrides the BufferingClientHttpRequestFactory in my rest template with a MockClientHttpRequestFactory, I am getting a null response from my body. This is because the logging interceptor is reading the input stream coming from the response and as such the stream no longer has content to read. The BufferingClientHttpRequestFactory would prevent that from happening. Now, I know that as of Spring 5.0.5, there is an extra option in the MockRestServiceServer builder called bufferContent, but I don't have the option of moving to Spring 5.x (Spring Boot 2.x), so I was wondering if there is a way to get this configured using Spring Boot 1.5.x / Spring 4.2.x.
I thank you in advance!
Juan
In order to workaround the issue, I needed to define a test configuration, that would allow me to override the client request factory. Please see the code below. It is a bit hacky, but I suppose the real solution here would be to upgrade to Spring 5.x / Spring Boot 2.x.
#Configuration
#Profile("test")
public class MockRestServiceServerConfiguration {
/**
* Wrap the Mock Rest client factory in the buffered one.
* #param restClientSdk The rest client SDK containing the rest template to use.
* #return The mock rest service server to use.
*/
#Bean
public MockRestServiceServer mockRestServiceServer(RestClientSdkImpl restClientSdkImpl) {
RestTemplate restTemplate = restClientSdkImpl.getRestTemplate();
MockRestServiceServer server = MockRestServiceServer.createServer(restTemplate);
//need to do this because getRequestFactory returns InterceptingHttpRequestFactory wraping the mock rest service server request factory
List<ClientHttpRequestInterceptor> templateInterceptors = restTemplate.getInterceptors();
restTemplate.setInterceptors(null);
//now we wrap the delegate, which should be the mock rest service server request factory
BufferingClientHttpRequestFactory bufferingFact = new BufferingClientHttpRequestFactory(restTemplate.getRequestFactory());
restTemplate.setRequestFactory(bufferingFact);
restTemplate.setInterceptors(templateInterceptors);
return server;
}
}

Both JWT and form auth in one SpringBoot app?

I have a spring boot app that uses spring security.I implemented form based auth and it works well. I want the app to serve as the backend of an angular app i've built.I know about CORS but how can i add JWT auth to the existing spring boot app,is it recommended.
I would recommend AOP concept to authenticate/validate your jwt token.
First thing you need to create a custom annotation. Named it as JWTsecured
#Component
#Target(value = {ElementType.METHOD, ElementType.TYPE})
#Retention(value = RetentionPolicy.RUNTIME)
public #interface JwtSecured {
}
Now You have a controller in which your api need to be JWT secured.
#RestController
public class YourController {
#GetMapping(value="/testApi")
#JWTsecured
public void isTestApi() {
}
}
Now you have to write an aspect to validate your token...
#Component
#Aspect
public class JWTsecuredAspect {
#Around(value =" #within(com.JWTSecured) || #annotation(com.JWTSecured)")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
String token = request.getHeader("Authorization");
if(!isTokenValidated(token)){
throw CustomException("Invalid Token.")
}
}
}
This is how you can use it along with auth.
There are several other ways.
Feel free to contact

Spring Integration - Get info from 3rd party services using replyChannel

Im new to Spring Integration, I have to get a list of online agents from 3rd party web services, i tried to configure spring integration to get it, but for the channel part, i not really sure how to configure it.
My original configuration was the following, i copied from a sample that use to send request to 3rd party web services:
public interface WebServiceGateway {
#Gateway(requestChannel = "getStatusChannel")
public String getStatus(String var); <------ being forced to send something
}
In my integration configuration,
#Configuration
public class IntegrationConfiguration {
#Bean
public MessageChannel getStatusChannel() {
return MessageChannels.direct().get();
}
}
The problem is, im not sending any parameter to the webservices, in requestChannel it force me to do so, so i modified the gateway part:
public interface WebServiceGateway {
#Gateway(replyChannel = "getStatusChannel")
public String getStatus();
}
This part remains unchanged:
#Configuration
public class IntegrationConfiguration {
#Bean
public MessageChannel getStatusChannel() {
return MessageChannels.direct().get();
}
}
It prompted me java.lang.IllegalStateException: receive is not supported, because no pollable reply channel has been configured, why can't i use MessageChannel as the reply channel? How should i configure the IntegrationConfiguration?
Please go through this https://spring.io/blog/2014/11/25/spring-integration-java-dsl-line-by-line-tutorial
All you need is to define an IntegrationFlow like below:
IntegrationFlows.from(requestchannel())
.handle("requestHandler","handleInput")
.channel(replyChannel())
.get();

How to invoke Restful WebService in Spring Boot

I have a SB service that is being used to send email. I wanted to use that in my existing application , how can I do that? I am thinking to create a controller that handles incoming HttpRequest and HttpResponse. But still no idea on how my existing application will invoke it. I need some high level overview too on how exactly SB application will run independently with other application.
P.S.- there is no UI interface for the email service so i wont be mapping url like we do in controllers generally.
Here is my sample email service:
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
I created a controller like this earlier to test this out:
#RestController
public class CourseController {
#Autowired
private EmailService emailService;
#RequestMapping(value = "/sendEmail", method = RequestMethod.POST)
public void sendEmail() {
emailService.sendMessage("abc#gmail.com","{client#gmail.com}", "testSubject",new Context);
}
Context has some business data.
I have a jsp that I am using and posting my form through which it is mapping. It all works fine.
But now I want to integrate this with my existing application (its on struts 1)so there wont be any uri to map. There must be some kind of HttpRequest need to be created from the invoking application and my controller should be handling it. How can I achieve this?
You have already this service implemented? Then you need a RestController class that mapps the uri of your choice. In this class you need to inject the service class that realizes your email sending method. Is this class annotated with #Service? Quite difficult to explain without seeing your code. Here an example for a REST-Interface:
#RestController
#RequestMapping("/api/v1/email")
public class RestClass {
private EmailService emailService;
#Autowired
public RestClass(EmailService emailService){
this.emailService = emailService;
}
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> sendEmail(#RequestBody EmailDTO emailDTO){
String emailAdress = emailDTO.getEmail();
this.emailService.sendEmail(emailAdress);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}
So in this case the emailService would be the class that has the method that sends your email. This class should be annotated with #Service. Hope that helps.
Here your existing class:
#Service
public class EmailService {
public HashMap<String, String> sendMessage(String emailFrom, String[] emailToList, String subject, Context ctx) {
...../*Business Logic*/
}
}
And in case the injection doesn't work you have to annotate your application class with #ComponentScan({"com.foo.dal.","com.foo.notification."}) replace this packages simply with the package of your service and resource class.
I am not sure about the problem. If I am right that you need to call a rest service from your application. In this case it is lot easier and convenient to use Spring's RestTemplate link
You can get some overview here

Exception using Spring Data JPA and QueryDsl via REST Controller

I'm trying to implement a controller method similar to how is documented in the latest Gosling release train of Spring Data that supports QueryDsl. I've implemented the controller as shown in the example in the docs at http://docs.spring.io/spring-data/jpa/docs/1.9.0.RELEASE/reference/html/#core.web.type-safe. Everything compiles and when I start the application (using Spring Boot 1.2.5.RELEASE), everything starts fine.
However, when I try to call my rest endpoint, I always get the following exception:
org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.mysema.query.types.Predicate]: Specified class is an interface
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:101)
at org.springframework.web.method.annotation.ModelAttributeMethodProcessor.createAttribute(ModelAttributeMethodProcessor.java:137)
at org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor.createAttribute(ServletModelAttributeMethodProcessor.java:80)
My guess is that the QuerydslPredicateArgumentResolver is not being applied to the request, and thus the exception. But I see that the QuerydslPredicateArgumentResolver is registered as a bean when I query the Spring Boot manage endpoint /manage/beans. I have also ensured that #EnableSpringDataWebSupport is on my #Configuration class to no effect.
I have the controller annotated with #BasePathAwareController, since I'm using this with Spring Data REST and I want the methods to be under a similar path as the ones that Spring Data REST exposes. I also tried using #RepositoryRestController, but that didn't seem to matter. However, when using #RestController and putting it under a path that was different then the base path that Spring Data REST is using, things worked. So the question is, should it work?
The entire controller right now is:
#RestController
#RequestMapping(value = "/query")
public class AvailController
{
private final AvailRepository repo;
#Autowired
public AvailController(AvailRepository repository)
{
this.repo = repository;
}
#RequestMapping(value = "/avails", method = GET)
public #ResponseBody Page<Avail> getAvails(Model model,
#QuerydslPredicate(root = Avail.class) Predicate predicate,
Pageable pageable,
#RequestParam MultiValueMap<String, String> parameters)
{
return repo.findAll(predicate, pageable);
}
}
I had the same problem with instantiation of Predicate. In the example:
#Controller
#RequiredArgsConstructor(onConstructor = #__(#Autowired) )
class UserController {
private final UserRepository repository;
#RequestMapping(value = "/", method = RequestMethod.GET)
String index(Model model, //
#QuerydslPredicate(root = User.class) Predicate predicate, //
#PageableDefault(sort = { "lastname", "firstname" }) Pageable pageable, //
#RequestParam MultiValueMap<String, String> parameters) {
(...)
(https://github.com/spring-projects/spring-data-examples/blob/master/web/querydsl/src/main/java/example/users/web/UserController.java#L42 ) is using just #Controller and I was using #RepositoryRestController, that seems to be the reason. #RestController also works for me.
I created https://jira.spring.io/browse/DATAREST-838
I also had this issue when trying to implement a custom controller that mimics the returned value as Spring Data REST. I wanted to inject QuerydslPredicate to the controller method and got the annoying 'BeanInstantiationException'.
I found a work around for this by adding the following configuration file to my application:
#Configuration
#Order(Ordered.HIGHEST_PRECEDENCE )
public class MvcConfig extends WebMvcConfigurerAdapter {
#Autowired
#Qualifier("repositoryExporterHandlerAdapter")
RequestMappingHandlerAdapter repositoryExporterHandlerAdapter;
#Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
List<HandlerMethodArgumentResolver> customArgumentResolvers = repositoryExporterHandlerAdapter.getCustomArgumentResolvers();
argumentResolvers.addAll(customArgumentResolvers);
}
}
See here for reference: https://jira.spring.io/browse/DATAREST-657

Categories