Spring Integration Java DSL中有没有办法修改现有的邮件头?
我正在使用SI Java DSL重新实现下载重试机制,并希望在发生故障时增加一个包含下载尝试的邮件头,然后根据与限制相比的尝试次数来路由邮件。
基于SI附带的RouterTests,我的路由工作很好。使用HeaderEnrichers,我可以轻松添加标题,但我无法看到修改现有标题的方法。
由于
/**
* Unit test of {@link RetryRouter}.
*
* Based on {@link RouterTests#testMethodInvokingRouter2()}.
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class RetryRouterTests {
/** Failed download attempts are sent to this channel to be routed by {@link ContextConfiguration#failedDownloadRouting( ) } */
@Autowired
@Qualifier("failed")
private MessageChannel failed;
/** Retry attempts for failed downloads are sent to this channel by {@link ContextConfiguration#failedDownloadRouting( ) }*/
@Autowired
@Qualifier("retry-channel")
private PollableChannel retryChannel;
/** Failed download attempts which will not be retried, are sent to this channel by {@link ContextConfiguration#failedDownloadRouting( ) }*/
@Autowired
@Qualifier("exhausted-channel")
private PollableChannel exhaustedChannel;
/**
* Unit test of {@link ContextConfiguration#failedDownloadRouting( ) } and {@link RetryRouter}.
*/
@Test
public void retryRouting() {
final int limit = 2;
for ( int attempt = 0 ; attempt <= limit + 1 ; attempt++ ){
this.failed.send( failed( attempt, limit) );
if ( attempt < limit){
assertEquals( payload( attempt ) , this.retryChannel.receive( ).getPayload( ) );
assertNull(this.exhaustedChannel.receive( 0 ) );
}else{
assertEquals( payload( attempt ) , this.exhaustedChannel.receive( ).getPayload( ) );
assertNotNull( this.exhaustedChannel.receive( ).getPayload( ) );
}
}
}
private Message<String> failed( int retry , int limit ) {
return MessageBuilder
.withPayload( payload( retry ) )
.setHeader("retries", new AtomicInteger( retry ) )
.setHeader("limit", limit)
.build();
}
private String payload (int retry){
return "retry attempt "+retry;
}
@Configuration
@EnableIntegration
public static class ContextConfiguration {
@Bean
public MessageChannel loggerChannel() {
return MessageChannels.direct().get();
}
@Bean(name = "retry-channel")
public MessageChannel retryChannel() {
return new QueueChannel();
}
@Bean(name = "exhausted-channel")
public MessageChannel exhaustedChannel() {
return new QueueChannel();
}
/**
* Decides if a failed download attempt can be retried or not, based upon the number of attempts already made
* and the limit to the number of attempts that may be made. Logic is in {@link RetryRouter}.
* <p>
* The number of download attempts already made is provided as a header {@link #attempts} from the connector doing the download,
* and the limit to the number of attempts is another header {@link #retryLimit} which is originally setup as
* a header by {@link DownloadDispatcher} from retry configuration.
* <p>
* Messages for failed download attempts are listened to on channel {@link #failed}.
* Retry attempts are routed to {@link #retryChannel()}
*
* @return
*/
@Bean
public IntegrationFlow failedDownloadRouting() {
return IntegrationFlows.from( "failed" )
.handle( "headers.retries.getAndIncrement()" )
.handle( logMessage ( "failed" ) )
.route(new RetryRouter())
.get();
}
/**
* Decides if a failed download attempt can be retried or not, based upon the number of attempts already made
* and the limit to the number of attempts that may be made.
* <p>
*/
private static class RetryRouter {
@Router
public String routeByHeader(@Header("retries") AtomicInteger attempts , @Header("limit") Integer limit) {
if (attempts.intValue() < limit.intValue()){
return "retry-channel";
}
return "exhausted-channel";
}
/** This method is not used but is required by Spring Integration otherwise application context doesn't load because of
* {@code Caused by: java.lang.IllegalArgumentException: Target object of type
* [class org.springframework.integration.dsl.test.routers.RetryRouterTests$RetryRouter] has no eligible methods for handling Messages.}
*
* @throws UnsupportedOperationException if called
*/
@SuppressWarnings("unused")
public String routeMessage(Message<?> message) {
throw new UnsupportedOperationException( "should not be used." );
}
}
}
答案 0 :(得分:1)
有一种方法可以做你需要的而无需修改标题:
.enrichHeaders(h -> h.header("downloadRetries", new AtomicInteger()))
然后当你需要增加它时,你应该这样做:
.handle(m -> m.getHeaders().get("downloadRetries", AtomicInteger.class).getAndIncrement())
并将此句柄作为重试服务的发布 - 订阅者 - 频道上的第一个单向第一个订阅者。
<强>更新强>
是单向'MessageHandler',不适合配置'outputChannel'。这是整合流程的结束。
感谢您分享关于此问题的配置:现在我遇到了一个问题并且您误解了。解决方案必须是这样的:
return IntegrationFlows.from( "failed" )
.publishSubscribeChannel(s -> s
.subscribe(f -> f
.handle(m -> m.getHeaders().get("downloadRetries",
AtomicInteger.class).getAndIncrement()))
.handle( logMessage ( "failed" ) )
.route(new RetryRouter())
.get();
}
如果我们有PublishSubscribeChannel
,则子流中的.subscribe()
是第一个订户的第一个订户,主流中的.handle( logMessage ( "failed" ) )
是第二个订户。在完成第一个订户的工作之前,不会调用最后一个。
有关详细信息,请参阅Spring Integration Reference Manual和Java DSL Manual。
答案 1 :(得分:0)
以下代码可以正常工作
.handle( new GenericHandler<Message<String>>() {
@Override
public Object handle( Message<String> payload , Map<String,Object> headers ) {
((AtomicInteger)headers.get( "retries" )).getAndIncrement();
return payload;
}})