diff --git a/.github/dco.yml b/.github/dco.yml new file mode 100644 index 000000000..0c4b142e9 --- /dev/null +++ b/.github/dco.yml @@ -0,0 +1,2 @@ +require: + members: false diff --git a/README.adoc b/README.adoc index cb06b375d..8df5028bc 100644 --- a/README.adoc +++ b/README.adoc @@ -31,21 +31,17 @@ tracker for issues and merging pull requests into main. If you want to contribute even something trivial please do not hesitate, but follow the guidelines below. -[[sign-the-contributor-license-agreement]] -== Sign the Contributor License Agreement +[[developer-certificate-of-origin]] +== Developer Certificate of Origin (DCO) -Before we accept a non-trivial patch or pull request we will need you to sign the -https://cla.pivotal.io/sign/spring[Contributor License Agreement]. -Signing the contributor's agreement does not grant anyone commit rights to the main -repository, but it does mean that we can accept your contributions, and you will get an -author credit if we do. Active contributors might be asked to join the core team, and -given the ability to merge pull requests. +All commits must include a __Signed-off-by__ trailer at the end of each commit message to indicate that the contributor agrees to the Developer Certificate of Origin. +For additional details, please refer to the blog post https://spring.io/blog/2025/01/06/hello-dco-goodbye-cla-simplifying-contributions-to-spring[Hello DCO, Goodbye CLA: Simplifying Contributions to Spring]. [[code-of-conduct]] == Code of Conduct -This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/src/main/asciidoc/code-of-conduct.adoc[code of +This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/modules/ROOT/partials/code-of-conduct.adoc[code of conduct]. By participating, you are expected to uphold this code. Please report -unacceptable behavior to spring-code-of-conduct@pivotal.io. +unacceptable behavior to code-of-conduct@spring.io. [[code-conventions-and-housekeeping]] == Code Conventions and Housekeeping @@ -138,7 +134,7 @@ Checkstyle rules are *disabled by default*. To add checkstyle to your project ju If you need to suppress some rules (e.g. line length needs to be longer), then it's enough for you to define a file under `${project.root}/src/checkstyle/checkstyle-suppressions.xml` with your suppressions. Example: -.projectRoot/src/checkstyle/checkstyle-suppresions.xml +.projectRoot/src/checkstyle/checkstyle-suppressions.xml ---- org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT jar Spring Cloud Function Docs diff --git a/pom.xml b/pom.xml index d31406aa6..5d1d4af40 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ spring-cloud-function-parent Spring Cloud Function Parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT pom org.springframework.cloud spring-cloud-build - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-adapters/pom.xml b/spring-cloud-function-adapters/pom.xml index 972ac286b..dc5d1e4de 100644 --- a/spring-cloud-function-adapters/pom.xml +++ b/spring-cloud-function-adapters/pom.xml @@ -10,7 +10,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT spring-cloud-function-adapter-parent diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml index 040ba71a1..bc0b31faf 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/pom.xml @@ -13,13 +13,13 @@ org.springframework.cloud spring-cloud-function-adapter-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT UTF-8 UTF-8 - 3.11.4 + 3.14.0 1.12.29 1.0.1 1.1.5 diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSTypesMessageConverter.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSTypesMessageConverter.java index 9c5c0bd39..7077a1595 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSTypesMessageConverter.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/AWSTypesMessageConverter.java @@ -17,11 +17,14 @@ package org.springframework.cloud.function.adapter.aws; import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.nio.charset.StandardCharsets; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer; import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers; +import com.amazonaws.services.lambda.runtime.serialization.events.serializers.S3EventSerializer; import org.springframework.cloud.function.cloudevent.CloudEventMessageUtils; import org.springframework.cloud.function.context.config.JsonMessageConverter; @@ -30,6 +33,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.MessageConverter; +import org.springframework.util.ClassUtils; import org.springframework.util.MimeType; /** @@ -44,6 +48,9 @@ class AWSTypesMessageConverter extends JsonMessageConverter { private final JsonMapper jsonMapper; + @SuppressWarnings("rawtypes") + private final AtomicReference s3EventSerializer = new AtomicReference<>(); + AWSTypesMessageConverter(JsonMapper jsonMapper) { this(jsonMapper, new MimeType("application", "json"), new MimeType(CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getType(), CloudEventMessageUtils.APPLICATION_CLOUDEVENTS.getSubtype() + "+json")); @@ -75,7 +82,6 @@ protected Object convertFromInternal(Message message, Class targetClass, @ if (message.getPayload().getClass().isAssignableFrom(targetClass)) { return message.getPayload(); } - if (targetClass.getPackage() != null && targetClass.getPackage().getName().startsWith("com.amazonaws.services.lambda.runtime.events")) { PojoSerializer serializer = LambdaEventSerializers.serializerFor(targetClass, Thread.currentThread().getContextClassLoader()); @@ -110,12 +116,23 @@ protected boolean canConvertTo(Object payload, @Nullable MessageHeaders headers) } + @SuppressWarnings("unchecked") @Override protected Object convertToInternal(Object payload, @Nullable MessageHeaders headers, @Nullable Object conversionHint) { if (payload instanceof String && headers.containsKey(AWSLambdaUtils.IS_BASE64_ENCODED) && (boolean) headers.get(AWSLambdaUtils.IS_BASE64_ENCODED)) { return ((String) payload).getBytes(StandardCharsets.UTF_8); } + if (payload.getClass().getName().equals("com.amazonaws.services.lambda.runtime.events.S3Event")) { + if (this.s3EventSerializer.get() == null) { + this.s3EventSerializer.set(new S3EventSerializer<>().withClassLoader(ClassUtils.getDefaultClassLoader())); + } + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + this.s3EventSerializer.get().toJson(payload, stream); + return stream.toByteArray(); + } + + return jsonMapper.toJson(payload); } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java index 917ca8693..0b55bc283 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoop.java @@ -28,10 +28,14 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import com.amazonaws.services.lambda.runtime.ClientContext; +import com.amazonaws.services.lambda.runtime.CognitoIdentity; +import com.amazonaws.services.lambda.runtime.Context; +import com.amazonaws.services.lambda.runtime.LambdaLogger; +import com.amazonaws.services.lambda.runtime.LambdaRuntime; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - import org.springframework.cloud.function.context.FunctionCatalog; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; import org.springframework.cloud.function.context.config.RoutingFunction; @@ -130,6 +134,8 @@ private void eventLoop(ConfigurableApplicationContext context) { logger.debug("Attempting to get new event"); ResponseEntity response = this.pollForData(rest, requestEntity); + Context clientContext = generateClientContext(response.getHeaders()); + if (logger.isDebugEnabled()) { logger.debug("New Event received: " + response); } @@ -140,9 +146,9 @@ private void eventLoop(ConfigurableApplicationContext context) { FunctionInvocationWrapper function = locateFunction(environment, functionCatalog, response.getHeaders()); ByteArrayInputStream is = new ByteArrayInputStream(response.getBody().getBytes(StandardCharsets.UTF_8)); - Message requestMessage = AWSLambdaUtils.generateMessage(is, function.getInputType(), function.isSupplier(), mapper, null); - + Message requestMessage = AWSLambdaUtils.generateMessage(is, function.getInputType(), function.isSupplier(), mapper, clientContext); Object functionResponse = function.apply(requestMessage); + byte[] responseBytes = AWSLambdaUtils.generateOutputFromObject(requestMessage, functionResponse, mapper, function.getOutputType()); String invocationUrl = MessageFormat @@ -157,12 +163,91 @@ private void eventLoop(ConfigurableApplicationContext context) { } } catch (Exception e) { + e.printStackTrace(); this.propagateAwsError(requestId, e, mapper, runtimeApi, rest); } } } } + private Context generateClientContext(HttpHeaders headers) { + + Map environment = System.getenv(); + + Context context = new Context() { + + @Override + public int getRemainingTimeInMillis() { + long now = System.currentTimeMillis(); + if (!headers.containsKey("Lambda-Runtime-Deadline-Ms")) { + return 0; + } + int delta = (int) (Long.parseLong(headers.getFirst("Lambda-Runtime-Deadline-Ms")) - now); + return delta > 0 ? delta : 0; + } + + @Override + public int getMemoryLimitInMB() { + if (!environment.containsKey("AWS_LAMBDA_FUNCTION_MEMORY_SIZE")) { + return 128; + } + return Integer.parseInt(environment.getOrDefault("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128")); + } + + @Override + public LambdaLogger getLogger() { + return LambdaRuntime.getLogger(); + } + + @Override + public String getLogStreamName() { + return environment.get("LOG_STREAM_NAME"); + } + + @Override + public String getLogGroupName() { + return environment.get("LOG_GROUP_NAME"); + } + + @Override + public String getInvokedFunctionArn() { + return headers.getFirst("Lambda-Runtime-Invoked-Function-Arn"); + } + + @Override + public CognitoIdentity getIdentity() { + return null; + } + + @Override + public String getFunctionVersion() { + return environment.get("FUNCTION_VERSION"); + } + + @Override + public String getFunctionName() { + return environment.get("FUNCTION_NAME"); + } + + @Override + public ClientContext getClientContext() { + return null; + } + + @Override + public String getAwsRequestId() { + return headers.getFirst("Lambda-Runtime-Aws-Request-Id"); + } + + public String toString() { + return "FUNCTION NAME: " + getFunctionName() + ", FUNCTION VERSION: " + getFunctionVersion() + + ", FUNCTION ARN: " + getInvokedFunctionArn() + ", FUNCTION MEM LIMIT: " + getMemoryLimitInMB() + + ", FUNCTION DEADLINE: " + getRemainingTimeInMillis(); + } + }; + return context; + } + private void propagateAwsError(String requestId, Exception e, JsonMapper mapper, String runtimeApi, RestTemplate rest) { String errorMessage = e.getMessage(); String errorType = e.getClass().getSimpleName(); diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvoker.java index 93bfde19d..dfbc067c7 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvoker.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/main/java/org/springframework/cloud/function/adapter/aws/FunctionInvoker.java @@ -85,6 +85,9 @@ public void handleRequest(InputStream input, OutputStream output, Context contex if (!this.started) { this.start(); } + if (context == null) { + logger.warn("Lambda is invoked with null Context"); + } Message requestMessage = AWSLambdaUtils .generateMessage(input, this.function.getInputType(), this.function.isSupplier(), jsonMapper, context); diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoopTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoopTest.java index 868c2ba01..817cbd776 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoopTest.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/CustomRuntimeEventLoopTest.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.adapter.aws; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -219,7 +220,7 @@ public void test_definitionLookupAndComposition() throws Exception { protected static class SingleFunctionConfiguration { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } } @@ -236,7 +237,7 @@ public Function, Flux> uppercase() { protected static class MultipleFunctionConfiguration { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean @@ -246,7 +247,7 @@ public Function toPersonJson() { @Bean public Function uppercasePerson() { - return p -> new Person(p.getName().toUpperCase()); + return p -> new Person(p.getName().toUpperCase(Locale.ROOT)); } @Bean @@ -267,7 +268,7 @@ public PersonFunction() { @Override public Person apply(Person input) { - return new Person(input.getName().toUpperCase()); + return new Person(input.getName().toUpperCase(Locale.ROOT)); } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/FunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/FunctionInvokerTests.java index 3db5bb6e4..887e49f18 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/FunctionInvokerTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-aws/src/test/java/org/springframework/cloud/function/adapter/aws/FunctionInvokerTests.java @@ -25,6 +25,7 @@ import java.util.Base64; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -45,6 +46,7 @@ import com.amazonaws.services.lambda.runtime.events.SNSEvent; import com.amazonaws.services.lambda.runtime.events.SQSEvent; import com.fasterxml.jackson.databind.ObjectMapper; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; @@ -63,7 +65,6 @@ import org.springframework.util.StreamUtils; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; /** * @@ -998,6 +999,18 @@ public void testS3StringEvent() throws Exception { assertThat(result).contains("s3SchemaVersion"); } + @Test + public void testS3EventAsOutput() throws Exception { + System.setProperty("MAIN_CLASS", S3Configuration.class.getName()); + System.setProperty("spring.cloud.function.definition", "outputS3Event"); + FunctionInvoker invoker = new FunctionInvoker(); + + InputStream targetStream = new ByteArrayInputStream(this.s3Event.getBytes()); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + invoker.handleRequest(targetStream, output, null); + assertThat(output.toByteArray()).isNotNull(); + } + @Test public void testS3Event() throws Exception { System.setProperty("MAIN_CLASS", S3Configuration.class.getName()); @@ -1449,7 +1462,7 @@ public void testWithDefaultRoutingFailure() throws Exception { try { invoker.handleRequest(targetStream, output, null); - fail(); + Assertions.fail(""); } catch (Exception e) { // TODO: handle exception @@ -1492,7 +1505,7 @@ public static class BasicConfiguration { @Bean public Function, Message> uppercase() { return v -> { - return MessageBuilder.withPayload(v.getPayload().toUpperCase()).build(); + return MessageBuilder.withPayload(v.getPayload().toUpperCase(Locale.ROOT)).build(); }; } } @@ -1525,7 +1538,7 @@ public Function echoString() { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean @@ -1678,6 +1691,13 @@ public Function, String> inputSNSEventAsMap() { @EnableAutoConfiguration @Configuration public static class S3Configuration { + + @Bean + public Function outputS3Event() { + return v -> { + return v; + }; + } @Bean public Function echoString() { return v -> v; @@ -1784,7 +1804,7 @@ public Consumer consume() { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean @@ -1795,7 +1815,7 @@ public Function, Mono> reactiveWithVoidReturn() { @Bean public Function uppercasePojo() { return v -> { - return v.getName().toUpperCase(); + return v.getName().toUpperCase(Locale.ROOT); }; } @@ -1803,7 +1823,7 @@ public Function uppercasePojo() { public Function uppercasePojoReturnPojo() { return v -> { Person p = new Person(); - p.setName(v.getName().toUpperCase()); + p.setName(v.getName().toUpperCase(Locale.ROOT)); return p; }; } @@ -1812,7 +1832,7 @@ public Function uppercasePojoReturnPojo() { public Function, Flux> uppercasePojoReturnPojoReactive() { return flux -> flux.map(v -> { Person p = new Person(); - p.setName(v.getName().toUpperCase()); + p.setName(v.getName().toUpperCase(Locale.ROOT)); return p; }); } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/pom.xml index 4888ab266..9bf12042c 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/pom.xml @@ -9,7 +9,7 @@ org.springframework.cloud spring-cloud-function-adapter-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT UTF-8 diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetData.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetData.java index 59989bf8a..7bfeb5241 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetData.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure-web/src/test/java/org/springframework/cloud/function/adapter/azure/web/PetData.java @@ -24,7 +24,10 @@ import java.util.List; import java.util.concurrent.ThreadLocalRandom; -public class PetData { +public final class PetData { + private PetData() { + + } private static List breeds = new ArrayList<>(); static { breeds.add("Afghan Hound"); diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/pom.xml index 36910303a..214064ef5 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/pom.xml @@ -12,7 +12,7 @@ org.springframework.cloud spring-cloud-function-adapter-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java index 2299007cc..ce639cf95 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/CustomFunctionInvokerTests.java @@ -18,6 +18,7 @@ import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.function.Function; import java.util.stream.Collectors; @@ -184,7 +185,7 @@ static class TestFunctionsConfig { @Bean public Function imperativeUppercase() { - return (s) -> s.toUpperCase(); + return (s) -> s.toUpperCase(Locale.ROOT); } @Bean diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java index a010af1d0..5ed105fb4 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/FunctionInvokerTests.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Locale; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -195,7 +196,7 @@ public Function, Flux> echoStream() { @Bean public Function, Mono> uppercaseMono() { - return f -> f.map(v -> v.toUpperCase()); + return f -> f.map(v -> v.toUpperCase(Locale.ROOT)); } } @@ -241,7 +242,7 @@ protected static class BareConfig { @Bean("uppercase") public Function, Flux> function() { - return foos -> foos.map(foo -> new Bar(foo.getValue().toUpperCase())); + return foos -> foos.map(foo -> new Bar(foo.getValue().toUpperCase(Locale.ROOT))); } } @@ -256,7 +257,7 @@ public Function, Bar> uppercase() { Foo foo = message.getPayload(); ExecutionContext targetContext = (ExecutionContext) message.getHeaders().get("executionContext"); targetContext.getLogger().info("Invoking 'uppercase' on " + foo.getValue()); - return new Bar(foo.getValue().toUpperCase()); + return new Bar(foo.getValue().toUpperCase(Locale.ROOT)); }; } @@ -269,7 +270,7 @@ protected static class ListConfig { @Bean public Function, List> uppercase() { return foos -> { - List bars = foos.stream().map(foo -> new Bar(foo.getValue().toUpperCase())) + List bars = foos.stream().map(foo -> new Bar(foo.getValue().toUpperCase(Locale.ROOT))) .collect(Collectors.toList()); return bars; }; @@ -283,7 +284,7 @@ protected static class CollectConfig { @Bean public Function, Bar> uppercase() { - return foos -> new Bar(foos.stream().map(foo -> foo.getValue().toUpperCase()) + return foos -> new Bar(foos.stream().map(foo -> foo.getValue().toUpperCase(Locale.ROOT)) .collect(Collectors.joining(","))); } @@ -300,7 +301,7 @@ public Function, Bar> uppercase() { ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext"); Foo foo = message.getPayload(); context.getLogger().info("Executing uppercase function"); - return new Bar(foo.getValue().toUpperCase()); + return new Bar(foo.getValue().toUpperCase(Locale.ROOT)); }; } @@ -310,7 +311,7 @@ public Function, Foo> lowercase() { ExecutionContext context = (ExecutionContext) message.getHeaders().get("executionContext"); Bar bar = message.getPayload(); context.getLogger().info("Executing lowercase function"); - return new Foo(bar.getValue().toLowerCase()); + return new Foo(bar.getValue().toLowerCase(Locale.ROOT)); }; } @@ -330,11 +331,11 @@ class Foo { } public String lowercase() { - return this.value.toLowerCase(); + return this.value.toLowerCase(Locale.ROOT); } public String uppercase() { - return this.value.toUpperCase(); + return this.value.toUpperCase(Locale.ROOT); } public String getValue() { diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java index 20445fdc4..82a3dfed0 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/HttpFunctionInvokerTests.java @@ -21,6 +21,7 @@ import java.net.URISyntaxException; import java.util.Collections; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -122,7 +123,7 @@ public Function, Message> function() { return (foo -> { Map headers = new HashMap<>(); return new GenericMessage<>( - new Bar(foo.getPayload().getValue().toUpperCase()), headers); + new Bar(foo.getPayload().getValue().toUpperCase(Locale.ROOT)), headers); }); } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java index 11dfa8c48..c4147e742 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/AzureFunctionInstanceInjectorTest.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.adapter.azure.injector; +import java.util.Locale; import java.util.Optional; import java.util.function.Function; @@ -106,7 +107,7 @@ public Function, String> uppercaseBean() { Assertions.assertThat(context).isNotNull(); Assertions.assertThat(context.getFunctionName()).isEqualTo("hello"); - return message.getPayload().toUpperCase(); + return message.getPayload().toUpperCase(Locale.ROOT); }; } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java index 0af13fcd7..b21eb3900 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-azure/src/test/java/org/springframework/cloud/function/adapter/azure/injector/FunctionInstanceInjectorServiceLoadingTest.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.adapter.azure.injector; import java.util.Iterator; +import java.util.Locale; import java.util.Optional; import java.util.ServiceLoader; import java.util.function.Function; @@ -108,7 +109,7 @@ public Function, String> uppercase() { .get(AzureFunctionUtil.EXECUTION_CONTEXT); Assertions.assertThat(context).isNotNull(); Assertions.assertThat(context.getFunctionName()).isEqualTo("hello"); - return message.getPayload().toUpperCase(); + return message.getPayload().toUpperCase(Locale.ROOT); }; } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml index 13f43a1a4..4aac8f64c 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/pom.xml @@ -11,7 +11,7 @@ spring-cloud-function-adapter-parent org.springframework.cloud - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/FunctionInvoker.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/FunctionInvoker.java index 062787d84..7c3f512ab 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/FunctionInvoker.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/main/java/org/springframework/cloud/function/adapter/gcp/FunctionInvoker.java @@ -121,7 +121,15 @@ public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws E if (result != null) { MessageHeaders headers = result.getHeaders(); - httpResponse.setContentType(result.getHeaders().get(MessageHeaders.CONTENT_TYPE).toString()); + if (result.getHeaders().containsKey(MessageHeaders.CONTENT_TYPE)) { + httpResponse.setContentType(result.getHeaders().get(MessageHeaders.CONTENT_TYPE).toString()); + } + else if (result.getHeaders().containsKey("Content-Type")) { + httpResponse.setContentType(result.getHeaders().get("Content-Type").toString()); + } + else { + httpRequest.getContentType().ifPresent(contentType -> httpResponse.setContentType(contentType)); + } httpResponse.getWriter().write(new String(result.getPayload(), StandardCharsets.UTF_8)); for (Entry header : headers.entrySet()) { Object values = header.getValue(); @@ -133,7 +141,6 @@ public void service(HttpRequest httpRequest, HttpResponse httpResponse) throws E httpResponse.appendHeader(header.getKey(), header.getValue().toString()); } } - httpRequest.getContentType().ifPresent(contentType -> httpResponse.setContentType(contentType)); if (headers.containsKey(HTTP_STATUS_CODE)) { if (headers.get(HTTP_STATUS_CODE) instanceof Integer) { diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerHttpTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerHttpTests.java index c178414ae..d02d322eb 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerHttpTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/FunctionInvokerHttpTests.java @@ -42,6 +42,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.messaging.Message; +import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.MessageBuilder; import static java.util.Arrays.asList; @@ -165,7 +166,7 @@ public void testStatusCodeSet() throws Exception { bufferedWriter.close(); verify(response).setStatusCode(404); - + verify(response).setContentType("text/plain"); } @Test @@ -200,7 +201,7 @@ public Function> function() { String payload = "hello"; - Message msg = MessageBuilder.withPayload(payload).setHeader("statusCode", 404) + Message msg = MessageBuilder.withPayload(payload).setHeader("statusCode", 404).setHeader(MessageHeaders.CONTENT_TYPE, "text/plain") .build(); return x -> msg; diff --git a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/FunctionInvokerIntegrationTests.java b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/FunctionInvokerIntegrationTests.java index 054de3b1f..fce82e875 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/FunctionInvokerIntegrationTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-adapter-gcp/src/test/java/org/springframework/cloud/function/adapter/gcp/integration/FunctionInvokerIntegrationTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.adapter.gcp.integration; import java.io.IOException; +import java.util.Locale; import java.util.function.Function; import java.util.function.Supplier; @@ -98,7 +99,7 @@ static class CloudFunctionMainSingular { @Bean Function uppercase() { - return input -> input.toUpperCase(); + return input -> input.toUpperCase(Locale.ROOT); } } @@ -109,7 +110,7 @@ static class CloudFunctionMain { @Bean Function uppercase() { - return input -> input.toUpperCase(); + return input -> input.toUpperCase(Locale.ROOT); } @Bean diff --git a/spring-cloud-function-adapters/spring-cloud-function-aws-gradle-parent/org.springframework.cloud.function.aws-lambda.packaging.gradle.plugin/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-aws-gradle-parent/org.springframework.cloud.function.aws-lambda.packaging.gradle.plugin/pom.xml index 544fd3b70..9e96402ec 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-aws-gradle-parent/org.springframework.cloud.function.aws-lambda.packaging.gradle.plugin/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-aws-gradle-parent/org.springframework.cloud.function.aws-lambda.packaging.gradle.plugin/pom.xml @@ -10,7 +10,7 @@ org.springframework.cloud.function.aws-lambda.packaging spring-cloud-function-aws-gradle-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT ${basedir}/../.. diff --git a/spring-cloud-function-adapters/spring-cloud-function-aws-gradle-parent/spring-cloud-function-aws-packaging-gradle-plugin/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-aws-gradle-parent/spring-cloud-function-aws-packaging-gradle-plugin/pom.xml index 5bd9a6c39..7bea5dbc1 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-aws-gradle-parent/spring-cloud-function-aws-packaging-gradle-plugin/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-aws-gradle-parent/spring-cloud-function-aws-packaging-gradle-plugin/pom.xml @@ -13,7 +13,7 @@ org.springframework.cloud.function.aws-lambda.packaging spring-cloud-function-aws-gradle-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/pom.xml index 67d881333..e26aa6e8a 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-grpc-cloudevent-ext/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-function-adapter-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT spring-cloud-function-grpc-cloudevent-ext spring-cloud-function-grpc-cloudevent-ext @@ -45,6 +45,16 @@ + org.apache.maven.plugins + maven-checkstyle-plugin + + + checkstyle-validation + none + + + + org.xolstice.maven.plugins protobuf-maven-plugin 0.6.1 diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-grpc/pom.xml index 628d9a3f7..c6b6ac9a9 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-grpc/pom.xml @@ -10,7 +10,7 @@ org.springframework.cloud spring-cloud-function-adapter-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.55.1 diff --git a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java index ef0d66c7b..be9411558 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-grpc/src/test/java/org/springframework/cloud/function/grpc/GrpcInteractionTests.java @@ -19,6 +19,7 @@ import java.time.Duration; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Random; import java.util.function.Function; @@ -323,17 +324,17 @@ public static class SampleConfiguration { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean public Function> uppercaseMonoReturn() { - return v -> Mono.just(v.toUpperCase()); + return v -> Mono.just(v.toUpperCase(Locale.ROOT)); } @Bean public Function> uppercaseFluxReturn() { - return v -> Flux.just(v.toUpperCase(), v.toUpperCase() + "-1", v.toUpperCase() + "-2"); + return v -> Flux.just(v.toUpperCase(Locale.ROOT), v.toUpperCase(Locale.ROOT) + "-1", v.toUpperCase(Locale.ROOT) + "-2"); } @Bean @@ -343,7 +344,7 @@ public Function reverse() { @Bean public Function, Flux> uppercaseReactive() { - return flux -> flux.map(v -> v.toUpperCase()); + return flux -> flux.map(v -> v.toUpperCase(Locale.ROOT)); } @Bean @@ -360,7 +361,7 @@ public Function, String> streamInStringOut() { @Bean public Function> stringInStreamOut() { - return value -> Flux.just(value, value.toUpperCase()); + return value -> Flux.just(value, value.toUpperCase(Locale.ROOT)); } } } diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml index 5a9b820f4..5b4784d0d 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/pom.xml @@ -10,7 +10,7 @@ org.springframework.cloud spring-cloud-function-adapter-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT UTF-8 @@ -59,7 +59,6 @@ spring-boot-starter-tomcat - test diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletRequest.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletRequest.java index 77b135555..b84faa11f 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletRequest.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessHttpServletRequest.java @@ -77,6 +77,7 @@ public class ServerlessHttpServletRequest implements HttpServletRequest { private static final BufferedReader EMPTY_BUFFERED_READER = new BufferedReader(new StringReader("")); + private static final InputStream EMPTY_INPUT_STREAM = new ByteArrayInputStream(new byte[0]); /** * Date formats as specified in the HTTP RFC. * @@ -283,7 +284,15 @@ public String getContentType() { @Override public ServletInputStream getInputStream() { - InputStream stream = new ByteArrayInputStream(this.content); + + InputStream stream; + if (this.content == null) { + stream = EMPTY_INPUT_STREAM; + } + else { + stream = new ByteArrayInputStream(this.content); + } + return new ServletInputStream() { boolean finished = false; diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessMVC.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessMVC.java index 486794734..0908621b6 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessMVC.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessMVC.java @@ -78,11 +78,9 @@ public final class ServerlessMVC { private volatile ServletWebServerApplicationContext applicationContext; - private ServletContext servletContext; - private final CountDownLatch contextStartupLatch = new CountDownLatch(1); - private final long initializatioinTimeout; + private final long initializationTimeout; public static ServerlessMVC INSTANCE(Class... componentClasses) { ServerlessMVC mvc = new ServerlessMVC(); @@ -103,7 +101,7 @@ private ServerlessMVC() { if (!StringUtils.hasText(timeoutValue)) { timeoutValue = System.getProperty(INIT_TIMEOUT); } - this.initializatioinTimeout = StringUtils.hasText(timeoutValue) ? Long.valueOf(timeoutValue) : 20000; + this.initializationTimeout = StringUtils.hasText(timeoutValue) ? Long.valueOf(timeoutValue) : 20000; } private void initializeContextAsync(Class... componentClasses) { @@ -137,7 +135,7 @@ public ConfigurableWebApplicationContext getApplicationContext() { public ServletContext getServletContext() { this.waitForContext(); - return this.servletContext; + return this.dispatcher.getServletContext(); } public void stop() { @@ -157,7 +155,7 @@ public void stop() { * @see org.springframework.test.web.servlet.result.MockMvcResultMatchers */ public void service(HttpServletRequest request, HttpServletResponse response) throws Exception { - Assert.state(this.waitForContext(), "Failed to initialize Application within the specified time of " + this.initializatioinTimeout + " milliseconds. " + Assert.state(this.waitForContext(), "Failed to initialize Application within the specified time of " + this.initializationTimeout + " milliseconds. " + "If you need to increase it, please set " + INIT_TIMEOUT + " environment variable"); this.service(request, response, (CountDownLatch) null); } @@ -189,7 +187,7 @@ public void service(HttpServletRequest request, HttpServletResponse response, Co public boolean waitForContext() { try { - return contextStartupLatch.await(initializatioinTimeout, TimeUnit.MILLISECONDS); + return contextStartupLatch.await(initializationTimeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); @@ -215,7 +213,6 @@ private static class ProxyFilterChain implements FilterChain { * Create a {@code FilterChain} with Filter's and a Servlet. * * @param servlet the {@link Servlet} to invoke in this {@link FilterChain} - * @param filters the {@link Filter}'s to invoke in this {@link FilterChain} * @since 4.0.x */ ProxyFilterChain(DispatcherServlet servlet) { diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessServletContext.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessServletContext.java index 921c5941f..024064a5f 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessServletContext.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessServletContext.java @@ -16,9 +16,13 @@ package org.springframework.cloud.function.serverless.web; +import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.InvalidPathException; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; @@ -104,7 +108,14 @@ public int getEffectiveMinorVersion() { @Override public String getMimeType(String file) { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); + String mimeType = null; + try { + mimeType = Files.probeContentType(Paths.get(file)); + } + catch (IOException | InvalidPathException e) { + log("unable to probe for content type " + file, e); + } + return mimeType; } @Override @@ -114,12 +125,12 @@ public Set getResourcePaths(String path) { @Override public URL getResource(String path) throws MalformedURLException { - throw new UnsupportedOperationException("This ServletContext does not represent a running web container"); + return ServerlessServletContext.class.getResource(path); } @Override public InputStream getResourceAsStream(String path) { - return null; + return ServerlessServletContext.class.getResourceAsStream(path); } @Override diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessWebApplication.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessWebApplication.java index c4ca109a0..a1ed042cb 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessWebApplication.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/main/java/org/springframework/cloud/function/serverless/web/ServerlessWebApplication.java @@ -20,6 +20,7 @@ import java.io.PrintStream; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.Set; import java.util.function.Consumer; @@ -157,7 +158,7 @@ private Banner printBanner(ConfigurableEnvironment environment) { ResourceLoader resourceLoader = (this.getResourceLoader() != null) ? this.getResourceLoader() : new DefaultResourceLoader(null); Banner.Mode bannerMode = environment.containsProperty("spring.main.banner-mode") - ? Banner.Mode.valueOf(environment.getProperty("spring.main.banner-mode").trim().toUpperCase()) + ? Banner.Mode.valueOf(environment.getProperty("spring.main.banner-mode").trim().toUpperCase(Locale.ROOT)) : Banner.Mode.CONSOLE; if (bannerMode == Banner.Mode.OFF) { diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/AsyncStartTests.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/AsyncStartTests.java index 95743a839..5bac22fab 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/AsyncStartTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/AsyncStartTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.serverless.web; import jakarta.servlet.http.HttpServletRequest; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -25,7 +26,8 @@ import org.springframework.web.servlet.config.annotation.EnableWebMvc; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; + + /** * @author Oleg Zhurakousky @@ -55,7 +57,7 @@ public void testAsyncWithEnvSet() throws Exception { ServerlessHttpServletResponse response = new ServerlessHttpServletResponse(); try { mvc.service(request, response); - fail(); + Assertions.fail(""); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalStateException.class); diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java index 1cbf27cc7..d02d36539 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/serverless/web/RequestResponseTests.java @@ -149,6 +149,21 @@ public void validatePostWithBody() throws Exception { assertThat(pet.getName()).isNotEmpty(); } + @Test + public void validatePostWithoutBody() throws Exception { + ServerlessHttpServletRequest request = new ServerlessHttpServletRequest(null, "POST", "/pets/"); + request.setContentType("application/json"); + ServerlessHttpServletResponse response = new ServerlessHttpServletResponse(); + try { + mvc.service(request, response); + } + catch (jakarta.servlet.ServletException e) { + assertThat(e.getCause()).isNotInstanceOf(NullPointerException.class); + } + + assertThat(response.getStatus()).isEqualTo(400); // application fail because the pet is empty ;) + } + @Test public void validatePostAsyncWithBody() throws Exception { // System.setProperty("spring.main.banner-mode", "off"); diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetData.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetData.java index 90b2a736f..ac00af9ef 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetData.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetData.java @@ -24,8 +24,13 @@ import java.util.List; import java.util.concurrent.ThreadLocalRandom; -public class PetData { +public final class PetData { private static List breeds = new ArrayList<>(); + + private PetData() { + + } + static { breeds.add("Afghan Hound"); breeds.add("Beagle"); diff --git a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java index 2cd6d734b..2a02c3267 100644 --- a/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java +++ b/spring-cloud-function-adapters/spring-cloud-function-serverless-web/src/test/java/org/springframework/cloud/function/test/app/PetStoreSpringAppConfig.java @@ -129,6 +129,9 @@ public AnotherFilter anotherFilter() { } public static class SimpleFilter extends OncePerRequestFilter { + /** + * + */ public boolean invoked; @Override @@ -146,6 +149,10 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse } public static class AnotherFilter extends OncePerRequestFilter { + + /** + * + */ public boolean invoked; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, diff --git a/spring-cloud-function-context/pom.xml b/spring-cloud-function-context/pom.xml index bff0f12bc..87232e473 100644 --- a/spring-cloud-function-context/pom.xml +++ b/spring-cloud-function-context/pom.xml @@ -12,7 +12,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT @@ -22,7 +22,7 @@ net.jodah typetools - 0.6.2 + 0.6.3 org.springframework.boot @@ -65,6 +65,10 @@ com.fasterxml.jackson.datatype jackson-datatype-jsr310 + + com.fasterxml.jackson.datatype + jackson-datatype-joda + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/actuator/FunctionsEndpoint.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/actuator/FunctionsEndpoint.java index 008a969db..4d0ad3883 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/actuator/FunctionsEndpoint.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/actuator/FunctionsEndpoint.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.actuator; import java.util.LinkedHashMap; +import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.TreeMap; @@ -72,10 +73,10 @@ else if (function.isConsumer()) { private String toSimplePolyOut(FunctionInvocationWrapper function) { - return FunctionTypeUtils.getRawType(function.getItemType(function.getOutputType())).getSimpleName().toLowerCase(); + return FunctionTypeUtils.getRawType(function.getItemType(function.getOutputType())).getSimpleName().toLowerCase(Locale.ROOT); } private String toSimplePolyIn(FunctionInvocationWrapper function) { - return FunctionTypeUtils.getRawType(function.getItemType(function.getInputType())).getSimpleName().toLowerCase(); + return FunctionTypeUtils.getRawType(function.getItemType(function.getInputType())).getSimpleName().toLowerCase(Locale.ROOT); } } diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java index af3c81931..90fdbcdbd 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/cloudevent/CloudEventMessageUtils.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.cloudevent; import java.net.URI; +import java.nio.charset.StandardCharsets; import java.time.OffsetDateTime; import java.util.Collections; import java.util.HashMap; @@ -170,7 +171,11 @@ private CloudEventMessageUtils() { public static String getId(Message message) { String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + MessageHeaders.ID); + Object value = message.getHeaders().get(prefix + MessageHeaders.ID); + if (value instanceof byte[] v) { + value = toString(v); + } + return (String) value; } public static URI getSource(Message message) { @@ -180,17 +185,29 @@ public static URI getSource(Message message) { public static String getSpecVersion(Message message) { String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + _SPECVERSION); + Object value = message.getHeaders().get(prefix + _SPECVERSION); + if (value instanceof byte[] v) { + value = toString(v); + } + return (String) value; } public static String getType(Message message) { String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + _TYPE); + Object value = message.getHeaders().get(prefix + _TYPE); + if (value instanceof byte[] v) { + value = toString(v); + } + return (String) value; } public static String getDataContentType(Message message) { String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + _DATACONTENTTYPE); + Object value = message.getHeaders().get(prefix + _DATACONTENTTYPE); + if (value instanceof byte[] v) { + value = toString(v); + } + return (String) value; } public static URI getDataSchema(Message message) { @@ -200,7 +217,11 @@ public static URI getDataSchema(Message message) { public static String getSubject(Message message) { String prefix = determinePrefixToUse(message.getHeaders()); - return (String) message.getHeaders().get(prefix + _SUBJECT); + Object value = message.getHeaders().get(prefix + _SUBJECT); + if (value instanceof byte[] v) { + value = toString(v); + } + return (String) value; } public static OffsetDateTime getTime(Message message) { @@ -434,12 +455,21 @@ private static Message buildBinaryMessageFromStructuredMap(Map map, String key) { Object uri = map.get(key); - if (uri != null && uri instanceof String) { - uri = URI.create((String) uri); + if (uri != null) { + if (uri instanceof String) { + uri = URI.create((String) uri); + } + else if (uri instanceof byte[] u) { + uri = URI.create(toString(u)); + } } return (URI) uri; } + private static String toString(byte[] value) { + return new String(value, StandardCharsets.UTF_8); + } + public static class Protocols { static String AMQP = "amqp"; static String AVRO = "avro"; diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/MessageRoutingCallback.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/MessageRoutingCallback.java index 6f3b2a506..f9b2803ca 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/MessageRoutingCallback.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/MessageRoutingCallback.java @@ -21,21 +21,22 @@ /** * Java-based strategy to assist with determining the name of the route-to function definition. - * Once implementation is registered as a bean in application context + * Once an implementation is registered as a bean in application context * it will be picked up by the {@link RoutingFunction}. - * + *

* While {@link RoutingFunction} provides several mechanisms to determine the route-to function definition * this callback takes precedence over all of them. * * @author Oleg Zhurakousky + * @author John Blum * @since 3.1 */ public interface MessageRoutingCallback { /** - * Computes and returns the instance of {@link String} which encapsulates, - * at the very minimum, function definition. - *

+ * Computes and returns an instance of {@link String}, which encapsulates, + * at the very minimum, a function definition. + *

* Providing such message is primarily an optimization feature. It could be useful for cases * where routing procedure is complex and results in, let's say, conversion of the payload to * the target type, which would effectively be thrown away if the ability to modify the target diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionAroundWrapper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionAroundWrapper.java index d656420d9..8a09989a5 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionAroundWrapper.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionAroundWrapper.java @@ -16,8 +16,6 @@ package org.springframework.cloud.function.context.catalog; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.reactivestreams.Publisher; import org.springframework.cloud.function.context.catalog.SimpleFunctionRegistry.FunctionInvocationWrapper; @@ -37,14 +35,15 @@ */ public abstract class FunctionAroundWrapper { - private static final Log log = LogFactory.getLog(FunctionAroundWrapper.class); - public final Object apply(Object input, FunctionInvocationWrapper targetFunction) { + String functionalTracingEnabledStr = System.getProperty("spring.cloud.function.observability.enabled"); boolean functionalTracingEnabled = !StringUtils.hasText(functionalTracingEnabledStr) || Boolean.parseBoolean(functionalTracingEnabledStr); if (functionalTracingEnabled && !(input instanceof Publisher) && input instanceof Message && !FunctionTypeUtils.isCollectionOfMessage(targetFunction.getOutputType())) { - return this.doApply(input, targetFunction); + Object result = this.doApply(input, targetFunction); + targetFunction.wrapped = false; + return result; } else { return targetFunction.apply(input); diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java index 24e181e12..3a09443b5 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/FunctionTypeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2022 the original author or authors. + * Copyright 2019-2025 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,18 +57,18 @@ import org.springframework.util.StringUtils; - /** * Set of utility operations to interrogate function definitions. * * @author Oleg Zhurakousky * @author Andrey Shlykov + * @author Artem Bilan * * @since 3.0 */ public final class FunctionTypeUtils { - private static Log logger = LogFactory.getLog(FunctionTypeUtils.class); + private static Log logger = LogFactory.getLog(FunctionTypeUtils.class); private FunctionTypeUtils() { @@ -76,17 +76,17 @@ private FunctionTypeUtils() { public static Type functionType(Type input, Type output) { return ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forType(input), ResolvableType.forType(output)).getType(); + ResolvableType.forType(input), ResolvableType.forType(output)).getType(); } public static Type consumerType(Type input) { return ResolvableType.forClassWithGenerics(Consumer.class, - ResolvableType.forType(input)).getType(); + ResolvableType.forType(input)).getType(); } public static Type supplierType(Type output) { return ResolvableType.forClassWithGenerics(Supplier.class, - ResolvableType.forType(output)).getType(); + ResolvableType.forType(output)).getType(); } /** @@ -146,7 +146,7 @@ public static Type getGenericType(Type type) { */ public static Class getRawType(Type type) { return type != null ? TypeResolver - .resolveRawClass(type instanceof GenericArrayType ? type : TypeResolver.reify(type), null) : null; + .resolveRawClass(type instanceof GenericArrayType ? type : TypeResolver.reify(type), null) : null; } /** @@ -161,15 +161,15 @@ public static Class getRawType(Type type) { public static Method discoverFunctionalMethod(Class pojoFunctionClass) { if (Supplier.class.isAssignableFrom(pojoFunctionClass)) { return Stream.of(ReflectionUtils.getDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("get")).findFirst().get(); + && m.getName().equals("get")).findFirst().get(); } else if (Consumer.class.isAssignableFrom(pojoFunctionClass) || BiConsumer.class.isAssignableFrom(pojoFunctionClass)) { return Stream.of(ReflectionUtils.getDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("accept")).findFirst().get(); + && m.getName().equals("accept")).findFirst().get(); } else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class.isAssignableFrom(pojoFunctionClass)) { return Stream.of(ReflectionUtils.getDeclaredMethods(pojoFunctionClass)).filter(m -> !m.isSynthetic() - && m.getName().equals("apply")).findFirst().get(); + && m.getName().equals("apply")).findFirst().get(); } List methods = new ArrayList<>(); @@ -180,10 +180,10 @@ else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class. }, method -> !method.getDeclaringClass().isAssignableFrom(Object.class) - && !method.isSynthetic() && !method.isBridge() && !method.isVarArgs()); + && !method.isSynthetic() && !method.isBridge() && !method.isVarArgs()); Assert.isTrue(methods.size() == 1, "Discovered " + methods.size() + " methods that would qualify as 'functional' - " - + methods + ".\n Class '" + pojoFunctionClass + "' is not a FunctionalInterface."); + + methods + ".\n Class '" + pojoFunctionClass + "' is not a FunctionalInterface."); return methods.get(0); } @@ -192,7 +192,12 @@ else if (Function.class.isAssignableFrom(pojoFunctionClass) || BiFunction.class. public static Type discoverFunctionTypeFromClass(Class functionalClass) { if (KotlinDetector.isKotlinPresent()) { if (Function1.class.isAssignableFrom(functionalClass)) { - return TypeResolver.reify(Function1.class, (Class>) functionalClass); + try { + return TypeResolver.reify(Function1.class, (Class>) functionalClass); + } + catch (Exception e) { + return discoverFunctionTypeFromFunctionMethod(discoverFunctionalMethod(functionalClass)); + } } else if (Function0.class.isAssignableFrom(functionalClass)) { return TypeResolver.reify(Function0.class, (Class>) functionalClass); @@ -201,7 +206,8 @@ else if (Function0.class.isAssignableFrom(functionalClass)) { if (Function.class.isAssignableFrom(functionalClass)) { for (Type superInterface : functionalClass.getGenericInterfaces()) { if (superInterface != null && !superInterface.equals(Object.class)) { - if (superInterface.toString().contains("KStream") && ResolvableType.forType(superInterface).getGeneric(1).isArray()) { + if (superInterface.toString().contains("KStream") && ResolvableType.forType(superInterface) + .getGeneric(1).isArray()) { return null; } } @@ -250,24 +256,25 @@ public static Type discoverFunctionTypeFromFunctionFactoryMethod(Method method) */ public static Type discoverFunctionTypeFromFunctionMethod(Method functionMethod) { Assert.isTrue( - functionMethod.getName().equals("apply") || + functionMethod.getName().equals("apply") || functionMethod.getName().equals("accept") || - functionMethod.getName().equals("get"), - "Only Supplier, Function or Consumer supported at the moment. Was " + functionMethod.getDeclaringClass()); + functionMethod.getName().equals("get") || + functionMethod.getName().equals("invoke"), + "Only Supplier, Function or Consumer supported at the moment. Was " + functionMethod.getDeclaringClass()); - if (functionMethod.getName().equals("apply")) { + if (functionMethod.getName().equals("apply") || functionMethod.getName().equals("invoke")) { return ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forMethodParameter(functionMethod, 0), - ResolvableType.forMethodReturnType(functionMethod)).getType(); + ResolvableType.forMethodParameter(functionMethod, 0), + ResolvableType.forMethodReturnType(functionMethod)).getType(); } else if (functionMethod.getName().equals("accept")) { return ResolvableType.forClassWithGenerics(Consumer.class, - ResolvableType.forMethodParameter(functionMethod, 0)).getType(); + ResolvableType.forMethodParameter(functionMethod, 0)).getType(); } else { return ResolvableType.forClassWithGenerics(Supplier.class, - ResolvableType.forMethodReturnType(functionMethod)).getType(); + ResolvableType.forMethodReturnType(functionMethod)).getType(); } } @@ -329,13 +336,13 @@ public static Type getInputType(Type functionType) { Type inputType; if (functionType instanceof Class) { functionType = Function.class.isAssignableFrom((Class) functionType) - ? TypeResolver.reify(Function.class, (Class>) functionType) - : TypeResolver.reify(Consumer.class, (Class>) functionType); + ? TypeResolver.reify(Function.class, (Class>) functionType) + : TypeResolver.reify(Consumer.class, (Class>) functionType); } inputType = functionType instanceof ParameterizedType - ? ((ParameterizedType) functionType).getActualTypeArguments()[0] - : Object.class; + ? ((ParameterizedType) functionType).getActualTypeArguments()[0] + : Object.class; return inputType; } @@ -350,7 +357,7 @@ else if (function instanceof FunctionRegistration) { } if (applicationContext.containsBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX)) { // for Kotlin primarily FunctionRegistration fr = applicationContext - .getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class); + .getBean(functionName + FunctionRegistration.REGISTRATION_NAME_SUFFIX, FunctionRegistration.class); return fr.getType(); } @@ -376,11 +383,17 @@ else if (!(type instanceof ParameterizedType)) { type = FunctionContextUtils.findType(applicationContext.getBeanFactory(), beanDefinitionName); } } + else if (type instanceof ParameterizedType) { + ResolvableType resolvableType = ResolvableType.forType(type); + if (FactoryBean.class.isAssignableFrom(resolvableType.toClass())) { + return resolvableType.getGeneric(0).getType(); + } + } return type; } public static String discoverBeanDefinitionNameByQualifier(ListableBeanFactory beanFactory, String qualifier) { - Map beanMap = BeanFactoryAnnotationUtils.qualifiedBeansOfType(beanFactory, Object.class, qualifier); + Map beanMap = BeanFactoryAnnotationUtils.qualifiedBeansOfType(beanFactory, Object.class, qualifier); if (!CollectionUtils.isEmpty(beanMap) && beanMap.size() == 1) { return beanMap.keySet().iterator().next(); } @@ -397,13 +410,13 @@ public static Type getOutputType(Type functionType) { Type outputType; if (functionType instanceof Class) { functionType = Function.class.isAssignableFrom((Class) functionType) - ? TypeResolver.reify(Function.class, (Class>) functionType) - : TypeResolver.reify(Supplier.class, (Class>) functionType); + ? TypeResolver.reify(Function.class, (Class>) functionType) + : TypeResolver.reify(Supplier.class, (Class>) functionType); } outputType = functionType instanceof ParameterizedType - ? (isSupplier(functionType) ? ((ParameterizedType) functionType).getActualTypeArguments()[0] : ((ParameterizedType) functionType).getActualTypeArguments()[1]) - : Object.class; + ? (isSupplier(functionType) ? ((ParameterizedType) functionType).getActualTypeArguments()[0] : ((ParameterizedType) functionType).getActualTypeArguments()[1]) + : Object.class; return outputType; } @@ -490,18 +503,18 @@ static Type fromFunctionMethod(Method functionalMethod) { Type functionType = null; switch (parameterTypes.length) { case 0: - functionType = ResolvableType.forClassWithGenerics(Supplier.class, - ResolvableType.forMethodReturnType(functionalMethod)).getType(); + functionType = ResolvableType.forClassWithGenerics(Supplier.class, + ResolvableType.forMethodReturnType(functionalMethod)).getType(); break; case 1: if (Void.class.isAssignableFrom(functionalMethod.getReturnType())) { - functionType = ResolvableType.forClassWithGenerics(Consumer.class, - ResolvableType.forMethodParameter(functionalMethod, 0)).getType(); + functionType = ResolvableType.forClassWithGenerics(Consumer.class, + ResolvableType.forMethodParameter(functionalMethod, 0)).getType(); } else { - functionType = ResolvableType.forClassWithGenerics(Function.class, - ResolvableType.forMethodParameter(functionalMethod, 0), - ResolvableType.forMethodReturnType(functionalMethod)).getType(); + functionType = ResolvableType.forClassWithGenerics(Function.class, + ResolvableType.forMethodParameter(functionalMethod, 0), + ResolvableType.forMethodReturnType(functionalMethod)).getType(); } break; default: @@ -528,17 +541,18 @@ private static void assertSupportedTypes(Type type) { if (type instanceof ParameterizedType) { type = ((ParameterizedType) type).getRawType(); Assert.isTrue(type instanceof Class, "Must be one of Supplier, Function, Consumer" - + " or FunctionRegistration. Was " + type); + + " or FunctionRegistration. Was " + type); } Class candidateType = (Class) type; Assert.isTrue(Supplier.class.isAssignableFrom(candidateType) - || Function.class.isAssignableFrom(candidateType) - || Consumer.class.isAssignableFrom(candidateType) - || FunctionRegistration.class.isAssignableFrom(candidateType) - || type.getTypeName().startsWith("org.springframework.context.annotation.ConfigurationClassEnhancer"), "Must be one of Supplier, Function, Consumer" - + " or FunctionRegistration. Was " + type); + || Function.class.isAssignableFrom(candidateType) + || Consumer.class.isAssignableFrom(candidateType) + || FunctionRegistration.class.isAssignableFrom(candidateType) + || type.getTypeName() + .startsWith("org.springframework.context.annotation.ConfigurationClassEnhancer"), "Must be one of Supplier, Function, Consumer" + + " or FunctionRegistration. Was " + type); } private static Type extractReactiveType(Type type) { diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java index cba41a6a4..cdf6380af 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistry.java @@ -25,6 +25,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -416,7 +417,7 @@ public class FunctionInvocationWrapper implements Function, Cons private boolean propagateInputHeaders; - private boolean wrapped; + protected boolean wrapped; private final ThreadLocal> unconvertedResult = new ThreadLocal<>(); @@ -908,7 +909,7 @@ private String contentTypeHeaderValue(Message msg) { if (contentType == null) { contentType = msg.getHeaders().get(HttpHeaders.CONTENT_TYPE); if (contentType == null) { - contentType = msg.getHeaders().get(HttpHeaders.CONTENT_TYPE.toLowerCase()); + contentType = msg.getHeaders().get(HttpHeaders.CONTENT_TYPE.toLowerCase(Locale.ROOT)); } } return Objects.toString(contentType); @@ -1438,9 +1439,15 @@ private Object convertMultipleOutputArgumentTypeIfNecesary(Object output, Type t */ @SuppressWarnings("unchecked") private Object convertOutputMessageIfNecessary(Object output, String expectedOutputContetntType) { - String contentType = ((Message) output).getHeaders().containsKey(FunctionProperties.EXPECT_CONTENT_TYPE_HEADER) - ? (String) ((Message) output).getHeaders().get(FunctionProperties.EXPECT_CONTENT_TYPE_HEADER) - : expectedOutputContetntType; + String contentType; + if (this.isOutputTypeMessage() && ((Message) output).getHeaders().containsKey(MessageHeaders.CONTENT_TYPE)) { + contentType = ((Message) output).getHeaders().get(MessageHeaders.CONTENT_TYPE).toString(); + } + else { + contentType = ((Message) output).getHeaders().containsKey(FunctionProperties.EXPECT_CONTENT_TYPE_HEADER) + ? (String) ((Message) output).getHeaders().get(FunctionProperties.EXPECT_CONTENT_TYPE_HEADER) + : expectedOutputContetntType; + } if (StringUtils.hasText(contentType)) { Map headersMap = new HashMap(((Message) output).getHeaders()); diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java index 7641957c8..2fb21e056 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfiguration.java @@ -25,12 +25,15 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.Module; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.joda.JodaModule; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.google.gson.Gson; import io.cloudevents.spring.messaging.CloudEventMessageConverter; +import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.BeanFactory; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -59,6 +62,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import org.springframework.context.expression.BeanFactoryResolver; +import org.springframework.core.KotlinDetector; import org.springframework.core.ResolvableType; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.core.convert.support.ConfigurableConversionService; @@ -129,7 +133,9 @@ public FunctionRegistry functionCatalog(List messageConverters mcList.add(new StringMessageConverter()); mcList.add(new PrimitiveTypesFromStringMessageConverter(conversionService)); - messageConverter = new SmartCompositeMessageConverter(mcList); + messageConverter = new SmartCompositeMessageConverter(mcList, () -> { + return context.getBeansOfType(MessageConverterHelper.class).values(); + }); if (functionInvocationHelper instanceof CloudEventsFunctionInvocationHelper) { ((CloudEventsFunctionInvocationHelper) functionInvocationHelper).setMessageConverter(messageConverter); } @@ -213,9 +219,30 @@ private JsonMapper gson(ApplicationContext context) { return new GsonMapper(gson); } + @SuppressWarnings("unchecked") private JsonMapper jackson(ApplicationContext context) { - ObjectMapper mapper = new ObjectMapper(); - mapper.registerModule(new JavaTimeModule()); + ObjectMapper mapper; + try { + mapper = context.getBean(ObjectMapper.class); + } + catch (Exception e) { + mapper = new ObjectMapper(); + mapper.registerModule(new JavaTimeModule()); + } + mapper.registerModule(new JodaModule()); + if (KotlinDetector.isKotlinPresent()) { + try { + if (!mapper.getRegisteredModuleIds().contains("com.fasterxml.jackson.module.kotlin.KotlinModule")) { + Class kotlinModuleClass = (Class) + ClassUtils.forName("com.fasterxml.jackson.module.kotlin.KotlinModule", ClassUtils.getDefaultClassLoader()); + Module kotlinModule = BeanUtils.instantiateClass(kotlinModuleClass); + mapper.registerModule(kotlinModule); + } + } + catch (ClassNotFoundException ex) { + // jackson-module-kotlin not available + } + } mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); mapper.configure(DeserializationFeature.FAIL_ON_TRAILING_TOKENS, true); mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java index 5c8828e87..2522d9c48 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/KotlinLambdaToFunctionAutoConfiguration.java @@ -22,7 +22,6 @@ import java.util.function.Function; import java.util.function.Supplier; -import com.fasterxml.jackson.module.kotlin.KotlinModule; import kotlin.Unit; import kotlin.jvm.functions.Function0; import kotlin.jvm.functions.Function1; @@ -37,14 +36,10 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; import org.springframework.cloud.function.context.FunctionRegistration; import org.springframework.cloud.function.context.catalog.FunctionTypeUtils; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.ResolvableType; -import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import org.springframework.util.ObjectUtils; /** @@ -62,19 +57,6 @@ public class KotlinLambdaToFunctionAutoConfiguration { protected final Log logger = LogFactory.getLog(getClass()); - @Bean - @ConditionalOnMissingBean - @ConditionalOnClass(name = {"org.springframework.http.converter.json.Jackson2ObjectMapperBuilder", - "com.fasterxml.jackson.module.kotlin.KotlinModule"}) - Jackson2ObjectMapperBuilderCustomizer customizer() { - return new Jackson2ObjectMapperBuilderCustomizer() { - @Override - public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) { - jacksonObjectMapperBuilder.modulesToInstall(KotlinModule.class); - } - }; - } - @SuppressWarnings({ "unchecked", "rawtypes" }) public static final class KotlinFunctionWrapper implements Function, Supplier, Consumer, diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/MessageConverterHelper.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/MessageConverterHelper.java new file mode 100644 index 000000000..cedb81a61 --- /dev/null +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/MessageConverterHelper.java @@ -0,0 +1,46 @@ +/* + * Copyright 2015-2021 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.cloud.function.context.config; + +import org.springframework.messaging.Message; + +/** + * @author Oleg Zhurakousky + */ +public interface MessageConverterHelper { + + /** + * This method will be called by the framework in cases when a message failed to convert. + * It allows you to signal to the framework if such failure should be considered fatal or not. + * + * @param message failed message + * @return true if conversion failure must be considered fatal. + */ + default boolean shouldFailIfCantConvert(Message message) { + return false; + } + + /** + * This method will be called by the framework in cases when a single message within batch of messages failed to convert. + * It provides a place for providing post-processing logic before message converter returns. + * + * @param message failed message. + * @param index index of failed message within the batch + */ + default void postProcessBatchMessageOnFailure(Message message, int index) { + } +} diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java index b15061df6..8aecd9500 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/RoutingFunction.java @@ -45,10 +45,11 @@ import org.springframework.util.StringUtils; /** - * An implementation of Function which acts as a gateway/router by actually + * An implementation of {@link Function} which acts as a gateway/router by actually * delegating incoming invocation to a function specified .. . * * @author Oleg Zhurakousky + * @author John Blum * @since 2.1 * */ @@ -127,8 +128,7 @@ public Object apply(Object input) { private Object route(Object input, boolean originalInputIsPublisher) { FunctionInvocationWrapper function = null; - if (input instanceof Message) { - Message message = (Message) input; + if (input instanceof Message message) { if (this.routingCallback != null) { String functionDefinition = this.routingCallback.routingResult(message); if (StringUtils.hasText(functionDefinition)) { @@ -155,7 +155,7 @@ else if (StringUtils.hasText(functionProperties.getDefinition())) { } } } - else if (input instanceof Publisher) { + else if (input instanceof Publisher publisher) { if (StringUtils.hasText(functionProperties.getDefinition())) { function = functionFromDefinition(functionProperties.getDefinition()); } @@ -163,9 +163,9 @@ else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { function = this.functionFromExpression(functionProperties.getRoutingExpression(), input); } else { - return input instanceof Mono - ? Mono.from((Publisher) input).map(v -> route(v, originalInputIsPublisher)) - : Flux.from((Publisher) input).map(v -> route(v, originalInputIsPublisher)); + return input instanceof Mono mono + ? Mono.from(mono).map(v -> route(v, originalInputIsPublisher)) + : Flux.from(publisher).map(v -> route(v, originalInputIsPublisher)); } } else { @@ -184,7 +184,7 @@ else if (StringUtils.hasText(functionProperties.getRoutingExpression())) { } } - if (function.getTarget().equals(this)) { + if (this.equals(function.getTarget())) { throw new IllegalStateException("Failed to establish route, and routing to itself is not allowed as it creates a loop. Please provide: " + "'spring.cloud.function.definition' as Message header or as application property or " + "'spring.cloud.function.routing-expression' as application property."); diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/SmartCompositeMessageConverter.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/SmartCompositeMessageConverter.java index 12c3bdf0a..22730d1ec 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/SmartCompositeMessageConverter.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/context/config/SmartCompositeMessageConverter.java @@ -19,8 +19,10 @@ import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.function.Supplier; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -31,6 +33,7 @@ import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.converter.AbstractMessageConverter; import org.springframework.messaging.converter.CompositeMessageConverter; +import org.springframework.messaging.converter.MessageConversionException; import org.springframework.messaging.converter.MessageConverter; import org.springframework.messaging.converter.SmartMessageConverter; import org.springframework.messaging.support.MessageBuilder; @@ -48,13 +51,22 @@ public class SmartCompositeMessageConverter extends CompositeMessageConverter { private Log logger = LogFactory.getLog(this.getClass()); + private final Supplier> messageConverterHelpersSupplier; + public SmartCompositeMessageConverter(Collection converters) { + this(converters, null); + } + + public SmartCompositeMessageConverter(Collection converters, Supplier> messageConverterHelpersSupplier) { super(converters); + this.messageConverterHelpersSupplier = messageConverterHelpersSupplier; } @Override @Nullable public Object fromMessage(Message message, Class targetClass) { + Collection messageConverterHelpers = this.messageConverterHelpersSupplier != null + ? this.messageConverterHelpersSupplier.get() : Collections.emptyList(); for (MessageConverter converter : getConverters()) { if (!(message.getPayload() instanceof byte[]) && targetClass.isInstance(message.getPayload()) && !(message.getPayload() instanceof Collection)) { return message.getPayload(); @@ -71,12 +83,15 @@ public Object fromMessage(Message message, Class targetClass) { } } } + this.failConversionIfNecessary(message, messageConverterHelpers); return null; } @SuppressWarnings("unchecked") @Override public Object fromMessage(Message message, Class targetClass, @Nullable Object conversionHint) { + Collection messageConverterHelpers = this.messageConverterHelpersSupplier != null + ? this.messageConverterHelpersSupplier.get() : Collections.emptyList(); if (!(message.getPayload() instanceof byte[]) && targetClass.isInstance(message.getPayload()) && !(message.getPayload() instanceof Collection)) { return message.getPayload(); } @@ -105,8 +120,12 @@ public Object fromMessage(Message message, Class targetClass, @Nullable Ob } } } + if (!isConverted) { + this.postProcessBatchMessage(message, messageConverterHelpers, resultList.size()); + this.failConversionIfNecessary(message, messageConverterHelpers); + } } - result = resultList; + return resultList; } else { for (MessageConverter converter : getConverters()) { @@ -120,10 +139,25 @@ public Object fromMessage(Message message, Class targetClass, @Nullable Ob } } } - + this.failConversionIfNecessary(message, messageConverterHelpers); return result; } + private void failConversionIfNecessary(Message message, Collection messageConverterHelpers) { + for (MessageConverterHelper messageConverterHelper : messageConverterHelpers) { + if (messageConverterHelper.shouldFailIfCantConvert(message)) { + throw new MessageConversionException("Failed to convert Message: " + message + + ". None of the available Message converters were able to convert this Message"); + } + } + } + + private void postProcessBatchMessage(Message message, Collection messageConverterHelpers, int index) { + for (MessageConverterHelper messageConverterHelper : messageConverterHelpers) { + messageConverterHelper.postProcessBatchMessageOnFailure(message, index); + } + } + @Override @Nullable public Message toMessage(Object payload, @Nullable MessageHeaders headers) { diff --git a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionMessageUtils.java b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionMessageUtils.java index 76a6974c0..8e021650c 100644 --- a/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionMessageUtils.java +++ b/spring-cloud-function-context/src/main/java/org/springframework/cloud/function/utils/FunctionMessageUtils.java @@ -17,6 +17,8 @@ package org.springframework.cloud.function.utils; +import java.util.Locale; + import org.springframework.cloud.function.context.message.MessageUtils; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; @@ -59,7 +61,7 @@ else if (key.startsWith("aws_")) { else if (key.startsWith("solace_")) { return "solace"; } - else if (key.toLowerCase().equals("user-agent") || key.toLowerCase().equals("accept-encoding") || key.toLowerCase().equals("host")) { + else if (key.toLowerCase(Locale.ROOT).equals("user-agent") || key.toLowerCase(Locale.ROOT).equals("accept-encoding") || key.toLowerCase(Locale.ROOT).equals("host")) { return "http"; } // add rsocket diff --git a/spring-cloud-function-context/src/main/resources/META-INF/spring-configuration-metadata.json b/spring-cloud-function-context/src/main/resources/META-INF/spring-configuration-metadata.json index 18830d28d..c5e320e15 100644 --- a/spring-cloud-function-context/src/main/resources/META-INF/spring-configuration-metadata.json +++ b/spring-cloud-function-context/src/main/resources/META-INF/spring-configuration-metadata.json @@ -21,4 +21,4 @@ "defaultValue": false } ] -} +} \ No newline at end of file diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HybridFunctionalRegistrationTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HybridFunctionalRegistrationTests.java index fd8b3d843..b33445084 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HybridFunctionalRegistrationTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/HybridFunctionalRegistrationTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.context; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -90,7 +91,7 @@ public static class UppercaseFunction implements Function { @Override public String apply(String t) { - return t.toUpperCase(); + return t.toUpperCase(Locale.ROOT); } } @@ -105,7 +106,7 @@ public static class UppercaseMessageFunction implements Function public String apply(Message message) { assertThat(message.getHeaders().get("foo")).isEqualTo("foo"); assertThat(message.getHeaders().get("blah")).isEqualTo("blah"); - return message.getPayload().toUpperCase(); + return message.getPayload().toUpperCase(Locale.ROOT); } } @@ -118,7 +119,7 @@ public static class UppercaseFluxFunction implements Function, Flux @Override public Flux apply(Flux flux) { - return flux.map(v -> v.toUpperCase()); + return flux.map(v -> v.toUpperCase(Locale.ROOT)); } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryMultiInOutTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryMultiInOutTests.java index 4039e1cd2..2c979741a 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryMultiInOutTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryMultiInOutTests.java @@ -20,6 +20,7 @@ import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.function.BiFunction; import java.util.function.Function; @@ -303,7 +304,7 @@ protected static class SampleFunctionConfiguration { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } // ============= MULTI-INPUT and MULTI-OUTPUT functions ============ diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java index 9407a1c11..5cf08fee7 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwareFunctionRegistryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019-2021 the original author or authors. + * Copyright 2019-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,6 +27,7 @@ import java.util.Date; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -42,7 +43,6 @@ import java.util.stream.Collectors; import com.fasterxml.jackson.databind.JsonNode; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -53,6 +53,7 @@ import reactor.util.function.Tuple3; import reactor.util.function.Tuples; +import org.springframework.beans.factory.FactoryBean; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.builder.SpringApplicationBuilder; @@ -79,11 +80,13 @@ import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; +import static org.assertj.core.api.Fail.fail; + /** * * @author Oleg Zhurakousky + * @author Artem Bilan * */ public class BeanFactoryAwareFunctionRegistryTests { @@ -432,9 +435,13 @@ public void testCompositionSupplierAndFunction() { public void testReactiveFunctionWithImperativeInputAndOutputFail() { FunctionCatalog catalog = this.configureCatalog(); Function reverse = catalog.lookup("reverseFlux"); - Assertions.assertThrows(ClassCastException.class, () -> { + try { String result = reverse.apply("reverseFlux"); - }); + fail("The function should return a Flux"); + } + catch (ClassCastException e) { + // ignore + } } @Test @@ -681,7 +688,7 @@ public void testRegisteringWithTypeThatDoesNotMatchDiscoveredType() { FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "a") .type(FunctionTypeUtils.functionType(Integer.class, String.class)); registry.register(registration); - fail(); + fail(""); } catch (IllegalArgumentException e) { // good as we expect it to fail @@ -691,7 +698,7 @@ public void testRegisteringWithTypeThatDoesNotMatchDiscoveredType() { FunctionRegistration registration = new FunctionRegistration(new MyFunction(), "b") .type(FunctionTypeUtils.functionType(String.class, Integer.class)); registry.register(registration); - fail(); + fail(""); } catch (IllegalArgumentException e) { // good as we expect it to fail @@ -890,6 +897,14 @@ public void testArrayPayloadOnFluxFunction() throws Exception { assertThat(result.size()).isEqualTo(3); } + @Test + void functionFromFactoryBeanIsProperlyResolved() { + FunctionCatalog catalog = configureCatalog(); + Function numberToStringFactoryBean = catalog.lookup("numberToStringFactoryBean"); + assertThat(numberToStringFactoryBean).isNotNull(); + assertThat(numberToStringFactoryBean.apply(1)).isEqualTo("1"); + } + @Test // see GH-707 public void testConcurrencyOnLookup() throws Exception { @@ -916,7 +931,7 @@ public static class PojoToMessageFunctionCompositionConfiguration { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean @@ -931,7 +946,7 @@ public Function toJson() { @Bean public Function, String> uppercasePerson() { - return v -> v.getPayload().getName().toUpperCase(); + return v -> v.getPayload().getName().toUpperCase(Locale.ROOT); } } @@ -1121,7 +1136,7 @@ protected boolean supports(Class clazz) { protected static class WrappedWithAroundAdviseConfiguration { @Bean public Function, Message> uppercase() { - return v -> MessageBuilder.withPayload(v.getPayload().toUpperCase()).copyHeaders(v.getHeaders()).build(); + return v -> MessageBuilder.withPayload(v.getPayload().toUpperCase(Locale.ROOT)).copyHeaders(v.getHeaders()).build(); } @Bean @@ -1148,7 +1163,7 @@ protected static class InputHeaderPropagationConfiguration { @Bean public Function uppercase() { - return x -> x.toUpperCase(); + return x -> x.toUpperCase(Locale.ROOT); } } @@ -1166,7 +1181,7 @@ public Function>>, Flux> echoGenericO @Bean public Function uppercasePerson() { return person -> { - return new Person(person.getName().toUpperCase(), person.getId()); + return new Person(person.getName().toUpperCase(Locale.ROOT), person.getId()); }; } @@ -1178,7 +1193,7 @@ public Supplier numberword() { @Bean public BiFunction biFuncUpperCase() { return (p, h) -> { - return p.toUpperCase(); + return p.toUpperCase(Locale.ROOT); }; } @@ -1192,7 +1207,7 @@ public Function, Person> maptopojo() { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean @@ -1214,7 +1229,7 @@ public Function consumerFunction() { @Bean public Function, Flux> uppercaseFlux() { - return flux -> flux.map(v -> v.toUpperCase()); + return flux -> flux.map(v -> v.toUpperCase(Locale.ROOT)); } @Bean @@ -1341,6 +1356,23 @@ public Consumer> reactiveConsumer() { public Consumer> reactivePojoConsumer() { return flux -> flux.subscribe(v -> consumerInputRef.set(v)); } + + @Bean + FactoryBean> numberToStringFactoryBean() { + return new FactoryBean<>() { + + @Override + public Function getObject() { + return Number::toString; + } + + @Override + public Class getObjectType() { + return Function.class; + } + + }; + } } @EnableAutoConfiguration @@ -1499,7 +1531,7 @@ public Message apply(String t) { public static class ComplexTypeFunctionConfiguration { @Bean public Function, String> function() { - return v -> v.getData().getName().toUpperCase(); + return v -> v.getData().getName().toUpperCase(Locale.ROOT); } } @@ -1578,12 +1610,12 @@ public static class CompositionReactiveSupplierWithConsumer { @Bean public Function, Flux> functionPrimitive() { - return flux -> flux.map(v -> v.toUpperCase()); + return flux -> flux.map(v -> v.toUpperCase(Locale.ROOT)); } @Bean public Function>, Flux>> functionMessage() { - return flux -> flux.map(v -> MessageBuilder.withPayload(v.getPayload().toUpperCase()).build()); + return flux -> flux.map(v -> MessageBuilder.withPayload(v.getPayload().toUpperCase(Locale.ROOT)).build()); } @Bean diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwarePojoFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwarePojoFunctionRegistryTests.java index accc4c5ba..e07f464c7 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwarePojoFunctionRegistryTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/BeanFactoryAwarePojoFunctionRegistryTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.context.catalog; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -123,7 +124,7 @@ public Function func() { // POJO Function that implements Function private static class MyFunction implements Function { public String uppercase(String value) { - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } @Override @@ -135,7 +136,7 @@ public String apply(String t) { // POJO Function public static class MyFunctionLike { public String uppercase(String value) { - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java index 5f2fb2fb9..3cee3dc71 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/catalog/SimpleFunctionRegistryTests.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.UUID; @@ -40,7 +41,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.gson.Gson; import com.google.protobuf.StringValue; -import org.junit.jupiter.api.Assertions; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -504,7 +505,8 @@ public void testReactiveFunctionMessages() { .build() )); - Assertions.assertIterableEquals(result.blockFirst(), Arrays.asList("item1", "item2")); + List blockFirst = result.blockFirst(); + Assertions.assertThatIterable(blockFirst).isEqualTo(Arrays.asList("item1", "item2")); } @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -665,7 +667,7 @@ void biConsumerUserFunctionTypeIsKnownInFunctionInvocationWrapper() { } public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @@ -779,7 +781,7 @@ private static class UpperCase implements Function { @Override public String apply(String t) { - return t.toUpperCase(); + return t.toUpperCase(Locale.ROOT); } } @@ -798,7 +800,7 @@ private static class UpperCaseMessage @Override public Message apply(Message t) { - return MessageBuilder.withPayload(t.getPayload().toUpperCase()) + return MessageBuilder.withPayload(t.getPayload().toUpperCase(Locale.ROOT)) .copyHeaders(t.getHeaders()).build(); } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java index 6e4230b0d..16affbe39 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogAutoConfigurationTests.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -476,7 +477,7 @@ protected static class SimpleConfiguration { @Bean public Function function() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } @Bean @@ -543,7 +544,7 @@ protected static class DependencyInjectionConfiguration { @Bean public Function foos(String foo) { - return value -> new Foo(foo + ": " + value.toUpperCase()); + return value -> new Foo(foo + ": " + value.toUpperCase(Locale.ROOT)); } @Bean @@ -560,7 +561,7 @@ protected static class FunctionConfiguration @Override public Flux apply(Flux flux) { - return flux.map(foo -> new Foo(value() + ": " + foo.toUpperCase())); + return flux.map(foo -> new Foo(value() + ": " + foo.toUpperCase(Locale.ROOT))); } @Bean @@ -588,7 +589,7 @@ protected static class AmbiguousConfiguration { @Bean public Function foos() { - return value -> new Foo(value.toUpperCase()); + return value -> new Foo(value.toUpperCase(Locale.ROOT)); } @Bean @@ -605,7 +606,7 @@ protected static class MultipleConfiguration { @Bean public Function foos() { - return value -> new Foo(value.toUpperCase()); + return value -> new Foo(value.toUpperCase(Locale.ROOT)); } @Bean @@ -632,7 +633,7 @@ protected static class GenericConfiguration { @Bean public Function, Map> function() { return m -> m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().toString().toUpperCase())); + e -> e.getValue().toString().toUpperCase(Locale.ROOT))); } } @@ -726,7 +727,7 @@ protected static class GenericFluxConfiguration { @Bean public Function>, Flux>> function() { return flux -> flux.map(m -> m.entrySet().stream().collect(Collectors - .toMap(e -> e.getKey(), e -> e.getValue().toString().toUpperCase()))); + .toMap(e -> e.getKey(), e -> e.getValue().toString().toUpperCase(Locale.ROOT)))); } } @@ -738,7 +739,7 @@ protected static class FluxMessageConfiguration { @Bean public Function>, Flux>> function() { return flux -> flux.map(m -> MessageBuilder - .withPayload(m.getPayload().toUpperCase()).build()); + .withPayload(m.getPayload().toUpperCase(Locale.ROOT)).build()); } } @@ -750,7 +751,7 @@ protected static class PublisherMessageConfiguration { @Bean public Function>, Publisher>> function() { return flux -> Flux.from(flux).map(m -> MessageBuilder - .withPayload(m.getPayload().toUpperCase()).build()); + .withPayload(m.getPayload().toUpperCase(Locale.ROOT)).build()); } } @@ -784,7 +785,7 @@ protected static class MessageConfiguration { @Bean public Function, Message> function() { - return m -> MessageBuilder.withPayload(m.getPayload().toUpperCase()).build(); + return m -> MessageBuilder.withPayload(m.getPayload().toUpperCase(Locale.ROOT)).build(); } } @@ -796,7 +797,7 @@ protected static class QualifiedConfiguration { @Bean @Qualifier("other") public Function function() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } @@ -807,7 +808,7 @@ protected static class AliasConfiguration { @Bean({ "function", "other" }) public Function function() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } @@ -824,7 +825,7 @@ public FunctionRegistration> registration() { @Bean public Function function() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } @@ -861,7 +862,7 @@ public Class getObjectType() { @Override protected Function createInstance() throws Exception { - return s -> s.toUpperCase() + "-bar"; + return s -> s.toUpperCase(Locale.ROOT) + "-bar"; } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java index 3b3075a73..f762a31d2 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/ContextFunctionCatalogInitializerTests.java @@ -20,14 +20,15 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; import com.google.gson.Gson; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; @@ -115,14 +116,13 @@ public void compose() { @Test public void missingType() { - Assertions.assertThrows(BeanCreationException.class, () -> { + try { create(MissingTypeConfiguration.class); - assertThat(this.context.getBean("function")) - .isInstanceOf(FunctionRegistration.class); - assertThat((Function) this.catalog.lookup(Function.class, "function")) - .isInstanceOf(Function.class); - // TODO: support for type inference from functional bean registrations - }); + Assertions.fail(""); + } + catch (BeanCreationException e) { + // ignore, the test call must fail + } } @Test @@ -235,7 +235,7 @@ public void initialize(GenericApplicationContext context) { @Bean public Function function() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } @@ -264,7 +264,7 @@ public void initialize(GenericApplicationContext context) { public Function function() { return person -> { Person p = new Person(); - p.setName(person.getName().toUpperCase()); + p.setName(person.getName().toUpperCase(Locale.ROOT)); return p; }; } @@ -359,7 +359,7 @@ public void initialize(GenericApplicationContext context) { @Bean public Function foos(String foo) { - return value -> new Foo(foo + ": " + value.toUpperCase()); + return value -> new Foo(foo + ": " + value.toUpperCase(Locale.ROOT)); } @Bean @@ -383,7 +383,7 @@ public void initialize(GenericApplicationContext context) { @Override public Flux apply(Flux flux) { - return flux.map(foo -> new Foo(value() + ": " + foo.toUpperCase())); + return flux.map(foo -> new Foo(value() + ": " + foo.toUpperCase(Locale.ROOT))); } @Bean diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java index 20e5ce9c6..544059029 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/config/RoutingFunctionTests.java @@ -21,8 +21,8 @@ import java.util.Map; import java.util.function.Function; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import reactor.core.publisher.Flux; import reactor.test.StepVerifier; @@ -43,7 +43,6 @@ import org.springframework.messaging.support.MessageBuilder; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; /** * @@ -83,7 +82,7 @@ public void testDefaultRouting() { assertThat(function).isNotNull(); try { function.apply(message); - fail(); + Assertions.fail(""); } catch (Exception e) { // Good @@ -207,7 +206,7 @@ public void failWithHeaderProvidedExpressionAccessingRuntime() { .build(); try { function.apply(message); - fail(); + Assertions.fail(""); } catch (Exception e) { assertThat(e.getMessage()).isEqualTo("EL1005E: Type cannot be found 'java.lang.Runtime'"); @@ -271,7 +270,7 @@ public void testOtherExpectedFailures() { // no function.definition header or function property try { function.apply(MessageBuilder.withPayload("hello").build()); - Assertions.fail(); + Assertions.fail(""); } catch (Exception e) { // ignore @@ -281,7 +280,7 @@ public void testOtherExpectedFailures() { try { function.apply(MessageBuilder.withPayload("hello") .setHeader(FunctionProperties.PREFIX + ".definition", "blah").build()); - Assertions.fail(); + Assertions.fail(""); } catch (Exception e) { // ignore diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java index ad12ee4b9..5a8c2cb7e 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/scan/TestFunction.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.context.scan; +import java.util.Locale; import java.util.function.Function; /** @@ -26,7 +27,7 @@ public class TestFunction implements Function { @Override public String apply(String t) { - return t.toUpperCase(); + return t.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/string/FunctionalStringSourceTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/string/FunctionalStringSourceTests.java index 585e2043a..f336e476c 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/string/FunctionalStringSourceTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/string/FunctionalStringSourceTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.context.string; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -52,7 +53,7 @@ protected static class TestConfiguration implements Function { @Override public String apply(String value) { - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/test/FunctionalTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/test/FunctionalTests.java index a22958732..b7cd6bf65 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/test/FunctionalTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/context/test/FunctionalTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.context.test; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -49,7 +50,7 @@ protected static class TestConfiguration implements Function { @Override public String apply(String value) { - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/inject/FooConfiguration.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/inject/FooConfiguration.java index e7db39122..a62f653b3 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/inject/FooConfiguration.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/inject/FooConfiguration.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.inject; +import java.util.Locale; import java.util.function.Function; import org.springframework.cloud.function.context.config.ContextFunctionCatalogAutoConfigurationTests.Foo; @@ -27,7 +28,7 @@ public class FooConfiguration { @Bean public Function foos(String foo) { - return value -> new Foo(foo + ": " + value.toUpperCase()); + return value -> new Foo(foo + ": " + value.toUpperCase(Locale.ROOT)); } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java index 83f49d38d..a829ad573 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/scan/ScannedFunction.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.scan; +import java.util.Locale; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @@ -33,7 +34,7 @@ public class ScannedFunction @Override public Map apply(Map m) { return m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().toString().toUpperCase())); + e -> e.getValue().toString().toUpperCase(Locale.ROOT))); } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/test/GenericFunction.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/test/GenericFunction.java index 46ae2b9d4..4389f4aff 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/test/GenericFunction.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/test/GenericFunction.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.test; +import java.util.Locale; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; @@ -33,7 +34,7 @@ public class GenericFunction { @Bean public Function, Map> function() { return m -> m.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), - e -> e.getValue().toString().toUpperCase())); + e -> e.getValue().toString().toUpperCase(Locale.ROOT))); } } diff --git a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMapperTests.java b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMapperTests.java index 30207d367..1b7e28eef 100644 --- a/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMapperTests.java +++ b/spring-cloud-function-context/src/test/java/org/springframework/cloud/function/utils/JsonMapperTests.java @@ -16,6 +16,10 @@ package org.springframework.cloud.function.utils; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.time.ZonedDateTime; +import java.util.Date; import java.util.List; import java.util.stream.Stream; @@ -27,10 +31,15 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.cloud.function.json.GsonMapper; import org.springframework.cloud.function.json.JacksonMapper; import org.springframework.cloud.function.json.JsonMapper; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Configuration; import org.springframework.core.ResolvableType; +import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; @@ -67,6 +76,26 @@ public void objectNode_isJsonStringRepresentsCollection() { assertThat(JsonMapper.isJsonStringRepresentsCollection(nodeAsString)).isFalse(); } + // see https://github.com/spring-cloud/spring-cloud-function/issues/1189 + @Test + public void testJsonDateTimeConversion() { + ApplicationContext context = SpringApplication.run(EmptyConfiguration.class); + JsonMapper jsonMapper = context.getBean(JsonMapper.class); + StringVsTimestamp dom = new StringVsTimestamp("2024-10-16T16:13:29.964361+02:00"); + String convertedJson = new String(jsonMapper.toJson(dom), StandardCharsets.UTF_8); + assertThat(convertedJson).contains("\"zonedDateTime\":\"2024-10-16T16:13:29.964361+02:00\""); + } + + @Test + public void testKotlinModuleRegistration() throws Exception { + ApplicationContext context = SpringApplication.run(EmptyConfiguration.class); + JsonMapper jsonMapper = context.getBean(JsonMapper.class); + Field mapperField = ReflectionUtils.findField(jsonMapper.getClass(), "mapper"); + mapperField.setAccessible(true); + ObjectMapper mapper = (ObjectMapper) mapperField.get(jsonMapper); + assertThat(mapper.getRegisteredModuleIds()).contains("com.fasterxml.jackson.module.kotlin.KotlinModule"); + } + @ParameterizedTest @MethodSource("params") public void vanillaArray(JsonMapper mapper) { @@ -140,4 +169,48 @@ public void setValue(String value) { } + @EnableAutoConfiguration + @Configuration + static class EmptyConfiguration { + + } + + static class StringVsTimestamp { + + private String type; + + private Date date; + + private ZonedDateTime zonedDateTime; + + StringVsTimestamp(String zonedDate) { + type = "StringVsTimestamp"; + date = new Date(); + zonedDateTime = ZonedDateTime.parse(zonedDate); + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Date getDate() { + return date; + } + + public void setDate(Date date) { + this.date = date; + } + + public ZonedDateTime getZonedDateTime() { + return zonedDateTime; + } + + public void setZonedDateTime(ZonedDateTime zonedDateTime) { + this.zonedDateTime = zonedDateTime; + } + } } diff --git a/spring-cloud-function-core/pom.xml b/spring-cloud-function-core/pom.xml index 4922661de..93984bc88 100644 --- a/spring-cloud-function-core/pom.xml +++ b/spring-cloud-function-core/pom.xml @@ -12,7 +12,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/core/FunctionInvocationHelper.java b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/core/FunctionInvocationHelper.java index ccd11c6e2..5c72fb92b 100644 --- a/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/core/FunctionInvocationHelper.java +++ b/spring-cloud-function-core/src/main/java/org/springframework/cloud/function/core/FunctionInvocationHelper.java @@ -21,13 +21,22 @@ /** * * @author Oleg Zhurakousky + * @author John Blum * @since 3.1 * */ public interface FunctionInvocationHelper { - default boolean isRetainOuputAsMessage(I input) { + default boolean isRetainOutputAsMessage(I input) { return true; + } + + /** + * @deprecated Use {@link #isRetainOutputAsMessage(I)} + */ + @Deprecated + default boolean isRetainOuputAsMessage(I input) { + return isRetainOutputAsMessage(input); }; default I preProcessInput(I input, Object inputConverter) { diff --git a/spring-cloud-function-dependencies/pom.xml b/spring-cloud-function-dependencies/pom.xml index 7929e6ad6..8a87e21ed 100644 --- a/spring-cloud-function-dependencies/pom.xml +++ b/spring-cloud-function-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT spring-cloud-function-dependencies - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT pom Spring Cloud Function Dependencies Spring Cloud Function Dependencies diff --git a/spring-cloud-function-deployer/pom.xml b/spring-cloud-function-deployer/pom.xml index 4d3c364f1..d3c3a8d1e 100644 --- a/spring-cloud-function-deployer/pom.xml +++ b/spring-cloud-function-deployer/pom.xml @@ -10,7 +10,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml b/spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml index 2ae5ab9a3..67e5cce72 100644 --- a/spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml +++ b/spring-cloud-function-deployer/src/it/bootapp-multi/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 17 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-javax/pom.xml b/spring-cloud-function-deployer/src/it/bootapp-with-javax/pom.xml index e3cb92290..6ad01c609 100644 --- a/spring-cloud-function-deployer/src/it/bootapp-with-javax/pom.xml +++ b/spring-cloud-function-deployer/src/it/bootapp-with-javax/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 17 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/SimpleFunctionAppApplication.java index ed8f694ef..54f837216 100644 --- a/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/SimpleFunctionAppApplication.java +++ b/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/SimpleFunctionAppApplication.java @@ -1,5 +1,6 @@ package function.example; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringApplication; @@ -25,7 +26,7 @@ public Function uppercasePerson() { return person -> { Person p = new Person(); p.setId(person.getId()); - p.setName(person.getName().toUpperCase()); + p.setName(person.getName().toUpperCase(Locale.ROOT)); return p; }; } diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/UpperCaseFunction.java index 735ad65fb..723035247 100644 --- a/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/UpperCaseFunction.java +++ b/spring-cloud-function-deployer/src/it/bootapp-with-javax/src/main/java/function/example/UpperCaseFunction.java @@ -1,5 +1,6 @@ package function.example; +import java.util.Locale; import java.util.function.Function; import javax.mail.Address; @@ -17,7 +18,7 @@ public String apply(String value) { catch (AddressException e) { throw new IllegalStateException("Failed to create and address: ", e); } - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-scf/pom.xml b/spring-cloud-function-deployer/src/it/bootapp-with-scf/pom.xml index 92be0094c..74aa8170c 100644 --- a/spring-cloud-function-deployer/src/it/bootapp-with-scf/pom.xml +++ b/spring-cloud-function-deployer/src/it/bootapp-with-scf/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 17 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/SimpleFunctionAppApplication.java index 63e0719c3..40ee3eaa3 100644 --- a/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/SimpleFunctionAppApplication.java +++ b/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/SimpleFunctionAppApplication.java @@ -1,5 +1,6 @@ package function.example; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringApplication; @@ -31,7 +32,7 @@ public Function uppercasePerson() { return person -> { Person p = new Person(); p.setId(person.getId()); - p.setName(person.getName().toUpperCase()); + p.setName(person.getName().toUpperCase(Locale.ROOT)); return p; }; } diff --git a/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/UpperCaseFunction.java index 859a54a58..7c3571826 100644 --- a/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/UpperCaseFunction.java +++ b/spring-cloud-function-deployer/src/it/bootapp-with-scf/src/main/java/function/example/UpperCaseFunction.java @@ -1,5 +1,6 @@ package function.example; +import java.util.Locale; import java.util.function.Function; public class UpperCaseFunction implements Function { @@ -7,7 +8,7 @@ public class UpperCaseFunction implements Function { @Override public String apply(String value) { System.out.println("Uppercasing " + value); - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-deployer/src/it/bootapp/pom.xml b/spring-cloud-function-deployer/src/it/bootapp/pom.xml index 5a695be71..026937126 100644 --- a/spring-cloud-function-deployer/src/it/bootapp/pom.xml +++ b/spring-cloud-function-deployer/src/it/bootapp/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 17 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java b/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java index ed8f694ef..54f837216 100644 --- a/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java +++ b/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/SimpleFunctionAppApplication.java @@ -1,5 +1,6 @@ package function.example; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringApplication; @@ -25,7 +26,7 @@ public Function uppercasePerson() { return person -> { Person p = new Person(); p.setId(person.getId()); - p.setName(person.getName().toUpperCase()); + p.setName(person.getName().toUpperCase(Locale.ROOT)); return p; }; } diff --git a/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java index 859a54a58..7c3571826 100644 --- a/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java +++ b/spring-cloud-function-deployer/src/it/bootapp/src/main/java/function/example/UpperCaseFunction.java @@ -1,5 +1,6 @@ package function.example; +import java.util.Locale; import java.util.function.Function; public class UpperCaseFunction implements Function { @@ -7,7 +8,7 @@ public class UpperCaseFunction implements Function { @Override public String apply(String value) { System.out.println("Uppercasing " + value); - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml b/spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml index 690c3c61f..276c2a6fe 100644 --- a/spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml +++ b/spring-cloud-function-deployer/src/it/bootjar-multi/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 17 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-deployer/src/it/bootjar/pom.xml b/spring-cloud-function-deployer/src/it/bootjar/pom.xml index e523b9e04..edd7a75f7 100644 --- a/spring-cloud-function-deployer/src/it/bootjar/pom.xml +++ b/spring-cloud-function-deployer/src/it/bootjar/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 17 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java index 859a54a58..7c3571826 100644 --- a/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java +++ b/spring-cloud-function-deployer/src/it/bootjar/src/main/java/function/example/UpperCaseFunction.java @@ -1,5 +1,6 @@ package function.example; +import java.util.Locale; import java.util.function.Function; public class UpperCaseFunction implements Function { @@ -7,7 +8,7 @@ public class UpperCaseFunction implements Function { @Override public String apply(String value) { System.out.println("Uppercasing " + value); - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml b/spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml index d1cd642a0..cca004bf7 100644 --- a/spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml +++ b/spring-cloud-function-deployer/src/it/bootjarnostart/pom.xml @@ -12,13 +12,13 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 17 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-deployer/src/it/bootjarnostart/src/main/java/function/example/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/bootjarnostart/src/main/java/function/example/UpperCaseFunction.java index 859a54a58..7c3571826 100644 --- a/spring-cloud-function-deployer/src/it/bootjarnostart/src/main/java/function/example/UpperCaseFunction.java +++ b/spring-cloud-function-deployer/src/it/bootjarnostart/src/main/java/function/example/UpperCaseFunction.java @@ -1,5 +1,6 @@ package function.example; +import java.util.Locale; import java.util.function.Function; public class UpperCaseFunction implements Function { @@ -7,7 +8,7 @@ public class UpperCaseFunction implements Function { @Override public String apply(String value) { System.out.println("Uppercasing " + value); - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-deployer/src/it/simplestjarcs/src/main/java/functions/UpperCaseFunction.java b/spring-cloud-function-deployer/src/it/simplestjarcs/src/main/java/functions/UpperCaseFunction.java index 07f535dc1..1cca83da1 100644 --- a/spring-cloud-function-deployer/src/it/simplestjarcs/src/main/java/functions/UpperCaseFunction.java +++ b/spring-cloud-function-deployer/src/it/simplestjarcs/src/main/java/functions/UpperCaseFunction.java @@ -1,5 +1,6 @@ package functions; +import java.util.Locale; import java.util.function.Function; public class UpperCaseFunction implements Function { @@ -7,7 +8,7 @@ public class UpperCaseFunction implements Function { @Override public String apply(String value) { System.out.println("Uppercasing " + value); - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-integration/pom.xml b/spring-cloud-function-integration/pom.xml index b56f997c6..cf2c136b4 100644 --- a/spring-cloud-function-integration/pom.xml +++ b/spring-cloud-function-integration/pom.xml @@ -12,7 +12,7 @@ spring-cloud-function-parent org.springframework.cloud - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-kotlin/pom.xml b/spring-cloud-function-kotlin/pom.xml index f5ff9c55e..a5d6e5e8e 100644 --- a/spring-cloud-function-kotlin/pom.xml +++ b/spring-cloud-function-kotlin/pom.xml @@ -12,7 +12,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageSuspendTests.kt b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageSuspendTests.kt index 85ad7f68f..ec1204aaa 100644 --- a/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageSuspendTests.kt +++ b/spring-cloud-function-kotlin/src/test/kotlin/org/springframework/cloud/function/kotlin/web/HeadersToMessageSuspendTests.kt @@ -18,6 +18,7 @@ package org.springframework.cloud.function.kotlin.web import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map import org.assertj.core.api.Assertions +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.autoconfigure.EnableAutoConfiguration @@ -40,6 +41,7 @@ import java.net.URI webEnvironment = WebEnvironment.RANDOM_PORT, properties = ["spring.cloud.function.web.path=/functions", "spring.main.web-application-type=reactive"] ) +@Disabled @ContextConfiguration(classes = [RestApplication::class, HeadersToMessageSuspendTests.TestConfiguration::class]) open class HeadersToMessageSuspendTests { @Autowired diff --git a/spring-cloud-function-rsocket/pom.xml b/spring-cloud-function-rsocket/pom.xml index c558f3220..d2cf50518 100644 --- a/spring-cloud-function-rsocket/pom.xml +++ b/spring-cloud-function-rsocket/pom.xml @@ -12,7 +12,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessageRoutingCallbackRSocketTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessageRoutingCallbackRSocketTests.java index 349566c1e..fcf26adb9 100644 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessageRoutingCallbackRSocketTests.java +++ b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessageRoutingCallbackRSocketTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.rsocket; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -117,22 +118,22 @@ public String routingResult(Message message) { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean public Function, Message> uppercaseMessage() { - return m -> MessageBuilder.withPayload(m.getPayload().toUpperCase()).copyHeaders(m.getHeaders()).build(); + return m -> MessageBuilder.withPayload(m.getPayload().toUpperCase(Locale.ROOT)).copyHeaders(m.getHeaders()).build(); } @Bean public Function, Flux> uppercaseReactive() { - return flux -> flux.map(v -> v.toUpperCase()); + return flux -> flux.map(v -> v.toUpperCase(Locale.ROOT)); } @Bean public Function>, Flux>> uppercaseReactiveMessage() { - return flux -> flux.map(m -> MessageBuilder.withPayload(m.getPayload().toUpperCase()).copyHeaders(m.getHeaders()).build()); + return flux -> flux.map(m -> MessageBuilder.withPayload(m.getPayload().toUpperCase(Locale.ROOT)).copyHeaders(m.getHeaders()).build()); } @Bean diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessagingTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessagingTests.java index 5238a87ba..726ae7a7a 100644 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessagingTests.java +++ b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/MessagingTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.rsocket; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -155,7 +156,7 @@ public void testPojoMessageToPojoViaMessage() { Message message = MessageBuilder.withPayload(p).setHeader("someHeader", "foo").build(); Person result = new Person(); - result.setName(p.getName().toUpperCase()); + result.setName(p.getName().toUpperCase(Locale.ROOT)); rsocketRequesterBuilder.tcp("localhost", port) .route("pojoMessageToPojo") .data(message) @@ -190,7 +191,7 @@ public void testPojoMessageToPojoViaMap() { Map map = jsonMapper.fromJson(message, Map.class); Person result = new Person(); - result.setName(p.getName().toUpperCase()); + result.setName(p.getName().toUpperCase(Locale.ROOT)); rsocketRequesterBuilder.tcp("localhost", port) .route("pojoMessageToPojo") .data(map) @@ -330,7 +331,7 @@ public static class MessagingConfiguration { @Bean public Function pojoToString() { return v -> { - return v.getName().toUpperCase(); + return v.getName().toUpperCase(Locale.ROOT); }; } @@ -355,7 +356,7 @@ public Function, Person> pojoMessageToPojo() { return p -> { assertThat(p.getHeaders().get("someHeader").equals("foo")); Person newPerson = new Person(); - newPerson.setName(p.getPayload().getName().toUpperCase()); + newPerson.setName(p.getPayload().getName().toUpperCase(Locale.ROOT)); return newPerson; }; } @@ -365,7 +366,7 @@ public Function, Message> pojoMessageToPojoMessage() { return p -> { assertThat(p.getHeaders().get("someHeader").equals("foo")); Person newPerson = new Person(); - newPerson.setName(p.getPayload().getName().toUpperCase()); + newPerson.setName(p.getPayload().getName().toUpperCase(Locale.ROOT)); return MessageBuilder.withPayload(newPerson).copyHeaders(p.getHeaders()).setHeader("xyz", "hello").build(); }; } diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationRoutingTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationRoutingTests.java index 9fb7a4a1e..0afa56200 100644 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationRoutingTests.java +++ b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationRoutingTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.rsocket; +import java.util.Locale; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -183,7 +184,7 @@ public static class SampleFunctionConfiguration { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean @@ -193,7 +194,7 @@ public Function, String> uppercaseMessage() { .get(DestinationPatternsMessageCondition.LOOKUP_DESTINATION_HEADER)).toString().equals("uppercase"); assertThat(msg.getHeaders() .get(FunctionRSocketMessageHandler.RECONCILED_LOOKUP_DESTINATION_HEADER)).toString().equals(RoutingFunction.FUNCTION_NAME); - return msg.getPayload().toUpperCase(); + return msg.getPayload().toUpperCase(Locale.ROOT); }; } @@ -211,7 +212,7 @@ public Function echo() { public Function, Flux> uppercaseReactive() { return flux -> flux.map(v -> { System.out.println("Uppercasing: " + v); - return v.toUpperCase(); + return v.toUpperCase(Locale.ROOT); }); } diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java index f0b942174..05dc9b43e 100644 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java +++ b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RSocketAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.rsocket; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -644,7 +645,7 @@ public static class SampleFunctionConfiguration { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } @Bean @@ -667,7 +668,7 @@ public Function, Map> echoMap() { public Function, Flux> uppercaseReactive() { return flux -> flux.map(v -> { System.out.println("Uppercasing: " + v); - return v.toUpperCase(); + return v.toUpperCase(Locale.ROOT); }); } diff --git a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RoutingBrokerTests.java b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RoutingBrokerTests.java index 77bbd565a..3b6005681 100644 --- a/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RoutingBrokerTests.java +++ b/spring-cloud-function-rsocket/src/test/java/org/springframework/cloud/function/rsocket/RoutingBrokerTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.rsocket; import java.time.Duration; +import java.util.Locale; import java.util.function.Function; import io.rsocket.broker.client.spring.BrokerMetadata; @@ -139,7 +140,7 @@ public static class SimpleConfiguration { public static class SampleFunctionConfiguration { @Bean public Function uppercase() { - return v -> v.toUpperCase(); + return v -> v.toUpperCase(Locale.ROOT); } } } diff --git a/spring-cloud-function-samples/function-functional-sample-aws/pom.xml b/spring-cloud-function-samples/function-functional-sample-aws/pom.xml index 20aff4adb..4015e405d 100644 --- a/spring-cloud-function-samples/function-functional-sample-aws/pom.xml +++ b/spring-cloud-function-samples/function-functional-sample-aws/pom.xml @@ -15,7 +15,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 @@ -24,7 +24,7 @@ UTF-8 1.0.27.RELEASE 3.9.0 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java index a237eff87..c6392af4e 100644 --- a/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java +++ b/spring-cloud-function-samples/function-functional-sample-aws/src/main/java/example/FunctionConfiguration.java @@ -1,5 +1,6 @@ package example; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringBootConfiguration; @@ -25,7 +26,7 @@ public static void main(String[] args) { @Override public void initialize(GenericApplicationContext context) { - Function function = (str) -> str + str.toUpperCase(); + Function function = (str) -> str + str.toUpperCase(Locale.ROOT); context.registerBean("uppercase", FunctionRegistration.class, () -> new FunctionRegistration<>(function).type(FunctionTypeUtils.functionType(String.class, String.class))); diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/pom.xml b/spring-cloud-function-samples/function-sample-aws-custom-bean/pom.xml index 1ce90c0f0..f3a970b9a 100644 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/pom.xml +++ b/spring-cloud-function-samples/function-sample-aws-custom-bean/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 io.spring.sample @@ -16,7 +16,7 @@ 1.0.27.RELEASE - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/java/com/example/LambdaApplication.java b/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/java/com/example/LambdaApplication.java index 7fb2ecec8..da84c1287 100644 --- a/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/java/com/example/LambdaApplication.java +++ b/spring-cloud-function-samples/function-sample-aws-custom-bean/src/main/java/com/example/LambdaApplication.java @@ -1,6 +1,7 @@ package com.example; import java.util.Arrays; +import java.util.Locale; import java.util.function.Consumer; import java.util.function.Function; @@ -30,7 +31,7 @@ public Consumer consume() { public Function uppercase() { return value -> { logger.info("UPPERCASING: " + value); - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); }; } diff --git a/spring-cloud-function-samples/function-sample-aws-custom/pom.xml b/spring-cloud-function-samples/function-sample-aws-custom/pom.xml index 8a99ecd04..f6bb7aa29 100644 --- a/spring-cloud-function-samples/function-sample-aws-custom/pom.xml +++ b/spring-cloud-function-samples/function-sample-aws-custom/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 io.spring.sample @@ -16,7 +16,7 @@ 1.0.27.RELEASE - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT @@ -42,7 +42,11 @@ org.springframework.boot spring-boot-starter - + + com.amazonaws + aws-lambda-java-core + 1.1.0 + org.springframework.boot spring-boot-starter-test diff --git a/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java b/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java index da9d85523..289ba7807 100644 --- a/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java +++ b/spring-cloud-function-samples/function-sample-aws-custom/src/main/java/com/example/LambdaApplication.java @@ -1,5 +1,6 @@ package com.example; +import java.util.Locale; import java.util.function.Function; import org.apache.commons.logging.Log; @@ -24,7 +25,7 @@ public Function uppercase() { if (value.equals("error")) { throw new IllegalArgumentException("Intentional"); } - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); }; } diff --git a/spring-cloud-function-samples/function-sample-aws-native/pom.xml b/spring-cloud-function-samples/function-sample-aws-native/pom.xml index 1d72ed382..0034a7964 100644 --- a/spring-cloud-function-samples/function-sample-aws-native/pom.xml +++ b/spring-cloud-function-samples/function-sample-aws-native/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 oz.native.sample @@ -15,7 +15,7 @@ Sample of AWS with Spring Native 19 - 2023.0.4-SNAPSHOT + 2023.0.7-SNAPSHOT @@ -47,7 +47,6 @@ com.amazonaws aws-lambda-java-core 1.1.0 - provided diff --git a/spring-cloud-function-samples/function-sample-aws-native/src/main/java/com/example/demo/NativeFunctionApplication.java b/spring-cloud-function-samples/function-sample-aws-native/src/main/java/com/example/demo/NativeFunctionApplication.java index 832a72111..399d786db 100644 --- a/spring-cloud-function-samples/function-sample-aws-native/src/main/java/com/example/demo/NativeFunctionApplication.java +++ b/spring-cloud-function-samples/function-sample-aws-native/src/main/java/com/example/demo/NativeFunctionApplication.java @@ -1,15 +1,20 @@ package com.example.demo; +import java.util.Locale; import java.util.function.Function; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.function.adapter.aws.AWSLambdaUtils; import org.springframework.context.annotation.Bean; // import org.springframework.cloud.function.context.DefaultMessageRoutingHandler; // import org.springframework.cloud.function.context.MessageRoutingCallback; // import org.springframework.messaging.Message; +import org.springframework.messaging.Message; + +import com.amazonaws.services.lambda.runtime.Context; @SpringBootApplication public class NativeFunctionApplication { @@ -32,10 +37,11 @@ public static void main(String[] args) { // } @Bean - public Function uppercase() { - return v -> { - System.out.println("Uppercasing " + v); - return v.toUpperCase(); + public Function, String> uppercase() { + return message -> { + System.out.println("AWS Context: " + message.getHeaders().get(AWSLambdaUtils.AWS_CONTEXT)); + System.out.println("Uppercasing " + message.getPayload()); + return message.getPayload().toUpperCase(Locale.ROOT); }; } diff --git a/spring-cloud-function-samples/function-sample-aws-routing/pom.xml b/spring-cloud-function-samples/function-sample-aws-routing/pom.xml index cebe4a00e..7a889d582 100644 --- a/spring-cloud-function-samples/function-sample-aws-routing/pom.xml +++ b/spring-cloud-function-samples/function-sample-aws-routing/pom.xml @@ -15,7 +15,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 @@ -24,7 +24,7 @@ UTF-8 1.0.27.RELEASE 2.0.2 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample-aws-routing/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-sample-aws-routing/src/main/java/example/FunctionConfiguration.java index 29ccdf0d5..937ffe70b 100644 --- a/spring-cloud-function-samples/function-sample-aws-routing/src/main/java/example/FunctionConfiguration.java +++ b/spring-cloud-function-samples/function-sample-aws-routing/src/main/java/example/FunctionConfiguration.java @@ -1,5 +1,6 @@ package example; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringApplication; @@ -19,7 +20,7 @@ public static void main(String[] args) { @Bean public Function uppercase() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } @Bean diff --git a/spring-cloud-function-samples/function-sample-aws-serverless-web-native/pom.xml b/spring-cloud-function-samples/function-sample-aws-serverless-web-native/pom.xml index 9df0188af..bc8c53f46 100644 --- a/spring-cloud-function-samples/function-sample-aws-serverless-web-native/pom.xml +++ b/spring-cloud-function-samples/function-sample-aws-serverless-web-native/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 oz.native.sample @@ -16,7 +16,7 @@ Sample of AWS with Spring Native 21 - 2023.0.4-SNAPSHOT + 2023.0.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample-aws/pom.xml b/spring-cloud-function-samples/function-sample-aws/pom.xml index 7b8015dcc..217f33ecd 100644 --- a/spring-cloud-function-samples/function-sample-aws/pom.xml +++ b/spring-cloud-function-samples/function-sample-aws/pom.xml @@ -15,7 +15,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 @@ -24,7 +24,7 @@ UTF-8 1.0.29.RELEASE 3.9.0 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample-aws/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-sample-aws/src/main/java/example/FunctionConfiguration.java index ade13c608..e1bc3f4fe 100644 --- a/spring-cloud-function-samples/function-sample-aws/src/main/java/example/FunctionConfiguration.java +++ b/spring-cloud-function-samples/function-sample-aws/src/main/java/example/FunctionConfiguration.java @@ -1,5 +1,6 @@ package example; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -24,7 +25,7 @@ public Function uppercase() { throw new RuntimeException("Intentional exception"); } else { - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } }; } diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/pom.xml b/spring-cloud-function-samples/function-sample-azure-blob-trigger/pom.xml index 5a2b1f4a8..8e1db6cb1 100644 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/pom.xml +++ b/spring-cloud-function-samples/function-sample-azure-blob-trigger/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 diff --git a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplication.java index ce0ab0212..9ef38a8a6 100644 --- a/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplication.java +++ b/spring-cloud-function-samples/function-sample-azure-blob-trigger/src/main/java/com/example/azure/di/azureblobtriggerdemo/AzureBlobTriggerDemoApplication.java @@ -16,6 +16,7 @@ package com.example.azure.di.azureblobtriggerdemo; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringApplication; @@ -31,6 +32,6 @@ public static void main(String[] args) { @Bean public Function uppercase() { - return payload -> new String(payload).toUpperCase().getBytes(); + return payload -> new String(payload).toUpperCase(Locale.ROOT).getBytes(); } } diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/java/org/scf/azure/gradle/GradleDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/java/org/scf/azure/gradle/GradleDemoApplication.java index 3d5902df8..7ae83b90c 100644 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/java/org/scf/azure/gradle/GradleDemoApplication.java +++ b/spring-cloud-function-samples/function-sample-azure-http-trigger-gradle/src/main/java/org/scf/azure/gradle/GradleDemoApplication.java @@ -1,5 +1,6 @@ package org.scf.azure.gradle; +import java.util.Locale; import java.util.Optional; import java.util.function.Function; @@ -49,11 +50,11 @@ public Function, String> uppercase() { return message -> { ExecutionContext context = (ExecutionContext) message.getHeaders().get(AzureFunctionUtil.EXECUTION_CONTEXT); - String updatedPayload = message.getPayload().toUpperCase(); + String updatedPayload = message.getPayload().toUpperCase(Locale.ROOT); context.getLogger().info("Azure Test: " + updatedPayload); - return message.getPayload().toUpperCase(); + return message.getPayload().toUpperCase(Locale.ROOT); }; } diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/pom.xml b/spring-cloud-function-samples/function-sample-azure-http-trigger/pom.xml index d5b7ce401..3609b3b99 100644 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/pom.xml +++ b/spring-cloud-function-samples/function-sample-azure-http-trigger/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 diff --git a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/HttpTriggerDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/HttpTriggerDemoApplication.java index fc7bd8ea3..21d550d6f 100644 --- a/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/HttpTriggerDemoApplication.java +++ b/spring-cloud-function-samples/function-sample-azure-http-trigger/src/main/java/com/example/azure/di/httptriggerdemo/HttpTriggerDemoApplication.java @@ -16,6 +16,7 @@ package com.example.azure.di.httptriggerdemo; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringApplication; @@ -32,7 +33,7 @@ public Function echo() { @Bean public Function uppercase() { - return payload -> payload.toUpperCase(); + return payload -> payload.toUpperCase(Locale.ROOT); } @Bean diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/pom.xml b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/pom.xml index d8366387e..bcd10d53a 100644 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/pom.xml +++ b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 diff --git a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/KafkaTriggerDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/KafkaTriggerDemoApplication.java index bb01e7c8a..235c42e02 100644 --- a/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/KafkaTriggerDemoApplication.java +++ b/spring-cloud-function-samples/function-sample-azure-kafka-trigger/src/main/java/example/KafkaTriggerDemoApplication.java @@ -15,6 +15,7 @@ */ package example; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -45,7 +46,7 @@ public Function, String> uppercase(JsonMapper mapper) { Map valueMap = mapper.fromJson(kafkaEntity.getValue(), Map.class); if (valueMap != null) { valueMap.forEach((k, v) -> valueMap.put(k, - v != null && v instanceof String ? ((String) v).toUpperCase() : null)); + v != null && v instanceof String ? ((String) v).toUpperCase(Locale.ROOT) : null)); return mapper.toString(valueMap); } } diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/pom.xml b/spring-cloud-function-samples/function-sample-azure-time-trigger/pom.xml index 5169af601..bcb098d02 100644 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/pom.xml +++ b/spring-cloud-function-samples/function-sample-azure-time-trigger/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 diff --git a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/TimeTriggerDemoApplication.java b/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/TimeTriggerDemoApplication.java index e3e10ddc8..c61aedda4 100644 --- a/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/TimeTriggerDemoApplication.java +++ b/spring-cloud-function-samples/function-sample-azure-time-trigger/src/main/java/com/example/azure/di/timetriggerdemo/TimeTriggerDemoApplication.java @@ -16,6 +16,7 @@ package com.example.azure.di.timetriggerdemo; +import java.util.Locale; import java.util.function.Consumer; import com.microsoft.azure.functions.ExecutionContext; @@ -40,7 +41,7 @@ public static void main(String[] args) { public Consumer> uppercase() { return message -> { String timeInfo = message.getPayload(); - String value = timeInfo.toUpperCase(); + String value = timeInfo.toUpperCase(Locale.ROOT); logger.info("Timer is triggered with TimeInfo: " + value); diff --git a/spring-cloud-function-samples/function-sample-azure-web/pom.xml b/spring-cloud-function-samples/function-sample-azure-web/pom.xml index 057e845b9..7b17c658c 100644 --- a/spring-cloud-function-samples/function-sample-azure-web/pom.xml +++ b/spring-cloud-function-samples/function-sample-azure-web/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 diff --git a/spring-cloud-function-samples/function-sample-azure/pom.xml b/spring-cloud-function-samples/function-sample-azure/pom.xml index 0b5356ec4..0c29edd40 100644 --- a/spring-cloud-function-samples/function-sample-azure/pom.xml +++ b/spring-cloud-function-samples/function-sample-azure/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java index b40249436..97ae9b2e7 100644 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java +++ b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/Config.java @@ -16,6 +16,7 @@ package example; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -54,7 +55,7 @@ public Function, String> uppercase(JsonMapper mapper) { Map map = mapper.fromJson(value, Map.class); if(map != null) - map.forEach((k, v) -> map.put(k, v != null ? v.toUpperCase() : null)); + map.forEach((k, v) -> map.put(k, v != null ? v.toUpperCase(Locale.ROOT) : null)); if(context != null) context.getLogger().info(new StringBuilder().append("Function: ") @@ -73,12 +74,12 @@ public Function, String> uppercase(JsonMapper mapper) { @Bean public Function, Mono> uppercaseReactive() { - return mono -> mono.map(value -> value.toUpperCase()); + return mono -> mono.map(value -> value.toUpperCase(Locale.ROOT)); } @Bean public Function, Flux> echoStream() { - return flux -> flux.map(value -> value.toUpperCase()); + return flux -> flux.map(value -> value.toUpperCase(Locale.ROOT)); } } diff --git a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveEchoCustomResultHandler.java b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveEchoCustomResultHandler.java index 8f72a80bc..744a46b6d 100644 --- a/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveEchoCustomResultHandler.java +++ b/spring-cloud-function-samples/function-sample-azure/src/main/java/example/ReactiveEchoCustomResultHandler.java @@ -17,6 +17,7 @@ package example; import java.util.List; +import java.util.Locale; import com.microsoft.azure.functions.ExecutionContext; import com.microsoft.azure.functions.HttpMethod; @@ -54,7 +55,7 @@ protected String postProcessFluxFunctionResult(List rawInputs, Object fu ) { functionResult .doFirst(() -> executionContext.getLogger().info("BEGIN echo post-processing work ...")) - .mapNotNull((v) -> v.toString().toUpperCase()) + .mapNotNull((v) -> v.toString().toUpperCase(Locale.ROOT)) .doFinally((signalType) -> executionContext.getLogger().info("END echo post-processing work")) .subscribe((v) -> executionContext.getLogger().info(" " + v)); return "Kicked off job for " + rawInputs; diff --git a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/pom.xml b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/pom.xml index ef77c6569..707f298ea 100644 --- a/spring-cloud-function-samples/function-sample-cloudevent-rsocket/pom.xml +++ b/spring-cloud-function-samples/function-sample-cloudevent-rsocket/pom.xml @@ -11,12 +11,12 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-samples/function-sample-cloudevent-sdk/pom.xml b/spring-cloud-function-samples/function-sample-cloudevent-sdk/pom.xml index e03225666..caf8640c3 100644 --- a/spring-cloud-function-samples/function-sample-cloudevent-sdk/pom.xml +++ b/spring-cloud-function-samples/function-sample-cloudevent-sdk/pom.xml @@ -11,12 +11,12 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-samples/function-sample-cloudevent-stream/pom.xml b/spring-cloud-function-samples/function-sample-cloudevent-stream/pom.xml index 7647ddf3d..8f514dd68 100644 --- a/spring-cloud-function-samples/function-sample-cloudevent-stream/pom.xml +++ b/spring-cloud-function-samples/function-sample-cloudevent-stream/pom.xml @@ -11,12 +11,12 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-samples/function-sample-cloudevent/pom.xml b/spring-cloud-function-samples/function-sample-cloudevent/pom.xml index f51f55a62..9fa70c33f 100644 --- a/spring-cloud-function-samples/function-sample-cloudevent/pom.xml +++ b/spring-cloud-function-samples/function-sample-cloudevent/pom.xml @@ -11,12 +11,12 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/pom.xml b/spring-cloud-function-samples/function-sample-functional-aws-routing/pom.xml index 83875c85c..7311f396f 100644 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/pom.xml +++ b/spring-cloud-function-samples/function-sample-functional-aws-routing/pom.xml @@ -15,7 +15,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 @@ -24,7 +24,7 @@ UTF-8 1.0.27.RELEASE 2.0.2 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/java/example/FunctionConfiguration.java b/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/java/example/FunctionConfiguration.java index 554133da0..3ae200a36 100644 --- a/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/java/example/FunctionConfiguration.java +++ b/spring-cloud-function-samples/function-sample-functional-aws-routing/src/main/java/example/FunctionConfiguration.java @@ -1,5 +1,6 @@ package example; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringApplication; @@ -23,7 +24,7 @@ public static void main(String[] args) { } public Function uppercase() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } public Function reverse() { diff --git a/spring-cloud-function-samples/function-sample-gcp-background/pom.xml b/spring-cloud-function-samples/function-sample-gcp-background/pom.xml index d40ed63cc..c64478f5b 100644 --- a/spring-cloud-function-samples/function-sample-gcp-background/pom.xml +++ b/spring-cloud-function-samples/function-sample-gcp-background/pom.xml @@ -15,7 +15,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 diff --git a/spring-cloud-function-samples/function-sample-gcp-http/pom.xml b/spring-cloud-function-samples/function-sample-gcp-http/pom.xml index 03f8ea10d..45063efe3 100644 --- a/spring-cloud-function-samples/function-sample-gcp-http/pom.xml +++ b/spring-cloud-function-samples/function-sample-gcp-http/pom.xml @@ -15,12 +15,12 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample-gcp-http/src/main/java/com/example/CloudFunctionMain.java b/spring-cloud-function-samples/function-sample-gcp-http/src/main/java/com/example/CloudFunctionMain.java index dc5f213ab..c5fe46794 100644 --- a/spring-cloud-function-samples/function-sample-gcp-http/src/main/java/com/example/CloudFunctionMain.java +++ b/spring-cloud-function-samples/function-sample-gcp-http/src/main/java/com/example/CloudFunctionMain.java @@ -16,6 +16,7 @@ package com.example; +import java.util.Locale; import java.util.function.Function; import org.springframework.boot.SpringApplication; @@ -31,6 +32,6 @@ public static void main(String[] args) { @Bean public Function function() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java b/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java index 2ebd4f819..69f813c1a 100644 --- a/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java +++ b/spring-cloud-function-samples/function-sample-gcp-http/src/test/java/com/example/FunctionSampleGcpIntegrationTest.java @@ -28,7 +28,7 @@ public class FunctionSampleGcpIntegrationTest { private TestRestTemplate rest = new TestRestTemplate(); - @Test + //@Test public void testSample() throws IOException, InterruptedException { try (LocalServerTestSupport.ServerProcess process = LocalServerTestSupport.startServer(CloudFunctionMain.class)) { String result = rest.postForObject("http://localhost:8080/", "Hello", String.class); diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/pom.xml b/spring-cloud-function-samples/function-sample-grpc-cloudevent/pom.xml index 0e22f9387..754bd1fa2 100644 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/pom.xml +++ b/spring-cloud-function-samples/function-sample-grpc-cloudevent/pom.xml @@ -7,7 +7,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 com.example.grpc @@ -16,7 +16,7 @@ function-sample-grpc-cloudevent Demo project for Spring Boot - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.55.1 diff --git a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/java/com/example/grpc/demo/DemoGrpcApplication.java b/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/java/com/example/grpc/demo/DemoGrpcApplication.java index 83d7bcc33..fe64801ff 100644 --- a/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/java/com/example/grpc/demo/DemoGrpcApplication.java +++ b/spring-cloud-function-samples/function-sample-grpc-cloudevent/src/main/java/com/example/grpc/demo/DemoGrpcApplication.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -55,7 +56,7 @@ public static void main(String[] args) throws Exception { @Bean public Function, Message> uppercase() { return message -> { - return MessageBuilder.withPayload(message.getPayload().toUpperCase()) + return MessageBuilder.withPayload(message.getPayload().toUpperCase(Locale.ROOT)) .copyHeaders(message.getHeaders()) .setHeader("uppercased", "true") .build(); diff --git a/spring-cloud-function-samples/function-sample-kotlin-web/pom.xml b/spring-cloud-function-samples/function-sample-kotlin-web/pom.xml index abe2eca22..ca2c7e968 100644 --- a/spring-cloud-function-samples/function-sample-kotlin-web/pom.xml +++ b/spring-cloud-function-samples/function-sample-kotlin-web/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 @@ -28,11 +28,6 @@ org.jetbrains.kotlin kotlin-stdlib-jdk8 - - org.springframework.cloud - spring-cloud-function-kotlin - 4.1.1-SNAPSHOT - org.springframework.cloud spring-cloud-function-web diff --git a/spring-cloud-function-samples/function-sample-pof/pom.xml b/spring-cloud-function-samples/function-sample-pof/pom.xml index b8081f2f0..fc775296f 100644 --- a/spring-cloud-function-samples/function-sample-pof/pom.xml +++ b/spring-cloud-function-samples/function-sample-pof/pom.xml @@ -13,7 +13,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 @@ -21,7 +21,7 @@ UTF-8 UTF-8 3.1.2.RELEASE - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample-pojo/pom.xml b/spring-cloud-function-samples/function-sample-pojo/pom.xml index 2b1abc464..be4f0d1a3 100644 --- a/spring-cloud-function-samples/function-sample-pojo/pom.xml +++ b/spring-cloud-function-samples/function-sample-pojo/pom.xml @@ -14,12 +14,12 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java b/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java index 52fe3dbf5..1e6aa9b44 100644 --- a/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java +++ b/spring-cloud-function-samples/function-sample-pojo/src/main/java/com/example/SampleApplication.java @@ -17,6 +17,7 @@ package com.example; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; @@ -71,11 +72,11 @@ class Foo { } public String lowercase() { - return this.value.toLowerCase(); + return this.value.toLowerCase(Locale.ROOT); } public String uppercase() { - return this.value.toUpperCase(); + return this.value.toUpperCase(Locale.ROOT); } public String getValue() { diff --git a/spring-cloud-function-samples/function-sample-spring-integration/pom.xml b/spring-cloud-function-samples/function-sample-spring-integration/pom.xml index b7f6158ee..14e6423ac 100644 --- a/spring-cloud-function-samples/function-sample-spring-integration/pom.xml +++ b/spring-cloud-function-samples/function-sample-spring-integration/pom.xml @@ -12,7 +12,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 @@ -20,7 +20,7 @@ UTF-8 UTF-8 17 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample-supplier-exporter/pom.xml b/spring-cloud-function-samples/function-sample-supplier-exporter/pom.xml index 6b1668a5e..96d630494 100644 --- a/spring-cloud-function-samples/function-sample-supplier-exporter/pom.xml +++ b/spring-cloud-function-samples/function-sample-supplier-exporter/pom.xml @@ -14,12 +14,12 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/function-sample/pom.xml b/spring-cloud-function-samples/function-sample/pom.xml index b35568b2d..8922b8dff 100644 --- a/spring-cloud-function-samples/function-sample/pom.xml +++ b/spring-cloud-function-samples/function-sample/pom.xml @@ -14,12 +14,12 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT 1.0.27.RELEASE diff --git a/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java b/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java index f26aec07f..533db8629 100644 --- a/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java +++ b/spring-cloud-function-samples/function-sample/src/main/java/com/example/SampleApplication.java @@ -17,6 +17,7 @@ package com.example; import java.time.Duration; +import java.util.Locale; import java.util.function.Function; import java.util.function.Supplier; @@ -38,17 +39,17 @@ public static void main(String[] args) throws Exception { @Bean public Function uppercase() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } @Bean public Function, Integer> uppercaseMessage() { - return value -> value.getPayload().toUpperCase().length(); + return value -> value.getPayload().toUpperCase(Locale.ROOT).length(); } @Bean public Function, Flux> lowercase() { - return flux -> flux.map(value -> value.toLowerCase()); + return flux -> flux.map(value -> value.toLowerCase(Locale.ROOT)); } @Bean diff --git a/spring-cloud-function-samples/pom.xml b/spring-cloud-function-samples/pom.xml index 0fb10f9aa..4136b7aef 100644 --- a/spring-cloud-function-samples/pom.xml +++ b/spring-cloud-function-samples/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT diff --git a/spring-cloud-function-samples/scf-aws-day1/pom.xml b/spring-cloud-function-samples/scf-aws-day1/pom.xml index 05f4ddf36..cf5d153a4 100644 --- a/spring-cloud-function-samples/scf-aws-day1/pom.xml +++ b/spring-cloud-function-samples/scf-aws-day1/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 oz.spring @@ -15,7 +15,7 @@ Template project for creating function that can be deployed as AWS Lambda 17 - 2023.0.4-SNAPSHOT + 2023.0.7-SNAPSHOT 1.0.31.RELEASE 3.9.0 1.1.0 diff --git a/spring-cloud-function-samples/scf-aws-day1/src/main/java/oz/spring/aws/functions/FunctionConfiguration.java b/spring-cloud-function-samples/scf-aws-day1/src/main/java/oz/spring/aws/functions/FunctionConfiguration.java index f019c0fc6..aeacc8950 100644 --- a/spring-cloud-function-samples/scf-aws-day1/src/main/java/oz/spring/aws/functions/FunctionConfiguration.java +++ b/spring-cloud-function-samples/scf-aws-day1/src/main/java/oz/spring/aws/functions/FunctionConfiguration.java @@ -1,5 +1,6 @@ package oz.spring.aws.functions; +import java.util.Locale; import java.util.function.Function; import org.springframework.context.annotation.Bean; @@ -10,6 +11,6 @@ public class FunctionConfiguration { @Bean public Function uppercase() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-samples/scf-aws-routing/pom.xml b/spring-cloud-function-samples/scf-aws-routing/pom.xml index dc7203694..cf88e8815 100644 --- a/spring-cloud-function-samples/scf-aws-routing/pom.xml +++ b/spring-cloud-function-samples/scf-aws-routing/pom.xml @@ -5,7 +5,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.8 + 3.3.13 oz.spring @@ -15,7 +15,7 @@ Template project for creating function that can be deployed as AWS Lambda 17 - 2023.0.4-SNAPSHOT + 2023.0.7-SNAPSHOT 1.0.31.RELEASE 3.9.0 1.1.0 diff --git a/spring-cloud-function-samples/scf-aws-routing/src/main/java/oz/spring/aws/functions/FunctionRoutingConfiguration.java b/spring-cloud-function-samples/scf-aws-routing/src/main/java/oz/spring/aws/functions/FunctionRoutingConfiguration.java index cfbfa9c3e..500b1b05c 100644 --- a/spring-cloud-function-samples/scf-aws-routing/src/main/java/oz/spring/aws/functions/FunctionRoutingConfiguration.java +++ b/spring-cloud-function-samples/scf-aws-routing/src/main/java/oz/spring/aws/functions/FunctionRoutingConfiguration.java @@ -1,5 +1,6 @@ package oz.spring.aws.functions; +import java.util.Locale; import java.util.function.Function; import org.springframework.context.annotation.Bean; @@ -10,7 +11,7 @@ public class FunctionRoutingConfiguration { @Bean public Function lowercase() { - return value -> value.toLowerCase(); + return value -> value.toLowerCase(Locale.ROOT); } @Bean diff --git a/spring-cloud-function-web/pom.xml b/spring-cloud-function-web/pom.xml index 87d7b8a61..3edcefa71 100644 --- a/spring-cloud-function-web/pom.xml +++ b/spring-cloud-function-web/pom.xml @@ -12,7 +12,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT @@ -55,6 +55,12 @@ org.springframework.boot spring-boot-starter-test test + + + com.vaadin.external.google + android-json + + org.springframework.boot diff --git a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/HeaderUtils.java b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/HeaderUtils.java index 3c8fbb56f..6c3d37265 100644 --- a/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/HeaderUtils.java +++ b/spring-cloud-function-web/src/main/java/org/springframework/cloud/function/web/util/HeaderUtils.java @@ -21,6 +21,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import org.springframework.http.HttpHeaders; @@ -59,7 +60,7 @@ public static HttpHeaders fromMessage(MessageHeaders headers, List ignor HttpHeaders result = new HttpHeaders(); for (String name : headers.keySet()) { Object value = headers.get(name); - name = name.toLowerCase(); + name = name.toLowerCase(Locale.ROOT); if (!IGNORED.containsKey(name) && !ignoredHeders.contains(name)) { Collection values = multi(value); for (Object object : values) { @@ -80,7 +81,7 @@ public static HttpHeaders sanitize(HttpHeaders request, List ignoredHede HttpHeaders result = new HttpHeaders(); for (String name : request.keySet()) { List value = request.get(name); - name = name.toLowerCase(); + name = name.toLowerCase(Locale.ROOT); if (!IGNORED.containsKey(name) && !REQUEST_ONLY.containsKey(name) && !ignoredHeders.contains(name) && !requestOnlyHeaders.contains(name)) { result.put(name, value); } @@ -97,10 +98,10 @@ public static MessageHeaders fromHttp(HttpHeaders headers) { Map map = new LinkedHashMap<>(); for (String name : headers.keySet()) { Collection values = multi(headers.get(name)); - name = name.toLowerCase(); + name = name.toLowerCase(Locale.ROOT); Object value = values == null ? null : (values.size() == 1 ? values.iterator().next() : values); - if (name.toLowerCase().equals(HttpHeaders.CONTENT_TYPE.toLowerCase())) { + if (name.toLowerCase(Locale.ROOT).equals(HttpHeaders.CONTENT_TYPE.toLowerCase(Locale.ROOT))) { name = MessageHeaders.CONTENT_TYPE; } map.put(name, value); diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/flux/FluxRestApplicationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/flux/FluxRestApplicationTests.java index 660910910..63f6a3a36 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/flux/FluxRestApplicationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/flux/FluxRestApplicationTests.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -300,13 +301,13 @@ public static class TestConfiguration { @PostMapping({ "/uppercase", "/transform", "/post/more" }) public Flux uppercase(@RequestBody List flux) { return Flux.fromIterable(flux).log() - .map(value -> "[" + value.trim().toUpperCase() + "]"); + .map(value -> "[" + value.trim().toUpperCase(Locale.ROOT) + "]"); } @PostMapping({ "/alt" }) public Mono> alt(@RequestBody List flux) { Publisher result = Flux.fromIterable(flux) - .map(value -> "[" + value.trim().toUpperCase() + "]"); + .map(value -> "[" + value.trim().toUpperCase(Locale.ROOT) + "]"); return Flux.from(result).log() .then(Mono.fromSupplier(() -> ResponseEntity.ok(result))); } @@ -314,12 +315,12 @@ public Mono> alt(@RequestBody List flux) { @PostMapping("/upFoos") public Flux upFoos(@RequestBody List list) { return Flux.fromIterable(list).log() - .map(value -> new Foo(value.getValue().trim().toUpperCase())); + .map(value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT))); } @GetMapping("/uppercase/{id}") public Mono> uppercaseGet(@PathVariable String id) { - return Mono.just(id).map(value -> "[" + value.trim().toUpperCase() + "]") + return Mono.just(id).map(value -> "[" + value.trim().toUpperCase(Locale.ROOT) + "]") .flatMap(body -> Mono.just(ResponseEntity.ok(body))); } @@ -339,7 +340,7 @@ public Mono> entity(@PathVariable Integer id) { public Flux> maps( @RequestBody List> flux) { return Flux.fromIterable(flux).map(value -> { - value.put("value", value.get("value").trim().toUpperCase()); + value.put("value", value.get("value").trim().toUpperCase(Locale.ROOT)); return value; }); } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/mvc/MvcRestApplicationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/mvc/MvcRestApplicationTests.java index 1d051ae3b..0a8f73bf4 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/mvc/MvcRestApplicationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/mvc/MvcRestApplicationTests.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import org.junit.jupiter.api.BeforeEach; @@ -289,18 +290,18 @@ public static class TestConfiguration { @PostMapping({ "/uppercase", "/transform", "/post/more" }) public Flux uppercase(@RequestBody List flux) { return Flux.fromIterable(flux).log() - .map(value -> "[" + value.trim().toUpperCase() + "]"); + .map(value -> "[" + value.trim().toUpperCase(Locale.ROOT) + "]"); } @PostMapping("/upFoos") public Flux upFoos(@RequestBody List list) { return Flux.fromIterable(list).log() - .map(value -> new Foo(value.getValue().trim().toUpperCase())); + .map(value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT))); } @GetMapping("/uppercase/{id}") public Mono uppercaseGet(@PathVariable String id) { - return Mono.just(id).map(value -> "[" + value.trim().toUpperCase() + "]"); + return Mono.just(id).map(value -> "[" + value.trim().toUpperCase(Locale.ROOT) + "]"); } @GetMapping("/wrap/{id}") @@ -318,7 +319,7 @@ public Mono> entity(@PathVariable Integer id) { public Flux> maps( @RequestBody List> flux) { return Flux.fromIterable(flux).map(value -> { - value.put("value", value.get("value").trim().toUpperCase()); + value.put("value", value.get("value").trim().toUpperCase(Locale.ROOT)); return value; }); } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ExplicitNonFunctionalTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ExplicitNonFunctionalTests.java index a25046754..588c017fe 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ExplicitNonFunctionalTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ExplicitNonFunctionalTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.test; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -54,7 +55,7 @@ protected static class TestConfiguration implements Function { @Override public String apply(String value) { - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java index 15ecb84c7..f72500d42 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalExporterTests.java @@ -19,6 +19,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.function.Function; @@ -111,7 +112,7 @@ protected static class ApplicationConfiguration Function, Message> uppercase() { return value -> { headers.putAll(value.getHeaders()); - return MessageBuilder.withPayload(value.getPayload().getName().toUpperCase()) + return MessageBuilder.withPayload(value.getPayload().getName().toUpperCase(Locale.ROOT)) .copyHeaders(value.getHeaders()).build(); }; } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalTests.java index 00883f4c8..434f2fb80 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.test; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -50,7 +51,7 @@ protected static class TestConfiguration implements Function { @Override public String apply(String value) { - return value.toUpperCase(); + return value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputListTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputListTests.java index d30b7df71..28dfc0f76 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputListTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputListTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.test; import java.util.List; +import java.util.Locale; import java.util.function.Function; import java.util.stream.Collectors; @@ -54,7 +55,7 @@ protected static class TestConfiguration implements Function, Foo> { @Override public Foo apply(List value) { - return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase()) + return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase(Locale.ROOT)) .collect(Collectors.joining())); } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputSetTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputSetTests.java index 066959957..e5f426834 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputSetTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/FunctionalWithInputSetTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.test; import java.time.Duration; +import java.util.Locale; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; @@ -62,7 +63,7 @@ protected static class TestConfiguration implements Function, Foo> { @Override public Foo apply(Set value) { - return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase()) + return new Foo(value.stream().map(foo -> foo.getValue().toUpperCase(Locale.ROOT)) .collect(Collectors.joining())); } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/HeadersToMessageTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/HeadersToMessageTests.java index 710843883..c0da259cc 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/HeadersToMessageTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/HeadersToMessageTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.test; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -57,7 +58,7 @@ protected static class TestConfiguration @Override public Message apply(Message request) { Message message = MessageBuilder - .withPayload(request.getPayload().toUpperCase()) + .withPayload(request.getPayload().toUpperCase(Locale.ROOT)) .setHeader("X-Content-Type", "application/xml") .setHeader("foo", "bar").build(); return message; diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ImplicitNonFunctionalTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ImplicitNonFunctionalTests.java index 28e067430..ac26fd14f 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ImplicitNonFunctionalTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/ImplicitNonFunctionalTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.test; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -54,7 +55,7 @@ protected static class TestConfiguration { @Bean public Function uppercase() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/PojoTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/PojoTests.java index b622e8a0f..44efc5ff8 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/PojoTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/test/PojoTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.function.test; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -60,7 +61,7 @@ protected static class TestConfiguration implements Function { @Override public Foo apply(Foo value) { - return new Foo(value.getValue().toUpperCase()); + return new Foo(value.getValue().toUpperCase(Locale.ROOT)); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpGetIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpGetIntegrationTests.java index 2505c6a53..effaa7c30 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpGetIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpGetIntegrationTests.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; @@ -278,7 +279,7 @@ public Function, Flux> reverse() { @Bean({ "uppercase", "post/more" }) public Function, Flux> uppercase() { return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase() + ")"); + .map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); } @Bean diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpPostIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpPostIntegrationTests.java index 0598c06f7..718f356f2 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpPostIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/flux/HttpPostIntegrationTests.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -410,37 +411,37 @@ public static void main(String[] args) throws Exception { @Bean({ "uppercase", "transform", "post/more" }) public Function, Flux> uppercase() { return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase() + ")"); + .map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); } @Bean public Function bareUppercase() { - return value -> "(" + value.trim().toUpperCase() + ")"; + return value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"; } @Bean public Function, Message> messages() { return value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase() + ")") + .withPayload("(" + value.getPayload().trim().toUpperCase(Locale.ROOT) + ")") .copyHeaders(value.getHeaders()).build(); } @Bean public Function>, Flux>> headers() { return flux -> flux.map(value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase() + ")") + .withPayload("(" + value.getPayload().trim().toUpperCase(Locale.ROOT) + ")") .setHeader("foo", "bar").build()); } @Bean public Function, Flux> upFoos() { return flux -> flux.log() - .map(value -> new Foo(value.getValue().trim().toUpperCase())); + .map(value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT))); } @Bean public Function bareUpFoos() { - return value -> new Foo(value.getValue().trim().toUpperCase()); + return value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT)); } @Bean @@ -464,7 +465,7 @@ public Function, Flux> doubler() { @Bean public Function>, Flux>> maps() { return flux -> flux.map(value -> { - value.put("value", value.get("value").trim().toUpperCase()); + value.put("value", value.get("value").trim().toUpperCase(Locale.ROOT)); return value; }); } @@ -473,7 +474,7 @@ public Function>, Flux>> maps() @Qualifier("foos") public Function qualifier() { return value -> { - return new Foo("[" + value.trim().toUpperCase() + "]"); + return new Foo("[" + value.trim().toUpperCase(Locale.ROOT) + "]"); }; } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerMVCTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerMVCTests.java index e6aa8efd4..fa749cd07 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerMVCTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerMVCTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.web.function; import java.net.URI; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -66,12 +67,12 @@ protected static class ApplicationConfiguration { @Bean public Function uppercase() { - return s -> s.toUpperCase(); + return s -> s.toUpperCase(Locale.ROOT); } @Bean public Function lowercase() { - return s -> s.toLowerCase(); + return s -> s.toLowerCase(Locale.ROOT); } @Bean diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java index 4429d0ce4..bcebc1b4d 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/FunctionEndpointInitializerTests.java @@ -19,6 +19,7 @@ import java.net.URI; import java.time.Duration; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -196,11 +197,11 @@ public Supplier supplier() { } public Function uppercase() { - return s -> s.toUpperCase(); + return s -> s.toUpperCase(Locale.ROOT); } public Function lowercase() { - return s -> s.toLowerCase(); + return s -> s.toLowerCase(Locale.ROOT); } public Function reverse() { diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/UserSubmittedTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/UserSubmittedTests.java index dfbe5413a..0994efb7d 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/UserSubmittedTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/function/UserSubmittedTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.web.function; import java.net.URI; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -69,7 +70,7 @@ protected static class Issue274Configuration { @Bean public Function echo() { - return s -> s.toUpperCase(); + return s -> s.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/DefaultRouteTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/DefaultRouteTests.java index e441eb5d9..e5510722c 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/DefaultRouteTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/DefaultRouteTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.web.mvc; import java.net.URI; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Disabled; @@ -77,7 +78,7 @@ protected static class TestConfiguration { @Bean public Function, Flux> uppercase() { - return flux -> flux.map(value -> value.toUpperCase()); + return flux -> flux.map(value -> value.toUpperCase(Locale.ROOT)); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpGetIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpGetIntegrationTests.java index d1629d0ec..e0092fbab 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpGetIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpGetIntegrationTests.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; @@ -292,7 +293,7 @@ public Function, Flux> reverse() { @Bean({ "uppercase", "post/more" }) public Function, Flux> uppercase() { return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase() + ")"); + .map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); } @Bean diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpPostIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpPostIntegrationTests.java index a12c46dcf..b3d9b2aa4 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpPostIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/HttpPostIntegrationTests.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -348,37 +349,37 @@ public static void main(String[] args) throws Exception { @Bean({ "uppercase", "transform", "post/more" }) public Function, Flux> uppercase() { return flux -> flux.log() - .map(value -> "(" + value.trim().toUpperCase() + ")"); + .map(value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"); } @Bean public Function bareUppercase() { - return value -> "(" + value.trim().toUpperCase() + ")"; + return value -> "(" + value.trim().toUpperCase(Locale.ROOT) + ")"; } @Bean public Function, Message> messages() { return value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase() + ")") + .withPayload("(" + value.getPayload().trim().toUpperCase(Locale.ROOT) + ")") .copyHeaders(value.getHeaders()).build(); } @Bean public Function>, Flux>> headers() { return flux -> flux.map(value -> MessageBuilder - .withPayload("(" + value.getPayload().trim().toUpperCase() + ")") + .withPayload("(" + value.getPayload().trim().toUpperCase(Locale.ROOT) + ")") .setHeader("foo", "bar").build()); } @Bean public Function, Flux> upFoos() { return flux -> flux.log() - .map(value -> new Foo(value.getValue().trim().toUpperCase())); + .map(value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT))); } @Bean public Function bareUpFoos() { - return value -> new Foo(value.getValue().trim().toUpperCase()); + return value -> new Foo(value.getValue().trim().toUpperCase(Locale.ROOT)); } @Bean @@ -394,7 +395,7 @@ public Function, Flux> doubler() { @Bean public Function>, Flux>> maps() { return flux -> flux.map(value -> { - value.put("value", value.get("value").trim().toUpperCase()); + value.put("value", value.get("value").trim().toUpperCase(Locale.ROOT)); return value; }); } @@ -402,7 +403,7 @@ public Function>, Flux>> maps() @Bean @Qualifier("foos") public Function qualifier() { - return value -> new Foo("[" + value.trim().toUpperCase() + "]"); + return value -> new Foo("[" + value.trim().toUpperCase(Locale.ROOT) + "]"); } @Bean diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/MultipartFileTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/MultipartFileTests.java index 5b35a86c4..e1fde9f2c 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/MultipartFileTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/MultipartFileTests.java @@ -18,6 +18,7 @@ import java.net.URI; import java.util.List; +import java.util.Locale; import java.util.function.Function; import org.junit.jupiter.api.Test; @@ -93,7 +94,7 @@ protected static class TestConfiguration { @Bean public Function uppercase() { return value -> { - return value.getOriginalFilename().toUpperCase(); + return value.getOriginalFilename().toUpperCase(Locale.ROOT); }; } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/RoutingFunctionTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/RoutingFunctionTests.java index e032af809..b107d96bf 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/RoutingFunctionTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/mvc/RoutingFunctionTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.function.web.mvc; import java.net.URI; +import java.util.Locale; import java.util.Map; import java.util.function.Consumer; import java.util.function.Function; @@ -210,7 +211,7 @@ public Function echo() { public Function, Flux> fluxuppercase() { return v -> v.map(s -> { System.out.println(s); - return s.toUpperCase(); + return s.toUpperCase(Locale.ROOT); }); } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationIntegrationTests.java index 73f768b4d..7d272f701 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationIntegrationTests.java @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.Iterator; import java.util.List; +import java.util.Locale; import java.util.function.Function; import org.apache.commons.logging.Log; @@ -94,7 +95,7 @@ public static class ApplicationConfiguration { @Bean public Function uppercase() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationWithRetriesIntegrationTests.java b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationWithRetriesIntegrationTests.java index e7957e32b..e15b571f8 100644 --- a/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationWithRetriesIntegrationTests.java +++ b/spring-cloud-function-web/src/test/java/org/springframework/cloud/function/web/source/FunctionAutoConfigurationWithRetriesIntegrationTests.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.function.Function; import org.apache.commons.logging.Log; @@ -96,7 +97,7 @@ public static class ApplicationConfiguration { @Bean public Function uppercase() { - return value -> value.toUpperCase(); + return value -> value.toUpperCase(Locale.ROOT); } } diff --git a/spring-cloud-starter-function-web/pom.xml b/spring-cloud-starter-function-web/pom.xml index c91db1633..da8f85626 100644 --- a/spring-cloud-starter-function-web/pom.xml +++ b/spring-cloud-starter-function-web/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT .. spring-cloud-starter-function-web diff --git a/spring-cloud-starter-function-webflux/pom.xml b/spring-cloud-starter-function-webflux/pom.xml index c5e4c29d6..a2683e1ed 100644 --- a/spring-cloud-starter-function-webflux/pom.xml +++ b/spring-cloud-starter-function-webflux/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-function-parent - 4.1.4-SNAPSHOT + 4.1.7-SNAPSHOT spring-cloud-starter-function-webflux spring-cloud-starter-function-webflux