Skip to content

Commit 7073253

Browse files
garyrussellartembilan
authored andcommitted
GH-3418: Fix Poller Undeclared Checked Exceptions
Resolves #3418 `Class.newInstance()` can propagate checked exceptions that are not declared. **cherry-pick/back-port to 5.3.x, 5.2.x, 4.3.x**
1 parent 6fd1e72 commit 7073253

File tree

2 files changed

+119
-2
lines changed

2 files changed

+119
-2
lines changed

spring-integration-core/src/main/java/org/springframework/integration/endpoint/AbstractPollingEndpoint.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import org.springframework.util.ClassUtils;
5454
import org.springframework.util.CollectionUtils;
5555
import org.springframework.util.ErrorHandler;
56+
import org.springframework.util.ReflectionUtils;
5657

5758
import reactor.core.publisher.Flux;
5859
import reactor.core.publisher.Mono;
@@ -354,7 +355,7 @@ private Message<?> pollForMessage() {
354355

355356
private Message<?> doPoll() {
356357
IntegrationResourceHolder holder = bindResourceHolderIfNecessary(getResourceKey(), getResourceToBind());
357-
Message<?> message;
358+
Message<?> message = null;
358359
try {
359360
message = receiveMessage();
360361
}
@@ -366,7 +367,7 @@ private Message<?> doPoll() {
366367
return null;
367368
}
368369
else {
369-
throw (RuntimeException) e;
370+
ReflectionUtils.rethrowRuntimeException(e);
370371
}
371372
}
372373

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/*
2+
* Copyright 2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.integration.endpoint;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import java.io.IOException;
22+
import java.lang.reflect.UndeclaredThrowableException;
23+
import java.util.concurrent.CountDownLatch;
24+
import java.util.concurrent.TimeUnit;
25+
26+
import org.aopalliance.intercept.MethodInterceptor;
27+
import org.junit.jupiter.api.Test;
28+
29+
import org.springframework.beans.factory.annotation.Autowired;
30+
import org.springframework.context.annotation.Bean;
31+
import org.springframework.context.annotation.Configuration;
32+
import org.springframework.integration.config.EnableIntegration;
33+
import org.springframework.integration.core.MessageSource;
34+
import org.springframework.integration.dsl.IntegrationFlow;
35+
import org.springframework.integration.dsl.IntegrationFlows;
36+
import org.springframework.integration.dsl.Pollers;
37+
import org.springframework.lang.Nullable;
38+
import org.springframework.messaging.Message;
39+
import org.springframework.test.annotation.DirtiesContext;
40+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
41+
42+
/**
43+
* @author Gary Russell
44+
* @since 4.3.23
45+
*
46+
*/
47+
@SpringJUnitConfig
48+
@DirtiesContext
49+
public class SourcePollingChannelAdapterTests {
50+
51+
@Autowired
52+
Config config;
53+
54+
@Test
55+
void undeclaredCheckedException() throws InterruptedException {
56+
assertThat(this.config.latchl.await(10, TimeUnit.SECONDS)).isTrue();
57+
assertThat(this.config.caught).isInstanceOf(UndeclaredThrowableException.class);
58+
}
59+
60+
@Configuration
61+
@EnableIntegration
62+
public static class Config {
63+
64+
volatile Exception caught;
65+
66+
volatile CountDownLatch latchl = new CountDownLatch(1);
67+
68+
@Bean
69+
public IntegrationFlow flow() {
70+
return IntegrationFlows.from(new BadMessageSource(), e -> e.poller(Pollers.fixedDelay(500)
71+
.advice(exceptionCaptor())))
72+
.nullChannel();
73+
}
74+
75+
private MethodInterceptor exceptionCaptor() {
76+
return invocation -> {
77+
try {
78+
return invocation.proceed();
79+
}
80+
catch (Exception e) {
81+
Config.this.caught = e;
82+
throw e;
83+
}
84+
finally {
85+
Config.this.latchl.countDown();
86+
}
87+
};
88+
}
89+
90+
}
91+
92+
public static class BadMessageSource implements MessageSource<String> {
93+
94+
@Override
95+
@Nullable
96+
public Message<String> receive() {
97+
try {
98+
Foo.class.newInstance();
99+
}
100+
catch (InstantiationException | IllegalAccessException e) {
101+
}
102+
return null;
103+
}
104+
105+
}
106+
107+
public static class Foo {
108+
109+
public Foo() throws IOException {
110+
throw new IOException();
111+
}
112+
113+
}
114+
115+
}
116+

0 commit comments

Comments
 (0)