From df7223f39c3bdc1a187a47443a8a34692280cb59 Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 7 Jun 2023 10:20:27 +0200 Subject: [PATCH] Remove APIs marked for removal See gh-30604 --- .../support/CronSequenceGenerator.java | 460 ------------------ .../support/CronSequenceGeneratorTests.java | 107 ---- .../DefaultPartHttpMessageReader.java | 35 +- .../http/codec/multipart/PartGenerator.java | 81 +-- .../reactive/AbstractServerHttpRequest.java | 42 +- .../DefaultPartHttpMessageReaderTests.java | 6 +- 6 files changed, 11 insertions(+), 720 deletions(-) delete mode 100644 spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java delete mode 100644 spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java diff --git a/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java b/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java deleted file mode 100644 index 7d502e4459e..00000000000 --- a/spring-context/src/main/java/org/springframework/scheduling/support/CronSequenceGenerator.java +++ /dev/null @@ -1,460 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.scheduling.support; - -import java.util.ArrayList; -import java.util.BitSet; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.List; -import java.util.TimeZone; - -import org.springframework.lang.Nullable; -import org.springframework.util.StringUtils; - -/** - * Date sequence generator for a - * Crontab pattern, - * allowing clients to specify a pattern that the sequence matches. - * - *

The pattern is a list of six single space-separated fields: representing - * second, minute, hour, day, month, weekday. Month and weekday names can be - * given as the first three letters of the English names. - * - *

Example patterns: - *

- * - * @author Dave Syer - * @author Juergen Hoeller - * @author Ruslan Sibgatullin - * @since 3.0 - * @see CronTrigger - * @deprecated as of 5.3, in favor of {@link CronExpression} - */ -@Deprecated(since = "5.3", forRemoval = true) -public class CronSequenceGenerator { - - private final String expression; - - @Nullable - private final TimeZone timeZone; - - private final BitSet months = new BitSet(12); - - private final BitSet daysOfMonth = new BitSet(31); - - private final BitSet daysOfWeek = new BitSet(7); - - private final BitSet hours = new BitSet(24); - - private final BitSet minutes = new BitSet(60); - - private final BitSet seconds = new BitSet(60); - - - /** - * Construct a {@code CronSequenceGenerator} from the pattern provided, - * using the default {@link TimeZone}. - * @param expression a space-separated list of time fields - * @throws IllegalArgumentException if the pattern cannot be parsed - * @see java.util.TimeZone#getDefault() - * @deprecated as of 5.3, in favor of {@link CronExpression#parse(String)} - */ - @Deprecated(since = "5.3", forRemoval = true) - public CronSequenceGenerator(String expression) { - this(expression, TimeZone.getDefault()); - } - - /** - * Construct a {@code CronSequenceGenerator} from the pattern provided, - * using the specified {@link TimeZone}. - * @param expression a space-separated list of time fields - * @param timeZone the TimeZone to use for generated trigger times - * @throws IllegalArgumentException if the pattern cannot be parsed - * @deprecated as of 5.3, in favor of {@link CronExpression#parse(String)} - */ - @Deprecated - public CronSequenceGenerator(String expression, TimeZone timeZone) { - this.expression = expression; - this.timeZone = timeZone; - parse(expression); - } - - private CronSequenceGenerator(String expression, String[] fields) { - this.expression = expression; - this.timeZone = null; - doParse(fields); - } - - - /** - * Return the cron pattern that this sequence generator has been built for. - */ - String getExpression() { - return this.expression; - } - - - /** - * Get the next {@link Date} in the sequence matching the Cron pattern and - * after the value provided. The return value will have a whole number of - * seconds, and will be after the input value. - * @param date a seed value - * @return the next value matching the pattern - */ - public Date next(Date date) { - /* - The plan: - - 1 Start with whole second (rounding up if necessary) - - 2 If seconds match move on, otherwise find the next match: - 2.1 If next match is in the next minute then roll forwards - - 3 If minute matches move on, otherwise find the next match - 3.1 If next match is in the next hour then roll forwards - 3.2 Reset the seconds and go to 2 - - 4 If hour matches move on, otherwise find the next match - 4.1 If next match is in the next day then roll forwards, - 4.2 Reset the minutes and seconds and go to 2 - */ - - Calendar calendar = new GregorianCalendar(); - calendar.setTimeZone(this.timeZone); - calendar.setTime(date); - - // First, just reset the milliseconds and try to calculate from there... - calendar.set(Calendar.MILLISECOND, 0); - long originalTimestamp = calendar.getTimeInMillis(); - doNext(calendar, calendar.get(Calendar.YEAR)); - - if (calendar.getTimeInMillis() == originalTimestamp) { - // We arrived at the original timestamp - round up to the next whole second and try again... - calendar.add(Calendar.SECOND, 1); - doNext(calendar, calendar.get(Calendar.YEAR)); - } - - return calendar.getTime(); - } - - private void doNext(Calendar calendar, int dot) { - List resets = new ArrayList<>(); - - int second = calendar.get(Calendar.SECOND); - List emptyList = Collections.emptyList(); - int updateSecond = findNext(this.seconds, second, calendar, Calendar.SECOND, Calendar.MINUTE, emptyList); - if (second == updateSecond) { - resets.add(Calendar.SECOND); - } - - int minute = calendar.get(Calendar.MINUTE); - int updateMinute = findNext(this.minutes, minute, calendar, Calendar.MINUTE, Calendar.HOUR_OF_DAY, resets); - if (minute == updateMinute) { - resets.add(Calendar.MINUTE); - } - else { - doNext(calendar, dot); - } - - int hour = calendar.get(Calendar.HOUR_OF_DAY); - int updateHour = findNext(this.hours, hour, calendar, Calendar.HOUR_OF_DAY, Calendar.DAY_OF_WEEK, resets); - if (hour == updateHour) { - resets.add(Calendar.HOUR_OF_DAY); - } - else { - doNext(calendar, dot); - } - - int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); - int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); - int updateDayOfMonth = findNextDay(calendar, this.daysOfMonth, dayOfMonth, this.daysOfWeek, dayOfWeek, resets); - if (dayOfMonth == updateDayOfMonth) { - resets.add(Calendar.DAY_OF_MONTH); - } - else { - doNext(calendar, dot); - } - - int month = calendar.get(Calendar.MONTH); - int updateMonth = findNext(this.months, month, calendar, Calendar.MONTH, Calendar.YEAR, resets); - if (month != updateMonth) { - if (calendar.get(Calendar.YEAR) - dot > 4) { - throw new IllegalArgumentException("Invalid cron expression \"" + this.expression + - "\" led to runaway search for next trigger"); - } - doNext(calendar, dot); - } - - } - - private int findNextDay(Calendar calendar, BitSet daysOfMonth, int dayOfMonth, BitSet daysOfWeek, int dayOfWeek, - List resets) { - - int count = 0; - int max = 366; - // the DAY_OF_WEEK values in java.util.Calendar start with 1 (Sunday), - // but in the cron pattern, they start with 0, so we subtract 1 here - while ((!daysOfMonth.get(dayOfMonth) || !daysOfWeek.get(dayOfWeek - 1)) && count++ < max) { - calendar.add(Calendar.DAY_OF_MONTH, 1); - dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH); - dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); - reset(calendar, resets); - } - if (count >= max) { - throw new IllegalArgumentException("Overflow in day for expression \"" + this.expression + "\""); - } - return dayOfMonth; - } - - /** - * Search the bits provided for the next set bit after the value provided, - * and reset the calendar. - * @param bits a {@link BitSet} representing the allowed values of the field - * @param value the current value of the field - * @param calendar the calendar to increment as we move through the bits - * @param field the field to increment in the calendar (@see - * {@link Calendar} for the static constants defining valid fields) - * @param lowerOrders the Calendar field ids that should be reset (i.e. the - * ones of lower significance than the field of interest) - * @return the value of the calendar field that is next in the sequence - */ - private int findNext(BitSet bits, int value, Calendar calendar, int field, int nextField, List lowerOrders) { - int nextValue = bits.nextSetBit(value); - // roll over if needed - if (nextValue == -1) { - calendar.add(nextField, 1); - reset(calendar, Collections.singletonList(field)); - nextValue = bits.nextSetBit(0); - } - if (nextValue != value) { - calendar.set(field, nextValue); - reset(calendar, lowerOrders); - } - return nextValue; - } - - /** - * Reset the calendar setting all the fields provided to zero. - */ - private void reset(Calendar calendar, List fields) { - for (int field : fields) { - calendar.set(field, field == Calendar.DAY_OF_MONTH ? 1 : 0); - } - } - - - // Parsing logic invoked by the constructor - - /** - * Parse the given pattern expression. - */ - private void parse(String expression) throws IllegalArgumentException { - String[] fields = StringUtils.tokenizeToStringArray(expression, " "); - if (!areValidCronFields(fields)) { - throw new IllegalArgumentException(String.format( - "Cron expression must consist of 6 fields (found %d in \"%s\")", fields.length, expression)); - } - doParse(fields); - } - - private void doParse(String[] fields) { - setNumberHits(this.seconds, fields[0], 0, 60); - setNumberHits(this.minutes, fields[1], 0, 60); - setNumberHits(this.hours, fields[2], 0, 24); - setDaysOfMonth(this.daysOfMonth, fields[3]); - setMonths(this.months, fields[4]); - setDays(this.daysOfWeek, replaceOrdinals(fields[5], "SUN,MON,TUE,WED,THU,FRI,SAT"), 8); - - if (this.daysOfWeek.get(7)) { - // Sunday can be represented as 0 or 7 - this.daysOfWeek.set(0); - this.daysOfWeek.clear(7); - } - } - - /** - * Replace the values in the comma-separated list (case-insensitive) - * with their index in the list. - * @return a new String with the values from the list replaced - */ - private String replaceOrdinals(String value, String commaSeparatedList) { - String[] list = StringUtils.commaDelimitedListToStringArray(commaSeparatedList); - for (int i = 0; i < list.length; i++) { - String item = list[i].toUpperCase(); - value = StringUtils.replace(value.toUpperCase(), item, "" + i); - } - return value; - } - - private void setDaysOfMonth(BitSet bits, String field) { - int max = 31; - // Days of month start with 1 (in Cron and Calendar) so add one - setDays(bits, field, max + 1); - // ... and remove it from the front - bits.clear(0); - } - - private void setDays(BitSet bits, String field, int max) { - if (field.contains("?")) { - field = "*"; - } - setNumberHits(bits, field, 0, max); - } - - private void setMonths(BitSet bits, String value) { - int max = 12; - value = replaceOrdinals(value, "FOO,JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC"); - BitSet months = new BitSet(13); - // Months start with 1 in Cron and 0 in Calendar, so push the values first into a longer bit set - setNumberHits(months, value, 1, max + 1); - // ... and then rotate it to the front of the months - for (int i = 1; i <= max; i++) { - if (months.get(i)) { - bits.set(i - 1); - } - } - } - - private void setNumberHits(BitSet bits, String value, int min, int max) { - String[] fields = StringUtils.delimitedListToStringArray(value, ","); - for (String field : fields) { - if (!field.contains("/")) { - // Not an incrementer so it must be a range (possibly empty) - int[] range = getRange(field, min, max); - bits.set(range[0], range[1] + 1); - } - else { - String[] split = StringUtils.delimitedListToStringArray(field, "/"); - if (split.length > 2) { - throw new IllegalArgumentException("Incrementer has more than two fields: '" + - field + "' in expression \"" + this.expression + "\""); - } - int[] range = getRange(split[0], min, max); - if (!split[0].contains("-")) { - range[1] = max - 1; - } - int delta = Integer.parseInt(split[1]); - if (delta <= 0) { - throw new IllegalArgumentException("Incrementer delta must be 1 or higher: '" + - field + "' in expression \"" + this.expression + "\""); - } - for (int i = range[0]; i <= range[1]; i += delta) { - bits.set(i); - } - } - } - } - - private int[] getRange(String field, int min, int max) { - int[] result = new int[2]; - if (field.contains("*")) { - result[0] = min; - result[1] = max - 1; - return result; - } - if (!field.contains("-")) { - result[0] = result[1] = Integer.parseInt(field); - } - else { - String[] split = StringUtils.delimitedListToStringArray(field, "-"); - if (split.length > 2) { - throw new IllegalArgumentException("Range has more than two fields: '" + - field + "' in expression \"" + this.expression + "\""); - } - result[0] = Integer.parseInt(split[0]); - result[1] = Integer.parseInt(split[1]); - } - if (result[0] >= max || result[1] >= max) { - throw new IllegalArgumentException("Range exceeds maximum (" + max + "): '" + - field + "' in expression \"" + this.expression + "\""); - } - if (result[0] < min || result[1] < min) { - throw new IllegalArgumentException("Range less than minimum (" + min + "): '" + - field + "' in expression \"" + this.expression + "\""); - } - if (result[0] > result[1]) { - throw new IllegalArgumentException("Invalid inverted range: '" + field + - "' in expression \"" + this.expression + "\""); - } - return result; - } - - - /** - * Determine whether the specified expression represents a valid cron pattern. - * @param expression the expression to evaluate - * @return {@code true} if the given expression is a valid cron expression - * @since 4.3 - */ - public static boolean isValidExpression(@Nullable String expression) { - if (expression == null) { - return false; - } - String[] fields = StringUtils.tokenizeToStringArray(expression, " "); - if (!areValidCronFields(fields)) { - return false; - } - try { - new CronSequenceGenerator(expression, fields); - return true; - } - catch (IllegalArgumentException ex) { - return false; - } - } - - private static boolean areValidCronFields(@Nullable String[] fields) { - return (fields != null && fields.length == 6); - } - - - @Override - public boolean equals(@Nullable Object other) { - if (this == other) { - return true; - } - if (!(other instanceof CronSequenceGenerator otherCron)) { - return false; - } - return (this.months.equals(otherCron.months) && this.daysOfMonth.equals(otherCron.daysOfMonth) && - this.daysOfWeek.equals(otherCron.daysOfWeek) && this.hours.equals(otherCron.hours) && - this.minutes.equals(otherCron.minutes) && this.seconds.equals(otherCron.seconds)); - } - - @Override - public int hashCode() { - return (17 * this.months.hashCode() + 29 * this.daysOfMonth.hashCode() + 37 * this.daysOfWeek.hashCode() + - 41 * this.hours.hashCode() + 53 * this.minutes.hashCode() + 61 * this.seconds.hashCode()); - } - - @Override - public String toString() { - return getClass().getSimpleName() + ": " + this.expression; - } - -} diff --git a/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java b/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java deleted file mode 100644 index 73895c8619d..00000000000 --- a/spring-context/src/test/java/org/springframework/scheduling/support/CronSequenceGeneratorTests.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.scheduling.support; - -import java.util.Date; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; - -/** - * @author Juergen Hoeller - * @author Ruslan Sibgatullin - */ -@SuppressWarnings({ "removal", "deprecation" }) -public class CronSequenceGeneratorTests { - - @Test - public void at50Seconds() { - assertThat(new CronSequenceGenerator("*/15 * 1-4 * * *").next(new Date(2012, 6, 1, 9, 53, 50))).isEqualTo(new Date(2012, 6, 2, 1, 0)); - } - - @Test - public void at0Seconds() { - assertThat(new CronSequenceGenerator("*/15 * 1-4 * * *").next(new Date(2012, 6, 1, 9, 53))).isEqualTo(new Date(2012, 6, 2, 1, 0)); - } - - @Test - public void at0Minutes() { - assertThat(new CronSequenceGenerator("0 */2 1-4 * * *").next(new Date(2012, 6, 1, 9, 0))).isEqualTo(new Date(2012, 6, 2, 1, 0)); - } - - @Test - public void with0Increment() { - assertThatIllegalArgumentException().isThrownBy(() -> - new CronSequenceGenerator("*/0 * * * * *").next(new Date(2012, 6, 1, 9, 0))); - } - - @Test - public void withNegativeIncrement() { - assertThatIllegalArgumentException().isThrownBy(() -> - new CronSequenceGenerator("*/-1 * * * * *").next(new Date(2012, 6, 1, 9, 0))); - } - - @Test - public void withInvertedMinuteRange() { - assertThatIllegalArgumentException().isThrownBy(() -> - new CronSequenceGenerator("* 6-5 * * * *").next(new Date(2012, 6, 1, 9, 0))); - } - - @Test - public void withInvertedHourRange() { - assertThatIllegalArgumentException().isThrownBy(() -> - new CronSequenceGenerator("* * 6-5 * * *").next(new Date(2012, 6, 1, 9, 0))); - } - - @Test - public void withSameMinuteRange() { - new CronSequenceGenerator("* 6-6 * * * *").next(new Date(2012, 6, 1, 9, 0)); - } - - @Test - public void withSameHourRange() { - new CronSequenceGenerator("* * 6-6 * * *").next(new Date(2012, 6, 1, 9, 0)); - } - - @Test - public void validExpression() { - assertThat(CronSequenceGenerator.isValidExpression("0 */2 1-4 * * *")).isTrue(); - } - - @Test - public void invalidExpressionWithLength() { - assertThat(CronSequenceGenerator.isValidExpression("0 */2 1-4 * * * *")).isFalse(); - } - - @Test - public void invalidExpressionWithSeconds() { - assertThat(CronSequenceGenerator.isValidExpression("100 */2 1-4 * * *")).isFalse(); - } - - @Test - public void invalidExpressionWithMonths() { - assertThat(CronSequenceGenerator.isValidExpression("0 */2 1-4 * INVALID *")).isFalse(); - } - - @Test - public void nullExpression() { - assertThat(CronSequenceGenerator.isValidExpression(null)).isFalse(); - } - -} diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReader.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReader.java index e1da5224dc2..637adc15905 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReader.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -66,8 +66,6 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements private int maxParts = -1; - private boolean streaming; - private Scheduler blockingOperationScheduler = Schedulers.boundedElastic(); private FileStorage fileStorage = FileStorage.tempDirectory(this::getBlockingOperationScheduler); @@ -99,8 +97,6 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements *
  • non-file parts are rejected with {@link DataBufferLimitException}. * *

    By default this is set to 256K. - *

    Note that this property is ignored when - * {@linkplain #setStreaming(boolean) streaming} is enabled. * @param maxInMemorySize the in-memory limit in bytes; if set to -1 the entire * contents will be stored in memory */ @@ -112,7 +108,6 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements * Configure the maximum amount of disk space allowed for file parts. *

    By default this is set to -1, meaning that there is no maximum. *

    Note that this property is ignored when - * {@linkplain #setStreaming(boolean) streaming} is enabled, or when * {@link #setMaxInMemorySize(int) maxInMemorySize} is set to -1. */ public void setMaxDiskUsagePerPart(long maxDiskUsagePerPart) { @@ -133,7 +128,6 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements * named {@code spring-webflux-multipart} is created under the system * temporary directory. *

    Note that this property is ignored when - * {@linkplain #setStreaming(boolean) streaming} is enabled, or when * {@link #setMaxInMemorySize(int) maxInMemorySize} is set to -1. * @throws IOException if an I/O error occurs, or the parent directory * does not exist @@ -149,7 +143,6 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements * {@link Schedulers#boundedElastic()} is used, but this property allows for * changing it to an externally managed scheduler. *

    Note that this property is ignored when - * {@linkplain #setStreaming(boolean) streaming} is enabled, or when * {@link #setMaxInMemorySize(int) maxInMemorySize} is set to -1. * @see Schedulers#newBoundedElastic */ @@ -162,30 +155,6 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements return this.blockingOperationScheduler; } - /** - * When set to {@code true}, the {@linkplain Part#content() part content} - * is streamed directly from the parsed input buffer stream, and not stored - * in memory nor file. - * When {@code false}, parts are backed by - * in-memory and/or file storage. Defaults to {@code false}. - *

    NOTE that with streaming enabled, the - * {@code Flux} that is produced by this message reader must be - * consumed in the original order, i.e. the order of the HTTP message. - * Additionally, the {@linkplain Part#content() body contents} must either - * be completely consumed or canceled before moving to the next part. - *

    Also note that enabling this property effectively ignores - * {@link #setMaxInMemorySize(int) maxInMemorySize}, - * {@link #setMaxDiskUsagePerPart(long) maxDiskUsagePerPart}, - * {@link #setFileStorageDirectory(Path) fileStorageDirectory}, and - * {@link #setBlockingOperationScheduler(Scheduler) fileCreationScheduler}. - * @deprecated as of 6.0, in favor of {@link PartEvent} and - * {@link PartEventHttpMessageReader} - */ - @Deprecated(since = "6.0", forRemoval = true) - public void setStreaming(boolean streaming) { - this.streaming = streaming; - } - /** * Set the character set used to decode headers. * Defaults to UTF-8 as per RFC 7578. @@ -236,7 +205,7 @@ public class DefaultPartHttpMessageReader extends LoggingCodecSupport implements } else { return PartGenerator.createPart(partsTokens, - this.maxInMemorySize, this.maxDiskUsagePerPart, this.streaming, + this.maxInMemorySize, this.maxDiskUsagePerPart, this.fileStorage.directory(), this.blockingOperationScheduler); } }); diff --git a/spring-web/src/main/java/org/springframework/http/codec/multipart/PartGenerator.java b/spring-web/src/main/java/org/springframework/http/codec/multipart/PartGenerator.java index c00189e068f..6519b8c7b0d 100644 --- a/spring-web/src/main/java/org/springframework/http/codec/multipart/PartGenerator.java +++ b/spring-web/src/main/java/org/springframework/http/codec/multipart/PartGenerator.java @@ -38,7 +38,6 @@ import org.apache.commons.logging.LogFactory; import org.reactivestreams.Subscription; import reactor.core.publisher.BaseSubscriber; import reactor.core.publisher.Flux; -import reactor.core.publisher.FluxSink; import reactor.core.publisher.Mono; import reactor.core.publisher.MonoSink; import reactor.core.scheduler.Scheduler; @@ -68,8 +67,6 @@ final class PartGenerator extends BaseSubscriber { private final MonoSink sink; - private final boolean streaming; - private final int maxInMemorySize; private final long maxDiskUsagePerPart; @@ -80,12 +77,11 @@ final class PartGenerator extends BaseSubscriber { private PartGenerator(MonoSink sink, int maxInMemorySize, long maxDiskUsagePerPart, - boolean streaming, Mono fileStorageDirectory, Scheduler blockingOperationScheduler) { + Mono fileStorageDirectory, Scheduler blockingOperationScheduler) { this.sink = sink; this.maxInMemorySize = maxInMemorySize; this.maxDiskUsagePerPart = maxDiskUsagePerPart; - this.streaming = streaming; this.fileStorageDirectory = fileStorageDirectory; this.blockingOperationScheduler = blockingOperationScheduler; } @@ -94,11 +90,10 @@ final class PartGenerator extends BaseSubscriber { * Creates parts from a given stream of tokens. */ public static Mono createPart(Flux tokens, int maxInMemorySize, - long maxDiskUsagePerPart, boolean streaming, Mono fileStorageDirectory, - Scheduler blockingOperationScheduler) { + long maxDiskUsagePerPart, Mono fileStorageDirectory, Scheduler blockingOperationScheduler) { return Mono.create(sink -> { - PartGenerator generator = new PartGenerator(sink, maxInMemorySize, maxDiskUsagePerPart, streaming, + PartGenerator generator = new PartGenerator(sink, maxInMemorySize, maxDiskUsagePerPart, fileStorageDirectory, blockingOperationScheduler); sink.onCancel(generator); @@ -134,20 +129,10 @@ final class PartGenerator extends BaseSubscriber { changeState(currentState, new FormFieldState(headers)); requestToken(); } - else if (!this.streaming) { + else { changeState(currentState, new InMemoryState(headers)); requestToken(); } - else { - Flux streamingContent = Flux.create(contentSink -> { - State newState = new StreamingState(contentSink); - if (changeState(currentState, newState)) { - contentSink.onRequest(l -> requestToken()); - requestToken(); - } - }); - emitPart(DefaultParts.part(headers, streamingContent)); - } } @Override @@ -225,8 +210,6 @@ final class PartGenerator extends BaseSubscriber { *

      *
    1. If the part is a {@linkplain MultipartUtils#isFormField(HttpHeaders) form field}, * the creator will be in the {@link FormFieldState}.
    2. - *
    3. If {@linkplain #streaming} is enabled, the creator will be in the - * {@link StreamingState}.
    4. *
    5. Otherwise, the creator will initially be in the * {@link InMemoryState}, but will switch over to {@link CreateFileState} * when the part byte count exceeds {@link #maxInMemorySize}, @@ -352,61 +335,11 @@ final class PartGenerator extends BaseSubscriber { /** - * The creator state when {@link #streaming} is {@code true} (and not - * handling a form field). Relays all received buffers to a sink. - */ - private final class StreamingState implements State { - - private final FluxSink bodySink; - - public StreamingState(FluxSink bodySink) { - this.bodySink = bodySink; - } - - @Override - public void body(DataBuffer dataBuffer) { - if (!this.bodySink.isCancelled()) { - this.bodySink.next(dataBuffer); - if (this.bodySink.requestedFromDownstream() > 0) { - requestToken(); - } - } - else { - DataBufferUtils.release(dataBuffer); - // even though the body sink is canceled, the (outer) part sink - // might not be, so request another token - requestToken(); - } - } - - @Override - public void onComplete() { - if (!this.bodySink.isCancelled()) { - this.bodySink.complete(); - } - } - - @Override - public void error(Throwable throwable) { - if (!this.bodySink.isCancelled()) { - this.bodySink.error(throwable); - } - } - - @Override - public String toString() { - return "STREAMING"; - } - - } - - - /** - * The creator state when {@link #streaming} is {@code false} (and not - * handling a form field). Stores all received buffers in a queue. + * The creator state when not handling a form field. + * Stores all received buffers in a queue. * If the byte count exceeds {@link #maxInMemorySize}, the creator state * is changed to {@link CreateFileState}, and eventually to - * {@link CreateFileState}. + * {@link WritingFileState}. */ private final class InMemoryState implements State { diff --git a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java index 0abd973b56f..829a2202a81 100644 --- a/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/reactive/AbstractServerHttpRequest.java @@ -51,8 +51,6 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest { private final HttpHeaders headers; - // TODO: remove @Nullable once deprecated constructors have been removed - @Nullable private final HttpMethod method; @Nullable @@ -71,22 +69,6 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest { private String logPrefix; - /** - * Constructor with the URI and headers for the request. - * @param uri the URI for the request - * @param contextPath the context path for the request - * @param headers the headers for the request (as {@link MultiValueMap}) - * @since 5.3 - * @deprecated since 6.0.8, in favor of {@link #AbstractServerHttpRequest(HttpMethod, URI, String, MultiValueMap)} - */ - @Deprecated(since = "6.0.8", forRemoval = true) - public AbstractServerHttpRequest(URI uri, @Nullable String contextPath, MultiValueMap headers) { - this.uri = uri; - this.path = RequestPath.parse(uri, contextPath); - this.headers = HttpHeaders.readOnlyHttpHeaders(headers); - this.method = null; - } - /** * Constructor with the method, URI and headers for the request. * @param method the HTTP method for the request @@ -108,21 +90,6 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest { this.headers = HttpHeaders.readOnlyHttpHeaders(headers); } - /** - * Constructor with the URI and headers for the request. - * @param uri the URI for the request - * @param contextPath the context path for the request - * @param headers the headers for the request (as {@link HttpHeaders}) - * @deprecated since 6.0.8, in favor of {@link #AbstractServerHttpRequest(HttpMethod, URI, String, MultiValueMap)} - */ - @Deprecated(since = "6.0.8", forRemoval = true) - public AbstractServerHttpRequest(URI uri, @Nullable String contextPath, HttpHeaders headers) { - this.uri = uri; - this.path = RequestPath.parse(uri, contextPath); - this.headers = HttpHeaders.readOnlyHttpHeaders(headers); - this.method = null; - } - @Override public String getId() { @@ -147,14 +114,7 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest { @Override public HttpMethod getMethod() { - // TODO: remove null check once deprecated constructors have been removed - if (this.method != null) { - return this.method; - } - else { - throw new IllegalStateException("No HttpMethod provided in constructor, " + - "and AbstractServerHttpRequest::getMethod not overridden"); - } + return this.method; } @Override diff --git a/spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReaderTests.java b/spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReaderTests.java index 1c9596fb5b1..7dd36e0bbf9 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReaderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/multipart/DefaultPartHttpMessageReaderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -420,9 +420,6 @@ class DefaultPartHttpMessageReaderTests { @SuppressWarnings("removal") static Stream messageReaders() { - DefaultPartHttpMessageReader streaming = new DefaultPartHttpMessageReader(); - streaming.setStreaming(true); - DefaultPartHttpMessageReader inMemory = new DefaultPartHttpMessageReader(); inMemory.setMaxInMemorySize(1000); @@ -430,7 +427,6 @@ class DefaultPartHttpMessageReaderTests { onDisk.setMaxInMemorySize(100); return Stream.of( - arguments(named("streaming", streaming)), arguments(named("in-memory", inMemory)), arguments(named("on-disk", onDisk))); }