3535import org .springframework .resilience .annotation .EnableResilientMethods ;
3636
3737import static org .assertj .core .api .Assertions .assertThat ;
38+ import static org .assertj .core .api .Assertions .assertThatExceptionOfType ;
39+ import static org .assertj .core .api .Assertions .assertThatIllegalStateException ;
3840
3941/**
4042 * @author Juergen Hoeller
4143 * @author Hyunsang Han
44+ * @author Sam Brannen
4245 * @since 7.0
4346 */
4447class ConcurrencyLimitTests {
@@ -61,12 +64,7 @@ void withSimpleInterceptor() {
6164
6265 @ Test
6366 void withPostProcessorForMethod () {
64- DefaultListableBeanFactory bf = new DefaultListableBeanFactory ();
65- bf .registerBeanDefinition ("bean" , new RootBeanDefinition (AnnotatedMethodBean .class ));
66- ConcurrencyLimitBeanPostProcessor bpp = new ConcurrencyLimitBeanPostProcessor ();
67- bpp .setBeanFactory (bf );
68- bf .addBeanPostProcessor (bpp );
69- AnnotatedMethodBean proxy = bf .getBean (AnnotatedMethodBean .class );
67+ AnnotatedMethodBean proxy = createProxy (AnnotatedMethodBean .class );
7068 AnnotatedMethodBean target = (AnnotatedMethodBean ) AopProxyUtils .getSingletonTarget (proxy );
7169
7270 List <CompletableFuture <?>> futures = new ArrayList <>(10 );
@@ -77,14 +75,22 @@ void withPostProcessorForMethod() {
7775 assertThat (target .current ).hasValue (0 );
7876 }
7977
78+ @ Test
79+ void withPostProcessorForMethodWithUnboundedConcurrency () {
80+ AnnotatedMethodBean proxy = createProxy (AnnotatedMethodBean .class );
81+ AnnotatedMethodBean target = (AnnotatedMethodBean ) AopProxyUtils .getSingletonTarget (proxy );
82+
83+ List <CompletableFuture <?>> futures = new ArrayList <>(10 );
84+ for (int i = 0 ; i < 10 ; i ++) {
85+ futures .add (CompletableFuture .runAsync (proxy ::unboundedConcurrency ));
86+ }
87+ CompletableFuture .allOf (futures .toArray (new CompletableFuture [0 ])).join ();
88+ assertThat (target .current ).hasValue (10 );
89+ }
90+
8091 @ Test
8192 void withPostProcessorForClass () {
82- DefaultListableBeanFactory bf = new DefaultListableBeanFactory ();
83- bf .registerBeanDefinition ("bean" , new RootBeanDefinition (AnnotatedClassBean .class ));
84- ConcurrencyLimitBeanPostProcessor bpp = new ConcurrencyLimitBeanPostProcessor ();
85- bpp .setBeanFactory (bf );
86- bf .addBeanPostProcessor (bpp );
87- AnnotatedClassBean proxy = bf .getBean (AnnotatedClassBean .class );
93+ AnnotatedClassBean proxy = createProxy (AnnotatedClassBean .class );
8894 AnnotatedClassBean target = (AnnotatedClassBean ) AopProxyUtils .getSingletonTarget (proxy );
8995
9096 List <CompletableFuture <?>> futures = new ArrayList <>(30 );
@@ -122,17 +128,52 @@ void withPlaceholderResolution() {
122128 ctx .close ();
123129 }
124130
131+ @ Test
132+ void configurationErrors () {
133+ ConfigurationErrorsBean proxy = createProxy (ConfigurationErrorsBean .class );
134+
135+ assertThatIllegalStateException ()
136+ .isThrownBy (proxy ::emptyDeclaration )
137+ .withMessageMatching ("@.+?ConcurrencyLimit(.+?) must be configured with a valid limit" )
138+ .withMessageContaining ("\" \" " )
139+ .withMessageContaining (String .valueOf (Integer .MIN_VALUE ));
140+
141+ assertThatIllegalStateException ()
142+ .isThrownBy (proxy ::negative42Int )
143+ .withMessageMatching ("@.+?ConcurrencyLimit(.+?) must be configured with a valid limit" )
144+ .withMessageContaining ("-42" );
145+
146+ assertThatIllegalStateException ()
147+ .isThrownBy (proxy ::negative42String )
148+ .withMessageMatching ("@.+?ConcurrencyLimit(.+?) must be configured with a valid limit" )
149+ .withMessageContaining ("-42" );
150+
151+ assertThatExceptionOfType (NumberFormatException .class )
152+ .isThrownBy (proxy ::alphanumericString )
153+ .withMessageContaining ("B2" );
154+ }
155+
156+
157+ private static <T > T createProxy (Class <T > beanClass ) {
158+ DefaultListableBeanFactory bf = new DefaultListableBeanFactory ();
159+ bf .registerBeanDefinition ("bean" , new RootBeanDefinition (beanClass ));
160+ ConcurrencyLimitBeanPostProcessor bpp = new ConcurrencyLimitBeanPostProcessor ();
161+ bpp .setBeanFactory (bf );
162+ bf .addBeanPostProcessor (bpp );
163+ return bf .getBean (beanClass );
164+ }
165+
125166
126167 static class NonAnnotatedBean {
127168
128- AtomicInteger counter = new AtomicInteger ();
169+ final AtomicInteger counter = new AtomicInteger ();
129170
130171 public void concurrentOperation () {
131172 if (counter .incrementAndGet () > 2 ) {
132173 throw new IllegalStateException ();
133174 }
134175 try {
135- Thread .sleep (100 );
176+ Thread .sleep (10 );
136177 }
137178 catch (InterruptedException ex ) {
138179 throw new IllegalStateException (ex );
@@ -144,37 +185,48 @@ public void concurrentOperation() {
144185
145186 static class AnnotatedMethodBean {
146187
147- AtomicInteger current = new AtomicInteger ();
188+ final AtomicInteger current = new AtomicInteger ();
148189
149190 @ ConcurrencyLimit (2 )
150191 public void concurrentOperation () {
151192 if (current .incrementAndGet () > 2 ) {
152193 throw new IllegalStateException ();
153194 }
154195 try {
155- Thread .sleep (100 );
196+ Thread .sleep (10 );
156197 }
157198 catch (InterruptedException ex ) {
158199 throw new IllegalStateException (ex );
159200 }
160201 current .decrementAndGet ();
161202 }
203+
204+ @ ConcurrencyLimit (limit = -1 )
205+ public void unboundedConcurrency () {
206+ current .incrementAndGet ();
207+ try {
208+ Thread .sleep (10 );
209+ }
210+ catch (InterruptedException ex ) {
211+ throw new IllegalStateException (ex );
212+ }
213+ }
162214 }
163215
164216
165217 @ ConcurrencyLimit (2 )
166218 static class AnnotatedClassBean {
167219
168- AtomicInteger current = new AtomicInteger ();
220+ final AtomicInteger current = new AtomicInteger ();
169221
170- AtomicInteger currentOverride = new AtomicInteger ();
222+ final AtomicInteger currentOverride = new AtomicInteger ();
171223
172224 public void concurrentOperation () {
173225 if (current .incrementAndGet () > 2 ) {
174226 throw new IllegalStateException ();
175227 }
176228 try {
177- Thread .sleep (100 );
229+ Thread .sleep (10 );
178230 }
179231 catch (InterruptedException ex ) {
180232 throw new IllegalStateException (ex );
@@ -187,7 +239,7 @@ public void otherOperation() {
187239 throw new IllegalStateException ();
188240 }
189241 try {
190- Thread .sleep (100 );
242+ Thread .sleep (10 );
191243 }
192244 catch (InterruptedException ex ) {
193245 throw new IllegalStateException (ex );
@@ -201,7 +253,7 @@ public void overrideOperation() {
201253 throw new IllegalStateException ();
202254 }
203255 try {
204- Thread .sleep (100 );
256+ Thread .sleep (10 );
205257 }
206258 catch (InterruptedException ex ) {
207259 throw new IllegalStateException (ex );
@@ -218,15 +270,15 @@ static class PlaceholderTestConfig {
218270
219271 static class PlaceholderBean {
220272
221- AtomicInteger current = new AtomicInteger ();
273+ final AtomicInteger current = new AtomicInteger ();
222274
223275 @ ConcurrencyLimit (limitString = "${test.concurrency.limit}" )
224276 public void concurrentOperation () {
225277 if (current .incrementAndGet () > 3 ) { // Assumes test.concurrency.limit=3
226278 throw new IllegalStateException ();
227279 }
228280 try {
229- Thread .sleep (100 );
281+ Thread .sleep (10 );
230282 }
231283 catch (InterruptedException ex ) {
232284 throw new IllegalStateException (ex );
@@ -235,4 +287,24 @@ public void concurrentOperation() {
235287 }
236288 }
237289
290+
291+ static class ConfigurationErrorsBean {
292+
293+ @ ConcurrencyLimit
294+ public void emptyDeclaration () {
295+ }
296+
297+ @ ConcurrencyLimit (-42 )
298+ public void negative42Int () {
299+ }
300+
301+ @ ConcurrencyLimit (limitString = "-42" )
302+ public void negative42String () {
303+ }
304+
305+ @ ConcurrencyLimit (limitString = "B2" )
306+ public void alphanumericString () {
307+ }
308+ }
309+
238310}
0 commit comments