ServiceActivator use only one advice instead of 2 advices fro retrying and error filtering - spring-integration

Do I really need to define 2 advices over ServiceActivator (RequestHandlerRetryAdvice) if I need to use RetryTemplate (with AlwaysRetryPolicy) and ExpressionEvaluatingRequestHandlerAdvice where I filter the error that I don't want to retry on?
#Bean
#ServiceActivator(inputChannel = "outboundChannel", adviceChain = {"retry", "filter"})
public MessageHandler handler() {
JdbcMessageHandler ...
}
This works fine, but why can't I do it within one place only?
Or I should override canRetry method of AlwaysRetryPolicy and do this form there?
I tired that (retuned false) but it caused some circular loop.

I think I said you before over here: RetryTemplate with ServiceActivator and JdbcMessageHandler
See different RetryPolicy strategy implementations
I think a BinaryExceptionClassifierRetryPolicy should meet your requirements:
* A policy, that is based on {#link BinaryExceptionClassifier}. Usually, binary
* classification is enough for retry purposes. If you need more flexible classification,
* use {#link ExceptionClassifierRetryPolicy}.
You also can implement a custom RetryPolicy which would delegate to something like BinaryExceptionClassifierRetryPolicy but then do some some other logic according to the SQLException and its error code.
The RequestHandlerRetryAdvice.recoveryCallback may be used to deal with exceptions which were retries or not.

Related

Why there is no #OutboundChannelAdapter annotation in Spring Integration?

1) I would like to create a bean of HttpRequestExecutingMessageHandler (outbound channel adapter for HTTP) and specify the channel via an annotation like #OutboundChannelAdapter, why this is not possible? I suppose there is some design decision that I'm not understanding.
2) What is the suggested way of define HttpRequestExecutingMessageHandler without using XML configuration files? Do I have to configure the bean and set it manually?
Thanks in advance.
The #ServiceActivator fully covers that functionality. Unlike #Transformer it doesn't require a return value. So, your POJO method can be just void and the flow is going to stop there similar way a <outbound-channel-adapter> does that in XML configuration.
But in case of HttpRequestExecutingMessageHandler we need to worry about some extra option to make it one-way and stop there without care about any HTTP reply.
So, for the HttpRequestExecutingMessageHandler you need to declare a bean like:
#Bean
#ServiceActivator(inputChannel = )
public HttpRequestExecutingMessageHandler httpRequestExecutingMessageHandler() {
HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler();
handler.setExpectReply(false)
return handler;
}
I think we need to improve docs on the matter anyway, but you can take a look into Java DSL configuration instead: https://docs.spring.io/spring-integration/docs/current/reference/html/#http-java-config. There is an Http.outboundChannelAdapter() for convenience.

Exception handling in transformer

We are facing an issue while exception is encountered in transformer.
Below is the scenario:
We have a router and a transformer with the below configuration
<bean id="commonMapper"
class="com.example.commonMapper"></bean>
<int:router input-channel="channelA" ref="commonMapper"
method="methodA" />
<int:transformer input-channel="channel_2"
ref="commonMapper" method="methodB"
output-channel="channelC"></int:transformer>
CommonMapper.java :
public String methodA(SomeBean someBean) {
if (<some business condition example someBean.getXValue()>) {
return "channel_1";
} else if(<some condition>) {
return "channel_2"; // Assuming it enters this condition, based on this the above transformer with input-channel="channel_2" gets called
}else if (<some condition>) {
return "channel_3";
} else {
return "channel_4";
}
}
public SomeBean methodB(Message<SomeBean> message)
throws Exception{
SomeBean someBean = message.getPayload();
someBean.setY(10/0); // Purposely introducing an exception
}
While debugging the application, we found that whenever an exception is encountered in methodB(), the control goes back to router reference method i.e. methodA() and again satisfy the condition and calls the transformer (with input-channel="channel_2"). This repeats for certain iteration. And then exception is logged via AnnotationMethodHandlerExceptionResolver -> resolveException.
Below are the queries:
Why does the router gets called again when it encounters an exception in transformer?
Is it the bug or the normal behavior?
How to tackle this issue?
Please let me know if you need any more details around it.
The Spring Integration flow is just a plain Java methods chain call. So, just looks at this like you call something like: foo() -> bar() -> baz(). So, when exception happens in the last one, without any try...catch in the call stack, the control will come back to the foo() and if there is some retry logic, it is going to call the same flow again.
I'm not sure what is your AnnotationMethodHandlerExceptionResolver, but looks like your talk about this one:
Deprecated.
as of Spring 3.2, in favor of ExceptionHandlerExceptionResolver
#Deprecated
public class AnnotationMethodHandlerExceptionResolver
extends AbstractHandlerExceptionResolver
Implementation of the HandlerExceptionResolver interface that handles exceptions through the ExceptionHandler annotation.
This exception resolver is enabled by default in the DispatcherServlet.
This means that you use pretty old Spring. I don't think that it is related though, but your top of the call stack is Spring MVC. You need to take a look there what's going on with the retry.
And answering to all you question at once: yes, this is a normal behavior - see Java call explanation above. You need to debug Spring code from the IDE to figure out what is going on the MVC level

How to set expire-groups-upon-completion with #Aggregator annotation?

The reference manul talked about how to set this in XML, and I saw the Jira issue for advanced configuration for #Aggregator completed but not seeing those advanced properties. So if using annotation, how to set expire group?
Well, according that JIRA ticket there is indeed a sample in the Reference Manual:
#ServiceActivator(inputChannel = "aggregatorChannel")
#Bean
public MessageHandler aggregator(MessageGroupStore jdbcMessageGroupStore) {
AggregatingMessageHandler aggregator =
new AggregatingMessageHandler(new DefaultAggregatingMessageGroupProcessor(),
jdbcMessageGroupStore);
aggregator.setOutputChannel(resultsChannel());
aggregator.setGroupTimeoutExpression(new ValueExpression<>(500L));
aggregator.setTaskScheduler(this.taskScheduler);
return aggregator;
}
And there is an explicit note on the matter:
Annotation configuration (#Aggregator and others) for the Aggregator component covers only simple use cases, where most default options are sufficient. If you need more control over those options using Annotation configuration, consider using a #Bean definition for the AggregatingMessageHandler and mark its #Bean method with #ServiceActivator
Even would be better to use this:
Starting with the version 4.2 the AggregatorFactoryBean is available, to simplify Java configuration for the AggregatingMessageHandler.
Seems for me everything is covered in the Docs. Is anything missed?
I mean the AggregatorFactoryBean has an option you need:
public void setExpireGroupsUponCompletion(Boolean expireGroupsUponCompletion) {
Is that not enough?

Spring Integration - AMQP Inferred Types In Java DSL?

I have been working on a "paved road" for setting up asynchronous messaging between two micro services using AMQP. We want to promote the use of separate domain objects for each service, which means that each service must define their own copy of any objects passed across the queue.
We are using Jackson2JsonMessageConverter on both the producer and the consumer side and we are using the Java DSL to wire the flows to/from the queues.
I am sure there is a way to do this, but it is escaping me: I want the consumer side to ignore the __TypeID__ header that is passed from the producer, as the consumer may have a different representation of that event (and it will likely be in in a different java package).
It appears there was work done such that if using the annotation #RabbitListener, an inferredArgumentTypeargument is derived and will override the header information. This is exactly what I would like to do, but I would like to use the Java DSL to do it. I have not yet found a clean way in which to do this and maybe I am just missing something obvious. It seems it would be fairly straight forward to derive the type when using the following DSL:
return IntegrationFlows
.from(
Amqp.inboundAdapter(factory, queueRemoteTaskStatus())
.concurrentConsumers(10)
.errorHandler(errorHandler)
.messageConverter(messageConverter)
)
.channel(channelRemoteTaskStatusIn())
.handle(listener, "handleRemoteTaskStatus")
.get();
However, this results in a ClassNotFound exception. The only way I have found to get around this, so far, is to set a custom message converter, which requires explicit definition of the type.
public class ForcedTypeJsonMessageConverter extends Jackson2JsonMessageConverter {
ForcedTypeJsonMessageConverter(final Class<?> forcedType) {
setClassMapper(new ClassMapper() {
#Override
public void fromClass(Class<?> clazz, MessageProperties properties) {
//this class is only used for inbound marshalling.
}
#Override
public Class<?> toClass(MessageProperties properties) {
return forcedType;
}
});
}
}
I would really like this to be derived, so the developer does not have to really deal with this.
Is there an easier way to do this?
The simplest way is to configure the Jackson converter's DefaultJackson2JavaTypeMapper with TypeIdMapping (setIdClassMapping()).
On the sending system, map foo:com.one.Foo and on the receiving system map foo:com.two.Foo.
Then, the __TypeId__ header gets foo and the receiving system will map it to its representation of a Foo.
EDIT
Another option would be to add an afterReceiveMessagePostProcessor to the inbound channel adapter's listener container - it could change the __TypeId__ header.

Rendering GORM classes from Spring Boot

I'm trying to write a simple Spring Boot controller that renders a GORM instance and failing.
Here's a shortened version of my code:
#RestController
#RequestMapping("/user")
class UserController {
#RequestMapping(value='/test', method=GET)
User test() {
return new User(username: 'my test username')
}
}
I get the following error message:
Could not write JSON: No serializer found for class org.springframework.validation.DefaultMessageCodesResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: users.domain.User["errors"]->grails.validation.ValidationErrors["messageCodesResolver"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.springframework.validation.DefaultMessageCodesResolver and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: users.domain.User["errors"]->grails.validation.ValidationErrors["messageCodesResolver"])
The error seems to be caused by extra properties injected by GORM. What is the proposed solution for this? Will this eventually be solved in gorm-hibernate4-spring-boot? Should I simply disable SerializationFeature.FAIL_ON_EMPTY_BEANS (I don't have a lot of experience with Jackson so I'm not entirely sure what side effects this may have)? Should I use Jackson's annotations to solve the problem? Any other options?
I've found a way to get rid of the error using this code:
#Component
class ObjectMapperConfiguration implements InitializingBean {
#Autowired
ObjectMapper objectMapper
#Override
void afterPropertiesSet() {
def validationErrorsModule = new SimpleModule()
validationErrorsModule.addSerializer(ValidationErrors, new ErrorsSerializer())
objectMapper.registerModule(validationErrorsModule)
}
}
class ErrorsSerializer extends JsonSerializer<ValidationErrors> {
#Override
void serialize(ValidationErrors errors, JsonGenerator jgen, SerializerProvider provider) {
jgen.writeStartObject()
jgen.writeEndObject()
}
}
Obviously this solution is far from perfect as it simply nukes all validation errors but right now it is good enough for me. I am pretty sure the Spring Boot team will have to address this issue eventually as the GORM objects are also being serialized with some internal Hibernate properties like attached. I'm not accepting this answer as it is not an acceptable solution for most scenarios, it basically just squelches the exception.
This did not work for me.
So I used this instead and the error disappeared.
#JsonIgnoreProperties(["errors"])
I'm using springBootVersion '1.4.1.RELEASE' with gorm & hibernate5:
compile("org.grails:gorm-hibernate5-spring-boot:6.0.3.RELEASE")
I am having to include the following at the top of each domain class in order to use them in a client response (i.e. json serialization using jackson):
#JsonIgnoreProperties(["errors", "metaClass", "dirty", "attached", "dirtyPropertyNames"])
When using springBootVersion '1.3.5.RELEASE' I was able to get away with:
#JsonIgnoreProperties(["errors"])
This is trending in the wrong direction :)

Resources