Spring Integration Java DSL - #ServiceActivator method with #Header parameter annotations - java-8

I have a Spring Integration 4 bean method with the following signature:
#Component
public class AService {
#ServiceActivator
public Message<?> serviceMethod(
Message<?> message,
#Header(ServiceHeader.A_STATE) AState state,
#Header(ServiceHeader.A_ID) String id) {
...
}
...
}
At the moment, I call this service method from within a Spring Integration Java DSL (spring-integration-java-dsl:1.0.1.RELEASE) flow like this:
.handle("aService", "serviceMethod")
This works absolutely fine but, I am wondering whether it is possible to call the service method in the following sort of way:
.handle(Message.class, (m, h) -> aService.serviceMethod(m, h))
The reason I would like to call the service method in this sort of manner is so that, when someone is looking at the code using an IDE such as Eclipse, they can drill into that service's method by for example highlighting the method and pressing F3.
So my question is, is there an alternative way to calling a #ServiceActivator method (which includes #Header annotations) without using strings for service name/service method in a .handle()?

Not exactly, you can't pass the whole message and selected headers, but you can pass the payload and individual headers...
.handle(String.class, (p, h) -> aService().serviceMethod(p,
(AState) h.get(ServiceHeader.A_STATE),
(String) h.get(ServiceHeader.A_ID)))
(assuming the payload is a String).
Note that the #Header annotations are meaningless in this scenario because you are directly pulling out the headers in the lambda expression.
It also doesn't need to be annotated as a #ServiceActivator; you can invoke any bean method that way.
public Message<?> serviceMethod(
String payload, AState state, String id) {
...
}
The
.handle("aService", "serviceMethod")
variation is where all the magic happens (matching the message contents to the method parameters). Of course, we can't do any "magic" when you want to invoke the method directly in Java.

Related

How to set header variables in GraphQL-SPQR

I'm running a GraphQL API using GraphQL-SPQR and Spring Boot.
At the moment, I am throwing RuntimeExceptions to return GraphQL errors. I have a customExceptionHandler that implements DataFetcherExceptionHandler that returns errors in the correct format, as shown below:
class CustomExceptionHandler : DataFetcherExceptionHandler {
override fun onException(handlerParameters: DataFetcherExceptionHandlerParameters?): DataFetcherExceptionHandlerResult {
// get exception
var exception = handlerParameters?.exception
val locations = listOf(handlerParameters?.sourceLocation)
val path = listOf(handlerParameters?.path?.segmentName)
// create a GraphQLError from your exception
if (exception !is GraphQLError) {
exception = CustomGraphQLError(exception?.localizedMessage, locations, path)
}
// cast to GraphQLError
exception as CustomGraphQLError
exception.locations = locations
exception.path = path
val errors = listOf<GraphQLError>(exception)
return DataFetcherExceptionHandlerResult.Builder().errors(errors).build()
}
}
I use the CustomExceptionHandler as follows (in my main application class):
#Bean
fun graphQL(schema: GraphQLSchema): GraphQL {
return GraphQL.newGraphQL(schema)
.queryExecutionStrategy(AsyncExecutionStrategy(CustomExceptionHandler()))
.mutationExecutionStrategy(AsyncSerialExecutionStrategy(CustomExceptionHandler()))
.build()
}
I'd like to set a header variable for a UUID that corresponds to the exception, for logging purposes. How would I do that?
Even better, is it possible to create a Spring Bean that puts the UUID in the header for all queries and mutations?
Thanks!
when you're using spring boot, there's two options:
you're using the spring boot graphql spqr starter (which brings it's own controller to handle all graphQL requests)
you're using plain graphql-spqr and have your own controller to handle GraphQL requests
In any case, you've got a few options:
Making your CustomExceptionHandler a Spring Bean and Autowiring HttpServletResponse
That would probably be the easiest way to go - and it would probably work in any case: You could simply make your CustomExceptionHandler a Spring bean and have it autowire the HttpServletRequest - in the handler method, you could then set it to whatever you would like it to be. Here's some dummy code in Java (sorry, I am not proficient enough in Kotlin):
#Component
class CustomExceptionHandler implements DataFetcherExceptionHandler {
private final HttpServletResponse response;
public CustomExceptionHandler(HttpServletResponse response) {
this.response = response;
}
#Override
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
response.setHeader("X-Request-ID", UUID.randomUUID().toString());
// ... your actual error handling code
}
}
This is going to work because spring will realise that HttpServletRequest differs for each request. It will therefore inject a dynamic proxy into your error handler that will point to the actual HttpServletResponse instance for every request.
I would argue, that it's not the most elegant way, but it will certainly solve your problem.
for the graphql-spqr spring boot starter
There's a default controller implementation that is used in projects using this starter. That controller will handle every graphql request that you receive. You can customise it, by implementing your own GraphQLExecutor and making it a spring bean. That executor is responsible to call the GraphQL engine, pass the parameters in and output the response. Here's the default implementation, that you might want to base your work on.
Similarly to the previous solution, you could autowire the HttpServletResponse in that class and set a HTTP Response header.
That solution would allow you to decide, if you want to set a request id in all cases, or just in specific error cases. (graphql.execute returns an object from which you can get the information if and what errors existed)
when using graphql-spqr without the spring boot starter
Locate your GraphQL controller, add an argument to that method of type HttpServletRequest - and then add headers to that as you prefer (see previous section on some more specific suggestions)

Spring REST #RequestBody consume (XML or JSON) to POJO without annotations

I am writing a Springboot REST endpoint and want to consume XML or JSON requests for a simple service. In either case I want Spring to construct an #RequestBody pojo WITHOUT annotating any of the POJO. Is this OK? Safe? Performant?
I was reading this which told me about configuration by exception. To me this means if I structure my request to contain the exact name and case as the POJO member variables I want to populate the #RequestBody will be able to create my class SomeRequest.
If this is my REST endpoint:
#RequestMapping(value = GET_FOR_SOMETHING, method = RequestMethod.POST,
consumes = {MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE},,
produces = {MediaType.APPLICATION_JSON_VALUE})
public #ResponseBody
StatusResponse<Boolean> getMpdForReqest(#RequestBody SomeRequest request)
And this is my POJO:
public class SomeRequest {
String one;
String two;
public String getOne() {
return one;
}
public void setOne(String one) {
this.one = one;
}
public String getTwo() {
return two;
}
public void setTwo(String two) {
this.two = two;
}
}
My JSON request:
{
"one": "str",
"two": "str"
}
My XML request:
<SomeRequest>
<one>str</one>
<two>str</two>
</SomeRequest>
My question is: why should I not do this or is it perfectly fine?
Thank you all.
TLDR; It is perfectly fine.
Is this OK? Safe? Performant?
Yes, it is as performant as it's annotated cousin, if you take program efficiency into account.
If you take the Programmer efficiency into account, it is much more efficient as the developer doesn't have to deal with a bunch of annotations.
Speaking of Programmer efficiency, I would encourage you to use project Lombok instead of crapping your POJO with bunch of getter and setter methods, that's what cool kids do now a days.
Catch
This will work fine as long as your json fields are one word and small case.
When you have multi-word field name, Java standard is the camelCase and usually JSON standard is the snake_case. In this case, you can either have a Class level Annotation (one per class, so not much ugly). Or, since you are using spring boot, you can use an application wide property (spring.jackson.property-naming-strategy = SNAKE_CASE ).
If you have weird json field names with spaces in between, you might need to use #JsonProperty annotation. Remember, this is a perfectly valid json
{
"just a name with a space" : "123"
}
POJO as RequestBody works perfectly fine. Just note that Spring however will return 400 - Bad Request for every request that can not be mapped to the #RequestBody annoted object.

Spring AOP - annotation with args

I am stuck with a problem in spring boot. I am trying to give extra functionality to some RestControllers, and I am trying to achieve it with some custom annotations. Here is an example.
My annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomAnnotation {
String someArg();
}
My aspect:
#Aspect
#Component
public class MyAspect {
#Around(
value = "#annotation(MyCustomAnnotation)",
argNames = "proceedingJoinPoint,someArg"
)
public Object addMyLogic(ProceedingJoinPoint proceedingJoinPoint, String someArg)
throws Throwable
{
System.out.println(someArg);
return proceedingJoinPoint.proceed();
}
}
My method:
#MyCustomAnnotation(someArg = "something")
#GetMapping("/whatever/route")
public SomeCustomResponse endpointAction(#RequestParam Long someId) {
SomeCustomResult result = someActionDoesNotMatter(someId);
return new SomeCustomResponse(result);
}
Mostly based on the docs (https://docs.spring.io/spring/docs/3.0.3.RELEASE/spring-framework-reference/html/aop.html - 7.2.4.6 Advice parameters) I am pretty sure, it should work.
I am here, because it does not...
What drives me crazy, is that even Intellij, when tries to help with argNames (empty string -> red underline -> alt+enter -> Correct argNames attribute) gives me this, and keeps it red...
Based on the docs, proceedingJoinPoint is not even needed (it does not work without it either): "If the first parameter is of the JoinPoint, ProceedingJoinPoint..."
With the current setup, it says "Unbound pointcut parameter 'someArg'"
At this point, I should also note, that without the args it is working fine.
I have two questions, actually:
Why does this does not work? (That was pretty obvious)
If I would like to give some extra functionality to some controllers, and I would like to parameterise it from the outside, is it the right pattern in spring boot? (With python, it was quite easy to do this with decorators - I am not quite sure, that I am not misguided by the similar syntax)
One example (the example above was pretty abtract):
I would like to create a #LogEndpointCall annotation, and the developer of a route can later just put it on the endpoint that he is developing
...however, it would be nice, if he could add a string (or more likely, an enum) as a parameter
#LogEndpointCall(EndpointCallLogEnum.NotVeryImportantCallWhoCares)
or
#LogEndpointCall(EndpointCallLogEnum.PrettySensitiveCallCheckItALot)
so that the same logic is triggered, but with a different param -> and a save to a different destination will be made.
You cannot directly bind an annotation property to an advice parameter. Just bind the annotation itself and access its parameter normally:
#Around("#annotation(myCustomAnnotation)")
public Object addMyLogic(
ProceedingJoinPoint thisJoinPoint,
MyCustomAnnotation myCustomAnnotation
)
throws Throwable
{
System.out.println(thisJoinPoint + " -> " + myCustomAnnotation.someArg());
return thisJoinPoint.proceed();
}
It will print the something like this with Spring AOP
execution(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something
and something like this with AspectJ (because AJ also knows call joinpoints, not just execution)
call(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something
execution(SomeCustomResponse de.scrum_master.app.Application.endpointAction(Long)) -> something
If you want your method to intercept method that take on consideration args you must explicit mention that in you pointcut expression , to make it work here is what you should do :
#Around(
value = "#annotation(MyCustomAnnotation) && args(someArg)",
argNames = "someArg")
notice that i add && args(someArg), you can add as much arguments as you want, in argNames you can omit proceedingJoinPoint.

Mockito Test method parameters

I have started newly working on testing using mockito. I have 2 questions ...
1. Question
I have method like below with optional and must have parameter so when I call this service method without the must params it should throw Exception.
#RequestMapping( method=RequestMethod.GET, produces={"application/xml", "application/json"})
public ResponseEntity<PResponse> get(#RequestParam(value="params1",required=false) String params1,
#RequestParam(value ="params2",required=false) String params2,
#RequestParam(value= "params3",required=true) String params3,
#RequestParam(value="refresh",required=false) boolean refresh,
#RequestParam(value="params4",required=true) List<String> params4)
{method logic ...}
Here params1,2,refresh are optional and params3,4 are must so when i get request with out params3,4 it should give an error. I am trying to write a test for this using mockito
#Test(expected = RuntimeException.class)
public void throwExceptionIfMissingParams34() throws RuntimeException {
when(myService.get(argThat(new MessagesArgumentMatcher()))).thenThrow(new RuntimeException()) ;
}
I am getting error saying get() in myService can't be applied to expected Parameters:, Actual Arguments:
2. Question :
In the above get method I am calling other method which calls other service method to get data from DB
List<Product> lstProduct = productComponent.readProduct(params3,params4);
which calls
Product product = productReader.readProduct(params3, params4, Product.class);
where in ProductReader.java Service class it gets data from DB by running query. I am trying to test the
List lstProduct = productComponent.readProduct(params3,params4);
in get() method so I tried mocking the Service Object but getting NullPointer Exception when I run the test.
Ad 1. Question
#RequestParam is an annotation from Spring Framework. It's used to define parameters for Controllers. The annotation is used by Spring to map the web request params to arguments which your controller accepts. Testing this behaviour would be actually testing Spring itself. I wouldn't do that. Another thing is, are you really testing a Service, or rather a Controller?
Besides, Java doesn't have the possibility to invoke a method with different arguments than defined, the only possibility is to use varargs.
Ad. Question 2
You didn't specify form where you are getting the NPE. But a first guess would be that you didn't configure Mockito correctly. For example take a look at: NullPointerException in mockito unit test

Spring Framework validate request parameter or path variable

I know I can validate forms in Spring, but can I apply similar validate to URL parameters? For example, I have a method in my controller as follows:
public String edit(#PathVariable("system") String system,
#RequestParam(value="group") String group,
ModelMap model) throws DAOException {
Can I validate the values of system and group before the method is called, to ensure they are of a certain value or match a certain regex?
Thanks
You may be able to use Spring Asserts for this. The Assert api (http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/util/Assert.html) runs a supplied expression against the specified parameters and if the expression equates to false then it throws an exception.
Ex:
Assert.isTrue(system.equals("ValidSystemName"), "You must supply a valid system");
It also contains functions to check that parameters are not null or are not empty strings, etc.
Create an annotation that marks parameters that should be validated. This annotation needs a #Retention of RUNTIME and a #Target of ElementType.PARAMETER.
Create a validator implemented as an AspectJ Aspect.
Wrap calls to controllers with this validator.
A sample annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
#Documented
public #interface ValidSystemParameter {
}
A sample validator:
#Aspect
public class ValidSystemParameterValidator {
#Pointcut("TODO: write your pointcut expression")
public void controllerMethodWithValidSystemParameter();
#Before(pointcut = "controllerMethodWithValidSystemParameter()")
public void validateSystemParameter(String systemParameter) {
// validate the parameter (throwing an exception)
}
}
To learn about the AspectJ pointcut expression language see: http://www.eclipse.org/aspectj/doc/released/progguide/language-joinPoints.html
To learn about AspectJ integration in Spring see: http://static.springsource.org/spring/docs/current/spring-framework-reference/html/aop.html#aop-ataspectj
I might be a little late, but with Spring 3.0 you have the option of using JSR-303 validation with the #Valid annotation. There are also some more specific annotations as #DateTimeFormat and #NumberFormat. More details here: http://static.springsource.org/spring/docs/3.0.5.RELEASE/reference/validation.html#validation-mvc
As I see it you have two options:
Define your request parameters as objects and user JSR-303
validation.
Use the Assert api as mentioned above.
If you just want to make a simple validation on a single value, I would go with the latter (that's what I did when I had simple int values to check for max value).

Resources