Skip to content

Commit 1532495

Browse files
committed
Align int and long in RandomValuePropertySource
This commit aligns int and long so that a random number is generated by delegating to ints/longs in the JDK's Random API. In the case of a single bound value, it needs to be greater than 0 because 0 is used as the lower bound. Fixes gh-26628
1 parent 3f23b92 commit 1532495

File tree

2 files changed

+107
-14
lines changed

2 files changed

+107
-14
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySource.java

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,15 +16,21 @@
1616

1717
package org.springframework.boot.env;
1818

19+
import java.util.OptionalInt;
20+
import java.util.OptionalLong;
1921
import java.util.Random;
2022
import java.util.UUID;
23+
import java.util.function.BiPredicate;
24+
import java.util.function.Function;
25+
import java.util.function.Predicate;
2126

2227
import org.apache.commons.logging.Log;
2328
import org.apache.commons.logging.LogFactory;
2429

2530
import org.springframework.core.env.ConfigurableEnvironment;
2631
import org.springframework.core.env.PropertySource;
2732
import org.springframework.core.env.StandardEnvironment;
33+
import org.springframework.util.Assert;
2834
import org.springframework.util.DigestUtils;
2935
import org.springframework.util.StringUtils;
3036

@@ -44,11 +50,13 @@
4450
* suffix whose syntax is:
4551
* <p>
4652
* {@code OPEN value (,max) CLOSE} where the {@code OPEN,CLOSE} are any character and
47-
* {@code value,max} are integers. If {@code max} is provided then {@code value} is the
48-
* minimum value and {@code max} is the maximum (exclusive).
53+
* {@code value,max} are integers. If {@code max} is not provided, then 0 is used as the
54+
* lower bound and {@code value} is the upper bound. If {@code max} is provided then
55+
* {@code value} is the minimum value and {@code max} is the maximum (exclusive).
4956
*
5057
* @author Dave Syer
5158
* @author Matt Benson
59+
* @author Madhura Bhave
5260
* @since 1.0.0
5361
*/
5462
public class RandomValuePropertySource extends PropertySource<Random> {
@@ -113,22 +121,21 @@ private String getRange(String type, String prefix) {
113121
}
114122

115123
private int getNextIntInRange(String range) {
116-
String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
117-
int start = Integer.parseInt(tokens[0]);
118-
if (tokens.length == 1) {
119-
return getSource().nextInt(start);
124+
Range<Integer> intRange = Range.get(range, Integer::parseInt, (t) -> t > 0, 0, (t1, t2) -> t1 < t2);
125+
OptionalInt first = getSource().ints(1, intRange.getMin(), intRange.getMax()).findFirst();
126+
if (!first.isPresent()) {
127+
throw new RuntimeException("Could not get random number for range '" + range + "'");
120128
}
121-
return start + getSource().nextInt(Integer.parseInt(tokens[1]) - start);
129+
return first.getAsInt();
122130
}
123131

124132
private long getNextLongInRange(String range) {
125-
String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
126-
if (tokens.length == 1) {
127-
return Math.abs(getSource().nextLong() % Long.parseLong(tokens[0]));
133+
Range<Long> longRange = Range.get(range, Long::parseLong, (t) -> t > 0L, 0L, (t1, t2) -> t1 < t2);
134+
OptionalLong first = getSource().longs(1, longRange.getMin(), longRange.getMax()).findFirst();
135+
if (!first.isPresent()) {
136+
throw new RuntimeException("Could not get random number for range '" + range + "'");
128137
}
129-
long lowerBound = Long.parseLong(tokens[0]);
130-
long upperBound = Long.parseLong(tokens[1]) - lowerBound;
131-
return lowerBound + Math.abs(getSource().nextLong() % upperBound);
138+
return first.getAsLong();
132139
}
133140

134141
private Object getRandomBytes() {
@@ -143,4 +150,39 @@ public static void addToEnvironment(ConfigurableEnvironment environment) {
143150
logger.trace("RandomValuePropertySource add to Environment");
144151
}
145152

153+
static final class Range<T extends Number> {
154+
155+
private final T min;
156+
157+
private final T max;
158+
159+
private Range(T min, T max) {
160+
this.min = min;
161+
this.max = max;
162+
163+
}
164+
165+
static <T extends Number> Range<T> get(String range, Function<String, T> parse, Predicate<T> boundValidator,
166+
T defaultMin, BiPredicate<T, T> rangeValidator) {
167+
String[] tokens = StringUtils.commaDelimitedListToStringArray(range);
168+
T token1 = parse.apply(tokens[0]);
169+
if (tokens.length == 1) {
170+
Assert.isTrue(boundValidator.test(token1), "Bound must be positive.");
171+
return new Range<>(defaultMin, token1);
172+
}
173+
T token2 = parse.apply(tokens[1]);
174+
Assert.isTrue(rangeValidator.test(token1, token2), "Lower bound must be less than upper bound.");
175+
return new Range<>(token1, token2);
176+
}
177+
178+
T getMin() {
179+
return this.min;
180+
}
181+
182+
T getMax() {
183+
return this.max;
184+
}
185+
186+
}
187+
146188
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/RandomValuePropertySourceTests.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.junit.jupiter.api.Test;
2323

2424
import static org.assertj.core.api.Assertions.assertThat;
25+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
2526
import static org.mockito.BDDMockito.given;
2627
import static org.mockito.Mockito.spy;
2728

@@ -66,12 +67,37 @@ void intRange() {
6667
assertThat(value < 10).isTrue();
6768
}
6869

70+
@Test
71+
void intRangeWhenLowerBoundEqualsUpperBoundShouldFailWithIllegalArgumentException() {
72+
assertThatIllegalArgumentException().isThrownBy(() -> this.source.getProperty("random.int[4,4]"))
73+
.withMessage("Lower bound must be less than upper bound.");
74+
}
75+
76+
@Test
77+
void intRangeWhenLowerBoundNegative() {
78+
Integer value = (Integer) this.source.getProperty("random.int[-4,4]");
79+
assertThat(value >= -4).isTrue();
80+
assertThat(value < 4).isTrue();
81+
}
82+
6983
@Test
7084
void intMax() {
7185
Integer value = (Integer) this.source.getProperty("random.int(10)");
7286
assertThat(value).isNotNull().isLessThan(10);
7387
}
7488

89+
@Test
90+
void intMaxZero() {
91+
assertThatIllegalArgumentException().isThrownBy(() -> this.source.getProperty("random.int(0)"))
92+
.withMessage("Bound must be positive.");
93+
}
94+
95+
@Test
96+
void intNegativeBound() {
97+
assertThatIllegalArgumentException().isThrownBy(() -> this.source.getProperty("random.int(-5)"))
98+
.withMessage("Bound must be positive.");
99+
}
100+
75101
@Test
76102
void longValue() {
77103
Long value = (Long) this.source.getProperty("random.long");
@@ -84,12 +110,37 @@ void longRange() {
84110
assertThat(value).isNotNull().isBetween(4L, 10L);
85111
}
86112

113+
@Test
114+
void longRangeWhenLowerBoundEqualsUpperBoundShouldFailWithIllegalArgumentException() {
115+
assertThatIllegalArgumentException().isThrownBy(() -> this.source.getProperty("random.long[4,4]"))
116+
.withMessage("Lower bound must be less than upper bound.");
117+
}
118+
119+
@Test
120+
void longRangeWhenLowerBoundNegativeShouldFailWithIllegalArgumentException() {
121+
Long value = (Long) this.source.getProperty("random.long[-4,4]");
122+
assertThat(value >= -4).isTrue();
123+
assertThat(value < 4).isTrue();
124+
}
125+
87126
@Test
88127
void longMax() {
89128
Long value = (Long) this.source.getProperty("random.long(10)");
90129
assertThat(value).isNotNull().isLessThan(10L);
91130
}
92131

132+
@Test
133+
void longMaxZero() {
134+
assertThatIllegalArgumentException().isThrownBy(() -> this.source.getProperty("random.long(0)"))
135+
.withMessage("Bound must be positive.");
136+
}
137+
138+
@Test
139+
void longNegativeBound() {
140+
assertThatIllegalArgumentException().isThrownBy(() -> this.source.getProperty("random.long(-5)"))
141+
.withMessage("Bound must be positive.");
142+
}
143+
93144
@Test
94145
void longOverflow() {
95146
RandomValuePropertySource source = spy(this.source);

0 commit comments

Comments
 (0)