mirror of
https://github.com/spring-projects/spring-framework
synced 2026-06-08 17:33:33 +00:00
Merge branch '7.0.x'
# Conflicts: # framework-platform/framework-platform.gradle
This commit is contained in:
@@ -50,7 +50,7 @@ public class CheckstyleConventions {
|
||||
project.getPlugins().apply(CheckstylePlugin.class);
|
||||
project.getTasks().withType(Checkstyle.class).forEach(checkstyle -> checkstyle.getMaxHeapSize().set("1g"));
|
||||
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
|
||||
checkstyle.setToolVersion("13.3.0");
|
||||
checkstyle.setToolVersion("13.4.0");
|
||||
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
|
||||
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
|
||||
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
|
||||
|
||||
@@ -12,15 +12,15 @@ dependencies {
|
||||
api(platform("io.netty:netty-bom:4.2.12.Final"))
|
||||
api(platform("io.projectreactor:reactor-bom:2025.0.4"))
|
||||
api(platform("io.rsocket:rsocket-bom:1.1.5"))
|
||||
api(platform("org.apache.groovy:groovy-bom:5.0.4"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.25.3"))
|
||||
api(platform("org.apache.groovy:groovy-bom:5.0.5"))
|
||||
api(platform("org.apache.logging.log4j:log4j-bom:2.25.4"))
|
||||
api(platform("org.assertj:assertj-bom:3.27.7"))
|
||||
api(platform("org.eclipse.jetty:jetty-bom:12.1.7"))
|
||||
api(platform("org.eclipse.jetty.ee11:jetty-ee11-bom:12.1.7"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.10.2"))
|
||||
api(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.9.0"))
|
||||
api(platform("org.junit:junit-bom:6.0.3"))
|
||||
api(platform("org.mockito:mockito-bom:5.22.0"))
|
||||
api(platform("org.mockito:mockito-bom:5.23.0"))
|
||||
api(platform("tools.jackson:jackson-bom:3.1.1"))
|
||||
|
||||
constraints {
|
||||
|
||||
+35
-7
@@ -16,6 +16,10 @@
|
||||
|
||||
package org.springframework.validation;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.beans.PropertyAccessException;
|
||||
import org.springframework.context.support.DefaultMessageSourceResolvable;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -59,9 +63,8 @@ public class DefaultBindingErrorProcessor implements BindingErrorProcessor {
|
||||
String fixedField = bindingResult.getNestedPath() + missingField;
|
||||
String[] codes = bindingResult.resolveMessageCodes(MISSING_FIELD_ERROR_CODE, missingField);
|
||||
Object[] arguments = getArgumentsForBindError(bindingResult.getObjectName(), fixedField);
|
||||
FieldError error = new FieldError(bindingResult.getObjectName(), fixedField, "", true,
|
||||
codes, arguments, "Field '" + fixedField + "' is required");
|
||||
bindingResult.addError(error);
|
||||
bindingResult.addError(new BindingFieldError(
|
||||
bindingResult.getObjectName(), fixedField, "", codes, arguments));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,10 +78,8 @@ public class DefaultBindingErrorProcessor implements BindingErrorProcessor {
|
||||
if (ObjectUtils.isArray(rejectedValue)) {
|
||||
rejectedValue = StringUtils.arrayToCommaDelimitedString(ObjectUtils.toObjectArray(rejectedValue));
|
||||
}
|
||||
FieldError error = new FieldError(bindingResult.getObjectName(), field, rejectedValue, true,
|
||||
codes, arguments, ex.getLocalizedMessage());
|
||||
error.wrap(ex);
|
||||
bindingResult.addError(error);
|
||||
bindingResult.addError(new BindingFieldError(
|
||||
bindingResult.getObjectName(), field, rejectedValue, codes, arguments, ex));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,4 +98,31 @@ public class DefaultBindingErrorProcessor implements BindingErrorProcessor {
|
||||
return new Object[] {new DefaultMessageSourceResolvable(codes, field)};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Subclass of {@code FieldError} with Spring-style default message rendering.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class BindingFieldError extends FieldError implements Serializable {
|
||||
|
||||
public BindingFieldError(String objectName, String field, @Nullable Object rejectedValue, String[] codes,
|
||||
Object[] arguments) {
|
||||
|
||||
super(objectName, field, rejectedValue, true, codes, arguments,
|
||||
"Field '" + field + "' is required");
|
||||
}
|
||||
|
||||
public BindingFieldError(String objectName, String field, @Nullable Object rejectedValue, String[] codes,
|
||||
Object[] arguments, PropertyAccessException ex) {
|
||||
|
||||
super(objectName, field, rejectedValue, true, codes, arguments, ex.getLocalizedMessage());
|
||||
wrap(ex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
-1
@@ -533,7 +533,7 @@ public class MethodValidationAdapter implements MethodValidator {
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static class ViolationMessageSourceResolvable extends DefaultMessageSourceResolvable {
|
||||
private class ViolationMessageSourceResolvable extends DefaultMessageSourceResolvable {
|
||||
|
||||
private final transient ConstraintViolation<Object> violation;
|
||||
|
||||
@@ -547,6 +547,11 @@ public class MethodValidationAdapter implements MethodValidator {
|
||||
public ConstraintViolation<Object> getViolation() {
|
||||
return this.violation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldRenderDefaultMessage() {
|
||||
return validatorAdapter.get().requiresMessageFormat(this.violation);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+22
-10
@@ -18,6 +18,7 @@ package org.springframework.validation.beanvalidation;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -64,7 +65,7 @@ import org.springframework.validation.SmartValidator;
|
||||
*/
|
||||
public class SpringValidatorAdapter implements SmartValidator, jakarta.validation.Validator {
|
||||
|
||||
private static final Set<String> internalAnnotationAttributes = Set.of("message", "groups", "payload");
|
||||
private static final Set<String> INTERNAL_ANNOTATION_ATTRIBUTES = Set.of("message", "groups", "payload");
|
||||
|
||||
|
||||
private jakarta.validation.@Nullable Validator targetValidator;
|
||||
@@ -144,10 +145,21 @@ public class SpringValidatorAdapter implements SmartValidator, jakarta.validatio
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
protected void processConstraintViolations(Set<ConstraintViolation<Object>> violations, Errors errors) {
|
||||
// Pre-identify binding failures (using getAllErrors to avoid intermediate list)
|
||||
Set<String> failedFields = new HashSet<>();
|
||||
if (errors.hasErrors()) {
|
||||
for (ObjectError error : errors.getAllErrors()) {
|
||||
if (error instanceof FieldError fieldError && fieldError.isBindingFailure()) {
|
||||
failedFields.add(fieldError.getField());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Turn the ConstraintViolations into Object/FieldErrors,
|
||||
// but only for fields where binding has been successful.
|
||||
for (ConstraintViolation<Object> violation : violations) {
|
||||
String field = determineField(violation);
|
||||
FieldError fieldError = errors.getFieldError(field);
|
||||
if (fieldError == null || !fieldError.isBindingFailure()) {
|
||||
if (!failedFields.contains(field)) {
|
||||
try {
|
||||
ConstraintDescriptor<?> cd = violation.getConstraintDescriptor();
|
||||
String errorCode = determineErrorCode(cd);
|
||||
@@ -158,14 +170,14 @@ public class SpringValidatorAdapter implements SmartValidator, jakarta.validatio
|
||||
String nestedField = bindingResult.getNestedPath() + field;
|
||||
if (nestedField.isEmpty()) {
|
||||
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode);
|
||||
ObjectError error = new ViolationObjectError(
|
||||
ObjectError error = new ValidationObjectError(
|
||||
errors.getObjectName(), errorCodes, errorArgs, violation, this);
|
||||
bindingResult.addError(error);
|
||||
}
|
||||
else {
|
||||
Object rejectedValue = getRejectedValue(field, violation, bindingResult);
|
||||
String[] errorCodes = bindingResult.resolveMessageCodes(errorCode, field);
|
||||
FieldError error = new ViolationFieldError(errors.getObjectName(), nestedField,
|
||||
FieldError error = new ValidationFieldError(errors.getObjectName(), nestedField,
|
||||
rejectedValue, errorCodes, errorArgs, violation, this);
|
||||
bindingResult.addError(error);
|
||||
}
|
||||
@@ -260,7 +272,7 @@ public class SpringValidatorAdapter implements SmartValidator, jakarta.validatio
|
||||
// Using a TreeMap for alphabetical ordering of attribute names
|
||||
Map<String, Object> attributesToExpose = new TreeMap<>();
|
||||
descriptor.getAttributes().forEach((attributeName, attributeValue) -> {
|
||||
if (!internalAnnotationAttributes.contains(attributeName)) {
|
||||
if (!INTERNAL_ANNOTATION_ATTRIBUTES.contains(attributeName)) {
|
||||
if (attributeValue instanceof String str) {
|
||||
attributeValue = new ResolvableAttribute(str);
|
||||
}
|
||||
@@ -439,13 +451,13 @@ public class SpringValidatorAdapter implements SmartValidator, jakarta.validatio
|
||||
* Subclass of {@code ObjectError} with Spring-style default message rendering.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class ViolationObjectError extends ObjectError implements Serializable {
|
||||
private static class ValidationObjectError extends ObjectError implements Serializable {
|
||||
|
||||
private @Nullable transient SpringValidatorAdapter adapter;
|
||||
|
||||
private @Nullable transient ConstraintViolation<?> violation;
|
||||
|
||||
public ViolationObjectError(String objectName, String[] codes, Object[] arguments,
|
||||
public ValidationObjectError(String objectName, String[] codes, Object[] arguments,
|
||||
ConstraintViolation<?> violation, SpringValidatorAdapter adapter) {
|
||||
|
||||
super(objectName, codes, arguments, violation.getMessage());
|
||||
@@ -467,13 +479,13 @@ public class SpringValidatorAdapter implements SmartValidator, jakarta.validatio
|
||||
* Subclass of {@code FieldError} with Spring-style default message rendering.
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static class ViolationFieldError extends FieldError implements Serializable {
|
||||
private static class ValidationFieldError extends FieldError implements Serializable {
|
||||
|
||||
private @Nullable transient SpringValidatorAdapter adapter;
|
||||
|
||||
private @Nullable transient ConstraintViolation<?> violation;
|
||||
|
||||
public ViolationFieldError(String objectName, String field, @Nullable Object rejectedValue, String[] codes,
|
||||
public ValidationFieldError(String objectName, String field, @Nullable Object rejectedValue, String[] codes,
|
||||
Object[] arguments, ConstraintViolation<?> violation, SpringValidatorAdapter adapter) {
|
||||
|
||||
super(objectName, field, rejectedValue, false, codes, arguments, violation.getMessage());
|
||||
|
||||
@@ -1854,8 +1854,12 @@ class DataBinderTests {
|
||||
|
||||
FieldError ageError = errors.getFieldError("age");
|
||||
assertThat(ageError.getCode()).isEqualTo("typeMismatch");
|
||||
assertThat(ageError.isBindingFailure()).isTrue();
|
||||
assertThat(ageError.shouldRenderDefaultMessage()).isFalse();
|
||||
FieldError nameError = errors.getFieldError("name");
|
||||
assertThat(nameError.getCode()).isEqualTo("badName");
|
||||
assertThat(nameError.isBindingFailure()).isFalse();
|
||||
assertThat(nameError.shouldRenderDefaultMessage()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -32,9 +32,11 @@ import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Simple LRU (Least Recently Used) cache, bounded by a specified cache capacity.
|
||||
*
|
||||
* <p>This is a simplified, opinionated implementation of an LRU cache for internal
|
||||
* use in Spring Framework. It is inspired from
|
||||
* <a href="https://github.com/ben-manes/concurrentlinkedhashmap">ConcurrentLinkedHashMap</a>.
|
||||
*
|
||||
* <p>Read and write operations are internally recorded in dedicated buffers,
|
||||
* then drained at chosen times to avoid contention.
|
||||
*
|
||||
@@ -70,6 +72,7 @@ public final class ConcurrentLruCache<K, V> {
|
||||
|
||||
private final AtomicReference<DrainStatus> drainStatus = new AtomicReference<>(DrainStatus.IDLE);
|
||||
|
||||
|
||||
/**
|
||||
* Create a new cache instance with the given capacity and generator function.
|
||||
* @param capacity the maximum number of entries in the cache
|
||||
@@ -89,6 +92,7 @@ public final class ConcurrentLruCache<K, V> {
|
||||
this.writeOperations = new WriteOperations();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieve an entry from the cache, potentially triggering generation of the value.
|
||||
* @param key the key to retrieve the entry for
|
||||
@@ -344,10 +348,13 @@ public final class ConcurrentLruCache<K, V> {
|
||||
abstract boolean shouldDrainBuffers(boolean delayable);
|
||||
}
|
||||
|
||||
|
||||
private enum CacheEntryState {
|
||||
|
||||
ACTIVE, PENDING_REMOVAL, REMOVED
|
||||
}
|
||||
|
||||
|
||||
private record CacheEntry<V>(V value, CacheEntryState state) {
|
||||
|
||||
boolean isActive() {
|
||||
@@ -355,6 +362,7 @@ public final class ConcurrentLruCache<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class ReadOperations<K, V> {
|
||||
|
||||
private static final int BUFFER_COUNT = detectNumberOfBuffers();
|
||||
@@ -452,6 +460,7 @@ public final class ConcurrentLruCache<K, V> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class WriteOperations {
|
||||
|
||||
private static final int DRAIN_THRESHOLD = 16;
|
||||
@@ -478,11 +487,12 @@ public final class ConcurrentLruCache<K, V> {
|
||||
task.run();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static final class Node<K, V> extends AtomicReference<CacheEntry<V>> {
|
||||
|
||||
final K key;
|
||||
|
||||
@Nullable Node<K, V> prev;
|
||||
@@ -522,7 +532,6 @@ public final class ConcurrentLruCache<K, V> {
|
||||
|
||||
@Nullable Node<K, V> last;
|
||||
|
||||
|
||||
@Nullable Node<K, V> poll() {
|
||||
if (this.first == null) {
|
||||
return null;
|
||||
@@ -596,7 +605,6 @@ public final class ConcurrentLruCache<K, V> {
|
||||
unlink(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,15 +29,15 @@ import org.springframework.http.ProblemDetail;
|
||||
|
||||
/**
|
||||
* Representation of a complete RFC 9457 error response including status,
|
||||
* headers, and an RFC 9457 formatted {@link ProblemDetail} body. Allows any
|
||||
* exception to expose HTTP error response information.
|
||||
* headers, and an RFC 9457 formatted {@link ProblemDetail} body.
|
||||
* Allows any exception to expose HTTP error response information.
|
||||
*
|
||||
* <p>{@link ErrorResponseException} is a default implementation of this
|
||||
* interface and a convenient base class for other exceptions to use.
|
||||
*
|
||||
* <p>{@code ErrorResponse} is supported as a return value from
|
||||
* {@code @ExceptionHandler} methods that render directly to the response, for example,
|
||||
* by being marked {@code @ResponseBody}, or declared in an
|
||||
* {@code @ExceptionHandler} methods that render directly to the response,
|
||||
* for example, by being marked {@code @ResponseBody}, or declared in an
|
||||
* {@code @RestController} or {@code RestControllerAdvice} class.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
|
||||
@@ -200,7 +200,9 @@ public class WebDataBinder extends DataBinder {
|
||||
* @return the resolved value, or {@code null}
|
||||
* @since 6.1
|
||||
*/
|
||||
protected @Nullable Object resolvePrefixValue(String name, Class<?> type, BiFunction<String, Class<?>, Object> resolver) {
|
||||
protected @Nullable Object resolvePrefixValue(
|
||||
String name, Class<?> type, BiFunction<String, Class<?>, @Nullable Object> resolver) {
|
||||
|
||||
Object value = resolver.apply(name, type);
|
||||
if (value == null) {
|
||||
String prefix = getFieldDefaultPrefix();
|
||||
|
||||
@@ -96,7 +96,7 @@ public class HandlerMethod extends AnnotatedMethod {
|
||||
|
||||
private @Nullable HandlerMethod resolvedFromHandlerMethod;
|
||||
|
||||
private @Nullable HandlerMethod resolvedBeanHandlerMethod;
|
||||
private volatile @Nullable HandlerMethod resolvedBeanHandlerMethod;
|
||||
|
||||
private final String description;
|
||||
|
||||
@@ -233,7 +233,10 @@ public class HandlerMethod extends AnnotatedMethod {
|
||||
this.responseStatus = annotation.code();
|
||||
this.responseStatusReason = resolvedReason;
|
||||
if (StringUtils.hasText(this.responseStatusReason) && getMethod().getReturnType() != void.class) {
|
||||
logger.warn("Return value of [" + getMethod() + "] will be ignored since @ResponseStatus 'reason' attribute is set.");
|
||||
if (logger.isWarnEnabled()) {
|
||||
logger.warn("Return value of [" + getMethod() +
|
||||
"] will be ignored since @ResponseStatus 'reason' attribute is set.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -352,23 +355,22 @@ public class HandlerMethod extends AnnotatedMethod {
|
||||
* <p>If the {@link #getBean() handler} is not String, return the same instance.
|
||||
*/
|
||||
public HandlerMethod createWithResolvedBean() {
|
||||
if (this.resolvedBeanHandlerMethod != null) {
|
||||
return this.resolvedBeanHandlerMethod;
|
||||
HandlerMethod resolvedBeanHandlerMethod = this.resolvedBeanHandlerMethod;
|
||||
if (resolvedBeanHandlerMethod != null) {
|
||||
return resolvedBeanHandlerMethod;
|
||||
}
|
||||
|
||||
// We need to resolve a bean name reference.
|
||||
if (!(this.bean instanceof String beanName)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
Assert.state(this.beanFactory != null, "Cannot resolve bean name without BeanFactory");
|
||||
Object handler = this.beanFactory.getBean(beanName);
|
||||
Assert.notNull(handler, "No handler instance");
|
||||
|
||||
HandlerMethod handlerMethod = new HandlerMethod(this, handler, false);
|
||||
if (this.beanFactory.isSingleton(beanName)) {
|
||||
this.resolvedBeanHandlerMethod = handlerMethod;
|
||||
}
|
||||
|
||||
return handlerMethod;
|
||||
}
|
||||
|
||||
|
||||
+23
-16
@@ -199,7 +199,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
/**
|
||||
* Configure the list of {@link ResourceResolver ResourceResolvers} to use.
|
||||
* <p>By default {@link PathResourceResolver} is configured. If using this property,
|
||||
* <p>By default, {@link PathResourceResolver} is configured. If using this property,
|
||||
* it is recommended to add {@link PathResourceResolver} as the last resolver.
|
||||
*/
|
||||
public void setResourceResolvers(@Nullable List<ResourceResolver> resourceResolvers) {
|
||||
@@ -218,7 +218,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
/**
|
||||
* Configure the list of {@link ResourceTransformer ResourceTransformers} to use.
|
||||
* <p>By default no transformers are configured for use.
|
||||
* <p>By default, no transformers are configured for use.
|
||||
*/
|
||||
public void setResourceTransformers(@Nullable List<ResourceTransformer> resourceTransformers) {
|
||||
this.resourceTransformers.clear();
|
||||
@@ -236,7 +236,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
/**
|
||||
* Configure the {@link ResourceHttpMessageConverter} to use.
|
||||
* <p>By default a {@link ResourceHttpMessageConverter} will be configured.
|
||||
* <p>By default, a {@link ResourceHttpMessageConverter} will be configured.
|
||||
* @since 4.3
|
||||
*/
|
||||
public void setResourceHttpMessageConverter(@Nullable ResourceHttpMessageConverter messageConverter) {
|
||||
@@ -253,7 +253,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
/**
|
||||
* Configure the {@link ResourceRegionHttpMessageConverter} to use.
|
||||
* <p>By default a {@link ResourceRegionHttpMessageConverter} will be configured.
|
||||
* <p>By default, a {@link ResourceRegionHttpMessageConverter} will be configured.
|
||||
* @since 4.3
|
||||
*/
|
||||
public void setResourceRegionHttpMessageConverter(@Nullable ResourceRegionHttpMessageConverter messageConverter) {
|
||||
@@ -294,7 +294,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
|
||||
/**
|
||||
* Specify the CORS configuration for resources served by this handler.
|
||||
* <p>By default this is not set in which allows cross-origin requests.
|
||||
* <p>By default, this is not set in which allows cross-origin requests.
|
||||
*/
|
||||
public void setCorsConfiguration(CorsConfiguration corsConfiguration) {
|
||||
this.corsConfiguration = corsConfiguration;
|
||||
@@ -523,7 +523,6 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
// For very general mappings (for example, "/") we need to check 404 first
|
||||
Resource resource = getResource(request);
|
||||
if (resource == null) {
|
||||
logger.debug("Resource not found");
|
||||
throw new NoResourceFoundException(HttpMethod.valueOf(request.getMethod()), request.getRequestURI(), getPath(request));
|
||||
}
|
||||
|
||||
@@ -539,10 +538,12 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
prepareResponse(response);
|
||||
|
||||
// Header phase
|
||||
String eTagValue = (getEtagGenerator() != null ? getEtagGenerator().apply(resource) : null);
|
||||
String etagValue = (getEtagGenerator() != null ? getEtagGenerator().apply(resource) : null);
|
||||
long lastModified = (isUseLastModified() ? resource.lastModified() : -1);
|
||||
if (new ServletWebRequest(request, response).checkNotModified(eTagValue, lastModified)) {
|
||||
logger.trace("Resource not modified");
|
||||
if (new ServletWebRequest(request, response).checkNotModified(etagValue, lastModified)) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("Resource not modified: " + resource);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -553,8 +554,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
// Content phase
|
||||
ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response);
|
||||
if (request.getHeader(HttpHeaders.RANGE) == null) {
|
||||
Assert.state(this.resourceHttpMessageConverter != null, "Not initialized");
|
||||
|
||||
Assert.state(this.resourceHttpMessageConverter != null, "Converter not initialized");
|
||||
if (HttpMethod.HEAD.matches(request.getMethod())) {
|
||||
this.resourceHttpMessageConverter.addDefaultHeaders(outputMessage, resource, mediaType);
|
||||
outputMessage.flush();
|
||||
@@ -564,7 +564,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
}
|
||||
}
|
||||
else {
|
||||
Assert.state(this.resourceRegionHttpMessageConverter != null, "Not initialized");
|
||||
Assert.state(this.resourceRegionHttpMessageConverter != null, "Converter not initialized");
|
||||
ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request);
|
||||
try {
|
||||
List<HttpRange> httpRanges = inputMessage.getHeaders().getRange();
|
||||
@@ -584,14 +584,21 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
String path = getPath(request);
|
||||
path = processPath(path);
|
||||
if (ResourceHandlerUtils.shouldIgnoreInputPath(path) || isInvalidPath(path)) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Ignoring invalid resource path [" + path + "]");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
Assert.state(this.resolverChain != null, "ResourceResolverChain not initialized.");
|
||||
Assert.state(this.transformerChain != null, "ResourceTransformerChain not initialized.");
|
||||
|
||||
Assert.state(this.resolverChain != null, "ResourceResolverChain not initialized");
|
||||
Assert.state(this.transformerChain != null, "ResourceTransformerChain not initialized");
|
||||
Resource resource = this.resolverChain.resolveResource(request, path, getLocations());
|
||||
if (resource != null) {
|
||||
if (resource == null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Resource not found for path [" + path + "]");
|
||||
}
|
||||
}
|
||||
else {
|
||||
resource = this.transformerChain.transform(request, resource);
|
||||
}
|
||||
return resource;
|
||||
|
||||
Reference in New Issue
Block a user