From 16f58243a26f1ef8d9ac075fe68153908f709e74 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Thu, 31 Oct 2024 10:59:05 -0400 Subject: [PATCH] GH-9620: Use Locale.ROOT for neutral, case insensitive comparisons Fixes: #9620 Issue link: https://github.com/spring-projects/spring-integration/issues/9620 (cherry picked from commit 0d2595ef7c02fb024c50b5a00ab987be0a5ecc43) --- .../mapping/AbstractHeaderMapper.java | 11 +-- .../support/utils/PatternMatchUtils.java | 7 +- .../http/support/DefaultHttpHeaderMapper.java | 88 ++++++++++--------- ...PostgresChannelMessageTableSubscriber.java | 3 +- .../integration/mail/ImapMailReceiver.java | 3 +- .../mail/config/MailReceiverFactoryBean.java | 7 +- .../IntegrationWebSocketContainer.java | 5 +- 7 files changed, 66 insertions(+), 58 deletions(-) diff --git a/spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java b/spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java index a737518b368..8156e6c6cdf 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/mapping/AbstractHeaderMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; @@ -490,13 +491,13 @@ public PatternBasedHeaderMatcher(Collection patterns) { Assert.notNull(patterns, "Patterns must no be null"); Assert.notEmpty(patterns, "At least one pattern must be specified"); for (String pattern : patterns) { - this.patterns.add(pattern.toLowerCase()); + this.patterns.add(pattern.toLowerCase(Locale.ROOT)); } } @Override public boolean matchHeader(String headerName) { - String header = headerName.toLowerCase(); + String header = headerName.toLowerCase(Locale.ROOT); for (String pattern : this.patterns) { if (PatternMatchUtils.simpleMatch(pattern, header)) { if (LOGGER.isDebugEnabled()) { @@ -534,13 +535,13 @@ public SinglePatternBasedHeaderMatcher(String pattern) { public SinglePatternBasedHeaderMatcher(String pattern, boolean negate) { Assert.notNull(pattern, "Pattern must no be null"); - this.pattern = pattern.toLowerCase(); + this.pattern = pattern.toLowerCase(Locale.ROOT); this.negate = negate; } @Override public boolean matchHeader(String headerName) { - String header = headerName.toLowerCase(); + String header = headerName.toLowerCase(Locale.ROOT); if (PatternMatchUtils.simpleMatch(this.pattern, header)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(MessageFormat.format( diff --git a/spring-integration-core/src/main/java/org/springframework/integration/support/utils/PatternMatchUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/support/utils/PatternMatchUtils.java index 94f8d581074..ff934f7d5ff 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/support/utils/PatternMatchUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/support/utils/PatternMatchUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2017-2022 the original author or authors. + * Copyright 2017-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.integration.support.utils; import java.util.Arrays; +import java.util.Locale; /** * Utility methods for pattern matching. @@ -48,9 +49,9 @@ private PatternMatchUtils() { */ public static Boolean smartMatchIgnoreCase(String str, String... patterns) { if (patterns != null) { - return smartMatch(str.toLowerCase(), + return smartMatch(str.toLowerCase(Locale.ROOT), Arrays.stream(patterns) - .map(String::toLowerCase) + .map((pattern) -> pattern.toLowerCase(Locale.ROOT)) .toArray(String[]::new)); } diff --git a/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java b/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java index c47dbe912e5..066d0fe606c 100644 --- a/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java +++ b/spring-integration-http/src/main/java/org/springframework/integration/http/support/DefaultHttpHeaderMapper.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -202,10 +202,10 @@ public class DefaultHttpHeaderMapper implements HeaderMapper, BeanF static { for (String header : HTTP_REQUEST_HEADER_NAMES) { - HTTP_REQUEST_HEADER_NAMES_LOWER.add(header.toLowerCase()); + HTTP_REQUEST_HEADER_NAMES_LOWER.add(header.toLowerCase(Locale.ROOT)); } for (String header : HTTP_RESPONSE_HEADER_NAMES) { - HTTP_RESPONSE_HEADER_NAMES_LOWER.add(header.toLowerCase()); + HTTP_RESPONSE_HEADER_NAMES_LOWER.add(header.toLowerCase(Locale.ROOT)); } } @@ -266,13 +266,13 @@ else if (Arrays.equals(HTTP_RESPONSE_HEADER_NAMES, outboundHeaderNames)) { outboundHeaderNamesLower[i] = this.outboundHeaderNames[i]; } else { - outboundHeaderNamesLower[i] = this.outboundHeaderNames[i].toLowerCase(); + outboundHeaderNamesLower[i] = this.outboundHeaderNames[i].toLowerCase(Locale.ROOT); } } this.outboundHeaderNamesLowerWithContentType = Arrays.copyOf(outboundHeaderNamesLower, this.outboundHeaderNames.length + 1); this.outboundHeaderNamesLowerWithContentType[this.outboundHeaderNamesLowerWithContentType.length - 1] - = MessageHeaders.CONTENT_TYPE.toLowerCase(); + = MessageHeaders.CONTENT_TYPE.toLowerCase(Locale.ROOT); } /** @@ -298,7 +298,7 @@ public void setInboundHeaderNames(String... inboundHeaderNamesArg) { this.inboundHeaderNamesLower[i] = this.inboundHeaderNames[i]; } else { - this.inboundHeaderNamesLower[i] = this.inboundHeaderNames[i].toLowerCase(); + this.inboundHeaderNamesLower[i] = this.inboundHeaderNames[i].toLowerCase(Locale.ROOT); } } } @@ -360,7 +360,7 @@ public void fromHeaders(MessageHeaders headers, HttpHeaders target) { for (Entry entry : headers.entrySet()) { String name = entry.getKey(); Object value = entry.getValue(); - String lowerName = name.toLowerCase(); + String lowerName = name.toLowerCase(Locale.ROOT); if (value != null && shouldMapOutboundHeader(lowerName)) { if (!HTTP_REQUEST_HEADER_NAMES_LOWER.contains(lowerName) && !HTTP_RESPONSE_HEADER_NAMES_LOWER.contains(lowerName) && @@ -380,7 +380,7 @@ public void fromHeaders(MessageHeaders headers, HttpHeaders target) { } private void setHttpHeader(HttpHeaders target, String name, Object value) { // NOSONAR - switch (name.toLowerCase()) { + switch (name.toLowerCase(Locale.ROOT)) { case ACCEPT_LOWER: setAccept(target, value); break; @@ -775,7 +775,7 @@ public Map toHeaders(HttpHeaders source) { Map target = new HashMap<>(); Set headerNames = source.keySet(); for (String name : headerNames) { - String lowerName = name.toLowerCase(); + String lowerName = name.toLowerCase(Locale.ROOT); if (shouldMapInboundHeader(lowerName)) { if (!HTTP_REQUEST_HEADER_NAMES_LOWER.contains(lowerName) && !HTTP_RESPONSE_HEADER_NAMES_LOWER.contains(lowerName)) { @@ -810,49 +810,51 @@ private void populateStandardHeader(HttpHeaders source, Map targ } protected Object getHttpHeader(HttpHeaders source, String name) { // NOSONAR - switch (name.toLowerCase()) { - case ACCEPT_LOWER: - return source.getAccept(); - case ACCEPT_CHARSET_LOWER: - return source.getAcceptCharset(); - case ALLOW_LOWER: - return source.getAllow(); - case CACHE_CONTROL_LOWER: + return switch (name.toLowerCase(Locale.ROOT)) { + case ACCEPT_LOWER -> source.getAccept(); + case ACCEPT_CHARSET_LOWER -> source.getAcceptCharset(); + case ALLOW_LOWER -> source.getAllow(); + case CACHE_CONTROL_LOWER -> { String cacheControl = source.getCacheControl(); - return (StringUtils.hasText(cacheControl)) ? cacheControl : null; - case CONTENT_LENGTH_LOWER: + yield (StringUtils.hasText(cacheControl)) ? cacheControl : null; + } + case CONTENT_LENGTH_LOWER -> { long contentLength = source.getContentLength(); - return (contentLength > -1) ? contentLength : null; - case CONTENT_TYPE_LOWER: - return source.getContentType(); - case DATE_LOWER: + yield (contentLength > -1) ? contentLength : null; + } + case CONTENT_TYPE_LOWER -> source.getContentType(); + case DATE_LOWER -> { long date = source.getDate(); - return (date > -1) ? date : null; - case ETAG_LOWER: + yield (date > -1) ? date : null; + } + case ETAG_LOWER -> { String eTag = source.getETag(); - return (StringUtils.hasText(eTag)) ? eTag : null; - case EXPIRES_LOWER: + yield (StringUtils.hasText(eTag)) ? eTag : null; + } + case EXPIRES_LOWER -> { long expires = source.getExpires(); - return (expires > -1) ? expires : null; - case IF_NONE_MATCH_LOWER: - return source.getIfNoneMatch(); - case IF_MODIFIED_SINCE_LOWER: + yield (expires > -1) ? expires : null; + } + case IF_NONE_MATCH_LOWER -> source.getIfNoneMatch(); + case IF_MODIFIED_SINCE_LOWER -> { long modifiedSince = source.getIfModifiedSince(); - return (modifiedSince > -1) ? modifiedSince : null; - case IF_UNMODIFIED_SINCE_LOWER: + yield (modifiedSince > -1) ? modifiedSince : null; + } + case IF_UNMODIFIED_SINCE_LOWER -> { long unmodifiedSince = source.getIfUnmodifiedSince(); - return (unmodifiedSince > -1) ? unmodifiedSince : null; - case LAST_MODIFIED_LOWER: + yield (unmodifiedSince > -1) ? unmodifiedSince : null; + } + case LAST_MODIFIED_LOWER -> { long lastModified = source.getLastModified(); - return (lastModified > -1) ? lastModified : null; - case LOCATION_LOWER: - return source.getLocation(); - case PRAGMA_LOWER: + yield (lastModified > -1) ? lastModified : null; + } + case LOCATION_LOWER -> source.getLocation(); + case PRAGMA_LOWER -> { String pragma = source.getPragma(); - return (StringUtils.hasText(pragma)) ? pragma : null; - default: - return source.get(name); - } + yield (StringUtils.hasText(pragma)) ? pragma : null; + } + default -> source.get(name); + }; } private void setMessageHeader(Map target, String name, Object value) { diff --git a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/channel/PostgresChannelMessageTableSubscriber.java b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/channel/PostgresChannelMessageTableSubscriber.java index 68ae5110a11..9c67161c9ae 100644 --- a/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/channel/PostgresChannelMessageTableSubscriber.java +++ b/spring-integration-jdbc/src/main/java/org/springframework/integration/jdbc/channel/PostgresChannelMessageTableSubscriber.java @@ -19,6 +19,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.time.Duration; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -212,7 +213,7 @@ private void doStart(CountDownLatch startingLatch) { try { PgConnection conn = this.connectionSupplier.get(); try (Statement stmt = conn.createStatement()) { - stmt.execute("LISTEN " + this.tablePrefix.toLowerCase() + "channel_message_notify"); + stmt.execute("LISTEN " + this.tablePrefix.toLowerCase(Locale.ROOT) + "channel_message_notify"); } catch (Exception ex) { try { diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapMailReceiver.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapMailReceiver.java index eb301b1f777..ed9fa1fb48c 100755 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapMailReceiver.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/ImapMailReceiver.java @@ -18,6 +18,7 @@ import java.time.Instant; import java.util.Arrays; +import java.util.Locale; import java.util.Objects; import java.util.Properties; import java.util.concurrent.ScheduledFuture; @@ -79,7 +80,7 @@ public ImapMailReceiver() { public ImapMailReceiver(String url) { super(url); if (url != null) { - Assert.isTrue(url.toLowerCase().startsWith(PROTOCOL), + Assert.isTrue(url.toLowerCase(Locale.ROOT).startsWith(PROTOCOL), "URL must start with 'imap' for the IMAP Mail receiver."); } else { diff --git a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java index 169085b7a8a..23eb391b179 100644 --- a/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java +++ b/spring-integration-mail/src/main/java/org/springframework/integration/mail/config/MailReceiverFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,7 @@ package org.springframework.integration.mail.config; +import java.util.Locale; import java.util.Properties; import jakarta.mail.Authenticator; @@ -162,8 +163,8 @@ public Class getObjectType() { private MailReceiver createReceiver() { // NOSONAR verifyProtocol(); - boolean isPop3 = this.protocol.toLowerCase().startsWith("pop3"); - boolean isImap = this.protocol.toLowerCase().startsWith("imap"); + boolean isPop3 = this.protocol.toLowerCase(Locale.ROOT).startsWith("pop3"); + boolean isImap = this.protocol.toLowerCase(Locale.ROOT).startsWith("imap"); Assert.isTrue(isPop3 || isImap, "the store URI must begin with 'pop3' or 'imap'"); AbstractMailReceiver mailReceiver = isPop3 ? new Pop3MailReceiver(this.storeUri) diff --git a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java index 1fad3076e8e..3feeb9c7231 100644 --- a/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java +++ b/spring-integration-websocket/src/main/java/org/springframework/integration/websocket/IntegrationWebSocketContainer.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2023 the original author or authors. + * Copyright 2014-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.Lock; @@ -124,7 +125,7 @@ public void setSupportedProtocols(String... protocols) { public void addSupportedProtocols(String... protocols) { for (String protocol : protocols) { - this.supportedProtocols.add(protocol.toLowerCase()); + this.supportedProtocols.add(protocol.toLowerCase(Locale.ROOT)); } }