From 0cf15c0fdd54a894e518928809600fdf5d87f0dd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2022 13:00:20 +0200 Subject: [PATCH 1/7] Upgrade to Tomcat 9.0.62, Jetty 9.4.46, Jetty Reactive HttpClient 1.1.11, Undertow 2.2.17, R2DBC Arabba-SR13, RSocket 1.1.2, OpenPDF 1.3.27, HtmlUnit 2.60, Checkstyle 10.1 --- build.gradle | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/build.gradle b/build.gradle index d21c458e1fc..98f15755263 100644 --- a/build.gradle +++ b/build.gradle @@ -30,9 +30,9 @@ configure(allprojects) { project -> mavenBom "com.fasterxml.jackson:jackson-bom:2.12.6" mavenBom "io.netty:netty-bom:4.1.75.Final" mavenBom "io.projectreactor:reactor-bom:2020.0.17" - mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR12" - mavenBom "io.rsocket:rsocket-bom:1.1.1" - mavenBom "org.eclipse.jetty:jetty-bom:9.4.45.v20220203" + mavenBom "io.r2dbc:r2dbc-bom:Arabba-SR13" + mavenBom "io.rsocket:rsocket-bom:1.1.2" + mavenBom "org.eclipse.jetty:jetty-bom:9.4.46.v20220331" mavenBom "org.jetbrains.kotlin:kotlin-bom:1.5.32" mavenBom "org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.5.2" mavenBom "org.jetbrains.kotlinx:kotlinx-serialization-bom:1.2.2" @@ -96,7 +96,7 @@ configure(allprojects) { project -> dependency "com.h2database:h2:2.1.210" dependency "com.github.ben-manes.caffeine:caffeine:2.9.3" - dependency "com.github.librepdf:openpdf:1.3.26" + dependency "com.github.librepdf:openpdf:1.3.27" dependency "com.rometools:rome:1.18.0" dependency "commons-io:commons-io:2.5" dependency "io.vavr:vavr:0.10.4" @@ -128,18 +128,18 @@ configure(allprojects) { project -> dependency "org.webjars:webjars-locator-core:0.48" dependency "org.webjars:underscorejs:1.8.3" - dependencySet(group: 'org.apache.tomcat', version: '9.0.60') { + dependencySet(group: 'org.apache.tomcat', version: '9.0.62') { entry 'tomcat-util' entry('tomcat-websocket') { exclude group: "org.apache.tomcat", name: "tomcat-servlet-api" exclude group: "org.apache.tomcat", name: "tomcat-websocket-api" } } - dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.60') { + dependencySet(group: 'org.apache.tomcat.embed', version: '9.0.62') { entry 'tomcat-embed-core' entry 'tomcat-embed-websocket' } - dependencySet(group: 'io.undertow', version: '2.2.16.Final') { + dependencySet(group: 'io.undertow', version: '2.2.17.Final') { entry 'undertow-core' entry('undertow-servlet') { exclude group: "org.jboss.spec.javax.servlet", name: "jboss-servlet-api_4.0_spec" @@ -150,7 +150,7 @@ configure(allprojects) { project -> } } - dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.10" + dependency "org.eclipse.jetty:jetty-reactive-httpclient:1.1.11" dependency 'org.apache.httpcomponents.client5:httpclient5:5.1.3' dependency 'org.apache.httpcomponents.core5:httpcore5-reactive:5.1.3' dependency("org.apache.httpcomponents:httpclient:4.5.13") { @@ -206,10 +206,10 @@ configure(allprojects) { project -> } dependency "io.mockk:mockk:1.12.1" - dependency("net.sourceforge.htmlunit:htmlunit:2.59.0") { + dependency("net.sourceforge.htmlunit:htmlunit:2.60.0") { exclude group: "commons-logging", name: "commons-logging" } - dependency("org.seleniumhq.selenium:htmlunit-driver:2.59.0") { + dependency("org.seleniumhq.selenium:htmlunit-driver:2.60.0") { exclude group: "commons-logging", name: "commons-logging" } dependency("org.seleniumhq.selenium:selenium-java:3.141.59") { @@ -340,7 +340,7 @@ configure([rootProject] + javaProjects) { project -> } checkstyle { - toolVersion = "9.3" + toolVersion = "10.1" configDirectory.set(rootProject.file("src/checkstyle")) } From 90103b0ae99361a21a21c5306fa85b1614a4e2fb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2022 13:00:31 +0200 Subject: [PATCH 2/7] Consistent support for direct column matches in DataClassRowMapper Closes gh-28243 --- .../jdbc/core/DataClassRowMapper.java | 15 +++++++++--- .../jdbc/core/AbstractRowMapperTests.java | 24 +++++++++++++++---- .../jdbc/core/BeanPropertyRowMapperTests.java | 11 +++++++++ .../jdbc/core/DataClassRowMapperTests.java | 10 ++++---- .../jdbc/core/test/AbstractPerson.java | 12 +++++----- .../test/ConstructorPersonWithGenerics.java | 10 ++++---- .../test/ConstructorPersonWithSetters.java | 16 ++++++------- .../jdbc/core/test/SpacePerson.java | 6 ++--- 8 files changed, 69 insertions(+), 35 deletions(-) diff --git a/spring-jdbc/src/main/java/org/springframework/jdbc/core/DataClassRowMapper.java b/spring-jdbc/src/main/java/org/springframework/jdbc/core/DataClassRowMapper.java index 7e09fcbe891..274bfdfa0cb 100644 --- a/spring-jdbc/src/main/java/org/springframework/jdbc/core/DataClassRowMapper.java +++ b/spring-jdbc/src/main/java/org/springframework/jdbc/core/DataClassRowMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -98,9 +98,18 @@ public class DataClassRowMapper extends BeanPropertyRowMapper { if (this.constructorParameterNames != null && this.constructorParameterTypes != null) { args = new Object[this.constructorParameterNames.length]; for (int i = 0; i < args.length; i++) { - String name = underscoreName(this.constructorParameterNames[i]); + String name = this.constructorParameterNames[i]; + int index; + try { + // Try direct name match first + index = rs.findColumn(lowerCaseName(name)); + } + catch (SQLException ex) { + // Try underscored name match instead + index = rs.findColumn(underscoreName(name)); + } TypeDescriptor td = this.constructorParameterTypes[i]; - Object value = getColumnValue(rs, rs.findColumn(name), td.getType()); + Object value = getColumnValue(rs, index, td.getType()); args[i] = tc.convertIfNecessary(value, td.getType(), td); } } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/AbstractRowMapperTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/AbstractRowMapperTests.java index 1c0a86ffed9..cf9d5817b0c 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/AbstractRowMapperTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/AbstractRowMapperTests.java @@ -20,6 +20,7 @@ import java.math.BigDecimal; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; +import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.Statement; import java.sql.Timestamp; @@ -63,7 +64,7 @@ public abstract class AbstractRowMapperTests { protected void verifyPerson(ConcretePerson person) { assertThat(person.getName()).isEqualTo("Bubba"); assertThat(person.getAge()).isEqualTo(22L); - assertThat(person.getBirth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); + assertThat(person.getBirthDate()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); assertThat(person.getBalance()).isEqualTo(new BigDecimal("1234.56")); verifyPersonViaBeanWrapper(person); } @@ -94,7 +95,14 @@ public abstract class AbstractRowMapperTests { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(person); assertThat(bw.getPropertyValue("name")).isEqualTo("Bubba"); assertThat(bw.getPropertyValue("age")).isEqualTo(22L); - assertThat((Date) bw.getPropertyValue("birth_date")).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); + Date birthDate; + if (bw.isReadableProperty("birth_date")) { + birthDate = (Date) bw.getPropertyValue("birth_date"); + } + else { + birthDate = (Date) bw.getPropertyValue("birthDate"); + } + assertThat(birthDate).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); assertThat(bw.getPropertyValue("balance")).isEqualTo(new BigDecimal("1234.56")); } @@ -107,7 +115,7 @@ public abstract class AbstractRowMapperTests { } - protected enum MockType {ONE, TWO, THREE} + protected enum MockType {ONE, TWO, THREE, FOUR} protected static class Mock { @@ -152,13 +160,19 @@ public abstract class AbstractRowMapperTests { given(resultSetMetaData.getColumnLabel(1)).willReturn( type == MockType.THREE ? "Last Name" : "name"); given(resultSetMetaData.getColumnLabel(2)).willReturn("age"); - given(resultSetMetaData.getColumnLabel(3)).willReturn("birth_date"); + given(resultSetMetaData.getColumnLabel(3)).willReturn(type == MockType.FOUR ? "birthdate" :"birth_date"); given(resultSetMetaData.getColumnLabel(4)).willReturn("balance"); given(resultSetMetaData.getColumnLabel(5)).willReturn("e_mail"); given(resultSet.findColumn("name")).willReturn(1); given(resultSet.findColumn("age")).willReturn(2); - given(resultSet.findColumn("birth_date")).willReturn(3); + if (type == MockType.FOUR) { + given(resultSet.findColumn("birthdate")).willReturn(3); + } + else { + given(resultSet.findColumn("birthdate")).willThrow(new SQLException()); + given(resultSet.findColumn("birth_date")).willReturn(3); + } given(resultSet.findColumn("balance")).willReturn(4); given(resultSet.findColumn("e_mail")).willReturn(5); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java index 99e9eb41627..5ef1f57f891 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/BeanPropertyRowMapperTests.java @@ -140,6 +140,17 @@ class BeanPropertyRowMapperTests extends AbstractRowMapperTests { mock.verifyClosed(); } + @Test + void queryWithDirectNameMatchOnBirthDate() throws Exception { + Mock mock = new Mock(MockType.FOUR); + List result = mock.getJdbcTemplate().query( + "select name, age, birthdate, balance from people", + new BeanPropertyRowMapper<>(ConcretePerson.class)); + assertThat(result).hasSize(1); + verifyPerson(result.get(0)); + mock.verifyClosed(); + } + @Test void queryWithUnderscoreInColumnNameAndPersonWithMultipleAdjacentUppercaseLettersInPropertyName() throws Exception { Mock mock = new Mock(); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/DataClassRowMapperTests.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/DataClassRowMapperTests.java index 48b0f7f0313..c612e5bcae6 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/DataClassRowMapperTests.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/DataClassRowMapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -57,7 +57,7 @@ public class DataClassRowMapperTests extends AbstractRowMapperTests { ConstructorPersonWithGenerics person = result.get(0); assertThat(person.name()).isEqualTo("Bubba"); assertThat(person.age()).isEqualTo(22L); - assertThat(person.birth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); + assertThat(person.birthDate()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); assertThat(person.balance()).isEqualTo(Collections.singletonList(new BigDecimal("1234.56"))); mock.verifyClosed(); @@ -65,15 +65,15 @@ public class DataClassRowMapperTests extends AbstractRowMapperTests { @Test public void testStaticQueryWithDataClassAndSetters() throws Exception { - Mock mock = new Mock(); + Mock mock = new Mock(MockType.FOUR); List result = mock.getJdbcTemplate().query( - "select name, age, birth_date, balance from people", + "select name, age, birthdate, balance from people", new DataClassRowMapper<>(ConstructorPersonWithSetters.class)); assertThat(result.size()).isEqualTo(1); ConstructorPersonWithSetters person = result.get(0); assertThat(person.name()).isEqualTo("BUBBA"); assertThat(person.age()).isEqualTo(22L); - assertThat(person.birth_date()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); + assertThat(person.birthDate()).usingComparator(Date::compareTo).isEqualTo(new java.util.Date(1221222L)); assertThat(person.balance()).isEqualTo(new BigDecimal("1234.56")); mock.verifyClosed(); diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/AbstractPerson.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/AbstractPerson.java index f2698d3073a..b084644c689 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/AbstractPerson.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/AbstractPerson.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 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. @@ -27,7 +27,7 @@ public abstract class AbstractPerson { private long age; - private Date birth_date; + private Date birthDate; public String getName() { @@ -46,12 +46,12 @@ public abstract class AbstractPerson { this.age = age; } - public Date getBirth_date() { - return birth_date; + public Date getBirthDate() { + return birthDate; } - public void setBirth_date(Date birth_date) { - this.birth_date = birth_date; + public void setBirthDate(Date birthDate) { + this.birthDate = birthDate; } } diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/ConstructorPersonWithGenerics.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/ConstructorPersonWithGenerics.java index 3ae8e271c81..289197b5639 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/ConstructorPersonWithGenerics.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/ConstructorPersonWithGenerics.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -29,7 +29,7 @@ public class ConstructorPersonWithGenerics { private final long age; - private final Date birth_date; + private final Date birthDate; private final List balance; @@ -37,7 +37,7 @@ public class ConstructorPersonWithGenerics { public ConstructorPersonWithGenerics(String name, long age, Date birth_date, List balance) { this.name = name; this.age = age; - this.birth_date = birth_date; + this.birthDate = birth_date; this.balance = balance; } @@ -50,8 +50,8 @@ public class ConstructorPersonWithGenerics { return this.age; } - public Date birth_date() { - return this.birth_date; + public Date birthDate() { + return this.birthDate; } public List balance() { diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/ConstructorPersonWithSetters.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/ConstructorPersonWithSetters.java index ef1feb9a324..0776b5cc48a 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/ConstructorPersonWithSetters.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/ConstructorPersonWithSetters.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -28,15 +28,15 @@ public class ConstructorPersonWithSetters { private long age; - private Date birth_date; + private Date birthDate; private BigDecimal balance; - public ConstructorPersonWithSetters(String name, long age, Date birth_date, BigDecimal balance) { + public ConstructorPersonWithSetters(String name, long age, Date birthDate, BigDecimal balance) { this.name = name.toUpperCase(); this.age = age; - this.birth_date = birth_date; + this.birthDate = birthDate; this.balance = balance; } @@ -49,8 +49,8 @@ public class ConstructorPersonWithSetters { this.age = age; } - public void setBirth_date(Date birth_date) { - this.birth_date = birth_date; + public void setBirthDate(Date birthDate) { + this.birthDate = birthDate; } public void setBalance(BigDecimal balance) { @@ -65,8 +65,8 @@ public class ConstructorPersonWithSetters { return this.age; } - public Date birth_date() { - return this.birth_date; + public Date birthDate() { + return this.birthDate; } public BigDecimal balance() { diff --git a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/SpacePerson.java b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/SpacePerson.java index 8dc8875e15c..2fc59db1b2e 100644 --- a/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/SpacePerson.java +++ b/spring-jdbc/src/test/java/org/springframework/jdbc/core/test/SpacePerson.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 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. @@ -60,8 +60,8 @@ public class SpacePerson { return balance; } - public void setBalance(BigDecimal balanace) { - this.balance = balanace; + public void setBalance(BigDecimal balance) { + this.balance = balance; } } From c3fe112fd7517cc350a868d3af9995d189cbdfb6 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2022 13:00:50 +0200 Subject: [PATCH 3/7] Consistent use of getLocalAddr() without DNS lookups in request adapters Closes gh-28280 --- .../springframework/http/server/ServletServerHttpRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java index 82f722310c3..45ce146f2ef 100644 --- a/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java +++ b/spring-web/src/main/java/org/springframework/http/server/ServletServerHttpRequest.java @@ -197,7 +197,7 @@ public class ServletServerHttpRequest implements ServerHttpRequest { @Override public InetSocketAddress getLocalAddress() { - return new InetSocketAddress(this.servletRequest.getLocalName(), this.servletRequest.getLocalPort()); + return new InetSocketAddress(this.servletRequest.getLocalAddr(), this.servletRequest.getLocalPort()); } @Override From 7aed6279a284283f079290f44c5d0eee7bef7cda Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2022 13:02:36 +0200 Subject: [PATCH 4/7] Consistent fallback in case of fast-class generation failure Closes gh-28138 --- .../aop/framework/CglibAopProxy.java | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java index 022cc0fddf2..87fa84d6b98 100644 --- a/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java +++ b/spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -375,6 +375,22 @@ class CglibAopProxy implements AopProxy, Serializable { return false; } + /** + * Invoke the given method with a CGLIB MethodProxy if possible, falling back + * to a plain reflection invocation in case of a fast-class generation failure. + */ + @Nullable + private static Object invokeMethod(@Nullable Object target, Method method, Object[] args, MethodProxy methodProxy) + throws Throwable { + try { + return methodProxy.invoke(target, args); + } + catch (CodeGenerationException ex) { + CglibMethodInvocation.logFastClassGenerationFailure(method); + return AopUtils.invokeJoinpointUsingReflection(target, method, args); + } + } + /** * Process a return value. Wraps a return of {@code this} if necessary to be the * {@code proxy} and also verifies that {@code null} is not returned as a primitive. @@ -425,7 +441,7 @@ class CglibAopProxy implements AopProxy, Serializable { @Override @Nullable public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { - Object retVal = methodProxy.invoke(this.target, args); + Object retVal = invokeMethod(this.target, method, args, methodProxy); return processReturnType(proxy, this.target, method, retVal); } } @@ -450,7 +466,7 @@ class CglibAopProxy implements AopProxy, Serializable { Object oldProxy = null; try { oldProxy = AopContext.setCurrentProxy(proxy); - Object retVal = methodProxy.invoke(this.target, args); + Object retVal = invokeMethod(this.target, method, args, methodProxy); return processReturnType(proxy, this.target, method, retVal); } finally { @@ -478,7 +494,7 @@ class CglibAopProxy implements AopProxy, Serializable { public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object target = this.targetSource.getTarget(); try { - Object retVal = methodProxy.invoke(target, args); + Object retVal = invokeMethod(target, method, args, methodProxy); return processReturnType(proxy, target, method, retVal); } finally { @@ -508,7 +524,7 @@ class CglibAopProxy implements AopProxy, Serializable { Object target = this.targetSource.getTarget(); try { oldProxy = AopContext.setCurrentProxy(proxy); - Object retVal = methodProxy.invoke(target, args); + Object retVal = invokeMethod(target, method, args, methodProxy); return processReturnType(proxy, target, method, retVal); } finally { @@ -685,13 +701,7 @@ class CglibAopProxy implements AopProxy, Serializable { // it does nothing but a reflective operation on the target, and no hot // swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); - try { - retVal = methodProxy.invoke(target, argsToUse); - } - catch (CodeGenerationException ex) { - CglibMethodInvocation.logFastClassGenerationFailure(method); - retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); - } + retVal = invokeMethod(target, method, argsToUse, methodProxy); } else { // We need to create a method invocation... From 9f9116839681909388fd87bf481a3d5cd353c9aa Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2022 13:02:53 +0200 Subject: [PATCH 5/7] Restore ability to configure setClassLoader methods Closes gh-28269 --- .../beans/CachedIntrospectionResults.java | 17 ++++++----- .../beans/BeanWrapperTests.java | 28 +++++++++++++++++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java index 4187097ce37..2929509d234 100644 --- a/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java +++ b/spring-beans/src/main/java/org/springframework/beans/CachedIntrospectionResults.java @@ -287,13 +287,15 @@ public final class CachedIntrospectionResults { // This call is slow so we do it once. PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors(); for (PropertyDescriptor pd : pds) { - if (Class.class == beanClass && (!"name".equals(pd.getName()) && !pd.getName().endsWith("Name"))) { + if (Class.class == beanClass && !("name".equals(pd.getName()) || + (pd.getName().endsWith("Name") && String.class == pd.getPropertyType()))) { // Only allow all name variants of Class properties continue; } - if (pd.getPropertyType() != null && (ClassLoader.class.isAssignableFrom(pd.getPropertyType()) - || ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) { - // Ignore ClassLoader and ProtectionDomain types - nobody needs to bind to those + if (pd.getWriteMethod() == null && pd.getPropertyType() != null && + (ClassLoader.class.isAssignableFrom(pd.getPropertyType()) || + ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) { + // Ignore ClassLoader and ProtectionDomain read-only properties - no need to bind to those continue; } if (logger.isTraceEnabled()) { @@ -342,9 +344,10 @@ public final class CachedIntrospectionResults { // GenericTypeAwarePropertyDescriptor leniently resolves a set* write method // against a declared read method, so we prefer read method descriptors here. pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd); - if (pd.getPropertyType() != null && (ClassLoader.class.isAssignableFrom(pd.getPropertyType()) - || ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) { - // Ignore ClassLoader and ProtectionDomain types - nobody needs to bind to those + if (pd.getWriteMethod() == null && pd.getPropertyType() != null && + (ClassLoader.class.isAssignableFrom(pd.getPropertyType()) || + ProtectionDomain.class.isAssignableFrom(pd.getPropertyType()))) { + // Ignore ClassLoader and ProtectionDomain read-only properties - no need to bind to those continue; } this.propertyDescriptors.put(pd.getName(), pd); diff --git a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java index 8856e31a0e0..ab154ea3c4e 100644 --- a/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/BeanWrapperTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -23,6 +23,8 @@ import java.util.Optional; import org.junit.jupiter.api.Test; import org.springframework.beans.testfixture.beans.TestBean; +import org.springframework.core.OverridingClassLoader; +import org.springframework.core.io.DefaultResourceLoader; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -105,7 +107,7 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests { .satisfies(ex -> assertThat(ex.getPossibleMatches()).containsExactly("age")); } - @Test // Can't be shared; there is no such thing as a read-only field + @Test // Can't be shared; there is no such thing as a read-only field void setReadOnlyMapProperty() { TypedReadOnlyMap map = new TypedReadOnlyMap(Collections.singletonMap("key", new TestBean())); TypedReadOnlyMapClient target = new TypedReadOnlyMapClient(); @@ -157,12 +159,34 @@ class BeanWrapperTests extends AbstractPropertyAccessorTests { BeanWrapper accessor = createAccessor(target); accessor.setPropertyValue("name", "a"); accessor.setPropertyValue("spouse.name", "b"); + assertThat(target.getName()).isEqualTo("a"); assertThat(target.getSpouse().getName()).isEqualTo("b"); assertThat(accessor.getPropertyValue("name")).isEqualTo("a"); assertThat(accessor.getPropertyValue("spouse.name")).isEqualTo("b"); assertThat(accessor.getPropertyDescriptor("name").getPropertyType()).isEqualTo(String.class); assertThat(accessor.getPropertyDescriptor("spouse.name").getPropertyType()).isEqualTo(String.class); + + assertThat(accessor.isReadableProperty("class.package")).isFalse(); + assertThat(accessor.isReadableProperty("class.module")).isFalse(); + assertThat(accessor.isReadableProperty("class.classLoader")).isFalse(); + assertThat(accessor.isReadableProperty("class.name")).isTrue(); + assertThat(accessor.isReadableProperty("class.simpleName")).isTrue(); + assertThat(accessor.getPropertyValue("class.name")).isEqualTo(TestBean.class.getName()); + assertThat(accessor.getPropertyValue("class.simpleName")).isEqualTo(TestBean.class.getSimpleName()); + assertThat(accessor.getPropertyDescriptor("class.name").getPropertyType()).isEqualTo(String.class); + assertThat(accessor.getPropertyDescriptor("class.simpleName").getPropertyType()).isEqualTo(String.class); + + accessor = createAccessor(new DefaultResourceLoader()); + + assertThat(accessor.isReadableProperty("class.package")).isFalse(); + assertThat(accessor.isReadableProperty("class.module")).isFalse(); + assertThat(accessor.isReadableProperty("class.classLoader")).isFalse(); + assertThat(accessor.isReadableProperty("classLoader")).isTrue(); + assertThat(accessor.isWritableProperty("classLoader")).isTrue(); + OverridingClassLoader ocl = new OverridingClassLoader(getClass().getClassLoader()); + accessor.setPropertyValue("classLoader", ocl); + assertThat(accessor.getPropertyValue("classLoader")).isSameAs(ocl); } @Test From eefdd2c768cbacdc97839ede2209a630cf1f0d3e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2022 13:03:13 +0200 Subject: [PATCH 6/7] Avoid return value reference in potentially cached MethodParameter instance Closes gh-28232 --- .../messaging/handler/HandlerMethod.java | 10 +++++----- .../org/springframework/web/method/HandlerMethod.java | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java index 4ace844929f..7a834c5d2f0 100644 --- a/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java +++ b/spring-messaging/src/main/java/org/springframework/messaging/handler/HandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -420,21 +420,21 @@ public class HandlerMethod { private class ReturnValueMethodParameter extends HandlerMethodParameter { @Nullable - private final Object returnValue; + private final Class returnValueType; public ReturnValueMethodParameter(@Nullable Object returnValue) { super(-1); - this.returnValue = returnValue; + this.returnValueType = (returnValue != null ? returnValue.getClass() : null); } protected ReturnValueMethodParameter(ReturnValueMethodParameter original) { super(original); - this.returnValue = original.returnValue; + this.returnValueType = original.returnValueType; } @Override public Class getParameterType() { - return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType()); + return (this.returnValueType != null ? this.returnValueType : super.getParameterType()); } @Override diff --git a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java index 80d6e7999eb..ca859130f69 100644 --- a/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java +++ b/spring-web/src/main/java/org/springframework/web/method/HandlerMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 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. @@ -582,21 +582,21 @@ public class HandlerMethod { private class ReturnValueMethodParameter extends HandlerMethodParameter { @Nullable - private final Object returnValue; + private final Class returnValueType; public ReturnValueMethodParameter(@Nullable Object returnValue) { super(-1); - this.returnValue = returnValue; + this.returnValueType = (returnValue != null ? returnValue.getClass() : null); } protected ReturnValueMethodParameter(ReturnValueMethodParameter original) { super(original); - this.returnValue = original.returnValue; + this.returnValueType = original.returnValueType; } @Override public Class getParameterType() { - return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType()); + return (this.returnValueType != null ? this.returnValueType : super.getParameterType()); } @Override From 4143b445d6328040e3697208ed3ac4a3a15cb2cb Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 8 Apr 2022 13:04:14 +0200 Subject: [PATCH 7/7] Polishing --- .../jmx/export/notificationPublisherTests.xml | 8 ++++---- .../org/springframework/core/convert/Property.java | 4 ++-- .../expression/spel/ast/ConstructorReference.java | 4 ++-- .../http/codec/json/Jackson2JsonEncoderTests.java | 11 ++++++----- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/spring-context/src/test/resources/org/springframework/jmx/export/notificationPublisherTests.xml b/spring-context/src/test/resources/org/springframework/jmx/export/notificationPublisherTests.xml index 8b8699a8e28..9e7c75135c2 100644 --- a/spring-context/src/test/resources/org/springframework/jmx/export/notificationPublisherTests.xml +++ b/spring-context/src/test/resources/org/springframework/jmx/export/notificationPublisherTests.xml @@ -5,16 +5,16 @@ - + - + - - + + diff --git a/spring-core/src/main/java/org/springframework/core/convert/Property.java b/spring-core/src/main/java/org/springframework/core/convert/Property.java index 5cddaea8769..bf9ae585181 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/Property.java +++ b/spring-core/src/main/java/org/springframework/core/convert/Property.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 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. @@ -47,7 +47,7 @@ import org.springframework.util.StringUtils; */ public final class Property { - private static Map annotationCache = new ConcurrentReferenceHashMap<>(); + private static final Map annotationCache = new ConcurrentReferenceHashMap<>(); private final Class objectType; diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java index 6f4f3c8c69c..c425c84746e 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/ConstructorReference.java @@ -288,8 +288,8 @@ public class ConstructorReference extends SpelNodeImpl { else { // There is an initializer if (this.dimensions == null || this.dimensions.length > 1) { - // There is an initializer but this is a multi-dimensional array (e.g. new int[][]{{1,2},{3,4}}) - this - // is not currently supported + // There is an initializer but this is a multi-dimensional array (e.g. new int[][]{{1,2},{3,4}}) + // - this is not currently supported throw new SpelEvaluationException(getStartPosition(), SpelMessage.MULTIDIM_ARRAY_INITIALIZER_NOT_SUPPORTED); } diff --git a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2JsonEncoderTests.java b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2JsonEncoderTests.java index 2e589ac2a8f..b66084e8299 100644 --- a/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2JsonEncoderTests.java +++ b/spring-web/src/test/java/org/springframework/http/codec/json/Jackson2JsonEncoderTests.java @@ -103,7 +103,7 @@ public class Jackson2JsonEncoderTests extends AbstractEncoderTests map.put(halMediaType, mapper)); + String ls = System.lineSeparator(); // output below is different between Unix and Windows testEncode(Mono.just(jacksonValue), type, halMediaType, Collections.emptyMap(), step -> step - .consumeNextWith(expectString("{\n \"withView1\" : \"with\"\n}").andThen(DataBufferUtils::release)) + .consumeNextWith(expectString("{" + ls + " \"withView1\" : \"with\"" + ls + "}") + .andThen(DataBufferUtils::release)) .verifyComplete() ); } - @Test // gh-22771 + @Test // gh-22771 public void encodeWithFlushAfterWriteOff() { ObjectMapper mapper = new ObjectMapper(); mapper.configure(SerializationFeature.FLUSH_AFTER_WRITE_VALUE, false);