Compare commits

...
Author SHA1 Message Date
Spring Buildmaster 445e8ea84a Release version 5.1.14.RELEASE 2020-02-25 16:23:08 +00:00
Juergen Hoeller df60919bb0 Polishing 2020-02-25 15:32:05 +01:00
Juergen Hoeller 2f1f36eeda Upgrade to Groovy 2.5.9, RxJava 2.2.18, Tomcat 9.0.31, Jetty 9.4.26, Netty 4.1.45, Checkstyle 8.29 2020-02-24 19:44:25 +01:00
Juergen Hoeller 1c0a4f8d78 Polishing 2020-02-24 19:34:15 +01:00
Juergen Hoeller cc376ac5ed Clarify setCacheMillis/setCacheSeconds vs java.util.ResourceBundle
Closes gh-24563
2020-02-24 19:10:25 +01:00
Rossen Stoyanchev 10c9d2fcec Upgrade to Reactor Calfiornium-SR16
Closes gh-24527
2020-02-24 14:57:34 +00:00
Arjen Poutsma 439ffe2e8a Convert non-UTF-8 JSON
Jackson's asynchronous parser does not support any encoding except UTF-8
(or ASCII). This commit converts non-UTF-8/ASCII encoded JSON to UTF-8.

Closes gh-24489
2020-02-20 11:21:00 +01:00
Rossen Stoyanchev 4e55262521 Upgrade to Reactor Californium snapshots
See gh-24527
2020-02-19 16:26:17 +00:00
Juergen Hoeller ebd2ec57fc Polishing 2020-02-12 17:58:26 +01:00
Rossen Stoyanchev f536819c5d Update advice on RestTemplate
Closes gh-24503
2020-02-12 17:19:57 +01:00
Juergen Hoeller af75c6db2f Consistent ROLE_INFRASTRUCTURE declarations for configuration classes
Closes gh-24509
2020-02-12 17:18:59 +01:00
Juergen Hoeller d12619cf1f Raise log level for exceptions from EntityManager close call
Closes gh-24501
2020-02-12 17:10:32 +01:00
Sam Brannen 4c1d227776 Polish documentation format
Closes gh-24462
2020-02-12 15:47:17 +01:00
Sam Brannen da02b7a141 Support SpEL compilation of interface methods again
Spring Framework 5.1.8 introduced a regression for the compilation of
SpEL expressions referencing a method declared in an interface. An
attempt to compile such an expression resulted in a
SpelEvaluationException caused by an IncompatibleClassChangeError.

This commit fixes this regression by adding explicit support in
ReflectivePropertyAccessor.OptimalPropertyAccessor.generateCode() for
methods declared in interfaces.

Closes gh-24357
2020-02-12 15:40:17 +01:00
Arjen Poutsma 17117bd21a Force TokenBuffer to use BigDecimal if necessary
This commit makes the Jackson2Tokenizer enable
TokenBuffer.forceUseOfBigDecimal if the element type given to the
Decoder is BigDecimal. Previous to this commit, values would be
converted to floats.

Closes gh-24479
2020-02-07 14:45:58 +01:00
Arjen Poutsma 0d7494ac52 Honour ObjectMapper feature in Jackson2Tokenizer
After this commit, Jackson2Tokenizer honours ObjectMapper's
DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS feature when creating
TokenBuffers.

Closes gh-24479
2020-02-06 17:12:05 +01:00
Christoph Dreis a59a33846e Polish documentation format
See gh-24462
2020-01-31 08:57:30 +01:00
Juergen Hoeller 51b8ba334a Polishing 2020-01-28 21:40:16 +01:00
Juergen Hoeller abc2269d97 Use local LoggerContext in Log4jLog when static field not initialized yet
Closes gh-24440
2020-01-28 21:21:30 +01:00
Juergen Hoeller ff385aaf7b Add Informix to supported database products for procedure calls
Closes gh-24443
2020-01-28 21:20:46 +01:00
Juergen Hoeller 1b50ca6bd3 Solve ReflectPermission issue in sandbox security policy model for CGLIB
Closes gh-24420
2020-01-28 21:19:15 +01:00
Arjen Poutsma 6bae759349 Use Jackson SequenceWriter for streaming
Before this commit, the AbstractJackson2Encoder instantiated a
ObjectWriter per value. This is not an issue for single values or
non-streaming scenarios (which effectively are the same, because in the
latter values are collected into a list until offered to Jackson).
However, this does create a problem for SMILE, because it allows for
shared references that do not match up when writing each value with a
new ObjectWriter, resulting in errors parsing the result.

This commit uses Jackson's SequenceWriter for streaming scenarios,
allowing Jackson to reuse the same context for writing multiple values,
fixing the issue described above.

Closes gh-24198
2020-01-22 10:12:05 +01:00
Brian Clozel e1e8c165db Avoid setting special Content-* response headers for Tomcat
As of gh-21783, Spring WebFlux uses a `TomcatHeadersAdapter`
implementation to directly address the native headers used by the
server.

In the case of Tomcat, "Content-Length" and "Content-Type" headers are
processed separately and should not be added to the native headers map.

This commit improves the `HandlerAdapter` implementation for Tomcat and
removes those headers, if previously set in the map. The adapter
already has a section that handles the Tomcat-specific calls for such
headers.

Fixes gh-24387
2020-01-17 15:15:14 +01:00
Sam Brannen 6699833121 Introduce regression test for gh-24375 2020-01-16 16:34:21 +01:00
Sam Brannen 095acefd7e Include Objenesis NOTICE file contents in binary distributions
Closes gh-24326
2020-01-16 10:54:23 +01:00
Spring Buildmaster b62e066b7d Next Development Version 2020-01-14 07:37:59 +00:00
52 changed files with 716 additions and 255 deletions
+7 -7
View File
@@ -30,21 +30,21 @@ ext {
aspectjVersion = "1.9.5"
freemarkerVersion = "2.3.28"
groovyVersion = "2.5.8"
groovyVersion = "2.5.9"
hsqldbVersion = "2.4.1"
jackson2Version = "2.9.9"
jettyVersion = "9.4.25.v20191220"
jettyVersion = "9.4.26.v20200117"
junit5Version = "5.3.2"
kotlinVersion = "1.2.71"
log4jVersion = "2.11.2"
nettyVersion = "4.1.44.Final"
reactorVersion = "Californium-SR15"
nettyVersion = "4.1.45.Final"
reactorVersion = "Californium-SR16"
rxjavaVersion = "1.3.8"
rxjavaAdapterVersion = "1.2.1"
rxjava2Version = "2.2.17"
rxjava2Version = "2.2.18"
slf4jVersion = "1.7.30" // spring-jcl + consistent 3rd party deps
tiles3Version = "3.0.8"
tomcatVersion = "9.0.30"
tomcatVersion = "9.0.31"
undertowVersion = "2.0.29.Final"
gradleScriptDir = "${rootProject.projectDir}/gradle"
@@ -142,7 +142,7 @@ configure(allprojects) { project ->
}
checkstyle {
toolVersion = "8.28"
toolVersion = "8.29"
configDir = rootProject.file("src/checkstyle")
}
+1 -1
View File
@@ -1 +1 @@
version=5.1.13.BUILD-SNAPSHOT
version=5.1.14.RELEASE
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -37,6 +37,7 @@ import org.springframework.scheduling.config.TaskManagementConfigUtils;
* @see org.springframework.scheduling.annotation.ProxyAsyncConfiguration
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class AspectJAsyncConfiguration extends AbstractAsyncConfiguration {
@Bean(name = TaskManagementConfigUtils.ASYNC_EXECUTION_ASPECT_BEAN_NAME)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -36,6 +36,7 @@ import org.springframework.transaction.config.TransactionManagementConfigUtils;
* @see TransactionManagementConfigurationSelector
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class AspectJJtaTransactionManagementConfiguration extends AspectJTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_BEAN_NAME)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -38,6 +38,7 @@ import org.springframework.transaction.config.TransactionManagementConfigUtils;
* @see AspectJJtaTransactionManagementConfiguration
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class AspectJTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ASPECT_BEAN_NAME)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -373,7 +373,7 @@ public class DependencyDescriptor extends InjectionPoint implements Serializable
/**
* Determine the name of the wrapped parameter/field.
* @return the declared name (never {@code null})
* @return the declared name (may be {@code null} if unresolvable)
*/
@Nullable
public String getDependencyName() {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -146,8 +146,9 @@ public class ArgumentConvertingMethodInvoker extends MethodInvoker {
for (Method candidate : candidates) {
if (candidate.getName().equals(targetMethod)) {
// Check if the inspected method has the correct number of parameters.
Class<?>[] paramTypes = candidate.getParameterTypes();
if (paramTypes.length == argCount) {
int parameterCount = candidate.getParameterCount();
if (parameterCount == argCount) {
Class<?>[] paramTypes = candidate.getParameterTypes();
Object[] convertedArguments = new Object[argCount];
boolean match = true;
for (int j = 0; j < argCount && match; j++) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -748,8 +748,7 @@ class ConfigurationClassParser {
* @param importSelector the selector to handle
*/
public void handle(ConfigurationClass configClass, DeferredImportSelector importSelector) {
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(
configClass, importSelector);
DeferredImportSelectorHolder holder = new DeferredImportSelectorHolder(configClass, importSelector);
if (this.deferredImportSelectors == null) {
DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler();
handler.register(holder);
@@ -775,7 +774,6 @@ class ConfigurationClassParser {
this.deferredImportSelectors = new ArrayList<>();
}
}
}
@@ -786,8 +784,7 @@ class ConfigurationClassParser {
private final Map<AnnotationMetadata, ConfigurationClass> configurationClasses = new HashMap<>();
public void register(DeferredImportSelectorHolder deferredImport) {
Class<? extends Group> group = deferredImport.getImportSelector()
.getImportGroup();
Class<? extends Group> group = deferredImport.getImportSelector().getImportGroup();
DeferredImportSelectorGrouping grouping = this.groupings.computeIfAbsent(
(group != null ? group : deferredImport),
key -> new DeferredImportSelectorGrouping(createGroup(group)));
@@ -799,8 +796,7 @@ class ConfigurationClassParser {
public void processGroupImports() {
for (DeferredImportSelectorGrouping grouping : this.groupings.values()) {
grouping.getImports().forEach(entry -> {
ConfigurationClass configurationClass = this.configurationClasses.get(
entry.getMetadata());
ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata());
try {
processImports(configurationClass, asSourceClass(configurationClass),
asSourceClasses(entry.getImportClassName()), false);
@@ -818,8 +814,7 @@ class ConfigurationClassParser {
}
private Group createGroup(@Nullable Class<? extends Group> type) {
Class<? extends Group> effectiveType = (type != null ? type
: DefaultDeferredImportSelectorGroup.class);
Class<? extends Group> effectiveType = (type != null ? type : DefaultDeferredImportSelectorGroup.class);
Group group = BeanUtils.instantiateClass(effectiveType);
ParserStrategyUtils.invokeAwareMethods(group,
ConfigurationClassParser.this.environment,
@@ -827,7 +822,6 @@ class ConfigurationClassParser {
ConfigurationClassParser.this.registry);
return group;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -51,6 +51,7 @@ public interface DeferredImportSelector extends ImportSelector {
/**
* Interface used to group results from different import selectors.
* @since 5.0
*/
interface Group {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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.
@@ -50,6 +50,7 @@ public interface ImportSelector {
/**
* Select and return the names of which class(es) should be imported based on
* the {@link AnnotationMetadata} of the importing @{@link Configuration} class.
* @return the class names, or an empty array if none
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -42,6 +42,7 @@ import org.springframework.util.Assert;
* @see ConfigurableApplicationContext#LOAD_TIME_WEAVER_BEAN_NAME
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class LoadTimeWeavingConfiguration implements ImportAware, BeanClassLoaderAware {
@Nullable
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -50,6 +50,7 @@ import org.springframework.util.StringUtils;
* @see EnableMBeanExport
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class MBeanExportConfiguration implements ImportAware, EnvironmentAware, BeanFactoryAware {
private static final String MBEAN_EXPORTER_BEAN_NAME = "mbeanExporter";
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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,6 +53,6 @@ public enum ScopedProxyMode {
/**
* Create a class-based proxy (uses CGLIB).
*/
TARGET_CLASS;
TARGET_CLASS
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -160,8 +160,9 @@ public abstract class AbstractResourceBasedMessageSource extends AbstractMessage
/**
* Set the number of seconds to cache loaded properties files.
* <ul>
* <li>Default is "-1", indicating to cache forever (just like
* {@code java.util.ResourceBundle}).
* <li>Default is "-1", indicating to cache forever (matching the default behavior
* of {@code java.util.ResourceBundle}). Note that this constant follows Spring
* conventions, not {@link java.util.ResourceBundle.Control#getTimeToLive}.
* <li>A positive number will cache loaded properties files for the given
* number of seconds. This is essentially the interval between refresh checks.
* Note that a refresh attempt will first check the last-modified timestamp
@@ -184,8 +185,9 @@ public abstract class AbstractResourceBasedMessageSource extends AbstractMessage
* Set the number of milliseconds to cache loaded properties files.
* Note that it is common to set seconds instead: {@link #setCacheSeconds}.
* <ul>
* <li>Default is "-1", indicating to cache forever (just like
* {@code java.util.ResourceBundle}).
* <li>Default is "-1", indicating to cache forever (matching the default behavior
* of {@code java.util.ResourceBundle}). Note that this constant follows Spring
* conventions, not {@link java.util.ResourceBundle.Control#getTimeToLive}.
* <li>A positive number will cache loaded properties files for the given
* number of milliseconds. This is essentially the interval between refresh checks.
* Note that a refresh attempt will first check the last-modified timestamp
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 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.
@@ -42,6 +42,6 @@ public enum RegistrationPolicy {
* Registration should replace the affected MBean when attempting to register an MBean
* under a name that already exists.
*/
REPLACE_EXISTING;
REPLACE_EXISTING
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -42,6 +42,7 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.componentscan.gh24375.MyComponent;
import org.springframework.context.index.CandidateComponentsTestClassLoader;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
@@ -501,6 +502,15 @@ public class ClassPathScanningCandidateComponentProviderTests {
}
}
@Test
public void gh24375() {
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
Set<BeanDefinition> components = provider.findCandidateComponents(MyComponent.class.getPackage().getName());
assertEquals(1, components.size());
assertEquals(MyComponent.class.getName(), components.iterator().next().getBeanClassName());
}
private boolean containsBeanClass(Set<BeanDefinition> candidates, Class<?> beanClass) {
for (BeanDefinition candidate : candidates) {
if (beanClass.getName().equals(candidate.getBeanClassName())) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -530,9 +530,10 @@ public class ImportSelectorTests {
static Map<String, List<String>> allImports() {
return TestImportGroup.imports.entrySet()
.stream()
.collect(Collectors.toMap((entry) -> entry.getKey().getClassName(),
.collect(Collectors.toMap(entry -> entry.getKey().getClassName(),
Map.Entry::getValue));
}
private final List<Entry> instanceImports = new ArrayList<>();
@Override
@@ -0,0 +1,35 @@
/*
* 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.
* 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.context.annotation.componentscan.gh24375;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface A {
@AliasFor("value")
B other() default @B;
@AliasFor("other")
B value() default @B;
}
@@ -0,0 +1,29 @@
/*
* 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.
* 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.context.annotation.componentscan.gh24375;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface B {
String name() default "";
}
@@ -0,0 +1,24 @@
/*
* 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.
* 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.context.annotation.componentscan.gh24375;
import org.springframework.stereotype.Component;
@Component
@A(other = @B)
public class MyComponent {
}
@@ -336,7 +336,15 @@ public class ReflectUtils {
public static Constructor getConstructor(Class type, Class[] parameterTypes) {
try {
Constructor constructor = type.getDeclaredConstructor(parameterTypes);
constructor.setAccessible(true);
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
constructor.setAccessible(true);
return null;
});
}
else {
constructor.setAccessible(true);
}
return constructor;
}
catch (NoSuchMethodException e) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -169,11 +169,12 @@ public final class GenericTypeResolver {
ParameterizedType parameterizedType = (ParameterizedType) genericType;
Class<?>[] generics = new Class<?>[parameterizedType.getActualTypeArguments().length];
Type[] typeArguments = parameterizedType.getActualTypeArguments();
ResolvableType contextType = ResolvableType.forClass(contextClass);
for (int i = 0; i < typeArguments.length; i++) {
Type typeArgument = typeArguments[i];
if (typeArgument instanceof TypeVariable) {
ResolvableType resolvedTypeArgument = resolveVariable(
(TypeVariable<?>) typeArgument, ResolvableType.forClass(contextClass));
(TypeVariable<?>) typeArgument, contextType);
if (resolvedTypeArgument != ResolvableType.NONE) {
generics[i] = resolvedTypeArgument.resolve();
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -149,9 +149,10 @@ public class MethodMetadataReadingVisitor extends MethodVisitor implements Metho
MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<>();
List<AnnotationAttributes> attributesList = this.attributesMap.get(annotationName);
if (attributesList != null) {
String annotatedElement = "method '" + getMethodName() + '\'';
for (AnnotationAttributes annotationAttributes : attributesList) {
AnnotationAttributes convertedAttributes = AnnotationReadingVisitorUtils.convertClassValues(
"method '" + getMethodName() + "'", this.classLoader, annotationAttributes, classValuesAsString);
annotatedElement, this.classLoader, annotationAttributes, classValuesAsString);
convertedAttributes.forEach(allAttributes::add);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -55,6 +55,7 @@ import org.springframework.util.StringUtils;
* @author Andy Clement
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
* @see StandardEvaluationContext
* @see SimpleEvaluationContext
@@ -765,8 +766,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
}
if (this.member instanceof Method) {
mv.visitMethodInsn((isStatic ? INVOKESTATIC : INVOKEVIRTUAL), classDesc, this.member.getName(),
CodeFlow.createSignatureDescriptor((Method) this.member), false);
Method method = (Method) this.member;
boolean isInterface = method.getDeclaringClass().isInterface();
int opcode = (isStatic ? INVOKESTATIC : isInterface ? INVOKEINTERFACE : INVOKEVIRTUAL);
mv.visitMethodInsn(opcode, classDesc, method.getName(),
CodeFlow.createSignatureDescriptor(method), isInterface);
}
else {
mv.visitFieldInsn((isStatic ? GETSTATIC : GETFIELD), classDesc, this.member.getName(),
@@ -0,0 +1,59 @@
/*
* 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.
* 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.expression.spel.standard;
import java.util.stream.IntStream;
import org.junit.Test;
import org.springframework.core.Ordered;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import static org.junit.Assert.assertEquals;
/**
* Tests for the {@link SpelCompiler}.
*
* @author Sam Brannen
* @since 5.1.14
*/
public class SpelCompilerTests {
@Test // gh-24357
public void expressionCompilesWhenMethodComesFromPublicInterface() {
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
SpelExpressionParser parser = new SpelExpressionParser(config);
OrderedComponent component = new OrderedComponent();
Expression expression = parser.parseExpression("order");
// Evaluate the expression multiple times to ensure that it gets compiled.
IntStream.rangeClosed(1, 5).forEach(i -> assertEquals(42, expression.getValue(component)));
}
static class OrderedComponent implements Ordered {
@Override
public int getOrder() {
return 42;
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -157,7 +157,12 @@ final class LogAdapter {
private final ExtendedLogger logger;
public Log4jLog(String name) {
this.logger = loggerContext.getLogger(name);
LoggerContext context = loggerContext;
if (context == null) {
// Circular call in early-init scenario -> static field not initialized yet
context = LogManager.getContext(Log4jLog.class.getClassLoader(), false);
}
this.logger = context.getLogger(name);
}
@Override
@@ -280,92 +285,110 @@ final class LogAdapter {
this.logger = logger;
}
@Override
public boolean isFatalEnabled() {
return isErrorEnabled();
}
@Override
public boolean isErrorEnabled() {
return this.logger.isErrorEnabled();
}
@Override
public boolean isWarnEnabled() {
return this.logger.isWarnEnabled();
}
@Override
public boolean isInfoEnabled() {
return this.logger.isInfoEnabled();
}
@Override
public boolean isDebugEnabled() {
return this.logger.isDebugEnabled();
}
@Override
public boolean isTraceEnabled() {
return this.logger.isTraceEnabled();
}
@Override
public void fatal(Object message) {
error(message);
}
@Override
public void fatal(Object message, Throwable exception) {
error(message, exception);
}
@Override
public void error(Object message) {
if (message instanceof String || this.logger.isErrorEnabled()) {
this.logger.error(String.valueOf(message));
}
}
@Override
public void error(Object message, Throwable exception) {
if (message instanceof String || this.logger.isErrorEnabled()) {
this.logger.error(String.valueOf(message), exception);
}
}
@Override
public void warn(Object message) {
if (message instanceof String || this.logger.isWarnEnabled()) {
this.logger.warn(String.valueOf(message));
}
}
@Override
public void warn(Object message, Throwable exception) {
if (message instanceof String || this.logger.isWarnEnabled()) {
this.logger.warn(String.valueOf(message), exception);
}
}
@Override
public void info(Object message) {
if (message instanceof String || this.logger.isInfoEnabled()) {
this.logger.info(String.valueOf(message));
}
}
@Override
public void info(Object message, Throwable exception) {
if (message instanceof String || this.logger.isInfoEnabled()) {
this.logger.info(String.valueOf(message), exception);
}
}
@Override
public void debug(Object message) {
if (message instanceof String || this.logger.isDebugEnabled()) {
this.logger.debug(String.valueOf(message));
}
}
@Override
public void debug(Object message, Throwable exception) {
if (message instanceof String || this.logger.isDebugEnabled()) {
this.logger.debug(String.valueOf(message), exception);
}
}
@Override
public void trace(Object message) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message));
}
}
@Override
public void trace(Object message, Throwable exception) {
if (message instanceof String || this.logger.isTraceEnabled()) {
this.logger.trace(String.valueOf(message), exception);
@@ -486,74 +509,92 @@ final class LogAdapter {
this.logger = java.util.logging.Logger.getLogger(name);
}
@Override
public boolean isFatalEnabled() {
return isErrorEnabled();
}
@Override
public boolean isErrorEnabled() {
return this.logger.isLoggable(java.util.logging.Level.SEVERE);
}
@Override
public boolean isWarnEnabled() {
return this.logger.isLoggable(java.util.logging.Level.WARNING);
}
@Override
public boolean isInfoEnabled() {
return this.logger.isLoggable(java.util.logging.Level.INFO);
}
@Override
public boolean isDebugEnabled() {
return this.logger.isLoggable(java.util.logging.Level.FINE);
}
@Override
public boolean isTraceEnabled() {
return this.logger.isLoggable(java.util.logging.Level.FINEST);
}
@Override
public void fatal(Object message) {
error(message);
}
@Override
public void fatal(Object message, Throwable exception) {
error(message, exception);
}
@Override
public void error(Object message) {
log(java.util.logging.Level.SEVERE, message, null);
}
@Override
public void error(Object message, Throwable exception) {
log(java.util.logging.Level.SEVERE, message, exception);
}
@Override
public void warn(Object message) {
log(java.util.logging.Level.WARNING, message, null);
}
@Override
public void warn(Object message, Throwable exception) {
log(java.util.logging.Level.WARNING, message, exception);
}
@Override
public void info(Object message) {
log(java.util.logging.Level.INFO, message, null);
}
@Override
public void info(Object message, Throwable exception) {
log(java.util.logging.Level.INFO, message, exception);
}
@Override
public void debug(Object message) {
log(java.util.logging.Level.FINE, message, null);
}
@Override
public void debug(Object message, Throwable exception) {
log(java.util.logging.Level.FINE, message, exception);
}
@Override
public void trace(Object message) {
log(java.util.logging.Level.FINEST, message, null);
}
@Override
public void trace(Object message, Throwable exception) {
log(java.util.logging.Level.FINEST, message, exception);
}
@@ -567,8 +608,8 @@ final class LogAdapter {
else {
rec = new LocationResolvingLogRecord(level, String.valueOf(message));
rec.setLoggerName(this.name);
rec.setResourceBundleName(logger.getResourceBundleName());
rec.setResourceBundle(logger.getResourceBundle());
rec.setResourceBundleName(this.logger.getResourceBundleName());
rec.setResourceBundle(this.logger.getResourceBundle());
rec.setThrown(exception);
}
logger.log(rec);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -42,8 +42,9 @@ public final class CallMetaDataProviderFactory {
public static final List<String> supportedDatabaseProductsForProcedures = Arrays.asList(
"Apache Derby",
"DB2",
"MySQL",
"Informix Dynamic Server",
"Microsoft SQL Server",
"MySQL",
"Oracle",
"PostgreSQL",
"Sybase"
@@ -51,8 +52,8 @@ public final class CallMetaDataProviderFactory {
/** List of supported database products for function calls. */
public static final List<String> supportedDatabaseProductsForFunctions = Arrays.asList(
"MySQL",
"Microsoft SQL Server",
"MySQL",
"Oracle",
"PostgreSQL"
);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -57,9 +57,12 @@ abstract class AbstractEmbeddedDatabaseConfigurer implements EmbeddedDatabaseCon
try {
con.close();
}
catch (Throwable ex) {
catch (SQLException ex) {
logger.debug("Could not close JDBC Connection on shutdown", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing JDBC Connection", ex);
}
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -52,10 +52,10 @@ public abstract class DatabasePopulatorUtils {
DataSourceUtils.releaseConnection(connection, dataSource);
}
}
catch (ScriptException ex) {
throw ex;
}
catch (Throwable ex) {
if (ex instanceof ScriptException) {
throw (ScriptException) ex;
}
throw new UncategorizedScriptException("Failed to execute database script", ex);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -505,7 +505,7 @@ public class SingleConnectionFactory implements ConnectionFactory, QueueConnecti
logger.debug("Ignoring Connection state exception - assuming already closed: " + ex);
}
catch (Throwable ex) {
logger.debug("Could not close shared JMS Connection", ex);
logger.warn("Could not close shared JMS Connection", ex);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 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.
@@ -41,6 +41,6 @@ public enum SimpMessageType {
DISCONNECT_ACK,
OTHER;
OTHER
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -170,11 +170,8 @@ public abstract class SessionFactoryUtils {
try {
session.close();
}
catch (HibernateException ex) {
logger.debug("Could not close Hibernate Session", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing Hibernate Session", ex);
logger.error("Failed to release Hibernate Session", ex);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -427,11 +427,8 @@ public abstract class EntityManagerFactoryUtils {
em.close();
}
}
catch (PersistenceException ex) {
logger.debug("Could not close JPA EntityManager", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing JPA EntityManager", ex);
logger.error("Failed to release JPA EntityManager", ex);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -603,9 +603,9 @@ public class JpaTransactionManager extends AbstractPlatformTransactionManager
getJpaDialect().releaseJdbcConnection(conHandle,
txObject.getEntityManagerHolder().getEntityManager());
}
catch (Exception ex) {
catch (Throwable ex) {
// Just log it, to keep a transaction-related exception.
logger.error("Could not close JDBC connection after transaction", ex);
logger.error("Failed to release JDBC connection after transaction", ex);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -18,5 +18,5 @@ package org.springframework.orm.jpa.hibernate.beans;
public enum BeanSource {
SPRING,
FALLBACK;
FALLBACK
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 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.
@@ -35,6 +35,7 @@ import org.springframework.transaction.interceptor.TransactionInterceptor;
* @see TransactionManagementConfigurationSelector
*/
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -18,11 +18,13 @@ package org.springframework.http.codec.json;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectReader;
@@ -113,17 +115,46 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
public Flux<Object> decode(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
Flux.from(input), this.jsonFactory, getObjectMapper(), true, getMaxInMemorySize());
boolean forceUseOfBigDecimal = getObjectMapper().isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
if (BigDecimal.class.equals(elementType.getType())) {
forceUseOfBigDecimal = true;
}
Flux<DataBuffer> processed = processInput(input, elementType, mimeType, hints);
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(processed, this.jsonFactory, getObjectMapper(),
true, forceUseOfBigDecimal, getMaxInMemorySize());
return decodeInternal(tokens, elementType, mimeType, hints);
}
/**
* Process the input publisher into a flux. Default implementation returns
* {@link Flux#from(Publisher)}, but subclasses can choose to to customize
* this behaviour.
* @param input the {@code DataBuffer} input stream to process
* @param elementType the expected type of elements in the output stream
* @param mimeType the MIME type associated with the input stream (optional)
* @param hints additional information about how to do encode
* @return the processed flux
* @since 5.1.14
*/
protected Flux<DataBuffer> processInput(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
return Flux.from(input);
}
@Override
public Mono<Object> decodeToMono(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
Flux.from(input), this.jsonFactory, getObjectMapper(), false, getMaxInMemorySize());
boolean forceUseOfBigDecimal = getObjectMapper().isEnabled(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
if (BigDecimal.class.equals(elementType.getType())) {
forceUseOfBigDecimal = true;
}
Flux<DataBuffer> processed = processInput(input, elementType, mimeType, hints);
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(processed, this.jsonFactory, getObjectMapper(),
false, forceUseOfBigDecimal, getMaxInMemorySize());
return decodeInternal(tokens, elementType, mimeType, hints).singleOrEmpty();
}
@@ -166,7 +197,7 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
}
// HttpMessageDecoder...
// HttpMessageDecoder
@Override
public Map<String, Object> getDecodeHints(ResolvableType actualType, ResolvableType elementType,
@@ -180,7 +211,8 @@ public abstract class AbstractJackson2Decoder extends Jackson2CodecSupport imple
return getMimeTypes();
}
// Jackson2CodecSupport ...
// Jackson2CodecSupport
@Override
protected <A extends Annotation> A getAnnotation(MethodParameter parameter, Class<A> annotType) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -17,7 +17,6 @@
package org.springframework.http.codec.json;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.annotation.Annotation;
import java.nio.charset.Charset;
import java.util.ArrayList;
@@ -29,9 +28,11 @@ import java.util.Map;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.util.ByteArrayBuilder;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import com.fasterxml.jackson.databind.SequenceWriter;
import com.fasterxml.jackson.databind.exc.InvalidDefinitionException;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
@@ -44,7 +45,6 @@ import org.springframework.core.codec.EncodingException;
import org.springframework.core.codec.Hints;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.log.LogFormatUtils;
import org.springframework.http.MediaType;
import org.springframework.http.codec.HttpMessageEncoder;
@@ -115,65 +115,53 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
Assert.notNull(bufferFactory, "'bufferFactory' must not be null");
Assert.notNull(elementType, "'elementType' must not be null");
JsonEncoding encoding = getJsonEncoding(mimeType);
if (inputStream instanceof Mono) {
return Mono.from(inputStream).map(value ->
encodeValue(value, mimeType, bufferFactory, elementType, hints, encoding)).flux();
return Mono.from(inputStream)
.map(value -> encodeValue(value, bufferFactory, elementType, mimeType, hints))
.flux();
}
else {
return this.streamingMediaTypes.stream()
.filter(mediaType -> mediaType.isCompatibleWith(mimeType))
.findFirst()
.map(mediaType -> {
byte[] separator = STREAM_SEPARATORS.getOrDefault(mediaType, NEWLINE_SEPARATOR);
return Flux.from(inputStream).map(value -> {
DataBuffer buffer = encodeValue(
value, mimeType, bufferFactory, elementType, hints, encoding);
if (separator != null) {
buffer.write(separator);
}
return buffer;
});
})
.orElseGet(() -> {
ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
return Flux.from(inputStream).collectList().map(list ->
encodeValue(list, mimeType, bufferFactory, listType, hints, encoding)).flux();
});
byte[] separator = streamSeparator(mimeType);
if (separator != null) { // streaming
try {
ObjectWriter writer = createObjectWriter(elementType, mimeType, hints);
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler());
JsonEncoding encoding = getJsonEncoding(mimeType);
JsonGenerator generator = getObjectMapper().getFactory().createGenerator(byteBuilder, encoding);
SequenceWriter sequenceWriter = writer.writeValues(generator);
return Flux.from(inputStream)
.map(value -> encodeStreamingValue(value, bufferFactory, hints, sequenceWriter, byteBuilder,
separator));
}
catch (IOException ex) {
return Flux.error(ex);
}
}
else { // non-streaming
ResolvableType listType = ResolvableType.forClassWithGenerics(List.class, elementType);
return Flux.from(inputStream)
.collectList()
.map(list -> encodeValue(list, bufferFactory, listType, mimeType, hints))
.flux();
}
}
}
private DataBuffer encodeValue(Object value, @Nullable MimeType mimeType, DataBufferFactory bufferFactory,
ResolvableType elementType, @Nullable Map<String, Object> hints, JsonEncoding encoding) {
public DataBuffer encodeValue(Object value, DataBufferFactory bufferFactory,
ResolvableType valueType, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
if (!Hints.isLoggingSuppressed(hints)) {
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(value, !traceOn);
return Hints.getLogPrefix(hints) + "Encoding [" + formatted + "]";
});
}
ObjectWriter writer = createObjectWriter(valueType, mimeType, hints);
ByteArrayBuilder byteBuilder = new ByteArrayBuilder(writer.getFactory()._getBufferRecycler());
JsonEncoding encoding = getJsonEncoding(mimeType);
JavaType javaType = getJavaType(elementType.getType(), null);
Class<?> jsonView = (hints != null ? (Class<?>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
ObjectWriter writer = (jsonView != null ?
getObjectMapper().writerWithView(jsonView) : getObjectMapper().writer());
if (javaType.isContainerType()) {
writer = writer.forType(javaType);
}
writer = customizeWriter(writer, mimeType, elementType, hints);
DataBuffer buffer = bufferFactory.allocateBuffer();
boolean release = true;
OutputStream outputStream = buffer.asOutputStream();
logValue(hints, value);
try {
JsonGenerator generator = getObjectMapper().getFactory().createGenerator(outputStream, encoding);
JsonGenerator generator = getObjectMapper().getFactory().createGenerator(byteBuilder, encoding);
writer.writeValue(generator, value);
generator.flush();
release = false;
}
catch (InvalidDefinitionException ex) {
throw new CodecException("Type definition error: " + ex.getType(), ex);
@@ -182,24 +170,96 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
throw new EncodingException("JSON encoding error: " + ex.getOriginalMessage(), ex);
}
catch (IOException ex) {
throw new IllegalStateException("Unexpected I/O error while writing to data buffer",
ex);
}
finally {
if (release) {
DataBufferUtils.release(buffer);
}
throw new IllegalStateException("Unexpected I/O error while writing to byte array builder", ex);
}
byte[] bytes = byteBuilder.toByteArray();
DataBuffer buffer = bufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return buffer;
}
private DataBuffer encodeStreamingValue(Object value, DataBufferFactory bufferFactory, @Nullable Map<String, Object> hints,
SequenceWriter sequenceWriter, ByteArrayBuilder byteArrayBuilder, byte[] separator) {
logValue(hints, value);
try {
sequenceWriter.write(value);
sequenceWriter.flush();
}
catch (InvalidDefinitionException ex) {
throw new CodecException("Type definition error: " + ex.getType(), ex);
}
catch (JsonProcessingException ex) {
throw new EncodingException("JSON encoding error: " + ex.getOriginalMessage(), ex);
}
catch (IOException ex) {
throw new IllegalStateException("Unexpected I/O error while writing to byte array builder", ex);
}
byte[] bytes = byteArrayBuilder.toByteArray();
byteArrayBuilder.reset();
int offset;
int length;
if (bytes.length > 0 && bytes[0] == ' ') {
// SequenceWriter writes an unnecessary space in between values
offset = 1;
length = bytes.length - 1;
}
else {
offset = 0;
length = bytes.length;
}
DataBuffer buffer = bufferFactory.allocateBuffer(length + separator.length);
buffer.write(bytes, offset, length);
buffer.write(separator);
return buffer;
}
private void logValue(@Nullable Map<String, Object> hints, Object value) {
if (!Hints.isLoggingSuppressed(hints)) {
LogFormatUtils.traceDebug(logger, traceOn -> {
String formatted = LogFormatUtils.formatValue(value, !traceOn);
return Hints.getLogPrefix(hints) + "Encoding [" + formatted + "]";
});
}
}
private ObjectWriter createObjectWriter(ResolvableType valueType, @Nullable MimeType mimeType,
@Nullable Map<String, Object> hints) {
JavaType javaType = getJavaType(valueType.getType(), null);
Class<?> jsonView = (hints != null ? (Class<?>) hints.get(Jackson2CodecSupport.JSON_VIEW_HINT) : null);
ObjectWriter writer = (jsonView != null ?
getObjectMapper().writerWithView(jsonView) : getObjectMapper().writer());
if (javaType.isContainerType()) {
writer = writer.forType(javaType);
}
return customizeWriter(writer, mimeType, valueType, hints);
}
protected ObjectWriter customizeWriter(ObjectWriter writer, @Nullable MimeType mimeType,
ResolvableType elementType, @Nullable Map<String, Object> hints) {
return writer;
}
@Nullable
private byte[] streamSeparator(@Nullable MimeType mimeType) {
for (MediaType streamingMediaType : this.streamingMediaTypes) {
if (streamingMediaType.isCompatibleWith(mimeType)) {
return STREAM_SEPARATORS.getOrDefault(streamingMediaType, NEWLINE_SEPARATOR);
}
}
return null;
}
/**
* Determine the JSON encoding to use for the given mime type.
* @param mimeType the mime type as requested by the caller
@@ -219,7 +279,7 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
}
// HttpMessageEncoder...
// HttpMessageEncoder
@Override
public List<MimeType> getEncodableMimeTypes() {
@@ -238,7 +298,8 @@ public abstract class AbstractJackson2Encoder extends Jackson2CodecSupport imple
return (actualType != null ? getHints(actualType) : Hints.none());
}
// Jackson2CodecSupport ...
// Jackson2CodecSupport
@Override
protected <A extends Annotation> A getAnnotation(MethodParameter parameter, Class<A> annotType) {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -16,10 +16,24 @@
package org.springframework.http.codec.json;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.StringDecoder;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.lang.Nullable;
import org.springframework.util.MimeType;
import org.springframework.util.MimeTypeUtils;
/**
* Decode a byte stream into JSON and convert to Object's with Jackson 2.9,
@@ -32,6 +46,11 @@ import org.springframework.util.MimeType;
*/
public class Jackson2JsonDecoder extends AbstractJackson2Decoder {
private static final StringDecoder STRING_DECODER = StringDecoder.textPlainOnly(Arrays.asList(",", "\n"), false);
private static final ResolvableType STRING_TYPE = ResolvableType.forClass(String.class);
public Jackson2JsonDecoder() {
super(Jackson2ObjectMapperBuilder.json().build());
}
@@ -40,4 +59,28 @@ public class Jackson2JsonDecoder extends AbstractJackson2Decoder {
super(mapper, mimeTypes);
}
@Override
protected Flux<DataBuffer> processInput(Publisher<DataBuffer> input, ResolvableType elementType,
@Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
Flux<DataBuffer> flux = Flux.from(input);
if (mimeType == null) {
return flux;
}
// Jackson asynchronous parser only supports UTF-8
Charset charset = mimeType.getCharset();
if (charset == null || StandardCharsets.UTF_8.equals(charset) || StandardCharsets.US_ASCII.equals(charset)) {
return flux;
}
// Potentially, the memory consumption of this conversion could be improved by using CharBuffers instead
// of allocating Strings, but that would require refactoring the buffer tokenization code from StringDecoder
MimeType textMimeType = new MimeType(MimeTypeUtils.TEXT_PLAIN, charset);
Flux<String> decoded = STRING_DECODER.decode(input, STRING_TYPE, textMimeType, null);
DataBufferFactory factory = new DefaultDataBufferFactory();
return decoded.map(s -> factory.wrap(s.getBytes(StandardCharsets.UTF_8)));
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -55,16 +55,18 @@ final class Jackson2Tokenizer {
private final boolean tokenizeArrayElements;
private TokenBuffer tokenBuffer;
private final boolean forceUseOfBigDecimal;
private final int maxInMemorySize;
private int objectDepth;
private int arrayDepth;
private final int maxInMemorySize;
private int byteCount;
private TokenBuffer tokenBuffer;
// TODO: change to ByteBufferFeeder when supported by Jackson
// See https://github.com/FasterXML/jackson-core/issues/478
@@ -72,17 +74,19 @@ final class Jackson2Tokenizer {
private Jackson2Tokenizer(JsonParser parser, DeserializationContext deserializationContext,
boolean tokenizeArrayElements, int maxInMemorySize) {
boolean tokenizeArrayElements, boolean forceUseOfBigDecimal, int maxInMemorySize) {
this.parser = parser;
this.deserializationContext = deserializationContext;
this.tokenizeArrayElements = tokenizeArrayElements;
this.tokenBuffer = new TokenBuffer(parser, deserializationContext);
this.forceUseOfBigDecimal = forceUseOfBigDecimal;
this.inputFeeder = (ByteArrayFeeder) this.parser.getNonBlockingInputFeeder();
this.maxInMemorySize = maxInMemorySize;
this.tokenBuffer = createToken();
}
private Flux<TokenBuffer> tokenize(DataBuffer dataBuffer) {
int bufferSize = dataBuffer.readableByteCount();
byte[] bytes = new byte[dataBuffer.readableByteCount()];
@@ -132,6 +136,9 @@ final class Jackson2Tokenizer {
previousNull = true;
continue;
}
else {
previousNull = false;
}
updateDepth(token);
if (!this.tokenizeArrayElements) {
processTokenNormal(token, result);
@@ -165,9 +172,8 @@ final class Jackson2Tokenizer {
if ((token.isStructEnd() || token.isScalarValue()) && this.objectDepth == 0 && this.arrayDepth == 0) {
result.add(this.tokenBuffer);
this.tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
this.tokenBuffer = createToken();
}
}
private void processTokenArray(JsonToken token, List<TokenBuffer> result) throws IOException {
@@ -178,10 +184,16 @@ final class Jackson2Tokenizer {
if (this.objectDepth == 0 && (this.arrayDepth == 0 || this.arrayDepth == 1) &&
(token == JsonToken.END_OBJECT || token.isScalarValue())) {
result.add(this.tokenBuffer);
this.tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
this.tokenBuffer = createToken();
}
}
private TokenBuffer createToken() {
TokenBuffer tokenBuffer = new TokenBuffer(this.parser, this.deserializationContext);
tokenBuffer.forceUseOfBigDecimal(this.forceUseOfBigDecimal);
return tokenBuffer;
}
private boolean isTopLevelArrayToken(JsonToken token) {
return this.objectDepth == 0 && ((token == JsonToken.START_ARRAY && this.arrayDepth == 1) ||
(token == JsonToken.END_ARRAY && this.arrayDepth == 0));
@@ -217,10 +229,13 @@ final class Jackson2Tokenizer {
* @param objectMapper the current mapper instance
* @param tokenizeArrays if {@code true} and the "top level" JSON object is
* an array, each element is returned individually immediately after it is received
* @param forceUseOfBigDecimal if {@code true}, any floating point values encountered
* in source will use {@link java.math.BigDecimal}
* @param maxInMemorySize maximum memory size
* @return the resulting token buffers
*/
public static Flux<TokenBuffer> tokenize(Flux<DataBuffer> dataBuffers, JsonFactory jsonFactory,
ObjectMapper objectMapper, boolean tokenizeArrays, int maxInMemorySize) {
ObjectMapper objectMapper, boolean tokenizeArrays, boolean forceUseOfBigDecimal, int maxInMemorySize) {
try {
JsonParser parser = jsonFactory.createNonBlockingByteArrayParser();
@@ -229,7 +244,8 @@ final class Jackson2Tokenizer {
context = ((DefaultDeserializationContext) context).createInstance(
objectMapper.getDeserializationConfig(), parser, objectMapper.getInjectableValues());
}
Jackson2Tokenizer tokenizer = new Jackson2Tokenizer(parser, context, tokenizeArrays, maxInMemorySize);
Jackson2Tokenizer tokenizer =
new Jackson2Tokenizer(parser, context, tokenizeArrays, forceUseOfBigDecimal, maxInMemorySize);
return dataBuffers.flatMap(tokenizer::tokenize, Flux::error, tokenizer::endOfInput);
}
catch (IOException ex) {
@@ -216,6 +216,7 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
if (response.getContentType() == null && contentType != null) {
response.setContentType(contentType.toString());
}
getHeaders().remove(HttpHeaders.CONTENT_TYPE);
Charset charset = (contentType != null ? contentType.getCharset() : null);
if (response.getCharacterEncoding() == null && charset != null) {
response.setCharacterEncoding(charset.name());
@@ -224,6 +225,7 @@ public class TomcatHttpHandlerAdapter extends ServletHttpHandlerAdapter {
if (contentLength != -1) {
response.setContentLengthLong(contentLength);
}
getHeaders().remove(HttpHeaders.CONTENT_LENGTH);
}
@Override
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -72,13 +72,10 @@ import org.springframework.web.util.UriTemplateHandler;
* addition to the generalized {@code exchange} and {@code execute} methods that
* support of less frequent cases.
*
* <p><strong>NOTE:</strong> As of 5.0, the non-blocking, reactive
* {@code org.springframework.web.reactive.client.WebClient} offers a
* modern alternative to the {@code RestTemplate} with efficient support for
* both sync and async, as well as streaming scenarios. The {@code RestTemplate}
* will be deprecated in a future version and will not have major new features
* added going forward. See the WebClient section of the Spring Framework reference
* documentation for more details and example code.
* <p><strong>NOTE:</strong> As of 5.0 this class is in maintenance mode, with
* only minor requests for changes and bugs to be accepted going forward. Please,
* consider using the {@code org.springframework.web.reactive.client.WebClient}
* which has a more modern API and supports sync, async, and streaming scenarios.
*
* @author Arjen Poutsma
* @author Brian Clozel
@@ -92,7 +89,7 @@ import org.springframework.web.util.UriTemplateHandler;
*/
public class RestTemplate extends InterceptingHttpAccessor implements RestOperations {
private static boolean romePresent;
private static final boolean romePresent;
private static final boolean jaxb2Present;
@@ -203,6 +200,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
this.uriTemplateHandler = initUriTemplateHandler();
}
private static DefaultUriBuilderFactory initUriTemplateHandler() {
DefaultUriBuilderFactory uriFactory = new DefaultUriBuilderFactory();
uriFactory.setEncodingMode(EncodingMode.URI_COMPONENT); // for backwards compatibility..
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -17,6 +17,8 @@
package org.springframework.http.codec.json;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.List;
@@ -33,6 +35,7 @@ import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.AbstractDecoderTestCase;
import org.springframework.core.codec.CodecException;
@@ -42,13 +45,21 @@ import org.springframework.http.MediaType;
import org.springframework.http.codec.Pojo;
import org.springframework.util.MimeType;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static org.junit.Assert.*;
import static org.springframework.core.ResolvableType.*;
import static org.springframework.http.MediaType.*;
import static org.springframework.http.codec.json.Jackson2JsonDecoder.*;
import static org.springframework.http.codec.json.JacksonViewBean.*;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonMap;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.core.ResolvableType.forClass;
import static org.springframework.http.MediaType.APPLICATION_JSON;
import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8;
import static org.springframework.http.MediaType.APPLICATION_STREAM_JSON;
import static org.springframework.http.MediaType.APPLICATION_XML;
import static org.springframework.http.codec.json.Jackson2JsonDecoder.JSON_VIEW_HINT;
import static org.springframework.http.codec.json.JacksonViewBean.MyJacksonView1;
import static org.springframework.http.codec.json.JacksonViewBean.MyJacksonView3;
/**
* Unit tests for {@link Jackson2JsonDecoder}.
@@ -199,9 +210,52 @@ public class Jackson2JsonDecoderTests extends AbstractDecoderTestCase<Jackson2Js
);
}
@Test
public void bigDecimalFlux() {
Flux<DataBuffer> input = stringBuffer("[ 1E+2 ]").flux();
testDecode(input, BigDecimal.class, step -> step
.expectNext(new BigDecimal("1E+2"))
.verifyComplete()
);
}
@Test
public void decodeNonUtf8Encoding() {
Mono<DataBuffer> input = stringBuffer("{\"foo\":\"bar\"}", StandardCharsets.UTF_16);
testDecode(input, ResolvableType.forType(new ParameterizedTypeReference<Map<String, String>>() {}),
step -> step.assertNext(o -> {
Map<String, String> map = (Map<String, String>) o;
assertEquals("bar", map.get("foo"));
})
.verifyComplete(),
MediaType.parseMediaType("application/json; charset=utf-16"),
null);
}
@Test
public void decodeMonoNonUtf8Encoding() {
Mono<DataBuffer> input = stringBuffer("{\"foo\":\"bar\"}", StandardCharsets.UTF_16);
testDecodeToMono(input, ResolvableType.forType(new ParameterizedTypeReference<Map<String, String>>() {
}),
step -> step.assertNext(o -> {
Map<String, String> map = (Map<String, String>) o;
assertEquals("bar", map.get("foo"));
})
.verifyComplete(),
MediaType.parseMediaType("application/json; charset=utf-16"),
null);
}
private Mono<DataBuffer> stringBuffer(String value) {
return stringBuffer(value, StandardCharsets.UTF_8);
}
private Mono<DataBuffer> stringBuffer(String value, Charset charset) {
return Mono.defer(() -> {
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
byte[] bytes = value.getBytes(charset);
DataBuffer buffer = this.bufferFactory.allocateBuffer(bytes.length);
buffer.write(bytes);
return Mono.just(buffer);
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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.
@@ -20,23 +20,26 @@ import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import com.fasterxml.jackson.databind.MappingIterator;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import org.springframework.core.ResolvableType;
import org.springframework.core.codec.AbstractEncoderTestCase;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.support.DataBufferTestUtils;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.codec.Pojo;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.util.MimeType;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.springframework.core.io.buffer.DataBufferUtils.release;
import static org.springframework.http.MediaType.APPLICATION_XML;
@@ -59,21 +62,6 @@ public class Jackson2SmileEncoderTests extends AbstractEncoderTestCase<Jackson2S
}
public Consumer<DataBuffer> pojoConsumer(Pojo expected) {
return dataBuffer -> {
try {
Pojo actual = this.mapper.reader().forType(Pojo.class)
.readValue(DataBufferTestUtils.dumpBytes(dataBuffer));
assertEquals(expected, actual);
release(dataBuffer);
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
};
}
@Override
@Test
public void canEncode() {
@@ -106,7 +94,19 @@ public class Jackson2SmileEncoderTests extends AbstractEncoderTestCase<Jackson2S
Flux<Pojo> input = Flux.fromIterable(list);
testEncode(input, Pojo.class, step -> step
.consumeNextWith(expect(list, List.class)));
.consumeNextWith(dataBuffer -> {
try {
Object actual = this.mapper.reader().forType(List.class)
.readValue(dataBuffer.asInputStream());
assertEquals(list, actual);
}
catch (IOException e) {
throw new UncheckedIOException(e);
}
finally {
release(dataBuffer);
}
}));
}
@Test
@@ -127,32 +127,30 @@ public class Jackson2SmileEncoderTests extends AbstractEncoderTestCase<Jackson2S
Flux<Pojo> input = Flux.just(pojo1, pojo2, pojo3);
ResolvableType type = ResolvableType.forClass(Pojo.class);
testEncodeAll(input, type, step -> step
.consumeNextWith(expect(pojo1, Pojo.class))
.consumeNextWith(expect(pojo2, Pojo.class))
.consumeNextWith(expect(pojo3, Pojo.class))
.verifyComplete(),
STREAM_SMILE_MIME_TYPE, null);
Flux<DataBuffer> result = this.encoder
.encode(input, bufferFactory, type, STREAM_SMILE_MIME_TYPE, null);
Mono<MappingIterator<Pojo>> joined = DataBufferUtils.join(result)
.map(buffer -> {
try {
return this.mapper.reader().forType(Pojo.class).readValues(buffer.asInputStream(true));
}
catch (IOException ex) {
throw new UncheckedIOException(ex);
}
});
StepVerifier.create(joined)
.assertNext(iter -> {
assertTrue(iter.hasNext());
assertEquals(pojo1, iter.next());
assertTrue(iter.hasNext());
assertEquals(pojo2, iter.next());
assertTrue(iter.hasNext());
assertEquals(pojo3, iter.next());
assertFalse(iter.hasNext());
})
.verifyComplete();
}
private <T> Consumer<DataBuffer> expect(T expected, Class<T> expectedType) {
return dataBuffer -> {
try {
Object actual = this.mapper.reader().forType(expectedType)
.readValue(dataBuffer.asInputStream());
assertEquals(expected, actual);
}
catch (IOException e) {
throw new UncheckedIOException(e);
}
finally {
release(dataBuffer);
}
};
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 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.
@@ -19,9 +19,12 @@ package org.springframework.http.codec.json;
import java.io.IOException;
import java.io.UncheckedIOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.util.TokenBuffer;
@@ -37,8 +40,10 @@ import org.springframework.core.io.buffer.AbstractLeakCheckingTestCase;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferLimitException;
import static java.util.Arrays.*;
import static java.util.Collections.*;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
/**
* @author Arjen Poutsma
@@ -242,7 +247,8 @@ public class Jackson2TokenizerTests extends AbstractLeakCheckingTestCase {
public void errorInStream() {
DataBuffer buffer = stringBuffer("{\"id\":1,\"name\":");
Flux<DataBuffer> source = Flux.just(buffer).concatWith(Flux.error(new RuntimeException()));
Flux<TokenBuffer> result = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, true, -1);
Flux<TokenBuffer> result = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, true,
false, -1);
StepVerifier.create(result)
.expectError(RuntimeException.class)
@@ -252,19 +258,49 @@ public class Jackson2TokenizerTests extends AbstractLeakCheckingTestCase {
@Test // SPR-16521
public void jsonEOFExceptionIsWrappedAsDecodingError() {
Flux<DataBuffer> source = Flux.just(stringBuffer("{\"status\": \"noClosingQuote}"));
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, false, -1);
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, false,
false, -1);
StepVerifier.create(tokens)
.expectError(DecodingException.class)
.verify();
}
@Test
public void useBigDecimalForFloats() {
for (boolean useBigDecimalForFloats : Arrays.asList(false, true)) {
Flux<DataBuffer> source = Flux.just(stringBuffer("1E+2"));
Flux<TokenBuffer> tokens =
Jackson2Tokenizer.tokenize(source, this.jsonFactory, this.objectMapper, false,
useBigDecimalForFloats, -1);
StepVerifier.create(tokens)
.assertNext(tokenBuffer -> {
try {
JsonParser parser = tokenBuffer.asParser();
JsonToken token = parser.nextToken();
assertEquals(JsonToken.VALUE_NUMBER_FLOAT, token);
JsonParser.NumberType numberType = parser.getNumberType();
if (useBigDecimalForFloats) {
assertEquals(JsonParser.NumberType.BIG_DECIMAL, numberType);
}
else {
assertEquals(JsonParser.NumberType.DOUBLE, numberType);
}
}
catch (IOException ex) {
fail(ex.getMessage());
}
})
.verifyComplete();
}
}
private Flux<String> decode(List<String> source, boolean tokenize, int maxInMemorySize) {
Flux<TokenBuffer> tokens = Jackson2Tokenizer.tokenize(
Flux.fromIterable(source).map(this::stringBuffer),
this.jsonFactory, this.objectMapper, tokenize, maxInMemorySize);
this.jsonFactory, this.objectMapper, tokenize, false, maxInMemorySize);
return tokens
.map(tokenBuffer -> {
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 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,9 +25,9 @@ import org.springframework.util.ObjectUtils;
/**
* Representation of a WebSocket message.
* <p>See static factory methods in {@link WebSocketSession} for creating messages
* with the {@link org.springframework.core.io.buffer.DataBufferFactory
* DataBufferFactory} for the session.
*
* <p>See static factory methods in {@link WebSocketSession} for creating messages with
* the {@link org.springframework.core.io.buffer.DataBufferFactory} for the session.
*
* @author Rossen Stoyanchev
* @since 5.0
@@ -139,6 +139,7 @@ public class WebSocketMessage {
return "WebSocket " + this.type.name() + " message (" + this.payload.readableByteCount() + " bytes)";
}
/**
* WebSocket message types.
*/
@@ -158,7 +159,7 @@ public class WebSocketMessage {
/**
* WebSocket pong.
*/
PONG;
PONG
}
}
+4 -5
View File
@@ -1618,11 +1618,10 @@ the Spring Validation API, as the following example shows:
==== Configuring Custom Constraints
Each bean validation constraint consists of two parts:
* A `@Constraint` annotation
that declares the constraint and its configurable properties.
* An implementation
of the `javax.validation.ConstraintValidator` interface that implements the constraint's
behavior.
* A `@Constraint` annotation that declares the constraint and its configurable properties.
* An implementation of the `javax.validation.ConstraintValidator` interface that implements
the constraint's behavior.
To associate a declaration with an implementation, each `@Constraint` annotation
references a corresponding `ConstraintValidator` implementation class. At runtime, a
+4 -4
View File
@@ -973,10 +973,10 @@ method API.
* <<web-reactive.adoc#webflux-client, WebClient>>: a non-blocking, reactive alternative
that supports both synchronous and asynchronous as well as streaming scenarios.
NOTE: As of 5.0, the non-blocking, reactive `WebClient` offers a modern alternative to the
`RestTemplate` with efficient support for both synchronous and asynchronous as well as streaming
scenarios. The `RestTemplate` will be deprecated in a future version and will not have
major new features added going forward.
NOTE: As of 5.0 the `RestTemplate` is in maintenance mode, with only minor requests for
changes and bugs to be accepted going forward. Please, consider using the
<<web-reactive.adoc#webflux-client, WebClient>> which offers a more modern API and
supports sync, async, and streaming scenarios.
[[rest-resttemplate]]
+1 -1
View File
@@ -3102,7 +3102,7 @@ use `FormatterRegistrar` implementations.
By default, if <<core.adoc#validation-beanvalidation-overview, Bean Validation>> is present
on the classpath (for example, the Hibernate Validator), the `LocalValidatorFactoryBean`
is registered as a global <<core.adoc#validator,validator>> for use with `@Valid` and
`Validated` on `@Controller` method arguments.
`@Validated` on `@Controller` method arguments.
In your Java configuration, you can customize the global `Validator` instance,
as the following example shows:
+4 -6
View File
@@ -13,12 +13,10 @@ This section describes options for client-side access to REST endpoints.
Spring REST client and exposes a simple, template-method API over underlying HTTP client
libraries.
NOTE: As of 5.0, the non-blocking, reactive `WebClient` offers a modern alternative to the
`RestTemplate`, with efficient support for both
<<web-reactive.adoc#webflux-client-synchronous, synchronous and asynchronous>>, as well as
streaming scenarios. The `RestTemplate` will be deprecated in a future version and will
not have major new features added going forward.
NOTE: As of 5.0 the `RestTemplate` is in maintenance mode, with only minor requests for
changes and bugs to be accepted going forward. Please, consider using the
<<web-reactive.adoc#webflux-client, WebClient>> which offers a more modern API and
supports sync, async, and streaming scenarios.
See <<integration.adoc#rest-client-access, REST Endpoints>> for details.
+1 -1
View File
@@ -4251,7 +4251,7 @@ FormatterRegistrar implementations.
By default, if <<core.adoc#validation-beanvalidation-overview, Bean Validation>> is present
on the classpath (for example, Hibernate Validator), the `LocalValidatorFactoryBean` is
registered as a global <<core.adoc#validator, Validator>> for use with `@Valid` and
`Validated` on controller method arguments.
`@Validated` on `@Controller` method arguments.
In Java configuration, you can customize the global `Validator` instance, as the
following example shows:
+7
View File
@@ -261,6 +261,13 @@ Per the LICENSE file in the Objenesis ZIP distribution downloaded from
http://objenesis.org/download.html, Objenesis 3.0.1 is licensed under the
Apache License, version 2.0, the text of which is included above.
Per the NOTICE file in the Objenesis ZIP distribution downloaded from
http://objenesis.org/download.html and corresponding to section 4d of the
Apache License, Version 2.0, in this case for Objenesis:
Objenesis
Copyright 2006-2018 Joe Walnes, Henri Tremblay, Leonardo Mesquita
===============================================================================