mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3540029931 |
+3
-3
@@ -28,8 +28,8 @@ configure(allprojects) { project ->
|
||||
dependencyManagement {
|
||||
imports {
|
||||
mavenBom "com.fasterxml.jackson:jackson-bom:2.12.7"
|
||||
mavenBom "io.netty:netty-bom:4.1.91.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.31"
|
||||
mavenBom "io.netty:netty-bom:4.1.90.Final"
|
||||
mavenBom "io.projectreactor:reactor-bom:2020.0.30"
|
||||
mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13"
|
||||
mavenBom "io.rsocket:rsocket-bom:1.1.3"
|
||||
mavenBom "org.eclipse.jetty:jetty-bom:9.4.51.v20230217"
|
||||
@@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project ->
|
||||
}
|
||||
|
||||
checkstyle {
|
||||
toolVersion = "10.9.3"
|
||||
toolVersion = "10.9.1"
|
||||
configDirectory.set(rootProject.file("src/checkstyle"))
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=5.3.27
|
||||
version=5.3.26
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
org.gradle.caching=true
|
||||
org.gradle.parallel=true
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -123,11 +123,6 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
|
||||
}
|
||||
if (classLoader == null || classLoader.getParent() == null) {
|
||||
// JDK bootstrap loader or platform loader suggested ->
|
||||
// use higher-level loader which can see Spring infrastructure classes
|
||||
classLoader = getClass().getClassLoader();
|
||||
}
|
||||
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* 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.
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
package org.springframework.aop.framework;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Savepoint;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import javax.accessibility.Accessible;
|
||||
@@ -383,40 +380,6 @@ public class ProxyFactoryTests {
|
||||
assertThat(proxy.getName()).isEqualTo("tb");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCharSequenceProxy() {
|
||||
CharSequence target = "test";
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
ClassLoader cl = target.getClass().getClassLoader();
|
||||
assertThat(((CharSequence) pf.getProxy(cl)).toString()).isEqualTo(target);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDateProxy() {
|
||||
Date target = new Date();
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
pf.setProxyTargetClass(true);
|
||||
ClassLoader cl = target.getClass().getClassLoader();
|
||||
assertThat(((Date) pf.getProxy(cl)).getTime()).isEqualTo(target.getTime());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdbcSavepointProxy() throws SQLException {
|
||||
Savepoint target = new Savepoint() {
|
||||
@Override
|
||||
public int getSavepointId() throws SQLException {
|
||||
return 1;
|
||||
}
|
||||
@Override
|
||||
public String getSavepointName() throws SQLException {
|
||||
return "sp";
|
||||
}
|
||||
};
|
||||
ProxyFactory pf = new ProxyFactory(target);
|
||||
ClassLoader cl = Savepoint.class.getClassLoader();
|
||||
assertThat(((Savepoint) pf.getProxy(cl)).getSavepointName()).isEqualTo("sp");
|
||||
}
|
||||
|
||||
|
||||
@Order(2)
|
||||
public static class A implements Runnable {
|
||||
@@ -428,7 +391,7 @@ public class ProxyFactoryTests {
|
||||
|
||||
|
||||
@Order(1)
|
||||
public static class B implements Runnable {
|
||||
public static class B implements Runnable{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -125,7 +125,7 @@ public class FieldError extends ObjectError {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Field error in object '" + getObjectName() + "' on field '" + this.field +
|
||||
"': rejected value [" + ObjectUtils.nullSafeConciseToString(this.rejectedValue) + "]; " +
|
||||
"': rejected value [" + ObjectUtils.nullSafeToString(this.rejectedValue) + "]; " +
|
||||
resolvableToString();
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* 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.
|
||||
@@ -133,7 +133,7 @@ public class DateFormattingTests {
|
||||
assertThat(exception)
|
||||
.hasMessageContaining("for property 'styleDate'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).cause()
|
||||
.hasMessageContaining("for value [99/01/01]")
|
||||
.hasMessageContaining("for value '99/01/01'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).cause()
|
||||
.hasMessageContaining("Parse attempt failed for value [99/01/01]")
|
||||
.hasCauseInstanceOf(ParseException.class).cause()
|
||||
@@ -353,7 +353,7 @@ public class DateFormattingTests {
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'patternDateWithFallbackPatterns'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).cause()
|
||||
.hasMessageContaining("for value [210302]")
|
||||
.hasMessageContaining("for value '210302'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).cause()
|
||||
.hasMessageContaining("Parse attempt failed for value [210302]")
|
||||
.hasCauseInstanceOf(ParseException.class).cause()
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* 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.
|
||||
@@ -333,7 +333,7 @@ class DateTimeFormattingTests {
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'isoLocalDate'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).cause()
|
||||
.hasMessageContaining("for value [2009-31-10]")
|
||||
.hasMessageContaining("for value '2009-31-10'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).cause()
|
||||
.hasMessageContaining("Parse attempt failed for value [2009-31-10]")
|
||||
.hasCauseInstanceOf(DateTimeParseException.class).cause()
|
||||
@@ -540,7 +540,7 @@ class DateTimeFormattingTests {
|
||||
assertThat(fieldError.unwrap(TypeMismatchException.class))
|
||||
.hasMessageContaining("for property 'patternLocalDateWithFallbackPatterns'")
|
||||
.hasCauseInstanceOf(ConversionFailedException.class).cause()
|
||||
.hasMessageContaining("for value [210302]")
|
||||
.hasMessageContaining("for value '210302'")
|
||||
.hasCauseInstanceOf(IllegalArgumentException.class).cause()
|
||||
.hasMessageContaining("Parse attempt failed for value [210302]")
|
||||
.hasCauseInstanceOf(DateTimeParseException.class).cause()
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2017 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.
|
||||
@@ -49,7 +49,7 @@ public class ConversionFailedException extends ConversionException {
|
||||
@Nullable Object value, Throwable cause) {
|
||||
|
||||
super("Failed to convert from type [" + sourceType + "] to type [" + targetType +
|
||||
"] for value [" + ObjectUtils.nullSafeConciseToString(value) + "]", cause);
|
||||
"] for value '" + ObjectUtils.nullSafeToString(value) + "'", cause);
|
||||
this.sourceType = sourceType;
|
||||
this.targetType = targetType;
|
||||
this.value = value;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -23,7 +23,6 @@ import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Utility methods for formatting and logging messages.
|
||||
@@ -79,7 +78,7 @@ public abstract class LogFormatUtils {
|
||||
result = ObjectUtils.nullSafeToString(ex);
|
||||
}
|
||||
if (maxLength != -1) {
|
||||
result = StringUtils.truncate(result, maxLength);
|
||||
result = (result.length() > maxLength ? result.substring(0, maxLength) + " (truncated)..." : result);
|
||||
}
|
||||
if (replaceNewlinesAndControlCharacters) {
|
||||
result = NEWLINE_PATTERN.matcher(result).replaceAll("<EOL>");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -17,13 +17,8 @@
|
||||
package org.springframework.util;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.time.temporal.Temporal;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.StringJoiner;
|
||||
@@ -658,7 +653,6 @@ public abstract class ObjectUtils {
|
||||
* Returns a {@code "null"} String if {@code obj} is {@code null}.
|
||||
* @param obj the object to build a String representation for
|
||||
* @return a String representation of {@code obj}
|
||||
* @see #nullSafeConciseToString(Object)
|
||||
*/
|
||||
public static String nullSafeToString(@Nullable Object obj) {
|
||||
if (obj == null) {
|
||||
@@ -914,73 +908,4 @@ public abstract class ObjectUtils {
|
||||
return stringJoiner.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a null-safe, concise string representation of the supplied object
|
||||
* as described below.
|
||||
* <p>Favor this method over {@link #nullSafeToString(Object)} when you need
|
||||
* the length of the generated string to be limited.
|
||||
* <p>Returns:
|
||||
* <ul>
|
||||
* <li>{@code "null"} if {@code obj} is {@code null}</li>
|
||||
* <li>{@linkplain Class#getName() Class name} if {@code obj} is a {@link Class}</li>
|
||||
* <li>Potentially {@linkplain StringUtils#truncate(CharSequence) truncated string}
|
||||
* if {@code obj} is a {@link String} or {@link CharSequence}</li>
|
||||
* <li>Potentially {@linkplain StringUtils#truncate(CharSequence) truncated string}
|
||||
* if {@code obj} is a <em>simple type</em> whose {@code toString()} method returns
|
||||
* a non-null value.</li>
|
||||
* <li>Otherwise, a string representation of the object's type name concatenated
|
||||
* with {@code @} and a hex string form of the object's identity hash code</li>
|
||||
* </ul>
|
||||
* <p>In the context of this method, a <em>simple type</em> is any of the following:
|
||||
* a primitive wrapper (excluding {@link Void}), an {@link Enum}, a {@link Number},
|
||||
* a {@link Date}, a {@link Temporal}, a {@link URI}, a {@link URL}, or a {@link Locale}.
|
||||
* @param obj the object to build a string representation for
|
||||
* @return a concise string representation of the supplied object
|
||||
* @since 5.3.27
|
||||
* @see #nullSafeToString(Object)
|
||||
* @see StringUtils#truncate(CharSequence)
|
||||
*/
|
||||
public static String nullSafeConciseToString(@Nullable Object obj) {
|
||||
if (obj == null) {
|
||||
return "null";
|
||||
}
|
||||
if (obj instanceof Class<?>) {
|
||||
return ((Class<?>) obj).getName();
|
||||
}
|
||||
if (obj instanceof CharSequence) {
|
||||
return StringUtils.truncate((CharSequence) obj);
|
||||
}
|
||||
Class<?> type = obj.getClass();
|
||||
if (isSimpleValueType(type)) {
|
||||
String str = obj.toString();
|
||||
if (str != null) {
|
||||
return StringUtils.truncate(str);
|
||||
}
|
||||
}
|
||||
return type.getTypeName() + "@" + getIdentityHexString(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy of {@link org.springframework.beans.BeanUtils#isSimpleValueType(Class)}.
|
||||
* <p>Check if the given type represents a "simple" value type: a primitive or
|
||||
* primitive wrapper, an enum, a String or other CharSequence, a Number, a
|
||||
* Date, a Temporal, a URI, a URL, a Locale, or a Class.
|
||||
* <p>{@code Void} and {@code void} are not considered simple value types.
|
||||
* @param type the type to check
|
||||
* @return whether the given type represents a "simple" value type
|
||||
*/
|
||||
private static boolean isSimpleValueType(Class<?> type) {
|
||||
return (Void.class != type && void.class != type &&
|
||||
(ClassUtils.isPrimitiveOrWrapper(type) ||
|
||||
Enum.class.isAssignableFrom(type) ||
|
||||
CharSequence.class.isAssignableFrom(type) ||
|
||||
Number.class.isAssignableFrom(type) ||
|
||||
Date.class.isAssignableFrom(type) ||
|
||||
Temporal.class.isAssignableFrom(type) ||
|
||||
URI.class == type ||
|
||||
URL.class == type ||
|
||||
Locale.class == type ||
|
||||
Class.class == type));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* 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.
|
||||
@@ -75,10 +75,6 @@ public abstract class StringUtils {
|
||||
|
||||
private static final char EXTENSION_SEPARATOR = '.';
|
||||
|
||||
private static final int DEFAULT_TRUNCATION_THRESHOLD = 100;
|
||||
|
||||
private static final String TRUNCATION_SUFFIX = " (truncated)...";
|
||||
|
||||
|
||||
//---------------------------------------------------------------------
|
||||
// General convenience methods for working with Strings
|
||||
@@ -1392,40 +1388,4 @@ public abstract class StringUtils {
|
||||
return arrayToDelimitedString(arr, ",");
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate the supplied {@link CharSequence}.
|
||||
* <p>Delegates to {@link #truncate(CharSequence, int)}, supplying {@code 100}
|
||||
* as the threshold.
|
||||
* @param charSequence the {@code CharSequence} to truncate
|
||||
* @return a truncated string, or a string representation of the original
|
||||
* {@code CharSequence} if its length does not exceed the threshold
|
||||
* @since 5.3.27
|
||||
*/
|
||||
public static String truncate(CharSequence charSequence) {
|
||||
return truncate(charSequence, DEFAULT_TRUNCATION_THRESHOLD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate the supplied {@link CharSequence}.
|
||||
* <p>If the length of the {@code CharSequence} is greater than the threshold,
|
||||
* this method returns a {@linkplain CharSequence#subSequence(int, int)
|
||||
* subsequence} of the {@code CharSequence} (up to the threshold) appended
|
||||
* with the suffix {@code " (truncated)..."}. Otherwise, this method returns
|
||||
* {@code charSequence.toString()}.
|
||||
* @param charSequence the {@code CharSequence} to truncate
|
||||
* @param threshold the maximum length after which to truncate; must be a
|
||||
* positive number
|
||||
* @return a truncated string, or a string representation of the original
|
||||
* {@code CharSequence} if its length does not exceed the threshold
|
||||
* @since 5.3.27
|
||||
*/
|
||||
public static String truncate(CharSequence charSequence, int threshold) {
|
||||
Assert.isTrue(threshold > 0,
|
||||
() -> "Truncation threshold must be a positive number: " + threshold);
|
||||
if (charSequence.length() > threshold) {
|
||||
return charSequence.subSequence(0, threshold) + TRUNCATION_SUFFIX;
|
||||
}
|
||||
return charSequence.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* 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.
|
||||
@@ -17,25 +17,15 @@
|
||||
package org.springframework.util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.springframework.util.ObjectUtils.isEmpty;
|
||||
|
||||
@@ -826,144 +816,7 @@ class ObjectUtilsTests {
|
||||
.withMessage("Constant [bogus] does not exist in enum type org.springframework.util.ObjectUtilsTests$Tropes");
|
||||
}
|
||||
|
||||
@Nested
|
||||
class NullSafeConciseToStringTests {
|
||||
|
||||
private final String truncated = " (truncated)...";
|
||||
private final int truncatedLength = 100 + truncated.length();
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForNull() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(null)).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForClass() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(String.class)).isEqualTo("java.lang.String");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForStrings() {
|
||||
String repeat100 = repeat("X", 100);
|
||||
String repeat101 = repeat("X", 101);
|
||||
|
||||
assertThat(ObjectUtils.nullSafeConciseToString("")).isEqualTo("");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString("foo")).isEqualTo("foo");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(repeat100)).isEqualTo(repeat100);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(repeat101)).hasSize(truncatedLength).endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForStringBuilders() {
|
||||
String repeat100 = repeat("X", 100);
|
||||
String repeat101 = repeat("X", 101);
|
||||
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new StringBuilder("foo"))).isEqualTo("foo");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new StringBuilder(repeat100))).isEqualTo(repeat100);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new StringBuilder(repeat101))).hasSize(truncatedLength).endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForEnum() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(Tropes.FOO)).isEqualTo("FOO");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForNumber() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(42L)).isEqualTo("42");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(99.1234D)).isEqualTo("99.1234");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForDate() {
|
||||
Date date = new Date();
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(date)).isEqualTo(date.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForTemporal() {
|
||||
LocalDate localDate = LocalDate.now();
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(localDate)).isEqualTo(localDate.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForUri() {
|
||||
String uri = "https://www.example.com/?foo=1&bar=2&baz=3";
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(URI.create(uri))).isEqualTo(uri);
|
||||
|
||||
uri += "&qux=" + repeat("4", 60);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(URI.create(uri)))
|
||||
.hasSize(truncatedLength)
|
||||
.startsWith(uri.subSequence(0, 100))
|
||||
.endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForUrl() throws Exception {
|
||||
String url = "https://www.example.com/?foo=1&bar=2&baz=3";
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new URL(url))).isEqualTo(url);
|
||||
|
||||
url += "&qux=" + repeat("4", 60);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new URL(url)))
|
||||
.hasSize(truncatedLength)
|
||||
.startsWith(url.subSequence(0, 100))
|
||||
.endsWith(truncated);
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForLocale() {
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(Locale.GERMANY)).isEqualTo("de_DE");
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForArraysAndCollections() {
|
||||
List<String> list = Arrays.asList("a", "b", "c");
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new int[][] {{1, 2}, {3, 4}})).startsWith(prefix(int[][].class));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(list.toArray(new Object[0]))).startsWith(prefix(Object[].class));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(list.toArray(new String[0]))).startsWith(prefix(String[].class));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new ArrayList<>(list))).startsWith(prefix(ArrayList.class));
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(new HashSet<>(list))).startsWith(prefix(HashSet.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void nullSafeConciseToStringForCustomTypes() {
|
||||
class ExplosiveType {
|
||||
@Override
|
||||
public String toString() {
|
||||
throw new UnsupportedOperationException("no-go");
|
||||
}
|
||||
}
|
||||
ExplosiveType explosiveType = new ExplosiveType();
|
||||
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(explosiveType::toString);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(explosiveType)).startsWith(prefix(ExplosiveType.class));
|
||||
|
||||
class WordyType {
|
||||
@Override
|
||||
public String toString() {
|
||||
return repeat("blah blah", 20);
|
||||
}
|
||||
}
|
||||
WordyType wordyType = new WordyType();
|
||||
assertThat(wordyType).asString().hasSizeGreaterThanOrEqualTo(180 /* 9x20 */);
|
||||
assertThat(ObjectUtils.nullSafeConciseToString(wordyType)).startsWith(prefix(WordyType.class));
|
||||
}
|
||||
|
||||
private String repeat(String str, int count) {
|
||||
String result = "";
|
||||
for (int i = 0; i < count; i++) {
|
||||
result += str;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String prefix(Class<?> clazz) {
|
||||
return clazz.getTypeName() + "@";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private static void assertEqualHashCodes(int expected, Object array) {
|
||||
private void assertEqualHashCodes(int expected, Object array) {
|
||||
int actual = ObjectUtils.nullSafeHashCode(array);
|
||||
assertThat(actual).isEqualTo(expected);
|
||||
assertThat(array.hashCode() != actual).isTrue();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* 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.
|
||||
@@ -22,8 +22,6 @@ import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
@@ -787,26 +785,4 @@ class StringUtilsTests {
|
||||
assertThat(StringUtils.collectionToCommaDelimitedString(Collections.singletonList(null))).isEqualTo("null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void truncatePreconditions() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> StringUtils.truncate("foo", 0))
|
||||
.withMessage("Truncation threshold must be a positive number: 0");
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> StringUtils.truncate("foo", -99))
|
||||
.withMessage("Truncation threshold must be a positive number: -99");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(delimiterString = "-->", value = {
|
||||
"'' --> ''",
|
||||
"aardvark --> aardvark",
|
||||
"aardvark12 --> aardvark12",
|
||||
"aardvark123 --> aardvark12 (truncated)...",
|
||||
"aardvark, bird, cat --> aardvark, (truncated)..."
|
||||
})
|
||||
void truncate(String text, String truncated) {
|
||||
assertThat(StringUtils.truncate(text, 10)).isEqualTo(truncated);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-39
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.expression;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
@@ -25,21 +24,12 @@ import org.springframework.lang.Nullable;
|
||||
* Expressions are executed in an evaluation context. It is in this context that
|
||||
* references are resolved when encountered during expression evaluation.
|
||||
*
|
||||
* <p>There are two default implementations of this interface.
|
||||
* <ul>
|
||||
* <li>{@link org.springframework.expression.spel.support.SimpleEvaluationContext
|
||||
* SimpleEvaluationContext}: a simpler builder-style {@code EvaluationContext}
|
||||
* variant for data-binding purposes, which allows for opting into several SpEL
|
||||
* features as needed.</li>
|
||||
* <li>{@link org.springframework.expression.spel.support.StandardEvaluationContext
|
||||
* StandardEvaluationContext}: a powerful and highly configurable {@code EvaluationContext}
|
||||
* implementation, which can be extended, rather than having to implement everything
|
||||
* manually.</li>
|
||||
* </ul>
|
||||
* <p>There is a default implementation of this EvaluationContext interface:
|
||||
* {@link org.springframework.expression.spel.support.StandardEvaluationContext}
|
||||
* which can be extended, rather than having to implement everything manually.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface EvaluationContext {
|
||||
@@ -95,30 +85,7 @@ public interface EvaluationContext {
|
||||
OperatorOverloader getOperatorOverloader();
|
||||
|
||||
/**
|
||||
* Assign the value created by the specified {@link Supplier} to a named variable
|
||||
* within this evaluation context.
|
||||
* <p>In contrast to {@link #setVariable(String, Object)}, this method should only
|
||||
* be invoked to support the assignment operator ({@code =}) within an expression.
|
||||
* <p>By default, this method delegates to {@code setVariable(String, Object)},
|
||||
* providing the value created by the {@code valueSupplier}. Concrete implementations
|
||||
* may override this <em>default</em> method to provide different semantics.
|
||||
* @param name the name of the variable to assign
|
||||
* @param valueSupplier the supplier of the value to be assigned to the variable
|
||||
* @return a {@link TypedValue} wrapping the assigned value
|
||||
* @since 5.2.24
|
||||
*/
|
||||
default TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
|
||||
TypedValue typedValue = valueSupplier.get();
|
||||
setVariable(name, typedValue.getValue());
|
||||
return typedValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a named variable in this evaluation context to a specified value.
|
||||
* <p>In contrast to {@link #assignVariable(String, Supplier)}, this method
|
||||
* should only be invoked programmatically when interacting directly with the
|
||||
* {@code EvaluationContext} — for example, to provide initial
|
||||
* configuration for the context.
|
||||
* Set a named variable within this evaluation context to a specified value.
|
||||
* @param name the name of the variable to set
|
||||
* @param value the value to be placed in the variable
|
||||
*/
|
||||
@@ -126,7 +93,7 @@ public interface EvaluationContext {
|
||||
|
||||
/**
|
||||
* Look up a named variable within this evaluation context.
|
||||
* @param name the name of the variable to look up
|
||||
* @param name variable to lookup
|
||||
* @return the value of the variable, or {@code null} if not found
|
||||
*/
|
||||
@Nullable
|
||||
|
||||
+4
-29
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -23,7 +23,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
@@ -39,19 +38,18 @@ import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* ExpressionState is for maintaining per-expression-evaluation state: any changes to
|
||||
* it are not seen by other expressions, but it gives a place to hold local variables and
|
||||
* An ExpressionState is for maintaining per-expression-evaluation state, any changes to
|
||||
* it are not seen by other expressions but it gives a place to hold local variables and
|
||||
* for component expressions in a compound expression to communicate state. This is in
|
||||
* contrast to the EvaluationContext, which is shared amongst expression evaluations, and
|
||||
* any changes to it will be seen by other expressions or any code that chooses to ask
|
||||
* questions of the context.
|
||||
*
|
||||
* <p>It also acts as a place to define common utility routines that the various AST
|
||||
* <p>It also acts as a place for to define common utility routines that the various AST
|
||||
* nodes might need.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class ExpressionState {
|
||||
@@ -140,29 +138,6 @@ public class ExpressionState {
|
||||
return this.scopeRootObjects.element();
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign the value created by the specified {@link Supplier} to a named variable
|
||||
* within the evaluation context.
|
||||
* <p>In contrast to {@link #setVariable(String, Object)}, this method should
|
||||
* only be invoked to support assignment within an expression.
|
||||
* @param name the name of the variable to assign
|
||||
* @param valueSupplier the supplier of the value to be assigned to the variable
|
||||
* @return a {@link TypedValue} wrapping the assigned value
|
||||
* @since 5.2.24
|
||||
* @see EvaluationContext#assignVariable(String, Supplier)
|
||||
*/
|
||||
public TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
|
||||
return this.relatedContext.assignVariable(name, valueSupplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a named variable in the evaluation context to a specified value.
|
||||
* <p>In contrast to {@link #assignVariable(String, Supplier)}, this method
|
||||
* should only be invoked programmatically.
|
||||
* @param name the name of the variable to set
|
||||
* @param value the value to be placed in the variable
|
||||
* @see EvaluationContext#setVariable(String, Object)
|
||||
*/
|
||||
public void setVariable(String name, @Nullable Object value) {
|
||||
this.relatedContext.setVariable(name, value);
|
||||
}
|
||||
|
||||
+4
-16
@@ -266,25 +266,13 @@ public enum SpelMessage {
|
||||
MAX_ARRAY_ELEMENTS_THRESHOLD_EXCEEDED(Kind.ERROR, 1075,
|
||||
"Array declares too many elements, exceeding the threshold of ''{0}''"),
|
||||
|
||||
/** @since 5.2.23 */
|
||||
/** @since 5.3.26 */
|
||||
MAX_REPEATED_TEXT_SIZE_EXCEEDED(Kind.ERROR, 1076,
|
||||
"Repeated text is too long, exceeding the threshold of ''{0}'' characters"),
|
||||
"Repeated text results in too many characters, exceeding the threshold of ''{0}''"),
|
||||
|
||||
/** @since 5.2.23 */
|
||||
/** @since 5.3.26 */
|
||||
MAX_REGEX_LENGTH_EXCEEDED(Kind.ERROR, 1077,
|
||||
"Regular expression is too long, exceeding the threshold of ''{0}'' characters"),
|
||||
|
||||
/** @since 5.2.24 */
|
||||
MAX_CONCATENATED_STRING_LENGTH_EXCEEDED(Kind.ERROR, 1078,
|
||||
"Concatenated string is too long, exceeding the threshold of ''{0}'' characters"),
|
||||
|
||||
/** @since 5.2.24 */
|
||||
MAX_EXPRESSION_LENGTH_EXCEEDED(Kind.ERROR, 1079,
|
||||
"SpEL expression is too long, exceeding the threshold of ''{0}'' characters"),
|
||||
|
||||
/** @since 5.2.24 */
|
||||
VARIABLE_ASSIGNMENT_NOT_SUPPORTED(Kind.ERROR, 1080,
|
||||
"Assignment to variable ''{0}'' is not supported");
|
||||
"Regular expression contains too many characters, exceeding the threshold of ''{0}''");
|
||||
|
||||
|
||||
private final Kind kind;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -27,7 +27,6 @@ import org.springframework.expression.spel.ExpressionState;
|
||||
* <p>Example: 'someNumberProperty=42'
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class Assign extends SpelNodeImpl {
|
||||
@@ -39,7 +38,9 @@ public class Assign extends SpelNodeImpl {
|
||||
|
||||
@Override
|
||||
public TypedValue getValueInternal(ExpressionState state) throws EvaluationException {
|
||||
return this.children[0].setValueInternal(state, () -> this.children[1].getValueInternal(state));
|
||||
TypedValue newValue = this.children[1].getValueInternal(state);
|
||||
getChild(0).setValue(state, newValue.getValue());
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -17,7 +17,6 @@
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
@@ -25,13 +24,13 @@ import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a DOT separated expression sequence, such as
|
||||
* {@code 'property1.property2.methodOne()'}.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class CompoundExpression extends SpelNodeImpl {
|
||||
@@ -96,12 +95,8 @@ public class CompoundExpression extends SpelNodeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
|
||||
throws EvaluationException {
|
||||
|
||||
TypedValue typedValue = valueSupplier.get();
|
||||
getValueRef(state).setValue(typedValue.getValue());
|
||||
return typedValue;
|
||||
public void setValue(ExpressionState state, @Nullable Object value) throws EvaluationException {
|
||||
getValueRef(state).setValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -25,7 +25,6 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -46,12 +45,11 @@ import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* An Indexer can index into some proceeding structure to access a particular piece of it.
|
||||
* <p>Supported structures are: strings / collections (lists/sets) / arrays.
|
||||
* Supported structures are: strings / collections (lists/sets) / arrays.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Phillip Webb
|
||||
* @author Stephane Nicoll
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
// TODO support multidimensional arrays
|
||||
@@ -104,12 +102,8 @@ public class Indexer extends SpelNodeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
|
||||
throws EvaluationException {
|
||||
|
||||
TypedValue typedValue = valueSupplier.get();
|
||||
getValueRef(state).setValue(typedValue.getValue());
|
||||
return typedValue;
|
||||
public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException {
|
||||
getValueRef(state).setValue(newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ public class OpMultiply extends Operator {
|
||||
|
||||
/**
|
||||
* Maximum number of characters permitted in repeated text.
|
||||
* @since 5.2.23
|
||||
* @since 5.3.26
|
||||
*/
|
||||
private static final int MAX_REPEATED_TEXT_SIZE = 256;
|
||||
|
||||
|
||||
+6
-39
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -27,8 +27,6 @@ import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.NumberUtils;
|
||||
@@ -48,18 +46,10 @@ import org.springframework.util.NumberUtils;
|
||||
* @author Juergen Hoeller
|
||||
* @author Ivo Smid
|
||||
* @author Giovanni Dall'Oglio Risso
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class OpPlus extends Operator {
|
||||
|
||||
/**
|
||||
* Maximum number of characters permitted in a concatenated string.
|
||||
* @since 5.2.24
|
||||
*/
|
||||
private static final int MAX_CONCATENATED_STRING_LENGTH = 100_000;
|
||||
|
||||
|
||||
public OpPlus(int startPos, int endPos, SpelNodeImpl... operands) {
|
||||
super("+", startPos, endPos, operands);
|
||||
Assert.notEmpty(operands, "Operands must not be empty");
|
||||
@@ -133,45 +123,22 @@ public class OpPlus extends Operator {
|
||||
|
||||
if (leftOperand instanceof String && rightOperand instanceof String) {
|
||||
this.exitTypeDescriptor = "Ljava/lang/String";
|
||||
String leftString = (String) leftOperand;
|
||||
String rightString = (String) rightOperand;
|
||||
checkStringLength(leftString);
|
||||
checkStringLength(rightString);
|
||||
return concatenate(leftString, rightString);
|
||||
return new TypedValue((String) leftOperand + rightOperand);
|
||||
}
|
||||
|
||||
if (leftOperand instanceof String) {
|
||||
String leftString = (String) leftOperand;
|
||||
checkStringLength(leftString);
|
||||
String rightString = (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state));
|
||||
checkStringLength(rightString);
|
||||
return concatenate(leftString, rightString);
|
||||
return new TypedValue(
|
||||
leftOperand + (rightOperand == null ? "null" : convertTypedValueToString(operandTwoValue, state)));
|
||||
}
|
||||
|
||||
if (rightOperand instanceof String) {
|
||||
String rightString = (String) rightOperand;
|
||||
checkStringLength(rightString);
|
||||
String leftString = (leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state));
|
||||
checkStringLength(leftString);
|
||||
return concatenate(leftString, rightString);
|
||||
return new TypedValue(
|
||||
(leftOperand == null ? "null" : convertTypedValueToString(operandOneValue, state)) + rightOperand);
|
||||
}
|
||||
|
||||
return state.operate(Operation.ADD, leftOperand, rightOperand);
|
||||
}
|
||||
|
||||
private void checkStringLength(String string) {
|
||||
if (string.length() > MAX_CONCATENATED_STRING_LENGTH) {
|
||||
throw new SpelEvaluationException(getStartPosition(),
|
||||
SpelMessage.MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, MAX_CONCATENATED_STRING_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
private TypedValue concatenate(String leftString, String rightString) {
|
||||
String result = leftString + rightString;
|
||||
checkStringLength(result);
|
||||
return new TypedValue(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
if (this.children.length < 2) { // unary plus
|
||||
|
||||
+4
-4
@@ -45,16 +45,16 @@ public class OperatorMatches extends Operator {
|
||||
|
||||
/**
|
||||
* Maximum number of characters permitted in a regular expression.
|
||||
* @since 5.2.23
|
||||
* @since 5.3.26
|
||||
*/
|
||||
private static final int MAX_REGEX_LENGTH = 1000;
|
||||
private static final int MAX_REGEX_LENGTH = 256;
|
||||
|
||||
private final ConcurrentMap<String, Pattern> patternCache;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link OperatorMatches} instance.
|
||||
* @deprecated as of Spring Framework 5.2.23 in favor of invoking
|
||||
* @deprecated as of Spring Framework 5.3.26 in favor of invoking
|
||||
* {@link #OperatorMatches(ConcurrentMap, int, int, SpelNodeImpl...)}
|
||||
* with a shared pattern cache instead
|
||||
*/
|
||||
@@ -65,7 +65,7 @@ public class OperatorMatches extends Operator {
|
||||
|
||||
/**
|
||||
* Create a new {@link OperatorMatches} instance with a shared pattern cache.
|
||||
* @since 5.2.23
|
||||
* @since 5.3.26
|
||||
*/
|
||||
public OperatorMatches(ConcurrentMap<String, Pattern> patternCache, int startPos, int endPos, SpelNodeImpl... operands) {
|
||||
super("matches", startPos, endPos, operands);
|
||||
|
||||
+3
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -21,7 +21,6 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.asm.Label;
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
@@ -47,7 +46,6 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Clark Duplichien
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
@@ -149,12 +147,8 @@ public class PropertyOrFieldReference extends SpelNodeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
|
||||
throws EvaluationException {
|
||||
|
||||
TypedValue typedValue = valueSupplier.get();
|
||||
writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, typedValue.getValue());
|
||||
return typedValue;
|
||||
public void setValue(ExpressionState state, @Nullable Object newValue) throws EvaluationException {
|
||||
writeProperty(state.getActiveContextObject(), state.getEvaluationContext(), this.name, newValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-28
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -19,7 +19,6 @@ package org.springframework.expression.spel.ast;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.asm.Opcodes;
|
||||
@@ -41,7 +40,6 @@ import org.springframework.util.ObjectUtils;
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
||||
@@ -66,7 +64,7 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
||||
* <p>The descriptor is like the bytecode form but is slightly easier to work with.
|
||||
* It does not include the trailing semicolon (for non array reference types).
|
||||
* Some examples: Ljava/lang/String, I, [I
|
||||
*/
|
||||
*/
|
||||
@Nullable
|
||||
protected volatile String exitTypeDescriptor;
|
||||
|
||||
@@ -85,8 +83,8 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
||||
|
||||
|
||||
/**
|
||||
* Return {@code true} if the next child is one of the specified classes.
|
||||
*/
|
||||
* Return {@code true} if the next child is one of the specified classes.
|
||||
*/
|
||||
protected boolean nextChildIs(Class<?>... classes) {
|
||||
if (this.parent != null) {
|
||||
SpelNodeImpl[] peers = this.parent.children;
|
||||
@@ -127,28 +125,6 @@ public abstract class SpelNodeImpl implements SpelNode, Opcodes {
|
||||
|
||||
@Override
|
||||
public void setValue(ExpressionState expressionState, @Nullable Object newValue) throws EvaluationException {
|
||||
setValueInternal(expressionState, () -> new TypedValue(newValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate the expression to a node and then set the new value created by the
|
||||
* specified {@link Supplier} on that node.
|
||||
* <p>For example, if the expression evaluates to a property reference, then the
|
||||
* property will be set to the new value.
|
||||
* <p>Favor this method over {@link #setValue(ExpressionState, Object)} when
|
||||
* the value should be lazily computed.
|
||||
* <p>By default, this method throws a {@link SpelEvaluationException},
|
||||
* effectively disabling this feature. Subclasses may override this method to
|
||||
* provide an actual implementation.
|
||||
* @param expressionState the current expression state (includes the context)
|
||||
* @param valueSupplier a supplier of the new value
|
||||
* @throws EvaluationException if any problem occurs evaluating the expression or
|
||||
* setting the new value
|
||||
* @since 5.2.24
|
||||
*/
|
||||
public TypedValue setValueInternal(ExpressionState expressionState, Supplier<TypedValue> valueSupplier)
|
||||
throws EvaluationException {
|
||||
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.SETVALUE_NOT_SUPPORTED, getClass());
|
||||
}
|
||||
|
||||
|
||||
+8
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -17,11 +17,9 @@
|
||||
package org.springframework.expression.spel.ast;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.asm.MethodVisitor;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.expression.spel.CodeFlow;
|
||||
import org.springframework.expression.spel.ExpressionState;
|
||||
@@ -29,11 +27,10 @@ import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.lang.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a variable reference — for example, {@code #someVar}. Note
|
||||
* that this is different than a <em>local</em> variable like {@code $someVar}.
|
||||
* Represents a variable reference, eg. #someVar. Note this is different to a *local*
|
||||
* variable like $someVar
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Sam Brannen
|
||||
* @since 3.0
|
||||
*/
|
||||
public class VariableReference extends SpelNodeImpl {
|
||||
@@ -56,14 +53,14 @@ public class VariableReference extends SpelNodeImpl {
|
||||
@Override
|
||||
public ValueRef getValueRef(ExpressionState state) throws SpelEvaluationException {
|
||||
if (this.name.equals(THIS)) {
|
||||
return new ValueRef.TypedValueHolderValueRef(state.getActiveContextObject(), this);
|
||||
return new ValueRef.TypedValueHolderValueRef(state.getActiveContextObject(),this);
|
||||
}
|
||||
if (this.name.equals(ROOT)) {
|
||||
return new ValueRef.TypedValueHolderValueRef(state.getRootContextObject(), this);
|
||||
return new ValueRef.TypedValueHolderValueRef(state.getRootContextObject(),this);
|
||||
}
|
||||
TypedValue result = state.lookupVariable(this.name);
|
||||
// a null value will mean either the value was null or the variable was not found
|
||||
return new VariableRef(this.name, result, state.getEvaluationContext());
|
||||
return new VariableRef(this.name,result,state.getEvaluationContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -93,10 +90,8 @@ public class VariableReference extends SpelNodeImpl {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedValue setValueInternal(ExpressionState state, Supplier<TypedValue> valueSupplier)
|
||||
throws EvaluationException {
|
||||
|
||||
return state.assignVariable(this.name, valueSupplier);
|
||||
public void setValue(ExpressionState state, @Nullable Object value) throws SpelEvaluationException {
|
||||
state.setVariable(this.name, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-15
@@ -29,7 +29,6 @@ import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.ParserContext;
|
||||
import org.springframework.expression.common.TemplateAwareExpressionParser;
|
||||
import org.springframework.expression.spel.InternalParseException;
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.expression.spel.SpelMessage;
|
||||
import org.springframework.expression.spel.SpelParseException;
|
||||
import org.springframework.expression.spel.SpelParserConfiguration;
|
||||
@@ -93,12 +92,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
|
||||
private static final Pattern VALID_QUALIFIED_ID_PATTERN = Pattern.compile("[\\p{L}\\p{N}_$]+");
|
||||
|
||||
/**
|
||||
* Maximum length permitted for a SpEL expression.
|
||||
* @since 5.2.24
|
||||
*/
|
||||
private static final int MAX_EXPRESSION_LENGTH = 10_000;
|
||||
|
||||
|
||||
private final SpelParserConfiguration configuration;
|
||||
|
||||
@@ -134,8 +127,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
protected SpelExpression doParseExpression(String expressionString, @Nullable ParserContext context)
|
||||
throws ParseException {
|
||||
|
||||
checkExpressionLength(expressionString);
|
||||
|
||||
try {
|
||||
this.expressionString = expressionString;
|
||||
Tokenizer tokenizer = new Tokenizer(expressionString);
|
||||
@@ -157,12 +148,6 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void checkExpressionLength(String string) {
|
||||
if (string.length() > MAX_EXPRESSION_LENGTH) {
|
||||
throw new SpelEvaluationException(SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED, MAX_EXPRESSION_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
// expression
|
||||
// : logicalOrExpression
|
||||
// ( (ASSIGN^ logicalOrExpression)
|
||||
|
||||
+1
-14
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2018 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.
|
||||
@@ -21,7 +21,6 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
@@ -79,7 +78,6 @@ import org.springframework.lang.Nullable;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Juergen Hoeller
|
||||
* @author Sam Brannen
|
||||
* @since 4.3.15
|
||||
* @see #forPropertyAccessors
|
||||
* @see #forReadOnlyDataBinding()
|
||||
@@ -202,17 +200,6 @@ public final class SimpleEvaluationContext implements EvaluationContext {
|
||||
return this.operatorOverloader;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@code SimpleEvaluationContext} does not support variable assignment within
|
||||
* expressions.
|
||||
* @throws SpelEvaluationException with {@link SpelMessage#VARIABLE_ASSIGNMENT_NOT_SUPPORTED}
|
||||
* @since 5.2.24
|
||||
*/
|
||||
@Override
|
||||
public TypedValue assignVariable(String name, Supplier<TypedValue> valueSupplier) {
|
||||
throw new SpelEvaluationException(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED, "#" + name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVariable(String name, @Nullable Object value) {
|
||||
this.variables.put(name, value);
|
||||
|
||||
+8
-48
@@ -25,8 +25,6 @@ import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
@@ -38,7 +36,6 @@ import org.springframework.expression.MethodFilter;
|
||||
import org.springframework.expression.ParseException;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.SimpleEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardTypeLocator;
|
||||
import org.springframework.expression.spel.testresources.TestPerson;
|
||||
@@ -63,20 +60,6 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
@Nested
|
||||
class MiscellaneousTests {
|
||||
|
||||
@Test
|
||||
void expressionLength() {
|
||||
String expression = String.format("'X' + '%s'", repeat(" ", 9_992));
|
||||
assertThat(expression).hasSize(10_000);
|
||||
Expression expr = parser.parseExpression(expression);
|
||||
String result = expr.getValue(context, String.class);
|
||||
assertThat(result).hasSize(9_993);
|
||||
assertThat(result.trim()).isEqualTo("X");
|
||||
|
||||
expression = String.format("'X' + '%s'", repeat(" ", 9_993));
|
||||
assertThat(expression).hasSize(10_001);
|
||||
evaluateAndCheckError(expression, String.class, SpelMessage.MAX_EXPRESSION_LENGTH_EXCEEDED);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createListsOnAttemptToIndexNull01() throws EvaluationException, ParseException {
|
||||
ExpressionParser parser = new SpelExpressionParser(new SpelParserConfiguration(true, true));
|
||||
@@ -152,24 +135,8 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
|
||||
// assignment
|
||||
@Test
|
||||
void assignmentToVariableWithStandardEvaluationContext() {
|
||||
evaluate("#var1 = 'value1'", "value1", String.class);
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@CsvSource(delimiterString = "->", value = {
|
||||
"'#var1 = \"value1\"' -> #var1",
|
||||
"'true ? #myVar = 4 : 0' -> #myVar"
|
||||
})
|
||||
void assignmentToVariableWithSimpleEvaluationContext(String expression, String varName) {
|
||||
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
|
||||
Expression expr = parser.parseExpression(expression);
|
||||
assertThatExceptionOfType(SpelEvaluationException.class)
|
||||
.isThrownBy(() -> expr.getValue(context))
|
||||
.satisfies(ex -> {
|
||||
assertThat(ex.getMessageCode()).isEqualTo(SpelMessage.VARIABLE_ASSIGNMENT_NOT_SUPPORTED);
|
||||
assertThat(ex.getInserts()).as("inserts").containsExactly(varName);
|
||||
});
|
||||
void assignmentToVariables() {
|
||||
evaluate("#var1='value1'", "value1", String.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -515,13 +482,15 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
|
||||
@Test
|
||||
void matchesWithPatternLengthThreshold() {
|
||||
String pattern = String.format("^(%s|X)", repeat("12345", 199));
|
||||
assertThat(pattern).hasSize(1000);
|
||||
Expression expr = parser.parseExpression("'X' matches '" + pattern + "'");
|
||||
String pattern = "(0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
||||
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789" +
|
||||
"01234567890123456789012345678901234567890123456789|abc)";
|
||||
assertThat(pattern).hasSize(256);
|
||||
Expression expr = parser.parseExpression("'abc' matches '" + pattern + "'");
|
||||
assertThat(expr.getValue(context, Boolean.class)).isTrue();
|
||||
|
||||
pattern += "?";
|
||||
assertThat(pattern).hasSize(1001);
|
||||
assertThat(pattern).hasSize(257);
|
||||
evaluateAndCheckError("'abc' matches '" + pattern + "'", Boolean.class, SpelMessage.MAX_REGEX_LENGTH_EXCEEDED);
|
||||
}
|
||||
|
||||
@@ -1499,15 +1468,6 @@ class EvaluationTests extends AbstractExpressionTests {
|
||||
}
|
||||
|
||||
|
||||
private static String repeat(String str, int count) {
|
||||
String result = "";
|
||||
for (int i = 0; i < count; i++) {
|
||||
result += str;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
static class TestClass {
|
||||
|
||||
|
||||
+10
-55
@@ -26,7 +26,6 @@ import org.springframework.expression.spel.ast.Operator;
|
||||
import org.springframework.expression.spel.standard.SpelExpression;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.expression.spel.SpelMessage.MAX_CONCATENATED_STRING_LENGTH_EXCEEDED;
|
||||
import static org.springframework.expression.spel.SpelMessage.MAX_REPEATED_TEXT_SIZE_EXCEEDED;
|
||||
|
||||
/**
|
||||
@@ -392,7 +391,11 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
evaluate("3.0f + 5.0f", 8.0f, Float.class);
|
||||
evaluate("3.0d + 5.0d", 8.0d, Double.class);
|
||||
evaluate("3 + new java.math.BigDecimal('5')", new BigDecimal("8"), BigDecimal.class);
|
||||
evaluate("5 + new Integer('37')", 42, Integer.class);
|
||||
|
||||
evaluate("'ab' + 2", "ab2", String.class);
|
||||
evaluate("2 + 'a'", "2a", String.class);
|
||||
evaluate("'ab' + null", "abnull", String.class);
|
||||
evaluate("null + 'ab'", "nullab", String.class);
|
||||
|
||||
// AST:
|
||||
SpelExpression expr = (SpelExpression) parser.parseExpression("+3");
|
||||
@@ -406,6 +409,11 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
evaluate("+5", 5, Integer.class);
|
||||
evaluate("+new java.math.BigDecimal('5')", new BigDecimal("5"), BigDecimal.class);
|
||||
evaluateAndCheckError("+'abc'", SpelMessage.OPERATOR_NOT_SUPPORTED_BETWEEN_TYPES);
|
||||
|
||||
// string concatenation
|
||||
evaluate("'abc'+'def'", "abcdef", String.class);
|
||||
|
||||
evaluate("5 + new Integer('37')", 42, Integer.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -579,59 +587,6 @@ class OperatorTests extends AbstractExpressionTests {
|
||||
evaluateAndCheckError("'a' * 257", String.class, MAX_REPEATED_TEXT_SIZE_EXCEEDED, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void stringConcatenation() {
|
||||
evaluate("'' + ''", "", String.class);
|
||||
evaluate("'' + null", "null", String.class);
|
||||
evaluate("null + ''", "null", String.class);
|
||||
evaluate("'ab' + null", "abnull", String.class);
|
||||
evaluate("null + 'ab'", "nullab", String.class);
|
||||
evaluate("'ab' + 2", "ab2", String.class);
|
||||
evaluate("2 + 'ab'", "2ab", String.class);
|
||||
evaluate("'abc' + 'def'", "abcdef", String.class);
|
||||
|
||||
// Text is big but not too big
|
||||
final int maxSize = 100_000;
|
||||
context.setVariable("text1", createString(maxSize));
|
||||
Expression expr = parser.parseExpression("#text1 + ''");
|
||||
assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
|
||||
|
||||
expr = parser.parseExpression("'' + #text1");
|
||||
assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
|
||||
|
||||
context.setVariable("text1", createString(maxSize / 2));
|
||||
expr = parser.parseExpression("#text1 + #text1");
|
||||
assertThat(expr.getValue(context, String.class)).hasSize(maxSize);
|
||||
|
||||
// Text is too big
|
||||
context.setVariable("text1", createString(maxSize + 1));
|
||||
evaluateAndCheckError("#text1 + ''", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
evaluateAndCheckError("#text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
evaluateAndCheckError("'' + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 3);
|
||||
evaluateAndCheckError("true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 5);
|
||||
|
||||
context.setVariable("text1", createString(maxSize / 2));
|
||||
context.setVariable("text2", createString((maxSize / 2) + 1));
|
||||
evaluateAndCheckError("#text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
evaluateAndCheckError("#text1 + #text2 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
evaluateAndCheckError("#text1 + true + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
|
||||
evaluateAndCheckError("true + #text1 + #text2", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
|
||||
|
||||
evaluateAndCheckError("#text2 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
evaluateAndCheckError("#text2 + #text1 + true", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
evaluateAndCheckError("#text2 + true + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
|
||||
evaluateAndCheckError("true + #text2 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 14);
|
||||
|
||||
context.setVariable("text1", createString((maxSize / 3) + 1));
|
||||
evaluateAndCheckError("#text1 + #text1 + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 16);
|
||||
evaluateAndCheckError("(#text1 + #text1) + #text1", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 18);
|
||||
evaluateAndCheckError("#text1 + (#text1 + #text1)", String.class, MAX_CONCATENATED_STRING_LENGTH_EXCEEDED, 7);
|
||||
}
|
||||
|
||||
private static String createString(int size) {
|
||||
return new String(new char[size]);
|
||||
}
|
||||
|
||||
@Test
|
||||
void longs() {
|
||||
evaluate("3L == 4L", false, Boolean.class);
|
||||
|
||||
+1
-1
@@ -436,7 +436,7 @@ public abstract class SharedEntityManagerCreator {
|
||||
entry.setValue(storedProc.getOutputParameterValue(key.toString()));
|
||||
}
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
catch (IllegalArgumentException ex) {
|
||||
entry.setValue(ex);
|
||||
}
|
||||
}
|
||||
|
||||
+12
-12
@@ -125,11 +125,11 @@ public abstract class MockRestRequestMatchers {
|
||||
* @param name the name of the query parameter whose value(s) will be asserted
|
||||
* @param matcher the Hamcrest matcher to apply to the entire list of values
|
||||
* for the given query parameter
|
||||
* @since 5.3.27
|
||||
* @since 5.3.26
|
||||
* @see #queryParam(String, Matcher...)
|
||||
* @see #queryParam(String, String...)
|
||||
*/
|
||||
public static RequestMatcher queryParamList(String name, Matcher<? super List<String>> matcher) {
|
||||
public static RequestMatcher queryParam(String name, Matcher<? super List<String>> matcher) {
|
||||
return request -> {
|
||||
MultiValueMap<String, String> params = getQueryParams(request);
|
||||
List<String> paramValues = params.get(name);
|
||||
@@ -147,14 +147,14 @@ public abstract class MockRestRequestMatchers {
|
||||
* values, effectively ignoring the additional parameter values. If the number
|
||||
* of provided {@code matchers} exceeds the number of query parameter values,
|
||||
* an {@link AssertionError} will be thrown to signal the mismatch.
|
||||
* <p>See {@link #queryParamList(String, Matcher)} for a variant which accepts a
|
||||
* <p>See {@link #queryParam(String, Matcher)} for a variant which accepts a
|
||||
* {@code Matcher} that applies to the entire list of values as opposed to
|
||||
* applying only to individual values.
|
||||
* @param name the name of the query parameter whose value(s) will be asserted
|
||||
* @param matchers the Hamcrest matchers to apply to individual query parameter
|
||||
* values; the n<sup>th</sup> matcher is applied to the n<sup>th</sup> query
|
||||
* parameter value
|
||||
* @see #queryParamList(String, Matcher)
|
||||
* @see #queryParam(String, Matcher)
|
||||
* @see #queryParam(String, String...)
|
||||
*/
|
||||
@SafeVarargs
|
||||
@@ -175,14 +175,14 @@ public abstract class MockRestRequestMatchers {
|
||||
* parameter values, effectively ignoring the additional parameter values. If
|
||||
* the number of {@code expectedValues} exceeds the number of query parameter
|
||||
* values, an {@link AssertionError} will be thrown to signal the mismatch.
|
||||
* <p>See {@link #queryParamList(String, Matcher)} for a variant which accepts a
|
||||
* <p>See {@link #queryParam(String, Matcher)} for a variant which accepts a
|
||||
* Hamcrest {@code Matcher} that applies to the entire list of values as opposed
|
||||
* to asserting only individual values.
|
||||
* @param name the name of the query parameter whose value(s) will be asserted
|
||||
* @param expectedValues the expected values of individual query parameter values;
|
||||
* the n<sup>th</sup> expected value is compared to the n<sup>th</sup> query
|
||||
* parameter value
|
||||
* @see #queryParamList(String, Matcher)
|
||||
* @see #queryParam(String, Matcher)
|
||||
* @see #queryParam(String, Matcher...)
|
||||
*/
|
||||
public static RequestMatcher queryParam(String name, String... expectedValues) {
|
||||
@@ -211,11 +211,11 @@ public abstract class MockRestRequestMatchers {
|
||||
* @param name the name of the header whose value(s) will be asserted
|
||||
* @param matcher the Hamcrest matcher to apply to the entire list of values
|
||||
* for the given header
|
||||
* @since 5.3.27
|
||||
* @since 5.3.26
|
||||
* @see #header(String, Matcher...)
|
||||
* @see #header(String, String...)
|
||||
*/
|
||||
public static RequestMatcher headerList(String name, Matcher<? super List<String>> matcher) {
|
||||
public static RequestMatcher header(String name, Matcher<? super List<String>> matcher) {
|
||||
return request -> {
|
||||
List<String> headerValues = request.getHeaders().get(name);
|
||||
if (headerValues == null) {
|
||||
@@ -232,13 +232,13 @@ public abstract class MockRestRequestMatchers {
|
||||
* effectively ignoring the additional header values. If the number of
|
||||
* provided {@code matchers} exceeds the number of header values, an
|
||||
* {@link AssertionError} will be thrown to signal the mismatch.
|
||||
* <p>See {@link #headerList(String, Matcher)} for a variant which accepts a
|
||||
* <p>See {@link #header(String, Matcher)} for a variant which accepts a
|
||||
* Hamcrest {@code Matcher} that applies to the entire list of values as
|
||||
* opposed to applying only to individual values.
|
||||
* @param name the name of the header whose value(s) will be asserted
|
||||
* @param matchers the Hamcrest matchers to apply to individual header values;
|
||||
* the n<sup>th</sup> matcher is applied to the n<sup>th</sup> header value
|
||||
* @see #headerList(String, Matcher)
|
||||
* @see #header(String, Matcher)
|
||||
* @see #header(String, String...)
|
||||
*/
|
||||
@SafeVarargs
|
||||
@@ -260,13 +260,13 @@ public abstract class MockRestRequestMatchers {
|
||||
* additional header values. If the number of {@code expectedValues} exceeds the
|
||||
* number of header values, an {@link AssertionError} will be thrown to signal the
|
||||
* mismatch.
|
||||
* <p>See {@link #headerList(String, Matcher)} for a variant which accepts a
|
||||
* <p>See {@link #header(String, Matcher)} for a variant which accepts a
|
||||
* Hamcrest {@code Matcher} that applies to the entire list of values as
|
||||
* opposed to applying only to individual values.
|
||||
* @param name the name of the header whose value(s) will be asserted
|
||||
* @param expectedValues the expected values of individual header values; the
|
||||
* n<sup>th</sup> expected value is compared to the n<sup>th</sup> header value
|
||||
* @see #headerList(String, Matcher)
|
||||
* @see #header(String, Matcher)
|
||||
* @see #header(String, Matcher...)
|
||||
*/
|
||||
public static RequestMatcher header(String name, String... expectedValues) {
|
||||
|
||||
+53
-57
@@ -29,17 +29,19 @@ import org.springframework.mock.http.client.MockClientHttpRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.any;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.either;
|
||||
import static org.hamcrest.Matchers.endsWith;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.everyItem;
|
||||
import static org.hamcrest.Matchers.hasItem;
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.hamcrest.Matchers.startsWith;
|
||||
|
||||
/**
|
||||
@@ -162,7 +164,7 @@ class MockRestRequestMatchersTests {
|
||||
@Test
|
||||
void headerListMissing() {
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.headerList("foo", hasSize(2)).match(this.request))
|
||||
.isThrownBy(() -> MockRestRequestMatchers.header("foo", hasSize(2)).match(this.request))
|
||||
.withMessage("Expected header <foo> to exist but was null");
|
||||
}
|
||||
|
||||
@@ -170,18 +172,29 @@ class MockRestRequestMatchersTests {
|
||||
void headerListMatchers() throws IOException {
|
||||
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
|
||||
|
||||
MockRestRequestMatchers.headerList("foo", containsInAnyOrder(endsWith("baz"), endsWith("bar"))).match(this.request);
|
||||
MockRestRequestMatchers.headerList("foo", contains(is("bar"), is("baz"))).match(this.request);
|
||||
MockRestRequestMatchers.headerList("foo", contains(is("bar"), anything())).match(this.request);
|
||||
MockRestRequestMatchers.headerList("foo", hasItem(endsWith("baz"))).match(this.request);
|
||||
MockRestRequestMatchers.headerList("foo", everyItem(startsWith("ba"))).match(this.request);
|
||||
MockRestRequestMatchers.headerList("foo", hasSize(2)).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", containsInAnyOrder(endsWith("baz"), endsWith("bar"))).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", contains(is("bar"), is("baz"))).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", contains(is("bar"), anything())).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", hasItem(endsWith("baz"))).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", everyItem(startsWith("ba"))).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", hasSize(2)).match(this.request);
|
||||
|
||||
MockRestRequestMatchers.headerList("foo", notNullValue()).match(this.request);
|
||||
MockRestRequestMatchers.headerList("foo", is(anything())).match(this.request);
|
||||
MockRestRequestMatchers.headerList("foo", allOf(notNullValue(), notNullValue())).match(this.request);
|
||||
// These can be a bit ambiguous when reading the test (the compiler selects the list matcher):
|
||||
MockRestRequestMatchers.header("foo", notNullValue()).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", is(anything())).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", allOf(notNullValue(), notNullValue())).match(this.request);
|
||||
|
||||
MockRestRequestMatchers.headerList("foo", allOf(notNullValue(), hasSize(2))).match(this.request);
|
||||
// These are not as ambiguous thanks to an inner matcher that is either obviously list-oriented,
|
||||
// string-oriented, or obviously a vararg of matchers
|
||||
|
||||
// list matcher version
|
||||
MockRestRequestMatchers.header("foo", allOf(notNullValue(), hasSize(2))).match(this.request);
|
||||
|
||||
// vararg version
|
||||
MockRestRequestMatchers.header("foo", allOf(notNullValue(), endsWith("ar"))).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", is((any(String.class)))).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", either(is("bar")).or(is(nullValue()))).match(this.request);
|
||||
MockRestRequestMatchers.header("foo", is(notNullValue()), is(notNullValue())).match(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -189,41 +202,27 @@ class MockRestRequestMatchersTests {
|
||||
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
|
||||
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.headerList("foo", contains(containsString("ba"))).match(this.request))
|
||||
.isThrownBy(() -> MockRestRequestMatchers.header("foo", contains(containsString("ba"))).match(this.request))
|
||||
.withMessageContainingAll(
|
||||
"Request header [foo] values",
|
||||
"Expected: iterable containing [a string containing \"ba\"]",
|
||||
"but: not matched: \"baz\"");
|
||||
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.headerList("foo", hasItem(endsWith("ba"))).match(this.request))
|
||||
.isThrownBy(() -> MockRestRequestMatchers.header("foo", hasItem(endsWith("ba"))).match(this.request))
|
||||
.withMessageContainingAll(
|
||||
"Request header [foo] values",
|
||||
"Expected: a collection containing a string ending with \"ba\"",
|
||||
"but: mismatches were: [was \"bar\", was \"baz\"]");
|
||||
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.headerList("foo", everyItem(endsWith("ar"))).match(this.request))
|
||||
.isThrownBy(() -> MockRestRequestMatchers.header("foo", everyItem(endsWith("ar"))).match(this.request))
|
||||
.withMessageContainingAll(
|
||||
"Request header [foo] values",
|
||||
"Expected: every item is a string ending with \"ar\"",
|
||||
"but: an item was \"baz\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void headerListDoesntHideHeaderWithSingleMatcher() throws IOException {
|
||||
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
|
||||
|
||||
MockRestRequestMatchers.header("foo", equalTo("bar")).match(this.request);
|
||||
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.headerList("foo", equalTo("bar")).match(this.request))
|
||||
.withMessageContainingAll(
|
||||
"Request header [foo] values",
|
||||
"Expected: \"bar\"",
|
||||
"but: was <[bar, baz]>");
|
||||
}
|
||||
|
||||
@Test
|
||||
void headers() throws Exception {
|
||||
this.request.getHeaders().put("foo", Arrays.asList("bar", "baz"));
|
||||
@@ -291,7 +290,7 @@ class MockRestRequestMatchersTests {
|
||||
@Test
|
||||
void queryParamListMissing() {
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParamList("foo", hasSize(2)).match(this.request))
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParam("foo", hasSize(2)).match(this.request))
|
||||
.withMessage("Expected query param <foo> to exist but was null");
|
||||
}
|
||||
|
||||
@@ -299,18 +298,29 @@ class MockRestRequestMatchersTests {
|
||||
void queryParamListMatchers() throws IOException {
|
||||
this.request.setURI(URI.create("http://www.foo.example/a?foo=bar&foo=baz"));
|
||||
|
||||
MockRestRequestMatchers.queryParamList("foo", containsInAnyOrder(endsWith("baz"), endsWith("bar"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParamList("foo", contains(is("bar"), is("baz"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParamList("foo", contains(is("bar"), anything())).match(this.request);
|
||||
MockRestRequestMatchers.queryParamList("foo", hasItem(endsWith("baz"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParamList("foo", everyItem(startsWith("ba"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParamList("foo", hasSize(2)).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", containsInAnyOrder(endsWith("baz"), endsWith("bar"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", contains(is("bar"), is("baz"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", contains(is("bar"), anything())).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", hasItem(endsWith("baz"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", everyItem(startsWith("ba"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", hasSize(2)).match(this.request);
|
||||
|
||||
MockRestRequestMatchers.queryParamList("foo", notNullValue()).match(this.request);
|
||||
MockRestRequestMatchers.queryParamList("foo", is(anything())).match(this.request);
|
||||
MockRestRequestMatchers.queryParamList("foo", allOf(notNullValue(), notNullValue())).match(this.request);
|
||||
// These can be a bit ambiguous when reading the test (the compiler selects the list matcher):
|
||||
MockRestRequestMatchers.queryParam("foo", notNullValue()).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", is(anything())).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", allOf(notNullValue(), notNullValue())).match(this.request);
|
||||
|
||||
MockRestRequestMatchers.queryParamList("foo", allOf(notNullValue(), hasSize(2))).match(this.request);
|
||||
// These are not as ambiguous thanks to an inner matcher that is either obviously list-oriented,
|
||||
// string-oriented, or obviously a vararg of matchers
|
||||
|
||||
// list matcher version
|
||||
MockRestRequestMatchers.queryParam("foo", allOf(notNullValue(), hasSize(2))).match(this.request);
|
||||
|
||||
// vararg version
|
||||
MockRestRequestMatchers.queryParam("foo", allOf(notNullValue(), endsWith("ar"))).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", is((any(String.class)))).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", either(is("bar")).or(is(nullValue()))).match(this.request);
|
||||
MockRestRequestMatchers.queryParam("foo", is(notNullValue()), is(notNullValue())).match(this.request);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -318,41 +328,27 @@ class MockRestRequestMatchersTests {
|
||||
this.request.setURI(URI.create("http://www.foo.example/a?foo=bar&foo=baz"));
|
||||
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParamList("foo", contains(containsString("ba"))).match(this.request))
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParam("foo", contains(containsString("ba"))).match(this.request))
|
||||
.withMessageContainingAll(
|
||||
"Query param [foo] values",
|
||||
"Expected: iterable containing [a string containing \"ba\"]",
|
||||
"but: not matched: \"baz\"");
|
||||
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParamList("foo", hasItem(endsWith("ba"))).match(this.request))
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParam("foo", hasItem(endsWith("ba"))).match(this.request))
|
||||
.withMessageContainingAll(
|
||||
"Query param [foo] values",
|
||||
"Expected: a collection containing a string ending with \"ba\"",
|
||||
"but: mismatches were: [was \"bar\", was \"baz\"]");
|
||||
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParamList("foo", everyItem(endsWith("ar"))).match(this.request))
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParam("foo", everyItem(endsWith("ar"))).match(this.request))
|
||||
.withMessageContainingAll(
|
||||
"Query param [foo] values",
|
||||
"Expected: every item is a string ending with \"ar\"",
|
||||
"but: an item was \"baz\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
void queryParamListDoesntHideQueryParamWithSingleMatcher() throws IOException {
|
||||
this.request.setURI(URI.create("http://www.foo.example/a?foo=bar&foo=baz"));
|
||||
|
||||
MockRestRequestMatchers.queryParam("foo", equalTo("bar")).match(this.request);
|
||||
|
||||
assertThatAssertionError()
|
||||
.isThrownBy(() -> MockRestRequestMatchers.queryParamList("foo", equalTo("bar")).match(this.request))
|
||||
.withMessageContainingAll(
|
||||
"Query param [foo] values",
|
||||
"Expected: \"bar\"",
|
||||
"but: was <[bar, baz]>");
|
||||
}
|
||||
|
||||
private static ThrowableTypeAssert<AssertionError> assertThatAssertionError() {
|
||||
return assertThatExceptionOfType(AssertionError.class);
|
||||
}
|
||||
|
||||
+15
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -106,12 +106,14 @@ public class HttpComponentsClientHttpConnector implements ClientHttpConnector, C
|
||||
Function<? super ClientHttpRequest, Mono<Void>> requestCallback) {
|
||||
|
||||
HttpClientContext context = this.contextProvider.apply(method, uri);
|
||||
|
||||
if (context.getCookieStore() == null) {
|
||||
context.setCookieStore(new BasicCookieStore());
|
||||
}
|
||||
|
||||
HttpComponentsClientHttpRequest request =
|
||||
new HttpComponentsClientHttpRequest(method, uri, context, this.dataBufferFactory);
|
||||
HttpComponentsClientHttpRequest request = new HttpComponentsClientHttpRequest(method, uri,
|
||||
context, this.dataBufferFactory);
|
||||
|
||||
return requestCallback.apply(request).then(Mono.defer(() -> execute(request, context)));
|
||||
}
|
||||
|
||||
@@ -121,6 +123,7 @@ public class HttpComponentsClientHttpConnector implements ClientHttpConnector, C
|
||||
return Mono.create(sink -> {
|
||||
ReactiveResponseConsumer reactiveResponseConsumer =
|
||||
new ReactiveResponseConsumer(new MonoFutureCallbackAdapter(sink, this.dataBufferFactory, context));
|
||||
|
||||
this.client.execute(requestProducer, reactiveResponseConsumer, context, null);
|
||||
});
|
||||
}
|
||||
@@ -130,7 +133,6 @@ public class HttpComponentsClientHttpConnector implements ClientHttpConnector, C
|
||||
this.client.close();
|
||||
}
|
||||
|
||||
|
||||
private static class MonoFutureCallbackAdapter
|
||||
implements FutureCallback<Message<HttpResponse, Publisher<ByteBuffer>>> {
|
||||
|
||||
@@ -142,7 +144,6 @@ public class HttpComponentsClientHttpConnector implements ClientHttpConnector, C
|
||||
|
||||
public MonoFutureCallbackAdapter(MonoSink<ClientHttpResponse> sink,
|
||||
DataBufferFactory dataBufferFactory, HttpClientContext context) {
|
||||
|
||||
this.sink = sink;
|
||||
this.dataBufferFactory = dataBufferFactory;
|
||||
this.context = context;
|
||||
@@ -150,12 +151,19 @@ public class HttpComponentsClientHttpConnector implements ClientHttpConnector, C
|
||||
|
||||
@Override
|
||||
public void completed(Message<HttpResponse, Publisher<ByteBuffer>> result) {
|
||||
this.sink.success(new HttpComponentsClientHttpResponse(this.dataBufferFactory, result, this.context));
|
||||
HttpComponentsClientHttpResponse response =
|
||||
new HttpComponentsClientHttpResponse(this.dataBufferFactory, result, this.context);
|
||||
this.sink.success(response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void failed(Exception ex) {
|
||||
this.sink.error(ex instanceof HttpStreamResetException && ex.getCause() != null ? ex.getCause() : ex);
|
||||
Throwable t = ex;
|
||||
if (t instanceof HttpStreamResetException) {
|
||||
HttpStreamResetException httpStreamResetException = (HttpStreamResetException) ex;
|
||||
t = httpStreamResetException.getCause();
|
||||
}
|
||||
this.sink.error(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -53,7 +53,6 @@ class HttpComponentsHeadersAdapter implements MultiValueMap<String, String> {
|
||||
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getFirst(String key) {
|
||||
Header header = this.message.getFirstHeader(key);
|
||||
return (header != null ? header.getValue() : null);
|
||||
|
||||
+2
-4
@@ -79,12 +79,10 @@ class ReactorServerHttpRequest extends AbstractServerHttpRequest {
|
||||
return new URI(resolveBaseUrl(request) + resolveRequestUri(request));
|
||||
}
|
||||
|
||||
private static URI resolveBaseUrl(HttpServerRequest request) throws URISyntaxException {
|
||||
private static String resolveBaseUrl(HttpServerRequest request) {
|
||||
String scheme = request.scheme();
|
||||
int port = request.hostPort();
|
||||
return (usePort(scheme, port) ?
|
||||
new URI(scheme, null, request.hostName(), port, null, null, null) :
|
||||
new URI(scheme, request.hostName(), null, null));
|
||||
return scheme + "://" + request.hostName() + (usePort(scheme, port) ? ":" + port : "");
|
||||
}
|
||||
|
||||
private static boolean usePort(String scheme, int port) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
description = "Spring WebFlux"
|
||||
|
||||
apply plugin: "kotlin"
|
||||
apply plugin: "kotlinx-serialization"
|
||||
|
||||
dependencies {
|
||||
api(project(":spring-beans"))
|
||||
@@ -46,7 +45,6 @@ dependencies {
|
||||
testImplementation('org.apache.httpcomponents.core5:httpcore5-reactive')
|
||||
testImplementation("com.squareup.okhttp3:mockwebserver")
|
||||
testImplementation("org.jetbrains.kotlin:kotlin-script-runtime")
|
||||
testImplementation("org.jetbrains.kotlinx:kotlinx-serialization-json")
|
||||
testRuntimeOnly("org.jetbrains.kotlin:kotlin-scripting-jsr223")
|
||||
testRuntimeOnly("org.jruby:jruby")
|
||||
testRuntimeOnly("org.python:jython-standalone")
|
||||
|
||||
+3
-10
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
@@ -139,15 +139,8 @@ public abstract class AbstractMessageWriterResultHandler extends HandlerResultHa
|
||||
}
|
||||
else {
|
||||
publisher = Mono.justOrEmpty(body);
|
||||
ResolvableType bodyInstanceType = ResolvableType.forInstance(body);
|
||||
if (bodyType.toClass() == Object.class && body != null) {
|
||||
actualElementType = bodyInstanceType;
|
||||
elementType = bodyInstanceType;
|
||||
}
|
||||
else {
|
||||
actualElementType = (body == null || bodyInstanceType.hasUnresolvableGenerics()) ? bodyType : bodyInstanceType;
|
||||
elementType = bodyType;
|
||||
}
|
||||
actualElementType = body != null ? ResolvableType.forInstance(body) : bodyType;
|
||||
elementType = (bodyType.toClass() == Object.class && body != null ? actualElementType : bodyType);
|
||||
}
|
||||
|
||||
if (elementType.resolve() == void.class || elementType.resolve() == Void.class) {
|
||||
|
||||
+1
-1
@@ -112,7 +112,7 @@ public class DelegatingWebFluxConfigurationTests {
|
||||
boolean condition = initializer.getValidator() instanceof LocalValidatorFactoryBean;
|
||||
assertThat(condition).isTrue();
|
||||
assertThat(initializer.getConversionService()).isSameAs(formatterRegistry.getValue());
|
||||
assertThat(codecsConfigurer.getValue().getReaders()).hasSize(15);
|
||||
assertThat(codecsConfigurer.getValue().getReaders().size()).isEqualTo(14);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-3
@@ -152,7 +152,7 @@ public class WebFluxConfigurationSupportTests {
|
||||
assertThat(adapter).isNotNull();
|
||||
|
||||
List<HttpMessageReader<?>> readers = adapter.getMessageReaders();
|
||||
assertThat(readers).hasSize(15);
|
||||
assertThat(readers.size()).isEqualTo(14);
|
||||
|
||||
ResolvableType multiValueMapType = forClassWithGenerics(MultiValueMap.class, String.class, String.class);
|
||||
|
||||
@@ -207,7 +207,7 @@ public class WebFluxConfigurationSupportTests {
|
||||
assertThat(handler.getOrder()).isEqualTo(0);
|
||||
|
||||
List<HttpMessageWriter<?>> writers = handler.getMessageWriters();
|
||||
assertThat(writers).hasSize(14);
|
||||
assertThat(writers.size()).isEqualTo(13);
|
||||
|
||||
assertHasMessageWriter(writers, forClass(byte[].class), APPLICATION_OCTET_STREAM);
|
||||
assertHasMessageWriter(writers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM);
|
||||
@@ -235,7 +235,7 @@ public class WebFluxConfigurationSupportTests {
|
||||
assertThat(handler.getOrder()).isEqualTo(100);
|
||||
|
||||
List<HttpMessageWriter<?>> writers = handler.getMessageWriters();
|
||||
assertThat(writers).hasSize(14);
|
||||
assertThat(writers.size()).isEqualTo(13);
|
||||
|
||||
assertHasMessageWriter(writers, forClass(byte[].class), APPLICATION_OCTET_STREAM);
|
||||
assertHasMessageWriter(writers, forClass(ByteBuffer.class), APPLICATION_OCTET_STREAM);
|
||||
|
||||
+1
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2020 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.
|
||||
@@ -62,7 +62,6 @@ import static org.springframework.web.testfixture.method.ResolvableMethod.on;
|
||||
* Unit tests for {@link AbstractMessageWriterResultHandler}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
public class MessageWriterResultHandlerTests {
|
||||
|
||||
@@ -181,17 +180,6 @@ public class MessageWriterResultHandlerTests {
|
||||
assertResponseBody("[{\"id\":123,\"name\":\"foo\"},{\"id\":456,\"name\":\"bar\"}]");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jacksonTypeWithSubTypeAndObjectReturnValue() {
|
||||
MethodParameter returnType = on(TestController.class).resolveReturnType(Object.class);
|
||||
|
||||
SimpleBean body = new SimpleBean(123L, "foo");
|
||||
this.resultHandler.writeBody(body, returnType, this.exchange).block(Duration.ofSeconds(5));
|
||||
|
||||
assertThat(this.exchange.getResponse().getHeaders().getContentType()).isEqualTo(APPLICATION_JSON);
|
||||
assertResponseBody("{\"id\":123,\"name\":\"foo\"}");
|
||||
}
|
||||
|
||||
|
||||
private void assertResponseBody(String responseBody) {
|
||||
StepVerifier.create(this.exchange.getResponse().getBody())
|
||||
@@ -299,8 +287,6 @@ public class MessageWriterResultHandlerTests {
|
||||
Identifiable identifiable() { return null; }
|
||||
|
||||
List<Identifiable> listIdentifiable() { return null; }
|
||||
|
||||
Object object() { return null; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-3
@@ -47,7 +47,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Stephane Maldini
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.0
|
||||
*/
|
||||
class RequestMappingIntegrationTests extends AbstractRequestMappingIntegrationTests {
|
||||
@@ -92,8 +91,8 @@ class RequestMappingIntegrationTests extends AbstractRequestMappingIntegrationTe
|
||||
void stream(HttpServer httpServer) throws Exception {
|
||||
startServer(httpServer);
|
||||
|
||||
Integer[] expected = {0, 1, 2, 3, 4};
|
||||
assertThat(performGet("/stream", new HttpHeaders(), Integer[].class).getBody()).isEqualTo(expected);
|
||||
String[] expected = {"0", "1", "2", "3", "4"};
|
||||
assertThat(performGet("/stream", new HttpHeaders(), String[].class).getBody()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
|
||||
|
||||
-161
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.web.reactive.result.method.annotation
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.assertj.core.api.Assertions
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.springframework.core.ResolvableType
|
||||
import org.springframework.core.io.buffer.DataBuffer
|
||||
import org.springframework.http.MediaType
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter
|
||||
import org.springframework.http.codec.HttpMessageWriter
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder
|
||||
import org.springframework.http.codec.json.KotlinSerializationJsonEncoder
|
||||
import org.springframework.util.ObjectUtils
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.web.reactive.accept.RequestedContentTypeResolverBuilder
|
||||
import org.springframework.web.testfixture.http.server.reactive.MockServerHttpRequest
|
||||
import org.springframework.web.testfixture.method.ResolvableMethod
|
||||
import org.springframework.web.testfixture.server.MockServerWebExchange
|
||||
import reactor.test.StepVerifier
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.time.Duration
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Kotlin unit tests for {@link AbstractMessageWriterResultHandler}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class KotlinMessageWriterResultHandlerTests {
|
||||
|
||||
private val resultHandler = initResultHandler()
|
||||
|
||||
private val exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/path"))
|
||||
|
||||
|
||||
private fun initResultHandler(vararg writers: HttpMessageWriter<*>): AbstractMessageWriterResultHandler {
|
||||
val writerList = if (ObjectUtils.isEmpty(writers)) {
|
||||
listOf(
|
||||
EncoderHttpMessageWriter(KotlinSerializationJsonEncoder()),
|
||||
EncoderHttpMessageWriter(Jackson2JsonEncoder())
|
||||
)
|
||||
} else {
|
||||
listOf(*writers)
|
||||
}
|
||||
val resolver = RequestedContentTypeResolverBuilder().build()
|
||||
return object : AbstractMessageWriterResultHandler(writerList, resolver) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nonSuspendWithoutResponseEntity() {
|
||||
val returnType = ResolvableMethod.on(SampleController::class.java)
|
||||
.resolveReturnType(List::class.java, Person::class.java)
|
||||
val body = listOf(Person(UserId(1), "John"))
|
||||
resultHandler.writeBody(body, returnType, exchange).block(Duration.ofSeconds(5))
|
||||
|
||||
Assertions.assertThat(exchange.response.headers.contentType).isEqualTo(MediaType.APPLICATION_JSON)
|
||||
assertResponseBody("[{\"userId\":1,\"name\":\"John\"}]")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nonSuspendWithResponseEntity() {
|
||||
val returnType = ResolvableMethod.on(SampleController::class.java)
|
||||
.returning(ResolvableType.forClassWithGenerics(ResponseEntity::class.java,
|
||||
ResolvableType.forClassWithGenerics(List::class.java, Person::class.java)))
|
||||
.build().returnType()
|
||||
val body = ResponseEntity.ok(listOf(Person(UserId(1), "John")))
|
||||
resultHandler.writeBody(body.body, returnType.nested(), returnType, exchange).block(Duration.ofSeconds(5))
|
||||
|
||||
Assertions.assertThat(exchange.response.headers.contentType).isEqualTo(MediaType.APPLICATION_JSON)
|
||||
assertResponseBody("[{\"userId\":1,\"name\":\"John\"}]")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendWithoutResponseEntity() {
|
||||
val returnType = ResolvableMethod.on(CoroutinesSampleController::class.java)
|
||||
.resolveReturnType(List::class.java, Person::class.java)
|
||||
val body = listOf(Person(UserId(1), "John"))
|
||||
resultHandler.writeBody(body, returnType, exchange).block(Duration.ofSeconds(5))
|
||||
|
||||
Assertions.assertThat(exchange.response.headers.contentType).isEqualTo(MediaType.APPLICATION_JSON)
|
||||
assertResponseBody("[{\"userId\":1,\"name\":\"John\"}]")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun suspendWithResponseEntity() {
|
||||
val returnType = ResolvableMethod.on(CoroutinesSampleController::class.java)
|
||||
.returning(ResolvableType.forClassWithGenerics(ResponseEntity::class.java,
|
||||
ResolvableType.forClassWithGenerics(List::class.java, Person::class.java)))
|
||||
.build().returnType()
|
||||
val body = ResponseEntity.ok(listOf(Person(UserId(1), "John")))
|
||||
resultHandler.writeBody(body.body, returnType.nested(), returnType, exchange).block(Duration.ofSeconds(5))
|
||||
|
||||
Assertions.assertThat(exchange.response.headers.contentType).isEqualTo(MediaType.APPLICATION_JSON)
|
||||
assertResponseBody("[{\"userId\":1,\"name\":\"John\"}]")
|
||||
}
|
||||
|
||||
private fun assertResponseBody(responseBody: String) {
|
||||
StepVerifier.create(exchange.response.body)
|
||||
.consumeNextWith { buf: DataBuffer ->
|
||||
Assertions.assertThat(
|
||||
buf.toString(StandardCharsets.UTF_8)
|
||||
).isEqualTo(responseBody)
|
||||
}
|
||||
.expectComplete()
|
||||
.verify()
|
||||
}
|
||||
|
||||
@RestController
|
||||
class SampleController {
|
||||
|
||||
@GetMapping("/non-suspend-with-response-entity")
|
||||
fun withResponseEntity(): ResponseEntity<List<Person>> =
|
||||
TODO()
|
||||
|
||||
@GetMapping("/non-suspend-without-response-entity")
|
||||
fun withoutResponseEntity(): List<Person> =
|
||||
TODO()
|
||||
}
|
||||
|
||||
@RestController
|
||||
class CoroutinesSampleController {
|
||||
|
||||
@GetMapping("/suspend-with-response-entity")
|
||||
suspend fun suspendAndResponseEntity(): ResponseEntity<List<Person>> =
|
||||
TODO()
|
||||
|
||||
@GetMapping("/suspend-without-response-entity")
|
||||
suspend fun suspendWithoutResponseEntity(): List<Person> =
|
||||
TODO()
|
||||
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Person(
|
||||
val userId: UserId,
|
||||
val name: String,
|
||||
)
|
||||
|
||||
@JvmInline
|
||||
@Serializable
|
||||
value class UserId(val id: Int)
|
||||
|
||||
}
|
||||
+4
-10
@@ -48,7 +48,6 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
* <a href="https://www.w3.org/TR/eventsource/">Server-Sent Events</a>.
|
||||
*
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
* @since 5.3.2
|
||||
*/
|
||||
final class SseServerResponse extends AbstractServerResponse {
|
||||
@@ -91,7 +90,7 @@ final class SseServerResponse extends AbstractServerResponse {
|
||||
}
|
||||
|
||||
DefaultAsyncServerResponse.writeAsync(request, response, result);
|
||||
this.sseConsumer.accept(new DefaultSseBuilder(response, context, result, this.headers()));
|
||||
this.sseConsumer.accept(new DefaultSseBuilder(response, context, result));
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -114,19 +113,15 @@ final class SseServerResponse extends AbstractServerResponse {
|
||||
|
||||
private final List<HttpMessageConverter<?>> messageConverters;
|
||||
|
||||
private final HttpHeaders httpHeaders;
|
||||
|
||||
private final StringBuilder builder = new StringBuilder();
|
||||
|
||||
private boolean sendFailed;
|
||||
|
||||
|
||||
public DefaultSseBuilder(HttpServletResponse response, Context context, DeferredResult<?> deferredResult,
|
||||
HttpHeaders httpHeaders) {
|
||||
public DefaultSseBuilder(HttpServletResponse response, Context context, DeferredResult<?> deferredResult) {
|
||||
this.outputMessage = new ServletServerHttpResponse(response);
|
||||
this.deferredResult = deferredResult;
|
||||
this.messageConverters = context.messageConverters();
|
||||
this.httpHeaders = httpHeaders;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -211,7 +206,7 @@ final class SseServerResponse extends AbstractServerResponse {
|
||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||
if (converter.canWrite(dataClass, MediaType.APPLICATION_JSON)) {
|
||||
HttpMessageConverter<Object> objectConverter = (HttpMessageConverter<Object>) converter;
|
||||
ServerHttpResponse response = new MutableHeadersServerHttpResponse(this.outputMessage, this.httpHeaders);
|
||||
ServerHttpResponse response = new MutableHeadersServerHttpResponse(this.outputMessage);
|
||||
objectConverter.write(data, MediaType.APPLICATION_JSON, response);
|
||||
this.outputMessage.getBody().write(NL_NL);
|
||||
this.outputMessage.flush();
|
||||
@@ -281,10 +276,9 @@ final class SseServerResponse extends AbstractServerResponse {
|
||||
|
||||
private final HttpHeaders mutableHeaders = new HttpHeaders();
|
||||
|
||||
public MutableHeadersServerHttpResponse(ServerHttpResponse delegate, HttpHeaders headers) {
|
||||
public MutableHeadersServerHttpResponse(ServerHttpResponse delegate) {
|
||||
super(delegate);
|
||||
this.mutableHeaders.putAll(delegate.getHeaders());
|
||||
this.mutableHeaders.putAll(headers);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-8
@@ -776,14 +776,9 @@ public class MvcUriComponentsBuilder {
|
||||
}
|
||||
|
||||
else if (controllerType.isInterface()) {
|
||||
ClassLoader classLoader = controllerType.getClassLoader();
|
||||
if (classLoader == null || classLoader.getParent() == null) {
|
||||
// JDK interface type from bootstrap loader or platform loader ->
|
||||
// use higher-level loader which can see Spring infrastructure classes
|
||||
classLoader = MethodInvocationInfo.class.getClassLoader();
|
||||
}
|
||||
Class<?>[] ifcs = new Class<?>[] {controllerType, MethodInvocationInfo.class};
|
||||
return (T) Proxy.newProxyInstance(classLoader, ifcs, interceptor);
|
||||
return (T) Proxy.newProxyInstance(controllerType.getClassLoader(),
|
||||
new Class<?>[] {controllerType, MethodInvocationInfo.class},
|
||||
interceptor);
|
||||
}
|
||||
|
||||
else {
|
||||
|
||||
-28
@@ -33,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Arjen Poutsma
|
||||
* @author Sebastien Deleuze
|
||||
*/
|
||||
class SseServerResponseTests {
|
||||
|
||||
@@ -90,33 +89,6 @@ class SseServerResponseTests {
|
||||
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendObjectWithPrettyPrint() throws Exception {
|
||||
Person person = new Person("John Doe", 42);
|
||||
ServerResponse response = ServerResponse.sse(sse -> {
|
||||
try {
|
||||
sse.send(person);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
});
|
||||
|
||||
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
|
||||
converter.setPrettyPrint(true);
|
||||
ServerResponse.Context context = () -> Collections.singletonList(converter);
|
||||
|
||||
ModelAndView mav = response.writeTo(this.mockRequest, this.mockResponse, context);
|
||||
assertThat(mav).isNull();
|
||||
|
||||
String expected = "data:{\n" +
|
||||
"data: \"name\" : \"John Doe\",\n" +
|
||||
"data: \"age\" : 42\n" +
|
||||
"data:}\n" +
|
||||
"\n";
|
||||
assertThat(this.mockResponse.getContentAsString()).isEqualTo(expected);
|
||||
}
|
||||
|
||||
@Test
|
||||
void builder() throws Exception {
|
||||
ServerResponse response = ServerResponse.sse(sse -> {
|
||||
|
||||
+9
-42
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012-2023 the original author or authors.
|
||||
* Copyright 2012-2020 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.
|
||||
@@ -21,7 +21,6 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.sql.Savepoint;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -379,7 +378,7 @@ public class MvcUriComponentsBuilderTests {
|
||||
assertThat(uriComponents.encode().toUri().toString()).isEqualTo("http://localhost/hotels/42/bookings/21");
|
||||
}
|
||||
|
||||
@Test // SPR-16710
|
||||
@Test // SPR-16710
|
||||
public void fromMethodCallWithStringReturnType() {
|
||||
assertThatIllegalStateException().isThrownBy(() -> {
|
||||
UriComponents uriComponents = fromMethodCall(
|
||||
@@ -396,24 +395,9 @@ public class MvcUriComponentsBuilderTests {
|
||||
assertThat(uriComponents.encode().toUri().toString()).isEqualTo("http://localhost/hotels/42/bookings/21");
|
||||
}
|
||||
|
||||
@Test // gh-30210
|
||||
public void fromMethodCallWithCharSequenceReturnType() {
|
||||
UriComponents uriComponents = fromMethodCall(
|
||||
on(BookingControllerWithCharSequence.class).getBooking(21L)).buildAndExpand(42);
|
||||
|
||||
assertThat(uriComponents.encode().toUri().toString()).isEqualTo("http://localhost/hotels/42/bookings/21");
|
||||
}
|
||||
|
||||
@Test // gh-30210
|
||||
public void fromMethodCallWithJdbc30115ReturnType() {
|
||||
UriComponents uriComponents = fromMethodCall(
|
||||
on(BookingControllerWithJdbcSavepoint.class).getBooking(21L)).buildAndExpand(42);
|
||||
|
||||
assertThat(uriComponents.encode().toUri().toString()).isEqualTo("http://localhost/hotels/42/bookings/21");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fromMappingNamePlain() {
|
||||
|
||||
initWebApplicationContext(WebConfig.class);
|
||||
|
||||
this.request.setServerName("example.org");
|
||||
@@ -427,6 +411,7 @@ public class MvcUriComponentsBuilderTests {
|
||||
|
||||
@Test
|
||||
public void fromMappingNameWithCustomBaseUrl() {
|
||||
|
||||
initWebApplicationContext(WebConfig.class);
|
||||
|
||||
UriComponentsBuilder baseUrl = UriComponentsBuilder.fromUriString("https://example.org:9999/base");
|
||||
@@ -435,8 +420,9 @@ public class MvcUriComponentsBuilderTests {
|
||||
assertThat(url).isEqualTo("https://example.org:9999/base/people/123/addresses/DE");
|
||||
}
|
||||
|
||||
@Test // SPR-17027
|
||||
@Test // SPR-17027
|
||||
public void fromMappingNameWithEncoding() {
|
||||
|
||||
initWebApplicationContext(WebConfig.class);
|
||||
|
||||
this.request.setServerName("example.org");
|
||||
@@ -450,6 +436,7 @@ public class MvcUriComponentsBuilderTests {
|
||||
|
||||
@Test
|
||||
public void fromMappingNameWithPathWithoutLeadingSlash() {
|
||||
|
||||
initWebApplicationContext(PathWithoutLeadingSlashConfig.class);
|
||||
|
||||
this.request.setServerName("example.org");
|
||||
@@ -463,6 +450,7 @@ public class MvcUriComponentsBuilderTests {
|
||||
|
||||
@Test
|
||||
public void fromControllerWithPrefix() {
|
||||
|
||||
initWebApplicationContext(PathPrefixWebConfig.class);
|
||||
|
||||
this.request.setScheme("https");
|
||||
@@ -475,6 +463,7 @@ public class MvcUriComponentsBuilderTests {
|
||||
|
||||
@Test
|
||||
public void fromMethodWithPrefix() {
|
||||
|
||||
initWebApplicationContext(PathPrefixWebConfig.class);
|
||||
|
||||
this.request.setScheme("https");
|
||||
@@ -712,26 +701,4 @@ public class MvcUriComponentsBuilderTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/hotels/{hotel}")
|
||||
static class BookingControllerWithCharSequence {
|
||||
|
||||
@GetMapping("/bookings/{booking}")
|
||||
public CharSequence getBooking(@PathVariable Long booking) {
|
||||
return "url";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Controller
|
||||
@RequestMapping("/hotels/{hotel}")
|
||||
static class BookingControllerWithJdbcSavepoint {
|
||||
|
||||
@GetMapping("/bookings/{booking}")
|
||||
public Savepoint getBooking(@PathVariable Long booking) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2019 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.
|
||||
@@ -49,7 +49,7 @@ import org.springframework.context.annotation.Import;
|
||||
* registry.addHandler(echoWebSocketHandler(), "/echo").withSockJS();
|
||||
* }
|
||||
*
|
||||
* @Bean
|
||||
* @Override
|
||||
* public WebSocketHandler echoWebSocketHandler() {
|
||||
* return new EchoWebSocketHandler();
|
||||
* }
|
||||
|
||||
@@ -12,7 +12,7 @@ Spring MVC Test, WebTestClient.
|
||||
JDBC, R2DBC, O/R Mapping, XML Marshalling.
|
||||
<<web.adoc#spring-web, Web Servlet>> :: Spring MVC, WebSocket, SockJS,
|
||||
STOMP Messaging.
|
||||
<<web-reactive.adoc#spring-web-reactive, Web Reactive>> :: Spring WebFlux, WebClient,
|
||||
<<web-reactive.adoc#spring-webflux, Web Reactive>> :: Spring WebFlux, WebClient,
|
||||
WebSocket, RSocket.
|
||||
<<integration.adoc#spring-integration, Integration>> :: Remoting, JMS, JCA, JMX,
|
||||
Email, Tasks, Scheduling, Caching.
|
||||
|
||||
@@ -1625,7 +1625,7 @@ content types that a controller method produces, as the following example shows:
|
||||
----
|
||||
@GetMapping("/pets/{petId}", produces = ["application/json"])
|
||||
@ResponseBody
|
||||
fun getPet(@PathVariable petId: String): Pet {
|
||||
fun getPet(@PathVariable String petId): Pet {
|
||||
// ...
|
||||
}
|
||||
----
|
||||
@@ -1674,7 +1674,7 @@ You can also use the same with request header conditions, as the following examp
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") // <1>
|
||||
@GetMapping(path = "/pets", headers = "myHeader=myValue") // <1>
|
||||
public void findPet(@PathVariable String petId) {
|
||||
// ...
|
||||
}
|
||||
@@ -1684,7 +1684,7 @@ You can also use the same with request header conditions, as the following examp
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) // <1>
|
||||
@GetMapping("/pets", headers = ["myHeader=myValue"]) // <1>
|
||||
fun findPet(@PathVariable petId: String) {
|
||||
// ...
|
||||
}
|
||||
|
||||
@@ -1911,7 +1911,7 @@ You can also use the same with request header conditions, as the following examp
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
.Java
|
||||
----
|
||||
@GetMapping(path = "/pets/{petId}", headers = "myHeader=myValue") // <1>
|
||||
@GetMapping(path = "/pets", headers = "myHeader=myValue") // <1>
|
||||
public void findPet(@PathVariable String petId) {
|
||||
// ...
|
||||
}
|
||||
@@ -1921,7 +1921,7 @@ You can also use the same with request header conditions, as the following examp
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
.Kotlin
|
||||
----
|
||||
@GetMapping("/pets/{petId}", headers = ["myHeader=myValue"]) // <1>
|
||||
@GetMapping("/pets", headers = ["myHeader=myValue"]) // <1>
|
||||
fun findPet(@PathVariable petId: String) {
|
||||
// ...
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user