mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6badc81d91 | |||
| 0976d1a252 | |||
| 2c0ce790d8 | |||
| d512aafc36 | |||
| faa000f330 | |||
| daf1b3d7a7 | |||
| a48897a241 | |||
| 7ffa12f90f | |||
| 0e74ffa144 | |||
| 357195289d | |||
| 4fbca99f20 | |||
| c48ca5151f | |||
| aaf2e8fbe6 | |||
| 3635ff0c17 | |||
| f20e76e227 | |||
| dd32f95192 | |||
| 75a920bc9f | |||
| 92874adae9 | |||
| 55167b7f50 | |||
| 3129afba19 | |||
| 1366d2926b |
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("10.20.1");
|
||||
checkstyle.setToolVersion("10.20.2");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
|
||||
@@ -36,4 +36,4 @@ runtime:
|
||||
failure_level: warn
|
||||
ui:
|
||||
bundle:
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.17/ui-bundle.zip
|
||||
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip
|
||||
|
||||
@@ -12,27 +12,27 @@ Java::
|
||||
+
|
||||
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
|
||||
----
|
||||
Inventor einstein = p.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
Inventor einstein = parser.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
.getValue(Inventor.class);
|
||||
|
||||
// create new Inventor instance within the add() method of List
|
||||
p.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor(
|
||||
'Albert Einstein', 'German'))").getValue(societyContext);
|
||||
parser.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
|
||||
.getValue(societyContext);
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,indent=0,subs="verbatim,quotes",role="secondary"]
|
||||
----
|
||||
val einstein = p.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
val einstein = parser.parseExpression(
|
||||
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
|
||||
.getValue(Inventor::class.java)
|
||||
|
||||
// create new Inventor instance within the add() method of List
|
||||
p.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
|
||||
parser.parseExpression(
|
||||
"Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))")
|
||||
.getValue(societyContext)
|
||||
----
|
||||
======
|
||||
|
||||
@@ -110,8 +110,8 @@ potentially more efficient use cases if the `MethodHandle` target and parameters
|
||||
been fully bound prior to registration; however, partially bound handles are also
|
||||
supported.
|
||||
|
||||
Consider the `String#formatted(String, Object...)` instance method, which produces a
|
||||
message according to a template and a variable number of arguments.
|
||||
Consider the `String#formatted(Object...)` instance method, which produces a message
|
||||
according to a template and a variable number of arguments.
|
||||
|
||||
You can register and use the `formatted` method as a `MethodHandle`, as the following
|
||||
example shows:
|
||||
@@ -151,10 +151,10 @@ Kotlin::
|
||||
----
|
||||
======
|
||||
|
||||
As hinted above, binding a `MethodHandle` and registering the bound `MethodHandle` is also
|
||||
supported. This is likely to be more performant if both the target and all the arguments
|
||||
are bound. In that case no arguments are necessary in the SpEL expression, as the
|
||||
following example shows:
|
||||
As mentioned above, binding a `MethodHandle` and registering the bound `MethodHandle` is
|
||||
also supported. This is likely to be more performant if both the target and all the
|
||||
arguments are bound. In that case no arguments are necessary in the SpEL expression, as
|
||||
the following example shows:
|
||||
|
||||
[tabs]
|
||||
======
|
||||
@@ -168,9 +168,10 @@ Java::
|
||||
String template = "This is a %s message with %s words: <%s>";
|
||||
Object varargs = new Object[] { "prerecorded", 3, "Oh Hello World!", "ignored" };
|
||||
MethodHandle mh = MethodHandles.lookup().findVirtual(String.class, "formatted",
|
||||
MethodType.methodType(String.class, Object[].class))
|
||||
MethodType.methodType(String.class, Object[].class))
|
||||
.bindTo(template)
|
||||
.bindTo(varargs); //here we have to provide arguments in a single array binding
|
||||
// Here we have to provide the arguments in a single array binding:
|
||||
.bindTo(varargs);
|
||||
context.setVariable("message", mh);
|
||||
|
||||
// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
|
||||
@@ -189,9 +190,10 @@ Kotlin::
|
||||
val varargs = arrayOf("prerecorded", 3, "Oh Hello World!", "ignored")
|
||||
|
||||
val mh = MethodHandles.lookup().findVirtual(String::class.java, "formatted",
|
||||
MethodType.methodType(String::class.java, Array<Any>::class.java))
|
||||
MethodType.methodType(String::class.java, Array<Any>::class.java))
|
||||
.bindTo(template)
|
||||
.bindTo(varargs) //here we have to provide arguments in a single array binding
|
||||
// Here we have to provide the arguments in a single array binding:
|
||||
.bindTo(varargs)
|
||||
context.setVariable("message", mh)
|
||||
|
||||
// evaluates to "This is a prerecorded message with 3 words: <Oh Hello World!>"
|
||||
|
||||
@@ -26,7 +26,7 @@ available through the `ServletRequest.getParameter{asterisk}()` family of method
|
||||
|
||||
|
||||
|
||||
[[forwarded-headers]]
|
||||
[[filters-forwarded-headers]]
|
||||
== Forwarded Headers
|
||||
[.small]#xref:web/webflux/reactive-spring.adoc#webflux-forwarded-headers[See equivalent in the Reactive stack]#
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ dependencies {
|
||||
api(platform("io.micrometer:micrometer-bom:1.12.12"))
|
||||
api(platform("io.netty:netty-bom:4.1.115.Final"))
|
||||
api(platform("io.netty:netty5-bom:5.0.0.Alpha5"))
|
||||
api(platform("io.projectreactor:reactor-bom:2023.0.12"))
|
||||
api(platform("io.projectreactor:reactor-bom:2023.0.13"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.4"))
|
||||
api(platform("org.apache.groovy:groovy-bom:4.0.24"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.21.1"))
|
||||
@@ -54,11 +54,11 @@ dependencies {
|
||||
api("io.r2dbc:r2dbc-h2:1.0.0.RELEASE")
|
||||
api("io.r2dbc:r2dbc-spi-test:1.0.0.RELEASE")
|
||||
api("io.r2dbc:r2dbc-spi:1.0.0.RELEASE")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.9")
|
||||
api("io.reactivex.rxjava3:rxjava:3.1.10")
|
||||
api("io.smallrye.reactive:mutiny:1.10.0")
|
||||
api("io.undertow:undertow-core:2.3.17.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.17.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.17.Final")
|
||||
api("io.undertow:undertow-core:2.3.18.Final")
|
||||
api("io.undertow:undertow-servlet:2.3.18.Final")
|
||||
api("io.undertow:undertow-websockets-jsr:2.3.18.Final")
|
||||
api("io.vavr:vavr:0.10.4")
|
||||
api("jakarta.activation:jakarta.activation-api:2.0.1")
|
||||
api("jakarta.annotation:jakarta.annotation-api:2.0.0")
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
version=6.1.16-SNAPSHOT
|
||||
version=6.1.16
|
||||
|
||||
org.gradle.caching=true
|
||||
org.gradle.jvmargs=-Xmx2048m
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
+5
@@ -1055,6 +1055,11 @@ public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFacto
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Removing alias '" + beanName + "' for bean '" + aliasedName +
|
||||
"' due to registration of bean definition for bean '" + beanName + "': [" +
|
||||
beanDefinition + "]");
|
||||
}
|
||||
removeAlias(beanName);
|
||||
}
|
||||
}
|
||||
|
||||
+5
@@ -885,10 +885,15 @@ class DefaultListableBeanFactoryTests {
|
||||
@Test
|
||||
void beanDefinitionOverriding() {
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(TestBean.class));
|
||||
// Override "test" bean definition.
|
||||
lbf.registerBeanDefinition("test", new RootBeanDefinition(NestedTestBean.class));
|
||||
// Temporary "test2" alias for nonexistent bean.
|
||||
lbf.registerAlias("otherTest", "test2");
|
||||
// Reassign "test2" alias to "test".
|
||||
lbf.registerAlias("test", "test2");
|
||||
// Assign "testX" alias to "test" as well.
|
||||
lbf.registerAlias("test", "testX");
|
||||
// Register new "testX" bean definition which also removes the "testX" alias for "test".
|
||||
lbf.registerBeanDefinition("testX", new RootBeanDefinition(TestBean.class));
|
||||
|
||||
assertThat(lbf.getBean("test")).isInstanceOf(NestedTestBean.class);
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -39,7 +39,6 @@ import org.springframework.beans.factory.support.AbstractBeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReader;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;
|
||||
@@ -318,7 +317,7 @@ class ConfigurationClassBeanDefinitionReader {
|
||||
|
||||
// At this point, it's a top-level override (probably XML), just having been parsed
|
||||
// before configuration class processing kicks in...
|
||||
if (this.registry instanceof DefaultListableBeanFactory dlbf && !dlbf.isBeanDefinitionOverridable(beanName)) {
|
||||
if (!this.registry.isBeanDefinitionOverridable(beanName)) {
|
||||
throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
|
||||
beanName, "@Bean definition illegally overridden by existing bean definition: " + existingBeanDef);
|
||||
}
|
||||
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2024 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,6 +17,7 @@
|
||||
package org.springframework.validation.beanvalidation;
|
||||
|
||||
import jakarta.validation.ValidationException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
@@ -39,7 +40,13 @@ public class OptionalValidatorFactoryBean extends LocalValidatorFactoryBean {
|
||||
super.afterPropertiesSet();
|
||||
}
|
||||
catch (ValidationException ex) {
|
||||
LogFactory.getLog(getClass()).debug("Failed to set up a Bean Validation provider", ex);
|
||||
Log logger = LogFactory.getLog(getClass());
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Failed to set up a Bean Validation provider", ex);
|
||||
}
|
||||
else if (logger.isInfoEnabled()) {
|
||||
logger.info("Failed to set up a Bean Validation provider: " + ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-11
@@ -209,18 +209,10 @@ abstract class AbstractSchedulingTaskExecutorTests {
|
||||
CompletableFuture<?> future1 = executor.submitCompletable(new TestTask(this.testName, -1));
|
||||
CompletableFuture<?> future2 = executor.submitCompletable(new TestTask(this.testName, -1));
|
||||
shutdownExecutor();
|
||||
|
||||
try {
|
||||
assertThatExceptionOfType(TimeoutException.class).isThrownBy(() -> {
|
||||
future1.get(1000, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// ignore
|
||||
}
|
||||
Awaitility.await()
|
||||
.atMost(5, TimeUnit.SECONDS)
|
||||
.pollInterval(10, TimeUnit.MILLISECONDS)
|
||||
.untilAsserted(() -> assertThatExceptionOfType(TimeoutException.class)
|
||||
.isThrownBy(() -> future2.get(1000, TimeUnit.MILLISECONDS)));
|
||||
future2.get(1000, TimeUnit.MILLISECONDS);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -309,12 +309,12 @@ public class ResolvableType implements Serializable {
|
||||
|
||||
// Deal with wildcard bounds
|
||||
WildcardBounds ourBounds = WildcardBounds.get(this);
|
||||
WildcardBounds typeBounds = WildcardBounds.get(other);
|
||||
WildcardBounds otherBounds = WildcardBounds.get(other);
|
||||
|
||||
// In the form X is assignable to <? extends Number>
|
||||
if (typeBounds != null) {
|
||||
return (ourBounds != null && ourBounds.isSameKind(typeBounds) &&
|
||||
ourBounds.isAssignableFrom(typeBounds.getBounds()));
|
||||
if (otherBounds != null) {
|
||||
return (ourBounds != null && ourBounds.isSameKind(otherBounds) &&
|
||||
ourBounds.isAssignableFrom(otherBounds.getBounds()));
|
||||
}
|
||||
|
||||
// In the form <? extends Number> is assignable to X...
|
||||
@@ -365,8 +365,8 @@ public class ResolvableType implements Serializable {
|
||||
if (checkGenerics) {
|
||||
// Recursively check each generic
|
||||
ResolvableType[] ourGenerics = getGenerics();
|
||||
ResolvableType[] typeGenerics = other.as(ourResolved).getGenerics();
|
||||
if (ourGenerics.length != typeGenerics.length) {
|
||||
ResolvableType[] otherGenerics = other.as(ourResolved).getGenerics();
|
||||
if (ourGenerics.length != otherGenerics.length) {
|
||||
return false;
|
||||
}
|
||||
if (ourGenerics.length > 0) {
|
||||
@@ -375,7 +375,7 @@ public class ResolvableType implements Serializable {
|
||||
}
|
||||
matchedBefore.put(this.type, other.type);
|
||||
for (int i = 0; i < ourGenerics.length; i++) {
|
||||
if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], true, matchedBefore)) {
|
||||
if (!ourGenerics[i].isAssignableFrom(otherGenerics[i], true, matchedBefore)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -169,7 +169,7 @@ class ResolvableTypeTests {
|
||||
|
||||
@Test
|
||||
void forInstanceProviderNull() {
|
||||
ResolvableType type = ResolvableType.forInstance(new MyGenericInterfaceType<String>(null));
|
||||
ResolvableType type = ResolvableType.forInstance(new MyGenericInterfaceType<>(null));
|
||||
assertThat(type.getType()).isEqualTo(MyGenericInterfaceType.class);
|
||||
assertThat(type.resolve()).isEqualTo(MyGenericInterfaceType.class);
|
||||
}
|
||||
@@ -1177,6 +1177,20 @@ class ResolvableTypeTests {
|
||||
assertThatResolvableType(complex4).isNotAssignableFrom(complex3);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAssignableFromForUnresolvedWildcards() {
|
||||
ResolvableType wildcard = ResolvableType.forInstance(new Wildcard<>());
|
||||
ResolvableType wildcardFixed = ResolvableType.forInstance(new WildcardFixed());
|
||||
ResolvableType wildcardConcrete = ResolvableType.forClassWithGenerics(Wildcard.class, Number.class);
|
||||
|
||||
assertThat(wildcard.isAssignableFrom(wildcardFixed)).isTrue();
|
||||
assertThat(wildcard.isAssignableFrom(wildcardConcrete)).isTrue();
|
||||
assertThat(wildcardFixed.isAssignableFrom(wildcard)).isFalse();
|
||||
assertThat(wildcardFixed.isAssignableFrom(wildcardConcrete)).isFalse();
|
||||
assertThat(wildcardConcrete.isAssignableFrom(wildcard)).isTrue();
|
||||
assertThat(wildcardConcrete.isAssignableFrom(wildcardFixed)).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void identifyTypeVariable() throws Exception {
|
||||
Method method = ClassArguments.class.getMethod("typedArgumentFirst", Class.class, Class.class, Class.class);
|
||||
@@ -1574,7 +1588,6 @@ class ResolvableTypeTests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public class MySimpleInterfaceType implements MyInterfaceType<String> {
|
||||
}
|
||||
|
||||
@@ -1584,7 +1597,6 @@ class ResolvableTypeTests {
|
||||
public abstract class ExtendsMySimpleInterfaceTypeWithImplementsRaw extends MySimpleInterfaceTypeWithImplementsRaw {
|
||||
}
|
||||
|
||||
|
||||
public class MyCollectionInterfaceType implements MyInterfaceType<Collection<String>> {
|
||||
}
|
||||
|
||||
@@ -1592,20 +1604,17 @@ class ResolvableTypeTests {
|
||||
public abstract class MySuperclassType<T> {
|
||||
}
|
||||
|
||||
|
||||
public class MySimpleSuperclassType extends MySuperclassType<String> {
|
||||
}
|
||||
|
||||
|
||||
public class MyCollectionSuperclassType extends MySuperclassType<Collection<String>> {
|
||||
}
|
||||
|
||||
|
||||
interface Wildcard<T extends Number> extends List<T> {
|
||||
public class Wildcard<T extends Number> {
|
||||
}
|
||||
|
||||
|
||||
interface RawExtendsWildcard extends Wildcard {
|
||||
public class WildcardFixed extends Wildcard<Integer> {
|
||||
}
|
||||
|
||||
|
||||
|
||||
+2
@@ -36,6 +36,8 @@ import static org.assertj.core.api.Assertions.assertThatException;
|
||||
* Tests invocation of constructors.
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @see MethodInvocationTests
|
||||
* @see VariableAndFunctionTests
|
||||
*/
|
||||
class ConstructorInvocationTests extends AbstractExpressionTests {
|
||||
|
||||
|
||||
+2
@@ -46,6 +46,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
* @author Andy Clement
|
||||
* @author Phillip Webb
|
||||
* @author Sam Brannen
|
||||
* @see ConstructorInvocationTests
|
||||
* @see VariableAndFunctionTests
|
||||
*/
|
||||
class MethodInvocationTests extends AbstractExpressionTests {
|
||||
|
||||
|
||||
+2
-1
@@ -597,7 +597,8 @@ class SpelDocumentationTests extends AbstractExpressionTests {
|
||||
MethodHandle methodHandle = MethodHandles.lookup().findVirtual(String.class, "formatted",
|
||||
MethodType.methodType(String.class, Object[].class))
|
||||
.bindTo(template)
|
||||
.bindTo(varargs); // here we have to provide arguments in a single array binding
|
||||
// Here we have to provide the arguments in a single array binding:
|
||||
.bindTo(varargs);
|
||||
context.registerFunction("message", methodHandle);
|
||||
|
||||
String message = parser.parseExpression("#message()").getValue(context, String.class);
|
||||
|
||||
+2
@@ -32,6 +32,8 @@ import static org.springframework.expression.spel.SpelMessage.INCORRECT_NUMBER_O
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Sam Brannen
|
||||
* @see ConstructorInvocationTests
|
||||
* @see MethodInvocationTests
|
||||
*/
|
||||
class VariableAndFunctionTests extends AbstractExpressionTests {
|
||||
|
||||
|
||||
+2
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2024 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,9 +22,7 @@ import org.springframework.messaging.Message;
|
||||
|
||||
/**
|
||||
* Convenient base class for {@link AsyncHandlerMethodReturnValueHandler}
|
||||
* implementations that support only asynchronous (Future-like) return values
|
||||
* and merely serve as adapters of such types to Spring's
|
||||
* {@link org.springframework.util.concurrent.ListenableFuture ListenableFuture}.
|
||||
* implementations that support only asynchronous (Future-like) return values.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 4.2
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -572,7 +572,7 @@ public abstract class AbstractMethodMessageHandler<T>
|
||||
if (returnValue != null && this.returnValueHandlers.isAsyncReturnValue(returnValue, returnType)) {
|
||||
CompletableFuture<?> future = this.returnValueHandlers.toCompletableFuture(returnValue, returnType);
|
||||
if (future != null) {
|
||||
future.whenComplete(new ReturnValueListenableFutureCallback(invocable, message));
|
||||
future.whenComplete(new ReturnValueCallback(invocable, message));
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -703,13 +703,13 @@ public abstract class AbstractMethodMessageHandler<T>
|
||||
}
|
||||
|
||||
|
||||
private class ReturnValueListenableFutureCallback implements BiConsumer<Object, Throwable> {
|
||||
private class ReturnValueCallback implements BiConsumer<Object, Throwable> {
|
||||
|
||||
private final InvocableHandlerMethod handlerMethod;
|
||||
|
||||
private final Message<?> message;
|
||||
|
||||
public ReturnValueListenableFutureCallback(InvocableHandlerMethod handlerMethod, Message<?> message) {
|
||||
public ReturnValueCallback(InvocableHandlerMethod handlerMethod, Message<?> message) {
|
||||
this.handlerMethod = handlerMethod;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -24,8 +24,6 @@ import org.springframework.lang.Nullable;
|
||||
/**
|
||||
* An extension of {@link HandlerMethodReturnValueHandler} for handling async,
|
||||
* Future-like return value types that support success and error callbacks.
|
||||
* Essentially anything that can be adapted to a
|
||||
* {@link org.springframework.util.concurrent.ListenableFuture ListenableFuture}.
|
||||
*
|
||||
* <p>Implementations should consider extending the convenient base class
|
||||
* {@link AbstractAsyncReturnValueHandler}.
|
||||
@@ -71,6 +69,7 @@ public interface AsyncHandlerMethodReturnValueHandler extends HandlerMethodRetur
|
||||
@Nullable
|
||||
default org.springframework.util.concurrent.ListenableFuture<?> toListenableFuture(
|
||||
Object returnValue, MethodParameter returnType) {
|
||||
|
||||
CompletableFuture<?> result = toCompletableFuture(returnValue, returnType);
|
||||
return (result != null ?
|
||||
new org.springframework.util.concurrent.CompletableToListenableFutureAdapter<>(result) :
|
||||
|
||||
+3
-3
@@ -182,10 +182,10 @@ public class SqlScriptsTestExecutionListener extends AbstractTestExecutionListen
|
||||
@Override
|
||||
public void processAheadOfTime(RuntimeHints runtimeHints, Class<?> testClass, ClassLoader classLoader) {
|
||||
getSqlAnnotationsFor(testClass).forEach(sql ->
|
||||
registerClasspathResources(getScripts(sql, testClass, null, true), runtimeHints, classLoader));
|
||||
registerClasspathResources(getScripts(sql, testClass, null, true), runtimeHints, classLoader));
|
||||
getSqlMethods(testClass).forEach(testMethod ->
|
||||
getSqlAnnotationsFor(testMethod).forEach(sql ->
|
||||
registerClasspathResources(getScripts(sql, testClass, testMethod, false), runtimeHints, classLoader)));
|
||||
getSqlAnnotationsFor(testMethod).forEach(sql ->
|
||||
registerClasspathResources(getScripts(sql, testClass, testMethod, false), runtimeHints, classLoader)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -853,7 +853,7 @@ public class MediaType extends MimeType implements Serializable {
|
||||
* <blockquote>audio/basic == text/html</blockquote>
|
||||
* <blockquote>audio/basic == audio/wave</blockquote>
|
||||
* @param mediaTypes the list of media types to be sorted
|
||||
* @deprecated As of 6.0, in favor of {@link MimeTypeUtils#sortBySpecificity(List)}
|
||||
* @deprecated as of 6.0, in favor of {@link MimeTypeUtils#sortBySpecificity(List)}
|
||||
*/
|
||||
@Deprecated(since = "6.0", forRemoval = true)
|
||||
public static void sortBySpecificity(List<MediaType> mediaTypes) {
|
||||
@@ -882,7 +882,7 @@ public class MediaType extends MimeType implements Serializable {
|
||||
* </ol>
|
||||
* @param mediaTypes the list of media types to be sorted
|
||||
* @see #getQualityValue()
|
||||
* @deprecated As of 6.0, with no direct replacement
|
||||
* @deprecated as of 6.0, with no direct replacement
|
||||
*/
|
||||
@Deprecated(since = "6.0", forRemoval = true)
|
||||
public static void sortByQualityValue(List<MediaType> mediaTypes) {
|
||||
@@ -895,9 +895,9 @@ public class MediaType extends MimeType implements Serializable {
|
||||
/**
|
||||
* Sorts the given list of {@code MediaType} objects by specificity as the
|
||||
* primary criteria and quality value the secondary.
|
||||
* @deprecated As of 6.0, in favor of {@link MimeTypeUtils#sortBySpecificity(List)}
|
||||
* @deprecated as of 6.0, in favor of {@link MimeTypeUtils#sortBySpecificity(List)}
|
||||
*/
|
||||
@Deprecated(since = "6.0")
|
||||
@Deprecated(since = "6.0", forRemoval = true)
|
||||
public static void sortBySpecificityAndQuality(List<MediaType> mediaTypes) {
|
||||
Assert.notNull(mediaTypes, "'mediaTypes' must not be null");
|
||||
if (mediaTypes.size() > 1) {
|
||||
@@ -908,7 +908,7 @@ public class MediaType extends MimeType implements Serializable {
|
||||
|
||||
/**
|
||||
* Comparator used by {@link #sortByQualityValue(List)}.
|
||||
* @deprecated As of 6.0, with no direct replacement
|
||||
* @deprecated as of 6.0, with no direct replacement
|
||||
*/
|
||||
@Deprecated(since = "6.0", forRemoval = true)
|
||||
public static final Comparator<MediaType> QUALITY_VALUE_COMPARATOR = (mediaType1, mediaType2) -> {
|
||||
@@ -948,7 +948,7 @@ public class MediaType extends MimeType implements Serializable {
|
||||
|
||||
/**
|
||||
* Comparator used by {@link #sortBySpecificity(List)}.
|
||||
* @deprecated As of 6.0, with no direct replacement
|
||||
* @deprecated as of 6.0, with no direct replacement
|
||||
*/
|
||||
@Deprecated(since = "6.0", forRemoval = true)
|
||||
@SuppressWarnings("removal")
|
||||
|
||||
+3
-15
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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,16 +62,9 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
|
||||
/**
|
||||
* Indicate whether this request factory should buffer the
|
||||
* {@linkplain ClientHttpRequest#getBody() request body} internally.
|
||||
* <p>Default is {@code true}. When sending large amounts of data via POST or PUT,
|
||||
* it is recommended to change this property to {@code false}, so as not to run
|
||||
* out of memory. This will result in a {@link ClientHttpRequest} that either
|
||||
* streams directly to the underlying {@link HttpURLConnection} (if the
|
||||
* {@link org.springframework.http.HttpHeaders#getContentLength() Content-Length}
|
||||
* is known in advance), or that will use "Chunked transfer encoding"
|
||||
* (if the {@code Content-Length} is not known in advance).
|
||||
* @see #setChunkSize(int)
|
||||
* @see HttpURLConnection#setFixedLengthStreamingMode(int)
|
||||
* @deprecated since 6.1 requests are never buffered, as if this property is {@code false}
|
||||
* @deprecated since 6.1 requests are never buffered,
|
||||
* as if this property is {@code false}
|
||||
*/
|
||||
@Deprecated(since = "6.1", forRemoval = true)
|
||||
public void setBufferRequestBody(boolean bufferRequestBody) {
|
||||
@@ -80,11 +73,6 @@ public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory
|
||||
/**
|
||||
* Set the number of bytes to write in each chunk when not buffering request
|
||||
* bodies locally.
|
||||
* <p>Note that this parameter is only used when
|
||||
* {@link #setBufferRequestBody(boolean) bufferRequestBody} is set to {@code false},
|
||||
* and the {@link org.springframework.http.HttpHeaders#getContentLength() Content-Length}
|
||||
* is not known in advance.
|
||||
* @see #setBufferRequestBody(boolean)
|
||||
*/
|
||||
public void setChunkSize(int chunkSize) {
|
||||
this.chunkSize = chunkSize;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -67,7 +67,7 @@ public abstract class HttpAccessor {
|
||||
* @see #createRequest(URI, HttpMethod)
|
||||
* @see SimpleClientHttpRequestFactory
|
||||
* @see org.springframework.http.client.HttpComponentsClientHttpRequestFactory
|
||||
* @see org.springframework.http.client.OkHttp3ClientHttpRequestFactory
|
||||
* @see org.springframework.http.client.JdkClientHttpRequestFactory
|
||||
*/
|
||||
public void setRequestFactory(ClientHttpRequestFactory requestFactory) {
|
||||
Assert.notNull(requestFactory, "ClientHttpRequestFactory must not be null");
|
||||
|
||||
+18
-16
@@ -66,25 +66,27 @@ public class UndertowHttpHandlerAdapter implements io.undertow.server.HttpHandle
|
||||
|
||||
@Override
|
||||
public void handleRequest(HttpServerExchange exchange) {
|
||||
UndertowServerHttpRequest request = null;
|
||||
try {
|
||||
request = new UndertowServerHttpRequest(exchange, getDataBufferFactory());
|
||||
}
|
||||
catch (URISyntaxException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.debug("Failed to get request URI: " + ex.getMessage());
|
||||
exchange.dispatch(() -> {
|
||||
UndertowServerHttpRequest request = null;
|
||||
try {
|
||||
request = new UndertowServerHttpRequest(exchange, getDataBufferFactory());
|
||||
}
|
||||
exchange.setStatusCode(400);
|
||||
return;
|
||||
}
|
||||
ServerHttpResponse response = new UndertowServerHttpResponse(exchange, getDataBufferFactory(), request);
|
||||
catch (URISyntaxException ex) {
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.debug("Failed to get request URI: " + ex.getMessage());
|
||||
}
|
||||
exchange.setStatusCode(400);
|
||||
return;
|
||||
}
|
||||
ServerHttpResponse response = new UndertowServerHttpResponse(exchange, getDataBufferFactory(), request);
|
||||
|
||||
if (request.getMethod() == HttpMethod.HEAD) {
|
||||
response = new HttpHeadResponseDecorator(response);
|
||||
}
|
||||
if (request.getMethod() == HttpMethod.HEAD) {
|
||||
response = new HttpHeadResponseDecorator(response);
|
||||
}
|
||||
|
||||
HandlerResultSubscriber resultSubscriber = new HandlerResultSubscriber(exchange, request);
|
||||
this.httpHandler.handle(request, response).subscribe(resultSubscriber);
|
||||
HandlerResultSubscriber resultSubscriber = new HandlerResultSubscriber(exchange, request);
|
||||
this.httpHandler.handle(request, response).subscribe(resultSubscriber);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
+9
-3
@@ -478,9 +478,9 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
try {
|
||||
service = new HandshakeWebSocketService();
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
catch (Throwable ex) {
|
||||
// Don't fail, test environment perhaps
|
||||
service = new NoUpgradeStrategyWebSocketService();
|
||||
service = new NoUpgradeStrategyWebSocketService(ex);
|
||||
}
|
||||
}
|
||||
return service;
|
||||
@@ -578,9 +578,15 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
|
||||
private static final class NoUpgradeStrategyWebSocketService implements WebSocketService {
|
||||
|
||||
private final Throwable ex;
|
||||
|
||||
public NoUpgradeStrategyWebSocketService(Throwable ex) {
|
||||
this.ex = ex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<Void> handleRequest(ServerWebExchange exchange, WebSocketHandler webSocketHandler) {
|
||||
return Mono.error(new IllegalStateException("No suitable RequestUpgradeStrategy"));
|
||||
return Mono.error(new IllegalStateException("No suitable RequestUpgradeStrategy", this.ex));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.reactive.result.method.annotation;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -331,6 +332,17 @@ class RequestMappingMessageConversionIntegrationTests extends AbstractRequestMap
|
||||
assertThat(performPost("/person-transform/flux", JSON, req, JSON, PERSON_LIST).getBody()).isEqualTo(res);
|
||||
}
|
||||
|
||||
@ParameterizedHttpServerTest // see gh-33885
|
||||
void personTransformWithFluxDelayed(HttpServer httpServer) throws Exception {
|
||||
startServer(httpServer);
|
||||
|
||||
List<?> req = asList(new Person("Robert"), new Person("Marie"));
|
||||
List<?> res = asList(new Person("ROBERT"), new Person("MARIE"));
|
||||
assertThat(performPost("/person-transform/flux-delayed", JSON, req, JSON, PERSON_LIST))
|
||||
.satisfies(r -> assertThat(r.getBody()).isEqualTo(res))
|
||||
.satisfies(r -> assertThat(r.getHeaders().getContentLength()).isNotZero());
|
||||
}
|
||||
|
||||
@ParameterizedHttpServerTest
|
||||
void personTransformWithObservable(HttpServer httpServer) throws Exception {
|
||||
startServer(httpServer);
|
||||
@@ -632,6 +644,11 @@ class RequestMappingMessageConversionIntegrationTests extends AbstractRequestMap
|
||||
return persons.map(person -> new Person(person.getName().toUpperCase()));
|
||||
}
|
||||
|
||||
@PostMapping("/flux-delayed")
|
||||
Flux<Person> transformDelayed(@RequestBody Flux<Person> persons) {
|
||||
return transformFlux(persons).delayElements(Duration.ofMillis(10));
|
||||
}
|
||||
|
||||
@PostMapping("/observable")
|
||||
Observable<Person> transformObservable(@RequestBody Observable<Person> persons) {
|
||||
return persons.map(person -> new Person(person.getName().toUpperCase()));
|
||||
|
||||
+3
-2
@@ -67,6 +67,7 @@ final class DefaultAsyncServerResponse extends ErrorHandlingServerResponse imple
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ServerResponse block() {
|
||||
try {
|
||||
@@ -114,8 +115,8 @@ final class DefaultAsyncServerResponse extends ErrorHandlingServerResponse imple
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@Nullable
|
||||
public ModelAndView writeTo(HttpServletRequest request, HttpServletResponse response, Context context)
|
||||
throws ServletException, IOException {
|
||||
|
||||
@@ -169,6 +170,7 @@ final class DefaultAsyncServerResponse extends ErrorHandlingServerResponse imple
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
public static AsyncServerResponse create(Object obj, @Nullable Duration timeout) {
|
||||
Assert.notNull(obj, "Argument to async must not be null");
|
||||
@@ -192,5 +194,4 @@ final class DefaultAsyncServerResponse extends ErrorHandlingServerResponse imple
|
||||
throw new IllegalArgumentException("Asynchronous type not supported: " + obj.getClass());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -54,6 +54,7 @@ class DefaultServerResponseBuilderTests {
|
||||
|
||||
static final ServerResponse.Context EMPTY_CONTEXT = Collections::emptyList;
|
||||
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("removal")
|
||||
void status() {
|
||||
@@ -75,7 +76,6 @@ class DefaultServerResponseBuilderTests {
|
||||
assertThat(result.cookies().getFirst("foo")).isEqualTo(cookie);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void ok() {
|
||||
ServerResponse response = ServerResponse.ok().build();
|
||||
|
||||
@@ -16,13 +16,13 @@ dependencies {
|
||||
exclude group: "org.apache.tomcat", module: "tomcat-servlet-api"
|
||||
exclude group: "org.apache.tomcat", module: "tomcat-websocket-api"
|
||||
}
|
||||
optional("org.eclipse.jetty.ee10:jetty-ee10-webapp") {
|
||||
exclude group: "jakarta.servlet", module: "jakarta.servlet-api"
|
||||
}
|
||||
optional("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jakarta-server")
|
||||
optional("org.eclipse.jetty.ee10.websocket:jetty-ee10-websocket-jetty-server") {
|
||||
exclude group: "jakarta.servlet", module: "jakarta.servlet-api"
|
||||
}
|
||||
optional("org.eclipse.jetty.ee10:jetty-ee10-webapp") {
|
||||
exclude group: "jakarta.servlet", module: "jakarta.servlet-api"
|
||||
}
|
||||
optional("org.glassfish.tyrus:tyrus-container-servlet")
|
||||
testImplementation(testFixtures(project(":spring-core")))
|
||||
testImplementation(testFixtures(project(":spring-web")))
|
||||
|
||||
-2
@@ -34,12 +34,10 @@ import org.springframework.web.socket.WebSocketExtension;
|
||||
*/
|
||||
public class StandardToWebSocketExtensionAdapter extends WebSocketExtension {
|
||||
|
||||
|
||||
public StandardToWebSocketExtensionAdapter(Extension extension) {
|
||||
super(extension.getName(), initParameters(extension));
|
||||
}
|
||||
|
||||
|
||||
private static Map<String, String> initParameters(Extension extension) {
|
||||
List<Extension.Parameter> parameters = extension.getParameters();
|
||||
Map<String, String> result = new LinkedCaseInsensitiveMap<>(parameters.size(), Locale.ROOT);
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -55,6 +55,7 @@ public class WebSocketToStandardExtensionAdapter implements Extension {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return this.name;
|
||||
|
||||
+6
-6
@@ -234,7 +234,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
|
||||
|
||||
/**
|
||||
* An overloaded version of
|
||||
* {@link #connect(String, StompSessionHandler, Object...)} that also
|
||||
* {@link #connectAsync(String, StompSessionHandler, Object...)} that also
|
||||
* accepts {@link WebSocketHttpHeaders} to use for the WebSocket handshake.
|
||||
* @param url the url to connect to
|
||||
* @param handshakeHeaders the headers for the WebSocket handshake
|
||||
@@ -254,7 +254,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
|
||||
|
||||
/**
|
||||
* An overloaded version of
|
||||
* {@link #connect(String, StompSessionHandler, Object...)} that also
|
||||
* {@link #connectAsync(String, StompSessionHandler, Object...)} that also
|
||||
* accepts {@link WebSocketHttpHeaders} to use for the WebSocket handshake.
|
||||
* @param url the url to connect to
|
||||
* @param handshakeHeaders the headers for the WebSocket handshake
|
||||
@@ -271,7 +271,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
|
||||
|
||||
/**
|
||||
* An overloaded version of
|
||||
* {@link #connect(String, StompSessionHandler, Object...)} that also accepts
|
||||
* {@link #connectAsync(String, StompSessionHandler, Object...)} that also accepts
|
||||
* {@link WebSocketHttpHeaders} to use for the WebSocket handshake and
|
||||
* {@link StompHeaders} for the STOMP CONNECT frame.
|
||||
* @param url the url to connect to
|
||||
@@ -293,7 +293,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
|
||||
|
||||
/**
|
||||
* An overloaded version of
|
||||
* {@link #connect(String, StompSessionHandler, Object...)} that also accepts
|
||||
* {@link #connectAsync(String, StompSessionHandler, Object...)} that also accepts
|
||||
* {@link WebSocketHttpHeaders} to use for the WebSocket handshake and
|
||||
* {@link StompHeaders} for the STOMP CONNECT frame.
|
||||
* @param url the url to connect to
|
||||
@@ -314,7 +314,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
|
||||
|
||||
/**
|
||||
* An overloaded version of
|
||||
* {@link #connect(String, WebSocketHttpHeaders, StompSessionHandler, Object...)}
|
||||
* {@link #connectAsync(String, WebSocketHttpHeaders, StompSessionHandler, Object...)}
|
||||
* that accepts a fully prepared {@link java.net.URI}.
|
||||
* @param url the url to connect to
|
||||
* @param handshakeHeaders the headers for the WebSocket handshake
|
||||
@@ -334,7 +334,7 @@ public class WebSocketStompClient extends StompClientSupport implements SmartLif
|
||||
|
||||
/**
|
||||
* An overloaded version of
|
||||
* {@link #connect(String, WebSocketHttpHeaders, StompSessionHandler, Object...)}
|
||||
* {@link #connectAsync(String, WebSocketHttpHeaders, StompSessionHandler, Object...)}
|
||||
* that accepts a fully prepared {@link java.net.URI}.
|
||||
* @param url the url to connect to
|
||||
* @param handshakeHeaders the headers for the WebSocket handshake
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2024 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.socket.server.support;
|
||||
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.aot.hint.ReflectionHints;
|
||||
import org.springframework.aot.hint.RuntimeHints;
|
||||
import org.springframework.aot.hint.RuntimeHintsRegistrar;
|
||||
import org.springframework.aot.hint.TypeReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* {@link RuntimeHintsRegistrar} implementation that registers reflection hints
|
||||
* for {@link AbstractHandshakeHandler}.
|
||||
*
|
||||
* @author Sebastien Deleuze
|
||||
* @since 6.0
|
||||
*/
|
||||
class HandshakeHandlerRuntimeHints implements RuntimeHintsRegistrar {
|
||||
|
||||
private static final boolean tomcatWsPresent;
|
||||
|
||||
private static final boolean jettyWsPresent;
|
||||
|
||||
private static final boolean undertowWsPresent;
|
||||
|
||||
private static final boolean glassfishWsPresent;
|
||||
|
||||
private static final boolean weblogicWsPresent;
|
||||
|
||||
private static final boolean websphereWsPresent;
|
||||
|
||||
static {
|
||||
ClassLoader classLoader = AbstractHandshakeHandler.class.getClassLoader();
|
||||
tomcatWsPresent = ClassUtils.isPresent(
|
||||
"org.apache.tomcat.websocket.server.WsHttpUpgradeHandler", classLoader);
|
||||
jettyWsPresent = ClassUtils.isPresent(
|
||||
"org.eclipse.jetty.ee10.websocket.server.JettyWebSocketServerContainer", classLoader);
|
||||
undertowWsPresent = ClassUtils.isPresent(
|
||||
"io.undertow.websockets.jsr.ServerWebSocketContainer", classLoader);
|
||||
glassfishWsPresent = ClassUtils.isPresent(
|
||||
"org.glassfish.tyrus.servlet.TyrusHttpUpgradeHandler", classLoader);
|
||||
weblogicWsPresent = ClassUtils.isPresent(
|
||||
"weblogic.websocket.tyrus.TyrusServletWriter", classLoader);
|
||||
websphereWsPresent = ClassUtils.isPresent(
|
||||
"com.ibm.websphere.wsoc.WsWsocServerContainer", classLoader);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
|
||||
ReflectionHints reflectionHints = hints.reflection();
|
||||
if (tomcatWsPresent) {
|
||||
registerType(reflectionHints, "org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy");
|
||||
}
|
||||
else if (jettyWsPresent) {
|
||||
registerType(reflectionHints, "org.springframework.web.socket.server.jetty.JettyRequestUpgradeStrategy");
|
||||
}
|
||||
else if (undertowWsPresent) {
|
||||
registerType(reflectionHints, "org.springframework.web.socket.server.standard.UndertowRequestUpgradeStrategy");
|
||||
}
|
||||
else if (glassfishWsPresent) {
|
||||
registerType(reflectionHints, "org.springframework.web.socket.server.standard.GlassFishRequestUpgradeStrategy");
|
||||
}
|
||||
else if (weblogicWsPresent) {
|
||||
registerType(reflectionHints, "org.springframework.web.socket.server.standard.WebLogicRequestUpgradeStrategy");
|
||||
}
|
||||
else if (websphereWsPresent) {
|
||||
registerType(reflectionHints, "org.springframework.web.socket.server.standard.WebSphereRequestUpgradeStrategy");
|
||||
}
|
||||
}
|
||||
|
||||
private void registerType(ReflectionHints reflectionHints, String className) {
|
||||
reflectionHints.registerType(TypeReference.of(className),
|
||||
builder -> builder.withMembers(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS));
|
||||
}
|
||||
|
||||
}
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2022 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -278,6 +278,7 @@ class DefaultTransportRequest implements TransportRequest {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the given (global) future based success or failure to connect for
|
||||
* the entire SockJS request regardless of which transport actually managed
|
||||
|
||||
+6
-6
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2024 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.
|
||||
@@ -116,13 +116,13 @@ public class SockJsClient implements WebSocketClient, Lifecycle {
|
||||
|
||||
|
||||
/**
|
||||
* The names of HTTP headers that should be copied from the handshake headers
|
||||
* of each call to {@link SockJsClient#doHandshake(WebSocketHandler, WebSocketHttpHeaders, URI)}
|
||||
* and also used with other HTTP requests issued as part of that SockJS
|
||||
* connection, e.g. the initial info request, XHR send or receive requests.
|
||||
* The names of HTTP headers that should be copied from the handshake headers of each
|
||||
* call to {@link SockJsClient#execute(WebSocketHandler, WebSocketHttpHeaders, URI)}
|
||||
* and also used with other HTTP requests issued as part of that SockJS connection,
|
||||
* for example, the initial info request, XHR send or receive requests.
|
||||
* <p>By default if this property is not set, all handshake headers are also
|
||||
* used for other HTTP requests. Set it if you want only a subset of handshake
|
||||
* headers (e.g. auth headers) to be used for other HTTP requests.
|
||||
* headers (for example, auth headers) to be used for other HTTP requests.
|
||||
* @param httpHeaderNames the HTTP header names
|
||||
*/
|
||||
public void setHttpHeaderNames(@Nullable String... httpHeaderNames) {
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
org.springframework.aot.hint.RuntimeHintsRegistrar= \
|
||||
org.springframework.web.socket.server.support.HandshakeHandlerRuntimeHints
|
||||
Reference in New Issue
Block a user